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.
@@ -0,0 +1,142 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { estimateTokens } from '../core/token-estimator.js';
3
+ import { resolveSafePath } from '../core/validation.js';
4
+ import { assessConfidence, formatConfidence } from '../core/confidence.js';
5
+ export async function handleReadSymbols(args, projectRoot, symbolResolver, fileCache, contextRegistry, astIndex, advisoryReminders = true) {
6
+ const absPath = resolveSafePath(projectRoot, args.path);
7
+ // Get file content ONCE
8
+ const cached = fileCache.get(absPath);
9
+ let lines;
10
+ if (cached) {
11
+ lines = cached.lines;
12
+ }
13
+ else {
14
+ const content = await readFile(absPath, 'utf-8');
15
+ lines = content.split('\n');
16
+ }
17
+ // Get AST structure ONCE
18
+ let structure = cached?.structure;
19
+ if (!structure && astIndex) {
20
+ structure = await astIndex.outline(absPath) ?? undefined;
21
+ }
22
+ const N = args.symbols.length;
23
+ const sections = [];
24
+ // Show mode constants (same as read_symbol.ts)
25
+ const MAX_SYMBOL_LINES = 300;
26
+ const MAX_FULL_LINES = 500;
27
+ const HEAD = 50;
28
+ const TAIL = 30;
29
+ let anyTruncated = false;
30
+ let anyResolved = false;
31
+ let totalTokens = 0;
32
+ for (let i = 0; i < N; i++) {
33
+ const symbolName = args.symbols[i];
34
+ const idx = i + 1;
35
+ const resolved = await symbolResolver.resolve(symbolName, structure);
36
+ if (!resolved) {
37
+ sections.push(`SYMBOL ${idx}/${N}: ${symbolName}\n` +
38
+ `ERROR: Symbol "${symbolName}" not found in ${args.path}.\n` +
39
+ `HINT: Use smart_read("${args.path}") to see available symbols.`);
40
+ continue;
41
+ }
42
+ anyResolved = true;
43
+ const source = symbolResolver.extractSource(resolved, lines, {
44
+ contextBefore: args.context_before ?? 2,
45
+ contextAfter: args.context_after ?? 0,
46
+ });
47
+ const loc = `[L${resolved.startLine}-${resolved.endLine}]`;
48
+ const lineCount = resolved.endLine - resolved.startLine + 1;
49
+ // Determine effective show mode
50
+ const showMode = args.show ?? (lineCount > MAX_SYMBOL_LINES ? 'outline' : 'full');
51
+ let displaySource = source;
52
+ let truncated = false;
53
+ if (showMode === 'full') {
54
+ if (lineCount > MAX_FULL_LINES) {
55
+ const sourceLines = source.split('\n');
56
+ displaySource = sourceLines.slice(0, MAX_FULL_LINES).join('\n');
57
+ displaySource += `\n\n ... truncated at ${MAX_FULL_LINES} lines (${lineCount - MAX_FULL_LINES} more). Use show="head"/"tail" for targeted view.`;
58
+ truncated = true;
59
+ }
60
+ }
61
+ else if (showMode === 'head') {
62
+ const sourceLines = source.split('\n');
63
+ displaySource = sourceLines.slice(0, HEAD).join('\n');
64
+ if (lineCount > HEAD) {
65
+ displaySource += `\n\n ... ${lineCount - HEAD} more lines. Use show="tail" or read_symbol("${args.path}", "MethodName") for specific parts.`;
66
+ truncated = true;
67
+ }
68
+ }
69
+ else if (showMode === 'tail') {
70
+ const sourceLines = source.split('\n');
71
+ displaySource = sourceLines.slice(-TAIL).join('\n');
72
+ if (lineCount > TAIL) {
73
+ displaySource = ` ... ${lineCount - TAIL} lines above ...\n\n` + displaySource;
74
+ truncated = true;
75
+ }
76
+ }
77
+ else {
78
+ // 'outline' mode: head + method list + tail
79
+ if (lineCount > HEAD + TAIL) {
80
+ const sourceLines = source.split('\n');
81
+ const head = sourceLines.slice(0, HEAD).join('\n');
82
+ const tail = sourceLines.slice(-TAIL).join('\n');
83
+ const omitted = sourceLines.length - HEAD - TAIL;
84
+ let methodOutline = '';
85
+ if (resolved.symbol.children && resolved.symbol.children.length > 0) {
86
+ const methodLines = resolved.symbol.children.map(c => {
87
+ const mLoc = `[L${c.location.startLine}-${c.location.endLine}]`;
88
+ return ` ${c.visibility === 'private' ? '🔒 ' : ''}${c.name}${c.kind === 'method' || c.kind === 'function' ? '()' : ''} ${mLoc} (${c.location.lineCount} lines)`;
89
+ });
90
+ methodOutline = `\nMETHODS (${resolved.symbol.children.length}):\n${methodLines.join('\n')}\n`;
91
+ }
92
+ displaySource = [
93
+ head,
94
+ '',
95
+ ` ... ${omitted} lines omitted — use read_symbol("${args.path}", "MethodName") to read specific methods ...`,
96
+ methodOutline,
97
+ tail,
98
+ ].join('\n');
99
+ truncated = true;
100
+ }
101
+ }
102
+ if (truncated)
103
+ anyTruncated = true;
104
+ const symbolLines = [
105
+ `SYMBOL ${idx}/${N}: ${symbolName} (${resolved.symbol.kind}) ${loc} (${lineCount} lines${truncated ? `, show=${showMode}` : ''})`,
106
+ '',
107
+ displaySource,
108
+ ];
109
+ if (resolved.symbol.references.length > 0) {
110
+ symbolLines.push('');
111
+ symbolLines.push(`REFERENCES: ${resolved.symbol.references.join(', ')}`);
112
+ }
113
+ sections.push(symbolLines.join('\n'));
114
+ // Track each symbol
115
+ const sectionTokens = estimateTokens(symbolLines.join('\n'));
116
+ totalTokens += sectionTokens;
117
+ contextRegistry.trackLoad(absPath, {
118
+ type: 'symbol',
119
+ symbolName,
120
+ startLine: resolved.startLine,
121
+ endLine: resolved.endLine,
122
+ tokens: sectionTokens,
123
+ });
124
+ }
125
+ if (cached?.hash) {
126
+ contextRegistry.setContentHash(absPath, cached.hash);
127
+ }
128
+ const header = `FILE: ${args.path} | SYMBOLS: ${N} requested`;
129
+ const body = sections.join('\n\n---\n\n');
130
+ const footer = 'CONTEXT TRACKED: These symbols are now in your context.';
131
+ const output = [header, '', body, '', footer].join('\n');
132
+ // Confidence metadata (aggregate)
133
+ const confidenceMeta = assessConfidence({
134
+ symbolResolved: anyResolved,
135
+ truncated: anyTruncated,
136
+ fullFile: false,
137
+ hasCallers: false,
138
+ astAvailable: !!structure,
139
+ });
140
+ return { content: [{ type: 'text', text: output + formatConfidence(confidenceMeta) }] };
141
+ }
142
+ //# sourceMappingURL=read-symbols.js.map
@@ -221,6 +221,29 @@ function formatSmartDiff(allFiles, processedFiles, symbolChanges, args, rawToken
221
221
  const lines = [];
222
222
  lines.push(`CHANGES: ${allFiles.length} file${allFiles.length !== 1 ? 's' : ''}, +${totalAdded} -${totalRemoved} (${scopeLabel})`);
223
223
  lines.push('');
224
+ // Affected symbols summary
225
+ const allSymbolChanges = [];
226
+ for (const [file, changes] of symbolChanges) {
227
+ for (const sc of changes) {
228
+ allSymbolChanges.push({ file, symbol: sc });
229
+ }
230
+ }
231
+ if (allSymbolChanges.length > 0) {
232
+ lines.push('AFFECTED SYMBOLS:');
233
+ // Group by changeType
234
+ const modified = allSymbolChanges.filter(s => s.symbol.changeType === 'MODIFIED');
235
+ const added = allSymbolChanges.filter(s => s.symbol.changeType === 'ADDED');
236
+ const removed = allSymbolChanges.filter(s => s.symbol.changeType === 'REMOVED');
237
+ for (const [label, group] of [['MODIFIED', modified], ['ADDED', added], ['REMOVED', removed]]) {
238
+ if (group.length === 0)
239
+ continue;
240
+ for (const item of group) {
241
+ const parens = ['function', 'method'].includes(item.symbol.kind) ? '()' : '';
242
+ lines.push(` ${label}: ${item.file}::${item.symbol.name}${parens}`);
243
+ }
244
+ }
245
+ lines.push('');
246
+ }
224
247
  for (const fd of processedFiles) {
225
248
  if (lines.length >= MAX_OUTPUT_LINES) {
226
249
  lines.push(`... truncated (${allFiles.length - processedFiles.indexOf(fd)} more files)`);
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
- import { readFileSync, realpathSync } from 'node:fs';
3
+ import { readFileSync, realpathSync, appendFileSync, mkdirSync } from 'node:fs';
4
+ import { join } from 'node:path';
4
5
  import { execFile } from 'node:child_process';
5
6
  import { promisify } from 'node:util';
6
7
  import { fileURLToPath } from 'node:url';
@@ -171,9 +172,10 @@ export function handleHookRead(filePathArg, denyThreshold = 300) {
171
172
  }
172
173
  // Check file size
173
174
  let lineCount = 0;
175
+ let fileContent = '';
174
176
  try {
175
- const content = readFileSync(filePath, 'utf-8');
176
- lineCount = content.split('\n').length;
177
+ fileContent = readFileSync(filePath, 'utf-8');
178
+ lineCount = fileContent.split('\n').length;
177
179
  if (lineCount <= denyThreshold) {
178
180
  process.exit(0);
179
181
  }
@@ -181,6 +183,19 @@ export function handleHookRead(filePathArg, denyThreshold = 300) {
181
183
  catch {
182
184
  process.exit(0);
183
185
  }
186
+ // Track denied read for session analytics
187
+ try {
188
+ const charEst = Math.ceil(fileContent.length / 4);
189
+ const wsRatio = (fileContent.match(/\s/g)?.length ?? 0) / fileContent.length;
190
+ const estTokens = Math.ceil(charEst * (1 - wsRatio * 0.3));
191
+ const entry = JSON.stringify({ filePath, lineCount, estimatedTokens: estTokens, timestamp: Date.now() });
192
+ const dir = join(process.cwd(), '.token-pilot');
193
+ mkdirSync(dir, { recursive: true });
194
+ appendFileSync(join(dir, 'hook-denied.jsonl'), entry + '\n');
195
+ }
196
+ catch {
197
+ // Silent fail — hook must not break
198
+ }
184
199
  // Large code file, unbounded Read → DENY
185
200
  // permissionDecisionReason is shown to Claude (not user) per official docs
186
201
  const deny = JSON.stringify({
@@ -29,6 +29,7 @@ export declare const TOOL_DEFINITIONS: ({
29
29
  context_before?: undefined;
30
30
  context_after?: undefined;
31
31
  show?: undefined;
32
+ symbols?: undefined;
32
33
  start_line?: undefined;
33
34
  end_line?: undefined;
34
35
  context_lines?: undefined;
@@ -45,6 +46,7 @@ export declare const TOOL_DEFINITIONS: ({
45
46
  include?: undefined;
46
47
  recursive?: undefined;
47
48
  max_depth?: undefined;
49
+ verbose?: undefined;
48
50
  module?: undefined;
49
51
  export_only?: undefined;
50
52
  check?: undefined;
@@ -88,6 +90,7 @@ export declare const TOOL_DEFINITIONS: ({
88
90
  show_imports?: undefined;
89
91
  show_docs?: undefined;
90
92
  depth?: undefined;
93
+ symbols?: undefined;
91
94
  start_line?: undefined;
92
95
  end_line?: undefined;
93
96
  context_lines?: undefined;
@@ -104,6 +107,71 @@ export declare const TOOL_DEFINITIONS: ({
104
107
  include?: undefined;
105
108
  recursive?: undefined;
106
109
  max_depth?: undefined;
110
+ verbose?: undefined;
111
+ module?: undefined;
112
+ export_only?: undefined;
113
+ check?: undefined;
114
+ pattern?: undefined;
115
+ name?: undefined;
116
+ ref?: undefined;
117
+ count?: undefined;
118
+ command?: undefined;
119
+ runner?: undefined;
120
+ timeout?: undefined;
121
+ };
122
+ required: string[];
123
+ };
124
+ } | {
125
+ name: string;
126
+ description: string;
127
+ inputSchema: {
128
+ type: "object";
129
+ properties: {
130
+ path: {
131
+ type: string;
132
+ description: string;
133
+ };
134
+ symbols: {
135
+ type: string;
136
+ items: {
137
+ type: string;
138
+ };
139
+ description: string;
140
+ };
141
+ context_before: {
142
+ type: string;
143
+ description: string;
144
+ };
145
+ context_after: {
146
+ type: string;
147
+ description: string;
148
+ };
149
+ show: {
150
+ type: string;
151
+ enum: string[];
152
+ description: string;
153
+ };
154
+ show_imports?: undefined;
155
+ show_docs?: undefined;
156
+ depth?: undefined;
157
+ symbol?: undefined;
158
+ start_line?: undefined;
159
+ end_line?: undefined;
160
+ context_lines?: undefined;
161
+ line?: undefined;
162
+ context?: undefined;
163
+ include_callers?: undefined;
164
+ include_tests?: undefined;
165
+ include_changes?: undefined;
166
+ paths?: undefined;
167
+ scope?: undefined;
168
+ kind?: undefined;
169
+ limit?: undefined;
170
+ lang?: undefined;
171
+ include?: undefined;
172
+ recursive?: undefined;
173
+ max_depth?: undefined;
174
+ verbose?: undefined;
107
175
  module?: undefined;
108
176
  export_only?: undefined;
109
177
  check?: undefined;
@@ -142,6 +210,7 @@ export declare const TOOL_DEFINITIONS: ({
142
210
  context_before?: undefined;
143
211
  context_after?: undefined;
144
212
  show?: undefined;
213
+ symbols?: undefined;
145
214
  context_lines?: undefined;
146
215
  line?: undefined;
147
216
  context?: undefined;
@@ -156,6 +225,7 @@ export declare const TOOL_DEFINITIONS: ({
156
225
  include?: undefined;
157
226
  recursive?: undefined;
158
227
  max_depth?: undefined;
228
+ verbose?: undefined;
159
229
  module?: undefined;
160
230
  export_only?: undefined;
161
231
  check?: undefined;
@@ -190,6 +260,7 @@ export declare const TOOL_DEFINITIONS: ({
190
260
  context_before?: undefined;
191
261
  context_after?: undefined;
192
262
  show?: undefined;
263
+ symbols?: undefined;
193
264
  start_line?: undefined;
194
265
  end_line?: undefined;
195
266
  line?: undefined;
@@ -205,6 +276,7 @@ export declare const TOOL_DEFINITIONS: ({
205
276
  include?: undefined;
206
277
  recursive?: undefined;
207
278
  max_depth?: undefined;
279
+ verbose?: undefined;
208
280
  module?: undefined;
209
281
  export_only?: undefined;
210
282
  check?: undefined;
@@ -232,6 +304,13 @@ export declare const TOOL_DEFINITIONS: ({
232
304
  type: string;
233
305
  description: string;
234
306
  };
307
+ symbols: {
308
+ type: string;
309
+ items: {
310
+ type: string;
311
+ };
312
+ description: string;
313
+ };
235
314
  line: {
236
315
  type: string;
237
316
  description: string;
@@ -269,6 +348,7 @@ export declare const TOOL_DEFINITIONS: ({
269
348
  include?: undefined;
270
349
  recursive?: undefined;
271
350
  max_depth?: undefined;
351
+ verbose?: undefined;
272
352
  module?: undefined;
273
353
  export_only?: undefined;
274
354
  check?: undefined;
@@ -303,6 +383,7 @@ export declare const TOOL_DEFINITIONS: ({
303
383
  context_before?: undefined;
304
384
  context_after?: undefined;
305
385
  show?: undefined;
386
+ symbols?: undefined;
306
387
  start_line?: undefined;
307
388
  end_line?: undefined;
308
389
  context_lines?: undefined;
@@ -318,6 +399,7 @@ export declare const TOOL_DEFINITIONS: ({
318
399
  include?: undefined;
319
400
  recursive?: undefined;
320
401
  max_depth?: undefined;
402
+ verbose?: undefined;
321
403
  module?: undefined;
322
404
  export_only?: undefined;
323
405
  check?: undefined;
@@ -359,6 +441,10 @@ export declare const TOOL_DEFINITIONS: ({
359
441
  type: string;
360
442
  description: string;
361
443
  };
444
+ context_lines: {
445
+ type: string;
446
+ description: string;
447
+ };
362
448
  path?: undefined;
363
449
  show_imports?: undefined;
364
450
  show_docs?: undefined;
@@ -366,9 +452,9 @@ export declare const TOOL_DEFINITIONS: ({
366
452
  context_before?: undefined;
367
453
  context_after?: undefined;
368
454
  show?: undefined;
455
+ symbols?: undefined;
369
456
  start_line?: undefined;
370
457
  end_line?: undefined;
371
- context_lines?: undefined;
372
458
  line?: undefined;
373
459
  context?: undefined;
374
460
  include_callers?: undefined;
@@ -378,6 +464,7 @@ export declare const TOOL_DEFINITIONS: ({
378
464
  include?: undefined;
379
465
  recursive?: undefined;
380
466
  max_depth?: undefined;
467
+ verbose?: undefined;
381
468
  module?: undefined;
382
469
  export_only?: undefined;
383
470
  check?: undefined;
@@ -413,6 +500,7 @@ export declare const TOOL_DEFINITIONS: ({
413
500
  context_before?: undefined;
414
501
  context_after?: undefined;
415
502
  show?: undefined;
503
+ symbols?: undefined;
416
504
  start_line?: undefined;
417
505
  end_line?: undefined;
418
506
  context_lines?: undefined;
@@ -428,6 +516,7 @@ export declare const TOOL_DEFINITIONS: ({
428
516
  lang?: undefined;
429
517
  recursive?: undefined;
430
518
  max_depth?: undefined;
519
+ verbose?: undefined;
431
520
  module?: undefined;
432
521
  export_only?: undefined;
433
522
  check?: undefined;
@@ -458,6 +547,7 @@ export declare const TOOL_DEFINITIONS: ({
458
547
  context_before?: undefined;
459
548
  context_after?: undefined;
460
549
  show?: undefined;
550
+ symbols?: undefined;
461
551
  start_line?: undefined;
462
552
  end_line?: undefined;
463
553
  context_lines?: undefined;
@@ -474,6 +564,7 @@ export declare const TOOL_DEFINITIONS: ({
474
564
  include?: undefined;
475
565
  recursive?: undefined;
476
566
  max_depth?: undefined;
567
+ verbose?: undefined;
477
568
  module?: undefined;
478
569
  export_only?: undefined;
479
570
  check?: undefined;
@@ -512,6 +603,7 @@ export declare const TOOL_DEFINITIONS: ({
512
603
  context_before?: undefined;
513
604
  context_after?: undefined;
514
605
  show?: undefined;
606
+ symbols?: undefined;
515
607
  start_line?: undefined;
516
608
  end_line?: undefined;
517
609
  context_lines?: undefined;
@@ -526,6 +618,7 @@ export declare const TOOL_DEFINITIONS: ({
526
618
  limit?: undefined;
527
619
  lang?: undefined;
528
620
  include?: undefined;
621
+ verbose?: undefined;
529
622
  module?: undefined;
530
623
  export_only?: undefined;
531
624
  check?: undefined;
@@ -545,6 +638,10 @@ export declare const TOOL_DEFINITIONS: ({
545
638
  inputSchema: {
546
639
  type: "object";
547
640
  properties: {
641
+ verbose: {
642
+ type: string;
643
+ description: string;
644
+ };
548
645
  path?: undefined;
549
646
  show_imports?: undefined;
550
647
  show_docs?: undefined;
@@ -553,6 +650,7 @@ export declare const TOOL_DEFINITIONS: ({
553
650
  context_before?: undefined;
554
651
  context_after?: undefined;
555
652
  show?: undefined;
653
+ symbols?: undefined;
556
654
  start_line?: undefined;
557
655
  end_line?: undefined;
558
656
  context_lines?: undefined;
@@ -608,6 +706,7 @@ export declare const TOOL_DEFINITIONS: ({
608
706
  context_before?: undefined;
609
707
  context_after?: undefined;
610
708
  show?: undefined;
709
+ symbols?: undefined;
611
710
  start_line?: undefined;
612
711
  end_line?: undefined;
613
712
  context_lines?: undefined;
@@ -623,6 +722,7 @@ export declare const TOOL_DEFINITIONS: ({
623
722
  include?: undefined;
624
723
  recursive?: undefined;
625
724
  max_depth?: undefined;
725
+ verbose?: undefined;
626
726
  check?: undefined;
627
727
  pattern?: undefined;
628
728
  name?: undefined;
@@ -669,6 +769,7 @@ export declare const TOOL_DEFINITIONS: ({
669
769
  context_before?: undefined;
670
770
  context_after?: undefined;
671
771
  show?: undefined;
772
+ symbols?: undefined;
672
773
  start_line?: undefined;
673
774
  end_line?: undefined;
674
775
  context_lines?: undefined;
@@ -683,6 +784,7 @@ export declare const TOOL_DEFINITIONS: ({
683
784
  include?: undefined;
684
785
  recursive?: undefined;
685
786
  max_depth?: undefined;
787
+ verbose?: undefined;
686
788
  module?: undefined;
687
789
  export_only?: undefined;
688
790
  ref?: undefined;
@@ -716,6 +818,7 @@ export declare const TOOL_DEFINITIONS: ({
716
818
  context_before?: undefined;
717
819
  context_after?: undefined;
718
820
  show?: undefined;
821
+ symbols?: undefined;
719
822
  start_line?: undefined;
720
823
  end_line?: undefined;
721
824
  context_lines?: undefined;
@@ -732,6 +835,7 @@ export declare const TOOL_DEFINITIONS: ({
732
835
  include?: undefined;
733
836
  recursive?: undefined;
734
837
  max_depth?: undefined;
838
+ verbose?: undefined;
735
839
  export_only?: undefined;
736
840
  pattern?: undefined;
737
841
  name?: undefined;
@@ -769,6 +873,7 @@ export declare const TOOL_DEFINITIONS: ({
769
873
  context_before?: undefined;
770
874
  context_after?: undefined;
771
875
  show?: undefined;
876
+ symbols?: undefined;
772
877
  start_line?: undefined;
773
878
  end_line?: undefined;
774
879
  context_lines?: undefined;
@@ -784,6 +889,7 @@ export declare const TOOL_DEFINITIONS: ({
784
889
  include?: undefined;
785
890
  recursive?: undefined;
786
891
  max_depth?: undefined;
892
+ verbose?: undefined;
787
893
  module?: undefined;
788
894
  export_only?: undefined;
789
895
  check?: undefined;
@@ -821,6 +927,7 @@ export declare const TOOL_DEFINITIONS: ({
821
927
  context_before?: undefined;
822
928
  context_after?: undefined;
823
929
  show?: undefined;
930
+ symbols?: undefined;
824
931
  start_line?: undefined;
825
932
  end_line?: undefined;
826
933
  context_lines?: undefined;
@@ -836,6 +943,7 @@ export declare const TOOL_DEFINITIONS: ({
836
943
  lang?: undefined;
837
944
  recursive?: undefined;
838
945
  max_depth?: undefined;
946
+ verbose?: undefined;
839
947
  module?: undefined;
840
948
  export_only?: undefined;
841
949
  check?: undefined;
@@ -874,6 +982,7 @@ export declare const TOOL_DEFINITIONS: ({
874
982
  context_before?: undefined;
875
983
  context_after?: undefined;
876
984
  show?: undefined;
985
+ symbols?: undefined;
877
986
  start_line?: undefined;
878
987
  end_line?: undefined;
879
988
  context_lines?: undefined;
@@ -890,6 +999,7 @@ export declare const TOOL_DEFINITIONS: ({
890
999
  include?: undefined;
891
1000
  recursive?: undefined;
892
1001
  max_depth?: undefined;
1002
+ verbose?: undefined;
893
1003
  module?: undefined;
894
1004
  export_only?: undefined;
895
1005
  check?: undefined;
@@ -928,6 +1038,7 @@ export declare const TOOL_DEFINITIONS: ({
928
1038
  context_before?: undefined;
929
1039
  context_after?: undefined;
930
1040
  show?: undefined;
1041
+ symbols?: undefined;
931
1042
  start_line?: undefined;
932
1043
  end_line?: undefined;
933
1044
  context_lines?: undefined;
@@ -944,6 +1055,7 @@ export declare const TOOL_DEFINITIONS: ({
944
1055
  include?: undefined;
945
1056
  recursive?: undefined;
946
1057
  max_depth?: undefined;
1058
+ verbose?: undefined;
947
1059
  module?: undefined;
948
1060
  export_only?: undefined;
949
1061
  check?: undefined;
@@ -10,6 +10,7 @@ export const MCP_INSTRUCTIONS = [
10
10
  '2. Starting work on a directory → explore_area (outline + imports + tests + git log in one call)',
11
11
  '3. Need to read a code file → smart_read (NOT Read/cat — returns structure, 60-80% fewer tokens)',
12
12
  '4. Need one function/class body → read_symbol (loads only that symbol, NOT the whole file)',
13
+ '4b. Need MULTIPLE function/class bodies from same file → read_symbols (batch — one call instead of N)',
13
14
  '5. Preparing an edit → read_for_edit (returns exact text for Edit old_string)',
14
15
  '6. Verify edits after editing → read_diff (only changed hunks — REQUIRES smart_read BEFORE editing)',
15
16
  '7. Multiple files at once → smart_read_many (batch up to 20 files)',
@@ -28,7 +29,7 @@ export const MCP_INSTRUCTIONS = [
28
29
  'WORKFLOWS:',
29
30
  '• Explore: project_overview → explore_area → smart_read → read_symbol',
30
31
  '• Edit: smart_read → read_for_edit → Edit → read_diff',
31
- '• Refactor: find_usages → read_symbol → read_for_edit → Edit → test_summary',
32
+ '• Refactor: find_usages → read_symbols → read_for_edit → Edit → test_summary',
32
33
  '• Audit: code_audit + find_unused + Grep (for regex patterns)',
33
34
  ].join('\n');
34
35
  export const TOOL_DEFINITIONS = [
@@ -62,6 +63,25 @@ export const TOOL_DEFINITIONS = [
62
63
  required: ['path', 'symbol'],
63
64
  },
64
65
  },
66
+ {
67
+ name: 'read_symbols',
68
+ description: 'Batch read MULTIPLE symbols from ONE file in a single call — saves N-1 round-trips vs calling read_symbol N times. Returns all requested symbol bodies.',
69
+ inputSchema: {
70
+ type: 'object',
71
+ properties: {
72
+ path: { type: 'string', description: 'File path' },
73
+ symbols: {
74
+ type: 'array',
75
+ items: { type: 'string' },
76
+ description: 'Array of symbol names (max 10), e.g. ["UserService.create", "UserService.update", "UserService.delete"]',
77
+ },
78
+ context_before: { type: 'number', description: 'Lines of context before each symbol (default: 2)' },
79
+ context_after: { type: 'number', description: 'Lines of context after each symbol (default: 0)' },
80
+ show: { type: 'string', enum: ['full', 'head', 'tail', 'outline'], description: 'Display mode for each symbol (default: auto)' },
81
+ },
82
+ required: ['path', 'symbols'],
83
+ },
84
+ },
65
85
  {
66
86
  name: 'read_range',
67
87
  description: 'Read a specific line range from a file. Use when you know exact lines — lighter than reading the whole file.',
@@ -89,12 +109,17 @@ export const TOOL_DEFINITIONS = [
89
109
  },
90
110
  {
91
111
  name: 'read_for_edit',
92
- description: 'Use INSTEAD OF Read when preparing an edit. Returns exact raw code around a symbol or line — copy directly as old_string for Edit tool. Optional: include_callers, include_tests, include_changes for enriched context.',
112
+ description: 'Use INSTEAD OF Read when preparing an edit. Returns exact raw code around a symbol or line — copy directly as old_string for Edit tool. Supports batch: pass "symbols" array to get multiple edit contexts in one call. Optional: include_callers, include_tests, include_changes for enriched context.',
93
113
  inputSchema: {
94
114
  type: 'object',
95
115
  properties: {
96
116
  path: { type: 'string', description: 'File path' },
97
117
  symbol: { type: 'string', description: 'Symbol name to edit (e.g. "UserService.updateUser")' },
118
+ symbols: {
119
+ type: 'array',
120
+ items: { type: 'string' },
121
+ description: 'Array of symbol names for batch edit context (max 10). Alternative to single "symbol" — returns all symbols in one call.',
122
+ },
98
123
  line: { type: 'number', description: 'Line number to edit (alternative to symbol)' },
99
124
  context: { type: 'number', description: 'Lines of context around target (default: 5)' },
100
125
  include_callers: { type: 'boolean', description: 'Show top callers of this symbol (saves a separate find_usages call)' },
@@ -122,7 +147,7 @@ export const TOOL_DEFINITIONS = [
122
147
  // --- Search & navigation ---
123
148
  {
124
149
  name: 'find_usages',
125
- description: 'Use INSTEAD OF Grep for finding symbol references. Semantic search — groups by: definitions, imports, usages. Supports scope, kind, limit, lang filters.',
150
+ description: 'Use INSTEAD OF Grep for finding symbol references. Semantic search — groups by: definitions, imports, usages. Supports scope, kind, limit, lang filters. Use context_lines to include surrounding code.',
126
151
  inputSchema: {
127
152
  type: 'object',
128
153
  properties: {
@@ -131,6 +156,7 @@ export const TOOL_DEFINITIONS = [
131
156
  kind: { type: 'string', enum: ['definitions', 'imports', 'usages', 'all'], description: 'Show only specific section (default: "all")' },
132
157
  limit: { type: 'number', description: 'Max results per category (default: 50, max: 500)' },
133
158
  lang: { type: 'string', description: 'Filter by language/extension (e.g., "php", "typescript")' },
159
+ context_lines: { type: 'number', description: 'Lines of source context around each match (0-10). When set, shows surrounding code — saves follow-up read_symbol calls.' },
134
160
  },
135
161
  required: ['symbol'],
136
162
  },
@@ -176,10 +202,12 @@ export const TOOL_DEFINITIONS = [
176
202
  // --- Analytics ---
177
203
  {
178
204
  name: 'session_analytics',
179
- description: 'Show token savings report: calls, tokens saved, per-tool breakdown, top files, cache hits.',
205
+ description: 'Show token savings report: calls, tokens saved, per-tool breakdown, top files, cache hits. Use verbose=true for full breakdown (per-intent, decision insights, savings by category).',
180
206
  inputSchema: {
181
207
  type: 'object',
182
- properties: {},
208
+ properties: {
209
+ verbose: { type: 'boolean', description: 'Show detailed breakdown: per-intent, savings by category, decision insights (default: false)' },
210
+ },
183
211
  },
184
212
  },
185
213
  // --- Analysis ---