token-pilot 0.31.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.
- 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/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 +25 -9
- package/dist/core/validation.js +212 -136
- package/dist/handlers/call-tree.d.ts +35 -0
- package/dist/handlers/call-tree.js +70 -0
- 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/hooks/session-start.d.ts +2 -0
- package/dist/hooks/session-start.js +49 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +284 -63
- package/dist/server/tool-definitions.d.ts +65 -0
- package/dist/server/tool-definitions.js +18 -0
- package/dist/server.js +36 -1
- 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.
|
|
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.
|
|
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.
|
|
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",
|
package/agents/tp-debugger.md
CHANGED
package/agents/tp-dep-health.md
CHANGED
package/agents/tp-doc-writer.md
CHANGED
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.33.0"
|
|
14
14
|
token_pilot_body_hash: 832e95633fbc8e9b0c10f3e540a327d4be062fb4b3f17a6cce6be13f414e2927
|
|
15
15
|
---
|
|
16
16
|
|
package/agents/tp-pr-reviewer.md
CHANGED
package/agents/tp-run.md
CHANGED
package/agents/tp-spec-writer.md
CHANGED
package/agents/tp-test-triage.md
CHANGED
package/agents/tp-test-writer.md
CHANGED
package/dist/ast-index/client.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|