token-pilot 0.32.0 → 0.33.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.
Files changed (46) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/agents/tp-api-surface-tracker.md +1 -1
  4. package/agents/tp-audit-scanner.md +1 -1
  5. package/agents/tp-commit-writer.md +1 -1
  6. package/agents/tp-context-engineer.md +1 -1
  7. package/agents/tp-dead-code-finder.md +1 -1
  8. package/agents/tp-debugger.md +1 -1
  9. package/agents/tp-dep-health.md +1 -1
  10. package/agents/tp-doc-writer.md +1 -1
  11. package/agents/tp-history-explorer.md +1 -1
  12. package/agents/tp-impact-analyzer.md +1 -1
  13. package/agents/tp-incident-timeline.md +1 -1
  14. package/agents/tp-incremental-builder.md +1 -1
  15. package/agents/tp-migration-scout.md +1 -1
  16. package/agents/tp-onboard.md +1 -1
  17. package/agents/tp-performance-profiler.md +1 -1
  18. package/agents/tp-pr-reviewer.md +1 -1
  19. package/agents/tp-refactor-planner.md +1 -1
  20. package/agents/tp-review-impact.md +1 -1
  21. package/agents/tp-run.md +1 -1
  22. package/agents/tp-session-restorer.md +1 -1
  23. package/agents/tp-ship-coordinator.md +1 -1
  24. package/agents/tp-spec-writer.md +1 -1
  25. package/agents/tp-test-coverage-gapper.md +1 -1
  26. package/agents/tp-test-triage.md +1 -1
  27. package/agents/tp-test-writer.md +1 -1
  28. package/dist/ast-index/client.js +17 -1
  29. package/dist/cli/install-agents.d.ts +18 -0
  30. package/dist/cli/install-agents.js +88 -1
  31. package/dist/cli/stats.js +9 -2
  32. package/dist/core/error-log.d.ts +86 -0
  33. package/dist/core/error-log.js +228 -0
  34. package/dist/core/event-log.d.ts +49 -1
  35. package/dist/core/event-log.js +114 -0
  36. package/dist/core/validation.d.ts +12 -0
  37. package/dist/core/validation.js +38 -8
  38. package/dist/handlers/smart-log.js +7 -2
  39. package/dist/hooks/installer.d.ts +40 -0
  40. package/dist/hooks/installer.js +145 -2
  41. package/dist/hooks/pre-task.js +44 -10
  42. package/dist/hooks/safe-runner.d.ts +48 -0
  43. package/dist/hooks/safe-runner.js +73 -0
  44. package/dist/index.d.ts +11 -0
  45. package/dist/index.js +284 -63
  46. package/package.json +1 -1
@@ -6,14 +6,14 @@
6
6
  },
7
7
  "metadata": {
8
8
  "description": "Token Pilot \u2014 save 60-90% tokens when AI reads code",
9
- "version": "0.32.0"
9
+ "version": "0.33.0"
10
10
  },
