token-pilot 0.44.0 → 0.45.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +69 -0
- package/README.md +2 -0
- 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/core/event-log.d.ts +11 -0
- package/dist/hooks/session-start.d.ts +9 -0
- package/dist/hooks/session-start.js +21 -1
- package/dist/hooks/subagent-stop.d.ts +7 -0
- package/dist/hooks/subagent-stop.js +3 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +66 -11
- package/dist/server/profile-recommender.js +18 -15
- package/dist/server/tool-definitions.d.ts +2 -2
- package/dist/server/tool-definitions.js +3 -3
- package/dist/server/tool-profiles.js +11 -3
- package/dist/server.js +31 -2
- package/package.json +1 -1
|
@@ -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.45.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. 23 MCP tools + 25 subagents + budget watchdog hooks.",
|
|
16
|
-
"version": "0.
|
|
16
|
+
"version": "0.45.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.
|
|
3
|
+
"version": "0.45.0",
|
|
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,75 @@ 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.45.0] - unreleased
|
|
9
|
+
|
|
10
|
+
Accumulating; not yet published. `0.44.0` is the published/consumed version —
|
|
11
|
+
the items below live only on `master` until `0.45.0` ships.
|
|
12
|
+
|
|
13
|
+
### Changed — default tool profile is now `full` (adoption fix)
|
|
14
|
+
|
|
15
|
+
The advice surface (rules, SessionStart/PostToolUse banners, the pre-edit hook)
|
|
16
|
+
references `read_for_edit`, batch reads, `test_summary` etc. unconditionally,
|
|
17
|
+
but the old default (`edit`) and any trimmed profile hide some of those — so the
|
|
18
|
+
model calls a hidden tool, hits `No such tool available`, and falls back to raw
|
|
19
|
+
`Read`/`Bash`. Those dead round-trips cost far more than the ~2k tokens the trim
|
|
20
|
+
saved. Default is now `full` (advertise everything); trimmed profiles stay
|
|
21
|
+
opt-in via `TOKEN_PILOT_PROFILE=nav|edit|minimal`. When a trimmed profile is
|
|
22
|
+
active the SessionStart banner now prepends a caveat naming what's hidden.
|
|
23
|
+
|
|
24
|
+
The profile **recommender** no longer pushes a trim: it used to suggest
|
|
25
|
+
`TOKEN_PILOT_PROFILE=nav` for read-heavy usage and print an "apply to
|
|
26
|
+
`.mcp.json`" snippet — users applied it, then the next edit session hit the
|
|
27
|
+
trimmed-away `read_for_edit` / `read_range` / batch reads. It now always
|
|
28
|
+
recommends `full`. And `token-pilot doctor` loudly flags an explicit trimmed
|
|
29
|
+
profile, names the hidden tools, and tells you to remove it.
|
|
30
|
+
|
|
31
|
+
### Added — tool failures are logged (no more silent breakage)
|
|
32
|
+
|
|
33
|
+
`createServer`'s tool dispatch now writes handler exceptions / validation errors
|
|
34
|
+
(and unknown-tool names that reach the server) to `~/.token-pilot/hook-errors.jsonl`,
|
|
35
|
+
visible via `token-pilot errors`. Previously tp breakage vanished while telemetry
|
|
36
|
+
reported "all ok". (`No such tool available` is rejected at the Claude Code layer
|
|
37
|
+
before reaching us and stays invisible by design — the full default removes its
|
|
38
|
+
main source.)
|
|
39
|
+
|
|
40
|
+
### Fixed — `read_section` is docs-only
|
|
41
|
+
|
|
42
|
+
Clarified that `read_section` reads Markdown/YAML/JSON/CSV by heading/key/row —
|
|
43
|
+
**not** code by line/symbol (use `read_range` / `read_symbol`). Removed its
|
|
44
|
+
misleading placement under "Batch variants" in the SessionStart banner.
|
|
45
|
+
|
|
46
|
+
### Added — bounded-read leak closed (gate on read span, not bound presence)
|
|
47
|
+
|
|
48
|
+
`PreToolUse:Read` passed *any* `offset`/`limit` Read straight through, so
|
|
49
|
+
`Read(file, limit=2000)` (Claude Code's default page) pulled a whole big file
|
|
50
|
+
hook-free **and** un-counted in the adaptive burn signal — the #1 invisible
|
|
51
|
+
leak. The hook now measures the span a Read actually pulls
|
|
52
|
+
(`effectiveReadSpanLines`) and applies the same deny threshold: a default-page
|
|
53
|
+
or offset-no-limit read of a big file denies with a structural summary, while a
|
|
54
|
+
genuinely narrow slice (`limit < threshold`) still passes. Cost estimates are
|
|
55
|
+
scaled by the span so bounded denies don't over-report savings.
|
|
56
|
+
|
|
57
|
+
### Added — `parent_session_id` capture in SubagentStop (groundwork)
|
|
58
|
+
|
|
59
|
+
A subagent's MCP server runs with `CLAUDE_CODE_SESSION_ID` = the *agent*
|
|
60
|
+
session, so subagent savings get tagged with that id and the statusline's
|
|
61
|
+
main-session badge drops them (savings look flat when subagents do the reading).
|
|
62
|
+
SubagentStop now captures `parent_session_id` (which CC ships in the payload),
|
|
63
|
+
enabling a future child→parent rollup in the badge. Additive/no-op when absent.
|
|
64
|
+
|
|
65
|
+
### Security — `vitest` 3.2.4 → 3.2.6
|
|
66
|
+
|
|
67
|
+
Patches GHSA-5xrq-8626-4rwp (Vitest UI arbitrary file read/exec, critical).
|
|
68
|
+
Dev-only dependency; shipped runtime deps unchanged. The other 32 Dependabot
|
|
69
|
+
alerts were already resolved (installed transitive versions at/above the
|
|
70
|
+
patched version) and auto-close on re-scan.
|
|
71
|
+
|
|
72
|
+
### Docs
|
|
73
|
+
|
|
74
|
+
Fable-5 economic positioning in the README — savings are in tokens, value is in
|
|
75
|
+
tokens × price; keep the premium thread lean.
|
|
76
|
+
|
|
8
77
|
## [0.44.0] - 2026-06-10
|
|
9
78
|
|
|
10
79
|
### Changed — adaptive deny threshold ON by default
|
package/README.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
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 — Claude's Fable 5 is the most capable (and most expensive-per-token) tier yet — 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
|
+
|
|
5
7
|
Three layers, each useful on its own, stronger together:
|
|
6
8
|
|
|
7
9
|
1. **MCP tools** — structural reads (`smart_read`, `read_symbol`, `read_for_edit`, …). Ask for an outline or load one function by name instead of the whole file.
|
|
@@ -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.45.0"
|
|
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.45.0"
|
|
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.45.0"
|
|
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.45.0"
|
|
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.45.0"
|
|
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.45.0"
|
|
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.45.0"
|
|
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.45.0"
|
|
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.45.0"
|
|
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.45.0"
|
|
12
12
|
token_pilot_body_hash: 362ecf4cb03b059421ea26933473700900073dc38b3a7fe271208dfb1ae14f90
|
|
13
13
|
requiredMcpServers:
|
|
14
14
|
- "token-pilot"
|
package/agents/tp-test-writer.md
CHANGED
package/dist/core/event-log.d.ts
CHANGED
|
@@ -38,6 +38,17 @@ export interface HookEvent {
|
|
|
38
38
|
* never populate it; events stay shape-compatible.
|
|
39
39
|
*/
|
|
40
40
|
parent_agent_id?: string | null;
|
|
41
|
+
/**
|
|
42
|
+
* v0.45.0 — root/parent SESSION id (distinct from agent_id). Claude Code
|
|
43
|
+
* exposes `parent_session_id` in subagent hook payloads (binary:
|
|
44
|
+
* `if(D.parentSessionId) X.parent_session_id = D.parentSessionId`). A
|
|
45
|
+
* subagent's MCP server is spawned with CLAUDE_CODE_SESSION_ID = the AGENT
|
|
46
|
+
* session, so its tool-call and denial savings get tagged with that id and
|
|
47
|
+
* the statusline's main-session filter drops them. Capturing the parent lets
|
|
48
|
+
* the badge roll subagent savings up to the conversation that spawned them.
|
|
49
|
+
* Optional — absent on the main thread and on older Claude Code.
|
|
50
|
+
*/
|
|
51
|
+
parent_session_id?: string | null;
|
|
41
52
|
/**
|
|
42
53
|
* v0.38.0 — id of the token-pilot workflow this event belongs to,
|
|
43
54
|
* when one is active (TOKEN_PILOT_WORKFLOW_ID set). Lets fleet-level
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* Output contract: one JSON line on stdout, or exit 0 silent.
|
|
9
9
|
*/
|
|
10
10
|
import { type HookEvent } from "../core/event-log.js";
|
|
11
|
+
import { type ToolProfile } from "../server/tool-profiles.js";
|
|
11
12
|
export declare function buildSubagentAdoptionNudge(events: HookEvent[], now: number, windowDays?: number, minSample?: number, threshold?: number): string | null;
|
|
12
13
|
export interface AgentEntry {
|
|
13
14
|
name: string;
|
|
@@ -43,5 +44,13 @@ export declare function buildReminderMessage(agents: AgentEntry[], maxReminderTo
|
|
|
43
44
|
* Returns the JSON string to write to stdout, or null for silent exit.
|
|
44
45
|
* Never throws — any error → null (fail-safe pass-through).
|
|
45
46
|
*/
|
|
47
|
+
/**
|
|
48
|
+
* v0.45.0 (token-pilot-2fd part 2) — when a trimmed TOOL profile is active,
|
|
49
|
+
* the banner still names tools that profile hides. The full default advertises
|
|
50
|
+
* everything, but an explicit nav/edit/minimal does not, so warn the agent
|
|
51
|
+
* before it calls a hidden tool, hits "No such tool available", and falls back
|
|
52
|
+
* to raw Read/Bash. Empty string for the default `full` profile.
|
|
53
|
+
*/
|
|
54
|
+
export declare function profileBannerNote(profile: ToolProfile): string;
|
|
46
55
|
export declare function handleSessionStart(opts: HandleSessionStartOptions): Promise<string | null>;
|
|
47
56
|
//# sourceMappingURL=session-start.d.ts.map
|
|
@@ -11,6 +11,7 @@ import { readdir, readFile } from "node:fs/promises";
|
|
|
11
11
|
import { join, basename } from "node:path";
|
|
12
12
|
import { loadLatestSnapshot } from "./../handlers/session-snapshot-persist.js";
|
|
13
13
|
import { loadEvents } from "../core/event-log.js";
|
|
14
|
+
import { parseProfileEnv } from "../server/tool-profiles.js";
|
|
14
15
|
const SNAPSHOT_FRESH_MS = 2 * 3600 * 1000; // 2h — enough to cover compaction/restart, tight enough that a new day's unrelated work doesn't inherit yesterday's thread
|
|
15
16
|
// ─── subagent adoption nudge (v0.32.0) ──────────────────────────────
|
|
16
17
|
// Pure function: takes the event log + current time, returns either a
|
|
@@ -138,7 +139,8 @@ MANDATORY — use these BEFORE raw Read / Grep / git:
|
|
|
138
139
|
smart_log(path?) — git log with symbol context (INSTEAD of raw git log)
|
|
139
140
|
test_summary(command) — test runs without dumping full output
|
|
140
141
|
project_overview — unfamiliar repo top-level map (first step)
|
|
141
|
-
Batch variants (prefer over loops): read_symbols, smart_read_many
|
|
142
|
+
Batch variants (prefer over loops): read_symbols, smart_read_many.
|
|
143
|
+
read_section — Markdown/YAML/JSON/CSV ONLY (by heading/key/row); for CODE use read_range / read_symbol.
|
|
142
144
|
Also available: read_range, read_diff, module_info, related_files, explore_area,
|
|
143
145
|
code_audit, find_unused, session_snapshot, session_budget, session_analytics.
|
|
144
146
|
Raw Read/Grep allowed only with offset/limit / narrow regex / non-code files,
|
|
@@ -235,6 +237,20 @@ export function buildReminderMessage(agents, maxReminderTokens) {
|
|
|
235
237
|
* Returns the JSON string to write to stdout, or null for silent exit.
|
|
236
238
|
* Never throws — any error → null (fail-safe pass-through).
|
|
237
239
|
*/
|
|
240
|
+
/**
|
|
241
|
+
* v0.45.0 (token-pilot-2fd part 2) — when a trimmed TOOL profile is active,
|
|
242
|
+
* the banner still names tools that profile hides. The full default advertises
|
|
243
|
+
* everything, but an explicit nav/edit/minimal does not, so warn the agent
|
|
244
|
+
* before it calls a hidden tool, hits "No such tool available", and falls back
|
|
245
|
+
* to raw Read/Bash. Empty string for the default `full` profile.
|
|
246
|
+
*/
|
|
247
|
+
export function profileBannerNote(profile) {
|
|
248
|
+
if (profile === "full")
|
|
249
|
+
return "";
|
|
250
|
+
return (`⚠ TOKEN_PILOT_PROFILE=${profile} — trimmed tool surface active. Some tools named below are NOT advertised this session ` +
|
|
251
|
+
`(test_summary / code_audit / find_unused always; read_for_edit / read_range / read_diff / batch reads on nav & minimal). ` +
|
|
252
|
+
`Calling them returns "No such tool available" — use the listed alternatives or unset TOKEN_PILOT_PROFILE to advertise all.\n\n`);
|
|
253
|
+
}
|
|
238
254
|
export async function handleSessionStart(opts) {
|
|
239
255
|
try {
|
|
240
256
|
if (!opts.sessionStartConfig.enabled) {
|
|
@@ -242,6 +258,10 @@ export async function handleSessionStart(opts) {
|
|
|
242
258
|
}
|
|
243
259
|
const agents = await scanAgents(opts.projectRoot, opts.homeDir);
|
|
244
260
|
let message = buildReminderMessage(agents, opts.sessionStartConfig.maxReminderTokens);
|
|
261
|
+
// Prepend a profile caveat when a trimmed surface hides referenced tools.
|
|
262
|
+
message =
|
|
263
|
+
profileBannerNote(parseProfileEnv(process.env.TOKEN_PILOT_PROFILE)) +
|
|
264
|
+
message;
|
|
245
265
|
// TP-340: surface a fresh snapshot so the new session can resume.
|
|
246
266
|
const snap = await loadLatestSnapshot(opts.projectRoot);
|
|
247
267
|
if (snap && snap.ageMs < SNAPSHOT_FRESH_MS) {
|
|
@@ -38,6 +38,13 @@ export interface SubagentStopInput {
|
|
|
38
38
|
last_assistant_message?: string;
|
|
39
39
|
session_id?: string;
|
|
40
40
|
parent_agent_id?: string;
|
|
41
|
+
/**
|
|
42
|
+
* v0.45.0 — root/parent session id. CC ships this in the SubagentStop
|
|
43
|
+
* payload (`X.parent_session_id`). Lets the statusline roll a subagent's
|
|
44
|
+
* savings (tagged with the agent's own session id) up to the parent
|
|
45
|
+
* conversation. Absent on older CC → field simply not written.
|
|
46
|
+
*/
|
|
47
|
+
parent_session_id?: string;
|
|
41
48
|
}
|
|
42
49
|
/**
|
|
43
50
|
* Best-effort token total from a subagent transcript (JSONL of CC
|
|
@@ -90,6 +90,9 @@ export function buildSubagentTaskEvent(input, now, tokensOverride) {
|
|
|
90
90
|
agent_type: input.agent_type ?? null,
|
|
91
91
|
agent_id: input.agent_id ?? null,
|
|
92
92
|
...(input.parent_agent_id ? { parent_agent_id: input.parent_agent_id } : {}),
|
|
93
|
+
...(input.parent_session_id
|
|
94
|
+
? { parent_session_id: input.parent_session_id }
|
|
95
|
+
: {}),
|
|
93
96
|
event: "task",
|
|
94
97
|
file: "",
|
|
95
98
|
lines: 0,
|
package/dist/index.d.ts
CHANGED
|
@@ -39,6 +39,21 @@ export declare function handleHookRead(filePathArg?: string, mode?: HookMode, de
|
|
|
39
39
|
* wrapping.
|
|
40
40
|
*/
|
|
41
41
|
export declare function runHookReadDispatch(filePathArg: string | undefined, mode: HookMode, denyThresholdArg?: number, projectRootArg?: string, adaptive?: HookReadAdaptiveOptions): Promise<string | null>;
|
|
42
|
+
/**
|
|
43
|
+
* v0.45.0 (token-pilot-xg9) — how many lines a Read actually pulls.
|
|
44
|
+
*
|
|
45
|
+
* An unbounded Read (no offset/limit) pulls the whole file. A bounded Read
|
|
46
|
+
* pulls `limit` lines starting at `offset` — but Claude Code's Read defaults
|
|
47
|
+
* to a 2000-line page, so `Read(file, limit=2000)` or an offset with no limit
|
|
48
|
+
* drags a whole big file through. The old hook passed ANY bounded Read
|
|
49
|
+
* straight through (`hasOffset || hasLimit → return null`), which is the leak:
|
|
50
|
+
* the model bounds with a large/default limit and reads everything hook-free
|
|
51
|
+
* AND un-counted in the adaptive burn signal. Comparing the *span* against the
|
|
52
|
+
* deny threshold closes that while still letting a genuinely narrow slice pass.
|
|
53
|
+
*
|
|
54
|
+
* `offset` / `limit` are null when the field is absent on the tool call.
|
|
55
|
+
*/
|
|
56
|
+
export declare function effectiveReadSpanLines(totalLines: number, offset: number | null, limit: number | null): number;
|
|
42
57
|
/**
|
|
43
58
|
* PreToolUse:Edit / MultiEdit / Write enforcement.
|
|
44
59
|
*
|
package/dist/index.js
CHANGED
|
@@ -767,14 +767,36 @@ export async function runHookReadDispatch(filePathArg, mode, denyThresholdArg, p
|
|
|
767
767
|
const projectRoot = projectRootArg ?? process.cwd();
|
|
768
768
|
return runHookReadDispatchImpl(filePathArg, mode, denyThreshold, projectRoot, adaptive);
|
|
769
769
|
}
|
|
770
|
+
/**
|
|
771
|
+
* v0.45.0 (token-pilot-xg9) — how many lines a Read actually pulls.
|
|
772
|
+
*
|
|
773
|
+
* An unbounded Read (no offset/limit) pulls the whole file. A bounded Read
|
|
774
|
+
* pulls `limit` lines starting at `offset` — but Claude Code's Read defaults
|
|
775
|
+
* to a 2000-line page, so `Read(file, limit=2000)` or an offset with no limit
|
|
776
|
+
* drags a whole big file through. The old hook passed ANY bounded Read
|
|
777
|
+
* straight through (`hasOffset || hasLimit → return null`), which is the leak:
|
|
778
|
+
* the model bounds with a large/default limit and reads everything hook-free
|
|
779
|
+
* AND un-counted in the adaptive burn signal. Comparing the *span* against the
|
|
780
|
+
* deny threshold closes that while still letting a genuinely narrow slice pass.
|
|
781
|
+
*
|
|
782
|
+
* `offset` / `limit` are null when the field is absent on the tool call.
|
|
783
|
+
*/
|
|
784
|
+
export function effectiveReadSpanLines(totalLines, offset, limit) {
|
|
785
|
+
if (offset == null && limit == null)
|
|
786
|
+
return totalLines;
|
|
787
|
+
const DEFAULT_READ_PAGE = 2000;
|
|
788
|
+
const start = offset != null && offset > 0 ? offset : 0;
|
|
789
|
+
const page = limit != null && limit >= 0 ? limit : DEFAULT_READ_PAGE;
|
|
790
|
+
return Math.max(0, Math.min(page, totalLines - start));
|
|
791
|
+
}
|
|
770
792
|
async function runHookReadDispatchImpl(filePathArg, mode, denyThreshold, projectRoot, adaptive = {}) {
|
|
771
793
|
if (mode === "off")
|
|
772
794
|
return null;
|
|
773
795
|
// Parse stdin to get tool_input + session/agent metadata, unless a
|
|
774
796
|
// filePath was supplied directly (tests, --filePath invocation).
|
|
775
797
|
let filePath = filePathArg;
|
|
776
|
-
let
|
|
777
|
-
let
|
|
798
|
+
let offsetVal = null;
|
|
799
|
+
let limitVal = null;
|
|
778
800
|
let sessionId = "";
|
|
779
801
|
let agentType = null;
|
|
780
802
|
let agentId = null;
|
|
@@ -783,8 +805,14 @@ async function runHookReadDispatchImpl(filePathArg, mode, denyThreshold, project
|
|
|
783
805
|
const stdin = readFileSync(0, "utf-8");
|
|
784
806
|
const input = JSON.parse(stdin);
|
|
785
807
|
filePath = input?.tool_input?.file_path;
|
|
786
|
-
|
|
787
|
-
|
|
808
|
+
offsetVal =
|
|
809
|
+
typeof input?.tool_input?.offset === "number"
|
|
810
|
+
? input.tool_input.offset
|
|
811
|
+
: null;
|
|
812
|
+
limitVal =
|
|
813
|
+
typeof input?.tool_input?.limit === "number"
|
|
814
|
+
? input.tool_input.limit
|
|
815
|
+
: null;
|
|
788
816
|
sessionId = typeof input?.session_id === "string" ? input.session_id : "";
|
|
789
817
|
agentType =
|
|
790
818
|
typeof input?.agent_type === "string" ? input.agent_type : null;
|
|
@@ -799,9 +827,6 @@ async function runHookReadDispatchImpl(filePathArg, mode, denyThreshold, project
|
|
|
799
827
|
const ext = filePath.split(".").pop()?.toLowerCase() ?? "";
|
|
800
828
|
if (!CODE_EXTENSIONS.has(ext))
|
|
801
829
|
return null;
|
|
802
|
-
// Bounded Reads are always passed through — the agent already narrowed scope.
|
|
803
|
-
if (hasOffset || hasLimit)
|
|
804
|
-
return null;
|
|
805
830
|
// Path safety: refuse to summarise any file outside the project root
|
|
806
831
|
// (traversal, symlinks pointing outside). Pass-through on failure so the
|
|
807
832
|
// agent is never blocked by a safety reject.
|
|
@@ -829,13 +854,20 @@ async function runHookReadDispatchImpl(filePathArg, mode, denyThreshold, project
|
|
|
829
854
|
try {
|
|
830
855
|
fileContent = readFileSync(filePath, "utf-8");
|
|
831
856
|
lineCount = fileContent.split("\n").length;
|
|
832
|
-
if (lineCount <= effectiveThreshold)
|
|
833
|
-
return null;
|
|
834
857
|
}
|
|
835
858
|
catch {
|
|
836
859
|
return null;
|
|
837
860
|
}
|
|
838
|
-
|
|
861
|
+
// v0.45.0 (token-pilot-xg9) — measure the span the Read actually pulls, not
|
|
862
|
+
// just the file size. A narrow bounded slice (< threshold) still passes; a
|
|
863
|
+
// `limit=2000` / offset-no-limit read of a big file no longer slips through.
|
|
864
|
+
const spanLines = effectiveReadSpanLines(lineCount, offsetVal, limitVal);
|
|
865
|
+
if (spanLines <= effectiveThreshold)
|
|
866
|
+
return null;
|
|
867
|
+
// Cost estimate reflects what the read would pull (the span), so a bounded
|
|
868
|
+
// deny doesn't over-report savings vs the whole-file figure.
|
|
869
|
+
const spanRatio = lineCount > 0 ? Math.min(1, spanLines / lineCount) : 1;
|
|
870
|
+
const charEst = Math.ceil((fileContent.length * spanRatio) / 4);
|
|
839
871
|
const wsRatio = (fileContent.match(/\s/g)?.length ?? 0) / fileContent.length;
|
|
840
872
|
const estTokens = Math.ceil(charEst * (1 - wsRatio * 0.3));
|
|
841
873
|
// Legacy telemetry (hook-denied.jsonl) — retained for backward compatibility
|
|
@@ -873,7 +905,7 @@ async function runHookReadDispatchImpl(filePathArg, mode, denyThreshold, project
|
|
|
873
905
|
if (mode === "advisory") {
|
|
874
906
|
const reason = `File "${filePath}" has ${lineCount} lines. Use mcp__token-pilot__smart_read("${filePath}") ` +
|
|
875
907
|
`for a structural overview, or mcp__token-pilot__read_for_edit("${filePath}", symbol="<name>") ` +
|
|
876
|
-
`for edit context.
|
|
908
|
+
`for edit context. A narrow bounded Read (small limit) is still allowed.`;
|
|
877
909
|
await writeEvent("denied", Math.ceil(reason.length / 4));
|
|
878
910
|
return JSON.stringify({
|
|
879
911
|
hookSpecificOutput: {
|
|
@@ -1282,6 +1314,29 @@ export async function handleDoctor() {
|
|
|
1282
1314
|
catch {
|
|
1283
1315
|
/* ignore */
|
|
1284
1316
|
}
|
|
1317
|
+
// ── explicit trimmed-profile warning (v0.45.0, token-pilot-26b) ──
|
|
1318
|
+
// An explicit TOKEN_PILOT_PROFILE=nav|edit|minimal hides tools the rules and
|
|
1319
|
+
// the pre-edit hook still reference (read_for_edit / read_range / batch),
|
|
1320
|
+
// trapping edit sessions on "No such tool available". This recurred for two
|
|
1321
|
+
// users. Surface it loudly so they can remove it.
|
|
1322
|
+
try {
|
|
1323
|
+
const raw = process.env.TOKEN_PILOT_PROFILE;
|
|
1324
|
+
if (raw && raw.trim()) {
|
|
1325
|
+
const { parseProfileEnv } = await import("./server/tool-profiles.js");
|
|
1326
|
+
if (parseProfileEnv(raw) !== "full") {
|
|
1327
|
+
console.log(`⚠ TOKEN_PILOT_PROFILE=${raw.trim()} is set — a TRIMMED tool surface.\n` +
|
|
1328
|
+
` It hides read_for_edit / read_range / batch reads (and code_audit /\n` +
|
|
1329
|
+
` find_unused / test_summary) that the rules + pre-edit hook still name →\n` +
|
|
1330
|
+
` calls to them fail with "No such tool available" and the agent falls\n` +
|
|
1331
|
+
` back to raw Read/Bash.\n` +
|
|
1332
|
+
` Fix: remove "TOKEN_PILOT_PROFILE" from your .mcp.json env block (or set\n` +
|
|
1333
|
+
` it to "full"), then restart. Full is the default since v0.45.0.\n`);
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
catch {
|
|
1338
|
+
/* doctor must never crash over an optional check */
|
|
1339
|
+
}
|
|
1285
1340
|
// ── profile recommendation ──
|
|
1286
1341
|
// v0.26.4 — data-driven. Reads cumulative tool-calls.jsonl and suggests
|
|
1287
1342
|
// the narrowest TOKEN_PILOT_PROFILE that wouldn't hide any tool the
|
|
@@ -41,25 +41,29 @@ export function recommendProfile(events) {
|
|
|
41
41
|
lowConfidence: true,
|
|
42
42
|
};
|
|
43
43
|
}
|
|
44
|
+
// v0.45.0 (token-pilot-26b) — we NO LONGER recommend trimming to nav/edit.
|
|
45
|
+
// Past tool usage doesn't predict future edits, and a trimmed profile hides
|
|
46
|
+
// read_for_edit / read_range / batch reads that the rules and the pre-edit
|
|
47
|
+
// hook still reference — so the agent calls them, hits "No such tool
|
|
48
|
+
// available", and falls back to raw Read/Bash. That recurring trap cost two
|
|
49
|
+
// users whole sessions. Full is the recommendation; minimal stays a
|
|
50
|
+
// self-serve, clearly-warned opt-in for context-critical work only.
|
|
44
51
|
const allInNav = [...used].every((t) => NAV_TOOLS.has(t));
|
|
45
52
|
if (allInNav) {
|
|
46
53
|
return {
|
|
47
|
-
recommended: "
|
|
48
|
-
reason: `
|
|
54
|
+
recommended: "full",
|
|
55
|
+
reason: `You've used only nav-subset tools so far (${uniqueToolsSeen} distinct), but read_for_edit / read_range / batch reads — named by the rules and the pre-edit hook — live in edit/full. Stay on full so an edit doesn't hit "No such tool available". Set TOKEN_PILOT_PROFILE=minimal yourself ONLY if context is critically tight (it hides edit tools).`,
|
|
49
56
|
uniqueToolsSeen,
|
|
50
57
|
totalCalls,
|
|
51
|
-
wouldHide: [
|
|
52
|
-
...[...EDIT_EXTRAS].filter((t) => !used.has(t)),
|
|
53
|
-
/* full-only — we don't enumerate here, keep the list short */
|
|
54
|
-
],
|
|
58
|
+
wouldHide: [],
|
|
55
59
|
lowConfidence: false,
|
|
56
60
|
};
|
|
57
61
|
}
|
|
58
62
|
const allInEditOrBelow = [...used].every((t) => NAV_TOOLS.has(t) || EDIT_EXTRAS.has(t));
|
|
59
63
|
if (allInEditOrBelow) {
|
|
60
64
|
return {
|
|
61
|
-
recommended: "
|
|
62
|
-
reason: `You use edit-
|
|
65
|
+
recommended: "full",
|
|
66
|
+
reason: `You use edit-prep tools but haven't reached for full-only ones (code_audit/test_summary/find_unused) yet. Stay on full — they cost ~1k tokens to advertise but trimming hides them the moment you need one, and dead calls cost more.`,
|
|
63
67
|
uniqueToolsSeen,
|
|
64
68
|
totalCalls,
|
|
65
69
|
wouldHide: [],
|
|
@@ -87,15 +91,14 @@ export function formatRecommendation(rec) {
|
|
|
87
91
|
lines.push(` data: ${rec.totalCalls} calls, ${rec.uniqueToolsSeen} distinct tools`);
|
|
88
92
|
lines.push(` recommend: TOKEN_PILOT_PROFILE=${rec.recommended}`);
|
|
89
93
|
lines.push(` why: ${rec.reason}`);
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
lines.push(` action: keep default (full). Re-run \`token-pilot doctor\` after a few real sessions for a data-backed suggestion.`);
|
|
94
|
+
// v0.45.0 (26b) — we no longer print an "apply nav/edit to .mcp.json"
|
|
95
|
+
// snippet. recommendProfile always returns `full`; the old snippet trapped
|
|
96
|
+
// users into trimming, which hid edit tools the rules reference.
|
|
97
|
+
if (rec.lowConfidence) {
|
|
98
|
+
lines.push(` action: keep default (full). Re-run \`token-pilot doctor\` after a few real sessions for a data-backed view.`);
|
|
96
99
|
}
|
|
97
100
|
else {
|
|
98
|
-
lines.push(` action: keep default (full).
|
|
101
|
+
lines.push(` action: keep default (full). Trim to minimal yourself only for context-critical, read-only work.`);
|
|
99
102
|
}
|
|
100
103
|
return lines.join("\n");
|
|
101
104
|
}
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
*
|
|
9
9
|
* minimal — 5 core tools, minimal context overhead
|
|
10
10
|
* nav — 10 exploration tools, no editing
|
|
11
|
-
* edit — nav + 6 edit-prep tools
|
|
12
|
-
* full — everything including audit tools
|
|
11
|
+
* edit — nav + 6 edit-prep tools
|
|
12
|
+
* full — everything including audit tools (DEFAULT since v0.45.0)
|
|
13
13
|
*/
|
|
14
14
|
import type { ToolProfile } from "./tool-profiles.js";
|
|
15
15
|
/**
|
|
@@ -42,7 +42,7 @@ const MCP_INSTRUCTIONS_NAV = [
|
|
|
42
42
|
"• Explore: project_overview → explore_area → smart_read → read_symbol",
|
|
43
43
|
].join("\n");
|
|
44
44
|
// ---------------------------------------------------------------------------
|
|
45
|
-
// Edit profile — nav + batch reads + edit-prep
|
|
45
|
+
// Edit profile — nav + batch reads + edit-prep
|
|
46
46
|
// ---------------------------------------------------------------------------
|
|
47
47
|
const MCP_INSTRUCTIONS_EDIT = [
|
|
48
48
|
"Token Pilot — token-efficient code reading (saves 60-80% tokens). ALWAYS prefer these tools over Read/cat/grep.",
|
|
@@ -87,7 +87,7 @@ const MCP_INSTRUCTIONS_EDIT = [
|
|
|
87
87
|
"• Long session: session_snapshot → compact context → continue with minimal state",
|
|
88
88
|
].join("\n");
|
|
89
89
|
// ---------------------------------------------------------------------------
|
|
90
|
-
// Full profile — all tools including audit (code_audit, find_unused, test_summary)
|
|
90
|
+
// Full profile — all tools including audit (code_audit, find_unused, test_summary) — DEFAULT since v0.45.0
|
|
91
91
|
// ---------------------------------------------------------------------------
|
|
92
92
|
const MCP_INSTRUCTIONS_FULL = [
|
|
93
93
|
"Token Pilot — token-efficient code reading (saves 60-80% tokens). ALWAYS prefer these tools over Read/cat/grep.",
|
|
@@ -297,7 +297,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
297
297
|
},
|
|
298
298
|
{
|
|
299
299
|
name: "read_section",
|
|
300
|
-
description: "Read a specific section from Markdown, YAML, JSON, or CSV files. Markdown: by heading name. YAML/JSON: by top-level key. CSV: by row range (rows:1-50). Much cheaper than reading the whole file.",
|
|
300
|
+
description: "Read a specific section from Markdown, YAML, JSON, or CSV files. Markdown: by heading name. YAML/JSON: by top-level key. CSV: by row range (rows:1-50). Much cheaper than reading the whole file. DOCS/DATA ONLY — `heading` is required; this does NOT read code by line/symbol. For source files use read_range (line range) or read_symbol (one symbol).",
|
|
301
301
|
inputSchema: {
|
|
302
302
|
type: "object",
|
|
303
303
|
properties: {
|
|
@@ -117,8 +117,16 @@ export function filterToolsByProfile(tools, profile) {
|
|
|
117
117
|
* silently apply a guess.
|
|
118
118
|
*/
|
|
119
119
|
export function parseProfileEnv(envValue, warn = () => { }) {
|
|
120
|
+
// v0.45.0 — default is now `full` (was `edit`). Trimming the advertised
|
|
121
|
+
// tools/list saved ~2k tokens but created a mismatch: the rules, the
|
|
122
|
+
// SessionStart/PostToolUse banners and the pre-edit hook all reference tools
|
|
123
|
+
// (read_for_edit, test_summary, batch reads) that a trimmed profile hides —
|
|
124
|
+
// so the model calls them, hits "No such tool available", and falls back to
|
|
125
|
+
// raw Read/Bash. Those dead round-trips cost far more than the 2k saved.
|
|
126
|
+
// Advertise everything by default; users who truly need the smaller surface
|
|
127
|
+
// opt in with TOKEN_PILOT_PROFILE=nav|edit|minimal.
|
|
120
128
|
if (!envValue)
|
|
121
|
-
return "
|
|
129
|
+
return "full";
|
|
122
130
|
const lower = envValue.trim().toLowerCase();
|
|
123
131
|
if (lower === "full" ||
|
|
124
132
|
lower === "nav" ||
|
|
@@ -126,7 +134,7 @@ export function parseProfileEnv(envValue, warn = () => { }) {
|
|
|
126
134
|
lower === "minimal") {
|
|
127
135
|
return lower;
|
|
128
136
|
}
|
|
129
|
-
warn(`[token-pilot] Unknown TOKEN_PILOT_PROFILE="${envValue}". Expected full|nav|edit|minimal. Falling back to
|
|
130
|
-
return "
|
|
137
|
+
warn(`[token-pilot] Unknown TOKEN_PILOT_PROFILE="${envValue}". Expected full|nav|edit|minimal. Falling back to full.`);
|
|
138
|
+
return "full";
|
|
131
139
|
}
|
|
132
140
|
//# sourceMappingURL=tool-profiles.js.map
|
package/dist/server.js
CHANGED
|
@@ -4,6 +4,7 @@ import { AstIndexClient } from "./ast-index/client.js";
|
|
|
4
4
|
import { FileCache } from "./core/file-cache.js";
|
|
5
5
|
import { ContextRegistry } from "./core/context-registry.js";
|
|
6
6
|
import { SessionRegistryManager } from "./core/session-registry.js";
|
|
7
|
+
import { appendError, classifyError } from "./core/error-log.js";
|
|
7
8
|
import { SymbolResolver } from "./core/symbol-resolver.js";
|
|
8
9
|
import { SessionAnalytics, } from "./core/session-analytics.js";
|
|
9
10
|
import { classifyIntent } from "./core/intent-classifier.js";
|
|
@@ -277,8 +278,8 @@ export async function createServer(projectRoot, options) {
|
|
|
277
278
|
instructions: getMcpInstructions(activeProfile),
|
|
278
279
|
});
|
|
279
280
|
const advertisedTools = filterToolsByProfile(TOOL_DEFINITIONS, activeProfile);
|
|
280
|
-
if (activeProfile !== "
|
|
281
|
-
process.stderr.write(`[token-pilot] Profile: ${activeProfile} — advertising ${advertisedTools.length}/${TOOL_DEFINITIONS.length} tools.
|
|
281
|
+
if (activeProfile !== "full") {
|
|
282
|
+
process.stderr.write(`[token-pilot] Profile: ${activeProfile} — advertising ${advertisedTools.length}/${TOOL_DEFINITIONS.length} tools (full is the default). A trimmed profile hides tools the rules/hooks still reference; unset TOKEN_PILOT_PROFILE to advertise all.\n`);
|
|
282
283
|
}
|
|
283
284
|
server.setRequestHandler(ListToolsRequestSchema, () => ({
|
|
284
285
|
tools: advertisedTools,
|
|
@@ -1057,6 +1058,17 @@ export async function createServer(projectRoot, options) {
|
|
|
1057
1058
|
return { content: budgetResult.content };
|
|
1058
1059
|
}
|
|
1059
1060
|
default:
|
|
1061
|
+
// token-pilot-m68 — an unknown tool NAME that still reached the
|
|
1062
|
+
// server (a forwarded call CC didn't reject). Log it so the gap is
|
|
1063
|
+
// visible via `token-pilot errors`.
|
|
1064
|
+
void appendError({
|
|
1065
|
+
ts: Date.now(),
|
|
1066
|
+
hook: `mcp-tool:${name}`,
|
|
1067
|
+
level: "warn",
|
|
1068
|
+
code: "unknown_tool",
|
|
1069
|
+
msg: `Unknown tool: ${name}`,
|
|
1070
|
+
input: { tool: name },
|
|
1071
|
+
});
|
|
1060
1072
|
return {
|
|
1061
1073
|
content: [{ type: "text", text: `Unknown tool: ${name}` }],
|
|
1062
1074
|
isError: true,
|
|
@@ -1065,6 +1077,23 @@ export async function createServer(projectRoot, options) {
|
|
|
1065
1077
|
}
|
|
1066
1078
|
catch (err) {
|
|
1067
1079
|
const message = err instanceof Error ? err.message : String(err);
|
|
1080
|
+
// v0.45.0 (token-pilot-m68) — surface tool failures that REACH the
|
|
1081
|
+
// server (validation errors, handler exceptions) in the same
|
|
1082
|
+
// ~/.token-pilot/hook-errors.jsonl the hooks use, so `token-pilot errors`
|
|
1083
|
+
// shows them. Previously these vanished — telemetry reported "saving
|
|
1084
|
+
// tokens, all ok" while broken tp calls were invisible. Best-effort;
|
|
1085
|
+
// appendError never throws. NOTE: "No such tool available" rejections
|
|
1086
|
+
// happen at the Claude Code layer BEFORE the call reaches us, so those
|
|
1087
|
+
// remain invisible by CC design (mitigated by the full-tool default).
|
|
1088
|
+
void appendError({
|
|
1089
|
+
ts: Date.now(),
|
|
1090
|
+
hook: `mcp-tool:${name}`,
|
|
1091
|
+
level: "error",
|
|
1092
|
+
code: classifyError(err),
|
|
1093
|
+
msg: message,
|
|
1094
|
+
stack: err instanceof Error ? err.stack : undefined,
|
|
1095
|
+
input: { tool: name },
|
|
1096
|
+
});
|
|
1068
1097
|
return {
|
|
1069
1098
|
content: [{ type: "text", text: `Error: ${message}` }],
|
|
1070
1099
|
isError: true,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "token-pilot",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.45.0",
|
|
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",
|