| |
| import { z } from 'zod'; |
| import { exec } from 'child_process'; |
| import { promisify } from 'util'; |
| import type { ToolDef } from '../../core/tools/index.js'; |
|
|
| const execAsync = promisify(exec); |
|
|
| export const shellExecTool: ToolDef< |
| { command: string; timeout?: number }, |
| { stdout: string; stderr: string; exitCode: number } |
| > = { |
| id: 'shell.exec', |
| name: 'shell_exec', |
| description: 'Execute a shell command and return stdout, stderr, and exit code. Use for running builds, tests, git commands, or any CLI tool.', |
| inputSchema: z.object({ |
| command: z.string().describe('Shell command to execute'), |
| timeout: z.number().optional().describe('Timeout in milliseconds (default: 30000)'), |
| }), |
| outputSchema: z.object({ |
| stdout: z.string(), |
| stderr: z.string(), |
| exitCode: z.number(), |
| }), |
| permission: 'exec', |
| sideEffect: 'process', |
| timeout: 60000, |
| retries: 0, |
| tags: ['shell', 'exec'], |
| renderer: { icon: 'β‘', color: 'green' }, |
| async execute(input, ctx) { |
| ctx.emit(`Executing: ${input.command}`); |
| const timeout = input.timeout ?? 30000; |
| try { |
| const { stdout, stderr } = await execAsync(input.command, { |
| cwd: ctx.workDir, |
| timeout, |
| maxBuffer: 1024 * 1024 * 10, |
| signal: ctx.signal, |
| }); |
| return { stdout: stdout.slice(0, 50000), stderr: stderr.slice(0, 10000), exitCode: 0 }; |
| } catch (err: any) { |
| return { |
| stdout: (err.stdout ?? '').slice(0, 50000), |
| stderr: (err.stderr ?? err.message ?? '').slice(0, 10000), |
| exitCode: err.code ?? 1, |
| }; |
| } |
| }, |
| }; |
|
|