token-pilot 0.19.2 → 0.23.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.
Files changed (96) hide show
  1. package/.claude-plugin/hooks/hooks.json +30 -0
  2. package/.claude-plugin/plugin.json +2 -2
  3. package/CHANGELOG.md +165 -0
  4. package/README.md +194 -313
  5. package/dist/agents/tp-audit-scanner.md +49 -0
  6. package/dist/agents/tp-commit-writer.md +41 -0
  7. package/dist/agents/tp-dead-code-finder.md +43 -0
  8. package/dist/agents/tp-debugger.md +45 -0
  9. package/dist/agents/tp-history-explorer.md +43 -0
  10. package/dist/agents/tp-impact-analyzer.md +44 -0
  11. package/dist/agents/tp-migration-scout.md +43 -0
  12. package/dist/agents/tp-onboard.md +40 -0
  13. package/dist/agents/tp-pr-reviewer.md +41 -0
  14. package/dist/agents/tp-refactor-planner.md +42 -0
  15. package/dist/agents/tp-run.md +48 -0
  16. package/dist/agents/tp-session-restorer.md +47 -0
  17. package/dist/agents/tp-test-triage.md +40 -0
  18. package/dist/agents/tp-test-writer.md +46 -0
  19. package/dist/cli/agent-frontmatter.d.ts +48 -0
  20. package/dist/cli/agent-frontmatter.js +189 -0
  21. package/dist/cli/bless-agents.d.ts +65 -0
  22. package/dist/cli/bless-agents.js +307 -0
  23. package/dist/cli/claudeignore.d.ts +33 -0
  24. package/dist/cli/claudeignore.js +88 -0
  25. package/dist/cli/claudemd-hygiene.d.ts +26 -0
  26. package/dist/cli/claudemd-hygiene.js +43 -0
  27. package/dist/cli/doctor-drift.d.ts +31 -0
  28. package/dist/cli/doctor-drift.js +130 -0
  29. package/dist/cli/doctor-env-check.d.ts +25 -0
  30. package/dist/cli/doctor-env-check.js +91 -0
  31. package/dist/cli/install-agents.d.ts +108 -0
  32. package/dist/cli/install-agents.js +402 -0
  33. package/dist/cli/save-doc.d.ts +42 -0
  34. package/dist/cli/save-doc.js +145 -0
  35. package/dist/cli/scan-agents.d.ts +46 -0
  36. package/dist/cli/scan-agents.js +227 -0
  37. package/dist/cli/stats.d.ts +36 -0
  38. package/dist/cli/stats.js +131 -0
  39. package/dist/cli/typo-guard.d.ts +27 -0
  40. package/dist/cli/typo-guard.js +119 -0
  41. package/dist/cli/unbless-agents.d.ts +33 -0
  42. package/dist/cli/unbless-agents.js +85 -0
  43. package/dist/cli/uninstall-agents.d.ts +36 -0
  44. package/dist/cli/uninstall-agents.js +117 -0
  45. package/dist/config/defaults.d.ts +1 -1
  46. package/dist/config/defaults.js +14 -8
  47. package/dist/config/loader.d.ts +1 -1
  48. package/dist/config/loader.js +105 -11
  49. package/dist/core/context-registry.d.ts +16 -1
  50. package/dist/core/context-registry.js +60 -28
  51. package/dist/core/event-log.d.ts +79 -0
  52. package/dist/core/event-log.js +190 -0
  53. package/dist/core/session-registry.d.ts +43 -0
  54. package/dist/core/session-registry.js +113 -0
  55. package/dist/core/session-savings.d.ts +19 -0
  56. package/dist/core/session-savings.js +60 -0
  57. package/dist/handlers/session-budget.d.ts +32 -0
  58. package/dist/handlers/session-budget.js +61 -0
  59. package/dist/handlers/session-snapshot-persist.d.ts +22 -0
  60. package/dist/handlers/session-snapshot-persist.js +76 -0
  61. package/dist/hooks/adaptive-threshold.d.ts +27 -0
  62. package/dist/hooks/adaptive-threshold.js +46 -0
  63. package/dist/hooks/format-deny-message.d.ts +21 -0
  64. package/dist/hooks/format-deny-message.js +147 -0
  65. package/dist/hooks/installer.js +130 -31
  66. package/dist/hooks/path-safety.d.ts +16 -0
  67. package/dist/hooks/path-safety.js +34 -0
  68. package/dist/hooks/post-bash.d.ts +46 -0
  69. package/dist/hooks/post-bash.js +77 -0
  70. package/dist/hooks/post-task.d.ts +67 -0
  71. package/dist/hooks/post-task.js +136 -0
  72. package/dist/hooks/session-start.d.ts +45 -0
  73. package/dist/hooks/session-start.js +179 -0
  74. package/dist/hooks/summary-ast-index.d.ts +28 -0
  75. package/dist/hooks/summary-ast-index.js +122 -0
  76. package/dist/hooks/summary-head-tail.d.ts +15 -0
  77. package/dist/hooks/summary-head-tail.js +78 -0
  78. package/dist/hooks/summary-pipeline.d.ts +35 -0
  79. package/dist/hooks/summary-pipeline.js +63 -0
  80. package/dist/hooks/summary-regex.d.ts +14 -0
  81. package/dist/hooks/summary-regex.js +130 -0
  82. package/dist/hooks/summary-types.d.ts +29 -0
  83. package/dist/hooks/summary-types.js +9 -0
  84. package/dist/index.d.ts +15 -3
  85. package/dist/index.js +538 -149
  86. package/dist/integration/context-mode-detector.d.ts +7 -1
  87. package/dist/integration/context-mode-detector.js +51 -15
  88. package/dist/server/tool-definitions.d.ts +149 -0
  89. package/dist/server/tool-definitions.js +424 -202
  90. package/dist/server.d.ts +1 -1
  91. package/dist/server.js +456 -179
  92. package/dist/templates/agent-builder.d.ts +49 -0
  93. package/dist/templates/agent-builder.js +104 -0
  94. package/dist/types.d.ts +38 -4
  95. package/package.json +4 -2
  96. package/skills/stats/SKILL.md +13 -2
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Phase 4 subtask 4.3 — agent composer.
3
+ *
4
+ * Composes a final agent markdown by splicing three source parts:
5
+ * 1. `templates/agents/tp-NAME.md` — frontmatter + role block (source of truth)
6
+ * 2. `templates/agents/_shared-preamble.md` — MCP-first contract
7
+ * 3. `templates/agents/_response-contract.md` — output discipline
8
+ *
9
+ * Deliberately uses a regex split instead of parseFrontmatter/writeFrontmatter
10
+ * so the frontmatter block is preserved **byte-for-byte** — no YAML re-
11
+ * serialisation that could reorder keys or change quoting.
12
+ *
13
+ * This is a pure in-memory transformation. No files are written by this
14
+ * module; Phase 5 install-agents is the only writer.
15
+ */
16
+ /**
17
+ * Pure string-to-string composer.
18
+ *
19
+ * @param source Content of `tp-NAME.md` (frontmatter + role block).
20
+ * @param shared Content of `_shared-preamble.md`.
21
+ * @param contract Content of `_response-contract.md`.
22
+ * @returns Composed agent body with exactly one frontmatter block, the
23
+ * shared preamble immediately after it, the role block next, and the
24
+ * response contract last. Ends with a single trailing newline.
25
+ * @throws if the source has no recognisable frontmatter delimiter pair.
26
+ */
27
+ export declare function composeAgent(source: string, shared: string, contract: string): string;
28
+ /**
29
+ * File-system wrapper around composeAgent. Reads the three parts from
30
+ * disk and returns the composed string. Does not write anything.
31
+ */
32
+ export declare function composeFromFiles(sourcePath: string, sharedPath: string, contractPath: string): string;
33
+ export interface ComposedAgent {
34
+ /** Agent name (file basename without `.md`). */
35
+ name: string;
36
+ /** Composed markdown ready to be written to `.claude/agents/<name>.md`. */
37
+ composed: string;
38
+ }
39
+ /**
40
+ * Scans `templatesDir` for `tp-*.md` files and composes each of them,
41
+ * using `_shared-preamble.md` and `_response-contract.md` from the same
42
+ * directory. Files starting with `_` are excluded.
43
+ *
44
+ * Returns an empty array if `templatesDir` contains no `tp-*.md` files
45
+ * (including when the directory is missing, to keep CLI invocations
46
+ * safe on fresh checkouts).
47
+ */
48
+ export declare function composeAll(templatesDir: string): ComposedAgent[];
49
+ //# sourceMappingURL=agent-builder.d.ts.map
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Phase 4 subtask 4.3 — agent composer.
3
+ *
4
+ * Composes a final agent markdown by splicing three source parts:
5
+ * 1. `templates/agents/tp-NAME.md` — frontmatter + role block (source of truth)
6
+ * 2. `templates/agents/_shared-preamble.md` — MCP-first contract
7
+ * 3. `templates/agents/_response-contract.md` — output discipline
8
+ *
9
+ * Deliberately uses a regex split instead of parseFrontmatter/writeFrontmatter
10
+ * so the frontmatter block is preserved **byte-for-byte** — no YAML re-
11
+ * serialisation that could reorder keys or change quoting.
12
+ *
13
+ * This is a pure in-memory transformation. No files are written by this
14
+ * module; Phase 5 install-agents is the only writer.
15
+ */
16
+ import { readFileSync, readdirSync } from "node:fs";
17
+ import { join } from "node:path";
18
+ const FRONTMATTER_RE = /^(---\r?\n[\s\S]*?\r?\n---\r?\n)([\s\S]*)$/;
19
+ /**
20
+ * Pure string-to-string composer.
21
+ *
22
+ * @param source Content of `tp-NAME.md` (frontmatter + role block).
23
+ * @param shared Content of `_shared-preamble.md`.
24
+ * @param contract Content of `_response-contract.md`.
25
+ * @returns Composed agent body with exactly one frontmatter block, the
26
+ * shared preamble immediately after it, the role block next, and the
27
+ * response contract last. Ends with a single trailing newline.
28
+ * @throws if the source has no recognisable frontmatter delimiter pair.
29
+ */
30
+ export function composeAgent(source, shared, contract) {
31
+ const m = source.match(FRONTMATTER_RE);
32
+ if (!m) {
33
+ throw new Error("composeAgent: source has no frontmatter block (expected ---\\n...\\n---\\n at start)");
34
+ }
35
+ const [, frontmatter, roleBlock] = m;
36
+ return (frontmatter +
37
+ "\n" +
38
+ shared.trim() +
39
+ "\n\n" +
40
+ roleBlock.trim() +
41
+ "\n\n" +
42
+ contract.trim() +
43
+ "\n");
44
+ }
45
+ /**
46
+ * File-system wrapper around composeAgent. Reads the three parts from
47
+ * disk and returns the composed string. Does not write anything.
48
+ */
49
+ export function composeFromFiles(sourcePath, sharedPath, contractPath) {
50
+ const source = readFileSync(sourcePath, "utf-8");
51
+ const shared = readFileSync(sharedPath, "utf-8");
52
+ const contract = readFileSync(contractPath, "utf-8");
53
+ return composeAgent(source, shared, contract);
54
+ }
55
+ /**
56
+ * Scans `templatesDir` for `tp-*.md` files and composes each of them,
57
+ * using `_shared-preamble.md` and `_response-contract.md` from the same
58
+ * directory. Files starting with `_` are excluded.
59
+ *
60
+ * Returns an empty array if `templatesDir` contains no `tp-*.md` files
61
+ * (including when the directory is missing, to keep CLI invocations
62
+ * safe on fresh checkouts).
63
+ */
64
+ export function composeAll(templatesDir) {
65
+ let entries;
66
+ try {
67
+ entries = readdirSync(templatesDir);
68
+ }
69
+ catch {
70
+ return [];
71
+ }
72
+ const sharedPath = join(templatesDir, "_shared-preamble.md");
73
+ const contractPath = join(templatesDir, "_response-contract.md");
74
+ let shared;
75
+ let contract;
76
+ try {
77
+ shared = readFileSync(sharedPath, "utf-8");
78
+ contract = readFileSync(contractPath, "utf-8");
79
+ }
80
+ catch {
81
+ // Missing parts → caller gets nothing; Phase 5 can surface a friendly
82
+ // error. We never throw out of the bulk path.
83
+ return [];
84
+ }
85
+ const results = [];
86
+ for (const entry of entries) {
87
+ if (!entry.endsWith(".md"))
88
+ continue;
89
+ if (entry.startsWith("_"))
90
+ continue;
91
+ if (!entry.startsWith("tp-"))
92
+ continue;
93
+ const name = entry.replace(/\.md$/, "");
94
+ try {
95
+ const source = readFileSync(join(templatesDir, entry), "utf-8");
96
+ results.push({ name, composed: composeAgent(source, shared, contract) });
97
+ }
98
+ catch {
99
+ // Skip unreadable / malformed source; Phase 5 may log.
100
+ }
101
+ }
102
+ return results;
103
+ }
104
+ //# sourceMappingURL=agent-builder.js.map
package/dist/types.d.ts CHANGED
@@ -1,8 +1,17 @@
1
1
  /**
2
2
  * Core domain types for Token Pilot.
3
3
  */
