cortexcode 0.8.1__py3-none-any.whl → 0.9.1__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.
- cortexcode/ai_docs/doc_generator.py +5 -5
- cortexcode/cli/cli_index.py +499 -0
- cortexcode/cli/cli_servers.py +100 -37
- cortexcode/context/context_query.py +35 -7
- cortexcode/indexer.py +45 -1
- cortexcode/main.py +5 -1
- cortexcode/mcp/mcp_registry.py +1 -1
- {cortexcode-0.8.1.dist-info → cortexcode-0.9.1.dist-info}/METADATA +38 -44
- {cortexcode-0.8.1.dist-info → cortexcode-0.9.1.dist-info}/RECORD +13 -13
- {cortexcode-0.8.1.dist-info → cortexcode-0.9.1.dist-info}/WHEEL +0 -0
- {cortexcode-0.8.1.dist-info → cortexcode-0.9.1.dist-info}/entry_points.txt +0 -0
- {cortexcode-0.8.1.dist-info → cortexcode-0.9.1.dist-info}/licenses/LICENSE +0 -0
- {cortexcode-0.8.1.dist-info → cortexcode-0.9.1.dist-info}/top_level.txt +0 -0
|
@@ -60,7 +60,7 @@ class AIDocGenerator:
|
|
|
60
60
|
messages = prompts.generate_project_overview_prompt(index_data)
|
|
61
61
|
output.overview = self._generate(messages, "overview")
|
|
62
62
|
if output.overview:
|
|
63
|
-
(output_dir / "AI_OVERVIEW.md").write_text(output.overview)
|
|
63
|
+
(output_dir / "AI_OVERVIEW.md").write_text(output.overview, encoding="utf-8")
|
|
64
64
|
print(" -> AI_OVERVIEW.md")
|
|
65
65
|
|
|
66
66
|
if "api" in docs:
|
|
@@ -68,7 +68,7 @@ class AIDocGenerator:
|
|
|
68
68
|
messages = prompts.generate_api_docs_prompt(index_data)
|
|
69
69
|
output.api_docs = self._generate(messages, "api")
|
|
70
70
|
if output.api_docs:
|
|
71
|
-
(output_dir / "AI_API.md").write_text(output.api_docs)
|
|
71
|
+
(output_dir / "AI_API.md").write_text(output.api_docs, encoding="utf-8")
|
|
72
72
|
print(" -> AI_API.md")
|
|
73
73
|
|
|
74
74
|
if "architecture" in docs:
|
|
@@ -76,7 +76,7 @@ class AIDocGenerator:
|
|
|
76
76
|
messages = prompts.generate_architecture_prompt(index_data)
|
|
77
77
|
output.architecture = self._generate(messages, "architecture")
|
|
78
78
|
if output.architecture:
|
|
79
|
-
(output_dir / "AI_ARCHITECTURE.md").write_text(output.architecture)
|
|
79
|
+
(output_dir / "AI_ARCHITECTURE.md").write_text(output.architecture, encoding="utf-8")
|
|
80
80
|
print(" -> AI_ARCHITECTURE.md")
|
|
81
81
|
|
|
82
82
|
if "flows" in docs:
|
|
@@ -84,7 +84,7 @@ class AIDocGenerator:
|
|
|
84
84
|
messages = prompts.generate_flow_docs_prompt(index_data)
|
|
85
85
|
output.flows = self._generate(messages, "flows")
|
|
86
86
|
if output.flows:
|
|
87
|
-
(output_dir / "AI_FLOWS.md").write_text(output.flows)
|
|
87
|
+
(output_dir / "AI_FLOWS.md").write_text(output.flows, encoding="utf-8")
|
|
88
88
|
print(" -> AI_FLOWS.md")
|
|
89
89
|
|
|
90
90
|
return output
|
|
@@ -107,7 +107,7 @@ class AIDocGenerator:
|
|
|
107
107
|
result = self._generate(messages, f"module_{module_name}")
|
|
108
108
|
|
|
109
109
|
if output_path and result:
|
|
110
|
-
Path(output_path).write_text(result)
|
|
110
|
+
Path(output_path).write_text(result, encoding="utf-8")
|
|
111
111
|
|
|
112
112
|
return result
|
|
113
113
|
|
cortexcode/cli/cli_index.py
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
import json
|
|
1
2
|
import time
|
|
3
|
+
import sys
|
|
2
4
|
from pathlib import Path
|
|
3
5
|
|
|
6
|
+
import click
|
|
4
7
|
from rich import box
|
|
5
8
|
from rich.console import Console
|
|
6
9
|
from rich.panel import Panel
|
|
@@ -8,6 +11,497 @@ from rich.progress import BarColumn, Progress, SpinnerColumn, TaskProgressColumn
|
|
|
8
11
|
from rich.table import Table
|
|
9
12
|
|
|
10
13
|
|
|
14
|
+
def _load_questionary():
|
|
15
|
+
try:
|
|
16
|
+
import questionary
|
|
17
|
+
except ImportError:
|
|
18
|
+
return None
|
|
19
|
+
return questionary
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _load_user_config() -> dict:
|
|
23
|
+
config_path = Path.home() / ".cortexcode" / "config.json"
|
|
24
|
+
if not config_path.exists():
|
|
25
|
+
return {}
|
|
26
|
+
try:
|
|
27
|
+
return json.loads(config_path.read_text(encoding="utf-8"))
|
|
28
|
+
except Exception:
|
|
29
|
+
return {}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _save_user_config(config: dict) -> None:
|
|
33
|
+
config_path = Path.home() / ".cortexcode" / "config.json"
|
|
34
|
+
config_path.parent.mkdir(parents=True, exist_ok=True)
|
|
35
|
+
config_path.write_text(json.dumps(config, indent=2), encoding="utf-8")
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _load_post_index_preferences() -> dict:
|
|
39
|
+
config = _load_user_config()
|
|
40
|
+
prefs = config.get("post_index_wizard", {})
|
|
41
|
+
if isinstance(prefs, dict):
|
|
42
|
+
return prefs
|
|
43
|
+
return {}
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _save_post_index_preferences(preferences: dict) -> None:
|
|
47
|
+
config = _load_user_config()
|
|
48
|
+
config["post_index_wizard"] = preferences
|
|
49
|
+
_save_user_config(config)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _should_show_post_index_wizard(watch: bool, disabled: bool, force: bool) -> bool:
|
|
53
|
+
if force:
|
|
54
|
+
return True
|
|
55
|
+
preferences = _load_post_index_preferences()
|
|
56
|
+
return not disabled and preferences.get("enabled", True) and not watch and sys.stdin.isatty() and sys.stdout.isatty()
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _prompt_generate_now() -> bool:
|
|
60
|
+
questionary = _load_questionary()
|
|
61
|
+
if questionary:
|
|
62
|
+
answer = questionary.confirm("Generate docs, diagrams, reports, or setup now?", default=False).ask()
|
|
63
|
+
return bool(answer)
|
|
64
|
+
return click.confirm("Generate docs, diagrams, reports, or setup now?", default=False)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _get_recommended_targets(index_data: dict, preferences: dict) -> list[str]:
|
|
68
|
+
saved_targets = preferences.get("targets")
|
|
69
|
+
if isinstance(saved_targets, list) and saved_targets:
|
|
70
|
+
return saved_targets
|
|
71
|
+
|
|
72
|
+
recommended = ["docs", "diagrams", "report"]
|
|
73
|
+
profile = index_data.get("project_profile", {}) if isinstance(index_data, dict) else {}
|
|
74
|
+
route_count = profile.get("route_count", 0)
|
|
75
|
+
entity_count = profile.get("entity_count", 0)
|
|
76
|
+
files = index_data.get("files", {}) if isinstance(index_data, dict) else {}
|
|
77
|
+
symbol_count = sum(
|
|
78
|
+
len(file_data.get("symbols", [])) if isinstance(file_data, dict) else len(file_data)
|
|
79
|
+
for file_data in files.values()
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
if route_count or entity_count:
|
|
83
|
+
recommended.append("dashboard")
|
|
84
|
+
if 0 < symbol_count <= 500:
|
|
85
|
+
recommended.append("viz")
|
|
86
|
+
|
|
87
|
+
seen = []
|
|
88
|
+
for item in recommended:
|
|
89
|
+
if item not in seen:
|
|
90
|
+
seen.append(item)
|
|
91
|
+
return seen
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def _prompt_generation_targets(console: Console, defaults: list[str] | None = None, recommended: list[str] | None = None) -> list[str]:
|
|
95
|
+
core_options = [
|
|
96
|
+
("docs", "Project docs"),
|
|
97
|
+
("diagrams", "Mermaid diagrams"),
|
|
98
|
+
("report", "Interactive terminal report"),
|
|
99
|
+
("viz", "Interactive graph visualization"),
|
|
100
|
+
("dashboard", "Live dashboard"),
|
|
101
|
+
]
|
|
102
|
+
ai_options = [
|
|
103
|
+
("ai_docs", "AI docs"),
|
|
104
|
+
("wiki", "CodeWiki"),
|
|
105
|
+
]
|
|
106
|
+
setup_options = [
|
|
107
|
+
("mcp_setup", "Configure MCP for your editor"),
|
|
108
|
+
]
|
|
109
|
+
options = core_options + ai_options + setup_options
|
|
110
|
+
defaults = defaults or []
|
|
111
|
+
recommended = recommended or []
|
|
112
|
+
questionary = _load_questionary()
|
|
113
|
+
if questionary:
|
|
114
|
+
choices = [questionary.Separator("══ Core outputs ══")]
|
|
115
|
+
choices.extend(
|
|
116
|
+
questionary.Choice(
|
|
117
|
+
title=f"{label}{' [recommended]' if value in recommended else ''}",
|
|
118
|
+
value=value,
|
|
119
|
+
checked=value in defaults,
|
|
120
|
+
)
|
|
121
|
+
for value, label in core_options
|
|
122
|
+
)
|
|
123
|
+
choices.append(questionary.Separator("══ AI-powered ══"))
|
|
124
|
+
choices.extend(
|
|
125
|
+
questionary.Choice(
|
|
126
|
+
title=f"{label}{' [recommended]' if value in recommended else ''}",
|
|
127
|
+
value=value,
|
|
128
|
+
checked=value in defaults,
|
|
129
|
+
)
|
|
130
|
+
for value, label in ai_options
|
|
131
|
+
)
|
|
132
|
+
choices.append(questionary.Separator("══ Setup ══"))
|
|
133
|
+
choices.extend(
|
|
134
|
+
questionary.Choice(
|
|
135
|
+
title=f"{label}{' [recommended]' if value in recommended else ''}",
|
|
136
|
+
value=value,
|
|
137
|
+
checked=value in defaults,
|
|
138
|
+
)
|
|
139
|
+
for value, label in setup_options
|
|
140
|
+
)
|
|
141
|
+
selections = questionary.checkbox(
|
|
142
|
+
"Select what to generate",
|
|
143
|
+
choices=choices,
|
|
144
|
+
validate=lambda items: True if items else "Select at least one option.",
|
|
145
|
+
).ask()
|
|
146
|
+
return selections or []
|
|
147
|
+
|
|
148
|
+
console.print("\n[bold]Select what to generate:[/bold]")
|
|
149
|
+
for idx, (_, label) in enumerate(options, 1):
|
|
150
|
+
suffix = " [recommended]" if options[idx - 1][0] in recommended else ""
|
|
151
|
+
console.print(f" {idx}. {label}{suffix}")
|
|
152
|
+
default_indexes = [str(idx) for idx, (value, _) in enumerate(options, 1) if value in defaults]
|
|
153
|
+
raw_value = click.prompt(
|
|
154
|
+
"Enter choices as comma-separated numbers",
|
|
155
|
+
default=",".join(default_indexes) if default_indexes else "1,2",
|
|
156
|
+
show_default=True,
|
|
157
|
+
)
|
|
158
|
+
selected = []
|
|
159
|
+
for part in raw_value.split(","):
|
|
160
|
+
part = part.strip()
|
|
161
|
+
if not part.isdigit():
|
|
162
|
+
continue
|
|
163
|
+
index = int(part) - 1
|
|
164
|
+
if 0 <= index < len(options):
|
|
165
|
+
selected.append(options[index][0])
|
|
166
|
+
seen = []
|
|
167
|
+
for item in selected:
|
|
168
|
+
if item not in seen:
|
|
169
|
+
seen.append(item)
|
|
170
|
+
return seen
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def _prompt_provider(default: str | None = None) -> str:
|
|
174
|
+
from cortexcode.ai_docs.config import get_config
|
|
175
|
+
|
|
176
|
+
provider = default or get_config().provider or "google"
|
|
177
|
+
choices = ["google", "openai", "anthropic", "ollama"]
|
|
178
|
+
questionary = _load_questionary()
|
|
179
|
+
if questionary:
|
|
180
|
+
answer = questionary.select("Choose provider", choices=choices, default=provider).ask()
|
|
181
|
+
return answer or provider
|
|
182
|
+
return click.prompt("Choose provider", type=click.Choice(choices), default=provider, show_choices=True)
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def _ensure_provider_access(console: Console, provider: str) -> bool:
|
|
186
|
+
from cortexcode.ai_docs.config import AIConfig, get_api_key, get_config, set_api_key, set_config
|
|
187
|
+
|
|
188
|
+
config = get_config()
|
|
189
|
+
set_config(AIConfig(provider=provider, model=config.model, temperature=config.temperature, max_tokens=config.max_tokens))
|
|
190
|
+
|
|
191
|
+
if provider == "ollama":
|
|
192
|
+
return True
|
|
193
|
+
|
|
194
|
+
if get_api_key(provider):
|
|
195
|
+
return True
|
|
196
|
+
|
|
197
|
+
provider_labels = {
|
|
198
|
+
"google": ("Gemini", "https://aistudio.google.com/apikey"),
|
|
199
|
+
"openai": ("OpenAI", "https://platform.openai.com/api-keys"),
|
|
200
|
+
"anthropic": ("Anthropic", "https://console.anthropic.com/settings/keys"),
|
|
201
|
+
}
|
|
202
|
+
label, url = provider_labels.get(provider, (provider, ""))
|
|
203
|
+
console.print(
|
|
204
|
+
f"\n[yellow]No API key found for {label}.[/yellow]\n"
|
|
205
|
+
f"[dim]Get your key at:[/dim] [cyan]{url}[/cyan]\n"
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
questionary = _load_questionary()
|
|
209
|
+
if questionary:
|
|
210
|
+
api_key = questionary.password(f"Enter your {label} API key (leave blank to skip)").ask() or ""
|
|
211
|
+
else:
|
|
212
|
+
api_key = click.prompt(
|
|
213
|
+
f"Enter your {label} API key (leave blank to skip)",
|
|
214
|
+
default="",
|
|
215
|
+
show_default=False,
|
|
216
|
+
hide_input=True,
|
|
217
|
+
).strip()
|
|
218
|
+
|
|
219
|
+
if not api_key:
|
|
220
|
+
console.print(f"[dim]Skipped. You can set it later with:[/dim] [cyan]cortexcode config set {provider}_api_key YOUR_KEY[/cyan]\n")
|
|
221
|
+
return False
|
|
222
|
+
|
|
223
|
+
set_api_key(provider, api_key)
|
|
224
|
+
console.print(f"[green]✓[/green] API key saved for {label}.\n")
|
|
225
|
+
return True
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def _prompt_report_type(available_reports: list[str], default: str | None = None) -> str:
|
|
229
|
+
questionary = _load_questionary()
|
|
230
|
+
selected_default = default if default in available_reports else (available_reports[0] if available_reports else "overview")
|
|
231
|
+
if questionary:
|
|
232
|
+
answer = questionary.select("Choose report type", choices=available_reports, default=selected_default).ask()
|
|
233
|
+
return answer or selected_default
|
|
234
|
+
return click.prompt(
|
|
235
|
+
"Choose report type",
|
|
236
|
+
type=click.Choice(available_reports),
|
|
237
|
+
default=selected_default,
|
|
238
|
+
show_choices=True,
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def _prompt_dashboard_port(default: int) -> int:
|
|
243
|
+
questionary = _load_questionary()
|
|
244
|
+
if questionary:
|
|
245
|
+
answer = questionary.text("Dashboard port", default=str(default), validate=lambda value: value.isdigit() or "Enter a valid port").ask()
|
|
246
|
+
return int(answer or default)
|
|
247
|
+
return int(click.prompt("Dashboard port", default=default, type=int, show_default=True))
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def _prompt_diagram_types(defaults: list[str]) -> list[str]:
|
|
251
|
+
diagram_types = ["call_graph", "class", "sequence", "architecture", "imports", "dependencies", "entities", "file_tree"]
|
|
252
|
+
questionary = _load_questionary()
|
|
253
|
+
if questionary:
|
|
254
|
+
selections = questionary.checkbox(
|
|
255
|
+
"Choose diagram types",
|
|
256
|
+
choices=[questionary.Choice(title=item, value=item, checked=item in defaults) for item in diagram_types],
|
|
257
|
+
validate=lambda items: True if items else "Select at least one diagram type.",
|
|
258
|
+
).ask()
|
|
259
|
+
return selections or defaults
|
|
260
|
+
|
|
261
|
+
raw_value = click.prompt(
|
|
262
|
+
"Choose diagram types (comma-separated)",
|
|
263
|
+
default=",".join(defaults),
|
|
264
|
+
show_default=True,
|
|
265
|
+
)
|
|
266
|
+
values = [item.strip() for item in raw_value.split(",") if item.strip() in diagram_types]
|
|
267
|
+
return values or defaults
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def _prompt_ai_doc_types(defaults: list[str]) -> list[str]:
|
|
271
|
+
doc_types = ["overview", "api", "architecture", "flows"]
|
|
272
|
+
questionary = _load_questionary()
|
|
273
|
+
if questionary:
|
|
274
|
+
selections = questionary.checkbox(
|
|
275
|
+
"Choose AI doc types",
|
|
276
|
+
choices=[questionary.Choice(title=item, value=item, checked=item in defaults) for item in doc_types],
|
|
277
|
+
validate=lambda items: True if items else "Select at least one AI doc type.",
|
|
278
|
+
).ask()
|
|
279
|
+
return selections or defaults
|
|
280
|
+
|
|
281
|
+
raw_value = click.prompt(
|
|
282
|
+
"Choose AI doc types (comma-separated)",
|
|
283
|
+
default=",".join(defaults),
|
|
284
|
+
show_default=True,
|
|
285
|
+
)
|
|
286
|
+
values = [item.strip() for item in raw_value.split(",") if item.strip() in doc_types]
|
|
287
|
+
return values or defaults
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
def _prompt_confirm(message: str, default: bool) -> bool:
|
|
291
|
+
questionary = _load_questionary()
|
|
292
|
+
if questionary:
|
|
293
|
+
answer = questionary.confirm(message, default=default).ask()
|
|
294
|
+
return bool(answer)
|
|
295
|
+
return click.confirm(message, default=default)
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
def _prompt_max_modules(default: int) -> int:
|
|
299
|
+
questionary = _load_questionary()
|
|
300
|
+
if questionary:
|
|
301
|
+
answer = questionary.text("Max module pages", default=str(default), validate=lambda value: value.isdigit() or "Enter a valid number").ask()
|
|
302
|
+
return int(answer or default)
|
|
303
|
+
return int(click.prompt("Max module pages", default=default, type=int, show_default=True))
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
def _prompt_save_preferences() -> bool:
|
|
307
|
+
questionary = _load_questionary()
|
|
308
|
+
if questionary:
|
|
309
|
+
answer = questionary.confirm("Save these choices as defaults for future index runs?", default=True).ask()
|
|
310
|
+
return bool(answer)
|
|
311
|
+
return click.confirm("Save these choices as defaults for future index runs?", default=True)
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
def _prompt_show_wizard_next_time(default: bool) -> bool:
|
|
315
|
+
questionary = _load_questionary()
|
|
316
|
+
if questionary:
|
|
317
|
+
answer = questionary.confirm("Show this wizard automatically after future index runs?", default=default).ask()
|
|
318
|
+
return bool(answer)
|
|
319
|
+
return click.confirm("Show this wizard automatically after future index runs?", default=default)
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
def _run_post_index_wizard(console: Console, path: Path, index_data: dict) -> None:
|
|
323
|
+
import webbrowser
|
|
324
|
+
|
|
325
|
+
from cortexcode import indexer
|
|
326
|
+
from cortexcode.cli import handle_ai_docs_command, handle_dashboard_command, handle_diagrams_command, handle_docs_command, handle_wiki_command
|
|
327
|
+
from cortexcode.cli.cli_servers import handle_mcp_setup
|
|
328
|
+
from cortexcode.cli.cli_support import require_ai_doc_generator, require_index_path
|
|
329
|
+
from cortexcode.docs import generate_all_docs
|
|
330
|
+
from cortexcode.docs.diagrams import save_diagrams
|
|
331
|
+
from cortexcode.dashboard import DashboardServer
|
|
332
|
+
from cortexcode.reports.site.viz import generate_viz_html
|
|
333
|
+
from cortexcode.terminal.completion import print_ai_docs_complete, print_diagrams_complete, print_docs_complete
|
|
334
|
+
from cortexcode.terminal.headers import print_ai_docs_header, print_diagrams_header, print_docs_header
|
|
335
|
+
from cortexcode.terminal.reports import get_available_reports, print_terminal_report
|
|
336
|
+
|
|
337
|
+
preferences = _load_post_index_preferences()
|
|
338
|
+
if not _prompt_generate_now():
|
|
339
|
+
return
|
|
340
|
+
|
|
341
|
+
recommended_targets = _get_recommended_targets(index_data, preferences)
|
|
342
|
+
default_targets = preferences.get("targets") or recommended_targets
|
|
343
|
+
targets = _prompt_generation_targets(console, default_targets, recommended_targets)
|
|
344
|
+
if not targets:
|
|
345
|
+
return
|
|
346
|
+
|
|
347
|
+
provider = preferences.get("provider")
|
|
348
|
+
if any(target in {"ai_docs", "wiki"} for target in targets):
|
|
349
|
+
provider = _prompt_provider(provider)
|
|
350
|
+
if not _ensure_provider_access(console, provider):
|
|
351
|
+
targets = [target for target in targets if target not in {"ai_docs", "wiki"}]
|
|
352
|
+
if not targets:
|
|
353
|
+
return
|
|
354
|
+
|
|
355
|
+
report_type = preferences.get("report_type")
|
|
356
|
+
if "report" in targets:
|
|
357
|
+
available_reports = get_available_reports(index_data, ["overview", "tech", "hotspots", "routes", "entities", "frontend", "cli"])
|
|
358
|
+
report_type = _prompt_report_type(available_reports, report_type)
|
|
359
|
+
|
|
360
|
+
dashboard_port = int(preferences.get("dashboard_port", 8787))
|
|
361
|
+
if "dashboard" in targets:
|
|
362
|
+
dashboard_port = _prompt_dashboard_port(dashboard_port)
|
|
363
|
+
|
|
364
|
+
docs_open_browser = bool(preferences.get("open_docs_browser", False))
|
|
365
|
+
if "docs" in targets:
|
|
366
|
+
docs_open_browser = _prompt_confirm("Open generated docs in browser?", docs_open_browser)
|
|
367
|
+
|
|
368
|
+
recommended_diagrams = index_data.get("project_profile", {}).get("recommendations", {}).get("diagrams", [])
|
|
369
|
+
diagram_defaults = preferences.get("diagram_types") or [item for item in recommended_diagrams if item in ["call_graph", "class", "sequence", "architecture", "imports", "dependencies", "entities", "file_tree"]] or ["architecture", "call_graph"]
|
|
370
|
+
selected_diagram_types = diagram_defaults
|
|
371
|
+
if "diagrams" in targets:
|
|
372
|
+
selected_diagram_types = _prompt_diagram_types(diagram_defaults)
|
|
373
|
+
|
|
374
|
+
ai_doc_defaults = preferences.get("ai_doc_types") or ["overview", "api", "architecture", "flows"]
|
|
375
|
+
selected_ai_doc_types = ai_doc_defaults
|
|
376
|
+
if "ai_docs" in targets:
|
|
377
|
+
selected_ai_doc_types = _prompt_ai_doc_types(ai_doc_defaults)
|
|
378
|
+
|
|
379
|
+
wiki_open_browser = bool(preferences.get("open_wiki_browser", False))
|
|
380
|
+
wiki_include_modules = bool(preferences.get("wiki_include_modules", True))
|
|
381
|
+
wiki_max_modules = int(preferences.get("wiki_max_modules", 15))
|
|
382
|
+
if "wiki" in targets:
|
|
383
|
+
wiki_open_browser = _prompt_confirm("Open generated CodeWiki in browser?", wiki_open_browser)
|
|
384
|
+
wiki_include_modules = _prompt_confirm("Include per-module wiki pages?", wiki_include_modules)
|
|
385
|
+
if wiki_include_modules:
|
|
386
|
+
wiki_max_modules = _prompt_max_modules(wiki_max_modules)
|
|
387
|
+
|
|
388
|
+
viz_open_browser = bool(preferences.get("open_viz_browser", True))
|
|
389
|
+
if "viz" in targets:
|
|
390
|
+
viz_open_browser = _prompt_confirm("Open graph visualization in browser?", viz_open_browser)
|
|
391
|
+
|
|
392
|
+
wizard_enabled = _prompt_show_wizard_next_time(bool(preferences.get("enabled", True)))
|
|
393
|
+
|
|
394
|
+
if _prompt_save_preferences():
|
|
395
|
+
_save_post_index_preferences(
|
|
396
|
+
{
|
|
397
|
+
"targets": targets,
|
|
398
|
+
"provider": provider,
|
|
399
|
+
"report_type": report_type,
|
|
400
|
+
"dashboard_port": dashboard_port,
|
|
401
|
+
"diagram_types": selected_diagram_types,
|
|
402
|
+
"ai_doc_types": selected_ai_doc_types,
|
|
403
|
+
"open_docs_browser": docs_open_browser,
|
|
404
|
+
"open_wiki_browser": wiki_open_browser,
|
|
405
|
+
"wiki_include_modules": wiki_include_modules,
|
|
406
|
+
"wiki_max_modules": wiki_max_modules,
|
|
407
|
+
"open_viz_browser": viz_open_browser,
|
|
408
|
+
"enabled": wizard_enabled,
|
|
409
|
+
}
|
|
410
|
+
)
|
|
411
|
+
|
|
412
|
+
if "docs" in targets:
|
|
413
|
+
handle_docs_command(
|
|
414
|
+
console,
|
|
415
|
+
path,
|
|
416
|
+
path / ".cortexcode" / "docs",
|
|
417
|
+
docs_open_browser,
|
|
418
|
+
require_index_path,
|
|
419
|
+
print_docs_header,
|
|
420
|
+
generate_all_docs,
|
|
421
|
+
print_docs_complete,
|
|
422
|
+
)
|
|
423
|
+
|
|
424
|
+
if "diagrams" in targets:
|
|
425
|
+
output_dir = path / ".cortexcode" / "diagrams"
|
|
426
|
+
diagram_types = ["call_graph", "class", "sequence", "architecture", "imports", "dependencies", "entities", "file_tree"]
|
|
427
|
+
if len(selected_diagram_types) == 1:
|
|
428
|
+
handle_diagrams_command(
|
|
429
|
+
console,
|
|
430
|
+
path,
|
|
431
|
+
output_dir,
|
|
432
|
+
selected_diagram_types[0],
|
|
433
|
+
require_index_path,
|
|
434
|
+
print_diagrams_header,
|
|
435
|
+
save_diagrams,
|
|
436
|
+
diagram_types,
|
|
437
|
+
print_diagrams_complete,
|
|
438
|
+
)
|
|
439
|
+
elif len(selected_diagram_types) == len(diagram_types):
|
|
440
|
+
handle_diagrams_command(
|
|
441
|
+
console,
|
|
442
|
+
path,
|
|
443
|
+
output_dir,
|
|
444
|
+
None,
|
|
445
|
+
require_index_path,
|
|
446
|
+
print_diagrams_header,
|
|
447
|
+
save_diagrams,
|
|
448
|
+
diagram_types,
|
|
449
|
+
print_diagrams_complete,
|
|
450
|
+
)
|
|
451
|
+
else:
|
|
452
|
+
_, index_path = require_index_path(console, path)
|
|
453
|
+
print_diagrams_header(console, path)
|
|
454
|
+
for diagram_type in selected_diagram_types:
|
|
455
|
+
save_diagrams(index_path, output_dir, diagram_type=diagram_type)
|
|
456
|
+
generated_files = ["DIAGRAMS.md", *[f"{item}.mmd" for item in selected_diagram_types]]
|
|
457
|
+
print_diagrams_complete(console, output_dir, generated_files)
|
|
458
|
+
|
|
459
|
+
if "ai_docs" in targets:
|
|
460
|
+
handle_ai_docs_command(
|
|
461
|
+
console,
|
|
462
|
+
path,
|
|
463
|
+
path / ".cortexcode" / "ai-docs",
|
|
464
|
+
provider,
|
|
465
|
+
None,
|
|
466
|
+
tuple(selected_ai_doc_types),
|
|
467
|
+
require_ai_doc_generator,
|
|
468
|
+
require_index_path,
|
|
469
|
+
print_ai_docs_header,
|
|
470
|
+
print_ai_docs_complete,
|
|
471
|
+
)
|
|
472
|
+
|
|
473
|
+
if "wiki" in targets:
|
|
474
|
+
handle_wiki_command(
|
|
475
|
+
console,
|
|
476
|
+
path,
|
|
477
|
+
path / ".cortexcode" / "wiki",
|
|
478
|
+
provider,
|
|
479
|
+
None,
|
|
480
|
+
None,
|
|
481
|
+
not wiki_include_modules,
|
|
482
|
+
wiki_max_modules,
|
|
483
|
+
wiki_open_browser,
|
|
484
|
+
)
|
|
485
|
+
|
|
486
|
+
if "report" in targets:
|
|
487
|
+
print_terminal_report(console, report_type or "overview", index_data, path)
|
|
488
|
+
|
|
489
|
+
if "viz" in targets:
|
|
490
|
+
viz_path = path / ".cortexcode" / "graph.html"
|
|
491
|
+
generate_viz_html(index_data, viz_path)
|
|
492
|
+
if viz_open_browser:
|
|
493
|
+
console.print(f"[cyan]Opening visualization:[/cyan] {viz_path}")
|
|
494
|
+
webbrowser.open(viz_path.as_uri())
|
|
495
|
+
else:
|
|
496
|
+
console.print(f"[green]✓[/green] Visualization generated: {viz_path}")
|
|
497
|
+
|
|
498
|
+
if "mcp_setup" in targets:
|
|
499
|
+
handle_mcp_setup(console)
|
|
500
|
+
|
|
501
|
+
if "dashboard" in targets:
|
|
502
|
+
handle_dashboard_command(console, path, dashboard_port, indexer, DashboardServer)
|
|
503
|
+
|
|
504
|
+
|
|
11
505
|
def handle_index_command(
|
|
12
506
|
console: Console,
|
|
13
507
|
path: str | Path,
|
|
@@ -20,6 +514,8 @@ def handle_index_command(
|
|
|
20
514
|
include,
|
|
21
515
|
root,
|
|
22
516
|
dry_run: bool,
|
|
517
|
+
no_post_index_wizard: bool,
|
|
518
|
+
force_wizard: bool,
|
|
23
519
|
indexer_module,
|
|
24
520
|
print_index_header,
|
|
25
521
|
print_project_profile_summary,
|
|
@@ -104,6 +600,9 @@ def handle_index_command(
|
|
|
104
600
|
if verbose:
|
|
105
601
|
show_index_summary(console, index_data)
|
|
106
602
|
|
|
603
|
+
if _should_show_post_index_wizard(watch, no_post_index_wizard, force_wizard):
|
|
604
|
+
_run_post_index_wizard(console, path, index_data)
|
|
605
|
+
|
|
107
606
|
if watch:
|
|
108
607
|
console.print("\n[yellow]Starting watcher...[/yellow]")
|
|
109
608
|
console.print("[dim]Press Ctrl+C to stop[/dim]")
|
cortexcode/cli/cli_servers.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import os
|
|
3
3
|
import shutil
|
|
4
|
+
import platform
|
|
4
5
|
from pathlib import Path
|
|
5
6
|
|
|
6
7
|
from rich.console import Console
|
|
@@ -10,68 +11,103 @@ from rich.prompt import Prompt
|
|
|
10
11
|
IDE_CONFIGS = {
|
|
11
12
|
"vscode": {
|
|
12
13
|
"name": "VS Code",
|
|
13
|
-
"file": ".vscode/
|
|
14
|
-
"
|
|
14
|
+
"file": ".vscode/mcp.json",
|
|
15
|
+
"global_file": "~/.vscode/mcp.json",
|
|
16
|
+
"config": {"servers": {"cortexcode": {"command": "cortexcode", "args": ["mcp", "start"]}}},
|
|
15
17
|
},
|
|
16
18
|
"cursor": {
|
|
17
19
|
"name": "Cursor",
|
|
18
|
-
"file": ".cursor/
|
|
19
|
-
"
|
|
20
|
+
"file": ".cursor/mcp.json",
|
|
21
|
+
"global_file": "~/.cursor/mcp.json",
|
|
22
|
+
"config": {"servers": {"cortexcode": {"command": "cortexcode", "args": ["mcp", "start"]}}},
|
|
20
23
|
},
|
|
21
24
|
"windsurf": {
|
|
22
25
|
"name": "Windsurf",
|
|
23
|
-
"file": ".windsurf/
|
|
24
|
-
"
|
|
26
|
+
"file": ".codeium/windsurf/mcp_config.json",
|
|
27
|
+
"global_file": "~/.codeium/windsurf/mcp_config.json",
|
|
28
|
+
"config": {"mcpServers": {"cortexcode": {"command": "cortexcode", "args": ["mcp", "start"]}}},
|
|
25
29
|
},
|
|
26
30
|
"claude": {
|
|
27
|
-
"name": "Claude
|
|
28
|
-
"file":
|
|
29
|
-
"
|
|
31
|
+
"name": "Claude Code",
|
|
32
|
+
"file": None,
|
|
33
|
+
"global_file": None,
|
|
34
|
+
"config": None,
|
|
35
|
+
"cli_command": "claude mcp add cortexcode stdio -- cortexcode mcp start",
|
|
30
36
|
},
|
|
31
37
|
"cline": {
|
|
32
38
|
"name": "Cline",
|
|
33
|
-
"file": ".cline/
|
|
34
|
-
"
|
|
39
|
+
"file": ".cline/mcp.json",
|
|
40
|
+
"global_file": "~/.cline/mcp.json",
|
|
41
|
+
"config": {"mcpServers": {"cortexcode": {"command": "cortexcode", "args": ["mcp", "start"]}}},
|
|
35
42
|
},
|
|
36
43
|
"roocode": {
|
|
37
44
|
"name": "RooCode",
|
|
38
|
-
"file": ".roocode/
|
|
39
|
-
"
|
|
45
|
+
"file": ".roocode/mcp.json",
|
|
46
|
+
"global_file": "~/.roocode/mcp.json",
|
|
47
|
+
"config": {"mcpServers": {"cortexcode": {"command": "cortexcode", "args": ["mcp", "start"]}}},
|
|
40
48
|
},
|
|
41
|
-
"
|
|
42
|
-
"name": "
|
|
43
|
-
"file": ".
|
|
44
|
-
"
|
|
49
|
+
"opencode": {
|
|
50
|
+
"name": "OpenCode",
|
|
51
|
+
"file": "opencode.json",
|
|
52
|
+
"global_file": "~/.opencode/opencode.json",
|
|
53
|
+
"config": {"mcp": {"cortexcode": {"type": "local", "command": ["cortexcode", "mcp", "start"], "enabled": True}}},
|
|
45
54
|
},
|
|
46
|
-
"
|
|
47
|
-
"name": "
|
|
48
|
-
"file": ".
|
|
49
|
-
"
|
|
55
|
+
"antigravity": {
|
|
56
|
+
"name": "Antigravity",
|
|
57
|
+
"file": ".antigravity/mcp.json",
|
|
58
|
+
"global_file": "~/.antigravity/mcp.json",
|
|
59
|
+
"config": {"mcpServers": {"cortexcode": {"command": "cortexcode", "args": ["mcp", "start"]}}},
|
|
50
60
|
},
|
|
51
61
|
}
|
|
52
62
|
|
|
53
63
|
|
|
64
|
+
def get_claude_config_path() -> Path:
|
|
65
|
+
"""Get Claude Desktop config path based on OS."""
|
|
66
|
+
home = Path.home()
|
|
67
|
+
system = platform.system()
|
|
68
|
+
|
|
69
|
+
if system == "Windows":
|
|
70
|
+
return home / "AppData" / "Roaming" / "Claude" / "claude_desktop_config.json"
|
|
71
|
+
elif system == "Darwin": # macOS
|
|
72
|
+
return home / "Library" / "Application Support" / "Claude" / "claude_desktop_config.json"
|
|
73
|
+
else: # Linux
|
|
74
|
+
return home / ".config" / "Claude" / "claude_desktop_config.json"
|
|
75
|
+
|
|
76
|
+
|
|
54
77
|
def detect_ide() -> list[str]:
|
|
55
78
|
"""Detect which IDEs are available in the current environment."""
|
|
56
79
|
detected = []
|
|
57
80
|
home = Path.home()
|
|
58
81
|
|
|
59
|
-
|
|
82
|
+
# VS Code / Cursor mcp.json
|
|
83
|
+
if (home / ".vscode/mcp.json").exists() or (Path.cwd() / ".vscode/mcp.json").exists():
|
|
60
84
|
detected.append("vscode")
|
|
61
|
-
if (home / ".cursor/
|
|
85
|
+
if (home / ".cursor/mcp.json").exists():
|
|
62
86
|
detected.append("cursor")
|
|
63
|
-
|
|
87
|
+
|
|
88
|
+
# Windsurf
|
|
89
|
+
if (home / ".codeium/windsurf/mcp_config.json").exists():
|
|
64
90
|
detected.append("windsurf")
|
|
65
|
-
|
|
91
|
+
|
|
92
|
+
# Claude Desktop
|
|
93
|
+
if get_claude_config_path().exists():
|
|
66
94
|
detected.append("claude")
|
|
67
|
-
|
|
95
|
+
|
|
96
|
+
# Cline
|
|
97
|
+
if (home / ".cline/mcp.json").exists():
|
|
68
98
|
detected.append("cline")
|
|
69
|
-
|
|
99
|
+
|
|
100
|
+
# RooCode
|
|
101
|
+
if (home / ".roocode/mcp.json").exists():
|
|
70
102
|
detected.append("roocode")
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
if (home / ".
|
|
74
|
-
detected.append("
|
|
103
|
+
|
|
104
|
+
# OpenCode
|
|
105
|
+
if (Path.cwd() / "opencode.json").exists() or (home / ".opencode/opencode.json").exists():
|
|
106
|
+
detected.append("opencode")
|
|
107
|
+
|
|
108
|
+
# Antigravity
|
|
109
|
+
if (home / ".antigravity/mcp.json").exists():
|
|
110
|
+
detected.append("antigravity")
|
|
75
111
|
|
|
76
112
|
return detected
|
|
77
113
|
|
|
@@ -112,12 +148,36 @@ def handle_mcp_setup(console: Console) -> None:
|
|
|
112
148
|
return
|
|
113
149
|
|
|
114
150
|
for target in targets:
|
|
151
|
+
ide = IDE_CONFIGS.get(target)
|
|
152
|
+
|
|
153
|
+
if target == "claude":
|
|
154
|
+
console.print(f"\n[yellow]Claude Code:[/yellow] Run this command manually:")
|
|
155
|
+
console.print(f" [cyan]{ide.get('cli_command')}[/cyan]")
|
|
156
|
+
console.print("[dim]Then restart Claude Code[/dim]")
|
|
157
|
+
continue
|
|
158
|
+
|
|
159
|
+
if target == "opencode":
|
|
160
|
+
config_path = Path.cwd() / ide["file"]
|
|
161
|
+
config_path.parent.mkdir(parents=True, exist_ok=True)
|
|
162
|
+
|
|
163
|
+
existing = {}
|
|
164
|
+
if config_path.exists():
|
|
165
|
+
try:
|
|
166
|
+
existing = json.loads(config_path.read_text())
|
|
167
|
+
except:
|
|
168
|
+
pass
|
|
169
|
+
|
|
170
|
+
if "mcp" not in existing:
|
|
171
|
+
existing["mcp"] = {}
|
|
172
|
+
existing["mcp"]["cortexcode"] = ide["config"]["mcp"]["cortexcode"]
|
|
173
|
+
|
|
174
|
+
config_path.write_text(json.dumps(existing, indent=2))
|
|
175
|
+
console.print(f"[green]✓[/green] Updated {config_path}")
|
|
176
|
+
continue
|
|
177
|
+
|
|
115
178
|
if target.startswith("custom:"):
|
|
116
179
|
config_path = Path(target.split(":", 1)[1])
|
|
117
180
|
else:
|
|
118
|
-
ide = IDE_CONFIGS.get(target)
|
|
119
|
-
if not ide:
|
|
120
|
-
continue
|
|
121
181
|
config_path = Path.cwd() / ide["file"]
|
|
122
182
|
|
|
123
183
|
config_path.parent.mkdir(parents=True, exist_ok=True)
|
|
@@ -129,15 +189,18 @@ def handle_mcp_setup(console: Console) -> None:
|
|
|
129
189
|
except:
|
|
130
190
|
pass
|
|
131
191
|
|
|
132
|
-
|
|
192
|
+
# VS Code/Cursor use "servers", others use "mcpServers"
|
|
193
|
+
if target in ("vscode", "cursor"):
|
|
194
|
+
mcp_key = "servers"
|
|
195
|
+
else:
|
|
196
|
+
mcp_key = "mcpServers"
|
|
197
|
+
|
|
133
198
|
if mcp_key not in existing:
|
|
134
199
|
existing[mcp_key] = {}
|
|
135
200
|
|
|
136
201
|
existing[mcp_key]["cortexcode"] = {
|
|
137
202
|
"command": "cortexcode",
|
|
138
203
|
"args": ["mcp"],
|
|
139
|
-
"disabled": False,
|
|
140
|
-
"alwaysAllow": [],
|
|
141
204
|
}
|
|
142
205
|
|
|
143
206
|
config_path.write_text(json.dumps(existing, indent=2))
|
|
@@ -15,16 +15,17 @@ def get_context(index_path: Path, query: str | None = None, num_results: int = 5
|
|
|
15
15
|
num_results: Number of results to return
|
|
16
16
|
|
|
17
17
|
Returns:
|
|
18
|
-
Dictionary with relevant symbols and their
|
|
18
|
+
Dictionary with relevant symbols and their code snippets
|
|
19
19
|
"""
|
|
20
20
|
index = json.loads(index_path.read_text(encoding="utf-8"))
|
|
21
21
|
|
|
22
22
|
files = index.get("files", {})
|
|
23
23
|
call_graph = index.get("call_graph", {})
|
|
24
24
|
file_deps = index.get("file_dependencies", {})
|
|
25
|
+
source_code = index.get("source_code", {})
|
|
25
26
|
|
|
26
27
|
if not query:
|
|
27
|
-
return _get_all_symbols(files, call_graph, num_results)
|
|
28
|
+
return _get_all_symbols(files, call_graph, source_code, num_results)
|
|
28
29
|
|
|
29
30
|
file_filter = None
|
|
30
31
|
query_lower = query.lower()
|
|
@@ -44,11 +45,11 @@ def get_context(index_path: Path, query: str | None = None, num_results: int = 5
|
|
|
44
45
|
|
|
45
46
|
for sym in symbols:
|
|
46
47
|
if file_filter and not query_lower:
|
|
47
|
-
results.append(_build_symbol_result(sym, rel_path, call_graph))
|
|
48
|
+
results.append(_build_symbol_result(sym, rel_path, call_graph, source_code))
|
|
48
49
|
continue
|
|
49
50
|
|
|
50
51
|
if _matches_query(sym, query_lower):
|
|
51
|
-
results.append(_build_symbol_result(sym, rel_path, call_graph))
|
|
52
|
+
results.append(_build_symbol_result(sym, rel_path, call_graph, source_code))
|
|
52
53
|
|
|
53
54
|
if query_lower:
|
|
54
55
|
for imp in imports:
|
|
@@ -79,8 +80,8 @@ def get_context(index_path: Path, query: str | None = None, num_results: int = 5
|
|
|
79
80
|
return response
|
|
80
81
|
|
|
81
82
|
|
|
82
|
-
def _build_symbol_result(sym: dict, rel_path: str, call_graph: dict) -> dict:
|
|
83
|
-
"""Build a context result dict for a symbol."""
|
|
83
|
+
def _build_symbol_result(sym: dict, rel_path: str, call_graph: dict, source_code: dict) -> dict:
|
|
84
|
+
"""Build a context result dict for a symbol with actual code."""
|
|
84
85
|
result = {
|
|
85
86
|
"name": sym.get("name"),
|
|
86
87
|
"type": sym.get("type"),
|
|
@@ -92,6 +93,11 @@ def _build_symbol_result(sym: dict, rel_path: str, call_graph: dict) -> dict:
|
|
|
92
93
|
"framework": sym.get("framework"),
|
|
93
94
|
}
|
|
94
95
|
|
|
96
|
+
# Add actual code snippet from source_code - look up by symbol name + type
|
|
97
|
+
code = _get_symbol_code(sym.get("name"), sym.get("type"), rel_path, source_code)
|
|
98
|
+
if code:
|
|
99
|
+
result["code"] = code
|
|
100
|
+
|
|
95
101
|
if sym.get("return_type"):
|
|
96
102
|
result["return_type"] = sym["return_type"]
|
|
97
103
|
|
|
@@ -105,7 +111,29 @@ def _build_symbol_result(sym: dict, rel_path: str, call_graph: dict) -> dict:
|
|
|
105
111
|
return result
|
|
106
112
|
|
|
107
113
|
|
|
108
|
-
def
|
|
114
|
+
def _get_symbol_code(sym_name: str, sym_type: str, rel_path: str, source_code: dict) -> str | None:
|
|
115
|
+
"""Get the actual code snippet for a symbol from source_code storage."""
|
|
116
|
+
if not source_code or not sym_name:
|
|
117
|
+
return None
|
|
118
|
+
|
|
119
|
+
# Try to find by symbol name + type key first
|
|
120
|
+
key = f"{sym_name}:{sym_type}"
|
|
121
|
+
if key in source_code:
|
|
122
|
+
file_data = source_code[key].get(rel_path)
|
|
123
|
+
if file_data:
|
|
124
|
+
return file_data.get("body")
|
|
125
|
+
|
|
126
|
+
# Fallback: search through all keys for this symbol name
|
|
127
|
+
for code_key, files in source_code.items():
|
|
128
|
+
if code_key.startswith(f"{sym_name}:"):
|
|
129
|
+
file_data = files.get(rel_path)
|
|
130
|
+
if file_data:
|
|
131
|
+
return file_data.get("body")
|
|
132
|
+
|
|
133
|
+
return None
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def _get_all_symbols(files: dict, call_graph: dict, source_code: dict, limit: int) -> dict[str, Any]:
|
|
109
137
|
"""Get all symbols, limited."""
|
|
110
138
|
all_symbols = []
|
|
111
139
|
|
cortexcode/indexer.py
CHANGED
|
@@ -45,6 +45,7 @@ class CodeIndexer(IndexerExtractorMixin):
|
|
|
45
45
|
self.symbols: list[dict[str, Any]] = []
|
|
46
46
|
self.call_graph: dict[str, list[str]] = {}
|
|
47
47
|
self.file_symbols: dict[str, list[dict[str, Any]]] = {}
|
|
48
|
+
self.source_code: dict[str, dict] = {} # Store source code lines for context
|
|
48
49
|
self.gitignore_patterns: list[tuple[str, bool]] = []
|
|
49
50
|
|
|
50
51
|
# Filter options
|
|
@@ -85,6 +86,7 @@ class CodeIndexer(IndexerExtractorMixin):
|
|
|
85
86
|
self.symbols = session["symbols"]
|
|
86
87
|
self.call_graph = session["call_graph"]
|
|
87
88
|
self.file_symbols = session["file_symbols"]
|
|
89
|
+
self.source_code = session.get("source_code", {})
|
|
88
90
|
self.parsers = session["parsers"]
|
|
89
91
|
|
|
90
92
|
self._load_gitignore(root_path)
|
|
@@ -153,6 +155,10 @@ class CodeIndexer(IndexerExtractorMixin):
|
|
|
153
155
|
|
|
154
156
|
def _index_file(self, file_path: Path, root: Path) -> None:
|
|
155
157
|
"""Index a single file."""
|
|
158
|
+
rel_path = file_path.relative_to(root)
|
|
159
|
+
rel_str = str(rel_path)
|
|
160
|
+
|
|
161
|
+
# First, run the actual indexing to get symbol positions
|
|
156
162
|
result = index_file(
|
|
157
163
|
file_path,
|
|
158
164
|
root,
|
|
@@ -171,6 +177,41 @@ class CodeIndexer(IndexerExtractorMixin):
|
|
|
171
177
|
return
|
|
172
178
|
|
|
173
179
|
rel_path, file_data, symbols = result
|
|
180
|
+
|
|
181
|
+
# Load full file content to extract complete function bodies
|
|
182
|
+
try:
|
|
183
|
+
content = file_path.read_text(encoding="utf-8", errors="ignore")
|
|
184
|
+
lines = content.split("\n")
|
|
185
|
+
|
|
186
|
+
# Store full function bodies for each symbol
|
|
187
|
+
for sym in symbols:
|
|
188
|
+
sym_line = sym.get("line", 1)
|
|
189
|
+
sym_name = sym.get("name", "")
|
|
190
|
+
sym_type = sym.get("type", "")
|
|
191
|
+
|
|
192
|
+
# Find the end of this symbol (next symbol or end of file)
|
|
193
|
+
end_line = len(lines)
|
|
194
|
+
for other_sym in symbols:
|
|
195
|
+
other_line = other_sym.get("line", 1)
|
|
196
|
+
if other_line > sym_line and (end_line == len(lines) or other_line < end_line):
|
|
197
|
+
end_line = other_line - 1
|
|
198
|
+
|
|
199
|
+
# Extract full body (with some context before)
|
|
200
|
+
start_line = max(1, sym_line - 2)
|
|
201
|
+
body_lines = lines[start_line-1:end_line]
|
|
202
|
+
body = "\n".join(body_lines)
|
|
203
|
+
|
|
204
|
+
# Store with key: "symbol_name:type" for quick lookup
|
|
205
|
+
key = f"{sym_name}:{sym_type}"
|
|
206
|
+
if key not in self.source_code:
|
|
207
|
+
self.source_code[key] = {}
|
|
208
|
+
self.source_code[key][rel_str] = {
|
|
209
|
+
"line": sym_line,
|
|
210
|
+
"body": body
|
|
211
|
+
}
|
|
212
|
+
except Exception:
|
|
213
|
+
pass
|
|
214
|
+
|
|
174
215
|
merge_indexed_file(
|
|
175
216
|
rel_path,
|
|
176
217
|
file_data,
|
|
@@ -182,7 +223,7 @@ class CodeIndexer(IndexerExtractorMixin):
|
|
|
182
223
|
|
|
183
224
|
def _build_index(self, root: Path) -> dict[str, Any]:
|
|
184
225
|
"""Build the final index structure."""
|
|
185
|
-
|
|
226
|
+
result = build_index_result(
|
|
186
227
|
root=root,
|
|
187
228
|
file_symbols=self.file_symbols,
|
|
188
229
|
call_graph=self.call_graph,
|
|
@@ -195,6 +236,9 @@ class CodeIndexer(IndexerExtractorMixin):
|
|
|
195
236
|
regex_languages=REGEX_LANGUAGES,
|
|
196
237
|
plugin_registry=plugin_registry,
|
|
197
238
|
)
|
|
239
|
+
# Add source code for context retrieval
|
|
240
|
+
result["source_code"] = self.source_code
|
|
241
|
+
return result
|
|
198
242
|
|
|
199
243
|
|
|
200
244
|
def index_directory(path: str | Path, incremental: bool = False, filter_opts: dict[str, Any] | None = None) -> dict[str, Any]:
|
cortexcode/main.py
CHANGED
|
@@ -494,7 +494,9 @@ def serve_lsp():
|
|
|
494
494
|
@click.option("-I", "--include", multiple=True, help="Patterns to include, e.g., 'apps/*', 'packages/*'")
|
|
495
495
|
@click.option("-r", "--root", default=None, help="Monorepo root (for nx-style projects)")
|
|
496
496
|
@click.option("-n", "--dry-run", is_flag=True, help="Preview what would be indexed without indexing")
|
|
497
|
-
|
|
497
|
+
@click.option("--no-post-index-wizard", is_flag=True, help="Skip the interactive post-index generation wizard")
|
|
498
|
+
@click.option("--force-wizard", is_flag=True, help="Force showing the post-index wizard even in non-TTY mode")
|
|
499
|
+
def index(path, output, verbose, watch, incremental, include_tests, exclude, include, root, dry_run, no_post_index_wizard, force_wizard):
|
|
498
500
|
"""Index a directory and save the code graph."""
|
|
499
501
|
handle_index_command(
|
|
500
502
|
console,
|
|
@@ -508,6 +510,8 @@ def index(path, output, verbose, watch, incremental, include_tests, exclude, inc
|
|
|
508
510
|
include,
|
|
509
511
|
root,
|
|
510
512
|
dry_run,
|
|
513
|
+
no_post_index_wizard,
|
|
514
|
+
force_wizard,
|
|
511
515
|
indexer,
|
|
512
516
|
print_index_header_renderer,
|
|
513
517
|
print_project_profile_summary_renderer,
|
cortexcode/mcp/mcp_registry.py
CHANGED
|
@@ -17,7 +17,7 @@ TOOL_DEFINITIONS = [
|
|
|
17
17
|
},
|
|
18
18
|
{
|
|
19
19
|
"name": "cortexcode_context",
|
|
20
|
-
"description": "USE THIS when you need to understand how a function/class works, see its implementation, or see what it calls/who calls it. Also use when user asks 'how does X work' or 'show me the code for X'.",
|
|
20
|
+
"description": "USE THIS when you need to understand how a function/class works, see its implementation, or see what it calls/who calls it. Also use when user asks 'how does X work' or 'show me the code for X'. Returns ACTUAL CODE SNIPPETS from the index - no need to read files separately. This saves tokens and time.",
|
|
21
21
|
"inputSchema": {
|
|
22
22
|
"type": "object",
|
|
23
23
|
"properties": {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cortexcode
|
|
3
|
-
Version: 0.
|
|
4
|
-
Summary: Lightweight code indexing for AI assistants —
|
|
3
|
+
Version: 0.9.1
|
|
4
|
+
Summary: Lightweight code indexing for AI assistants — build a searchable codegraph and grounded context
|
|
5
5
|
Author-email: Naveen <naveen_joshi07@outlook.com>
|
|
6
6
|
License: MIT
|
|
7
7
|
Project-URL: Homepage, https://github.com/naveen-joshi/cortexcode
|
|
@@ -32,6 +32,7 @@ Requires-Dist: tree-sitter-rust>=0.23.0
|
|
|
32
32
|
Requires-Dist: tree-sitter-java>=0.23.0
|
|
33
33
|
Requires-Dist: tree-sitter-c-sharp>=0.23.0
|
|
34
34
|
Requires-Dist: click>=8.1.0
|
|
35
|
+
Requires-Dist: questionary>=2.0.1
|
|
35
36
|
Requires-Dist: watchdog>=4.0.0
|
|
36
37
|
Requires-Dist: rich>=13.0.0
|
|
37
38
|
Requires-Dist: pyyaml>=6.0.0
|
|
@@ -54,44 +55,36 @@ Dynamic: license-file
|
|
|
54
55
|
<h1 align="center">CortexCode</h1>
|
|
55
56
|
<p align="center">
|
|
56
57
|
<strong>Lightweight code indexing for AI assistants</strong><br>
|
|
57
|
-
|
|
58
|
+
Build a searchable codegraph, generate documentation, and give AI assistants grounded context.
|
|
58
59
|
</p>
|
|
59
60
|
</p>
|
|
60
61
|
|
|
61
|
-
<p align="center">
|
|
62
|
-
<a href="https://pypi.org/project/cortexcode/"><img src="https://img.shields.io/pypi/v/cortexcode?style=flat-square&color=blue" alt="PyPI"></a>
|
|
63
|
-
<a href="https://pypi.org/project/cortexcode/"><img src="https://img.shields.io/pypi/pyversions/cortexcode?style=flat-square" alt="Python"></a>
|
|
64
|
-
<a href="https://github.com/naveen-joshi/cortexcode/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-green?style=flat-square" alt="License"></a>
|
|
65
|
-
<a href="https://github.com/naveen-joshi/cortexcode"><img src="https://img.shields.io/github/stars/naveen-joshi/cortexcode?style=flat-square" alt="Stars"></a>
|
|
66
|
-
<a href="https://marketplace.visualstudio.com/items?itemName=cortexcode.cortexcode-vscode"><img src="https://img.shields.io/visual-studio-marketplace/v/cortexcode.cortexcode-vscode?style=flat-square" alt="VS Code"></a>
|
|
67
|
-
</p>
|
|
68
|
-
|
|
69
62
|
---
|
|
70
63
|
|
|
71
64
|
## The Problem
|
|
72
65
|
|
|
73
66
|
AI coding assistants (Copilot, Cursor, Windsurf, etc.) need to understand your codebase. The current approach: **dump entire source files into the context window**. This is:
|
|
74
67
|
|
|
75
|
-
- **
|
|
76
|
-
- **
|
|
77
|
-
- **
|
|
68
|
+
- **Noisy** — Important signals are buried inside large files
|
|
69
|
+
- **Fragile** — Assistants miss architecture, relationships, and runtime surface area
|
|
70
|
+
- **Hard to reuse** — Raw file dumps do not become a durable project map
|
|
78
71
|
|
|
79
72
|
## The Solution
|
|
80
73
|
|
|
81
|
-
CortexCode indexes your codebase using **AST parsing** (tree-sitter) and
|
|
74
|
+
CortexCode indexes your codebase using **AST parsing** (tree-sitter) and builds a structured, searchable codegraph. You can explore symbols, trace flows, generate docs and diagrams, produce CodeWiki pages, and connect AI tools through MCP.
|
|
82
75
|
|
|
83
76
|
```
|
|
84
77
|
┌─────────────────────────────────────────────────┐
|
|
85
78
|
│ Without CortexCode With CortexCode │
|
|
86
79
|
│ │
|
|
87
|
-
│
|
|
88
|
-
│
|
|
89
|
-
│
|
|
90
|
-
│ No
|
|
80
|
+
│ Raw files only → Searchable codegraph │
|
|
81
|
+
│ Manual digging → Linked symbols │
|
|
82
|
+
│ Ad-hoc prompts → Reusable context │
|
|
83
|
+
│ No project map → Docs + diagrams │
|
|
91
84
|
└─────────────────────────────────────────────────┘
|
|
92
85
|
```
|
|
93
86
|
|
|
94
|
-
Run `cortexcode
|
|
87
|
+
Run `cortexcode index` on your project to generate the codegraph and start exploring.
|
|
95
88
|
|
|
96
89
|
## Quick Start
|
|
97
90
|
|
|
@@ -107,9 +100,6 @@ cd cortexcode && pip install -e .
|
|
|
107
100
|
cd your-project
|
|
108
101
|
cortexcode index
|
|
109
102
|
|
|
110
|
-
# See token savings
|
|
111
|
-
cortexcode stats
|
|
112
|
-
|
|
113
103
|
# Get context for AI
|
|
114
104
|
cortexcode context "handleAuth"
|
|
115
105
|
|
|
@@ -117,6 +107,22 @@ cortexcode context "handleAuth"
|
|
|
117
107
|
cortexcode docs --open
|
|
118
108
|
```
|
|
119
109
|
|
|
110
|
+
### Post-Index Wizard
|
|
111
|
+
|
|
112
|
+
After running `cortexcode index`, an interactive wizard can guide you through generating outputs:
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
cortexcode index # Run index, then wizard prompts
|
|
116
|
+
cortexcode index --force-wizard # Force wizard in non-TTY mode
|
|
117
|
+
cortexcode index --no-post-index-wizard # Skip wizard
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
**Wizard features:**
|
|
121
|
+
- Multi-select checkbox for docs, diagrams, AI docs, CodeWiki, reports, visualization, dashboard, MCP setup
|
|
122
|
+
- Smart recommendations based on detected project shape
|
|
123
|
+
- Follow-up prompts for diagram types, AI doc types, wiki options
|
|
124
|
+
- Preference persistence — saves your choices for future runs
|
|
125
|
+
|
|
120
126
|
## Features
|
|
121
127
|
|
|
122
128
|
### Multi-Language AST Indexing
|
|
@@ -142,24 +148,14 @@ Parses source code into structured symbols using tree-sitter grammars.
|
|
|
142
148
|
- **Entities** — Database models and ORM definitions
|
|
143
149
|
- **Framework Detection** — React components, Angular services, etc.
|
|
144
150
|
|
|
145
|
-
###
|
|
151
|
+
### Project Exploration & Context
|
|
146
152
|
|
|
147
|
-
CortexCode
|
|
153
|
+
Use CortexCode to inspect structure and answer practical questions about a codebase:
|
|
148
154
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
│ Source files 154 files │
|
|
154
|
-
│ Raw project tokens 203,847 │
|
|
155
|
-
│ Full index tokens 45,291 │
|
|
156
|
-
│ Context query tokens 487 │
|
|
157
|
-
│ │
|
|
158
|
-
│ Tokens saved 203,360 │
|
|
159
|
-
│ Savings 99.8% │
|
|
160
|
-
│ Compression ratio 418.6x │
|
|
161
|
-
╰─────────────────────────────────────────╯
|
|
162
|
-
```
|
|
155
|
+
- **Context lookup** — Pull relevant symbols and files for a query
|
|
156
|
+
- **Call graph tracing** — Follow how behavior moves across modules
|
|
157
|
+
- **Architecture visibility** — Inspect routes, entities, dependencies, and layers
|
|
158
|
+
- **Reusable index** — Keep a structured project map that tools can build on
|
|
163
159
|
|
|
164
160
|
### Interactive HTML Documentation
|
|
165
161
|
|
|
@@ -194,7 +190,6 @@ cortexcode wiki --no-modules # Skip per-module pages (faster)
|
|
|
194
190
|
- **Mermaid diagrams** — Auto-generated flow diagrams
|
|
195
191
|
- **Concept mapping** — Maps technical concepts to symbols and files
|
|
196
192
|
- **Concept search** — Ask "how does authentication work?" and get grounded answers
|
|
197
|
-
- **Token tracking** — See exactly how many tokens each page used
|
|
198
193
|
|
|
199
194
|
**Output:** `.cortexcode/wiki/index.html` — Open directly or serve locally.
|
|
200
195
|
|
|
@@ -245,12 +240,12 @@ CortexCode supports both a short form (`cc`) and full form (`cortexcode`). Comma
|
|
|
245
240
|
| Command | Description |
|
|
246
241
|
|---------|-------------|
|
|
247
242
|
| `cc analyze context [query]` | Get relevant context for AI |
|
|
248
|
-
| `cc analyze context [query] --tokens` | Show
|
|
243
|
+
| `cc analyze context [query] --tokens` | Show context size estimates for a query |
|
|
249
244
|
| `cc analyze search [query]` | Grep-like symbol search with type/file filters |
|
|
250
245
|
| `cc analyze find [query]` | Semantic search by meaning ("auth handler") |
|
|
251
246
|
| `cc analyze diff` | Show changed symbols since last commit |
|
|
252
247
|
| `cc analyze diff --ref HEAD~3` | Compare against any git ref |
|
|
253
|
-
| `cc analyze stats` | Show project
|
|
248
|
+
| `cc analyze stats` | Show project index and analysis stats |
|
|
254
249
|
| `cc analyze scan` | Scan dependencies for security warnings |
|
|
255
250
|
| `cc analyze trace <symbol>` | Trace code flow through call graph |
|
|
256
251
|
| `cc analyze flow <concept>` | Analyze code flow grouped by file |
|
|
@@ -355,7 +350,7 @@ Add to `~/.windsurf/config.json`:
|
|
|
355
350
|
|
|
356
351
|
```bash
|
|
357
352
|
# Paste this output into your AI chat
|
|
358
|
-
cortexcode context "useAuth"
|
|
353
|
+
cortexcode context "useAuth"
|
|
359
354
|
```
|
|
360
355
|
|
|
361
356
|
### 2. JSON Index (programmatic)
|
|
@@ -552,7 +547,6 @@ CortexCode respects `.gitignore` files (including nested ones) and has built-in
|
|
|
552
547
|
## Roadmap
|
|
553
548
|
|
|
554
549
|
- [x] MCP server for direct AI agent integration
|
|
555
|
-
- [x] Tiktoken-based accurate token counting
|
|
556
550
|
- [x] Semantic search over symbols (TF-IDF + synonym expansion)
|
|
557
551
|
- [x] Cross-file type inference
|
|
558
552
|
- [x] Git diff-aware context (show only changed symbols)
|
|
@@ -6,9 +6,9 @@ cortexcode/context.py,sha256=CEX__dc21fdSLNg-Ec48ROTBILzEDufKPKANah_0HKk,342
|
|
|
6
6
|
cortexcode/dashboard.py,sha256=NNK4EhgBo3gBuwwntT5wIi7ZlX1ThHVlbxn6q0c-HdE,5361
|
|
7
7
|
cortexcode/docs.py,sha256=GT6GxWK7r-Zq-9VqxaLFAo9Yzh11kgwUgdaz46H4NmI,912
|
|
8
8
|
cortexcode/git_diff.py,sha256=YEEZM2yKYztE-T7rI5iBf2NluY4G1WIjlp-DprZ-6Rs,5742
|
|
9
|
-
cortexcode/indexer.py,sha256=
|
|
9
|
+
cortexcode/indexer.py,sha256=lcw6iGF1rPpBpvyX9XnAKBnjl6Yatv9RpB78VceQFyo,10477
|
|
10
10
|
cortexcode/lsp_server.py,sha256=mAPk78OVWQodBjBI3WhqEjrwjXrb0MF630IYDZ8_MLM,11102
|
|
11
|
-
cortexcode/main.py,sha256=
|
|
11
|
+
cortexcode/main.py,sha256=G7iiyXkE9c5Oe6cI4HnH6g4yXOCRih2O_Hihx9an82M,43380
|
|
12
12
|
cortexcode/performance.py,sha256=tkr7RMjIuutQqcNO8wgLBNzNMFXti7y9CkiSetPam_A,706
|
|
13
13
|
cortexcode/plugins.py,sha256=6WsmnvRT7D_coxD6YgwB-Dpu_iKOS89FNnT-gkgK4-8,6298
|
|
14
14
|
cortexcode/semantic_search.py,sha256=PO1FjbQhKf6EGfwtwHcdqVXL5W_xMr4e7GxuctdrFzM,8745
|
|
@@ -26,7 +26,7 @@ cortexcode/advanced_analysis/advanced_analysis_security.py,sha256=ZkXbzZHim7KnHs
|
|
|
26
26
|
cortexcode/ai_docs/__init__.py,sha256=W68LGb82g2wVOsvm3DfMd9esntVfgZH4cwDzcAxRpGg,472
|
|
27
27
|
cortexcode/ai_docs/config.py,sha256=7qwDS2t7q6t-eQepBi51PsV9uTggrtzsD_JI7jH8_6k,3388
|
|
28
28
|
cortexcode/ai_docs/doc_cache.py,sha256=Im4nhro2sT0uytq0rajRntd3v6KUCmspS4VDmQWeHl8,1220
|
|
29
|
-
cortexcode/ai_docs/doc_generator.py,sha256=
|
|
29
|
+
cortexcode/ai_docs/doc_generator.py,sha256=8zelx0xH6tCrEeAPyYuzWb5a7rejPT5AjfmPu2znZ64,7108
|
|
30
30
|
cortexcode/ai_docs/doc_lookup.py,sha256=L_q4o_tYb_UK3l7CT-ZQBdKZPzCL7oH30LkaCxYoVdI,1067
|
|
31
31
|
cortexcode/ai_docs/doc_models.py,sha256=jNGOposSsYrcrzC7UJ_81PP2yP9lzZtJ3dsxfbV7cio,339
|
|
32
32
|
cortexcode/ai_docs/explainer.py,sha256=rMPab2N9UkfeY6NrmiaM2OZNElRUKqv7ZXVuzUnq6bo,9557
|
|
@@ -53,13 +53,13 @@ cortexcode/cli/cli_explain.py,sha256=q_Gh1m3XXzcJXa9px1OOMPlLx3-CqJ0x_VK3TgciltE
|
|
|
53
53
|
cortexcode/cli/cli_find.py,sha256=EueM1_fYWgDIRD5mUJ798lN5QW3UOyz7gW0jeEMytlM,1314
|
|
54
54
|
cortexcode/cli/cli_githook.py,sha256=TJoVrTlRDSeL9SzQQzKDWy8lVAR8utHxldUlawCVE-o,3315
|
|
55
55
|
cortexcode/cli/cli_impact.py,sha256=KXAt8asPE9HoQmixGaBGPX7iRBGVjN7jalFnM8DDNXY,2080
|
|
56
|
-
cortexcode/cli/cli_index.py,sha256=
|
|
56
|
+
cortexcode/cli/cli_index.py,sha256=LR79rBBPQpjt1c70eTyjVUKX3EIs-SYo3vk0rRE4Pj0,23524
|
|
57
57
|
cortexcode/cli/cli_jobs.py,sha256=Zy_p2xS5KNWSlZ0dEgYbJbUNMV4CMuN22s_PwHUYptc,4820
|
|
58
58
|
cortexcode/cli/cli_package.py,sha256=YQ3BI2-BUTvy7zaLHulFRxpFQzwt5pBwPi55XFs83CI,7688
|
|
59
59
|
cortexcode/cli/cli_report.py,sha256=4scGu4Tv_mq7jRMu1KJ8YxBWrdMovOJmfZEfwu5olnU,562
|
|
60
60
|
cortexcode/cli/cli_scan.py,sha256=Br3-8DLnnOspnLCHFi5Jg8_XBViRaQjHiBcazkQGadA,1224
|
|
61
61
|
cortexcode/cli/cli_search.py,sha256=FKBjdXAgj3wu4DM_n175HMTPHjTIuZDcEQ5nYnEjOz8,1925
|
|
62
|
-
cortexcode/cli/cli_servers.py,sha256=
|
|
62
|
+
cortexcode/cli/cli_servers.py,sha256=xnK4xL5qrr2FS-dw0z1EEOgMKMR7LSRmxPew91Wc51g,7717
|
|
63
63
|
cortexcode/cli/cli_shell.py,sha256=J6ji1zhM4g-JqbUrzD43lWqsqvSkAGYvTiiyWg9OZso,5173
|
|
64
64
|
cortexcode/cli/cli_stats.py,sha256=P7RrX4CcqslGWQ1ZDUjOfPojDZ6hYVXwhRUbNvL3Iq4,601
|
|
65
65
|
cortexcode/cli/cli_support.py,sha256=Y05HqRtMeNsGPJruJZj8g0PLflEHRNByoFBWyf_1po4,954
|
|
@@ -69,7 +69,7 @@ cortexcode/cli/cli_wiki.py,sha256=VLKxFkqWsWa9Dp4b4k5aV8B8d607pDcnwElqTXHIf2c,58
|
|
|
69
69
|
cortexcode/cli/cli_workspace.py,sha256=_l3aGwHfPvI-UfriM8FfztAwINN5dnlV2UFQ5AtWjiQ,3805
|
|
70
70
|
cortexcode/context/__init__.py,sha256=vHyIXWJ7LRvtgh1PVywTUZ3JkFLRgJT8OD_xj9WW2YU,430
|
|
71
71
|
cortexcode/context/context_format.py,sha256=4cH-MEDZbdDTYunUxDqgB0Wvm7CP9b_lVkyVkkC9FxM,623
|
|
72
|
-
cortexcode/context/context_query.py,sha256=
|
|
72
|
+
cortexcode/context/context_query.py,sha256=iu4nUdRS5-gQ2CKJBB5EcjeNsSsAZTDhpAD3DGfIogA,7047
|
|
73
73
|
cortexcode/context/context_tokens.py,sha256=xN69ADHUn7rUlKmDJN6q7HY_I4fX9QzJyoz0Hkv6WEw,2217
|
|
74
74
|
cortexcode/diagrams/__init__.py,sha256=xKmWh67Kt8ngSnNuOQ1ff2uwzZJ9boxepQsPNu6vm7I,1143
|
|
75
75
|
cortexcode/diagrams/architecture.py,sha256=xx5yekiAF-VF0LxC9dbXsKjKskOlXQRuS6jlefoRPY8,2482
|
|
@@ -135,7 +135,7 @@ cortexcode/knowledge/snippets.py,sha256=8cpetnmY-KdFj4aWSt55mmO6JJ502CU6r01Jpjpx
|
|
|
135
135
|
cortexcode/knowledge/usage.py,sha256=U6_wuXMqsNBSyZtOeerWtexoAu6ma1Eb8yds7HpTgCs,3549
|
|
136
136
|
cortexcode/mcp/__init__.py,sha256=ibMesuUdMZtpjx294SNPZi5oNitZmj3DJYCOntw9Aw4,699
|
|
137
137
|
cortexcode/mcp/mcp_protocol.py,sha256=grKhPjzbYSi7GI93WifnZ4NHYW8K-NdYeGUZtqrN4Zc,379
|
|
138
|
-
cortexcode/mcp/mcp_registry.py,sha256=
|
|
138
|
+
cortexcode/mcp/mcp_registry.py,sha256=9pRGfkHxnQB1pT6MBto9RwOo9AdEXoop8cX_-qnkjDw,9546
|
|
139
139
|
cortexcode/mcp/mcp_server.py,sha256=RM7PGxRqpijgxc_GS3r-8ax42myEl-uDnKiXgRkpPx4,2835
|
|
140
140
|
cortexcode/mcp/mcp_tool_handlers.py,sha256=QOA1-dWMl_iE65y1y7Gq79fICbSq4H1n0v5HqLsbdWw,9304
|
|
141
141
|
cortexcode/mcp/mcp_transport.py,sha256=-ep5iL4z6lP0gahFq_3fb_fruh8kNrQMEBpK1HoPCpo,1864
|
|
@@ -165,9 +165,9 @@ cortexcode/terminal/headers.py,sha256=Yk9IGniMCiZD45V_yc1mjM3WjHimxoe9vL3CE5NAWv
|
|
|
165
165
|
cortexcode/terminal/prompts.py,sha256=KRwjLipbO5Ihv3ycp1T2nGQtoGmKPP1ZNXb3BUz4oF4,359
|
|
166
166
|
cortexcode/terminal/reports.py,sha256=4nLu0fBqDWKuQowX78VqX1nBaJl2zrTt9vEYCVP--O0,10135
|
|
167
167
|
cortexcode/terminal/stats.py,sha256=PooaYP-OqaewhwypnUHESOrdwwICvggb3n2HdqEQxrc,1365
|
|
168
|
-
cortexcode-0.
|
|
169
|
-
cortexcode-0.
|
|
170
|
-
cortexcode-0.
|
|
171
|
-
cortexcode-0.
|
|
172
|
-
cortexcode-0.
|
|
173
|
-
cortexcode-0.
|
|
168
|
+
cortexcode-0.9.1.dist-info/licenses/LICENSE,sha256=DWE8vkHgP2ChQTJHFWtrutLbuHQUBsU_InyfesP4neo,1067
|
|
169
|
+
cortexcode-0.9.1.dist-info/METADATA,sha256=D4Xoobbt4QrM5hxIQiU-ZTz8SRyE2gcGZ3Ud1uxsB5s,20648
|
|
170
|
+
cortexcode-0.9.1.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
171
|
+
cortexcode-0.9.1.dist-info/entry_points.txt,sha256=3TfwRp96e7ICJDUoJpcAu3NXVGT6HcyvHVU3pQ_vftg,78
|
|
172
|
+
cortexcode-0.9.1.dist-info/top_level.txt,sha256=r8FxzjLfKhRXXcORnECtGo7i2zKYXlV7v1XnIJ0SOc0,11
|
|
173
|
+
cortexcode-0.9.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|