token-pilot 0.13.0 → 0.14.2

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 (69) hide show
  1. package/.claude-plugin/hooks/hooks.json +9 -0
  2. package/.claude-plugin/marketplace.json +1 -1
  3. package/.claude-plugin/plugin.json +1 -1
  4. package/CHANGELOG.md +29 -0
  5. package/README.md +36 -15
  6. package/dist/config/defaults.js +12 -0
  7. package/dist/core/architecture-fingerprint.d.ts +34 -0
  8. package/dist/core/architecture-fingerprint.js +127 -0
  9. package/dist/core/budget-planner.d.ts +21 -0
  10. package/dist/core/budget-planner.js +68 -0
  11. package/dist/core/confidence.d.ts +31 -0
  12. package/dist/core/confidence.js +99 -0
  13. package/dist/core/context-registry.d.ts +14 -0
  14. package/dist/core/context-registry.js +55 -0
  15. package/dist/core/decision-trace.d.ts +31 -0
  16. package/dist/core/decision-trace.js +45 -0
  17. package/dist/core/intent-classifier.d.ts +13 -0
  18. package/dist/core/intent-classifier.js +44 -0
  19. package/dist/core/policy-engine.d.ts +41 -0
  20. package/dist/core/policy-engine.js +76 -0
  21. package/dist/core/session-analytics.d.ts +8 -0
  22. package/dist/core/session-analytics.js +86 -7
  23. package/dist/core/session-cache.d.ts +74 -0
  24. package/dist/core/session-cache.js +162 -0
  25. package/dist/core/validation.d.ts +3 -0
  26. package/dist/core/validation.js +3 -0
  27. package/dist/git/file-watcher.d.ts +6 -0
  28. package/dist/git/file-watcher.js +18 -2
  29. package/dist/git/watcher.d.ts +3 -0
  30. package/dist/git/watcher.js +6 -0
  31. package/dist/handlers/code-audit.d.ts +7 -2
  32. package/dist/handlers/code-audit.js +19 -5
  33. package/dist/handlers/explore-area.d.ts +10 -0
  34. package/dist/handlers/explore-area.js +39 -13
  35. package/dist/handlers/find-unused.d.ts +3 -0
  36. package/dist/handlers/find-unused.js +3 -2
  37. package/dist/handlers/find-usages.d.ts +7 -0
  38. package/dist/handlers/find-usages.js +36 -5
  39. package/dist/handlers/module-info.d.ts +3 -0
  40. package/dist/handlers/module-info.js +22 -2
  41. package/dist/handlers/project-overview.d.ts +1 -1
  42. package/dist/handlers/project-overview.js +18 -2
  43. package/dist/handlers/read-for-edit.d.ts +3 -0
  44. package/dist/handlers/read-for-edit.js +185 -3
  45. package/dist/handlers/read-range.d.ts +1 -1
  46. package/dist/handlers/read-range.js +16 -1
  47. package/dist/handlers/read-symbol.d.ts +1 -1
  48. package/dist/handlers/read-symbol.js +26 -2
  49. package/dist/handlers/related-files.d.ts +11 -0
  50. package/dist/handlers/related-files.js +178 -42
  51. package/dist/handlers/smart-read-many.js +70 -16
  52. package/dist/handlers/smart-read.js +10 -1
  53. package/dist/handlers/test-summary.js +26 -3
  54. package/dist/hooks/installer.d.ts +12 -8
  55. package/dist/hooks/installer.js +24 -8
  56. package/dist/index.d.ts +16 -1
  57. package/dist/index.js +62 -56
  58. package/dist/server.js +395 -30
  59. package/dist/types.d.ts +12 -0
  60. package/package.json +18 -14
  61. package/start.sh +28 -27
  62. package/dist/handlers/class-hierarchy.d.ts +0 -11
  63. package/dist/handlers/class-hierarchy.js +0 -28
  64. package/dist/handlers/export-ast-index.d.ts +0 -22
  65. package/dist/handlers/export-ast-index.js +0 -175
  66. package/dist/handlers/find-implementations.d.ts +0 -11
  67. package/dist/handlers/find-implementations.js +0 -27
  68. package/dist/handlers/search-code.d.ts +0 -14
  69. package/dist/handlers/search-code.js +0 -32
@@ -9,6 +9,15 @@
9
9
  "command": "node ${CLAUDE_PLUGIN_ROOT}/dist/index.js hook-read"
10
10
  }
11
11
  ]
12
+ },
13
+ {
14
+ "matcher": "Edit",
15
+ "hooks": [
16
+ {
17
+ "type": "command",
18
+ "command": "node ${CLAUDE_PLUGIN_ROOT}/dist/index.js hook-edit"
19
+ }
20
+ ]
12
21
  }
13
22
  ]
