@colbymchenry/codegraph 0.9.3 → 0.9.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +142 -82
- package/npm-shim.js +215 -27
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -6,19 +6,21 @@
|
|
|
6
6
|
|
|
7
7
|
**~35% cheaper · ~70% fewer tool calls · 100% local**
|
|
8
8
|
|
|
9
|
+
### [Documentation & Website →](https://colbymchenry.github.io/codegraph/)
|
|
10
|
+
|
|
9
11
|
[](https://www.npmjs.com/package/@colbymchenry/codegraph)
|
|
10
12
|
[](https://opensource.org/licenses/MIT)
|
|
11
13
|
[](https://nodejs.org/)
|
|
12
14
|
|
|
13
|
-
[](#)
|
|
14
|
-
[](#)
|
|
15
|
-
[](#)
|
|
15
|
+
[](#supported-platforms)
|
|
16
|
+
[](#supported-platforms)
|
|
17
|
+
[](#supported-platforms)
|
|
16
18
|
|
|
17
|
-
[](#)
|
|
18
|
-
[](#)
|
|
19
|
-
[](#)
|
|
20
|
-
[](#)
|
|
21
|
-
[](#)
|
|
19
|
+
[](#supported-agents)
|
|
20
|
+
[](#supported-agents)
|
|
21
|
+
[](#supported-agents)
|
|
22
|
+
[](#supported-agents)
|
|
23
|
+
[](#supported-agents)
|
|
22
24
|
|
|
23
25
|
</div>
|
|
24
26
|
|
|
@@ -56,6 +58,16 @@ codegraph init -i
|
|
|
56
58
|
|
|
57
59
|
</div>
|
|
58
60
|
|
|
61
|
+
### Uninstall
|
|
62
|
+
|
|
63
|
+
Changed your mind? One command removes CodeGraph from every agent it configured:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
codegraph uninstall
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
<sub>Reverses the installer — strips CodeGraph's MCP server config, instructions, and permissions from each configured agent. Your project indexes (`.codegraph/`) are left untouched; remove those per-project with `codegraph uninit`. Use `--target` to remove from specific agents, or `--yes` to run non-interactively.</sub>
|
|
70
|
+
|
|
59
71
|
---
|
|
60
72
|
|
|
61
73
|
## Why CodeGraph?
|
|
@@ -66,26 +78,26 @@ When Claude Code explores a codebase, it spawns **Explore agents** that scan fil
|
|
|
66
78
|
|
|
67
79
|
### Benchmark Results
|
|
68
80
|
|
|
69
|
-
Tested across **7 real-world open-source codebases** spanning 7 languages, comparing an agent (Claude Code, headless) answering one architecture question **with** and **without** CodeGraph. Each cell is the savings at the **median of 4 runs per arm**.
|
|
81
|
+
Tested across **7 real-world open-source codebases** spanning 7 languages, comparing an agent (Claude Code, headless) answering one architecture question **with** and **without** CodeGraph. Each cell is the savings at the **median of 4 runs per arm**. _Re-validated on **v0.9.4** (2026-05-24)._
|
|
70
82
|
|
|
71
|
-
> **Average: 35% cheaper ·
|
|
83
|
+
> **Average: 35% cheaper · 57% fewer tokens · 46% faster · 71% fewer tool calls**
|
|
72
84
|
|
|
73
85
|
| Codebase | Language | Cost | Tokens | Time | Tool calls |
|
|
74
86
|
|----------|----------|------|--------|------|------------|
|
|
75
|
-
| **VS Code** | TypeScript · ~10k files |
|
|
76
|
-
| **Excalidraw** | TypeScript · ~
|
|
77
|
-
| **Django** | Python · ~
|
|
78
|
-
| **Tokio** | Rust · ~
|
|
79
|
-
| **OkHttp** | Java · ~
|
|
80
|
-
| **Gin** | Go · ~
|
|
81
|
-
| **Alamofire** | Swift · ~
|
|
87
|
+
| **VS Code** | TypeScript · ~10k files | 26% cheaper | 78% fewer | 52% faster | 85% fewer |
|
|
88
|
+
| **Excalidraw** | TypeScript · ~640 | 52% cheaper | 90% fewer | 73% faster | 96% fewer |
|
|
89
|
+
| **Django** | Python · ~3k | 12% cheaper | 36% fewer | 19% faster | 53% fewer |
|
|
90
|
+
| **Tokio** | Rust · ~790 | 82% cheaper | 86% fewer | 71% faster | 92% fewer |
|
|
91
|
+
| **OkHttp** | Java · ~645 | 2% cheaper | 13% fewer | 31% faster | 45% fewer |
|
|
92
|
+
| **Gin** | Go · ~110 | 21% cheaper | 34% fewer | 27% faster | 40% fewer |
|
|
93
|
+
| **Alamofire** | Swift · ~110 | 47% cheaper | 64% fewer | 48% faster | 83% fewer |
|
|
82
94
|
|
|
83
95
|
The gains scale with codebase size: on large repos the agent answers from the index in a handful of calls with **zero file reads**, while the no-CodeGraph agent fans out across grep/find/Read (and the sub-agents it spawns). On a small repo like Gin (~150 files) native search is already cheap, so the margin narrows.
|
|
84
96
|
|
|
85
97
|
<details>
|
|
86
98
|
<summary><strong>Full benchmark details</strong></summary>
|
|
87
99
|
|
|
88
|
-
**Methodology.** Each arm is `claude -p` (Claude Opus 4.7
|
|
100
|
+
**Methodology.** Each arm is `claude -p` (Claude Opus 4.7) run headlessly against the repo with `--strict-mcp-config`: **WITH** = CodeGraph's MCP server enabled, **WITHOUT** = an empty MCP config. Built-in Read/Grep/Bash stay available to both. Same question per repo, **4 runs per arm, median reported**. Cost = the run's `total_cost_usd`; Tokens = total tokens processed (input incl. cached + output); Time = wall-clock; Tool calls = every tool invocation, including those inside any sub-agents the model spawns. Repos cloned at `--depth 1` and indexed by the same CodeGraph build that served them. Re-validated on codegraph **v0.9.4** (2026-05-24); per-repo numbers move run-to-run with how hard the without-arm thrashes (the median-of-4 smooths it, but tails remain — e.g. Tokio's without-arm hit $2.41/3m one batch).
|
|
89
101
|
|
|
90
102
|
**Queries:**
|
|
91
103
|
| Codebase | Query |
|
|
@@ -101,13 +113,13 @@ The gains scale with codebase size: on large repos the agent answers from the in
|
|
|
101
113
|
**Raw medians — WITH → WITHOUT:**
|
|
102
114
|
| Codebase | Cost | Tokens | Time | Tool calls |
|
|
103
115
|
|----------|------|--------|------|------------|
|
|
104
|
-
| VS Code | $0.
|
|
105
|
-
| Excalidraw | $0.
|
|
106
|
-
| Django | $0.
|
|
107
|
-
| Tokio | $0.
|
|
108
|
-
| OkHttp | $0.
|
|
109
|
-
| Gin | $0.
|
|
110
|
-
| Alamofire | $0.61 → $
|
|
116
|
+
| VS Code | $0.60 → $0.80 | 601k → 2.8M | 1m 10s → 2m 26s | 8 → 55 |
|
|
117
|
+
| Excalidraw | $0.43 → $0.90 | 344k → 3.5M | 48s → 2m 58s | 3 → 79 |
|
|
118
|
+
| Django | $0.59 → $0.67 | 739k → 1.2M | 1m 19s → 1m 38s | 9 → 19 |
|
|
119
|
+
| Tokio | $0.42 → $2.41 | 379k → 2.6M | 53s → 3m 2s | 4 → 53 |
|
|
120
|
+
| OkHttp | $0.47 → $0.47 | 636k → 730k | 42s → 1m 1s | 6 → 11 |
|
|
121
|
+
| Gin | $0.37 → $0.47 | 444k → 675k | 44s → 1m 0s | 6 → 10 |
|
|
122
|
+
| Alamofire | $0.61 → $1.14 | 1.0M → 2.8M | 1m 17s → 2m 27s | 12 → 69 |
|
|
111
123
|
|
|
112
124
|
**Why CodeGraph wins:** with the index available, the agent answers directly — `codegraph_context` to map the area, then one `codegraph_explore` for the relevant source — and stops, usually with zero file reads. Without it, the agent (and the Explore sub-agents it spawns) spends most of its budget on discovery (find/ls/grep) before reading the right code. CodeGraph only helps when queried *directly*, so its instructions steer agents to answer directly rather than delegate exploration to file-reading sub-agents — otherwise a sub-agent reads files regardless and CodeGraph becomes overhead.
|
|
113
125
|
|
|
@@ -123,8 +135,9 @@ The gains scale with codebase size: on large repos the agent answers from the in
|
|
|
123
135
|
| **Full-Text Search** | Find code by name instantly across your entire codebase, powered by FTS5 |
|
|
124
136
|
| **Impact Analysis** | Trace callers, callees, and the full impact radius of any symbol before making changes |
|
|
125
137
|
| **Always Fresh** | File watcher uses native OS events (FSEvents/inotify/ReadDirectoryChangesW) with debounced auto-sync — the graph stays current as you code, zero config |
|
|
126
|
-
| **
|
|
138
|
+
| **20+ Languages** | TypeScript, JavaScript, Python, Go, Rust, Java, C#, PHP, Ruby, C, C++, Objective-C, Swift, Kotlin, Dart, Lua, Luau, Svelte, Liquid, Pascal/Delphi |
|
|
127
139
|
| **Framework-aware Routes** | Recognizes web-framework routing files and links URL patterns to their handlers across 14 frameworks |
|
|
140
|
+
| **Mixed iOS / React Native / Expo** | Closes cross-language flows that static parsing misses: Swift ↔ ObjC bridging, React Native legacy bridge + TurboModules + Fabric view components, native → JS event emitters, Expo Modules |
|
|
128
141
|
| **100% Local** | No data leaves your machine. No API keys. No external services. SQLite database only |
|
|
129
142
|
|
|
130
143
|
---
|
|
@@ -152,6 +165,35 @@ CodeGraph detects web-framework routing files and emits `route` nodes linked by
|
|
|
152
165
|
|
|
153
166
|
---
|
|
154
167
|
|
|
168
|
+
## Mixed iOS / React Native / Expo bridging
|
|
169
|
+
|
|
170
|
+
Real iOS and React Native codebases live across multiple languages — a Swift caller invokes an Objective-C selector that's been auto-bridged, a JS file calls into a native module via the React Native bridge, a JSX component delegates to a native view manager. Static tree-sitter extraction stops at each language boundary. CodeGraph bridges them so `trace`, `callers`, `callees`, and `impact` connect end-to-end across the gap.
|
|
171
|
+
|
|
172
|
+
| Boundary | JS / Swift side | Native side | How |
|
|
173
|
+
|---|---|---|---|
|
|
174
|
+
| **Swift → ObjC** | Swift `obj.foo(bar:)` | ObjC selector `-fooWithBar:` | `@objc` auto-bridging rules (including init/property/protocol forms) + Cocoa preposition prefixes (`With`/`For`/`By`/`In`/`On`/`At`/…) |
|
|
175
|
+
| **ObjC → Swift** | ObjC `[obj fooWithBar:]` | Swift `@objc func foo(bar:)` | Reverse-bridge name candidates; verifies `@objc` exposure from source |
|
|
176
|
+
| **React Native legacy bridge** | JS `NativeModules.X.fn(...)` | ObjC `RCT_EXPORT_METHOD` / `RCT_REMAP_METHOD` · Java/Kotlin `@ReactMethod` | Parses macro/annotation declarations to build a JS-name → native-method map |
|
|
177
|
+
| **React Native TurboModules** | JS `import M from './NativeM'; M.fn(...)` | Native impl matching the Codegen spec | Treats the `Native<X>.ts` spec interface as ground truth |
|
|
178
|
+
| **RN native → JS events** | JS `new NativeEventEmitter(...).addListener('e', cb)` | ObjC `[self sendEventWithName:@"e" body:...]` · Swift `sendEvent(withName: "e", ...)` · Java/Kotlin `.emit("e", ...)` | Synthesized cross-language event channel keyed by literal event name |
|
|
179
|
+
| **Expo Modules** | JS `requireNativeModule('X').fn(...)` | Swift / Kotlin `Module { Name("X"); AsyncFunction("fn") { ... } }` | Parses the Expo DSL literals; synthetic method nodes resolve via existing name-match |
|
|
180
|
+
| **Fabric view components** | JSX `<MyView prop={v}/>` | TS Codegen spec + native impl class | Spec → `component` node; convention-based name+suffix lookup (`View`/`ComponentView`/`Manager`/`ViewManager`) bridges to native |
|
|
181
|
+
| **Legacy Paper view managers** | JSX `<MyView prop={v}/>` | ObjC `RCT_EXPORT_VIEW_PROPERTY` · Java/Kotlin `@ReactProp` | Same as Fabric — Paper-era declarations also produce `component` + `property` nodes |
|
|
182
|
+
|
|
183
|
+
**Validated on real codebases** (small + medium + large for each bridge):
|
|
184
|
+
|
|
185
|
+
| Bridge | Small | Medium | Large |
|
|
186
|
+
|---|---|---|---|
|
|
187
|
+
| Swift ↔ ObjC | [Charts](https://github.com/danielgindi/Charts) | [realm-swift](https://github.com/realm/realm-swift) | [Wikipedia-iOS](https://github.com/wikimedia/wikipedia-ios) |
|
|
188
|
+
| RN legacy bridge | [AsyncStorage](https://github.com/react-native-async-storage/async-storage) | [react-native-svg](https://github.com/software-mansion/react-native-svg) | [react-native-firebase](https://github.com/invertase/react-native-firebase) |
|
|
189
|
+
| RN native → JS events | [RNGeolocation](https://github.com/Agontuk/react-native-geolocation-service) | — | react-native-firebase |
|
|
190
|
+
| Expo Modules | expo-haptics | expo-camera | expo SDK sweep (7 packages) |
|
|
191
|
+
| Fabric / Paper views | [react-native-segmented-control](https://github.com/react-native-segmented-control/segmented-control) | [react-native-screens](https://github.com/software-mansion/react-native-screens) | [react-native-skia](https://github.com/Shopify/react-native-skia) |
|
|
192
|
+
|
|
193
|
+
Each bridge emits edges tagged `provenance:'heuristic'` with `metadata.synthesizedBy:` set to a stable channel name (e.g. `swift-objc-bridge`, `rn-event-channel`, `fabric-native-impl`, `expo-module-extract`), so the agent can tell at a glance how a hop got into the graph.
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
155
197
|
## Quick Start
|
|
156
198
|
|
|
157
199
|
### 1. Run the Installer
|
|
@@ -253,25 +295,21 @@ CodeGraph builds a semantic knowledge graph of codebases for faster, smarter cod
|
|
|
253
295
|
|
|
254
296
|
### If `.codegraph/` exists in the project
|
|
255
297
|
|
|
256
|
-
**
|
|
257
|
-
|
|
258
|
-
**When spawning Explore agents**, include this instruction in the prompt:
|
|
259
|
-
|
|
260
|
-
> This project has CodeGraph initialized (.codegraph/ exists). Use `codegraph_explore` as your PRIMARY tool — it returns full source code sections from all relevant files in one call.
|
|
261
|
-
>
|
|
262
|
-
> **Rules:**
|
|
263
|
-
> 1. Follow the explore call budget in the `codegraph_explore` tool description — it scales automatically based on project size.
|
|
264
|
-
> 2. Do NOT re-read files that codegraph_explore already returned source code for. The source sections are complete and authoritative.
|
|
265
|
-
> 3. Only fall back to grep/glob/read for files listed under "Additional relevant files" if you need more detail, or if codegraph returned no results.
|
|
298
|
+
**Answer directly with CodeGraph — don't delegate exploration to a file-reading sub-agent or a grep/read loop.** CodeGraph *is* the pre-built search index; re-deriving its answers with grep + Read repeats work it already did and costs more for the same result. For "how does X work?", architecture, trace, or where-is-X questions, answer in a handful of CodeGraph calls and stop — typically with **zero file reads**. The returned source is complete and authoritative: treat it as already read and do not re-open those files. Reach for raw Read/Grep only to confirm a specific detail CodeGraph didn't cover.
|
|
266
299
|
|
|
267
|
-
**
|
|
300
|
+
**Tool selection by intent:**
|
|
268
301
|
|
|
269
302
|
| Tool | Use For |
|
|
270
303
|
|------|---------|
|
|
271
|
-
| `
|
|
272
|
-
| `
|
|
304
|
+
| `codegraph_context` | Map a task / feature / area first — composes search + node + callers + callees in one call |
|
|
305
|
+
| `codegraph_trace` | "How does X reach Y" — the call path, each hop's body inline (follows dynamic-dispatch hops grep can't) |
|
|
306
|
+
| `codegraph_explore` | Survey several related symbols' source in ONE budget-capped call |
|
|
307
|
+
| `codegraph_search` | Find a symbol by name |
|
|
308
|
+
| `codegraph_callers` / `codegraph_callees` | Walk call flow one hop at a time |
|
|
273
309
|
| `codegraph_impact` | Check what's affected before editing |
|
|
274
|
-
| `codegraph_node` | Get a single symbol's
|
|
310
|
+
| `codegraph_node` | Get a single symbol's source / signature |
|
|
311
|
+
|
|
312
|
+
A direct CodeGraph answer is a handful of calls; a grep/read exploration is dozens.
|
|
275
313
|
|
|
276
314
|
### If `.codegraph/` does NOT exist
|
|
277
315
|
|
|
@@ -287,34 +325,23 @@ At the start of a session, ask the user if they'd like to initialize CodeGraph:
|
|
|
287
325
|
## How It Works
|
|
288
326
|
|
|
289
327
|
```
|
|
290
|
-
┌─────────────────────────────────────────────────────────────────┐
|
|
291
|
-
│ Claude Code │
|
|
292
|
-
│ │
|
|
293
|
-
│ "Implement user authentication" │
|
|
294
|
-
│ │ │
|
|
295
|
-
│ ▼ │
|
|
296
|
-
│ ┌─────────────────┐ ┌─────────────────┐ │
|
|
297
|
-
│ │ Explore Agent │ ──── │ Explore Agent │ │
|
|
298
|
-
│ └────────┬────────┘ └────────┬────────┘ │
|
|
299
|
-
│ │ │ │
|
|
300
|
-
└───────────┼────────────────────────┼─────────────────────────────┘
|
|
301
|
-
│ │
|
|
302
|
-
▼ ▼
|
|
303
328
|
┌───────────────────────────────────────────────────────────────────┐
|
|
304
|
-
│
|
|
305
|
-
│
|
|
306
|
-
│
|
|
307
|
-
│
|
|
308
|
-
│
|
|
309
|
-
|
|
310
|
-
│
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
│
|
|
314
|
-
│
|
|
315
|
-
│
|
|
316
|
-
│
|
|
317
|
-
│
|
|
329
|
+
│ Claude Code │
|
|
330
|
+
│ │
|
|
331
|
+
│ "How does a request reach the database?" │
|
|
332
|
+
│ calls CodeGraph tools directly — no Explore sub-agent │
|
|
333
|
+
│ │ │
|
|
334
|
+
└─────────────────────────────────┬─────────────────────────────────┘
|
|
335
|
+
│
|
|
336
|
+
▼
|
|
337
|
+
┌───────────────────────────────────────────────────────────────────┐
|
|
338
|
+
│ CodeGraph MCP Server │
|
|
339
|
+
│ │
|
|
340
|
+
│ context · trace · explore · callers · callees · impact │
|
|
341
|
+
│ │ │
|
|
342
|
+
│ ▼ │
|
|
343
|
+
│ SQLite knowledge graph │
|
|
344
|
+
│ symbols · edges · files · FTS5 full-text search │
|
|
318
345
|
└───────────────────────────────────────────────────────────────────┘
|
|
319
346
|
```
|
|
320
347
|
|
|
@@ -333,6 +360,7 @@ At the start of a session, ask the user if they'd like to initialize CodeGraph:
|
|
|
333
360
|
```bash
|
|
334
361
|
codegraph # Run interactive installer
|
|
335
362
|
codegraph install # Run installer (explicit)
|
|
363
|
+
codegraph uninstall # Remove CodeGraph from your agents (inverse of install)
|
|
336
364
|
codegraph init [path] # Initialize in a project (--index to also index)
|
|
337
365
|
codegraph uninit [path] # Remove CodeGraph from a project (--force to skip prompt)
|
|
338
366
|
codegraph index [path] # Full index (--force to re-index, --quiet for less output)
|
|
@@ -341,6 +369,9 @@ codegraph status [path] # Show statistics
|
|
|
341
369
|
codegraph query <search> # Search symbols (--kind, --limit, --json)
|
|
342
370
|
codegraph files [path] # Show file structure (--format, --filter, --max-depth, --json)
|
|
343
371
|
codegraph context <task> # Build context for AI (--format, --max-nodes)
|
|
372
|
+
codegraph callers <symbol> # Find what calls a function/method (--limit, --json)
|
|
373
|
+
codegraph callees <symbol> # Find what a function/method calls (--limit, --json)
|
|
374
|
+
codegraph impact <symbol> # Analyze what code is affected by changing a symbol (--depth, --json)
|
|
344
375
|
codegraph affected [files...] # Find test files affected by changes (see below)
|
|
345
376
|
codegraph serve --mcp # Start MCP server
|
|
346
377
|
```
|
|
@@ -383,10 +414,12 @@ When running as an MCP server, CodeGraph exposes these tools to Claude Code:
|
|
|
383
414
|
|------|---------|
|
|
384
415
|
| `codegraph_search` | Find symbols by name across the codebase |
|
|
385
416
|
| `codegraph_context` | Build relevant code context for a task |
|
|
417
|
+
| `codegraph_trace` | Trace the call path between two symbols ("how does X reach Y") in one call — each hop with its body inline, following dynamic-dispatch hops (callbacks, React re-render, interface→impl) that grep can't |
|
|
386
418
|
| `codegraph_callers` | Find what calls a function |
|
|
387
419
|
| `codegraph_callees` | Find what a function calls |
|
|
388
420
|
| `codegraph_impact` | Analyze what code is affected by changing a symbol |
|
|
389
421
|
| `codegraph_node` | Get details about a specific symbol (optionally with source code) |
|
|
422
|
+
| `codegraph_explore` | Return source for several related symbols grouped by file, plus a relationship map, in one call |
|
|
390
423
|
| `codegraph_files` | Get indexed file structure (faster than filesystem scanning) |
|
|
391
424
|
| `codegraph_status` | Check index health and statistics |
|
|
392
425
|
|
|
@@ -418,23 +451,49 @@ cg.close();
|
|
|
418
451
|
|
|
419
452
|
## Configuration
|
|
420
453
|
|
|
421
|
-
There isn't any — CodeGraph is zero-config
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
454
|
+
There isn't any — CodeGraph is zero-config, with **no config file** to write or
|
|
455
|
+
keep in sync. Language support is automatic from the file extension; there's
|
|
456
|
+
nothing to wire up per language.
|
|
457
|
+
|
|
458
|
+
What it skips out of the box:
|
|
459
|
+
|
|
460
|
+
- **Dependency, build, and cache directories** — `node_modules`, `vendor`,
|
|
461
|
+
`dist`, `build`, `target`, `.venv`, `Pods`, `.next`, and the like across every
|
|
462
|
+
[supported stack](#supported-languages) — so the graph is your code, not
|
|
463
|
+
third-party noise. This holds even with no `.gitignore`.
|
|
464
|
+
- **Anything in your `.gitignore`** — honored in git repos via git, and in
|
|
465
|
+
non-git projects by reading `.gitignore` directly (root and nested).
|
|
466
|
+
- **Files larger than 1 MB** — generated bundles, minified JS, vendored blobs.
|
|
467
|
+
|
|
468
|
+
To keep something else out, add it to `.gitignore`. To pull a default-excluded
|
|
469
|
+
directory back **in** (say you really do want a vendored dependency indexed),
|
|
470
|
+
add a negation — `!vendor/`. The defaults apply uniformly, so committing a
|
|
471
|
+
dependency or build directory doesn't force it into the graph; the `.gitignore`
|
|
472
|
+
negation is the explicit opt-in.
|
|
473
|
+
|
|
474
|
+
## Supported Platforms
|
|
475
|
+
|
|
476
|
+
Every release ships a self-contained build (bundled Node runtime — nothing to
|
|
477
|
+
compile) for all three desktop OSes, on both Intel/AMD (x64) and ARM (arm64):
|
|
478
|
+
|
|
479
|
+
| Platform | Architectures | Install |
|
|
480
|
+
|----------|---------------|---------|
|
|
481
|
+
| Windows | x64, arm64 | PowerShell installer or npm |
|
|
482
|
+
| macOS | x64, arm64 | shell installer or npm |
|
|
483
|
+
| Linux | x64, arm64 | shell installer or npm |
|
|
484
|
+
|
|
485
|
+
See [Get Started](#get-started) for the one-line install commands.
|
|
425
486
|
|
|
426
|
-
|
|
487
|
+
## Supported Agents
|
|
427
488
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
- There's no config file to write or keep in sync, and nothing to wire up per
|
|
431
|
-
language: support is automatic from the file extension.
|
|
432
|
-
- Files larger than 1 MB are skipped (generated bundles, minified JS, vendored
|
|
433
|
-
blobs) — they cost parse budget for no useful symbols.
|
|
489
|
+
The interactive installer auto-detects and configures each of these — wiring up
|
|
490
|
+
the MCP server and writing its instructions file:
|
|
434
491
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
492
|
+
- **Claude Code**
|
|
493
|
+
- **Cursor**
|
|
494
|
+
- **Codex CLI**
|
|
495
|
+
- **opencode**
|
|
496
|
+
- **Hermes Agent**
|
|
438
497
|
|
|
439
498
|
## Supported Languages
|
|
440
499
|
|
|
@@ -451,6 +510,7 @@ What that means in practice:
|
|
|
451
510
|
| Ruby | `.rb` | Full support |
|
|
452
511
|
| C | `.c`, `.h` | Full support |
|
|
453
512
|
| C++ | `.cpp`, `.hpp`, `.cc` | Full support |
|
|
513
|
+
| Objective-C | `.m`, `.mm`, `.h` | Partial support (classes, protocols, methods, `@property`, `#import`, message sends; `.mm` ObjC++ may parse incompletely) |
|
|
454
514
|
| Swift | `.swift` | Full support |
|
|
455
515
|
| Kotlin | `.kt`, `.kts` | Full support |
|
|
456
516
|
| Scala | `.scala`, `.sc` | Full support (classes, traits, methods, type aliases, Scala 3 enums) |
|
|
@@ -475,7 +535,7 @@ What that means in practice:
|
|
|
475
535
|
|
|
476
536
|
**MCP server not connecting** — Ensure the project is initialized/indexed, verify the path in your MCP config, and check that `codegraph serve --mcp` works from the command line.
|
|
477
537
|
|
|
478
|
-
**Missing symbols** — The MCP server auto-syncs on save (wait a couple seconds). Run `codegraph sync` manually if needed. Check that the file's language is supported and isn't excluded
|
|
538
|
+
**Missing symbols** — The MCP server auto-syncs on save (wait a couple seconds). Run `codegraph sync` manually if needed. Check that the file's language is supported and isn't inside a `.gitignore`d or default-excluded directory (e.g. `node_modules`, `dist`).
|
|
479
539
|
|
|
480
540
|
## Star History
|
|
481
541
|
|
package/npm-shim.js
CHANGED
|
@@ -11,48 +11,236 @@
|
|
|
11
11
|
// (with node:sqlite), regardless of the user's Node version. The user's Node is
|
|
12
12
|
// only ever a launcher; even an ancient version can run this file.
|
|
13
13
|
//
|
|
14
|
+
// Self-heal (issue #303): some registries — notably the npmmirror/cnpm mirrors,
|
|
15
|
+
// and some corporate proxies — don't reliably mirror the per-platform
|
|
16
|
+
// optionalDependencies. npm treats an unfetchable optional dep as success and
|
|
17
|
+
// silently skips it, so the bundle goes missing and every command fails. When
|
|
18
|
+
// the installed bundle can't be resolved, this shim falls back to downloading
|
|
19
|
+
// the matching bundle straight from GitHub Releases — the very archive
|
|
20
|
+
// install.sh uses — into a cache dir, then runs that. Knobs:
|
|
21
|
+
// CODEGRAPH_NO_DOWNLOAD=1 disable the network fallback (print guidance)
|
|
22
|
+
// CODEGRAPH_INSTALL_DIR=DIR cache location (default: ~/.codegraph)
|
|
23
|
+
// CODEGRAPH_DOWNLOAD_BASE=URL release-download base (for mirrors/air-gapped)
|
|
24
|
+
//
|
|
14
25
|
// Wired up at release time as the main package's `bin`:
|
|
15
|
-
// "bin": { "codegraph": "
|
|
26
|
+
// "bin": { "codegraph": "npm-shim.js" }
|
|
16
27
|
// with the platform packages listed in `optionalDependencies`.
|
|
17
28
|
|
|
18
29
|
var childProcess = require('child_process');
|
|
30
|
+
var fs = require('fs');
|
|
31
|
+
var os = require('os');
|
|
32
|
+
var path = require('path');
|
|
19
33
|
|
|
20
34
|
var target = process.platform + '-' + process.arch; // e.g. darwin-arm64, linux-x64
|
|
21
35
|
var pkg = '@colbymchenry/codegraph-' + target;
|
|
22
36
|
var isWindows = process.platform === 'win32';
|
|
37
|
+
var REPO = 'colbymchenry/codegraph';
|
|
38
|
+
|
|
39
|
+
main().catch(function (e) {
|
|
40
|
+
process.stderr.write('codegraph: ' + (e && e.message ? e.message : String(e)) + '\n');
|
|
41
|
+
process.exit(1);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
async function main() {
|
|
45
|
+
// Happy path: the npm-installed optional dependency. Fall back to a download
|
|
46
|
+
// when the registry didn't deliver it.
|
|
47
|
+
var resolved = resolveInstalledBundle() || (await selfHealBundle());
|
|
48
|
+
var res = childProcess.spawnSync(resolved.command, resolved.args, { stdio: 'inherit' });
|
|
49
|
+
if (res.error) {
|
|
50
|
+
process.stderr.write('codegraph: ' + res.error.message + '\n');
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
process.exit(res.status === null ? 1 : res.status);
|
|
54
|
+
}
|
|
23
55
|
|
|
24
|
-
//
|
|
25
|
-
//
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
56
|
+
// Resolve the launcher from the installed per-platform optionalDependency.
|
|
57
|
+
// Returns {command, args} or null if the package isn't installed.
|
|
58
|
+
function resolveInstalledBundle() {
|
|
59
|
+
try {
|
|
60
|
+
if (isWindows) {
|
|
61
|
+
// Modern Node refuses to spawn the bundle's .cmd directly (EINVAL, the
|
|
62
|
+
// CVE-2024-27980 hardening on Node 24), so invoke the bundled node.exe
|
|
63
|
+
// against the app entry point and pass --liftoff-only here.
|
|
64
|
+
var nodeExe = require.resolve(pkg + '/node.exe');
|
|
65
|
+
var entry = require.resolve(pkg + '/lib/dist/bin/codegraph.js');
|
|
66
|
+
return { command: nodeExe, args: liftoff(entry) };
|
|
67
|
+
}
|
|
68
|
+
return { command: require.resolve(pkg + '/bin/codegraph'), args: process.argv.slice(2) };
|
|
69
|
+
} catch (e) {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Locate the launcher inside an extracted GitHub bundle directory (same
|
|
75
|
+
// node/lib/bin layout as the npm platform package). Returns {command, args} or
|
|
76
|
+
// null when the directory doesn't hold a usable bundle yet.
|
|
77
|
+
function launcherIn(dir) {
|
|
31
78
|
if (isWindows) {
|
|
32
|
-
|
|
33
|
-
var entry =
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
args = ['--liftoff-only', entry].concat(process.argv.slice(2));
|
|
79
|
+
var nodeExe = path.join(dir, 'node.exe');
|
|
80
|
+
var entry = path.join(dir, 'lib', 'dist', 'bin', 'codegraph.js');
|
|
81
|
+
if (fs.existsSync(nodeExe) && fs.existsSync(entry)) {
|
|
82
|
+
return { command: nodeExe, args: liftoff(entry) };
|
|
83
|
+
}
|
|
38
84
|
} else {
|
|
39
|
-
|
|
40
|
-
args
|
|
85
|
+
var launcher = path.join(dir, 'bin', 'codegraph');
|
|
86
|
+
if (fs.existsSync(launcher)) return { command: launcher, args: process.argv.slice(2) };
|
|
87
|
+
}
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// --liftoff-only keeps tree-sitter's WASM grammars off V8's turboshaft tier to
|
|
92
|
+
// avoid the Zone OOM on Node >= 22 (issues #293/#298). The unix bin/codegraph
|
|
93
|
+
// launcher already passes it; on Windows we invoke node.exe directly so add it.
|
|
94
|
+
function liftoff(entry) {
|
|
95
|
+
return ['--liftoff-only', entry].concat(process.argv.slice(2));
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Download + cache the platform bundle from GitHub Releases. Returns
|
|
99
|
+
// {command, args}; exits the process with guidance if it can't.
|
|
100
|
+
async function selfHealBundle() {
|
|
101
|
+
var version = readVersion();
|
|
102
|
+
var bundlesDir = path.join(process.env.CODEGRAPH_INSTALL_DIR || path.join(os.homedir(), '.codegraph'), 'bundles');
|
|
103
|
+
var dest = path.join(bundlesDir, target + '-' + version);
|
|
104
|
+
|
|
105
|
+
// Already downloaded by a previous run? Use it even when downloads are
|
|
106
|
+
// disabled — CODEGRAPH_NO_DOWNLOAD blocks fetching, not a cached bundle.
|
|
107
|
+
var cached = launcherIn(dest);
|
|
108
|
+
if (cached) return cached;
|
|
109
|
+
|
|
110
|
+
if (process.env.CODEGRAPH_NO_DOWNLOAD) {
|
|
111
|
+
fail('the network fallback is disabled (CODEGRAPH_NO_DOWNLOAD is set).');
|
|
41
112
|
}
|
|
42
|
-
|
|
113
|
+
|
|
114
|
+
var asset = 'codegraph-' + target + (isWindows ? '.zip' : '.tar.gz');
|
|
115
|
+
var base = process.env.CODEGRAPH_DOWNLOAD_BASE || ('https://github.com/' + REPO + '/releases/download');
|
|
116
|
+
var url = base + '/v' + version + '/' + asset;
|
|
117
|
+
|
|
43
118
|
process.stderr.write(
|
|
44
|
-
'codegraph:
|
|
45
|
-
'
|
|
46
|
-
'Try reinstalling: npm i -g @colbymchenry/codegraph\n' +
|
|
47
|
-
'Or use the standalone installer (no Node required):\n' +
|
|
48
|
-
' curl -fsSL https://raw.githubusercontent.com/colbymchenry/codegraph/main/install.sh | sh\n'
|
|
119
|
+
'codegraph: platform bundle missing (registry did not provide ' + pkg + ').\n' +
|
|
120
|
+
'codegraph: downloading ' + asset + ' from GitHub Releases (' + version + ')...\n'
|
|
49
121
|
);
|
|
50
|
-
|
|
122
|
+
|
|
123
|
+
// Stage inside bundlesDir so the final rename is on the same filesystem (atomic,
|
|
124
|
+
// no EXDEV across tmpfs). Strip the archive's top-level codegraph-<target>/ dir.
|
|
125
|
+
fs.mkdirSync(bundlesDir, { recursive: true });
|
|
126
|
+
var stage = fs.mkdtempSync(path.join(bundlesDir, '.dl-'));
|
|
127
|
+
try {
|
|
128
|
+
var archivePath = path.join(stage, asset);
|
|
129
|
+
await download(url, archivePath, 6);
|
|
130
|
+
await verifyChecksum(archivePath, asset, base, version);
|
|
131
|
+
var extracted = path.join(stage, 'bundle');
|
|
132
|
+
fs.mkdirSync(extracted);
|
|
133
|
+
extract(archivePath, extracted);
|
|
134
|
+
|
|
135
|
+
var raced = launcherIn(dest); // another process may have finished meanwhile
|
|
136
|
+
if (raced) { rmrf(stage); return raced; }
|
|
137
|
+
try {
|
|
138
|
+
fs.renameSync(extracted, dest);
|
|
139
|
+
} catch (e) {
|
|
140
|
+
var other = launcherIn(dest); // lost the race but theirs is valid
|
|
141
|
+
if (other) { rmrf(stage); return other; }
|
|
142
|
+
throw e;
|
|
143
|
+
}
|
|
144
|
+
} catch (e) {
|
|
145
|
+
rmrf(stage);
|
|
146
|
+
fail('download failed (' + e.message + ').\n URL: ' + url);
|
|
147
|
+
}
|
|
148
|
+
rmrf(stage);
|
|
149
|
+
|
|
150
|
+
var ready = launcherIn(dest);
|
|
151
|
+
if (!ready) fail('downloaded bundle is missing its launcher under ' + dest + '.');
|
|
152
|
+
process.stderr.write('codegraph: bundle ready.\n');
|
|
153
|
+
return ready;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function readVersion() {
|
|
157
|
+
try {
|
|
158
|
+
return require(path.join(__dirname, 'package.json')).version;
|
|
159
|
+
} catch (e) {
|
|
160
|
+
fail('could not read this package\'s version to locate a matching release.');
|
|
161
|
+
}
|
|
51
162
|
}
|
|
52
163
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
164
|
+
// GET with manual redirect following (GitHub release URLs redirect to a CDN).
|
|
165
|
+
function download(url, dest, redirectsLeft) {
|
|
166
|
+
return new Promise(function (resolve, reject) {
|
|
167
|
+
var https = require('https');
|
|
168
|
+
// timeout is an idle/inactivity timeout — it won't kill a slow-but-progressing
|
|
169
|
+
// download, only a stalled connection (so a blocked mirror fails fast with
|
|
170
|
+
// guidance instead of hanging the user's command forever).
|
|
171
|
+
var req = https.get(url, { headers: { 'User-Agent': 'codegraph-npm-shim' }, timeout: 30000 }, function (res) {
|
|
172
|
+
var status = res.statusCode;
|
|
173
|
+
if (status >= 300 && status < 400 && res.headers.location) {
|
|
174
|
+
res.resume();
|
|
175
|
+
if (redirectsLeft <= 0) { reject(new Error('too many redirects')); return; }
|
|
176
|
+
download(new URL(res.headers.location, url).toString(), dest, redirectsLeft - 1).then(resolve, reject);
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
if (status !== 200) { res.resume(); reject(new Error('HTTP ' + status)); return; }
|
|
180
|
+
var file = fs.createWriteStream(dest);
|
|
181
|
+
res.on('error', reject);
|
|
182
|
+
res.pipe(file);
|
|
183
|
+
file.on('error', reject);
|
|
184
|
+
file.on('finish', function () { file.close(function () { resolve(); }); });
|
|
185
|
+
});
|
|
186
|
+
req.on('timeout', function () { req.destroy(new Error('connection timed out')); });
|
|
187
|
+
req.on('error', reject);
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Best-effort integrity check. When the release publishes a SHA256SUMS file, the
|
|
192
|
+
// downloaded archive MUST match its listed hash or we abort. When that file is
|
|
193
|
+
// absent (older releases) or simply unreachable, we proceed — the archive still
|
|
194
|
+
// arrived from GitHub over TLS. So tampering/corruption is caught, while a
|
|
195
|
+
// missing checksum never breaks an install.
|
|
196
|
+
async function verifyChecksum(archivePath, asset, base, version) {
|
|
197
|
+
var sumsPath = archivePath + '.SHA256SUMS';
|
|
198
|
+
try {
|
|
199
|
+
await download(base + '/v' + version + '/SHA256SUMS', sumsPath, 6);
|
|
200
|
+
} catch (e) {
|
|
201
|
+
return; // not published / unreachable → skip
|
|
202
|
+
}
|
|
203
|
+
var expected = null;
|
|
204
|
+
var lines = fs.readFileSync(sumsPath, 'utf8').split('\n');
|
|
205
|
+
for (var i = 0; i < lines.length; i++) {
|
|
206
|
+
var m = lines[i].trim().match(/^([0-9a-fA-F]{64})\s+\*?(.+)$/);
|
|
207
|
+
if (m && path.basename(m[2].trim()) === asset) { expected = m[1].toLowerCase(); break; }
|
|
208
|
+
}
|
|
209
|
+
if (!expected) return; // asset not listed → nothing to check
|
|
210
|
+
var actual = require('crypto').createHash('sha256').update(fs.readFileSync(archivePath)).digest('hex');
|
|
211
|
+
if (actual !== expected) {
|
|
212
|
+
throw new Error('checksum mismatch for ' + asset +
|
|
213
|
+
' (expected ' + expected.slice(0, 12) + '…, got ' + actual.slice(0, 12) + '…)');
|
|
214
|
+
}
|
|
215
|
+
process.stderr.write('codegraph: checksum verified.\n');
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Extract via the system tar — present on macOS, Linux, and Windows 10+
|
|
219
|
+
// (bsdtar reads .zip too). No third-party dependency in the shim.
|
|
220
|
+
function extract(archive, destDir) {
|
|
221
|
+
var args = isWindows
|
|
222
|
+
? ['-xf', archive, '-C', destDir, '--strip-components=1']
|
|
223
|
+
: ['-xzf', archive, '-C', destDir, '--strip-components=1'];
|
|
224
|
+
var res = childProcess.spawnSync('tar', args, { stdio: 'ignore' });
|
|
225
|
+
if (res.error) throw new Error('tar unavailable: ' + res.error.message);
|
|
226
|
+
if (res.status !== 0) throw new Error('tar exited ' + res.status);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function rmrf(p) {
|
|
230
|
+
try { fs.rmSync(p, { recursive: true, force: true }); } catch (e) { /* best effort */ }
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function fail(reason) {
|
|
234
|
+
process.stderr.write(
|
|
235
|
+
'codegraph: no prebuilt bundle for ' + target + '.\n' +
|
|
236
|
+
(reason ? 'codegraph: ' + reason + '\n' : '') +
|
|
237
|
+
'Expected the optional package ' + pkg + ' to be installed.\n' +
|
|
238
|
+
'A registry mirror (e.g. npmmirror/cnpm) that did not mirror the per-platform\n' +
|
|
239
|
+
'package is the usual cause. Fixes:\n' +
|
|
240
|
+
' - install from the official registry:\n' +
|
|
241
|
+
' npm i -g @colbymchenry/codegraph --registry=https://registry.npmjs.org\n' +
|
|
242
|
+
' - or use the standalone installer (no Node required):\n' +
|
|
243
|
+
' curl -fsSL https://raw.githubusercontent.com/' + REPO + '/main/install.sh | sh\n'
|
|
244
|
+
);
|
|
56
245
|
process.exit(1);
|
|
57
246
|
}
|
|
58
|
-
process.exit(res.status === null ? 1 : res.status);
|
package/package.json
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@colbymchenry/codegraph",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.5",
|
|
4
4
|
"description": "Local-first code intelligence for AI agents (MCP). Self-contained — bundles its own runtime.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"codegraph": "npm-shim.js"
|
|
7
7
|
},
|
|
8
8
|
"optionalDependencies": {
|
|
9
|
-
"@colbymchenry/codegraph-darwin-arm64": "0.9.
|
|
10
|
-
"@colbymchenry/codegraph-darwin-x64": "0.9.
|
|
11
|
-
"@colbymchenry/codegraph-linux-arm64": "0.9.
|
|
12
|
-
"@colbymchenry/codegraph-linux-x64": "0.9.
|
|
13
|
-
"@colbymchenry/codegraph-win32-arm64": "0.9.
|
|
14
|
-
"@colbymchenry/codegraph-win32-x64": "0.9.
|
|
9
|
+
"@colbymchenry/codegraph-darwin-arm64": "0.9.5",
|
|
10
|
+
"@colbymchenry/codegraph-darwin-x64": "0.9.5",
|
|
11
|
+
"@colbymchenry/codegraph-linux-arm64": "0.9.5",
|
|
12
|
+
"@colbymchenry/codegraph-linux-x64": "0.9.5",
|
|
13
|
+
"@colbymchenry/codegraph-win32-arm64": "0.9.5",
|
|
14
|
+
"@colbymchenry/codegraph-win32-x64": "0.9.5"
|
|
15
15
|
},
|
|
16
16
|
"files": [
|
|
17
17
|
"npm-shim.js",
|