cortexcode 0.9.0__py3-none-any.whl → 0.10.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
 
@@ -35,6 +35,21 @@ def generate_architecture_diagram(index_data: dict[str, Any]) -> str:
35
35
  lines.append(f" {framework_id}[\"{framework.get('name', 'unknown')} ({framework.get('count', 0)})\"]")
36
36
  lines.append(" end")
37
37
 
38
+ # Nx project graph overlay
39
+ nx_graph = project_profile.get("nx_project_graph", {})
40
+ nx_projects = project_profile.get("nx_projects", [])
41
+ if nx_graph and nx_projects:
42
+ lines.append(" subgraph nx_workspace [Nx Workspace]")
43
+ for proj_name in nx_projects:
44
+ proj_id = sanitize_id(f"nx_{proj_name}")
45
+ lines.append(f" {proj_id}[\"{proj_name}\"]")
46
+ lines.append(" end")
47
+ for proj_name, deps in nx_graph.items():
48
+ proj_id = sanitize_id(f"nx_{proj_name}")
49
+ for dep in deps[:10]:
50
+ dep_id = sanitize_id(f"nx_{dep}")
51
+ lines.append(f" {proj_id} --> {dep_id}")
52
+
38
53
  return "\n".join(lines)
39
54
 
40
55
  files = index_data.get("files", {})
cortexcode/indexer.py CHANGED
@@ -27,6 +27,7 @@ from cortexcode.indexing.resolution import (
27
27
  build_file_dependencies,
28
28
  build_type_map,
29
29
  )
30
+ from cortexcode.indexing.nx_projects import parse_nx_workspace, build_nx_project_graph
30
31
  from cortexcode.plugins import plugin_registry
31
32
 
32
33
 
@@ -223,18 +224,32 @@ class CodeIndexer(IndexerExtractorMixin):
223
224
 
224
225
  def _build_index(self, root: Path) -> dict[str, Any]:
225
226
  """Build the final index structure."""
227
+ nx_workspace = parse_nx_workspace(root)
228
+ tsconfig_paths = nx_workspace.get("tsconfig_paths", {}) if nx_workspace else {}
229
+
230
+ def _build_file_deps(ts_paths=None):
231
+ return build_file_dependencies(self.file_symbols, ts_paths)
232
+
233
+ def _build_type_map(ts_paths=None):
234
+ return build_type_map(self.file_symbols, ts_paths)
235
+
236
+ def _build_project_profile(_root, file_deps, nx=None):
237
+ return build_project_profile(self.file_symbols, self.call_graph, file_deps, nx)
238
+
226
239
  result = build_index_result(
227
240
  root=root,
228
241
  file_symbols=self.file_symbols,
229
242
  call_graph=self.call_graph,
230
243
  timestamp=timestamp_now(),
231
244
  file_hashes=compute_hashes(root, self.file_symbols),
232
- build_file_dependencies_fn=lambda: build_file_dependencies(self.file_symbols),
233
- build_type_map_fn=lambda: build_type_map(self.file_symbols),
234
- build_project_profile_fn=lambda _root, file_deps: build_project_profile(self.file_symbols, self.call_graph, file_deps),
245
+ build_file_dependencies_fn=lambda ts_paths=tsconfig_paths: _build_file_deps(ts_paths),
246
+ build_type_map_fn=lambda ts_paths=tsconfig_paths: _build_type_map(ts_paths),
247
+ build_project_profile_fn=lambda _root, file_deps, nx=nx_workspace: _build_project_profile(_root, file_deps, nx),
235
248
  language_map=LANGUAGE_MAP,
236
249
  regex_languages=REGEX_LANGUAGES,
237
250
  plugin_registry=plugin_registry,
251
+ nx_workspace=nx_workspace,
252
+ tsconfig_paths=tsconfig_paths,
238
253
  )
239
254
  # Add source code for context retrieval
240
255
  result["source_code"] = self.source_code
@@ -18,6 +18,8 @@ def build_index_result(
18
18
  language_map: LanguageMap,
19
19
  regex_languages: RegexLanguages,
20
20
  plugin_registry,
21
+ nx_workspace: dict[str, Any] | None = None,
22
+ tsconfig_paths: dict[str, str] | None = None,
21
23
  ) -> dict[str, Any]:
22
24
  languages = set()
23
25
  for file_path in file_symbols.keys():