11
11
  "plugins": [
12
12
  {
13
13
  "name": "token-pilot",
14
14
  "source": "./",
15
15
  "description": "Reduces token consumption by 60-90% via AST-aware lazy file reading, structural symbol navigation, and cross-session tool-usage analytics. 22 MCP tools + 19 subagents + budget watchdog hooks.",
16
- "version": "0.32.0",
16
+ "version": "0.33.0",
17
17
  "author": {
18
18
  "name": "Digital-Threads"
19
19
  },
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "token-pilot",
3
- "version": "0.32.0",
3
+ "version": "0.33.0",
4
4
  "description": "Saves 60-90% tokens when AI reads code. AST-aware lazy reading, symbol navigation, cross-session tool-usage analytics, 22 subagents (haiku/sonnet/opus-tiered) with budget watchdog.",
5
5
  "author": {
6
6
  "name": "Digital-Threads",
@@ -9,7 +9,7 @@ tools:
9
9
  - mcp__token-pilot__read_symbol
10
10
  - Bash
11
11
  model: haiku
12
- token_pilot_version: "0.32.0"
12
+ token_pilot_version: "0.33.0"
13
13
  token_pilot_body_hash: dd184501203fa7f3c73f419c4ffbe33c4be75400cb64a7a51733a3fe23f6e085
14
14
  ---
15
15
 
@@ -11,7 +11,7 @@ tools:
11
11
  - Grep
12
12
  - Read
13
13
  model: sonnet
14
- token_pilot_version: "0.32.0"
14
+ token_pilot_version: "0.33.0"
15
15
  token_pilot_body_hash: d172f600bf32277ea6eb4cbbee4542ddd698a986dcd96997d33930561964569b
16
16
  ---
17
17
 
@@ -8,7 +8,7 @@ tools:
8
8
  - mcp__token-pilot__test_summary
9
9
  - mcp__token-pilot__outline
10
10
  - Bash
11
- token_pilot_version: "0.32.0"
11
+ token_pilot_version: "0.33.0"
12
12
  token_pilot_body_hash: de64a406b5176de19f7422619c7de7949b1f28865f225402c9cea9255f377428
13
13
  ---
14
14
 
@@ -13,7 +13,7 @@ tools:
13
13
  - Edit
14
14
  - Glob
15
15
  model: sonnet
16
- token_pilot_version: "0.32.0"
16
+ token_pilot_version: "0.33.0"
17
17
  token_pilot_body_hash: 68b32af2dacd82ebe52c4eec93edb903d452688274c3065218270627c564d8b0
18
18
  ---
19
19
 
@@ -11,7 +11,7 @@ tools:
11
11
  - Grep
12
12
  - Read
13
13
  model: sonnet
14
- token_pilot_version: "0.32.0"
14
+ token_pilot_version: "0.33.0"
15
15
  token_pilot_body_hash: d9b7f5b7ae6f4ae21305c775361bcab097cc774370a6d976c093571d46d55021
16
16
  ---
17
17
 
@@ -12,7 +12,7 @@ tools:
12
12
  - Read
13
13
  - Bash
14
14
  model: sonnet
15
- token_pilot_version: "0.32.0"
15
+ token_pilot_version: "0.33.0"
16
16
  token_pilot_body_hash: 052413de8d92377edcde6ae5c823f5378db304baccfa29e8866467f42553a500
17
17
  ---
18
18
 
@@ -9,7 +9,7 @@ tools:
9
9
  - Bash
10
10
  - Read
11
11
  model: haiku
12
- token_pilot_version: "0.32.0"
12
+ token_pilot_version: "0.33.0"
13
13
  token_pilot_body_hash: e14dc57493d816f8c2e017963e2ef5f66bea50fd0b805a80e8a0d97c968427e7
14
14
  ---
15
15
 
@@ -13,7 +13,7 @@ tools:
13
13
  - Edit
14
14
  - Glob
15
15
  model: haiku
16
- token_pilot_version: "0.32.0"
16
+ token_pilot_version: "0.33.0"
17
17
  token_pilot_body_hash: 57d741794ab40e31a7ac49c68ea39a9088f5827cdef866ce81bfca1b7c9180cf
18
18
  ---
19
19
 
@@ -10,7 +10,7 @@ tools:
10
10
  - Bash
11
11
  - Read
12
12
  model: haiku
13
- token_pilot_version: "0.32.0"
13
+ token_pilot_version: "0.33.0"
14
14
  token_pilot_body_hash: 7b70fa76a60e3c58a1de4f56c32c0f166424137e203a0cf1c8654e7c9235d904
15
15
  ---
16
16
 
@@ -12,7 +12,7 @@ tools:
12
12
  - mcp__token-pilot__read_symbols
13
13
  - Read
14
14
  model: sonnet
15
- token_pilot_version: "0.32.0"
15
+ token_pilot_version: "0.33.0"
16
16
  token_pilot_body_hash: 351a987e11eba63852f5431a16d8eb53104f4f689f82fdcc5a2bf4db948ba92f
17
17
  ---
18
18
 
@@ -8,7 +8,7 @@ tools:
8
8
  - mcp__token-pilot__read_symbol
9
9
  - Bash
10
10
  model: inherit
11
- token_pilot_version: "0.32.0"
11
+ token_pilot_version: "0.33.0"
12
12
  token_pilot_body_hash: de5722bfea374eaab096c1ae635c37879e7a91370ee3cd0532f4240be03c91eb
13
13
  ---
14
14
 
@@ -13,7 +13,7 @@ tools:
13
13
  - Edit
14
14
  - Bash
15
15
  model: sonnet
16
- token_pilot_version: "0.32.0"
16
+ token_pilot_version: "0.33.0"
17
17
  token_pilot_body_hash: 375a824d0d847bb5453ec594c7a62ad566ee7e4d92717b0473f771f1a0477c60
18
18
  ---
19
19
 
@@ -11,7 +11,7 @@ tools:
11
11
  - Grep
12
12
  - Glob
13
13
  model: sonnet
14
- token_pilot_version: "0.32.0"
14
+ token_pilot_version: "0.33.0"
15
15
  token_pilot_body_hash: 0334de1bf99b431b65359637d125cda7c44c6f780eb92c57cc538715b1939536
16
16
  ---
17
17
 
@@ -10,7 +10,7 @@ tools:
10
10
  - mcp__token-pilot__smart_read
11
11
  - mcp__token-pilot__smart_read_many
12
12
  - mcp__token-pilot__read_section
13
- token_pilot_version: "0.32.0"
13
+ token_pilot_version: "0.33.0"
14
14
  token_pilot_body_hash: 832e95633fbc8e9b0c10f3e540a327d4be062fb4b3f17a6cce6be13f414e2927
15
15
  ---
16
16
 
@@ -11,7 +11,7 @@ tools:
11
11
  - Bash
12
12
  - Read
13
13
  model: sonnet
14
- token_pilot_version: "0.32.0"
14
+ token_pilot_version: "0.33.0"
15
15
  token_pilot_body_hash: b61f06380d80798fa2e49d37bcba0653495bee04dd6bdbc1feff9a75607b0508
16
16
  ---
17
17
 
@@ -11,7 +11,7 @@ tools:
11
11
  - mcp__token-pilot__read_for_edit
12
12
  - Read
13
13
  model: sonnet
14
- token_pilot_version: "0.32.0"
14
+ token_pilot_version: "0.33.0"
15
15
  token_pilot_body_hash: f83f50d05b4f70285ae7afed2b1a406fc436df56e61a0aedbfb31edc7f2b6e66
16
16
  ---
17
17
 
@@ -8,7 +8,7 @@ tools:
8
8
  - mcp__token-pilot__outline
9
9
  - mcp__token-pilot__read_symbol
10
10
  model: sonnet
11
- token_pilot_version: "0.32.0"
11
+ token_pilot_version: "0.33.0"
12
12
  token_pilot_body_hash: c5f6fc122c89e16e5cf774045f92169ee3468555320b898171ba13eca5323550
13
13
  ---
14
14
 
@@ -9,7 +9,7 @@ tools:
9
9
  - mcp__token-pilot__module_info
10
10
  - Bash
11
11
  model: sonnet
12
- token_pilot_version: "0.32.0"
12
+ token_pilot_version: "0.33.0"
13
13
  token_pilot_body_hash: 8ef3c3341cbfed4eb8dd130126a9683edc57e378c92ff0ca764d584fd941c55c
14
14
  ---
15
15
 
package/agents/tp-run.md CHANGED
@@ -16,7 +16,7 @@ tools:
16
16
  - Glob
17
17
  - Bash
18
18
  model: haiku
19
- token_pilot_version: "0.32.0"
19
+ token_pilot_version: "0.33.0"
20
20
  token_pilot_body_hash: 2b08618d34a61f00aafccbda9fed6d83243296dedb83440edbd2d5c28bb6dbc4
21
21
  ---
22
22
 
@@ -9,7 +9,7 @@ tools:
9
9
  - mcp__token-pilot__session_budget
10
10
  - Bash
11
11
  - Read
12
- token_pilot_version: "0.32.0"
12
+ token_pilot_version: "0.33.0"
13
13
  token_pilot_body_hash: 529374ed728f5eed5b758b3be3da65624783c0bf0c1a253d7d661a843eb5f767
14
14
  ---
15
15
 
@@ -11,7 +11,7 @@ tools:
11
11
  - Read
12
12
  - Grep
13
13
  model: sonnet
14
- token_pilot_version: "0.32.0"
14
+ token_pilot_version: "0.33.0"
15
15
  token_pilot_body_hash: a60f6ae110eb3138064bce074e8ba26fa0ce5f4659df1624a9d9d3646803391b
16
16
  ---
17
17
 
@@ -9,7 +9,7 @@ tools:
9
9
  - Read
10
10
  - Write
11
11
  model: sonnet
12
- token_pilot_version: "0.32.0"
12
+ token_pilot_version: "0.33.0"
13
13
  token_pilot_body_hash: c7a4e8b39228fd5158528f389c924c5ff2d98c4b9b05ee0106d54a26c5dc1350
14
14
  ---
15
15
 
@@ -10,7 +10,7 @@ tools:
10
10
  - mcp__token-pilot__test_summary
11
11
  - Glob
12
12
  - Grep
13
- token_pilot_version: "0.32.0"
13
+ token_pilot_version: "0.33.0"
14
14
  token_pilot_body_hash: be81eed53a3720d146cf89e4a14a7a56577633f7c84c234c412ab70d64c05b11
15
15
  ---
16
16
 
@@ -8,7 +8,7 @@ tools:
8
8
  - mcp__token-pilot__find_usages
9
9
  - mcp__token-pilot__read_symbol
10
10
  model: sonnet
11
- token_pilot_version: "0.32.0"
11
+ token_pilot_version: "0.33.0"
12
12
  token_pilot_body_hash: 362ecf4cb03b059421ea26933473700900073dc38b3a7fe271208dfb1ae14f90
13
13
  ---
14
14
 
@@ -13,7 +13,7 @@ tools:
13
13
  - Edit
14
14
  - Bash
15
15
  model: sonnet
16
- token_pilot_version: "0.32.0"
16
+ token_pilot_version: "0.33.0"
17
17
  token_pilot_body_hash: 269f2fe22ff4517c277d3f56ca67d8a5527b93290ab21079a83ba7af22c1b5a9
18
18
  ---
19
19
 
@@ -739,8 +739,24 @@ export class AstIndexClient {
739
739
  this.indexed = false;
740
740
  }
741
741
  async exec(args, timeoutMs) {
742
+ // v0.33.0 (B10) — lazy retry. server.ts calls init() at startup,
743
+ // but it can fail silently (binary download flaky, permissions
744
+ // on shared FS, or the user invoked us before postinstall ran).
745
+ // Subsequent MCP calls would otherwise throw "not initialized"
746
+ // forever. Try once more here; on failure surface a friendlier
747
+ // error pointing at the install command.
742
748
  if (!this.binaryPath) {
743
- throw new Error("ast-index not initialized. Call init() first.");
749
+ try {
750
+ await this.init();
751
+ }
752
+ catch (err) {
753
+ const msg = err instanceof Error ? err.message : String(err);
754
+ throw new Error(`ast-index not initialized — auto-init also failed: ${msg}\n` +
755
+ `Run: npx token-pilot install-ast-index`);
756
+ }
757
+ if (!this.binaryPath) {
758
+ throw new Error("ast-index not initialized. Run `npx token-pilot install-ast-index`.");
759
+ }
744
760
  }
745
761
  // ast-index v3.39+ honours AST_INDEX_WALK_UP=1 — read-commands then
746
762
  // traverse past nested VCS markers (submodule .git, inner Cargo.toml,
@@ -55,6 +55,24 @@ export declare function installAgents(opts: InstallOptions): Promise<InstallResu
55
55
  * levels up to reach the repo/package root, then into `agents/`.
56
56
  */
57
57
  export declare function resolveDistAgentsDir(scriptUrl: string): string;
58
+ /**
59
+ * v0.33.0 — locate the freshest tp-* template source available on disk.
60
+ *
61
+ * The plugin cache (`~/.claude/plugins/cache/token-pilot/token-pilot/<v>/agents/`)
62
+ * almost always trails or matches the version of the running binary, but
63
+ * users with a stale npm-global token-pilot (e.g. v0.20.1 — see beads B1)
64
+ * end up running a binary whose own `agents/` directory contains an old
65
+ * subset of templates while the plugin cache holds the up-to-date 25-agent
66
+ * set. Probe both, pick whichever has the higher version (or, when
67
+ * versions cannot be read, the more complete set).
68
+ *
69
+ * Pure: never throws — returns the script-relative dir as fallback when
70
+ * the cache cannot be read.
71
+ */
72
+ export declare function pickFreshestAgentsSource(scriptUrl: string, homeDir: string): Promise<{
73
+ dir: string;
74
+ source: "self" | "plugin-cache";
75
+ }>;
58
76
  /**
59
77
  * Yes/no prompt used by other CLI entry points that want to offer
60
78
  * an opt-in step (e.g. `token-pilot init` → "install agents now?").
@@ -189,6 +189,80 @@ export function resolveDistAgentsDir(scriptUrl) {
189
189
  const here = dirname(fileURLToPath(scriptUrl));
190
190
  return join(here, "..", "..", "agents");
191
191
  }
192
+ /**
193
+ * Compare two semver-ish version strings. Returns positive if `a > b`,
194
+ * negative if `a < b`, 0 if equal. Tolerates non-semver suffixes by
195
+ * stripping anything past `-` or `+`. Pre-release / metadata are
196
+ * ignored. Used only for picking the highest version of a directory
197
+ * sibling list — bullet-proofness not required.
198
+ */
199
+ function compareVersions(a, b) {
200
+ const norm = (s) => s
201
+ .split(/[-+]/)[0]
202
+ .split(".")
203
+ .map((p) => (Number.isFinite(parseInt(p, 10)) ? parseInt(p, 10) : 0));
204
+ const [ax, ay, az] = norm(a);
205
+ const [bx, by, bz] = norm(b);
206
+ if (ax !== bx)
207
+ return ax - bx;
208
+ if (ay !== by)
209
+ return ay - by;
210
+ return az - bz;
211
+ }
212
+ /**
213
+ * v0.33.0 — locate the freshest tp-* template source available on disk.
214
+ *
215
+ * The plugin cache (`~/.claude/plugins/cache/token-pilot/token-pilot/<v>/agents/`)
216
+ * almost always trails or matches the version of the running binary, but
217
+ * users with a stale npm-global token-pilot (e.g. v0.20.1 — see beads B1)
218
+ * end up running a binary whose own `agents/` directory contains an old
219
+ * subset of templates while the plugin cache holds the up-to-date 25-agent
220
+ * set. Probe both, pick whichever has the higher version (or, when
221
+ * versions cannot be read, the more complete set).
222
+ *
223
+ * Pure: never throws — returns the script-relative dir as fallback when
224
+ * the cache cannot be read.
225
+ */
226
+ export async function pickFreshestAgentsSource(scriptUrl, homeDir) {
227
+ const selfDir = resolveDistAgentsDir(scriptUrl);
228
+ const cacheRoot = join(homeDir, ".claude", "plugins", "cache", "token-pilot", "token-pilot");
229
+ let cacheVersion = null;
230
+ let cacheDir = null;
231
+ try {
232
+ const versions = (await readdir(cacheRoot)).filter((v) => /\d+\.\d+/.test(v));
233
+ if (versions.length > 0) {
234
+ versions.sort(compareVersions);
235
+ cacheVersion = versions[versions.length - 1];
236
+ cacheDir = join(cacheRoot, cacheVersion, "agents");
237
+ }
238
+ }
239
+ catch {
240
+ /* no plugin cache — keep self */
241
+ }
242
+ if (!cacheDir)
243
+ return { dir: selfDir, source: "self" };
244
+ // Compare counts of tp-*.md to spot the obvious "stale binary" case
245
+ // where the running CLI is old but plugin cache is current.
246
+ let selfCount = 0;
247
+ let cacheCount = 0;
248
+ try {
249
+ selfCount = (await readdir(selfDir)).filter((f) => f.endsWith(".md") && f.startsWith("tp-")).length;
250
+ }
251
+ catch {
252
+ /* selfDir missing — pick cache */
253
+ }
254
+ try {
255
+ cacheCount = (await readdir(cacheDir)).filter((f) => f.endsWith(".md") && f.startsWith("tp-")).length;
256
+ }
257
+ catch {
258
+ return { dir: selfDir, source: "self" };
259
+ }
260
+ if (cacheCount > selfCount) {
261
+ return { dir: cacheDir, source: "plugin-cache" };
262
+ }
263
+ // Tie or self has more — keep self (newer un-released templates win).
264
+ return { dir: selfDir, source: "self" };
265
+ }
192
266
  /** Read one line from an interactive TTY prompt. */
193
267
  async function promptLine(question) {
194
268
  process.stderr.write(question);
@@ -356,7 +430,20 @@ export async function handleInstallAgents(argv, opts) {
356
430
  scope = await promptScope();
357
431
  }
358
432
  }
359
- const distAgentsDir = opts?.distAgentsDir ?? resolveDistAgentsDir(import.meta.url);
433
+ // v0.33.0 (B1+B3) explicit override wins; otherwise probe the
434
+ // plugin cache and pick whichever has more tp-*.md templates. This
435
+ // protects users with stale npm-global token-pilot from copying
436
+ // an outdated 6-agent subset when the bundled plugin already
437
+ // shipped the full 25.
438
+ let distAgentsDir = opts?.distAgentsDir;
439
+ if (!distAgentsDir) {
440
+ const picked = await pickFreshestAgentsSource(import.meta.url, homeDir);
441
+ distAgentsDir = picked.dir;
442
+ if (picked.source === "plugin-cache") {
443
+ process.stderr.write(`[token-pilot] using agents from plugin cache: ${distAgentsDir}\n` +
444
+ ` (running CLI's own agents/ has fewer templates — bundled plugin is fresher).\n`);
445
+ }
446
+ }
360
447
  try {
361
448
  const result = await installAgents({
362
449
  scope,
package/dist/cli/stats.js CHANGED
@@ -11,7 +11,7 @@
11
11
  * formatStats() is pure (events in → string out) so tests drive it
12
12
  * directly without touching the filesystem.
13
13
  */
14
- import { loadEvents } from "../core/event-log.js";
14
+ import { loadEvents, loadEventsTree, } from "../core/event-log.js";
15
15
  function sumSaved(events) {
16
16
  return events.reduce((sum, e) => sum + e.savedTokens, 0);
17
17
  }
@@ -161,7 +161,14 @@ function parseFlag(argv, key) {
161
161
  */
162
162
  export async function handleStats(argv, opts) {
163
163
  const projectRoot = opts?.projectRoot ?? process.cwd();
164
- const events = await loadEvents(projectRoot);
164
+ // v0.33.0 (B5) `--no-merge` disables the repo-tree walk and reads
165
+ // only the top-level `.token-pilot/hook-events.jsonl`. Default is to
166
+ // merge events from every subdirectory log so monorepo subdir
167
+ // sessions show up in the same totals.
168
+ const noMerge = argv.includes("--no-merge");
169
+ const events = noMerge
170
+ ? await loadEvents(projectRoot)
171
+ : await loadEventsTree(projectRoot);
165
172
  const session = parseFlag(argv, "session");
166
173
  const byAgent = parseFlag(argv, "by-agent");
167
174
  const tasks = parseFlag(argv, "tasks");
@@ -0,0 +1,86 @@
1
+ /**
2
+ * v0.34.0 — error / diagnostic channel for token-pilot hooks + CLI.
3
+ *
4
+ * Why a separate file from `hook-events.jsonl`:
5
+ * - hook-events lives in `<projectRoot>/.token-pilot/`. When the hook
6
+ * itself fails BEFORE projectRoot is resolved (B8 WSL detection,
7
+ * missing dir, ENOENT), there is nowhere to write the regular log.
8
+ * - Errors must outlive a single project — when a user reports
9
+ * "nothing logs anymore" we want one absolute path to look at.
10
+ *
11
+ * Layout:
12
+ * ~/.token-pilot/hook-errors.jsonl
13
+ *
14
+ * Format: one JSON record per line. Schema in `HookErrorRecord` below.
15
+ *
16
+ * Discipline:
17
+ * - Never throws. The error logger is itself the last line of defence —
18
+ * a throw here would defeat the wrapper that calls it.
19
+ * - Cap-and-rotate: when the file passes MAX_BYTES the writer renames
20
+ * it to `hook-errors.<ts>.jsonl` and starts fresh. Old archives
21
+ * past RETENTION_MS are pruned best-effort on each append.
22
+ * - `TOKEN_PILOT_NO_ERROR_LOG=1` opts out entirely.
23
+ *
24
+ * Privacy:
25
+ * - The `input` field is whatever the hook chose to record. Callers
26
+ * MUST sanitize before passing it in — no full paths, no file
27
+ * content, no prompts. Helpers `safeBasename()` / `safePathInfo()`
28
+ * are provided for the common cases.
29
+ */
30
+ import { existsSync } from "node:fs";
31
+ import { dirname, resolve } from "node:path";
32
+ export type ErrorLevel = "info" | "warn" | "error";
33
+ export interface HookErrorRecord {
34
+ ts: number;
35
+ hook: string;
36
+ level: ErrorLevel;
37
+ code: string;
38
+ msg: string;
39
+ stack?: string;
40
+ input?: Record<string, unknown>;
41
+ pluginVersion?: string;
42
+ nodeVersion?: string;
43
+ platform?: NodeJS.Platform;
44
+ }
45
+ export declare function errorLogDir(): string;
46
+ export declare function errorLogPath(): string;
47
+ /**
48
+ * Reduce a path to its basename. Use everywhere a path could leak:
49
+ * the user's project tree, file content, anything not strictly an
50
+ * identifier. Returns `"<empty>"` for missing input rather than null
51
+ * so the field stays shape-stable.
52
+ */
53
+ export declare function safeBasename(p: unknown): string;
54
+ /**
55
+ * Capture only the metadata about a path — basename + length + ext —
56
+ * dropping the absolute path entirely. Useful when the analysis
57
+ * benefits from "kind of file" without revealing where it lived.
58
+ */
59
+ export declare function safePathInfo(p: unknown): {
60
+ name: string;
61
+ ext: string;
62
+ };
63
+ /**
64
+ * Map a thrown value to a stable, searchable `code`. The classifier
65
+ * is intentionally simple — node ErrnoException codes pass through,
66
+ * everything else falls into a coarse bucket. New cases land here
67
+ * only when a pattern shows up repeatedly in the wild.
68
+ */
69
+ export declare function classifyError(err: unknown): string;
70
+ export declare function appendError(rec: HookErrorRecord): Promise<void>;
71
+ export interface LoadErrorsOptions {
72
+ /** Limit to the last N records (most recent first). */
73
+ tail?: number;
74
+ /** Filter by `code`. */
75
+ code?: string;
76
+ /** Filter by `hook` name. */
77
+ hook?: string;
78
+ /** Filter by `level`. */
79
+ level?: ErrorLevel;
80
+ /** Override the default current-log path (testing). */
81
+ path?: string;
82
+ }
83
+ export declare function loadErrors(opts?: LoadErrorsOptions): Promise<HookErrorRecord[]>;
84
+ export declare function formatErrorList(records: HookErrorRecord[]): string;
85
+ export { existsSync, resolve, dirname };
86
+ //# sourceMappingURL=error-log.d.ts.map