token-pilot 0.45.1 → 0.46.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/CHANGELOG.md +49 -0
- package/README.md +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/cli/typo-guard.d.ts +1 -1
- package/dist/cli/typo-guard.js +2 -0
- package/dist/core/event-log.d.ts +7 -0
- package/dist/core/event-log.js +9 -1
- package/dist/handlers/test-summary.js +42 -0
- package/dist/hooks/installer.js +8 -0
- package/dist/hooks/user-prompt.d.ts +30 -0
- package/dist/hooks/user-prompt.js +44 -0
- package/dist/index.js +22 -0
- package/hooks/hooks.json +10 -0
- package/package.json +4 -4
|
@@ -6,14 +6,14 @@
|
|
|
6
6
|
},
|
|
7
7
|
"metadata": {
|
|
8
8
|
"description": "Token Pilot — save 60-90% tokens when AI reads code",
|
|
9
|
-
"version": "0.
|
|
9
|
+
"version": "0.46.1"
|
|
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. 23 MCP tools + 25 subagents + budget watchdog hooks.",
|
|
16
|
-
"version": "0.
|
|
16
|
+
"version": "0.46.1",
|
|
17
17
|
"author": {
|
|
18
18
|
"name": "Digital-Threads"
|
|
19
19
|
},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "token-pilot",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.46.1",
|
|
4
4
|
"description": "Saves 60-90% tokens on AI code reading. AST-aware lazy reads, symbol navigation, find_usages, structural git diff/log, edit-safety guard, Task-routing matcher, cross-session telemetry (errors + diagnostics), 25 tp-* subagents tiered to haiku/sonnet/opus with budget watchdog.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Digital-Threads",
|
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,55 @@ All notable changes to Token Pilot will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.46.1] - 2026-06-18
|
|
9
|
+
|
|
10
|
+
### Fixed — node:test (`node --test`) TAP output parsing
|
|
11
|
+
|
|
12
|
+
`test_summary` did not recognise `node --test`: `detectRunner` had no `node`
|
|
13
|
+
case, so node:test output fell through to the generic parser. node:test emits a
|
|
14
|
+
TAP footer (`# pass N` / `# fail N`, number after the word) and `ok N - name`
|
|
15
|
+
points (`ok` before the number), which the generic `<N> passed` regex never
|
|
16
|
+
matches — a green 2/2 run was reported as 0 passed. Added a `node` runner:
|
|
17
|
+
detected by command (`node --test` / `node:test`) or by the TAP footer, parsing
|
|
18
|
+
pass/fail/skipped/tests from the footer with a fallback to counting `ok` /
|
|
19
|
+
`not ok` lines; failure names come from `not ok N - name`. Purely additive — no
|
|
20
|
+
existing parser touched.
|
|
21
|
+
|
|
22
|
+
### Changed — dev-dependency security + decoupled registry publish (no shipped change)
|
|
23
|
+
|
|
24
|
+
- Bump `vitest` / `@vitest/coverage-v8` to 4.x — clears the 6 remaining
|
|
25
|
+
dev-only high advisories in the vitest/vite/esbuild chain. `npm audit` now
|
|
26
|
+
reports **0 vulnerabilities** (dev + prod). No runtime/package change: dev
|
|
27
|
+
deps are not shipped to npm consumers; the full 1402-test suite is green on
|
|
28
|
+
vitest 4.
|
|
29
|
+
- `publish-mcp.yml`: the MCP Registry job no longer hard-depends on npm-job
|
|
30
|
+
**success** (`if: !cancelled()`). A failed npm publish (e.g. EOTP on a
|
|
31
|
+
manual-token release) no longer blocks the registry update; re-run the job
|
|
32
|
+
via `workflow_dispatch` after a manual `npm publish`.
|
|
33
|
+
|
|
34
|
+
## [0.46.0] - 2026-06-13
|
|
35
|
+
|
|
36
|
+
### Added — UserPromptSubmit per-turn reinforcement (caveman-style awareness)
|
|
37
|
+
|
|
38
|
+
The `SessionStart` reminder injects the full mandatory-tool ruleset exactly
|
|
39
|
+
once (start / `/clear` / `/compact`). Over a long conversation that block decays
|
|
40
|
+
out of the model's attention and competing instructions crowd it out, so
|
|
41
|
+
sessions drift back to raw `Read` / `Grep` and stop using token-pilot — even
|
|
42
|
+
with hooks and CLAUDE.md rules in place. The caveman plugin solves the identical
|
|
43
|
+
problem with a `UserPromptSubmit` hook that re-injects a tiny anchor on every
|
|
44
|
+
user message; we now do the same.
|
|
45
|
+
|
|
46
|
+
New `hook-user-prompt` (`UserPromptSubmit`) emits a one-line **minimal anchor**
|
|
47
|
+
(~30 tokens) on every prompt — the floor that keeps token-pilot in the working
|
|
48
|
+
set. Deliberately a single short line: the heavy full ruleset stays in
|
|
49
|
+
`SessionStart`, so this per-turn channel never undercuts the tool's own token
|
|
50
|
+
budget (no event-log reads, no per-turn growth).
|
|
51
|
+
|
|
52
|
+
`additionalContext` only — never blocks the prompt. Safe-runner wrapped (always
|
|
53
|
+
exits 0). Respects `sessionStart.enabled`, `TOKEN_PILOT_BYPASS=1`, and a
|
|
54
|
+
dedicated `TOKEN_PILOT_PROMPT_REMINDER=0` opt-out. Wired into `hooks/hooks.json`,
|
|
55
|
+
the `install-hook` installer, and the typo-guard command allowlist.
|
|
56
|
+
|
|
8
57
|
## [0.45.1] - 2026-06-11
|
|
9
58
|
|
|
10
59
|
### Fixed — refuse a multi-repo workspace parent (cross-project index bleed)
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
**Token-efficient AI coding, enforced.** Cuts context consumption in AI coding assistants by up to **90%** without changing the way you work.
|
|
4
4
|
|
|
5
|
-
> **Why it matters more now:** as frontier models move up in price
|
|
5
|
+
> **Why it matters more now:** as frontier models move up in price, the tokens you *don't* spend reading code are worth more, not less. The savings are in tokens; the value is in tokens × price. Token Pilot keeps the expensive main thread lean so the premium model spends its budget on reasoning, not on re-reading files.
|
|
6
6
|
|
|
7
7
|
Three layers, each useful on its own, stronger together:
|
|
8
8
|
|
|
@@ -9,7 +9,7 @@ tools:
|
|
|
9
9
|
- mcp__token-pilot__read_symbol
|
|
10
10
|
- Bash
|
|
11
11
|
model: haiku
|
|
12
|
-
token_pilot_version: "0.
|
|
12
|
+
token_pilot_version: "0.46.1"
|
|
13
13
|
token_pilot_body_hash: dd184501203fa7f3c73f419c4ffbe33c4be75400cb64a7a51733a3fe23f6e085
|
|
14
14
|
requiredMcpServers:
|
|
15
15
|
- "token-pilot"
|
|
@@ -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.
|
|
11
|
+
token_pilot_version: "0.46.1"
|
|
12
12
|
token_pilot_body_hash: de64a406b5176de19f7422619c7de7949b1f28865f225402c9cea9255f377428
|
|
13
13
|
requiredMcpServers:
|
|
14
14
|
- "token-pilot"
|
package/agents/tp-debugger.md
CHANGED
package/agents/tp-dep-health.md
CHANGED
package/agents/tp-doc-writer.md
CHANGED
|
@@ -12,7 +12,7 @@ tools:
|
|
|
12
12
|
- mcp__token-pilot__read_symbols
|
|
13
13
|
- Read
|
|
14
14
|
model: sonnet
|
|
15
|
-
token_pilot_version: "0.
|
|
15
|
+
token_pilot_version: "0.46.1"
|
|
16
16
|
token_pilot_body_hash: 351a987e11eba63852f5431a16d8eb53104f4f689f82fdcc5a2bf4db948ba92f
|
|
17
17
|
requiredMcpServers:
|
|
18
18
|
- "token-pilot"
|
|
@@ -8,7 +8,7 @@ tools:
|
|
|
8
8
|
- mcp__token-pilot__read_symbol
|
|
9
9
|
- Bash
|
|
10
10
|
model: inherit
|
|
11
|
-
token_pilot_version: "0.
|
|
11
|
+
token_pilot_version: "0.46.1"
|
|
12
12
|
token_pilot_body_hash: de5722bfea374eaab096c1ae635c37879e7a91370ee3cd0532f4240be03c91eb
|
|
13
13
|
requiredMcpServers:
|
|
14
14
|
- "token-pilot"
|
package/agents/tp-onboard.md
CHANGED
|
@@ -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.
|
|
13
|
+
token_pilot_version: "0.46.1"
|
|
14
14
|
token_pilot_body_hash: 832e95633fbc8e9b0c10f3e540a327d4be062fb4b3f17a6cce6be13f414e2927
|
|
15
15
|
requiredMcpServers:
|
|
16
16
|
- "token-pilot"
|
package/agents/tp-pr-reviewer.md
CHANGED
|
@@ -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.
|
|
14
|
+
token_pilot_version: "0.46.1"
|
|
15
15
|
token_pilot_body_hash: f83f50d05b4f70285ae7afed2b1a406fc436df56e61a0aedbfb31edc7f2b6e66
|
|
16
16
|
requiredMcpServers:
|
|
17
17
|
- "token-pilot"
|
|
@@ -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.
|
|
11
|
+
token_pilot_version: "0.46.1"
|
|
12
12
|
token_pilot_body_hash: c5f6fc122c89e16e5cf774045f92169ee3468555320b898171ba13eca5323550
|
|
13
13
|
requiredMcpServers:
|
|
14
14
|
- "token-pilot"
|
|
@@ -9,7 +9,7 @@ tools:
|
|
|
9
9
|
- mcp__token-pilot__module_info
|
|
10
10
|
- Bash
|
|
11
11
|
model: sonnet
|
|
12
|
-
token_pilot_version: "0.
|
|
12
|
+
token_pilot_version: "0.46.1"
|
|
13
13
|
token_pilot_body_hash: 8ef3c3341cbfed4eb8dd130126a9683edc57e378c92ff0ca764d584fd941c55c
|
|
14
14
|
requiredMcpServers:
|
|
15
15
|
- "token-pilot"
|
package/agents/tp-run.md
CHANGED
package/agents/tp-spec-writer.md
CHANGED
|
@@ -10,7 +10,7 @@ tools:
|
|
|
10
10
|
- mcp__token-pilot__test_summary
|
|
11
11
|
- Glob
|
|
12
12
|
- Grep
|
|
13
|
-
token_pilot_version: "0.
|
|
13
|
+
token_pilot_version: "0.46.1"
|
|
14
14
|
token_pilot_body_hash: be81eed53a3720d146cf89e4a14a7a56577633f7c84c234c412ab70d64c05b11
|
|
15
15
|
requiredMcpServers:
|
|
16
16
|
- "token-pilot"
|
package/agents/tp-test-triage.md
CHANGED
|
@@ -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.
|
|
11
|
+
token_pilot_version: "0.46.1"
|
|
12
12
|
token_pilot_body_hash: 362ecf4cb03b059421ea26933473700900073dc38b3a7fe271208dfb1ae14f90
|
|
13
13
|
requiredMcpServers:
|
|
14
14
|
- "token-pilot"
|
package/agents/tp-test-writer.md
CHANGED
package/dist/cli/typo-guard.d.ts
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* Everything else passes through untouched — a real project root like
|
|
18
18
|
* `/home/user/my-project` or `./subdir` goes to startServer as before.
|
|
19
19
|
*/
|
|
20
|
-
export declare const KNOWN_COMMANDS: readonly ["hook-read", "hook-edit", "hook-pre-bash", "hook-pre-grep", "hook-pre-task", "hook-post-bash", "hook-post-task", "hook-session-start", "hook-bootstrap", "hook-subagent-stop", "install-statusline", "install-hook", "uninstall-hook", "install-ast-index", "doctor", "bless-agents", "unbless-agents", "install-agents", "uninstall-agents", "stats", "tool-audit", "save-doc", "list-docs", "init", "migrate-hooks", "errors", "workflow", "--version", "-v", "--help", "-h"];
|
|
20
|
+
export declare const KNOWN_COMMANDS: readonly ["hook-read", "hook-edit", "hook-pre-bash", "hook-pre-grep", "hook-pre-task", "hook-post-bash", "hook-post-task", "hook-session-start", "hook-bootstrap", "hook-subagent-stop", "hook-user-prompt", "install-statusline", "install-hook", "uninstall-hook", "install-ast-index", "doctor", "bless-agents", "unbless-agents", "install-agents", "uninstall-agents", "stats", "tool-audit", "save-doc", "list-docs", "init", "migrate-hooks", "errors", "workflow", "--version", "-v", "--help", "-h"];
|
|
21
21
|
export interface TypoGuardResult {
|
|
22
22
|
kind: "pass-through" | "typo";
|
|
23
23
|
suggestion?: string;
|
package/dist/cli/typo-guard.js
CHANGED
|
@@ -37,6 +37,8 @@ export const KNOWN_COMMANDS = [
|
|
|
37
37
|
"hook-bootstrap",
|
|
38
38
|
// v0.40.0 — canonical subagent-completion capture
|
|
39
39
|
"hook-subagent-stop",
|
|
40
|
+
// UserPromptSubmit per-turn reinforcement (caveman-style awareness)
|
|
41
|
+
"hook-user-prompt",
|
|
40
42
|
// v0.42.0 — one-command statusline badge installer
|
|
41
43
|
"install-statusline",
|
|
42
44
|
"install-hook",
|
package/dist/core/event-log.d.ts
CHANGED
|
@@ -56,6 +56,13 @@ export interface HookEvent {
|
|
|
56
56
|
* Optional — absent when no workflow is active.
|
|
57
57
|
*/
|
|
58
58
|
workflow_id?: string;
|
|
59
|
+
/**
|
|
60
|
+
* Loom spine link — id of the Loom task this event belongs to, when the
|
|
61
|
+
* session was launched by Loom (LOOM_TASK_ID set). Lets Loom attribute token
|
|
62
|
+
* savings to a task exactly. Optional — absent outside Loom, so standalone
|
|
63
|
+
* token-pilot events stay byte-identical to before.
|
|
64
|
+
*/
|
|
65
|
+
task_id?: string;
|
|
59
66
|
event: "denied" | "allowed" | "bypass" | "pass-through" | "task" | "diagnostic" | string;
|
|
60
67
|
file: string;
|
|
61
68
|
lines: number;
|
package/dist/core/event-log.js
CHANGED
|
@@ -115,8 +115,16 @@ export async function appendEvent(projectRoot, event) {
|
|
|
115
115
|
const wf = event.workflow_id ??
|
|
116
116
|
process.env.TOKEN_PILOT_WORKFLOW_ID ??
|
|
117
117
|
process.env.CLAUDE_CODE_WORKFLOW_ID ??
|
|
118
|
+
process.env.LOOM_WORKFLOW_ID ??
|
|
118
119
|
undefined;
|
|
119
|
-
|
|
120
|
+
// Loom spine: tag the task id when the session was launched by Loom.
|
|
121
|
+
// Env-driven so call sites stay unchanged; absent outside Loom → no field.
|
|
122
|
+
const taskId = event.task_id ?? process.env.LOOM_TASK_ID ?? undefined;
|
|
123
|
+
let tagged = event;
|
|
124
|
+
if (wf)
|
|
125
|
+
tagged = { ...tagged, workflow_id: wf };
|
|
126
|
+
if (taskId)
|
|
127
|
+
tagged = { ...tagged, task_id: taskId };
|
|
120
128
|
await ensureLogDir(projectRoot);
|
|
121
129
|
await rotateIfNeeded(projectRoot);
|
|
122
130
|
const line = JSON.stringify(tagged) + "\n";
|
|
@@ -73,6 +73,8 @@ export function detectRunner(command, output) {
|
|
|
73
73
|
return 'rspec';
|
|
74
74
|
if (cmd.includes('mocha'))
|
|
75
75
|
return 'mocha';
|
|
76
|
+
if (cmd.includes('node --test') || cmd.includes('node:test'))
|
|
77
|
+
return 'node';
|
|
76
78
|
// Detect from output
|
|
77
79
|
const lower = output.toLowerCase();
|
|
78
80
|
if (lower.includes('vitest') || lower.includes('vite'))
|
|
@@ -85,6 +87,9 @@ export function detectRunner(command, output) {
|
|
|
85
87
|
return 'phpunit';
|
|
86
88
|
if (lower.includes('--- fail:') || lower.includes('--- pass:') || lower.includes('ok \t'))
|
|
87
89
|
return 'go';
|
|
90
|
+
// node:test prints a TAP summary footer: "# tests N" + "# pass N" + "# fail N".
|
|
91
|
+
if (/^#\s*tests\s+\d+/m.test(output) && /^#\s*pass\s+\d+/m.test(output))
|
|
92
|
+
return 'node';
|
|
88
93
|
return 'generic';
|
|
89
94
|
}
|
|
90
95
|
// ──────────────────────────────────────────────
|
|
@@ -103,6 +108,8 @@ export function parseTestOutput(output, runner) {
|
|
|
103
108
|
return parseGoTest(output);
|
|
104
109
|
case 'cargo':
|
|
105
110
|
return parseCargoTest(output);
|
|
111
|
+
case 'node':
|
|
112
|
+
return parseNodeTest(output);
|
|
106
113
|
default:
|
|
107
114
|
return parseGeneric(output);
|
|
108
115
|
}
|
|
@@ -243,6 +250,41 @@ function parseGoTest(output) {
|
|
|
243
250
|
}
|
|
244
251
|
return result;
|
|
245
252
|
}
|
|
253
|
+
function parseNodeTest(output) {
|
|
254
|
+
const result = { total: 0, passed: 0, failed: 0, skipped: 0, failures: [] };
|
|
255
|
+
// node:test (`node --test`) prints a TAP summary footer:
|
|
256
|
+
// # tests 2
|
|
257
|
+
// # pass 2
|
|
258
|
+
// # fail 0
|
|
259
|
+
// # skipped 0
|
|
260
|
+
const num = (re) => {
|
|
261
|
+
const m = output.match(re);
|
|
262
|
+
return m ? parseInt(m[1], 10) : null;
|
|
263
|
+
};
|
|
264
|
+
const pass = num(/^#\s*pass\s+(\d+)/m);
|
|
265
|
+
const fail = num(/^#\s*fail\s+(\d+)/m);
|
|
266
|
+
const skip = num(/^#\s*skipped\s+(\d+)/m);
|
|
267
|
+
const tests = num(/^#\s*tests\s+(\d+)/m);
|
|
268
|
+
if (pass !== null || fail !== null) {
|
|
269
|
+
result.passed = pass ?? 0;
|
|
270
|
+
result.failed = fail ?? 0;
|
|
271
|
+
result.skipped = skip ?? 0;
|
|
272
|
+
result.total = tests ?? result.passed + result.failed + result.skipped;
|
|
273
|
+
}
|
|
274
|
+
else {
|
|
275
|
+
// No footer (truncated output) — count the TAP point lines instead.
|
|
276
|
+
result.passed = (output.match(/^ok\s+\d+/gm) ?? []).length;
|
|
277
|
+
result.failed = (output.match(/^not ok\s+\d+/gm) ?? []).length;
|
|
278
|
+
result.total = result.passed + result.failed + result.skipped;
|
|
279
|
+
}
|
|
280
|
+
// Failure names come from the TAP point: "not ok 3 - the test name".
|
|
281
|
+
const failPattern = /^not ok\s+\d+\s*-\s*(.+)$/gm;
|
|
282
|
+
let match;
|
|
283
|
+
while ((match = failPattern.exec(output)) !== null) {
|
|
284
|
+
result.failures.push({ name: match[1].trim(), error: '' });
|
|
285
|
+
}
|
|
286
|
+
return result;
|
|
287
|
+
}
|
|
246
288
|
function parseCargoTest(output) {
|
|
247
289
|
const result = { total: 0, passed: 0, failed: 0, skipped: 0, failures: [] };
|
|
248
290
|
// test result: ok. 5 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out
|
package/dist/hooks/installer.js
CHANGED
|
@@ -127,6 +127,14 @@ function createHookConfig(options) {
|
|
|
127
127
|
hooks: [hookEntry("hook-session-start", options)],
|
|
128
128
|
},
|
|
129
129
|
],
|
|
130
|
+
// Per-turn reinforcement — re-injects a tiny anchor on every user
|
|
131
|
+
// message so the mandatory-tool rules don't decay out of attention
|
|
132
|
+
// over a long session (SessionStart fires only once).
|
|
133
|
+
UserPromptSubmit: [
|
|
134
|
+
{
|
|
135
|
+
hooks: [hookEntry("hook-user-prompt", options)],
|
|
136
|
+
},
|
|
137
|
+
],
|
|
130
138
|
PostToolUse: [
|
|
131
139
|
{
|
|
132
140
|
matcher: "Bash",
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UserPromptSubmit reminder hook — per-turn reinforcement of the
|
|
3
|
+
* token-pilot mandatory-tool rules.
|
|
4
|
+
*
|
|
5
|
+
* Why this exists: `hook-session-start` injects the full ruleset exactly
|
|
6
|
+
* once (start / `/clear` / `/compact`). Over a long conversation that
|
|
7
|
+
* block decays out of the model's attention and competing instructions
|
|
8
|
+
* crowd it out, so sessions drift back to raw Read / Grep. The caveman
|
|
9
|
+
* plugin solves the identical problem by re-injecting a tiny anchor on
|
|
10
|
+
* EVERY user message; we do the same — one short line per prompt, the
|
|
11
|
+
* full heavy ruleset stays in SessionStart.
|
|
12
|
+
*
|
|
13
|
+
* Contract: emits `additionalContext` only — never blocks the prompt,
|
|
14
|
+
* never throws. Pure module: the builder takes (enabled, bypass) and
|
|
15
|
+
* returns a string or null; the thin `index.ts` case does the IO.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* The per-turn anchor. Deliberately one line — re-sending the full
|
|
19
|
+
* mandatory block every turn would burn hundreds of tokens per message
|
|
20
|
+
* inside a tool whose entire point is saving tokens.
|
|
21
|
+
*/
|
|
22
|
+
export declare const MINIMAL_ANCHOR: string;
|
|
23
|
+
/**
|
|
24
|
+
* Build the per-turn `additionalContext`, or null when disabled /
|
|
25
|
+
* bypassed (caller emits nothing).
|
|
26
|
+
*/
|
|
27
|
+
export declare function buildPromptReminder(enabled: boolean, bypass: boolean): string | null;
|
|
28
|
+
/** Wrap the message in the UserPromptSubmit hook output envelope. */
|
|
29
|
+
export declare function formatPromptReminderOutput(message: string): string;
|
|
30
|
+
//# sourceMappingURL=user-prompt.d.ts.map
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UserPromptSubmit reminder hook — per-turn reinforcement of the
|
|
3
|
+
* token-pilot mandatory-tool rules.
|
|
4
|
+
*
|
|
5
|
+
* Why this exists: `hook-session-start` injects the full ruleset exactly
|
|
6
|
+
* once (start / `/clear` / `/compact`). Over a long conversation that
|
|
7
|
+
* block decays out of the model's attention and competing instructions
|
|
8
|
+
* crowd it out, so sessions drift back to raw Read / Grep. The caveman
|
|
9
|
+
* plugin solves the identical problem by re-injecting a tiny anchor on
|
|
10
|
+
* EVERY user message; we do the same — one short line per prompt, the
|
|
11
|
+
* full heavy ruleset stays in SessionStart.
|
|
12
|
+
*
|
|
13
|
+
* Contract: emits `additionalContext` only — never blocks the prompt,
|
|
14
|
+
* never throws. Pure module: the builder takes (enabled, bypass) and
|
|
15
|
+
* returns a string or null; the thin `index.ts` case does the IO.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* The per-turn anchor. Deliberately one line — re-sending the full
|
|
19
|
+
* mandatory block every turn would burn hundreds of tokens per message
|
|
20
|
+
* inside a tool whose entire point is saving tokens.
|
|
21
|
+
*/
|
|
22
|
+
export const MINIMAL_ANCHOR = "[token-pilot] Before raw Read/Grep/git, use the token-pilot tools: " +
|
|
23
|
+
"smart_read · read_symbol · find_usages · smart_diff / smart_log. " +
|
|
24
|
+
"Delegate scoped work to tp-* specialists. " +
|
|
25
|
+
"Raw Read/Grep only with offset/limit or a narrow regex.";
|
|
26
|
+
/**
|
|
27
|
+
* Build the per-turn `additionalContext`, or null when disabled /
|
|
28
|
+
* bypassed (caller emits nothing).
|
|
29
|
+
*/
|
|
30
|
+
export function buildPromptReminder(enabled, bypass) {
|
|
31
|
+
if (!enabled || bypass)
|
|
32
|
+
return null;
|
|
33
|
+
return MINIMAL_ANCHOR;
|
|
34
|
+
}
|
|
35
|
+
/** Wrap the message in the UserPromptSubmit hook output envelope. */
|
|
36
|
+
export function formatPromptReminderOutput(message) {
|
|
37
|
+
return JSON.stringify({
|
|
38
|
+
hookSpecificOutput: {
|
|
39
|
+
hookEventName: "UserPromptSubmit",
|
|
40
|
+
additionalContext: message,
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=user-prompt.js.map
|
package/dist/index.js
CHANGED
|
@@ -47,6 +47,7 @@ import { detectDrift, formatDriftFinding } from "./cli/doctor-drift.js";
|
|
|
47
47
|
import { handleInstallAgents, maybeEmitStartupReminder, } from "./cli/install-agents.js";
|
|
48
48
|
import { handleUninstallAgents } from "./cli/uninstall-agents.js";
|
|
49
49
|
import { appendEvent, applyRetention, } from "./core/event-log.js";
|
|
50
|
+
import { buildPromptReminder, formatPromptReminderOutput, } from "./hooks/user-prompt.js";
|
|
50
51
|
import { handleStats } from "./cli/stats.js";
|
|
51
52
|
import { handleToolAudit } from "./cli/tool-audit.js";
|
|
52
53
|
import { promptYesNo } from "./cli/install-agents.js";
|
|
@@ -387,6 +388,27 @@ export async function main(cliArgs = process.argv.slice(2)) {
|
|
|
387
388
|
});
|
|
388
389
|
return;
|
|
389
390
|
}
|
|
391
|
+
case "hook-user-prompt": {
|
|
392
|
+
// UserPromptSubmit per-turn reinforcement. SessionStart injects the
|
|
393
|
+
// full ruleset once; this re-injects a tiny anchor on every user
|
|
394
|
+
// message so token-pilot stays in the model's working set instead of
|
|
395
|
+
// decaying out of attention (the failure mode caveman fixes the same
|
|
396
|
+
// way). additionalContext only — never blocks the prompt.
|
|
397
|
+
await runHookEntryPoint({ hook: "hook-user-prompt" }, async () => {
|
|
398
|
+
const cfg = await loadConfig(process.cwd());
|
|
399
|
+
// Reuse the awareness toggle — disabling SessionStart reminders
|
|
400
|
+
// disables per-turn too. TOKEN_PILOT_PROMPT_REMINDER=0 turns off
|
|
401
|
+
// just the per-turn channel while keeping SessionStart.
|
|
402
|
+
const enabled = cfg.sessionStart.enabled &&
|
|
403
|
+
process.env.TOKEN_PILOT_PROMPT_REMINDER !== "0";
|
|
404
|
+
const bypass = process.env.TOKEN_PILOT_BYPASS === "1";
|
|
405
|
+
const message = buildPromptReminder(enabled, bypass);
|
|
406
|
+
if (message) {
|
|
407
|
+
process.stdout.write(formatPromptReminderOutput(message));
|
|
408
|
+
}
|
|
409
|
+
});
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
390
412
|
case "install-hook":
|
|
391
413
|
await handleInstallHook(cliArgs[1] || process.cwd());
|
|
392
414
|
return;
|
package/hooks/hooks.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "token-pilot",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.46.1",
|
|
4
4
|
"description": "Save up to 80% tokens when AI reads code — MCP server for token-efficient code navigation, AST-aware structural reading instead of dumping full files into context window",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -67,15 +67,15 @@
|
|
|
67
67
|
"mcpName": "io.github.Digital-Threads/token-pilot",
|
|
68
68
|
"license": "MIT",
|
|
69
69
|
"dependencies": {
|
|
70
|
-
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
71
70
|
"@ast-index/cli": "^3.44.0",
|
|
71
|
+
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
72
72
|
"chokidar": "^4.0.3"
|
|
73
73
|
},
|
|
74
74
|
"devDependencies": {
|
|
75
|
-
"@vitest/coverage-v8": "^3.2.4",
|
|
76
75
|
"@types/node": "^22.0.0",
|
|
76
|
+
"@vitest/coverage-v8": "^4.1.8",
|
|
77
77
|
"typescript": "^5.7.0",
|
|
78
|
-
"vitest": "^
|
|
78
|
+
"vitest": "^4.1.8"
|
|
79
79
|
},
|
|
80
80
|
"engines": {
|
|
81
81
|
"node": ">=18.0.0"
|