token-pilot 0.32.0 → 0.33.1
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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/agents/tp-api-surface-tracker.md +1 -1
- package/agents/tp-audit-scanner.md +1 -1
- package/agents/tp-commit-writer.md +1 -1
- package/agents/tp-context-engineer.md +1 -1
- package/agents/tp-dead-code-finder.md +1 -1
- package/agents/tp-debugger.md +1 -1
- package/agents/tp-dep-health.md +1 -1
- package/agents/tp-doc-writer.md +1 -1
- package/agents/tp-history-explorer.md +1 -1
- package/agents/tp-impact-analyzer.md +1 -1
- package/agents/tp-incident-timeline.md +1 -1
- package/agents/tp-incremental-builder.md +1 -1
- package/agents/tp-migration-scout.md +1 -1
- package/agents/tp-onboard.md +1 -1
- package/agents/tp-performance-profiler.md +1 -1
- package/agents/tp-pr-reviewer.md +1 -1
- package/agents/tp-refactor-planner.md +1 -1
- package/agents/tp-review-impact.md +1 -1
- package/agents/tp-run.md +1 -1
- package/agents/tp-session-restorer.md +1 -1
- package/agents/tp-ship-coordinator.md +1 -1
- package/agents/tp-spec-writer.md +1 -1
- package/agents/tp-test-coverage-gapper.md +1 -1
- package/agents/tp-test-triage.md +1 -1
- package/agents/tp-test-writer.md +1 -1
- package/dist/ast-index/client.js +17 -1
- package/dist/cli/install-agents.d.ts +18 -0
- package/dist/cli/install-agents.js +88 -1
- package/dist/cli/stats.js +9 -2
- package/dist/cli/typo-guard.d.ts +1 -1
- package/dist/cli/typo-guard.js +9 -0
- package/dist/core/error-log.d.ts +86 -0
- package/dist/core/error-log.js +228 -0
- package/dist/core/event-log.d.ts +49 -1
- package/dist/core/event-log.js +114 -0
- package/dist/core/validation.d.ts +12 -0
- package/dist/core/validation.js +38 -8
- package/dist/handlers/smart-log.js +7 -2
- package/dist/hooks/installer.d.ts +40 -0
- package/dist/hooks/installer.js +145 -2
- package/dist/hooks/pre-task.js +44 -10
- package/dist/hooks/safe-runner.d.ts +48 -0
- package/dist/hooks/safe-runner.js +73 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +284 -63
- package/package.json +1 -1
package/dist/hooks/installer.js
CHANGED
|
@@ -12,6 +12,40 @@ function buildHookCommand(action, options) {
|
|
|
12
12
|
}
|
|
13
13
|
return `token-pilot ${action}`;
|
|
14
14
|
}
|
|
15
|
+
/**
|
|
16
|
+
* Detect a stale token-pilot hook command — one that points at a
|
|
17
|
+
* pinned npx-cache snapshot (`npx/_npx/<hash>/...`) or any other
|
|
18
|
+
* version-pinned path that won't follow plugin upgrades.
|
|
19
|
+
*
|
|
20
|
+
* v0.33.0 fix: users who ran `npx token-pilot init` early on got
|
|
21
|
+
* settings.json entries with literal `~/.npm/_npx/<hash>/...` paths.
|
|
22
|
+
* When the npx cache rotates or token-pilot publishes a new minor,
|
|
23
|
+
* those entries silently call the old binary, missing every hook
|
|
24
|
+
* shipped after install (e.g. v0.31.0 Task hooks). Removing the
|
|
25
|
+
* stale entry lets the next install or the bundled plugin's
|
|
26
|
+
* `hooks/hooks.json` (CLAUDE_PLUGIN_ROOT) take over.
|
|
27
|
+
*/
|
|
28
|
+
export function isStaleTokenPilotHookCommand(cmd) {
|
|
29
|
+
if (typeof cmd !== "string")
|
|
30
|
+
return false;
|
|
31
|
+
if (!cmd.includes("token-pilot"))
|
|
32
|
+
return false;
|
|
33
|
+
// npm/npx cache hash — always stale (will rotate)
|
|
34
|
+
if (/\/_npx\/[0-9a-f]+\//.test(cmd))
|
|
35
|
+
return true;
|
|
36
|
+
// Pinned plugin-cache version path — old version that may not
|
|
37
|
+
// contain a hook handler the new settings entry references.
|
|
38
|
+
// Match `/plugins/cache/token-pilot/token-pilot/<version>/`.
|
|
39
|
+
const pinned = cmd.match(/\/plugins\/cache\/token-pilot\/token-pilot\/([^/]+)\//);
|
|
40
|
+
if (pinned) {
|
|
41
|
+
// The plugin runtime always uses ${CLAUDE_PLUGIN_ROOT} which
|
|
42
|
+
// resolves to the *current* version dir. A literal version in
|
|
43
|
+
// the path means someone wrote it from a CLI that captured the
|
|
44
|
+
// dir at install time — stale by definition.
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
15
49
|
function createHookConfig(options) {
|
|
16
50
|
return {
|
|
17
51
|
hooks: {
|
|
@@ -154,9 +188,11 @@ export async function installHook(projectRoot, options) {
|
|
|
154
188
|
const isTokenPilotHook = (h) => h.hooks?.some((hook) => hook.command?.includes("token-pilot"));
|
|
155
189
|
if (Array.isArray(existingHooks)) {
|
|
156
190
|
// Remove old broken hooks (bare "token-pilot" without absolute path)
|
|
157
|
-
//
|
|
191
|
+
// OR stale npx-cache / pinned-version paths (v0.33.0)
|
|
192
|
+
// and replace with working ones using absolute paths.
|
|
158
193
|
const oldBrokenHooks = existingHooks.filter((h) => isTokenPilotHook(h) &&
|
|
159
|
-
h.hooks?.some((hook) => hook.command?.match(/^token-pilot\s/)
|
|
194
|
+
h.hooks?.some((hook) => hook.command?.match(/^token-pilot\s/) ||
|
|
195
|
+
isStaleTokenPilotHookCommand(hook.command)));
|
|
160
196
|
if (oldBrokenHooks.length > 0 && options?.scriptPath) {
|
|
161
197
|
// Remove old broken hooks, will re-add with absolute paths below
|
|
162
198
|
settings.hooks.PreToolUse = existingHooks.filter((h) => !isTokenPilotHook(h));
|
|
@@ -309,4 +345,111 @@ export async function uninstallHook(projectRoot) {
|
|
|
309
345
|
};
|
|
310
346
|
}
|
|
311
347
|
}
|
|
348
|
+
/**
|
|
349
|
+
* Scan a settings.json (user-level or project-level) and remove every
|
|
350
|
+
* token-pilot hook entry whose command points at a pinned npx-cache
|
|
351
|
+
* snapshot or a literal plugin-cache version path. The plugin's bundled
|
|
352
|
+
* `hooks/hooks.json` (resolved through `${CLAUDE_PLUGIN_ROOT}` at
|
|
353
|
+
* runtime) supersedes them.
|
|
354
|
+
*
|
|
355
|
+
* Pure-ish: writes only when something changed. Never throws — bad JSON
|
|
356
|
+
* or missing file are reported in the result so callers (CLI, init)
|
|
357
|
+
* can surface them without aborting.
|
|
358
|
+
*/
|
|
359
|
+
export async function cleanStaleHookEntries(settingsPath) {
|
|
360
|
+
const result = {
|
|
361
|
+
scanned: [settingsPath],
|
|
362
|
+
cleaned: [],
|
|
363
|
+
staleEntriesRemoved: 0,
|
|
364
|
+
message: "",
|
|
365
|
+
};
|
|
366
|
+
let raw;
|
|
367
|
+
try {
|
|
368
|
+
raw = await readFile(settingsPath, "utf-8");
|
|
369
|
+
}
|
|
370
|
+
catch (err) {
|
|
371
|
+
if (err?.code === "ENOENT") {
|
|
372
|
+
result.message = `No settings at ${settingsPath} — nothing to migrate.`;
|
|
373
|
+
return result;
|
|
374
|
+
}
|
|
375
|
+
result.message = `Cannot read ${settingsPath}: ${err?.message ?? err}`;
|
|
376
|
+
return result;
|
|
377
|
+
}
|
|
378
|
+
let settings;
|
|
379
|
+
try {
|
|
380
|
+
settings = JSON.parse(raw);
|
|
381
|
+
}
|
|
382
|
+
catch {
|
|
383
|
+
result.message = `Invalid JSON in ${settingsPath} — skipped (fix manually).`;
|
|
384
|
+
return result;
|
|
385
|
+
}
|
|
386
|
+
const sections = ["PreToolUse", "PostToolUse", "SessionStart"];
|
|
387
|
+
let removed = 0;
|
|
388
|
+
for (const section of sections) {
|
|
389
|
+
const arr = settings.hooks?.[section];
|
|
390
|
+
if (!Array.isArray(arr))
|
|
391
|
+
continue;
|
|
392
|
+
const filtered = arr.filter((entry) => {
|
|
393
|
+
const inner = Array.isArray(entry?.hooks) ? entry.hooks : [];
|
|
394
|
+
const hasStale = inner.some((h) => isStaleTokenPilotHookCommand(h?.command));
|
|
395
|
+
if (hasStale) {
|
|
396
|
+
removed++;
|
|
397
|
+
return false;
|
|
398
|
+
}
|
|
399
|
+
return true;
|
|
400
|
+
});
|
|
401
|
+
if (filtered.length !== arr.length) {
|
|
402
|
+
if (filtered.length === 0) {
|
|
403
|
+
delete settings.hooks[section];
|
|
404
|
+
}
|
|
405
|
+
else {
|
|
406
|
+
settings.hooks[section] = filtered;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
if (removed === 0) {
|
|
411
|
+
result.message = `No stale token-pilot hook entries in ${settingsPath}.`;
|
|
412
|
+
return result;
|
|
413
|
+
}
|
|
414
|
+
// Drop empty hooks container so JSON stays clean.
|
|
415
|
+
if (settings.hooks &&
|
|
416
|
+
typeof settings.hooks === "object" &&
|
|
417
|
+
Object.keys(settings.hooks).length === 0) {
|
|
418
|
+
delete settings.hooks;
|
|
419
|
+
}
|
|
420
|
+
await writeFile(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
421
|
+
result.cleaned.push(settingsPath);
|
|
422
|
+
result.staleEntriesRemoved = removed;
|
|
423
|
+
result.message = `Removed ${removed} stale token-pilot hook entr${removed === 1 ? "y" : "ies"} from ${settingsPath}.`;
|
|
424
|
+
return result;
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* Inspect `~/.claude/settings.json` to determine whether the user has
|
|
428
|
+
* enabled the bundled `token-pilot` plugin in Claude Code. When true,
|
|
429
|
+
* the plugin's own `hooks/hooks.json` is the source of truth and any
|
|
430
|
+
* additional hook entries written by the npm CLI are duplicates that
|
|
431
|
+
* also lock the user to whichever binary path the CLI captured.
|
|
432
|
+
*/
|
|
433
|
+
export async function isTokenPilotPluginEnabled(homeDir) {
|
|
434
|
+
const settingsPath = resolve(homeDir, ".claude", "settings.json");
|
|
435
|
+
let raw;
|
|
436
|
+
try {
|
|
437
|
+
raw = await readFile(settingsPath, "utf-8");
|
|
438
|
+
}
|
|
439
|
+
catch {
|
|
440
|
+
return false;
|
|
441
|
+
}
|
|
442
|
+
let settings;
|
|
443
|
+
try {
|
|
444
|
+
settings = JSON.parse(raw);
|
|
445
|
+
}
|
|
446
|
+
catch {
|
|
447
|
+
return false;
|
|
448
|
+
}
|
|
449
|
+
const enabled = settings?.enabledPlugins;
|
|
450
|
+
if (!enabled || typeof enabled !== "object")
|
|
451
|
+
return false;
|
|
452
|
+
// keys look like `token-pilot@token-pilot` — match prefix.
|
|
453
|
+
return Object.entries(enabled).some(([key, val]) => val === true && typeof key === "string" && key.startsWith("token-pilot@"));
|
|
454
|
+
}
|
|
312
455
|
//# sourceMappingURL=installer.js.map
|
package/dist/hooks/pre-task.js
CHANGED
|
@@ -54,6 +54,19 @@ function containsEscape(description) {
|
|
|
54
54
|
const n = description.toLowerCase();
|
|
55
55
|
return ESCAPE_PHRASES.some((p) => n.includes(p));
|
|
56
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* v0.33.0 (B14) — generic context appended to every advice payload
|
|
59
|
+
* for non-tp-* dispatches. Subagents like `general-purpose` and
|
|
60
|
+
* `code-analyzer` don't know about the token-pilot MCP tools and
|
|
61
|
+
* loop on raw `Read` even after `hook-pre-read` denies them. This
|
|
62
|
+
* paragraph lands in their context window before they take their
|
|
63
|
+
* first action and tells them what to use instead.
|
|
64
|
+
*/
|
|
65
|
+
const SUBAGENT_TOOL_GUIDE = "When working in this task: prefer `mcp__token-pilot__smart_read` " +
|
|
66
|
+
"(file structure), `read_symbol` (one function/class), and " +
|
|
67
|
+
"`find_usages` (semantic search) over raw Read/Grep. The token-pilot " +
|
|
68
|
+
"PreToolUse hooks block large-file Read and unbounded Grep — use " +
|
|
69
|
+
"the MCP tools or pass `offset`/`limit` to Read.";
|
|
57
70
|
/**
|
|
58
71
|
* Pure decision function. Caller resolves all context (env, mode,
|
|
59
72
|
* agent index) up front so this stays a plain input → output mapping.
|
|
@@ -67,23 +80,44 @@ export function decidePreTask(input, ctx) {
|
|
|
67
80
|
if (typeof subagentType === "string" && subagentType.startsWith("tp-")) {
|
|
68
81
|
return { kind: "allow" };
|
|
69
82
|
}
|
|
70
|
-
//
|
|
71
|
-
//
|
|
72
|
-
|
|
73
|
-
|
|
83
|
+
// v0.33.0 (B4) — TOKEN_PILOT_FORCE_SUBAGENTS=1 with an empty agent
|
|
84
|
+
// catalog used to silently allow every Task call (no matches → no
|
|
85
|
+
// suggestion). That defeats the env's only purpose. Fail loud
|
|
86
|
+
// instead: tell the user to install the templates.
|
|
87
|
+
const indexEmpty = !ctx.agentIndex.agents || ctx.agentIndex.agents.length === 0;
|
|
88
|
+
if (ctx.force && indexEmpty) {
|
|
89
|
+
return {
|
|
90
|
+
kind: "deny",
|
|
91
|
+
reason: "TOKEN_PILOT_FORCE_SUBAGENTS=1 is set but no tp-* agents are " +
|
|
92
|
+
"installed in this project (or `~/.claude/agents/`). " +
|
|
93
|
+
"Run `npx token-pilot install-agents --scope=project` first, " +
|
|
94
|
+
"or unset TOKEN_PILOT_FORCE_SUBAGENTS.",
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
// No description → nothing to match against. Inject the generic
|
|
98
|
+
// tool-guide so the subagent still picks tp-tools (B14).
|
|
99
|
+
if (!description || description.length === 0) {
|
|
100
|
+
return { kind: "advise", message: SUBAGENT_TOOL_GUIDE };
|
|
101
|
+
}
|
|
74
102
|
// Author-blessed escape clauses — user is explicitly saying
|
|
75
|
-
// "this is broad".
|
|
76
|
-
if (containsEscape(description))
|
|
77
|
-
return { kind: "
|
|
103
|
+
// "this is broad". Inject the tool-guide but no agent suggestion.
|
|
104
|
+
if (containsEscape(description)) {
|
|
105
|
+
return { kind: "advise", message: SUBAGENT_TOOL_GUIDE };
|
|
106
|
+
}
|
|
78
107
|
const hit = matchTpAgent(description, ctx.agentIndex);
|
|
79
|
-
if (!hit)
|
|
80
|
-
|
|
108
|
+
if (!hit) {
|
|
109
|
+
// No specific tp-* match. Still send the generic tool-guide so
|
|
110
|
+
// the subagent learns about smart_read / read_symbol — covers the
|
|
111
|
+
// common code-analyzer / general-purpose loop on raw Read (B14).
|
|
112
|
+
return { kind: "advise", message: SUBAGENT_TOOL_GUIDE };
|
|
113
|
+
}
|
|
81
114
|
const suggestion = `Consider dispatching \`${hit.agent}\` instead of \`${subagentType || "general-purpose"}\` — ` +
|
|
82
115
|
`the description matches its trigger phrases (confidence: ${hit.confidence}). ` +
|
|
83
116
|
`tp-* agents run under a tighter budget and output in terse style, typically ` +
|
|
84
117
|
`~50-70 % fewer tokens than general-purpose. ` +
|
|
85
118
|
`Escape: add "ad-hoc" or "open-ended" to the description to bypass, or set ` +
|
|
86
|
-
`TOKEN_PILOT_MODE=advisory for warn-only behaviour
|
|
119
|
+
`TOKEN_PILOT_MODE=advisory for warn-only behaviour.\n\n` +
|
|
120
|
+
SUBAGENT_TOOL_GUIDE;
|
|
87
121
|
const hardBlock = ctx.force ||
|
|
88
122
|
ctx.mode === "strict" ||
|
|
89
123
|
(ctx.mode === "deny" && hit.confidence === "high" && ctx.force);
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* v0.34.0 — `runHookSafely` / `runHookEntryPoint`.
|
|
3
|
+
*
|
|
4
|
+
* Every token-pilot hook used to wrap its own try/catch. When the
|
|
5
|
+
* branch broke (B2 stale binary, B8 bad cwd, B10 ast-index init
|
|
6
|
+
* race), throws were swallowed silently and the user saw "nothing
|
|
7
|
+
* happens". This wrapper centralises the discipline:
|
|
8
|
+
*
|
|
9
|
+
* - Run the hook body
|
|
10
|
+
* - On throw → record one structured `HookErrorRecord` to the
|
|
11
|
+
* user-level error log
|
|
12
|
+
* - Optionally measure duration and emit a `diagnostic` event
|
|
13
|
+
* (Pack 3 — timing)
|
|
14
|
+
* - ALWAYS exit 0 — Claude Code must never see a hook error
|
|
15
|
+
* because that aborts the user's tool call.
|
|
16
|
+
*
|
|
17
|
+
* Hooks themselves keep responsibility for emitting domain-level
|
|
18
|
+
* diagnostics (matcher empty, WSL reject, etc.) — this wrapper is
|
|
19
|
+
* the safety net for unexpected throws.
|
|
20
|
+
*/
|
|
21
|
+
export interface RunHookOptions {
|
|
22
|
+
/** Hook name (matcher in hooks.json — e.g. "hook-pre-task"). */
|
|
23
|
+
hook: string;
|
|
24
|
+
/** Optional safe summary of the hook input — sanitised by caller. */
|
|
25
|
+
inputSummary?: Record<string, unknown>;
|
|
26
|
+
/** Plugin version, captured by caller and forwarded to the log. */
|
|
27
|
+
pluginVersion?: string;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Run a hook body and swallow any throw into the structured error log.
|
|
31
|
+
* Returns true on success, false on caught error — useful when the
|
|
32
|
+
* caller still wants to take a fallback action (e.g. emit a generic
|
|
33
|
+
* permissionDecision to Claude before exiting).
|
|
34
|
+
*/
|
|
35
|
+
export declare function runHookSafely(options: RunHookOptions, fn: () => Promise<void> | void): Promise<boolean>;
|
|
36
|
+
/**
|
|
37
|
+
* The full entry-point wrapper used by `index.ts` cases. Wraps
|
|
38
|
+
* `runHookSafely` and additionally guarantees `process.exit(0)` at
|
|
39
|
+
* the end so a stray throw cannot leak a non-zero status to Claude.
|
|
40
|
+
*
|
|
41
|
+
* Pack 3: optionally measures duration and forwards it to the
|
|
42
|
+
* caller via `onTiming` so the timing diagnostic can be emitted
|
|
43
|
+
* after the hook body decided what to log to hook-events.jsonl.
|
|
44
|
+
*/
|
|
45
|
+
export declare function runHookEntryPoint(options: RunHookOptions & {
|
|
46
|
+
onTiming?: (durationMs: number) => Promise<void> | void;
|
|
47
|
+
}, fn: () => Promise<void> | void): Promise<never>;
|
|
48
|
+
//# sourceMappingURL=safe-runner.d.ts.map
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* v0.34.0 — `runHookSafely` / `runHookEntryPoint`.
|
|
3
|
+
*
|
|
4
|
+
* Every token-pilot hook used to wrap its own try/catch. When the
|
|
5
|
+
* branch broke (B2 stale binary, B8 bad cwd, B10 ast-index init
|
|
6
|
+
* race), throws were swallowed silently and the user saw "nothing
|
|
7
|
+
* happens". This wrapper centralises the discipline:
|
|
8
|
+
*
|
|
9
|
+
* - Run the hook body
|
|
10
|
+
* - On throw → record one structured `HookErrorRecord` to the
|
|
11
|
+
* user-level error log
|
|
12
|
+
* - Optionally measure duration and emit a `diagnostic` event
|
|
13
|
+
* (Pack 3 — timing)
|
|
14
|
+
* - ALWAYS exit 0 — Claude Code must never see a hook error
|
|
15
|
+
* because that aborts the user's tool call.
|
|
16
|
+
*
|
|
17
|
+
* Hooks themselves keep responsibility for emitting domain-level
|
|
18
|
+
* diagnostics (matcher empty, WSL reject, etc.) — this wrapper is
|
|
19
|
+
* the safety net for unexpected throws.
|
|
20
|
+
*/
|
|
21
|
+
import { appendError, classifyError } from "../core/error-log.js";
|
|
22
|
+
/**
|
|
23
|
+
* Run a hook body and swallow any throw into the structured error log.
|
|
24
|
+
* Returns true on success, false on caught error — useful when the
|
|
25
|
+
* caller still wants to take a fallback action (e.g. emit a generic
|
|
26
|
+
* permissionDecision to Claude before exiting).
|
|
27
|
+
*/
|
|
28
|
+
export async function runHookSafely(options, fn) {
|
|
29
|
+
try {
|
|
30
|
+
await fn();
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
catch (err) {
|
|
34
|
+
const e = err;
|
|
35
|
+
await appendError({
|
|
36
|
+
ts: Date.now(),
|
|
37
|
+
hook: options.hook,
|
|
38
|
+
level: "error",
|
|
39
|
+
code: classifyError(err),
|
|
40
|
+
msg: e?.message ?? String(err),
|
|
41
|
+
stack: e?.stack,
|
|
42
|
+
input: options.inputSummary,
|
|
43
|
+
pluginVersion: options.pluginVersion,
|
|
44
|
+
nodeVersion: process.version,
|
|
45
|
+
platform: process.platform,
|
|
46
|
+
});
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* The full entry-point wrapper used by `index.ts` cases. Wraps
|
|
52
|
+
* `runHookSafely` and additionally guarantees `process.exit(0)` at
|
|
53
|
+
* the end so a stray throw cannot leak a non-zero status to Claude.
|
|
54
|
+
*
|
|
55
|
+
* Pack 3: optionally measures duration and forwards it to the
|
|
56
|
+
* caller via `onTiming` so the timing diagnostic can be emitted
|
|
57
|
+
* after the hook body decided what to log to hook-events.jsonl.
|
|
58
|
+
*/
|
|
59
|
+
export async function runHookEntryPoint(options, fn) {
|
|
60
|
+
const started = Date.now();
|
|
61
|
+
await runHookSafely(options, fn);
|
|
62
|
+
const durationMs = Date.now() - started;
|
|
63
|
+
if (options.onTiming) {
|
|
64
|
+
try {
|
|
65
|
+
await options.onTiming(durationMs);
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
/* timing emit must never affect exit */
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
process.exit(0);
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=safe-runner.js.map
|
package/dist/index.d.ts
CHANGED
|
@@ -15,6 +15,17 @@ export declare function main(cliArgs?: string[]): Promise<void>;
|
|
|
15
15
|
* dev installs (cloning the repo and running against itself stays legal).
|
|
16
16
|
*/
|
|
17
17
|
export declare function looksLikePluginCacheDir(candidate: string): boolean;
|
|
18
|
+
/**
|
|
19
|
+
* v0.33.0 (B8) — reject candidates that are obviously not a project
|
|
20
|
+
* directory. Triggered by WSL launches where the shell starts in
|
|
21
|
+
* `C:\Windows\System32`, `/mnt/c/Windows/...`, or a UNC path. Without
|
|
22
|
+
* this guard, `git rev-parse --show-toplevel` either fails noisily or
|
|
23
|
+
* returns the Windows tree, leaving every subsequent git/MCP call
|
|
24
|
+
* looking at the wrong filesystem.
|
|
25
|
+
*
|
|
26
|
+
* Conservative — only matches paths we are certain are not user code.
|
|
27
|
+
*/
|
|
28
|
+
export declare function isWindowsSystemPath(candidate: string): boolean;
|
|
18
29
|
export declare function startServer(cliArgs?: string[]): Promise<void>;
|
|
19
30
|
export interface HookReadAdaptiveOptions {
|
|
20
31
|
adaptiveThreshold?: boolean;
|