token-pilot 0.29.0 → 0.30.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/CHANGELOG.md +35 -0
- package/README.md +39 -390
- 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/tool-audit.d.ts +5 -0
- package/dist/cli/tool-audit.js +9 -1
- package/dist/core/policy-engine.d.ts +1 -5
- package/dist/core/policy-engine.js +9 -24
- package/dist/hooks/pre-bash.d.ts +2 -1
- package/dist/hooks/pre-bash.js +3 -1
- package/dist/hooks/pre-grep.d.ts +2 -1
- package/dist/hooks/pre-grep.js +3 -1
- package/dist/index.js +4 -2
- package/dist/server/enforcement-mode.d.ts +47 -0
- package/dist/server/enforcement-mode.js +59 -0
- package/dist/server/tool-definitions.d.ts +20 -0
- package/dist/server/tool-definitions.js +111 -8
- package/dist/server/tool-profiles.d.ts +19 -1
- package/dist/server/tool-profiles.js +38 -4
- package/dist/server.d.ts +2 -0
- package/dist/server.js +68 -16
- package/docs/agents.md +82 -0
- package/docs/configuration.md +117 -0
- package/docs/hooks.md +99 -0
- package/docs/installation.md +143 -0
- package/docs/tools.md +61 -0
- package/package.json +2 -2
|
@@ -1,8 +1,89 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Minimal profile — 5 essential tools, near-zero instructions overhead
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
const MCP_INSTRUCTIONS_MINIMAL = [
|
|
5
|
+
"Token Pilot — token-efficient code reading. ALWAYS prefer these tools over Read/cat/grep.",
|
|
6
|
+
"",
|
|
7
|
+
"TOOLS:",
|
|
8
|
+
"• smart_read(path) — read a code file (NOT cat/Read — returns structure, 60-80% fewer tokens)",
|
|
9
|
+
"• read_symbol(path, symbol) — read ONE function/class body (NOT the whole file)",
|
|
10
|
+
"• find_usages(symbol) — find where a symbol is defined, imported, or used",
|
|
11
|
+
"• smart_diff — review git changes mapped to functions/classes (NOT git diff)",
|
|
12
|
+
"• smart_log — structured commit history (NOT git log)",
|
|
13
|
+
"",
|
|
14
|
+
"USE Read/Grep ONLY for: non-code configs (JSON, YAML, markdown), regex patterns.",
|
|
15
|
+
].join("\n");
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
// Nav profile — exploration only, no edit-prep tools
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
const MCP_INSTRUCTIONS_NAV = [
|
|
20
|
+
"Token Pilot — token-efficient code reading (saves 60-80% tokens). ALWAYS prefer these tools over Read/cat/grep.",
|
|
21
|
+
"",
|
|
22
|
+
"DECISION RULES — pick the first match:",
|
|
23
|
+
"1. New codebase / unfamiliar project → project_overview",
|
|
24
|
+
"2. Starting work on a directory → explore_area (outline + imports + tests + git log in one call)",
|
|
25
|
+
"3. Need to read a code file → smart_read (NOT Read/cat — returns structure, 60-80% fewer tokens)",
|
|
26
|
+
' - For navigation/browsing: smart_read(scope="nav") — names + lines only, 2-3x smaller',
|
|
27
|
+
' - For public API overview: smart_read(scope="exports")',
|
|
28
|
+
"4. Need one function/class body → read_symbol (loads only that symbol, NOT the whole file)",
|
|
29
|
+
"5. Find where a symbol is used → find_usages (semantic: definitions + imports + usages)",
|
|
30
|
+
' - For initial discovery: find_usages(mode="list") — file:line only, 5-10x smaller',
|
|
31
|
+
"6. Understand file dependencies → related_files (imports, importers, tests — ranked by relevance)",
|
|
32
|
+
"7. List all symbols in a directory → outline (classes, functions, methods in one call)",
|
|
33
|
+
"8. Review git changes → smart_diff (NOT git diff — maps changes to functions/classes)",
|
|
34
|
+
"9. Commit history → smart_log (NOT git log — structured with categories)",
|
|
35
|
+
"10. Module architecture → module_info (deps, dependents, public API)",
|
|
36
|
+
"11. Read markdown/yaml/json/csv section → read_section (loads one heading/key/row-range, NOT the whole file)",
|
|
37
|
+
"12. Long session / before compaction → session_snapshot (<200 token state capture)",
|
|
38
|
+
"",
|
|
39
|
+
"USE Read/Grep ONLY for: regex text search → Grep | exact raw content → Read",
|
|
40
|
+
"",
|
|
41
|
+
"WORKFLOW:",
|
|
42
|
+
"• Explore: project_overview → explore_area → smart_read → read_symbol",
|
|
43
|
+
].join("\n");
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
// Edit profile — nav + batch reads + edit-prep (DEFAULT)
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
const MCP_INSTRUCTIONS_EDIT = [
|
|
48
|
+
"Token Pilot — token-efficient code reading (saves 60-80% tokens). ALWAYS prefer these tools over Read/cat/grep.",
|
|
49
|
+
"",
|
|
50
|
+
"DECISION RULES — pick the first match:",
|
|
51
|
+
"1. New codebase / unfamiliar project → project_overview",
|
|
52
|
+
"2. Starting work on a directory → explore_area (outline + imports + tests + git log in one call)",
|
|
53
|
+
"3. Need to read a code file → smart_read (NOT Read/cat — returns structure, 60-80% fewer tokens)",
|
|
54
|
+
' - For navigation/browsing: smart_read(scope="nav") — names + lines only, 2-3x smaller',
|
|
55
|
+
' - For public API overview: smart_read(scope="exports")',
|
|
56
|
+
"4. Need one function/class body → read_symbol (loads only that symbol, NOT the whole file)",
|
|
57
|
+
" - Preparing edit? Add include_edit_context=true to skip separate read_for_edit call",
|
|
58
|
+
"5. Need MULTIPLE function/class bodies from same file → read_symbols (batch — one call instead of N)",
|
|
59
|
+
"6. Preparing an edit → read_for_edit (returns exact text for Edit old_string)",
|
|
60
|
+
"7. Verify edits after editing → read_diff (only changed hunks — REQUIRES smart_read BEFORE editing)",
|
|
61
|
+
"8. Multiple files at once → smart_read_many (batch up to 20 files)",
|
|
62
|
+
"9. Find where a symbol is used → find_usages (semantic: definitions + imports + usages)",
|
|
63
|
+
' - For initial discovery: find_usages(mode="list") — file:line only, 5-10x smaller',
|
|
64
|
+
"10. Understand file dependencies → related_files (imports, importers, tests — ranked by relevance)",
|
|
65
|
+
"11. List all symbols in a directory → outline (classes, functions, methods in one call)",
|
|
66
|
+
"12. Review git changes → smart_diff (NOT git diff — maps changes to functions/classes)",
|
|
67
|
+
"13. Commit history → smart_log (NOT git log — structured with categories)",
|
|
68
|
+
"14. Module architecture → module_info (deps, dependents, public API)",
|
|
69
|
+
"15. Read markdown/yaml/json/csv section → read_section (loads one heading/key/row-range, NOT the whole file)",
|
|
70
|
+
' - For editing sections: read_for_edit(path, section="Section Name")',
|
|
71
|
+
"16. Long session / before compaction → session_snapshot (capture goal, decisions, confirmed facts, files, next step as <200 token block)",
|
|
72
|
+
" - Budget-constrained? Use smart_read(max_tokens=N) to auto-downgrade output size",
|
|
73
|
+
"",
|
|
74
|
+
"USE Read/Grep ONLY for: regex text search → Grep | exact raw content → Read",
|
|
75
|
+
"",
|
|
76
|
+
"WORKFLOWS:",
|
|
77
|
+
"• Explore: project_overview → explore_area → smart_read → read_symbol",
|
|
78
|
+
"• Edit: smart_read → read_symbol(include_edit_context=true) → Edit → read_diff",
|
|
79
|
+
"• Docs: smart_read (outline) → read_section → read_for_edit(section=) → Edit → read_diff",
|
|
80
|
+
"• Refactor: find_usages → read_symbols → read_for_edit → Edit",
|
|
81
|
+
"• Long session: session_snapshot → compact context → continue with minimal state",
|
|
82
|
+
].join("\n");
|
|
83
|
+
// ---------------------------------------------------------------------------
|
|
84
|
+
// Full profile — all tools including audit (code_audit, find_unused, test_summary)
|
|
85
|
+
// ---------------------------------------------------------------------------
|
|
86
|
+
const MCP_INSTRUCTIONS_FULL = [
|
|
6
87
|
"Token Pilot — token-efficient code reading (saves 60-80% tokens). ALWAYS prefer these tools over Read/cat/grep.",
|
|
7
88
|
"",
|
|
8
89
|
"DECISION RULES — pick the first match:",
|
|
@@ -28,11 +109,11 @@ export const MCP_INSTRUCTIONS = [
|
|
|
28
109
|
"16. Dead code → find_unused (unreferenced symbols across project)",
|
|
29
110
|
"17. Module architecture → module_info (deps, dependents, public API)",
|
|
30
111
|
"18. Read markdown/yaml/json/csv section → read_section (loads one heading/key/row-range, NOT the whole file)",
|
|
31
|
-
'
|
|
112
|
+
' - For editing sections: read_for_edit(path, section="Section Name")',
|
|
32
113
|
"19. Long session / before compaction → session_snapshot (capture goal, decisions, confirmed facts, files, next step as <200 token block)",
|
|
33
|
-
"
|
|
114
|
+
" - Budget-constrained? Use smart_read(max_tokens=N) to auto-downgrade output size",
|
|
34
115
|
"",
|
|
35
|
-
"USE
|
|
116
|
+
"USE Read/Grep ONLY for: regex text search → Grep | exact raw content → Read | non-code configs → Read",
|
|
36
117
|
"",
|
|
37
118
|
"WORKFLOWS:",
|
|
38
119
|
"• Explore: project_overview → explore_area → smart_read → read_symbol",
|
|
@@ -42,6 +123,28 @@ export const MCP_INSTRUCTIONS = [
|
|
|
42
123
|
"• Audit: code_audit + find_unused + Grep (for regex patterns)",
|
|
43
124
|
"• Long session: session_snapshot → compact context → continue with minimal state",
|
|
44
125
|
].join("\n");
|
|
126
|
+
/**
|
|
127
|
+
* Select MCP instructions for the given tool profile.
|
|
128
|
+
* Each profile only mentions tools that are actually advertised in its
|
|
129
|
+
* tools/list — prevents the agent from calling tools it can't see.
|
|
130
|
+
*/
|
|
131
|
+
export function getMcpInstructions(profile) {
|
|
132
|
+
switch (profile) {
|
|
133
|
+
case "minimal":
|
|
134
|
+
return MCP_INSTRUCTIONS_MINIMAL;
|
|
135
|
+
case "nav":
|
|
136
|
+
return MCP_INSTRUCTIONS_NAV;
|
|
137
|
+
case "edit":
|
|
138
|
+
return MCP_INSTRUCTIONS_EDIT;
|
|
139
|
+
case "full":
|
|
140
|
+
return MCP_INSTRUCTIONS_FULL;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* @deprecated Use getMcpInstructions(profile) instead.
|
|
145
|
+
* Kept for backward-compat — resolves to the full profile instructions.
|
|
146
|
+
*/
|
|
147
|
+
export const MCP_INSTRUCTIONS = MCP_INSTRUCTIONS_FULL;
|
|
45
148
|
export const TOOL_DEFINITIONS = [
|
|
46
149
|
// --- Core reading tools ---
|
|
47
150
|
{
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* Selection: TOKEN_PILOT_PROFILE=nav|edit|full env var. Unknown values
|
|
22
22
|
* fall back to full with a stderr warning. Silent on missing env.
|
|
23
23
|
*/
|
|
24
|
-
export type ToolProfile = "full" | "nav" | "edit";
|
|
24
|
+
export type ToolProfile = "full" | "nav" | "edit" | "minimal";
|
|
25
25
|
export declare const PROFILE_NAMES: readonly ToolProfile[];
|
|
26
26
|
/**
|
|
27
27
|
* Meta-tools — diagnostic / self-observation tools that must be visible
|
|
@@ -30,6 +30,12 @@ export declare const PROFILE_NAMES: readonly ToolProfile[];
|
|
|
30
30
|
* would you trust the savings number?
|
|
31
31
|
*/
|
|
32
32
|
export declare const META_TOOLS: ReadonlySet<string>;
|
|
33
|
+
/**
|
|
34
|
+
* Minimal profile — 5 core tools for emergency / context-constrained sessions.
|
|
35
|
+
* Token overhead: tools/list is tiny; instructions are ~80 tokens vs ~350 for full.
|
|
36
|
+
* Use TOKEN_PILOT_PROFILE=minimal when the agent's context budget is nearly full.
|
|
37
|
+
*/
|
|
38
|
+
export declare const MINIMAL_TOOLS: ReadonlySet<string>;
|
|
33
39
|
/** Minimum nav profile — exploration only, no editing support. */
|
|
34
40
|
export declare const NAV_TOOLS: ReadonlySet<string>;
|
|
35
41
|
/** Edit profile adds batch reads + edit-preparation tools. */
|
|
@@ -48,5 +54,17 @@ export declare function filterToolsByProfile<T extends {
|
|
|
48
54
|
* Parse the TOKEN_PILOT_PROFILE env value. Unknown values get a warning
|
|
49
55
|
* and fall back to full — we never silently apply a guess.
|
|
50
56
|
*/
|
|
57
|
+
/**
|
|
58
|
+
* Parse the TOKEN_PILOT_PROFILE env value.
|
|
59
|
+
*
|
|
60
|
+
* Default changed in v0.30.0: full → edit.
|
|
61
|
+
* Rationale: 'full' was exposing 22 tools + full instruction set on every
|
|
62
|
+
* session, burning ~3 k tokens before any work. 'edit' covers 99% of
|
|
63
|
+
* development workflows (reading + writing code). Switch to 'full' only
|
|
64
|
+
* when you need audit tools (code_audit, find_unused, test_summary).
|
|
65
|
+
*
|
|
66
|
+
* Unknown values fall back to 'edit' with a stderr warning — we never
|
|
67
|
+
* silently apply a guess.
|
|
68
|
+
*/
|
|
51
69
|
export declare function parseProfileEnv(envValue: string | undefined, warn?: (msg: string) => void): ToolProfile;
|
|
52
70
|
//# sourceMappingURL=tool-profiles.d.ts.map
|
|
@@ -25,6 +25,7 @@ export const PROFILE_NAMES = [
|
|
|
25
25
|
"full",
|
|
26
26
|
"nav",
|
|
27
27
|
"edit",
|
|
28
|
+
"minimal",
|
|
28
29
|
];
|
|
29
30
|
/**
|
|
30
31
|
* Meta-tools — diagnostic / self-observation tools that must be visible
|
|
@@ -37,6 +38,18 @@ export const META_TOOLS = new Set([
|
|
|
37
38
|
"session_budget",
|
|
38
39
|
"session_snapshot",
|
|
39
40
|
]);
|
|
41
|
+
/**
|
|
42
|
+
* Minimal profile — 5 core tools for emergency / context-constrained sessions.
|
|
43
|
+
* Token overhead: tools/list is tiny; instructions are ~80 tokens vs ~350 for full.
|
|
44
|
+
* Use TOKEN_PILOT_PROFILE=minimal when the agent's context budget is nearly full.
|
|
45
|
+
*/
|
|
46
|
+
export const MINIMAL_TOOLS = new Set([
|
|
47
|
+
"smart_read",
|
|
48
|
+
"read_symbol",
|
|
49
|
+
"find_usages",
|
|
50
|
+
"smart_diff",
|
|
51
|
+
"smart_log",
|
|
52
|
+
]);
|
|
40
53
|
/** Minimum nav profile — exploration only, no editing support. */
|
|
41
54
|
export const NAV_TOOLS = new Set([
|
|
42
55
|
"smart_read",
|
|
@@ -49,6 +62,7 @@ export const NAV_TOOLS = new Set([
|
|
|
49
62
|
"explore_area",
|
|
50
63
|
"smart_log",
|
|
51
64
|
"smart_diff",
|
|
65
|
+
"read_section", // v0.30.0: section reading is nav-class (read-only, no edit prep)
|
|
52
66
|
]);
|
|
53
67
|
/** Edit profile adds batch reads + edit-preparation tools. */
|
|
54
68
|
export const EDIT_EXTRAS = new Set([
|
|
@@ -73,6 +87,11 @@ export function filterToolsByProfile(tools, profile) {
|
|
|
73
87
|
// session_snapshot are the instruments for verifying the profile is
|
|
74
88
|
// doing its job. Hiding them would turn "did this save tokens?" into
|
|
75
89
|
// a guess.
|
|
90
|
+
if (profile === "minimal") {
|
|
91
|
+
// Minimal: 5 core tools only. META excluded — keep the footprint tiny.
|
|
92
|
+
// The agent can still call session_analytics by name if it knows it.
|
|
93
|
+
return tools.filter((t) => MINIMAL_TOOLS.has(t.name));
|
|
94
|
+
}
|
|
76
95
|
if (profile === "nav") {
|
|
77
96
|
return tools.filter((t) => NAV_TOOLS.has(t.name) || META_TOOLS.has(t.name));
|
|
78
97
|
}
|
|
@@ -85,14 +104,29 @@ export function filterToolsByProfile(tools, profile) {
|
|
|
85
104
|
* Parse the TOKEN_PILOT_PROFILE env value. Unknown values get a warning
|
|
86
105
|
* and fall back to full — we never silently apply a guess.
|
|
87
106
|
*/
|
|
107
|
+
/**
|
|
108
|
+
* Parse the TOKEN_PILOT_PROFILE env value.
|
|
109
|
+
*
|
|
110
|
+
* Default changed in v0.30.0: full → edit.
|
|
111
|
+
* Rationale: 'full' was exposing 22 tools + full instruction set on every
|
|
112
|
+
* session, burning ~3 k tokens before any work. 'edit' covers 99% of
|
|
113
|
+
* development workflows (reading + writing code). Switch to 'full' only
|
|
114
|
+
* when you need audit tools (code_audit, find_unused, test_summary).
|
|
115
|
+
*
|
|
116
|
+
* Unknown values fall back to 'edit' with a stderr warning — we never
|
|
117
|
+
* silently apply a guess.
|
|
118
|
+
*/
|
|
88
119
|
export function parseProfileEnv(envValue, warn = () => { }) {
|
|
89
120
|
if (!envValue)
|
|
90
|
-
return "
|
|
121
|
+
return "edit";
|
|
91
122
|
const lower = envValue.trim().toLowerCase();
|
|
92
|
-
if (lower === "full" ||
|
|
123
|
+
if (lower === "full" ||
|
|
124
|
+
lower === "nav" ||
|
|
125
|
+
lower === "edit" ||
|
|
126
|
+
lower === "minimal") {
|
|
93
127
|
return lower;
|
|
94
128
|
}
|
|
95
|
-
warn(`[token-pilot] Unknown TOKEN_PILOT_PROFILE="${envValue}". Expected full|nav|edit. Falling back to
|
|
96
|
-
return "
|
|
129
|
+
warn(`[token-pilot] Unknown TOKEN_PILOT_PROFILE="${envValue}". Expected full|nav|edit|minimal. Falling back to edit.`);
|
|
130
|
+
return "edit";
|
|
97
131
|
}
|
|
98
132
|
//# sourceMappingURL=tool-profiles.js.map
|
package/dist/server.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
2
|
+
import { type EnforcementMode } from "./server/enforcement-mode.js";
|
|
2
3
|
export declare function createServer(projectRoot: string, options?: {
|
|
3
4
|
skipAstIndex?: boolean;
|
|
5
|
+
enforcementMode?: EnforcementMode;
|
|
4
6
|
}): Promise<Server<{
|
|
5
7
|
method: string;
|
|
6
8
|
params?: {
|
package/dist/server.js
CHANGED
|
@@ -45,11 +45,13 @@ import { detectContextMode } from "./integration/context-mode-detector.js";
|
|
|
45
45
|
import { estimateTokens } from "./core/token-estimator.js";
|
|
46
46
|
import { checkPolicy, isFullReadTool } from "./core/policy-engine.js";
|
|
47
47
|
import { appendToolCall } from "./core/tool-call-log.js";
|
|
48
|
-
import {
|
|
48
|
+
import { getMcpInstructions, TOOL_DEFINITIONS, } from "./server/tool-definitions.js";
|
|
49
49
|
import { filterToolsByProfile, parseProfileEnv, } from "./server/tool-profiles.js";
|
|
50
|
+
import { STRICT_SMART_READ_MAX_TOKENS, STRICT_EXPLORE_AREA_INCLUDE, } from "./server/enforcement-mode.js";
|
|
50
51
|
import { createTokenEstimates } from "./server/token-estimates.js";
|
|
51
52
|
import { validateSmartReadArgs, validateReadSymbolArgs, validateReadSymbolsArgs, validateReadRangeArgs, validateReadDiffArgs, validateFindUsagesArgs, validateSmartReadManyArgs, validateReadForEditArgs, validateRelatedFilesArgs, validateOutlineArgs, validateFindUnusedArgs, validateCodeAuditArgs, validateProjectOverviewArgs, validateModuleInfoArgs, validateSmartDiffArgs, validateExploreAreaArgs, validateSmartLogArgs, validateTestSummaryArgs, validateReadSectionArgs, } from "./core/validation.js";
|
|
52
53
|
export async function createServer(projectRoot, options) {
|
|
54
|
+
const mode = options?.enforcementMode ?? "deny";
|
|
53
55
|
const config = await loadConfig(projectRoot);
|
|
54
56
|
const astIndex = new AstIndexClient(projectRoot, config.astIndex.timeout, {
|
|
55
57
|
binaryPath: config.astIndex.binaryPath,
|
|
@@ -193,7 +195,6 @@ export async function createServer(projectRoot, options) {
|
|
|
193
195
|
let fullFileReadsCount = 0;
|
|
194
196
|
let totalCallCount = 0;
|
|
195
197
|
let totalTokensReturned = 0;
|
|
196
|
-
const readForEditCalled = new Set();
|
|
197
198
|
// Detect context-mode companion
|
|
198
199
|
const cmEnabled = config.contextMode.enabled;
|
|
199
200
|
const contextModeStatus = await detectContextMode(projectRoot, cmEnabled === "auto" ? undefined : cmEnabled);
|
|
@@ -238,18 +239,20 @@ export async function createServer(projectRoot, options) {
|
|
|
238
239
|
catch {
|
|
239
240
|
/* fallback to hardcoded */
|
|
240
241
|
}
|
|
242
|
+
// v0.26.3 — tool profiles. TOKEN_PILOT_PROFILE=nav|edit|full|minimal
|
|
243
|
+
// (default: edit since v0.30.0) trims the advertised tools/list payload.
|
|
244
|
+
// Handlers stay live, so a subagent that explicitly names a filtered-out
|
|
245
|
+
// tool still gets a response — we just don't brag about every tool upfront.
|
|
246
|
+
// v0.30.0 — profile also selects matching MCP instructions so the agent
|
|
247
|
+
// doesn't see rules for tools that aren't in its tools/list.
|
|
248
|
+
const activeProfile = parseProfileEnv(process.env.TOKEN_PILOT_PROFILE, (m) => process.stderr.write(m + "\n"));
|
|
241
249
|
const server = new Server({ name: "token-pilot", version: pkgVersion }, {
|
|
242
250
|
capabilities: { tools: {} },
|
|
243
|
-
instructions:
|
|
251
|
+
instructions: getMcpInstructions(activeProfile),
|
|
244
252
|
});
|
|
245
|
-
// v0.26.3 — tool profiles. TOKEN_PILOT_PROFILE=nav|edit|full (default
|
|
246
|
-
// full) trims the advertised tools/list payload. Handlers stay live,
|
|
247
|
-
// so a subagent that explicitly names a filtered-out tool still gets
|
|
248
|
-
// a response — we just don't brag about every tool upfront.
|
|
249
|
-
const activeProfile = parseProfileEnv(process.env.TOKEN_PILOT_PROFILE, (m) => process.stderr.write(m + "\n"));
|
|
250
253
|
const advertisedTools = filterToolsByProfile(TOOL_DEFINITIONS, activeProfile);
|
|
251
|
-
if (activeProfile !== "
|
|
252
|
-
process.stderr.write(`[token-pilot] Profile: ${activeProfile} — advertising ${advertisedTools.length}/${TOOL_DEFINITIONS.length} tools.
|
|
254
|
+
if (activeProfile !== "edit") {
|
|
255
|
+
process.stderr.write(`[token-pilot] Profile: ${activeProfile} — advertising ${advertisedTools.length}/${TOOL_DEFINITIONS.length} tools. Set TOKEN_PILOT_PROFILE=edit for the default set.\n`);
|
|
253
256
|
}
|
|
254
257
|
server.setRequestHandler(ListToolsRequestSchema, () => ({
|
|
255
258
|
tools: advertisedTools,
|
|
@@ -289,14 +292,10 @@ export async function createServer(projectRoot, options) {
|
|
|
289
292
|
if (isFullReadTool(rest.tool)) {
|
|
290
293
|
fullFileReadsCount++;
|
|
291
294
|
}
|
|
292
|
-
if (rest.tool === "read_for_edit" && call.path) {
|
|
293
|
-
readForEditCalled.add(call.path);
|
|
294
|
-
}
|
|
295
295
|
// Policy check
|
|
296
296
|
const advisory = checkPolicy(config.policies, rest.tool, {
|
|
297
297
|
fullFileReadsCount,
|
|
298
298
|
tokensReturned: rest.tokensReturned,
|
|
299
|
-
readForEditCalled,
|
|
300
299
|
totalCallCount,
|
|
301
300
|
totalTokensReturned,
|
|
302
301
|
});
|
|
@@ -331,6 +330,14 @@ export async function createServer(projectRoot, options) {
|
|
|
331
330
|
switch (name) {
|
|
332
331
|
case "smart_read": {
|
|
333
332
|
const validArgs = validateSmartReadArgs(args);
|
|
333
|
+
// v0.30.0 strict mode: cap max_tokens when caller didn't set it.
|
|
334
|
+
let strictReadCapNote;
|
|
335
|
+
if (mode === "strict" && validArgs.max_tokens === undefined) {
|
|
336
|
+
validArgs.max_tokens = STRICT_SMART_READ_MAX_TOKENS;
|
|
337
|
+
strictReadCapNote =
|
|
338
|
+
`\n\n[token-pilot strict] Output capped at ${STRICT_SMART_READ_MAX_TOKENS} tokens ` +
|
|
339
|
+
`(TOKEN_PILOT_MODE=strict). Pass max_tokens explicitly to override.`;
|
|
340
|
+
}
|
|
334
341
|
const picked = pickRegistry(args);
|
|
335
342
|
// Try non-code handler for JSON/YAML/MD etc.
|
|
336
343
|
if (isNonCodeStructured(validArgs.path)) {
|
|
@@ -373,8 +380,9 @@ export async function createServer(projectRoot, options) {
|
|
|
373
380
|
absPath: resolve(projectRoot, validArgs.path),
|
|
374
381
|
args: validArgs,
|
|
375
382
|
});
|
|
376
|
-
|
|
377
|
-
|
|
383
|
+
const srSuffix = (policyAdv ?? "") + (strictReadCapNote ?? "");
|
|
384
|
+
if (srSuffix)
|
|
385
|
+
result.content[0] = { type: "text", text: text + srSuffix };
|
|
378
386
|
return result;
|
|
379
387
|
}
|
|
380
388
|
case "read_symbol": {
|
|
@@ -527,6 +535,15 @@ export async function createServer(projectRoot, options) {
|
|
|
527
535
|
}
|
|
528
536
|
case "find_usages": {
|
|
529
537
|
const usagesArgs = validateFindUsagesArgs(args);
|
|
538
|
+
// v0.30.0 strict mode: default mode to "list" when caller didn't set it.
|
|
539
|
+
// Injected before cache lookup so the key matches strict-mode cached results.
|
|
540
|
+
let strictFuNote;
|
|
541
|
+
if (mode === "strict" && usagesArgs.mode === undefined) {
|
|
542
|
+
usagesArgs.mode = "list";
|
|
543
|
+
strictFuNote =
|
|
544
|
+
`\n\n[token-pilot strict] find_usages mode defaulted to "list" ` +
|
|
545
|
+
`(TOKEN_PILOT_MODE=strict). Pass mode explicitly to override.`;
|
|
546
|
+
}
|
|
530
547
|
const cachedUsages = sessionCache?.get("find_usages", usagesArgs);
|
|
531
548
|
if (cachedUsages) {
|
|
532
549
|
recordWithTrace({
|
|
@@ -558,6 +575,12 @@ export async function createServer(projectRoot, options) {
|
|
|
558
575
|
savingsCategory: "compression",
|
|
559
576
|
args: usagesArgs,
|
|
560
577
|
});
|
|
578
|
+
if (strictFuNote && usagesResult.content[0]) {
|
|
579
|
+
usagesResult.content[0] = {
|
|
580
|
+
type: "text",
|
|
581
|
+
text: usagesText + strictFuNote,
|
|
582
|
+
};
|
|
583
|
+
}
|
|
561
584
|
return usagesResult;
|
|
562
585
|
}
|
|
563
586
|
case "project_overview": {
|
|
@@ -801,6 +824,15 @@ export async function createServer(projectRoot, options) {
|
|
|
801
824
|
}
|
|
802
825
|
case "explore_area": {
|
|
803
826
|
const eaArgs = validateExploreAreaArgs(args);
|
|
827
|
+
// v0.30.0 strict mode: default include to outline-only when caller didn't set it.
|
|
828
|
+
// Injected before cache lookup so the key matches strict-mode cached results.
|
|
829
|
+
let strictEaCapNote;
|
|
830
|
+
if (mode === "strict" && eaArgs.include === undefined) {
|
|
831
|
+
eaArgs.include = STRICT_EXPLORE_AREA_INCLUDE;
|
|
832
|
+
strictEaCapNote =
|
|
833
|
+
`\n\n[token-pilot strict] include defaulted to ["outline"] ` +
|
|
834
|
+
`(TOKEN_PILOT_MODE=strict). Pass include explicitly to override.`;
|
|
835
|
+
}
|
|
804
836
|
const cachedEa = sessionCache?.get("explore_area", eaArgs);
|
|
805
837
|
if (cachedEa) {
|
|
806
838
|
recordWithTrace({
|
|
@@ -833,10 +865,24 @@ export async function createServer(projectRoot, options) {
|
|
|
833
865
|
savingsCategory: "compression",
|
|
834
866
|
args: eaArgs,
|
|
835
867
|
});
|
|
868
|
+
if (strictEaCapNote && eaResult.content[0]) {
|
|
869
|
+
eaResult.content[0] = {
|
|
870
|
+
type: "text",
|
|
871
|
+
text: eaText + strictEaCapNote,
|
|
872
|
+
};
|
|
873
|
+
}
|
|
836
874
|
return eaResult;
|
|
837
875
|
}
|
|
838
876
|
case "smart_log": {
|
|
839
877
|
const slArgs = validateSmartLogArgs(args);
|
|
878
|
+
// v0.30.0 strict mode: bound count to 20 when caller didn't set it.
|
|
879
|
+
let strictSlNote;
|
|
880
|
+
if (mode === "strict" && slArgs.count === undefined) {
|
|
881
|
+
slArgs.count = 20;
|
|
882
|
+
strictSlNote =
|
|
883
|
+
`\n\n[token-pilot strict] smart_log count defaulted to 20 ` +
|
|
884
|
+
`(TOKEN_PILOT_MODE=strict). Pass count explicitly to override.`;
|
|
885
|
+
}
|
|
840
886
|
const slResult = await handleSmartLog(slArgs, projectRoot);
|
|
841
887
|
const slText = slResult.content[0]?.text ?? "";
|
|
842
888
|
const slTokens = estimateTokens(slText);
|
|
@@ -849,6 +895,12 @@ export async function createServer(projectRoot, options) {
|
|
|
849
895
|
savingsCategory: "compression",
|
|
850
896
|
args: slArgs,
|
|
851
897
|
});
|
|
898
|
+
if (strictSlNote && slResult.content[0]) {
|
|
899
|
+
slResult.content[0] = {
|
|
900
|
+
type: "text",
|
|
901
|
+
text: slText + strictSlNote,
|
|
902
|
+
};
|
|
903
|
+
}
|
|
852
904
|
return { content: slResult.content };
|
|
853
905
|
}
|
|
854
906
|
case "test_summary": {
|
package/docs/agents.md
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# tp-* Subagents (Claude Code only)
|
|
2
|
+
|
|
3
|
+
`tp-*` subagents are a Claude Code feature. Other clients get the MCP tools + hooks but cannot invoke subagents. Each agent carries an explicit `model:` field in its frontmatter; the budget is enforced post-response — overshoots beyond 10% land in `.token-pilot/over-budget.log`.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npx token-pilot install-agents --scope=user # all projects
|
|
9
|
+
npx token-pilot install-agents --scope=project # this repo only
|
|
10
|
+
npx token-pilot install-agents --scope=user --force # re-apply after an update
|
|
11
|
+
npx token-pilot uninstall-agents --scope=user|project
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
`init` offers to install these; to add them to another project run `npx token-pilot install-agents`.
|
|
15
|
+
|
|
16
|
+
## Tier 1 — Workhorses (invoke proactively)
|
|
17
|
+
|
|
18
|
+
| Agent | When to invoke | Budget |
|
|
19
|
+
|-------|---------------|-------:|
|
|
20
|
+
| `tp-run` | General MCP-first workhorse; use when no specialised agent fits | 800 |
|
|
21
|
+
| `tp-onboard` | Orient to an unfamiliar repo (layout, entry points, modules) | 600 |
|
|
22
|
+
| `tp-pr-reviewer` | Review a diff / PR / changeset; verdict-first, Critical/Important tiers | 600 |
|
|
23
|
+
| `tp-impact-analyzer` | Trace blast-radius of a change (callers, transitive deps) | 400 |
|
|
24
|
+
| `tp-refactor-planner` | Plan a refactor with exact edit context per step | 500 |
|
|
25
|
+
| `tp-test-triage` | Investigate test failures → root cause → minimal fix | 500 |
|
|
26
|
+
|
|
27
|
+
## Tier 2 — Specialists
|
|
28
|
+
|
|
29
|
+
| Agent | When to invoke | Budget |
|
|
30
|
+
|-------|---------------|-------:|
|
|
31
|
+
| `tp-debugger` | Stack trace / error → root-cause line via call-tree traversal | 700 |
|
|
32
|
+
| `tp-migration-scout` | Pre-migration impact map grouped by effort class | 800 |
|
|
33
|
+
| `tp-test-writer` | Write tests for ONE symbol, mirrors project style, runs tests | 900 |
|
|
34
|
+
| `tp-dead-code-finder` | Cross-checked dead-code detection, output-only (never deletes) | 600 |
|
|
35
|
+
| `tp-commit-writer` | Draft Conventional-Commit from staged diff; refuses failing tests | 400 |
|
|
36
|
+
| `tp-history-explorer` | "Why is this like this?" — minimum commit chain explaining current state | 600 |
|
|
37
|
+
| `tp-audit-scanner` | Read-only security / quality audit; Critical / Important / Minor findings | 800 |
|
|
38
|
+
| `tp-session-restorer` | Rehydrate state after /clear or compaction from latest snapshot | 400 |
|
|
39
|
+
|
|
40
|
+
## Tier 3 — Combo / Workflow
|
|
41
|
+
|
|
42
|
+
| Agent | When to invoke | Budget |
|
|
43
|
+
|-------|---------------|-------:|
|
|
44
|
+
| `tp-review-impact` | Pre-merge blast-radius review (diff × dependents × API surface) | 700 |
|
|
45
|
+
| `tp-test-coverage-gapper` | Find symbols with zero test references, prioritised | 500 |
|
|
46
|
+
| `tp-api-surface-tracker` | Public API diff vs last release → MAJOR / MINOR / PATCH verdict | 600 |
|
|
47
|
+
| `tp-dep-health` | Dep audit: stale × heavily-used × removable | 600 |
|
|
48
|
+
| `tp-incident-timeline` | Correlate an incident window with commits, rank likely culprits | 700 |
|
|
49
|
+
|
|
50
|
+
## Tier 4 — Methodology
|
|
51
|
+
|
|
52
|
+
| Agent | When to invoke | Budget |
|
|
53
|
+
|-------|---------------|-------:|
|
|
54
|
+
| `tp-context-engineer` | Audit / write CLAUDE.md / AGENTS.md rules files per project | 800 |
|
|
55
|
+
| `tp-spec-writer` | Pre-code spec with gated workflow; surfaces assumptions before code | 900 |
|
|
56
|
+
| `tp-performance-profiler` | Measure → identify → fix → verify → guard; refuses to optimise without data | 800 |
|
|
57
|
+
| `tp-incremental-builder` | Multi-file feature work in thin vertical slices, test between each | 900 |
|
|
58
|
+
| `tp-doc-writer` | ADRs + READMEs + API docs; documents *why* not *what* | 700 |
|
|
59
|
+
| `tp-ship-coordinator` | 5-pillar pre-launch checklist (quality / security / observability / rollback / rollout) | 800 |
|
|
60
|
+
|
|
61
|
+
## Model Tiers
|
|
62
|
+
|
|
63
|
+
Every agent carries an explicit `model:` field:
|
|
64
|
+
|
|
65
|
+
| Model | Count | Used for |
|
|
66
|
+
|-------|------:|---------|
|
|
67
|
+
| `haiku` | 9 | Structured / format-bound output (commit messages, onboarding maps, ADRs, session briefings) |
|
|
68
|
+
| `sonnet` | 15 | Reasoning tasks (review, debug, test, plan, audit, spec, profile, ship) |
|
|
69
|
+
| `inherit` | 1 | Deep correlation needing the main thread's model (`tp-incident-timeline`) |
|
|
70
|
+
|
|
71
|
+
Under Opus 4.7's +35% tokenizer tax, keeping the majority of agent spawns on haiku/sonnet saves 5–10× model cost vs an all-Opus baseline.
|
|
72
|
+
|
|
73
|
+
## Third-party Agent Integration (bless-agents)
|
|
74
|
+
|
|
75
|
+
For third-party agents (e.g. `acc-*` plugins) whose tool allowlist excludes token-pilot MCP:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
npx token-pilot bless-agents # add token-pilot MCP to project-level overrides
|
|
79
|
+
npx token-pilot unbless-agents <name>... | --all
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
`doctor` warns when the original agent has changed since blessing.
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# Configuration & Tool Profiles
|
|
2
|
+
|
|
3
|
+
## .token-pilot.json
|
|
4
|
+
|
|
5
|
+
Drop `.token-pilot.json` in your project root. All fields are optional.
|
|
6
|
+
|
|
7
|
+
```json
|
|
8
|
+
{
|
|
9
|
+
"hooks": { "mode": "deny-enhanced", "denyThreshold": 300 },
|
|
10
|
+
"sessionStart": { "enabled": true, "showStats": false, "maxReminderTokens": 250 },
|
|
11
|
+
"agents": { "scope": null, "reminder": true },
|
|
12
|
+
"smartRead": { "smallFileThreshold": 200 },
|
|
13
|
+
"cache": { "maxSizeMB": 100, "watchFiles": true },
|
|
14
|
+
"policies": { "maxFullFileReads": 10, "largeReadThreshold": 2000 },
|
|
15
|
+
"astIndex": { "binaryPath": null },
|
|
16
|
+
"updates": { "checkOnStartup": true, "autoUpdate": false },
|
|
17
|
+
"ignore": ["node_modules/**", "dist/**", ".git/**"]
|
|
18
|
+
}
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
| Option | Default | What it does |
|
|
22
|
+
|--------|---------|--------------|
|
|
23
|
+
| `hooks.mode` | `"deny-enhanced"` | Read hook mode: `off` / `advisory` / `deny-enhanced` |
|
|
24
|
+
| `hooks.denyThreshold` | `300` | Line count above which the hook intervenes on unbounded `Read` |
|
|
25
|
+
| `sessionStart.enabled` | `true` | Re-inject MCP-rules reminder at every new session / `/clear` / `/compact` |
|
|
26
|
+
| `agents.scope` | `null` | Persisted scope of last `install-agents` run; reused silently |
|
|
27
|
+
| `agents.reminder` | `true` | Show the "agents not installed" startup nudge |
|
|
28
|
+
| `smartRead.smallFileThreshold` | `200` | Files with fewer lines bypass AST overhead and are returned in full |
|
|
29
|
+
| `cache.maxSizeMB` | `100` | File cache ceiling (LRU eviction) |
|
|
30
|
+
| `policies.maxFullFileReads` | `10` | Warn after N full-file reads in session |
|
|
31
|
+
| `policies.largeReadThreshold` | `2000` | Token threshold above which a read is flagged as "large" in analytics |
|
|
32
|
+
|
|
33
|
+
## Tool Profiles
|
|
34
|
+
|
|
35
|
+
Trim the advertised `tools/list` to save ~2 k tokens per session. Set via `TOKEN_PILOT_PROFILE` in your MCP server env block.
|
|
36
|
+
|
|
37
|
+
| Profile | Tools | ~Tokens | Use when |
|
|
38
|
+
|---------|------:|--------:|----------|
|
|
39
|
+
| `full` *(default)* | 22 | ~4 150 | All capabilities |
|
|
40
|
+
| `edit` | 16 | ~3 120 | Code-change workflows (nav + batch reads + `read_for_edit`) |
|
|
41
|
+
| `nav` | 10 | ~1 910 | Read-only exploration / subagents that only navigate |
|
|
42
|
+
|
|
43
|
+
Handlers remain active regardless of profile — a subagent that explicitly names a filtered-out tool still gets served. The profile only controls what appears in `tools/list` at session start.
|
|
44
|
+
|
|
45
|
+
### Setting a profile
|
|
46
|
+
|
|
47
|
+
**In `.mcp.json`:**
|
|
48
|
+
```json
|
|
49
|
+
{
|
|
50
|
+
"mcpServers": {
|
|
51
|
+
"token-pilot": {
|
|
52
|
+
"command": "npx",
|
|
53
|
+
"args": ["-y", "token-pilot"],
|
|
54
|
+
"env": { "TOKEN_PILOT_PROFILE": "nav" }
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
**Via shell:**
|
|
61
|
+
```bash
|
|
62
|
+
TOKEN_PILOT_PROFILE=edit npx token-pilot
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## CLI Reference
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
token-pilot # start MCP server
|
|
69
|
+
token-pilot init # create/merge .mcp.json; prompt about subagents
|
|
70
|
+
token-pilot install-agents [--scope=user|project] [--force]
|
|
71
|
+
token-pilot uninstall-agents --scope=user|project
|
|
72
|
+
token-pilot bless-agents # extend third-party agents with token-pilot MCP
|
|
73
|
+
token-pilot unbless-agents <name>... | --all
|
|
74
|
+
token-pilot install-hook # install PreToolUse hooks
|
|
75
|
+
token-pilot uninstall-hook
|
|
76
|
+
token-pilot stats # totals + top files from hook-events.jsonl
|
|
77
|
+
token-pilot stats --session[=<id>] | --by-agent
|
|
78
|
+
token-pilot tool-audit # per-tool savings distribution
|
|
79
|
+
token-pilot tool-audit --json
|
|
80
|
+
token-pilot doctor # diagnostics (ast-index, config, upstream drift)
|
|
81
|
+
token-pilot doctor --check=env # env var check only
|
|
82
|
+
token-pilot install-ast-index # download ast-index binary (auto on first run)
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Architecture
|
|
86
|
+
|
|
87
|
+
```
|
|
88
|
+
src/
|
|
89
|
+
index.ts — CLI entry + MCP server bootstrap
|
|
90
|
+
server.ts — MCP server: 22 tool definitions + enforcement mode
|
|
91
|
+
server/
|
|
92
|
+
enforcement-mode.ts — TOKEN_PILOT_MODE parsing (advisory / deny / strict)
|
|
93
|
+
ast-index/ — ast-index binary client + auto-install
|
|
94
|
+
core/
|
|
95
|
+
event-log.ts — hook-events.jsonl + rotation + retention
|
|
96
|
+
session-analytics.ts, policy-engine.ts, intent-classifier.ts
|
|
97
|
+
hooks/
|
|
98
|
+
installer.ts — hook install/uninstall for Claude Code
|
|
99
|
+
pre-bash.ts — PreToolUse:Bash advisor (Bash/sh/eval/loop patterns)
|
|
100
|
+
pre-grep.ts — PreToolUse:Grep advisor (symbol-like pattern detection)
|
|
101
|
+
session-start.ts — SessionStart reminder handler
|
|
102
|
+
summary-pipeline.ts — ast-index → regex → head+tail → pass-through
|
|
103
|
+
cli/
|
|
104
|
+
install-agents.ts, uninstall-agents.ts
|
|
105
|
+
bless-agents.ts, unbless-agents.ts, doctor-drift.ts
|
|
106
|
+
stats.ts, tool-audit.ts
|
|
107
|
+
templates/agent-builder.ts
|
|
108
|
+
config/loader.ts, defaults.ts
|
|
109
|
+
handlers/ — 22 MCP tool handlers
|
|
110
|
+
git/ — HEAD + file watchers (cache invalidation)
|
|
111
|
+
|
|
112
|
+
scripts/
|
|
113
|
+
build-agents.mjs — render templates/ → dist/agents/
|
|
114
|
+
bench-hook.mjs — hook latency benchmark
|
|
115
|
+
|
|
116
|
+
templates/agents/ — source for tp-* family + shared preamble + contract
|
|
117
|
+
```
|