codegraph-cli 2.0.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.
- codegraph_cli/__init__.py +4 -0
- codegraph_cli/agents.py +191 -0
- codegraph_cli/bug_detector.py +386 -0
- codegraph_cli/chat_agent.py +352 -0
- codegraph_cli/chat_session.py +220 -0
- codegraph_cli/cli.py +330 -0
- codegraph_cli/cli_chat.py +367 -0
- codegraph_cli/cli_diagnose.py +133 -0
- codegraph_cli/cli_refactor.py +230 -0
- codegraph_cli/cli_setup.py +470 -0
- codegraph_cli/cli_test.py +177 -0
- codegraph_cli/cli_v2.py +267 -0
- codegraph_cli/codegen_agent.py +265 -0
- codegraph_cli/config.py +31 -0
- codegraph_cli/config_manager.py +341 -0
- codegraph_cli/context_manager.py +500 -0
- codegraph_cli/crew_agents.py +123 -0
- codegraph_cli/crew_chat.py +159 -0
- codegraph_cli/crew_tools.py +497 -0
- codegraph_cli/diff_engine.py +265 -0
- codegraph_cli/embeddings.py +241 -0
- codegraph_cli/graph_export.py +144 -0
- codegraph_cli/llm.py +642 -0
- codegraph_cli/models.py +47 -0
- codegraph_cli/models_v2.py +185 -0
- codegraph_cli/orchestrator.py +49 -0
- codegraph_cli/parser.py +800 -0
- codegraph_cli/performance_analyzer.py +223 -0
- codegraph_cli/project_context.py +230 -0
- codegraph_cli/rag.py +200 -0
- codegraph_cli/refactor_agent.py +452 -0
- codegraph_cli/security_scanner.py +366 -0
- codegraph_cli/storage.py +390 -0
- codegraph_cli/templates/graph_interactive.html +257 -0
- codegraph_cli/testgen_agent.py +316 -0
- codegraph_cli/validation_engine.py +285 -0
- codegraph_cli/vector_store.py +293 -0
- codegraph_cli-2.0.0.dist-info/METADATA +318 -0
- codegraph_cli-2.0.0.dist-info/RECORD +43 -0
- codegraph_cli-2.0.0.dist-info/WHEEL +5 -0
- codegraph_cli-2.0.0.dist-info/entry_points.txt +2 -0
- codegraph_cli-2.0.0.dist-info/licenses/LICENSE +21 -0
- codegraph_cli-2.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"""CLI commands for error detection and fixing."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
import typer
|
|
8
|
+
|
|
9
|
+
from .diff_engine import DiffEngine
|
|
10
|
+
from .models_v2 import CodeProposal
|
|
11
|
+
from .storage import ProjectManager
|
|
12
|
+
from .validation_engine import ValidationEngine
|
|
13
|
+
|
|
14
|
+
# Create sub-app for diagnostic commands
|
|
15
|
+
diagnose_app = typer.Typer(help="Detect and fix code errors")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@diagnose_app.command("check")
|
|
19
|
+
def check_errors(
|
|
20
|
+
path: str = typer.Argument(".", help="Path to check (default: current directory)"),
|
|
21
|
+
):
|
|
22
|
+
"""Scan project for syntax errors."""
|
|
23
|
+
project_path = Path(path).resolve()
|
|
24
|
+
|
|
25
|
+
if not project_path.exists():
|
|
26
|
+
typer.echo(f"ā Path not found: {path}")
|
|
27
|
+
raise typer.Exit(1)
|
|
28
|
+
|
|
29
|
+
typer.echo(f"š Scanning {project_path} for errors...")
|
|
30
|
+
|
|
31
|
+
validator = ValidationEngine()
|
|
32
|
+
errors = validator.diagnose_project(project_path)
|
|
33
|
+
|
|
34
|
+
if not errors:
|
|
35
|
+
typer.echo("ā
No syntax errors found!")
|
|
36
|
+
return
|
|
37
|
+
|
|
38
|
+
typer.echo(f"\nš Found {len(errors)} error(s):\n")
|
|
39
|
+
|
|
40
|
+
for i, error in enumerate(errors, 1):
|
|
41
|
+
typer.echo(f"{i}. {error['file']}:{error['line']}")
|
|
42
|
+
typer.echo(f" {error['type']}: {error['error']}")
|
|
43
|
+
typer.echo("")
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@diagnose_app.command("fix")
|
|
47
|
+
def fix_errors(
|
|
48
|
+
path: str = typer.Argument(".", help="Path to fix (default: current directory)"),
|
|
49
|
+
preview_only: bool = typer.Option(False, "--preview", "-p", help="Preview fixes without applying"),
|
|
50
|
+
auto_apply: bool = typer.Option(False, "--auto-apply", "-y", help="Apply fixes without confirmation"),
|
|
51
|
+
):
|
|
52
|
+
"""Automatically fix common syntax errors."""
|
|
53
|
+
project_path = Path(path).resolve()
|
|
54
|
+
|
|
55
|
+
if not project_path.exists():
|
|
56
|
+
typer.echo(f"ā Path not found: {path}")
|
|
57
|
+
raise typer.Exit(1)
|
|
58
|
+
|
|
59
|
+
typer.echo(f"š§ Fixing errors in {project_path}...")
|
|
60
|
+
|
|
61
|
+
validator = ValidationEngine()
|
|
62
|
+
errors = validator.diagnose_project(project_path)
|
|
63
|
+
|
|
64
|
+
if not errors:
|
|
65
|
+
typer.echo("ā
No errors to fix!")
|
|
66
|
+
return
|
|
67
|
+
|
|
68
|
+
typer.echo(f"\nš Found {len(errors)} error(s), attempting fixes...\n")
|
|
69
|
+
|
|
70
|
+
# Try to fix each file
|
|
71
|
+
changes = []
|
|
72
|
+
for error in errors:
|
|
73
|
+
file_path = Path(error['file'])
|
|
74
|
+
|
|
75
|
+
typer.echo(f"š§ Fixing {file_path.name}:{error['line']}...")
|
|
76
|
+
typer.echo(f" Problem: {error['error']}")
|
|
77
|
+
|
|
78
|
+
fix = validator.fix_common_errors(file_path)
|
|
79
|
+
|
|
80
|
+
if fix:
|
|
81
|
+
changes.append(fix)
|
|
82
|
+
typer.echo(f" ā
Fix applied")
|
|
83
|
+
else:
|
|
84
|
+
typer.echo(f" ā ļø Could not auto-fix (manual intervention needed)")
|
|
85
|
+
typer.echo("")
|
|
86
|
+
|
|
87
|
+
if not changes:
|
|
88
|
+
typer.echo("ā No automatic fixes available. Manual fixes required.")
|
|
89
|
+
return
|
|
90
|
+
|
|
91
|
+
# Show preview
|
|
92
|
+
diff_engine = DiffEngine()
|
|
93
|
+
proposal = CodeProposal(
|
|
94
|
+
id="fix-errors",
|
|
95
|
+
description=f"Fix {len(changes)} syntax error(s)",
|
|
96
|
+
changes=changes
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
typer.echo("="*60)
|
|
100
|
+
typer.echo("PROPOSED FIXES")
|
|
101
|
+
typer.echo("="*60)
|
|
102
|
+
preview = diff_engine.preview_changes(proposal)
|
|
103
|
+
typer.echo(preview)
|
|
104
|
+
|
|
105
|
+
if preview_only:
|
|
106
|
+
typer.echo("\nš Preview only mode - no changes applied")
|
|
107
|
+
return
|
|
108
|
+
|
|
109
|
+
if not auto_apply:
|
|
110
|
+
apply = typer.confirm(f"\nā Apply {len(changes)} fix(es)?", default=False)
|
|
111
|
+
if not apply:
|
|
112
|
+
typer.echo("ā Fixes not applied")
|
|
113
|
+
return
|
|
114
|
+
|
|
115
|
+
# Apply fixes
|
|
116
|
+
typer.echo("\n⨠Applying fixes...")
|
|
117
|
+
result = diff_engine.apply_changes(proposal, backup=True)
|
|
118
|
+
|
|
119
|
+
if result.success:
|
|
120
|
+
typer.echo(f"ā
Successfully fixed {len(result.files_changed)} file(s)")
|
|
121
|
+
if result.backup_id:
|
|
122
|
+
typer.echo(f"š¾ Backup created: {result.backup_id}")
|
|
123
|
+
typer.echo(f" Rollback with: cg v2 rollback {result.backup_id}")
|
|
124
|
+
|
|
125
|
+
# Re-check for remaining errors
|
|
126
|
+
typer.echo("\nš Re-checking for errors...")
|
|
127
|
+
remaining = validator.diagnose_project(project_path)
|
|
128
|
+
if remaining:
|
|
129
|
+
typer.echo(f"ā ļø {len(remaining)} error(s) remain (require manual fixing)")
|
|
130
|
+
else:
|
|
131
|
+
typer.echo("ā
All errors fixed!")
|
|
132
|
+
else:
|
|
133
|
+
typer.echo(f"ā Failed to apply fixes: {result.error}")
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
"""CLI commands for refactoring operations."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import List, Optional
|
|
7
|
+
|
|
8
|
+
import typer
|
|
9
|
+
|
|
10
|
+
from . import config
|
|
11
|
+
from .diff_engine import DiffEngine
|
|
12
|
+
from .refactor_agent import RefactorAgent
|
|
13
|
+
from .storage import GraphStore, ProjectManager
|
|
14
|
+
|
|
15
|
+
# Create sub-app for refactor commands
|
|
16
|
+
refactor_app = typer.Typer(help="Safe refactoring with dependency tracking")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _get_refactor_agent(pm: ProjectManager) -> RefactorAgent:
|
|
20
|
+
"""Get RefactorAgent with current project context."""
|
|
21
|
+
project = pm.get_current_project()
|
|
22
|
+
if not project:
|
|
23
|
+
raise typer.BadParameter("No project loaded. Use 'cg load-project <name>' or run 'cg index <path>'.")
|
|
24
|
+
|
|
25
|
+
project_dir = pm.project_dir(project)
|
|
26
|
+
if not project_dir.exists():
|
|
27
|
+
raise typer.BadParameter(f"Loaded project '{project}' does not exist in memory.")
|
|
28
|
+
|
|
29
|
+
store = GraphStore(project_dir)
|
|
30
|
+
return RefactorAgent(store)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@refactor_app.command("rename")
|
|
34
|
+
def rename_symbol(
|
|
35
|
+
old_name: str = typer.Argument(..., help="Current symbol name"),
|
|
36
|
+
new_name: str = typer.Argument(..., help="New symbol name"),
|
|
37
|
+
preview_only: bool = typer.Option(False, "--preview", "-p", help="Preview changes without applying"),
|
|
38
|
+
auto_apply: bool = typer.Option(False, "--auto-apply", "-y", help="Apply changes without confirmation"),
|
|
39
|
+
):
|
|
40
|
+
"""Rename a symbol and update all references."""
|
|
41
|
+
pm = ProjectManager()
|
|
42
|
+
agent = _get_refactor_agent(pm)
|
|
43
|
+
|
|
44
|
+
typer.echo(f"š Renaming '{old_name}' to '{new_name}'...")
|
|
45
|
+
|
|
46
|
+
try:
|
|
47
|
+
plan = agent.rename_symbol(old_name, new_name)
|
|
48
|
+
except ValueError as e:
|
|
49
|
+
typer.echo(f"ā Error: {e}")
|
|
50
|
+
raise typer.Exit(1)
|
|
51
|
+
|
|
52
|
+
# Show refactoring plan
|
|
53
|
+
typer.echo(f"\nš Refactoring Plan: {plan.description}")
|
|
54
|
+
typer.echo(f" Files to modify: {len(plan.changes)}")
|
|
55
|
+
typer.echo(f" Call sites to update: {plan.num_call_sites}")
|
|
56
|
+
typer.echo("")
|
|
57
|
+
|
|
58
|
+
# Show changes
|
|
59
|
+
diff_engine = DiffEngine()
|
|
60
|
+
for change in plan.changes:
|
|
61
|
+
typer.echo(f"{'='*60}")
|
|
62
|
+
typer.echo(f"[MODIFY] {change.file_path}")
|
|
63
|
+
typer.echo(f"{'='*60}")
|
|
64
|
+
if change.diff:
|
|
65
|
+
typer.echo(change.diff)
|
|
66
|
+
typer.echo("")
|
|
67
|
+
|
|
68
|
+
if preview_only:
|
|
69
|
+
typer.echo("š Preview only mode - no changes applied")
|
|
70
|
+
return
|
|
71
|
+
|
|
72
|
+
if not auto_apply:
|
|
73
|
+
apply = typer.confirm("\nā Apply refactoring?", default=False)
|
|
74
|
+
if not apply:
|
|
75
|
+
typer.echo("ā Refactoring cancelled")
|
|
76
|
+
return
|
|
77
|
+
|
|
78
|
+
# Apply changes
|
|
79
|
+
typer.echo("\n⨠Applying refactoring...")
|
|
80
|
+
|
|
81
|
+
# Create a CodeProposal-like structure for applying
|
|
82
|
+
from .models_v2 import CodeProposal
|
|
83
|
+
proposal = CodeProposal(
|
|
84
|
+
id=str(plan.description),
|
|
85
|
+
description=plan.description,
|
|
86
|
+
changes=plan.changes
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
result = diff_engine.apply_changes(proposal, backup=True)
|
|
90
|
+
|
|
91
|
+
if result.success:
|
|
92
|
+
typer.echo(f"ā
Successfully refactored {len(result.files_changed)} file(s)")
|
|
93
|
+
if result.backup_id:
|
|
94
|
+
typer.echo(f"š¾ Backup created: {result.backup_id}")
|
|
95
|
+
typer.echo(f" Rollback with: cg v2 rollback {result.backup_id}")
|
|
96
|
+
else:
|
|
97
|
+
typer.echo(f"ā Failed to apply refactoring: {result.error}")
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
@refactor_app.command("extract-function")
|
|
101
|
+
def extract_function(
|
|
102
|
+
file_path: str = typer.Argument(..., help="File containing code to extract"),
|
|
103
|
+
start_line: int = typer.Argument(..., help="Start line number"),
|
|
104
|
+
end_line: int = typer.Argument(..., help="End line number"),
|
|
105
|
+
function_name: str = typer.Argument(..., help="Name for the new function"),
|
|
106
|
+
preview_only: bool = typer.Option(False, "--preview", "-p", help="Preview changes without applying"),
|
|
107
|
+
auto_apply: bool = typer.Option(False, "--auto-apply", "-y", help="Apply changes without confirmation"),
|
|
108
|
+
):
|
|
109
|
+
"""Extract code range into a new function."""
|
|
110
|
+
pm = ProjectManager()
|
|
111
|
+
agent = _get_refactor_agent(pm)
|
|
112
|
+
|
|
113
|
+
typer.echo(f"š¤ Extracting lines {start_line}-{end_line} to function '{function_name}'...")
|
|
114
|
+
|
|
115
|
+
try:
|
|
116
|
+
plan = agent.extract_function(file_path, start_line, end_line, function_name)
|
|
117
|
+
except ValueError as e:
|
|
118
|
+
typer.echo(f"ā Error: {e}")
|
|
119
|
+
raise typer.Exit(1)
|
|
120
|
+
|
|
121
|
+
# Show plan
|
|
122
|
+
typer.echo(f"\nš Refactoring Plan: {plan.description}")
|
|
123
|
+
typer.echo("")
|
|
124
|
+
|
|
125
|
+
# Show changes
|
|
126
|
+
diff_engine = DiffEngine()
|
|
127
|
+
for change in plan.changes:
|
|
128
|
+
typer.echo(f"{'='*60}")
|
|
129
|
+
typer.echo(f"[MODIFY] {change.file_path}")
|
|
130
|
+
typer.echo(f"{'='*60}")
|
|
131
|
+
if change.diff:
|
|
132
|
+
typer.echo(change.diff)
|
|
133
|
+
typer.echo("")
|
|
134
|
+
|
|
135
|
+
if preview_only:
|
|
136
|
+
typer.echo("š Preview only mode - no changes applied")
|
|
137
|
+
return
|
|
138
|
+
|
|
139
|
+
if not auto_apply:
|
|
140
|
+
apply = typer.confirm("\nā Apply refactoring?", default=False)
|
|
141
|
+
if not apply:
|
|
142
|
+
typer.echo("ā Refactoring cancelled")
|
|
143
|
+
return
|
|
144
|
+
|
|
145
|
+
# Apply changes
|
|
146
|
+
typer.echo("\n⨠Applying refactoring...")
|
|
147
|
+
|
|
148
|
+
from .models_v2 import CodeProposal
|
|
149
|
+
proposal = CodeProposal(
|
|
150
|
+
id=str(plan.description),
|
|
151
|
+
description=plan.description,
|
|
152
|
+
changes=plan.changes
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
result = diff_engine.apply_changes(proposal, backup=True)
|
|
156
|
+
|
|
157
|
+
if result.success:
|
|
158
|
+
typer.echo(f"ā
Successfully refactored {len(result.files_changed)} file(s)")
|
|
159
|
+
if result.backup_id:
|
|
160
|
+
typer.echo(f"š¾ Backup created: {result.backup_id}")
|
|
161
|
+
else:
|
|
162
|
+
typer.echo(f"ā Failed to apply refactoring: {result.error}")
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
@refactor_app.command("extract-service")
|
|
166
|
+
def extract_service(
|
|
167
|
+
symbols: List[str] = typer.Argument(..., help="Function names to extract (space-separated)"),
|
|
168
|
+
target_file: str = typer.Option(..., "--target", "-t", help="Target service file path"),
|
|
169
|
+
preview_only: bool = typer.Option(False, "--preview", "-p", help="Preview changes without applying"),
|
|
170
|
+
auto_apply: bool = typer.Option(False, "--auto-apply", "-y", help="Apply changes without confirmation"),
|
|
171
|
+
):
|
|
172
|
+
"""Extract multiple functions to a new service file."""
|
|
173
|
+
pm = ProjectManager()
|
|
174
|
+
agent = _get_refactor_agent(pm)
|
|
175
|
+
|
|
176
|
+
typer.echo(f"š¤ Extracting {len(symbols)} function(s) to {target_file}...")
|
|
177
|
+
|
|
178
|
+
try:
|
|
179
|
+
plan = agent.extract_service(symbols, target_file)
|
|
180
|
+
except ValueError as e:
|
|
181
|
+
typer.echo(f"ā Error: {e}")
|
|
182
|
+
raise typer.Exit(1)
|
|
183
|
+
|
|
184
|
+
# Show plan
|
|
185
|
+
typer.echo(f"\nš Refactoring Plan: {plan.description}")
|
|
186
|
+
typer.echo(f" Files to create: {sum(1 for c in plan.changes if c.change_type == 'create')}")
|
|
187
|
+
typer.echo(f" Files to modify: {sum(1 for c in plan.changes if c.change_type == 'modify')}")
|
|
188
|
+
typer.echo(f" Call sites to update: {plan.num_call_sites}")
|
|
189
|
+
typer.echo("")
|
|
190
|
+
|
|
191
|
+
# Show changes
|
|
192
|
+
diff_engine = DiffEngine()
|
|
193
|
+
for change in plan.changes:
|
|
194
|
+
typer.echo(f"{'='*60}")
|
|
195
|
+
typer.echo(f"[{change.change_type.upper()}] {change.file_path}")
|
|
196
|
+
typer.echo(f"{'='*60}")
|
|
197
|
+
if change.change_type == "create":
|
|
198
|
+
typer.echo(change.new_content[:500] + "..." if len(change.new_content or "") > 500 else change.new_content)
|
|
199
|
+
elif change.diff:
|
|
200
|
+
typer.echo(change.diff)
|
|
201
|
+
typer.echo("")
|
|
202
|
+
|
|
203
|
+
if preview_only:
|
|
204
|
+
typer.echo("š Preview only mode - no changes applied")
|
|
205
|
+
return
|
|
206
|
+
|
|
207
|
+
if not auto_apply:
|
|
208
|
+
apply = typer.confirm("\nā Apply refactoring?", default=False)
|
|
209
|
+
if not apply:
|
|
210
|
+
typer.echo("ā Refactoring cancelled")
|
|
211
|
+
return
|
|
212
|
+
|
|
213
|
+
# Apply changes
|
|
214
|
+
typer.echo("\n⨠Applying refactoring...")
|
|
215
|
+
|
|
216
|
+
from .models_v2 import CodeProposal
|
|
217
|
+
proposal = CodeProposal(
|
|
218
|
+
id=str(plan.description),
|
|
219
|
+
description=plan.description,
|
|
220
|
+
changes=plan.changes
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
result = diff_engine.apply_changes(proposal, backup=True)
|
|
224
|
+
|
|
225
|
+
if result.success:
|
|
226
|
+
typer.echo(f"ā
Successfully refactored {len(result.files_changed)} file(s)")
|
|
227
|
+
if result.backup_id:
|
|
228
|
+
typer.echo(f"š¾ Backup created: {result.backup_id}")
|
|
229
|
+
else:
|
|
230
|
+
typer.echo(f"ā Failed to apply refactoring: {result.error}")
|