ARG002 Unused method argument: `config_type`
   --> src/skill_seekers/cli/config_extractor.py:294:47
    |
292 |         return None
293 |
294 |     def _infer_purpose(self, file_path: Path, config_type: str) -> str:
    |                                               ^^^^^^^^^^^
295 |         """Infer configuration purpose from file path and name"""
296 |         path_lower = str(file_path).lower()
    |

SIM102 Use a single `if` statement instead of nested `if` statements
   --> src/skill_seekers/cli/config_extractor.py:469:17
    |
468 |               for node in ast.walk(tree):
469 | /                 if isinstance(node, ast.Assign):
470 | |                     # Get variable name and skip private variables
471 | |                     if len(node.targets) == 1 and isinstance(node.targets[0], ast.Name) and not node.targets[0].id.startswith("_"):
    | |___________________________________________________________________________________________________________________________________^
472 |                           key = node.targets[0].id
    |
help: Combine `if` statements using `and`

ARG002 Unused method argument: `node`
   --> src/skill_seekers/cli/config_extractor.py:585:41
    |
583 |         return ""
584 |
585 |     def _extract_python_docstring(self, node: ast.AST) -> str:
    |                                         ^^^^
586 |         """Extract docstring/comment for Python node"""
587 |         # This is simplified - real implementation would need more context
    |

B904 Within an `except` clause, raise exceptions with `raise ... from err` or `raise ... from None` to distinguish them from errors in exception handling
  --> src/skill_seekers/cli/config_validator.py:60:13
   |
58 |                 return json.load(f)
59 |         except FileNotFoundError:
60 |             raise ValueError(f"Config file not found: {self.config_path}")
   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
61 |         except json.JSONDecodeError as e:
62 |             raise ValueError(f"Invalid JSON in config file: {e}")
   |

B904 Within an `except` clause, raise exceptions with `raise ... from err` or `raise ... from None` to distinguish them from errors in exception handling
  --> src/skill_seekers/cli/config_validator.py:62:13
   |
60 |             raise ValueError(f"Config file not found: {self.config_path}")
61 |         except json.JSONDecodeError as e:
62 |             raise ValueError(f"Invalid JSON in config file: {e}")
   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
63 |
64 |     def _detect_format(self) -> bool:
   |

SIM113 Use `enumerate()` for index variable `completed` in `for` loop
    --> src/skill_seekers/cli/doc_scraper.py:1068:25
     |
1066 |                                 logger.warning("  ⚠️  Worker exception: %s", e)
1067 |
1068 |                         completed += 1
     |                         ^^^^^^^^^^^^^^
1069 |
1070 |                         with self.lock:
     |

B904 Within an `except` clause, raise exceptions with `raise ... from err` or `raise ... from None` to distinguish them from errors in exception handling
   --> src/skill_seekers/cli/github_scraper.py:353:17
    |
351 |         except GithubException as e:
352 |             if e.status == 404:
353 |                 raise ValueError(f"Repository not found: {self.repo_name}")
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
354 |             raise
    |

E402 Module level import not at top of file
 --> src/skill_seekers/cli/llms_txt_downloader.py:5:1
  |
3 | """ABOUTME: Validates markdown content and handles timeouts with exponential backoff"""
4 |
5 | import time
  | ^^^^^^^^^^^
6 |
7 | import requests
  |

E402 Module level import not at top of file
 --> src/skill_seekers/cli/llms_txt_downloader.py:7:1
  |
5 | import time
6 |
7 | import requests
  | ^^^^^^^^^^^^^^^
  |

E402 Module level import not at top of file
 --> src/skill_seekers/cli/llms_txt_parser.py:5:1
  |
3 | """ABOUTME: Extracts titles, content, code samples, and headings from markdown"""
4 |
5 | import re
  | ^^^^^^^^^
6 | from urllib.parse import urljoin
  |

E402 Module level import not at top of file
 --> src/skill_seekers/cli/llms_txt_parser.py:6:1
  |
5 | import re
6 | from urllib.parse import urljoin
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |

SIM102 Use a single `if` statement instead of nested `if` statements
   --> src/skill_seekers/cli/pattern_recognizer.py:430:13
    |
