codegraph-cli 2.1.1__py3-none-any.whl → 2.1.2__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 +1 -1
- codegraph_cli/agents.py +59 -3
- codegraph_cli/chat_agent.py +58 -11
- codegraph_cli/cli.py +569 -54
- codegraph_cli/cli_chat.py +200 -95
- codegraph_cli/cli_diagnose.py +13 -2
- codegraph_cli/cli_docs.py +207 -0
- codegraph_cli/cli_explore.py +1053 -0
- codegraph_cli/cli_export.py +941 -0
- codegraph_cli/cli_groups.py +33 -0
- codegraph_cli/cli_health.py +316 -0
- codegraph_cli/cli_history.py +213 -0
- codegraph_cli/cli_onboard.py +380 -0
- codegraph_cli/cli_quickstart.py +256 -0
- codegraph_cli/cli_refactor.py +17 -3
- codegraph_cli/cli_setup.py +12 -12
- codegraph_cli/cli_suggestions.py +90 -0
- codegraph_cli/cli_test.py +17 -3
- codegraph_cli/cli_tui.py +210 -0
- codegraph_cli/cli_v2.py +24 -4
- codegraph_cli/cli_watch.py +158 -0
- codegraph_cli/cli_workflows.py +255 -0
- codegraph_cli/codegen_agent.py +15 -1
- codegraph_cli/config.py +18 -5
- codegraph_cli/context_manager.py +117 -15
- codegraph_cli/crew_agents.py +26 -7
- codegraph_cli/crew_chat.py +141 -12
- codegraph_cli/crew_tools.py +21 -1
- codegraph_cli/embeddings.py +95 -5
- codegraph_cli/llm.py +42 -55
- codegraph_cli/project_context.py +64 -1
- codegraph_cli/rag.py +282 -19
- codegraph_cli/storage.py +310 -14
- codegraph_cli/vector_store.py +110 -8
- {codegraph_cli-2.1.1.dist-info → codegraph_cli-2.1.2.dist-info}/METADATA +35 -24
- codegraph_cli-2.1.2.dist-info/RECORD +55 -0
- codegraph_cli-2.1.2.dist-info/entry_points.txt +2 -0
- codegraph_cli-2.1.1.dist-info/RECORD +0 -43
- codegraph_cli-2.1.1.dist-info/entry_points.txt +0 -2
- {codegraph_cli-2.1.1.dist-info → codegraph_cli-2.1.2.dist-info}/WHEEL +0 -0
- {codegraph_cli-2.1.1.dist-info → codegraph_cli-2.1.2.dist-info}/licenses/LICENSE +0 -0
- {codegraph_cli-2.1.1.dist-info → codegraph_cli-2.1.2.dist-info}/top_level.txt +0 -0
codegraph_cli/cli_chat.py
CHANGED
|
@@ -2,13 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
import os
|
|
6
5
|
import shutil
|
|
7
6
|
from datetime import datetime
|
|
8
7
|
from pathlib import Path
|
|
9
8
|
from typing import Optional
|
|
10
9
|
|
|
11
10
|
import typer
|
|
11
|
+
from rich.console import Console
|
|
12
|
+
from rich.panel import Panel
|
|
13
|
+
from rich.rule import Rule
|
|
14
|
+
from rich.table import Table
|
|
15
|
+
from rich.text import Text
|
|
12
16
|
|
|
13
17
|
from . import config
|
|
14
18
|
from .chat_agent import ChatAgent
|
|
@@ -18,19 +22,7 @@ from .orchestrator import MCPOrchestrator
|
|
|
18
22
|
from .rag import RAGRetriever
|
|
19
23
|
from .storage import GraphStore, ProjectManager
|
|
20
24
|
|
|
21
|
-
|
|
22
|
-
# ── Theme colors ──────────────────────────────────────────────
|
|
23
|
-
C_RESET = "\033[0m"
|
|
24
|
-
C_BOLD = "\033[1m"
|
|
25
|
-
C_DIM = "\033[2m"
|
|
26
|
-
C_CYAN = "\033[36m"
|
|
27
|
-
C_GREEN = "\033[32m"
|
|
28
|
-
C_YELLOW = "\033[33m"
|
|
29
|
-
C_MAGENTA = "\033[35m"
|
|
30
|
-
C_RED = "\033[31m"
|
|
31
|
-
C_BLUE = "\033[34m"
|
|
32
|
-
C_WHITE = "\033[97m"
|
|
33
|
-
C_BG_DARK = "\033[48;5;235m"
|
|
25
|
+
console = Console()
|
|
34
26
|
|
|
35
27
|
|
|
36
28
|
def _term_width() -> int:
|
|
@@ -38,45 +30,40 @@ def _term_width() -> int:
|
|
|
38
30
|
return shutil.get_terminal_size((80, 24)).columns
|
|
39
31
|
|
|
40
32
|
|
|
41
|
-
def
|
|
42
|
-
"""
|
|
43
|
-
|
|
44
|
-
inner = w - 4
|
|
45
|
-
lines = text.split("\n")
|
|
46
|
-
out = [f"{color}╭{'─' * (w - 2)}╮{C_RESET}"]
|
|
47
|
-
for line in lines:
|
|
48
|
-
padded = line.ljust(inner)[:inner]
|
|
49
|
-
out.append(f"{color}│{C_RESET} {padded} {color}│{C_RESET}")
|
|
50
|
-
out.append(f"{color}╰{'─' * (w - 2)}╯{C_RESET}")
|
|
51
|
-
return "\n".join(out)
|
|
33
|
+
def _print_welcome(project_name: str, use_crew: bool, provider: str, model: str, auto_context: dict | None = None):
|
|
34
|
+
"""Print the modern welcome banner."""
|
|
35
|
+
mode = "CrewAI Multi-Agent" if use_crew else "Chat"
|
|
52
36
|
|
|
37
|
+
console.print()
|
|
38
|
+
console.print(Panel(f"⚡ CodeGraph {mode}", style="bold cyan", width=min(_term_width(), 70)))
|
|
39
|
+
console.print(f" [dim]Project[/dim] [white]{project_name}[/white]")
|
|
40
|
+
console.print(f" [dim]LLM[/dim] [white]{provider}/{model}[/white]")
|
|
53
41
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
42
|
+
# Show auto-context summary
|
|
43
|
+
if auto_context:
|
|
44
|
+
summary = auto_context.get("summary", {})
|
|
45
|
+
indexed_files = summary.get("indexed_files", 0)
|
|
46
|
+
total_nodes = summary.get("total_nodes", 0)
|
|
47
|
+
node_types = summary.get("node_types", {})
|
|
48
|
+
if indexed_files or total_nodes:
|
|
49
|
+
funcs = node_types.get("function", 0)
|
|
50
|
+
classes = node_types.get("class", 0)
|
|
51
|
+
console.print(f" [dim]Indexed[/dim] [white]{indexed_files} files • {funcs} functions • {classes} classes[/white]")
|
|
52
|
+
recent = auto_context.get("recent_files", [])
|
|
53
|
+
if recent:
|
|
54
|
+
console.print(f" [dim]Recent[/dim] [white]{', '.join(recent[:3])}[/white]")
|
|
57
55
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
mode = "CrewAI Multi-Agent" if use_crew else "Chat"
|
|
62
|
-
|
|
63
|
-
banner = (
|
|
64
|
-
f" {C_BOLD}{C_CYAN}⚡ CodeGraph {mode}{C_RESET}\n"
|
|
65
|
-
f" {C_DIM}Project: {C_WHITE}{project_name}{C_RESET}\n"
|
|
66
|
-
f" {C_DIM}LLM: {C_WHITE}{provider}/{model}{C_RESET}"
|
|
67
|
-
)
|
|
68
|
-
print(f"\n{_box(f'⚡ CodeGraph {mode}', C_CYAN)}")
|
|
69
|
-
print(f" {C_DIM}Project {C_RESET}{C_WHITE}{project_name}{C_RESET}")
|
|
70
|
-
print(f" {C_DIM}LLM {C_RESET}{C_WHITE}{provider}/{model}{C_RESET}")
|
|
71
|
-
print(f" {C_DIM}Type {C_YELLOW}/help{C_DIM} for commands, {C_YELLOW}/exit{C_DIM} to quit{C_RESET}")
|
|
72
|
-
print(_divider())
|
|
73
|
-
print()
|
|
56
|
+
console.print(" [dim]Type [yellow]/help[/yellow] for commands, [yellow]/exit[/yellow] to quit[/dim]")
|
|
57
|
+
console.print(Rule(style="dim"))
|
|
58
|
+
console.print()
|
|
74
59
|
|
|
75
60
|
|
|
76
61
|
def _print_help(use_crew: bool):
|
|
77
62
|
"""Print help with styled formatting."""
|
|
78
|
-
print(
|
|
79
|
-
|
|
63
|
+
console.print()
|
|
64
|
+
table = Table(show_header=False, box=None, padding=(0, 2), title="📖 Commands", title_style="bold cyan")
|
|
65
|
+
table.add_column(style="yellow", min_width=22)
|
|
66
|
+
table.add_column(style="dim")
|
|
80
67
|
cmds = [
|
|
81
68
|
("/exit", "Exit chat session"),
|
|
82
69
|
("/clear", "Clear conversation history & start fresh"),
|
|
@@ -95,22 +82,23 @@ def _print_help(use_crew: bool):
|
|
|
95
82
|
("/preview", "Preview pending changes"),
|
|
96
83
|
])
|
|
97
84
|
for cmd, desc in cmds:
|
|
98
|
-
|
|
99
|
-
print()
|
|
85
|
+
table.add_row(cmd, desc)
|
|
86
|
+
console.print(table)
|
|
87
|
+
console.print()
|
|
100
88
|
|
|
101
89
|
|
|
102
90
|
def _print_response(text: str):
|
|
103
91
|
"""Print assistant response with styling."""
|
|
104
|
-
print(
|
|
105
|
-
|
|
92
|
+
console.print()
|
|
93
|
+
console.print(" [green]●[/green] [bold]Assistant[/bold]")
|
|
106
94
|
for line in text.split("\n"):
|
|
107
|
-
print(f" {line}")
|
|
108
|
-
print()
|
|
95
|
+
console.print(f" {line}", highlight=False)
|
|
96
|
+
console.print()
|
|
109
97
|
|
|
110
98
|
|
|
111
|
-
def _print_status(emoji: str, msg: str,
|
|
99
|
+
def _print_status(emoji: str, msg: str, style: str = "green"):
|
|
112
100
|
"""Print a status message."""
|
|
113
|
-
print(f" {
|
|
101
|
+
console.print(f" [{style}]{emoji} {msg}[/{style}]")
|
|
114
102
|
|
|
115
103
|
|
|
116
104
|
def start_chat_repl(
|
|
@@ -121,13 +109,14 @@ def start_chat_repl(
|
|
|
121
109
|
use_crew: bool = False,
|
|
122
110
|
provider: str = "",
|
|
123
111
|
model: str = "",
|
|
112
|
+
auto_context: dict | None = None,
|
|
124
113
|
):
|
|
125
114
|
"""Start interactive REPL for chat."""
|
|
126
115
|
# Load or create session
|
|
127
116
|
if session_id:
|
|
128
117
|
session = session_manager.load_session(session_id)
|
|
129
118
|
if not session:
|
|
130
|
-
_print_status("🆕", "Session not found. Starting new session.",
|
|
119
|
+
_print_status("🆕", "Session not found. Starting new session.", "yellow")
|
|
131
120
|
session = session_manager.create_session(project_name)
|
|
132
121
|
else:
|
|
133
122
|
_print_status("📂", f"Resumed session ({session.message_count} messages)")
|
|
@@ -141,17 +130,21 @@ def start_chat_repl(
|
|
|
141
130
|
session = session_manager.create_session(project_name)
|
|
142
131
|
_print_status("🆕", "Started new chat session")
|
|
143
132
|
|
|
133
|
+
# Hydrate agent with session history (enables crew continuity across restarts)
|
|
134
|
+
if hasattr(agent, 'load_session_history') and session.messages:
|
|
135
|
+
agent.load_session_history(session)
|
|
136
|
+
|
|
144
137
|
# Welcome
|
|
145
|
-
_print_welcome(project_name, use_crew, provider, model)
|
|
138
|
+
_print_welcome(project_name, use_crew, provider, model, auto_context=auto_context)
|
|
146
139
|
|
|
147
140
|
# REPL loop
|
|
148
141
|
while True:
|
|
149
142
|
try:
|
|
150
143
|
# Prompt
|
|
151
144
|
try:
|
|
152
|
-
user_input = input(
|
|
145
|
+
user_input = console.input(" [blue]●[/blue] [bold]You ›[/bold] ").strip()
|
|
153
146
|
except EOFError:
|
|
154
|
-
print(
|
|
147
|
+
console.print("\n [dim]👋 Goodbye! Session saved.[/dim]\n")
|
|
155
148
|
break
|
|
156
149
|
|
|
157
150
|
if not user_input:
|
|
@@ -163,22 +156,28 @@ def start_chat_repl(
|
|
|
163
156
|
|
|
164
157
|
if cmd == "/exit":
|
|
165
158
|
session_manager.save_session(session)
|
|
166
|
-
print(
|
|
159
|
+
console.print("\n [dim]👋 Goodbye! Session saved.[/dim]\n")
|
|
167
160
|
break
|
|
168
161
|
|
|
169
162
|
elif cmd == "/clear":
|
|
170
163
|
session.clear_history()
|
|
171
164
|
session.clear_proposals()
|
|
172
165
|
session_manager.save_session(session)
|
|
173
|
-
|
|
174
|
-
|
|
166
|
+
# Clear agent-side memory (crew mode)
|
|
167
|
+
if hasattr(agent, 'clear_history'):
|
|
168
|
+
agent.clear_history()
|
|
169
|
+
_print_status("🧹", "Conversation cleared. Fresh start!")
|
|
170
|
+
console.print()
|
|
175
171
|
continue
|
|
176
172
|
|
|
177
173
|
elif cmd == "/new":
|
|
178
174
|
session_manager.save_session(session)
|
|
179
175
|
session = session_manager.create_session(project_name)
|
|
180
|
-
|
|
181
|
-
|
|
176
|
+
# Clear agent-side memory (crew mode)
|
|
177
|
+
if hasattr(agent, 'clear_history'):
|
|
178
|
+
agent.clear_history()
|
|
179
|
+
_print_status("🆕", "New session started.")
|
|
180
|
+
console.print()
|
|
182
181
|
continue
|
|
183
182
|
|
|
184
183
|
elif cmd == "/help":
|
|
@@ -191,57 +190,57 @@ def start_chat_repl(
|
|
|
191
190
|
result = agent.apply_pending_proposal(session)
|
|
192
191
|
_print_status("📋", result)
|
|
193
192
|
else:
|
|
194
|
-
_print_status("📋", "No pending proposals.",
|
|
193
|
+
_print_status("📋", "No pending proposals.", "yellow")
|
|
195
194
|
continue
|
|
196
195
|
elif cmd == "/preview":
|
|
197
196
|
if session.pending_proposals:
|
|
198
197
|
for i, prop in enumerate(session.pending_proposals):
|
|
199
|
-
print(f"\n
|
|
198
|
+
console.print(f"\n [bold]Proposal {i+1}:[/bold] {prop.description}")
|
|
200
199
|
for ch in prop.changes:
|
|
201
200
|
icon = {"create": "🆕", "modify": "✏️", "delete": "🗑️"}.get(ch.change_type, "📄")
|
|
202
|
-
print(f" {icon} {ch.file_path}")
|
|
201
|
+
console.print(f" {icon} {ch.file_path}")
|
|
203
202
|
else:
|
|
204
|
-
_print_status("📋", "No pending proposals.",
|
|
205
|
-
print()
|
|
203
|
+
_print_status("📋", "No pending proposals.", "yellow")
|
|
204
|
+
console.print()
|
|
206
205
|
continue
|
|
207
206
|
|
|
208
207
|
elif use_crew:
|
|
209
208
|
if cmd in ("/rollback", "/undo"):
|
|
210
209
|
parts = user_input.split(maxsplit=1)
|
|
211
210
|
if len(parts) < 2:
|
|
212
|
-
_print_status("❓", "Usage: /rollback <file_path> [timestamp]",
|
|
211
|
+
_print_status("❓", "Usage: /rollback <file_path> [timestamp]", "yellow")
|
|
213
212
|
continue
|
|
214
213
|
args = parts[1].split()
|
|
215
214
|
file_path = args[0]
|
|
216
215
|
ts = args[1] if len(args) > 1 else None
|
|
217
216
|
result = agent.rollback(file_path, ts)
|
|
218
217
|
_print_status("⏪", result)
|
|
219
|
-
print()
|
|
218
|
+
console.print()
|
|
220
219
|
continue
|
|
221
220
|
|
|
222
221
|
elif cmd == "/backups":
|
|
223
222
|
backups = agent.list_all_backups()
|
|
224
223
|
if not backups:
|
|
225
|
-
_print_status("📦", "No backups found.",
|
|
224
|
+
_print_status("📦", "No backups found.", "yellow")
|
|
226
225
|
else:
|
|
227
|
-
print(
|
|
228
|
-
print(
|
|
226
|
+
console.print("\n [bold cyan]📦 File Backups[/bold cyan]")
|
|
227
|
+
console.print(Rule(style="dim"))
|
|
229
228
|
for b in backups:
|
|
230
229
|
ts = b["timestamp"]
|
|
231
230
|
fp = b["original_path"]
|
|
232
|
-
print(f" {
|
|
233
|
-
print(
|
|
234
|
-
print()
|
|
231
|
+
console.print(f" [white]{ts}[/white] [dim]{fp}[/dim]")
|
|
232
|
+
console.print("\n [dim]Use /rollback <file_path> to restore[/dim]")
|
|
233
|
+
console.print()
|
|
235
234
|
continue
|
|
236
235
|
|
|
237
|
-
_print_status("❓", f"Unknown command: {cmd}. Type /help",
|
|
236
|
+
_print_status("❓", f"Unknown command: {cmd}. Type /help", "yellow")
|
|
238
237
|
continue
|
|
239
238
|
|
|
240
239
|
# ── Process message ──────────────────────────────
|
|
241
240
|
session.add_message("user", user_input, datetime.now().isoformat())
|
|
242
241
|
|
|
243
242
|
# Show thinking indicator
|
|
244
|
-
print(
|
|
243
|
+
console.print("\n [dim]⏳ Thinking...[/dim]", end="")
|
|
245
244
|
|
|
246
245
|
if use_crew:
|
|
247
246
|
response = agent.process_message(user_input)
|
|
@@ -249,7 +248,7 @@ def start_chat_repl(
|
|
|
249
248
|
response = agent.process_message(user_input, session)
|
|
250
249
|
|
|
251
250
|
# Clear thinking indicator
|
|
252
|
-
print(f"\r{' ' * 30}\r", end="")
|
|
251
|
+
console.print(f"\r{' ' * 30}\r", end="")
|
|
253
252
|
|
|
254
253
|
# Save & display
|
|
255
254
|
session.add_message("assistant", response, datetime.now().isoformat())
|
|
@@ -259,16 +258,46 @@ def start_chat_repl(
|
|
|
259
258
|
|
|
260
259
|
except KeyboardInterrupt:
|
|
261
260
|
session_manager.save_session(session)
|
|
262
|
-
print(
|
|
261
|
+
console.print("\n\n [dim]👋 Goodbye! Session saved.[/dim]\n")
|
|
263
262
|
break
|
|
264
263
|
except Exception as e:
|
|
265
|
-
print(f"\n
|
|
264
|
+
console.print(f"\n [red]❌ Error: {str(e)}[/red]\n")
|
|
266
265
|
|
|
267
266
|
|
|
268
267
|
# ── Typer app ────────────────────────────────────────────────
|
|
269
268
|
chat_app = typer.Typer(help="💬 Interactive chat with AI agents")
|
|
270
269
|
|
|
271
270
|
|
|
271
|
+
def _gather_auto_context(project_context) -> dict:
|
|
272
|
+
"""Gather automatic context from the project for chat enrichment.
|
|
273
|
+
|
|
274
|
+
Collects project summary (file count, symbol count, node types)
|
|
275
|
+
and recently modified files so the chat session starts with
|
|
276
|
+
useful awareness of the codebase.
|
|
277
|
+
"""
|
|
278
|
+
auto_ctx: dict = {}
|
|
279
|
+
|
|
280
|
+
try:
|
|
281
|
+
summary = project_context.get_project_summary()
|
|
282
|
+
auto_ctx["summary"] = summary
|
|
283
|
+
except Exception:
|
|
284
|
+
auto_ctx["summary"] = {}
|
|
285
|
+
|
|
286
|
+
# Recently modified source files
|
|
287
|
+
try:
|
|
288
|
+
if project_context.has_source_access:
|
|
289
|
+
items = project_context.list_directory(".")
|
|
290
|
+
files = [f for f in items if f["type"] == "file"]
|
|
291
|
+
files.sort(key=lambda f: f.get("modified", ""), reverse=True)
|
|
292
|
+
auto_ctx["recent_files"] = [f["name"] for f in files[:5]]
|
|
293
|
+
else:
|
|
294
|
+
auto_ctx["recent_files"] = []
|
|
295
|
+
except Exception:
|
|
296
|
+
auto_ctx["recent_files"] = []
|
|
297
|
+
|
|
298
|
+
return auto_ctx
|
|
299
|
+
|
|
300
|
+
|
|
272
301
|
@chat_app.command("start")
|
|
273
302
|
def start_chat(
|
|
274
303
|
session_id: Optional[str] = typer.Option(None, "--session", "-s", help="Resume specific session ID"),
|
|
@@ -279,7 +308,14 @@ def start_chat(
|
|
|
279
308
|
use_crew: bool = typer.Option(False, "--crew", help="Use CrewAI multi-agent system"),
|
|
280
309
|
new_session: bool = typer.Option(False, "--new", "-n", help="Force start a new session"),
|
|
281
310
|
):
|
|
282
|
-
"""Start interactive chat session.
|
|
311
|
+
"""💬 Start interactive chat session.
|
|
312
|
+
|
|
313
|
+
Example:
|
|
314
|
+
cg chat start
|
|
315
|
+
cg chat start --crew
|
|
316
|
+
cg chat start --new
|
|
317
|
+
cg chat start --session abc123
|
|
318
|
+
"""
|
|
283
319
|
from .embeddings import get_embedder
|
|
284
320
|
from .project_context import ProjectContext
|
|
285
321
|
|
|
@@ -287,8 +323,8 @@ def start_chat(
|
|
|
287
323
|
project = pm.get_current_project()
|
|
288
324
|
|
|
289
325
|
if not project:
|
|
290
|
-
print(
|
|
291
|
-
print(
|
|
326
|
+
console.print("\n [red]❌ No project loaded.[/red]")
|
|
327
|
+
console.print(" [dim]Use: cg load-project <name> or cg index <path>[/dim]\n")
|
|
292
328
|
raise typer.Exit(1)
|
|
293
329
|
|
|
294
330
|
# Initialize components
|
|
@@ -301,10 +337,10 @@ def start_chat(
|
|
|
301
337
|
try:
|
|
302
338
|
from .crew_chat import CrewChatAgent
|
|
303
339
|
except ImportError:
|
|
304
|
-
print(
|
|
305
|
-
print(
|
|
340
|
+
console.print("\n [red]CrewAI is not installed.[/red]")
|
|
341
|
+
console.print(" [dim]Install with: pip install codegraph-cli\\[crew][/dim]\n")
|
|
306
342
|
raise typer.Exit(1)
|
|
307
|
-
print(
|
|
343
|
+
console.print("\n [magenta]Initializing CrewAI multi-agent system...[/magenta]")
|
|
308
344
|
agent = CrewChatAgent(context, llm, rag_retriever)
|
|
309
345
|
else:
|
|
310
346
|
orchestrator = MCPOrchestrator(
|
|
@@ -318,6 +354,9 @@ def start_chat(
|
|
|
318
354
|
|
|
319
355
|
session_manager = SessionManager()
|
|
320
356
|
|
|
357
|
+
# Gather auto-context from the project for enriched chat experience
|
|
358
|
+
auto_context = _gather_auto_context(context)
|
|
359
|
+
|
|
321
360
|
# Force new session if requested
|
|
322
361
|
effective_session_id = None if new_session else session_id
|
|
323
362
|
|
|
@@ -327,6 +366,7 @@ def start_chat(
|
|
|
327
366
|
use_crew=use_crew,
|
|
328
367
|
provider=llm_provider,
|
|
329
368
|
model=llm_model,
|
|
369
|
+
auto_context=auto_context,
|
|
330
370
|
)
|
|
331
371
|
finally:
|
|
332
372
|
context.close()
|
|
@@ -336,16 +376,21 @@ def start_chat(
|
|
|
336
376
|
def list_sessions(
|
|
337
377
|
project: Optional[str] = typer.Option(None, "--project", "-p", help="Filter by project")
|
|
338
378
|
):
|
|
339
|
-
"""List all chat sessions.
|
|
379
|
+
"""📋 List all chat sessions.
|
|
380
|
+
|
|
381
|
+
Example:
|
|
382
|
+
cg chat list
|
|
383
|
+
cg chat list --project my-api
|
|
384
|
+
"""
|
|
340
385
|
session_manager = SessionManager()
|
|
341
386
|
sessions = session_manager.list_sessions(project_name=project)
|
|
342
387
|
|
|
343
388
|
if not sessions:
|
|
344
|
-
print(
|
|
389
|
+
console.print("\n [dim]No chat sessions found.[/dim]\n")
|
|
345
390
|
return
|
|
346
391
|
|
|
347
|
-
print(f"\n
|
|
348
|
-
print(
|
|
392
|
+
console.print(f"\n [bold cyan]📋 Chat Sessions ({len(sessions)})[/bold cyan]")
|
|
393
|
+
console.print(Rule(style="dim"))
|
|
349
394
|
|
|
350
395
|
for i, sess in enumerate(sessions, 1):
|
|
351
396
|
created = sess['created_at'][:16].replace("T", " ")
|
|
@@ -353,20 +398,80 @@ def list_sessions(
|
|
|
353
398
|
proj = sess['project_name']
|
|
354
399
|
sid = sess['id'][:8]
|
|
355
400
|
|
|
356
|
-
print(f" {
|
|
401
|
+
console.print(f" [white]{i}.[/white] [bold]{proj}[/bold] [dim]({msgs} msgs, {created})[/dim] [dim]id:{sid}…[/dim]")
|
|
357
402
|
|
|
358
|
-
print()
|
|
403
|
+
console.print()
|
|
359
404
|
|
|
360
405
|
|
|
361
406
|
@chat_app.command("delete")
|
|
362
407
|
def delete_session(
|
|
363
408
|
session_id: str = typer.Argument(..., help="Session ID to delete")
|
|
364
409
|
):
|
|
365
|
-
"""Delete a chat session.
|
|
410
|
+
"""🗑️ Delete a chat session.
|
|
411
|
+
|
|
412
|
+
Example:
|
|
413
|
+
cg chat delete abc12345
|
|
414
|
+
"""
|
|
366
415
|
session_manager = SessionManager()
|
|
367
416
|
|
|
368
417
|
if session_manager.delete_session(session_id):
|
|
369
418
|
_print_status("✅", f"Deleted session {session_id[:8]}…")
|
|
370
419
|
else:
|
|
371
|
-
print(
|
|
420
|
+
console.print(" [red]❌ Session not found[/red]")
|
|
372
421
|
raise typer.Exit(1)
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
@chat_app.command("export")
|
|
425
|
+
def export_session(
|
|
426
|
+
session_id: str = typer.Argument(..., help="Session ID to export"),
|
|
427
|
+
fmt: str = typer.Option("markdown", "--format", "-f", help="Export format: markdown, json"),
|
|
428
|
+
output: Optional[str] = typer.Option(None, "--output", "-o", help="Output file path"),
|
|
429
|
+
):
|
|
430
|
+
"""📤 Export chat session to a file.
|
|
431
|
+
|
|
432
|
+
Example:
|
|
433
|
+
cg chat export abc12345
|
|
434
|
+
cg chat export abc12345 --format json
|
|
435
|
+
cg chat export abc12345 --output conversation.md
|
|
436
|
+
"""
|
|
437
|
+
import json as _json
|
|
438
|
+
|
|
439
|
+
session_manager = SessionManager()
|
|
440
|
+
session = session_manager.load_session(session_id)
|
|
441
|
+
|
|
442
|
+
if not session:
|
|
443
|
+
console.print(f" [red]❌ Session '{session_id}' not found[/red]")
|
|
444
|
+
raise typer.Exit(1)
|
|
445
|
+
|
|
446
|
+
if fmt == "markdown":
|
|
447
|
+
lines = [
|
|
448
|
+
f"# Chat Session: {session.project_name}",
|
|
449
|
+
f"\nSession ID: {session.id}",
|
|
450
|
+
f"Messages: {session.message_count}\n",
|
|
451
|
+
"---\n",
|
|
452
|
+
]
|
|
453
|
+
for msg in session.messages:
|
|
454
|
+
role = "**You**" if msg["role"] == "user" else "**CodeGraph**"
|
|
455
|
+
lines.append(f"### {role}\n")
|
|
456
|
+
lines.append(f"{msg['content']}\n")
|
|
457
|
+
lines.append("\n---\n")
|
|
458
|
+
content = "\n".join(lines)
|
|
459
|
+
ext = ".md"
|
|
460
|
+
|
|
461
|
+
elif fmt == "json":
|
|
462
|
+
data = {
|
|
463
|
+
"session_id": session.id,
|
|
464
|
+
"project_name": session.project_name,
|
|
465
|
+
"message_count": session.message_count,
|
|
466
|
+
"messages": session.messages,
|
|
467
|
+
}
|
|
468
|
+
content = _json.dumps(data, indent=2)
|
|
469
|
+
ext = ".json"
|
|
470
|
+
|
|
471
|
+
else:
|
|
472
|
+
console.print(f" [red]❌ Unknown format: {fmt}[/red]")
|
|
473
|
+
raise typer.Exit(1)
|
|
474
|
+
|
|
475
|
+
filename = output or f"{session_id[:12]}_export{ext}"
|
|
476
|
+
Path(filename).write_text(content)
|
|
477
|
+
_print_status("✅", f"Exported to {filename}")
|
codegraph_cli/cli_diagnose.py
CHANGED
|
@@ -19,7 +19,12 @@ diagnose_app = typer.Typer(help="Detect and fix code errors")
|
|
|
19
19
|
def check_errors(
|
|
20
20
|
path: str = typer.Argument(".", help="Path to check (default: current directory)"),
|
|
21
21
|
):
|
|
22
|
-
"""Scan project for syntax errors.
|
|
22
|
+
"""🔍 Scan project for syntax errors.
|
|
23
|
+
|
|
24
|
+
Example:
|
|
25
|
+
cg v2 diagnose check
|
|
26
|
+
cg v2 diagnose check ./src
|
|
27
|
+
"""
|
|
23
28
|
project_path = Path(path).resolve()
|
|
24
29
|
|
|
25
30
|
if not project_path.exists():
|
|
@@ -49,7 +54,13 @@ def fix_errors(
|
|
|
49
54
|
preview_only: bool = typer.Option(False, "--preview", "-p", help="Preview fixes without applying"),
|
|
50
55
|
auto_apply: bool = typer.Option(False, "--auto-apply", "-y", help="Apply fixes without confirmation"),
|
|
51
56
|
):
|
|
52
|
-
"""Automatically fix common syntax errors.
|
|
57
|
+
"""🔧 Automatically fix common syntax errors.
|
|
58
|
+
|
|
59
|
+
Example:
|
|
60
|
+
cg v2 diagnose fix
|
|
61
|
+
cg v2 diagnose fix ./src --preview
|
|
62
|
+
cg v2 diagnose fix ./src --auto-apply
|
|
63
|
+
"""
|
|
53
64
|
project_path = Path(path).resolve()
|
|
54
65
|
|
|
55
66
|
if not project_path.exists():
|