@yuaone/tools 0.1.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/LICENSE +663 -0
- package/README.md +15 -0
- package/dist/__tests__/file-edit.test.d.ts +8 -0
- package/dist/__tests__/file-edit.test.d.ts.map +1 -0
- package/dist/__tests__/file-edit.test.js +125 -0
- package/dist/__tests__/file-edit.test.js.map +1 -0
- package/dist/__tests__/registry.test.d.ts +7 -0
- package/dist/__tests__/registry.test.d.ts.map +1 -0
- package/dist/__tests__/registry.test.js +83 -0
- package/dist/__tests__/registry.test.js.map +1 -0
- package/dist/__tests__/validators.test.d.ts +8 -0
- package/dist/__tests__/validators.test.d.ts.map +1 -0
- package/dist/__tests__/validators.test.js +189 -0
- package/dist/__tests__/validators.test.js.map +1 -0
- package/dist/base-tool.d.ts +45 -0
- package/dist/base-tool.d.ts.map +1 -0
- package/dist/base-tool.js +87 -0
- package/dist/base-tool.js.map +1 -0
- package/dist/browser-tool.d.ts +39 -0
- package/dist/browser-tool.d.ts.map +1 -0
- package/dist/browser-tool.js +518 -0
- package/dist/browser-tool.js.map +1 -0
- package/dist/code-search.d.ts +42 -0
- package/dist/code-search.d.ts.map +1 -0
- package/dist/code-search.js +298 -0
- package/dist/code-search.js.map +1 -0
- package/dist/design-tools.d.ts +70 -0
- package/dist/design-tools.d.ts.map +1 -0
- package/dist/design-tools.js +471 -0
- package/dist/design-tools.js.map +1 -0
- package/dist/dev-server-manager.d.ts +32 -0
- package/dist/dev-server-manager.d.ts.map +1 -0
- package/dist/dev-server-manager.js +183 -0
- package/dist/dev-server-manager.js.map +1 -0
- package/dist/file-edit.d.ts +19 -0
- package/dist/file-edit.d.ts.map +1 -0
- package/dist/file-edit.js +217 -0
- package/dist/file-edit.js.map +1 -0
- package/dist/file-read.d.ts +19 -0
- package/dist/file-read.d.ts.map +1 -0
- package/dist/file-read.js +142 -0
- package/dist/file-read.js.map +1 -0
- package/dist/file-write.d.ts +18 -0
- package/dist/file-write.d.ts.map +1 -0
- package/dist/file-write.js +139 -0
- package/dist/file-write.js.map +1 -0
- package/dist/git-ops.d.ts +25 -0
- package/dist/git-ops.d.ts.map +1 -0
- package/dist/git-ops.js +219 -0
- package/dist/git-ops.js.map +1 -0
- package/dist/glob.d.ts +18 -0
- package/dist/glob.d.ts.map +1 -0
- package/dist/glob.js +91 -0
- package/dist/glob.js.map +1 -0
- package/dist/grep.d.ts +19 -0
- package/dist/grep.d.ts.map +1 -0
- package/dist/grep.js +177 -0
- package/dist/grep.js.map +1 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +29 -0
- package/dist/index.js.map +1 -0
- package/dist/security-scan.d.ts +62 -0
- package/dist/security-scan.d.ts.map +1 -0
- package/dist/security-scan.js +445 -0
- package/dist/security-scan.js.map +1 -0
- package/dist/shell-exec.d.ts +20 -0
- package/dist/shell-exec.d.ts.map +1 -0
- package/dist/shell-exec.js +206 -0
- package/dist/shell-exec.js.map +1 -0
- package/dist/test-run.d.ts +51 -0
- package/dist/test-run.d.ts.map +1 -0
- package/dist/test-run.js +359 -0
- package/dist/test-run.js.map +1 -0
- package/dist/tool-registry.d.ts +70 -0
- package/dist/tool-registry.d.ts.map +1 -0
- package/dist/tool-registry.js +181 -0
- package/dist/tool-registry.js.map +1 -0
- package/dist/types.d.ts +137 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +8 -0
- package/dist/types.js.map +1 -0
- package/dist/validators.d.ts +57 -0
- package/dist/validators.d.ts.map +1 -0
- package/dist/validators.js +218 -0
- package/dist/validators.js.map +1 -0
- package/package.json +42 -0
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @yuaone/tools — shell_exec tool
|
|
3
|
+
*
|
|
4
|
+
* Executes a command using execFile (no shell interpretation).
|
|
5
|
+
* - executable + args[] pattern (not a string command)
|
|
6
|
+
* - Shell metacharacter validation
|
|
7
|
+
* - Blocked commands (rm -rf /, sudo, chmod 777, etc.)
|
|
8
|
+
* - 30-second default timeout
|
|
9
|
+
* - stdout/stderr separate return
|
|
10
|
+
*/
|
|
11
|
+
import { execFile } from 'node:child_process';
|
|
12
|
+
import { BaseTool } from './base-tool.js';
|
|
13
|
+
import { validateNoShellMeta, validateCommand, validatePath } from './validators.js';
|
|
14
|
+
const DEFAULT_TIMEOUT = 30_000;
|
|
15
|
+
const MAX_STDOUT = 100_000; // 100KB
|
|
16
|
+
const MAX_STDERR = 50_000; // 50KB
|
|
17
|
+
export class ShellExecTool extends BaseTool {
|
|
18
|
+
name = 'shell_exec';
|
|
19
|
+
description = 'Execute a command with explicit executable and args (no shell interpretation). ' +
|
|
20
|
+
'Use for build tools, test runners, linters, etc.';
|
|
21
|
+
riskLevel = 'critical';
|
|
22
|
+
parameters = {
|
|
23
|
+
executable: {
|
|
24
|
+
type: 'string',
|
|
25
|
+
description: 'Executable name or path (e.g., "npx", "tsc", "node")',
|
|
26
|
+
required: true,
|
|
27
|
+
},
|
|
28
|
+
args: {
|
|
29
|
+
type: 'array',
|
|
30
|
+
description: 'Argument array (passed directly, no shell interpretation)',
|
|
31
|
+
required: true,
|
|
32
|
+
items: { type: 'string', description: 'A single argument' },
|
|
33
|
+
},
|
|
34
|
+
cwd: {
|
|
35
|
+
type: 'string',
|
|
36
|
+
description: 'Working directory (relative to project root, default: project root)',
|
|
37
|
+
required: false,
|
|
38
|
+
},
|
|
39
|
+
timeout: {
|
|
40
|
+
type: 'number',
|
|
41
|
+
description: 'Timeout in milliseconds (default: 30000)',
|
|
42
|
+
required: false,
|
|
43
|
+
default: DEFAULT_TIMEOUT,
|
|
44
|
+
},
|
|
45
|
+
env: {
|
|
46
|
+
type: 'object',
|
|
47
|
+
description: 'Additional environment variables',
|
|
48
|
+
required: false,
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
async execute(args, workDir, abortSignal) {
|
|
52
|
+
const toolCallId = args._toolCallId ?? '';
|
|
53
|
+
const executable = args.executable;
|
|
54
|
+
const execArgs = args.args;
|
|
55
|
+
const cwd = args.cwd;
|
|
56
|
+
const timeout = args.timeout ?? DEFAULT_TIMEOUT;
|
|
57
|
+
const env = args.env;
|
|
58
|
+
if (!executable) {
|
|
59
|
+
return this.fail(toolCallId, 'Missing required parameter: executable');
|
|
60
|
+
}
|
|
61
|
+
if (!execArgs || !Array.isArray(execArgs)) {
|
|
62
|
+
return this.fail(toolCallId, 'Missing required parameter: args (must be an array)');
|
|
63
|
+
}
|
|
64
|
+
// Block shell binaries and command wrappers that can bypass tool validation
|
|
65
|
+
const SHELL_BINARIES = new Set(['bash', 'sh', 'zsh', 'dash', 'csh', 'ksh', 'fish']);
|
|
66
|
+
const COMMAND_WRAPPERS = new Set([
|
|
67
|
+
'env', 'xargs', 'nohup', 'strace', 'ltrace', 'gdb',
|
|
68
|
+
'script', 'expect', 'unbuffer', 'setsid', 'timeout',
|
|
69
|
+
]);
|
|
70
|
+
const execBase = executable.split('/').pop() ?? executable;
|
|
71
|
+
if (SHELL_BINARIES.has(execBase)) {
|
|
72
|
+
return this.fail(toolCallId, `Shell binary "${execBase}" cannot be executed directly. ` +
|
|
73
|
+
'Use specific tool commands instead (e.g., "node", "pnpm", "git").');
|
|
74
|
+
}
|
|
75
|
+
if (COMMAND_WRAPPERS.has(execBase)) {
|
|
76
|
+
return this.fail(toolCallId, `Command wrapper "${execBase}" is blocked — it can be used to bypass security controls. ` +
|
|
77
|
+
'Execute the target command directly instead.');
|
|
78
|
+
}
|
|
79
|
+
// Validate no shell metacharacters
|
|
80
|
+
try {
|
|
81
|
+
validateNoShellMeta(executable, execArgs);
|
|
82
|
+
}
|
|
83
|
+
catch (err) {
|
|
84
|
+
return this.fail(toolCallId, err.message);
|
|
85
|
+
}
|
|
86
|
+
// Validate command is not blocked
|
|
87
|
+
try {
|
|
88
|
+
validateCommand(executable, execArgs);
|
|
89
|
+
}
|
|
90
|
+
catch (err) {
|
|
91
|
+
return this.fail(toolCallId, err.message);
|
|
92
|
+
}
|
|
93
|
+
// Resolve cwd
|
|
94
|
+
let resolvedCwd = workDir;
|
|
95
|
+
if (cwd) {
|
|
96
|
+
try {
|
|
97
|
+
resolvedCwd = validatePath(cwd, workDir);
|
|
98
|
+
}
|
|
99
|
+
catch (err) {
|
|
100
|
+
return this.fail(toolCallId, err.message);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// Execute
|
|
104
|
+
const startTime = Date.now();
|
|
105
|
+
try {
|
|
106
|
+
const result = await new Promise((resolve) => {
|
|
107
|
+
// Check if already aborted before starting
|
|
108
|
+
if (abortSignal?.aborted) {
|
|
109
|
+
resolve({ stdout: '', stderr: '', exitCode: 1, timedOut: false });
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
const child = execFile(executable, execArgs, {
|
|
113
|
+
cwd: resolvedCwd,
|
|
114
|
+
timeout,
|
|
115
|
+
maxBuffer: MAX_STDOUT + MAX_STDERR,
|
|
116
|
+
env: env ? { ...process.env, ...sanitizeEnv(env) } : process.env,
|
|
117
|
+
}, (error, stdout, stderr) => {
|
|
118
|
+
const execError = error;
|
|
119
|
+
let exitCode;
|
|
120
|
+
if (execError && typeof execError.code === 'number') {
|
|
121
|
+
exitCode = execError.code;
|
|
122
|
+
}
|
|
123
|
+
else if (execError && 'status' in execError && typeof execError.status === 'number') {
|
|
124
|
+
exitCode = execError.status;
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
exitCode = execError ? 1 : 0;
|
|
128
|
+
}
|
|
129
|
+
const timedOut = execError !== null && 'killed' in (execError ?? {}) && execError?.killed === true;
|
|
130
|
+
resolve({
|
|
131
|
+
stdout: truncateStr(String(stdout ?? ''), MAX_STDOUT),
|
|
132
|
+
stderr: truncateStr(String(stderr ?? ''), MAX_STDERR),
|
|
133
|
+
exitCode: typeof exitCode === 'number' ? exitCode : 1,
|
|
134
|
+
timedOut,
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
// Wire AbortSignal to kill the child process
|
|
138
|
+
if (abortSignal) {
|
|
139
|
+
const onAbort = () => {
|
|
140
|
+
child.kill('SIGTERM');
|
|
141
|
+
// If SIGTERM doesn't work, escalate to SIGKILL after 3s
|
|
142
|
+
setTimeout(() => {
|
|
143
|
+
if (!child.killed) {
|
|
144
|
+
child.kill('SIGKILL');
|
|
145
|
+
}
|
|
146
|
+
}, 3000).unref();
|
|
147
|
+
};
|
|
148
|
+
abortSignal.addEventListener('abort', onAbort, { once: true });
|
|
149
|
+
// Clean up listener when child exits
|
|
150
|
+
child.on('exit', () => {
|
|
151
|
+
abortSignal.removeEventListener('abort', onAbort);
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
const durationMs = Date.now() - startTime;
|
|
156
|
+
let output = '';
|
|
157
|
+
if (result.stdout) {
|
|
158
|
+
output += `[stdout]\n${result.stdout}\n`;
|
|
159
|
+
}
|
|
160
|
+
if (result.stderr) {
|
|
161
|
+
output += `[stderr]\n${result.stderr}\n`;
|
|
162
|
+
}
|
|
163
|
+
if (result.timedOut) {
|
|
164
|
+
output += `\n[TIMED OUT after ${timeout}ms]`;
|
|
165
|
+
}
|
|
166
|
+
output += `\n[exit code: ${result.exitCode}] [duration: ${durationMs}ms]`;
|
|
167
|
+
return {
|
|
168
|
+
tool_call_id: toolCallId,
|
|
169
|
+
name: this.name,
|
|
170
|
+
success: result.exitCode === 0 && !result.timedOut,
|
|
171
|
+
output: this.truncateOutput(output),
|
|
172
|
+
durationMs,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
catch (err) {
|
|
176
|
+
const durationMs = Date.now() - startTime;
|
|
177
|
+
return this.fail(toolCallId, `Execution failed: ${err.message} [duration: ${durationMs}ms]`);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Strip dangerous environment variables that could enable arbitrary code execution.
|
|
183
|
+
* Blocks PATH override, dynamic linker injection, and interpreter hooks.
|
|
184
|
+
*/
|
|
185
|
+
const BLOCKED_ENV_VARS = new Set([
|
|
186
|
+
'PATH', 'LD_PRELOAD', 'LD_LIBRARY_PATH', 'DYLD_INSERT_LIBRARIES',
|
|
187
|
+
'DYLD_LIBRARY_PATH', 'NODE_OPTIONS', 'PYTHONPATH', 'PYTHONSTARTUP',
|
|
188
|
+
'PERL5OPT', 'PERL5LIB', 'RUBYOPT', 'RUBYLIB',
|
|
189
|
+
'SHELL', 'BASH_ENV', 'ENV', 'CDPATH',
|
|
190
|
+
'IFS', 'SHELLOPTS', 'BASHOPTS',
|
|
191
|
+
]);
|
|
192
|
+
function sanitizeEnv(env) {
|
|
193
|
+
const safe = {};
|
|
194
|
+
for (const [key, value] of Object.entries(env)) {
|
|
195
|
+
if (!BLOCKED_ENV_VARS.has(key.toUpperCase())) {
|
|
196
|
+
safe[key] = value;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return safe;
|
|
200
|
+
}
|
|
201
|
+
function truncateStr(s, max) {
|
|
202
|
+
if (s.length <= max)
|
|
203
|
+
return s;
|
|
204
|
+
return s.slice(-max) + `\n... (truncated, showing last ${max} chars)`;
|
|
205
|
+
}
|
|
206
|
+
//# sourceMappingURL=shell-exec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shell-exec.js","sourceRoot":"","sources":["../src/shell-exec.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @yuaone/tools — test_run tool
|
|
3
|
+
*
|
|
4
|
+
* Test framework auto-detection + execution.
|
|
5
|
+
*
|
|
6
|
+
* Detection order:
|
|
7
|
+
* 1. package.json scripts.test
|
|
8
|
+
* 2. jest.config / vitest.config / pytest.ini config files
|
|
9
|
+
* 3. Error if no framework detected
|
|
10
|
+
*
|
|
11
|
+
* Supports: Jest (--json), Vitest (--reporter=json), Pytest (--tb=short)
|
|
12
|
+
*
|
|
13
|
+
* riskLevel: 'medium' (tests execute code)
|
|
14
|
+
*/
|
|
15
|
+
import type { ParameterDef, RiskLevel, ToolResult } from './types.js';
|
|
16
|
+
import { BaseTool } from './base-tool.js';
|
|
17
|
+
export declare class TestRunTool extends BaseTool {
|
|
18
|
+
readonly name = "test_run";
|
|
19
|
+
readonly description: string;
|
|
20
|
+
readonly riskLevel: RiskLevel;
|
|
21
|
+
readonly parameters: Record<string, ParameterDef>;
|
|
22
|
+
execute(args: Record<string, unknown>, workDir: string): Promise<ToolResult>;
|
|
23
|
+
/**
|
|
24
|
+
* Detect test framework from project configuration.
|
|
25
|
+
*/
|
|
26
|
+
private detectFramework;
|
|
27
|
+
/**
|
|
28
|
+
* Build the executable and args for the detected framework.
|
|
29
|
+
*/
|
|
30
|
+
private buildCommand;
|
|
31
|
+
/**
|
|
32
|
+
* Execute the test command using execFile (no shell injection).
|
|
33
|
+
*/
|
|
34
|
+
private runTest;
|
|
35
|
+
/**
|
|
36
|
+
* Parse test output into structured results.
|
|
37
|
+
*/
|
|
38
|
+
private parseResults;
|
|
39
|
+
private parseJestResults;
|
|
40
|
+
private parseVitestResults;
|
|
41
|
+
private parsePytestResults;
|
|
42
|
+
/**
|
|
43
|
+
* Fallback parser for raw text output when JSON parsing fails.
|
|
44
|
+
*/
|
|
45
|
+
private parseRawOutput;
|
|
46
|
+
/**
|
|
47
|
+
* Format the output for the agent.
|
|
48
|
+
*/
|
|
49
|
+
private formatOutput;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=test-run.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-run.d.ts","sourceRoot":"","sources":["../src/test-run.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAKH,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACtE,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAgB1C,qBAAa,WAAY,SAAQ,QAAQ;IACvC,QAAQ,CAAC,IAAI,cAAc;IAC3B,QAAQ,CAAC,WAAW,SAEsD;IAC1E,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAY;IAEzC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAmB/C;IAEI,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IA4DlF;;OAEG;YACW,eAAe;IAgD7B;;OAEG;IACH,OAAO,CAAC,YAAY;IA8BpB;;OAEG;IACH,OAAO,CAAC,OAAO;IA4Bf;;OAEG;IACH,OAAO,CAAC,YAAY;IAWpB,OAAO,CAAC,gBAAgB;IAkDxB,OAAO,CAAC,kBAAkB;IAiD1B,OAAO,CAAC,kBAAkB;IA4B1B;;OAEG;IACH,OAAO,CAAC,cAAc;IAmBtB;;OAEG;IACH,OAAO,CAAC,YAAY;CAkCrB"}
|
package/dist/test-run.js
ADDED
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @yuaone/tools — test_run tool
|
|
3
|
+
*
|
|
4
|
+
* Test framework auto-detection + execution.
|
|
5
|
+
*
|
|
6
|
+
* Detection order:
|
|
7
|
+
* 1. package.json scripts.test
|
|
8
|
+
* 2. jest.config / vitest.config / pytest.ini config files
|
|
9
|
+
* 3. Error if no framework detected
|
|
10
|
+
*
|
|
11
|
+
* Supports: Jest (--json), Vitest (--reporter=json), Pytest (--tb=short)
|
|
12
|
+
*
|
|
13
|
+
* riskLevel: 'medium' (tests execute code)
|
|
14
|
+
*/
|
|
15
|
+
import { execFile } from 'node:child_process';
|
|
16
|
+
import { readFile, access } from 'node:fs/promises';
|
|
17
|
+
import { join } from 'node:path';
|
|
18
|
+
import { BaseTool } from './base-tool.js';
|
|
19
|
+
const DEFAULT_TIMEOUT = 60_000; // 60s
|
|
20
|
+
const MAX_OUTPUT = 100_000; // 100KB
|
|
21
|
+
export class TestRunTool extends BaseTool {
|
|
22
|
+
name = 'test_run';
|
|
23
|
+
description = 'Run tests with auto-detected framework (Jest, Vitest, Pytest). ' +
|
|
24
|
+
'Returns structured results with pass/fail counts and failure details.';
|
|
25
|
+
riskLevel = 'medium';
|
|
26
|
+
parameters = {
|
|
27
|
+
testPath: {
|
|
28
|
+
type: 'string',
|
|
29
|
+
description: 'Specific test file or directory to run (relative to project root)',
|
|
30
|
+
required: false,
|
|
31
|
+
},
|
|
32
|
+
framework: {
|
|
33
|
+
type: 'string',
|
|
34
|
+
description: 'Test framework to use (default: auto-detect)',
|
|
35
|
+
required: false,
|
|
36
|
+
enum: ['jest', 'vitest', 'pytest', 'auto'],
|
|
37
|
+
default: 'auto',
|
|
38
|
+
},
|
|
39
|
+
coverage: {
|
|
40
|
+
type: 'boolean',
|
|
41
|
+
description: 'Include coverage report',
|
|
42
|
+
required: false,
|
|
43
|
+
default: false,
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
async execute(args, workDir) {
|
|
47
|
+
const toolCallId = args._toolCallId ?? '';
|
|
48
|
+
const testPath = args.testPath;
|
|
49
|
+
const requestedFramework = args.framework ?? 'auto';
|
|
50
|
+
const coverage = args.coverage ?? false;
|
|
51
|
+
// Validate testPath if provided
|
|
52
|
+
if (testPath) {
|
|
53
|
+
try {
|
|
54
|
+
this.validatePath(testPath, workDir);
|
|
55
|
+
}
|
|
56
|
+
catch (err) {
|
|
57
|
+
return this.fail(toolCallId, err.message);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
// Detect framework
|
|
61
|
+
let framework;
|
|
62
|
+
if (requestedFramework === 'auto') {
|
|
63
|
+
const detected = await this.detectFramework(workDir);
|
|
64
|
+
if (!detected) {
|
|
65
|
+
return this.fail(toolCallId, 'Could not detect test framework. No test script in package.json and no config files found (jest.config, vitest.config, pytest.ini).');
|
|
66
|
+
}
|
|
67
|
+
framework = detected;
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
framework = requestedFramework;
|
|
71
|
+
}
|
|
72
|
+
// Build command
|
|
73
|
+
const { executable, cmdArgs } = this.buildCommand(framework, testPath, coverage);
|
|
74
|
+
// Execute
|
|
75
|
+
const startTime = Date.now();
|
|
76
|
+
try {
|
|
77
|
+
const result = await this.runTest(executable, cmdArgs, workDir, DEFAULT_TIMEOUT);
|
|
78
|
+
const durationMs = Date.now() - startTime;
|
|
79
|
+
// Parse results
|
|
80
|
+
const summary = this.parseResults(framework, result.stdout, result.stderr);
|
|
81
|
+
const output = this.formatOutput(framework, summary, durationMs, result.exitCode);
|
|
82
|
+
return {
|
|
83
|
+
tool_call_id: toolCallId,
|
|
84
|
+
name: this.name,
|
|
85
|
+
success: summary.failed === 0 && result.exitCode === 0,
|
|
86
|
+
output: this.truncateOutput(output),
|
|
87
|
+
durationMs,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
catch (err) {
|
|
91
|
+
const durationMs = Date.now() - startTime;
|
|
92
|
+
return this.fail(toolCallId, `Test execution failed: ${err.message} [duration: ${durationMs}ms]`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Detect test framework from project configuration.
|
|
97
|
+
*/
|
|
98
|
+
async detectFramework(workDir) {
|
|
99
|
+
// 1. Check package.json scripts.test
|
|
100
|
+
try {
|
|
101
|
+
const pkgPath = join(workDir, 'package.json');
|
|
102
|
+
const pkgContent = await readFile(pkgPath, 'utf-8');
|
|
103
|
+
const pkg = JSON.parse(pkgContent);
|
|
104
|
+
const scripts = pkg.scripts;
|
|
105
|
+
if (scripts?.test) {
|
|
106
|
+
const testScript = scripts.test;
|
|
107
|
+
if (testScript.includes('vitest'))
|
|
108
|
+
return 'vitest';
|
|
109
|
+
if (testScript.includes('jest'))
|
|
110
|
+
return 'jest';
|
|
111
|
+
if (testScript.includes('pytest'))
|
|
112
|
+
return 'pytest';
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
// No package.json or invalid — continue checking config files
|
|
117
|
+
}
|
|
118
|
+
// 2. Check config files
|
|
119
|
+
const configChecks = [
|
|
120
|
+
{ files: ['vitest.config.ts', 'vitest.config.js', 'vitest.config.mts'], framework: 'vitest' },
|
|
121
|
+
{ files: ['jest.config.ts', 'jest.config.js', 'jest.config.cjs', 'jest.config.mjs'], framework: 'jest' },
|
|
122
|
+
{ files: ['pytest.ini', 'pyproject.toml', 'setup.cfg'], framework: 'pytest' },
|
|
123
|
+
];
|
|
124
|
+
for (const check of configChecks) {
|
|
125
|
+
for (const file of check.files) {
|
|
126
|
+
try {
|
|
127
|
+
await access(join(workDir, file));
|
|
128
|
+
// For pyproject.toml, verify it has pytest config
|
|
129
|
+
if (file === 'pyproject.toml') {
|
|
130
|
+
const content = await readFile(join(workDir, file), 'utf-8');
|
|
131
|
+
if (!content.includes('[tool.pytest') && !content.includes('pytest'))
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
if (file === 'setup.cfg') {
|
|
135
|
+
const content = await readFile(join(workDir, file), 'utf-8');
|
|
136
|
+
if (!content.includes('[tool:pytest]'))
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
return check.framework;
|
|
140
|
+
}
|
|
141
|
+
catch {
|
|
142
|
+
// File doesn't exist — continue
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Build the executable and args for the detected framework.
|
|
150
|
+
*/
|
|
151
|
+
buildCommand(framework, testPath, coverage) {
|
|
152
|
+
switch (framework) {
|
|
153
|
+
case 'jest': {
|
|
154
|
+
const cmdArgs = ['--json', '--no-coverage'];
|
|
155
|
+
if (coverage) {
|
|
156
|
+
cmdArgs[1] = '--coverage';
|
|
157
|
+
cmdArgs.push('--coverageReporters=json-summary');
|
|
158
|
+
}
|
|
159
|
+
if (testPath)
|
|
160
|
+
cmdArgs.push(testPath);
|
|
161
|
+
return { executable: 'npx', cmdArgs: ['jest', ...cmdArgs] };
|
|
162
|
+
}
|
|
163
|
+
case 'vitest': {
|
|
164
|
+
const cmdArgs = ['--reporter=json', '--run'];
|
|
165
|
+
if (coverage)
|
|
166
|
+
cmdArgs.push('--coverage');
|
|
167
|
+
if (testPath)
|
|
168
|
+
cmdArgs.push(testPath);
|
|
169
|
+
return { executable: 'npx', cmdArgs: ['vitest', ...cmdArgs] };
|
|
170
|
+
}
|
|
171
|
+
case 'pytest': {
|
|
172
|
+
const cmdArgs = ['--tb=short', '-q'];
|
|
173
|
+
if (coverage)
|
|
174
|
+
cmdArgs.push('--cov', '--cov-report=term');
|
|
175
|
+
if (testPath)
|
|
176
|
+
cmdArgs.push(testPath);
|
|
177
|
+
return { executable: 'python3', cmdArgs: ['-m', 'pytest', ...cmdArgs] };
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Execute the test command using execFile (no shell injection).
|
|
183
|
+
*/
|
|
184
|
+
runTest(executable, args, cwd, timeout) {
|
|
185
|
+
return new Promise((resolve) => {
|
|
186
|
+
execFile(executable, args, {
|
|
187
|
+
cwd,
|
|
188
|
+
timeout,
|
|
189
|
+
maxBuffer: MAX_OUTPUT * 2,
|
|
190
|
+
env: { ...process.env, FORCE_COLOR: '0', CI: 'true' },
|
|
191
|
+
}, (error, stdout, stderr) => {
|
|
192
|
+
const exitCode = error && typeof error.code === 'number' ? error.code : (error ? 1 : 0);
|
|
193
|
+
resolve({
|
|
194
|
+
stdout: String(stdout ?? ''),
|
|
195
|
+
stderr: String(stderr ?? ''),
|
|
196
|
+
exitCode,
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Parse test output into structured results.
|
|
203
|
+
*/
|
|
204
|
+
parseResults(framework, stdout, stderr) {
|
|
205
|
+
switch (framework) {
|
|
206
|
+
case 'jest':
|
|
207
|
+
return this.parseJestResults(stdout, stderr);
|
|
208
|
+
case 'vitest':
|
|
209
|
+
return this.parseVitestResults(stdout, stderr);
|
|
210
|
+
case 'pytest':
|
|
211
|
+
return this.parsePytestResults(stdout, stderr);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
parseJestResults(stdout, stderr) {
|
|
215
|
+
// Jest --json outputs JSON to stdout
|
|
216
|
+
try {
|
|
217
|
+
// Find JSON in output (Jest may prepend non-JSON text)
|
|
218
|
+
const jsonStart = stdout.indexOf('{');
|
|
219
|
+
if (jsonStart >= 0) {
|
|
220
|
+
const json = JSON.parse(stdout.slice(jsonStart));
|
|
221
|
+
const failedTests = [];
|
|
222
|
+
if (json.testResults) {
|
|
223
|
+
for (const suite of json.testResults) {
|
|
224
|
+
if (suite.testResults) {
|
|
225
|
+
for (const test of suite.testResults) {
|
|
226
|
+
if (test.status === 'failed' && test.failureMessages) {
|
|
227
|
+
failedTests.push({
|
|
228
|
+
name: test.title ?? 'unknown',
|
|
229
|
+
error: test.failureMessages.join('\n').slice(0, 500),
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
return {
|
|
237
|
+
passed: json.numPassedTests ?? 0,
|
|
238
|
+
failed: json.numFailedTests ?? 0,
|
|
239
|
+
skipped: json.numPendingTests ?? 0,
|
|
240
|
+
failedTests,
|
|
241
|
+
stdout: stdout + stderr,
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
catch {
|
|
246
|
+
// JSON parse failed — fall through to raw output
|
|
247
|
+
}
|
|
248
|
+
return this.parseRawOutput(stdout, stderr);
|
|
249
|
+
}
|
|
250
|
+
parseVitestResults(stdout, stderr) {
|
|
251
|
+
// Vitest --reporter=json outputs JSON to stdout
|
|
252
|
+
try {
|
|
253
|
+
const jsonStart = stdout.indexOf('{');
|
|
254
|
+
if (jsonStart >= 0) {
|
|
255
|
+
const json = JSON.parse(stdout.slice(jsonStart));
|
|
256
|
+
const failedTests = [];
|
|
257
|
+
if (json.testResults) {
|
|
258
|
+
for (const suite of json.testResults) {
|
|
259
|
+
if (suite.assertionResults) {
|
|
260
|
+
for (const test of suite.assertionResults) {
|
|
261
|
+
if (test.status === 'failed' && test.failureMessages) {
|
|
262
|
+
failedTests.push({
|
|
263
|
+
name: test.title ?? 'unknown',
|
|
264
|
+
error: test.failureMessages.join('\n').slice(0, 500),
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
return {
|
|
272
|
+
passed: json.numPassedTests ?? 0,
|
|
273
|
+
failed: json.numFailedTests ?? 0,
|
|
274
|
+
skipped: json.numPendingTests ?? 0,
|
|
275
|
+
failedTests,
|
|
276
|
+
stdout: stdout + stderr,
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
catch {
|
|
281
|
+
// Fall through
|
|
282
|
+
}
|
|
283
|
+
return this.parseRawOutput(stdout, stderr);
|
|
284
|
+
}
|
|
285
|
+
parsePytestResults(stdout, stderr) {
|
|
286
|
+
const combined = stdout + stderr;
|
|
287
|
+
const failedTests = [];
|
|
288
|
+
// Parse pytest summary line: "X passed, Y failed, Z skipped"
|
|
289
|
+
let passed = 0;
|
|
290
|
+
let failed = 0;
|
|
291
|
+
let skipped = 0;
|
|
292
|
+
const summaryMatch = combined.match(/(\d+)\s+passed/);
|
|
293
|
+
if (summaryMatch)
|
|
294
|
+
passed = parseInt(summaryMatch[1], 10);
|
|
295
|
+
const failedMatch = combined.match(/(\d+)\s+failed/);
|
|
296
|
+
if (failedMatch)
|
|
297
|
+
failed = parseInt(failedMatch[1], 10);
|
|
298
|
+
const skippedMatch = combined.match(/(\d+)\s+skipped/);
|
|
299
|
+
if (skippedMatch)
|
|
300
|
+
skipped = parseInt(skippedMatch[1], 10);
|
|
301
|
+
// Extract FAILED test names
|
|
302
|
+
const failedPattern = /FAILED\s+(\S+)/g;
|
|
303
|
+
let match;
|
|
304
|
+
while ((match = failedPattern.exec(combined)) !== null) {
|
|
305
|
+
failedTests.push({ name: match[1], error: '' });
|
|
306
|
+
}
|
|
307
|
+
return { passed, failed, skipped, failedTests, stdout: combined };
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Fallback parser for raw text output when JSON parsing fails.
|
|
311
|
+
*/
|
|
312
|
+
parseRawOutput(stdout, stderr) {
|
|
313
|
+
const combined = stdout + stderr;
|
|
314
|
+
let passed = 0;
|
|
315
|
+
let failed = 0;
|
|
316
|
+
let skipped = 0;
|
|
317
|
+
// Try to extract numbers from common patterns
|
|
318
|
+
const passMatch = combined.match(/(\d+)\s+(?:pass(?:ed|ing)?|✓)/i);
|
|
319
|
+
if (passMatch)
|
|
320
|
+
passed = parseInt(passMatch[1], 10);
|
|
321
|
+
const failMatch = combined.match(/(\d+)\s+(?:fail(?:ed|ing|ure)?|✗|✕)/i);
|
|
322
|
+
if (failMatch)
|
|
323
|
+
failed = parseInt(failMatch[1], 10);
|
|
324
|
+
const skipMatch = combined.match(/(\d+)\s+(?:skip(?:ped)?|pending|todo)/i);
|
|
325
|
+
if (skipMatch)
|
|
326
|
+
skipped = parseInt(skipMatch[1], 10);
|
|
327
|
+
return { passed, failed, skipped, failedTests: [], stdout: combined };
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Format the output for the agent.
|
|
331
|
+
*/
|
|
332
|
+
formatOutput(framework, summary, durationMs, exitCode) {
|
|
333
|
+
const lines = [];
|
|
334
|
+
lines.push(`[Test Results — ${framework}]`);
|
|
335
|
+
lines.push(`Passed: ${summary.passed} | Failed: ${summary.failed} | Skipped: ${summary.skipped}`);
|
|
336
|
+
if (summary.coverage !== undefined) {
|
|
337
|
+
lines.push(`Coverage: ${summary.coverage}%`);
|
|
338
|
+
}
|
|
339
|
+
lines.push(`Duration: ${durationMs}ms | Exit code: ${exitCode}`);
|
|
340
|
+
if (summary.failedTests.length > 0) {
|
|
341
|
+
lines.push('');
|
|
342
|
+
lines.push('[Failed Tests]');
|
|
343
|
+
for (const t of summary.failedTests.slice(0, 20)) {
|
|
344
|
+
lines.push(` ✗ ${t.name}`);
|
|
345
|
+
if (t.error) {
|
|
346
|
+
lines.push(` ${t.error.split('\n')[0]}`);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
if (summary.failedTests.length > 20) {
|
|
350
|
+
lines.push(` ... and ${summary.failedTests.length - 20} more`);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
lines.push('');
|
|
354
|
+
lines.push('[Raw Output]');
|
|
355
|
+
lines.push(summary.stdout.slice(0, MAX_OUTPUT));
|
|
356
|
+
return lines.join('\n');
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
//# sourceMappingURL=test-run.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-run.js","sourceRoot":"","sources":["../src/test-run.ts"],"names":[],"mappings":"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"}
|