428 |               # Python: __init__ or __new__
429 |               # Java/C#: private constructor (detected by naming)
430 | /             if method.name in ["__new__", "__init__", "constructor"]:
431 | |                 # Check if it has logic (not just pass)
432 | |                 if method.docstring or len(method.parameters) > 1:
    | |__________________________________________________________________^
433 |                       evidence.append(f"Controlled initialization: {method.name}")
434 |                       confidence += 0.3
    |
help: Combine `if` statements using `and`

SIM102 Use a single `if` statement instead of nested `if` statements
   --> src/skill_seekers/cli/pattern_recognizer.py:538:13
    |
536 |           for method in class_sig.methods:
537 |               method_lower = method.name.lower()
538 | /             if any(name in method_lower for name in factory_method_names):
539 | |                 # Check if method returns something (has return type or is not void)
540 | |                 if method.return_type or "create" in method_lower:
    | |__________________________________________________________________^
541 |                       return PatternInstance(
542 |                           pattern_type=self.pattern_type,
    |
help: Combine `if` statements using `and`

SIM102 Use a single `if` statement instead of nested `if` statements
   --> src/skill_seekers/cli/pattern_recognizer.py:916:9
    |
914 |           # Check __init__ for composition (takes object parameter)
915 |           init_method = next((m for m in class_sig.methods if m.name == "__init__"), None)
916 | /         if init_method:
917 | |             # Check if takes object parameter (not just self)
918 | |             if len(init_method.parameters) > 1:  # More than just 'self'
    | |_______________________________________________^
919 |                   param_names = [p.name for p in init_method.parameters if p.name != "self"]
920 |                   if any(
    |
help: Combine `if` statements using `and`

F821 Undefined name `l`
   --> src/skill_seekers/cli/pdf_extractor_poc.py:302:28
    |
300 |             1 for line in code.split("\n") if line.strip().startswith(("#", "//", "/*", "*", "--"))
301 |         )
302 |         total_lines = len([l for line in code.split("\n") if line.strip()])
    |                            ^
303 |         if total_lines > 0 and comment_lines / total_lines > 0.7:
304 |             issues.append("Mostly comments")
    |

F821 Undefined name `l`
   --> src/skill_seekers/cli/pdf_extractor_poc.py:330:18
    |
329 |         # Factor 3: Number of lines
330 |         lines = [l for line in code.split("\n") if line.strip()]
    |                  ^
331 |         if 2 <= len(lines) <= 50:
332 |             score += 1.0
    |

B007 Loop control variable `keywords` not used within loop body
   --> src/skill_seekers/cli/pdf_scraper.py:167:30
    |
165 |                 # Keyword-based categorization
166 |                 # Initialize categories
167 |                 for cat_key, keywords in self.categories.items():
    |                              ^^^^^^^^
168 |                     categorized[cat_key] = {"title": cat_key.replace("_", " ").title(), "pages": []}
    |
help: Rename unused `keywords` to `_keywords`

SIM115 Use a context manager for opening files
   --> src/skill_seekers/cli/pdf_scraper.py:434:26
    |
432 |             f.write("**Generated by Skill Seeker** | PDF Documentation Scraper\n")
433 |
434 |         line_count = len(open(filename, encoding="utf-8").read().split("\n"))
    |                          ^^^^
435 |         print(f"   Generated: {filename} ({line_count} lines)")
    |

E741 Ambiguous variable name: `l`
   --> src/skill_seekers/cli/quality_checker.py:318:44
    |
316 |         else:
317 |             if links:
318 |                 internal_links = [l for t, l in links if not l.startswith("http")]
    |                                            ^
319 |                 if internal_links:
320 |                     self.report.add_info(
    |

SIM102 Use a single `if` statement instead of nested `if` statements
   --> src/skill_seekers/cli/test_example_extractor.py:364:13
    |
363 |           for node in ast.walk(func_node):
364 | /             if isinstance(node, ast.Assign) and isinstance(node.value, ast.Call):
365 | |                 # Check if meaningful instantiation
366 | |                 if self._is_meaningful_instantiation(node):
    | |___________________________________________________________^
367 |                       code = ast.unparse(node)
    |
help: Combine `if` statements using `and`

SIM102 Use a single `if` statement instead of nested `if` statements
   --> src/skill_seekers/cli/test_example_extractor.py:412:13
    |
410 |           for i, stmt in enumerate(statements):
411 |               # Look for method calls
412 | /             if isinstance(stmt, ast.Expr) and isinstance(stmt.value, ast.Call):
413 | |                 # Check if next statement is an assertion
414 | |                 if i + 1 < len(statements):
    | |___________________________________________^
415 |                       next_stmt = statements[i + 1]
416 |                       if self._is_assertion(next_stmt):
    |
help: Combine `if` statements using `and`

SIM102 Use a single `if` statement instead of nested `if` statements
   --> src/skill_seekers/cli/test_example_extractor.py:460:13
    |
459 |           for node in ast.walk(func_node):
460 | /             if isinstance(node, ast.Assign) and isinstance(node.value, ast.Dict):
461 | |                 # Must have 2+ keys and be meaningful
462 | |                 if len(node.value.keys) >= 2:
    | |_____________________________________________^
463 |                       code = ast.unparse(node)
    |
help: Combine `if` statements using `and`

SIM102 Use a single `if` statement instead of nested `if` statements
    --> src/skill_seekers/cli/unified_skill_builder.py:1070:13
     |
1069 |               # If no languages from C3.7, try to get from GitHub data
1070 | /             if not languages:
1071 | |                 # github_data already available from method scope
1072 | |                 if github_data.get("languages"):
     | |________________________________________________^
1073 |                       # GitHub data has languages as list, convert to dict with count 1
1074 |                       languages = dict.fromkeys(github_data["languages"], 1)
     |
help: Combine `if` statements using `and`

ARG001 Unused function argument: `request`
    --> src/skill_seekers/mcp/server_fastmcp.py:1159:32
     |
1157 |         from starlette.routing import Route
1158 |
1159 |         async def health_check(request):
     |                                ^^^^^^^
1160 |             """Health check endpoint."""
1161 |             return JSONResponse(
     |

ARG002 Unused method argument: `tmp_path`
  --> tests/test_bootstrap_skill.py:54:56
   |
53 |     @pytest.mark.slow
54 |     def test_bootstrap_script_runs(self, project_root, tmp_path):
   |                                                        ^^^^^^^^
55 |         """Test that bootstrap script runs successfully.
   |

B007 Loop control variable `message` not used within loop body
   --> tests/test_install_agent.py:374:44
    |
372 |                 # With force - should succeed
373 |                 results_with_force = install_to_all_agents(self.skill_dir, force=True)
374 |                 for _agent_name, (success, message) in results_with_force.items():
    |                                            ^^^^^^^
375 |                     assert success is True
    |
help: Rename unused `message` to `_message`

SIM117 Use a single `with` statement with multiple contexts instead of nested `with` statements
   --> tests/test_install_agent.py:418:9
    |
416 |       def test_cli_requires_agent_flag(self):
417 |           """Test that CLI fails without --agent flag."""
418 | /         with pytest.raises(SystemExit) as exc_info:
419 | |             with patch("sys.argv", ["install_agent.py", str(self.skill_dir)]):
    | |______________________________________________________________________________^
420 |                   main()
    |
help: Combine `with` statements

SIM117 Use a single `with` statement with multiple contexts instead of nested `with` statements
   --> tests/test_issue_219_e2e.py:278:9
    |
276 |               self.skipTest("anthropic package not installed")
277 |
278 | /         with patch.dict(os.environ, {"ANTHROPIC_API_KEY": "test-key"}):
279 | |             with patch("skill_seekers.cli.enhance_skill.anthropic.Anthropic") as mock_anthropic:
    | |________________________________________________________________________________________________^
280 |                   enhancer = SkillEnhancer(self.skill_dir)
    |
help: Combine `with` statements

SIM117 Use a single `with` statement with multiple contexts instead of nested `with` statements
  --> tests/test_llms_txt_downloader.py:33:5
   |
31 |       downloader = LlmsTxtDownloader("https://example.com/llms.txt", max_retries=2)
32 |
33 | /     with patch("requests.get", side_effect=requests.Timeout("Connection timeout")) as mock_get:
34 | |         with patch("time.sleep") as mock_sleep:  # Mock sleep to speed up test
   | |_______________________________________________^
35 |               content = downloader.download()
   |
help: Combine `with` statements

SIM117 Use a single `with` statement with multiple contexts instead of nested `with` statements
  --> tests/test_llms_txt_downloader.py:88:5
   |
86 |       downloader = LlmsTxtDownloader("https://example.com/llms.txt", max_retries=3)
87 |
88 | /     with patch("requests.get", side_effect=requests.Timeout("Connection timeout")):
89 | |         with patch("time.sleep") as mock_sleep:
   | |_______________________________________________^
90 |               content = downloader.download()
   |
help: Combine `with` statements

F821 Undefined name `l`
   --> tests/test_markdown_parsing.py:100:21
    |
 98 |         )
 99 |         # Should only include .md links
100 |         md_links = [l for line in result["links"] if ".md" in l]
    |                     ^
101 |         self.assertEqual(len(md_links), len(result["links"]))
    |

F821 Undefined name `l`
   --> tests/test_markdown_parsing.py:100:63
    |
 98 |         )
 99 |         # Should only include .md links
100 |         md_links = [l for line in result["links"] if ".md" in l]
    |                                                               ^
101 |         self.assertEqual(len(md_links), len(result["links"]))
    |

SIM117 Use a single `with` statement with multiple contexts instead of nested `with` statements
  --> tests/test_skip_llms_txt.py:75:17
   |
73 |                   converter = DocToSkillConverter(config, dry_run=False)
74 |
75 | /                 with patch.object(converter, "_try_llms_txt", return_value=False) as mock_try:
76 | |                     with patch.object(converter, "scrape_page"):
   | |________________________________________________________________^
77 |                           with patch.object(converter, "save_summary"):
78 |                               converter.scrape_all()
   |
help: Combine `with` statements

SIM117 Use a single `with` statement with multiple contexts instead of nested `with` statements
   --> tests/test_skip_llms_txt.py:98:17
    |
 96 |                   converter = DocToSkillConverter(config, dry_run=False)
 97 |
 98 | /                 with patch.object(converter, "_try_llms_txt") as mock_try:
 99 | |                     with patch.object(converter, "scrape_page"):
    | |________________________________________________________________^
100 |                           with patch.object(converter, "save_summary"):
101 |                               converter.scrape_all()
    |
help: Combine `with` statements

SIM117 Use a single `with` statement with multiple contexts instead of nested `with` statements
   --> tests/test_skip_llms_txt.py:121:17
    |
119 |                   converter = DocToSkillConverter(config, dry_run=True)
120 |
121 | /                 with patch.object(converter, "_try_llms_txt") as mock_try:
122 | |                     with patch.object(converter, "save_summary"):
    | |_________________________________________________________________^
123 |                           converter.scrape_all()
124 |                           mock_try.assert_not_called()
    |
help: Combine `with` statements

SIM117 Use a single `with` statement with multiple contexts instead of nested `with` statements
   --> tests/test_skip_llms_txt.py:148:17
    |
146 |                   converter = DocToSkillConverter(config, dry_run=False)
147 |
148 | /                 with patch.object(converter, "_try_llms_txt", return_value=False) as mock_try:
149 | |                     with patch.object(converter, "scrape_page_async", return_value=None):
    | |_________________________________________________________________________________________^
150 |                           with patch.object(converter, "save_summary"):
151 |                               converter.scrape_all()
    |
help: Combine `with` statements

SIM117 Use a single `with` statement with multiple contexts instead of nested `with` statements
   --> tests/test_skip_llms_txt.py:172:17
    |
170 |                   converter = DocToSkillConverter(config, dry_run=False)
171 |
172 | /                 with patch.object(converter, "_try_llms_txt") as mock_try:
173 | |                     with patch.object(converter, "scrape_page_async", return_value=None):
    | |_________________________________________________________________________________________^
174 |                           with patch.object(converter, "save_summary"):
175 |                               converter.scrape_all()
    |
help: Combine `with` statements

SIM117 Use a single `with` statement with multiple contexts instead of nested `with` statements
   --> tests/test_skip_llms_txt.py:304:17
    |
302 |                       return None
303 |
304 | /                 with patch.object(converter, "scrape_page", side_effect=mock_scrape):
305 | |                     with patch.object(converter, "save_summary"):
    | |_________________________________________________________________^
306 |                           converter.scrape_all()
307 |                           # Should have attempted to scrape the base URL
    |
help: Combine `with` statements

Found 38 errors.
