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.
Files changed (42) hide show
  1. codegraph_cli/__init__.py +1 -1
  2. codegraph_cli/agents.py +59 -3
  3. codegraph_cli/chat_agent.py +58 -11
  4. codegraph_cli/cli.py +569 -54
  5. codegraph_cli/cli_chat.py +204 -94
  6. codegraph_cli/cli_diagnose.py +13 -2
  7. codegraph_cli/cli_docs.py +207 -0
  8. codegraph_cli/cli_explore.py +1053 -0
  9. codegraph_cli/cli_export.py +941 -0
  10. codegraph_cli/cli_groups.py +33 -0
  11. codegraph_cli/cli_health.py +316 -0
  12. codegraph_cli/cli_history.py +213 -0
  13. codegraph_cli/cli_onboard.py +380 -0
  14. codegraph_cli/cli_quickstart.py +256 -0
  15. codegraph_cli/cli_refactor.py +17 -3
  16. codegraph_cli/cli_setup.py +12 -12
  17. codegraph_cli/cli_suggestions.py +90 -0
  18. codegraph_cli/cli_test.py +17 -3
  19. codegraph_cli/cli_tui.py +210 -0
  20. codegraph_cli/cli_v2.py +24 -4
  21. codegraph_cli/cli_watch.py +158 -0
  22. codegraph_cli/cli_workflows.py +255 -0
  23. codegraph_cli/codegen_agent.py +15 -1
  24. codegraph_cli/config.py +18 -5
  25. codegraph_cli/context_manager.py +117 -15
  26. codegraph_cli/crew_agents.py +32 -8
  27. codegraph_cli/crew_chat.py +146 -13
  28. codegraph_cli/crew_tools.py +30 -2
  29. codegraph_cli/embeddings.py +95 -5
  30. codegraph_cli/llm.py +42 -55
  31. codegraph_cli/project_context.py +64 -1
  32. codegraph_cli/rag.py +282 -19
  33. codegraph_cli/storage.py +310 -14
  34. codegraph_cli/vector_store.py +110 -8
  35. {codegraph_cli-2.1.0.dist-info → codegraph_cli-2.1.2.dist-info}/METADATA +75 -21
  36. codegraph_cli-2.1.2.dist-info/RECORD +55 -0
  37. codegraph_cli-2.1.2.dist-info/entry_points.txt +2 -0
  38. codegraph_cli-2.1.0.dist-info/RECORD +0 -43
  39. codegraph_cli-2.1.0.dist-info/entry_points.txt +0 -2
  40. {codegraph_cli-2.1.0.dist-info → codegraph_cli-2.1.2.dist-info}/WHEEL +0 -0
  41. {codegraph_cli-2.1.0.dist-info → codegraph_cli-2.1.2.dist-info}/licenses/LICENSE +0 -0
  42. {codegraph_cli-2.1.0.dist-info → codegraph_cli-2.1.2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,380 @@
1
+ """CLI command for AI-generated project onboarding README."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+ from typing import Optional
7
+
8
+ import typer
9
+ from rich.console import Console
10
+ from rich.panel import Panel
11
+ from rich.progress import Progress, SpinnerColumn, TextColumn
12
+
13
+ from . import config
14
+ from .llm import LocalLLM
15
+ from .storage import GraphStore, ProjectManager
16
+
17
+ console = Console()
18
+
19
+
20
+ def _collect_project_intel(store: GraphStore, source_path: Optional[Path]) -> dict:
21
+ """Gather all graph data needed to generate the README.
22
+
23
+ Returns a dict with keys:
24
+ files, functions, classes, modules, edges, entry_points,
25
+ top_callers, dependency_tree, docstrings, file_tree
26
+ """
27
+ nodes = store.get_nodes()
28
+ edges = store.get_edges()
29
+
30
+ files: set[str] = set()
31
+ functions: list[dict] = []
32
+ classes: list[dict] = []
33
+ modules: list[dict] = []
34
+ docstrings: dict[str, str] = {} # qualname → docstring
35
+
36
+ for n in nodes:
37
+ files.add(n["file_path"])
38
+ entry = {
39
+ "name": n["name"],
40
+ "qualname": n["qualname"],
41
+ "file": n["file_path"],
42
+ "lines": f"L{n['start_line']}-{n['end_line']}",
43
+ }
44
+ if n["node_type"] == "function":
45
+ functions.append(entry)
46
+ elif n["node_type"] == "class":
47
+ classes.append(entry)
48
+ elif n["node_type"] == "module":
49
+ modules.append(entry)
50
+ if n["docstring"] and n["docstring"].strip():
51
+ docstrings[n["qualname"]] = n["docstring"].strip()
52
+
53
+ # Build call count per function (who gets called the most?)
54
+ call_count: dict[str, int] = {}
55
+ callers_of: dict[str, list[str]] = {}
56
+ imports: list[tuple[str, str]] = []
57
+
58
+ for e in edges:
59
+ if e["edge_type"] == "calls":
60
+ call_count[e["dst"]] = call_count.get(e["dst"], 0) + 1
61
+ callers_of.setdefault(e["dst"], []).append(e["src"])
62
+ elif e["edge_type"] == "depends_on":
63
+ imports.append((e["src"], e["dst"]))
64
+
65
+ # Top-called functions (likely core logic)
66
+ top_called = sorted(call_count.items(), key=lambda x: x[1], reverse=True)[:15]
67
+
68
+ # Entry points: functions that are called by nothing (zero incoming call edges)
69
+ all_dst = {e["dst"] for e in edges if e["edge_type"] == "calls"}
70
+ all_func_ids = {f["qualname"] for f in functions}
71
+ entry_points = sorted(all_func_ids - all_dst)[:20]
72
+
73
+ # File tree (compact)
74
+ sorted_files = sorted(files)
75
+ file_tree = "\n".join(f" {f}" for f in sorted_files[:60])
76
+ if len(sorted_files) > 60:
77
+ file_tree += f"\n ... and {len(sorted_files) - 60} more files"
78
+
79
+ # Detect likely config / entry files
80
+ notable_files = []
81
+ for f in sorted_files:
82
+ name = Path(f).name.lower()
83
+ if name in (
84
+ "main.py", "app.py", "cli.py", "__main__.py", "server.py",
85
+ "wsgi.py", "asgi.py", "manage.py", "setup.py", "conftest.py",
86
+ "settings.py", "config.py", "urls.py", "routes.py",
87
+ "index.js", "index.ts", "app.js", "app.ts", "server.js",
88
+ ):
89
+ notable_files.append(f)
90
+
91
+ return {
92
+ "file_count": len(files),
93
+ "function_count": len(functions),
94
+ "class_count": len(classes),
95
+ "module_count": len(modules),
96
+ "edge_count": len(edges),
97
+ "files": sorted_files,
98
+ "notable_files": notable_files,
99
+ "functions": functions,
100
+ "classes": classes,
101
+ "entry_points": entry_points,
102
+ "top_called": top_called,
103
+ "imports": imports[:30],
104
+ "docstrings": docstrings,
105
+ "file_tree": file_tree,
106
+ }
107
+
108
+
109
+ def _build_onboard_prompt(project_name: str, intel: dict, source_path: Optional[str]) -> str:
110
+ """Build the LLM prompt from collected intelligence."""
111
+ # Top-called functions with callers
112
+ top_called_text = ""
113
+ for qualname, count in intel["top_called"]:
114
+ top_called_text += f" - {qualname} (called {count} times)\n"
115
+
116
+ # Entry points
117
+ entry_text = "\n".join(f" - {ep}" for ep in intel["entry_points"][:15])
118
+
119
+ # Classes
120
+ class_text = "\n".join(
121
+ f" - {c['qualname']} ({c['file']})" for c in intel["classes"][:20]
122
+ )
123
+
124
+ # Notable files
125
+ notable_text = "\n".join(f" - {f}" for f in intel["notable_files"])
126
+
127
+ # Key docstrings (first 12)
128
+ doc_text = ""
129
+ for qualname, doc in list(intel["docstrings"].items())[:12]:
130
+ first_line = doc.split("\n")[0][:120]
131
+ doc_text += f" - {qualname}: {first_line}\n"
132
+
133
+ # Import relationships (module dependencies)
134
+ import_text = "\n".join(
135
+ f" - {src} → {dst}" for src, dst in intel["imports"][:20]
136
+ )
137
+
138
+ prompt = f"""You are a senior developer writing a README.md for a project you just analyzed.
139
+ Generate a complete, professional README.md based on the code graph analysis below.
140
+
141
+ PROJECT: {project_name}
142
+ SOURCE: {source_path or 'unknown'}
143
+
144
+ STATISTICS:
145
+ - {intel['file_count']} files
146
+ - {intel['function_count']} functions
147
+ - {intel['class_count']} classes
148
+ - {intel['edge_count']} dependency edges
149
+
150
+ FILE STRUCTURE:
151
+ {intel['file_tree']}
152
+
153
+ NOTABLE FILES (likely entry points / config):
154
+ {notable_text or ' (none detected)'}
155
+
156
+ TOP ENTRY POINTS (functions not called by anything else — likely CLI/API handlers):
157
+ {entry_text or ' (none detected)'}
158
+
159
+ MOST-CALLED FUNCTIONS (core logic):
160
+ {top_called_text or ' (none detected)'}
161
+
162
+ CLASSES:
163
+ {class_text or ' (none)'}
164
+
165
+ KEY DOCSTRINGS:
166
+ {doc_text or ' (none found)'}
167
+
168
+ MODULE DEPENDENCIES:
169
+ {import_text or ' (none)'}
170
+
171
+ INSTRUCTIONS:
172
+ Write a complete README.md with these sections:
173
+ 1. **Project title and one-line description** — infer the purpose from the code structure, docstrings, and function names.
174
+ 2. **Overview** — 2-3 paragraphs explaining what this project does, its architecture, and key design decisions.
175
+ 3. **Project Structure** — a clean tree view of the major directories/files with brief descriptions.
176
+ 4. **Key Modules** — describe the 5-8 most important modules and what they do.
177
+ 5. **Getting Started** — installation steps (infer from file structure: requirements.txt, pyproject.toml, package.json, etc.)
178
+ 6. **Usage** — example commands or API usage (infer from entry points and CLI handlers).
179
+ 7. **Architecture** — how the components connect. Use the dependency graph data.
180
+ 8. **Contributing** — brief contributing guidelines.
181
+
182
+ RULES:
183
+ - Output ONLY the markdown content, no preamble or explanation.
184
+ - Be specific — use real function names, file paths, and class names from the analysis.
185
+ - Do NOT invent features not evidenced by the code graph.
186
+ - Keep it concise but comprehensive. Target ~200-400 lines.
187
+ - Use proper markdown formatting with headers, code blocks, and tables where appropriate.
188
+ """
189
+ return prompt
190
+
191
+
192
+ def _generate_fallback_readme(project_name: str, intel: dict, source_path: Optional[str]) -> str:
193
+ """Generate a README without LLM — pure template from graph data."""
194
+ lines = [
195
+ f"# {project_name}",
196
+ "",
197
+ ]
198
+
199
+ # One-liner from first docstring or generic
200
+ first_doc = next(iter(intel["docstrings"].values()), None)
201
+ if first_doc:
202
+ lines.append(f"> {first_doc.split(chr(10))[0]}")
203
+ else:
204
+ lines.append(f"> Auto-generated documentation for **{project_name}**.")
205
+ lines.append("")
206
+
207
+ # Stats
208
+ lines.extend([
209
+ "## Overview",
210
+ "",
211
+ f"| Metric | Count |",
212
+ f"|--------|-------|",
213
+ f"| Files | {intel['file_count']} |",
214
+ f"| Functions | {intel['function_count']} |",
215
+ f"| Classes | {intel['class_count']} |",
216
+ f"| Dependencies | {intel['edge_count']} |",
217
+ "",
218
+ ])
219
+
220
+ # Project structure
221
+ lines.extend(["## Project Structure", "", "```"])
222
+ for f in intel["files"][:40]:
223
+ lines.append(f)
224
+ if len(intel["files"]) > 40:
225
+ lines.append(f"... and {len(intel['files']) - 40} more files")
226
+ lines.extend(["```", ""])
227
+
228
+ # Key classes
229
+ if intel["classes"]:
230
+ lines.extend(["## Key Classes", ""])
231
+ for c in intel["classes"][:15]:
232
+ doc = intel["docstrings"].get(c["qualname"], "")
233
+ desc = f" — {doc.split(chr(10))[0]}" if doc else ""
234
+ lines.append(f"- **`{c['qualname']}`** ({c['file']}){desc}")
235
+ lines.append("")
236
+
237
+ # Entry points
238
+ if intel["entry_points"]:
239
+ lines.extend(["## Entry Points", "", "Functions that are not called by any other indexed code:", ""])
240
+ for ep in intel["entry_points"][:15]:
241
+ lines.append(f"- `{ep}`")
242
+ lines.append("")
243
+
244
+ # Most-called functions
245
+ if intel["top_called"]:
246
+ lines.extend(["## Core Functions", "", "Most frequently called functions in the codebase:", ""])
247
+ lines.append("| Function | Call Count |")
248
+ lines.append("|----------|-----------|")
249
+ for qualname, count in intel["top_called"]:
250
+ lines.append(f"| `{qualname}` | {count} |")
251
+ lines.append("")
252
+
253
+ # Notable files
254
+ if intel["notable_files"]:
255
+ lines.extend(["## Notable Files", ""])
256
+ for f in intel["notable_files"]:
257
+ lines.append(f"- `{f}`")
258
+ lines.append("")
259
+
260
+ lines.extend([
261
+ "---",
262
+ "",
263
+ f"*Generated by [CodeGraph CLI](https://github.com/al1-nasir/codegraph-cli) from code graph analysis.*",
264
+ ])
265
+
266
+ return "\n".join(lines)
267
+
268
+
269
+ def onboard(
270
+ output: Optional[Path] = typer.Option(
271
+ None, "--output", "-o",
272
+ help="Output file path (default: prints to stdout).",
273
+ ),
274
+ save: bool = typer.Option(
275
+ False, "--save", "-s",
276
+ help="Save as ONBOARD.md in the project source directory.",
277
+ ),
278
+ no_llm: bool = typer.Option(
279
+ False, "--no-llm",
280
+ help="Skip LLM and generate from template only.",
281
+ ),
282
+ llm_provider: str = typer.Option(config.LLM_PROVIDER, help="LLM provider."),
283
+ llm_model: str = typer.Option(config.LLM_MODEL, help="LLM model."),
284
+ llm_api_key: Optional[str] = typer.Option(config.LLM_API_KEY, help="API key."),
285
+ ):
286
+ """🚀 Auto-generate a project README from the code graph.
287
+
288
+ Analyzes the indexed project's structure, dependencies, entry points,
289
+ and docstrings to produce a comprehensive README.md — either via LLM
290
+ or from a pure template.
291
+
292
+ Examples:
293
+ cg onboard # print to stdout
294
+ cg onboard --save # save as ONBOARD.md in project dir
295
+ cg onboard -o README.md # save to specific file
296
+ cg onboard --no-llm # template only, no LLM call
297
+ """
298
+ pm = ProjectManager()
299
+ project = pm.get_current_project()
300
+ if not project:
301
+ console.print("[red]❌ No project loaded.[/red]")
302
+ console.print("[dim]Use: cg project index <path> or cg project load <name>[/dim]")
303
+ raise typer.Exit(1)
304
+
305
+ project_dir = pm.project_dir(project)
306
+ if not project_dir.exists():
307
+ console.print(f"[red]❌ Project '{project}' not found in memory.[/red]")
308
+ raise typer.Exit(1)
309
+
310
+ store = GraphStore(project_dir)
311
+ metadata = store.get_metadata()
312
+ source_path = metadata.get("source_path") or metadata.get("project_root")
313
+
314
+ # ── Step 1: Collect intelligence from the graph ──────────────
315
+ with Progress(
316
+ SpinnerColumn(), TextColumn("[cyan]{task.description}[/cyan]"), console=console,
317
+ ) as progress:
318
+ task = progress.add_task("Analyzing project graph...", total=None)
319
+ intel = _collect_project_intel(store, Path(source_path) if source_path else None)
320
+ progress.update(task, description="[green]Graph analysis complete")
321
+
322
+ console.print(
323
+ f" [dim]Analyzed[/dim] [white]{intel['file_count']} files, "
324
+ f"{intel['function_count']} functions, "
325
+ f"{intel['class_count']} classes[/white]"
326
+ )
327
+
328
+ # ── Step 2: Generate README ──────────────────────────────────
329
+ if no_llm:
330
+ console.print(" [dim]Generating template-based README (--no-llm)...[/dim]")
331
+ readme_content = _generate_fallback_readme(project, intel, source_path)
332
+ else:
333
+ llm = LocalLLM(
334
+ model=llm_model,
335
+ provider=llm_provider,
336
+ api_key=llm_api_key,
337
+ )
338
+ prompt = _build_onboard_prompt(project, intel, source_path)
339
+
340
+ with Progress(
341
+ SpinnerColumn(), TextColumn("[cyan]{task.description}[/cyan]"), console=console,
342
+ ) as progress:
343
+ task = progress.add_task("Generating README with LLM...", total=None)
344
+ result = llm.explain(prompt)
345
+ progress.update(task, description="[green]README generated")
346
+
347
+ # If LLM returned something useful, use it; otherwise fall back
348
+ if result and len(result) > 200 and not result.startswith("LLM provider"):
349
+ readme_content = result
350
+ else:
351
+ console.print(" [yellow]⚠ LLM unavailable, using template fallback.[/yellow]")
352
+ readme_content = _generate_fallback_readme(project, intel, source_path)
353
+
354
+ store.close()
355
+
356
+ # ── Step 3: Output ───────────────────────────────────────────
357
+ if output:
358
+ output.write_text(readme_content, encoding="utf-8")
359
+ console.print(f"\n[green]✅ Saved to {output}[/green]")
360
+ elif save:
361
+ if source_path:
362
+ dest = Path(source_path) / "ONBOARD.md"
363
+ else:
364
+ dest = Path.cwd() / "ONBOARD.md"
365
+ dest.write_text(readme_content, encoding="utf-8")
366
+ console.print(f"\n[green]✅ Saved to {dest}[/green]")
367
+ else:
368
+ # Print to stdout with a panel
369
+ console.print()
370
+ console.print(Panel(
371
+ readme_content,
372
+ title=f"📄 Generated README for {project}",
373
+ title_align="left",
374
+ border_style="cyan",
375
+ expand=False,
376
+ ))
377
+
378
+ console.print(
379
+ f"\n[dim]Tip: Review and edit the output — AI-generated docs are a starting point, not final.[/dim]"
380
+ )
@@ -0,0 +1,256 @@
1
+ """Quick-start wizard for CodeGraph CLI — get started in 30 seconds."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+ from typing import Optional
7
+
8
+ import typer
9
+ from rich.console import Console
10
+ from rich.panel import Panel
11
+ from rich.table import Table
12
+
13
+ from . import config
14
+ from .config import BASE_DIR
15
+
16
+ console = Console()
17
+
18
+ quickstart_app = typer.Typer(help="🚀 Quick setup and demo")
19
+
20
+
21
+ def detect_project_type() -> str:
22
+ """Auto-detect project type from common files in the current directory."""
23
+ cwd = Path.cwd()
24
+ detectors = [
25
+ ("package.json", "JavaScript/TypeScript"),
26
+ ("tsconfig.json", "TypeScript"),
27
+ ("setup.py", "Python"),
28
+ ("pyproject.toml", "Python"),
29
+ ("setup.cfg", "Python"),
30
+ ("requirements.txt", "Python"),
31
+ ("go.mod", "Go"),
32
+ ("Cargo.toml", "Rust"),
33
+ ("pom.xml", "Java"),
34
+ ("build.gradle", "Java/Kotlin"),
35
+ ("build.gradle.kts", "Kotlin"),
36
+ ("Gemfile", "Ruby"),
37
+ ("composer.json", "PHP"),
38
+ ("mix.exs", "Elixir"),
39
+ ("CMakeLists.txt", "C/C++"),
40
+ ("Makefile", "C/C++"),
41
+ (".csproj", "C#/.NET"),
42
+ ("pubspec.yaml", "Dart/Flutter"),
43
+ ("Package.swift", "Swift"),
44
+ ]
45
+ for filename, project_type in detectors:
46
+ if (cwd / filename).exists():
47
+ return project_type
48
+ # Also check for glob patterns (e.g. *.csproj)
49
+ if filename.startswith(".") and list(cwd.glob(f"*{filename}")):
50
+ return project_type
51
+ return "Unknown"
52
+
53
+
54
+ def config_exists() -> bool:
55
+ """Check if CodeGraph configuration already exists."""
56
+ config_file = BASE_DIR / "config.toml"
57
+ return config_file.exists()
58
+
59
+
60
+ def has_active_project() -> bool:
61
+ """Check if a project is currently loaded."""
62
+ try:
63
+ from .storage import ProjectManager
64
+ pm = ProjectManager()
65
+ return pm.get_current_project() is not None
66
+ except Exception:
67
+ return False
68
+
69
+
70
+ def run_setup() -> None:
71
+ """Run the interactive setup wizard."""
72
+ try:
73
+ from .cli_setup import setup
74
+ setup()
75
+ except (typer.Exit, SystemExit):
76
+ pass
77
+
78
+
79
+ def run_index(path: str = ".") -> Optional[dict]:
80
+ """Index the current directory and return stats."""
81
+ from .orchestrator import MCPOrchestrator
82
+ from .storage import GraphStore, ProjectManager
83
+
84
+ project_path = Path(path).resolve()
85
+ pm = ProjectManager()
86
+ name = project_path.name.replace(" ", "_")
87
+ project_dir = pm.create_or_get_project(name)
88
+
89
+ store = GraphStore(project_dir)
90
+ orchestrator = MCPOrchestrator(store)
91
+ stats = orchestrator.index(project_path)
92
+
93
+ from datetime import datetime
94
+ store.set_metadata({
95
+ **store.get_metadata(),
96
+ "project_name": name,
97
+ "source_path": str(project_path),
98
+ "indexed_at": datetime.now().isoformat(),
99
+ })
100
+ pm.set_current_project(name)
101
+ store.close()
102
+ return stats
103
+
104
+
105
+ def demo_search_based_on_project_type(project_type: str) -> None:
106
+ """Run a demo search relevant to the detected project type."""
107
+ demo_queries = {
108
+ "Python": "main entry point or application startup",
109
+ "JavaScript/TypeScript": "main entry point or app initialization",
110
+ "TypeScript": "main entry point or app initialization",
111
+ "Go": "main function or server startup",
112
+ "Rust": "main function or entry point",
113
+ "Java": "main class or application entry",
114
+ "Java/Kotlin": "main class or application entry",
115
+ "Kotlin": "main class or application entry",
116
+ "Ruby": "application controller or main module",
117
+ "PHP": "index or main controller",
118
+ "C/C++": "main function",
119
+ "C#/.NET": "program entry point",
120
+ }
121
+ query = demo_queries.get(project_type, "main entry point")
122
+
123
+ try:
124
+ from .storage import ProjectManager, GraphStore
125
+ from .orchestrator import MCPOrchestrator
126
+
127
+ pm = ProjectManager()
128
+ project = pm.get_current_project()
129
+ if not project:
130
+ return
131
+ project_dir = pm.project_dir(project)
132
+ store = GraphStore(project_dir)
133
+ orchestrator = MCPOrchestrator(store)
134
+ results = orchestrator.search(query, top_k=3)
135
+
136
+ if results:
137
+ console.print(f" [green]✓[/green] Found {len(results)} results for [cyan]'{query}'[/cyan]")
138
+ for item in results[:3]:
139
+ console.print(
140
+ f" [dim]•[/dim] [{item.node_type}] {item.qualname} "
141
+ f"[dim]score={item.score:.3f}[/dim]"
142
+ )
143
+ else:
144
+ console.print(" [yellow]No results yet — try a specific search after indexing[/yellow]")
145
+ store.close()
146
+ except Exception:
147
+ console.print(" [yellow]Demo search skipped (index may still be building)[/yellow]")
148
+
149
+
150
+ def show_quickstart_next_steps(project_type: str) -> None:
151
+ """Show contextual next-step suggestions after quickstart."""
152
+ suggestions = {
153
+ "Python": [
154
+ "[cyan]cg search[/cyan] 'database models' — Find code semantically",
155
+ "[cyan]cg chat start[/cyan] — Chat with AI about your code",
156
+ "[cyan]cg v2 generate[/cyan] 'add API endpoint' — Generate new code",
157
+ ],
158
+ "JavaScript/TypeScript": [
159
+ "[cyan]cg search[/cyan] 'API routes' — Find code semantically",
160
+ "[cyan]cg chat start[/cyan] — Chat with AI about your code",
161
+ "[cyan]cg v2 generate[/cyan] 'add REST endpoint' — Generate new code",
162
+ ],
163
+ "Go": [
164
+ "[cyan]cg search[/cyan] 'HTTP handler' — Find code semantically",
165
+ "[cyan]cg chat start[/cyan] — Chat with AI about your code",
166
+ "[cyan]cg v2 generate[/cyan] 'add gRPC service' — Generate new code",
167
+ ],
168
+ }
169
+ defaults = [
170
+ "[cyan]cg search[/cyan] 'your query' — Find code semantically",
171
+ "[cyan]cg chat start[/cyan] — Chat with AI about your code",
172
+ "[cyan]cg v2 generate[/cyan] 'description' — Generate new code",
173
+ ]
174
+ steps = suggestions.get(project_type, defaults)
175
+
176
+ console.print()
177
+ console.print(
178
+ Panel.fit(
179
+ "\n".join([f" {step}" for step in steps]),
180
+ title="[bold green]🎉 Try these commands next:[/bold green]",
181
+ border_style="green",
182
+ padding=(0, 1),
183
+ )
184
+ )
185
+ console.print()
186
+
187
+
188
+ @quickstart_app.command("run")
189
+ def quickstart(
190
+ path: str = typer.Argument(".", help="Path to the project to index."),
191
+ skip_setup: bool = typer.Option(False, "--skip-setup", help="Skip LLM setup even if not configured."),
192
+ skip_index: bool = typer.Option(False, "--skip-index", help="Skip auto-indexing."),
193
+ ):
194
+ """🚀 Quick setup wizard — get started in 30 seconds.
195
+
196
+ Example:
197
+ cg quickstart
198
+ cg quickstart ./my-project
199
+ cg quickstart --skip-setup
200
+ """
201
+ console.print()
202
+ console.print(
203
+ Panel.fit(
204
+ "[bold cyan]🚀 CodeGraph Quick Start[/bold cyan]\n"
205
+ "[dim]AI-powered code intelligence in 30 seconds[/dim]",
206
+ border_style="cyan",
207
+ )
208
+ )
209
+ console.print()
210
+
211
+ # 1. Detect project type
212
+ project_type = detect_project_type()
213
+ if project_type != "Unknown":
214
+ console.print(f" [green]✓[/green] Detected [bold]{project_type}[/bold] project")
215
+ else:
216
+ console.print(" [yellow]⚠[/yellow] Could not detect project type — will index all supported files")
217
+
218
+ # 2. Check if setup needed
219
+ if not skip_setup and not config_exists():
220
+ console.print()
221
+ console.print(" [yellow]⚙️ No LLM configured yet[/yellow]")
222
+ do_setup = typer.confirm(" Run interactive setup?", default=True)
223
+ if do_setup:
224
+ run_setup()
225
+ else:
226
+ console.print(" [dim]Skipped — using defaults (ollama/hash). Run 'cg config setup' later.[/dim]")
227
+ else:
228
+ console.print(f" [green]✓[/green] Configuration found")
229
+
230
+ # 3. Index current directory
231
+ if not skip_index:
232
+ console.print()
233
+ console.print(" [cyan]📊 Indexing your project...[/cyan]")
234
+ try:
235
+ stats = run_index(path)
236
+ if stats:
237
+ console.print(
238
+ f" [green]✓[/green] Indexed [bold]{stats['nodes']}[/bold] nodes "
239
+ f"and [bold]{stats['edges']}[/bold] edges"
240
+ )
241
+ else:
242
+ console.print(" [green]✓[/green] Indexing complete")
243
+ except Exception as e:
244
+ console.print(f" [red]✗[/red] Indexing failed: {e}")
245
+ console.print(" [dim]You can index manually later with: cg index <path>[/dim]")
246
+ else:
247
+ console.print(" [dim]Skipped indexing[/dim]")
248
+
249
+ # 4. Run demo search
250
+ if not skip_index:
251
+ console.print()
252
+ console.print(" [magenta]🔍 Running sample search...[/magenta]")
253
+ demo_search_based_on_project_type(project_type)
254
+
255
+ # 5. Show next steps
256
+ show_quickstart_next_steps(project_type)
@@ -37,7 +37,12 @@ def rename_symbol(
37
37
  preview_only: bool = typer.Option(False, "--preview", "-p", help="Preview changes without applying"),
38
38
  auto_apply: bool = typer.Option(False, "--auto-apply", "-y", help="Apply changes without confirmation"),
39
39
  ):
40
- """Rename a symbol and update all references."""
40
+ """✏️ Rename a symbol and update all references.
41
+
42
+ Example:
43
+ cg v2 refactor rename "old_function" "new_function"
44
+ cg v2 refactor rename "UserModel" "Account" --preview
45
+ """
41
46
  pm = ProjectManager()
42
47
  agent = _get_refactor_agent(pm)
43
48
 
@@ -106,7 +111,12 @@ def extract_function(
106
111
  preview_only: bool = typer.Option(False, "--preview", "-p", help="Preview changes without applying"),
107
112
  auto_apply: bool = typer.Option(False, "--auto-apply", "-y", help="Apply changes without confirmation"),
108
113
  ):
109
- """Extract code range into a new function."""
114
+ """📤 Extract code range into a new function.
115
+
116
+ Example:
117
+ cg v2 refactor extract-function src/handler.py 10 25 process_request
118
+ cg v2 refactor extract-function src/utils.py 5 15 validate_input --preview
119
+ """
110
120
  pm = ProjectManager()
111
121
  agent = _get_refactor_agent(pm)
112
122
 
@@ -169,7 +179,11 @@ def extract_service(
169
179
  preview_only: bool = typer.Option(False, "--preview", "-p", help="Preview changes without applying"),
170
180
  auto_apply: bool = typer.Option(False, "--auto-apply", "-y", help="Apply changes without confirmation"),
171
181
  ):
172
- """Extract multiple functions to a new service file."""
182
+ """📤 Extract multiple functions to a new service file.
183
+
184
+ Example:
185
+ cg v2 refactor extract-service send_email notify_user --target src/notifications.py
186
+ """
173
187
  pm = ProjectManager()
174
188
  agent = _get_refactor_agent(pm)
175
189