14
23
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "token-pilot",
3
3
  "displayName": "Token Pilot",
4
- "description": "Reduces token consumption by 80-95% via AST-aware lazy file reading. 18 MCP tools for structural code reading, symbol navigation, and cross-file search.",
4
+ "description": "Reduces token consumption by 60-80% via AST-aware lazy file reading. 18 MCP tools for structural code reading, symbol navigation, and cross-file search.",
5
5
  "version": "0.13.0",
6
6
  "author": "Digital-Threads",
7
7
  "repository": "https://github.com/Digital-Threads/token-pilot",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "token-pilot",
3
3
  "version": "0.13.0",
4
- "description": "Reduces token consumption by 80-95% via AST-aware lazy file reading. Returns structural overviews instead of full files.",
4
+ "description": "Reduces token consumption by 60-80% via AST-aware lazy file reading. Returns structural overviews instead of full files.",
5
5
  "author": "token-pilot",
6
6
  "license": "MIT",
7
7
  "skills": "../skills",
package/CHANGELOG.md CHANGED
@@ -5,6 +5,35 @@ 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.14.1] - 2026-03-14
9
+
10
+ ### Fixed
11
+ - **CI: Node.js 24 runtime** — opted into `FORCE_JAVASCRIPT_ACTIONS_TO_NODE24` for GitHub Actions, resolving deprecation warnings for `actions/checkout@v4` and `actions/setup-node@v4`.
12
+ - **CI: test matrix** — updated from Node 18+22 to Node 20+22 (Node 18 is EOL).
13
+ - **Test: git commit in CI** — `read-for-edit` tests now pass `-c user.name` / `-c user.email` to `git commit`, fixing failures in environments without global git config.
14
+
15
+ ## [0.14.0] - 2026-03-14
16
+
17
+ ### Added
18
+ - **R&D Track 0: Instrumentation** — per-call decision trace capturing file size, context state, estimated vs actual cost, and cheaper alternative suggestions. Integrated into all 18 tool handlers via `recordWithTrace()`.
19
+ - **R&D Track 1: Budget Planner** — advisory layer suggesting cheaper tool alternatives (e.g. `smart_read` → `read_diff` when file already in context, → `read_symbol` when symbol known). Analytics-only, no blocking.
20
+ - **R&D Track 2: Intent Router** — classifies tool calls into 7 intents (edit/debug/explore/review/analyze/search/read). Per-intent breakdown in session analytics.
21
+ - **R&D Track 3: Edit Prep Mode** — `read_for_edit` with `include_callers`, `include_tests`, `include_changes` enrichment options.
22
+ - **R&D Track 4: Session Cache** — tool-result-level caching with file/AST/git invalidation.
23
+ - **R&D Track 5: Confidence-Based Escalation** — confidence metadata (high/medium/low) appended to `smart_read`, `read_symbol`, `read_for_edit`, `find_usages` responses. Shows known unknowns and suggested next steps.
24
+ - **R&D Track 6: Working Set / Dedup** — compact reminders for already-loaded files and symbols.
25
+ - **R&D Track 7: Related Files Ranking** — scored ranking with 6 signals (test +5, import +4, importer +3, same-dir +2, recently-changed +2, multi-ref +1). HIGH VALUE / MEDIUM / LOW buckets.
26
+ - **R&D Track 8: Architecture Fingerprint** — caches architecture in `.token-pilot-fingerprint.json` (24h TTL). Amortizes `project_overview` cost across sessions.
27
+ - **R&D Track 9: Verified Savings Dashboard** — savings breakdown by category (compression/cache/dedup), session cache hit rate, dedup stats.
28
+ - **R&D Track 10: Team Policy Mode** — configurable policies: `preferCheapReads`, `maxFullFileReads`, `warnOnLargeReads`, `requireReadForEditBeforeEdit`.
29
+ - **7 new core modules** — `confidence.ts`, `intent-classifier.ts`, `budget-planner.ts`, `decision-trace.ts`, `session-cache.ts`, `architecture-fingerprint.ts`, `policy-engine.ts`.
30
+ - **35 new tests** — confidence (11), architecture-fingerprint (11), policy-engine (13). Total: 393 tests.
31
+
32
+ ### Changed
33
+ - **`session_analytics`** — per-intent breakdown, decision insights, savings by category.
34
+ - **`project_overview`** — saves/loads architecture fingerprint for cross-session caching.
35
+ - **Config** — added `policies` section to `TokenPilotConfig`.
36
+
8
37
  ## [0.13.0] - 2026-03-14
9
38
 
10
39
  ### Added
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Token Pilot
2
2
 
3
- MCP server that reduces token consumption in AI coding assistants by **60-80%** via AST-aware lazy file reading.
3
+ MCP server that reduces token consumption in AI coding assistants by **up to 80%** via AST-aware lazy file reading.
4
4
 