@@ -30,9 +32,9 @@ def build_index_result(
30
32
  else:
31
33
  languages.add(ext.lstrip("."))
32
34
 
33
- file_deps = build_file_dependencies_fn()
34
- type_map = build_type_map_fn()
35
- project_profile = build_project_profile_fn(root, file_deps)
35
+ file_deps = build_file_dependencies_fn(tsconfig_paths)
36
+ type_map = build_type_map_fn(tsconfig_paths)
37
+ project_profile = build_project_profile_fn(root, file_deps, nx_workspace)
36
38
 
37
39
  result = {
38
40
  "project_root": str(root),
@@ -48,4 +50,9 @@ def build_index_result(
48
50
  if type_map:
49
51
  result["type_map"] = type_map
50
52
 
53
+ if nx_workspace:
54
+ result["nx_workspace"] = nx_workspace
55
+ if tsconfig_paths:
56
+ result["nx_workspace"]["tsconfig_paths"] = tsconfig_paths
57
+
51
58
  return plugin_registry.run_post_processors(result)
@@ -4,7 +4,7 @@ DEFAULT_IGNORE_PATTERNS = {
4
4
  "*.egg-info", ".eggs", "*.pyc", "*.pyo",
5
5
  "node_modules", ".npm", ".yarn", ".pnpm-store", "bower_components",
6
6
  "dist", "build", "out", "output", "target", "bin", "obj",
7
- ".build", "_build", "public/build", "lib",
7
+ ".build", "_build", "public/build",
8
8
  ".next", ".nuxt", ".svelte-kit", ".angular", ".turbo",
9
9
  ".parcel-cache", ".webpack", ".rollup.cache", ".vite",
10
10
  ".expo", ".gradle", "Pods", "DerivedData",
@@ -22,7 +22,7 @@ def should_ignore_file(
22
22
  rel_posix = rel_path.as_posix()
23
23
 
24
24
  for pattern in default_ignore_patterns:
25
- if pattern in path_str or pattern in path_posix or pattern in rel_posix:
25
+ if pattern in rel_str or pattern in rel_posix:
26
26
  return True
27
27
 
28
28
  for pattern in exclude_patterns:
@@ -139,6 +139,13 @@ def find_js_exports(node: Any, exports: list[dict[str, Any]], get_node_name: Get
139
139
  name = export_clause_child.child_by_field_name("name")
140
140
  if name:
141
141
  exports.append({"name": name.text.decode("utf-8"), "type": "named"})
142
+ elif child.type == "export_clause":
143
+ # Barrel re-export: export { Button } from './lib/button'
144
+ for export_clause_child in child.children:
145
+ if export_clause_child.type == "export_specifier":
146
+ name = export_clause_child.child_by_field_name("name")
147
+ if name:
148
+ exports.append({"name": name.text.decode("utf-8"), "type": "re-export"})
142
149
  elif child.type == "variable_declaration":
143
150
  for declaration_child in child.children:
144
151
  if declaration_child.type == "variable_declarator":
@@ -0,0 +1,228 @@
1
+ """Nx monorepo workspace parsing and project graph extraction.
2
+
3
+ Handles modern Nx workspaces (v15+) where project configuration lives in
4
+ individual project.json files rather than a centralized workspace.json.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import json
10
+ from pathlib import Path
11
+ from typing import Any
12
+
13
+
14
+ def _safe_json(path: Path) -> dict[str, Any] | None:
15
+ try:
16
+ return json.loads(path.read_text(encoding="utf-8"))
17
+ except (OSError, json.JSONDecodeError):
18
+ return None
19
+
20
+
21
+ def _find_project_jsons(root: Path) -> list[Path]:
22
+ """Find all project.json files under apps/ and libs/ directories."""
23
+ project_jsons: list[Path] = []
24
+ for candidate_dir in ("apps", "libs", "packages"):
25
+ candidate = root / candidate_dir
26
+ if candidate.is_dir():
27
+ for sub in candidate.rglob("project.json"):
28
+ project_jsons.append(sub)
29
+ return sorted(project_jsons)
30
+
31
+
32
+ def parse_nx_workspace(root: Path) -> dict[str, Any] | None:
33
+ """Parse an Nx workspace and return project graph + path mappings.
34
+
35
+ Returns None if the directory does not look like an Nx workspace.
36
+ """
37
+ root = Path(root).resolve()
38
+ nx_json = root / "nx.json"
39
+ if not nx_json.exists():
40
+ return None
41
+
42
+ nx_config = _safe_json(nx_json) or {}
43
+
44
+ # Collect projects from project.json files
45
+ projects: dict[str, dict[str, Any]] = {}
46
+ for project_json_path in _find_project_jsons(root):
47
+ data = _safe_json(project_json_path)
48
+ if not data:
49
+ continue
50
+ name = data.get("name", project_json_path.parent.name)
51
+ projects[name] = {
52
+ "name": name,
53
+ "projectType": data.get("projectType", "library"),
54
+ "sourceRoot": data.get("sourceRoot", str(project_json_path.parent.relative_to(root))),
55
+ "tags": data.get("tags", []),
56
+ "implicitDependencies": data.get("implicitDependencies", []),
57
+ "root": str(project_json_path.parent.relative_to(root)).replace("\\", "/"),
58
+ "targets": data.get("targets", {}),
59
+ "project_json": str(project_json_path.relative_to(root)).replace("\\", "/"),
60
+ }
61
+
62
+ if not projects:
63
+ return None
64
+
65
+ # Parse tsconfig.base.json for path mappings
66
+ tsconfig_paths = _parse_tsconfig_paths(root)
67
+
68
+ return {
69
+ "nx_version": nx_config.get("nxVersion", nx_config.get("installationVersion")),
70
+ "projects": projects,
71
+ "tsconfig_paths": tsconfig_paths,
72
+ "namedInputs": nx_config.get("namedInputs", {}),
73
+ "targetDefaults": nx_config.get("targetDefaults", {}),
74
+ }
75
+
76
+
77
+ def _parse_tsconfig_paths(root: Path) -> dict[str, str]:
78
+ """Parse tsconfig.base.json (or tsconfig.json) compilerOptions.paths.
79
+
80
+ Returns a mapping of alias -> relative directory path.
81
+ """
82
+ for tsconfig_name in ("tsconfig.base.json", "tsconfig.json"):
83
+ tsconfig = root / tsconfig_name
84
+ if not tsconfig.exists():
85
+ continue
86
+ data = _safe_json(tsconfig)
87
+ if not data:
88
+ continue
89
+ paths = data.get("compilerOptions", {}).get("paths", {})
90
+ result: dict[str, str] = {}
91
+ for alias, targets in paths.items():
92
+ if not targets:
93
+ continue
94
+ # Take the first mapping; remove trailing /index.ts if present
95
+ target = targets[0]
96
+ target = target.rstrip("/").replace("\\", "/")
97
+ if target.endswith("/index.ts") or target.endswith("/index.tsx") or target.endswith("/index.js"):
98
+ target = str(Path(target).parent).replace("\\", "/")
99
+ result[alias] = target
100
+ return result
101
+ return {}
102
+
103
+
104
+ def nx_framework_from_executor(targets: dict[str, Any]) -> str | None:
105
+ """Infer framework from Nx target executors / generators."""
106
+ for target_name, target in targets.items():
107
+ executor = target.get("executor", "")
108
+ if not executor:
109
+ continue
110
+
111
+ if "@nx/angular" in executor or "@angular-devkit" in executor:
112
+ return "angular"
113
+ if "@nx/react" in executor or "@nx/webpack" in executor:
114
+ return "react"
115
+ if "@nx/next" in executor:
116
+ return "nextjs"
117
+ if "@nx/vue" in executor or "@nx/vite" in executor and "vue" in executor.lower():
118
+ return "vue"
119
+ if "@nx/nuxt" in executor:
120
+ return "nuxt"
121
+ if "@nx/expo" in executor:
122
+ return "expo"
123
+ if "@nx/react-native" in executor:
124
+ return "react-native"
125
+ if "@nx/nest" in executor or "@nestjs" in executor:
126
+ return "nestjs"
127
+ if "@nx/node" in executor or "@nx/express" in executor:
128
+ return "nodejs"
129
+ if "@nx/plugin" in executor:
130
+ return "nx-plugin"
131
+
132
+ return None
133
+
134
+
135
+ def build_nx_project_graph(
136
+ workspace: dict[str, Any],
137
+ file_dependencies: dict[str, list[str]] | None = None,
138
+ ) -> dict[str, list[str]]:
139
+ """Build adjacency list of project -> dependent project names.
140
+
141
+ Uses both implicitDependencies from project.json and file-level imports
142
+ (when file_dependencies is provided) to derive cross-project edges.
143
+ """
144
+ projects = workspace.get("projects", {})
145
+ tsconfig_paths = workspace.get("tsconfig_paths", {})
146
+
147
+ # Build reverse lookups
148
+ alias_to_project: dict[str, str] = {}
149
+ for name, proj in projects.items():
150
+ for alias, target in tsconfig_paths.items():
151
+ if target.startswith(proj["root"] + "/") or target == proj["root"]:
152
+ alias_to_project[alias] = name
153
+
154
+ # Map file path -> project name
155
+ file_to_project: dict[str, str] = {}
156
+ for name, proj in projects.items():
157
+ root = proj["root"]
158
+ for file_path in (file_dependencies or {}).keys():
159
+ if file_path.startswith(root + "/") or file_path == root:
160
+ file_to_project[file_path] = name
161
+
162
+ graph: dict[str, set[str]] = {name: set() for name in projects}
163
+
164
+ # Implicit dependencies from project.json
165
+ for name, proj in projects.items():
166
+ for dep in proj.get("implicitDependencies", []):
167
+ if dep in projects:
168
+ graph[name].add(dep)
169
+
170
+ # Derive from file dependencies: if a file in project A imports a file in project B
171
+ if file_dependencies:
172
+ for source_file, target_files in file_dependencies.items():
173
+ source_proj = file_to_project.get(source_file)
174
+ if not source_proj:
175
+ continue
176
+ for target_file in target_files:
177
+ target_proj = file_to_project.get(target_file)
178
+ if target_proj and target_proj != source_proj:
179
+ graph[source_proj].add(target_proj)
180
+
181
+ # Also derive from tsconfig alias usage in imports
182
+ # (file_dependencies already captures resolved files, so this is redundant
183
+ # but kept as a fallback if file_deps resolution missed something)
184
+ return {name: sorted(deps) for name, deps in graph.items()}
185
+
186
+
187
+ def detect_shell_app(workspace: dict[str, Any], project_graph: dict[str, list[str]] | None = None) -> str | None:
188
+ """Identify the likely shell / root application in an Nx workspace.
189
+
190
+ Heuristics (in order of priority):
191
+ 1. A project tagged "shell" or "host" or "root".
192
+ 2. An application with the most outgoing project dependencies.
193
+ 3. An application named "shell", "host", "app", or matching the repo name.
194
+ """
195
+ projects = workspace.get("projects", {})
196
+ if not projects:
197
+ return None
198
+
199
+ apps = {n: p for n, p in projects.items() if p.get("projectType") == "application"}
200
+ if not apps:
201
+ return None
202
+
203
+ # 1. Tag-based detection
204
+ shell_tags = {"shell", "host", "root", "entry", "main"}
205
+ for name, proj in apps.items():
206
+ tags = {t.lower() for t in proj.get("tags", [])}
207
+ if tags & shell_tags:
208
+ return name
209
+
210
+ # 2. Most outgoing deps among apps
211
+ graph = project_graph or build_nx_project_graph(workspace)
212
+ if graph:
213
+ sorted_apps = sorted(
214
+ apps.keys(),
215
+ key=lambda n: len(graph.get(n, [])),
216
+ reverse=True,
217
+ )
218
+ if sorted_apps and graph.get(sorted_apps[0]):
219
+ return sorted_apps[0]
220
+
221
+ # 3. Name heuristic
222
+ for keyword in ("shell", "host", "app", "main", "root"):
223
+ for name in apps:
224
+ if keyword in name.lower():
225
+ return name
226
+
227
+ # Fallback: first application
228
+ return next(iter(apps))
@@ -33,7 +33,7 @@ def index_file(
33
33
  except (UnicodeDecodeError, OSError):
34
34
  return None
35
35
 
36
- rel_path = str(file_path.relative_to(root))
36
+ rel_path = file_path.relative_to(root).as_posix()
37
37
 
38
38
  plugin_symbols = plugin_registry.extract_symbols(content, ext, rel_path)
39
39
  if plugin_symbols is not None:
@@ -1,6 +1,8 @@
1
1
  from pathlib import Path
2
2
  from typing import Any
3
3
 
4
+ from .nx_projects import nx_framework_from_executor
5
+
4
6
 
5
7
  FRONTEND_FRAMEWORKS = {"react", "react-native", "nextjs", "angular", "expo", "flutter", "swiftui", "uikit", "remix"}
6
8
 
@@ -42,7 +44,7 @@ def normalize_framework(framework: str | None) -> str | None:
42
44
  return normalized
43
45
 
44
46
 
45
- def infer_file_role(rel_path: str, file_data: dict[str, Any]) -> str:
47
+ def infer_file_role(rel_path: str, file_data: dict[str, Any], nx_workspace: dict[str, Any] | None = None) -> str:
46
48
  normalized_path = rel_path.replace("\\", "/").lower()
47
49
  file_name = Path(normalized_path).name
48
50
  symbols = file_data.get("symbols", [])
@@ -54,6 +56,27 @@ def infer_file_role(rel_path: str, file_data: dict[str, Any]) -> str:
54
56
  if isinstance(sym, dict) and sym.get("framework")
55
57
  }
56
58
 
59
+ # Nx monorepo: infer role from project type (app vs lib) and path
60
+ if nx_workspace:
61
+ projects = nx_workspace.get("projects", {})
62
+ for proj in projects.values():
63
+ root = proj.get("root", "")
64
+ if root and normalized_path.startswith(root.lower() + "/"):
65
+ ptype = proj.get("projectType", "")
66
+ tags = [t.lower() for t in proj.get("tags", [])]
67
+ if ptype == "application":
68
+ return "app"
69
+ # Library roles from tags
70
+ if any(t in tags for t in ("ui", "frontend", "component")):
71
+ return "ui"
72
+ if any(t in tags for t in ("data", "model", "entity", "db")):
73
+ return "data"
74
+ if any(t in tags for t in ("api", "service", "controller", "route")):
75
+ return "api"
76
+ if any(t in tags for t in ("util", "shared", "helper", "infra")):
77
+ return "infra"
78
+ return "lib"
79
+
57
80
  if file_name in ("cli.py", "manage.py") or any(segment in normalized_path for segment in ("/cli/", "/commands/")):
58
81
  return "cli"
59
82
  if routes or any(segment in normalized_path for segment in ("/api/", "/routes/", "/route/", "/controllers/", "/handlers/", "/endpoints/")):
@@ -137,6 +160,7 @@ def build_project_profile(
137
160
  file_symbols: dict[str, Any],
138
161
  call_graph: dict[str, list[str]],
139
162
  file_deps: dict[str, list[str]],
163
+ nx_workspace: dict[str, Any] | None = None,
140
164
  ) -> dict[str, Any]:
141
165
  framework_counts: dict[str, int] = {}
142
166
  symbol_type_counts: dict[str, int] = {}
@@ -155,7 +179,7 @@ def build_project_profile(
155
179
  symbols = file_data.get("symbols", [])
156
180
  api_routes = file_data.get("api_routes", [])
157
181
  entities = file_data.get("entities", [])
158
- role = infer_file_role(rel_path, file_data)
182
+ role = infer_file_role(rel_path, file_data, nx_workspace)
159
183
  role_by_file[rel_path] = role
160
184
 
161
185
  bucket = layer_stats.setdefault(role, {"files": 0, "symbols": 0, "routes": 0, "entities": 0})
@@ -189,6 +213,15 @@ def build_project_profile(
189
213
 
190
214
  entry_points.extend(infer_file_entry_points(rel_path, file_data, callers))
191
215
 
216
+ # Augment with Nx project-level framework detection from executors
217
+ if nx_workspace:
218
+ for project in nx_workspace.get("projects", {}).values():
219
+ fw = nx_framework_from_executor(project.get("targets", {}))
220
+ if fw:
221
+ nf = normalize_framework(fw)
222
+ if nf:
223
+ framework_counts[nf] = framework_counts.get(nf, 0) + 1
224
+
192
225
  layer_dependencies: dict[tuple[str, str], int] = {}
193
226
  for source_file, target_files in file_deps.items():
194
227
  source_role = role_by_file.get(source_file, "core")
@@ -249,7 +282,7 @@ def build_project_profile(
249
282
 
250
283
  recommendations = build_recommendations(frameworks, route_samples, entity_samples, layers)
251
284
 
252
- return {
285
+ profile = {
253
286
  "frameworks": frameworks,
254
287
  "symbol_types": [
255
288
  {"name": symbol_type, "count": count}
@@ -269,3 +302,15 @@ def build_project_profile(
269
302
  "entity_samples": entity_samples[:20],
270
303
  "recommendations": recommendations,
271
304
  }
305
+
306
+ # Add Nx project graph if available
307
+ if nx_workspace:
308
+ from cortexcode.indexing.nx_projects import build_nx_project_graph, detect_shell_app
309
+ nx_graph = build_nx_project_graph(nx_workspace, file_deps)
310
+ profile["nx_project_graph"] = nx_graph
311
+ profile["nx_projects"] = list(nx_workspace.get("projects", {}).keys())
312
+ shell = detect_shell_app(nx_workspace, nx_graph)
313
+ if shell:
314
+ profile["nx_shell_app"] = shell
315
+
316
+ return profile
@@ -33,6 +33,7 @@ def build_exports_by_file(file_symbols: dict[str, Any]) -> dict[str, dict[str, d
33
33
  "defined_in": rel_path,
34
34
  "type": exp.get("type", "export"),
35
35
  "line": exp.get("line"),
36
+ "source": exp.get("source"),
36
37
  }
37
38
 
38
39
  exports_by_file[rel_path] = file_exports
@@ -74,7 +75,12 @@ def build_module_lookup(file_symbols: dict[str, Any]) -> dict[str, set[str]]:
74
75
  return module_lookup
75
76
 
76
77
 
77
- def candidate_module_keys(rel_path: str, imp: dict[str, Any]) -> list[str]:
78
+ def candidate_module_keys(
79
+ rel_path: str,
80
+ imp: dict[str, Any],
81
+ tsconfig_paths: dict[str, str] | None = None,
82
+ root_path: str | None = None,
83
+ ) -> list[str]:
78
84
  module = str(imp.get("module", "")).strip()
79
85
  imported_names = [name for name in imp.get("imported", []) if name and name != "*"]
80
86
  if not module:
@@ -86,7 +92,7 @@ def candidate_module_keys(rel_path: str, imp: dict[str, Any]) -> list[str]:
86
92
 
87
93
  if module.startswith("."):
88
94
  dot_prefix = len(module) - len(module.lstrip("."))
89
- remainder = module[dot_prefix:]
95
+ remainder = module[dot_prefix:].lstrip("/")
90
96
  base_dir = current_dir
91
97
  for _ in range(max(dot_prefix - 1, 0)):
92
98
  base_dir = base_dir.parent
@@ -99,6 +105,27 @@ def candidate_module_keys(rel_path: str, imp: dict[str, Any]) -> list[str]:
99
105
  for imported_name in imported_names:
100
106
  candidates.append(str(base_candidate / imported_name.replace(".", "/")))
101
107
  else:
108
+ # Nx / tsconfig path mapping: @scope/lib -> libs/scope/lib
109
+ if tsconfig_paths and module in tsconfig_paths:
110
+ mapped = tsconfig_paths[module]
111
+ candidates.append(mapped)
112
+ for imported_name in imported_names:
113
+ candidates.append(f"{mapped}/{imported_name.replace('.', '/')}")
114
+ return candidates
115
+ # Partial prefix match for sub-path imports like @scope/lib/sub
116
+ if tsconfig_paths and "/" in module:
117
+ parts = module.split("/")
118
+ # Handle scoped packages: @scope/lib/sub -> prefix is @scope/lib
119
+ prefix = "/".join(parts[:2]) if parts[0].startswith("@") else parts[0]
120
+ if prefix in tsconfig_paths:
121
+ mapped = tsconfig_paths[prefix]
122
+ suffix = module[len(prefix):].lstrip("/")
123
+ if suffix:
124
+ candidates.append(f"{mapped}/{suffix.replace('.', '/')}")
125
+ else:
126
+ candidates.append(mapped)
127
+ return candidates
128
+
102
129
  cleaned = module.replace("@/", "src/").replace("~/", "")
103
130
  candidates.append(cleaned)
104
131
  if "/" not in cleaned and "." in cleaned:
@@ -113,9 +140,10 @@ def resolve_import_to_files(
113
140
  rel_path: str,
114
141
  imp: dict[str, Any],
115
142
  module_lookup: dict[str, set[str]],
143
+ tsconfig_paths: dict[str, str] | None = None,
116
144
  ) -> list[str]:
117
145
  resolved_files = set()
118
- for candidate in candidate_module_keys(rel_path, imp):
146
+ for candidate in candidate_module_keys(rel_path, imp, tsconfig_paths):
119
147
  normalized_candidate = normalize_module_key(candidate)
120
148
  if not normalized_candidate:
121
149
  continue
@@ -131,7 +159,29 @@ def resolve_import_to_files(
131
159
  return sorted(file_path for file_path in resolved_files if file_path != normalized_rel)
132
160
 
133
161
 
134
- def build_type_map(file_symbols: dict[str, Any]) -> dict[str, dict[str, Any]]:
162
+ def _resolve_reexport_source(barrel_path: str, source: str, file_symbols: dict[str, Any]) -> str | None:
163
+ """Resolve a re-export source path like './lib/button' to an actual file path."""
164
+ if not source:
165
+ return None
166
+ source = source.lstrip("./")
167
+ dir_parts = barrel_path.split("/")[:-1]
168
+ while source.startswith("../"):
169
+ source = source[3:]
170
+ if dir_parts:
171
+ dir_parts.pop()
172
+ base = "/".join(dir_parts) + "/" + source if dir_parts else source
173
+ candidates = [base]
174
+ for ext in (".ts", ".tsx", ".js", ".jsx"):
175
+ candidates.append(base + ext)
176
+ for idx in ("index.ts", "index.tsx", "index.js", "index.jsx"):
177
+ candidates.append(base + "/" + idx)
178
+ for candidate in candidates:
179
+ if candidate in file_symbols:
180
+ return candidate
181
+ return None
182
+
183
+
184
+ def build_type_map(file_symbols: dict[str, Any], tsconfig_paths: dict[str, str] | None = None) -> dict[str, dict[str, Any]]:
135
185
  exports_by_file = build_exports_by_file(file_symbols)
136
186
  module_lookup = build_module_lookup(file_symbols)
137
187
 
@@ -142,7 +192,7 @@ def build_type_map(file_symbols: dict[str, Any]) -> dict[str, dict[str, Any]]:
142
192
 
143
193
  for imp in file_data.get("imports", []):
144
194
  imported_names = imp.get("imported", [])
145
- target_files = resolve_import_to_files(rel_path, imp, module_lookup)
195
+ target_files = resolve_import_to_files(rel_path, imp, module_lookup, tsconfig_paths)
146
196
  if not target_files:
147
197
  continue
148
198
 
@@ -158,19 +208,34 @@ def build_type_map(file_symbols: dict[str, Any]) -> dict[str, dict[str, Any]]:
158
208
  defn = target_exports[imported_name]
159
209
  if defn["defined_in"] == rel_path:
160
210
  continue
211
+ # Trace re-exports through barrel files to actual definitions
212
+ traced = defn
213
+ depth = 0
214
+ while traced.get("type") == "re-export" and traced.get("source") and depth < 3:
215
+ resolved = _resolve_reexport_source(traced["defined_in"], traced["source"], file_symbols)
216
+ if not resolved:
217
+ break
218
+ next_exports = exports_by_file.get(resolved, {})
219
+ if imported_name not in next_exports:
220
+ break
221
+ traced = next_exports[imported_name]
222
+ depth += 1
161
223
  key = f"{rel_path}:{imported_name}"
162
224
  type_map[key] = {
163
225
  "imported_in": rel_path,
164
226
  "name": imported_name,
165
- "defined_in": defn["defined_in"],
166
- "type": defn.get("type"),
167
- "line": defn.get("line"),
227
+ "defined_in": traced["defined_in"],
228
+ "type": traced.get("type"),
229
+ "line": traced.get("line"),
168
230
  }
169
231
 
170
232
  return type_map
171
233
 
172
234
 
173
- def build_file_dependencies(file_symbols: dict[str, Any]) -> dict[str, list[str]]:
235
+ def build_file_dependencies(
236
+ file_symbols: dict[str, Any],
237
+ tsconfig_paths: dict[str, str] | None = None,
238
+ ) -> dict[str, list[str]]:
174
239
  deps = {}
175
240
  module_lookup = build_module_lookup(file_symbols)
176
241
 
@@ -183,7 +248,7 @@ def build_file_dependencies(file_symbols: dict[str, Any]) -> dict[str, list[str]
183
248
 
184
249
  dep_files = set()
185
250
  for imp in imports:
186
- dep_files.update(resolve_import_to_files(rel_path, imp, module_lookup))
251
+ dep_files.update(resolve_import_to_files(rel_path, imp, module_lookup, tsconfig_paths))
187
252
 
188
253
  if dep_files:
189
254
  deps[rel_path] = sorted(dep_files)
@@ -34,6 +34,26 @@ def detect_monorepo(root_path: Path) -> Optional[Dict[str, Any]]:
34
34
 
35
35
  nx_json = root_path / "nx.json"
36
36
  if nx_json.exists():
37
+ from cortexcode.indexing.nx_projects import parse_nx_workspace
38
+
39
+ nx_workspace = parse_nx_workspace(root_path)
40
+ if nx_workspace:
41
+ projects = nx_workspace.get("projects", {})
42
+ if projects:
43
+ # Use actual source roots as include patterns
44
+ patterns = []
45
+ for proj in projects.values():
46
+ source_root = proj.get("sourceRoot", proj.get("root", ""))
47
+ if source_root:
48
+ patterns.append(f"{source_root}/**/*")
49
+ # Always include root-level configs (nx.json, tsconfig, package.json)
50
+ patterns.extend(["nx.json", "package.json", "tsconfig*.json"])
51
+ return {
52
+ "type": "nx",
53
+ "include_patterns": patterns,
54
+ "nx_workspace": nx_workspace,
55
+ }
56
+ # Fallback: old-style nx.json with projects array (pre-Nx v15)
37
57
  try:
38
58
  data = json.loads(nx_json.read_text(encoding="utf-8"))
39
59
  projects = data.get("projects", [])
@@ -4,6 +4,7 @@ from typing import Any
4
4
 
5
5
  def generate_structure_docs(index: dict[str, Any], output_path: Path) -> None:
6
6
  files = index.get("files", {})
7
+ profile = index.get("project_profile", {})
7
8
 
8
9
  lines = [
9
10
  "# Project Structure",
@@ -16,4 +17,30 @@ def generate_structure_docs(index: dict[str, Any], output_path: Path) -> None:
16
17
 
17
18
  lines.append("```")
18
19
 
20
+ # Nx monorepo section
21
+ nx_graph = profile.get("nx_project_graph")
22
+ nx_projects = profile.get("nx_projects")
23
+ if nx_projects:
24
+ lines.append("")
25
+ lines.append("## Nx Workspace")
26
+ lines.append("")
27
+ shell = profile.get("nx_shell_app")
28
+ if shell:
29
+ lines.append(f"**Shell / Root App:** `{shell}`")
30
+ lines.append("")
31
+ lines.append("### Projects")
32
+ lines.append("")
33
+ for name in nx_projects:
34
+ lines.append(f"- `{name}`")
35
+ lines.append("")
36
+ if nx_graph:
37
+ lines.append("### Project Dependencies")
38
+ lines.append("")
39
+ for proj, deps in nx_graph.items():
40
+ if deps:
41
+ lines.append(f"- `{proj}` → {', '.join(f'`{d}`' for d in deps)}")
42
+ else:
43
+ lines.append(f"- `{proj}` (no dependencies)")
44
+ lines.append("")
45
+
19
46
  output_path.write_text("\n".join(lines), encoding="utf-8")
@@ -10,15 +10,26 @@ def generate_tech_docs(index: dict[str, Any], output_path: Path) -> None:
10
10
  route_samples = project_profile.get("route_samples", [])
11
11
  entity_samples = project_profile.get("entity_samples", [])
12
12
 
13
+ nx_projects = project_profile.get("nx_projects")
14
+ nx_shell = project_profile.get("nx_shell_app")
15
+
13
16
  lines = [
14
17
  "# Technology Profile",
15
18
  "",
16
19
  f"**Project Root:** `{index.get('project_root', 'N/A')}`",
17
20
  f"**Languages:** {', '.join(index.get('languages', [])) or 'N/A'}",
21
+ ]
22
+
23
+ if nx_projects:
24
+ lines.append(f"**Workspace Type:** Nx Monorepo ({len(nx_projects)} projects)")
25
+ if nx_shell:
26
+ lines.append(f"**Shell App:** `{nx_shell}`")
27
+
28
+ lines.extend([
18
29
  "",
19
30
  "## Frameworks",
20
31
  "",
21
- ]
32
+ ])
22
33
 
23
34
  if frameworks:
24
35
  for framework in frameworks:
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cortexcode
3
- Version: 0.9.0
4
- Summary: Lightweight code indexing for AI assistants — save 90%+ tokens with structured context
3
+ Version: 0.10.0
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
@@ -55,44 +55,36 @@ Dynamic: license-file
55
55
  <h1 align="center">CortexCode</h1>
56
56
  <p align="center">
57
57
  <strong>Lightweight code indexing for AI assistants</strong><br>
58
- Save 90%+ tokens by giving AI agents structured context instead of raw source files.
58
+ Build a searchable codegraph, generate documentation, and give AI assistants grounded context.
59
59
  </p>
60
60
  </p>
61
61
 
62
- <p align="center">
63
- <a href="https://pypi.org/project/cortexcode/"><img src="https://img.shields.io/pypi/v/cortexcode?style=flat-square&color=blue" alt="PyPI"></a>
64
- <a href="https://pypi.org/project/cortexcode/"><img src="https://img.shields.io/pypi/pyversions/cortexcode?style=flat-square" alt="Python"></a>
65
- <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>
66
- <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>
67
- <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>
68
- </p>
69
-
70
62
  ---
71
63
 
72
64
  ## The Problem
73
65
 
74
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:
75
67
 
76
- - **Expensive** — A 150-file project can cost 200K+ tokens per query
77
- - **Slow** — More tokens = slower responses
78
- - **Wasteful** — Most of those tokens are irrelevant to the question
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
79
71
 
80
72
  ## The Solution
81
73
 
82
- CortexCode indexes your codebase using **AST parsing** (tree-sitter) and provides a structured, searchable index. Instead of feeding 200K tokens of raw code, you feed **~500 tokens of relevant context**.
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.
83
75
 
84
76
  ```
85
77
  ┌─────────────────────────────────────────────────┐
86
78
  │ Without CortexCode With CortexCode │
87
79
  │ │
88
- 200,000 tokens500 tokens
89
- $0.006/query $0.00002/query
90
- All files dumped Only relevant syms
91
- │ No structure Call graph + types
80
+ Raw files onlySearchable codegraph
81
+ Manual digging Linked symbols
82
+ Ad-hoc prompts Reusable context
83
+ │ No project map Docs + diagrams
92
84
  └─────────────────────────────────────────────────┘
93
85
  ```
94
86
 
95
- Run `cortexcode stats` on your project to see your actual savings.
87
+ Run `cortexcode index` on your project to generate the codegraph and start exploring.
96
88
 
97
89
  ## Quick Start
98
90
 
@@ -108,9 +100,6 @@ cd cortexcode && pip install -e .
108
100
  cd your-project
109
101
  cortexcode index
110
102
 
111
- # See token savings
112
- cortexcode stats
113
-
114
103
  # Get context for AI
115
104
  cortexcode context "handleAuth"
116
105
 
@@ -159,24 +148,14 @@ Parses source code into structured symbols using tree-sitter grammars.
159
148
  - **Entities** — Database models and ORM definitions
160
149
  - **Framework Detection** — React components, Angular services, etc.
161
150
 
162
- ### Token Savings
151
+ ### Project Exploration & Context
163
152
 
164
- CortexCode dramatically reduces the tokens needed to give AI assistants useful context:
153
+ Use CortexCode to inspect structure and answer practical questions about a codebase:
165
154
 
166
- ```bash
167
- $ cortexcode stats
168
-
169
- ╭──────── Token Savings Analysis ────────╮
170
- │ Source files 154 files │
171
- │ Raw project tokens 203,847 │
172
- │ Full index tokens 45,291 │
173
- │ Context query tokens 487 │
174
- │ │
175
- │ Tokens saved 203,360 │
176
- │ Savings 99.8% │
177
- │ Compression ratio 418.6x │
178
- ╰─────────────────────────────────────────╯
179
- ```
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
180
159
 
181
160
  ### Interactive HTML Documentation
182
161
 
@@ -211,7 +190,6 @@ cortexcode wiki --no-modules # Skip per-module pages (faster)
211
190
  - **Mermaid diagrams** — Auto-generated flow diagrams
212
191
  - **Concept mapping** — Maps technical concepts to symbols and files
213
192
  - **Concept search** — Ask "how does authentication work?" and get grounded answers
214
- - **Token tracking** — See exactly how many tokens each page used
215
193
 
216
194
  **Output:** `.cortexcode/wiki/index.html` — Open directly or serve locally.
217
195
 
@@ -262,12 +240,12 @@ CortexCode supports both a short form (`cc`) and full form (`cortexcode`). Comma
262
240
  | Command | Description |
263
241
  |---------|-------------|
264
242
  | `cc analyze context [query]` | Get relevant context for AI |
265
- | `cc analyze context [query] --tokens` | Show token savings for query |
243
+ | `cc analyze context [query] --tokens` | Show context size estimates for a query |
266
244
  | `cc analyze search [query]` | Grep-like symbol search with type/file filters |
267
245
  | `cc analyze find [query]` | Semantic search by meaning ("auth handler") |
268
246
  | `cc analyze diff` | Show changed symbols since last commit |
269
247
  | `cc analyze diff --ref HEAD~3` | Compare against any git ref |
270
- | `cc analyze stats` | Show project stats and token savings |
248
+ | `cc analyze stats` | Show project index and analysis stats |
271
249
  | `cc analyze scan` | Scan dependencies for security warnings |
272
250
  | `cc analyze trace <symbol>` | Trace code flow through call graph |
273
251
  | `cc analyze flow <concept>` | Analyze code flow grouped by file |
@@ -372,7 +350,7 @@ Add to `~/.windsurf/config.json`:
372
350
 
373
351
  ```bash
374
352
  # Paste this output into your AI chat
375
- cortexcode context "useAuth" --tokens
353
+ cortexcode context "useAuth"
376
354
  ```
377
355
 
378
356
  ### 2. JSON Index (programmatic)
@@ -569,7 +547,6 @@ CortexCode respects `.gitignore` files (including nested ones) and has built-in
569
547
  ## Roadmap
570
548
 
571
549
  - [x] MCP server for direct AI agent integration
572
- - [x] Tiktoken-based accurate token counting
573
550
  - [x] Semantic search over symbols (TF-IDF + synonym expansion)
574
551
  - [x] Cross-file type inference
575
552
  - [x] Git diff-aware context (show only changed symbols)
@@ -6,7 +6,7 @@ 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=lcw6iGF1rPpBpvyX9XnAKBnjl6Yatv9RpB78VceQFyo,10477
9
+ cortexcode/indexer.py,sha256=riiIOXytAri_HXKpQGjCwo9HTgL0hFJCQ03ZLKvllHs,11182
10
10
  cortexcode/lsp_server.py,sha256=mAPk78OVWQodBjBI3WhqEjrwjXrb0MF630IYDZ8_MLM,11102
11
11
  cortexcode/main.py,sha256=G7iiyXkE9c5Oe6cI4HnH6g4yXOCRih2O_Hihx9an82M,43380
12
12
  cortexcode/performance.py,sha256=tkr7RMjIuutQqcNO8wgLBNzNMFXti7y9CkiSetPam_A,706
@@ -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=O8Yy_okZf5Kcc2sLK6LN87JyWAZCS83H-l0dwdMbu1o,7018
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
@@ -72,7 +72,7 @@ cortexcode/context/context_format.py,sha256=4cH-MEDZbdDTYunUxDqgB0Wvm7CP9b_lVkyV
72
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
- cortexcode/diagrams/architecture.py,sha256=xx5yekiAF-VF0LxC9dbXsKjKskOlXQRuS6jlefoRPY8,2482
75
+ cortexcode/diagrams/architecture.py,sha256=lP_4DAqj7lzi2baxE8tpNiJs7akp31HuFULqznAKSMw,3221
76
76
  cortexcode/diagrams/call_graph.py,sha256=zcHYLKR_Sl7d8LCf5VDt0ZixgrgxrQZwpJd-YMj7FwE,2449
77
77
  cortexcode/diagrams/class_diagram.py,sha256=3Wj6Q2pI8BtqxPd220xW_1V4hvij4961_miYgZW3Pzg,2527
78
78
  cortexcode/diagrams/dependencies.py,sha256=aA2nK6BkWmZ2AiT11gSn1Y9fEdPzKac1_dWsQ2UHUvE,1098
@@ -92,28 +92,29 @@ cortexcode/docs/javascript.py,sha256=8BW1RkSV3nYFO4-ZT-ToGo_J0vBGZjJLicoUyfxsOFw
92
92
  cortexcode/docs/javascript_sections.py,sha256=3WFSYft9jRMDoFhHssKoLjoZI7JYbCXu1s3a2A4Iftg,20326
93
93
  cortexcode/docs/templates.py,sha256=9OHgXzYl_dYUBbfa-vaEgrREGtHTKuWBdcbdUyE55LY,15055
94
94
  cortexcode/indexing/__init__.py,sha256=gflZkukf26TCFhdoaaVzPghiUBRuCNNzJHfBAL0nlG4,2384
95
- cortexcode/indexing/build.py,sha256=RB_6daCcZxm-32HqDIhRt8CL_hzSAzqtm2UXHnhEMfQ,1415
95
+ cortexcode/indexing/build.py,sha256=qekM5ce27HzeQqltqowoTPFjEnoLNGo9TTpVmdBFyII,1720
96
96
  cortexcode/indexing/calls.py,sha256=zJAXfSKH7kPpAGoWhSC9l-VYa5rVnMmjRYQnSoelG6c,1144
97
97
  cortexcode/indexing/config.py,sha256=Hr1Ip2dfQUVolIkKX-KOkiNa9YF7Hd5FQoBtbkCmpdo,700
98
- cortexcode/indexing/defaults.py,sha256=mBO8sCiQIfP6vLrUNAGLiG12OGkssiVEmXjziJjcn2Y,974
98
+ cortexcode/indexing/defaults.py,sha256=5chctf4NCO13scI67x_7BZ6q8crXMdohQ_08RKR58ok,967
99
99
  cortexcode/indexing/dispatch.py,sha256=03bgGwIjc0GQdDOgsWo4aBeXZtn_Lr5Pr15cfqfSXmM,1262
100
100
  cortexcode/indexing/entities.py,sha256=OqrFCyJag12g-p1MdXM7J6JFXspBMuxxtGJufWWv6AE,3351
101
101
  cortexcode/indexing/extensions.py,sha256=Th_xfWIjz1lGYxNeeaq7oAssvr28AVSC5Ix4pPf-dGI,157
102
102
  cortexcode/indexing/extractor_mixin.py,sha256=-VznExUzl2dPqANJLFh3DhHqbcBqRawFAADBOyX2wA4,14518
103
- cortexcode/indexing/filtering.py,sha256=s6weCqElN7f-5nN4Ud4kTT9U2PsgW93p4qrxwmF2YFs,2439
103
+ cortexcode/indexing/filtering.py,sha256=xWaMYuDq_b3F1zxDmjgI7QtYeRu-UmpUWiSePhvN8Dg,2413
104
104
  cortexcode/indexing/frameworks.py,sha256=fiVLSvUasEg1SVk2qAD9iOc54Vdp9wQJSO4UJ0u_5Wk,10061
105
105
  cortexcode/indexing/gitignore.py,sha256=LNe_9NLI0nfbGxcauEIPzHdOa4B4d0ROPaByAJj76wc,2139
106
- cortexcode/indexing/imports_exports.py,sha256=0AAHHbxcRq5wvxtw7WYQZcsEmXuCD1Gv5bVWpvZnq1g,6867
106
+ cortexcode/indexing/imports_exports.py,sha256=gloMMVLaEXLyiL6xvx6EIxmwVSQX_wH_La2ADRgEZHk,7332
107
107
  cortexcode/indexing/incremental.py,sha256=2CqwzN4AJJiDY7xEv4wE6ZOkfNZLuI1h94Kl5RZgQvI,1578
108
108
  cortexcode/indexing/languages.py,sha256=VY-51IrhPn4ZHNSX8MQhapW5nthQ7Fe8iHqdYa4qg04,1324
109
109
  cortexcode/indexing/metadata.py,sha256=kvw7tm6nbtL1uNqNTff2Iki0c4SyfWpoEQ4Em52IPcw,3514
110
110
  cortexcode/indexing/nodes.py,sha256=dGfEscBjTGbeN-Cs6U84ap7KuQ6SZAWzFFc7MDDShKU,202
111
+ cortexcode/indexing/nx_projects.py,sha256=z7qAg0h8LN4eFRIW8v_ZiCmpw_7JZcDhYRZeTG4JGQM,8405
111
112
  cortexcode/indexing/output.py,sha256=UiMVoBojdPRPmX4KfeYp-E4WR5ajyBiYUwzxm88OGrU,515
112
113
  cortexcode/indexing/params.py,sha256=tMh6wBAAnKqf1hjKcap9Z9Mpb3_W0lBkf9hHAutRmaQ,921
113
114
  cortexcode/indexing/parsers.py,sha256=29JuPThW308LRzCAzDamOkY4aE17S-m6zgxc7Ni9l7Y,554
114
- cortexcode/indexing/pipeline.py,sha256=EfettW_jcHkqRMAw8HNYvL6scddVFkLl_PL1bBAc-FM,3268
115
- cortexcode/indexing/profile.py,sha256=P6BatbLwHg04Xl313_kAnlYu6rhp5vZggQG2H3AYH80,10126
116
- cortexcode/indexing/resolution.py,sha256=Vmjt_-iCJj4vRlYkh4uDIS3iivWt0rb4EObfYRB8qbk,6784
115
+ cortexcode/indexing/pipeline.py,sha256=Yjr4HkGOavoE86-QCt5JDIP1YgEoljhLD3VaUH_F0Ls,3274
116
+ cortexcode/indexing/profile.py,sha256=zi-QIER03tu2SpvH71GLmeiMI6f5xV44b8VxrCzcuTg,12183
117
+ cortexcode/indexing/resolution.py,sha256=zvAxsQffjXg9-icraHWYyPvsE30xVlQoEFaFNKPVDK0,9712
117
118
  cortexcode/indexing/routes.py,sha256=I7Umx04EgT42nuUb-Plwr8TPJBn7Pzwo1AAmKn2Kdig,5317
118
119
  cortexcode/indexing/session.py,sha256=e1KnjMK-5jxbUS1SXhpKoQTOjxiEn3rExZTJYwJn2NY,744
119
120
  cortexcode/indexing/storage.py,sha256=BZe5iNsM-ZrE7yOxBtmt1AKHoxfD2NKfTtTFKTl8q6o,418
@@ -140,7 +141,7 @@ cortexcode/mcp/mcp_server.py,sha256=RM7PGxRqpijgxc_GS3r-8ax42myEl-uDnKiXgRkpPx4,
140
141
  cortexcode/mcp/mcp_tool_handlers.py,sha256=QOA1-dWMl_iE65y1y7Gq79fICbSq4H1n0v5HqLsbdWw,9304
141
142
  cortexcode/mcp/mcp_transport.py,sha256=-ep5iL4z6lP0gahFq_3fb_fruh8kNrQMEBpK1HoPCpo,1864
142
143
  cortexcode/performance/__init__.py,sha256=GZZQcGoR-JxST8dvKMBZE-XiEQFSiMn34tcLoQYBIq0,796
143
- cortexcode/performance/performance_config.py,sha256=T28Jg-3-5Hn50wdBD5-So1o9FFDZhSzt81iH09wh_7g,4931
144
+ cortexcode/performance/performance_config.py,sha256=In90r5ZvJ3FMmSiXaPLg8tbqQ-ggYudQdzbo8hRj5eI,5892
144
145
  cortexcode/performance/performance_index_storage.py,sha256=ChRyY4iVhaLKskulGnoRQ5W7DjPVmmulZyv_83_OFF4,2151
145
146
  cortexcode/performance/performance_preview.py,sha256=i5IFXzUwuq6pJn_RZ3i8dT7Baq0dybmwVQhDPZNTUf4,2186
146
147
  cortexcode/reports/__init__.py,sha256=bYDMzLA8-wijUDc2_2g7ZBmtVWNfG6ZM1yCcdGJWdzA,369
@@ -153,8 +154,8 @@ cortexcode/reports/markdown/api.py,sha256=yxAlNS2s6JTlebkZbQIIiykuJXDwWj5FqXQIq3
153
154
  cortexcode/reports/markdown/flows.py,sha256=0XHrztcreIqTdpDXEUqR1n54HQqsS9KFc5OOSXMYnY4,1876
154
155
  cortexcode/reports/markdown/insights.py,sha256=dGHEbqOu9gojOojrM9c_ee77nAui5IZckMsNhslcP-E,2412
155
156
  cortexcode/reports/markdown/readme.py,sha256=RaLTpOSfTMy-8AbvnNoHbOGLcZ9ZjPj7xISMD4JOcek,2289
156
- cortexcode/reports/markdown/structure.py,sha256=A3DW2iEsevRmEvhucjIxYbX6oFXht2SXaF6hx2Pw5uA,406
157
- cortexcode/reports/markdown/tech.py,sha256=kvNkDI6YgBnvUXioCKPpCW-hKVj5xuxpRTaQNNTzFSo,3043
157
+ cortexcode/reports/markdown/structure.py,sha256=ETOaHbdDW8xsEf2GSyrZT0CCaUT24TsYV70Ibn5Xldg,1375
158
+ cortexcode/reports/markdown/tech.py,sha256=G6DQTt_Q7xpYb4ZsEdRuZVQIUQk3cB2yTcedDWBOiJg,3361
158
159
  cortexcode/reports/site/__init__.py,sha256=04hogJ20YLdkqpqknYqYsN2eDZdrHNJ5ZGBiMzaST3k,162
159
160
  cortexcode/reports/site/generator.py,sha256=QFjGMJZC4RPOoU0Eme09ZpLJiyrJsYdzvPWJrI6ZbUM,25044
160
161
  cortexcode/reports/site/viz.py,sha256=bb64-nkL6QwBd_WrFYpKtgQJcfucRqLAfa0CxEsW5_k,29758
@@ -165,9 +166,9 @@ cortexcode/terminal/headers.py,sha256=Yk9IGniMCiZD45V_yc1mjM3WjHimxoe9vL3CE5NAWv
165
166
  cortexcode/terminal/prompts.py,sha256=KRwjLipbO5Ihv3ycp1T2nGQtoGmKPP1ZNXb3BUz4oF4,359
166
167
  cortexcode/terminal/reports.py,sha256=4nLu0fBqDWKuQowX78VqX1nBaJl2zrTt9vEYCVP--O0,10135
167
168
  cortexcode/terminal/stats.py,sha256=PooaYP-OqaewhwypnUHESOrdwwICvggb3n2HdqEQxrc,1365
168
- cortexcode-0.9.0.dist-info/licenses/LICENSE,sha256=DWE8vkHgP2ChQTJHFWtrutLbuHQUBsU_InyfesP4neo,1067
169
- cortexcode-0.9.0.dist-info/METADATA,sha256=u5zgfzIujTGkuokhAX-zdrZ6jGHwvLr1CHc-2T-CO9Q,21856
170
- cortexcode-0.9.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
171
- cortexcode-0.9.0.dist-info/entry_points.txt,sha256=3TfwRp96e7ICJDUoJpcAu3NXVGT6HcyvHVU3pQ_vftg,78
172
- cortexcode-0.9.0.dist-info/top_level.txt,sha256=r8FxzjLfKhRXXcORnECtGo7i2zKYXlV7v1XnIJ0SOc0,11
173
- cortexcode-0.9.0.dist-info/RECORD,,
169
+ cortexcode-0.10.0.dist-info/licenses/LICENSE,sha256=DWE8vkHgP2ChQTJHFWtrutLbuHQUBsU_InyfesP4neo,1067
170
+ cortexcode-0.10.0.dist-info/METADATA,sha256=32uIm5U-rW0nuwDBubZyxiEKYpNgvPdziSYHpkItn2E,20649
171
+ cortexcode-0.10.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
172
+ cortexcode-0.10.0.dist-info/entry_points.txt,sha256=3TfwRp96e7ICJDUoJpcAu3NXVGT6HcyvHVU3pQ_vftg,78
173
+ cortexcode-0.10.0.dist-info/top_level.txt,sha256=r8FxzjLfKhRXXcORnECtGo7i2zKYXlV7v1XnIJ0SOc0,11
174
+ cortexcode-0.10.0.dist-info/RECORD,,