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.
- package/.claude-plugin/hooks/hooks.json +9 -0
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +29 -0
- package/README.md +36 -15
- package/dist/config/defaults.js +12 -0
- package/dist/core/architecture-fingerprint.d.ts +34 -0
- package/dist/core/architecture-fingerprint.js +127 -0
- package/dist/core/budget-planner.d.ts +21 -0
- package/dist/core/budget-planner.js +68 -0
- package/dist/core/confidence.d.ts +31 -0
- package/dist/core/confidence.js +99 -0
- package/dist/core/context-registry.d.ts +14 -0
- package/dist/core/context-registry.js +55 -0
- package/dist/core/decision-trace.d.ts +31 -0
- package/dist/core/decision-trace.js +45 -0
- package/dist/core/intent-classifier.d.ts +13 -0
- package/dist/core/intent-classifier.js +44 -0
- package/dist/core/policy-engine.d.ts +41 -0
- package/dist/core/policy-engine.js +76 -0
- package/dist/core/session-analytics.d.ts +8 -0
- package/dist/core/session-analytics.js +86 -7
- package/dist/core/session-cache.d.ts +74 -0
- package/dist/core/session-cache.js +162 -0
- package/dist/core/validation.d.ts +3 -0
- package/dist/core/validation.js +3 -0
- package/dist/git/file-watcher.d.ts +6 -0
- package/dist/git/file-watcher.js +18 -2
- package/dist/git/watcher.d.ts +3 -0
- package/dist/git/watcher.js +6 -0
- package/dist/handlers/code-audit.d.ts +7 -2
- package/dist/handlers/code-audit.js +19 -5
- package/dist/handlers/explore-area.d.ts +10 -0
- package/dist/handlers/explore-area.js +39 -13
- package/dist/handlers/find-unused.d.ts +3 -0
- package/dist/handlers/find-unused.js +3 -2
- package/dist/handlers/find-usages.d.ts +7 -0
- package/dist/handlers/find-usages.js +36 -5
- package/dist/handlers/module-info.d.ts +3 -0
- package/dist/handlers/module-info.js +22 -2
- package/dist/handlers/project-overview.d.ts +1 -1
- package/dist/handlers/project-overview.js +18 -2
- package/dist/handlers/read-for-edit.d.ts +3 -0
- package/dist/handlers/read-for-edit.js +185 -3
- package/dist/handlers/read-range.d.ts +1 -1
- package/dist/handlers/read-range.js +16 -1
- package/dist/handlers/read-symbol.d.ts +1 -1
- package/dist/handlers/read-symbol.js +26 -2
- package/dist/handlers/related-files.d.ts +11 -0
- package/dist/handlers/related-files.js +178 -42
- package/dist/handlers/smart-read-many.js +70 -16
- package/dist/handlers/smart-read.js +10 -1
- package/dist/handlers/test-summary.js +26 -3
- package/dist/hooks/installer.d.ts +12 -8
- package/dist/hooks/installer.js +24 -8
- package/dist/index.d.ts +16 -1
- package/dist/index.js +62 -56
- package/dist/server.js +395 -30
- package/dist/types.d.ts +12 -0
- package/package.json +18 -14
- package/start.sh +28 -27
- package/dist/handlers/class-hierarchy.d.ts +0 -11
- package/dist/handlers/class-hierarchy.js +0 -28
- package/dist/handlers/export-ast-index.d.ts +0 -22
- package/dist/handlers/export-ast-index.js +0 -175
- package/dist/handlers/find-implementations.d.ts +0 -11
- package/dist/handlers/find-implementations.js +0 -27
- package/dist/handlers/search-code.d.ts +0 -14
- package/dist/handlers/search-code.js +0 -32
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "token-pilot",
|
|
3
3
|
"displayName": "Token Pilot",
|
|
4
|
-
"description": "Reduces token consumption by 80
|
|
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
|
|
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 **
|
|
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
|
-
|
|
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
|
|
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
|
|
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 (
|
|
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.
|
|
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:
|
|
283
|
+
**Combined savings: up to 80%** in a typical coding session.
|
|
269
284
|
|
|
270
285
|
## Supported Languages
|
|
271
286
|
|
|
272
|
-
Token Pilot supports all
|
|
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
|
|
323
|
-
server.ts — MCP server setup,
|
|
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
|
|
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
|
|
package/dist/config/defaults.js
CHANGED
|
@@ -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(): {
|