graph-indexer 3.0.0 → 3.1.0
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/CHANGELOG.md +456 -0
- package/README.md +72 -15
- package/SECURITY.md +21 -4
- package/bin/graph-indexer.mjs +46 -5
- package/integrations/claude-code/.claude-plugin/plugin.json +3 -2
- package/integrations/claude-code/agents/code-structure.md +0 -3
- package/integrations/claude-code/skills/setup/SKILL.md +26 -0
- package/integrations/claude-code-local/.claude-plugin/plugin.json +9 -0
- package/integrations/{claude-code → claude-code-local}/.mcp.json +1 -1
- package/integrations/claude-code-local/README.md +10 -0
- package/integrations/claude-code-local/agents/code-structure.md +22 -0
- package/integrations/{claude-code → claude-code-local}/hooks/gi-hook.mjs +12 -6
- package/integrations/opencode/graph-indexer.js +13 -8
- package/package.json +2 -2
- package/src/cli/helper.mjs +15 -1
- package/src/cli/hook.mjs +140 -48
- package/src/cli/init.mjs +49 -17
- package/src/cli/resident.mjs +15 -5
- package/src/index/discover.mjs +3 -5
- package/src/index/indexer.mjs +77 -41
- package/src/index/modules.mjs +82 -7
- package/src/index/parse-worker.mjs +12 -0
- package/src/index/parsepool.mjs +73 -0
- package/src/index/resolver.mjs +371 -25
- package/src/mcp/server.mjs +16 -12
- package/src/mcp/tools.mjs +419 -26
- package/src/parse/arity.mjs +41 -3
- package/src/parse/extract.mjs +277 -46
- package/src/parse/lang/ecma.mjs +2 -0
- package/src/parse/lang/go.mjs +2 -0
- package/src/parse/lang/jvm.mjs +33 -0
- package/src/parse/lang/markdown.mjs +92 -0
- package/src/parse/lang/python.mjs +43 -0
- package/src/parse/lang/rust.mjs +215 -15
- package/src/parse/lang/sfc.mjs +125 -0
- package/src/parse/languages.mjs +9 -2
- package/src/query/check.mjs +55 -17
- package/src/query/facts.mjs +3 -2
- package/src/query/injection-spring.mjs +405 -0
- package/src/query/injection.mjs +173 -0
- package/src/query/intel.mjs +87 -22
- package/src/query/issue.mjs +156 -0
- package/src/query/orm.mjs +286 -0
- package/src/query/read.mjs +6 -1
- package/src/query/report.mjs +53 -0
- package/src/query/routes.mjs +142 -0
- package/src/query/showcase.mjs +59 -0
- package/src/query/testcmd.mjs +25 -14
- package/src/query/textsearch.mjs +29 -11
- package/src/search/search.mjs +85 -17
- package/src/search/semantic.mjs +201 -0
- package/src/search/vectors.mjs +122 -0
- package/src/store/db.mjs +22 -4
- package/src/util/git.mjs +30 -0
- package/src/util/paths.mjs +40 -1
- package/src/util/regex.mjs +2 -0
- package/vendor/grammars/README.md +47 -0
- package/vendor/grammars/kotlin.wasm.gz +0 -0
- package/vendor/grammars/manifest.json +4 -4
- /package/integrations/{claude-code → claude-code-local}/hooks/hooks.json +0 -0
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
<a href="https://www.npmjs.com/package/graph-indexer"><img src="https://img.shields.io/npm/v/graph-indexer?color=007acc&style=for-the-badge" alt="npm version"></a>
|
|
11
11
|
<a href="https://www.npmjs.com/package/graph-indexer"><img src="https://img.shields.io/npm/dt/graph-indexer?color=4caf50&style=for-the-badge" alt="NPM Downloads"></a>
|
|
12
|
-
<a href="https://nodejs.org"><img src="https://img.shields.io/badge/Node.js
|
|
12
|
+
<a href="https://nodejs.org"><img src="https://img.shields.io/badge/Node.js-22.23.4%2B%20%7C%2024.22.1%2B%20%7C%2026.12.1%2B-brightgreen?style=for-the-badge&logo=nodedotjs" alt="Node.js 22.23.4+, 24.22.1+ or 26.12.1+"></a>
|
|
13
13
|
<a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-yellow.svg?style=for-the-badge" alt="License: MIT"></a>
|
|
14
14
|
</div>
|
|
15
15
|
|
|
@@ -40,14 +40,18 @@ instead of reading whole files and grepping.
|
|
|
40
40
|
|
|
41
41
|
## Quick start
|
|
42
42
|
|
|
43
|
-
Requires **Node.js 22.
|
|
43
|
+
Requires **Node.js 22.23.4+, 24.22.1+ or 26.12.1+** (the built-in `node:sqlite`, and the October 2026
|
|
44
|
+
Node.js security releases, which fix CVE-2026-111497).
|
|
44
45
|
|
|
45
46
|
```bash
|
|
46
47
|
cd your-repo
|
|
47
|
-
npx graph-indexer init
|
|
48
|
-
npx graph-indexer index
|
|
48
|
+
npx -y --prefix / graph-indexer@3 init # wires the MCP server into the agents it detects
|
|
49
|
+
npx -y --prefix / graph-indexer@3 index # optional: build the index now (the server also does it on start)
|
|
49
50
|
```
|
|
50
51
|
|
|
52
|
+
`--prefix /` makes npx run the published package even in a repository that ships its own
|
|
53
|
+
`node_modules/graph-indexer`.
|
|
54
|
+
|
|
51
55
|
`init` writes project-level MCP configuration for the agents it finds (`.mcp.json` for Claude
|
|
52
56
|
Code, `.cursor/mcp.json`, `.vscode/mcp.json`, `.gemini/settings.json`, `opencode.json` for
|
|
53
57
|
OpenCode and Kilo Code, `.junie/mcp/mcp.json`, `.zed/settings.json`; it prints the Codex TOML
|
|
@@ -56,12 +60,18 @@ to `.gitignore`, and adds a short block to `CLAUDE.md` / `AGENTS.md` on how to l
|
|
|
56
60
|
For Claude Code it also writes a structural helper, `.claude/agents/code-structure.md`: a
|
|
57
61
|
read-only sub-agent on a small model that answers call-site, caller, subclass and impact
|
|
58
62
|
questions from the index, so the main agent can hand them off and get back only the list (the
|
|
59
|
-
Claude Code plugin ships it too). `--hooks` adds the Claude Code hooks to `.claude/settings.json`,
|
|
63
|
+
Claude Code plugin ships it too (`integrations/claude-code`; the full plugin with hooks is `integrations/claude-code-local`)). `--hooks` adds the Claude Code hooks to `.claude/settings.json`,
|
|
60
64
|
which Devin, Copilot CLI and Cursor also run, and the OpenCode / Kilo Code plugin that appends the
|
|
61
65
|
same context to their tool output. Flags: `--agents claude,cursor,vscode,gemini,codex,opencode,junie,zed,devin`,
|
|
62
66
|
`--all`, `--hooks`, `--local` (use this checkout instead of `npx`), `--dry-run`,
|
|
63
67
|
`--no-instructions`, `--no-helper`.
|
|
64
68
|
|
|
69
|
+
In Claude Code (2.1.275 or later), the plugin installs in one step:
|
|
70
|
+
|
|
71
|
+
```text
|
|
72
|
+
/plugin install graph-indexer --marketplace MaquinaTech/graph-indexer
|
|
73
|
+
```
|
|
74
|
+
|
|
65
75
|
<details>
|
|
66
76
|
<summary>Manual configuration</summary>
|
|
67
77
|
|
|
@@ -70,7 +80,7 @@ Any MCP client can run the server over stdio:
|
|
|
70
80
|
```json
|
|
71
81
|
{
|
|
72
82
|
"mcpServers": {
|
|
73
|
-
"graph-indexer": { "command": "npx", "args": ["-y", "graph-indexer@3", "serve", "--repo", "/path/to/repo"] }
|
|
83
|
+
"graph-indexer": { "command": "npx", "args": ["-y", "--prefix", "/", "graph-indexer@3", "serve", "--repo", "/path/to/repo"] }
|
|
74
84
|
}
|
|
75
85
|
}
|
|
76
86
|
```
|
|
@@ -81,7 +91,7 @@ VS Code uses `"servers"` instead of `"mcpServers"` and `"type": "stdio"`. Codex
|
|
|
81
91
|
```toml
|
|
82
92
|
[mcp_servers.graph-indexer]
|
|
83
93
|
command = "npx"
|
|
84
|
-
args = ["-y", "graph-indexer@3", "serve", "--repo", "/path/to/repo"]
|
|
94
|
+
args = ["-y", "--prefix", "/", "graph-indexer@3", "serve", "--repo", "/path/to/repo"]
|
|
85
95
|
```
|
|
86
96
|
|
|
87
97
|
Without `--repo` the server indexes the git repository containing its working directory
|
|
@@ -90,11 +100,19 @@ Without `--repo` the server indexes the git repository containing its working di
|
|
|
90
100
|
|
|
91
101
|
## The tools
|
|
92
102
|
|
|
103
|
+
`npx graph-indexer init` ends with an example from your own repository: a method that shares its
|
|
104
|
+
name with other definitions, what grep returns for that name, and the calls the index binds to
|
|
105
|
+
that one definition (`npx graph-indexer demo` shows it again).
|
|
106
|
+
|
|
93
107
|
| Tool | Use it to | Typical reply |
|
|
94
108
|
|---|---|---|
|
|
109
|
+
| `explore` | start a task or bug in code you have not read, in one call | the most relevant definitions with their code, who calls them, overrides and subclasses, where the names they use are defined and the tests to run with the command — inside a token budget, naming what it left out |
|
|
110
|
+
| `explore` + routes | ask about an endpoint ("what happens on POST /owners/42/edit") | the handler the framework registers for it — FastAPI, Flask, NestJS, Spring, Express/Fastify/Koa/Hono, Gin/Echo/chi; `npx graph-indexer routes` lists them all |
|
|
111
|
+
| dependency injection | look up a NestJS or Spring class, interface or token (`find_references`, `explore`) | what an injection point receives — NestJS: the `provide: TOKEN, useClass/useFactory/…` or `ClientsModule` registration of an `@Inject(TOKEN)`; Spring: the bean of an `@Autowired`/constructor/`@Bean` parameter, chosen by `@Qualifier`, `@Primary` and name, Feign, Spring Data and MyBatis proxies named as such — and where a provider is injected, which neither the type checker nor grep for the type connects; `npx graph-indexer inject` lists them all |
|
|
112
|
+
| ORM models | look up a model (`find_references`, `explore`; `npx graph-indexer models`) | its table, the migrations that touch it, and for Prisma the client calls (`prisma.user.findMany`) that only the generated client types — Prisma, Django, SQLAlchemy/SQLModel, TypeORM |
|
|
95
113
|
| `read_code` | read symbols, line ranges or files — several per call — without searching for what they use | code with line numbers, then where each name it uses is defined (file:line and signature); who uses a symbol and which tests reference it; a long file's outline |
|
|
96
114
|
| `search_text` | grep any text across the repository (code, config, docs) | grep lines with the enclosing definition and, for identifiers, the definition each match refers to; where the definition is when the search missed it |
|
|
97
|
-
| `search_code` | find where a behaviour or identifier lives (natural language or names) | ranked symbols with location, signature, doc line and matching lines |
|
|
115
|
+
| `search_code` | find where a behaviour or identifier lives (natural language or names); `queries: [...]` asks for several phrasings at once | ranked symbols with location, signature, doc line and matching lines; for a question about docs, design or the reason for something, the Markdown sections that answer it |
|
|
98
116
|
| `find_references` | see every use before renaming or changing a signature | uses grouped by file, enclosing function, confidence, calls through base types marked |
|
|
99
117
|
| `call_graph` | trace callers and callees across files | a tree with locations and confidence |
|
|
100
118
|
| `change_impact` | get the blast radius of a change, or of your uncommitted diff | transitive dependents by distance, tests to run, co-changed files, risk |
|
|
@@ -132,6 +150,12 @@ often changed together (git): context.go (14/42), context_test.go (10/42), …
|
|
|
132
150
|
risk: medium (9 dependents, 1 test files)
|
|
133
151
|
```
|
|
134
152
|
|
|
153
|
+
**Search by meaning (optional).** `npx graph-indexer semantic install` downloads a 32 MB static code
|
|
154
|
+
embedding model (potion-code-16M-v2, MIT, pinned and checksummed); with `GRAPH_INDEXER_SEMANTIC=1`
|
|
155
|
+
search also ranks definitions by meaning, in plain JavaScript and without network access. It helps
|
|
156
|
+
queries written in words (top-5 success 0.674 → 0.728) and not the localization of real commits,
|
|
157
|
+
so it is off by default ([measurements](docs/BENCHMARKS.md#3-symbol-search)).
|
|
158
|
+
|
|
135
159
|
Every reference carries a confidence: **exact/high** means it was bound through a scope,
|
|
136
160
|
import or inferred receiver type; **likely/possible** means only the name matched. Footers
|
|
137
161
|
report same-name call sites that could not be bound, so an agent knows when to double-check
|
|
@@ -157,8 +181,10 @@ own `findReferences` (import lines and declarations excluded):
|
|
|
157
181
|
do not. Against the compiler's full rename set (sibling overrides included) recall is 0.925. The
|
|
158
182
|
fixture has no `node_modules`, so library-typed values are invisible to the compiler: precision is
|
|
159
183
|
a lower bound.* The same measurement against the Go type checker (gin, caddy, nats-server) gives
|
|
160
|
-
precision 0.992–1.000 and recall 0.972–0.981,
|
|
161
|
-
jsoup, Commons Collections) 0.
|
|
184
|
+
precision 0.992–1.000 and recall 0.972–0.981, against the Java compiler (spring-petclinic,
|
|
185
|
+
jsoup, Commons Collections) 0.961–0.994 and 0.883–0.989, against pyright (six Python
|
|
186
|
+
repositories) 0.895–0.998 and 0.927–0.998, and against rust-analyzer (serde_json, ripgrep, tokio)
|
|
187
|
+
0.968–0.990 and 0.799–0.942; grep's precision there is 0.01–0.61.
|
|
162
188
|
In the server of Twenty, an open-source CRM monorepo (28,512 files), precision is 0.999 and
|
|
163
189
|
recall 0.924; grep's precision is 0.009 and 2.x's 0.052.
|
|
164
190
|
|
|
@@ -168,7 +194,7 @@ rolled back to the parent commit first.
|
|
|
168
194
|
|
|
169
195
|
| | file Acc@1 | file Acc@5 | function Acc@5 | function MRR@10 |
|
|
170
196
|
|---|---|---|---|---|
|
|
171
|
-
| graph-indexer `search_code` | **0.
|
|
197
|
+
| graph-indexer `search_code` | **0.550** | **0.799** | **0.528** | **0.433** |
|
|
172
198
|
| grep, files ranked by idf-weighted hits | 0.485 | 0.769 | 0.514 | 0.374 |
|
|
173
199
|
| BM25 over the same index | 0.373 | 0.722 | 0.401 | 0.290 |
|
|
174
200
|
|
|
@@ -176,6 +202,14 @@ rolled back to the parent commit first.
|
|
|
176
202
|
strictly at symbol level: rank-1 0.703 and MRR 0.760, against 0.552 and 0.646 for graph-indexer
|
|
177
203
|
2.x on the same queries. On the 169 held-out queries (never used for tuning) MRR is 0.773 vs 0.632.
|
|
178
204
|
|
|
205
|
+
**Against zvec-grep 0.3.0** (hybrid BM25 + vector search, with its default embedding model,
|
|
206
|
+
Qwen3-Embedding-0.6B, run on the same Mac): on the same 377 queries its best setting, code only,
|
|
207
|
+
reaches rank-1 0.581 and MRR 0.639 (graph-indexer 0.703 and 0.761); on the 169 commits it puts
|
|
208
|
+
the changed file first in 0.420 of them (graph-indexer 0.550, ranked grep 0.485). It is ahead on
|
|
209
|
+
two repositories, gin and fastapi. It has no references or callers. In a one-repetition pilot of
|
|
210
|
+
agent sessions (Haiku 5.5, 24 tasks, 13 of them from zvec-grep's own benchmark) neither tool beat
|
|
211
|
+
Claude Code alone. [Details](docs/BENCHMARKS.md#zvec-grep-030-2026-11-06).
|
|
212
|
+
|
|
179
213
|
**Against 2.x.** In real Claude Code sessions on nestjs and Twenty (40 tasks, 120 runs), 3.0
|
|
180
214
|
cost 0.43 of 2.x on code questions and 0.46 on refactors, with a quarter to a third of the
|
|
181
215
|
tokens spent looking code up; 2.x cost about twice as much as no index, as its tools and prompt
|
|
@@ -236,9 +270,32 @@ files are re-parsed. Warm tool calls take 1–4 ms for the graph tools, about 30
|
|
|
236
270
|
|
|
237
271
|
## Languages
|
|
238
272
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
273
|
+
Every language gets definitions, outlines, search and edit checks. How far references are bound
|
|
274
|
+
differs, and is stated rather than implied:
|
|
275
|
+
|
|
276
|
+
| Level | Languages | What it means |
|
|
277
|
+
|---|---|---|
|
|
278
|
+
| **Verified** | TypeScript/TSX, Go, Java, Python, Rust | references measured against the language's own compiler or type checker on three or more real repositories ([docs/BENCHMARKS.md](docs/BENCHMARKS.md)): TypeScript 0.997 precision / 0.962 recall (nestjs), Go ≥ 0.992 / ≥ 0.972 (gin, caddy, nats-server), Java 0.961–0.994 / 0.883–0.989 (spring-petclinic, jsoup, Commons Collections), Python 0.895–0.998 / 0.927–0.998 against pyright (fastapi, flask, requests, httpx, rich, click), Rust 0.974–0.990 / 0.838–0.947 against rust-analyzer (serde_json, ripgrep, tokio) |
|
|
279
|
+
| **Resolved** | JavaScript, C#, Vue and Svelte components (`<script>` as TypeScript; template tags as uses of the components imported) | scopes, imports and receiver types inferred from declarations, annotations and constructors; no compiler oracle yet |
|
|
280
|
+
| **Scoped** | Kotlin, Scala, C, C++, Ruby, PHP | scopes, imports/includes and packages; less type inference, so more calls stay unbound or by name |
|
|
281
|
+
| **Symbols** | Bash, CSS/SCSS/Less | definitions, outlines and search; references by name |
|
|
282
|
+
|
|
283
|
+
Each answer marks how a reference was bound (`exact`, `likely`, `possible`) and says when a list is
|
|
284
|
+
complete, whatever the level.
|
|
285
|
+
|
|
286
|
+
How much of the code the parser reads, measured on public repositories (68,000 files in 46
|
|
287
|
+
repositories: the fixtures, the agent benchmark's repositories, okhttp, kotlinx.coroutines,
|
|
288
|
+
nowinandroid, Exposed, spring-boot, cats, Play and Jellyfin):
|
|
289
|
+
|
|
290
|
+
| | files with a region the parser cannot read |
|
|
291
|
+
|---|---|
|
|
292
|
+
| TypeScript, TSX, JavaScript, Python, Java, Kotlin, Scala, C#, Rust, Go, Ruby, PHP | 0–0.2% (test data with deliberate syntax errors, `typeof import()` in type arguments, Python 3.15 unpacking comprehensions) |
|
|
293
|
+
| Bash | 4% (zsh completion scripts) |
|
|
294
|
+
| C, C++ | 39% of files, 15% of lines: macros inside declarations (`void (CJSON_CDECL *fn)(…)`); the functions themselves are kept |
|
|
295
|
+
| SCSS, Less | about half of the files: they are read with the CSS grammar, which does not know nesting, `#{…}` or mixins |
|
|
296
|
+
|
|
297
|
+
Where a symbol's name occurs in code the parser could not read, `find_references` and
|
|
298
|
+
`call_graph` say so and name the lines, instead of reporting the list as complete.
|
|
242
299
|
|
|
243
300
|
## Command line
|
|
244
301
|
|
|
@@ -276,7 +333,7 @@ Version 3 is a rewrite; see the [changelog](CHANGELOG.md). In short: the tools c
|
|
|
276
333
|
`check_changes`, `outline`), the
|
|
277
334
|
embedding, Ollama, reranker and daemon options are gone (search is lexical plus graph and needs
|
|
278
335
|
no model), grammars are bundled instead of installed on first use, and Node.js 22.5+ is required.
|
|
279
|
-
Re-run `npx graph-indexer init` to update agent configuration; old `.graph-indexer/` contents are
|
|
336
|
+
Re-run `npx -y --prefix / graph-indexer@3 init` to update agent configuration; old `.graph-indexer/` contents are
|
|
280
337
|
replaced automatically.
|
|
281
338
|
|
|
282
339
|
## License
|
package/SECURITY.md
CHANGED
|
@@ -33,13 +33,24 @@ graph-indexer is a local developer tool. Its threat model follows from a few del
|
|
|
33
33
|
(`node:sqlite`, `node:fs`, `node:child_process`, …) and the vendored WebAssembly runtime.
|
|
34
34
|
- **No code execution.** Source files are parsed into syntax trees; the indexed code is never
|
|
35
35
|
imported, evaluated or run. The only subprocesses are read-only git commands
|
|
36
|
-
(`git rev-parse`, `git ls-files`, `git diff`, `git log`) run in the indexed repository
|
|
37
|
-
fixed arguments
|
|
36
|
+
(`git rev-parse`, `git ls-files`, `git diff`, `git log`, `git show`) run in the indexed repository
|
|
37
|
+
with fixed arguments. The only tool input they take is the `base` revision of `check_changes`,
|
|
38
|
+
which is refused when git could read it as an option. Every git call switches off what the
|
|
39
|
+
repository's own git config could run: `core.fsmonitor`, hooks, signature verification,
|
|
40
|
+
clean/smudge/process filters, `diff.external` and textconv drivers.
|
|
41
|
+
- **No code from the repository through npx.** Every `npx` command graph-indexer runs or writes into
|
|
42
|
+
an agent's configuration passes `--prefix`, so a `node_modules/graph-indexer` that the repository
|
|
43
|
+
ships is never run in place of the published package.
|
|
38
44
|
- **Stays inside the repository.** Only files listed by git (or found by a `.gitignore`-aware
|
|
39
45
|
walk) under the repository root are indexed. Symlinks are followed only when their target
|
|
40
46
|
resolves inside the repository, so a link to a file elsewhere on the machine is never read,
|
|
41
47
|
indexed or served. Paths given to the tools (`read_code "file.ts:42"`, `outline`,
|
|
42
|
-
`change_impact`) are
|
|
48
|
+
`change_impact`) are resolved (`..` and symbolic links) and must stay inside the repository;
|
|
49
|
+
`.git/` and `.graph-indexer/` are never served. Manifests (`package.json`, `tsconfig.json`,
|
|
50
|
+
`go.mod`) are read only as small regular files inside the repository.
|
|
51
|
+
- **Bounded work.** A regular expression given to `search_text` runs under a 5 s budget, and a file
|
|
52
|
+
whose syntax makes the structural queries super-linear is given up after 15 s, so neither can
|
|
53
|
+
block the server.
|
|
43
54
|
- **Read-only tools.** All MCP tools are read-only (`readOnlyHint: true`) and never modify
|
|
44
55
|
the repository.
|
|
45
56
|
- **stdio transport.** The MCP server talks to the local client over stdin/stdout. It does not
|
|
@@ -51,10 +62,16 @@ graph-indexer is a local developer tool. Its threat model follows from a few del
|
|
|
51
62
|
both sides check before using it. It answers the same read-only queries as the CLI.
|
|
52
63
|
`GRAPH_INDEXER_RESIDENT=0` disables it.
|
|
53
64
|
- **Local artifacts.** The index is a SQLite database in `<repo>/.graph-indexer/` (git-ignored by
|
|
54
|
-
`init`). It contains excerpts of your source code — treat it with the same sensitivity as the
|
|
65
|
+
`init`, mode 0700; a `.graph-indexer` that is a symbolic link is refused). It contains excerpts of your source code — treat it with the same sensitivity as the
|
|
55
66
|
repository itself and do not commit or share it. Deleting the directory is always safe; it is
|
|
56
67
|
rebuilt on the next run.
|
|
57
68
|
|
|
69
|
+
## Runtime
|
|
70
|
+
|
|
71
|
+
graph-indexer runs in the Node.js the user installs. It requires a release that includes the
|
|
72
|
+
October 2026 Node.js security fixes (22.23.4+, 24.22.1+ or 26.12.1+; CVE-2026-111497 and others).
|
|
73
|
+
Keep Node.js updated to the latest patch release of a supported line.
|
|
74
|
+
|
|
58
75
|
## Operational guidance
|
|
59
76
|
|
|
60
77
|
- Run the server only on repositories you trust and intend to index.
|
package/bin/graph-indexer.mjs
CHANGED
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
* graph-indexer index [--repo DIR] build / update the index and print stats
|
|
7
7
|
* graph-indexer init [--repo DIR] [--agents …] wire the MCP server into your coding agents
|
|
8
8
|
* graph-indexer status [--repo DIR]
|
|
9
|
-
* graph-indexer
|
|
9
|
+
* graph-indexer explore <task in words> [--symbols a,b] [--path P] [--max-tokens N]
|
|
10
|
+
* graph-indexer search <query> [--or QUERY]… [--path P] [--kind K] [--limit N]
|
|
10
11
|
* graph-indexer read <target>… [--full] symbols, file:START-END ranges or files, with the
|
|
11
12
|
* definitions each one uses
|
|
12
13
|
* graph-indexer symbol <name>… [--no-code]
|
|
@@ -15,6 +16,7 @@
|
|
|
15
16
|
* graph-indexer impact [--symbols a,b] [--files x,y] [--diff] [--depth N]
|
|
16
17
|
* graph-indexer outline [path] [--focus TEXT] [--max-tokens N]
|
|
17
18
|
* graph-indexer files <text|glob> [--path P]
|
|
19
|
+
* graph-indexer demo grep versus exact uses, on this repository
|
|
18
20
|
*
|
|
19
21
|
* The query commands print exactly what the MCP tools return, so humans (and agents limited to a
|
|
20
22
|
* shell) get the same answers.
|
|
@@ -33,6 +35,8 @@ let cmd = aliasOf[invokedAs] ?? null;
|
|
|
33
35
|
if (!cmd && argv.length && !argv[0].startsWith('-')) { const c = argv.shift(); cmd = aliasOf[c] ?? c; }
|
|
34
36
|
|
|
35
37
|
function flag(name) { const i = argv.indexOf(name); if (i < 0) return false; argv.splice(i, 1); return true; }
|
|
38
|
+
/** Every value of a repeatable option (`--or a --or b`). */
|
|
39
|
+
function opts(name) { const out = []; let v; while ((v = opt(name)) != null) out.push(v); return out; }
|
|
36
40
|
function opt(name, dflt = null) {
|
|
37
41
|
const i = argv.indexOf(name);
|
|
38
42
|
if (i < 0) return dflt;
|
|
@@ -120,13 +124,21 @@ async function serveResident(intel, { idleMs = 0, onIdle = null } = {}) {
|
|
|
120
124
|
const HELP = `graph-indexer ${pkg.version} — live code graph & search for AI coding agents (MCP)
|
|
121
125
|
|
|
122
126
|
Usage:
|
|
123
|
-
graph-indexer init [--repo DIR] [--agents claude,cursor,vscode,gemini,codex,opencode,junie,zed,devin] [--all] [--hooks] [--local] [--dry-run] [--no-instructions] [--no-helper]
|
|
127
|
+
graph-indexer init [--repo DIR] [--agents claude,cursor,vscode,gemini,codex,opencode,junie,zed,devin] [--all] [--hooks] [--local] [--dry-run] [--no-instructions] [--no-helper] [--no-demo]
|
|
124
128
|
graph-indexer serve [--repo DIR] start the MCP server on stdio
|
|
125
129
|
graph-indexer index [--repo DIR] build or update the index
|
|
126
130
|
graph-indexer status [--repo DIR]
|
|
127
|
-
graph-indexer
|
|
131
|
+
graph-indexer explore <task in words> [--or QUERY]… [--symbols a,b] [--path P] [--max-tokens N]
|
|
132
|
+
relevant code, callers, definitions used and tests
|
|
133
|
+
graph-indexer search <query> [--or QUERY]… [--path P] [--kind K] [--limit N]
|
|
134
|
+
--or: other phrasings of the same need, fused
|
|
128
135
|
graph-indexer grep <regex> [--path P] [--literal|-F] [-i] [--limit N]
|
|
129
136
|
graph-indexer files <text|glob> [--path P] [--limit N] find files by path or name
|
|
137
|
+
graph-indexer demo a method that shares its name: grep versus its exact uses
|
|
138
|
+
graph-indexer report summary to paste into an issue (no paths, names or code)
|
|
139
|
+
graph-indexer routes [path | METHOD /path] HTTP routes and their handlers
|
|
140
|
+
graph-indexer inject [token | class] dependency injection: tokens, providers, injection points
|
|
141
|
+
graph-indexer models [name | table] ORM models: table, migrations, Prisma client calls
|
|
130
142
|
graph-indexer read <target>… [--full] [--max-lines N] symbols (Class.method), ranges (file:10-80) or files,
|
|
131
143
|
with where each name they use is defined
|
|
132
144
|
graph-indexer symbol <name>… [--no-code] one or several definitions
|
|
@@ -136,6 +148,8 @@ Usage:
|
|
|
136
148
|
graph-indexer outline [path] [--focus TEXT] [--max-tokens N]
|
|
137
149
|
graph-indexer check [--files x,y] [--base REV] verify uncommitted edits
|
|
138
150
|
graph-indexer hook post-tool|session-start|subagent-start agent hook (JSON on stdin)
|
|
151
|
+
graph-indexer semantic install|status optional static code-embedding model (32 MB, MIT) for
|
|
152
|
+
search in words; used with GRAPH_INDEXER_SEMANTIC=1
|
|
139
153
|
graph-indexer daemon [--idle-minutes N] keep the index open for hooks and CLI queries
|
|
140
154
|
(the hooks start it; the MCP server does the same)
|
|
141
155
|
|
|
@@ -170,6 +184,17 @@ async function main() {
|
|
|
170
184
|
for (const sig of ['SIGINT', 'SIGTERM', 'SIGHUP']) process.on(sig, stop);
|
|
171
185
|
return;
|
|
172
186
|
}
|
|
187
|
+
case 'semantic': {
|
|
188
|
+
const { installModel, modelDir, semanticEnabled, MODEL } = await import('../src/search/semantic.mjs');
|
|
189
|
+
const sub = argv.shift() ?? 'status';
|
|
190
|
+
if (sub === 'install') {
|
|
191
|
+
const dir = await installModel({ log });
|
|
192
|
+
process.stdout.write(`${MODEL.repo}@${MODEL.revision.slice(0, 12)} (${MODEL.license}) installed in ${dir}\nTurn the semantic channel on with GRAPH_INDEXER_SEMANTIC=1.\n`);
|
|
193
|
+
} else {
|
|
194
|
+
process.stdout.write(JSON.stringify({ model: MODEL.repo, revision: MODEL.revision, dir: modelDir(), installed: fs.existsSync(path.join(modelDir(), 'model.safetensors')), enabled: semanticEnabled() }, null, 2) + '\n');
|
|
195
|
+
}
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
173
198
|
case 'index': {
|
|
174
199
|
const t0 = Date.now();
|
|
175
200
|
const intel = await openIntel();
|
|
@@ -191,9 +216,13 @@ async function main() {
|
|
|
191
216
|
await runInit({ argv, opt, flag, log, version: pkg.version, repo: repoArg });
|
|
192
217
|
return;
|
|
193
218
|
}
|
|
219
|
+
case 'explore': {
|
|
220
|
+
const symbols = (opt('--symbols') ?? '').split(',').filter(Boolean); const pathF = opt('--path'); const maxTokens = Number(opt('--max-tokens', 2000)); const queries = opts('--or');
|
|
221
|
+
return runTool('explore', { query: argv.join(' '), queries, symbols, path: pathF, max_tokens: maxTokens });
|
|
222
|
+
}
|
|
194
223
|
case 'search': {
|
|
195
|
-
const pathF = opt('--path'); const kind = opt('--kind'); const limit = Number(opt('--limit', 8));
|
|
196
|
-
return runTool('search_code', { query: argv.join(' '), path: pathF, kind, limit });
|
|
224
|
+
const pathF = opt('--path'); const kind = opt('--kind'); const limit = Number(opt('--limit', 8)); const queries = opts('--or');
|
|
225
|
+
return runTool('search_code', { query: argv.join(' '), queries, path: pathF, kind, limit });
|
|
197
226
|
}
|
|
198
227
|
case 'grep': {
|
|
199
228
|
const pathF = opt('--path'); const literal = flag('--literal') || flag('-F'); const ic = flag('-i') || flag('--ignore-case'); const limit = Number(opt('--limit', 60));
|
|
@@ -234,6 +263,18 @@ async function main() {
|
|
|
234
263
|
const files = (opt('--files') ?? '').split(',').filter(Boolean); const base = opt('--base', 'HEAD');
|
|
235
264
|
return runTool('check_changes', { files, base });
|
|
236
265
|
}
|
|
266
|
+
case 'demo': return runTool('demo', {});
|
|
267
|
+
case 'routes': return runTool('routes', { filter: argv.join(' ') });
|
|
268
|
+
case 'inject': return runTool('inject', { filter: argv.join(' ') });
|
|
269
|
+
case 'models': return runTool('models', { filter: argv.join(' ') });
|
|
270
|
+
case 'report': {
|
|
271
|
+
// for bug reports: sizes, languages, binding rates and latencies — no paths, names or code
|
|
272
|
+
const intel = await openIntel({ quiet: true });
|
|
273
|
+
const { reportText } = await import('../src/query/report.mjs');
|
|
274
|
+
process.stdout.write(await reportText(intel, { version: pkg.version }) + '\n');
|
|
275
|
+
intel.close();
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
237
278
|
case 'files': {
|
|
238
279
|
const pathF = opt('--path'); const limit = Number(opt('--limit', 100));
|
|
239
280
|
return runTool('find_files', { pattern: argv.join(' '), path: pathF, limit });
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "graph-indexer",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"description": "Live code graph for Claude Code: exact references, structural grep, change impact and a post-edit check, from a local index with no model or network.",
|
|
5
5
|
"author": { "name": "MaquinaTech", "url": "https://github.com/MaquinaTech" },
|
|
6
6
|
"homepage": "https://github.com/MaquinaTech/graph-indexer",
|
|
7
|
-
"license": "MIT"
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"icon": "https://raw.githubusercontent.com/MaquinaTech/graph-indexer/main/assets/logo.jpg"
|
|
8
9
|
}
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: code-structure
|
|
3
3
|
description: "Exact answers to structural questions about this repository's code, from graph-indexer's index, on a small fast model: every call site of a function or method (told apart from same-name methods of other classes), callers of callers, classes that extend or implement a type, and what a change would affect with the tests that cover it. Give it the symbol (Class.method, with its file when you know it) and the scope (directories, whether tests count, how many levels); it replies with path:line lists and says whether they are complete. Delegate these instead of grepping for names and reading files yourself."
|
|
4
|
-
model: haiku
|
|
5
|
-
disallowedTools: Edit, Write, MultiEdit, NotebookEdit
|
|
6
|
-
omitClaudeMd: true
|
|
7
4
|
---
|
|
8
5
|
You answer structural questions about the code in this repository — where something is used, who calls it, what extends or implements it, what a change would affect — exactly and briefly, from graph-indexer's index (the graph-indexer tools). The index binds every reference through scopes, imports and receiver types, so methods that only share a name are kept apart, and it re-syncs with the files before every answer.
|
|
9
6
|
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: setup
|
|
3
|
+
description: Connect graph-indexer's MCP server to this repository so Claude Code can use exact references, call graphs and change impact. Use when the user installs this plugin, or when the graph-indexer tools (find_references, call_graph, change_impact) are not available.
|
|
4
|
+
---
|
|
5
|
+
# Connect graph-indexer
|
|
6
|
+
|
|
7
|
+
This plugin brings the `code-structure` sub-agent and these instructions. The graph-indexer tools
|
|
8
|
+
come from a local MCP server that the user adds to the repository once. The plugin directory does not
|
|
9
|
+
allow plugins to start local programs, so the user must approve this step.
|
|
10
|
+
|
|
11
|
+
1. Check that Node.js 22.23.4+, 24.22.1+ or 26.12.1+ is installed (`node --version`).
|
|
12
|
+
2. Ask the user to confirm, then run from the repository root:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
claude mcp add graph-indexer -- npx -y --prefix / graph-indexer@3.1.0 serve
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
`--prefix /` makes npx run only the published package, never a copy shipped inside the repository.
|
|
19
|
+
The version is pinned on purpose: update it by hand when a newer graph-indexer is released.
|
|
20
|
+
|
|
21
|
+
3. Optional, with the user's consent: `npx -y --prefix / graph-indexer@3.1.0 init --hooks` also writes the
|
|
22
|
+
Claude Code hooks (an edit check after each edit, grep disambiguation) to `.claude/settings.json`.
|
|
23
|
+
4. Restart Claude Code, or run `/mcp`, and confirm `graph-indexer` is connected.
|
|
24
|
+
|
|
25
|
+
Then use `find_references`, `call_graph`, `change_impact`, `read_code` and `check_changes`, or delegate
|
|
26
|
+
structural questions to the `code-structure` sub-agent.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "graph-indexer",
|
|
3
|
+
"version": "3.1.0",
|
|
4
|
+
"description": "Live code graph for Claude Code: exact references, structural grep, change impact and a post-edit check, from a local index with no model or network.",
|
|
5
|
+
"author": { "name": "MaquinaTech", "url": "https://github.com/MaquinaTech" },
|
|
6
|
+
"homepage": "https://github.com/MaquinaTech/graph-indexer",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"icon": "https://raw.githubusercontent.com/MaquinaTech/graph-indexer/main/assets/logo.jpg"
|
|
9
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# graph-indexer: full Claude Code plugin (manual install)
|
|
2
|
+
|
|
3
|
+
This folder is the plugin with everything wired in: the MCP server (`.mcp.json`), the hooks
|
|
4
|
+
(`hooks/`) and the `code-structure` sub-agent. The plugin directory lists only plugins made of
|
|
5
|
+
Markdown and JSON (see `../claude-code`), so this one is not listed there. To use it, copy the
|
|
6
|
+
folder or point a local marketplace at it. `npx -y --prefix / graph-indexer@3.1.0 init --hooks`
|
|
7
|
+
gives the same result without a plugin.
|
|
8
|
+
|
|
9
|
+
Each release must update the version in `.mcp.json`, `hooks/gi-hook.mjs` and
|
|
10
|
+
`.claude-plugin/plugin.json`; `test/plugin-pins.test.mjs` checks that they equal `package.json`.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: code-structure
|
|
3
|
+
description: "Exact answers to structural questions about this repository's code, from graph-indexer's index, on a small fast model: every call site of a function or method (told apart from same-name methods of other classes), callers of callers, classes that extend or implement a type, and what a change would affect with the tests that cover it. Give it the symbol (Class.method, with its file when you know it) and the scope (directories, whether tests count, how many levels); it replies with path:line lists and says whether they are complete. Delegate these instead of grepping for names and reading files yourself."
|
|
4
|
+
model: haiku
|
|
5
|
+
disallowedTools: Edit, Write, MultiEdit, NotebookEdit
|
|
6
|
+
omitClaudeMd: true
|
|
7
|
+
---
|
|
8
|
+
You answer structural questions about the code in this repository — where something is used, who calls it, what extends or implements it, what a change would affect — exactly and briefly, from graph-indexer's index (the graph-indexer tools). The index binds every reference through scopes, imports and receiver types, so methods that only share a name are kept apart, and it re-syncs with the files before every answer.
|
|
9
|
+
|
|
10
|
+
How to answer
|
|
11
|
+
- Call sites or other uses of a function, method or class: `find_references` on it (`Class.method`, or `path:Name` when the name is ambiguous), with kind `call` for calls only, `include_tests: false` when tests are excluded and `path` for one directory. It says when the list is complete.
|
|
12
|
+
- Callers of callers: `call_graph` with direction `callers` and the depth asked for (two levels up is depth 2), with `include_tests: false` when tests are excluded.
|
|
13
|
+
- Classes that extend a class or implement an interface, directly or through other classes: `find_references` with kind `inherit`.
|
|
14
|
+
- What a change affects (call sites to update, overrides, subclasses that inherit it, tests to run): `change_impact` with the symbols.
|
|
15
|
+
- A definition or a signature: `read_code` with its name.
|
|
16
|
+
- Answer from these results. Read code or search text only to settle what they report as unresolved or incomplete (a call through a name built at run time, a receiver of unknown type), not to redo a list they give as complete.
|
|
17
|
+
- Keep to the scope asked: the directories, whether tests count, how many levels.
|
|
18
|
+
|
|
19
|
+
Reply
|
|
20
|
+
- When the request gives an answer format, follow it exactly.
|
|
21
|
+
- Otherwise one item per line as `path:LINE`, relative to the repository root, with a few words after it when they help (the enclosing function, the kind of use), then one line saying whether the list is complete or what could not be resolved.
|
|
22
|
+
- Nothing else: no account of how you found it. You do not change files.
|
|
@@ -20,6 +20,8 @@ import { spawn } from 'node:child_process';
|
|
|
20
20
|
import { pathToFileURL } from 'node:url';
|
|
21
21
|
|
|
22
22
|
const PROTOCOL = 1;
|
|
23
|
+
// the exact release this plugin version runs through npx (never a range): keep it equal to package.json's version
|
|
24
|
+
export const VERSION = '3.1.0';
|
|
23
25
|
const WIN = process.platform === 'win32';
|
|
24
26
|
|
|
25
27
|
export function findRepoRoot(start) {
|
|
@@ -43,11 +45,12 @@ export function socketPath(root) {
|
|
|
43
45
|
|
|
44
46
|
function trusted(file, root) {
|
|
45
47
|
if (WIN) return true;
|
|
46
|
-
const
|
|
47
|
-
if (dir === path.join(path.resolve(root), '.graph-indexer')) return fs.existsSync(file);
|
|
48
|
+
const mine = (st) => !process.getuid || st.uid === process.getuid();
|
|
48
49
|
try {
|
|
49
|
-
const
|
|
50
|
-
|
|
50
|
+
const sock = fs.lstatSync(file), dir = fs.lstatSync(path.dirname(file));
|
|
51
|
+
const inRepo = path.dirname(file) === path.join(path.resolve(root), '.graph-indexer');
|
|
52
|
+
// the socket and its directory are ours, not links or listeners planted by the repository or another user
|
|
53
|
+
return sock.isSocket() && mine(sock) && dir.isDirectory() && mine(dir) && (inRepo || (dir.mode & 0o077) === 0);
|
|
51
54
|
} catch { return false; }
|
|
52
55
|
}
|
|
53
56
|
|
|
@@ -85,8 +88,11 @@ async function main() {
|
|
|
85
88
|
const root = findRepoRoot(input.cwd || process.cwd());
|
|
86
89
|
const reply = await ask(root, { op: 'hook', event, input }, 2500);
|
|
87
90
|
if (reply) { if (reply.out) process.stdout.write(reply.out + '\n'); return; }
|
|
88
|
-
// no resident yet: the full hook through npx (it starts one for the next calls)
|
|
89
|
-
|
|
91
|
+
// no resident yet: the full hook through npx (it starts one for the next calls); --prefix keeps
|
|
92
|
+
// npx from running a graph-indexer that the repository ships in its own node_modules
|
|
93
|
+
// (on Windows npx.cmd needs a shell, so the home directory is quoted for cmd.exe)
|
|
94
|
+
const home = WIN ? `"${os.homedir()}"` : os.homedir();
|
|
95
|
+
const child = spawn(WIN ? 'npx.cmd' : 'npx', ['-y', '--prefix', home, `graph-indexer@${VERSION}`, 'hook', event], { stdio: ['pipe', 'inherit', 'ignore'], shell: WIN });
|
|
90
96
|
child.on('error', () => { /* fail open */ });
|
|
91
97
|
child.stdin.on('error', () => { /* fail open */ });
|
|
92
98
|
child.stdin.end(raw);
|
|
@@ -19,8 +19,9 @@ import { spawn } from 'node:child_process';
|
|
|
19
19
|
|
|
20
20
|
const PROTOCOL = 1;
|
|
21
21
|
const WIN = process.platform === 'win32';
|
|
22
|
-
// how to start a resident when none answers (rewritten by `init --local` and installed binaries)
|
|
23
|
-
|
|
22
|
+
// how to start a resident when none answers (rewritten by `init --local` and installed binaries);
|
|
23
|
+
// --prefix keeps npx from running a graph-indexer that the repository ships in its own node_modules
|
|
24
|
+
const DAEMON = ['npx', '-y', '--prefix', os.homedir(), 'graph-indexer@3', 'daemon'];
|
|
24
25
|
// OpenCode tool ids → the tool names graph-indexer's hook understands
|
|
25
26
|
const TOOLS = { read: 'Read', grep: 'Grep', bash: 'Bash', edit: 'Edit', write: 'Write' };
|
|
26
27
|
|
|
@@ -46,12 +47,12 @@ function socketPath(root) {
|
|
|
46
47
|
|
|
47
48
|
function trusted(file, root) {
|
|
48
49
|
if (WIN) return true;
|
|
49
|
-
|
|
50
|
-
const dir = path.dirname(file);
|
|
51
|
-
if (dir === path.join(path.resolve(root), '.graph-indexer')) return true;
|
|
50
|
+
const mine = (st) => !process.getuid || st.uid === process.getuid();
|
|
52
51
|
try {
|
|
53
|
-
const
|
|
54
|
-
|
|
52
|
+
const sock = fs.lstatSync(file), dir = fs.lstatSync(path.dirname(file));
|
|
53
|
+
const inRepo = path.dirname(file) === path.join(path.resolve(root), '.graph-indexer');
|
|
54
|
+
// the socket and its directory are ours, not links or listeners planted by the repository or another user
|
|
55
|
+
return sock.isSocket() && mine(sock) && dir.isDirectory() && mine(dir) && (inRepo || (dir.mode & 0o077) === 0);
|
|
55
56
|
} catch { return false; }
|
|
56
57
|
}
|
|
57
58
|
|
|
@@ -107,7 +108,11 @@ export const GraphIndexer = async ({ directory, worktree } = {}) => {
|
|
|
107
108
|
if (!reply) {
|
|
108
109
|
if (!started) {
|
|
109
110
|
started = true;
|
|
110
|
-
|
|
111
|
+
// cmd.exe (npx.cmd needs a shell on Windows) would run `&`, `|`… in a path: quote
|
|
112
|
+
// every argument, and give up on one that a quote cannot hold
|
|
113
|
+
const args = [...DAEMON.slice(1), '--repo', root];
|
|
114
|
+
if (WIN && args.some(a => /["%\r\n]/.test(a))) return;
|
|
115
|
+
const child = spawn(DAEMON[0], WIN ? args.map(a => `"${a}"`) : args, { detached: true, stdio: 'ignore', windowsHide: true, shell: WIN });
|
|
111
116
|
child.on('error', () => { /* optional */ });
|
|
112
117
|
child.unref();
|
|
113
118
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "graph-indexer",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"description": "Live code graph and search for AI coding agents over MCP: tree-sitter (WASM) symbols, scope/import/type-aware references, call graphs, change impact and hybrid search — local, zero-dependency, no native builds.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/query/intel.mjs",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"index": "node bin/graph-indexer.mjs index"
|
|
35
35
|
},
|
|
36
36
|
"engines": {
|
|
37
|
-
"node": "
|
|
37
|
+
"node": "^22.23.4 || ^24.22.1 || >=26.12.1"
|
|
38
38
|
},
|
|
39
39
|
"repository": {
|
|
40
40
|
"type": "git",
|
package/src/cli/helper.mjs
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* index, and replies with the answer alone. The main agent delegates the question and gets back a
|
|
5
5
|
* path:line list instead of the searches and reads behind it, and the lookup runs on a model that
|
|
6
6
|
* costs a fraction per token. `init` writes it for Claude Code (.claude/agents/) and the Claude Code
|
|
7
|
-
* plugin ships
|
|
7
|
+
* plugin ships it too (integrations/claude-code/agents/, with only the frontmatter keys the plugin directory accepts).
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
export const HELPER_NAME = 'code-structure';
|
|
@@ -65,3 +65,17 @@ omitClaudeMd: true
|
|
|
65
65
|
${helperInstructions()}
|
|
66
66
|
`;
|
|
67
67
|
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* The same sub-agent for the Claude Code plugin listed in the plugin directory, which accepts only
|
|
71
|
+
* a short list of frontmatter keys (name and description): the model and the tool limits are left
|
|
72
|
+
* to the session, and the instructions already say it does not change files.
|
|
73
|
+
*/
|
|
74
|
+
export function pluginAgentFile() {
|
|
75
|
+
return `---
|
|
76
|
+
name: ${HELPER_NAME}
|
|
77
|
+
description: ${JSON.stringify(DESCRIPTION)}
|
|
78
|
+
---
|
|
79
|
+
${helperInstructions()}
|
|
80
|
+
`;
|
|
81
|
+
}
|