fix: Resolve all linting errors from ruff
Fix 145 linting errors across CLI refactor code: Type annotation modernization (Python 3.9+): - Replace typing.Dict with dict - Replace typing.List with list - Replace typing.Set with set - Replace Optional[X] with X | None Code quality improvements: - Remove trailing whitespace (W291) - Remove whitespace from blank lines (W293) - Remove unused imports (F401) - Use dictionary lookup instead of if-elif chains (SIM116) - Combine nested if statements (SIM102) Files fixed (45 files): - src/skill_seekers/cli/arguments/*.py (10 files) - src/skill_seekers/cli/parsers/*.py (24 files) - src/skill_seekers/cli/presets/*.py (4 files) - src/skill_seekers/cli/create_command.py - src/skill_seekers/cli/source_detector.py - src/skill_seekers/cli/github_scraper.py - tests/test_*.py (5 test files) All files now pass ruff linting checks. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -8,10 +8,9 @@ Includes preset system support for #268.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
from typing import Dict, Any
|
||||
from typing import Any
|
||||
|
||||
|
||||
ANALYZE_ARGUMENTS: Dict[str, Dict[str, Any]] = {
|
||||
ANALYZE_ARGUMENTS: dict[str, dict[str, Any]] = {
|
||||
# Core options
|
||||
"directory": {
|
||||
"flags": ("--directory",),
|
||||
@@ -172,7 +171,6 @@ ANALYZE_ARGUMENTS: Dict[str, Dict[str, Any]] = {
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def add_analyze_arguments(parser: argparse.ArgumentParser) -> None:
|
||||
"""Add all analyze command arguments to a parser."""
|
||||
for arg_name, arg_def in ANALYZE_ARGUMENTS.items():
|
||||
@@ -180,7 +178,6 @@ def add_analyze_arguments(parser: argparse.ArgumentParser) -> None:
|
||||
kwargs = arg_def["kwargs"]
|
||||
parser.add_argument(*flags, **kwargs)
|
||||
|
||||
|
||||
def get_analyze_argument_names() -> set:
|
||||
"""Get the set of analyze argument destination names."""
|
||||
return set(ANALYZE_ARGUMENTS.keys())
|
||||
|
||||
@@ -5,12 +5,11 @@ and provide consistent behavior for configuration, output control, and help.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
from typing import Dict, Any
|
||||
|
||||
from typing import Any
|
||||
|
||||
# Common argument definitions as data structure
|
||||
# These are arguments that appear in MULTIPLE commands
|
||||
COMMON_ARGUMENTS: Dict[str, Dict[str, Any]] = {
|
||||
COMMON_ARGUMENTS: dict[str, dict[str, Any]] = {
|
||||
"config": {
|
||||
"flags": ("--config", "-c"),
|
||||
"kwargs": {
|
||||
@@ -67,10 +66,9 @@ COMMON_ARGUMENTS: Dict[str, Dict[str, Any]] = {
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
# RAG (Retrieval-Augmented Generation) arguments
|
||||
# These are shared across commands that support RAG chunking
|
||||
RAG_ARGUMENTS: Dict[str, Dict[str, Any]] = {
|
||||
RAG_ARGUMENTS: dict[str, dict[str, Any]] = {
|
||||
"chunk_for_rag": {
|
||||
"flags": ("--chunk-for-rag",),
|
||||
"kwargs": {
|
||||
@@ -98,15 +96,14 @@ RAG_ARGUMENTS: Dict[str, Dict[str, Any]] = {
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def add_common_arguments(parser: argparse.ArgumentParser) -> None:
|
||||
"""Add common arguments to a parser.
|
||||
|
||||
|
||||
These arguments are shared across most commands for consistent UX.
|
||||
|
||||
|
||||
Args:
|
||||
parser: The ArgumentParser to add arguments to
|
||||
|
||||
|
||||
Example:
|
||||
>>> parser = argparse.ArgumentParser()
|
||||
>>> add_common_arguments(parser)
|
||||
@@ -117,7 +114,6 @@ def add_common_arguments(parser: argparse.ArgumentParser) -> None:
|
||||
kwargs = arg_def["kwargs"]
|
||||
parser.add_argument(*flags, **kwargs)
|
||||
|
||||
|
||||
def get_common_argument_names() -> set:
|
||||
"""Get the set of common argument destination names.
|
||||
|
||||
@@ -126,7 +122,6 @@ def get_common_argument_names() -> set:
|
||||
"""
|
||||
return set(COMMON_ARGUMENTS.keys())
|
||||
|
||||
|
||||
def add_rag_arguments(parser: argparse.ArgumentParser) -> None:
|
||||
"""Add RAG (Retrieval-Augmented Generation) arguments to a parser.
|
||||
|
||||
@@ -145,7 +140,6 @@ def add_rag_arguments(parser: argparse.ArgumentParser) -> None:
|
||||
kwargs = arg_def["kwargs"]
|
||||
parser.add_argument(*flags, **kwargs)
|
||||
|
||||
|
||||
def get_rag_argument_names() -> set:
|
||||
"""Get the set of RAG argument destination names.
|
||||
|
||||
@@ -154,16 +148,15 @@ def get_rag_argument_names() -> set:
|
||||
"""
|
||||
return set(RAG_ARGUMENTS.keys())
|
||||
|
||||
|
||||
def get_argument_help(arg_name: str) -> str:
|
||||
"""Get the help text for a common argument.
|
||||
|
||||
|
||||
Args:
|
||||
arg_name: Name of the argument (e.g., 'config')
|
||||
|
||||
|
||||
Returns:
|
||||
Help text string
|
||||
|
||||
|
||||
Raises:
|
||||
KeyError: If argument doesn't exist
|
||||
"""
|
||||
|
||||
@@ -10,18 +10,17 @@ This enables progressive disclosure in help text while maintaining
|
||||
"""
|
||||
|
||||
import argparse
|
||||
from typing import Dict, Any, Set, List
|
||||
from typing import Any
|
||||
|
||||
from skill_seekers.cli.constants import DEFAULT_RATE_LIMIT
|
||||
from .common import RAG_ARGUMENTS
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# TIER 1: UNIVERSAL ARGUMENTS (15 flags)
|
||||
# =============================================================================
|
||||
# These arguments work for ALL source types
|
||||
|
||||
UNIVERSAL_ARGUMENTS: Dict[str, Dict[str, Any]] = {
|
||||
UNIVERSAL_ARGUMENTS: dict[str, dict[str, Any]] = {
|
||||
# Identity arguments
|
||||
"name": {
|
||||
"flags": ("--name",),
|
||||
@@ -118,13 +117,12 @@ UNIVERSAL_ARGUMENTS: Dict[str, Dict[str, Any]] = {
|
||||
# Merge RAG arguments from common.py into universal arguments
|
||||
UNIVERSAL_ARGUMENTS.update(RAG_ARGUMENTS)
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# TIER 2: SOURCE-SPECIFIC ARGUMENTS
|
||||
# =============================================================================
|
||||
|
||||
# Web scraping specific (from scrape.py)
|
||||
WEB_ARGUMENTS: Dict[str, Dict[str, Any]] = {
|
||||
WEB_ARGUMENTS: dict[str, dict[str, Any]] = {
|
||||
"url": {
|
||||
"flags": ("--url",),
|
||||
"kwargs": {
|
||||
@@ -189,7 +187,7 @@ WEB_ARGUMENTS: Dict[str, Dict[str, Any]] = {
|
||||
}
|
||||
|
||||
# GitHub repository specific (from github.py)
|
||||
GITHUB_ARGUMENTS: Dict[str, Dict[str, Any]] = {
|
||||
GITHUB_ARGUMENTS: dict[str, dict[str, Any]] = {
|
||||
"repo": {
|
||||
"flags": ("--repo",),
|
||||
"kwargs": {
|
||||
@@ -261,7 +259,7 @@ GITHUB_ARGUMENTS: Dict[str, Dict[str, Any]] = {
|
||||
}
|
||||
|
||||
# Local codebase specific (from analyze.py)
|
||||
LOCAL_ARGUMENTS: Dict[str, Dict[str, Any]] = {
|
||||
LOCAL_ARGUMENTS: dict[str, dict[str, Any]] = {
|
||||
"directory": {
|
||||
"flags": ("--directory",),
|
||||
"kwargs": {
|
||||
@@ -324,7 +322,7 @@ LOCAL_ARGUMENTS: Dict[str, Dict[str, Any]] = {
|
||||
}
|
||||
|
||||
# PDF specific (from pdf.py)
|
||||
PDF_ARGUMENTS: Dict[str, Dict[str, Any]] = {
|
||||
PDF_ARGUMENTS: dict[str, dict[str, Any]] = {
|
||||
"pdf": {
|
||||
"flags": ("--pdf",),
|
||||
"kwargs": {
|
||||
@@ -350,13 +348,12 @@ PDF_ARGUMENTS: Dict[str, Dict[str, Any]] = {
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# TIER 3: ADVANCED/RARE ARGUMENTS
|
||||
# =============================================================================
|
||||
# Hidden from default help, shown only with --help-advanced
|
||||
|
||||
ADVANCED_ARGUMENTS: Dict[str, Dict[str, Any]] = {
|
||||
ADVANCED_ARGUMENTS: dict[str, dict[str, Any]] = {
|
||||
"no_rate_limit": {
|
||||
"flags": ("--no-rate-limit",),
|
||||
"kwargs": {
|
||||
@@ -387,17 +384,15 @@ ADVANCED_ARGUMENTS: Dict[str, Dict[str, Any]] = {
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# HELPER FUNCTIONS
|
||||
# =============================================================================
|
||||
|
||||
def get_universal_argument_names() -> Set[str]:
|
||||
def get_universal_argument_names() -> set[str]:
|
||||
"""Get set of universal argument names."""
|
||||
return set(UNIVERSAL_ARGUMENTS.keys())
|
||||
|
||||
|
||||
def get_source_specific_arguments(source_type: str) -> Dict[str, Dict[str, Any]]:
|
||||
def get_source_specific_arguments(source_type: str) -> dict[str, dict[str, Any]]:
|
||||
"""Get source-specific arguments for a given source type.
|
||||
|
||||
Args:
|
||||
@@ -406,21 +401,16 @@ def get_source_specific_arguments(source_type: str) -> Dict[str, Dict[str, Any]]
|
||||
Returns:
|
||||
Dict of argument definitions
|
||||
"""
|
||||
if source_type == 'web':
|
||||
return WEB_ARGUMENTS
|
||||
elif source_type == 'github':
|
||||
return GITHUB_ARGUMENTS
|
||||
elif source_type == 'local':
|
||||
return LOCAL_ARGUMENTS
|
||||
elif source_type == 'pdf':
|
||||
return PDF_ARGUMENTS
|
||||
elif source_type == 'config':
|
||||
return {} # Config files don't have extra args
|
||||
else:
|
||||
return {}
|
||||
source_args = {
|
||||
'web': WEB_ARGUMENTS,
|
||||
'github': GITHUB_ARGUMENTS,
|
||||
'local': LOCAL_ARGUMENTS,
|
||||
'pdf': PDF_ARGUMENTS,
|
||||
'config': {}, # Config files don't have extra args
|
||||
}
|
||||
return source_args.get(source_type, {})
|
||||
|
||||
|
||||
def get_compatible_arguments(source_type: str) -> List[str]:
|
||||
def get_compatible_arguments(source_type: str) -> list[str]:
|
||||
"""Get list of compatible argument names for a source type.
|
||||
|
||||
Args:
|
||||
@@ -441,7 +431,6 @@ def get_compatible_arguments(source_type: str) -> List[str]:
|
||||
|
||||
return compatible
|
||||
|
||||
|
||||
def add_create_arguments(parser: argparse.ArgumentParser, mode: str = 'default') -> None:
|
||||
"""Add create command arguments to parser.
|
||||
|
||||
|
||||
@@ -6,10 +6,9 @@ import and use these definitions.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
from typing import Dict, Any
|
||||
from typing import Any
|
||||
|
||||
|
||||
ENHANCE_ARGUMENTS: Dict[str, Dict[str, Any]] = {
|
||||
ENHANCE_ARGUMENTS: dict[str, dict[str, Any]] = {
|
||||
# Positional argument
|
||||
"skill_directory": {
|
||||
"flags": ("skill_directory",),
|
||||
@@ -69,7 +68,6 @@ ENHANCE_ARGUMENTS: Dict[str, Dict[str, Any]] = {
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def add_enhance_arguments(parser: argparse.ArgumentParser) -> None:
|
||||
"""Add all enhance command arguments to a parser."""
|
||||
for arg_name, arg_def in ENHANCE_ARGUMENTS.items():
|
||||
|
||||
@@ -8,11 +8,10 @@ This ensures the parsers NEVER drift out of sync.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
from typing import Dict, Any
|
||||
|
||||
from typing import Any
|
||||
|
||||
# GitHub-specific argument definitions as data structure
|
||||
GITHUB_ARGUMENTS: Dict[str, Dict[str, Any]] = {
|
||||
GITHUB_ARGUMENTS: dict[str, dict[str, Any]] = {
|
||||
# Core GitHub options
|
||||
"repo": {
|
||||
"flags": ("--repo",),
|
||||
@@ -134,18 +133,17 @@ GITHUB_ARGUMENTS: Dict[str, Dict[str, Any]] = {
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def add_github_arguments(parser: argparse.ArgumentParser) -> None:
|
||||
"""Add all github command arguments to a parser.
|
||||
|
||||
|
||||
This is the SINGLE SOURCE OF TRUTH for github arguments.
|
||||
Used by:
|
||||
- github_scraper.py (standalone scraper)
|
||||
- parsers/github_parser.py (unified CLI)
|
||||
|
||||
|
||||
Args:
|
||||
parser: The ArgumentParser to add arguments to
|
||||
|
||||
|
||||
Example:
|
||||
>>> parser = argparse.ArgumentParser()
|
||||
>>> add_github_arguments(parser) # Adds all github args
|
||||
@@ -155,19 +153,17 @@ def add_github_arguments(parser: argparse.ArgumentParser) -> None:
|
||||
kwargs = arg_def["kwargs"]
|
||||
parser.add_argument(*flags, **kwargs)
|
||||
|
||||
|
||||
def get_github_argument_names() -> set:
|
||||
"""Get the set of github argument destination names.
|
||||
|
||||
|
||||
Returns:
|
||||
Set of argument dest names
|
||||
"""
|
||||
return set(GITHUB_ARGUMENTS.keys())
|
||||
|
||||
|
||||
def get_github_argument_count() -> int:
|
||||
"""Get the total number of github arguments.
|
||||
|
||||
|
||||
Returns:
|
||||
Number of arguments
|
||||
"""
|
||||
|
||||
@@ -6,10 +6,9 @@ import and use these definitions.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
from typing import Dict, Any
|
||||
from typing import Any
|
||||
|
||||
|
||||
PACKAGE_ARGUMENTS: Dict[str, Dict[str, Any]] = {
|
||||
PACKAGE_ARGUMENTS: dict[str, dict[str, Any]] = {
|
||||
# Positional argument
|
||||
"skill_directory": {
|
||||
"flags": ("skill_directory",),
|
||||
@@ -124,7 +123,6 @@ PACKAGE_ARGUMENTS: Dict[str, Dict[str, Any]] = {
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def add_package_arguments(parser: argparse.ArgumentParser) -> None:
|
||||
"""Add all package command arguments to a parser."""
|
||||
for arg_name, arg_def in PACKAGE_ARGUMENTS.items():
|
||||
|
||||
@@ -6,10 +6,9 @@ import and use these definitions.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
from typing import Dict, Any
|
||||
from typing import Any
|
||||
|
||||
|
||||
PDF_ARGUMENTS: Dict[str, Dict[str, Any]] = {
|
||||
PDF_ARGUMENTS: dict[str, dict[str, Any]] = {
|
||||
"config": {
|
||||
"flags": ("--config",),
|
||||
"kwargs": {
|
||||
@@ -52,7 +51,6 @@ PDF_ARGUMENTS: Dict[str, Dict[str, Any]] = {
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def add_pdf_arguments(parser: argparse.ArgumentParser) -> None:
|
||||
"""Add all pdf command arguments to a parser."""
|
||||
for arg_name, arg_def in PDF_ARGUMENTS.items():
|
||||
|
||||
@@ -8,15 +8,14 @@ This ensures the parsers NEVER drift out of sync.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
from typing import Dict, Any
|
||||
from typing import Any
|
||||
|
||||
from skill_seekers.cli.constants import DEFAULT_RATE_LIMIT
|
||||
from .common import RAG_ARGUMENTS
|
||||
|
||||
|
||||
# Scrape-specific argument definitions as data structure
|
||||
# This enables introspection for UI generation and testing
|
||||
SCRAPE_ARGUMENTS: Dict[str, Dict[str, Any]] = {
|
||||
SCRAPE_ARGUMENTS: dict[str, dict[str, Any]] = {
|
||||
# Positional argument
|
||||
"url_positional": {
|
||||
"flags": ("url",),
|
||||
@@ -199,18 +198,17 @@ SCRAPE_ARGUMENTS: Dict[str, Dict[str, Any]] = {
|
||||
# Merge RAG arguments from common.py
|
||||
SCRAPE_ARGUMENTS.update(RAG_ARGUMENTS)
|
||||
|
||||
|
||||
def add_scrape_arguments(parser: argparse.ArgumentParser) -> None:
|
||||
"""Add all scrape command arguments to a parser.
|
||||
|
||||
|
||||
This is the SINGLE SOURCE OF TRUTH for scrape arguments.
|
||||
Used by:
|
||||
- doc_scraper.py (standalone scraper)
|
||||
- parsers/scrape_parser.py (unified CLI)
|
||||
|
||||
|
||||
Args:
|
||||
parser: The ArgumentParser to add arguments to
|
||||
|
||||
|
||||
Example:
|
||||
>>> parser = argparse.ArgumentParser()
|
||||
>>> add_scrape_arguments(parser) # Adds all 26 scrape args
|
||||
@@ -220,19 +218,17 @@ def add_scrape_arguments(parser: argparse.ArgumentParser) -> None:
|
||||
kwargs = arg_def["kwargs"]
|
||||
parser.add_argument(*flags, **kwargs)
|
||||
|
||||
|
||||
def get_scrape_argument_names() -> set:
|
||||
"""Get the set of scrape argument destination names.
|
||||
|
||||
|
||||
Returns:
|
||||
Set of argument dest names
|
||||
"""
|
||||
return set(SCRAPE_ARGUMENTS.keys())
|
||||
|
||||
|
||||
def get_scrape_argument_count() -> int:
|
||||
"""Get the total number of scrape arguments.
|
||||
|
||||
|
||||
Returns:
|
||||
Number of arguments
|
||||
"""
|
||||
|
||||
@@ -6,10 +6,9 @@ import and use these definitions.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
from typing import Dict, Any
|
||||
from typing import Any
|
||||
|
||||
|
||||
UNIFIED_ARGUMENTS: Dict[str, Dict[str, Any]] = {
|
||||
UNIFIED_ARGUMENTS: dict[str, dict[str, Any]] = {
|
||||
"config": {
|
||||
"flags": ("--config", "-c"),
|
||||
"kwargs": {
|
||||
@@ -43,7 +42,6 @@ UNIFIED_ARGUMENTS: Dict[str, Dict[str, Any]] = {
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def add_unified_arguments(parser: argparse.ArgumentParser) -> None:
|
||||
"""Add all unified command arguments to a parser."""
|
||||
for arg_name, arg_def in UNIFIED_ARGUMENTS.items():
|
||||
|
||||
@@ -6,10 +6,9 @@ import and use these definitions.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
from typing import Dict, Any
|
||||
from typing import Any
|
||||
|
||||
|
||||
UPLOAD_ARGUMENTS: Dict[str, Dict[str, Any]] = {
|
||||
UPLOAD_ARGUMENTS: dict[str, dict[str, Any]] = {
|
||||
# Positional argument
|
||||
"package_file": {
|
||||
"flags": ("package_file",),
|
||||
@@ -99,7 +98,6 @@ UPLOAD_ARGUMENTS: Dict[str, Dict[str, Any]] = {
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def add_upload_arguments(parser: argparse.ArgumentParser) -> None:
|
||||
"""Add all upload command arguments to a parser."""
|
||||
for arg_name, arg_def in UPLOAD_ARGUMENTS.items():
|
||||
|
||||
@@ -7,7 +7,6 @@ to appropriate scraper while maintaining full backward compatibility.
|
||||
import sys
|
||||
import logging
|
||||
import argparse
|
||||
from typing import List, Optional
|
||||
|
||||
from skill_seekers.cli.source_detector import SourceDetector, SourceInfo
|
||||
from skill_seekers.cli.arguments.create import (
|
||||
@@ -17,7 +16,6 @@ from skill_seekers.cli.arguments.create import (
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class CreateCommand:
|
||||
"""Unified create command implementation."""
|
||||
|
||||
@@ -28,7 +26,7 @@ class CreateCommand:
|
||||
args: Parsed command-line arguments
|
||||
"""
|
||||
self.args = args
|
||||
self.source_info: Optional[SourceInfo] = None
|
||||
self.source_info: SourceInfo | None = None
|
||||
|
||||
def execute(self) -> int:
|
||||
"""Execute the create command.
|
||||
@@ -311,7 +309,7 @@ class CreateCommand:
|
||||
finally:
|
||||
sys.argv = original_argv
|
||||
|
||||
def _add_common_args(self, argv: List[str]) -> None:
|
||||
def _add_common_args(self, argv: list[str]) -> None:
|
||||
"""Add common/universal arguments to argv list.
|
||||
|
||||
Args:
|
||||
@@ -367,7 +365,6 @@ class CreateCommand:
|
||||
if getattr(self.args, 'interactive_enhancement', False):
|
||||
argv.append('--interactive-enhancement')
|
||||
|
||||
|
||||
def main() -> int:
|
||||
"""Entry point for create command.
|
||||
|
||||
@@ -510,6 +507,5 @@ Common Workflows:
|
||||
command = CreateCommand(args)
|
||||
return command.execute()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
|
||||
@@ -21,7 +21,7 @@ import os
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import Any, Optional
|
||||
from typing import Any
|
||||
|
||||
try:
|
||||
from github import Github, GithubException, Repository
|
||||
@@ -100,7 +100,6 @@ EXCLUDED_DIRS = {
|
||||
".tmp",
|
||||
}
|
||||
|
||||
|
||||
def extract_description_from_readme(readme_content: str, repo_name: str) -> str:
|
||||
"""
|
||||
Extract a meaningful description from README content for skill description.
|
||||
@@ -181,7 +180,6 @@ def extract_description_from_readme(readme_content: str, repo_name: str) -> str:
|
||||
project_name = repo_name.split("/")[-1]
|
||||
return f"Use when working with {project_name}"
|
||||
|
||||
|
||||
class GitHubScraper:
|
||||
"""
|
||||
GitHub Repository Scraper (C1.1-C1.9)
|
||||
@@ -563,7 +561,7 @@ class GitHubScraper:
|
||||
|
||||
return False
|
||||
|
||||
def _load_gitignore(self) -> Optional["pathspec.PathSpec"]:
|
||||
def _load_gitignore(self) -> "pathspec.PathSpec" | None:
|
||||
"""
|
||||
Load .gitignore file and create pathspec matcher (C2.1).
|
||||
|
||||
@@ -894,7 +892,6 @@ class GitHubScraper:
|
||||
|
||||
logger.info(f"Data saved to: {self.data_file}")
|
||||
|
||||
|
||||
class GitHubToSkillConverter:
|
||||
"""
|
||||
Convert extracted GitHub data to Claude skill format (C1.10).
|
||||
@@ -1350,14 +1347,13 @@ Use this skill when you need to:
|
||||
f.write(content)
|
||||
logger.info(f"Generated: {structure_path}")
|
||||
|
||||
|
||||
def setup_argument_parser() -> argparse.ArgumentParser:
|
||||
"""Setup and configure command-line argument parser.
|
||||
|
||||
|
||||
Creates an ArgumentParser with all CLI options for the github scraper.
|
||||
All arguments are defined in skill_seekers.cli.arguments.github to ensure
|
||||
consistency between the standalone scraper and unified CLI.
|
||||
|
||||
|
||||
Returns:
|
||||
argparse.ArgumentParser: Configured argument parser
|
||||
"""
|
||||
@@ -1378,7 +1374,6 @@ Examples:
|
||||
|
||||
return parser
|
||||
|
||||
|
||||
def main():
|
||||
"""C1.10: CLI tool entry point."""
|
||||
parser = setup_argument_parser()
|
||||
@@ -1476,6 +1471,5 @@ def main():
|
||||
logger.error(f"Error: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
@@ -28,7 +28,6 @@ from .update_parser import UpdateParser
|
||||
from .multilang_parser import MultilangParser
|
||||
from .quality_parser import QualityParser
|
||||
|
||||
|
||||
# Registry of all parsers (in order of usage frequency)
|
||||
PARSERS = [
|
||||
CreateParser(), # NEW: Unified create command (placed first for prominence)
|
||||
@@ -53,7 +52,6 @@ PARSERS = [
|
||||
QualityParser(),
|
||||
]
|
||||
|
||||
|
||||
def register_parsers(subparsers):
|
||||
"""Register all subcommand parsers.
|
||||
|
||||
@@ -66,7 +64,6 @@ def register_parsers(subparsers):
|
||||
for parser_instance in PARSERS:
|
||||
parser_instance.create_parser(subparsers)
|
||||
|
||||
|
||||
def get_parser_names():
|
||||
"""Get list of all subcommand names.
|
||||
|
||||
@@ -75,7 +72,6 @@ def get_parser_names():
|
||||
"""
|
||||
return [p.name for p in PARSERS]
|
||||
|
||||
|
||||
__all__ = [
|
||||
"SubcommandParser",
|
||||
"PARSERS",
|
||||
|
||||
@@ -9,7 +9,6 @@ Includes preset system support (Issue #268).
|
||||
from .base import SubcommandParser
|
||||
from skill_seekers.cli.arguments.analyze import add_analyze_arguments
|
||||
|
||||
|
||||
class AnalyzeParser(SubcommandParser):
|
||||
"""Parser for analyze subcommand."""
|
||||
|
||||
@@ -27,10 +26,10 @@ class AnalyzeParser(SubcommandParser):
|
||||
|
||||
def add_arguments(self, parser):
|
||||
"""Add analyze-specific arguments.
|
||||
|
||||
|
||||
Uses shared argument definitions to ensure consistency
|
||||
with codebase_scraper.py (standalone scraper).
|
||||
|
||||
|
||||
Includes preset system for simplified UX.
|
||||
"""
|
||||
add_analyze_arguments(parser)
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
from abc import ABC, abstractmethod
|
||||
import argparse
|
||||
|
||||
|
||||
class SubcommandParser(ABC):
|
||||
"""Base class for subcommand parsers.
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
from .base import SubcommandParser
|
||||
|
||||
|
||||
class ConfigParser(SubcommandParser):
|
||||
"""Parser for config subcommand."""
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@ import argparse
|
||||
from .base import SubcommandParser
|
||||
from skill_seekers.cli.arguments.create import add_create_arguments
|
||||
|
||||
|
||||
class CreateParser(SubcommandParser):
|
||||
"""Parser for create subcommand with multi-mode help."""
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ consistency with the standalone enhance_skill_local module.
|
||||
from .base import SubcommandParser
|
||||
from skill_seekers.cli.arguments.enhance import add_enhance_arguments
|
||||
|
||||
|
||||
class EnhanceParser(SubcommandParser):
|
||||
"""Parser for enhance subcommand."""
|
||||
|
||||
@@ -25,7 +24,7 @@ class EnhanceParser(SubcommandParser):
|
||||
|
||||
def add_arguments(self, parser):
|
||||
"""Add enhance-specific arguments.
|
||||
|
||||
|
||||
Uses shared argument definitions to ensure consistency
|
||||
with enhance_skill_local.py (standalone enhancer).
|
||||
"""
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
from .base import SubcommandParser
|
||||
|
||||
|
||||
class EnhanceStatusParser(SubcommandParser):
|
||||
"""Parser for enhance-status subcommand."""
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
from .base import SubcommandParser
|
||||
|
||||
|
||||
class EstimateParser(SubcommandParser):
|
||||
"""Parser for estimate subcommand."""
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ consistency with the standalone github_scraper module.
|
||||
from .base import SubcommandParser
|
||||
from skill_seekers.cli.arguments.github import add_github_arguments
|
||||
|
||||
|
||||
class GitHubParser(SubcommandParser):
|
||||
"""Parser for github subcommand."""
|
||||
|
||||
@@ -25,7 +24,7 @@ class GitHubParser(SubcommandParser):
|
||||
|
||||
def add_arguments(self, parser):
|
||||
"""Add github-specific arguments.
|
||||
|
||||
|
||||
Uses shared argument definitions to ensure consistency
|
||||
with github_scraper.py (standalone scraper).
|
||||
"""
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
from .base import SubcommandParser
|
||||
|
||||
|
||||
class InstallAgentParser(SubcommandParser):
|
||||
"""Parser for install-agent subcommand."""
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
from .base import SubcommandParser
|
||||
|
||||
|
||||
class InstallParser(SubcommandParser):
|
||||
"""Parser for install subcommand."""
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
from .base import SubcommandParser
|
||||
|
||||
|
||||
class MultilangParser(SubcommandParser):
|
||||
"""Parser for multilang subcommand."""
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ consistency with the standalone package_skill module.
|
||||
from .base import SubcommandParser
|
||||
from skill_seekers.cli.arguments.package import add_package_arguments
|
||||
|
||||
|
||||
class PackageParser(SubcommandParser):
|
||||
"""Parser for package subcommand."""
|
||||
|
||||
@@ -25,7 +24,7 @@ class PackageParser(SubcommandParser):
|
||||
|
||||
def add_arguments(self, parser):
|
||||
"""Add package-specific arguments.
|
||||
|
||||
|
||||
Uses shared argument definitions to ensure consistency
|
||||
with package_skill.py (standalone packager).
|
||||
"""
|
||||
|
||||
@@ -7,7 +7,6 @@ consistency with the standalone pdf_scraper module.
|
||||
from .base import SubcommandParser
|
||||
from skill_seekers.cli.arguments.pdf import add_pdf_arguments
|
||||
|
||||
|
||||
class PDFParser(SubcommandParser):
|
||||
"""Parser for pdf subcommand."""
|
||||
|
||||
@@ -25,7 +24,7 @@ class PDFParser(SubcommandParser):
|
||||
|
||||
def add_arguments(self, parser):
|
||||
"""Add pdf-specific arguments.
|
||||
|
||||
|
||||
Uses shared argument definitions to ensure consistency
|
||||
with pdf_scraper.py (standalone scraper).
|
||||
"""
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
from .base import SubcommandParser
|
||||
|
||||
|
||||
class QualityParser(SubcommandParser):
|
||||
"""Parser for quality subcommand."""
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
from .base import SubcommandParser
|
||||
|
||||
|
||||
class ResumeParser(SubcommandParser):
|
||||
"""Parser for resume subcommand."""
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ consistency with the standalone doc_scraper module.
|
||||
from .base import SubcommandParser
|
||||
from skill_seekers.cli.arguments.scrape import add_scrape_arguments
|
||||
|
||||
|
||||
class ScrapeParser(SubcommandParser):
|
||||
"""Parser for scrape subcommand."""
|
||||
|
||||
@@ -25,7 +24,7 @@ class ScrapeParser(SubcommandParser):
|
||||
|
||||
def add_arguments(self, parser):
|
||||
"""Add scrape-specific arguments.
|
||||
|
||||
|
||||
Uses shared argument definitions to ensure consistency
|
||||
with doc_scraper.py (standalone scraper).
|
||||
"""
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
from .base import SubcommandParser
|
||||
|
||||
|
||||
class StreamParser(SubcommandParser):
|
||||
"""Parser for stream subcommand."""
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
from .base import SubcommandParser
|
||||
|
||||
|
||||
class TestExamplesParser(SubcommandParser):
|
||||
"""Parser for extract-test-examples subcommand."""
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ consistency with the standalone unified_scraper module.
|
||||
from .base import SubcommandParser
|
||||
from skill_seekers.cli.arguments.unified import add_unified_arguments
|
||||
|
||||
|
||||
class UnifiedParser(SubcommandParser):
|
||||
"""Parser for unified subcommand."""
|
||||
|
||||
@@ -25,7 +24,7 @@ class UnifiedParser(SubcommandParser):
|
||||
|
||||
def add_arguments(self, parser):
|
||||
"""Add unified-specific arguments.
|
||||
|
||||
|
||||
Uses shared argument definitions to ensure consistency
|
||||
with unified_scraper.py (standalone scraper).
|
||||
"""
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
from .base import SubcommandParser
|
||||
|
||||
|
||||
class UpdateParser(SubcommandParser):
|
||||
"""Parser for update subcommand."""
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ consistency with the standalone upload_skill module.
|
||||
from .base import SubcommandParser
|
||||
from skill_seekers.cli.arguments.upload import add_upload_arguments
|
||||
|
||||
|
||||
class UploadParser(SubcommandParser):
|
||||
"""Parser for upload subcommand."""
|
||||
|
||||
@@ -25,7 +24,7 @@ class UploadParser(SubcommandParser):
|
||||
|
||||
def add_arguments(self, parser):
|
||||
"""Add upload-specific arguments.
|
||||
|
||||
|
||||
Uses shared argument definitions to ensure consistency
|
||||
with upload_skill.py (standalone uploader).
|
||||
"""
|
||||
|
||||
@@ -20,7 +20,6 @@ from .manager import (
|
||||
|
||||
# Analyze presets
|
||||
from .analyze_presets import (
|
||||
AnalysisPreset as AnalyzeAnalysisPreset, # Alternative version (without enhance_level)
|
||||
ANALYZE_PRESETS,
|
||||
apply_analyze_preset,
|
||||
get_preset_help_text,
|
||||
|
||||
@@ -12,17 +12,16 @@ Examples:
|
||||
"""
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Dict, Optional
|
||||
import argparse
|
||||
|
||||
import argparse
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class AnalysisPreset:
|
||||
"""Definition of an analysis preset.
|
||||
|
||||
|
||||
Presets control analysis depth and features ONLY.
|
||||
AI Enhancement is controlled separately via --enhance or --enhance-level.
|
||||
|
||||
|
||||
Attributes:
|
||||
name: Human-readable preset name
|
||||
description: Brief description of what this preset does
|
||||
@@ -33,10 +32,9 @@ class AnalysisPreset:
|
||||
name: str
|
||||
description: str
|
||||
depth: str
|
||||
features: Dict[str, bool] = field(default_factory=dict)
|
||||
features: dict[str, bool] = field(default_factory=dict)
|
||||
estimated_time: str = ""
|
||||
|
||||
|
||||
# Preset definitions
|
||||
ANALYZE_PRESETS = {
|
||||
"quick": AnalysisPreset(
|
||||
@@ -53,7 +51,7 @@ ANALYZE_PRESETS = {
|
||||
},
|
||||
estimated_time="1-2 minutes"
|
||||
),
|
||||
|
||||
|
||||
"standard": AnalysisPreset(
|
||||
name="Standard",
|
||||
description="Balanced analysis with core features (recommended)",
|
||||
@@ -68,7 +66,7 @@ ANALYZE_PRESETS = {
|
||||
},
|
||||
estimated_time="5-10 minutes"
|
||||
),
|
||||
|
||||
|
||||
"comprehensive": AnalysisPreset(
|
||||
name="Comprehensive",
|
||||
description="Full analysis with all features",
|
||||
@@ -85,21 +83,20 @@ ANALYZE_PRESETS = {
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
def apply_analyze_preset(args: argparse.Namespace, preset_name: str) -> None:
|
||||
"""Apply an analysis preset to the args namespace.
|
||||
|
||||
|
||||
This modifies the args object to set the preset's depth and feature flags.
|
||||
NOTE: This does NOT set enhance_level - that's controlled separately via
|
||||
--enhance or --enhance-level flags.
|
||||
|
||||
|
||||
Args:
|
||||
args: The argparse.Namespace to modify
|
||||
preset_name: Name of the preset to apply
|
||||
|
||||
|
||||
Raises:
|
||||
KeyError: If preset_name is not a valid preset
|
||||
|
||||
|
||||
Example:
|
||||
>>> args = parser.parse_args(['--directory', '.', '--preset', 'quick'])
|
||||
>>> apply_analyze_preset(args, args.preset)
|
||||
@@ -107,22 +104,21 @@ def apply_analyze_preset(args: argparse.Namespace, preset_name: str) -> None:
|
||||
>>> # enhance_level is still 0 (default) unless --enhance was specified
|
||||
"""
|
||||
preset = ANALYZE_PRESETS[preset_name]
|
||||
|
||||
|
||||
# Set depth
|
||||
args.depth = preset.depth
|
||||
|
||||
|
||||
# Set feature flags (skip_* attributes)
|
||||
for feature, enabled in preset.features.items():
|
||||
skip_attr = f"skip_{feature}"
|
||||
setattr(args, skip_attr, not enabled)
|
||||
|
||||
|
||||
def get_preset_help_text(preset_name: str) -> str:
|
||||
"""Get formatted help text for a preset.
|
||||
|
||||
|
||||
Args:
|
||||
preset_name: Name of the preset
|
||||
|
||||
|
||||
Returns:
|
||||
Formatted help string
|
||||
"""
|
||||
@@ -133,29 +129,28 @@ def get_preset_help_text(preset_name: str) -> str:
|
||||
f" Depth: {preset.depth}"
|
||||
)
|
||||
|
||||
|
||||
def show_preset_list() -> None:
|
||||
"""Print the list of available presets to stdout.
|
||||
|
||||
|
||||
This is used by the --preset-list flag.
|
||||
"""
|
||||
print("\nAvailable Analysis Presets")
|
||||
print("=" * 60)
|
||||
print()
|
||||
|
||||
|
||||
for name, preset in ANALYZE_PRESETS.items():
|
||||
marker = " (DEFAULT)" if name == "standard" else ""
|
||||
print(f" {name}{marker}")
|
||||
print(f" {preset.description}")
|
||||
print(f" Estimated time: {preset.estimated_time}")
|
||||
print(f" Depth: {preset.depth}")
|
||||
|
||||
|
||||
# Show enabled features
|
||||
enabled = [f for f, v in preset.features.items() if v]
|
||||
if enabled:
|
||||
print(f" Features: {', '.join(enabled)}")
|
||||
print()
|
||||
|
||||
|
||||
print("AI Enhancement (separate from presets):")
|
||||
print(" --enhance Enable AI enhancement (default level 1)")
|
||||
print(" --enhance-level N Set AI enhancement level (0-3)")
|
||||
@@ -166,91 +161,88 @@ def show_preset_list() -> None:
|
||||
print(" skill-seekers analyze --directory <dir> --preset comprehensive --enhance-level 2")
|
||||
print()
|
||||
|
||||
|
||||
def resolve_enhance_level(args: argparse.Namespace) -> int:
|
||||
"""Determine the enhance level based on user arguments.
|
||||
|
||||
|
||||
This is separate from preset application. Enhance level is controlled by:
|
||||
- --enhance-level N (explicit)
|
||||
- --enhance (use default level 1)
|
||||
- Neither (default to 0)
|
||||
|
||||
|
||||
Args:
|
||||
args: Parsed command-line arguments
|
||||
|
||||
|
||||
Returns:
|
||||
The enhance level to use (0-3)
|
||||
"""
|
||||
# Explicit enhance level takes priority
|
||||
if args.enhance_level is not None:
|
||||
return args.enhance_level
|
||||
|
||||
|
||||
# --enhance flag enables default level (1)
|
||||
if args.enhance:
|
||||
return 1
|
||||
|
||||
|
||||
# Default is no enhancement
|
||||
return 0
|
||||
|
||||
|
||||
def apply_preset_with_warnings(args: argparse.Namespace) -> str:
|
||||
"""Apply preset with deprecation warnings for legacy flags.
|
||||
|
||||
|
||||
This is the main entry point for applying presets. It:
|
||||
1. Determines which preset to use
|
||||
2. Prints deprecation warnings if legacy flags were used
|
||||
3. Applies the preset (depth and features only)
|
||||
4. Sets enhance_level separately based on --enhance/--enhance-level
|
||||
5. Returns the preset name
|
||||
|
||||
|
||||
Args:
|
||||
args: Parsed command-line arguments
|
||||
|
||||
|
||||
Returns:
|
||||
The preset name that was applied
|
||||
"""
|
||||
preset_name = None
|
||||
|
||||
|
||||
# Check for explicit preset
|
||||
if args.preset:
|
||||
preset_name = args.preset
|
||||
|
||||
|
||||
# Check for legacy flags and print warnings
|
||||
elif args.quick:
|
||||
print_deprecation_warning("--quick", "--preset quick")
|
||||
preset_name = "quick"
|
||||
|
||||
|
||||
elif args.comprehensive:
|
||||
print_deprecation_warning("--comprehensive", "--preset comprehensive")
|
||||
preset_name = "comprehensive"
|
||||
|
||||
|
||||
elif args.depth:
|
||||
depth_to_preset = {
|
||||
"surface": "quick",
|
||||
"deep": "standard",
|
||||
"deep": "standard",
|
||||
"full": "comprehensive",
|
||||
}
|
||||
if args.depth in depth_to_preset:
|
||||
new_flag = f"--preset {depth_to_preset[args.depth]}"
|
||||
print_deprecation_warning(f"--depth {args.depth}", new_flag)
|
||||
preset_name = depth_to_preset[args.depth]
|
||||
|
||||
|
||||
# Default to standard
|
||||
if preset_name is None:
|
||||
preset_name = "standard"
|
||||
|
||||
|
||||
# Apply the preset (depth and features only)
|
||||
apply_analyze_preset(args, preset_name)
|
||||
|
||||
|
||||
# Set enhance_level separately (not part of preset)
|
||||
args.enhance_level = resolve_enhance_level(args)
|
||||
|
||||
return preset_name
|
||||
|
||||
return preset_name
|
||||
|
||||
def print_deprecation_warning(old_flag: str, new_flag: str) -> None:
|
||||
"""Print a deprecation warning for legacy flags.
|
||||
|
||||
|
||||
Args:
|
||||
old_flag: The old/deprecated flag name
|
||||
new_flag: The new recommended flag/preset
|
||||
|
||||
@@ -9,14 +9,13 @@ Presets:
|
||||
"""
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Dict
|
||||
import argparse
|
||||
|
||||
import argparse
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class GitHubPreset:
|
||||
"""Definition of a GitHub preset.
|
||||
|
||||
|
||||
Attributes:
|
||||
name: Human-readable preset name
|
||||
description: Brief description of what this preset does
|
||||
@@ -27,10 +26,9 @@ class GitHubPreset:
|
||||
name: str
|
||||
description: str
|
||||
max_issues: int
|
||||
features: Dict[str, bool] = field(default_factory=dict)
|
||||
features: dict[str, bool] = field(default_factory=dict)
|
||||
estimated_time: str = ""
|
||||
|
||||
|
||||
# Preset definitions
|
||||
GITHUB_PRESETS = {
|
||||
"quick": GitHubPreset(
|
||||
@@ -44,7 +42,7 @@ GITHUB_PRESETS = {
|
||||
},
|
||||
estimated_time="1-3 minutes"
|
||||
),
|
||||
|
||||
|
||||
"standard": GitHubPreset(
|
||||
name="Standard",
|
||||
description="Balanced scraping with issues and releases (recommended)",
|
||||
@@ -56,7 +54,7 @@ GITHUB_PRESETS = {
|
||||
},
|
||||
estimated_time="5-15 minutes"
|
||||
),
|
||||
|
||||
|
||||
"comprehensive": GitHubPreset(
|
||||
name="Comprehensive",
|
||||
description="Comprehensive scraping with all available data",
|
||||
@@ -70,48 +68,46 @@ GITHUB_PRESETS = {
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
def apply_github_preset(args: argparse.Namespace, preset_name: str) -> None:
|
||||
"""Apply a GitHub preset to the args namespace.
|
||||
|
||||
|
||||
Args:
|
||||
args: The argparse.Namespace to modify
|
||||
preset_name: Name of the preset to apply
|
||||
|
||||
|
||||
Raises:
|
||||
KeyError: If preset_name is not a valid preset
|
||||
"""
|
||||
preset = GITHUB_PRESETS[preset_name]
|
||||
|
||||
|
||||
# Apply max_issues only if not set by user
|
||||
if args.max_issues is None or args.max_issues == 100: # 100 is default
|
||||
args.max_issues = preset.max_issues
|
||||
|
||||
|
||||
# Apply feature flags (only if not explicitly disabled by user)
|
||||
for feature, enabled in preset.features.items():
|
||||
skip_attr = f"no_{feature}"
|
||||
if not hasattr(args, skip_attr) or not getattr(args, skip_attr):
|
||||
setattr(args, skip_attr, not enabled)
|
||||
|
||||
|
||||
def show_github_preset_list() -> None:
|
||||
"""Print the list of available GitHub presets to stdout."""
|
||||
print("\nAvailable GitHub Presets")
|
||||
print("=" * 60)
|
||||
print()
|
||||
|
||||
|
||||
for name, preset in GITHUB_PRESETS.items():
|
||||
marker = " (DEFAULT)" if name == "standard" else ""
|
||||
print(f" {name}{marker}")
|
||||
print(f" {preset.description}")
|
||||
print(f" Estimated time: {preset.estimated_time}")
|
||||
print(f" Max issues: {preset.max_issues}")
|
||||
|
||||
|
||||
# Show enabled features
|
||||
enabled = [f.replace("include_", "") for f, v in preset.features.items() if v]
|
||||
if enabled:
|
||||
print(f" Features: {', '.join(enabled)}")
|
||||
print()
|
||||
|
||||
|
||||
print("Usage: skill-seekers github --repo <owner/repo> --preset <name>")
|
||||
print()
|
||||
|
||||
@@ -6,7 +6,6 @@ between speed and comprehensiveness.
|
||||
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
@dataclass
|
||||
class AnalysisPreset:
|
||||
"""Analysis preset configuration.
|
||||
@@ -23,7 +22,6 @@ class AnalysisPreset:
|
||||
estimated_time: str
|
||||
icon: str
|
||||
|
||||
|
||||
# Preset definitions
|
||||
PRESETS = {
|
||||
"quick": AnalysisPreset(
|
||||
@@ -79,7 +77,6 @@ PRESETS = {
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
class PresetManager:
|
||||
"""Manages analysis presets and applies them to CLI arguments."""
|
||||
|
||||
@@ -167,7 +164,6 @@ class PresetManager:
|
||||
"""
|
||||
return "standard"
|
||||
|
||||
|
||||
# Public API
|
||||
__all__ = [
|
||||
"AnalysisPreset",
|
||||
|
||||
@@ -9,14 +9,13 @@ Presets:
|
||||
"""
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Dict, Optional
|
||||
import argparse
|
||||
|
||||
import argparse
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ScrapePreset:
|
||||
"""Definition of a scrape preset.
|
||||
|
||||
|
||||
Attributes:
|
||||
name: Human-readable preset name
|
||||
description: Brief description of what this preset does
|
||||
@@ -29,12 +28,11 @@ class ScrapePreset:
|
||||
name: str
|
||||
description: str
|
||||
rate_limit: float
|
||||
features: Dict[str, bool] = field(default_factory=dict)
|
||||
features: dict[str, bool] = field(default_factory=dict)
|
||||
async_mode: bool = False
|
||||
workers: int = 1
|
||||
estimated_time: str = ""
|
||||
|
||||
|
||||
# Preset definitions
|
||||
SCRAPE_PRESETS = {
|
||||
"quick": ScrapePreset(
|
||||
@@ -49,7 +47,7 @@ SCRAPE_PRESETS = {
|
||||
workers=5,
|
||||
estimated_time="2-5 minutes"
|
||||
),
|
||||
|
||||
|
||||
"standard": ScrapePreset(
|
||||
name="Standard",
|
||||
description="Balanced scraping with good coverage (recommended)",
|
||||
@@ -62,7 +60,7 @@ SCRAPE_PRESETS = {
|
||||
workers=3,
|
||||
estimated_time="10-30 minutes"
|
||||
),
|
||||
|
||||
|
||||
"comprehensive": ScrapePreset(
|
||||
name="Comprehensive",
|
||||
description="Comprehensive scraping with all features",
|
||||
@@ -77,43 +75,40 @@ SCRAPE_PRESETS = {
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
def apply_scrape_preset(args: argparse.Namespace, preset_name: str) -> None:
|
||||
"""Apply a scrape preset to the args namespace.
|
||||
|
||||
|
||||
Args:
|
||||
args: The argparse.Namespace to modify
|
||||
preset_name: Name of the preset to apply
|
||||
|
||||
|
||||
Raises:
|
||||
KeyError: If preset_name is not a valid preset
|
||||
"""
|
||||
preset = SCRAPE_PRESETS[preset_name]
|
||||
|
||||
|
||||
# Apply rate limit (only if not set by user)
|
||||
if args.rate_limit is None:
|
||||
args.rate_limit = preset.rate_limit
|
||||
|
||||
|
||||
# Apply workers (only if not set by user)
|
||||
if args.workers is None:
|
||||
args.workers = preset.workers
|
||||
|
||||
|
||||
# Apply async mode
|
||||
args.async_mode = preset.async_mode
|
||||
|
||||
|
||||
# Apply feature flags
|
||||
for feature, enabled in preset.features.items():
|
||||
if feature == "rag_chunking":
|
||||
if not hasattr(args, 'chunk_for_rag') or not args.chunk_for_rag:
|
||||
args.chunk_for_rag = enabled
|
||||
|
||||
if feature == "rag_chunking" and (not hasattr(args, 'chunk_for_rag') or not args.chunk_for_rag):
|
||||
args.chunk_for_rag = enabled
|
||||
|
||||
def show_scrape_preset_list() -> None:
|
||||
"""Print the list of available scrape presets to stdout."""
|
||||
print("\nAvailable Scrape Presets")
|
||||
print("=" * 60)
|
||||
print()
|
||||
|
||||
|
||||
for name, preset in SCRAPE_PRESETS.items():
|
||||
marker = " (DEFAULT)" if name == "standard" else ""
|
||||
print(f" {name}{marker}")
|
||||
@@ -122,6 +117,6 @@ def show_scrape_preset_list() -> None:
|
||||
print(f" Workers: {preset.workers}")
|
||||
print(f" Async: {preset.async_mode}, Rate limit: {preset.rate_limit}s")
|
||||
print()
|
||||
|
||||
|
||||
print("Usage: skill-seekers scrape <url> --preset <name>")
|
||||
print()
|
||||
|
||||
@@ -7,13 +7,12 @@ local directory, PDF file, or config file based on patterns.
|
||||
import os
|
||||
import re
|
||||
from dataclasses import dataclass
|
||||
from typing import Dict, Any, Optional
|
||||
from typing import Any
|
||||
from urllib.parse import urlparse
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@dataclass
|
||||
class SourceInfo:
|
||||
"""Information about a detected source.
|
||||
@@ -25,11 +24,10 @@ class SourceInfo:
|
||||
raw_input: Original user input
|
||||
"""
|
||||
type: str
|
||||
parsed: Dict[str, Any]
|
||||
parsed: dict[str, Any]
|
||||
suggested_name: str
|
||||
raw_input: str
|
||||
|
||||
|
||||
class SourceDetector:
|
||||
"""Detects source type from user input and extracts relevant information."""
|
||||
|
||||
@@ -124,7 +122,7 @@ class SourceDetector:
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def _detect_github(cls, source: str) -> Optional[SourceInfo]:
|
||||
def _detect_github(cls, source: str) -> SourceInfo | None:
|
||||
"""Detect GitHub repository source.
|
||||
|
||||
Supports patterns:
|
||||
|
||||
@@ -12,9 +12,6 @@ These tests verify that the unified CLI architecture works correctly:
|
||||
import pytest
|
||||
import subprocess
|
||||
import argparse
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
class TestParserSync:
|
||||
"""E2E tests for parser synchronization (Issue #285)."""
|
||||
@@ -77,7 +74,6 @@ class TestParserSync:
|
||||
for flag in expected_flags:
|
||||
assert flag in result.stdout, f"Help should show {flag} flag"
|
||||
|
||||
|
||||
class TestPresetSystem:
|
||||
"""E2E tests for preset system (Issue #268)."""
|
||||
|
||||
@@ -141,7 +137,6 @@ class TestPresetSystem:
|
||||
assert "DEPRECATED" in output, "Should show deprecation warning"
|
||||
assert "--preset comprehensive" in output, "Should suggest alternative"
|
||||
|
||||
|
||||
class TestBackwardCompatibility:
|
||||
"""E2E tests for backward compatibility."""
|
||||
|
||||
@@ -185,7 +180,6 @@ class TestBackwardCompatibility:
|
||||
assert flag in unified_result.stdout, f"Unified should have {flag}"
|
||||
assert flag in standalone_result.stdout, f"Standalone should have {flag}"
|
||||
|
||||
|
||||
class TestProgrammaticAPI:
|
||||
"""Test that the shared argument functions work programmatically."""
|
||||
|
||||
@@ -227,7 +221,6 @@ class TestProgrammaticAPI:
|
||||
# Note: enhance_level is not part of AnalysisPreset anymore.
|
||||
# It's controlled separately via --enhance-level flag (default 2)
|
||||
|
||||
|
||||
class TestIntegration:
|
||||
"""Integration tests for the complete flow."""
|
||||
|
||||
@@ -278,7 +271,6 @@ class TestIntegration:
|
||||
assert "--preset" in result.stdout, "Should show --preset flag"
|
||||
assert "DEFAULT" in result.stdout or "default" in result.stdout, "Should indicate default preset"
|
||||
|
||||
|
||||
class TestE2EWorkflow:
|
||||
"""End-to-end workflow tests."""
|
||||
|
||||
@@ -322,6 +314,5 @@ class TestE2EWorkflow:
|
||||
assert "--preset" in result.stdout, "Should have --preset flag"
|
||||
assert "unrecognized arguments" not in result.stderr.lower()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
pytest.main([__file__, "-v", "-s"])
|
||||
|
||||
@@ -6,7 +6,6 @@ Tests the three-tier argument system:
|
||||
3. Advanced arguments
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from skill_seekers.cli.arguments.create import (
|
||||
UNIVERSAL_ARGUMENTS,
|
||||
WEB_ARGUMENTS,
|
||||
@@ -20,7 +19,6 @@ from skill_seekers.cli.arguments.create import (
|
||||
add_create_arguments,
|
||||
)
|
||||
|
||||
|
||||
class TestUniversalArguments:
|
||||
"""Test universal argument definitions."""
|
||||
|
||||
@@ -51,7 +49,6 @@ class TestUniversalArguments:
|
||||
assert 'kwargs' in arg_def
|
||||
assert 'help' in arg_def['kwargs']
|
||||
|
||||
|
||||
class TestSourceSpecificArguments:
|
||||
"""Test source-specific argument definitions."""
|
||||
|
||||
@@ -96,7 +93,6 @@ class TestSourceSpecificArguments:
|
||||
assert flag not in all_flags, f"Duplicate flag: {flag}"
|
||||
all_flags.add(flag)
|
||||
|
||||
|
||||
class TestAdvancedArguments:
|
||||
"""Test advanced/rare argument definitions."""
|
||||
|
||||
@@ -106,7 +102,6 @@ class TestAdvancedArguments:
|
||||
assert 'no_rate_limit' in ADVANCED_ARGUMENTS
|
||||
assert 'interactive_enhancement' in ADVANCED_ARGUMENTS
|
||||
|
||||
|
||||
class TestArgumentHelpers:
|
||||
"""Test helper functions."""
|
||||
|
||||
@@ -148,7 +143,6 @@ class TestArgumentHelpers:
|
||||
args = get_source_specific_arguments('unknown')
|
||||
assert args == {}
|
||||
|
||||
|
||||
class TestCompatibleArguments:
|
||||
"""Test compatible argument detection."""
|
||||
|
||||
@@ -217,7 +211,6 @@ class TestCompatibleArguments:
|
||||
assert 'repo' not in compatible
|
||||
assert 'directory' not in compatible
|
||||
|
||||
|
||||
class TestAddCreateArguments:
|
||||
"""Test add_create_arguments function."""
|
||||
|
||||
@@ -284,7 +277,6 @@ class TestAddCreateArguments:
|
||||
args = parser.parse_args(['some_source'])
|
||||
assert args.source == 'some_source'
|
||||
|
||||
|
||||
class TestNoDuplicates:
|
||||
"""Test that there are no duplicate arguments across tiers."""
|
||||
|
||||
@@ -320,7 +312,6 @@ class TestNoDuplicates:
|
||||
assert len(github_flags & pdf_flags) == 0
|
||||
assert len(local_flags & pdf_flags) == 0
|
||||
|
||||
|
||||
class TestArgumentQuality:
|
||||
"""Test argument definition quality."""
|
||||
|
||||
|
||||
@@ -5,10 +5,6 @@ and routes to the correct scrapers without actually scraping.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import tempfile
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
class TestCreateCommandBasic:
|
||||
"""Basic integration tests for create command (dry-run mode)."""
|
||||
@@ -124,7 +120,6 @@ class TestCreateCommandBasic:
|
||||
assert '--preset' in result.stdout
|
||||
assert '--dry-run' in result.stdout
|
||||
|
||||
|
||||
class TestBackwardCompatibility:
|
||||
"""Test that old commands still work."""
|
||||
|
||||
|
||||
@@ -6,8 +6,6 @@ the same arguments as the standalone scraper modules. This prevents the
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import pytest
|
||||
|
||||
|
||||
class TestScrapeParserSync:
|
||||
"""Ensure scrape_parser has all arguments from doc_scraper."""
|
||||
@@ -87,7 +85,6 @@ class TestScrapeParserSync:
|
||||
for arg in required_args:
|
||||
assert arg in arg_dests, f"Required argument '{arg}' missing from scrape parser"
|
||||
|
||||
|
||||
class TestGitHubParserSync:
|
||||
"""Ensure github_parser has all arguments from github_scraper."""
|
||||
|
||||
@@ -131,7 +128,6 @@ class TestGitHubParserSync:
|
||||
assert not missing, f"github_parser missing arguments: {missing}"
|
||||
assert not extra, f"github_parser has extra arguments not in github_scraper: {extra}"
|
||||
|
||||
|
||||
class TestUnifiedCLI:
|
||||
"""Test the unified CLI main parser."""
|
||||
|
||||
|
||||
@@ -9,13 +9,10 @@ Tests the SourceDetector class's ability to identify and parse:
|
||||
"""
|
||||
|
||||
import os
|
||||
import tempfile
|
||||
import pytest
|
||||
from pathlib import Path
|
||||
|
||||
from skill_seekers.cli.source_detector import SourceDetector, SourceInfo
|
||||
|
||||
|
||||
class TestWebDetection:
|
||||
"""Test web URL detection."""
|
||||
|
||||
@@ -58,7 +55,6 @@ class TestWebDetection:
|
||||
assert info.type == 'web'
|
||||
assert info.suggested_name == 'vue'
|
||||
|
||||
|
||||
class TestGitHubDetection:
|
||||
"""Test GitHub repository detection."""
|
||||
|
||||
@@ -97,7 +93,6 @@ class TestGitHubDetection:
|
||||
assert info.parsed['repo'] == "microsoft/vscode-python"
|
||||
assert info.suggested_name == 'vscode-python'
|
||||
|
||||
|
||||
class TestLocalDetection:
|
||||
"""Test local directory detection."""
|
||||
|
||||
@@ -136,7 +131,6 @@ class TestLocalDetection:
|
||||
assert info.type == 'local'
|
||||
assert info.parsed['directory'] == cwd
|
||||
|
||||
|
||||
class TestPDFDetection:
|
||||
"""Test PDF file detection."""
|
||||
|
||||
@@ -160,7 +154,6 @@ class TestPDFDetection:
|
||||
assert info.type == 'pdf'
|
||||
assert info.suggested_name == 'my-awesome-guide'
|
||||
|
||||
|
||||
class TestConfigDetection:
|
||||
"""Test config file detection."""
|
||||
|
||||
@@ -178,7 +171,6 @@ class TestConfigDetection:
|
||||
assert info.parsed['config_path'] == "configs/django.json"
|
||||
assert info.suggested_name == 'django'
|
||||
|
||||
|
||||
class TestValidation:
|
||||
"""Test source validation."""
|
||||
|
||||
@@ -246,7 +238,6 @@ class TestValidation:
|
||||
)
|
||||
SourceDetector.validate_source(info)
|
||||
|
||||
|
||||
class TestAmbiguousCases:
|
||||
"""Test handling of ambiguous inputs."""
|
||||
|
||||
@@ -277,7 +268,6 @@ class TestAmbiguousCases:
|
||||
# Should detect as local directory, not web
|
||||
assert info.type == 'local'
|
||||
|
||||
|
||||
class TestRawInputPreservation:
|
||||
"""Test that raw_input is preserved correctly."""
|
||||
|
||||
@@ -302,7 +292,6 @@ class TestRawInputPreservation:
|
||||
info = SourceDetector.detect(original)
|
||||
assert info.raw_input == original
|
||||
|
||||
|
||||
class TestEdgeCases:
|
||||
"""Test edge cases and corner cases."""
|
||||
|
||||
|
||||
Reference in New Issue
Block a user