feat(ccw): 添加 ccw tool exec 工具系统

新增工具:
- edit_file: AI辅助文件编辑 (update/line模式)
- get_modules_by_depth: 项目结构分析
- update_module_claude: CLAUDE.md文档生成
- generate_module_docs: 模块文档生成
- detect_changed_modules: Git变更检测
- classify_folders: 文件夹分类
- discover_design_files: 设计文件发现
- convert_tokens_to_css: 设计token转CSS
- ui_generate_preview: UI预览生成
- ui_instantiate_prototypes: 原型实例化

使用方式:
  ccw tool list              # 列出所有工具
  ccw tool exec <name> '{}'  # 执行工具

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
catlog22
2025-12-08 21:10:31 +08:00
parent 813bfa8f97
commit 91e4792aa9
18 changed files with 3332 additions and 73 deletions

181
ccw/src/commands/tool.js Normal file
View File

@@ -0,0 +1,181 @@
/**
* Tool Command - Execute and manage CCW tools
*/
import chalk from 'chalk';
import { listTools, executeTool, getTool, getAllToolSchemas } from '../tools/index.js';
/**
* List all available tools
*/
async function listAction() {
const tools = listTools();
if (tools.length === 0) {
console.log(chalk.yellow('No tools registered'));
return;
}
console.log(chalk.bold.cyan('\nAvailable Tools:\n'));
for (const tool of tools) {
console.log(chalk.bold.white(` ${tool.name}`));
console.log(chalk.gray(` ${tool.description}`));
if (tool.parameters?.properties) {
const props = tool.parameters.properties;
const required = tool.parameters.required || [];
console.log(chalk.gray(' Parameters:'));
for (const [name, schema] of Object.entries(props)) {
const req = required.includes(name) ? chalk.red('*') : '';
const defaultVal = schema.default !== undefined ? chalk.gray(` (default: ${schema.default})`) : '';
console.log(chalk.gray(` - ${name}${req}: ${schema.description}${defaultVal}`));
}
}
console.log();
}
}
/**
* Show tool schema in MCP-compatible JSON format
*/
async function schemaAction(options) {
const { name } = options;
if (name) {
const tool = getTool(name);
if (!tool) {
console.error(chalk.red(`Tool not found: ${name}`));
process.exit(1);
}
const schema = {
name: tool.name,
description: tool.description,
inputSchema: {
type: 'object',
properties: tool.parameters?.properties || {},
required: tool.parameters?.required || []
}
};
console.log(JSON.stringify(schema, null, 2));
} else {
const schemas = getAllToolSchemas();
console.log(JSON.stringify({ tools: schemas }, null, 2));
}
}
/**
* Read from stdin if available
*/
async function readStdin() {
// Check if stdin is a TTY (interactive terminal)
if (process.stdin.isTTY) {
return null;
}
return new Promise((resolve, reject) => {
let data = '';
process.stdin.setEncoding('utf8');
process.stdin.on('readable', () => {
let chunk;
while ((chunk = process.stdin.read()) !== null) {
data += chunk;
}
});
process.stdin.on('end', () => {
resolve(data.trim() || null);
});
process.stdin.on('error', (err) => {
reject(err);
});
});
}
/**
* Execute a tool with given parameters
*/
async function execAction(toolName, jsonInput, options) {
if (!toolName) {
console.error(chalk.red('Tool name is required'));
console.error(chalk.gray('Usage: ccw tool exec <tool-name> \'{"param": "value"}\''));
process.exit(1);
}
const tool = getTool(toolName);
if (!tool) {
console.error(chalk.red(`Tool not found: ${toolName}`));
console.error(chalk.gray('Use "ccw tool list" to see available tools'));
process.exit(1);
}
// Parse JSON input (default format)
let params = {};
if (jsonInput) {
try {
params = JSON.parse(jsonInput);
} catch (error) {
console.error(chalk.red(`Invalid JSON: ${error.message}`));
process.exit(1);
}
}
// Check for stdin input (for piped commands)
const stdinData = await readStdin();
if (stdinData) {
// If tool has an 'input' parameter, use it
// Otherwise, try to parse stdin as JSON and merge with params
if (tool.parameters?.properties?.input) {
params.input = stdinData;
} else {
try {
const stdinJson = JSON.parse(stdinData);
params = { ...stdinJson, ...params };
} catch {
// If not JSON, store as 'input' anyway
params.input = stdinData;
}
}
}
// Execute tool
const result = await executeTool(toolName, params);
// Always output JSON
console.log(JSON.stringify(result, null, 2));
}
/**
* Tool command entry point
*/
export async function toolCommand(subcommand, args, options) {
// Handle subcommands
switch (subcommand) {
case 'list':
await listAction();
break;
case 'schema':
await schemaAction({ name: args });
break;
case 'exec':
await execAction(args, options.json, options);
break;
default:
console.log(chalk.bold.cyan('\nCCW Tool System\n'));
console.log('Subcommands:');
console.log(chalk.gray(' list List all available tools'));
console.log(chalk.gray(' schema [name] Show tool schema (JSON)'));
console.log(chalk.gray(' exec <name> Execute a tool'));
console.log();
console.log('Examples:');
console.log(chalk.gray(' ccw tool list'));
console.log(chalk.gray(' ccw tool schema edit_file'));
console.log(chalk.gray(' ccw tool exec edit_file \'{"path":"file.txt","oldText":"old","newText":"new"}\''));
}
}