5
5
  Instead of dumping entire files into the LLM context, Token Pilot returns structural overviews (classes, functions, signatures, line ranges) and lets the AI load only the specific symbols it needs.
6
6
 
@@ -13,7 +13,7 @@ Token Pilot: smart_read("user-service.ts") → 15-line outline → ~200 tok
13
13
  After edit: read_diff("user-service.ts") → ~20 tokens
14
14
  ```
15
15
 
16
- **~80% reduction** in this example. Files under 200 lines are returned in full automatically (no overhead for small files). Real savings start at ~200+ lines.
16
+ **Up to 80% reduction** on large files. Files under 200 lines are returned in full automatically (zero overhead for small files). Typical sessions with a mix of file sizes see **30-50% savings**, scaling higher with repeated reads (session cache, compact reminders) and targeted symbol loading.
17
17
 
18
18
  ## Installation
19
19
 
@@ -113,7 +113,10 @@ npx token-pilot install-ast-index
113
113
 
114
114
  ### PreToolUse Hook (Claude Code only)
115
115
 
116
- Optional hook that intercepts `Read` calls for large code files (>500 lines) and suggests `smart_read`. Claude Code only.
116
+ Optional Claude Code hook support:
117
+
118
+ - blocks unbounded `Read` on large code files (>500 lines) and points the agent to `smart_read`
119
+ - adds `read_for_edit` guidance before `Edit`
117
120
 
118
121
  ```bash
119
122
  npx token-pilot install-hook # Install
@@ -129,7 +132,7 @@ npx token-pilot uninstall-hook # Remove
129
132
  When connected, every MCP client receives rules like:
130
133
 
131
134
  ```
132
- WHEN TO USE TOKEN PILOT (saves 60-80% tokens):
135
+ WHEN TO USE TOKEN PILOT (saves up to 80% tokens):
133
136
  • Reading code files → smart_read (returns structure, not raw content)
134
137
  • Need one function/class → read_symbol (loads only that symbol)
135
138
  • Exploring a directory → outline (all symbols in one call)
@@ -149,13 +152,13 @@ For more control, you can add rules to your project:
149
152
  - **Cursor** → `.cursorrules` in project root
150
153
  - **Codex** → `AGENTS.md` in project root
151
154
 
152
- ## MCP Tools (14)
155
+ ## MCP Tools (18)
153
156
 
154
157
  ### Core Reading
155
158
 
156
159
  | Tool | Instead of | Description |
157
160
  |------|-----------|-------------|
158
- | `smart_read` | `Read` | AST structural overview: classes, functions, methods with signatures. 60-80% savings. Framework-aware: shows HTTP routes, column types, validation rules. |
161
+ | `smart_read` | `Read` | AST structural overview: classes, functions, methods with signatures. Up to 80% savings on large files. Framework-aware: shows HTTP routes, column types, validation rules. |
159
162
  | `read_symbol` | `Read` + scroll | Load source of a specific symbol. Supports `Class.method`. `show` param: full/head/tail/outline. |
160
163
  | `read_for_edit` | `Read` before `Edit` | Minimal RAW code around a symbol — copy directly as `old_string` for Edit tool. |
161
164
  | `read_range` | `Read` offset | Read a specific line range from a file. |
@@ -182,7 +185,7 @@ For more control, you can add rules to your project:
182
185
 
183
186
  | Tool | Description |
184
187
  |------|-------------|
185
- | `session_analytics` | Token savings report: total saved, per-tool breakdown, top files. |
188
+ | `session_analytics` | Token savings report: total saved, per-tool breakdown, top files, per-intent breakdown, decision insights, policy advisories. |
186
189
 
187
190
  ## CLI Commands
188
191
 
@@ -194,6 +197,7 @@ token-pilot install-ast-index # Download ast-index binary (auto on first run)
194
197
  token-pilot install-hook [root] # Install PreToolUse hook
195
198
  token-pilot uninstall-hook # Remove hook
196
199
  token-pilot hook-read <file> # Hook handler (called by Claude Code)
200
+ token-pilot hook-edit # Edit hook handler (called by Claude Code)
197
201
  token-pilot doctor # Run diagnostics (ast-index, config, updates)
198
202
  token-pilot --version # Show version
199
203
  token-pilot --help # Show help
@@ -222,6 +226,13 @@ Create `.token-pilot.json` in your project root to customize behavior:
222
226
  "adviseDelegation": true,
223
227
  "largeNonCodeThreshold": 200
224
228
  },
229
+ "policies": {
230
+ "preferCheapReads": true,
231
+ "maxFullFileReads": 10,
232
+ "warnOnLargeReads": true,
233
+ "largeReadThreshold": 2000,
234
+ "requireReadForEditBeforeEdit": true
235
+ },
225
236
  "display": {
226
237
  "showImports": true,
227
238
  "showDocs": true,
@@ -248,6 +259,10 @@ All fields are optional — sensible defaults are used for anything not specifie
248
259
  | `git.watchHead` | `true` | Watch `.git/HEAD` for branch switches, invalidate changed files. |
249
260
  | `contextMode.enabled` | `"auto"` | Detect context-mode plugin. `true`/`false` to force. |
250
261
  | `contextMode.adviseDelegation` | `true` | Suggest context-mode for large non-code files. |
262
+ | `policies.preferCheapReads` | `true` | Advisory hints when expensive tool used where cheaper exists. |
263
+ | `policies.maxFullFileReads` | `10` | Warn after N full-file reads in session. |
264
+ | `policies.warnOnLargeReads` | `true` | Warn when single response exceeds threshold. |
265
+ | `policies.largeReadThreshold` | `2000` | Token threshold for large read warning. |
251
266
 
252
267
  ## Integration with context-mode
253
268
 
@@ -265,13 +280,13 @@ When both are configured, Token Pilot automatically:
265
280
  - Suggests context-mode for large non-code files
266
281
  - Shows combined architecture info in `session_analytics`
267
282
 
268
- **Combined savings: 60-80%** in a typical coding session.
283
+ **Combined savings: up to 80%** in a typical coding session.
269
284
 
270
285
  ## Supported Languages
271
286
 
272
- Token Pilot supports all 23 languages that [ast-index](https://github.com/defendend/Claude-ast-index-search) supports:
287
+ Token Pilot supports all 29 languages that [ast-index](https://github.com/defendend/Claude-ast-index-search) supports:
273
288
 
274
- TypeScript, JavaScript, Python, Rust, Go, Java, Kotlin, Swift, C#, C++, C, PHP, Ruby, Scala, Dart, Lua, Shell/Bash, SQL, R, Vue, Svelte, Perl, Groovy
289
+ TypeScript, JavaScript, Python, Rust, Go, Java, Kotlin, Swift, Objective-C, C#, C++, C, PHP, Ruby, Scala, Dart, Lua, Shell/Bash, SQL, R, Vue, Svelte, Perl, Groovy, Elixir, Common Lisp, Matlab, Protocol Buffers, BSL (1C:Enterprise)
275
290
 
276
291
  Plus structural summaries for non-code files: JSON, YAML, Markdown, TOML, XML, CSV.
277
292
 
@@ -319,8 +334,8 @@ npm run dev # TypeScript watch mode
319
334
 
320
335
  ```
