token-pilot 0.15.0 → 0.16.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.
package/CHANGELOG.md CHANGED
@@ -5,22 +5,45 @@ 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.16.1] - 2026-03-21
9
+
10
+ ### Added
11
+ - **Hook interception tracking** — PreToolUse hook now records denied Read calls (file path, line count, estimated tokens) to `.token-pilot/hook-denied.jsonl`. Session analytics shows how many tokens the hook saved by intercepting unbounded reads on large code files.
12
+ - **`session_analytics` hook savings** — compact report adds "Hook: intercepted N reads, saved ~X tokens" line. Verbose mode shows per-file breakdown of intercepted reads.
13
+
14
+ ## [0.16.0] - 2026-03-21
15
+
16
+ ### Added
17
+ - **`read_symbols` tool** — batch read multiple symbols from one file in a single call (max 10). File is read once, AST resolved once. Saves N-1 round-trips vs calling `read_symbol` N times.
18
+ - **`read_for_edit` batch mode** — new `symbols` array parameter reads multiple symbol edit contexts in one call. Each symbol returns raw code ready for Edit tool's `old_string`.
19
+ - **`find_usages` context_lines** — new `context_lines` parameter (0-10) shows surrounding source code for each match. Eliminates follow-up `read_symbol` calls after finding usages.
20
+ - **`smart_diff` affected symbols summary** — consolidated "AFFECTED SYMBOLS" section at the top of diff output, grouped by MODIFIED/ADDED/REMOVED. See all changed functions/classes at a glance.
21
+
22
+ ### Changed
23
+ - **19 tools** (was 18) — added `read_symbols`.
24
+ - **MCP instructions** — added batch read_symbols to decision rules and refactor workflow.
25
+ - **427 tests** (unchanged — all pass with new features).
26
+
8
27
  ## [0.15.0] - 2026-03-19
9
28
 
10
29
  ### Added
11
- - **Regex fallback parser** — `smart_read` now works for TS/JS files even without ast-index binary. Parses classes, functions, interfaces, types, enums, and class methods via regex. Zero dependencies, 130 lines. Covers ~80% of new users who fail to download ast-index.
30
+ - **Regex fallback parser (TS/JS)** — `smart_read` now works for TypeScript/JavaScript files even without ast-index binary. Parses classes, functions, interfaces, types, enums, and class methods via regex. Zero dependencies, 130 lines. Covers ~80% of new users who fail to download ast-index.
31
+ - **Regex fallback parser (Python)** — `smart_read` now works for Python files without ast-index. Parses classes, functions, async functions, decorators (`@dataclass`, `@app.route`), module constants (`UPPER_CASE`), methods with visibility detection (`_private`, `__dunder__`). 150 lines.
32
+ - **Benchmark script** — `scripts/benchmark.ts` measures real token savings on public repos (express, fastify, flask). 92% average savings across 97 files ≥50 lines. Run: `npx tsx scripts/benchmark.ts`.
12
33
  - **Guide skill** — `/guide` command shows a quick-reference table of all Token Pilot tools with usage examples and recommended workflow.
13
34
  - **`hooks.denyThreshold` config** — hook deny threshold is now configurable in `.token-pilot.json` (default: 300, was hardcoded 500). Intercepts ~2x more native Read calls.
14
35
 
15
36
  ### Changed
16
- - **Compact session analytics** — `session_analytics` report reduced from ~30 lines to ~5 lines. Shows calls, tokens saved, top 5 tools, top 3 files, cache hit rate on a single screen.
37
+ - **Compact session analytics** — `session_analytics` report reduced from ~30 lines to ~5 lines. Shows calls, tokens saved, top 5 tools, top 3 files, cache hit rate on a single screen. Verbose mode (`verbose=true`) restores full breakdown.
17
38
  - **`server.ts` refactor** — extracted tool definitions to `server/tool-definitions.ts` and token estimate helpers to `server/token-estimates.ts` (−500 lines from server.ts).
18
39
  - **`find_usages` output** — results grouped by file with compact rendering. Single match per file on one line, multiple matches indented under file header.
19
40
  - **Stale references** — all `grep_search` hints updated to `Grep` (code-audit, find-unused, find-usages).
