token-pilot 0.31.0 → 0.32.0

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 (37) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/agents/tp-api-surface-tracker.md +1 -1
  4. package/agents/tp-audit-scanner.md +1 -1
  5. package/agents/tp-commit-writer.md +1 -1
  6. package/agents/tp-context-engineer.md +1 -1
  7. package/agents/tp-dead-code-finder.md +1 -1
  8. package/agents/tp-debugger.md +1 -1
  9. package/agents/tp-dep-health.md +1 -1
  10. package/agents/tp-doc-writer.md +1 -1
  11. package/agents/tp-history-explorer.md +1 -1
  12. package/agents/tp-impact-analyzer.md +1 -1
  13. package/agents/tp-incident-timeline.md +1 -1
  14. package/agents/tp-incremental-builder.md +1 -1
  15. package/agents/tp-migration-scout.md +1 -1
  16. package/agents/tp-onboard.md +1 -1
  17. package/agents/tp-performance-profiler.md +1 -1
  18. package/agents/tp-pr-reviewer.md +1 -1
  19. package/agents/tp-refactor-planner.md +1 -1
  20. package/agents/tp-review-impact.md +1 -1
  21. package/agents/tp-run.md +1 -1
  22. package/agents/tp-session-restorer.md +1 -1
  23. package/agents/tp-ship-coordinator.md +1 -1
  24. package/agents/tp-spec-writer.md +1 -1
  25. package/agents/tp-test-coverage-gapper.md +1 -1
  26. package/agents/tp-test-triage.md +1 -1
  27. package/agents/tp-test-writer.md +1 -1
  28. package/dist/core/validation.d.ts +13 -9
  29. package/dist/core/validation.js +180 -134
  30. package/dist/handlers/call-tree.d.ts +35 -0
  31. package/dist/handlers/call-tree.js +70 -0
  32. package/dist/hooks/session-start.d.ts +2 -0
  33. package/dist/hooks/session-start.js +49 -0
  34. package/dist/server/tool-definitions.d.ts +65 -0
  35. package/dist/server/tool-definitions.js +18 -0
  36. package/dist/server.js +36 -1
  37. package/package.json +1 -1
@@ -1,4 +1,4 @@
1
- import { resolve, relative } from 'node:path';
1
+ import { resolve, relative } from "node:path";
2
2
  /**
3
3
  * Resolve a user-provided path and validate it stays within projectRoot.
4
4
  * Prevents path traversal attacks (e.g. ../../etc/passwd).
@@ -6,7 +6,7 @@ import { resolve, relative } from 'node:path';
6
6
  export function resolveSafePath(projectRoot, userPath) {
7
7
  const absPath = resolve(projectRoot, userPath);
8
8
  const rel = relative(projectRoot, absPath);
9
- if (rel.startsWith('..') || resolve(projectRoot, rel) !== absPath) {
9
+ if (rel.startsWith("..") || resolve(projectRoot, rel) !== absPath) {
10
10
  throw new Error(`Path "${userPath}" resolves outside project root.`);
11
11
  }
12
12
  return absPath;
@@ -15,40 +15,40 @@ export function resolveSafePath(projectRoot, userPath) {
15
15
  * Validate smart_read arguments.
16
16
  */
