code-review-graph-codeblackwell 2.3.6.post1__tar.gz → 2.4.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.
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/PKG-INFO +3 -3
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/README.md +2 -2
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/code_review_graph/graph.py +45 -1
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/code_review_graph/incremental.py +225 -1
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/code_review_graph/main.py +5 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/code_review_graph/parser.py +1143 -42
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/code_review_graph/tools/query.py +37 -1
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/code_review_graph/tools/review.py +8 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/code_review_graph/visualization.py +28 -7
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/docs/COMMANDS.md +3 -1
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/docs/FEATURES.md +1 -1
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/docs/USAGE.md +1 -1
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/docs/schema.md +27 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/pyproject.toml +1 -1
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/.beads/README.md +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/.beads/hooks/post-checkout +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/.beads/hooks/post-merge +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/.beads/hooks/pre-commit +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/.beads/hooks/pre-push +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/.beads/hooks/prepare-commit-msg +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/.gitignore +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/LICENSE +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/code-review-graph-vscode/LICENSE +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/code-review-graph-vscode/README.md +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/code_review_graph/__init__.py +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/code_review_graph/__main__.py +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/code_review_graph/analysis.py +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/code_review_graph/changes.py +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/code_review_graph/cli.py +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/code_review_graph/communities.py +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/code_review_graph/constants.py +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/code_review_graph/context_savings.py +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/code_review_graph/custom_languages.py +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/code_review_graph/daemon.py +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/code_review_graph/daemon_cli.py +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/code_review_graph/embeddings.py +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/code_review_graph/enrich.py +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/code_review_graph/eval/__init__.py +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/code_review_graph/eval/benchmarks/__init__.py +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/code_review_graph/eval/benchmarks/agent_baseline.py +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/code_review_graph/eval/benchmarks/build_performance.py +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/code_review_graph/eval/benchmarks/flow_completeness.py +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/code_review_graph/eval/benchmarks/impact_accuracy.py +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/code_review_graph/eval/benchmarks/multi_hop_retrieval.py +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/code_review_graph/eval/benchmarks/search_quality.py +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/code_review_graph/eval/benchmarks/token_efficiency.py +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/code_review_graph/eval/configs/code-review-graph.yaml +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/code_review_graph/eval/configs/express.yaml +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/code_review_graph/eval/configs/fastapi.yaml +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/code_review_graph/eval/configs/flask.yaml +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/code_review_graph/eval/configs/gin.yaml +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/code_review_graph/eval/configs/httpx.yaml +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/code_review_graph/eval/reporter.py +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/code_review_graph/eval/runner.py +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/code_review_graph/eval/scorer.py +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/code_review_graph/eval/token_benchmark.py +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/code_review_graph/exports.py +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/code_review_graph/flows.py +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/code_review_graph/graph_diff.py +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/code_review_graph/hints.py +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/code_review_graph/jedi_resolver.py +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/code_review_graph/memory.py +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/code_review_graph/migrations.py +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/code_review_graph/postprocessing.py +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/code_review_graph/prompts.py +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/code_review_graph/refactor.py +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/code_review_graph/registry.py +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/code_review_graph/rescript_resolver.py +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/code_review_graph/search.py +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/code_review_graph/skills.py +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/code_review_graph/spring_resolver.py +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/code_review_graph/temporal_resolver.py +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/code_review_graph/token_benchmark.py +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/code_review_graph/tools/__init__.py +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/code_review_graph/tools/_common.py +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/code_review_graph/tools/analysis_tools.py +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/code_review_graph/tools/build.py +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/code_review_graph/tools/community_tools.py +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/code_review_graph/tools/context.py +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/code_review_graph/tools/docs.py +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/code_review_graph/tools/flows_tools.py +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/code_review_graph/tools/refactor_tools.py +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/code_review_graph/tools/registry_tools.py +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/code_review_graph/tsconfig_resolver.py +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/code_review_graph/wiki.py +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/docs/CUSTOM_LANGUAGES.md +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/docs/FAQ.md +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/docs/GITHUB_ACTION.md +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/docs/INDEX.md +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/docs/LEGAL.md +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/docs/LLM-OPTIMIZED-REFERENCE.md +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/docs/REPRODUCING.md +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/docs/ROADMAP.md +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/docs/TROUBLESHOOTING.md +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/docs/architecture.md +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/hooks/hooks.json +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/hooks/session-start.sh +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/skills/build-graph/SKILL.md +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/skills/debug-issue/SKILL.md +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/skills/explore-codebase/SKILL.md +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/skills/refactor-safely/SKILL.md +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/skills/review-changes/SKILL.md +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/skills/review-delta/SKILL.md +0 -0
- {code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/skills/review-pr/SKILL.md +0 -0
{code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: code-review-graph-codeblackwell
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.4.0
|
|
4
4
|
Summary: Local-first knowledge graph for token-efficient code review through MCP and CLI. Fork of tirth8205/code-review-graph with additional fixes; CLI and MCP server remain `code-review-graph`.
|
|
5
5
|
Project-URL: Homepage, https://github.com/CodeBlackwell/code-review-graph-codeblackwell
|
|
6
6
|
Project-URL: Repository, https://github.com/CodeBlackwell/code-review-graph-codeblackwell
|
|
@@ -191,7 +191,7 @@ Large monorepos are where token waste is most painful. The graph cuts through th
|
|
|
191
191
|
<img src="diagrams/diagram9_language_coverage.png" alt="Language coverage organized by category: Web, Backend, Systems, Mobile, Scripting, Config, plus Jupyter and Databricks notebook support" width="90%" />
|
|
192
192
|
</p>
|
|
193
193
|
|
|
194
|
-
Parser support covers functions, classes, imports, call sites, inheritance, and test detection across the current parser surface, using Tree-sitter where available and targeted fallbacks where needed. Current support includes Python, JavaScript/TypeScript/TSX, Go, Rust, Java, C/C++, C#, Ruby, Kotlin, Swift, PHP, Scala, Solidity, Dart, R, Perl, Lua/Luau, Objective-C, shell scripts, Elixir, Zig, PowerShell, Julia, ReScript, GDScript, Nix, Verilog/SystemVerilog, SQL, Vue/Svelte SFCs, Astro files parsed through the TypeScript parser, Jupyter/Databricks notebooks (`.ipynb`), and Perl XS files (`.xs`).
|
|
194
|
+
Parser support covers functions, classes, imports, call sites, inheritance, and test detection across the current parser surface, using Tree-sitter where available and targeted fallbacks where needed. Current support includes Python, JavaScript/TypeScript/TSX, Go, Rust, Java, C/C++, C#, Ruby, Kotlin, Swift, PHP, Scala, Solidity, Dart, R, Perl, Lua/Luau, Objective-C, shell scripts, Elixir, Zig, PowerShell, Julia, ReScript, GDScript, Nix, Verilog/SystemVerilog, SQL, CSS/SCSS/LESS stylesheets, Vue/Svelte SFCs (including `<style>` blocks), Astro files parsed through the TypeScript parser, Jupyter/Databricks notebooks (`.ipynb`), and Perl XS files (`.xs`).
|
|
195
195
|
|
|
196
196
|
### Add your own language (no fork needed)
|
|
197
197
|
|
|
@@ -320,7 +320,7 @@ The benchmark also runs an honest **co-change mode**: the predictor is seeded wi
|
|
|
320
320
|
| Feature | Details |
|
|
321
321
|
|---------|---------|
|
|
322
322
|
| **Incremental updates** | Re-parses only changed files. Subsequent updates complete in under 2 seconds. |
|
|
323
|
-
| **Broad language + notebook support** | Python, JavaScript/TypeScript/TSX, Go, Rust, Java, C/C++, C#, Ruby, Kotlin, Swift, PHP, Scala, Solidity, Dart, R, Perl, Lua/Luau, Objective-C, shell scripts, Elixir, Zig, PowerShell, Julia, ReScript, GDScript, Nix, Verilog/SystemVerilog, SQL, Vue/Svelte SFCs, Astro files parsed through the TypeScript parser, Jupyter/Databricks (.ipynb), and Perl XS (.xs) |
|
|
323
|
+
| **Broad language + notebook support** | Python, JavaScript/TypeScript/TSX, Go, Rust, Java, C/C++, C#, Ruby, Kotlin, Swift, PHP, Scala, Solidity, Dart, R, Perl, Lua/Luau, Objective-C, shell scripts, Elixir, Zig, PowerShell, Julia, ReScript, GDScript, Nix, Verilog/SystemVerilog, SQL, CSS/SCSS/LESS, Vue/Svelte SFCs (including `<style>` blocks), Astro files parsed through the TypeScript parser, Jupyter/Databricks (.ipynb), and Perl XS (.xs) |
|
|
324
324
|
| **Blast-radius analysis** | Shows which functions, classes, and files are likely affected by a change |
|
|
325
325
|
| **Auto-update hooks** | Hooks and watch mode can update the graph on file saves and supported commit hooks |
|
|
326
326
|
| **Semantic search** | Optional vector embeddings via sentence-transformers, Google Gemini, MiniMax, or any OpenAI-compatible endpoint (real OpenAI, Azure, new-api, LiteLLM, vLLM, LocalAI) |
|
{code_review_graph_codeblackwell-2.3.6.post1 → code_review_graph_codeblackwell-2.4.0}/README.md
RENAMED
|
@@ -129,7 +129,7 @@ Large monorepos are where token waste is most painful. The graph cuts through th
|
|
|
129
129
|
<img src="diagrams/diagram9_language_coverage.png" alt="Language coverage organized by category: Web, Backend, Systems, Mobile, Scripting, Config, plus Jupyter and Databricks notebook support" width="90%" />
|
|
130
130
|
</p>
|
|
131
131
|
|
|
132
|
-
Parser support covers functions, classes, imports, call sites, inheritance, and test detection across the current parser surface, using Tree-sitter where available and targeted fallbacks where needed. Current support includes Python, JavaScript/TypeScript/TSX, Go, Rust, Java, C/C++, C#, Ruby, Kotlin, Swift, PHP, Scala, Solidity, Dart, R, Perl, Lua/Luau, Objective-C, shell scripts, Elixir, Zig, PowerShell, Julia, ReScript, GDScript, Nix, Verilog/SystemVerilog, SQL, Vue/Svelte SFCs, Astro files parsed through the TypeScript parser, Jupyter/Databricks notebooks (`.ipynb`), and Perl XS files (`.xs`).
|
|
132
|
+
Parser support covers functions, classes, imports, call sites, inheritance, and test detection across the current parser surface, using Tree-sitter where available and targeted fallbacks where needed. Current support includes Python, JavaScript/TypeScript/TSX, Go, Rust, Java, C/C++, C#, Ruby, Kotlin, Swift, PHP, Scala, Solidity, Dart, R, Perl, Lua/Luau, Objective-C, shell scripts, Elixir, Zig, PowerShell, Julia, ReScript, GDScript, Nix, Verilog/SystemVerilog, SQL, CSS/SCSS/LESS stylesheets, Vue/Svelte SFCs (including `<style>` blocks), Astro files parsed through the TypeScript parser, Jupyter/Databricks notebooks (`.ipynb`), and Perl XS files (`.xs`).
|
|
133
133
|
|
|
134
134
|
### Add your own language (no fork needed)
|
|
135
135
|
|
|
@@ -258,7 +258,7 @@ The benchmark also runs an honest **co-change mode**: the predictor is seeded wi
|
|
|
258
258
|
| Feature | Details |
|
|
259
259
|
|---------|---------|
|
|
260
260
|
| **Incremental updates** | Re-parses only changed files. Subsequent updates complete in under 2 seconds. |
|
|
261
|
-
| **Broad language + notebook support** | Python, JavaScript/TypeScript/TSX, Go, Rust, Java, C/C++, C#, Ruby, Kotlin, Swift, PHP, Scala, Solidity, Dart, R, Perl, Lua/Luau, Objective-C, shell scripts, Elixir, Zig, PowerShell, Julia, ReScript, GDScript, Nix, Verilog/SystemVerilog, SQL, Vue/Svelte SFCs, Astro files parsed through the TypeScript parser, Jupyter/Databricks (.ipynb), and Perl XS (.xs) |
|
|
261
|
+
| **Broad language + notebook support** | Python, JavaScript/TypeScript/TSX, Go, Rust, Java, C/C++, C#, Ruby, Kotlin, Swift, PHP, Scala, Solidity, Dart, R, Perl, Lua/Luau, Objective-C, shell scripts, Elixir, Zig, PowerShell, Julia, ReScript, GDScript, Nix, Verilog/SystemVerilog, SQL, CSS/SCSS/LESS, Vue/Svelte SFCs (including `<style>` blocks), Astro files parsed through the TypeScript parser, Jupyter/Databricks (.ipynb), and Perl XS (.xs) |
|
|
262
262
|
| **Blast-radius analysis** | Shows which functions, classes, and files are likely affected by a change |
|
|
263
263
|
| **Auto-update hooks** | Hooks and watch mode can update the graph on file saves and supported commit hooks |
|
|
264
264
|
| **Semantic search** | Optional vector embeddings via sentence-transformers, Google Gemini, MiniMax, or any OpenAI-compatible endpoint (real OpenAI, Azure, new-api, LiteLLM, vLLM, LocalAI) |
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"""SQLite-backed knowledge graph storage and query engine.
|
|
2
2
|
|
|
3
3
|
Stores code structure as nodes (File, Class, Function, Type, Test) and
|
|
4
|
-
edges (CALLS, IMPORTS_FROM, INHERITS, IMPLEMENTS, CONTAINS, TESTED_BY, DEPENDS_ON,
|
|
4
|
+
edges (CALLS, IMPORTS_FROM, INHERITS, IMPLEMENTS, CONTAINS, TESTED_BY, DEPENDS_ON,
|
|
5
|
+
REFERENCES, OVERRIDES, STYLES, POTENTIAL_CONFLICT).
|
|
5
6
|
Supports impact-radius queries and subgraph extraction.
|
|
6
7
|
"""
|
|
7
8
|
|
|
@@ -1310,6 +1311,32 @@ class GraphStore:
|
|
|
1310
1311
|
nodes_by_id=nodes_by_id,
|
|
1311
1312
|
)
|
|
1312
1313
|
|
|
1314
|
+
def delete_edges_by_kind(self, kind: str) -> int:
|
|
1315
|
+
"""Delete all edges of the given kind. Returns count of deleted rows."""
|
|
1316
|
+
cur = self._conn.execute("DELETE FROM edges WHERE kind = ?", (kind,))
|
|
1317
|
+
self._invalidate_cache()
|
|
1318
|
+
return cur.rowcount
|
|
1319
|
+
|
|
1320
|
+
def get_nodes_by_extra_pattern(
|
|
1321
|
+
self, pattern: str, kind: str | None = None,
|
|
1322
|
+
) -> list[GraphNode]:
|
|
1323
|
+
"""Find nodes where extra JSON matches a LIKE pattern.
|
|
1324
|
+
|
|
1325
|
+
Args:
|
|
1326
|
+
pattern: SQL LIKE pattern, e.g. '%"css_kind":"selector"%'.
|
|
1327
|
+
kind: Optional node kind filter (Class, Function, etc.).
|
|
1328
|
+
"""
|
|
1329
|
+
if kind:
|
|
1330
|
+
rows = self._conn.execute(
|
|
1331
|
+
"SELECT * FROM nodes WHERE extra LIKE ? AND kind = ?",
|
|
1332
|
+
(pattern, kind),
|
|
1333
|
+
).fetchall()
|
|
1334
|
+
else:
|
|
1335
|
+
rows = self._conn.execute(
|
|
1336
|
+
"SELECT * FROM nodes WHERE extra LIKE ?", (pattern,),
|
|
1337
|
+
).fetchall()
|
|
1338
|
+
return [self._row_to_node(r) for r in rows]
|
|
1339
|
+
|
|
1313
1340
|
# --- Internal helpers ---
|
|
1314
1341
|
|
|
1315
1342
|
def _build_networkx_graph(self) -> nx.DiGraph:
|
|
@@ -1406,6 +1433,21 @@ def _sanitize_name(s: str, max_len: int = 256) -> str:
|
|
|
1406
1433
|
return cleaned[:max_len]
|
|
1407
1434
|
|
|
1408
1435
|
|
|
1436
|
+
def _sanitize_extra(extra: dict) -> dict:
|
|
1437
|
+
"""Sanitize string values in extra dict for safe display."""
|
|
1438
|
+
if not extra:
|
|
1439
|
+
return {}
|
|
1440
|
+
|
|
1441
|
+
def _sanitize_value(v): # type: ignore[no-untyped-def]
|
|
1442
|
+
if isinstance(v, str):
|
|
1443
|
+
return _sanitize_name(v)
|
|
1444
|
+
if isinstance(v, list):
|
|
1445
|
+
return [_sanitize_value(item) for item in v]
|
|
1446
|
+
return v
|
|
1447
|
+
|
|
1448
|
+
return {k: _sanitize_value(v) for k, v in extra.items()}
|
|
1449
|
+
|
|
1450
|
+
|
|
1409
1451
|
def node_to_dict(n: GraphNode) -> dict:
|
|
1410
1452
|
return {
|
|
1411
1453
|
"id": n.id, "kind": n.kind, "name": _sanitize_name(n.name),
|
|
@@ -1414,6 +1456,7 @@ def node_to_dict(n: GraphNode) -> dict:
|
|
|
1414
1456
|
"language": n.language,
|
|
1415
1457
|
"parent_name": _sanitize_name(n.parent_name) if n.parent_name else n.parent_name,
|
|
1416
1458
|
"is_test": n.is_test,
|
|
1459
|
+
"extra": _sanitize_extra(n.extra),
|
|
1417
1460
|
}
|
|
1418
1461
|
|
|
1419
1462
|
|
|
@@ -1424,4 +1467,5 @@ def edge_to_dict(e: GraphEdge) -> dict:
|
|
|
1424
1467
|
"target": _sanitize_name(e.target_qualified),
|
|
1425
1468
|
"file_path": e.file_path, "line": e.line,
|
|
1426
1469
|
"confidence": e.confidence, "confidence_tier": e.confidence_tier,
|
|
1470
|
+
"extra": _sanitize_extra(e.extra),
|
|
1427
1471
|
}
|
|
@@ -82,6 +82,30 @@ def _run_spring_resolver(store: GraphStore) -> Optional[dict]:
|
|
|
82
82
|
return None
|
|
83
83
|
|
|
84
84
|
|
|
85
|
+
def _run_css_resolver(store: GraphStore) -> Optional[dict]:
|
|
86
|
+
"""Run cross-file CSS linking + conflict detection, swallowing any
|
|
87
|
+
failure so build never fails because of it. Returns stats or None.
|
|
88
|
+
"""
|
|
89
|
+
try:
|
|
90
|
+
selectors = _get_css_selectors(store)
|
|
91
|
+
styles_count = link_css_styles(store, selectors)
|
|
92
|
+
conflicts_count = detect_cross_file_conflicts(store, selectors)
|
|
93
|
+
logger.info(
|
|
94
|
+
"CSS linking: %d STYLES edges, %d POTENTIAL_CONFLICT edges",
|
|
95
|
+
styles_count, conflicts_count,
|
|
96
|
+
)
|
|
97
|
+
return {"styles_edges": styles_count, "conflict_edges": conflicts_count}
|
|
98
|
+
except Exception as exc: # noqa: BLE001 - best-effort post-pass
|
|
99
|
+
logger.warning("CSS resolver failed: %s", exc)
|
|
100
|
+
return None
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
# File extensions whose changes can affect STYLES/POTENTIAL_CONFLICT edges.
|
|
104
|
+
_CSS_RELEVANT_EXTENSIONS = (
|
|
105
|
+
".css", ".scss", ".less", ".vue", ".svelte", ".tsx", ".jsx", ".ts", ".js",
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
|
|
85
109
|
def _run_temporal_resolver(store: GraphStore) -> Optional[dict]:
|
|
86
110
|
"""Run the Temporal workflow/activity call resolver, swallowing any failure so
|
|
87
111
|
build never fails because of it. Returns stats or None on error.
|
|
@@ -728,7 +752,9 @@ def _single_hop_dependents(store: GraphStore, file_path: str) -> set[str]:
|
|
|
728
752
|
nodes = store.get_nodes_by_file(file_path)
|
|
729
753
|
for node in nodes:
|
|
730
754
|
for e in store.get_edges_by_target(node.qualified_name):
|
|
731
|
-
if e.kind in (
|
|
755
|
+
if e.kind in (
|
|
756
|
+
"CALLS", "IMPORTS_FROM", "INHERITS", "IMPLEMENTS", "STYLES",
|
|
757
|
+
):
|
|
732
758
|
dependents.add(e.file_path)
|
|
733
759
|
|
|
734
760
|
dependents.discard(file_path)
|
|
@@ -817,6 +843,194 @@ def _parse_single_file(
|
|
|
817
843
|
return (rel_path, [], [], str(e), "")
|
|
818
844
|
|
|
819
845
|
|
|
846
|
+
def _get_css_selectors(store: GraphStore) -> list:
|
|
847
|
+
"""Fetch all CSS selector nodes (css_kind == "selector")."""
|
|
848
|
+
return store.get_nodes_by_extra_pattern(
|
|
849
|
+
'%"css_kind":%"selector"%', kind="Class",
|
|
850
|
+
)
|
|
851
|
+
|
|
852
|
+
|
|
853
|
+
def link_css_styles(store: GraphStore, selectors: list | None = None) -> int:
|
|
854
|
+
"""Create STYLES edges between class references and CSS selectors.
|
|
855
|
+
|
|
856
|
+
Queries the graph for:
|
|
857
|
+
1. All CSS selector nodes (css_kind == "selector")
|
|
858
|
+
2. All nodes with css_classes in extra (JSX functions, Vue/Svelte files)
|
|
859
|
+
|
|
860
|
+
Creates STYLES edges from the referencing component/function to
|
|
861
|
+
the matching CSS selector node.
|
|
862
|
+
|
|
863
|
+
Returns count of STYLES edges created.
|
|
864
|
+
"""
|
|
865
|
+
from .parser import CodeParser, EdgeInfo # noqa: F811 (avoid circular)
|
|
866
|
+
|
|
867
|
+
# Clean up previous STYLES edges
|
|
868
|
+
store.delete_edges_by_kind("STYLES")
|
|
869
|
+
|
|
870
|
+
# Build selector index: bare class name → list of (qualified_name, file_path)
|
|
871
|
+
if selectors is None:
|
|
872
|
+
selectors = _get_css_selectors(store)
|
|
873
|
+
selector_index: dict[str, list[tuple[str, str]]] = {}
|
|
874
|
+
for sel in selectors:
|
|
875
|
+
sel_name = sel.name.strip()
|
|
876
|
+
# Only index class selectors (starting with .)
|
|
877
|
+
if sel_name.startswith("."):
|
|
878
|
+
bare = sel_name.lstrip(".")
|
|
879
|
+
selector_index.setdefault(bare, []).append(
|
|
880
|
+
(sel.qualified_name, sel.file_path),
|
|
881
|
+
)
|
|
882
|
+
# For compound selectors, also index by last class segment
|
|
883
|
+
parts = sel_name.split()
|
|
884
|
+
if len(parts) > 1:
|
|
885
|
+
last = parts[-1].strip()
|
|
886
|
+
if last.startswith("."):
|
|
887
|
+
bare = last.lstrip(".")
|
|
888
|
+
selector_index.setdefault(bare, []).append(
|
|
889
|
+
(sel.qualified_name, sel.file_path),
|
|
890
|
+
)
|
|
891
|
+
|
|
892
|
+
if not selector_index:
|
|
893
|
+
store.commit()
|
|
894
|
+
return 0
|
|
895
|
+
|
|
896
|
+
count = 0
|
|
897
|
+
|
|
898
|
+
# Link static css_classes references
|
|
899
|
+
class_nodes = store.get_nodes_by_extra_pattern('%"css_classes"%')
|
|
900
|
+
for node in class_nodes:
|
|
901
|
+
classes = node.extra.get("css_classes", [])
|
|
902
|
+
for cls_name in classes:
|
|
903
|
+
matches = selector_index.get(cls_name, [])
|
|
904
|
+
for sel_qn, sel_file in matches:
|
|
905
|
+
store.upsert_edge(EdgeInfo(
|
|
906
|
+
kind="STYLES",
|
|
907
|
+
source=node.qualified_name,
|
|
908
|
+
target=sel_qn,
|
|
909
|
+
file_path=node.file_path,
|
|
910
|
+
line=node.line_start or 0,
|
|
911
|
+
extra={
|
|
912
|
+
"class_name": cls_name,
|
|
913
|
+
"resolution": "static",
|
|
914
|
+
},
|
|
915
|
+
))
|
|
916
|
+
count += 1
|
|
917
|
+
|
|
918
|
+
# Link CSS Module references
|
|
919
|
+
module_nodes = store.get_nodes_by_extra_pattern('%"css_module_refs"%')
|
|
920
|
+
# Map of file_path → {import name: module path}, built once for all nodes
|
|
921
|
+
file_mod_imports: dict[str, dict[str, str]] = {}
|
|
922
|
+
if module_nodes:
|
|
923
|
+
for fn in store.get_nodes_by_extra_pattern(
|
|
924
|
+
'%"css_module_imports"%', kind="File",
|
|
925
|
+
):
|
|
926
|
+
file_mod_imports[fn.file_path] = fn.extra.get(
|
|
927
|
+
"css_module_imports", {},
|
|
928
|
+
)
|
|
929
|
+
for node in module_nodes:
|
|
930
|
+
refs = node.extra.get("css_module_refs", [])
|
|
931
|
+
mod_imports = file_mod_imports.get(node.file_path, {})
|
|
932
|
+
for ref in refs:
|
|
933
|
+
imp_name = ref.get("import", "")
|
|
934
|
+
prop_name = ref.get("property", "")
|
|
935
|
+
if not imp_name or not prop_name:
|
|
936
|
+
continue
|
|
937
|
+
# Find the module path
|
|
938
|
+
if imp_name not in mod_imports:
|
|
939
|
+
continue
|
|
940
|
+
# Convert camelCase to kebab-case for selector lookup
|
|
941
|
+
kebab = CodeParser._camel_to_kebab(prop_name)
|
|
942
|
+
matches = selector_index.get(kebab, [])
|
|
943
|
+
for sel_qn, sel_file in matches:
|
|
944
|
+
store.upsert_edge(EdgeInfo(
|
|
945
|
+
kind="STYLES",
|
|
946
|
+
source=node.qualified_name,
|
|
947
|
+
target=sel_qn,
|
|
948
|
+
file_path=node.file_path,
|
|
949
|
+
line=node.line_start or 0,
|
|
950
|
+
extra={
|
|
951
|
+
"class_name": kebab,
|
|
952
|
+
"resolution": "css_module",
|
|
953
|
+
"import_name": imp_name,
|
|
954
|
+
"property": prop_name,
|
|
955
|
+
},
|
|
956
|
+
))
|
|
957
|
+
count += 1
|
|
958
|
+
|
|
959
|
+
store.commit()
|
|
960
|
+
return count
|
|
961
|
+
|
|
962
|
+
|
|
963
|
+
def detect_cross_file_conflicts(
|
|
964
|
+
store: GraphStore, selectors: list | None = None,
|
|
965
|
+
) -> int:
|
|
966
|
+
"""Detect potential CSS conflicts across files.
|
|
967
|
+
|
|
968
|
+
Scans for CSS selectors across different files that target the same
|
|
969
|
+
class name. Creates POTENTIAL_CONFLICT edges when found.
|
|
970
|
+
|
|
971
|
+
Returns count of conflict edges created.
|
|
972
|
+
"""
|
|
973
|
+
from .parser import EdgeInfo # noqa: F811
|
|
974
|
+
|
|
975
|
+
store.delete_edges_by_kind("POTENTIAL_CONFLICT")
|
|
976
|
+
|
|
977
|
+
if selectors is None:
|
|
978
|
+
selectors = _get_css_selectors(store)
|
|
979
|
+
|
|
980
|
+
# Group by bare class name
|
|
981
|
+
by_class: dict[str, list[tuple[str, str, list]]] = {}
|
|
982
|
+
for sel in selectors:
|
|
983
|
+
sel_name = sel.name.strip()
|
|
984
|
+
if not sel_name.startswith("."):
|
|
985
|
+
continue
|
|
986
|
+
bare = sel_name.lstrip(".")
|
|
987
|
+
specificity = sel.extra.get("specificity", [0, 0, 0])
|
|
988
|
+
by_class.setdefault(bare, []).append(
|
|
989
|
+
(sel.qualified_name, sel.file_path, specificity),
|
|
990
|
+
)
|
|
991
|
+
|
|
992
|
+
count = 0
|
|
993
|
+
max_pairs_per_class = 50
|
|
994
|
+
|
|
995
|
+
for bare, entries in by_class.items():
|
|
996
|
+
# Only flag if selectors appear in 2+ different files
|
|
997
|
+
files_seen = {fp for _, fp, _ in entries}
|
|
998
|
+
if len(files_seen) < 2:
|
|
999
|
+
continue
|
|
1000
|
+
|
|
1001
|
+
pairs = 0
|
|
1002
|
+
for i, (src_qn, src_fp, src_spec) in enumerate(entries):
|
|
1003
|
+
for tgt_qn, tgt_fp, tgt_spec in entries[i + 1:]:
|
|
1004
|
+
if src_fp == tgt_fp:
|
|
1005
|
+
continue
|
|
1006
|
+
if pairs >= max_pairs_per_class:
|
|
1007
|
+
break
|
|
1008
|
+
# Conflict confidence label; keyed as conflict_confidence
|
|
1009
|
+
# because extra["confidence"] is reserved for the numeric
|
|
1010
|
+
# edge-confidence score read by GraphStore.upsert_edge.
|
|
1011
|
+
conflict_confidence = "high" # same bare class name
|
|
1012
|
+
store.upsert_edge(EdgeInfo(
|
|
1013
|
+
kind="POTENTIAL_CONFLICT",
|
|
1014
|
+
source=src_qn,
|
|
1015
|
+
target=tgt_qn,
|
|
1016
|
+
file_path="<cross-file>",
|
|
1017
|
+
line=0,
|
|
1018
|
+
extra={
|
|
1019
|
+
"class_name": f".{bare}",
|
|
1020
|
+
"source_specificity": src_spec,
|
|
1021
|
+
"target_specificity": tgt_spec,
|
|
1022
|
+
"conflict_confidence": conflict_confidence,
|
|
1023
|
+
},
|
|
1024
|
+
))
|
|
1025
|
+
count += 1
|
|
1026
|
+
pairs += 1
|
|
1027
|
+
if pairs >= max_pairs_per_class:
|
|
1028
|
+
break
|
|
1029
|
+
|
|
1030
|
+
store.commit()
|
|
1031
|
+
return count
|
|
1032
|
+
|
|
1033
|
+
|
|
820
1034
|
def full_build(
|
|
821
1035
|
repo_root: Path,
|
|
822
1036
|
store: GraphStore,
|
|
@@ -901,6 +1115,7 @@ def full_build(
|
|
|
901
1115
|
_store_vcs_metadata(repo_root, store)
|
|
902
1116
|
store.commit()
|
|
903
1117
|
|
|
1118
|
+
css_stats = _run_css_resolver(store)
|
|
904
1119
|
rescript_stats = _run_rescript_resolver(store)
|
|
905
1120
|
spring_stats = _run_spring_resolver(store)
|
|
906
1121
|
temporal_stats = _run_temporal_resolver(store)
|
|
@@ -917,6 +1132,8 @@ def full_build(
|
|
|
917
1132
|
"total_nodes": total_nodes,
|
|
918
1133
|
"total_edges": total_edges,
|
|
919
1134
|
"disambiguated_nodes": disambiguated,
|
|
1135
|
+
"styles_edges": (css_stats or {}).get("styles_edges"),
|
|
1136
|
+
"conflict_edges": (css_stats or {}).get("conflict_edges"),
|
|
920
1137
|
"errors": errors,
|
|
921
1138
|
"rescript_resolution": rescript_stats,
|
|
922
1139
|
"spring_resolution": spring_stats,
|
|
@@ -1040,6 +1257,11 @@ def incremental_update(
|
|
|
1040
1257
|
store.commit()
|
|
1041
1258
|
|
|
1042
1259
|
# Only re-run language-specific resolvers when the relevant files changed.
|
|
1260
|
+
css_changed = any(
|
|
1261
|
+
rp.endswith(_CSS_RELEVANT_EXTENSIONS) for rp in all_files
|
|
1262
|
+
)
|
|
1263
|
+
css_stats = _run_css_resolver(store) if css_changed else None
|
|
1264
|
+
|
|
1043
1265
|
rescript_changed = any(
|
|
1044
1266
|
rp.endswith((".res", ".resi")) for rp in all_files
|
|
1045
1267
|
)
|
|
@@ -1058,6 +1280,8 @@ def incremental_update(
|
|
|
1058
1280
|
"total_nodes": total_nodes,
|
|
1059
1281
|
"total_edges": total_edges,
|
|
1060
1282
|
"disambiguated_nodes": disambiguated,
|
|
1283
|
+
"styles_edges": (css_stats or {}).get("styles_edges"),
|
|
1284
|
+
"conflict_edges": (css_stats or {}).get("conflict_edges"),
|
|
1061
1285
|
"changed_files": list(changed_files),
|
|
1062
1286
|
"dependent_files": list(dependent_files),
|
|
1063
1287
|
"errors": errors,
|
|
@@ -229,6 +229,11 @@ def query_graph_tool(
|
|
|
229
229
|
- children_of: Find nodes contained in a file or class
|
|
230
230
|
- tests_for: Find tests for the target
|
|
231
231
|
- inheritors_of: Find classes inheriting from the target
|
|
232
|
+
- overrides_of: Find CSS selectors that the target overrides
|
|
233
|
+
- overridden_by: Find CSS selectors that override the target
|
|
234
|
+
- styles_of: Find CSS selectors that style a component or function
|
|
235
|
+
- styled_by: Find components/functions using a given CSS selector
|
|
236
|
+
- conflicts_of: Find potential cross-file CSS conflicts for a selector
|
|
232
237
|
- file_summary: Get all nodes in a file
|
|
233
238
|
|
|
234
239
|
Args:
|