20
- - **409 tests** (was 393).
41
+ - **README** — benchmark table with real data from 4 public repos. Updated savings claims from 80% to 90% (backed by benchmark).
42
+ - **427 tests** (was 393).
21
43
 
22
44
  ### Fixed
23
45
  - **`npx token-pilot` CLI** — symlink path resolution in `isDirectRun` check. All CLI commands now work correctly via npx.
46
+ - **Regex fallback was dead code** — parsers existed but weren't wired into `client.ts` `outline()` method. Now properly called as fallback when ast-index unavailable.
24
47
 
25
48
  ## [0.14.1] - 2026-03-14
26
49
 
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 **up to 80%** via AST-aware lazy file reading.
3
+ MCP server that reduces token consumption in AI coding assistants by **up to 90%** 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,23 @@ 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
- **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.
16
+ **Up to 90% reduction** on large files. Files under 200 lines are returned in full automatically (zero overhead for small files).
17
+
18
+ ### Benchmarks (real data)
19
+
20
+ Measured on public open-source repos using the regex fallback parser (no ast-index binary). Files ≥50 lines only:
21
+
22
+ | Repo | Files | Raw Tokens | Outline Tokens | Savings |
23
+ |------|------:|----------:|--------------:|--------:|
24
+ | [token-pilot](https://github.com/Digital-Threads/token-pilot) (TS) | 48 | 88,920 | 8,123 | **91%** |
25
+ | [express](https://github.com/expressjs/express) (JS) | 6 | 14,421 | 193 | **99%** |
26
+ | [fastify](https://github.com/fastify/fastify) (JS) | 23 | 50,000 | 3,161 | **94%** |
27
+ | [flask](https://github.com/pallets/flask) (Python) | 20 | 78,236 | 7,418 | **91%** |
28
+ | **Total** | **97** | **231,577** | **18,895** | **92%** |
29
+
30
+ > This measures `smart_read` structural outline savings only. Real sessions also benefit from session cache, dedup reminders, `read_symbol` targeted loading, and `read_for_edit` minimal context.
31
+ >
32
+ > Run the benchmark yourself: `npx tsx scripts/benchmark.ts`
17
33
 
18
34
  ## Installation
19
35
 
@@ -152,15 +168,16 @@ For more control, you can add rules to your project:
152
168
  - **Cursor** → `.cursorrules` in project root
153
169
  - **Codex** → `AGENTS.md` in project root
154
170
 
155
- ## MCP Tools (18)
171
+ ## MCP Tools (19)
156
172
 
157
173
  ### Core Reading
158
174
 
159
175
  | Tool | Instead of | Description |
160
176
  |------|-----------|-------------|
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. |
177
+ | `smart_read` | `Read` | AST structural overview: classes, functions, methods with signatures. Up to 90% savings on large files. Framework-aware: shows HTTP routes, column types, validation rules. |
162
178
  | `read_symbol` | `Read` + scroll | Load source of a specific symbol. Supports `Class.method`. `show` param: full/head/tail/outline. |
163
- | `read_for_edit` | `Read` before `Edit` | Minimal RAW code around a symbol copy directly as `old_string` for Edit tool. |
179
+ | `read_symbols` | N x `read_symbol` | Batch read multiple symbols from one file in a single call (max 10). One round-trip instead of N. |
180
+ | `read_for_edit` | `Read` before `Edit` | Minimal RAW code around a symbol — copy directly as `old_string` for Edit tool. Batch mode: pass `symbols` array for multiple edit contexts. |
164
181
  | `read_range` | `Read` offset | Read a specific line range from a file. |
165
182
  | `read_diff` | re-`Read` | Show only changed hunks since last smart_read. Requires smart_read before editing (for baseline). Works with any edit tool. |
166
183
  | `smart_read_many` | multiple `Read` | Batch smart_read for up to 20 files in one call. |
@@ -169,14 +186,14 @@ For more control, you can add rules to your project:
169
186
 
170
187
  | Tool | Instead of | Description |
171
188
  |------|-----------|-------------|
172
- | `find_usages` | `Grep` (refs) | All usages of a symbol: definitions, imports, references. Filters: `scope` (path prefix), `kind` (definitions/imports/usages), `lang`, `limit`. |
189
+ | `find_usages` | `Grep` (refs) | All usages of a symbol: definitions, imports, references. Filters: `scope`, `kind`, `lang`, `limit`. Use `context_lines` to include surrounding source code. |
173
190
  | `project_overview` | `ls` + explore | Dual-detection (ast-index + config files) with confidence scoring. Project type, frameworks, quality tools, CI, architecture, directory map. Filter sections with `include`. |
174
191
  | `related_files` | manual explore | Import graph: what a file imports, what imports it, test files. |
175
192
  | `outline` | multiple `smart_read` | Compact overview of all code files in a directory. One call instead of 5-6. Supports `recursive` mode with `max_depth` for deep exploration. |
176
193
  | `find_unused` | manual | Detect dead code — unused functions, classes, variables. |
177
194
  | `code_audit` | multiple `Grep` | Code quality issues in one call: TODO/FIXME comments, deprecated symbols, structural code patterns (via ast-grep), decorator search. |
178
195
  | `module_info` | manual analysis | Module dependency analysis: dependencies, dependents, public API, unused deps. Use for architecture understanding and dependency cleanup. |
179
- | `smart_diff` | raw `git diff` | Structural diff with AST symbol mapping — shows which functions/classes changed instead of raw patch. Scopes: unstaged, staged, commit, branch. |
196
+ | `smart_diff` | raw `git diff` | Structural diff with AST symbol mapping — shows which functions/classes changed instead of raw patch. Affected symbols summary at top. Scopes: unstaged, staged, commit, branch. |
180
197
  | `explore_area` | outline + related_files + git log | One-call directory exploration: structure, imports, tests, recent changes. Replaces 3-5 separate calls. |
181
198
  | `smart_log` | raw `git log` | Structured commit history with category detection (feat/fix/refactor/docs), file stats, author breakdown. Filters by path and ref. |
182
199
  | `test_summary` | raw test output | Run tests and get structured summary: total/passed/failed + failure details. Supports vitest, jest, pytest, phpunit, go, cargo, rspec, mocha. |
@@ -280,7 +297,7 @@ When both are configured, Token Pilot automatically:
280
297
  - Suggests context-mode for large non-code files
281
298
  - Shows combined architecture info in `session_analytics`
282
299
 
283
- **Combined savings: up to 80%** in a typical coding session.
300
+ **Combined savings: up to 90%** in a typical coding session.
284
301
 
285
302
  ## Supported Languages
286
303
 
@@ -22,6 +22,8 @@ export declare class AstIndexClient {
22
22
  private buildIndex;
23
23
  private handleOversizedIndex;
24
24
  outline(filePath: string): Promise<FileStructure | null>;
25
+ /** Regex-based fallback for TS/JS/Python when ast-index binary is unavailable. */
26
+ private regexFallback;
25
27
  symbol(name: string): Promise<AstIndexSymbolDetail | null>;
26
28
  search(query: string, options?: {
27
29
  inFile?: string;
@@ -3,6 +3,10 @@ import { promisify } from 'node:util';
3
3
  import { findBinary, installBinary } from './binary-manager.js';
4
4
  import { parseFileCount, parseOutlineText, parseImportsText, parseImplementationsText, parseHierarchyText, parseAgrepText, parseTodoText, parseDeprecatedText, parseAnnotationsText, parseModuleListText, parseModuleDepText, parseUnusedDepsText, parseModuleApiText, } from './parser.js';
5
5
  import { buildFileStructure } from './enricher.js';
6
+ import { parseTypeScriptRegex } from './regex-parser.js';
7
+ import { parsePythonRegex } from './regex-parser-python.js';
8
+ const TS_JS_EXTENSIONS = new Set(['ts', 'tsx', 'js', 'jsx', 'mjs', 'cjs']);
9
+ const PYTHON_EXTENSIONS = new Set(['py', 'pyw']);
6
10
  const execFileAsync = promisify(execFile);
7
11
  export class AstIndexClient {
8
12
  static MAX_INDEX_FILES = 50_000;
@@ -153,7 +157,7 @@ export class AstIndexClient {
153
157
  }
154
158
  catch {
155
159
  if (this.indexDisabled || this.indexOversized)
156
- return null;
160
+ return this.regexFallback(filePath);
157
161
  try {
158
162
  await this.ensureIndex();
159
163
  const result = await this.exec(['outline', filePath]);
@@ -164,10 +168,30 @@ export class AstIndexClient {
164
168
  }
165
169
  catch (err) {
166
170
  console.error(`[token-pilot] ast-index outline failed for ${filePath}: ${err instanceof Error ? err.message : err}`);
167
- return null;
171
+ return this.regexFallback(filePath);
168
172
  }
169
173
  }
170
174
  }
175
+ /** Regex-based fallback for TS/JS/Python when ast-index binary is unavailable. */
176
+ async regexFallback(filePath) {
177
+ const ext = filePath.split('.').pop()?.toLowerCase() ?? '';
178
+ const parser = TS_JS_EXTENSIONS.has(ext) ? parseTypeScriptRegex
179
+ : PYTHON_EXTENSIONS.has(ext) ? parsePythonRegex
180
+ : null;
181
+ if (!parser)
182
+ return null;
183
+ try {
184
+ const { readFile } = await import('node:fs/promises');
185
+ const content = await readFile(filePath, 'utf-8');
186
+ const entries = parser(content);
187
+ if (entries.length === 0)
188
+ return null;
189
+ return await buildFileStructure(filePath, entries);
190
+ }
191
+ catch {
192
+ return null;
193
+ }
194
+ }
171
195
  async symbol(name) {
172
196
  try {
173
197
  const result = await this.exec(['symbol', name, '--format', 'json']);
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Regex-based fallback parser for Python.
3
+ * Used when the ast-index binary is unavailable.
4
+ * Extracts top-level symbols (classes, functions, variables) and class methods.
5
+ */
6
+ import type { AstIndexOutlineEntry } from './types.js';
7
+ export declare function parsePythonRegex(content: string): AstIndexOutlineEntry[];
8
+ //# sourceMappingURL=regex-parser-python.d.ts.map
@@ -0,0 +1,132 @@
1
+ /**
2
+ * Regex-based fallback parser for Python.
3
+ * Used when the ast-index binary is unavailable.
4
+ * Extracts top-level symbols (classes, functions, variables) and class methods.
5
+ */
6
+ const PY_RESERVED = new Set([
7
+ 'if', 'else', 'elif', 'for', 'while', 'try', 'except', 'finally',
8
+ 'with', 'return', 'yield', 'raise', 'pass', 'break', 'continue',
9
+ 'import', 'from', 'as', 'del', 'assert', 'lambda', 'not', 'and', 'or',
10
+ ]);
11
+ // Top-level patterns (indent 0)
12
+ const PY_CLASS_RE = /^class\s+(\w+)\s*[\(:]/;
13
+ const PY_FUNC_RE = /^(?:async\s+)?def\s+(\w+)\s*\(/;
14
+ const PY_ASSIGN_RE = /^([A-Z][A-Z_0-9]+)\s*[=:]/; // MODULE_CONSTANT = ...
15
+ // Method patterns inside class body (indented 4+ spaces or 1+ tab)
16
+ const PY_METHOD_RE = /^(\s{4,}|\t+)(?:async\s+)?def\s+(\w+)\s*\(/;
17
+ // Decorator
18
+ const PY_DECORATOR_RE = /^@(\w[\w.]*)/;
19
+ export function parsePythonRegex(content) {
20
+ const lines = content.split('\n');
21
+ const entries = [];
22
+ let currentClass = null;
23
+ let pendingDecorators = [];
24
+ for (let i = 0; i < lines.length; i++) {
25
+ const line = lines[i];
26
+ const lineNum = i + 1;
27
+ const trimmed = line.trim();
28
+ if (!trimmed || trimmed.startsWith('#'))
29
+ continue;
30
+ const indent = line.match(/^(\s*)/)?.[0].length ?? 0;
31
+ // Collect decorators
32
+ const decMatch = trimmed.match(PY_DECORATOR_RE);
33
+ if (decMatch) {
34
+ pendingDecorators.push(`@${decMatch[1]}`);
35
+ continue;
36
+ }
37
+ // Close current class when we hit a non-empty line at indent 0
38
+ // that is a new definition (not a continuation)
39
+ if (currentClass && indent === 0 && (PY_CLASS_RE.test(trimmed) || PY_FUNC_RE.test(trimmed) || PY_ASSIGN_RE.test(trimmed))) {
40
+ currentClass.end_line = lineNum - 1;
41
+ currentClass = null;
42
+ }
43
+ // Top-level class
44
+ if (indent === 0) {
45
+ const classMatch = trimmed.match(PY_CLASS_RE);
46
+ if (classMatch && !PY_RESERVED.has(classMatch[1])) {
47
+ const entry = {
48
+ name: classMatch[1],
49
+ kind: 'class',
50
+ start_line: pendingDecorators.length > 0 ? lineNum - pendingDecorators.length : lineNum,
51
+ end_line: 0,
52
+ signature: trimmed.slice(0, 120),
53
+ decorators: pendingDecorators.length > 0 ? [...pendingDecorators] : undefined,
54
+ children: [],
55
+ };
56
+ entries.push(entry);
57
+ currentClass = entry;
58
+ pendingDecorators = [];
59
+ continue;
60
+ }
61
+ // Top-level function
62
+ const funcMatch = trimmed.match(PY_FUNC_RE);
63
+ if (funcMatch && !PY_RESERVED.has(funcMatch[1])) {
64
+ entries.push({
65
+ name: funcMatch[1],
66
+ kind: 'function',
67
+ start_line: pendingDecorators.length > 0 ? lineNum - pendingDecorators.length : lineNum,
68
+ end_line: 0,
69
+ signature: trimmed.slice(0, 120),
70
+ is_async: trimmed.startsWith('async '),
71
+ decorators: pendingDecorators.length > 0 ? [...pendingDecorators] : undefined,
72
+ });
73
+ pendingDecorators = [];
74
+ continue;
75
+ }
76
+ // Module-level constant
77
+ const assignMatch = trimmed.match(PY_ASSIGN_RE);
78
+ if (assignMatch) {
79
+ entries.push({
80
+ name: assignMatch[1],
81
+ kind: 'variable',
82
+ start_line: lineNum,
83
+ end_line: lineNum,
84
+ signature: trimmed.slice(0, 120),
85
+ });
86
+ pendingDecorators = [];
87
+ continue;
88
+ }
89
+ }
90
+ // Methods inside class body
91
+ if (currentClass && indent >= 4) {
92
+ const methodMatch = line.match(PY_METHOD_RE);
93
+ if (methodMatch) {
94
+ const name = methodMatch[2];
95
+ if (name && !PY_RESERVED.has(name)) {
96
+ const decoratorStart = pendingDecorators.length > 0 ? lineNum - pendingDecorators.length : lineNum;
97
+ currentClass.children.push({
98
+ name,
99
+ kind: 'method',
100
+ start_line: decoratorStart,
101
+ end_line: lineNum + 5,
102
+ signature: trimmed.slice(0, 120),
103
+ is_async: trimmed.includes('async '),
104
+ visibility: name.startsWith('__') && name.endsWith('__') ? 'public'
105
+ : name.startsWith('_') ? 'private' : 'public',
106
+ decorators: pendingDecorators.length > 0 ? [...pendingDecorators] : undefined,
107
+ });
108
+ pendingDecorators = [];
109
+ }
110
+ continue;
111
+ }
112
+ }
113
+ // Non-matching line resets decorators
114
+ if (!decMatch) {
115
+ pendingDecorators = [];
116
+ }
117
+ }
118
+ // Close last class
119
+ if (currentClass) {
120
+ currentClass.end_line = lines.length;
121
+ }
122
+ // Fill in end_line for entries
123
+ for (let i = 0; i < entries.length; i++) {
124
+ if (entries[i].end_line === 0) {
125
+ entries[i].end_line = i < entries.length - 1
126
+ ? entries[i + 1].start_line - 1
127
+ : lines.length;
128
+ }
129
+ }
130
+ return entries;
131
+ }
132
+ //# sourceMappingURL=regex-parser-python.js.map
@@ -0,0 +1,20 @@
1
+ export interface DeniedRead {
2
+ filePath: string;
3
+ lineCount: number;
4
+ estimatedTokens: number;
5
+ timestamp: number;
6
+ }
7
+ /**
8
+ * Called from hook-read process when a Read is denied.
9
+ * Appends to a shared JSONL file so the MCP server can read it.
10
+ */
11
+ export declare function appendDeniedRead(filePath: string, lineCount: number, fileContent: string, projectRoot?: string): void;
12
+ /**
13
+ * Called from MCP server to load denied reads for analytics.
14
+ */
15
+ export declare function loadDeniedReads(projectRoot?: string): DeniedRead[];
16
+ /**
17
+ * Clear denied reads (called on session reset or after reporting).
18
+ */
19
+ export declare function clearDeniedReads(projectRoot?: string): void;
20
+ //# sourceMappingURL=hook-tracker.d.ts.map
@@ -0,0 +1,57 @@
1
+ import { appendFileSync, readFileSync, unlinkSync, mkdirSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ function getDeniedReadsPath(projectRoot) {
4
+ const root = projectRoot || process.cwd();
5
+ return join(root, '.token-pilot', 'hook-denied.jsonl');
6
+ }
7
+ /**
8
+ * Called from hook-read process when a Read is denied.
9
+ * Appends to a shared JSONL file so the MCP server can read it.
10
+ */
11
+ export function appendDeniedRead(filePath, lineCount, fileContent, projectRoot) {
12
+ try {
13
+ const charEstimate = Math.ceil(fileContent.length / 4);
14
+ const whitespaceRatio = (fileContent.match(/\s/g)?.length ?? 0) / fileContent.length;
15
+ const estimatedTokens = Math.ceil(charEstimate * (1 - whitespaceRatio * 0.3));
16
+ const entry = {
17
+ filePath,
18
+ lineCount,
19
+ estimatedTokens,
20
+ timestamp: Date.now(),
21
+ };
22
+ const outPath = getDeniedReadsPath(projectRoot);
23
+ mkdirSync(join(outPath, '..'), { recursive: true });
24
+ appendFileSync(outPath, JSON.stringify(entry) + '\n');
25
+ }
26
+ catch {
27
+ // Silent fail — hook must not break
28
+ }
29
+ }
30
+ /**
31
+ * Called from MCP server to load denied reads for analytics.
32
+ */
33
+ export function loadDeniedReads(projectRoot) {
34
+ try {
35
+ const raw = readFileSync(getDeniedReadsPath(projectRoot), 'utf-8');
36
+ return raw
37
+ .trim()
38
+ .split('\n')
39
+ .filter(Boolean)
40
+ .map(line => JSON.parse(line));
41
+ }
42
+ catch {
43
+ return [];
44
+ }
45
+ }
46
+ /**
47
+ * Clear denied reads (called on session reset or after reporting).
48
+ */
49
+ export function clearDeniedReads(projectRoot) {
50
+ try {
51
+ unlinkSync(getDeniedReadsPath(projectRoot));
52
+ }
53
+ catch {
54
+ // File may not exist
55
+ }
56
+ }
57
+ //# sourceMappingURL=hook-tracker.js.map
@@ -23,12 +23,14 @@ export declare class SessionAnalytics {
23
23
  private calls;
24
24
  private sessionStart;
25
25
  private contextModeStatus;
26
+ private projectRoot?;
27
+ setProjectRoot(root: string): void;
26
28
  setContextModeStatus(status: ContextModeStatus): void;
27
29
  record(call: ToolCall): void;
28
30
  /**
29
- * Generate a compact session report (~5 lines).
31
+ * Generate session report. Compact by default (~5 lines), verbose=true for full breakdown.
30
32
  */
31
- report(): string;
33
+ report(verbose?: boolean): string;
32
34
  reset(): void;
33
35
  }
34
36
  //# sourceMappingURL=session-analytics.d.ts.map