17
17
  export function validateSmartReadArgs(args) {
18
- if (!args || typeof args !== 'object') {
19
- throw new Error('Arguments must be an object.');
18
+ if (!args || typeof args !== "object") {
19
+ throw new Error("Arguments must be an object.");
20
20
  }
21
21
  const a = args;
22
- if (typeof a.path !== 'string' || a.path.length === 0) {
22
+ if (typeof a.path !== "string" || a.path.length === 0) {
23
23
  throw new Error('Required parameter "path" must be a non-empty string.');
24
24
  }
25
25
  return {
26
26
  path: a.path,
27
- show_imports: optionalBool(a.show_imports, 'show_imports'),
28
- show_docs: optionalBool(a.show_docs, 'show_docs'),
29
- show_references: optionalBool(a.show_references, 'show_references'),
30
- depth: optionalNumber(a.depth, 'depth'),
31
- max_tokens: optionalNumber(a.max_tokens, 'max_tokens'),
27
+ show_imports: optionalBool(a.show_imports, "show_imports"),
28
+ show_docs: optionalBool(a.show_docs, "show_docs"),
29
+ show_references: optionalBool(a.show_references, "show_references"),
30
+ depth: optionalNumber(a.depth, "depth"),
31
+ max_tokens: optionalNumber(a.max_tokens, "max_tokens"),
32
32
  };
33
33
  }
34
34
  /**
35
35
  * Validate read_symbol arguments.
36
36
  */
37
37
  export function validateReadSymbolArgs(args) {
38
- if (!args || typeof args !== 'object') {
39
- throw new Error('Arguments must be an object.');
38
+ if (!args || typeof args !== "object") {
39
+ throw new Error("Arguments must be an object.");
40
40
  }
41
41
  const a = args;
42
- if (typeof a.path !== 'string' || a.path.length === 0) {
42
+ if (typeof a.path !== "string" || a.path.length === 0) {
43
43
  throw new Error('Required parameter "path" must be a non-empty string.');
44
44
  }
45
- if (typeof a.symbol !== 'string' || a.symbol.length === 0) {
45
+ if (typeof a.symbol !== "string" || a.symbol.length === 0) {
46
46
  throw new Error('Required parameter "symbol" must be a non-empty string.');
47
47
  }
48
48
  let show;
49
49
  if (a.show !== undefined && a.show !== null) {
50
- const valid = ['full', 'head', 'tail', 'outline'];
51
- if (typeof a.show !== 'string' || !valid.includes(a.show)) {
50
+ const valid = ["full", "head", "tail", "outline"];
51
+ if (typeof a.show !== "string" || !valid.includes(a.show)) {
52
52
  throw new Error('"show" must be one of: full, head, tail, outline.');
53
53
  }
54
54
  show = a.show;
@@ -56,8 +56,8 @@ export function validateReadSymbolArgs(args) {
56
56
  return {
57
57
  path: a.path,
58
58
  symbol: a.symbol,
59
- context_before: optionalNumber(a.context_before, 'context_before'),
60
- context_after: optionalNumber(a.context_after, 'context_after'),
59
+ context_before: optionalNumber(a.context_before, "context_before"),
60
+ context_after: optionalNumber(a.context_after, "context_after"),
61
61
  show,
62
62
  };
63
63
  }
@@ -65,11 +65,11 @@ export function validateReadSymbolArgs(args) {
65
65
  * Validate read_symbols arguments (batch multi-symbol read).
66
66
  */
67
67
  export function validateReadSymbolsArgs(args) {
68
- if (!args || typeof args !== 'object') {
69
- throw new Error('Arguments must be an object.');
68
+ if (!args || typeof args !== "object") {
69
+ throw new Error("Arguments must be an object.");
70
70
  }
71
71
  const a = args;
72
- if (typeof a.path !== 'string' || a.path.length === 0) {
72
+ if (typeof a.path !== "string" || a.path.length === 0) {
73
73
  throw new Error('Required parameter "path" must be a non-empty string.');
74
74
  }
75
75
  if (!Array.isArray(a.symbols) || a.symbols.length === 0) {
@@ -79,14 +79,14 @@ export function validateReadSymbolsArgs(args) {
79
79
  throw new Error('"symbols" can contain at most 10 symbols.');
80
80
  }
81
81
  for (const s of a.symbols) {
82
- if (typeof s !== 'string' || s.length === 0) {
82
+ if (typeof s !== "string" || s.length === 0) {
83
83
  throw new Error('Each symbol in "symbols" must be a non-empty string.');
84
84
  }
85
85
  }
86
86
  let show;
87
87
  if (a.show !== undefined && a.show !== null) {
88
- const valid = ['full', 'head', 'tail', 'outline'];
89
- if (typeof a.show !== 'string' || !valid.includes(a.show)) {
88
+ const valid = ["full", "head", "tail", "outline"];
89
+ if (typeof a.show !== "string" || !valid.includes(a.show)) {
90
90
  throw new Error('"show" must be one of: full, head, tail, outline.');
91
91
  }
92
92
  show = a.show;
@@ -94,8 +94,8 @@ export function validateReadSymbolsArgs(args) {
94
94
  return {
95
95
  path: a.path,
96
96
  symbols: a.symbols,
97
- context_before: optionalNumber(a.context_before, 'context_before'),
98
- context_after: optionalNumber(a.context_after, 'context_after'),
97
+ context_before: optionalNumber(a.context_before, "context_before"),
98
+ context_after: optionalNumber(a.context_after, "context_after"),
99
99
  show,
100
100
  };
101
101
  }
@@ -103,17 +103,21 @@ export function validateReadSymbolsArgs(args) {
103
103
  * Validate read_range arguments.
104
104
  */
105
105
  export function validateReadRangeArgs(args) {
106
- if (!args || typeof args !== 'object') {
107
- throw new Error('Arguments must be an object.');
106
+ if (!args || typeof args !== "object") {
107
+ throw new Error("Arguments must be an object.");
108
108
  }
109
109
  const a = args;
110
- if (typeof a.path !== 'string' || a.path.length === 0) {
110
+ if (typeof a.path !== "string" || a.path.length === 0) {
111
111
  throw new Error('Required parameter "path" must be a non-empty string.');
112
112
  }
113
- if (typeof a.start_line !== 'number' || !Number.isInteger(a.start_line) || a.start_line < 1) {
113
+ if (typeof a.start_line !== "number" ||
114
+ !Number.isInteger(a.start_line) ||
115
+ a.start_line < 1) {
114
116
  throw new Error('Required parameter "start_line" must be a positive integer.');
115
117
  }
116
- if (typeof a.end_line !== 'number' || !Number.isInteger(a.end_line) || a.end_line < 1) {
118
+ if (typeof a.end_line !== "number" ||
119
+ !Number.isInteger(a.end_line) ||
120
+ a.end_line < 1) {
117
121
  throw new Error('Required parameter "end_line" must be a positive integer.');
118
122
  }
119
123
  if (a.end_line < a.start_line) {
@@ -125,56 +129,57 @@ export function validateReadRangeArgs(args) {
125
129
  * Validate read_diff arguments.
126
130
  */
127
131
  export function validateReadDiffArgs(args) {
128
- if (!args || typeof args !== 'object') {
129
- throw new Error('Arguments must be an object.');
132
+ if (!args || typeof args !== "object") {
133
+ throw new Error("Arguments must be an object.");
130
134
  }
131
135
  const a = args;
132
- if (typeof a.path !== 'string' || a.path.length === 0) {
136
+ if (typeof a.path !== "string" || a.path.length === 0) {
133
137
  throw new Error('Required parameter "path" must be a non-empty string.');
134
138
  }
135
139
  return {
136
140
  path: a.path,
137
- context_lines: optionalNumber(a.context_lines, 'context_lines'),
141
+ context_lines: optionalNumber(a.context_lines, "context_lines"),
138
142
  };
139
143
  }
140
144
  export function validateFindUsagesArgs(args) {
141
- if (!args || typeof args !== 'object') {
142
- throw new Error('Arguments must be an object.');
145
+ if (!args || typeof args !== "object") {
146
+ throw new Error("Arguments must be an object.");
143
147
  }
144
148
  const a = args;
145
- if (typeof a.symbol !== 'string' || a.symbol.length === 0) {
149
+ if (typeof a.symbol !== "string" || a.symbol.length === 0) {
146
150
  throw new Error('Required parameter "symbol" must be a non-empty string.');
147
151
  }
148
152
  let kind;
149
153
  if (a.kind !== undefined && a.kind !== null) {
150
- const validKinds = ['definitions', 'imports', 'usages', 'all'];
151
- if (typeof a.kind !== 'string' || !validKinds.includes(a.kind)) {
152
- throw new Error(`"kind" must be one of: ${validKinds.join(', ')}`);
154
+ const validKinds = ["definitions", "imports", "usages", "all"];
155
+ if (typeof a.kind !== "string" || !validKinds.includes(a.kind)) {
156
+ throw new Error(`"kind" must be one of: ${validKinds.join(", ")}`);
153
157
  }
154
158
  kind = a.kind;
155
159
  }
156
- const limit = optionalNumber(a.limit, 'limit');
160
+ const limit = optionalNumber(a.limit, "limit");
157
161
  if (limit !== undefined && (limit < 1 || limit > 500)) {
158
162
  throw new Error('"limit" must be between 1 and 500.');
159
163
  }
160
- const context_lines = optionalNumber(a.context_lines, 'context_lines');
161
- if (context_lines !== undefined && (context_lines < 0 || context_lines > 10)) {
164
+ const context_lines = optionalNumber(a.context_lines, "context_lines");
165
+ if (context_lines !== undefined &&
166
+ (context_lines < 0 || context_lines > 10)) {
162
167
  throw new Error('"context_lines" must be between 0 and 10.');
163
168
  }
164
169
  let mode;
165
170
  if (a.mode !== undefined && a.mode !== null) {
166
- const validModes = ['full', 'list'];
167
- if (typeof a.mode !== 'string' || !validModes.includes(a.mode)) {
168
- throw new Error(`"mode" must be one of: ${validModes.join(', ')}`);
171
+ const validModes = ["full", "list"];
172
+ if (typeof a.mode !== "string" || !validModes.includes(a.mode)) {
173
+ throw new Error(`"mode" must be one of: ${validModes.join(", ")}`);
169
174
  }
170
175
  mode = a.mode;
171
176
  }
172
177
  return {
173
178
  symbol: a.symbol,
174
- scope: optionalString(a.scope, 'scope'),
179
+ scope: optionalString(a.scope, "scope"),
175
180
  kind,
176
181
  limit,
177
- lang: optionalString(a.lang, 'lang'),
182
+ lang: optionalString(a.lang, "lang"),
178
183
  context_lines,
179
184
  mode,
180
185
  };
@@ -183,38 +188,41 @@ export function validateFindUsagesArgs(args) {
183
188
  * Validate smart_read_many arguments.
184
189
  */
185
190
  export function validateSmartReadManyArgs(args) {
186
- if (!args || typeof args !== 'object') {
187
- throw new Error('Arguments must be an object.');
191
+ if (!args || typeof args !== "object") {
192
+ throw new Error("Arguments must be an object.");
188
193
  }
189
194
  const a = args;
190
195
  if (!Array.isArray(a.paths)) {
191
196
  throw new Error('Required parameter "paths" must be an array of strings.');
192
197
  }
193
198
  for (const p of a.paths) {
194
- if (typeof p !== 'string' || p.length === 0) {
199
+ if (typeof p !== "string" || p.length === 0) {
195
200
  throw new Error('Each path in "paths" must be a non-empty string.');
196
201
  }
197
202
  }
198
- return { paths: a.paths, max_tokens: optionalNumber(a.max_tokens, 'max_tokens') };
203
+ return {
204
+ paths: a.paths,
205
+ max_tokens: optionalNumber(a.max_tokens, "max_tokens"),
206
+ };
199
207
  }
200
208
  function optionalString(val, name) {
201
209
  if (val === undefined || val === null)
202
210
  return undefined;
203
- if (typeof val !== 'string')
211
+ if (typeof val !== "string")
204
212
  throw new Error(`"${name}" must be a string.`);
205
213
  return val;
206
214
  }
207
215
  function optionalBool(val, name) {
208
216
  if (val === undefined || val === null)
209
217
  return undefined;
210
- if (typeof val !== 'boolean')
218
+ if (typeof val !== "boolean")
211
219
  throw new Error(`"${name}" must be a boolean.`);
212
220
  return val;
213
221
  }
214
222
  function optionalNumber(val, name) {
215
223
  if (val === undefined || val === null)
216
224
  return undefined;
217
- if (typeof val !== 'number' || !Number.isFinite(val))
225
+ if (typeof val !== "number" || !Number.isFinite(val))
218
226
  throw new Error(`"${name}" must be a finite number.`);
219
227
  return val;
220
228
  }
@@ -222,14 +230,17 @@ function optionalNumber(val, name) {
222
230
  * Validate read_for_edit arguments.
223
231
  */
224
232
  export function validateReadForEditArgs(args) {
225
- if (!args || typeof args !== 'object') {
226
- throw new Error('Arguments must be an object.');
233
+ if (!args || typeof args !== "object") {
234
+ throw new Error("Arguments must be an object.");
227
235
  }
228
236
  const a = args;
229
- if (typeof a.path !== 'string' || a.path.length === 0) {
237
+ if (typeof a.path !== "string" || a.path.length === 0) {
230
238
  throw new Error('Required parameter "path" must be a non-empty string.');
231
239
  }
232
- if (!a.symbol && !a.line && (!Array.isArray(a.symbols) || a.symbols.length === 0) && !a.section) {
240
+ if (!a.symbol &&
241
+ !a.line &&
242
+ (!Array.isArray(a.symbols) || a.symbols.length === 0) &&
243
+ !a.section) {
233
244
  throw new Error('Either "symbol", "symbols", "line", or "section" must be provided.');
234
245
  }
235
246
  // Validate symbols array (batch mode)
@@ -242,7 +253,7 @@ export function validateReadForEditArgs(args) {
242
253
  throw new Error('"symbols" can contain at most 10 symbols.');
243
254
  }
244
255
  for (const s of a.symbols) {
245
- if (typeof s !== 'string' || s.length === 0) {
256
+ if (typeof s !== "string" || s.length === 0) {
246
257
  throw new Error('Each symbol in "symbols" must be a non-empty string.');
247
258
  }
248
259
  }
@@ -250,87 +261,106 @@ export function validateReadForEditArgs(args) {
250
261
  }
251
262
  return {
252
263
  path: a.path,
253
- symbol: optionalString(a.symbol, 'symbol'),
264
+ symbol: optionalString(a.symbol, "symbol"),
254
265
  symbols,
255
- line: optionalNumber(a.line, 'line'),
256
- context: optionalNumber(a.context, 'context'),
257
- include_callers: optionalBool(a.include_callers, 'include_callers'),
258
- include_tests: optionalBool(a.include_tests, 'include_tests'),
259
- include_changes: optionalBool(a.include_changes, 'include_changes'),
260
- section: optionalString(a.section, 'section'),
266
+ line: optionalNumber(a.line, "line"),
267
+ context: optionalNumber(a.context, "context"),
268
+ include_callers: optionalBool(a.include_callers, "include_callers"),
269
+ include_tests: optionalBool(a.include_tests, "include_tests"),
270
+ include_changes: optionalBool(a.include_changes, "include_changes"),
271
+ section: optionalString(a.section, "section"),
261
272
  };
262
273
  }
263
274
  /**
264
275
  * Validate related_files arguments.
265
276
  */
266
277
  export function validateRelatedFilesArgs(args) {
267
- if (!args || typeof args !== 'object') {
268
- throw new Error('Arguments must be an object.');
278
+ if (!args || typeof args !== "object") {
279
+ throw new Error("Arguments must be an object.");
269
280
  }
270
281
  const a = args;
271
- if (typeof a.path !== 'string' || a.path.length === 0) {
282
+ if (typeof a.path !== "string" || a.path.length === 0) {
272
283
  throw new Error('Required parameter "path" must be a non-empty string.');
273
284
  }
274
285
  return { path: a.path };
275
286
  }
276
287
  export function validateOutlineArgs(args) {
277
- if (!args || typeof args !== 'object') {
278
- throw new Error('Arguments must be an object.');
288
+ if (!args || typeof args !== "object") {
289
+ throw new Error("Arguments must be an object.");
279
290
  }
280
291
  const a = args;
281
- if (typeof a.path !== 'string' || a.path.length === 0) {
292
+ if (typeof a.path !== "string" || a.path.length === 0) {
282
293
  throw new Error('Required parameter "path" must be a non-empty string.');
283
294
  }
284
- const maxDepth = optionalNumber(a.max_depth, 'max_depth');
295
+ const maxDepth = optionalNumber(a.max_depth, "max_depth");
285
296
  if (maxDepth !== undefined && (maxDepth < 1 || maxDepth > 5)) {
286
297
  throw new Error('"max_depth" must be between 1 and 5.');
287
298
  }
288
299
  return {
289
300
  path: a.path,
290
- recursive: optionalBool(a.recursive, 'recursive'),
301
+ recursive: optionalBool(a.recursive, "recursive"),
291
302
  max_depth: maxDepth,
292
303
  };
293
304
  }
294
305
  export function validateFindUnusedArgs(args) {
295
- if (!args || typeof args !== 'object')
306
+ if (!args || typeof args !== "object")
296
307
  return {};
297
308
  const a = args;
298
309
  return {
299
- module: optionalString(a.module, 'module'),
300
- export_only: optionalBool(a.export_only, 'export_only'),
301
- limit: optionalNumber(a.limit, 'limit'),
310
+ module: optionalString(a.module, "module"),
311
+ export_only: optionalBool(a.export_only, "export_only"),
312
+ limit: optionalNumber(a.limit, "limit"),
313
+ };
314
+ }
315
+ export function validateCallTreeArgs(args) {
316
+ if (!args || typeof args !== "object") {
317
+ throw new Error("call_tree requires { symbol: string }");
318
+ }
319
+ const a = args;
320
+ const symbol = optionalString(a.symbol, "symbol");
321
+ if (!symbol) {
322
+ throw new Error("call_tree: `symbol` is required and must be a non-empty string.");
323
+ }
324
+ return {
325
+ symbol,
326
+ depth: optionalNumber(a.depth, "depth"),
302
327
  };
303
328
  }
304
329
  export function validateCodeAuditArgs(args) {
305
- if (!args || typeof args !== 'object') {
330
+ if (!args || typeof args !== "object") {
306
331
  throw new Error('Arguments must be an object with a "check" parameter.');
307
332
  }
308
333
  const a = args;
309
- const validChecks = ['pattern', 'todo', 'deprecated', 'annotations', 'all'];
310
- if (typeof a.check !== 'string' || !validChecks.includes(a.check)) {
311
- throw new Error(`Required parameter "check" must be one of: ${validChecks.join(', ')}`);
334
+ const validChecks = ["pattern", "todo", "deprecated", "annotations", "all"];
335
+ if (typeof a.check !== "string" || !validChecks.includes(a.check)) {
336
+ throw new Error(`Required parameter "check" must be one of: ${validChecks.join(", ")}`);
312
337
  }
313
- if (a.check === 'pattern') {
314
- if (typeof a.pattern !== 'string' || a.pattern.length === 0) {
338
+ if (a.check === "pattern") {
339
+ if (typeof a.pattern !== "string" || a.pattern.length === 0) {
315
340
  throw new Error('Parameter "pattern" is required when check="pattern". Example: "except:" or "print($$$ARGS)"');
316
341
  }
317
342
  }
318
- if (a.check === 'annotations') {
319
- if (typeof a.name !== 'string' || a.name.length === 0) {
343
+ if (a.check === "annotations") {
344
+ if (typeof a.name !== "string" || a.name.length === 0) {
320
345
  throw new Error('Parameter "name" is required when check="annotations". Example: "Deprecated" or "Controller"');
321
346
  }
322
347
  }
323
348
  return {
324
349
  check: a.check,
325
- pattern: optionalString(a.pattern, 'pattern'),
326
- name: optionalString(a.name, 'name'),
327
- lang: optionalString(a.lang, 'lang'),
328
- limit: optionalNumber(a.limit, 'limit'),
350
+ pattern: optionalString(a.pattern, "pattern"),
351
+ name: optionalString(a.name, "name"),
352
+ lang: optionalString(a.lang, "lang"),
353
+ limit: optionalNumber(a.limit, "limit"),
329
354
  };
330
355
  }
331
- const VALID_INCLUDE_SECTIONS = ['stack', 'ci', 'quality', 'architecture'];
356
+ const VALID_INCLUDE_SECTIONS = [
357
+ "stack",
358
+ "ci",
359
+ "quality",
360
+ "architecture",
361
+ ];
332
362
  export function validateProjectOverviewArgs(args) {
333
- if (!args || typeof args !== 'object')
363
+ if (!args || typeof args !== "object")
334
364
  return {};
335
365
  const a = args;
336
366
  if (a.include !== undefined && a.include !== null) {
@@ -338,8 +368,9 @@ export function validateProjectOverviewArgs(args) {
338
368
  throw new Error('"include" must be an array of section names.');
339
369
  }
340
370
  for (const item of a.include) {
341
- if (typeof item !== 'string' || !VALID_INCLUDE_SECTIONS.includes(item)) {
342
- throw new Error(`Each element of "include" must be one of: ${VALID_INCLUDE_SECTIONS.join(', ')}. Got: "${item}"`);
371
+ if (typeof item !== "string" ||
372
+ !VALID_INCLUDE_SECTIONS.includes(item)) {
373
+ throw new Error(`Each element of "include" must be one of: ${VALID_INCLUDE_SECTIONS.join(", ")}. Got: "${item}"`);
343
374
  }
344
375
  }
345
376
  return { include: a.include };
@@ -347,55 +378,60 @@ export function validateProjectOverviewArgs(args) {
347
378
  return {};
348
379
  }
349
380
  export function validateModuleInfoArgs(args) {
350
- if (!args || typeof args !== 'object') {
381
+ if (!args || typeof args !== "object") {
351
382
  throw new Error('Arguments must be an object with a "module" parameter.');
352
383
  }
353
384
  const a = args;
354
- if (typeof a.module !== 'string' || a.module.length === 0) {
385
+ if (typeof a.module !== "string" || a.module.length === 0) {
355
386
  throw new Error('Required parameter "module" must be a non-empty string.');
356
387
  }
357
388
  let check;
358
389
  if (a.check !== undefined && a.check !== null) {
359
- const validChecks = ['deps', 'dependents', 'api', 'unused-deps', 'all'];
360
- if (typeof a.check !== 'string' || !validChecks.includes(a.check)) {
361
- throw new Error(`"check" must be one of: ${validChecks.join(', ')}`);
390
+ const validChecks = ["deps", "dependents", "api", "unused-deps", "all"];
391
+ if (typeof a.check !== "string" || !validChecks.includes(a.check)) {
392
+ throw new Error(`"check" must be one of: ${validChecks.join(", ")}`);
362
393
  }
363
394
  check = a.check;
364
395
  }
365
396
  return {
366
397
  module: a.module,
367
- check: check ?? 'all',
398
+ check: check ?? "all",
368
399
  };
369
400
  }
370
401
  export function validateSmartDiffArgs(args) {
371
- if (!args || typeof args !== 'object')
372
- return { scope: 'unstaged' };
402
+ if (!args || typeof args !== "object")
403
+ return { scope: "unstaged" };
373
404
  const a = args;
374
405
  let scope;
375
406
  if (a.scope !== undefined && a.scope !== null) {
376
- const validScopes = ['unstaged', 'staged', 'commit', 'branch'];
377
- if (typeof a.scope !== 'string' || !validScopes.includes(a.scope)) {
378
- throw new Error(`"scope" must be one of: ${validScopes.join(', ')}`);
407
+ const validScopes = ["unstaged", "staged", "commit", "branch"];
408
+ if (typeof a.scope !== "string" || !validScopes.includes(a.scope)) {
409
+ throw new Error(`"scope" must be one of: ${validScopes.join(", ")}`);
379
410
  }
380
411
  scope = a.scope;
381
412
  }
382
- const ref = optionalString(a.ref, 'ref');
383
- if ((scope === 'commit' || scope === 'branch') && !ref) {
413
+ const ref = optionalString(a.ref, "ref");
414
+ if ((scope === "commit" || scope === "branch") && !ref) {
384
415
  throw new Error(`"ref" is required when scope="${scope}".`);
385
416
  }
386
417
  return {
387
- scope: scope ?? 'unstaged',
388
- path: optionalString(a.path, 'path'),
418
+ scope: scope ?? "unstaged",
419
+ path: optionalString(a.path, "path"),
389
420
  ref,
390
421
  };
391
422
  }
392
- const VALID_EXPLORE_SECTIONS = ['outline', 'imports', 'tests', 'changes'];
423
+ const VALID_EXPLORE_SECTIONS = [
424
+ "outline",
425
+ "imports",
426
+ "tests",
427
+ "changes",
428
+ ];
393
429
  export function validateExploreAreaArgs(args) {
394
- if (!args || typeof args !== 'object') {
430
+ if (!args || typeof args !== "object") {
395
431
  throw new Error('Arguments must be an object with a "path" parameter.');
396
432
  }
397
433
  const a = args;
398
- if (typeof a.path !== 'string' || a.path.length === 0) {
434
+ if (typeof a.path !== "string" || a.path.length === 0) {
399
435
  throw new Error('Required parameter "path" must be a non-empty string.');
400
436
  }
401
437
  if (a.include !== undefined && a.include !== null) {
@@ -403,8 +439,9 @@ export function validateExploreAreaArgs(args) {
403
439
  throw new Error('"include" must be an array of section names.');
404
440
  }
405
441
  for (const item of a.include) {
406
- if (typeof item !== 'string' || !VALID_EXPLORE_SECTIONS.includes(item)) {
407
- throw new Error(`Each element of "include" must be one of: ${VALID_EXPLORE_SECTIONS.join(', ')}. Got: "${item}"`);
442
+ if (typeof item !== "string" ||
443
+ !VALID_EXPLORE_SECTIONS.includes(item)) {
444
+ throw new Error(`Each element of "include" must be one of: ${VALID_EXPLORE_SECTIONS.join(", ")}. Got: "${item}"`);
408
445
  }
409
446
  }
410
447
  return { path: a.path, include: a.include };
@@ -412,42 +449,51 @@ export function validateExploreAreaArgs(args) {
412
449
  return { path: a.path };
413
450
  }
414
451
  export function validateSmartLogArgs(args) {
415
- if (!args || typeof args !== 'object')
452
+ if (!args || typeof args !== "object")
416
453
  return {};
417
454
  const a = args;
418
- const path = optionalString(a.path, 'path');
455
+ const path = optionalString(a.path, "path");
419
456
  if (path !== undefined && path.length === 0) {
420
457
  throw new Error('"path" must be a non-empty string.');
421
458
  }
422
- const count = optionalNumber(a.count, 'count');
459
+ const count = optionalNumber(a.count, "count");
423
460
  if (count !== undefined) {
424
461
  if (!Number.isInteger(count) || count < 1 || count > 50) {
425
462
  throw new Error('"count" must be an integer between 1 and 50.');
426
463
  }
427
464
  }
428
- const ref = optionalString(a.ref, 'ref');
465
+ const ref = optionalString(a.ref, "ref");
429
466
  if (ref !== undefined && ref.length === 0) {
430
467
  throw new Error('"ref" must be a non-empty string.');
431
468
  }
432
469
  return { path, count, ref };
433
470
  }
434
- const VALID_RUNNERS = ['vitest', 'jest', 'pytest', 'phpunit', 'go', 'cargo', 'rspec', 'mocha'];
471
+ const VALID_RUNNERS = [
472
+ "vitest",
473
+ "jest",
474
+ "pytest",
475
+ "phpunit",
476
+ "go",
477
+ "cargo",
478
+ "rspec",
479
+ "mocha",
480
+ ];
435
481
  export function validateTestSummaryArgs(args) {
436
- if (!args || typeof args !== 'object') {
482
+ if (!args || typeof args !== "object") {
437
483
  throw new Error('Arguments must be an object with a "command" parameter.');
438
484
  }
439
485
  const a = args;
440
- if (typeof a.command !== 'string' || a.command.length === 0) {
486
+ if (typeof a.command !== "string" || a.command.length === 0) {
441
487
  throw new Error('Required parameter "command" must be a non-empty string.');
442
488
  }
443
489
  let runner;
444
490
  if (a.runner !== undefined && a.runner !== null) {
445
- if (typeof a.runner !== 'string' || !VALID_RUNNERS.includes(a.runner)) {
446
- throw new Error(`"runner" must be one of: ${VALID_RUNNERS.join(', ')}`);
491
+ if (typeof a.runner !== "string" || !VALID_RUNNERS.includes(a.runner)) {
492
+ throw new Error(`"runner" must be one of: ${VALID_RUNNERS.join(", ")}`);
447
493
  }
448
494
  runner = a.runner;
449
495
  }
450
- const timeout = optionalNumber(a.timeout, 'timeout');
496
+ const timeout = optionalNumber(a.timeout, "timeout");
451
497
  if (timeout !== undefined) {
452
498
  if (!Number.isInteger(timeout) || timeout < 1000 || timeout > 300000) {
453
499
  throw new Error('"timeout" must be an integer between 1000 and 300000 (ms).');
@@ -456,27 +502,27 @@ export function validateTestSummaryArgs(args) {
456
502
  return { command: a.command, runner, timeout };
457
503
  }
458
504
  export function validateReadSectionArgs(args) {
459
- if (!args || typeof args !== 'object') {
460
- throw new Error('Arguments must be an object.');
505
+ if (!args || typeof args !== "object") {
506
+ throw new Error("Arguments must be an object.");
461
507
  }
462
508
  const a = args;
463
- if (typeof a.path !== 'string' || a.path.length === 0) {
509
+ if (typeof a.path !== "string" || a.path.length === 0) {
464
510
  throw new Error('Required parameter "path" must be a non-empty string.');
465
511
  }
466
- if (typeof a.heading !== 'string' || a.heading.length === 0) {
512
+ if (typeof a.heading !== "string" || a.heading.length === 0) {
467
513
  throw new Error('Required parameter "heading" must be a non-empty string.');
468
514
  }
469
515
  return { path: a.path, heading: a.heading };
470
516
  }
471
517
  /** Detect roots that would cause ast-index to scan the entire filesystem */
472
518
  export function isDangerousRoot(root) {
473
- const normalized = root.replace(/\/+$/, '') || '/';
519
+ const normalized = root.replace(/\/+$/, "") || "/";
474
520
  // System roots
475
- if (normalized === '/' || normalized === '/tmp' || normalized === '/var')
521
+ if (normalized === "/" || normalized === "/tmp" || normalized === "/var")
476
522
  return true;
477
523
  // Home directories (macOS, Linux)
478
- const home = process.env.HOME || process.env.USERPROFILE || '';
479
- if (home && normalized === home.replace(/\/+$/, ''))
524
+ const home = process.env.HOME || process.env.USERPROFILE || "";
525
+ if (home && normalized === home.replace(/\/+$/, ""))
480
526
  return true;
481
527
  // Common dangerous patterns: /Users, /home, /root, C:\, C:\Users
482
528
  if (/^\/(?:Users|home|root)$/.test(normalized))