4
- export type SymbolKind = 'function' | 'class' | 'method' | 'property' | 'variable' | 'type' | 'interface' | 'enum' | 'constant' | 'namespace';
5
- export type Visibility = 'public' | 'private' | 'protected' | 'default';
4
+ /**
5
+ * Hook enforcement mode.
6
+ * - 'off': PreToolUse hook is inert (no advisory, no deny).
7
+ * - 'advisory': hook emits a short tip but does not block Read.
8
+ * - 'deny-enhanced': hook denies oversized code Reads and returns a structural
9
+ * summary inside permissionDecisionReason. Default from v0.20; preserves
10
+ * v0.19 deny-behaviour while upgrading the message quality.
11
+ */
12
+ export type HookMode = "off" | "advisory" | "deny-enhanced";
13
+ export type SymbolKind = "function" | "class" | "method" | "property" | "variable" | "type" | "interface" | "enum" | "constant" | "namespace";
14
+ export type Visibility = "public" | "private" | "protected" | "default";
6
15
  export interface FileStructure {
7
16
  path: string;
8
17
  language: string;
@@ -62,7 +71,7 @@ export interface ContextEntry {
62
71
  symbolNames?: string[];
63
72
  }
64
73
  export interface LoadedRegion {
65
- type: 'structure' | 'symbol' | 'range' | 'full';
74
+ type: "structure" | "symbol" | "range" | "full";
66
75
  symbolName?: string;
67
76
  startLine: number;
68
77
  endLine: number;
@@ -104,6 +113,17 @@ export interface TokenPilotConfig {
104
113
  interceptRead: boolean;
105
114
  autoInstall: boolean;
106
115
  denyThreshold: number;
116
+ mode: HookMode;
117
+ /**
118
+ * When true, hook auto-lowers denyThreshold as session burns through
119
+ * adaptiveBudgetTokens. Opt-in — default false keeps v0.20 behaviour.
120
+ */
121
+ adaptiveThreshold: boolean;
122
+ /**
123
+ * Reference budget (in saved-token units from hook-events.jsonl) used
124
+ * to compute burn fraction. Defaults to a rough 100k proxy.
125
+ */
126
+ adaptiveBudgetTokens: number;
107
127
  };
108
128
  context: {
109
129
  estimateTokens: boolean;
@@ -118,7 +138,7 @@ export interface TokenPilotConfig {
118
138
  actionableHints: boolean;
119
139
  };
120
140
  contextMode: {
121
- enabled: boolean | 'auto';
141
+ enabled: boolean | "auto";
122
142
  adviseDelegation: boolean;
123
143
  largeNonCodeThreshold: number;
124
144
  };
@@ -140,6 +160,20 @@ export interface TokenPilotConfig {
140
160
  compactionCallThreshold: number;
141
161
  compactionTokenThreshold: number;
142
162
  };
163
+ sessionStart: {
164
+ enabled: boolean;
165
+ showStats: boolean;
166
+ maxReminderTokens: number;
167
+ };
168
+ agents: {
169
+ /** Scope of last `install-agents` run, null until first install. */
170
+ scope: "user" | "project" | null;
171
+ /**
172
+ * Emit a one-time stderr reminder at MCP startup if no tp-* agents
173
+ * are installed. Can also be suppressed by env TOKEN_PILOT_NO_AGENT_REMINDER=1.
174
+ */
175
+ reminder: boolean;
176
+ };
143
177
  ignore: string[];
144
178
  }
145
179
  //# sourceMappingURL=types.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "token-pilot",
3
- "version": "0.19.2",
3
+ "version": "0.23.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",
@@ -10,6 +10,7 @@
10
10
  "files": [
11
11
  "dist/**/*.js",
12
12
  "dist/**/*.d.ts",
13
+ "dist/agents/*.md",
13
14
  "start.sh",
14
15
  ".claude-plugin/",
15
16
  ".mcp.json",
@@ -19,12 +20,13 @@
19
20
  ],
20
21
  "scripts": {
21
22
  "prebuild": "node --input-type=module -e \"import { rm } from 'node:fs/promises'; await rm('dist', { recursive: true, force: true });\"",
22
- "build": "tsc",
23
+ "build": "tsc && node scripts/build-agents.mjs",
23
24
  "dev": "tsc --watch",
24
25
  "start": "node dist/index.js",
25
26
  "test": "vitest run",
26
27
  "test:coverage": "vitest run --coverage",
27
28
  "test:watch": "vitest",
29
+ "bench:hook": "node scripts/bench-hook.mjs",
28
30
  "lint": "tsc --noEmit",
29
31
  "prepublishOnly": "npm run build && node --input-type=module -e \"import { chmod } from 'node:fs/promises'; await chmod('dist/index.js', 0o755);\""
30
32
  },
@@ -1,8 +1,19 @@
1
1
  ---
2
2
  name: stats
3
- description: Show Token Pilot session analytics — token savings, per-tool breakdown, top files
3
+ description: Show Token Pilot session analytics — token savings, per-tool breakdown, top files, per-agent grouping
4
4
  command: stats
5
5
  user_invocable: true
6
6
  ---
7
7
 
8
- Call the `session_analytics` MCP tool from the token-pilot server to display the current session's token savings report. Show the output to the user as-is.
8
+ Two entry points:
9
+
10
+ 1. **In-session rich summary (MCP tool).** Call the `session_analytics` MCP tool from the token-pilot server to display the current session's token savings with per-tool breakdown. Show the output to the user as-is.
11
+
12
+ 2. **CLI shortcut (works without a running MCP server).** When the user explicitly asks for a specific view, or when the MCP tool is unavailable, invoke:
13
+
14
+ - `token-pilot stats` — totals + top files by savedTokens
15
+ - `token-pilot stats --session` — totals for the most recent session
16
+ - `token-pilot stats --session=<id>` — totals for a specific session_id
17
+ - `token-pilot stats --by-agent` — savings grouped by agent_type (tp-run, tp-onboard, …, or "main" for the top-level session)
18
+
19
+ The CLI reads `.token-pilot/hook-events.jsonl`, so it only shows data for qualifying Reads that were intercepted by the hook (not raw MCP tool calls — those live in session-analytics).