Files
SuperCharged-Claude-Code-Up…/brainstorming/.agent/workspace/shell-tool.cjs
admin 07242683bf Add 260+ Claude Code skills from skills.sh
Complete collection of AI agent skills including:
- Frontend Development (Vue, React, Next.js, Three.js)
- Backend Development (NestJS, FastAPI, Node.js)
- Mobile Development (React Native, Expo)
- Testing (E2E, frontend, webapp)
- DevOps (GitHub Actions, CI/CD)
- Marketing (SEO, copywriting, analytics)
- Security (binary analysis, vulnerability scanning)
- And many more...

Synchronized from: https://skills.sh/

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-23 18:02:28 +00:00

267 lines
6.2 KiB
JavaScript

/**
* Shell Command Tool
* Executes shell commands with proper error handling and output formatting
*/
const { exec, spawn } = require('child_process');
const { promisify } = require('util');
const { BaseTool, ToolResult } = require('./tool-base.cjs');
const execAsync = promisify(exec);
class ShellTool extends BaseTool {
constructor(config = {}) {
super({
name: 'shell',
description: 'Execute shell commands in the terminal',
parameters: [
{
name: 'command',
type: 'string',
required: true,
description: 'The shell command to execute'
},
{
name: 'cwd',
type: 'string',
required: false,
description: 'Working directory for command execution'
},
{
name: 'timeout',
type: 'number',
required: false,
description: 'Execution timeout in milliseconds (default: 30000)'
},
{
name: 'env',
type: 'object',
required: false,
description: 'Environment variables for the command'
}
],
...config
});
this.defaultTimeout = config.defaultTimeout || 30000;
this.maxOutputSize = config.maxOutputSize || 100000; // 100KB
}
/**
* Execute a shell command
*/
async execute(params) {
const { command, cwd, timeout = this.defaultTimeout, env } = params;
try {
// Security check for dangerous commands
const securityCheck = this.checkSecurity(command);
if (!securityCheck.safe) {
throw new Error(`Security warning: ${securityCheck.reason}`);
}
const options = {
timeout,
cwd: cwd || process.cwd(),
env: { ...process.env, ...env },
maxBuffer: 10 * 1024 * 1024 // 10MB
};
// Execute command
const { stdout, stderr } = await execAsync(command, options);
// Format output
const output = this.formatOutput(stdout, stderr);
return ToolResult.success(
{ stdout, stderr, exitCode: 0 },
output,
{ command, cwd: options.cwd }
);
} catch (error) {
// Handle execution errors
const output = this.formatErrorOutput(error);
return ToolResult.failure(
error,
output,
{ command, exitCode: error.code || 1 }
);
}
}
/**
* Basic security check for commands
*/
checkSecurity(command) {
const dangerousPatterns = [
'rm -rf /',
'rm -rf /*',
'mkfs',
'format',
'> /dev/sd',
'dd if=',
':(){:|:&};:', // Fork bomb
'chmod 000 /',
'chown -R'
];
const lowerCommand = command.toLowerCase();
for (const pattern of dangerousPatterns) {
if (lowerCommand.includes(pattern)) {
return {
safe: false,
reason: `Command contains dangerous pattern: ${pattern}`
};
}
}
return { safe: true };
}
/**
* Format command output for display
*/
formatOutput(stdout, stderr) {
let output = '';
if (stdout && stdout.trim()) {
output += stdout;
}
if (stderr && stderr.trim()) {
if (output) output += '\n';
output += `[stderr]: ${stderr}`;
}
// Truncate if too large
if (output.length > this.maxOutputSize) {
output = output.substring(0, this.maxOutputSize);
output += `\n... [Output truncated, exceeded ${this.maxOutputSize} bytes]`;
}
return output || '[No output]';
}
/**
* Format error output
*/
formatErrorOutput(error) {
let output = '';
if (error.killed) {
output = `Command timed out after ${error.timeout}ms`;
} else if (error.code) {
output = `Command failed with exit code ${error.code}`;
} else {
output = `Command failed: ${error.message}`;
}
if (error.stderr) {
output += `\n${error.stderr}`;
}
if (error.stdout) {
output += `\n${error.stdout}`;
}
return output;
}
}
/**
* Streaming Shell Tool
* For long-running commands with real-time output
*/
class StreamingShellTool extends BaseTool {
constructor(config = {}) {
super({
name: 'shell_stream',
description: 'Execute long-running shell commands with streaming output',
parameters: [
{
name: 'command',
type: 'string',
required: true,
description: 'The shell command to execute'
},
{
name: 'cwd',
type: 'string',
required: false,
description: 'Working directory for command execution'
},
{
name: 'onData',
type: 'function',
required: false,
description: 'Callback for streaming data chunks'
}
],
...config
});
}
async execute(params) {
return new Promise((resolve, reject) => {
const { command, cwd, onData } = params;
const output = { stdout: '', stderr: '' };
const options = {
cwd: cwd || process.cwd(),
shell: true
};
const proc = spawn(command, options);
proc.stdout.on('data', (data) => {
const text = data.toString();
output.stdout += text;
if (onData) onData({ type: 'stdout', data: text });
});
proc.stderr.on('data', (data) => {
const text = data.toString();
output.stderr += text;
if (onData) onData({ type: 'stderr', data: text });
});
proc.on('close', (code) => {
if (code === 0) {
resolve(
ToolResult.success(
output,
output.stdout || '[Process completed successfully]',
{ exitCode: code }
)
);
} else {
resolve(
ToolResult.failure(
new Error(`Process exited with code ${code}`),
output.stderr || output.stdout,
{ exitCode: code, ...output }
)
);
}
});
proc.on('error', (error) => {
resolve(
ToolResult.failure(
error,
`Failed to spawn process: ${error.message}`,
{ error: error.message }
)
);
});
});
}
}
module.exports = {
ShellTool,
StreamingShellTool
};