Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions .claude/hooks/enrich-context.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
#!/usr/bin/env bash
# enrich-context.sh — PreToolUse hook for Read and Grep tools
# Provides dependency context from codegraph when reading/searching files.
# Always exits 0 (informational only, never blocks).

set -euo pipefail

# Read the tool input from stdin
INPUT=$(cat)

# Extract file path based on tool type
# Read tool uses tool_input.file_path, Grep uses tool_input.path
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // .tool_input.path // empty' 2>/dev/null)

# Guard: no file path found
if [ -z "$FILE_PATH" ]; then
exit 0
fi

# Guard: codegraph DB must exist
DB_PATH="${CLAUDE_PROJECT_DIR:-.}/.codegraph/graph.db"
if [ ! -f "$DB_PATH" ]; then
exit 0
fi

# Guard: codegraph must be available
if ! command -v codegraph &>/dev/null && ! command -v npx &>/dev/null; then
exit 0
fi

# Convert absolute path to relative (strip project dir prefix)
REL_PATH="$FILE_PATH"
if [[ "$FILE_PATH" == "${CLAUDE_PROJECT_DIR}"* ]]; then
REL_PATH="${FILE_PATH#"${CLAUDE_PROJECT_DIR}"/}"
fi
# Normalize backslashes to forward slashes (Windows compatibility)
REL_PATH="${REL_PATH//\\//}"

# Run codegraph deps and capture output
DEPS=""
if command -v codegraph &>/dev/null; then
DEPS=$(codegraph deps "$REL_PATH" --json -d "$DB_PATH" 2>/dev/null) || true
else
DEPS=$(npx --yes @optave/codegraph deps "$REL_PATH" --json -d "$DB_PATH" 2>/dev/null) || true
fi

# Guard: no output or error
if [ -z "$DEPS" ] || [ "$DEPS" = "null" ]; then
exit 0
fi

