ctxgraph-code 0.2.0__tar.gz → 0.2.1__tar.gz
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.
- {ctxgraph_code-0.2.0 → ctxgraph_code-0.2.1}/PKG-INFO +1 -1
- {ctxgraph_code-0.2.0 → ctxgraph_code-0.2.1}/pyproject.toml +1 -1
- {ctxgraph_code-0.2.0 → ctxgraph_code-0.2.1}/src/ctxgraph_code/cli.py +80 -30
- {ctxgraph_code-0.2.0 → ctxgraph_code-0.2.1}/src/ctxgraph_code.egg-info/PKG-INFO +1 -1
- {ctxgraph_code-0.2.0 → ctxgraph_code-0.2.1}/README.md +0 -0
- {ctxgraph_code-0.2.0 → ctxgraph_code-0.2.1}/setup.cfg +0 -0
- {ctxgraph_code-0.2.0 → ctxgraph_code-0.2.1}/src/ctxgraph_code/__init__.py +0 -0
- {ctxgraph_code-0.2.0 → ctxgraph_code-0.2.1}/src/ctxgraph_code/__main__.py +0 -0
- {ctxgraph_code-0.2.0 → ctxgraph_code-0.2.1}/src/ctxgraph_code/analyzers/__init__.py +0 -0
- {ctxgraph_code-0.2.0 → ctxgraph_code-0.2.1}/src/ctxgraph_code/analyzers/python/__init__.py +0 -0
- {ctxgraph_code-0.2.0 → ctxgraph_code-0.2.1}/src/ctxgraph_code/analyzers/python/importer.py +0 -0
- {ctxgraph_code-0.2.0 → ctxgraph_code-0.2.1}/src/ctxgraph_code/analyzers/python/semantic.py +0 -0
- {ctxgraph_code-0.2.0 → ctxgraph_code-0.2.1}/src/ctxgraph_code/analyzers/python/symbols.py +0 -0
- {ctxgraph_code-0.2.0 → ctxgraph_code-0.2.1}/src/ctxgraph_code/config/__init__.py +0 -0
- {ctxgraph_code-0.2.0 → ctxgraph_code-0.2.1}/src/ctxgraph_code/config/global_paths.py +0 -0
- {ctxgraph_code-0.2.0 → ctxgraph_code-0.2.1}/src/ctxgraph_code/config/init.py +0 -0
- {ctxgraph_code-0.2.0 → ctxgraph_code-0.2.1}/src/ctxgraph_code/config/settings.py +0 -0
- {ctxgraph_code-0.2.0 → ctxgraph_code-0.2.1}/src/ctxgraph_code/exclude/__init__.py +0 -0
- {ctxgraph_code-0.2.0 → ctxgraph_code-0.2.1}/src/ctxgraph_code/exclude/patterns.py +0 -0
- {ctxgraph_code-0.2.0 → ctxgraph_code-0.2.1}/src/ctxgraph_code/graph/__init__.py +0 -0
- {ctxgraph_code-0.2.0 → ctxgraph_code-0.2.1}/src/ctxgraph_code/graph/builder.py +0 -0
- {ctxgraph_code-0.2.0 → ctxgraph_code-0.2.1}/src/ctxgraph_code/graph/models.py +0 -0
- {ctxgraph_code-0.2.0 → ctxgraph_code-0.2.1}/src/ctxgraph_code/graph/query.py +0 -0
- {ctxgraph_code-0.2.0 → ctxgraph_code-0.2.1}/src/ctxgraph_code/graph/storage.py +0 -0
- {ctxgraph_code-0.2.0 → ctxgraph_code-0.2.1}/src/ctxgraph_code/render.py +0 -0
- {ctxgraph_code-0.2.0 → ctxgraph_code-0.2.1}/src/ctxgraph_code/view/__init__.py +0 -0
- {ctxgraph_code-0.2.0 → ctxgraph_code-0.2.1}/src/ctxgraph_code/view/visualizer.py +0 -0
- {ctxgraph_code-0.2.0 → ctxgraph_code-0.2.1}/src/ctxgraph_code.egg-info/SOURCES.txt +0 -0
- {ctxgraph_code-0.2.0 → ctxgraph_code-0.2.1}/src/ctxgraph_code.egg-info/dependency_links.txt +0 -0
- {ctxgraph_code-0.2.0 → ctxgraph_code-0.2.1}/src/ctxgraph_code.egg-info/entry_points.txt +0 -0
- {ctxgraph_code-0.2.0 → ctxgraph_code-0.2.1}/src/ctxgraph_code.egg-info/requires.txt +0 -0
- {ctxgraph_code-0.2.0 → ctxgraph_code-0.2.1}/src/ctxgraph_code.egg-info/top_level.txt +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "ctxgraph-code"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.1"
|
|
8
8
|
description = "Code knowledge graph for Claude Code. Build a relationship graph of your Python codebase and query it during coding sessions."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = {text = "MIT"}
|
|
@@ -261,6 +261,76 @@ def _build_single_graph(path, exts, user_patterns, db_path, label):
|
|
|
261
261
|
return stats
|
|
262
262
|
|
|
263
263
|
|
|
264
|
+
def _build_dir_worker(path, exts, user_patterns, db_path, label):
|
|
265
|
+
"""Silent build worker for parallel execution. No console output."""
|
|
266
|
+
stats = build_graph(
|
|
267
|
+
path,
|
|
268
|
+
db_path=db_path,
|
|
269
|
+
exclude_patterns=user_patterns,
|
|
270
|
+
extensions=exts,
|
|
271
|
+
)
|
|
272
|
+
return label, stats
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def _build_dirs_parallel(path, exts, user_patterns, top_dirs, graphs_dir, jobs):
|
|
276
|
+
"""Build per-directory graphs in parallel using a thread pool."""
|
|
277
|
+
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
278
|
+
|
|
279
|
+
n_workers = max(1, jobs) if jobs > 0 else os.cpu_count() or 1
|
|
280
|
+
console.print(f"[dim]Building {len(top_dirs)} graphs with {n_workers} workers...[/dim]")
|
|
281
|
+
|
|
282
|
+
futures = []
|
|
283
|
+
with ThreadPoolExecutor(max_workers=n_workers) as pool:
|
|
284
|
+
for d in top_dirs:
|
|
285
|
+
db_path = graphs_dir / f"{d.name}.db"
|
|
286
|
+
fut = pool.submit(_build_dir_worker, path, exts, user_patterns, db_path, d.name)
|
|
287
|
+
futures.append(fut)
|
|
288
|
+
|
|
289
|
+
results = []
|
|
290
|
+
for f in as_completed(futures):
|
|
291
|
+
try:
|
|
292
|
+
label, stats = f.result()
|
|
293
|
+
results.append((label, stats))
|
|
294
|
+
except Exception as e:
|
|
295
|
+
results.append(("error", str(e)))
|
|
296
|
+
|
|
297
|
+
results.sort(key=lambda x: x[0] if isinstance(x[0], str) else "")
|
|
298
|
+
|
|
299
|
+
summary = Table(title="Build Summary")
|
|
300
|
+
summary.add_column("Directory", style="cyan")
|
|
301
|
+
summary.add_column("Files", style="green")
|
|
302
|
+
summary.add_column("Nodes", style="blue")
|
|
303
|
+
summary.add_column("Edges", style="yellow")
|
|
304
|
+
summary.add_column("Time", style="magenta")
|
|
305
|
+
|
|
306
|
+
total_files = 0
|
|
307
|
+
total_nodes = 0
|
|
308
|
+
total_edges = 0
|
|
309
|
+
total_time = 0.0
|
|
310
|
+
|
|
311
|
+
for label, stats in results:
|
|
312
|
+
if isinstance(stats, str):
|
|
313
|
+
summary.add_row(f"{label}/", "[red]ERROR[/red]", stats, "", "")
|
|
314
|
+
else:
|
|
315
|
+
files = stats.get("files_analyzed", 0)
|
|
316
|
+
nodes = stats.get("total_nodes", 0)
|
|
317
|
+
edges = stats.get("total_edges", 0)
|
|
318
|
+
t = stats.get("elapsed_seconds", 0)
|
|
319
|
+
summary.add_row(f"{label}/", str(files), str(nodes), str(edges), f"{t}s")
|
|
320
|
+
total_files += files
|
|
321
|
+
total_nodes += nodes
|
|
322
|
+
total_edges += edges
|
|
323
|
+
total_time = max(total_time, t)
|
|
324
|
+
|
|
325
|
+
console.print(summary)
|
|
326
|
+
console.print(
|
|
327
|
+
f"\n[green]Built {len(top_dirs)} graphs: "
|
|
328
|
+
f"{total_files} files, {total_nodes} nodes, {total_edges} edges "
|
|
329
|
+
f"in {total_time}s[/green]"
|
|
330
|
+
)
|
|
331
|
+
return results
|
|
332
|
+
|
|
333
|
+
|
|
264
334
|
@app.command()
|
|
265
335
|
def build(
|
|
266
336
|
repo_path: Optional[str] = typer.Argument(
|
|
@@ -275,10 +345,13 @@ def build(
|
|
|
275
345
|
all_graph: bool = typer.Option(
|
|
276
346
|
False, "--all", "-a", help="Build a single combined graph instead of per-directory"
|
|
277
347
|
),
|
|
348
|
+
jobs: int = typer.Option(
|
|
349
|
+
0, "--jobs", "-j", help="Number of parallel workers (0 = auto, default: CPU count)"
|
|
350
|
+
),
|
|
278
351
|
):
|
|
279
352
|
"""Build the knowledge graph from source files.
|
|
280
353
|
|
|
281
|
-
Default: builds a separate graph per top-level directory.
|
|
354
|
+
Default: builds a separate graph per top-level directory in parallel.
|
|
282
355
|
Use --all to build one combined graph instead.
|
|
283
356
|
"""
|
|
284
357
|
path = Path(repo_path).resolve() if repo_path else Path.cwd()
|
|
@@ -299,7 +372,6 @@ def build(
|
|
|
299
372
|
db_path = ctx_dir / "graph.db"
|
|
300
373
|
_build_single_graph(path, exts, user_patterns, db_path, "combined")
|
|
301
374
|
else:
|
|
302
|
-
# Per-directory: find top-level dirs (skip excluded ones)
|
|
303
375
|
from ctxgraph_code.exclude.patterns import should_exclude
|
|
304
376
|
top_dirs = sorted([
|
|
305
377
|
d for d in path.iterdir()
|
|
@@ -314,28 +386,7 @@ def build(
|
|
|
314
386
|
else:
|
|
315
387
|
graphs_dir = ctx_dir / "graphs"
|
|
316
388
|
graphs_dir.mkdir(parents=True, exist_ok=True)
|
|
317
|
-
|
|
318
|
-
total_nodes = 0
|
|
319
|
-
total_edges = 0
|
|
320
|
-
for d in top_dirs:
|
|
321
|
-
db_path = graphs_dir / f"{d.name}.db"
|
|
322
|
-
s = _build_single_graph(path, exts, user_patterns, db_path, d.name)
|
|
323
|
-
total_nodes += s.get("total_nodes", 0)
|
|
324
|
-
total_edges += s.get("total_edges", 0)
|
|
325
|
-
|
|
326
|
-
summary = Table(title="Build Summary")
|
|
327
|
-
summary.add_column("Directory", style="cyan")
|
|
328
|
-
for d in top_dirs:
|
|
329
|
-
db = graphs_dir / f"{d.name}.db"
|
|
330
|
-
if db.exists():
|
|
331
|
-
from ctxgraph_code.graph.storage import Storage
|
|
332
|
-
st = Storage(db)
|
|
333
|
-
st.connect()
|
|
334
|
-
nd = st.stats()["nodes"]
|
|
335
|
-
ed = st.stats()["edges"]
|
|
336
|
-
summary.add_row(f"{d.name}/: {nd} nodes, {ed} edges")
|
|
337
|
-
console.print(summary)
|
|
338
|
-
console.print(f"\n[green]Built {len(top_dirs)} graphs in {graphs_dir}[/green]")
|
|
389
|
+
_build_dirs_parallel(path, exts, user_patterns, top_dirs, graphs_dir, jobs)
|
|
339
390
|
|
|
340
391
|
|
|
341
392
|
@app.command()
|
|
@@ -490,6 +541,9 @@ def setup(
|
|
|
490
541
|
False, "--project-slash",
|
|
491
542
|
help="Install slash command in project .claude/ instead of globally",
|
|
492
543
|
),
|
|
544
|
+
jobs: int = typer.Option(
|
|
545
|
+
0, "--jobs", "-j", help="Number of parallel workers (0 = auto, default: CPU count)"
|
|
546
|
+
),
|
|
493
547
|
):
|
|
494
548
|
"""Initialize config, build the graph, and install the Claude Code slash command.
|
|
495
549
|
|
|
@@ -529,7 +583,6 @@ def setup(
|
|
|
529
583
|
init_project(path, extensions=exts, exclude_patterns=excl)
|
|
530
584
|
console.print("[green][OK] Initialized .ctxgraph/[/green]")
|
|
531
585
|
|
|
532
|
-
# Build per-directory graphs
|
|
533
586
|
ctx_dir = path / ".ctxgraph"
|
|
534
587
|
graphs_dir = ctx_dir / "graphs"
|
|
535
588
|
graphs_dir.mkdir(parents=True, exist_ok=True)
|
|
@@ -539,10 +592,7 @@ def setup(
|
|
|
539
592
|
])
|
|
540
593
|
|
|
541
594
|
if top_dirs:
|
|
542
|
-
|
|
543
|
-
db_path = graphs_dir / f"{d.name}.db"
|
|
544
|
-
_build_single_graph(path, exts, excl, db_path, d.name)
|
|
545
|
-
console.print(f"[green][OK] Built graphs for {len(top_dirs)} directories[/green]")
|
|
595
|
+
_build_dirs_parallel(path, exts, excl, top_dirs, graphs_dir, jobs)
|
|
546
596
|
else:
|
|
547
597
|
db_path = ctx_dir / "graph.db"
|
|
548
598
|
_build_single_graph(path, exts, excl, db_path, "combined")
|
|
@@ -684,7 +734,7 @@ def version():
|
|
|
684
734
|
try:
|
|
685
735
|
ver = _v("ctxgraph-code")
|
|
686
736
|
except Exception:
|
|
687
|
-
ver = "0.2.
|
|
737
|
+
ver = "0.2.1"
|
|
688
738
|
console.print(f"ctxgraph-code version [bold]{ver}[/bold]")
|
|
689
739
|
|
|
690
740
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|