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.
- 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/core/validation.d.ts +13 -9
- package/dist/core/validation.js +180 -134
- package/dist/handlers/call-tree.d.ts +35 -0
- package/dist/handlers/call-tree.js +70 -0
- package/dist/hooks/session-start.d.ts +2 -0
- package/dist/hooks/session-start.js +49 -0
- 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,4 @@
|
|
|
1
|
-
import { resolve, relative } from
|
|
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(
|
|
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 !==
|
|
19
|
-
throw new Error(
|
|
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 !==
|
|
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,
|
|
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,
|
|
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 !==
|
|
39
|
-
throw new Error(
|
|
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 !==
|
|
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 !==
|
|
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 = [
|
|
51
|
-
if (typeof 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,
|
|
60
|
-
context_after: optionalNumber(a.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 !==
|
|
69
|
-
throw new Error(
|
|
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 !==
|
|
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 !==
|
|
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 = [
|
|
89
|
-
if (typeof 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,
|
|
98
|
-
context_after: optionalNumber(a.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 !==
|
|
107
|
-
throw new Error(
|
|
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 !==
|
|
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 !==
|
|
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 !==
|
|
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 !==
|
|
129
|
-
throw new Error(
|
|
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 !==
|
|
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,
|
|
141
|
+
context_lines: optionalNumber(a.context_lines, "context_lines"),
|
|
138
142
|
};
|
|
139
143
|
}
|
|
140
144
|
export function validateFindUsagesArgs(args) {
|
|
141
|
-
if (!args || typeof args !==
|
|
142
|
-
throw new Error(
|
|
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 !==
|
|
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 = [
|
|
151
|
-
if (typeof 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,
|
|
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,
|
|
161
|
-
if (context_lines !== undefined &&
|
|
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 = [
|
|
167
|
-
if (typeof 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,
|
|
179
|
+
scope: optionalString(a.scope, "scope"),
|
|
175
180
|
kind,
|
|
176
181
|
limit,
|
|
177
|
-
lang: optionalString(a.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 !==
|
|
187
|
-
throw new Error(
|
|
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 !==
|
|
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 {
|
|
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 !==
|
|
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 !==
|
|
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 !==
|
|
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 !==
|
|
226
|
-
throw new Error(
|
|
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 !==
|
|
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 &&
|
|
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 !==
|
|
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,
|
|
264
|
+
symbol: optionalString(a.symbol, "symbol"),
|
|
254
265
|
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,
|
|
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 !==
|
|
268
|
-
throw new Error(
|
|
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 !==
|
|
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 !==
|
|
278
|
-
throw new Error(
|
|
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 !==
|
|
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,
|
|
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,
|
|
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 !==
|
|
306
|
+
if (!args || typeof args !== "object")
|
|
296
307
|
return {};
|
|
297
308
|
const a = args;
|
|
298
309
|
return {
|
|
299
|
-
module: optionalString(a.module,
|
|
300
|
-
export_only: optionalBool(a.export_only,
|
|
301
|
-
limit: optionalNumber(a.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 !==
|
|
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 = [
|
|
310
|
-
if (typeof 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 ===
|
|
314
|
-
if (typeof a.pattern !==
|
|
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 ===
|
|
319
|
-
if (typeof a.name !==
|
|
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,
|
|
326
|
-
name: optionalString(a.name,
|
|
327
|
-
lang: optionalString(a.lang,
|
|
328
|
-
limit: optionalNumber(a.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 = [
|
|
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 !==
|
|
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 !==
|
|
342
|
-
|
|
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 !==
|
|
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 !==
|
|
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 = [
|
|
360
|
-
if (typeof 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 ??
|
|
398
|
+
check: check ?? "all",
|
|
368
399
|
};
|
|
369
400
|
}
|
|
370
401
|
export function validateSmartDiffArgs(args) {
|
|
371
|
-
if (!args || typeof args !==
|
|
372
|
-
return { scope:
|
|
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 = [
|
|
377
|
-
if (typeof 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,
|
|
383
|
-
if ((scope ===
|
|
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 ??
|
|
388
|
-
path: optionalString(a.path,
|
|
418
|
+
scope: scope ?? "unstaged",
|
|
419
|
+
path: optionalString(a.path, "path"),
|
|
389
420
|
ref,
|
|
390
421
|
};
|
|
391
422
|
}
|
|
392
|
-
const VALID_EXPLORE_SECTIONS = [
|
|
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 !==
|
|
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 !==
|
|
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 !==
|
|
407
|
-
|
|
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 !==
|
|
452
|
+
if (!args || typeof args !== "object")
|
|
416
453
|
return {};
|
|
417
454
|
const a = args;
|
|
418
|
-
const path = optionalString(a.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,
|
|
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,
|
|
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 = [
|
|
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 !==
|
|
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 !==
|
|
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 !==
|
|
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,
|
|
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 !==
|
|
460
|
-
throw new Error(
|
|
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 !==
|
|
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 !==
|
|
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 ===
|
|
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))
|