@raymondchins/agentmap 0.2.1 → 0.2.3
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 +131 -93
- package/agentmap.mjs +44 -13
- package/hooks/INSTALL.md +4 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,12 +6,13 @@
|
|
|
6
6
|
|
|
7
7
|
**The repo map your coding agent is _forced_ to use — ~98% fewer tokens to understand your TS/JS codebase.**
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
Your AI coding agent re-learns your codebase every session — opening files and grepping to find
|
|
10
|
+
what connects to what, burning tokens before it writes a line. agentmap gives it a **queryable,
|
|
11
|
+
ranked code-relationship map for TypeScript/JavaScript repos** instead — a `ts-morph` import/symbol
|
|
12
|
+
graph ranked by personalized PageRank. Ask it to *"add a field"* or *"fix the login bug"* and it
|
|
13
|
+
finds the right files, their imports, and what already exists in
|
|
14
|
+
**~98% fewer tokens on average** (up to **99.9% per task**) — kept current by a post-commit
|
|
15
|
+
auto-refresh and actually used via a `PreToolUse(Grep)` hook.
|
|
15
16
|
|
|
16
17
|
[](https://www.npmjs.com/package/@raymondchins/agentmap)
|
|
17
18
|
[](https://github.com/raymondchins/agentmap/actions/workflows/ci.yml)
|
|
@@ -23,13 +24,61 @@ published.
|
|
|
23
24
|
|
|
24
25
|
---
|
|
25
26
|
|
|
27
|
+
## Benchmark
|
|
28
|
+
|
|
29
|
+
Every task you hand a coding agent starts with the same hidden step — *find the relevant code*.
|
|
30
|
+
Here's the token cost of that step, **reading raw files vs querying agentmap**, on a real 154-file
|
|
31
|
+
Next.js app ([vercel/ai-chatbot](https://github.com/vercel/ai-chatbot)). Every figure is captured
|
|
32
|
+
tool output (`node benchmark/bench.mjs <repo>` at the pinned sha):
|
|
33
|
+
|
|
34
|
+
<table width="100%">
|
|
35
|
+
<thead>
|
|
36
|
+
<tr>
|
|
37
|
+
<th align="left">The question the agent has to answer first</th>
|
|
38
|
+
<th align="right">Reading files</th>
|
|
39
|
+
<th align="right">With agentmap</th>
|
|
40
|
+
<th align="right">Saved</th>
|
|
41
|
+
</tr>
|
|
42
|
+
</thead>
|
|
43
|
+
<tbody>
|
|
44
|
+
<tr><td align="left">Where is this symbol defined?</td><td align="right">1,950</td><td align="right">20</td><td align="right">99%</td></tr>
|
|
45
|
+
<tr><td align="left">Does a helper for this already exist? <i>(reuse)</i></td><td align="right">14,740</td><td align="right">19</td><td align="right">99.9%</td></tr>
|
|
46
|
+
<tr><td align="left">What breaks if I change this file? <i>(blast radius)</i></td><td align="right">81,038</td><td align="right">616</td><td align="right">99.2%</td></tr>
|
|
47
|
+
<tr><td align="left">What files make up this feature?</td><td align="right">6,121</td><td align="right">1,025</td><td align="right">83.3%</td></tr>
|
|
48
|
+
<tr><td align="left">Give me a repo overview</td><td align="right">3,065</td><td align="right">1,127</td><td align="right">63.2%</td></tr>
|
|
49
|
+
<tr><td align="left">Load the whole repo into context</td><td align="right">150,281</td><td align="right">1,127</td><td align="right">99.3%</td></tr>
|
|
50
|
+
<tr><td align="left">What does this one file import?</td><td align="right">583</td><td align="right">517</td><td align="right">11.3%</td></tr>
|
|
51
|
+
<tr><td align="left"><b>All 7 tasks combined</b></td><td align="right"><b>257,778</b></td><td align="right"><b>4,451</b></td><td align="right"><b>98.3%</b></td></tr>
|
|
52
|
+
</tbody>
|
|
53
|
+
</table>
|
|
54
|
+
|
|
55
|
+
<sub>Context tokens the agent burns to answer each question — token est = chars/4, applied to both sides.</sub>
|
|
56
|
+
|
|
57
|
+
That's the agent reaching the same answer on **58× fewer tokens** overall — and the pattern holds
|
|
58
|
+
across [zod](https://github.com/colinhacks/zod) (367 files, **99.2%**) and
|
|
59
|
+
[taxonomy](https://github.com/shadcn-ui/taxonomy) (125 files, **96.0%**), peaking at **646× fewer**
|
|
60
|
+
on a single whole-repo map. Reproducible at pinned shas; full per-scenario tables in
|
|
61
|
+
**[`./benchmark/RESULTS.md`](./benchmark/RESULTS.md)**.
|
|
62
|
+
|
|
63
|
+
**Speed:** a cold build (parse + PageRank + symbol graph) takes **~1.2s**; a warm cached query
|
|
64
|
+
returns in **~0.1s** (the lazy-loaded path added in 0.2.2) — the agent has a ranked answer back
|
|
65
|
+
before it would have finished opening the first handful of files.
|
|
66
|
+
|
|
67
|
+
Honest notes: the win scales with the work — the small rows above (63%, 11%) are the floor, and a
|
|
68
|
+
*trivial single-file* lookup can even cost **more** than `cat`+`grep` (taxonomy's file-import task
|
|
69
|
+
hit −313%; we leave it in). Numbers measure **context-token volume**, not answer quality or wall-clock.
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
26
73
|
## Why it's different
|
|
27
74
|
|
|
28
|
-
Most "repo context" tools are
|
|
29
|
-
prompt and
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
75
|
+
Most "repo context" tools are a photocopy: they dump your repository (or a slice of it) into
|
|
76
|
+
the prompt once and walk away. The copy goes stale the moment you edit a file, and nothing
|
|
77
|
+
makes the agent actually read it.
|
|
78
|
+
|
|
79
|
+
agentmap is the opposite — a **queryable, ranked, self-refreshing** map the agent interrogates
|
|
80
|
+
flag-by-flag, that **rebuilds itself on every commit**, and that a `PreToolUse` hook steers the
|
|
81
|
+
agent toward *before* it falls back to serial grep.
|
|
33
82
|
|
|
34
83
|
| | **agentmap** | Aider repo map | RepoMapper | Repomix | code2prompt |
|
|
35
84
|
| --- | --- | --- | --- | --- | --- |
|
|
@@ -47,6 +96,73 @@ and it's a **file-level import graph**, not a full call-site/reference resolver
|
|
|
47
96
|
|
|
48
97
|
---
|
|
49
98
|
|
|
99
|
+
## The agent loop (the actual point)
|
|
100
|
+
|
|
101
|
+
Here's the quiet failure of every other repo-map tool: it builds a beautiful map, and then the
|
|
102
|
+
agent forgets it exists and greps anyway. A map the agent doesn't open is just dead weight.
|
|
103
|
+
|
|
104
|
+
agentmap closes that loop. Two hooks (in [`./hooks/`](./hooks/)) do the work: the map
|
|
105
|
+
**refreshes itself after every commit**, and the agent gets **nudged to query it before it
|
|
106
|
+
serial-greps**. You wire it once — then it stays current on its own, and stays used.
|
|
107
|
+
|
|
108
|
+
### 1. Auto-refresh on commit
|
|
109
|
+
|
|
110
|
+
[`hooks/post-commit`](./hooks/post-commit) rebuilds `.claude/agentmap.json` after each
|
|
111
|
+
commit, detached + silenced so it never slows the commit. It skips during
|
|
112
|
+
rebase/merge/cherry-pick and no-ops if Node is missing.
|
|
113
|
+
|
|
114
|
+
The hooks ship inside the npm package. The simplest setup:
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
npx @raymondchins/agentmap --install-hooks
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
This copies `hooks/post-commit` into `.git/hooks/`, sets it executable, ensures
|
|
121
|
+
`.claude/agentmap.json` is in `.gitignore`, and **auto-wires the `PreToolUse` nudge
|
|
122
|
+
hook into `.claude/settings.json`** (merge-safe + idempotent) so map enforcement is
|
|
123
|
+
on by default — no manual paste. Manual alternative for just the post-commit hook:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
# from your repo root
|
|
127
|
+
cp hooks/post-commit .git/hooks/post-commit
|
|
128
|
+
chmod +x .git/hooks/post-commit
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
The hook auto-locates the builder: a local `agentmap.mjs`, then `scripts/agentmap.mjs`, then
|
|
132
|
+
the installed `agentmap` binary, then `npx --no-install @raymondchins/agentmap`.
|
|
133
|
+
|
|
134
|
+
### 2. Force the agent to use it — `PreToolUse` hook
|
|
135
|
+
|
|
136
|
+
[`hooks/agentmap-nudge.mjs`](./hooks/agentmap-nudge.mjs) is a **non-blocking** `PreToolUse(Grep)`
|
|
137
|
+
hook for Claude Code. When a `Grep` looks like a dependency / who-imports / component-usage /
|
|
138
|
+
reuse search, it injects a reminder steering the agent to `agentmap --any` first. It never
|
|
139
|
+
denies the grep, and stays silent for raw-string / Tailwind-class / lowercase-HTML-tag
|
|
140
|
+
sweeps — so it's high-signal, not nagging.
|
|
141
|
+
|
|
142
|
+
`--install-hooks` writes this into `.claude/settings.json` for you (merge-safe — it
|
|
143
|
+
preserves existing settings and won't duplicate on re-run). For reference, or to wire
|
|
144
|
+
it by hand:
|
|
145
|
+
|
|
146
|
+
```json
|
|
147
|
+
{
|
|
148
|
+
"hooks": {
|
|
149
|
+
"PreToolUse": [
|
|
150
|
+
{
|
|
151
|
+
"matcher": "Grep",
|
|
152
|
+
"hooks": [
|
|
153
|
+
{ "type": "command", "command": "node ./hooks/agentmap-nudge.mjs" }
|
|
154
|
+
]
|
|
155
|
+
}
|
|
156
|
+
]
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
That's the "forced to use it" in the tagline: the map stays current on its own, and the
|
|
162
|
+
agent is steered to it the moment it reaches for a dependency-shaped grep.
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
50
166
|
## Quickstart
|
|
51
167
|
|
|
52
168
|
No install needed:
|
|
@@ -77,8 +193,9 @@ agentmap: 154 files | 4 features | top hub: lib/utils.ts (deg 52, pr 0.105171)
|
|
|
77
193
|
|
|
78
194
|
## The `--any` router
|
|
79
195
|
|
|
80
|
-
|
|
81
|
-
|
|
196
|
+
Don't want to learn eight flags? You don't have to. Throw anything at `--any` — a filename, a
|
|
197
|
+
function, a feature, even a raw string — and it figures out what you meant, returning the first
|
|
198
|
+
layer that hits:
|
|
82
199
|
|
|
83
200
|
```
|
|
84
201
|
--any <query>
|
|
@@ -339,74 +456,13 @@ $ node agentmap.mjs --print | jq '.hubs[0]'
|
|
|
339
456
|
| `--help` / `-h` | Print a usage block listing every flag and exit 0. |
|
|
340
457
|
| `--version` / `-v` | Print the version from `package.json` and exit 0. |
|
|
341
458
|
| `--json` | **Global modifier.** When present, every command prints exactly one JSON object to stdout (no prose). Shapes vary per command: `--json --hubs` → `{command,fileCount,sha,hubs:[string]}`, `--json --find X` → `{command,query,matches:[{file,name,kind}]}`, `--json --relates X` → `{command,file,pagerank,exports,imports,dependents,related}`, `--json --any X` → `{command,query,kind,…payload}`, etc. Bare `--json` (no query flag) → `{command:"build",fileCount,features,topHub}`. |
|
|
342
|
-
| `--install-hooks` | Copy `hooks/post-commit` into `.git/hooks/` (chmod 0755), ensure `.claude/agentmap.json` is in `.gitignore`, and
|
|
459
|
+
| `--install-hooks` | Copy `hooks/post-commit` into `.git/hooks/` (chmod 0755), ensure `.claude/agentmap.json` is in `.gitignore`, and auto-wire the Claude Code `PreToolUse(Grep)` nudge into `.claude/settings.json` (merge-safe + idempotent). Exit 0 on success, stderr + exit 1 on failure. |
|
|
343
460
|
| `--mcp` | Start agentmap as a **stdio MCP server** so non-Claude-Code agents (Cursor, Cline, any MCP client) can call every flag as a first-class tool. |
|
|
344
461
|
|
|
345
462
|
**Exit-code contract:** `0` = success / match / help / version; `1` = query returned zero results (`--any`, `--find`, `--relates`, `--feature` with no match); `2` = usage error (missing required arg, unknown flag). Any token starting with `-` that matches no known flag prints an error to stderr and exits 2.
|
|
346
463
|
|
|
347
464
|
---
|
|
348
465
|
|
|
349
|
-
## The agent loop (the actual point)
|
|
350
|
-
|
|
351
|
-
A repo map only helps if the agent uses it. agentmap ships two hooks (in [`./hooks/`](./hooks/))
|
|
352
|
-
that close the loop: the map refreshes itself after every commit, and the agent gets nudged
|
|
353
|
-
to query the map before it serial-greps.
|
|
354
|
-
|
|
355
|
-
### 1. Auto-refresh on commit
|
|
356
|
-
|
|
357
|
-
[`hooks/post-commit`](./hooks/post-commit) rebuilds `.claude/agentmap.json` after each
|
|
358
|
-
commit, detached + silenced so it never slows the commit. It skips during
|
|
359
|
-
rebase/merge/cherry-pick and no-ops if Node is missing.
|
|
360
|
-
|
|
361
|
-
The hooks ship inside the npm package. The simplest setup:
|
|
362
|
-
|
|
363
|
-
```bash
|
|
364
|
-
npx @raymondchins/agentmap --install-hooks
|
|
365
|
-
```
|
|
366
|
-
|
|
367
|
-
This copies `hooks/post-commit` into `.git/hooks/`, sets it executable, ensures
|
|
368
|
-
`.claude/agentmap.json` is in `.gitignore`, and prints the `.claude/settings.json`
|
|
369
|
-
snippet for the `PreToolUse` nudge hook (below). Manual alternative:
|
|
370
|
-
|
|
371
|
-
```bash
|
|
372
|
-
# from your repo root
|
|
373
|
-
cp hooks/post-commit .git/hooks/post-commit
|
|
374
|
-
chmod +x .git/hooks/post-commit
|
|
375
|
-
```
|
|
376
|
-
|
|
377
|
-
The hook auto-locates the builder: a local `agentmap.mjs`, then `scripts/agentmap.mjs`, then
|
|
378
|
-
the installed `agentmap` binary, then `npx --no-install @raymondchins/agentmap`.
|
|
379
|
-
|
|
380
|
-
### 2. Force the agent to use it — `PreToolUse` hook
|
|
381
|
-
|
|
382
|
-
[`hooks/agentmap-nudge.mjs`](./hooks/agentmap-nudge.mjs) is a **non-blocking** `PreToolUse(Grep)`
|
|
383
|
-
hook for Claude Code. When a `Grep` looks like a dependency / who-imports / component-usage /
|
|
384
|
-
reuse search, it injects a reminder steering the agent to `agentmap --any` first. It never
|
|
385
|
-
denies the grep, and stays silent for raw-string / Tailwind-class / lowercase-HTML-tag
|
|
386
|
-
sweeps — so it's high-signal, not nagging.
|
|
387
|
-
|
|
388
|
-
Wire it up in `.claude/settings.json`:
|
|
389
|
-
|
|
390
|
-
```json
|
|
391
|
-
{
|
|
392
|
-
"hooks": {
|
|
393
|
-
"PreToolUse": [
|
|
394
|
-
{
|
|
395
|
-
"matcher": "Grep",
|
|
396
|
-
"hooks": [
|
|
397
|
-
{ "type": "command", "command": "node ./hooks/agentmap-nudge.mjs" }
|
|
398
|
-
]
|
|
399
|
-
}
|
|
400
|
-
]
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
```
|
|
404
|
-
|
|
405
|
-
That's the "forced to use it" in the tagline: the map stays current on its own, and the
|
|
406
|
-
agent is steered to it the moment it reaches for a dependency-shaped grep.
|
|
407
|
-
|
|
408
|
-
---
|
|
409
|
-
|
|
410
466
|
## Scope & limitations
|
|
411
467
|
|
|
412
468
|
Honesty first — this is deliberately a small, sharp tool, not a universal code-graph.
|
|
@@ -433,24 +489,6 @@ Honesty first — this is deliberately a small, sharp tool, not a universal code
|
|
|
433
489
|
|
|
434
490
|
---
|
|
435
491
|
|
|
436
|
-
## Benchmark
|
|
437
|
-
|
|
438
|
-
Measured across **7 agent tasks on 3 real public repos** — reproducible with `node benchmark/bench.mjs <repo>`:
|
|
439
|
-
|
|
440
|
-
| Repo | Files | Tokens saved |
|
|
441
|
-
|------|------:|-------------:|
|
|
442
|
-
| [vercel/ai-chatbot](https://github.com/vercel/ai-chatbot) | 154 | **98.3%** |
|
|
443
|
-
| [colinhacks/zod](https://github.com/colinhacks/zod) | 367 | **99.2%** |
|
|
444
|
-
| [shadcn-ui/taxonomy](https://github.com/shadcn-ui/taxonomy) | 125 | **96.0%** |
|
|
445
|
-
|
|
446
|
-
Per-task peaks (real, across the three repos): **whole-repo map 99.8%**, **reuse-before-rebuild lookup 99.9%**, **blast-radius 99.2%**, **find-symbol 99%**. Cold build (parse + PageRank + symbol graph) **~1.2s**; warm cached query **~0.2s**.
|
|
447
|
-
|
|
448
|
-
Honest notes: the win scales with repo size — a *trivial single-file* `--any` lookup can actually cost **more** than `cat`+`grep` (taxonomy showed −313% on that one task; we leave it in). Numbers measure **context-token volume**, not end-to-end retrieval accuracy. Token est = `chars / 4`, applied to both sides.
|
|
449
|
-
|
|
450
|
-
Full methodology, per-repo tables, and all caveats: **[`./benchmark/RESULTS.md`](./benchmark/RESULTS.md)**.
|
|
451
|
-
|
|
452
|
-
---
|
|
453
|
-
|
|
454
492
|
## Contributing
|
|
455
493
|
|
|
456
494
|
Issues and PRs welcome. High-value directions:
|
package/agentmap.mjs
CHANGED
|
@@ -13,11 +13,18 @@
|
|
|
13
13
|
// Near-zero deps (ts-morph only). Runs in the target repo's cwd.
|
|
14
14
|
// Algorithm credit: Aider's repo map (Apache-2.0) — github.com/Aider-AI/aider
|
|
15
15
|
// ============================================================================
|
|
16
|
-
import { Project, SyntaxKind } from "ts-morph";
|
|
17
|
-
const CallExpression = SyntaxKind.CallExpression;
|
|
18
16
|
import { writeFileSync, readFileSync, existsSync, mkdirSync, renameSync, readdirSync, statSync, chmodSync } from "node:fs";
|
|
19
17
|
import { execSync, execFileSync } from "node:child_process";
|
|
20
18
|
import { createHash } from "node:crypto";
|
|
19
|
+
import { createRequire } from "node:module";
|
|
20
|
+
|
|
21
|
+
// Lazy ts-morph: its ~105ms module init only fires on a COLD rebuild. Warm cache
|
|
22
|
+
// queries (the common case) never construct a Project, so they skip the load
|
|
23
|
+
// entirely (~2x faster warm). createRequire keeps it synchronous — no async to
|
|
24
|
+
// thread through build()/makeProject().
|
|
25
|
+
const _require = createRequire(import.meta.url);
|
|
26
|
+
let _tsm = null;
|
|
27
|
+
const tsMorph = () => (_tsm ??= _require("ts-morph"));
|
|
21
28
|
|
|
22
29
|
const MAP = ".claude/agentmap.json";
|
|
23
30
|
const SCHEMA_VERSION = 2;
|
|
@@ -177,6 +184,7 @@ function identMul(ident, defineCount, mentioned) {
|
|
|
177
184
|
// else (missing / malformed / solution-style references that index 0 files) fall
|
|
178
185
|
// back to broad source globs so the tool degrades gracefully instead of crashing.
|
|
179
186
|
function makeProject() {
|
|
187
|
+
const { Project } = tsMorph();
|
|
180
188
|
// skipFileDependencyResolution: ~40% faster build, verified identical edge
|
|
181
189
|
// set (we resolve module specifiers explicitly below, never via the implicit
|
|
182
190
|
// dependency graph). allowJs so .js/.jsx are parsed.
|
|
@@ -248,6 +256,8 @@ function makeProject() {
|
|
|
248
256
|
function build() {
|
|
249
257
|
const t0 = Date.now();
|
|
250
258
|
const project = makeProject();
|
|
259
|
+
const { SyntaxKind } = tsMorph();
|
|
260
|
+
const CallExpression = SyntaxKind.CallExpression;
|
|
251
261
|
const cwd = process.cwd().replace(/\\/g, "/");
|
|
252
262
|
const rel = (p) => p.replace(cwd + "/", "");
|
|
253
263
|
const files = {}, dependents = {}, features = {};
|
|
@@ -534,14 +544,13 @@ function fileBlock(key, f) {
|
|
|
534
544
|
|
|
535
545
|
// ---------------------------------------------------------------------------
|
|
536
546
|
// --install-hooks: copy the package post-commit hook into .git/hooks, ensure
|
|
537
|
-
// .claude/agentmap.json is gitignored, and
|
|
538
|
-
//
|
|
539
|
-
//
|
|
540
|
-
//
|
|
547
|
+
// .claude/agentmap.json is gitignored, and auto-wire the Claude Code
|
|
548
|
+
// PreToolUse(Grep) nudge into the project's .claude/settings.json so map
|
|
549
|
+
// enforcement is ON by default (no manual copy-paste). Merge-safe + idempotent.
|
|
550
|
+
// Throws on any failure so the caller can stderr+exit 1.
|
|
541
551
|
// ---------------------------------------------------------------------------
|
|
542
552
|
function installHooks() {
|
|
543
553
|
const src = new URL("./hooks/post-commit", import.meta.url);
|
|
544
|
-
const nudge = new URL("./hooks/agentmap-nudge.mjs", import.meta.url);
|
|
545
554
|
// The package hooks/ dir must ship alongside agentmap.mjs.
|
|
546
555
|
if (!existsSync(src)) throw new Error(`packaged hook not found at ${src.pathname} (is the hooks/ dir present?)`);
|
|
547
556
|
|
|
@@ -565,14 +574,36 @@ function installHooks() {
|
|
|
565
574
|
writeFileSync(".gitignore", IGNORE_LINE + "\n");
|
|
566
575
|
}
|
|
567
576
|
|
|
568
|
-
//
|
|
569
|
-
//
|
|
577
|
+
// Auto-wire the PreToolUse(Grep) enforcement nudge into the PROJECT settings
|
|
578
|
+
// (.claude/settings.json) so "the agent is forced to use the map" is ON by
|
|
579
|
+
// default — not a manual paste. Merge-safe + idempotent: preserves any
|
|
580
|
+
// existing settings/hooks, never duplicates our entry. Uses a project-relative
|
|
581
|
+
// command so a committed settings.json stays portable across machines.
|
|
582
|
+
const NUDGE_CMD = "node node_modules/@raymondchins/agentmap/hooks/agentmap-nudge.mjs";
|
|
583
|
+
const settingsPath = ".claude/settings.json";
|
|
584
|
+
let settings = {};
|
|
585
|
+
if (existsSync(settingsPath)) {
|
|
586
|
+
try { settings = JSON.parse(readFileSync(settingsPath, "utf8")) || {}; }
|
|
587
|
+
catch { throw new Error(`${settingsPath} is not valid JSON — fix or remove it, then re-run --install-hooks`); }
|
|
588
|
+
}
|
|
589
|
+
settings.hooks ??= {};
|
|
590
|
+
settings.hooks.PreToolUse ??= [];
|
|
591
|
+
const alreadyWired = settings.hooks.PreToolUse.some(
|
|
592
|
+
(e) => Array.isArray(e?.hooks) && e.hooks.some((h) => typeof h?.command === "string" && h.command.includes("agentmap-nudge")),
|
|
593
|
+
);
|
|
594
|
+
if (!alreadyWired) {
|
|
595
|
+
settings.hooks.PreToolUse.push({ matcher: "Grep", hooks: [{ type: "command", command: NUDGE_CMD }] });
|
|
596
|
+
mkdirSync(".claude", { recursive: true });
|
|
597
|
+
writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
// Success report.
|
|
570
601
|
console.log(`installed post-commit hook → ${dest}`);
|
|
571
602
|
console.log(ignoredAlready ? `.gitignore already has ${IGNORE_LINE}` : `added ${IGNORE_LINE} to .gitignore`);
|
|
572
|
-
console.log(
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
603
|
+
console.log(alreadyWired
|
|
604
|
+
? `${settingsPath} already wires the PreToolUse(Grep) → agentmap nudge — left as-is`
|
|
605
|
+
: `wired PreToolUse(Grep) → agentmap nudge into ${settingsPath} (map enforcement on by default)`);
|
|
606
|
+
console.log("\nDone — the map auto-refreshes on commit, and greps are nudged to agentmap first.");
|
|
576
607
|
}
|
|
577
608
|
|
|
578
609
|
// ---------------------------------------------------------------------------
|
package/hooks/INSTALL.md
CHANGED
|
@@ -121,8 +121,10 @@ agentmap --install-hooks
|
|
|
121
121
|
```
|
|
122
122
|
|
|
123
123
|
This copies `hooks/post-commit` to `.git/hooks/post-commit`, chmods it, ensures
|
|
124
|
-
`.claude/agentmap.json` is in `.gitignore`, and
|
|
125
|
-
`settings.json`
|
|
124
|
+
`.claude/agentmap.json` is in `.gitignore`, and auto-wires the Claude Code
|
|
125
|
+
`PreToolUse(Grep)` nudge into `.claude/settings.json` (merge-safe — preserves
|
|
126
|
+
existing settings, idempotent on re-run) — enforcement on by default, all in one
|
|
127
|
+
step. No manual paste needed.
|
|
126
128
|
|
|
127
129
|
**Manual alternative:**
|
|
128
130
|
|