321
336
  src/
322
- index.ts — CLI entry point (6 commands)
323
- server.ts — MCP server setup, 14 tool definitions, instructions
337
+ index.ts — CLI entry point and server bootstrap
338
+ server.ts — MCP server setup, tool definitions, instructions
324
339
  types.ts — Core domain types
325
340
  ast-index/
326
341
  client.ts — ast-index CLI wrapper (22+ methods)
@@ -332,10 +347,17 @@ src/
332
347
  context-registry.ts — Advisory context tracking + compact reminders
333
348
  symbol-resolver.ts — Qualified symbol resolution
334
349
  token-estimator.ts — Token count estimation
335
- session-analytics.ts — Token savings tracking
350
+ session-analytics.ts — Token savings tracking with intent + decision trace
336
351
  validation.ts — Input validators for all tools
337
352
  format-duration.ts — Shared duration formatter
338
353
  project-detector.ts — Config-based project detection (frameworks, CI, quality tools)
354
+ confidence.ts — Confidence metadata for response completeness
355
+ intent-classifier.ts — Tool → intent mapping (edit/debug/explore/review/analyze/search/read)
356
+ budget-planner.ts — Advisory: suggests cheaper tool alternatives
357
+ decision-trace.ts — Per-call instrumentation (cost, context state, alternatives)
358
+ session-cache.ts — Tool-result-level caching with invalidation
359
+ architecture-fingerprint.ts — Cross-session architecture caching
360
+ policy-engine.ts — Configurable team policies for consistent savings
339
361
  config/
340
362
  loader.ts — Config loading + deep merge
341
363
  defaults.ts — Default config values
@@ -360,7 +382,6 @@ src/
360
382
  smart-log.ts — smart_log handler (structured git log + category detection)
361
383
  test-summary.ts — test_summary handler (run tests + parse output)
362
384
  non-code.ts — JSON/YAML/MD/TOML structural summaries
363
- export-ast-index.ts — AST export for context-mode BM25
364
385
  git/
365
386
  watcher.ts — Git HEAD watcher (branch switch detection)
366
387
  file-watcher.ts — File system watcher (cache invalidation)
@@ -374,7 +395,7 @@ src/
374
395
 
