ai-harness / src /tools /shell /index.ts
stevenkhan's picture
Initial AI Harness - production-grade model-agnostic CLI agent runtime
908562b verified
// ─── Shell Tool ─────────────────────────────────────────────────────────────
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, // 10MB
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,
};
}
},
};