@kernel.chat/kbot 2.9.0 → 2.10.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 (70) hide show
  1. package/dist/agent.d.ts +2 -0
  2. package/dist/agent.d.ts.map +1 -1
  3. package/dist/agent.js +1 -1
  4. package/dist/agent.js.map +1 -1
  5. package/dist/auth.d.ts.map +1 -1
  6. package/dist/auth.js +17 -12
  7. package/dist/auth.js.map +1 -1
  8. package/dist/cli.js +194 -2
  9. package/dist/cli.js.map +1 -1
  10. package/dist/cloud-sync.d.ts.map +1 -1
  11. package/dist/cloud-sync.js +15 -4
  12. package/dist/cloud-sync.js.map +1 -1
  13. package/dist/confidence.d.ts.map +1 -1
  14. package/dist/confidence.js +4 -1
  15. package/dist/confidence.js.map +1 -1
  16. package/dist/context-manager.d.ts.map +1 -1
  17. package/dist/context-manager.js +31 -5
  18. package/dist/context-manager.js.map +1 -1
  19. package/dist/context-manager.test.d.ts +2 -0
  20. package/dist/context-manager.test.d.ts.map +1 -0
  21. package/dist/context-manager.test.js +121 -0
  22. package/dist/context-manager.test.js.map +1 -0
  23. package/dist/export.d.ts +20 -0
  24. package/dist/export.d.ts.map +1 -0
  25. package/dist/export.js +301 -0
  26. package/dist/export.js.map +1 -0
  27. package/dist/ide/acp-server.js +5 -4
  28. package/dist/ide/acp-server.js.map +1 -1
  29. package/dist/ide/lsp-bridge.d.ts.map +1 -1
  30. package/dist/ide/lsp-bridge.js +11 -5
  31. package/dist/ide/lsp-bridge.js.map +1 -1
  32. package/dist/marketplace.d.ts +25 -0
  33. package/dist/marketplace.d.ts.map +1 -0
  34. package/dist/marketplace.js +327 -0
  35. package/dist/marketplace.js.map +1 -0
  36. package/dist/planner.d.ts.map +1 -1
  37. package/dist/planner.js +3 -2
  38. package/dist/planner.js.map +1 -1
  39. package/dist/rate-limiter.d.ts +45 -0
  40. package/dist/rate-limiter.d.ts.map +1 -0
  41. package/dist/rate-limiter.js +200 -0
  42. package/dist/rate-limiter.js.map +1 -0
  43. package/dist/sessions.test.d.ts +2 -0
  44. package/dist/sessions.test.d.ts.map +1 -0
  45. package/dist/sessions.test.js +55 -0
  46. package/dist/sessions.test.js.map +1 -0
  47. package/dist/streaming.d.ts.map +1 -1
  48. package/dist/streaming.js +105 -26
  49. package/dist/streaming.js.map +1 -1
  50. package/dist/streaming.test.d.ts +2 -0
  51. package/dist/streaming.test.d.ts.map +1 -0
  52. package/dist/streaming.test.js +41 -0
  53. package/dist/streaming.test.js.map +1 -0
  54. package/dist/tools/index.d.ts.map +1 -1
  55. package/dist/tools/index.js +22 -10
  56. package/dist/tools/index.js.map +1 -1
  57. package/dist/tools/test-runner.d.ts +2 -0
  58. package/dist/tools/test-runner.d.ts.map +1 -0
  59. package/dist/tools/test-runner.js +550 -0
  60. package/dist/tools/test-runner.js.map +1 -0
  61. package/dist/ui.js +1 -1
  62. package/dist/voice.d.ts +25 -0
  63. package/dist/voice.d.ts.map +1 -0
  64. package/dist/voice.js +336 -0
  65. package/dist/voice.js.map +1 -0
  66. package/dist/watch.d.ts +37 -0
  67. package/dist/watch.d.ts.map +1 -0
  68. package/dist/watch.js +369 -0
  69. package/dist/watch.js.map +1 -0
  70. package/package.json +1 -1
