codegraph-cli 2.1.0__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 +204 -94
- 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 +32 -8
- codegraph_cli/crew_chat.py +146 -13
- codegraph_cli/crew_tools.py +30 -2
- 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.0.dist-info → codegraph_cli-2.1.2.dist-info}/METADATA +75 -21
- 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.0.dist-info/RECORD +0 -43
- codegraph_cli-2.1.0.dist-info/entry_points.txt +0 -2
- {codegraph_cli-2.1.0.dist-info → codegraph_cli-2.1.2.dist-info}/WHEEL +0 -0
- {codegraph_cli-2.1.0.dist-info → codegraph_cli-2.1.2.dist-info}/licenses/LICENSE +0 -0
- {codegraph_cli-2.1.0.dist-info → codegraph_cli-2.1.2.dist-info}/top_level.txt +0 -0
codegraph_cli/cli_chat.py
CHANGED
|
@@ -2,36 +2,27 @@
|
|
|
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
|
|
15
19
|
from .chat_session import SessionManager
|
|
16
|
-
from .crew_chat import CrewChatAgent
|
|
17
20
|
from .llm import LocalLLM
|
|
18
21
|
from .orchestrator import MCPOrchestrator
|
|
19
22
|
from .rag import RAGRetriever
|
|
20
23
|
from .storage import GraphStore, ProjectManager
|
|
21
24
|
|
|
22
|
-
|
|
23
|
-
# ── Theme colors ──────────────────────────────────────────────
|
|
24
|
-
C_RESET = "\033[0m"
|
|
25
|
-
C_BOLD = "\033[1m"
|
|
26
|
-
C_DIM = "\033[2m"
|
|
27
|
-
C_CYAN = "\033[36m"
|
|
28
|
-
C_GREEN = "\033[32m"
|
|
29
|
-
C_YELLOW = "\033[33m"
|
|
30
|
-
C_MAGENTA = "\033[35m"
|
|
31
|
-
C_RED = "\033[31m"
|
|
32
|
-
C_BLUE = "\033[34m"
|
|
33
|
-
C_WHITE = "\033[97m"
|
|
34
|
-
C_BG_DARK = "\033[48;5;235m"
|
|
25
|
+
console = Console()
|
|
35
26
|
|
|
36
27
|
|
|
37
28
|
def _term_width() -> int:
|
|
@@ -39,45 +30,40 @@ def _term_width() -> int:
|
|
|
39
30
|
return shutil.get_terminal_size((80, 24)).columns
|
|
40
31
|
|
|
41
32
|
|
|
42
|
-
def
|
|
43
|
-
"""
|
|
44
|
-
|
|
45
|
-
inner = w - 4
|
|
46
|
-
lines = text.split("\n")
|
|
47
|
-
out = [f"{color}╭{'─' * (w - 2)}╮{C_RESET}"]
|
|
48
|
-
for line in lines:
|
|
49
|
-
padded = line.ljust(inner)[:inner]
|
|
50
|
-
out.append(f"{color}│{C_RESET} {padded} {color}│{C_RESET}")
|
|
51
|
-
out.append(f"{color}╰{'─' * (w - 2)}╯{C_RESET}")
|
|
52
|
-
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"
|
|
53
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]")
|
|
54
41
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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]")
|
|
58
55
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
mode = "CrewAI Multi-Agent" if use_crew else "Chat"
|
|
63
|
-
|
|
64
|
-
banner = (
|
|
65
|
-
f" {C_BOLD}{C_CYAN}⚡ CodeGraph {mode}{C_RESET}\n"
|
|
66
|
-
f" {C_DIM}Project: {C_WHITE}{project_name}{C_RESET}\n"
|
|
67
|
-
f" {C_DIM}LLM: {C_WHITE}{provider}/{model}{C_RESET}"
|
|
68
|
-
)
|
|
69
|
-
print(f"\n{_box(f'⚡ CodeGraph {mode}', C_CYAN)}")
|
|
70
|
-
print(f" {C_DIM}Project {C_RESET}{C_WHITE}{project_name}{C_RESET}")
|
|
71
|
-
print(f" {C_DIM}LLM {C_RESET}{C_WHITE}{provider}/{model}{C_RESET}")
|
|
72
|
-
print(f" {C_DIM}Type {C_YELLOW}/help{C_DIM} for commands, {C_YELLOW}/exit{C_DIM} to quit{C_RESET}")
|
|
73
|
-
print(_divider())
|
|
74
|
-
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()
|
|
75
59
|
|
|
76
60
|
|
|
77
61
|
def _print_help(use_crew: bool):
|
|
78
62
|
"""Print help with styled formatting."""
|
|
79
|
-
print(
|
|
80
|
-
|
|
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")
|
|
81
67
|
cmds = [
|
|
82
68
|
("/exit", "Exit chat session"),
|
|
83
69
|
("/clear", "Clear conversation history & start fresh"),
|
|
@@ -96,22 +82,23 @@ def _print_help(use_crew: bool):
|
|
|
96
82
|
("/preview", "Preview pending changes"),
|
|
97
83
|
])
|
|
98
84
|
for cmd, desc in cmds:
|
|
99
|
-
|
|
100
|
-
print()
|
|
85
|
+
table.add_row(cmd, desc)
|
|
86
|
+
console.print(table)
|
|
87
|
+
console.print()
|
|
101
88
|
|
|
102
89
|
|
|
103
90
|
def _print_response(text: str):
|
|
104
91
|
"""Print assistant response with styling."""
|
|
105
|
-
print(
|
|
106
|
-
|
|
92
|
+
console.print()
|
|
93
|
+
console.print(" [green]●[/green] [bold]Assistant[/bold]")
|
|
107
94
|
for line in text.split("\n"):
|
|
108
|
-
print(f" {line}")
|
|
109
|
-
print()
|
|
95
|
+
console.print(f" {line}", highlight=False)
|
|
96
|
+
console.print()
|
|
110
97
|
|
|
111
98
|
|
|
112
|
-
def _print_status(emoji: str, msg: str,
|
|
99
|
+
def _print_status(emoji: str, msg: str, style: str = "green"):
|
|
113
100
|
"""Print a status message."""
|
|
114
|
-
print(f" {
|
|
101
|
+
console.print(f" [{style}]{emoji} {msg}[/{style}]")
|
|
115
102
|
|
|
116
103
|
|
|
117
104
|
def start_chat_repl(
|
|
@@ -122,13 +109,14 @@ def start_chat_repl(
|
|
|
122
109
|
use_crew: bool = False,
|
|
123
110
|
provider: str = "",
|
|
124
111
|
model: str = "",
|
|
112
|
+
auto_context: dict | None = None,
|
|
125
113
|
):
|
|
126
114
|
"""Start interactive REPL for chat."""
|
|
127
115
|
# Load or create session
|
|
128
116
|
if session_id:
|
|
129
117
|
session = session_manager.load_session(session_id)
|
|
130
118
|
if not session:
|
|
131
|
-
_print_status("🆕", "Session not found. Starting new session.",
|
|
119
|
+
_print_status("🆕", "Session not found. Starting new session.", "yellow")
|
|
132
120
|
session = session_manager.create_session(project_name)
|
|
133
121
|
else:
|
|
134
122
|
_print_status("📂", f"Resumed session ({session.message_count} messages)")
|
|
@@ -142,17 +130,21 @@ def start_chat_repl(
|
|
|
142
130
|
session = session_manager.create_session(project_name)
|
|
143
131
|
_print_status("🆕", "Started new chat session")
|
|
144
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
|
+
|
|
145
137
|
# Welcome
|
|
146
|
-
_print_welcome(project_name, use_crew, provider, model)
|
|
138
|
+
_print_welcome(project_name, use_crew, provider, model, auto_context=auto_context)
|
|
147
139
|
|
|
148
140
|
# REPL loop
|
|
149
141
|
while True:
|
|
150
142
|
try:
|
|
151
143
|
# Prompt
|
|
152
144
|
try:
|
|
153
|
-
user_input = input(
|
|
145
|
+
user_input = console.input(" [blue]●[/blue] [bold]You ›[/bold] ").strip()
|
|
154
146
|
except EOFError:
|
|
155
|
-
print(
|
|
147
|
+
console.print("\n [dim]👋 Goodbye! Session saved.[/dim]\n")
|
|
156
148
|
break
|
|
157
149
|
|
|
158
150
|
if not user_input:
|
|
@@ -164,22 +156,28 @@ def start_chat_repl(
|
|
|
164
156
|
|
|
165
157
|
if cmd == "/exit":
|
|
166
158
|
session_manager.save_session(session)
|
|
167
|
-
print(
|
|
159
|
+
console.print("\n [dim]👋 Goodbye! Session saved.[/dim]\n")
|
|
168
160
|
break
|
|
169
161
|
|
|
170
162
|
elif cmd == "/clear":
|
|
171
163
|
session.clear_history()
|
|
172
164
|
session.clear_proposals()
|
|
173
165
|
session_manager.save_session(session)
|
|
174
|
-
|
|
175
|
-
|
|
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()
|
|
176
171
|
continue
|
|
177
172
|
|
|
178
173
|
elif cmd == "/new":
|
|
179
174
|
session_manager.save_session(session)
|
|
180
175
|
session = session_manager.create_session(project_name)
|
|
181
|
-
|
|
182
|
-
|
|
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()
|
|
183
181
|
continue
|
|
184
182
|
|
|
185
183
|
elif cmd == "/help":
|
|
@@ -192,57 +190,57 @@ def start_chat_repl(
|
|
|
192
190
|
result = agent.apply_pending_proposal(session)
|
|
193
191
|
_print_status("📋", result)
|
|
194
192
|
else:
|
|
195
|
-
_print_status("📋", "No pending proposals.",
|
|
193
|
+
_print_status("📋", "No pending proposals.", "yellow")
|
|
196
194
|
continue
|
|
197
195
|
elif cmd == "/preview":
|
|
198
196
|
if session.pending_proposals:
|
|
199
197
|
for i, prop in enumerate(session.pending_proposals):
|
|
200
|
-
print(f"\n
|
|
198
|
+
console.print(f"\n [bold]Proposal {i+1}:[/bold] {prop.description}")
|
|
201
199
|
for ch in prop.changes:
|
|
202
200
|
icon = {"create": "🆕", "modify": "✏️", "delete": "🗑️"}.get(ch.change_type, "📄")
|
|
203
|
-
print(f" {icon} {ch.file_path}")
|
|
201
|
+
console.print(f" {icon} {ch.file_path}")
|
|
204
202
|
else:
|
|
205
|
-
_print_status("📋", "No pending proposals.",
|
|
206
|
-
print()
|
|
203
|
+
_print_status("📋", "No pending proposals.", "yellow")
|
|
204
|
+
console.print()
|
|
207
205
|
continue
|
|
208
206
|
|
|
209
207
|
elif use_crew:
|
|
210
208
|
if cmd in ("/rollback", "/undo"):
|
|
211
209
|
parts = user_input.split(maxsplit=1)
|
|
212
210
|
if len(parts) < 2:
|
|
213
|
-
_print_status("❓", "Usage: /rollback <file_path> [timestamp]",
|
|
211
|
+
_print_status("❓", "Usage: /rollback <file_path> [timestamp]", "yellow")
|
|
214
212
|
continue
|
|
215
213
|
args = parts[1].split()
|
|
216
214
|
file_path = args[0]
|
|
217
215
|
ts = args[1] if len(args) > 1 else None
|
|
218
216
|
result = agent.rollback(file_path, ts)
|
|
219
217
|
_print_status("⏪", result)
|
|
220
|
-
print()
|
|
218
|
+
console.print()
|
|
221
219
|
continue
|
|
222
220
|
|
|
223
221
|
elif cmd == "/backups":
|
|
224
222
|
backups = agent.list_all_backups()
|
|
225
223
|
if not backups:
|
|
226
|
-
_print_status("📦", "No backups found.",
|
|
224
|
+
_print_status("📦", "No backups found.", "yellow")
|
|
227
225
|
else:
|
|
228
|
-
print(
|
|
229
|
-
print(
|
|
226
|
+
console.print("\n [bold cyan]📦 File Backups[/bold cyan]")
|
|
227
|
+
console.print(Rule(style="dim"))
|
|
230
228
|
for b in backups:
|
|
231
229
|
ts = b["timestamp"]
|
|
232
230
|
fp = b["original_path"]
|
|
233
|
-
print(f" {
|
|
234
|
-
print(
|
|
235
|
-
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()
|
|
236
234
|
continue
|
|
237
235
|
|
|
238
|
-
_print_status("❓", f"Unknown command: {cmd}. Type /help",
|
|
236
|
+
_print_status("❓", f"Unknown command: {cmd}. Type /help", "yellow")
|
|
239
237
|
continue
|
|
240
238
|
|
|
241
239
|
# ── Process message ──────────────────────────────
|
|
242
240
|
session.add_message("user", user_input, datetime.now().isoformat())
|
|
243
241
|
|
|
244
242
|
# Show thinking indicator
|
|
245
|
-
print(
|
|
243
|
+
console.print("\n [dim]⏳ Thinking...[/dim]", end="")
|
|
246
244
|
|
|
247
245
|
if use_crew:
|
|
248
246
|
response = agent.process_message(user_input)
|
|
@@ -250,7 +248,7 @@ def start_chat_repl(
|
|
|
250
248
|
response = agent.process_message(user_input, session)
|
|
251
249
|
|
|
252
250
|
# Clear thinking indicator
|
|
253
|
-
print(f"\r{' ' * 30}\r", end="")
|
|
251
|
+
console.print(f"\r{' ' * 30}\r", end="")
|
|
254
252
|
|
|
255
253
|
# Save & display
|
|
256
254
|
session.add_message("assistant", response, datetime.now().isoformat())
|
|
@@ -260,16 +258,46 @@ def start_chat_repl(
|
|
|
260
258
|
|
|
261
259
|
except KeyboardInterrupt:
|
|
262
260
|
session_manager.save_session(session)
|
|
263
|
-
print(
|
|
261
|
+
console.print("\n\n [dim]👋 Goodbye! Session saved.[/dim]\n")
|
|
264
262
|
break
|
|
265
263
|
except Exception as e:
|
|
266
|
-
print(f"\n
|
|
264
|
+
console.print(f"\n [red]❌ Error: {str(e)}[/red]\n")
|
|
267
265
|
|
|
268
266
|
|
|
269
267
|
# ── Typer app ────────────────────────────────────────────────
|
|
270
268
|
chat_app = typer.Typer(help="💬 Interactive chat with AI agents")
|
|
271
269
|
|
|
272
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
|
+
|
|
273
301
|
@chat_app.command("start")
|
|
274
302
|
def start_chat(
|
|
275
303
|
session_id: Optional[str] = typer.Option(None, "--session", "-s", help="Resume specific session ID"),
|
|
@@ -280,7 +308,14 @@ def start_chat(
|
|
|
280
308
|
use_crew: bool = typer.Option(False, "--crew", help="Use CrewAI multi-agent system"),
|
|
281
309
|
new_session: bool = typer.Option(False, "--new", "-n", help="Force start a new session"),
|
|
282
310
|
):
|
|
283
|
-
"""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
|
+
"""
|
|
284
319
|
from .embeddings import get_embedder
|
|
285
320
|
from .project_context import ProjectContext
|
|
286
321
|
|
|
@@ -288,8 +323,8 @@ def start_chat(
|
|
|
288
323
|
project = pm.get_current_project()
|
|
289
324
|
|
|
290
325
|
if not project:
|
|
291
|
-
print(
|
|
292
|
-
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")
|
|
293
328
|
raise typer.Exit(1)
|
|
294
329
|
|
|
295
330
|
# Initialize components
|
|
@@ -299,7 +334,13 @@ def start_chat(
|
|
|
299
334
|
rag_retriever = RAGRetriever(context.store, embedding_model)
|
|
300
335
|
|
|
301
336
|
if use_crew:
|
|
302
|
-
|
|
337
|
+
try:
|
|
338
|
+
from .crew_chat import CrewChatAgent
|
|
339
|
+
except ImportError:
|
|
340
|
+
console.print("\n [red]CrewAI is not installed.[/red]")
|
|
341
|
+
console.print(" [dim]Install with: pip install codegraph-cli\\[crew][/dim]\n")
|
|
342
|
+
raise typer.Exit(1)
|
|
343
|
+
console.print("\n [magenta]Initializing CrewAI multi-agent system...[/magenta]")
|
|
303
344
|
agent = CrewChatAgent(context, llm, rag_retriever)
|
|
304
345
|
else:
|
|
305
346
|
orchestrator = MCPOrchestrator(
|
|
@@ -313,6 +354,9 @@ def start_chat(
|
|
|
313
354
|
|
|
314
355
|
session_manager = SessionManager()
|
|
315
356
|
|
|
357
|
+
# Gather auto-context from the project for enriched chat experience
|
|
358
|
+
auto_context = _gather_auto_context(context)
|
|
359
|
+
|
|
316
360
|
# Force new session if requested
|
|
317
361
|
effective_session_id = None if new_session else session_id
|
|
318
362
|
|
|
@@ -322,6 +366,7 @@ def start_chat(
|
|
|
322
366
|
use_crew=use_crew,
|
|
323
367
|
provider=llm_provider,
|
|
324
368
|
model=llm_model,
|
|
369
|
+
auto_context=auto_context,
|
|
325
370
|
)
|
|
326
371
|
finally:
|
|
327
372
|
context.close()
|
|
@@ -331,16 +376,21 @@ def start_chat(
|
|
|
331
376
|
def list_sessions(
|
|
332
377
|
project: Optional[str] = typer.Option(None, "--project", "-p", help="Filter by project")
|
|
333
378
|
):
|
|
334
|
-
"""List all chat sessions.
|
|
379
|
+
"""📋 List all chat sessions.
|
|
380
|
+
|
|
381
|
+
Example:
|
|
382
|
+
cg chat list
|
|
383
|
+
cg chat list --project my-api
|
|
384
|
+
"""
|
|
335
385
|
session_manager = SessionManager()
|
|
336
386
|
sessions = session_manager.list_sessions(project_name=project)
|
|
337
387
|
|
|
338
388
|
if not sessions:
|
|
339
|
-
print(
|
|
389
|
+
console.print("\n [dim]No chat sessions found.[/dim]\n")
|
|
340
390
|
return
|
|
341
391
|
|
|
342
|
-
print(f"\n
|
|
343
|
-
print(
|
|
392
|
+
console.print(f"\n [bold cyan]📋 Chat Sessions ({len(sessions)})[/bold cyan]")
|
|
393
|
+
console.print(Rule(style="dim"))
|
|
344
394
|
|
|
345
395
|
for i, sess in enumerate(sessions, 1):
|
|
346
396
|
created = sess['created_at'][:16].replace("T", " ")
|
|
@@ -348,20 +398,80 @@ def list_sessions(
|
|
|
348
398
|
proj = sess['project_name']
|
|
349
399
|
sid = sess['id'][:8]
|
|
350
400
|
|
|
351
|
-
print(f" {
|
|
401
|
+
console.print(f" [white]{i}.[/white] [bold]{proj}[/bold] [dim]({msgs} msgs, {created})[/dim] [dim]id:{sid}…[/dim]")
|
|
352
402
|
|
|
353
|
-
print()
|
|
403
|
+
console.print()
|
|
354
404
|
|
|
355
405
|
|
|
356
406
|
@chat_app.command("delete")
|
|
357
407
|
def delete_session(
|
|
358
408
|
session_id: str = typer.Argument(..., help="Session ID to delete")
|
|
359
409
|
):
|
|
360
|
-
"""Delete a chat session.
|
|
410
|
+
"""🗑️ Delete a chat session.
|
|
411
|
+
|
|
412
|
+
Example:
|
|
413
|
+
cg chat delete abc12345
|
|
414
|
+
"""
|
|
361
415
|
session_manager = SessionManager()
|
|
362
416
|
|
|
363
417
|
if session_manager.delete_session(session_id):
|
|
364
418
|
_print_status("✅", f"Deleted session {session_id[:8]}…")
|
|
365
419
|
else:
|
|
366
|
-
print(
|
|
420
|
+
console.print(" [red]❌ Session not found[/red]")
|
|
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]")
|
|
367
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():
|