375
396
  Token Pilot is built on top of these excellent open-source projects:
376
397
 
377
- - **[ast-index](https://github.com/defendend/Claude-ast-index-search)** by [@defendend](https://github.com/defendend) — Rust-based AST indexing engine with tree-sitter, SQLite FTS5, and support for 23 programming languages. Token Pilot uses it as the backend for all code analysis.
398
+ - **[ast-index](https://github.com/defendend/Claude-ast-index-search)** by [@defendend](https://github.com/defendend) — Rust-based AST indexing engine with tree-sitter, SQLite FTS5, and support for 29 programming languages. Token Pilot uses it as the backend for all code analysis.
378
399
  - **[claude-context-mode](https://github.com/mksglu/claude-context-mode)** by [@mksglu](https://github.com/mksglu) — Complementary MCP plugin for shell output and data file processing via sandbox + BM25. Token Pilot integrates with it for maximum combined savings.
379
400
  - **[Model Context Protocol](https://modelcontextprotocol.io/)** by Anthropic — The protocol that makes all of this possible.
380
401
 
@@ -42,6 +42,18 @@ export const DEFAULT_CONFIG = {
42
42
  checkOnStartup: true,
43
43
  autoUpdate: false,
44
44
  },
45
+ sessionCache: {
46
+ enabled: true,
47
+ maxEntries: 200,
48
+ },
49
+ policies: {
50
+ preferCheapReads: true,
51
+ requireReadForEditBeforeEdit: true,
52
+ cacheProjectOverview: true,
53
+ maxFullFileReads: 10,
54
+ warnOnLargeReads: true,
55
+ largeReadThreshold: 2000,
56
+ },
45
57
  ignore: [
46
58
  'node_modules/**',
47
59
  'dist/**',
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Architecture fingerprint — caches project architecture data to a file
3
+ * to amortize overview cost across sessions.
4
+ * Track 8: Architecture Fingerprint
5
+ */
6
+ export interface ArchitectureFingerprint {
7
+ version: string;
8
+ generatedAt: number;
9
+ projectType?: string;
10
+ frameworks: string[];
11
+ testLayout?: string;
12
+ entrypoints: string[];
13
+ moduleCount: number;
14
+ sourceFileCount: number;
15
+ namingConventions: string[];
16
+ }
17
+ /**
18
+ * Load fingerprint from disk. Returns null if missing or expired.
19
+ */
20
+ export declare function loadFingerprint(projectRoot: string): Promise<ArchitectureFingerprint | null>;
21
+ /**
22
+ * Save fingerprint to disk.
23
+ */
24
+ export declare function saveFingerprint(projectRoot: string, fp: ArchitectureFingerprint): Promise<void>;
25
+ /**
26
+ * Build fingerprint from project_overview text output.
27
+ * Parses the structured overview text to extract key architecture data.
28
+ */
29
+ export declare function buildFingerprint(overviewText: string, version: string): ArchitectureFingerprint;
30
+ /**
31
+ * Format a cached fingerprint as a summary section.
32
+ */
33
+ export declare function formatCachedFingerprint(fp: ArchitectureFingerprint): string;
34
+ //# sourceMappingURL=architecture-fingerprint.d.ts.map
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Architecture fingerprint — caches project architecture data to a file
3
+ * to amortize overview cost across sessions.
4
+ * Track 8: Architecture Fingerprint
5
+ */
6
+ import { readFile, writeFile, stat } from 'node:fs/promises';
7
+ import { join } from 'node:path';
8
+ const FINGERPRINT_FILE = '.token-pilot-fingerprint.json';
9
+ const FINGERPRINT_TTL_MS = 24 * 60 * 60 * 1000; // 24 hours
10
+ /**
11
+ * Load fingerprint from disk. Returns null if missing or expired.
12
+ */
13
+ export async function loadFingerprint(projectRoot) {
14
+ const filePath = join(projectRoot, FINGERPRINT_FILE);
15
+ try {
16
+ const fileStat = await stat(filePath);
17
+ const age = Date.now() - fileStat.mtimeMs;
18
+ if (age > FINGERPRINT_TTL_MS) {
19
+ return null; // expired
20
+ }
21
+ const raw = await readFile(filePath, 'utf-8');
22
+ const data = JSON.parse(raw);
23
+ // Validate minimal structure
24
+ if (!data.version || !data.generatedAt) {
25
+ return null;
26
+ }
27
+ return data;
28
+ }
29
+ catch {
30
+ return null; // file doesn't exist or is invalid
31
+ }
32
+ }
33
+ /**
34
+ * Save fingerprint to disk.
35
+ */
36
+ export async function saveFingerprint(projectRoot, fp) {
37
+ const filePath = join(projectRoot, FINGERPRINT_FILE);
38
+ await writeFile(filePath, JSON.stringify(fp, null, 2) + '\n', 'utf-8');
39
+ }
40
+ /**
41
+ * Build fingerprint from project_overview text output.
42
+ * Parses the structured overview text to extract key architecture data.
43
+ */
44
+ export function buildFingerprint(overviewText, version) {
45
+ const fp = {
46
+ version,
47
+ generatedAt: Date.now(),
48
+ frameworks: [],
49
+ entrypoints: [],
50
+ moduleCount: 0,
51
+ sourceFileCount: 0,
52
+ namingConventions: [],
53
+ };
54
+ // Extract project type
55
+ const typeMatch = overviewText.match(/TYPE\s*(?:\([^)]*\))?:\s*(.+)/);
56
+ if (typeMatch) {
57
+ fp.projectType = typeMatch[1].trim().split('\n')[0];
58
+ }
59
+ // Extract frameworks
60
+ const fwMatch = overviewText.match(/FRAMEWORKS:\s*(.+)/);
61
+ if (fwMatch) {
62
+ fp.frameworks = fwMatch[1].split(',').map(s => s.trim()).filter(Boolean);
63
+ }
64
+ // Extract file count from MAP or ast-index data
65
+ const fileCountMatch = overviewText.match(/(\d+)\s*files/);
66
+ if (fileCountMatch) {
67
+ fp.sourceFileCount = parseInt(fileCountMatch[1], 10);
68
+ }
69
+ // Extract naming patterns
70
+ const patternsMatch = overviewText.match(/PATTERNS:\s*(.+)/);
71
+ if (patternsMatch) {
72
+ fp.namingConventions = patternsMatch[1].split(',').map(s => s.trim()).filter(Boolean);
73
+ }
74
+ // Extract architecture
75
+ const archMatch = overviewText.match(/ARCHITECTURE:\s*(.+)/);
76
+ if (archMatch) {
77
+ fp.testLayout = archMatch[1].trim();
78
+ }
79
+ // Extract MAP entries as module indicators
80
+ const mapEntries = overviewText.match(/^\s{2}\S+.*\(\d+ files/gm);
81
+ if (mapEntries) {
82
+ fp.moduleCount = mapEntries.length;
83
+ // Detect entrypoints from common patterns
84
+ for (const entry of mapEntries) {
85
+ const dirMatch = entry.match(/^\s*(\S+)/);
86
+ if (dirMatch) {
87
+ const dir = dirMatch[1];
88
+ if (/^(src|lib|app|main|index)/.test(dir)) {
89
+ fp.entrypoints.push(dir);
90
+ }
91
+ }
92
+ }
93
+ }
94
+ return fp;
95
+ }
96
+ /**
97
+ * Format a cached fingerprint as a summary section.
98
+ */
99
+ export function formatCachedFingerprint(fp) {
100
+ const lines = [
101
+ '--- Cached Architecture (from previous session) ---',
102
+ ];
103
+ if (fp.projectType) {
104
+ lines.push(`TYPE: ${fp.projectType}`);
105
+ }
106
+ if (fp.frameworks.length > 0) {
107
+ lines.push(`FRAMEWORKS: ${fp.frameworks.join(', ')}`);
108
+ }
109
+ if (fp.sourceFileCount > 0) {
110
+ lines.push(`FILES: ${fp.sourceFileCount}`);
111
+ }
112
+ if (fp.moduleCount > 0) {
113
+ lines.push(`MODULES: ${fp.moduleCount}`);
114
+ }
115
+ if (fp.namingConventions.length > 0) {
116
+ lines.push(`PATTERNS: ${fp.namingConventions.join(', ')}`);
117
+ }
118
+ if (fp.entrypoints.length > 0) {
119
+ lines.push(`ENTRYPOINTS: ${fp.entrypoints.join(', ')}`);
120
+ }
121
+ const age = Date.now() - fp.generatedAt;
122
+ const hoursAgo = Math.round(age / (60 * 60 * 1000));
123
+ lines.push(`CACHED: ${hoursAgo}h ago (v${fp.version})`);
124
+ lines.push('---');
125
+ return lines.join('\n');
126
+ }
127
+ //# sourceMappingURL=architecture-fingerprint.js.map
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Budget planner — advisory layer that suggests cheaper tool alternatives.
3
+ * Phase 1: analytics-only, no active blocking.
4
+ */
5
+ export interface CheaperAlternative {
6
+ tool: string;
7
+ estimatedTokens: number;
8
+ reason: string;
9
+ }
10
+ export interface BudgetContext {
11
+ fileLines?: number;
12
+ alreadyInContext: boolean;
13
+ symbolKnown: boolean;
14
+ recentlyEdited: boolean;
15
+ }
16
+ /**
17
+ * Given the tool that was used and the context, suggest a cheaper alternative.
18
+ * Returns null if the chosen tool was already optimal.
19
+ */
20
+ export declare function suggestCheaperAlternative(usedTool: string, args: Record<string, unknown>, context: BudgetContext): CheaperAlternative | null;
21
+ //# sourceMappingURL=budget-planner.d.ts.map
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Budget planner — advisory layer that suggests cheaper tool alternatives.
3
+ * Phase 1: analytics-only, no active blocking.
4
+ */
5
+ /**
6
+ * Given the tool that was used and the context, suggest a cheaper alternative.
7
+ * Returns null if the chosen tool was already optimal.
8
+ */
9
+ export function suggestCheaperAlternative(usedTool, args, context) {
10
+ const fileLines = context.fileLines ?? 0;
11
+ switch (usedTool) {
12
+ case 'smart_read': {
13
+ // If file is already in context and was recently edited, read_diff is much cheaper
14
+ if (context.alreadyInContext && context.recentlyEdited) {
15
+ return {
16
+ tool: 'read_diff',
17
+ estimatedTokens: Math.max(20, Math.round(fileLines * 0.1)),
18
+ reason: 'file already in context and recently edited — read_diff shows only changes',
19
+ };
20
+ }
21
+ // If a specific symbol is known, read_symbol is cheaper
22
+ if (context.symbolKnown && fileLines > 50) {
23
+ return {
24
+ tool: 'read_symbol',
25
+ estimatedTokens: Math.round(fileLines * 0.15),
26
+ reason: 'specific symbol known — read_symbol returns only the target',
27
+ };
28
+ }
29
+ break;
30
+ }
31
+ case 'smart_read_many': {
32
+ // If all files are already in context, this is wasteful
33
+ if (context.alreadyInContext) {
34
+ return {
35
+ tool: 'read_diff',
36
+ estimatedTokens: Math.max(20, Math.round(fileLines * 0.1)),
37
+ reason: 'files already in context — use read_diff for changed files only',
38
+ };
39
+ }
40
+ break;
41
+ }
42
+ case 'read_range': {
43
+ // Large ranges (>60 lines) could use read_symbol if symbol is known
44
+ const limit = typeof args.limit === 'number' ? args.limit : 0;
45
+ if (limit > 60 && context.symbolKnown) {
46
+ return {
47
+ tool: 'read_symbol',
48
+ estimatedTokens: Math.round(limit * 0.4),
49
+ reason: 'large range with known symbol — read_symbol is more targeted',
50
+ };
51
+ }
52
+ break;
53
+ }
54
+ case 'read_symbol': {
55
+ // If file was recently edited and symbol already loaded, read_diff is better
56
+ if (context.alreadyInContext && context.recentlyEdited) {
57
+ return {
58
+ tool: 'read_diff',
59
+ estimatedTokens: Math.max(20, Math.round(fileLines * 0.1)),
60
+ reason: 'symbol already loaded and file edited — read_diff shows changes only',
61
+ };
62
+ }
63
+ break;
64
+ }
65
+ }
66
+ return null;
67
+ }
68
+ //# sourceMappingURL=budget-planner.js.map
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Confidence metadata — tells the LLM how complete the response is
3
+ * and what follow-up actions might be needed.
4
+ * Track 5: Confidence-Based Escalation
5
+ */
6
+ export type ConfidenceLevel = 'high' | 'medium' | 'low';
7
+ export interface ConfidenceMetadata {
8
+ confidence: ConfidenceLevel;
9
+ knownUnknowns: string[];
10
+ suggestedNextStep?: string;
11
+ }
12
+ export interface ConfidenceInput {
13
+ symbolResolved?: boolean;
14
+ fullFile?: boolean;
15
+ truncated?: boolean;
16
+ hasTests?: boolean;
17
+ hasCallers?: boolean;
18
+ crossFileDeps?: number;
19
+ refsFound?: boolean;
20
+ astAvailable?: boolean;
21
+ dedupHit?: boolean;
22
+ }
23
+ /**
24
+ * Assess confidence level based on response completeness signals.
25
+ */
26
+ export declare function assessConfidence(input: ConfidenceInput): ConfidenceMetadata;
27
+ /**
28
+ * Format confidence metadata as a text section for tool output.
29
+ */
30
+ export declare function formatConfidence(meta: ConfidenceMetadata): string;
31
+ //# sourceMappingURL=confidence.d.ts.map
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Confidence metadata — tells the LLM how complete the response is
3
+ * and what follow-up actions might be needed.
4
+ * Track 5: Confidence-Based Escalation
5
+ */
6
+ /**
7
+ * Assess confidence level based on response completeness signals.
8
+ */
9
+ export function assessConfidence(input) {
10
+ const unknowns = [];
11
+ let score = 0;
12
+ // Positive signals
13
+ if (input.symbolResolved)
14
+ score += 3;
15
+ if (input.fullFile)
16
+ score += 2;
17
+ if (input.hasTests)
18
+ score += 1;
19
+ if (input.hasCallers)
20
+ score += 1;
21
+ if (input.refsFound)
22
+ score += 2;
23
+ if (input.astAvailable)
24
+ score += 1;
25
+ // Negative signals
26
+ if (input.truncated) {
27
+ score -= 2;
28
+ unknowns.push('output was truncated — some content not shown');
29
+ }
30
+ if (input.crossFileDeps !== undefined && input.crossFileDeps > 3) {
31
+ score -= 1;
32
+ unknowns.push(`${input.crossFileDeps} cross-file dependencies not explored`);
33
+ }
34
+ if (input.symbolResolved === false) {
35
+ score -= 2;
36
+ unknowns.push('target symbol not resolved');
37
+ }
38
+ if (input.astAvailable === false) {
39
+ score -= 1;
40
+ unknowns.push('AST index unavailable — structural analysis limited');
41
+ }
42
+ if (input.hasTests === false) {
43
+ unknowns.push('no test file found for this module');
44
+ }
45
+ // Dedup hit is informational, not a quality issue
46
+ if (input.dedupHit) {
47
+ score += 1; // already known = high confidence in context
48
+ }
49
+ // Determine level
50
+ let confidence;
51
+ if (score >= 5) {
52
+ confidence = 'high';
53
+ }
54
+ else if (score >= 2) {
55
+ confidence = 'medium';
56
+ }
57
+ else {
58
+ confidence = 'low';
59
+ }
60
+ // Generate suggested next step based on unknowns
61
+ let suggestedNextStep;
62
+ if (input.truncated) {
63
+ suggestedNextStep = 'use read_range() or read_symbol() with show="full" for remaining content';
64
+ }
65
+ else if (input.symbolResolved === false) {
66
+ suggestedNextStep = 'use smart_read() to see available symbols, then read_symbol() for the target';
67
+ }
68
+ else if (input.astAvailable === false) {
69
+ suggestedNextStep = 'structural reading unavailable — use read_range() for raw content';
70
+ }
71
+ else if (input.crossFileDeps !== undefined && input.crossFileDeps > 3) {
72
+ suggestedNextStep = 'use find_usages() or related_files() to explore cross-file dependencies';
73
+ }
74
+ const result = { confidence, knownUnknowns: unknowns };
75
+ if (suggestedNextStep) {
76
+ result.suggestedNextStep = suggestedNextStep;
77
+ }
78
+ return result;
79
+ }
80
+ /**
81
+ * Format confidence metadata as a text section for tool output.
82
+ */
83
+ export function formatConfidence(meta) {
84
+ const lines = [
85
+ '',
86
+ `CONFIDENCE: ${meta.confidence}`,
87
+ ];
88
+ if (meta.knownUnknowns.length > 0) {
89
+ lines.push(`KNOWN UNKNOWNS: ${meta.knownUnknowns.join('; ')}`);
90
+ }
91
+ else {
92
+ lines.push('KNOWN UNKNOWNS: none');
93
+ }
94
+ if (meta.suggestedNextStep) {
95
+ lines.push(`SUGGESTED: ${meta.suggestedNextStep}`);
96
+ }
97
+ return lines.join('\n');
98
+ }
99
+ //# sourceMappingURL=confidence.js.map
@@ -11,12 +11,26 @@ export declare class ContextRegistry {
11
11
  setContentHash(path: string, hash: string): void;
12
12
  getLoaded(path: string): LoadedRegion[] | null;
13
13
  isSymbolLoaded(path: string, symbolName: string): boolean;
14
+ /** Check if any region of a file has been loaded into context. */
15
+ hasAnyLoaded(path: string): boolean;
14
16
  isStale(path: string, currentHash: string): boolean;
15
17
  /**
16
18
  * Generate a compact reminder for previously loaded content.
17
19
  * Returns a brief summary instead of full re-read.
18
20
  */
19
21
  compactReminder(path: string, symbols: SymbolInfo[]): string;
22
+ /** Check if file was loaded in full (type='full' region exists). */
23
+ isFullyLoaded(path: string): boolean;
24
+ /**
25
+ * Generate a compact dedup reminder for read_symbol.
26
+ * Fires when same symbol was already loaded OR full file is in context.
27
+ */
28
+ symbolReminder(path: string, symbolName: string): string;
29
+ /**
30
+ * Generate a compact dedup reminder for read_range.
31
+ * Only fires when full file is in context.
32
+ */
33
+ rangeReminder(path: string, startLine: number, endLine: number): string;
20
34
  forget(path: string, symbolName?: string): void;
21
35
  forgetAll(): void;
22
36
  summary(): {