@@ -100,15 +100,25 @@ export async function executeTool(call) {
100
100
  const maxResult = tool.maxResultSize ?? DEFAULT_MAX_RESULT;
101
101
  const startTime = Date.now();
102
102
  try {
103
- // Race tool execution against timeout
104
- const result = await Promise.race([
105
- tool.execute(call.arguments),
106
- new Promise((_, reject) => setTimeout(() => reject(new Error(`Tool '${call.name}' timed out after ${timeout / 1000}s`)), timeout)),
107
- ]);
108
- const durationMs = Date.now() - startTime;
109
- const truncated = truncateResult(result, maxResult);
110
- recordMetrics(call.name, durationMs, false);
111
- return { tool_call_id: call.id, result: truncated, duration_ms: durationMs };
103
+ // Execute tool with AbortController-based timeout (prevents leaked promises)
104
+ const controller = new AbortController();
105
+ const timer = setTimeout(() => controller.abort(), timeout);
106
+ try {
107
+ const result = await Promise.race([
108
+ tool.execute(call.arguments),
109
+ new Promise((_, reject) => {
110
+ controller.signal.addEventListener('abort', () => reject(new Error(`Tool '${call.name}' timed out after ${timeout / 1000}s`)));
111
+ }),
112
+ ]);
113
+ clearTimeout(timer);
114
+ const durationMs = Date.now() - startTime;
115
+ const truncated = truncateResult(result, maxResult);
116
+ recordMetrics(call.name, durationMs, false);
117
+ return { tool_call_id: call.id, result: truncated, duration_ms: durationMs };
118
+ }
119
+ finally {
120
+ clearTimeout(timer);
121
+ }
112
122
  }
113
123
  catch (err) {
114
124
  const durationMs = Date.now() - startTime;
@@ -124,7 +134,7 @@ export async function executeTool(call) {
124
134
  /** Register all built-in tools. Call once at startup. Uses parallel imports for speed. */
125
135
  export async function registerAllTools(opts) {
126
136
  // Parallel import all tool modules at once
127
- const [{ registerFileTools }, { registerBashTools }, { registerGitTools }, { registerSearchTools }, { registerFetchTools }, { registerGitHubTools }, { registerMatrixTools }, { registerParallelTools }, { registerMcpClientTools }, { registerTaskTools }, { registerNotebookTools }, { registerBackgroundTools }, { registerSandboxTools }, { registerBuildMatrixTools }, { registerSubagentTools }, { registerWorktreeTools }, { registerOpenClawTools }, { registerQualityTools }, { registerMemoryTools }, { registerBrowserTools }, { registerE2bTools }, { registerLspTools }, { registerMcpPluginTools }, { registerGraphMemoryTools }, { registerConfidenceTools }, { registerAgentProtocolTools }, { registerTemporalTools }, { registerReasoningTools }, { registerIntentionalityTools },] = await Promise.all([
137
+ const [{ registerFileTools }, { registerBashTools }, { registerGitTools }, { registerSearchTools }, { registerFetchTools }, { registerGitHubTools }, { registerMatrixTools }, { registerParallelTools }, { registerMcpClientTools }, { registerTaskTools }, { registerNotebookTools }, { registerBackgroundTools }, { registerSandboxTools }, { registerBuildMatrixTools }, { registerSubagentTools }, { registerWorktreeTools }, { registerOpenClawTools }, { registerQualityTools }, { registerMemoryTools }, { registerBrowserTools }, { registerE2bTools }, { registerLspTools }, { registerMcpPluginTools }, { registerGraphMemoryTools }, { registerConfidenceTools }, { registerAgentProtocolTools }, { registerTemporalTools }, { registerReasoningTools }, { registerIntentionalityTools }, { registerTestRunnerTools },] = await Promise.all([
128
138
  import('./files.js'),
129
139
  import('./bash.js'),
130
140
  import('./git.js'),
@@ -154,6 +164,7 @@ export async function registerAllTools(opts) {
154
164
  import('../temporal.js'),
155
165
  import('../reasoning.js'),
156
166
  import('../intentionality.js'),
167
+ import('./test-runner.js'),
157
168
  ]);
158
169
  // Register all tools (synchronous, fast)
159
170
  registerFileTools();
@@ -185,6 +196,7 @@ export async function registerAllTools(opts) {
185
196
  registerTemporalTools();
186
197
  registerReasoningTools();
187
198
  registerIntentionalityTools();
199
+ registerTestRunnerTools();
188
200
  // Computer use tools — opt-in only via --computer-use flag
189
201
  if (opts?.computerUse) {
190
202
  const { registerComputerTools } = await import('./computer.js');
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare function registerTestRunnerTools(): void;
2
+ //# sourceMappingURL=test-runner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-runner.d.ts","sourceRoot":"","sources":["../../src/tools/test-runner.ts"],"names":[],"mappings":"AA8iBA,wBAAgB,uBAAuB,IAAI,IAAI,CAqD9C"}
@@ -0,0 +1,550 @@
1
+ import { registerTool } from './index.js';
2
+ import { execSync } from 'node:child_process';
3
+ import { existsSync, readFileSync } from 'node:fs';
4
+ import { join } from 'node:path';
5
+ function detectFramework(projectPath) {
6
+ // 1. Vitest — explicit config or vite config with test section
7
+ const vitestConfigs = [
8
+ 'vitest.config.ts',
9
+ 'vitest.config.js',
10
+ 'vitest.config.mts',
11
+ 'vitest.config.mjs',
12
+ ];
13
+ for (const cfg of vitestConfigs) {
14
+ if (existsSync(join(projectPath, cfg)))
15
+ return 'vitest';
16
+ }
17
+ // Check vite.config.* for a `test` key (lightweight heuristic)
18
+ const viteConfigs = [
19
+ 'vite.config.ts',
20
+ 'vite.config.js',
21
+ 'vite.config.mts',
22
+ 'vite.config.mjs',
23
+ ];
24
+ for (const cfg of viteConfigs) {
25
+ const p = join(projectPath, cfg);
26
+ if (existsSync(p)) {
27
+ try {
28
+ const content = readFileSync(p, 'utf-8');
29
+ if (/\btest\s*[:{]/.test(content))
30
+ return 'vitest';
31
+ }
32
+ catch {
33
+ // ignore read errors
34
+ }
35
+ }
36
+ }
37
+ // 2. Jest — explicit config or "jest" key in package.json
38
+ const jestConfigs = [
39
+ 'jest.config.ts',
40
+ 'jest.config.js',
41
+ 'jest.config.mjs',
42
+ 'jest.config.cjs',
43
+ 'jest.config.json',
44
+ ];
45
+ for (const cfg of jestConfigs) {
46
+ if (existsSync(join(projectPath, cfg)))
47
+ return 'jest';
48
+ }
49
+ const pkgPath = join(projectPath, 'package.json');
50
+ if (existsSync(pkgPath)) {
51
+ try {
52
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
53
+ if (pkg.jest)
54
+ return 'jest';
55
+ }
56
+ catch {
57
+ // ignore parse errors
58
+ }
59
+ }
60
+ // 3. Pytest
61
+ if (existsSync(join(projectPath, 'pytest.ini')))
62
+ return 'pytest';
63
+ if (existsSync(join(projectPath, 'setup.cfg'))) {
64
+ try {
65
+ const content = readFileSync(join(projectPath, 'setup.cfg'), 'utf-8');
66
+ if (/\[tool:pytest\]/.test(content))
67
+ return 'pytest';
68
+ }
69
+ catch {
70
+ // ignore
71
+ }
72
+ }
73
+ if (existsSync(join(projectPath, 'pyproject.toml'))) {
74
+ try {
75
+ const content = readFileSync(join(projectPath, 'pyproject.toml'), 'utf-8');
76
+ if (/\[tool\.pytest/.test(content))
77
+ return 'pytest';
78
+ }
79
+ catch {
80
+ // ignore
81
+ }
82
+ }
83
+ // 4. Cargo (Rust)
84
+ if (existsSync(join(projectPath, 'Cargo.toml')))
85
+ return 'cargo';
86
+ // 5. Go
87
+ if (existsSync(join(projectPath, 'go.mod')))
88
+ return 'go';
89
+ // 6. Fallback — npm test script
90
+ if (existsSync(pkgPath)) {
91
+ try {
92
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
93
+ if (pkg.scripts?.test && pkg.scripts.test !== 'echo "Error: no test specified" && exit 1') {
94
+ return 'npm';
95
+ }
96
+ }
97
+ catch {
98
+ // ignore
99
+ }
100
+ }
101
+ return null;
102
+ }
103
+ // ---------------------------------------------------------------------------
104
+ // Command builders
105
+ // ---------------------------------------------------------------------------
106
+ function buildCommand(framework, opts) {
107
+ switch (framework) {
108
+ case 'vitest': {
109
+ let cmd = 'npx vitest run --reporter=json';
110
+ if (opts.file)
111
+ cmd += ` ${opts.file}`;
112
+ else if (opts.pattern)
113
+ cmd += ` --testPathPattern="${opts.pattern}"`;
114
+ return cmd;
115
+ }
116
+ case 'jest': {
117
+ let cmd = 'npx jest --json';
118
+ if (opts.file)
119
+ cmd += ` ${opts.file}`;
120
+ else if (opts.pattern)
121
+ cmd += ` --testPathPattern="${opts.pattern}"`;
122
+ return cmd;
123
+ }
124
+ case 'pytest': {
125
+ let cmd = 'python -m pytest --tb=short -q';
126
+ if (opts.file)
127
+ cmd += ` ${opts.file}`;
128
+ else if (opts.pattern)
129
+ cmd += ` -k "${opts.pattern}"`;
130
+ if (opts.verbose)
131
+ cmd += ' -v';
132
+ return cmd;
133
+ }
134
+ case 'cargo': {
135
+ let cmd = 'cargo test';
136
+ if (opts.file)
137
+ cmd += ` --test ${opts.file}`;
138
+ else if (opts.pattern)
139
+ cmd += ` ${opts.pattern}`;
140
+ cmd += ' 2>&1';
141
+ return cmd;
142
+ }
143
+ case 'go': {
144
+ let cmd = 'go test';
145
+ if (opts.file)
146
+ cmd += ` ${opts.file}`;
147
+ else
148
+ cmd += ' ./...';
149
+ if (opts.verbose)
150
+ cmd += ' -v';
151
+ cmd += ' 2>&1';
152
+ return cmd;
153
+ }
154
+ case 'npm': {
155
+ return 'npm test 2>&1';
156
+ }
157
+ }
158
+ }
159
+ // ---------------------------------------------------------------------------
160
+ // Output parsers
161
+ // ---------------------------------------------------------------------------
162
+ function parseVitestJson(raw) {
163
+ const result = {
164
+ framework: 'vitest',
165
+ total: 0,
166
+ passed: 0,
167
+ failed: 0,
168
+ skipped: 0,
169
+ failures: [],
170
+ };
171
+ try {
172
+ // vitest --reporter=json may prefix non-JSON output; find the JSON object
173
+ const jsonStart = raw.indexOf('{');
174
+ const jsonEnd = raw.lastIndexOf('}');
175
+ if (jsonStart === -1 || jsonEnd === -1)
176
+ return { ...result, raw };
177
+ const data = JSON.parse(raw.substring(jsonStart, jsonEnd + 1));
178
+ result.total = data.numTotalTests ?? 0;
179
+ result.passed = data.numPassedTests ?? 0;
180
+ result.failed = data.numFailedTests ?? 0;
181
+ result.skipped = (data.numPendingTests ?? 0) + (data.numTodoTests ?? 0);
182
+ if (Array.isArray(data.testResults)) {
183
+ for (const suite of data.testResults) {
184
+ if (suite.status === 'failed' && Array.isArray(suite.assertionResults)) {
185
+ for (const assertion of suite.assertionResults) {
186
+ if (assertion.status === 'failed') {
187
+ result.failures.push({
188
+ name: assertion.ancestorTitles
189
+ ? [...assertion.ancestorTitles, assertion.title].join(' > ')
190
+ : assertion.fullName ?? assertion.title ?? 'unknown',
191
+ message: Array.isArray(assertion.failureMessages)
192
+ ? assertion.failureMessages.join('\n').slice(0, 500)
193
+ : String(assertion.failureMessages ?? '').slice(0, 500),
194
+ });
195
+ }
196
+ }
197
+ }
198
+ }
199
+ }
200
+ }
201
+ catch {
202
+ result.raw = raw;
203
+ }
204
+ return result;
205
+ }
206
+ function parseJestJson(raw) {
207
+ const result = {
208
+ framework: 'jest',
209
+ total: 0,
210
+ passed: 0,
211
+ failed: 0,
212
+ skipped: 0,
213
+ failures: [],
214
+ };
215
+ try {
216
+ const jsonStart = raw.indexOf('{');
217
+ const jsonEnd = raw.lastIndexOf('}');
218
+ if (jsonStart === -1 || jsonEnd === -1)
219
+ return { ...result, raw };
220
+ const data = JSON.parse(raw.substring(jsonStart, jsonEnd + 1));
221
+ result.total = data.numTotalTests ?? 0;
222
+ result.passed = data.numPassedTests ?? 0;
223
+ result.failed = data.numFailedTests ?? 0;
224
+ result.skipped = data.numPendingTests ?? 0;
225
+ if (Array.isArray(data.testResults)) {
226
+ for (const suite of data.testResults) {
227
+ if (Array.isArray(suite.assertionResults)) {
228
+ for (const assertion of suite.assertionResults) {
229
+ if (assertion.status === 'failed') {
230
+ result.failures.push({
231
+ name: assertion.ancestorTitles
232
+ ? [...assertion.ancestorTitles, assertion.title].join(' > ')
233
+ : assertion.fullName ?? assertion.title ?? 'unknown',
234
+ message: Array.isArray(assertion.failureMessages)
235
+ ? assertion.failureMessages.join('\n').slice(0, 500)
236
+ : String(assertion.failureMessages ?? '').slice(0, 500),
237
+ });
238
+ }
239
+ }
240
+ }
241
+ }
242
+ }
243
+ }
244
+ catch {
245
+ result.raw = raw;
246
+ }
247
+ return result;
248
+ }
249
+ function parsePytestOutput(raw) {
250
+ const result = {
251
+ framework: 'pytest',
252
+ total: 0,
253
+ passed: 0,
254
+ failed: 0,
255
+ skipped: 0,
256
+ failures: [],
257
+ };
258
+ // Match summary line: "X passed, Y failed, Z skipped" etc.
259
+ const summaryMatch = raw.match(/(\d+)\s+passed|(\d+)\s+failed|(\d+)\s+error|(\d+)\s+skipped|(\d+)\s+warning/g);
260
+ if (summaryMatch) {
261
+ for (const part of summaryMatch) {
262
+ const numMatch = part.match(/(\d+)\s+(\w+)/);
263
+ if (!numMatch)
264
+ continue;
265
+ const count = parseInt(numMatch[1], 10);
266
+ const label = numMatch[2];
267
+ if (label === 'passed')
268
+ result.passed = count;
269
+ else if (label === 'failed' || label === 'error')
270
+ result.failed += count;
271
+ else if (label === 'skipped')
272
+ result.skipped = count;
273
+ }
274
+ result.total = result.passed + result.failed + result.skipped;
275
+ }
276
+ // Extract FAILED lines
277
+ const failedLines = raw.match(/FAILED\s+(.+?)(?:\s+-\s+(.+))?$/gm);
278
+ if (failedLines) {
279
+ for (const line of failedLines) {
280
+ const m = line.match(/FAILED\s+(.+?)(?:\s+-\s+(.+))?$/);
281
+ if (m) {
282
+ result.failures.push({
283
+ name: m[1].trim(),
284
+ message: m[2]?.trim() ?? '',
285
+ });
286
+ }
287
+ }
288
+ }
289
+ if (result.total === 0)
290
+ result.raw = raw;
291
+ return result;
292
+ }
293
+ function parseCargoOutput(raw) {
294
+ const result = {
295
+ framework: 'cargo',
296
+ total: 0,
297
+ passed: 0,
298
+ failed: 0,
299
+ skipped: 0,
300
+ failures: [],
301
+ };
302
+ // Match "test result: ok/FAILED. X passed; Y failed; Z ignored"
303
+ const summaryMatch = raw.match(/test result:\s*\w+\.\s*(\d+)\s+passed;\s*(\d+)\s+failed;\s*(\d+)\s+ignored/);
304
+ if (summaryMatch) {
305
+ result.passed = parseInt(summaryMatch[1], 10);
306
+ result.failed = parseInt(summaryMatch[2], 10);
307
+ result.skipped = parseInt(summaryMatch[3], 10);
308
+ result.total = result.passed + result.failed + result.skipped;
309
+ }
310
+ // Extract failures: lines matching "---- <test_name> stdout ----" followed by failure text
311
+ const failureBlocks = raw.split(/----\s+(.+?)\s+stdout\s+----/);
312
+ for (let i = 1; i < failureBlocks.length; i += 2) {
313
+ const testName = failureBlocks[i];
314
+ const body = failureBlocks[i + 1] ?? '';
315
+ // Only include if there's an assertion failure
316
+ if (body.includes("thread '") && body.includes('panicked at')) {
317
+ const msgMatch = body.match(/panicked at\s+'([^']+)'/);
318
+ result.failures.push({
319
+ name: testName,
320
+ message: msgMatch ? msgMatch[1].slice(0, 500) : body.slice(0, 500),
321
+ });
322
+ }
323
+ }
324
+ if (result.total === 0)
325
+ result.raw = raw;
326
+ return result;
327
+ }
328
+ function parseGoOutput(raw) {
329
+ const result = {
330
+ framework: 'go',
331
+ total: 0,
332
+ passed: 0,
333
+ failed: 0,
334
+ skipped: 0,
335
+ failures: [],
336
+ };
337
+ // Count pass/fail from "--- PASS:" and "--- FAIL:" lines
338
+ const passMatches = raw.match(/---\s+PASS:/g);
339
+ const failMatches = raw.match(/---\s+FAIL:/g);
340
+ const skipMatches = raw.match(/---\s+SKIP:/g);
341
+ result.passed = passMatches?.length ?? 0;
342
+ result.failed = failMatches?.length ?? 0;
343
+ result.skipped = skipMatches?.length ?? 0;
344
+ result.total = result.passed + result.failed + result.skipped;
345
+ // Extract failure details
346
+ const failLines = raw.match(/---\s+FAIL:\s+(\S+)\s+\([\d.]+s\)/g);
347
+ if (failLines) {
348
+ for (const line of failLines) {
349
+ const m = line.match(/---\s+FAIL:\s+(\S+)/);
350
+ if (m) {
351
+ // Try to find the error message above this FAIL line
352
+ const idx = raw.indexOf(line);
353
+ const preceding = raw.substring(Math.max(0, idx - 500), idx);
354
+ const errLines = preceding
355
+ .split('\n')
356
+ .filter((l) => l.includes('Error') || l.includes('expected') || l.includes('got'))
357
+ .slice(-3);
358
+ result.failures.push({
359
+ name: m[1],
360
+ message: errLines.join('\n').slice(0, 500) || 'Test failed',
361
+ });
362
+ }
363
+ }
364
+ }
365
+ // Fallback: check for "FAIL" at package level if no individual tests found
366
+ if (result.total === 0) {
367
+ const pkgFail = raw.match(/^FAIL\s+\S+/gm);
368
+ const pkgOk = raw.match(/^ok\s+\S+/gm);
369
+ if (pkgFail || pkgOk) {
370
+ result.failed = pkgFail?.length ?? 0;
371
+ result.passed = pkgOk?.length ?? 0;
372
+ result.total = result.passed + result.failed;
373
+ }
374
+ }
375
+ if (result.total === 0)
376
+ result.raw = raw;
377
+ return result;
378
+ }
379
+ function parseGenericOutput(raw) {
380
+ const result = {
381
+ framework: 'npm',
382
+ total: 0,
383
+ passed: 0,
384
+ failed: 0,
385
+ skipped: 0,
386
+ failures: [],
387
+ raw,
388
+ };
389
+ // Try common patterns
390
+ // "X passing", "Y failing", "Z pending"
391
+ const passingMatch = raw.match(/(\d+)\s+passing/i);
392
+ const failingMatch = raw.match(/(\d+)\s+failing/i);
393
+ const pendingMatch = raw.match(/(\d+)\s+pending/i);
394
+ if (passingMatch)
395
+ result.passed = parseInt(passingMatch[1], 10);
396
+ if (failingMatch)
397
+ result.failed = parseInt(failingMatch[1], 10);
398
+ if (pendingMatch)
399
+ result.skipped = parseInt(pendingMatch[1], 10);
400
+ // "Tests: X passed, Y failed, Z total"
401
+ const testsLine = raw.match(/Tests:\s*(\d+)\s+passed.*?(\d+)\s+failed.*?(\d+)\s+total/i);
402
+ if (testsLine) {
403
+ result.passed = parseInt(testsLine[1], 10);
404
+ result.failed = parseInt(testsLine[2], 10);
405
+ result.total = parseInt(testsLine[3], 10);
406
+ }
407
+ if (result.total === 0) {
408
+ result.total = result.passed + result.failed + result.skipped;
409
+ }
410
+ return result;
411
+ }
412
+ function parseOutput(framework, raw) {
413
+ switch (framework) {
414
+ case 'vitest':
415
+ return parseVitestJson(raw);
416
+ case 'jest':
417
+ return parseJestJson(raw);
418
+ case 'pytest':
419
+ return parsePytestOutput(raw);
420
+ case 'cargo':
421
+ return parseCargoOutput(raw);
422
+ case 'go':
423
+ return parseGoOutput(raw);
424
+ case 'npm':
425
+ return parseGenericOutput(raw);
426
+ }
427
+ }
428
+ // ---------------------------------------------------------------------------
429
+ // Format result for agent consumption
430
+ // ---------------------------------------------------------------------------
431
+ function formatResult(result) {
432
+ const lines = [];
433
+ lines.push(`Framework: ${result.framework}`);
434
+ lines.push(`Total: ${result.total} | Passed: ${result.passed} | Failed: ${result.failed}${result.skipped ? ` | Skipped: ${result.skipped}` : ''}`);
435
+ if (result.failures.length > 0) {
436
+ lines.push('');
437
+ lines.push('Failures:');
438
+ for (let i = 0; i < result.failures.length; i++) {
439
+ const f = result.failures[i];
440
+ lines.push(` ${i + 1}. ${f.name}`);
441
+ if (f.message) {
442
+ const msgLines = f.message.split('\n').filter(Boolean);
443
+ for (const ml of msgLines) {
444
+ lines.push(` ${ml.trim()}`);
445
+ }
446
+ }
447
+ }
448
+ }
449
+ if (result.failed === 0 && result.total > 0) {
450
+ lines.push('');
451
+ lines.push('All tests passed.');
452
+ }
453
+ if (result.raw && result.total === 0) {
454
+ lines.push('');
455
+ lines.push('Could not parse test output. Raw output:');
456
+ lines.push(result.raw.slice(0, 2000));
457
+ }
458
+ return lines.join('\n');
459
+ }
460
+ // ---------------------------------------------------------------------------
461
+ // Run tests
462
+ // ---------------------------------------------------------------------------
463
+ function runTests(projectPath, opts = {}) {
464
+ const resolvedPath = projectPath || process.cwd();
465
+ if (!existsSync(resolvedPath)) {
466
+ return `Error: path does not exist: ${resolvedPath}`;
467
+ }
468
+ const framework = detectFramework(resolvedPath);
469
+ if (!framework) {
470
+ return ('No test framework detected. Looked for:\n' +
471
+ ' - vitest.config.* or vite.config.* with test section\n' +
472
+ ' - jest.config.* or "jest" in package.json\n' +
473
+ ' - pytest.ini, pyproject.toml with [tool.pytest]\n' +
474
+ ' - Cargo.toml\n' +
475
+ ' - go.mod\n' +
476
+ ' - package.json with "test" script');
477
+ }
478
+ const cmd = buildCommand(framework, opts);
479
+ let stdout;
480
+ try {
481
+ stdout = execSync(cmd, {
482
+ cwd: resolvedPath,
483
+ timeout: 120_000,
484
+ maxBuffer: 10 * 1024 * 1024, // 10 MB
485
+ encoding: 'utf-8',
486
+ stdio: ['pipe', 'pipe', 'pipe'],
487
+ env: { ...process.env, FORCE_COLOR: '0', CI: 'true' },
488
+ });
489
+ }
490
+ catch (err) {
491
+ // Test failures cause non-zero exit codes — capture stdout anyway
492
+ const execErr = err;
493
+ stdout = execErr.stdout ?? execErr.stderr ?? execErr.message ?? 'Unknown error';
494
+ }
495
+ const result = parseOutput(framework, stdout);
496
+ return formatResult(result);
497
+ }
498
+ // ---------------------------------------------------------------------------
499
+ // Registration
500
+ // ---------------------------------------------------------------------------
501
+ export function registerTestRunnerTools() {
502
+ registerTool({
503
+ name: 'run_tests',
504
+ description: 'Run project tests. Auto-detects the test framework (vitest, jest, pytest, cargo, go, npm) by checking config files, runs tests, and returns structured pass/fail results with failure details the agent can use to auto-fix issues.',
505
+ parameters: {
506
+ path: { type: 'string', description: 'Project root path. Defaults to current working directory.' },
507
+ pattern: { type: 'string', description: 'Test file pattern or filter to narrow which tests to run.' },
508
+ verbose: { type: 'boolean', description: 'Enable verbose output from the test runner.' },
509
+ },
510
+ tier: 'free',
511
+ async execute(args) {
512
+ return runTests(String(args.path || process.cwd()), {
513
+ pattern: args.pattern ? String(args.pattern) : undefined,
514
+ verbose: args.verbose === true,
515
+ });
516
+ },
517
+ });
518
+ registerTool({
519
+ name: 'test_file',
520
+ description: 'Run tests for a specific file. Auto-detects the test framework and runs only the specified test file, returning structured results with failure details.',
521
+ parameters: {
522
+ path: { type: 'string', description: 'Absolute or relative path to the test file to run.', required: true },
523
+ },
524
+ tier: 'free',
525
+ async execute(args) {
526
+ const args_path = String(args.path || '');
527
+ if (!args_path) {
528
+ return 'Error: path is required. Provide the path to the test file.';
529
+ }
530
+ // Derive project root by walking up from the test file to find a config
531
+ const { dirname } = await import('node:path');
532
+ let dir = dirname(args_path.startsWith('/') ? args_path : join(process.cwd(), args_path));
533
+ let projectRoot = process.cwd();
534
+ // Walk up to find project root (look for package.json, Cargo.toml, go.mod, etc.)
535
+ const rootMarkers = ['package.json', 'Cargo.toml', 'go.mod', 'pyproject.toml', 'pytest.ini'];
536
+ for (let i = 0; i < 10; i++) {
537
+ if (rootMarkers.some((marker) => existsSync(join(dir, marker)))) {
538
+ projectRoot = dir;
539
+ break;
540
+ }
541
+ const parent = dirname(dir);
542
+ if (parent === dir)
543
+ break;
544
+ dir = parent;
545
+ }
546
+ return runTests(projectRoot, { file: args_path });
547
+ },
548
+ });
549
+ }
550
+ //# sourceMappingURL=test-runner.js.map