* feat(mcp): line numbers in explore output + per-file cluster fixes
Follow-up to #185. Three changes to codegraph_explore:
1. Source sections now carry cat -n style line-number prefixes
(<num>\t<code>), so the agent can cite file:line straight from the
payload instead of re-Reading the file just to recover a line number.
Isolated A/B: the no-line-numbers arm spent 2 Reads + a grep to find a
line number the line-numbered arm cited with zero follow-up calls.
Payload cost ~3-5%. Toggle off with CODEGRAPH_EXPLORE_LINENUMS=0.
2. Per-file cluster selection now ranks clusters containing a query entry
point ahead of dense declaration blocks. Density-only ranking buried
the relevant methods (perform/didCreateURLRequest/task in Alamofire's
Session.swift) under the top-of-file class header + property list.
3. Whole-file "envelope" nodes (a class/struct/etc. spanning >50% of the
file) are excluded from clustering. The Session class spans ~1,400
lines; keeping it collapsed every method into one giant cluster that
tail-trimmed down to just the class header, hiding the methods.
Net vs the 0.7.10 baseline, line numbers on: Alamofire -60%, Excalidraw
-32%, VS Code -12% per explore call.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(mcp): language-neutral omission markers in explore output
The gap separator and the two tail-trim markers used C-style `//`
comments, which aren't comments in Python, Ruby, etc. Switch to plain
`... (gap) ...` / `... (trimmed) ...` so they read correctly inside any
language's fenced source block. With line numbers on, the line-number
jump already corroborates a gap.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(mcp): language-neutral truncation marker in codegraph_context
Sibling to the explore marker fix: codegraph_context's code-block
truncation used a C-style `// ... truncated ...`. Switch to
`... (truncated) ...` so it reads correctly in any language's fenced
source block.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore(release): bump version to 0.7.11
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Output is now scaled to indexed file count. Small projects (<500 files)
cap at ~18KB and skip the "Additional relevant files" / completeness /
explore-budget reminders that earn their keep on larger codebases; medium
(<5,000) caps at ~28KB; large (<15,000) keeps the historical ~35KB; very
large goes up to ~38KB.
A per-file char cap also prevents a single file with many adjacent
symbols from collapsing into one whole-file dump (the pathological
Alamofire `Session.swift` case reported in #185), and a per-file symbol-
list cap stops the `#### path — sym(kind), ...` header from leaking
multi-KB lists when many adjacent symbols cluster together.
Measured against the README's benchmark repos: Alamofire (~100 files)
~62% smaller per call, Excalidraw (~600 files) ~35%, VS Code (~10k
files) ~14%. Agent-trust floor preserved — Relationships, scored cluster
selection, and structured-source output are all retained.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`codegraph_callees stage_apply::run` (and `_node`, `_impact`, ...)
returned "not found" against a repo with 7-9 sibling Rust modules,
each exporting `pub async fn run`. Two underlying issues:
1. The FTS5 query builder stripped `:` as a special char without
splitting on `::`, so `stage_apply::run` collapsed to the literal
`stage_applyrun` which matches nothing. Treat `::` as whitespace
before the strip step so both halves become FTS tokens.
2. `matchesSymbol` only understood `Parent.child` qualifiers and
relied on `qualifiedName` carrying the module path. Rust file-
level functions don't have their module name in `qualifiedName`
(it's encoded in the file path instead), so even dot-style
lookups failed. Accept `::`, `.`, `/` as separators; multi-level
forms compose; Rust `crate::`/`super::`/`self::` prefixes get
stripped before path matching. Fall back to file-path containment
when the qualified-name suffix doesn't match — `stage_apply::run`
matches a `run` in any file whose path has a `stage_apply` segment.
Also tightens the no-match branch: qualified lookups no longer fall
through to a fuzzy text match. `stage_apply::nonexistent_fn` returns
`null` instead of silently resolving to an unrelated `rollback` in
the same file.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The MCP `initialize` handler was awaiting `tryInitializeDefault` —
which opens the SQLite DB and runs `await initGrammars()` (tree-sitter
WASM bootstrap) — before sending the JSON-RPC response. On slow
filesystems (Docker Desktop VirtioFS on macOS, WSL2) this could exceed
Claude Code's ~30s handshake timeout, leaving the codegraph child
process alive and unresponsive with no tools visible in the client.
Send the response first; defer the open to a tracked background
promise. The lazy retry path used by `tools/list` and `tools/call`
now awaits that promise instead of racing it with `openSync`, so we
never double-open the SQLite file.
Adds a subprocess-based regression test that asserts the JSON-RPC
response arrives on stdout before `startWatching()` logs to stderr.
This ordering check catches the regression on any filesystem, not
just slow ones where the timing matters in practice.
Reported by @sashanclrp; isolated by @sgrimm's wire capture.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Closes the visibility gap behind issues #138 (WASM-on-macOS) and #139
(MCP "database is locked"). `better-sqlite3` is in optionalDependencies,
so when the native build fails npm install still succeeds and the
runtime silently falls back to node-sqlite3-wasm — 5-10x slower and
without WAL, so writers block readers (which is what makes the MCP
server appear to "lock the DB" in #139). The only existing signal was
a one-line `console.warn` to stderr that MCP transports typically
swallow.
This patch does NOT change install behavior — better-sqlite3 stays in
optionalDependencies so cross-platform installs keep working. It just
makes the substitution observable + recoverable.
## Visibility (4 surfaces)
- CLI `codegraph status`: new `Backend:` line under Index Statistics.
`native` rendered green; `wasm` rendered yellow with an inline
`npm rebuild better-sqlite3` nudge. Also exposed in `--json` as
`backend: 'native' | 'wasm'`.
- MCP `codegraph_status`: new `**Backend:**` line. Native form reads
`native (better-sqlite3)`; wasm form prepends a warning glyph and
includes the full fix recipe.
- Stderr banner on fallback (`buildWasmFallbackBanner`): replaces the
bare one-line `console.warn` with a multi-line bordered banner
covering macOS + Linux fix steps and optionally appending the
native load error.
- README troubleshooting: new "Indexing is slow / MCP database is
locked / WASM fallback active" entry that walks users to the
`Backend:` line and the fix.
## Per-instance backend tracking
`createDatabase` previously set a module-level `activeBackend` global.
MCP can open multiple project DBs in one process via the
`getCodeGraph()` cache, so the global would race / overwrite. Refactor:
`createDatabase` now returns `{db, backend}`, `DatabaseConnection`
carries `private backend` and exposes `getBackend()`, and
`CodeGraph.getBackend()` is the public surface. The CLI and MCP both
call `cg.getBackend()`.
## What this does NOT fix
The root cause of users landing on WASM is environment-specific (Mac
without Xcode CLT, Node version mismatch, etc.) and not fixable in
code without changing the optionalDependencies design. The README
entry tells users what to run; `Backend: native` after rebuild is the
confirmation signal.
## Tests
New `__tests__/sqlite-backend.test.ts` (6 tests) pins the banner
recipe content (so future edits can't strip the recovery commands),
the `WASM_FALLBACK_FIX_RECIPE` constant, and per-instance
`DatabaseConnection.getBackend()` / `CodeGraph.getBackend()` reporting.
Suite: 503 → 509, all passing.
Credit to @andreinknv whose analysis on #138 (and patches on his fork
at 6d0e7a2 + 69f7001) framed the visibility approach.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a universal tool-selection playbook surfaced by MCP clients
(Claude Code, Cursor, opencode, LangChain, OpenAI Agent SDK) in the
agent's system prompt automatically. Without this, agents have to
infer tool composition from individual tool descriptions and tend to
walk callers manually instead of reaching for codegraph_impact, etc.
Scoped tight: only the 9 tools that exist on main today
(search/context/callers/callees/impact/node/explore/files/status), no
"(when present)" references to unmerged tools, no per-language
guidance. ~40 lines of useful guidance.
Salvaged from #121, which bundled the instructions with #117's MCP
tool-registry refactor and referenced many tools that don't exist on
main.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Changes search result deduplication to use max scores across channels instead of first-seen prioritization, adds template component usage extraction for Svelte files, exempts exact matches from single-term score dampening, prioritizes structural edges in graph traversal, and increases explore tool node budget while including edge source locations in file clustering.
Adds Svelte to the list of supported languages and enhances the codegraph_explore tool description with specific guidance to use symbol names and file names rather than natural language queries. Recommends using codegraph_search first to discover relevant names for more effective exploration.
Updates Swift and Kotlin language support from basic to full in documentation and reduces explore budget thresholds to optimize performance for smaller codebases.
Addresses the need for automatic graph synchronization on file changes. Implements FileWatcher using native OS file events (FSEvents/inotify/ReadDirectoryChangesW) with 2-second debouncing to prevent thrashing on rapid saves. Filters changes against include/exclude patterns and ignores .codegraph directory modifications. Integrates with CodeGraph API (watch/unwatch/isWatching methods) and MCP server for automatic activation. Updates documentation to reflect shift from semantic to full-text search and removal of manual hook installation requirements.
Two fixes discovered while benchmarking Swift (Alamofire):
1. codegraph_explore traversalDepth 2→3: Deep call chains (e.g., Alamofire's
9-step Session.request()→URLSession flow) couldn't be followed in a single
explore call, forcing agents to fall back to file reads.
2. findSymbol/findAllSymbols now support "Parent.child" notation (e.g.,
"Session.request") by matching against qualified names (::Parent::child).
Previously only checked node.name === symbol, which never matched qualified
queries since node names are unqualified.
Also adds Alamofire Swift benchmark data to README (91% fewer tool calls,
78% faster with CodeGraph).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Decreases maximum output from 50,000 to 35,000 characters to prevent exceeding ~10k token limits in MCP clients that could cause truncation or errors when processing exploration results.
Changes guidance to recommend spawning Explore agents for exploration questions instead of using codegraph_explore/codegraph_context directly in main session to avoid filling up context with large amounts of source code. Adds completeness signal to codegraph_explore output so agents know not to re-read files that already have source code included.
Introduces a new MCP tool that performs deep code exploration in a single call, returning comprehensive context with full source code sections grouped by file and relationship mapping. Designed to replace multiple codegraph_node + file read operations for thorough understanding of code topics. Updates documentation to position explore as the primary tool for deep exploration questions.
Three issues discovered testing CodeGraph against a Shopify Liquid theme:
1. Callers/callees only traversed 'calls' edges, missing 'references' and
'imports' edges that Liquid extraction creates for {% render %} and
{% section %} tags. Expanded edge filter in getCallers/getCallees.
2. Context builder only ran text search as a fallback when semantic search
returned nothing. For template-heavy codebases, semantic search returns
irrelevant results (e.g., "Toast" for a header navigation query) while
text/path-based matching would find the right files. Now always runs
text search alongside semantic search with multi-term boosting.
3. MCP findAllSymbols only matched nodes by exact name, missing file nodes
whose basename (without extension) matched the symbol. This caused
callers to find zero results even with correct edges, since references
edges point to file nodes (e.g., "product-card.liquid") not component
nodes (e.g., "product-card").
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Java extraction:
- Handle Java method_invocation AST (receiver.method pattern)
- Support Java extends_interfaces and super_interfaces with type_list
- Create unresolved references for Java imports for cross-file resolution
- Extract interface inheritance via extractInheritance
MCP tools:
- Aggregate callers/callees/impact across ALL matching symbols (e.g. multiple
overloads or same-named methods in different classes)
- New findAllSymbols() helper for multi-symbol lookup
Graph traversal:
- Impact analysis now traverses into container children (class → methods)
so that callers of methods appear in the impact radius of their class
Other:
- Add deleteSpecificResolvedReferences() for precise cleanup after resolution
- Add 'instance-method' to resolvedBy union type
- Version bump to 0.6.8
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Eliminates anonymous error reporting functionality that was collecting stack traces and error context via Sentry. Removes all telemetry-related code, configuration options, and documentation references.
Fixes#47 — "database is locked" after crash and MCP "not initialized"
when project IS initialized.
- FileLock: treat locks older than 10 minutes as stale regardless of PID
status, covering cases where PID was reused or kill signal check fails
- MCP server: log errors from tryInitializeDefault() to stderr instead of
silently swallowing, so transient open failures are diagnosable
- MCP server: retryInitIfNeeded() properly cleans up failed instances
before retrying, preventing resource leaks
- CLI: add 'codegraph unlock' command for manual lock file removal
Replace native tree-sitter with web-tree-sitter + tree-sitter-wasms for
universal cross-platform support. Add node-sqlite3-wasm as a fallback
when better-sqlite3 native bindings aren't available. Move better-sqlite3
and sqlite-vss to optionalDependencies so installs never fail.
Fix installer to use npx fallback when global npm install fails, so MCP
config, hooks, and quick-start instructions all work without the bare
codegraph command in PATH.
Fix tests: update schema version expectation, fix db test paths and
method names, extract MAX_OUTPUT_LENGTH as module constant, normalize
Windows path separators in import resolver.
- Add validateProjectPath() to reject sensitive system directories
- Add isPathWithinRoot/isPathWithinRootReal for symlink-aware path checks
- Replace hand-rolled glob-to-regex with picomatch to prevent ReDoS
- Add isSafeRegex() to reject custom patterns with nested quantifiers
- Replace FileLock with PID-tracking version that detects stale locks
- Add symlink detection in removeDirectory/listDirectoryContents
- Add subdirectory name validation in ensureSubdirectory
- Add atomicWriteFileSync and corrupted file backup in config-writer
- Add MCP input validation (validateString) for all tool handlers
- Fix CLAUDE.md section replacement to handle ### subsections correctly
- Add findSymbol() helper that prefers exact name matches and notes
alternatives when multiple symbols share the same name
- Add output truncation (15K char cap) to prevent context window bloat
- Apply to callers, callees, impact, node, search, and files tools
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implements security improvements inspired by PR #16 (credit: MO2k4):
- Add validatePathWithinRoot() to prevent path traversal attacks in
extraction and context building
- Clamp MCP tool inputs (limit, depth, maxDepth) to sane ranges
- Use atomic writes (temp file + rename) for config saves
- Add symlink cycle detection in directory scanning to prevent infinite loops
- Replace all JSON.parse calls in db/queries.ts with safeJsonParse fallbacks
to handle corrupted database metadata gracefully
- Add cross-process FileLock for DB write operations (indexAll, indexFiles,
sync) to prevent concurrent writes from CLI, MCP server, and git hooks
- Remove unused path import from context/index.ts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds support for Dart and Liquid languages with tree-sitter parsing.
Improves accuracy of code symbol extraction for existing languages.
Indexes project files to enhance code navigation features.
Migrates build system to facilitate code contributions.
Removes git hook functionality.
Integrates Sentry for error tracking and reporting.
Enhances project initialization and configuration loading.
The hybrid approach (Claude's native Explore agents using codegraph tools)
is more effective than a custom explore tool because Explore agents already
know what format the main session needs.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Instead of returning file paths for Claude to read separately,
codegraph_explore now:
- Reads relevant files internally using fs
- Extracts code snippets for key symbols (functions, types, APIs)
- Includes the code directly in the response
- Returns a synthesis with data flow and key code included
This matches native explore agent behavior where file reading
happens in the sub-agent context, keeping main context clean.
Bump version to 0.1.8
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Move warnings to TOP of output so Claude sees them first
- If existing implementations found: "STOP: Similar implementations exist!"
- If feature request: "BEFORE PLANNING: Use AskUserQuestion to clarify..."
- Update tool description to emphasize checking existing code first
- Fixes issue where Claude listed questions but never asked them
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Pattern generation now uses multiple naming conventions:
PascalCase, snake_case, and camelCase
- Common suffixes/prefixes work across languages (Service,
Handler, Controller, handle_, create_, etc.)
- Removed JS/React-specific patterns like useBundle, SwapDialog
- Bump version to 0.1.6
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add codegraph_explore MCP tool for deep exploration with condensed output
- Expand default exclude patterns for framework build outputs (.next, .nuxt, .expo, etc.)
- Increase node ID hash length from 16 to 32 chars to prevent collisions
- Add feature request detection with UX clarification reminders
- Update README with MCP tools reference and best practices
- Update CLAUDE.md with context usage guidelines
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add evaluation test suite with TypeScript and Python fixtures
- Fix MCP server to defer CodeGraph init until rootUri received
- Fix call edge extraction by calling resolveReferences() after indexAll/sync
- Fix glob matching for root-level files (e.g., **/*.py now matches auth.py)
- Fix duplicate node extraction for methods inside classes
- Update context tests to use buildContext for semantic search + graph traversal
- Export unused formatter functions to fix build
Evaluation results:
- TypeScript: 96% precision, 79% recall, 85% F1
- Python: 99% precision, 80% recall, 85% F1
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>