ctxgraph-code 0.4.1__tar.gz → 0.5.0__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.4.1 → ctxgraph_code-0.5.0}/PKG-INFO +4 -1
- {ctxgraph_code-0.4.1 → ctxgraph_code-0.5.0}/pyproject.toml +5 -1
- {ctxgraph_code-0.4.1 → ctxgraph_code-0.5.0}/src/ctxgraph_code/analyzers/treesitter/analyzer.py +10 -2
- {ctxgraph_code-0.4.1 → ctxgraph_code-0.5.0}/src/ctxgraph_code/cli.py +49 -26
- {ctxgraph_code-0.4.1 → ctxgraph_code-0.5.0}/src/ctxgraph_code/config/settings.py +8 -0
- {ctxgraph_code-0.4.1 → ctxgraph_code-0.5.0}/src/ctxgraph_code/graph/builder.py +56 -11
- {ctxgraph_code-0.4.1 → ctxgraph_code-0.5.0}/src/ctxgraph_code.egg-info/PKG-INFO +4 -1
- ctxgraph_code-0.5.0/src/ctxgraph_code.egg-info/requires.txt +9 -0
- ctxgraph_code-0.4.1/src/ctxgraph_code.egg-info/requires.txt +0 -5
- {ctxgraph_code-0.4.1 → ctxgraph_code-0.5.0}/README.md +0 -0
- {ctxgraph_code-0.4.1 → ctxgraph_code-0.5.0}/setup.cfg +0 -0
- {ctxgraph_code-0.4.1 → ctxgraph_code-0.5.0}/src/ctxgraph_code/__init__.py +0 -0
- {ctxgraph_code-0.4.1 → ctxgraph_code-0.5.0}/src/ctxgraph_code/__main__.py +0 -0
- {ctxgraph_code-0.4.1 → ctxgraph_code-0.5.0}/src/ctxgraph_code/analyzers/__init__.py +0 -0
- {ctxgraph_code-0.4.1 → ctxgraph_code-0.5.0}/src/ctxgraph_code/analyzers/python/__init__.py +0 -0
- {ctxgraph_code-0.4.1 → ctxgraph_code-0.5.0}/src/ctxgraph_code/analyzers/python/importer.py +0 -0
- {ctxgraph_code-0.4.1 → ctxgraph_code-0.5.0}/src/ctxgraph_code/analyzers/python/semantic.py +0 -0
- {ctxgraph_code-0.4.1 → ctxgraph_code-0.5.0}/src/ctxgraph_code/analyzers/python/symbols.py +0 -0
- {ctxgraph_code-0.4.1 → ctxgraph_code-0.5.0}/src/ctxgraph_code/analyzers/treesitter/__init__.py +0 -0
- {ctxgraph_code-0.4.1 → ctxgraph_code-0.5.0}/src/ctxgraph_code/analyzers/treesitter/languages.py +0 -0
- {ctxgraph_code-0.4.1 → ctxgraph_code-0.5.0}/src/ctxgraph_code/config/__init__.py +0 -0
- {ctxgraph_code-0.4.1 → ctxgraph_code-0.5.0}/src/ctxgraph_code/config/build_status.py +0 -0
- {ctxgraph_code-0.4.1 → ctxgraph_code-0.5.0}/src/ctxgraph_code/config/global_paths.py +0 -0
- {ctxgraph_code-0.4.1 → ctxgraph_code-0.5.0}/src/ctxgraph_code/config/hooks.py +0 -0
- {ctxgraph_code-0.4.1 → ctxgraph_code-0.5.0}/src/ctxgraph_code/config/init.py +0 -0
- {ctxgraph_code-0.4.1 → ctxgraph_code-0.5.0}/src/ctxgraph_code/exclude/__init__.py +0 -0
- {ctxgraph_code-0.4.1 → ctxgraph_code-0.5.0}/src/ctxgraph_code/exclude/patterns.py +0 -0
- {ctxgraph_code-0.4.1 → ctxgraph_code-0.5.0}/src/ctxgraph_code/graph/__init__.py +0 -0
- {ctxgraph_code-0.4.1 → ctxgraph_code-0.5.0}/src/ctxgraph_code/graph/models.py +0 -0
- {ctxgraph_code-0.4.1 → ctxgraph_code-0.5.0}/src/ctxgraph_code/graph/query.py +0 -0
- {ctxgraph_code-0.4.1 → ctxgraph_code-0.5.0}/src/ctxgraph_code/graph/storage.py +0 -0
- {ctxgraph_code-0.4.1 → ctxgraph_code-0.5.0}/src/ctxgraph_code/render.py +0 -0
- {ctxgraph_code-0.4.1 → ctxgraph_code-0.5.0}/src/ctxgraph_code/view/__init__.py +0 -0
- {ctxgraph_code-0.4.1 → ctxgraph_code-0.5.0}/src/ctxgraph_code/view/visualizer.py +0 -0
- {ctxgraph_code-0.4.1 → ctxgraph_code-0.5.0}/src/ctxgraph_code.egg-info/SOURCES.txt +0 -0
- {ctxgraph_code-0.4.1 → ctxgraph_code-0.5.0}/src/ctxgraph_code.egg-info/dependency_links.txt +0 -0
- {ctxgraph_code-0.4.1 → ctxgraph_code-0.5.0}/src/ctxgraph_code.egg-info/entry_points.txt +0 -0
- {ctxgraph_code-0.4.1 → ctxgraph_code-0.5.0}/src/ctxgraph_code.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ctxgraph-code
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: Code knowledge graph for Claude Code. Build a relationship graph of your Python codebase and query it during coding sessions.
|
|
5
5
|
Author: ctxgraph-code contributors
|
|
6
6
|
License: MIT
|
|
@@ -18,6 +18,9 @@ Requires-Python: >=3.10
|
|
|
18
18
|
Description-Content-Type: text/markdown
|
|
19
19
|
Requires-Dist: typer>=0.9
|
|
20
20
|
Requires-Dist: rich>=13.0
|
|
21
|
+
Provides-Extra: full
|
|
22
|
+
Requires-Dist: tree-sitter>=0.22; extra == "full"
|
|
23
|
+
Requires-Dist: tree-sitter-language-pack>=0.13; extra == "full"
|
|
21
24
|
Provides-Extra: dev
|
|
22
25
|
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
23
26
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "ctxgraph-code"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.5.0"
|
|
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"}
|
|
@@ -30,6 +30,10 @@ dependencies = [
|
|
|
30
30
|
]
|
|
31
31
|
|
|
32
32
|
[project.optional-dependencies]
|
|
33
|
+
full = [
|
|
34
|
+
"tree-sitter>=0.22",
|
|
35
|
+
"tree-sitter-language-pack>=0.13",
|
|
36
|
+
]
|
|
33
37
|
dev = [
|
|
34
38
|
"pytest>=7.0",
|
|
35
39
|
]
|
{ctxgraph_code-0.4.1 → ctxgraph_code-0.5.0}/src/ctxgraph_code/analyzers/treesitter/analyzer.py
RENAMED
|
@@ -38,8 +38,16 @@ class TSAnalyzer:
|
|
|
38
38
|
if not self.can_handle():
|
|
39
39
|
return TSAnalyzerResult()
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
try:
|
|
42
|
+
import tree_sitter as ts
|
|
43
|
+
from tree_sitter_language_pack import get_language
|
|
44
|
+
except ImportError:
|
|
45
|
+
import warnings
|
|
46
|
+
warnings.warn(
|
|
47
|
+
f"Missing tree-sitter dependency for {self.lang_name} files. "
|
|
48
|
+
"Install with: pip install 'ctxgraph-code[full]'"
|
|
49
|
+
)
|
|
50
|
+
raise
|
|
43
51
|
|
|
44
52
|
lang = self._get_lang(self.lang_name)
|
|
45
53
|
if not lang:
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import os
|
|
4
4
|
import time
|
|
@@ -245,12 +245,8 @@ def _write_slash_command(slash_path: Path, path: Path):
|
|
|
245
245
|
content = SLASH_COMMAND_TEMPLATE.format(build_time=build_label, available=avail_str)
|
|
246
246
|
|
|
247
247
|
slash_path.parent.mkdir(parents=True, exist_ok=True)
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
console.print(f"[green][OK] Created [bold]{slash_path}[/bold][/green]")
|
|
251
|
-
else:
|
|
252
|
-
console.print(f"[yellow] Skipped (already exists): [bold]{slash_path}[/bold][/yellow]")
|
|
253
|
-
console.print("[yellow] To overwrite, delete it and run again.[/yellow]")
|
|
248
|
+
slash_path.write_text(content, encoding="utf-8")
|
|
249
|
+
console.print(f"[green][OK] {'Updated' if slash_path.exists() else 'Created'} [bold]{slash_path}[/bold][/green]")
|
|
254
250
|
|
|
255
251
|
|
|
256
252
|
# ── slash command template ───────────────────────────────────────────────────
|
|
@@ -311,8 +307,7 @@ def _print_build_hint(path: Path):
|
|
|
311
307
|
|
|
312
308
|
def _build_single_graph(path, exts, user_patterns, db_path, label,
|
|
313
309
|
incremental=False, verbose=False, no_summary=False):
|
|
314
|
-
|
|
315
|
-
mark_build_started(ctx_dir, os.getpid())
|
|
310
|
+
mark_build_started(path, os.getpid())
|
|
316
311
|
start = time.time()
|
|
317
312
|
with console.status(f"Scanning {', '.join(exts)} files for '{label}'..."):
|
|
318
313
|
stats = build_graph(
|
|
@@ -324,7 +319,7 @@ def _build_single_graph(path, exts, user_patterns, db_path, label,
|
|
|
324
319
|
verbose=verbose,
|
|
325
320
|
no_summary=no_summary,
|
|
326
321
|
)
|
|
327
|
-
mark_build_complete(
|
|
322
|
+
mark_build_complete(path, time.time() - start)
|
|
328
323
|
|
|
329
324
|
table = Table(title=f"Graph Build: {label}")
|
|
330
325
|
table.add_column("Metric", style="cyan")
|
|
@@ -341,8 +336,7 @@ def _build_single_graph(path, exts, user_patterns, db_path, label,
|
|
|
341
336
|
def _build_dir_worker(path, exts, user_patterns, db_path, label,
|
|
342
337
|
incremental=False, verbose=False, no_summary=False):
|
|
343
338
|
"""Silent build worker for parallel execution. No console output."""
|
|
344
|
-
|
|
345
|
-
mark_build_started(ctx_dir, os.getpid())
|
|
339
|
+
mark_build_started(path, os.getpid())
|
|
346
340
|
start = time.time()
|
|
347
341
|
stats = build_graph(
|
|
348
342
|
path,
|
|
@@ -353,7 +347,7 @@ def _build_dir_worker(path, exts, user_patterns, db_path, label,
|
|
|
353
347
|
verbose=verbose,
|
|
354
348
|
no_summary=no_summary,
|
|
355
349
|
)
|
|
356
|
-
mark_build_complete(
|
|
350
|
+
mark_build_complete(path, time.time() - start)
|
|
357
351
|
return label, stats
|
|
358
352
|
|
|
359
353
|
|
|
@@ -363,9 +357,11 @@ def _build_dirs_parallel(path, exts, user_patterns, top_dirs, graphs_dir, jobs,
|
|
|
363
357
|
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
364
358
|
|
|
365
359
|
n_workers = max(1, jobs) if jobs > 0 else os.cpu_count() or 1
|
|
366
|
-
|
|
360
|
+
total = len(top_dirs)
|
|
361
|
+
console.print(f"[bold]Building {total} graphs with {n_workers} workers...[/bold]")
|
|
367
362
|
|
|
368
|
-
futures =
|
|
363
|
+
futures = {}
|
|
364
|
+
_build_progress_start = time.time()
|
|
369
365
|
with ThreadPoolExecutor(max_workers=n_workers) as pool:
|
|
370
366
|
for d in top_dirs:
|
|
371
367
|
db_path = graphs_dir / f"{d.name}.db"
|
|
@@ -373,15 +369,37 @@ def _build_dirs_parallel(path, exts, user_patterns, top_dirs, graphs_dir, jobs,
|
|
|
373
369
|
_build_dir_worker, path, exts, user_patterns, db_path, d.name,
|
|
374
370
|
incremental, verbose, no_summary,
|
|
375
371
|
)
|
|
376
|
-
futures.
|
|
372
|
+
futures[fut] = d.name
|
|
377
373
|
|
|
378
374
|
results = []
|
|
375
|
+
completed = 0
|
|
376
|
+
err_count = 0
|
|
377
|
+
|
|
379
378
|
for f in as_completed(futures):
|
|
379
|
+
label = futures[f]
|
|
380
|
+
completed += 1
|
|
380
381
|
try:
|
|
381
|
-
|
|
382
|
-
results.append((
|
|
382
|
+
lbl, stats = f.result()
|
|
383
|
+
results.append((lbl, stats))
|
|
384
|
+
files = stats.get("files_analyzed", 0)
|
|
385
|
+
nodes = stats.get("total_nodes", 0)
|
|
386
|
+
edges = stats.get("total_edges", 0)
|
|
387
|
+
t = stats.get("elapsed_seconds", 0)
|
|
388
|
+
console.print(
|
|
389
|
+
f" [green]✔[/green] {label}/ "
|
|
390
|
+
f"({files} files, {nodes} nodes, {edges} edges, {t}s)"
|
|
391
|
+
)
|
|
383
392
|
except Exception as e:
|
|
384
|
-
results.append((
|
|
393
|
+
results.append((label, str(e)))
|
|
394
|
+
err_count += 1
|
|
395
|
+
console.print(f" [red]✘[/red] {label}/ ([red]{e}[/red])")
|
|
396
|
+
|
|
397
|
+
elapsed_total = time.time() - _build_progress_start
|
|
398
|
+
if err_count:
|
|
399
|
+
console.print(f"\n[yellow]Built {total - err_count}/{total} graphs"
|
|
400
|
+
f" ({err_count} failed) in {elapsed_total:.1f}s[/yellow]")
|
|
401
|
+
else:
|
|
402
|
+
console.print(f"\n[green]Built all {total} graphs in {elapsed_total:.1f}s[/green]")
|
|
385
403
|
|
|
386
404
|
results.sort(key=lambda x: x[0] if isinstance(x[0], str) else "")
|
|
387
405
|
|
|
@@ -413,9 +431,8 @@ def _build_dirs_parallel(path, exts, user_patterns, top_dirs, graphs_dir, jobs,
|
|
|
413
431
|
|
|
414
432
|
console.print(summary)
|
|
415
433
|
console.print(
|
|
416
|
-
f"
|
|
417
|
-
f"
|
|
418
|
-
f"in {total_time}s[/green]"
|
|
434
|
+
f"[green]Total: {total_files} files, {total_nodes} nodes, {total_edges} edges "
|
|
435
|
+
f"in {elapsed_total:.1f}s[/green]"
|
|
419
436
|
)
|
|
420
437
|
return results
|
|
421
438
|
|
|
@@ -921,7 +938,7 @@ def probe(
|
|
|
921
938
|
5, "--max", "-m", help="Maximum files to probe"
|
|
922
939
|
),
|
|
923
940
|
context_lines: int = typer.Option(
|
|
924
|
-
|
|
941
|
+
40, "--context", "-c", help="Number of lines to show per file"
|
|
925
942
|
),
|
|
926
943
|
dir_name: Optional[str] = typer.Option(
|
|
927
944
|
None, "--dir", "-d", help="Directory graph to query"
|
|
@@ -971,10 +988,16 @@ def probe(
|
|
|
971
988
|
|
|
972
989
|
if code:
|
|
973
990
|
lines = code.splitlines()
|
|
974
|
-
|
|
991
|
+
n = context_lines if context_lines > 0 else len(lines)
|
|
992
|
+
snippet_lines = lines[:n]
|
|
975
993
|
snippet = "\n".join(snippet_lines)
|
|
976
|
-
extra = f"\n... ({len(lines) -
|
|
977
|
-
|
|
994
|
+
extra = f"\n... ({len(lines) - n} more lines)" if len(lines) > n else ""
|
|
995
|
+
lang = "python"
|
|
996
|
+
if node.path:
|
|
997
|
+
ext = Path(node.path).suffix.lower()
|
|
998
|
+
lang_map = {".js": "javascript", ".ts": "typescript", ".tsx": "typescript", ".jsx": "javascript", ".go": "go", ".rs": "rust", ".c": "c", ".h": "c", ".cpp": "cpp", ".java": "java", ".rb": "ruby", ".kt": "kotlin", ".swift": "swift", ".cs": "csharp", ".scala": "scala", ".lua": "lua", ".zig": "zig", ".php": "php", ".sh": "bash", ".ps1": "powershell", ".json": "json", ".yaml": "yaml", ".yml": "yaml"}
|
|
999
|
+
lang = lang_map.get(ext, "python")
|
|
1000
|
+
syntax = Syntax(snippet + extra, lang, theme="monokai", line_numbers=True)
|
|
978
1001
|
panel = Panel(syntax, title=header, border_style="dim")
|
|
979
1002
|
console.print(panel)
|
|
980
1003
|
else:
|
|
@@ -52,6 +52,14 @@ class Settings:
|
|
|
52
52
|
def exclude_patterns(self) -> list[str]:
|
|
53
53
|
return self._data["graph"].get("exclude", [])
|
|
54
54
|
|
|
55
|
+
@property
|
|
56
|
+
def follow_symlinks(self) -> bool:
|
|
57
|
+
return self._data["graph"].get("follow_symlinks", False)
|
|
58
|
+
|
|
59
|
+
@property
|
|
60
|
+
def max_file_size_mb(self) -> int:
|
|
61
|
+
return self._data["graph"].get("max_file_size_mb", 5)
|
|
62
|
+
|
|
55
63
|
def to_dict(self) -> dict:
|
|
56
64
|
return dict(self._data)
|
|
57
65
|
|
|
@@ -59,6 +59,11 @@ def build_graph(
|
|
|
59
59
|
if jobs <= 0:
|
|
60
60
|
jobs = (os.cpu_count() or 1)
|
|
61
61
|
|
|
62
|
+
from ctxgraph_code.config.settings import Settings
|
|
63
|
+
_settings = Settings(repo_path)
|
|
64
|
+
follow_symlinks = _settings.follow_symlinks
|
|
65
|
+
max_bytes = _settings.max_file_size_mb * 1024 * 1024
|
|
66
|
+
|
|
62
67
|
@lru_cache(maxsize=None)
|
|
63
68
|
def _should_exclude(path: Path) -> bool:
|
|
64
69
|
return should_exclude(path, repo_path, exclude_patterns)
|
|
@@ -68,7 +73,7 @@ def build_graph(
|
|
|
68
73
|
scan_files: list[Path] = []
|
|
69
74
|
current_mtimes: dict[str, float] = {}
|
|
70
75
|
|
|
71
|
-
for dirpath, dirnames, filenames in os.walk(repo_path):
|
|
76
|
+
for dirpath, dirnames, filenames in os.walk(repo_path, followlinks=follow_symlinks):
|
|
72
77
|
dirnames[:] = [
|
|
73
78
|
d for d in dirnames
|
|
74
79
|
if not _should_exclude(repo_path / d)
|
|
@@ -140,7 +145,7 @@ def build_graph(
|
|
|
140
145
|
|
|
141
146
|
with mp.Pool(jobs) as pool:
|
|
142
147
|
results = [
|
|
143
|
-
pool.apply_async(_worker_batch, (chunk, repo_path, path_index, no_summary))
|
|
148
|
+
pool.apply_async(_worker_batch, (chunk, repo_path, path_index, no_summary, max_bytes))
|
|
144
149
|
for chunk in chunks
|
|
145
150
|
]
|
|
146
151
|
for i, r in enumerate(results):
|
|
@@ -159,7 +164,7 @@ def build_graph(
|
|
|
159
164
|
else:
|
|
160
165
|
for i, file_path in enumerate(changed_files):
|
|
161
166
|
try:
|
|
162
|
-
nds, eds, fh = _process_file(file_path, repo_path, path_index, no_summary)
|
|
167
|
+
nds, eds, fh = _process_file(file_path, repo_path, path_index, no_summary, max_bytes)
|
|
163
168
|
all_nodes.extend(nds)
|
|
164
169
|
all_edges.extend(eds)
|
|
165
170
|
file_hashes.update(fh)
|
|
@@ -213,14 +218,22 @@ def build_graph(
|
|
|
213
218
|
# ── worker helpers ───────────────────────────────────────────────────────────
|
|
214
219
|
|
|
215
220
|
|
|
216
|
-
def _quick_scan(source: str) -> bool:
|
|
217
|
-
"""Quick pre-check: does this file have
|
|
218
|
-
|
|
221
|
+
def _quick_scan(source: str, is_python: bool = True) -> bool:
|
|
222
|
+
"""Quick pre-check: does this file have meaningful code?
|
|
223
|
+
Returns True if full parsing is needed."""
|
|
219
224
|
for line in source.splitlines():
|
|
220
225
|
line = line.strip()
|
|
221
|
-
if not line or line.startswith("#"):
|
|
226
|
+
if not line or line.startswith(("#", "//", "/*", "*", "--", ";")) and is_python:
|
|
227
|
+
continue
|
|
228
|
+
if not line:
|
|
222
229
|
continue
|
|
223
|
-
if
|
|
230
|
+
if is_python:
|
|
231
|
+
if any(line.startswith(kw) for kw in ("import ", "from ", "class ", "def ", "@")):
|
|
232
|
+
return True
|
|
233
|
+
else:
|
|
234
|
+
if any(kw in line for kw in ("function", "class", "struct", "trait", "interface", "import ", "include ", "fn ", "def ", "pub ", "export ", "impl ")):
|
|
235
|
+
return True
|
|
236
|
+
if any(c in line for c in ("{", "(", "=", ";")):
|
|
224
237
|
return True
|
|
225
238
|
return False
|
|
226
239
|
|
|
@@ -234,9 +247,17 @@ def _process_file(
|
|
|
234
247
|
root_path: Path,
|
|
235
248
|
path_index: set[str],
|
|
236
249
|
no_summary: bool = False,
|
|
250
|
+
max_bytes: int = 0,
|
|
237
251
|
) -> tuple[list[dict], list[dict], dict[str, str]]:
|
|
238
252
|
rel = str(file_path.relative_to(root_path)).replace("\\", "/")
|
|
239
253
|
|
|
254
|
+
if max_bytes > 0:
|
|
255
|
+
try:
|
|
256
|
+
if file_path.stat().st_size > max_bytes:
|
|
257
|
+
return [], [], {}
|
|
258
|
+
except OSError:
|
|
259
|
+
pass
|
|
260
|
+
|
|
240
261
|
try:
|
|
241
262
|
source = file_path.read_text(encoding="utf-8", errors="replace")
|
|
242
263
|
except OSError:
|
|
@@ -261,7 +282,7 @@ def _process_python(
|
|
|
261
282
|
) -> tuple[list[dict], list[dict], dict[str, str]]:
|
|
262
283
|
fhash = fhash or {}
|
|
263
284
|
|
|
264
|
-
if not _quick_scan(source):
|
|
285
|
+
if not _quick_scan(source, is_python=True):
|
|
265
286
|
return [{
|
|
266
287
|
"id": f"{root_path}:{rel}",
|
|
267
288
|
"type": "file",
|
|
@@ -320,13 +341,36 @@ def _process_treesitter(
|
|
|
320
341
|
fhash: dict[str, str] | None = None,
|
|
321
342
|
) -> tuple[list[dict], list[dict], dict[str, str]]:
|
|
322
343
|
fhash = fhash or {}
|
|
344
|
+
|
|
345
|
+
if not _quick_scan(source, is_python=False):
|
|
346
|
+
return [{
|
|
347
|
+
"id": f"{root_path}:{rel}",
|
|
348
|
+
"type": "file",
|
|
349
|
+
"name": file_path.name,
|
|
350
|
+
"path": rel,
|
|
351
|
+
"parent_id": None,
|
|
352
|
+
"summary": None,
|
|
353
|
+
"importance": 0.5,
|
|
354
|
+
"size_bytes": len(source),
|
|
355
|
+
"lineno": 0,
|
|
356
|
+
}], [], fhash
|
|
357
|
+
|
|
323
358
|
from ctxgraph_code.analyzers.treesitter import TSAnalyzer
|
|
324
359
|
|
|
325
360
|
analyzer = TSAnalyzer(file_path, root_path)
|
|
326
361
|
if not analyzer.can_handle():
|
|
327
362
|
return [], [], fhash
|
|
328
363
|
|
|
329
|
-
|
|
364
|
+
import warnings
|
|
365
|
+
try:
|
|
366
|
+
result = analyzer.analyze(source)
|
|
367
|
+
except ImportError:
|
|
368
|
+
warnings.warn(
|
|
369
|
+
f"tree-sitter not installed — skipping {rel}. "
|
|
370
|
+
"Install with: pip install 'ctxgraph-code[full]'"
|
|
371
|
+
)
|
|
372
|
+
return [], [], fhash
|
|
373
|
+
|
|
330
374
|
return result.nodes, result.edges, fhash
|
|
331
375
|
|
|
332
376
|
|
|
@@ -335,6 +379,7 @@ def _worker_batch(
|
|
|
335
379
|
root_path: Path,
|
|
336
380
|
path_index: set[str],
|
|
337
381
|
no_summary: bool = False,
|
|
382
|
+
max_bytes: int = 0,
|
|
338
383
|
) -> tuple[list[dict], list[dict], int, int, dict[str, str]]:
|
|
339
384
|
nodes: list[dict] = []
|
|
340
385
|
edges: list[dict] = []
|
|
@@ -343,7 +388,7 @@ def _worker_batch(
|
|
|
343
388
|
err = 0
|
|
344
389
|
for fp in file_paths:
|
|
345
390
|
try:
|
|
346
|
-
nds, eds, fh = _process_file(fp, root_path, path_index, no_summary)
|
|
391
|
+
nds, eds, fh = _process_file(fp, root_path, path_index, no_summary, max_bytes)
|
|
347
392
|
nodes.extend(nds)
|
|
348
393
|
edges.extend(eds)
|
|
349
394
|
hashes.update(fh)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ctxgraph-code
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: Code knowledge graph for Claude Code. Build a relationship graph of your Python codebase and query it during coding sessions.
|
|
5
5
|
Author: ctxgraph-code contributors
|
|
6
6
|
License: MIT
|
|
@@ -18,6 +18,9 @@ Requires-Python: >=3.10
|
|
|
18
18
|
Description-Content-Type: text/markdown
|
|
19
19
|
Requires-Dist: typer>=0.9
|
|
20
20
|
Requires-Dist: rich>=13.0
|
|
21
|
+
Provides-Extra: full
|
|
22
|
+
Requires-Dist: tree-sitter>=0.22; extra == "full"
|
|
23
|
+
Requires-Dist: tree-sitter-language-pack>=0.13; extra == "full"
|
|
21
24
|
Provides-Extra: dev
|
|
22
25
|
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
23
26
|
|
|
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
|
{ctxgraph_code-0.4.1 → ctxgraph_code-0.5.0}/src/ctxgraph_code/analyzers/treesitter/__init__.py
RENAMED
|
File without changes
|
{ctxgraph_code-0.4.1 → ctxgraph_code-0.5.0}/src/ctxgraph_code/analyzers/treesitter/languages.py
RENAMED
|
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
|