code-compass-cli 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- code_compass_cli-0.1.0.dist-info/METADATA +46 -0
- code_compass_cli-0.1.0.dist-info/RECORD +31 -0
- code_compass_cli-0.1.0.dist-info/WHEEL +5 -0
- code_compass_cli-0.1.0.dist-info/entry_points.txt +2 -0
- code_compass_cli-0.1.0.dist-info/top_level.txt +1 -0
- src/__init__.py +3 -0
- src/__pycache__/__init__.cpython-313.pyc +0 -0
- src/cli/__init__.py +1 -0
- src/cli/__pycache__/__init__.cpython-313.pyc +0 -0
- src/cli/__pycache__/main.cpython-313.pyc +0 -0
- src/cli/main.py +431 -0
- src/docs/__init__.py +1 -0
- src/docs/__pycache__/__init__.cpython-313.pyc +0 -0
- src/docs/__pycache__/doc_generator.cpython-313.pyc +0 -0
- src/docs/doc_generator.py +734 -0
- src/quality/__init__.py +1 -0
- src/quality/__pycache__/__init__.cpython-313.pyc +0 -0
- src/quality/__pycache__/analyzer.cpython-313.pyc +0 -0
- src/quality/analyzer.py +300 -0
- src/query/__init__.py +1 -0
- src/query/__pycache__/__init__.cpython-313.pyc +0 -0
- src/query/__pycache__/copilot_query.cpython-313.pyc +0 -0
- src/query/copilot_query.py +475 -0
- src/scanner/__init__.py +1 -0
- src/scanner/__pycache__/__init__.cpython-313.pyc +0 -0
- src/scanner/__pycache__/repo_scanner.cpython-313.pyc +0 -0
- src/scanner/repo_scanner.py +139 -0
- src/visualizer/__init__.py +1 -0
- src/visualizer/__pycache__/__init__.cpython-313.pyc +0 -0
- src/visualizer/__pycache__/flow_tracer.cpython-313.pyc +0 -0
- src/visualizer/flow_tracer.py +294 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: code-compass-cli
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A Python CLI tool for intelligent code analysis and navigation
|
|
5
|
+
Home-page: https://github.com/techwhiz/code-compass
|
|
6
|
+
Author: CodeCompass Team
|
|
7
|
+
Author-email: CodeCompass Team <support@codecompass.dev>
|
|
8
|
+
License: MIT
|
|
9
|
+
Project-URL: Homepage, https://github.com/techwhiz/code-compass
|
|
10
|
+
Project-URL: Documentation, https://github.com/techwhiz/code-compass#readme
|
|
11
|
+
Project-URL: Repository, https://github.com/techwhiz/code-compass
|
|
12
|
+
Project-URL: Bug Tracker, https://github.com/techwhiz/code-compass/issues
|
|
13
|
+
Keywords: code-analysis,code-navigation,code-quality,static-analysis,cli,python,security,performance
|
|
14
|
+
Classifier: Development Status :: 3 - Alpha
|
|
15
|
+
Classifier: Environment :: Console
|
|
16
|
+
Classifier: Intended Audience :: Developers
|
|
17
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
18
|
+
Classifier: Natural Language :: English
|
|
19
|
+
Classifier: Operating System :: OS Independent
|
|
20
|
+
Classifier: Programming Language :: Python :: 3
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
25
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
26
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
27
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
28
|
+
Classifier: Topic :: Utilities
|
|
29
|
+
Requires-Python: >=3.7
|
|
30
|
+
Description-Content-Type: text/markdown
|
|
31
|
+
Requires-Dist: click>=8.1.0
|
|
32
|
+
Requires-Dist: rich>=13.0.0
|
|
33
|
+
Provides-Extra: dev
|
|
34
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
35
|
+
Requires-Dist: pytest-cov>=3.0; extra == "dev"
|
|
36
|
+
Requires-Dist: flake8>=4.0; extra == "dev"
|
|
37
|
+
Requires-Dist: black>=22.0; extra == "dev"
|
|
38
|
+
Requires-Dist: mypy>=0.950; extra == "dev"
|
|
39
|
+
Provides-Extra: docs
|
|
40
|
+
Requires-Dist: sphinx>=4.0; extra == "docs"
|
|
41
|
+
Requires-Dist: sphinx-rtd-theme>=1.0; extra == "docs"
|
|
42
|
+
Dynamic: author
|
|
43
|
+
Dynamic: home-page
|
|
44
|
+
Dynamic: requires-python
|
|
45
|
+
|
|
46
|
+
# code-compass
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
src/__init__.py,sha256=XGy3Yc6rmb-_q05j0fzZvBC2zJLuWr05slwkxDK9xTE,93
|
|
2
|
+
src/__pycache__/__init__.cpython-313.pyc,sha256=IEcu2pQZ2NHIHN7eWOKJmfLumdybVxZPTou_1BlYl3E,255
|
|
3
|
+
src/cli/__init__.py,sha256=TPerMu1FQSbvDIUN0aqKJCkVk4clbDrn3hT2SCDIbZU,34
|
|
4
|
+
src/cli/main.py,sha256=Z-KIQLImy1J8UbgQZtAPOib-QU0wmJXHIBGwCTnWb9Y,15050
|
|
5
|
+
src/cli/__pycache__/__init__.cpython-313.pyc,sha256=tbNWWnS7yq3BZGjpgM0KVPPn0hNW-Zo6MMLCGnA0JsM,194
|
|
6
|
+
src/cli/__pycache__/main.cpython-313.pyc,sha256=3J9aJ8Pcu0wYWyO5i-AzT8C0hbjnTsAgDzq9L2586YI,21573
|
|
7
|
+
src/docs/__init__.py,sha256=DJcV80OlqdVZe19OyYf7kIraz6UDlyQg9TG8SM8ZIzY,39
|
|
8
|
+
src/docs/doc_generator.py,sha256=d58SAMKhSKdvIRW4VFkeD9wVgPk30UVj8fIEUkFRiag,17826
|
|
9
|
+
src/docs/__pycache__/__init__.cpython-313.pyc,sha256=SlZxBpYc8zjPlQMb9jFS26hxOaQB53UFsaovx4xd6u0,200
|
|
10
|
+
src/docs/__pycache__/doc_generator.cpython-313.pyc,sha256=fG7xfZ0k6T2kzOvSRw9CN0_2XnJeO-xMNIQXlqFEa3Q,20455
|
|
11
|
+
src/quality/__init__.py,sha256=oC6QaaIgpAoh04g4dAtP97ryljAsHdYzqyYNLjg68Dc,36
|
|
12
|
+
src/quality/analyzer.py,sha256=fjkyOC0ATM3dn7QwYl2fNu8way0cvQVnpe31dwyQUjc,11682
|
|
13
|
+
src/quality/__pycache__/__init__.cpython-313.pyc,sha256=267KS5gJBtJCb8RJN_VAUQSGEqxiBbpuY_QfJnGdJ_0,200
|
|
14
|
+
src/quality/__pycache__/analyzer.cpython-313.pyc,sha256=9mvodhIVYCDlpxNJhdoQuHGtzlalN83ZlnO-R77w7XQ,11754
|
|
15
|
+
src/query/__init__.py,sha256=cBnG7wxPqcGh87L6WjnxIqtwpb5OA13s8XupH9EWnDU,38
|
|
16
|
+
src/query/copilot_query.py,sha256=knwMdTbG7QzqtY1bFyvAsP4M3iRvhFRSXmnAybazwt4,17990
|
|
17
|
+
src/query/__pycache__/__init__.cpython-313.pyc,sha256=dDHK6vyrTF68KTwdCeODALuKoiT0LYsMmH5l78Emk4g,200
|
|
18
|
+
src/query/__pycache__/copilot_query.cpython-313.pyc,sha256=5sK-4dUe5lWcSjetLpXTfYjLVQ4tLLnWRoYagWXK1Mo,20467
|
|
19
|
+
src/scanner/__init__.py,sha256=_F8EUHghATi8hiXGSoBZtuANrkebEOXuR320LmF7mhQ,34
|
|
20
|
+
src/scanner/repo_scanner.py,sha256=vOumUiKE0EmfkjgGelsUmJH7MziIMMHDFrA8g6iipCY,4402
|
|
21
|
+
src/scanner/__pycache__/__init__.cpython-313.pyc,sha256=5ta4-hZX4lgHb6RAJObKpR7nWu9X3AJWRsL_tLzx4bQ,198
|
|
22
|
+
src/scanner/__pycache__/repo_scanner.cpython-313.pyc,sha256=oSl-JuOMzfBTSYu0Tqjo1xCLkoy_mY1j-C-P-8nEKiw,6596
|
|
23
|
+
src/visualizer/__init__.py,sha256=JrDBHDMcq_hE7NCNrD25MFrEpjMCmGoxcNvv1iqPtq0,60
|
|
24
|
+
src/visualizer/flow_tracer.py,sha256=yviHzfXyzWAWWRYzx-W2jATDd_vHNRKAKcOAVjsuogk,9864
|
|
25
|
+
src/visualizer/__pycache__/__init__.cpython-313.pyc,sha256=4BfRJO1E8jCNTwROOJ2psmMCiYuQ0m0K6Ucu8ld-ToI,227
|
|
26
|
+
src/visualizer/__pycache__/flow_tracer.cpython-313.pyc,sha256=NKswmtaZ2JiT36AfEBToTVQMCnoSQFXIO0KQbxl90Ek,10662
|
|
27
|
+
code_compass_cli-0.1.0.dist-info/METADATA,sha256=ZtC1rOneto0s3Ztd3-ijupQ8tI_QRFuW42eMy-BpcM8,1929
|
|
28
|
+
code_compass_cli-0.1.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
29
|
+
code_compass_cli-0.1.0.dist-info/entry_points.txt,sha256=myMcO1H0BbBI4opXlmpaFWjgyPl4RyjGfPGSQxJpjCI,49
|
|
30
|
+
code_compass_cli-0.1.0.dist-info/top_level.txt,sha256=74rtVfumQlgAPzR5_2CgYN24MB0XARCg0t-gzk6gTrM,4
|
|
31
|
+
code_compass_cli-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
src
|
src/__init__.py
ADDED
|
Binary file
|
src/cli/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""CLI module for CodeCompass."""
|
|
Binary file
|
|
Binary file
|
src/cli/main.py
ADDED
|
@@ -0,0 +1,431 @@
|
|
|
1
|
+
"""Main CLI entry point for CodeCompass."""
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
import click
|
|
7
|
+
from rich.console import Console
|
|
8
|
+
from rich.panel import Panel
|
|
9
|
+
from rich.syntax import Syntax
|
|
10
|
+
from rich.table import Table
|
|
11
|
+
from rich.tree import Tree
|
|
12
|
+
|
|
13
|
+
# Add parent directory to path for imports
|
|
14
|
+
sys.path.insert(0, str(Path(__file__).parent.parent.parent))
|
|
15
|
+
|
|
16
|
+
from src.scanner.repo_scanner import RepoScanner
|
|
17
|
+
from src.query.copilot_query import CopilotQuery
|
|
18
|
+
from src.visualizer.flow_tracer import FlowTracer
|
|
19
|
+
from src.quality.analyzer import CodeAnalyzer
|
|
20
|
+
from src.docs.doc_generator import DocumentationGenerator
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
console = Console()
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@click.group()
|
|
27
|
+
@click.version_option()
|
|
28
|
+
def cli():
|
|
29
|
+
"""CodeCompass - Navigate and analyze code with ease."""
|
|
30
|
+
pass
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _tree_to_rich(tree_node, max_files: int = 100) -> Tree:
|
|
34
|
+
"""Convert TreeNode to rich Tree for display.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
tree_node: The TreeNode to convert
|
|
38
|
+
max_files: Maximum number of files to display
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
Rich Tree object
|
|
42
|
+
"""
|
|
43
|
+
rich_tree = Tree(f"📁 {tree_node.name}")
|
|
44
|
+
_populate_rich_tree(tree_node, rich_tree, 0, max_files)
|
|
45
|
+
return rich_tree
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _populate_rich_tree(tree_node, rich_parent: Tree, depth: int, max_files: int, file_count: list = None) -> None:
|
|
49
|
+
"""Recursively populate rich tree with nodes.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
tree_node: Current TreeNode
|
|
53
|
+
rich_parent: Parent Rich Tree node
|
|
54
|
+
depth: Current recursion depth
|
|
55
|
+
max_files: Maximum files to display
|
|
56
|
+
file_count: List to track file count [current_count]
|
|
57
|
+
"""
|
|
58
|
+
if file_count is None:
|
|
59
|
+
file_count = [0]
|
|
60
|
+
|
|
61
|
+
if depth > 10:
|
|
62
|
+
return
|
|
63
|
+
|
|
64
|
+
if file_count[0] >= max_files:
|
|
65
|
+
rich_parent.label += " [dim]...[/dim]"
|
|
66
|
+
return
|
|
67
|
+
|
|
68
|
+
for child in sorted(tree_node.children, key=lambda x: (not x.is_dir, x.name)):
|
|
69
|
+
if file_count[0] >= max_files:
|
|
70
|
+
break
|
|
71
|
+
|
|
72
|
+
file_count[0] += 1
|
|
73
|
+
|
|
74
|
+
if child.is_dir:
|
|
75
|
+
child_tree = rich_parent.add(f"📂 [bold]{child.name}[/bold]")
|
|
76
|
+
_populate_rich_tree(child, child_tree, depth + 1, max_files, file_count)
|
|
77
|
+
else:
|
|
78
|
+
# Add file with icon based on extension
|
|
79
|
+
icon = _get_file_icon(child.name)
|
|
80
|
+
rich_parent.add(f"{icon} [cyan]{child.name}[/cyan]")
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _get_file_icon(filename: str) -> str:
|
|
84
|
+
"""Get appropriate icon for file type.
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
filename: The file name
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
Icon string
|
|
91
|
+
"""
|
|
92
|
+
ext = filename.split('.')[-1].lower() if '.' in filename else ''
|
|
93
|
+
icons = {
|
|
94
|
+
'py': '🐍',
|
|
95
|
+
'js': '📜',
|
|
96
|
+
'ts': '📘',
|
|
97
|
+
'json': '📋',
|
|
98
|
+
'md': '📝',
|
|
99
|
+
'txt': '📄',
|
|
100
|
+
'yaml': '⚙️',
|
|
101
|
+
'yml': '⚙️',
|
|
102
|
+
'toml': '⚙️',
|
|
103
|
+
'lock': '🔒',
|
|
104
|
+
'git': '🔗',
|
|
105
|
+
'env': '🔑',
|
|
106
|
+
}
|
|
107
|
+
return icons.get(ext, '📄')
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
@cli.command()
|
|
111
|
+
@click.argument('path', default='.', required=False)
|
|
112
|
+
def scan(path):
|
|
113
|
+
"""Scan a repository for code structure."""
|
|
114
|
+
try:
|
|
115
|
+
scanner = RepoScanner(path)
|
|
116
|
+
result = scanner.scan()
|
|
117
|
+
|
|
118
|
+
console.print()
|
|
119
|
+
console.print(Panel("[bold cyan]CodeCompass Repository Scan[/bold cyan]", expand=False))
|
|
120
|
+
console.print(f"[bold]Path:[/bold] [yellow]{result['path']}[/yellow]")
|
|
121
|
+
console.print(f"[bold]Files found:[/bold] {len(result['files'])}")
|
|
122
|
+
console.print(f"[bold]Directories:[/bold] {len(result['directories'])}")
|
|
123
|
+
console.print()
|
|
124
|
+
|
|
125
|
+
# Display directory tree
|
|
126
|
+
rich_tree = _tree_to_rich(result['tree'], max_files=500)
|
|
127
|
+
console.print(rich_tree)
|
|
128
|
+
console.print()
|
|
129
|
+
|
|
130
|
+
console.print("[bold green]✓ Scan completed successfully[/bold green]")
|
|
131
|
+
except FileNotFoundError as e:
|
|
132
|
+
console.print(f"[bold red]✗ Error:[/bold red] {e}")
|
|
133
|
+
sys.exit(1)
|
|
134
|
+
except Exception as e:
|
|
135
|
+
console.print(f"[bold red]✗ Unexpected error:[/bold red] {e}")
|
|
136
|
+
sys.exit(1)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
@cli.command()
|
|
141
|
+
@click.argument('query_text')
|
|
142
|
+
@click.argument('path', default='.', required=False)
|
|
143
|
+
def query(query_text, path):
|
|
144
|
+
"""Query code with natural language."""
|
|
145
|
+
try:
|
|
146
|
+
copilot = CopilotQuery(path)
|
|
147
|
+
|
|
148
|
+
console.print()
|
|
149
|
+
console.print(Panel("[bold cyan]CodeCompass Query[/bold cyan]", expand=False))
|
|
150
|
+
console.print(f"[bold]Query:[/bold] {query_text}")
|
|
151
|
+
console.print(f"[dim]Path:[/dim] {path}")
|
|
152
|
+
console.print()
|
|
153
|
+
|
|
154
|
+
result = copilot.execute(query_text)
|
|
155
|
+
|
|
156
|
+
if result.get("results"):
|
|
157
|
+
console.print("[bold green]✓ Results found:[/bold green]")
|
|
158
|
+
console.print()
|
|
159
|
+
|
|
160
|
+
for idx, match in enumerate(result["results"], 1):
|
|
161
|
+
# Display file header
|
|
162
|
+
match_type = match.get("match_type", "Match")
|
|
163
|
+
file_path = match.get("file", "unknown")
|
|
164
|
+
console.print(f"[bold cyan][{idx}] {file_path}[/bold cyan] [dim]({match_type})[/dim]")
|
|
165
|
+
|
|
166
|
+
# Display keyword
|
|
167
|
+
if match.get("keyword"):
|
|
168
|
+
console.print(f" [yellow]Keyword:[/yellow] {match['keyword']}")
|
|
169
|
+
|
|
170
|
+
# Display content for filename/content matches
|
|
171
|
+
if match.get("content"):
|
|
172
|
+
console.print(" [bold]Code:[/bold]")
|
|
173
|
+
code_lines = match["content"].split("\n")
|
|
174
|
+
for line in code_lines[:15]: # Limit to 15 lines
|
|
175
|
+
console.print(f" [dim]|[/dim] {line}")
|
|
176
|
+
if len(code_lines) > 15:
|
|
177
|
+
console.print(f" [dim]... ({len(code_lines) - 15} more lines)[/dim]")
|
|
178
|
+
|
|
179
|
+
# Display definitions
|
|
180
|
+
if match.get("definitions"):
|
|
181
|
+
console.print(" [bold]Definitions:[/bold]")
|
|
182
|
+
for defn in match["definitions"][:3]: # Max 3 definitions
|
|
183
|
+
console.print(f" [green]{defn.get('type', 'definition')}[/green] at line {defn.get('line_num')}")
|
|
184
|
+
console.print(f" [cyan]{defn.get('name', 'unknown')}[/cyan]")
|
|
185
|
+
if defn.get("code"):
|
|
186
|
+
console.print(f" [dim]{defn['code'][:80]}[/dim]")
|
|
187
|
+
if defn.get("body"):
|
|
188
|
+
body_preview = defn['body'][:100].replace("\n", " ")
|
|
189
|
+
console.print(f" [dim]Body: {body_preview}...[/dim]")
|
|
190
|
+
|
|
191
|
+
# Display lines for keyword matches
|
|
192
|
+
if match.get("lines"):
|
|
193
|
+
console.print(" [bold]Matches:[/bold]")
|
|
194
|
+
for line_info in match["lines"][:2]: # Max 2 context blocks
|
|
195
|
+
console.print(f" Line {line_info.get('line_num')}:")
|
|
196
|
+
console.print(f" [yellow]{line_info.get('content', '')}[/yellow]")
|
|
197
|
+
|
|
198
|
+
# Display import statements
|
|
199
|
+
if match.get("content") and "import" in match.get("content", "").lower():
|
|
200
|
+
console.print(f" [bold]Import:[/bold] [dim]{match['content'][:100]}[/dim]")
|
|
201
|
+
|
|
202
|
+
console.print()
|
|
203
|
+
|
|
204
|
+
console.print(f"[dim]{result['summary']}[/dim]")
|
|
205
|
+
else:
|
|
206
|
+
console.print("[bold yellow]⚠ No results found[/bold yellow]")
|
|
207
|
+
console.print(f"[dim]Try searching with different keywords or longer file names[/dim]")
|
|
208
|
+
|
|
209
|
+
console.print()
|
|
210
|
+
except Exception as e:
|
|
211
|
+
console.print(f"[bold red]✗ Error:[/bold red] {e}")
|
|
212
|
+
sys.exit(1)
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
@cli.command()
|
|
217
|
+
@click.argument('symbol')
|
|
218
|
+
@click.argument('path', default='.', required=False)
|
|
219
|
+
def trace(symbol, path):
|
|
220
|
+
"""Trace execution flow for a symbol."""
|
|
221
|
+
tracer = FlowTracer(path)
|
|
222
|
+
|
|
223
|
+
console.print()
|
|
224
|
+
console.print(Panel("[bold cyan]CodeCompass Flow Trace[/bold cyan]", expand=False))
|
|
225
|
+
console.print(f"[bold]Entry Point:[/bold] {symbol}")
|
|
226
|
+
console.print(f"[dim]Path:[/dim] {path}")
|
|
227
|
+
console.print()
|
|
228
|
+
|
|
229
|
+
result = tracer.trace(symbol)
|
|
230
|
+
|
|
231
|
+
if result["flow"]:
|
|
232
|
+
console.print("[bold green]✓ Execution flow traced:[/bold green]")
|
|
233
|
+
console.print()
|
|
234
|
+
|
|
235
|
+
# Build a rich tree for visualization
|
|
236
|
+
tree = Tree(f"[bold]{result['entry_point']}[/bold]")
|
|
237
|
+
_build_tree(tree, result["flow"])
|
|
238
|
+
|
|
239
|
+
console.print(tree)
|
|
240
|
+
console.print()
|
|
241
|
+
console.print(f"[dim]Total function calls: {result['total_calls']}[/dim]")
|
|
242
|
+
else:
|
|
243
|
+
console.print("[yellow]No execution flow found. Function not located in codebase.[/yellow]")
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
@cli.command()
|
|
247
|
+
@click.argument('path', default='.', required=False)
|
|
248
|
+
def analyze(path):
|
|
249
|
+
"""Analyze code quality (security, performance, code smells)."""
|
|
250
|
+
analyzer = CodeAnalyzer(path)
|
|
251
|
+
|
|
252
|
+
console.print()
|
|
253
|
+
console.print(Panel("[bold cyan]CodeCompass Quality Analysis[/bold cyan]", expand=False))
|
|
254
|
+
console.print(f"[dim]Path:[/dim] {path}")
|
|
255
|
+
console.print()
|
|
256
|
+
|
|
257
|
+
result = analyzer.analyze()
|
|
258
|
+
|
|
259
|
+
# Display summary
|
|
260
|
+
total = result["total"]
|
|
261
|
+
critical_count = len(result["critical"])
|
|
262
|
+
warning_count = len(result["warning"])
|
|
263
|
+
info_count = len(result["info"])
|
|
264
|
+
|
|
265
|
+
console.print("[bold]Analysis Results:[/bold]")
|
|
266
|
+
console.print(f" [red]🔴 Critical:[/red] {critical_count}")
|
|
267
|
+
console.print(f" [yellow]🟡 Warnings:[/yellow] {warning_count}")
|
|
268
|
+
console.print(f" [blue]🔵 Info:[/blue] {info_count}")
|
|
269
|
+
console.print(f" [dim]Total Issues: {total}[/dim]")
|
|
270
|
+
console.print()
|
|
271
|
+
|
|
272
|
+
# Display critical issues
|
|
273
|
+
if critical_count:
|
|
274
|
+
console.print("[bold red]━ CRITICAL ISSUES ━[/bold red]")
|
|
275
|
+
for i, issue in enumerate(result["critical"], 1):
|
|
276
|
+
_display_issue(issue, i)
|
|
277
|
+
|
|
278
|
+
# Display warning issues
|
|
279
|
+
if warning_count:
|
|
280
|
+
console.print("[bold yellow]━ WARNINGS ━[/bold yellow]")
|
|
281
|
+
for i, issue in enumerate(result["warning"], 1):
|
|
282
|
+
_display_issue(issue, i)
|
|
283
|
+
|
|
284
|
+
# Display info issues
|
|
285
|
+
if info_count:
|
|
286
|
+
console.print("[bold blue]━ INFO ━[/bold blue]")
|
|
287
|
+
for i, issue in enumerate(result["info"][:5], 1): # Limit to first 5
|
|
288
|
+
_display_issue(issue, i)
|
|
289
|
+
if len(result["info"]) > 5:
|
|
290
|
+
console.print(f"[dim]... and {len(result['info']) - 5} more info messages[/dim]")
|
|
291
|
+
|
|
292
|
+
if total == 0:
|
|
293
|
+
console.print("[bold green]✓ No issues found! Code looks clean.[/bold green]")
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
def _display_issue(issue, index):
|
|
297
|
+
"""Display a single issue.
|
|
298
|
+
|
|
299
|
+
Args:
|
|
300
|
+
issue: Issue object
|
|
301
|
+
index: Issue number
|
|
302
|
+
"""
|
|
303
|
+
severity_color = {
|
|
304
|
+
"critical": "red",
|
|
305
|
+
"warning": "yellow",
|
|
306
|
+
"info": "blue",
|
|
307
|
+
}.get(issue.severity, "white")
|
|
308
|
+
|
|
309
|
+
console.print()
|
|
310
|
+
console.print(f" [{severity_color}]{index}. {issue.category.upper()}[/{severity_color}] "
|
|
311
|
+
f"[bold]{issue.message}[/bold]")
|
|
312
|
+
console.print(f" [dim]📄 {issue.file}:{issue.line}[/dim]")
|
|
313
|
+
console.print(f" [dim]{issue.code_snippet}[/dim]")
|
|
314
|
+
console.print(f" [cyan]💡 Suggestion:[/cyan] {issue.suggestion}")
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
def _build_tree(parent_tree, flow_node, depth=0):
|
|
318
|
+
"""Build a rich Tree from flow data.
|
|
319
|
+
|
|
320
|
+
Args:
|
|
321
|
+
parent_tree: Parent Tree node
|
|
322
|
+
flow_node: Flow data dictionary
|
|
323
|
+
depth: Current depth
|
|
324
|
+
"""
|
|
325
|
+
if not flow_node or depth > 5:
|
|
326
|
+
return
|
|
327
|
+
|
|
328
|
+
# Add definition info
|
|
329
|
+
if flow_node.get("file"):
|
|
330
|
+
label = f"[cyan]{flow_node['name']}[/cyan] [dim]({flow_node['type']})[/dim]"
|
|
331
|
+
label += f"\n[blue]📄 {flow_node['file']}:{flow_node['line']}[/blue]"
|
|
332
|
+
child = parent_tree.add(label)
|
|
333
|
+
else:
|
|
334
|
+
label = f"[dim]{flow_node['name']}[/dim] [yellow](not found)[/yellow]"
|
|
335
|
+
child = parent_tree.add(label)
|
|
336
|
+
|
|
337
|
+
# Add called functions
|
|
338
|
+
if flow_node.get("calls"):
|
|
339
|
+
for call in flow_node["calls"]:
|
|
340
|
+
call_label = f"[cyan]{call['name']}[/cyan]"
|
|
341
|
+
if call.get("called_from"):
|
|
342
|
+
call_label += f"\n[green]→ called from {call['called_from']['file']}:{call['called_from']['line']}[/green]"
|
|
343
|
+
sub_child = child.add(call_label)
|
|
344
|
+
|
|
345
|
+
# Recursively add children
|
|
346
|
+
if call.get("calls"):
|
|
347
|
+
for sub_call in call["calls"]:
|
|
348
|
+
_build_tree_node(sub_child, sub_call, depth + 1)
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
def _build_tree_node(parent_tree, flow_node, depth=0):
|
|
352
|
+
"""Helper to build tree nodes recursively.
|
|
353
|
+
|
|
354
|
+
Args:
|
|
355
|
+
parent_tree: Parent Tree node
|
|
356
|
+
flow_node: Flow data dictionary
|
|
357
|
+
depth: Current depth
|
|
358
|
+
"""
|
|
359
|
+
if not flow_node or depth > 5:
|
|
360
|
+
return
|
|
361
|
+
|
|
362
|
+
label = f"[cyan]{flow_node['name']}[/cyan]"
|
|
363
|
+
if flow_node.get("file"):
|
|
364
|
+
label += f" [blue]({flow_node['file']}:{flow_node['line']})[/blue]"
|
|
365
|
+
else:
|
|
366
|
+
label += " [yellow](not found)[/yellow]"
|
|
367
|
+
|
|
368
|
+
child = parent_tree.add(label)
|
|
369
|
+
|
|
370
|
+
if flow_node.get("calls"):
|
|
371
|
+
for call in flow_node["calls"]:
|
|
372
|
+
_build_tree_node(child, call, depth + 1)
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
@cli.command()
|
|
376
|
+
@click.argument('path', default='.', required=False)
|
|
377
|
+
def gendocs(path):
|
|
378
|
+
"""Generate documentation for the project."""
|
|
379
|
+
generator = DocumentationGenerator(path)
|
|
380
|
+
|
|
381
|
+
console.print()
|
|
382
|
+
console.print(Panel("[bold cyan]CodeCompass Documentation Generator[/bold cyan]", expand=False))
|
|
383
|
+
console.print(f"[dim]Path:[/dim] {path}")
|
|
384
|
+
console.print()
|
|
385
|
+
|
|
386
|
+
console.print("[bold]Generating documentation...[/bold]")
|
|
387
|
+
console.print()
|
|
388
|
+
|
|
389
|
+
with console.status("[bold cyan]Analyzing project structure..."):
|
|
390
|
+
docs = generator.generate()
|
|
391
|
+
|
|
392
|
+
# Display results
|
|
393
|
+
console.print("[bold green]✓ Documentation generated successfully![/bold green]")
|
|
394
|
+
console.print()
|
|
395
|
+
|
|
396
|
+
# Show generated files
|
|
397
|
+
docs_path = Path(path) / "docs"
|
|
398
|
+
console.print("[bold]Generated Files:[/bold]")
|
|
399
|
+
for filename in sorted(docs.keys()):
|
|
400
|
+
filepath = docs_path / filename
|
|
401
|
+
if filepath.exists():
|
|
402
|
+
file_size = filepath.stat().st_size
|
|
403
|
+
console.print(f" [green]✓[/green] [cyan]{filename}[/cyan] ({file_size:,} bytes)")
|
|
404
|
+
|
|
405
|
+
console.print()
|
|
406
|
+
console.print(f"[dim]📁 Location: {docs_path.absolute()}[/dim]")
|
|
407
|
+
console.print()
|
|
408
|
+
|
|
409
|
+
# Display file descriptions
|
|
410
|
+
console.print("[bold]File Descriptions:[/bold]")
|
|
411
|
+
console.print()
|
|
412
|
+
|
|
413
|
+
descriptions = {
|
|
414
|
+
"README.md": "Project overview, features, and quick start guide",
|
|
415
|
+
"ARCHITECTURE.md": "Detailed architecture, module descriptions, and data flow diagrams",
|
|
416
|
+
"SETUP.md": "Installation instructions for different operating systems",
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
for filename, description in descriptions.items():
|
|
420
|
+
console.print(f" [yellow]•[/yellow] [bold]{filename}[/bold]")
|
|
421
|
+
console.print(f" [dim]{description}[/dim]")
|
|
422
|
+
|
|
423
|
+
console.print()
|
|
424
|
+
console.print("[bold cyan]Next Steps:[/bold cyan]")
|
|
425
|
+
console.print(f" • Review the generated documentation in: [cyan]{docs_path}[/cyan]")
|
|
426
|
+
console.print(" • Use the documentation for onboarding and reference")
|
|
427
|
+
console.print(" • Share with team members for project understanding")
|
|
428
|
+
|
|
429
|
+
|
|
430
|
+
if __name__ == '__main__':
|
|
431
|
+
cli()
|
src/docs/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Documentation generation module."""
|
|
Binary file
|
|
Binary file
|