Current version: 3.1.1 | Status: Active development | Updated: March 2026
Codegraph is a strong local-first code graph CLI. This roadmap describes planned improvements across ten phases -- closing gaps with commercial code intelligence platforms while preserving codegraph's core strengths: fully local, open source, zero cloud dependency by default.
LLM strategy: All LLM-powered features are optional enhancements. Everything works without an API key. When configured (OpenAI, Anthropic, Ollama, or any OpenAI-compatible endpoint), users unlock richer semantic search and natural language queries.
| Phase | Theme | Key Deliverables | Status |
|---|---|---|---|
| 1 | Rust Core | Rust parsing engine via napi-rs, parallel parsing, incremental tree-sitter, JS orchestration layer | Complete (v1.3.0) |
| 2 | Foundation Hardening | Parser registry, complete MCP, test coverage, enhanced config, multi-repo MCP | Complete (v1.4.0) |
| 2.5 | Analysis Expansion | Complexity metrics, community detection, flow tracing, co-change, manifesto, boundary rules, check, triage, audit, batch, hybrid search | Complete (v2.6.0) |
| 2.7 | Deep Analysis & Graph Enrichment | Dataflow analysis, intraprocedural CFG, AST node storage, expanded node/edge types, extractors refactoring, CLI consolidation, interactive viewer, exports command, normalizeSymbol | Complete (v3.0.0) |
| 3 | Architectural Refactoring | Unified AST analysis framework, command/query separation, repository pattern, queries.js decomposition, composable MCP, CLI commands, domain errors, curated API, unified graph model | In Progress (v3.1.1) |
| 4 | TypeScript Migration | Project setup, core type definitions, leaf -> core -> orchestration module migration, test migration | Planned |
| 5 | Intelligent Embeddings | LLM-generated descriptions, enhanced embeddings, build-time semantic metadata, module summaries | Planned |
| 6 | Natural Language Queries | ask command, conversational sessions, LLM-narrated graph queries, onboarding tools |
Planned |
| 7 | Expanded Language Support | 8 new languages (11 -> 19), parser utilities | Planned |
| 8 | GitHub Integration & CI | Reusable GitHub Action, LLM-enhanced PR review, visual impact graphs, SARIF output | Planned |
| 9 | Visualization & Advanced | Web UI, dead code detection, monorepo, agentic search, refactoring analysis | Planned |
Phase 1 (Rust Core)
|--> Phase 2 (Foundation Hardening)
|--> Phase 2.5 (Analysis Expansion)
|--> Phase 2.7 (Deep Analysis & Graph Enrichment)
|--> Phase 3 (Architectural Refactoring)
|--> Phase 4 (TypeScript Migration)
|--> Phase 5 (Embeddings + Metadata) --> Phase 6 (NL Queries + Narration)
|--> Phase 7 (Languages)
|--> Phase 8 (GitHub/CI) <-- Phase 5 (risk_score, side_effects)
Phases 1-6 --> Phase 9 (Visualization + Refactoring Analysis)
Status: Complete -- shipped in v1.3.0
Goal: Move the CPU-intensive parsing and graph engine to Rust, keeping JS for CLI orchestration, MCP, and embeddings. This unlocks parallel parsing, incremental tree-sitter, lower memory usage, and optional standalone binary distribution.
Bootstrap the Rust side of the project.
- Create
crates/codegraph-core/with a Cargo workspace - Set up napi-rs to compile Rust ->
.nodenative addon - Configure CI matrix for prebuilt binaries:
linux-x64,darwin-arm64,darwin-x64,win32-x64 - Add npm optionalDependencies for platform-specific packages (same pattern as SWC/esbuild)
- Fallback to existing JS/WASM path if native addon is unavailable
Result: npm install pulls a prebuilt binary; no Rust toolchain required for end users.
Replace WASM-based parsing with native tree-sitter in Rust.
- Link tree-sitter grammars natively (no more
.wasmfiles) - Implement file parsing with rayon for multi-core parallelism
- Expose
parseFiles(filePaths)to JS via napi-rs, returning extracted symbols/imports/calls - Benchmark: target 10-50x improvement over WASM on large codebases
Result: Parsing thousands of files uses all CPU cores. The grammars/ directory and build:wasm step are no longer needed.
Affected files: src/parser.js (becomes a thin JS wrapper over native addon)
Leverage native tree-sitter's edit + re-parse API.
- Track previous parse trees in memory for open/watched files
- On file change, apply edits to the existing tree and re-parse only the changed regions
- Integrate with
codegraph watchfor near-instant incremental rebuilds
Result: Watch mode re-parses only changed lines instead of entire files.
Affected files: src/watcher.js, src/parser.js
Move the hot-path graph logic to Rust.
- Port the 6-level import resolution priority system with confidence scoring
- Port cycle detection (currently
src/cycles.js) to Rust - Keep SQLite operations in JS (better-sqlite3 is already fast and synchronous)
- Expose
resolveImports()anddetectCycles()to JS via napi-rs
Result: Import resolution and cycle detection run in Rust with full type safety. Complex state machines benefit from Rust's type system.
Ensure the transition is seamless.
- Keep the existing JS/WASM parser as a fallback when the native addon is unavailable
- Auto-detect at startup: native addon available -> use Rust path; otherwise -> WASM path
- No breaking changes to CLI, MCP, or programmatic API
- Add
--engine native|wasmflag for explicit selection - Migrate existing tests to validate both engines produce identical output
Result: Zero breaking changes. Users get faster parsing automatically; nothing else changes.
Status: Complete -- shipped in v1.4.0
Goal: Fix structural issues that make subsequent phases harder.
Replace scattered parser init/selection logic with a single declarative registry.
- ✅ Create a
LANGUAGE_REGISTRYarray mapping each language to{ id, extensions, grammarFile, extractor, required } - ✅ Refactor
createParsers()to iterate the registry instead of individual try/catch blocks (returnsMap<string, Parser|null>) - ✅ Refactor
getParser()to use registry extension lookup via_extToLangMap - ✅ Refactor
wasmExtractSymbols()to dispatch extractors viaentry.extractor - ✅ Auto-generate
EXTENSIONSfrom registry (re-exported fromparser.jsviaSUPPORTED_EXTENSIONS)
Result: Adding a new language becomes a single registry entry + extractor function.
Affected files: src/parser.js, src/constants.js
Expose all CLI capabilities through MCP, going from 5 -> 11 tools.
| New tool | Wraps | Description |
|---|---|---|
✅ fn_deps |
fnDepsData |
Function-level dependency chain |
✅ fn_impact |
fnImpactData |
Function-level blast radius |
✅ diff_impact |
diffImpactData |
Git diff impact analysis |
✅ semantic_search |
searchData |
Embedding-powered search |
✅ export_graph |
export functions | DOT/Mermaid/JSON export |
✅ list_functions |
-- | List functions in a file or by pattern |
Affected files: src/mcp.js
Add tests for currently untested modules.
| New test file | Coverage |
|---|---|
✅ tests/unit/mcp.test.js |
All MCP tools (mock stdio transport) |
✅ tests/unit/config.test.js |
Config loading, defaults, env overrides, apiKeyCommand |
✅ tests/integration/cli.test.js |
End-to-end CLI smoke tests |
✅ tests/unit/*.test.js |
Unit tests for 8 core modules (coverage 62% -> 75%) |
New configuration options in .codegraphrc.json:
{
"embeddings": { "model": "minilm", "llmProvider": null },
"llm": {
"provider": "openai",
"model": "gpt-4o-mini",
"baseUrl": null,
"apiKey": null,
"apiKeyCommand": "op read op://vault/openai/api-key"
},
"search": { "defaultMinScore": 0.2, "rrfK": 60, "topK": 15 },
"ci": { "failOnCycles": false, "impactThreshold": null }
}- ✅ Environment variable fallbacks:
CODEGRAPH_LLM_PROVIDER,CODEGRAPH_LLM_API_KEY,CODEGRAPH_LLM_MODEL - ✅
apiKeyCommand-- shell out to external secret managers (1Password, Bitwarden, Vault, pass, macOS Keychain) at runtime viaexecFileSync(no shell injection). Priority: command output > env var > file config > defaults. Graceful fallback on failure.
Affected files: src/config.js
Support querying multiple codebases from a single MCP server instance.
- ✅ Registry file at
~/.codegraph/registry.jsonmapping repo names to their.codegraph/graph.dbpaths - ✅ Add optional
repoparameter to all 11 MCP tools to target a specific repository - ✅ New
list_reposMCP tool (12th tool) to enumerate registered repositories - ✅ Auto-registration:
codegraph buildadds the current project to the registry - ✅ New CLI commands:
codegraph registry list|add|removefor manual management - ✅ Default behavior: when
repois omitted, use the local.codegraph/graph.db(backwards compatible)
New files: src/registry.js
Affected files: src/mcp.js, src/cli.js, src/builder.js, src/index.js
Status: Complete -- shipped across v2.0.0 -> v2.6.0
Goal: Build a comprehensive analysis toolkit on top of the graph -- complexity metrics, community detection, risk triage, architecture boundary enforcement, CI validation, and hybrid search. This phase emerged organically as features were needed and wasn't in the original roadmap.
Per-function complexity analysis using language-specific AST rules.
- ✅ Cognitive complexity, cyclomatic complexity, max nesting depth for 8 languages
- ✅ Halstead metrics (vocabulary, volume, difficulty, effort, bugs)
- ✅ LOC, SLOC, comment lines per function
- ✅ Maintainability Index (MI) computation
- ✅ Native Rust engine support for all complexity metrics
- ✅ CLI:
codegraph complexity [target]with--sort,--limit,--kindoptions - ✅
function_complexityDB table for persistent storage
New file: src/complexity.js (2,163 lines)
Louvain community detection at file or function level.
- ✅ Graphology-based Louvain algorithm for community assignment
- ✅ Modularity score computation
- ✅ Drift analysis: identify split/merge candidates between communities
- ✅ CLI:
codegraph communitieswith--level file|function
New file: src/communities.js (310 lines)
Directory structure graph with node role classification.
- ✅ Directory nodes and edges with cohesion, density, fan-in/fan-out metrics
- ✅ Node role classification: entry, core, utility, adapter, leaf, dead
- ✅ Framework entry point detection (route:, event:, command: prefixes)
- ✅ Hotspot detection: high fan-in x high complexity
- ✅ Module boundary analysis: high-cohesion directories with cross-boundary imports
- ✅ CLI:
codegraph structure,codegraph hotspots,codegraph roles
New file: src/structure.js (668 lines)
Forward BFS from framework entry points through callees to leaves.
- ✅ Entry point enumeration with type classification
- ✅ Forward BFS trace with cycle detection
- ✅ CLI:
codegraph flow [name]with--listand--depthoptions
New file: src/flow.js (362 lines)
Git history analysis for temporal file coupling.
- ✅ Jaccard similarity computation from commit history
- ✅
co_changes,co_change_meta,file_commit_countsDB tables - ✅ Per-file and global co-change queries
- ✅ CLI:
codegraph co-change [file]
New file: src/cochange.js (502 lines)
Configurable rule engine with warn/fail thresholds for function, file, and graph rules.
- ✅ Function rules: cognitive, cyclomatic, nesting depth
- ✅ File rules: imports, exports, LOC, fan-in, fan-out
- ✅ Graph rules: cycles, boundary violations
- ✅ Configurable via
.codegraphrc.jsonmanifestosection - ✅ CLI:
codegraph manifestowith table format
New file: src/manifesto.js (511 lines)
Architecture enforcement using glob patterns and presets.
- ✅ Presets: hexagonal, layered, clean, onion
- ✅ Custom boundary definitions with allow/deny rules
- ✅ Violation detection from DB edges
- ✅ Integration with manifesto and check commands
New file: src/boundaries.js (347 lines)
Structured pass/fail checks for CI pipelines.
- ✅
checkNoNewCycles-- cycle predicate - ✅
checkMaxBlastRadius-- blast radius predicate - ✅
checkNoSignatureChanges-- signature stability predicate - ✅
checkNoBoundaryViolations-- architecture predicate - ✅ Composable result objects with pass/fail semantics
- ✅ MCP tool:
check - ✅ CLI:
codegraph check [ref]with exit code 0/1
New file: src/check.js (433 lines)
High-level commands that compose multiple analysis steps.
- ✅ Audit: explain + impact + health + manifesto breaches in one call
- ✅ Batch: run same query against multiple targets for multi-agent dispatch
- ✅ Triage: risk-ranked audit queue using normalized fan-in, complexity, churn, MI signals
New files: src/audit.js (424 lines), src/batch.js (91 lines), src/triage.js (274 lines)
BM25 keyword search + semantic vector search with RRF fusion.
- ✅ FTS5 full-text index on node names and source previews
- ✅ BM25 keyword search via
ftsSearchData() - ✅ Hybrid search with configurable RRF fusion via
hybridSearchData() - ✅ Three search modes:
hybrid(default),semantic,keyword - ✅ 8 embedding model options (minilm, jina-small/base/code, nomic/v1.5, bge-large)
Affected file: src/embedder.js (grew from 525 -> 1,113 lines)
Cross-cutting utilities added during the expansion.
- ✅ Pagination: offset/limit with MCP defaults per command (
src/paginate.js, 106 lines) - ✅ Snapshot: SQLite DB backup/restore via VACUUM INTO (
src/snapshot.js, 150 lines) - ✅ CODEOWNERS: ownership integration for boundary analysis (
src/owners.js, 360 lines) - ✅ Branch Compare: structural diff between git refs (
src/branch-compare.js, 569 lines) - ✅ Change Journal: NDJSON event log for watch mode (
src/change-journal.js, 131 lines) - ✅ Journal: change journal validation/management (
src/journal.js, 110 lines) - ✅ Update Check: npm registry polling with 24h cache (
src/update-check.js, 161 lines)
MCP grew from 12 -> 25 tools, covering all new analysis capabilities.
| New tool | Wraps |
|---|---|
✅ structure |
structureData |
✅ node_roles |
rolesData |
✅ hotspots |
hotspotsData |
✅ co_changes |
coChangeData |
✅ execution_flow |
flowData |
✅ list_entry_points |
listEntryPointsData |
✅ complexity |
complexityData |
✅ manifesto |
manifestoData |
✅ communities |
communitiesData |
✅ code_owners |
ownersData |
✅ audit |
auditData |
✅ batch_query |
batchData |
✅ triage |
triageData |
✅ branch_compare |
branchCompareData |
✅ check |
checkData |
Affected file: src/mcp.js (grew from 354 -> 1,212 lines)
Status: Complete -- shipped as v3.0.0 across PRs #254-#285
Goal: Add deeper static analysis capabilities (dataflow, control flow graphs, AST querying), enrich the graph model with sub-declaration node types and structural edges, refactor extractors into per-language modules, consolidate the CLI surface area, and introduce interactive visualization. This phase emerged from competitive analysis against Joern and Narsil-MCP.
Define-use chain extraction tracking how data flows between functions.
- ✅ Three edge types:
flows_to(parameter flow),returns(call return assignment),mutates(parameter-derived mutations) - ✅ Confidence scoring (1.0 param, 0.9 call return, 0.8 destructured)
- ✅ Scope-stack AST walk with function-level tracking
- ✅ Forward BFS impact analysis via return value consumers
- ✅ Path finding through dataflow edges
- ✅ Opt-in via
build --dataflow(dynamic import, only loaded when flag passed) - ✅ DB migration v10:
dataflowtable with source, target, kind, param_index, expression, confidence - ✅ JS/TS/TSX only (MVP -- language-specific scope analysis)
- ✅ CLI:
codegraph dataflow <name>with--impactmode for transitive data-dependent blast radius - ✅ MCP tool:
dataflowwithedgesandimpactmodes (path mode removed during CLI consolidation PR #263)
New file: src/dataflow.js (1,187 lines)
Extend the graph model with sub-declaration node kinds.
- ✅ 3 new node kinds:
parameter,property,constant - ✅ Tiered constants:
CORE_SYMBOL_KINDS(10),EXTENDED_SYMBOL_KINDS(3),EVERY_SYMBOL_KIND(13) - ✅ Backward-compatible alias:
ALL_SYMBOL_KINDS = CORE_SYMBOL_KINDS - ✅
parent_idcolumn onnodestable (DB migration v11) linking children to parent definitions - ✅ All 9 WASM extractors updated to emit
childrenarrays - ✅ CLI:
codegraph children <name> - ✅ MCP tool:
symbol_childrenwith extended kind enum
Affected files: All extractors, src/builder.js, src/queries.js, src/db.js
Structural edges for richer graph relationships.
- ✅ 3 new edge kinds:
contains(parent→child),parameter_of(param→function),receiver(method call receiver) - ✅ Tiered constants:
CORE_EDGE_KINDS(6),STRUCTURAL_EDGE_KINDS(3),EVERY_EDGE_KIND(9) - ✅ Structural edges excluded from
moduleMapData()coupling counts - ✅ MCP tool enums updated to include new edge kinds
Affected files: src/builder.js, src/queries.js
Basic-block control flow graph construction from function ASTs.
- ✅
makeCfgRules(overrides)factory with per-language defaults and validation - ✅
CFG_RULESMap covering all 9 languages (JS/TS, Python, Go, Rust, Java, C#, PHP, Ruby) - ✅ Handles: if/else, for/while/do-while, switch, try/catch/finally, break/continue (with labels), return/throw
- ✅ Opt-in via
build --cfg(dynamic import) - ✅ DB migration v12:
cfg_blocksandcfg_edgestables - ✅ DOT and Mermaid export:
cfgToDOT(),cfgToMermaid() - ✅ CLI:
codegraph cfg <name>with--format text|dot|mermaid - ✅ MCP tool:
cfg
New file: src/cfg.js (1,451 lines)
Persist and query selected AST node types for pattern-based codebase exploration.
- ✅ 6 AST node kinds:
call,new,string,regex,throw,await - ✅
AST_NODE_KINDSconstant - ✅ Pattern matching via SQL GLOB with auto-wrapping for substring search
- ✅ Parent resolution via narrowest enclosing definition
- ✅ Always-on extraction during build (post-parse, before complexity to preserve
_tree) - ✅ DB migration v13:
ast_nodestable with indexes on kind, name, file, parent - ✅ CLI:
codegraph ast [pattern]with-k,-f,-T,-j - ✅ MCP tool:
ast_query
New file: src/ast.js (392 lines)
Split per-language extractors from monolithic parser.js into dedicated modules.
- ✅ New
src/extractors/directory with 11 files (3,023 lines total) - ✅ One file per language:
javascript.js(892),csharp.js(311),php.js(322),java.js(290),rust.js(295),ruby.js(277),go.js(237),python.js(284),hcl.js(95) - ✅ Shared utilities in
helpers.js(nodeEndLine(),findChild()) - ✅ Barrel export via
index.js - ✅ Consistent return schema:
{ definitions, calls, imports, classes, exports } - ✅ All extractors support extended node kinds (parameter, property, constant)
- ✅
parser.jsreduced to thin WASM fallback withLANGUAGE_REGISTRY(404 lines)
New directory: src/extractors/
Stable JSON schema for symbol output across all query functions.
- ✅
normalizeSymbol(row, db, hashCache)returns 7-field shape:{ name, kind, file, line, endLine, role, fileHash } - ✅ File hash caching for efficient batch operations
- ✅ Adopted by dataflow, context, where, query, and other functions
Affected file: src/queries.js
Self-contained HTML visualization with vis-network.
- ✅ File-level and function-level graph modes
- ✅ Layout switching (hierarchical, force, radial), physics toggle, search
- ✅ Color by kind/role/community/complexity (configurable)
- ✅ Size by uniform/fan-in/complexity
- ✅ Clustering by community or directory
- ✅ Drill-down with seed strategies (all, top-fanin, entry)
- ✅ Detail panel with metrics, callers, callees on node click
- ✅ Risk overlays (dead-code, high-blast-radius, low-MI)
- ✅ Configuration via
.plotDotCfg/.plotDotCfg.jsonwith deep merge defaults - ✅ CLI:
codegraph plotwith--functions,--config,--color-by,--size-by,--cluster-by,--overlay
New file: src/viewer.js (948 lines)
Per-symbol consumer analysis for file exports.
- ✅
exportsData(file)returns each exported symbol with its consumers (who calls it and from where) - ✅ CLI:
codegraph exports <file> - ✅ MCP tool:
file_exports - ✅ Integrated into batch command system
Affected file: src/queries.js
Three new graph export formats for external tooling integration.
- ✅ GraphML (XML format for graph tools like yEd, Gephi)
- ✅ GraphSON (Gremlin/TinkerPop server format)
- ✅ Neo4j CSV (bulk loader format for Neo4j import)
Affected file: src/export.js (681 lines)
First CLI surface area reduction -- 5 commands merged into existing ones.
- ✅
hotspots→ folded intotriage --level file|directory - ✅
manifesto→ merged intocheck(no args = manifesto,--rulesfor both) - ✅
explain→ replaced byaudit --quick - ✅
batch-query→ usebatch whereinstead - ✅
query --path→ standalonepath <from> <to>command (deprecation notice on old syntax) - ✅ MCP tools unchanged for backward compatibility
Affected file: src/cli.js
MCP tools were both consolidated and expanded, resulting in a net change from 25 → 30 tools (31 in multi-repo mode).
Added:
| New tool | Wraps |
|---|---|
✅ cfg |
cfgData |
✅ ast_query |
astQueryData |
✅ dataflow |
dataflowData (edges + impact modes) |
✅ file_exports |
exportsData |
✅ symbol_children |
childrenData |
Removed (PR #263 consolidation):
| Removed tool | Replacement |
|---|---|
fn_deps |
query with deps mode |
symbol_path |
query with path mode |
list_entry_points |
execution_flow with list mode |
Plus updated enums on existing tools (edge_kinds, symbol kinds).
Affected file: src/mcp.js (grew from 1,212 -> 1,370 lines)
| Metric | Before (v2.6.0) | After (v3.0.0) | Delta |
|---|---|---|---|
| Source modules | 35 | 50 | +15 |
| Total source lines | 17,830 | 26,277 | +47% |
| DB tables | 9 | 13 | +4 |
| DB migrations | v9 | v13 | +4 |
| MCP tools | 25 | 30 | +5 (net: +8 added, -3 consolidated) |
| CLI commands | 45 | 39 | -6 (net: +7 added, -5 consolidated, -8 merged) |
| Node kinds | 10 | 13 | +3 |
| Edge kinds | 6 | 9 | +3 |
| Test files | 59 | 70 | +11 |
Status: In Progress -- started in v3.1.1
Goal: Restructure the codebase for modularity, testability, and long-term maintainability. These are internal improvements -- no new user-facing features, but they make every subsequent phase easier to build and maintain.
Reference: generated/architecture.md -- full analysis with code examples and rationale.
Context: Phases 2.5 and 2.7 added 38 modules and grew the codebase from 5K to 26,277 lines without introducing shared abstractions. The dual-function anti-pattern was replicated across 19 modules. Three independent AST analysis engines (complexity, CFG, dataflow) totaling 4,801 lines share the same fundamental pattern but no infrastructure. Raw SQL is scattered across 25+ modules touching 13 tables. The priority ordering has been revised based on actual growth patterns -- the new #1 priority is the unified AST analysis framework.
Unify the independent AST analysis engines (complexity, CFG, dataflow) plus AST node storage into a shared visitor framework. These four modules independently implement the same pattern: per-language rules map → AST walk → collect data → write to DB → query → format.
Completed: All 4 analyses (complexity, CFG, dataflow, AST-store) now run in a single DFS walk via walkWithVisitors. The CFG visitor rewrite (#392) eliminated the Mode A/B split, replaced the 813-line buildFunctionCFG with a node-level visitor, and derives cyclomatic complexity directly from CFG structure (E - N + 2). cfg.js reduced from 1,242 → 518 lines.
src/
ast-analysis/
visitor.js # Shared DFS walker with pluggable visitor hooks
engine.js # Orchestrates all analyses in one coordinated pass
metrics.js # Halstead, MI, LOC/SLOC (extracted from complexity.js)
visitor-utils.js # Shared helpers (functionName, extractParams, etc.)
visitors/
complexity-visitor.js # Cognitive/cyclomatic/nesting + Halstead
cfg-visitor.js # Basic-block + edge construction via DFS hooks
ast-store-visitor.js # new/throw/await/string/regex extraction
dataflow-visitor.js # Scope stack + define-use chains
shared.js # findFunctionNode, rule factories, ext mapping
rules/ # Per-language rule files (unchanged)
- ✅ Shared DFS walker with
enterNode/exitNode/enterFunction/exitFunctionhooks,skipChildrenper-visitor, nesting/scope tracking - ✅ Complexity visitor (cognitive, cyclomatic, max nesting, Halstead) — file-level and function-level modes
- ✅ AST-store visitor (new/throw/await/string/regex extraction)
- ✅ Dataflow visitor (define-use chains, arg flows, mutations, scope stack)
- ✅ Engine orchestrator: unified pre-walk stores results as pre-computed data on
symbols, then delegates to existingbuildXxxfor DB writes - ✅
builder.js→ singlerunAnalysescall replaces 4 sequential blocks + WASM pre-parse - ✅ Extracted pure computations to
metrics.js(Halstead derived math, LOC, MI) - ✅ Extracted shared helpers to
visitor-utils.js(from dataflow.js) - ✅ CFG visitor rewrite — node-level DFS visitor replaces statement-level
buildFunctionCFG, Mode A/B split eliminated (#392) - ✅ Cyclomatic complexity derived from CFG (
E - N + 2) — single source of truth for control flow metrics (#392)
Affected files: src/complexity.js, src/cfg.js, src/dataflow.js, src/ast.js → split into src/ast-analysis/
CLI display wrappers extracted from all 19 analysis modules into dedicated src/commands/ files. Shared infrastructure (result-formatter.js, test-filter.js) moved to src/infrastructure/. *Data() functions remain in original modules — MCP dynamic imports unchanged. ~1,059 lines of CLI formatting code separated from analysis logic (#373, #393).
src/
commands/ # One file per command (16 files)
audit.js, batch.js, cfg.js, check.js, cochange.js, communities.js,
complexity.js, dataflow.js, flow.js, branch-compare.js, manifesto.js,
owners.js, sequence.js, structure.js, triage.js, query.js (barrel re-export)
infrastructure/
result-formatter.js # Shared formatting: JSON, NDJSON dispatch
test-filter.js # Shared --no-tests / isTestFile logic
- ✅
queries.jsCLI wrappers →queries-cli.js(15 functions) - ✅ Shared
result-formatter.js(outputResultfor JSON/NDJSON dispatch) - ✅ Shared
test-filter.js(isTestFilepredicate) - ✅ CLI wrappers extracted from remaining 15 modules into
src/commands/(#393) - ✅ Per-command
src/commands/directory structure (#393) - ✅
src/infrastructure/directory for shared utilities (#393) - ⏭️
CommandRunnershared lifecycle — deferred (command files vary too much for a single pattern today)
Affected files: All 19 modules with dual-function pattern, src/cli.js, src/mcp.js
v3.1.1 progress:
src/db/directory created withrepository.js(134 lines),query-builder.js(280 lines), andmigrations.js(312 lines). All db usage across the codebase wrapped in try/finally for reliabledb.close()(#371, #384, #383).v3.1.2 progress:
repository.jssplit intosrc/db/repository/directory with 10 domain files (nodes, edges, build-stmts, complexity, cfg, dataflow, cochange, embeddings, graph-read, barrel). Raw SQL migrated from 14 src/ modules into repository layer.connection.jsalready complete (89 lines handling open/close/WAL/pragma/locks/readonly).
- ✅
src/db/directory structure created - ✅
repository/— domain-split repository (nodes, edges, build-stmts, complexity, cfg, dataflow, cochange, embeddings, graph-read) - ✅
query-builder.js— lightweight SQL builder (280 lines) - ✅
migrations.js— schema migrations extracted (312 lines) - ✅
connection.js— connection setup (open, WAL mode, pragma tuning, readonly, locks) - ✅ All db usage wrapped in try/finally for reliable
db.close() - ✅ Migrate remaining raw SQL from 14 modules into Repository
src/
db/
connection.js # Open, WAL mode, pragma tuning
migrations.js # Schema versions (currently 13 migrations)
query-builder.js # Lightweight SQL builder for common filtered queries
repository/
index.js # Barrel re-export
nodes.js # Node lookups: getNodeId, findFileNodes, bulkNodeIdsByFile, etc.
edges.js # Edge queries: findCallees, findCallers, import/hierarchy edges
build-stmts.js # Cascade purge: purgeFileData, purgeFilesData
complexity.js # function_complexity table reads
cfg.js # cfg_blocks/cfg_edges reads + deletes
dataflow.js # dataflow table checks
cochange.js # co_changes/co_change_meta reads
embeddings.js # embeddings/embedding_meta reads
graph-read.js # Cross-table reads for export/communities
Affected files: src/db.js barrel updated, raw SQL extracted from queries.js, builder.js, watcher.js, structure.js, complexity.js, cfg.js, dataflow.js, ast.js, ast-analysis/engine.js, embedder.js, sequence.js, communities.js
v3.1.1 progress:
queries.jsreduced from 3,395 → 2,490 lines by extracting all CLI formatting toqueries-cli.js(3.2). Symbol kind constants extracted tokinds.js(49 lines) (#378).
- ✅ CLI formatting separated →
queries-cli.js(via 3.2) - ✅
kinds.js— symbol kind constants extracted - 🔲 Split remaining
queries.jsdata functions intosrc/analysis/modules - 🔲 Extract
shared/normalize.js,shared/generators.js
Split into pure analysis modules that return data and share no formatting concerns.
src/
analysis/
symbol-lookup.js # queryNameData, whereData, listFunctionsData, childrenData
impact.js # impactAnalysisData, fnImpactData, diffImpactData
dependencies.js # fileDepsData, fnDepsData, pathData
module-map.js # moduleMapData, statsData
context.js # contextData, explainData
exports.js # exportsData
roles.js # rolesData
shared/
constants.js # CORE_SYMBOL_KINDS, EXTENDED_SYMBOL_KINDS, EVERY_SYMBOL_KIND,
# CORE_EDGE_KINDS, STRUCTURAL_EDGE_KINDS, EVERY_EDGE_KIND,
# VALID_ROLES, FALSE_POSITIVE_NAMES, AST_NODE_KINDS
normalize.js # normalizeSymbol, isTestFile, kindIcon
generators.js # iterListFunctions, iterRoles, iterWhere
Note: Phase 2.7 introduced tiered constants (
CORE_/EXTENDED_/EVERY_) andnormalizeSymbol()— the right abstractions, just in the wrong file. Moving them toshared/is the first step.
Affected files: src/queries.js -> split into src/analysis/ + src/shared/
Replace the monolithic 1,370-line mcp.js (30 tools in one switch dispatch) with self-contained tool modules.
src/
mcp/
server.js # MCP server setup, transport, lifecycle
tool-registry.js # Auto-discovery + dynamic registration
middleware.js # Pagination, error handling, repo resolution
tools/
query-function.js # { schema, handler } -- one per tool (30 files)
...
Adding a new MCP tool = adding a file. No other files change.
Affected files: src/mcp.js -> split into src/mcp/
Move from 1,557 lines of inline Commander chains to self-contained command modules.
Note: Phase 2.7.11 consolidated 5 commands — the first CLI surface area reduction. This item continues that direction by making each of the 39 remaining commands independently testable.
src/
cli/
index.js # Commander setup, auto-discover commands
shared/
output.js # --json, --ndjson, table, plain text
options.js # Shared options (--no-tests, --json, --db, etc.)
commands/ # 39 files, one per command
build.js # { name, description, options, validate, execute }
...
Each command is independently testable by calling execute() directly.
Affected files: src/cli.js -> split into src/cli/
Reduce index.js from 140+ exports to ~35 curated exports. Use package.json exports field to enforce module boundaries.
{ "exports": { ".": "./src/index.js", "./cli": "./src/cli.js" } }Export only *Data() functions (the command execute functions). Never export CLI formatters. Group by domain.
Affected files: src/index.js, package.json
Removed: Decompose complexity.js — Subsumed by 3.1. The standalone complexity decomposition from the previous revision is now part of the unified AST analysis framework (3.1). The
complexity.jsper-language rules becomeast-analysis/rules/complexity/{lang}.jsalongside CFG and dataflow rules.
Replace ad-hoc error handling (mix of thrown Error, returned null, logger.warn(), process.exit(1)) across 50 modules with structured domain errors.
class CodegraphError extends Error { constructor(message, { code, file, cause }) { ... } }
class ParseError extends CodegraphError { code = 'PARSE_FAILED' }
class DbError extends CodegraphError { code = 'DB_ERROR' }
class ConfigError extends CodegraphError { code = 'CONFIG_INVALID' }
class ResolutionError extends CodegraphError { code = 'RESOLUTION_FAILED' }
class EngineError extends CodegraphError { code = 'ENGINE_UNAVAILABLE' }
class AnalysisError extends CodegraphError { code = 'ANALYSIS_FAILED' }
class BoundaryError extends CodegraphError { code = 'BOUNDARY_VIOLATION' }The CLI catches domain errors and formats for humans. MCP returns structured error responses. No more process.exit() from library code.
New file: src/errors.js
Refactor buildGraph() (1,355 lines) from a mega-function into explicit, independently testable pipeline stages. Phase 2.7 added 4 opt-in stages, bringing the total to 11 core + 4 optional.
const pipeline = [
// Core (always)
collectFiles, // (rootDir, config) => filePaths[]
detectChanges, // (filePaths, db) => { changed, removed, isFullBuild }
parseFiles, // (filePaths, engineOpts) => Map<file, symbols>
insertNodes, // (symbolMap, db) => nodeIndex
resolveImports, // (symbolMap, rootDir, aliases) => importEdges[]
buildCallEdges, // (symbolMap, nodeIndex) => callEdges[]
buildClassEdges, // (symbolMap, nodeIndex) => classEdges[]
resolveBarrels, // (edges, symbolMap) => resolvedEdges[]
insertEdges, // (allEdges, db) => stats
extractASTNodes, // (fileSymbols, db) => astStats (always, post-parse)
buildStructure, // (db, fileSymbols, rootDir) => structureStats
classifyRoles, // (db) => roleStats
emitChangeJournal, // (rootDir, changes) => void
// Opt-in (dynamic imports)
computeComplexity, // --complexity: (db, rootDir, engine) => complexityStats
buildDataflowEdges, // --dataflow: (db, fileSymbols, rootDir) => dataflowStats
buildCFGData, // --cfg: (db, fileSymbols, rootDir) => cfgStats
]Watch mode reuses the same stages triggered per-file, eliminating the watcher.js divergence.
Affected files: src/builder.js, src/watcher.js
Restructure embedder.js (1,113 lines) -- which now contains 3 search engines -- into a standalone subsystem.
src/
embeddings/
index.js # Public API
models.js # 8 model definitions, batch sizes, loading
generator.js # Source -> text preparation -> batch embedding
stores/
sqlite-blob.js # Current O(n) cosine similarity
fts5.js # BM25 keyword search
search/
semantic.js # Vector similarity
keyword.js # FTS5 BM25
hybrid.js # RRF fusion
strategies/
structured.js # Structured text preparation
source.js # Raw source preparation
The pluggable store interface enables future O(log n) ANN search (e.g., hnswlib-node) when symbol counts reach 50K+.
Affected files: src/embedder.js -> split into src/embeddings/
Unify the four parallel graph representations (structure.js, cochange.js, communities.js, viewer.js) into a shared in-memory graph model.
src/
graph/
model.js # Shared in-memory graph (nodes + edges + metadata)
builders/
dependency.js # Build from SQLite edges
structure.js # Build from file/directory hierarchy
temporal.js # Build from git history (co-changes)
algorithms/
bfs.js # Breadth-first traversal
shortest-path.js # Path finding
tarjan.js # Cycle detection
louvain.js # Community detection
centrality.js # Fan-in/fan-out, betweenness
clustering.js # Cohesion, coupling, density
classifiers/
roles.js # Node role classification
risk.js # Risk scoring
Algorithms become composable -- run community detection on the dependency graph, the temporal graph, or a merged graph.
Affected files: src/structure.js, src/cochange.js, src/communities.js, src/cycles.js, src/triage.js, src/viewer.js
Phase 2.7 progress:
parent_idcolumn,containsedges,parameter_ofedges, andchildrenData()query now model one-level parent-child relationships. This addresses ~80% of the use case.
Remaining work -- enrich the node model with deeper scope information:
ALTER TABLE nodes ADD COLUMN qualified_name TEXT; -- 'DateHelper.format'
ALTER TABLE nodes ADD COLUMN scope TEXT; -- 'DateHelper'
ALTER TABLE nodes ADD COLUMN visibility TEXT; -- 'public' | 'private' | 'protected'Enables queries like "all methods of class X" without traversing edges. The parent_id FK only goes one level -- deeply nested scopes (namespace > class > method > closure) aren't fully represented. qualified_name would allow direct lookup.
Affected files: src/db.js, src/extractors/, src/queries.js, src/builder.js
The repository pattern (3.3) enables true unit testing:
- Pure unit tests for graph algorithms (pass adjacency list, assert result)
- Pure unit tests for risk/confidence scoring (pass parameters, assert score)
InMemoryRepositoryfor query tests (no SQLite, instant setup)- Existing 70 test files continue as integration tests
Current gap: Many "unit" tests still hit SQLite because there's no repository abstraction.
These items from the original Phase 3 are still valid but less urgent:
- Event-driven pipeline: Add event/streaming architecture for progress reporting, cancellation, and large-repo support.
- Unified engine interface (Strategy): Replace scattered
engine.name === 'native'branching. Less critical now that native is the primary path. - Subgraph export filtering:
codegraph export --focus src/builder.js --depth 2for usable visualizations. - Transitive import-aware confidence: Walk import graph before falling back to proximity heuristics.
- Query result caching: LRU/TTL cache between analysis layer and repository. More valuable now with 34 MCP tools.
- Configuration profiles:
--profile backendfor monorepos with multiple services. - Pagination standardization: SQL-level LIMIT/OFFSET in repository + command runner shaping.
Goal: Migrate the codebase from plain JavaScript to TypeScript, leveraging the clean module boundaries established in Phase 3. Incremental module-by-module migration starting from leaf modules inward.
Why after Phase 3: The architectural refactoring creates small, well-bounded modules with explicit interfaces (Repository, Engine, BaseExtractor, Pipeline stages, Command objects). These are natural type boundaries -- typing monolithic 2,000-line files that are about to be split would be double work.
- Add
typescriptas a devDependency - Create
tsconfig.jsonwith strict mode, ES module output, path aliases matching the Phase 3 module structure - Update Biome config to lint
.tsfiles - Configure build step:
tscemits todist/,package.jsonexportspoint to compiled output - Add
tsc --noEmitto CI as a type-checking gate - Enable incremental compilation for fast rebuilds
Affected files: package.json, biome.json, new tsconfig.json
Define TypeScript interfaces for all abstractions introduced in Phase 3:
// Types for the core domain model
interface SymbolNode { id: number; name: string; qualifiedName?: string; kind: SymbolKind; file: string; line: number; endLine: number; parentId?: number; }
interface Edge { source: number; target: number; kind: EdgeKind; confidence: number; }
type CoreSymbolKind = 'function' | 'method' | 'class' | 'interface' | 'type' | 'struct' | 'enum' | 'trait' | 'record' | 'module'
type ExtendedSymbolKind = 'parameter' | 'property' | 'constant'
type SymbolKind = CoreSymbolKind | ExtendedSymbolKind
type CoreEdgeKind = 'imports' | 'imports-type' | 'reexports' | 'calls' | 'extends' | 'implements'
type StructuralEdgeKind = 'contains' | 'parameter_of' | 'receiver'
type EdgeKind = CoreEdgeKind | StructuralEdgeKind
type ASTNodeKind = 'call' | 'new' | 'string' | 'regex' | 'throw' | 'await'
// Interfaces for Phase 3 abstractions
interface Repository { insertNode(node: SymbolNode): void; findNodesByName(name: string, opts?: QueryOpts): SymbolNode[]; }
interface Engine { parseFile(path: string, source: string): ParseResult; resolveImports(batch: ImportBatch): ResolvedImport[]; }
interface ASTVisitor { name: string; visit(node: TreeSitterNode, context: VisitorContext): void; } // Phase 3.1
interface Extractor { language: string; handlers: Record<string, NodeHandler>; }
interface Command { name: string; options: OptionDef[]; validate(args: unknown, opts: unknown): void; execute(args: unknown, opts: unknown): Promise<void>; }These interfaces serve as the migration contract -- each module is migrated to satisfy its interface.
New file: src/types.ts
Migrate modules with no internal dependencies first:
| Module | Notes |
|---|---|
src/errors.ts |
Domain error hierarchy (Phase 3.7) |
src/logger.ts |
Minimal, no internal deps |
src/constants.ts |
Pure data |
src/config.ts |
Config types derived from .codegraphrc.json schema |
src/db/connection.ts |
SQLite connection wrapper |
src/db/migrations.ts |
Schema version management |
src/formatters/*.ts |
Pure input->string transforms |
src/paginate.ts |
Generic pagination helpers |
Allow .js and .ts to coexist during migration (allowJs: true in tsconfig).
Migrate modules that implement Phase 3 interfaces:
| Module | Key types |
|---|---|
src/db/repository.ts |
Repository interface, all prepared statements typed |
src/parser/engine.ts |
Engine interface, native/WASM dispatch |
src/parser/registry.ts |
LanguageEntry type, extension mapping |
src/parser/tree-utils.ts |
Tree-sitter node helpers |
src/parser/base-extractor.ts |
Extractor interface, handler map |
src/parser/extractors/*.ts |
Per-language extractors |
src/analysis/*.ts |
Typed analysis results (impact scores, call chains) |
src/resolve.ts |
Import resolution with confidence types |
Migrate top-level orchestration and entry points:
| Module | Notes |
|---|---|
src/builder.ts |
Pipeline stages with typed PipelineStage |
src/watcher.ts |
File system events + pipeline |
src/embeddings/*.ts |
Vector store interface, model registry |
src/mcp/*.ts |
Tool schemas, typed handlers |
src/cli/*.ts |
Command objects with typed options |
src/index.ts |
Curated public API with proper export types |
- Migrate test files from
.jsto.ts - Add type-safe test utilities and fixture builders
- Verify vitest TypeScript integration with path aliases
- Maintain
InMemoryRepository(from Phase 3.2) as a typed test double
Verification: All existing tests pass. tsc --noEmit succeeds with zero errors. No any escape hatches except at FFI boundaries (napi-rs addon, tree-sitter WASM).
Affected files: All src/**/*.js -> src/**/*.ts, all tests/**/*.js -> tests/**/*.ts, package.json, biome.json
Goal: Dramatically improve semantic search quality by embedding natural-language descriptions instead of raw code.
Phase 5.3 (Hybrid Search) was completed early during Phase 2.5 -- FTS5 BM25 + semantic search with RRF fusion is already shipped in v2.6.0.
For each function/method/class node, generate a concise natural-language description:
"Validates a JWT token against the provided secret and algorithm options.
Params: token (string), options (object with secret, algorithms).
Returns: boolean. Throws on expired tokens.
Called by: authenticateRequest, verifySession.
Calls: jwt.verify, validateOptions."
How it works:
- Read source code from
linetoend_line - Extract existing JSDoc/docstring/comments if present
- Query caller/callee relationships from the graph DB
- Build a prompt combining code + context + relationships
- Call configured LLM (OpenAI, Anthropic, Ollama, or any OpenAI-compatible API)
- Batch processing with rate limiting and progress bar
- Store descriptions in a new
descriptionscolumn on thenodestable
Fallback when no LLM configured: Use existing raw-code embedding (current behavior, unchanged).
Incremental: Only regenerate descriptions for nodes whose file hash changed.
New file: src/describer.js
- When descriptions exist, embed the description text instead of raw code
- Keep raw code as fallback when no description is available
- Add
--use-descriptionsflag tocodegraph embed(default: true when descriptions exist) - Store embedding source type in
embedding_meta(code vs description)
Expected improvement: ~12% better semantic similarity for natural-language queries.
Affected files: src/embedder.js
Shipped in v2.6.0. FTS5 BM25 keyword search + semantic vector search with RRF fusion. Three search modes: hybrid (default), semantic, keyword.
Enrich nodes with LLM-generated metadata beyond descriptions. Computed incrementally at build time (only for changed nodes), stored as columns on the nodes table.
| Column | Content | Example |
|---|---|---|
side_effects |
Mutation/IO tags | "writes DB", "sends email", "mutates state" |
complexity_notes |
Responsibility count, cohesion rating | "3 responsibilities, low cohesion -- consider splitting" |
risk_score |
Fragility metric from graph centrality + LLM assessment | 0.82 (high fan-in + complex logic) |
- MCP tool:
assess <name>-- returns complexity rating + specific concerns - Cascade invalidation: when a node changes, mark dependents for re-enrichment
Depends on: 5.1 (LLM provider abstraction)
Aggregate function descriptions + dependency direction into file-level narratives.
module_summariestable -- one entry per file, re-rolled when any contained node changes- MCP tool:
explain_module <file>-- returns module purpose, key exports, role in the system naming_conventionsmetadata per module -- detected patterns (camelCase, snake_case, verb-first), flag outliers
Depends on: 5.1 (function-level descriptions must exist first)
Full spec: See llm-integration.md for detailed architecture, infrastructure table, and prompt design.
Goal: Allow developers to ask questions about their codebase in plain English.
codegraph ask "How does the authentication flow work?"Pipeline:
- Embed the question using the same model as search
- Retrieve top-K relevant functions/classes via hybrid search
- For each result, fetch caller/callee context from the graph
- Build a prompt with the question + retrieved code + graph context
- Send to configured LLM for answer generation
- Stream response to stdout
Context assembly strategy:
- Full source of top 5 matches
- Signatures of top 15 matches
- 1-hop caller/callee names for each match
- Total context budget: ~8K tokens (configurable)
Requires: LLM API key configured (no fallback -- this is inherently an LLM feature).
New file: src/nlquery.js
Multi-turn conversations with session memory.
codegraph ask "How does auth work?" --session my-session
codegraph ask "What about the token refresh?" --session my-session
codegraph sessions list
codegraph sessions clear- Store conversation history in SQLite table
sessions - Include prior Q&A pairs in subsequent prompts
New MCP tool: ask_codebase -- natural language query via MCP.
Enables AI coding agents (Claude Code, Cursor, etc.) to ask codegraph questions about the codebase.
Affected files: src/mcp.js
Graph traversal + LLM narration for questions that require both structural data and natural-language explanation. Each query walks the graph first, then sends the structural result to the LLM for narration.
| Query | Graph operation | LLM adds |
|---|---|---|
trace_flow <entry> |
BFS from entry point to leaves | Sequential narrative: "1. handler validates -> 2. calls createOrder -> 3. writes DB" |
trace_upstream <name> |
Recursive caller walk | Ranked suspects: "most likely cause is X because it modifies the same state" |
effect_analysis <name> |
Full callee tree walk, aggregate side_effects |
"Calling X will: write to DB (via Y), send email (via Z)" |
dependency_path <A> <B> |
Shortest path(s) between two symbols | Narrates each hop: "A imports X from B because A needs to validate tokens" |
Pre-computed flow_narratives table caches results for key entry points at build time, invalidated when any node in the chain changes.
Depends on: 5.4 (side_effects metadata), 5.1 (descriptions for narration context)
Help new contributors and AI agents orient in an unfamiliar codebase.
entry_pointsquery -- graph finds roots (high fan-out, low fan-in) + LLM ranks by importanceonboarding_guidecommand -- generates a reading order based on dependency layers- MCP tool:
get_started-- returns ordered list: "start here, then read this, then this" change_plan <description>-- LLM reads description, graph identifies relevant modules, returns touch points and test coverage gaps
Depends on: 5.5 (module summaries for context), 6.1 (query engine)
Goal: Go from 11 -> 19 supported languages.
| Language | Extensions | Grammar | Effort |
|---|---|---|---|
| C | .c, .h |
tree-sitter-c |
Low |
| C++ | .cpp, .hpp, .cc, .cxx |
tree-sitter-cpp |
Medium |
| Kotlin | .kt, .kts |
tree-sitter-kotlin |
Low |
| Swift | .swift |
tree-sitter-swift |
Medium |
| Language | Extensions | Grammar | Effort |
|---|---|---|---|
| Scala | .scala, .sc |
tree-sitter-scala |
Medium |
| Dart | .dart |
tree-sitter-dart |
Low |
| Lua | .lua |
tree-sitter-lua |
Low |
| Zig | .zig |
tree-sitter-zig |
Low |
Extract shared patterns from existing extractors into reusable helpers.
| Helper | Purpose |
|---|---|
findParentNode(node, typeNames) |
Walk parent chain to find enclosing class/struct |
extractBodyMethods(bodyNode, parentName) |
Extract method definitions from a body block |
normalizeImportPath(importText) |
Cross-language import path normalization |
Result: Reduces boilerplate for each new language from ~200 lines to ~80 lines.
New file: src/parser-utils.js
Goal: Bring codegraph's analysis into pull request workflows.
Note: Phase 2.5 delivered
codegraph check(CI validation predicates with exit code 0/1), which provides the foundation for GitHub Action integration. The boundary violation, blast radius, and cycle detection predicates are already available.
A reusable GitHub Action that runs on PRs:
codegraph buildon the repositorycodegraph diff-impactagainst the PR's base branchcodegraph check --stagedto run CI predicates (cycles, blast radius, signatures, boundaries)- Posts a PR comment summarizing:
- Number of affected functions and files
- New cycles introduced (if any)
- Boundary violations
- Top impacted functions with caller counts
Configuration via .codegraphrc.json:
{ "ci": { "failOnCycles": true, "impactThreshold": 50 } }Fail conditions: Configurable -- fail if new cycles or impact exceeds threshold.
New file: .github/actions/codegraph-ci/action.yml
codegraph review --pr <number>Requires gh CLI. For each changed function:
- Fetch PR diff via
gh pr diff - Run
diff-impacton the diff - Check: blast radius (caller count), contract changes (signature/return type), test coverage for affected callers
- Generate review summary (optionally LLM-enhanced)
- Post as PR comment via
gh pr comment
LLM-enhanced mode (when LLM provider configured):
- Risk labels per node:
low(cosmetic / internal),medium(behavior change),high(breaking / public API) - Review focus ranking: rank affected files by risk x blast radius -- "review this file first"
- Critical path highlighting: shortest path from a changed function to a high-fan-in entry point
- Test coverage gaps: cross-reference affected code with test file graph edges
New file: src/github.js
Extend the existing diff-impact --format mermaid foundation with CI automation and LLM annotations.
CI automation (GitHub Action):
codegraph build .(incremental, fast on CI cache)codegraph diff-impact $BASE_REF --format mermaid -Tto generate the graph- Post as PR comment -- GitHub renders Mermaid natively in markdown
- Update on new pushes (edit the existing comment)
LLM-enriched annotations (when provider configured):
- For each changed function: one-line summary of WHAT changed (from diff hunks)
- For each affected caller: WHY it's affected -- what behavior might change downstream
- Node colors shift from green -> yellow -> red based on risk labels
- Overall PR risk score (aggregate of node risks weighted by centrality)
Historical context overlay:
- Annotate nodes with churn data: "this function changed 12 times in the last 30 days"
- Highlight fragile nodes: high churn + high fan-in = high breakage risk
- Track blast radius trends: "this PR's blast radius is 2x larger than your average"
Depends on: 8.1 (GitHub Action), 5.4 (risk_score, side_effects)
Add SARIF output format for cycle detection. SARIF integrates with GitHub Code Scanning, showing issues inline in the PR.
Affected files: src/export.js
Phase 2.7 progress:
codegraph plot(Phase 2.7.8) ships a self-contained HTML viewer with vis-network. It supports layout switching, color/size/cluster overlays, drill-down, community detection, and a detail panel. The remaining work is the server-based experience below.
codegraph vizOpens a local web UI at localhost:3000 extending the static HTML viewer with:
- Server-side filtering for large graphs (the current
plotcommand embeds all data as JSON, scaling poorly past ~1K nodes) - Lazy edge loading and progressive disclosure
- Code preview on hover (reads from source files via local server)
- Filter panel: toggle node kinds, confidence thresholds, test files
- Edge styling by type (imports=solid, calls=dashed, extends=bold)
- Persistent view state (zoom, pan, expanded nodes saved across sessions)
Data source: Serve from DB via lightweight HTTP server, lazy-load on interaction.
New file: src/visualizer.js
codegraph dead
codegraph dead --exclude-exports --exclude-testsFind functions/methods/classes with zero incoming edges (never called). Filters for exports, test files, and entry points.
Note: Phase 2.5 added role classification (
deadrole in structure.js) and Phase 2.7 added AST node storage (ast_querycan find unreferenced exports). This extends those foundations with a dedicated command, smarter filtering, and cross-reference withexportscommand data.
Affected files: src/queries.js
Support multi-package monorepos with cross-package edges.
- Detect workspace root (
package.jsonworkspaces,pnpm-workspace.yaml,lerna.json) - Resolve internal package imports (e.g.,
@myorg/utils) to actual source files - Add
packagecolumn to nodes table codegraph build --workspaceto scan all packages- Impact analysis across package boundaries
Recursive reference-following search that traces connections.
codegraph agent-search "payment processing"Pipeline:
- Initial semantic search for the query
- For top results, fetch 1-hop neighbors from the graph
- Re-rank neighbors by relevance to the original query
- Follow the most relevant references (up to configurable depth)
- Return the full chain of related code
Use case: "Find everything related to payment processing" -> finds payment functions -> follows to validation -> follows to database layer -> returns complete picture.
Requires: LLM for relevance re-ranking (optional -- degrades to BFS without LLM).
New file: src/agentic-search.js
LLM-powered structural analysis that identifies refactoring opportunities. The graph provides the structural data; the LLM interprets it.
| Command | Graph operation | LLM adds |
|---|---|---|
split_analysis <file> |
Cluster tightly-coupled functions within a file | Proposed split, cross-boundary edges, circular import risk |
extraction_candidates |
Find high fan-in, low internal coupling functions | Rank by utility: "pure helper" vs "has side effects, risky to move" |
signature_impact <name> |
All call sites from graph edges | Suggested new signature, adapter pattern if needed, call sites to update |
lint-names |
Detect naming patterns per module | Flag outliers against detected conventions (camelCase, snake_case, verb-first) |
hotspots |
High fan-in + high fan-out + on many paths | Ranked fragility report with explanations, risk_score per node |
boundary_analysis |
Graph clustering (tightly-coupled groups spanning modules) | Reorganization suggestions: "these 4 functions in 3 files all deal with auth" |
Note:
hotspotsandboundary_analysisalready have data foundations from Phase 2.5 (structure.js hotspots, boundaries.js evaluation). This phase adds LLM interpretation on top.
Depends on: 5.4 (risk_score, complexity_notes), 5.5 (module summaries)
codegraph annotate
codegraph annotate --changed-onlyLLM-generated docstrings aware of callers, callees, and types. Diff-aware: only regenerate for functions whose code or dependencies changed. Stores in docstrings column on nodes table -- does not modify source files unless explicitly requested.
Depends on: 5.1 (LLM provider abstraction), 5.4 (side effects context)
Full spec: See llm-integration.md for detailed architecture, infrastructure tables, and prompt design for all LLM-powered features.
Each phase includes targeted verification:
| Phase | Verification |
|---|---|
| 1 | Benchmark native vs WASM parsing on a large repo, verify identical output from both engines |
| 2 | npm test, manual MCP client test for all tools, config loading tests |
| 2.5 | All 59 test files pass; integration tests for every new command; engine parity tests |
| 2.7 | All 70 test files pass; CFG + AST + dataflow integration tests; extractors produce identical output to pre-refactoring inline extractors (shipped as v3.0.0) |
| 3 | All existing tests pass; each refactored module produces identical output to the pre-refactoring version; unit tests for pure analysis modules; InMemoryRepository tests |
| 4 | tsc --noEmit passes with zero errors; all existing tests pass after migration; no runtime behavior changes |
| 5 | Compare codegraph search quality before/after descriptions; verify side_effects and risk_score populated for LLM-enriched builds |
| 6 | codegraph ask "How does import resolution work?" against codegraph itself; verify trace_flow and get_started produce coherent narration |
| 7 | Parse sample files for each new language, verify definitions/calls/imports |
| 8 | Test PR in a fork, verify GitHub Action comment with Mermaid graph and risk labels is posted |
| 9 | codegraph viz loads; hotspots returns ranked list with LLM commentary; split_analysis produces actionable output |
Full integration test after all phases:
codegraph build .
codegraph embed --describe # LLM-enhanced descriptions
codegraph search "middleware error handling"
codegraph ask "How does routing work?"
codegraph trace_flow handleRequest # LLM-narrated execution flow
codegraph hotspots # Fragility report with risk scores
codegraph diff-impact HEAD~5
codegraph review --pr 42 # LLM-enhanced PR review
codegraph vizTechnology changes to monitor that may unlock future improvements.
node:sqlite(Node.js built-in) -- primary target. Zero native dependencies, eliminates C++ addon breakage on Node major releases (better-sqlite3already broken on Node 24/25). Currently Stability 1.1 (Active Development) as of Node 25.x. Adopt when it reaches Stability 2, or use as a fallback alongsidebetter-sqlite3(dual-engine pattern like native/WASM parsing). Backed by the Node.js project -- no startup risk.libsql(SQLite fork by Turso) -- monitor only. Drop-inbetter-sqlite3replacement with built-in DiskANN vector search. However, Turso is pivoting engineering focus to Limbo (full Rust SQLite rewrite), leaving libsql as legacy. Pre-1.0 (v0.5.x) with uncertain long-term maintenance. Low switching cost (API-compatible, data is standard SQLite), but not worth adopting until the Turso/Limbo situation clarifies.
Want to help? Contributions to any phase are welcome. See CONTRIBUTING for setup instructions.
If you're interested in working on a specific phase, open an issue to discuss the approach before starting.