# Output as informational context (never deny)
echo "$DEPS" | jq -c '{
hookSpecificOutput: (
"Codegraph context for " + (.file // "unknown") + ":\n" +
" Imports: " + ((.results[0].imports // []) | length | tostring) + " files\n" +
" Imported by: " + ((.results[0].importedBy // []) | length | tostring) + " files\n" +
" Definitions: " + ((.results[0].definitions // []) | length | tostring) + " symbols"
)
}' 2>/dev/null || true

exit 0
20 changes: 20 additions & 0 deletions .claude/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,26 @@
"timeout": 10
}
]
},
{
"matcher": "Read",
"hooks": [
{
"type": "command",
"command": "bash \"$CLAUDE_PROJECT_DIR/.claude/hooks/enrich-context.sh\"",
"timeout": 5
}
]
},
{
"matcher": "Grep",
"hooks": [
{
"type": "command",
"command": "bash \"$CLAUDE_PROJECT_DIR/.claude/hooks/enrich-context.sh\"",
"timeout": 5
}
]
}
]
}
Expand Down
24 changes: 24 additions & 0 deletions .github/workflows/shield-license-compliance.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,32 @@ jobs:
echo "" >> $GITHUB_STEP_SUMMARY

# Check for restrictive licenses
# Allowlist: packages reviewed and approved despite containing LGPL in license string
# Each entry requires a justification comment above it
# @img/sharp-* — bundles libvips (LGPL-3.0) as dynamic lib inside Apache-2.0 sharp;
# LGPL satisfied by npm's replaceable node_modules; codegraph doesn't use sharp directly
LICENSE_ALLOWLIST="@img/sharp-"

restrictive=$(jq -r 'to_entries[] | select(.value.licenses | test("GPL|AGPL|LGPL|SSPL|BSL"; "i")) | "- **\(.key)**: \(.value.licenses)"' "$report" 2>/dev/null || true)

# Filter out explicitly allowlisted packages
if [ -n "$restrictive" ]; then
filtered=""
while IFS= read -r line; do
skip=false
for allowed in $LICENSE_ALLOWLIST; do
if echo "$line" | grep -q "$allowed"; then
skip=true
break
fi
done
if [ "$skip" = false ]; then
filtered="${filtered}${line}\n"
fi
done <<< "$restrictive"
restrictive=$(echo -e "$filtered" | sed '/^$/d')
fi

if [ -n "$restrictive" ]; then
echo "### Restrictive Licenses Found" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
Expand Down
43 changes: 26 additions & 17 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,38 @@ All notable changes to this project will be documented in this file. See [commit

## [1.4.0](https://github.com/optave/codegraph/compare/v1.3.0...v1.4.0) (2026-02-22)

**Phase 2 — Foundation Hardening** is complete. This release hardens the core infrastructure: a declarative parser registry, a full MCP server, significantly improved test coverage, and secure credential management.

### Features

* **config:** add apiKeyCommand for secure credential resolution ([f3ab237](https://github.com/optave/codegraph/commit/f3ab23790369df00b50c75ae7c3b6bba47fde2c6))
* **mcp:** expand MCP server from 5 to 11 tools ([510dd74](https://github.com/optave/codegraph/commit/510dd74ed14d455e50aa3166fa28cf90d05925dd))
* **mcp:** expand MCP server from 5 to 11 tools — `fn_deps`, `fn_impact`, `diff_impact`, `semantic_search`, `export_graph`, `list_functions` ([510dd74](https://github.com/optave/codegraph/commit/510dd74ed14d455e50aa3166fa28cf90d05925dd))
* **config:** add `apiKeyCommand` for secure credential resolution via external secret managers (1Password, Bitwarden, Vault, pass, macOS Keychain) ([f3ab237](https://github.com/optave/codegraph/commit/f3ab23790369df00b50c75ae7c3b6bba47fde2c6))
* **parser:** add `LANGUAGE_REGISTRY` for declarative parser dispatch — adding a new language is now a single registry entry + extractor function ([cb08bb5](https://github.com/optave/codegraph/commit/cb08bb58adac8d7aa4d5fb6ea463ce6d3dba8007))

### Testing

### Bug Fixes
* add unit tests for 8 core modules, improve coverage from 62% to 75% ([62d2694](https://github.com/optave/codegraph/commit/62d2694))
* add end-to-end CLI smoke tests ([15211c0](https://github.com/optave/codegraph/commit/15211c0))
* add 11 tests for `resolveSecrets` and `apiKeyCommand` integration
* make normalizePath test cross-platform ([36fa9cf](https://github.com/optave/codegraph/commit/36fa9cf))
* skip native engine parity tests for known Rust gaps ([7d89cd9](https://github.com/optave/codegraph/commit/7d89cd9))

### Documentation

* add napi-rs package.json for build-native workflow ([b9d7e0e](https://github.com/optave/codegraph/commit/b9d7e0e58dcf3e2a54645d87fdf1a5a90c7c7b98))
* align native platform package versions with root ([93c9c4b](https://github.com/optave/codegraph/commit/93c9c4b31c7c01471c37277067fd095214a643b1))
* **ci:** add --provenance to platform package publishes for OIDC auth ([bc595f7](https://github.com/optave/codegraph/commit/bc595f78ab35fe5db3a00711977ab2b963c4f3ef))
* **ci:** add allowed_tools to Claude Code review action ([eb5d9f2](https://github.com/optave/codegraph/commit/eb5d9f270b446c2d2c72bb2ee7ffd0433463c720))
* **ci:** grant write permissions to Claude workflows for PR comments ([aded63c](https://github.com/optave/codegraph/commit/aded63c19375ede0037ac62736c6049f6b77daba))
* **ci:** prefix platform package path with ./ for npm 10 compatibility ([06fa212](https://github.com/optave/codegraph/commit/06fa212bba55b11d77e689c8d5e91faca4eef5a4))
* **ci:** skip version bump when override matches current version ([df19486](https://github.com/optave/codegraph/commit/df19486ff30724791c71e49b130417e30281b659))
* handle null baseUrl in native alias conversion, skip flaky native cache tests ([d0077e1](https://github.com/optave/codegraph/commit/d0077e175446fc27619b767d8fcf06b91d3a042c))
* repair build-native workflow ([67d7650](https://github.com/optave/codegraph/commit/67d7650235e6291b002224a31dfc765df666a36a))
* reset lockfile before npm version to avoid dirty-tree error ([6f0a40a](https://github.com/optave/codegraph/commit/6f0a40a48cbb589e672ea149ee5f920fb258e697))
* **test:** make normalizePath test cross-platform ([36fa9cf](https://github.com/optave/codegraph/commit/36fa9cfa3a084af173c85fca47c5f5cd2ed3d700))
* **test:** skip native engine parity tests for known Rust gaps ([7d89cd9](https://github.com/optave/codegraph/commit/7d89cd957c7cda937c4bc8a1e9d389e76807ceb2))
* add secure credential management guide with examples for 5 secret managers
* update ROADMAP marking Phase 2 complete
* add community health files (CONTRIBUTING, CODE_OF_CONDUCT, SECURITY)

### CI/CD

### Refactoring
* add license compliance workflow and CI testing pipeline ([eeeb68b](https://github.com/optave/codegraph/commit/eeeb68b))
* add OIDC trusted publishing with `--provenance` for npm packages ([bc595f7](https://github.com/optave/codegraph/commit/bc595f7))
* add automated semantic versioning and commit enforcement ([b8e5277](https://github.com/optave/codegraph/commit/b8e5277))
* add Claude Code review action for PRs ([eb5d9f2](https://github.com/optave/codegraph/commit/eb5d9f2))
* add Biome linter and formatter ([a6e6bd4](https://github.com/optave/codegraph/commit/a6e6bd4))

### Bug Fixes

* add LANGUAGE_REGISTRY for declarative parser dispatch ([cb08bb5](https://github.com/optave/codegraph/commit/cb08bb58adac8d7aa4d5fb6ea463ce6d3dba8007))
* handle null `baseUrl` in native alias conversion ([d0077e1](https://github.com/optave/codegraph/commit/d0077e1))
* align native platform package versions with root ([93c9c4b](https://github.com/optave/codegraph/commit/93c9c4b))
* reset lockfile before `npm version` to avoid dirty-tree error ([6f0a40a](https://github.com/optave/codegraph/commit/6f0a40a))
3 changes: 2 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ JS source is plain JavaScript (ES modules) in `src/`. No transpilation step. The
| `index.js` | Programmatic API exports |
| `builder.js` | Graph building: file collection, parsing, import resolution, incremental hashing |
| `parser.js` | tree-sitter WASM wrapper; `LANGUAGE_REGISTRY` + per-language extractors for functions, classes, methods, imports, exports, call sites |
| `queries.js` | Query functions: symbol search, file deps, impact analysis, diff-impact |
| `queries.js` | Query functions: symbol search, file deps, impact analysis, diff-impact; `SYMBOL_KINDS` constant defines all node kinds |
| `embedder.js` | Semantic search with `@huggingface/transformers`; multi-query RRF ranking |
| `db.js` | SQLite schema and operations (`better-sqlite3`) |
| `mcp.js` | MCP server exposing graph queries to AI agents |
Expand All @@ -60,6 +60,7 @@ JS source is plain JavaScript (ES modules) in `src/`. No transpilation step. The
- Platform-specific prebuilt binaries published as optional npm packages (`@optave/codegraph-{platform}-{arch}`)
- WASM grammars are built from devDeps on `npm install` (via `prepare` script) and not committed to git — used as fallback when native addon is unavailable
- **Language parser registry:** `LANGUAGE_REGISTRY` in `parser.js` is the single source of truth for all supported languages — maps each language to `{ id, extensions, grammarFile, extractor, required }`. `EXTENSIONS` in `constants.js` is derived from the registry. Adding a new language requires one registry entry + extractor function
- **Node kinds:** `SYMBOL_KINDS` in `queries.js` lists all valid kinds: `function`, `method`, `class`, `interface`, `type`, `struct`, `enum`, `trait`, `record`, `module`. Language-specific types use their native kind (e.g. Go structs → `struct`, Rust traits → `trait`, Ruby modules → `module`) rather than mapping everything to `class`/`interface`
- `@huggingface/transformers` and `@modelcontextprotocol/sdk` are optional dependencies, lazy-loaded
- Non-required parsers (all except JS/TS/TSX) fail gracefully if their WASM grammar is unavailable
- Import resolution uses a 6-level priority system with confidence scoring (import-aware → same-file → directory → parent → global → method hierarchy)
Expand Down
33 changes: 17 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,27 +45,27 @@ Most dependency graph tools only tell you which **files** import which — codeg

### Feature comparison

| Capability | codegraph | Madge | dep-cruiser | Skott | Nx graph | Sourcetrail |
|---|:---:|:---:|:---:|:---:|:---:|:---:|
| Function-level analysis | **Yes** | — | — | — | — | **Yes** |
| Multi-language | **10** | 1 | 1 | 1 | Any (project) | 4 |
| Semantic search | **Yes** | — | — | — | — | — |
| MCP / AI agent support | **Yes** | — | — | — | — | — |
| Git diff impact | **Yes** | — | — | — | Partial | — |
| Persistent database | **Yes** | — | — | — | — | Yes |
| Watch mode | **Yes** | — | — | — | Daemon | — |
| CI workflow included | **Yes** | — | Rules | — | Yes | — |
| Cycle detection | **Yes** | Yes | Yes | Yes | — | — |
| Zero config | **Yes** | Yes | — | Yes | — | — |
| Fully local / no telemetry | **Yes** | Yes | Yes | Yes | Partial | Yes |
| Free & open source | **Yes** | Yes | Yes | Yes | Partial | Archived |
| Capability | codegraph | Madge | dep-cruiser | Skott | Nx graph | Sourcetrail | GitNexus |
|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
| Function-level analysis | **Yes** | — | — | — | — | **Yes** | **Yes** |
| Multi-language | **11** | 1 | 1 | 1 | Any (project) | 4 | 9 |
| Semantic search | **Yes** | — | — | — | — | — | **Yes** |
| MCP / AI agent support | **Yes** | — | — | — | — | — | **Yes** |
| Git diff impact | **Yes** | — | — | — | Partial | — | **Yes** |
| Persistent database | **Yes** | — | — | — | — | Yes | **Yes** |
| Watch mode | **Yes** | — | — | — | Daemon | — | — |
| CI workflow included | **Yes** | — | Rules | — | Yes | — | — |
| Cycle detection | **Yes** | Yes | Yes | Yes | — | — | — |
| Zero config | **Yes** | Yes | — | Yes | — | — | **Yes** |
| Fully local / no telemetry | **Yes** | Yes | Yes | Yes | Partial | Yes | **Yes** |
| Free & open source | **Yes** | Yes | Yes | Yes | Partial | Archived | No |

### What makes codegraph different

| | Differentiator | In practice |
|---|---|---|
| **🔬** | **Function-level, not just files** | Traces `handleAuth()` → `validateToken()` → `decryptJWT()` and shows 14 callers across 9 files break if `decryptJWT` changes |
| **🌐** | **Multi-language, one CLI** | JS/TS + Python + Go + Rust + Java + C# + PHP + Ruby + Terraform in a single graph — no juggling Madge, pyan, and cflow |
| **🌐** | **Multi-language, one CLI** | JS/TS + Python + Go + Rust + Java + C# + PHP + Ruby + HCL in a single graph — no juggling Madge, pyan, and cflow |
| **🤖** | **AI-agent ready** | Built-in [MCP server](https://modelcontextprotocol.io/) — AI assistants query your graph directly via `codegraph fn <name>` |
| **💥** | **Git diff impact** | `codegraph diff-impact` shows changed functions, their callers, and full blast radius — ships with a GitHub Actions workflow |
| **🔒** | **Fully local, zero telemetry** | No accounts, no API keys, no cloud, no data exfiltration — Apache-2.0, free forever |
Expand All @@ -88,6 +88,7 @@ Many tools in this space are cloud-based or SaaS — meaning your code leaves yo
| [Understand](https://scitools.com/) | Deep multi-language static analysis | $100+/month per seat, proprietary, GUI-only, no CI or AI integration |
| [Snyk Code](https://snyk.io/) | AI-powered security scanning | Cloud-based — code sent to Snyk servers for analysis, not a dependency graph tool |
| [pyan](https://github.com/Technologicat/pyan) / [cflow](https://www.gnu.org/software/cflow/) | Function-level call graphs | Single-language each (Python / C only), no persistence, no queries |
| [GitNexus](https://gitnexus.dev/) | Function-level graph with hybrid search and MCP | PolyForm Noncommercial license, no watch mode, no cycle detection, no CI workflow |

---

Expand Down Expand Up @@ -228,7 +229,7 @@ codegraph mcp # Start MCP server for AI assistants
| `-j, --json` | Output as JSON |
| `-v, --verbose` | Enable debug output |
| `--engine <engine>` | Parser engine: `native`, `wasm`, or `auto` (default: `auto`) |
| `-k, --kind <kind>` | Filter by kind: `function`, `method`, `class` (search) |
| `-k, --kind <kind>` | Filter by kind: `function`, `method`, `class`, `struct`, `enum`, `trait`, `record`, `module` (search) |
| `--file <pattern>` | Filter by file path pattern (search) |
| `--rrf-k <n>` | RRF smoothing constant for multi-query search (default 60) |

Expand Down
16 changes: 15 additions & 1 deletion ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Codegraph is a strong local-first code graph CLI. This roadmap describes planned
| Phase | Theme | Key Deliverables | Status |
|-------|-------|-----------------|--------|
| [**1**](#phase-1--rust-core) | Rust Core | Rust parsing engine via napi-rs, parallel parsing, incremental tree-sitter, JS orchestration layer | **Complete** (v1.3.0) |
| [**2**](#phase-2--foundation-hardening) | Foundation Hardening | Parser registry, complete MCP, test coverage, enhanced config | **Complete** (v1.4.0) |
| [**2**](#phase-2--foundation-hardening) | Foundation Hardening | Parser registry, complete MCP, test coverage, enhanced config, multi-repo MCP | **Partial** — core complete (v1.4.0), 2.5 planned |
| [**3**](#phase-3--intelligent-embeddings) | Intelligent Embeddings | LLM-generated descriptions, hybrid search | Planned |
| [**4**](#phase-4--natural-language-queries) | Natural Language Queries | `ask` command, conversational sessions | Planned |
| [**5**](#phase-5--expanded-language-support) | Expanded Language Support | 8 new languages (12 → 20), parser utilities | Planned |
Expand Down Expand Up @@ -171,6 +171,20 @@ New configuration options in `.codegraphrc.json`:

**Affected files:** `src/config.js`

### 2.5 — Multi-Repo MCP

Support querying multiple codebases from a single MCP server instance.

- Registry file at `~/.codegraph/registry.json` mapping repo names to their `.codegraph/graph.db` paths
- Lazy DB connections — only opened when a repo is first queried
- Add optional `repo` parameter to all MCP tools to target a specific repository
- Auto-registration: `codegraph build` adds the current project to the registry
- New CLI commands: `codegraph registry list|add|remove` for manual management
- Default behavior: when `repo` is 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`

---

## Phase 3 — Intelligent Embeddings
Expand Down
6 changes: 3 additions & 3 deletions crates/codegraph-core/src/extractors/csharp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ fn walk_node(node: &Node, source: &[u8], symbols: &mut FileSymbols) {
let name = node_text(&name_node, source).to_string();
symbols.definitions.push(Definition {
name: name.clone(),
kind: "class".to_string(),
kind: "struct".to_string(),
line: start_line(node),
end_line: Some(end_line(node)),
decorators: None,
Expand All @@ -65,7 +65,7 @@ fn walk_node(node: &Node, source: &[u8], symbols: &mut FileSymbols) {
let name = node_text(&name_node, source).to_string();
symbols.definitions.push(Definition {
name: name.clone(),
kind: "class".to_string(),
kind: "record".to_string(),
line: start_line(node),
end_line: Some(end_line(node)),
decorators: None,
Expand Down Expand Up @@ -112,7 +112,7 @@ fn walk_node(node: &Node, source: &[u8], symbols: &mut FileSymbols) {
if let Some(name_node) = node.child_by_field_name("name") {
symbols.definitions.push(Definition {
name: node_text(&name_node, source).to_string(),
kind: "class".to_string(),
kind: "enum".to_string(),
line: start_line(node),
end_line: Some(end_line(node)),
decorators: None,
Expand Down
2 changes: 1 addition & 1 deletion crates/codegraph-core/src/extractors/go.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ fn walk_node(node: &Node, source: &[u8], symbols: &mut FileSymbols) {
"struct_type" => {
symbols.definitions.push(Definition {
name,
kind: "class".to_string(),
kind: "struct".to_string(),
line: start_line(node),
end_line: Some(end_line(node)),
decorators: None,
Expand Down
Loading
Loading