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>
This commit is contained in:
266
brainstorming/.agent/workspace/shell-tool.cjs
Normal file
266
brainstorming/.agent/workspace/shell-tool.cjs
Normal file
@@ -0,0 +1,266 @@
|
||||
/**
|
||||
* 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
|
||||
};
|
||||
Reference in New Issue
Block a user