mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-08 02:14:08 +08:00
refactor: Update workflow plan system and template organization
- Remove --analyze|--deep parameters from plan.md, use default analysis - Change .analysis to .process directory structure for better organization - Create ANALYSIS_RESULTS.md template focused on verified results - Add .process folder to workflow-architecture.md file structure - Template emphasizes verification of files, methods, and commands - Prevent execution errors from non-existent references 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
207
.claude/python_script/cli.py
Normal file
207
.claude/python_script/cli.py
Normal file
@@ -0,0 +1,207 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
CLI Interface for Path-Aware Analysis
|
||||
Provides command-line interface for intelligent file analysis and pattern matching.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import argparse
|
||||
import logging
|
||||
import json
|
||||
import time
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Optional, Any
|
||||
|
||||
# Add current directory to path for imports
|
||||
sys.path.insert(0, str(Path(__file__).parent))
|
||||
|
||||
from core.config import get_config
|
||||
from core.file_indexer import FileIndexer
|
||||
from core.context_analyzer import ContextAnalyzer
|
||||
from core.path_matcher import PathMatcher
|
||||
from utils.colors import Colors
|
||||
|
||||
|
||||
class AnalysisCLI:
|
||||
"""Command-line interface for file analysis and pattern matching."""
|
||||
|
||||
def __init__(self, config_path: Optional[str] = None, root_path: str = "."):
|
||||
self.root_path = Path(root_path).resolve()
|
||||
self.config = get_config(config_path)
|
||||
|
||||
# Setup logging
|
||||
logging.basicConfig(
|
||||
level=getattr(logging, self.config.get('logging.level', 'INFO')),
|
||||
format=self.config.get('logging.format', '%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
||||
)
|
||||
self.logger = logging.getLogger(__name__)
|
||||
|
||||
# Initialize core components
|
||||
self.indexer = FileIndexer(self.config, str(self.root_path))
|
||||
self.context_analyzer = ContextAnalyzer(self.config)
|
||||
self.path_matcher = PathMatcher(self.config)
|
||||
|
||||
def analyze(self, prompt: str, patterns: Optional[List[str]] = None) -> Dict[str, Any]:
|
||||
"""Analyze and return relevant file paths for a given prompt."""
|
||||
print(Colors.yellow("Analyzing project and prompt..."))
|
||||
start_time = time.time()
|
||||
|
||||
# Load index (build if not exists)
|
||||
index = self.indexer.load_index()
|
||||
if not index:
|
||||
print(Colors.warning("No file index found. Run 'python indexer.py --build' first or use --auto-build"))
|
||||
return {}
|
||||
|
||||
stats = self.indexer.get_stats()
|
||||
print(Colors.cyan(f"Project stats: ~{stats.total_tokens:,} tokens across {stats.total_files} files"))
|
||||
print(Colors.cyan(f"Categories: {', '.join(f'{k}: {v}' for k, v in stats.categories.items())}"))
|
||||
|
||||
# Determine project size
|
||||
project_size = self._classify_project_size(stats.total_tokens)
|
||||
print(Colors.cyan(f"Project size: {project_size}"))
|
||||
|
||||
# Analyze prompt context
|
||||
print(Colors.yellow("Analyzing prompt context..."))
|
||||
context_result = self.context_analyzer.analyze(prompt)
|
||||
|
||||
print(Colors.cyan(f"Identified: {len(context_result.domains)} domains, {len(context_result.languages)} languages"))
|
||||
if context_result.domains:
|
||||
print(Colors.cyan(f"Top domains: {', '.join(context_result.domains[:3])}"))
|
||||
|
||||
# Match files to context
|
||||
print(Colors.yellow("Matching files to context..."))
|
||||
matching_result = self.path_matcher.match_files(
|
||||
index,
|
||||
context_result,
|
||||
explicit_patterns=patterns
|
||||
)
|
||||
|
||||
elapsed = time.time() - start_time
|
||||
|
||||
print(Colors.green(f"Analysis complete: {len(matching_result.matched_files)} files, ~{matching_result.total_tokens:,} tokens"))
|
||||
print(Colors.cyan(f"Confidence: {matching_result.confidence_score:.2f}"))
|
||||
print(Colors.cyan(f"Execution time: {elapsed:.2f}s"))
|
||||
|
||||
return {
|
||||
'files': [match.file_info.relative_path for match in matching_result.matched_files],
|
||||
'total_tokens': matching_result.total_tokens,
|
||||
'confidence': matching_result.confidence_score,
|
||||
'context': {
|
||||
'domains': context_result.domains,
|
||||
'languages': context_result.languages,
|
||||
'keywords': context_result.keywords
|
||||
},
|
||||
'stats': {
|
||||
'project_size': project_size,
|
||||
'total_files': stats.total_files,
|
||||
'analysis_time': elapsed
|
||||
}
|
||||
}
|
||||
|
||||
def generate_command(self, prompt: str, tool: str, files: List[str]) -> str:
|
||||
"""Generate a command for external tools (gemini/codex)."""
|
||||
file_patterns = " ".join(f"@{{{file}}}" for file in files)
|
||||
|
||||
if tool == "gemini":
|
||||
if len(files) > 50:
|
||||
return f'gemini --all-files -p "{prompt}"'
|
||||
else:
|
||||
return f'gemini -p "{file_patterns} {prompt}"'
|
||||
elif tool == "codex":
|
||||
# Estimate tokens for workspace selection
|
||||
total_tokens = sum(len(file) * 50 for file in files) # Rough estimate
|
||||
workspace_flag = "-s workspace-write" if total_tokens > 100000 else "-s danger-full-access"
|
||||
return f'codex {workspace_flag} --full-auto exec "{file_patterns} {prompt}"'
|
||||
else:
|
||||
raise ValueError(f"Unsupported tool: {tool}")
|
||||
|
||||
def _classify_project_size(self, tokens: int) -> str:
|
||||
"""Classify project size based on token count."""
|
||||
small_limit = self.config.get('token_limits.small_project', 500000)
|
||||
medium_limit = self.config.get('token_limits.medium_project', 2000000)
|
||||
|
||||
if tokens < small_limit:
|
||||
return "small"
|
||||
elif tokens < medium_limit:
|
||||
return "medium"
|
||||
else:
|
||||
return "large"
|
||||
|
||||
def auto_build_index(self):
|
||||
"""Auto-build index if it doesn't exist."""
|
||||
from indexer import ProjectIndexer
|
||||
indexer = ProjectIndexer(root_path=str(self.root_path))
|
||||
indexer.build_index()
|
||||
|
||||
|
||||
def main():
|
||||
"""CLI entry point for analysis."""
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Path-Aware Analysis CLI - Intelligent file pattern detection",
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog="""
|
||||
Examples:
|
||||
python cli.py "analyze authentication flow"
|
||||
python cli.py "fix database connection" --patterns "src/**/*.py"
|
||||
python cli.py "review API endpoints" --tool gemini
|
||||
"""
|
||||
)
|
||||
|
||||
parser.add_argument('prompt', help='Analysis prompt or task description')
|
||||
parser.add_argument('--patterns', nargs='*', help='Explicit file patterns to include')
|
||||
parser.add_argument('--tool', choices=['gemini', 'codex'], help='Generate command for specific tool')
|
||||
parser.add_argument('--output', choices=['patterns', 'json'], default='patterns', help='Output format')
|
||||
parser.add_argument('--verbose', '-v', action='store_true', help='Verbose output')
|
||||
parser.add_argument('--auto-build', action='store_true', help='Auto-build index if missing')
|
||||
parser.add_argument('--config', help='Configuration file path')
|
||||
parser.add_argument('--root', default='.', help='Root directory to analyze')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Create CLI interface
|
||||
cli = AnalysisCLI(args.config, args.root)
|
||||
|
||||
try:
|
||||
# Auto-build index if requested and missing
|
||||
if args.auto_build:
|
||||
index = cli.indexer.load_index()
|
||||
if not index:
|
||||
print(Colors.yellow("Auto-building missing index..."))
|
||||
cli.auto_build_index()
|
||||
|
||||
# Perform analysis
|
||||
result = cli.analyze(args.prompt, patterns=args.patterns)
|
||||
|
||||
if not result:
|
||||
sys.exit(1)
|
||||
|
||||
# Generate output
|
||||
if args.tool:
|
||||
command = cli.generate_command(args.prompt, args.tool, result['files'])
|
||||
print(command)
|
||||
elif args.output == 'json':
|
||||
print(json.dumps(result, indent=2, default=str))
|
||||
else: # patterns output (default)
|
||||
for file_path in result['files']:
|
||||
print(f"@{{{file_path}}}")
|
||||
|
||||
# Show verbose details
|
||||
if args.verbose:
|
||||
print(f"\n# Analysis Details:")
|
||||
print(f"# Matched files: {len(result['files'])}")
|
||||
print(f"# Total tokens: {result['total_tokens']:,}")
|
||||
print(f"# Confidence: {result['confidence']:.2f}")
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print(Colors.warning("\nAnalysis interrupted by user"))
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(Colors.error(f"Analysis failed: {e}"))
|
||||
if args.verbose:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user