token-pilot 0.12.0 → 0.14.1

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 +2 -2
  3. package/.claude-plugin/plugin.json +2 -2
  4. package/CHANGELOG.md +30 -1
  5. package/README.md +28 -7
  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 +61 -55
  58. package/dist/server.js +395 -30
  59. package/dist/types.d.ts +12 -0
  60. package/package.json +5 -3
  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,8 +1,8 @@
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.",
5
- "version": "0.12.0",
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
+ "version": "0.13.0",
6
6
  "author": "Digital-Threads",
7
7
  "repository": "https://github.com/Digital-Threads/token-pilot",
8
8
  "license": "MIT",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "token-pilot",
3
- "version": "0.12.0",
4
- "description": "Reduces token consumption by 80-95% via AST-aware lazy file reading. Returns structural overviews instead of full files.",
3
+ "version": "0.13.0",
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,7 +5,36 @@ 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.12.0] - 2026-03-14
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
+
37
+ ## [0.13.0] - 2026-03-14
9
38
 
10
39
  ### Added
11
40
  - **Version check for all components** — on startup, checks token-pilot (npm), ast-index (GitHub releases), and context-mode (npm) in parallel. Non-blocking, fire-and-forget. Shows update notifications in stderr.
package/README.md CHANGED
@@ -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
@@ -149,7 +152,7 @@ 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
 
@@ -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
 
@@ -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)
@@ -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(): {