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