mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-04 01:40:45 +08:00
Add CLI endpoints documentation and unified script template for Bash and Python
- Updated AGENTS.md to include CLI tools usage and configuration details. - Introduced a new script template for both Bash and Python, outlining usage context, calling conventions, and implementation guidelines. - Provided examples for common patterns in both Bash and Python scripts. - Established a directory convention for script organization and naming.
This commit is contained in:
@@ -9,12 +9,7 @@
|
||||
|
||||
**Strictly follow the cli-tools.json configuration**
|
||||
|
||||
Available CLI endpoints are dynamically defined by the config file:
|
||||
- Built-in tools and their enable/disable status
|
||||
- Custom API endpoints registered via the Dashboard
|
||||
- Managed through the CCW Dashboard Status page
|
||||
|
||||
|
||||
Available CLI endpoints are dynamically defined by the config file
|
||||
## Tool Execution
|
||||
|
||||
- **Context Requirements**: @~/.claude/workflows/context-tools.md
|
||||
|
||||
@@ -105,8 +105,7 @@ Phase 01 → Phase 02 → Phase 03 → ... → Phase N
|
||||
| [templates/autonomous-action.md](templates/autonomous-action.md) | Autonomous Action 模板 |
|
||||
| [templates/code-analysis-action.md](templates/code-analysis-action.md) | 代码分析 Action 模板 |
|
||||
| [templates/llm-action.md](templates/llm-action.md) | LLM Action 模板 |
|
||||
| [templates/script-bash.md](templates/script-bash.md) | Bash 脚本模板 |
|
||||
| [templates/script-python.md](templates/script-python.md) | Python 脚本模板 |
|
||||
| [templates/script-template.md](templates/script-template.md) | 统一脚本模板 (Bash + Python) |
|
||||
|
||||
### 规范文档 (按需阅读)
|
||||
|
||||
@@ -371,8 +370,7 @@ ELSE IF execution_mode === "autonomous":
|
||||
| [autonomous-action.md](templates/autonomous-action.md) | Action files | Phase 3 |
|
||||
| [code-analysis-action.md](templates/code-analysis-action.md) | Code analysis actions | Phase 3 |
|
||||
| [llm-action.md](templates/llm-action.md) | LLM-powered actions | Phase 3 |
|
||||
| [script-bash.md](templates/script-bash.md) | Bash scripts | Phase 3/4 |
|
||||
| [script-python.md](templates/script-python.md) | Python scripts | Phase 3/4 |
|
||||
| [script-template.md](templates/script-template.md) | Bash + Python scripts | Phase 3/4 |
|
||||
|
||||
## Output Structure
|
||||
|
||||
|
||||
@@ -84,10 +84,42 @@ return {
|
||||
| `{{error_handling_table}}` | 错误处理表格 |
|
||||
| `{{next_actions_hints}}` | 后续动作提示 |
|
||||
|
||||
## 动作生命周期
|
||||
|
||||
```
|
||||
状态驱动执行流:
|
||||
|
||||
state.status === 'pending'
|
||||
↓
|
||||
┌─ Init ─┐ ← 1次执行,环境准备
|
||||
│ 创建工作目录 │
|
||||
│ 初始化 context │
|
||||
│ status → running │
|
||||
└────┬────┘
|
||||
↓
|
||||
┌─ CRUD Loop ─┐ ← N次迭代,核心业务
|
||||
│ 编排器选择动作 │ List / Create / Edit / Delete
|
||||
│ execute(state) │ 共享模式: 收集输入 → 操作 context.items → 返回更新
|
||||
│ 更新 state │
|
||||
└────┬────┘
|
||||
↓
|
||||
┌─ Complete ─┐ ← 1次执行,保存结果
|
||||
│ 序列化输出 │
|
||||
│ status → completed │
|
||||
└──────────┘
|
||||
|
||||
共享状态结构:
|
||||
state.status → 'pending' | 'running' | 'completed'
|
||||
state.context.items → 业务数据数组
|
||||
state.completed_actions → 已执行动作 ID 列表
|
||||
```
|
||||
|
||||
## 动作类型模板
|
||||
|
||||
### 1. 初始化动作 (Init)
|
||||
|
||||
**触发条件**: `state.status === 'pending'`,仅执行一次
|
||||
|
||||
```markdown
|
||||
# Action: Initialize
|
||||
|
||||
@@ -105,101 +137,29 @@ return {
|
||||
|
||||
\`\`\`javascript
|
||||
async function execute(state) {
|
||||
// 1. 创建工作目录
|
||||
Bash(\`mkdir -p "\${workDir}"\`);
|
||||
|
||||
// 2. 初始化数据
|
||||
const initialData = {
|
||||
items: [],
|
||||
metadata: {}
|
||||
};
|
||||
|
||||
// 3. 返回状态更新
|
||||
|
||||
return {
|
||||
stateUpdates: {
|
||||
status: 'running',
|
||||
context: initialData
|
||||
started_at: new Date().toISOString(),
|
||||
context: { items: [], metadata: {} }
|
||||
}
|
||||
};
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
## State Updates
|
||||
|
||||
\`\`\`javascript
|
||||
return {
|
||||
stateUpdates: {
|
||||
status: 'running',
|
||||
started_at: new Date().toISOString(),
|
||||
context: { /* 初始数据 */ }
|
||||
}
|
||||
};
|
||||
\`\`\`
|
||||
|
||||
## Next Actions
|
||||
|
||||
- 成功: 进入主处理循环
|
||||
- 成功: 进入主处理循环 (由编排器选择首个 CRUD 动作)
|
||||
- 失败: action-abort
|
||||
```
|
||||
|
||||
### 2. 列表动作 (List)
|
||||
### 2. CRUD 动作 (List / Create / Edit / Delete)
|
||||
|
||||
```markdown
|
||||
# Action: List Items
|
||||
**触发条件**: `state.status === 'running'`,循环执行直至用户退出
|
||||
|
||||
显示当前项目列表。
|
||||
|
||||
## Purpose
|
||||
|
||||
展示所有项目供用户查看和选择。
|
||||
|
||||
## Preconditions
|
||||
|
||||
- [ ] state.status === 'running'
|
||||
|
||||
## Execution
|
||||
|
||||
\`\`\`javascript
|
||||
async function execute(state) {
|
||||
const items = state.context.items || [];
|
||||
|
||||
if (items.length === 0) {
|
||||
console.log('暂无项目');
|
||||
} else {
|
||||
console.log('项目列表:');
|
||||
items.forEach((item, i) => {
|
||||
console.log(\`\${i + 1}. \${item.name} - \${item.status}\`);
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
stateUpdates: {
|
||||
last_action: 'list',
|
||||
current_view: 'list'
|
||||
}
|
||||
};
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
## State Updates
|
||||
|
||||
\`\`\`javascript
|
||||
return {
|
||||
stateUpdates: {
|
||||
current_view: 'list',
|
||||
last_viewed_at: new Date().toISOString()
|
||||
}
|
||||
};
|
||||
\`\`\`
|
||||
|
||||
## Next Actions
|
||||
|
||||
- 用户选择创建: action-create
|
||||
- 用户选择编辑: action-edit
|
||||
- 用户退出: action-complete
|
||||
```
|
||||
|
||||
### 3. 创建动作 (Create)
|
||||
> 以 Create 为示例展示共享模式。List / Edit / Delete 遵循同一结构,仅 `执行逻辑` 和 `状态更新字段` 不同。
|
||||
|
||||
```markdown
|
||||
# Action: Create Item
|
||||
@@ -208,7 +168,7 @@ return {
|
||||
|
||||
## Purpose
|
||||
|
||||
引导用户创建新项目。
|
||||
收集用户输入,向 context.items 追加新记录。
|
||||
|
||||
## Preconditions
|
||||
|
||||
@@ -218,26 +178,24 @@ return {
|
||||
|
||||
\`\`\`javascript
|
||||
async function execute(state) {
|
||||
// 1. 收集信息
|
||||
// 1. 收集输入
|
||||
const input = await AskUserQuestion({
|
||||
questions: [{
|
||||
question: "请输入项目名称:",
|
||||
header: "名称",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{ label: "手动输入", description: "输入自定义名称" }
|
||||
]
|
||||
options: [{ label: "手动输入", description: "输入自定义名称" }]
|
||||
}]
|
||||
});
|
||||
|
||||
// 2. 创建项目
|
||||
|
||||
// 2. 操作 context.items (核心逻辑因动作类型而异)
|
||||
const newItem = {
|
||||
id: Date.now().toString(),
|
||||
name: input["名称"],
|
||||
status: 'pending',
|
||||
created_at: new Date().toISOString()
|
||||
};
|
||||
|
||||
|
||||
// 3. 返回状态更新
|
||||
return {
|
||||
stateUpdates: {
|
||||
@@ -245,177 +203,30 @@ async function execute(state) {
|
||||
...state.context,
|
||||
items: [...(state.context.items || []), newItem]
|
||||
},
|
||||
last_created_id: newItem.id
|
||||
last_action: 'create'
|
||||
}
|
||||
};
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
## State Updates
|
||||
|
||||
\`\`\`javascript
|
||||
return {
|
||||
stateUpdates: {
|
||||
'context.items': [...items, newItem],
|
||||
last_action: 'create',
|
||||
last_created_id: newItem.id
|
||||
}
|
||||
};
|
||||
\`\`\`
|
||||
|
||||
## Next Actions
|
||||
|
||||
- 继续创建: action-create
|
||||
- 返回列表: action-list
|
||||
- 继续操作: 编排器根据 state 选择下一动作
|
||||
- 用户退出: action-complete
|
||||
```
|
||||
|
||||
### 4. 编辑动作 (Edit)
|
||||
**其他 CRUD 动作差异对照:**
|
||||
|
||||
```markdown
|
||||
# Action: Edit Item
|
||||
| 动作 | 核心逻辑 | 额外前置条件 | 关键状态字段 |
|
||||
|------|---------|------------|------------|
|
||||
| List | `items.forEach(→ console.log)` | 无 | `current_view: 'list'` |
|
||||
| Create | `items.push(newItem)` | 无 | `last_created_id` |
|
||||
| Edit | `items.map(→ 替换匹配项)` | `selected_item_id !== null` | `updated_at` |
|
||||
| Delete | `items.filter(→ 排除匹配项)` | `selected_item_id !== null` | 确认对话 → 执行 |
|
||||
|
||||
编辑现有项目。
|
||||
### 3. 完成动作 (Complete)
|
||||
|
||||
## Purpose
|
||||
|
||||
修改已存在的项目。
|
||||
|
||||
## Preconditions
|
||||
|
||||
- [ ] state.status === 'running'
|
||||
- [ ] state.selected_item_id !== null
|
||||
|
||||
## Execution
|
||||
|
||||
\`\`\`javascript
|
||||
async function execute(state) {
|
||||
const itemId = state.selected_item_id;
|
||||
const items = state.context.items || [];
|
||||
const item = items.find(i => i.id === itemId);
|
||||
|
||||
if (!item) {
|
||||
throw new Error(\`Item not found: \${itemId}\`);
|
||||
}
|
||||
|
||||
// 1. 显示当前值
|
||||
console.log(\`当前名称: \${item.name}\`);
|
||||
|
||||
// 2. 收集新值
|
||||
const input = await AskUserQuestion({
|
||||
questions: [{
|
||||
question: "请输入新名称(留空保持不变):",
|
||||
header: "新名称",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{ label: "保持不变", description: \`当前: \${item.name}\` },
|
||||
{ label: "手动输入", description: "输入新名称" }
|
||||
]
|
||||
}]
|
||||
});
|
||||
|
||||
// 3. 更新项目
|
||||
const updatedItems = items.map(i =>
|
||||
i.id === itemId
|
||||
? { ...i, name: input["新名称"] || i.name, updated_at: new Date().toISOString() }
|
||||
: i
|
||||
);
|
||||
|
||||
return {
|
||||
stateUpdates: {
|
||||
context: { ...state.context, items: updatedItems },
|
||||
selected_item_id: null
|
||||
}
|
||||
};
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
## State Updates
|
||||
|
||||
\`\`\`javascript
|
||||
return {
|
||||
stateUpdates: {
|
||||
'context.items': updatedItems,
|
||||
selected_item_id: null,
|
||||
last_action: 'edit'
|
||||
}
|
||||
};
|
||||
\`\`\`
|
||||
|
||||
## Next Actions
|
||||
|
||||
- 返回列表: action-list
|
||||
```
|
||||
|
||||
### 5. 删除动作 (Delete)
|
||||
|
||||
```markdown
|
||||
# Action: Delete Item
|
||||
|
||||
删除项目。
|
||||
|
||||
## Purpose
|
||||
|
||||
从列表中移除项目。
|
||||
|
||||
## Preconditions
|
||||
|
||||
- [ ] state.status === 'running'
|
||||
- [ ] state.selected_item_id !== null
|
||||
|
||||
## Execution
|
||||
|
||||
\`\`\`javascript
|
||||
async function execute(state) {
|
||||
const itemId = state.selected_item_id;
|
||||
const items = state.context.items || [];
|
||||
|
||||
// 1. 确认删除
|
||||
const confirm = await AskUserQuestion({
|
||||
questions: [{
|
||||
question: "确认删除此项目?",
|
||||
header: "确认",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{ label: "确认删除", description: "不可恢复" },
|
||||
{ label: "取消", description: "返回列表" }
|
||||
]
|
||||
}]
|
||||
});
|
||||
|
||||
if (confirm["确认"] === "取消") {
|
||||
return { stateUpdates: { selected_item_id: null } };
|
||||
}
|
||||
|
||||
// 2. 执行删除
|
||||
const updatedItems = items.filter(i => i.id !== itemId);
|
||||
|
||||
return {
|
||||
stateUpdates: {
|
||||
context: { ...state.context, items: updatedItems },
|
||||
selected_item_id: null
|
||||
}
|
||||
};
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
## State Updates
|
||||
|
||||
\`\`\`javascript
|
||||
return {
|
||||
stateUpdates: {
|
||||
'context.items': filteredItems,
|
||||
selected_item_id: null,
|
||||
last_action: 'delete'
|
||||
}
|
||||
};
|
||||
\`\`\`
|
||||
|
||||
## Next Actions
|
||||
|
||||
- 返回列表: action-list
|
||||
```
|
||||
|
||||
### 6. 完成动作 (Complete)
|
||||
**触发条件**: 用户明确退出或终止条件满足,仅执行一次
|
||||
|
||||
```markdown
|
||||
# Action: Complete
|
||||
@@ -424,7 +235,7 @@ return {
|
||||
|
||||
## Purpose
|
||||
|
||||
保存最终状态,结束 Skill 执行。
|
||||
序列化最终状态,结束 Skill 执行。
|
||||
|
||||
## Preconditions
|
||||
|
||||
@@ -434,41 +245,26 @@ return {
|
||||
|
||||
\`\`\`javascript
|
||||
async function execute(state) {
|
||||
// 1. 保存最终数据
|
||||
Write(\`\${workDir}/final-output.json\`, JSON.stringify(state.context, null, 2));
|
||||
|
||||
// 2. 生成摘要
|
||||
|
||||
const summary = {
|
||||
total_items: state.context.items?.length || 0,
|
||||
duration: Date.now() - new Date(state.started_at).getTime(),
|
||||
actions_executed: state.completed_actions.length
|
||||
};
|
||||
|
||||
console.log('任务完成!');
|
||||
console.log(\`处理项目: \${summary.total_items}\`);
|
||||
console.log(\`执行动作: \${summary.actions_executed}\`);
|
||||
|
||||
|
||||
console.log(\`任务完成: \${summary.total_items} 项, \${summary.actions_executed} 次操作\`);
|
||||
|
||||
return {
|
||||
stateUpdates: {
|
||||
status: 'completed',
|
||||
summary: summary
|
||||
completed_at: new Date().toISOString(),
|
||||
summary
|
||||
}
|
||||
};
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
## State Updates
|
||||
|
||||
\`\`\`javascript
|
||||
return {
|
||||
stateUpdates: {
|
||||
status: 'completed',
|
||||
completed_at: new Date().toISOString(),
|
||||
summary: { /* 统计信息 */ }
|
||||
}
|
||||
};
|
||||
\`\`\`
|
||||
|
||||
## Next Actions
|
||||
|
||||
- 无(终止状态)
|
||||
|
||||
@@ -1,291 +0,0 @@
|
||||
# Bash Script Template
|
||||
|
||||
Bash 脚本模板,用于生成技能中的确定性脚本。
|
||||
|
||||
## Purpose
|
||||
|
||||
为 Skill 生成 Bash 脚本,用于执行确定性操作(文件处理、系统命令、数据转换等)。
|
||||
|
||||
## Usage Context
|
||||
|
||||
| Phase | Usage |
|
||||
|-------|-------|
|
||||
| Optional | Phase/Action 中声明 `## Scripts` 时使用 |
|
||||
| Execution | 通过 `ExecuteScript('script-id', params)` 调用 |
|
||||
| Output Location | `.claude/skills/{skill-name}/scripts/{script-id}.sh` |
|
||||
|
||||
---
|
||||
|
||||
## 模板代码
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# {{script_description}}
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# ============================================================
|
||||
# 参数解析
|
||||
# ============================================================
|
||||
|
||||
INPUT_PATH=""
|
||||
OUTPUT_DIR="" # 由调用方指定,不设默认值
|
||||
|
||||
show_help() {
|
||||
echo "用法: $0 --input-path <path> --output-dir <dir>"
|
||||
echo ""
|
||||
echo "参数:"
|
||||
echo " --input-path 输入文件路径 (必需)"
|
||||
echo " --output-dir 输出目录 (必需,由调用方指定)"
|
||||
echo " --help 显示帮助信息"
|
||||
}
|
||||
|
||||
while [[ "$#" -gt 0 ]]; do
|
||||
case $1 in
|
||||
--input-path)
|
||||
INPUT_PATH="$2"
|
||||
shift
|
||||
;;
|
||||
--output-dir)
|
||||
OUTPUT_DIR="$2"
|
||||
shift
|
||||
;;
|
||||
--help)
|
||||
show_help
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "错误: 未知参数 $1" >&2
|
||||
show_help >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
# ============================================================
|
||||
# 参数验证
|
||||
# ============================================================
|
||||
|
||||
if [[ -z "$INPUT_PATH" ]]; then
|
||||
echo "错误: --input-path 是必需参数" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -z "$OUTPUT_DIR" ]]; then
|
||||
echo "错误: --output-dir 是必需参数" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -f "$INPUT_PATH" ]]; then
|
||||
echo "错误: 输入文件不存在: $INPUT_PATH" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 检查 jq 是否可用(用于 JSON 输出)
|
||||
if ! command -v jq &> /dev/null; then
|
||||
echo "错误: 需要安装 jq" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$OUTPUT_DIR"
|
||||
|
||||
# ============================================================
|
||||
# 核心逻辑
|
||||
# ============================================================
|
||||
|
||||
OUTPUT_FILE="$OUTPUT_DIR/result.txt"
|
||||
ITEMS_COUNT=0
|
||||
|
||||
# TODO: 实现处理逻辑
|
||||
# 示例:处理输入文件
|
||||
while IFS= read -r line; do
|
||||
echo "$line" >> "$OUTPUT_FILE"
|
||||
((ITEMS_COUNT++))
|
||||
done < "$INPUT_PATH"
|
||||
|
||||
# ============================================================
|
||||
# 输出 JSON 结果(使用 jq 构建,避免特殊字符问题)
|
||||
# ============================================================
|
||||
|
||||
jq -n \
|
||||
--arg output_file "$OUTPUT_FILE" \
|
||||
--argjson items_processed "$ITEMS_COUNT" \
|
||||
'{output_file: $output_file, items_processed: $items_processed, status: "success"}'
|
||||
```
|
||||
|
||||
## 变量说明
|
||||
|
||||
| 变量 | 说明 |
|
||||
|------|------|
|
||||
| `{{script_description}}` | 脚本功能描述 |
|
||||
|
||||
## 使用规范
|
||||
|
||||
### 脚本头部
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
set -euo pipefail # 严格模式:出错退出、未定义变量报错、管道错误传递
|
||||
```
|
||||
|
||||
### 参数解析模式
|
||||
|
||||
```bash
|
||||
while [[ "$#" -gt 0 ]]; do
|
||||
case $1 in
|
||||
--param-name)
|
||||
PARAM_VAR="$2"
|
||||
shift
|
||||
;;
|
||||
--flag)
|
||||
FLAG_VAR=true
|
||||
;;
|
||||
*)
|
||||
echo "Unknown: $1" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
```
|
||||
|
||||
### 输出格式
|
||||
|
||||
- 最后一行打印单行 JSON
|
||||
- **强烈推荐使用 `jq`**:自动处理转义和类型
|
||||
|
||||
```bash
|
||||
# 推荐:使用 jq 构建(安全、可靠)
|
||||
jq -n \
|
||||
--arg file "$FILE" \
|
||||
--argjson count "$COUNT" \
|
||||
'{output_file: $file, items_processed: $count}'
|
||||
|
||||
# 备选:简单场景手动拼接(注意特殊字符转义)
|
||||
echo "{\"file\": \"$FILE\", \"count\": $COUNT}"
|
||||
```
|
||||
|
||||
**jq 参数类型**:
|
||||
- `--arg name value`:字符串类型
|
||||
- `--argjson name value`:数字/布尔/null 类型
|
||||
|
||||
### 错误处理
|
||||
|
||||
```bash
|
||||
# 验证错误
|
||||
if [[ -z "$PARAM" ]]; then
|
||||
echo "错误: 参数不能为空" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 命令错误
|
||||
if ! command -v jq &> /dev/null; then
|
||||
echo "错误: 需要安装 jq" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 运行时错误
|
||||
if ! some_command; then
|
||||
echo "错误: 命令执行失败" >&2
|
||||
exit 1
|
||||
fi
|
||||
```
|
||||
|
||||
## 常用模式
|
||||
|
||||
### 文件遍历
|
||||
|
||||
```bash
|
||||
for file in "$INPUT_DIR"/*.json; do
|
||||
[[ -f "$file" ]] || continue
|
||||
echo "处理: $file"
|
||||
# 处理逻辑...
|
||||
done
|
||||
```
|
||||
|
||||
### 临时文件
|
||||
|
||||
```bash
|
||||
TEMP_FILE=$(mktemp)
|
||||
trap "rm -f $TEMP_FILE" EXIT
|
||||
|
||||
echo "data" > "$TEMP_FILE"
|
||||
```
|
||||
|
||||
### 调用其他工具
|
||||
|
||||
```bash
|
||||
# 检查工具存在
|
||||
require_command() {
|
||||
if ! command -v "$1" &> /dev/null; then
|
||||
echo "错误: 需要 $1" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
require_command jq
|
||||
require_command curl
|
||||
```
|
||||
|
||||
### JSON 处理(使用 jq)
|
||||
|
||||
```bash
|
||||
# 读取 JSON 字段
|
||||
VALUE=$(jq -r '.field' "$INPUT_PATH")
|
||||
|
||||
# 修改 JSON
|
||||
jq '.field = "new_value"' "$INPUT_PATH" > "$OUTPUT_FILE"
|
||||
|
||||
# 合并 JSON 文件
|
||||
jq -s 'add' file1.json file2.json > merged.json
|
||||
```
|
||||
|
||||
## 生成函数
|
||||
|
||||
```javascript
|
||||
function generateBashScript(scriptConfig) {
|
||||
return `#!/bin/bash
|
||||
# ${scriptConfig.description}
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# 参数定义
|
||||
${scriptConfig.inputs.map(i =>
|
||||
`${i.name.toUpperCase().replace(/-/g, '_')}="${i.default || ''}"`
|
||||
).join('\n')}
|
||||
|
||||
# 参数解析
|
||||
while [[ "$#" -gt 0 ]]; do
|
||||
case $1 in
|
||||
${scriptConfig.inputs.map(i =>
|
||||
` --${i.name})
|
||||
${i.name.toUpperCase().replace(/-/g, '_')}="$2"
|
||||
shift
|
||||
;;`
|
||||
).join('\n')}
|
||||
*)
|
||||
echo "未知参数: $1" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
# 参数验证
|
||||
${scriptConfig.inputs.filter(i => i.required).map(i =>
|
||||
`if [[ -z "$${i.name.toUpperCase().replace(/-/g, '_')}" ]]; then
|
||||
echo "错误: --${i.name} 是必需参数" >&2
|
||||
exit 1
|
||||
fi`
|
||||
).join('\n\n')}
|
||||
|
||||
# TODO: 实现处理逻辑
|
||||
|
||||
# 输出结果
|
||||
echo "{${scriptConfig.outputs.map(o =>
|
||||
`\\"${o.name}\\": \\"\\$${o.name.toUpperCase().replace(/-/g, '_')}\\"`
|
||||
).join(', ')}}"
|
||||
`;
|
||||
}
|
||||
```
|
||||
@@ -1,212 +0,0 @@
|
||||
# Python Script Template
|
||||
|
||||
Python 脚本模板,用于生成技能中的确定性脚本。
|
||||
|
||||
## Purpose
|
||||
|
||||
为 Skill 生成 Python 脚本,用于执行确定性操作(数据处理、分析、转换等),比 Bash 提供更强的数据处理能力。
|
||||
|
||||
## Usage Context
|
||||
|
||||
| Phase | Usage |
|
||||
|-------|-------|
|
||||
| Optional | Phase/Action 中声明 `## Scripts` 时使用 |
|
||||
| Execution | 通过 `ExecuteScript('script-id', params)` 调用 |
|
||||
| Output Location | `.claude/skills/{skill-name}/scripts/{script-id}.py` |
|
||||
|
||||
---
|
||||
|
||||
## 模板代码
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
{{script_description}}
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def main():
|
||||
# 1. 定义参数
|
||||
parser = argparse.ArgumentParser(description='{{script_description}}')
|
||||
parser.add_argument('--input-path', type=str, required=True,
|
||||
help='输入文件路径')
|
||||
parser.add_argument('--output-dir', type=str, required=True,
|
||||
help='输出目录(由调用方指定)')
|
||||
# 添加更多参数...
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# 2. 验证输入
|
||||
input_path = Path(args.input_path)
|
||||
if not input_path.exists():
|
||||
print(f"错误: 输入文件不存在: {input_path}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
output_dir = Path(args.output_dir)
|
||||
output_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# 3. 执行核心逻辑
|
||||
try:
|
||||
result = process(input_path, output_dir)
|
||||
except Exception as e:
|
||||
print(f"错误: {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
# 4. 输出 JSON 结果
|
||||
print(json.dumps(result))
|
||||
|
||||
|
||||
def process(input_path: Path, output_dir: Path) -> dict:
|
||||
"""
|
||||
核心处理逻辑
|
||||
|
||||
Args:
|
||||
input_path: 输入文件路径
|
||||
output_dir: 输出目录
|
||||
|
||||
Returns:
|
||||
dict: 包含输出结果的字典
|
||||
"""
|
||||
# TODO: 实现处理逻辑
|
||||
|
||||
output_file = output_dir / 'result.json'
|
||||
|
||||
# 示例:读取并处理数据
|
||||
with open(input_path, 'r', encoding='utf-8') as f:
|
||||
data = json.load(f)
|
||||
|
||||
# 处理数据...
|
||||
processed_count = len(data) if isinstance(data, list) else 1
|
||||
|
||||
# 写入输出
|
||||
with open(output_file, 'w', encoding='utf-8') as f:
|
||||
json.dump(data, f, indent=2, ensure_ascii=False)
|
||||
|
||||
return {
|
||||
'output_file': str(output_file),
|
||||
'items_processed': processed_count,
|
||||
'status': 'success'
|
||||
}
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
```
|
||||
|
||||
## 变量说明
|
||||
|
||||
| 变量 | 说明 |
|
||||
|------|------|
|
||||
| `{{script_description}}` | 脚本功能描述 |
|
||||
|
||||
## 使用规范
|
||||
|
||||
### 输入参数
|
||||
|
||||
- 使用 `argparse` 定义参数
|
||||
- 参数名使用 kebab-case:`--input-path`
|
||||
- 必需参数设置 `required=True`
|
||||
- 可选参数提供 `default` 值
|
||||
|
||||
### 输出格式
|
||||
|
||||
- 最后一行打印单行 JSON
|
||||
- 包含所有输出文件路径和关键数据
|
||||
- 错误信息输出到 stderr
|
||||
|
||||
### 错误处理
|
||||
|
||||
```python
|
||||
# 验证错误 - 直接退出
|
||||
if not valid:
|
||||
print("错误信息", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
# 运行时错误 - 捕获并退出
|
||||
try:
|
||||
result = process()
|
||||
except Exception as e:
|
||||
print(f"错误: {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
```
|
||||
|
||||
## 常用模式
|
||||
|
||||
### 文件处理
|
||||
|
||||
```python
|
||||
def process_files(input_dir: Path, pattern: str = '*.json') -> list:
|
||||
results = []
|
||||
for file in input_dir.glob(pattern):
|
||||
with open(file, 'r') as f:
|
||||
data = json.load(f)
|
||||
results.append({'file': str(file), 'data': data})
|
||||
return results
|
||||
```
|
||||
|
||||
### 数据转换
|
||||
|
||||
```python
|
||||
def transform_data(data: dict) -> dict:
|
||||
return {
|
||||
'id': data.get('id'),
|
||||
'name': data.get('name', '').strip(),
|
||||
'timestamp': datetime.now().isoformat()
|
||||
}
|
||||
```
|
||||
|
||||
### 调用外部命令
|
||||
|
||||
```python
|
||||
import subprocess
|
||||
|
||||
def run_command(cmd: list) -> str:
|
||||
result = subprocess.run(cmd, capture_output=True, text=True)
|
||||
if result.returncode != 0:
|
||||
raise RuntimeError(result.stderr)
|
||||
return result.stdout
|
||||
```
|
||||
|
||||
## 生成函数
|
||||
|
||||
```javascript
|
||||
function generatePythonScript(scriptConfig) {
|
||||
return `#!/usr/bin/env python3
|
||||
"""
|
||||
${scriptConfig.description}
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='${scriptConfig.description}')
|
||||
${scriptConfig.inputs.map(i =>
|
||||
` parser.add_argument('--${i.name}', type=${i.type || 'str'}, ${i.required ? 'required=True' : `default='${i.default}'`},
|
||||
help='${i.description}')`
|
||||
).join('\n')}
|
||||
args = parser.parse_args()
|
||||
|
||||
# TODO: 实现处理逻辑
|
||||
result = {
|
||||
${scriptConfig.outputs.map(o =>
|
||||
` '${o.name}': None # ${o.description}`
|
||||
).join(',\n')}
|
||||
}
|
||||
|
||||
print(json.dumps(result))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
`;
|
||||
}
|
||||
```
|
||||
368
.claude/skills/skill-generator/templates/script-template.md
Normal file
368
.claude/skills/skill-generator/templates/script-template.md
Normal file
@@ -0,0 +1,368 @@
|
||||
# Script Template
|
||||
|
||||
统一的脚本模板,覆盖 Bash 和 Python 两种运行时。
|
||||
|
||||
## Usage Context
|
||||
|
||||
| Phase | Usage |
|
||||
|-------|-------|
|
||||
| Optional | Phase/Action 中声明 `## Scripts` 时使用 |
|
||||
| Execution | 通过 `ExecuteScript('script-id', params)` 调用 |
|
||||
| Output Location | `.claude/skills/{skill-name}/scripts/{script-id}.{ext}` |
|
||||
|
||||
---
|
||||
|
||||
## 调用接口规范
|
||||
|
||||
所有脚本共享相同的调用约定:
|
||||
|
||||
```
|
||||
调用者
|
||||
↓ ExecuteScript('script-id', { key: value })
|
||||
↓
|
||||
脚本入口
|
||||
├─ 参数解析 (--key value)
|
||||
├─ 输入验证 (必需参数检查, 文件存在)
|
||||
├─ 核心处理 (数据读取 → 转换 → 写入)
|
||||
└─ 输出结果 (最后一行: 单行 JSON → stdout)
|
||||
├─ 成功: {"status":"success", "output_file":"...", ...}
|
||||
└─ 失败: stderr 输出错误信息, exit 1
|
||||
```
|
||||
|
||||
### 返回格式
|
||||
|
||||
```typescript
|
||||
interface ScriptResult {
|
||||
success: boolean; // exit code === 0
|
||||
stdout: string; // 标准输出
|
||||
stderr: string; // 标准错误
|
||||
outputs: object; // 从 stdout 最后一行解析的 JSON
|
||||
}
|
||||
```
|
||||
|
||||
### 参数约定
|
||||
|
||||
| 参数 | 必需 | 说明 |
|
||||
|------|------|------|
|
||||
| `--input-path` | ✓ | 输入文件路径 |
|
||||
| `--output-dir` | ✓ | 输出目录(由调用方指定) |
|
||||
| 其他 | 按需 | 脚本特定参数 |
|
||||
|
||||
---
|
||||
|
||||
## Bash 实现
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# {{script_description}}
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# ============================================================
|
||||
# 参数解析
|
||||
# ============================================================
|
||||
|
||||
INPUT_PATH=""
|
||||
OUTPUT_DIR=""
|
||||
|
||||
while [[ "$#" -gt 0 ]]; do
|
||||
case $1 in
|
||||
--input-path) INPUT_PATH="$2"; shift ;;
|
||||
--output-dir) OUTPUT_DIR="$2"; shift ;;
|
||||
--help)
|
||||
echo "用法: $0 --input-path <path> --output-dir <dir>"
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "错误: 未知参数 $1" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
# ============================================================
|
||||
# 参数验证
|
||||
# ============================================================
|
||||
|
||||
[[ -z "$INPUT_PATH" ]] && { echo "错误: --input-path 是必需参数" >&2; exit 1; }
|
||||
[[ -z "$OUTPUT_DIR" ]] && { echo "错误: --output-dir 是必需参数" >&2; exit 1; }
|
||||
[[ ! -f "$INPUT_PATH" ]] && { echo "错误: 输入文件不存在: $INPUT_PATH" >&2; exit 1; }
|
||||
command -v jq &> /dev/null || { echo "错误: 需要安装 jq" >&2; exit 1; }
|
||||
|
||||
mkdir -p "$OUTPUT_DIR"
|
||||
|
||||
# ============================================================
|
||||
# 核心逻辑
|
||||
# ============================================================
|
||||
|
||||
OUTPUT_FILE="$OUTPUT_DIR/result.txt"
|
||||
ITEMS_COUNT=0
|
||||
|
||||
# TODO: 实现处理逻辑
|
||||
while IFS= read -r line; do
|
||||
echo "$line" >> "$OUTPUT_FILE"
|
||||
((ITEMS_COUNT++))
|
||||
done < "$INPUT_PATH"
|
||||
|
||||
# ============================================================
|
||||
# 输出 JSON 结果(使用 jq 构建,避免转义问题)
|
||||
# ============================================================
|
||||
|
||||
jq -n \
|
||||
--arg output_file "$OUTPUT_FILE" \
|
||||
--argjson items_processed "$ITEMS_COUNT" \
|
||||
'{output_file: $output_file, items_processed: $items_processed, status: "success"}'
|
||||
```
|
||||
|
||||
### Bash 常用模式
|
||||
|
||||
```bash
|
||||
# 文件遍历
|
||||
for file in "$INPUT_DIR"/*.json; do
|
||||
[[ -f "$file" ]] || continue
|
||||
# 处理逻辑...
|
||||
done
|
||||
|
||||
# 临时文件 (自动清理)
|
||||
TEMP_FILE=$(mktemp)
|
||||
trap "rm -f $TEMP_FILE" EXIT
|
||||
|
||||
# 工具依赖检查
|
||||
require_command() {
|
||||
command -v "$1" &> /dev/null || { echo "错误: 需要 $1" >&2; exit 1; }
|
||||
}
|
||||
require_command jq
|
||||
|
||||
# jq 处理
|
||||
VALUE=$(jq -r '.field' "$INPUT_PATH") # 读取字段
|
||||
jq '.field = "new"' input.json > output.json # 修改字段
|
||||
jq -s 'add' file1.json file2.json > merged.json # 合并文件
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Python 实现
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
{{script_description}}
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='{{script_description}}')
|
||||
parser.add_argument('--input-path', type=str, required=True, help='输入文件路径')
|
||||
parser.add_argument('--output-dir', type=str, required=True, help='输出目录')
|
||||
args = parser.parse_args()
|
||||
|
||||
# 验证输入
|
||||
input_path = Path(args.input_path)
|
||||
if not input_path.exists():
|
||||
print(f"错误: 输入文件不存在: {input_path}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
output_dir = Path(args.output_dir)
|
||||
output_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# 执行处理
|
||||
try:
|
||||
result = process(input_path, output_dir)
|
||||
except Exception as e:
|
||||
print(f"错误: {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
# 输出 JSON 结果
|
||||
print(json.dumps(result))
|
||||
|
||||
|
||||
def process(input_path: Path, output_dir: Path) -> dict:
|
||||
"""核心处理逻辑"""
|
||||
# TODO: 实现处理逻辑
|
||||
|
||||
output_file = output_dir / 'result.json'
|
||||
|
||||
with open(input_path, 'r', encoding='utf-8') as f:
|
||||
data = json.load(f)
|
||||
|
||||
processed_count = len(data) if isinstance(data, list) else 1
|
||||
|
||||
with open(output_file, 'w', encoding='utf-8') as f:
|
||||
json.dump(data, f, indent=2, ensure_ascii=False)
|
||||
|
||||
return {
|
||||
'output_file': str(output_file),
|
||||
'items_processed': processed_count,
|
||||
'status': 'success'
|
||||
}
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
```
|
||||
|
||||
### Python 常用模式
|
||||
|
||||
```python
|
||||
# 文件遍历
|
||||
def process_files(input_dir: Path, pattern: str = '*.json') -> list:
|
||||
return [
|
||||
{'file': str(f), 'data': json.load(f.open())}
|
||||
for f in input_dir.glob(pattern)
|
||||
]
|
||||
|
||||
# 数据转换
|
||||
def transform(data: dict) -> dict:
|
||||
return {
|
||||
'id': data.get('id'),
|
||||
'name': data.get('name', '').strip(),
|
||||
'timestamp': datetime.now().isoformat()
|
||||
}
|
||||
|
||||
# 外部命令调用
|
||||
import subprocess
|
||||
|
||||
def run_command(cmd: list) -> str:
|
||||
result = subprocess.run(cmd, capture_output=True, text=True)
|
||||
if result.returncode != 0:
|
||||
raise RuntimeError(result.stderr)
|
||||
return result.stdout
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 运行时选择指南
|
||||
|
||||
```
|
||||
任务特征
|
||||
↓
|
||||
├─ 文件处理 / 系统命令 / 管道操作
|
||||
│ └─ 选 Bash (.sh)
|
||||
│
|
||||
├─ JSON 数据处理 / 复杂转换 / 数据分析
|
||||
│ └─ 选 Python (.py)
|
||||
│
|
||||
└─ 简单读写 / 格式转换
|
||||
└─ 任选(Bash 更轻量)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 生成函数
|
||||
|
||||
```javascript
|
||||
function generateScript(scriptConfig) {
|
||||
const runtime = scriptConfig.runtime || 'bash'; // 'bash' | 'python'
|
||||
const ext = runtime === 'python' ? '.py' : '.sh';
|
||||
|
||||
if (runtime === 'python') {
|
||||
return generatePythonScript(scriptConfig);
|
||||
}
|
||||
return generateBashScript(scriptConfig);
|
||||
}
|
||||
|
||||
function generateBashScript(scriptConfig) {
|
||||
const { description, inputs = [], outputs = [] } = scriptConfig;
|
||||
|
||||
const paramDefs = inputs.map(i =>
|
||||
`${i.name.toUpperCase().replace(/-/g, '_')}="${i.default || ''}"`
|
||||
).join('\n');
|
||||
|
||||
const paramParse = inputs.map(i =>
|
||||
` --${i.name}) ${i.name.toUpperCase().replace(/-/g, '_')}="$2"; shift ;;`
|
||||
).join('\n');
|
||||
|
||||
const paramValidation = inputs.filter(i => i.required).map(i => {
|
||||
const VAR = i.name.toUpperCase().replace(/-/g, '_');
|
||||
return `[[ -z "$${VAR}" ]] && { echo "错误: --${i.name} 是必需参数" >&2; exit 1; }`;
|
||||
}).join('\n');
|
||||
|
||||
return `#!/bin/bash
|
||||
# ${description}
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
${paramDefs}
|
||||
|
||||
while [[ "$#" -gt 0 ]]; do
|
||||
case $1 in
|
||||
${paramParse}
|
||||
*) echo "未知参数: $1" >&2; exit 1 ;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
${paramValidation}
|
||||
|
||||
# TODO: 实现处理逻辑
|
||||
|
||||
# 输出结果 (jq 构建)
|
||||
jq -n ${outputs.map(o =>
|
||||
`--arg ${o.name} "$${o.name.toUpperCase().replace(/-/g, '_')}"`
|
||||
).join(' \\\n ')} \
|
||||
'{${outputs.map(o => `${o.name}: $${o.name}`).join(', ')}}'
|
||||
`;
|
||||
}
|
||||
|
||||
function generatePythonScript(scriptConfig) {
|
||||
const { description, inputs = [], outputs = [] } = scriptConfig;
|
||||
|
||||
const argDefs = inputs.map(i =>
|
||||
` parser.add_argument('--${i.name}', type=${i.type || 'str'}, ${
|
||||
i.required ? 'required=True' : `default='${i.default || ''}'`
|
||||
}, help='${i.description || i.name}')`
|
||||
).join('\n');
|
||||
|
||||
const resultFields = outputs.map(o =>
|
||||
` '${o.name}': None # ${o.description || o.name}`
|
||||
).join(',\n');
|
||||
|
||||
return `#!/usr/bin/env python3
|
||||
"""
|
||||
${description}
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='${description}')
|
||||
${argDefs}
|
||||
args = parser.parse_args()
|
||||
|
||||
# TODO: 实现处理逻辑
|
||||
result = {
|
||||
${resultFields}
|
||||
}
|
||||
|
||||
print(json.dumps(result))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
`;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 目录约定
|
||||
|
||||
```
|
||||
scripts/
|
||||
├── process-data.py # id: process-data, runtime: python
|
||||
├── validate.sh # id: validate, runtime: bash
|
||||
└── transform.js # id: transform, runtime: node
|
||||
```
|
||||
|
||||
- **命名即 ID**: 文件名(不含扩展名)= 脚本 ID
|
||||
- **扩展名即运行时**: `.py` → python, `.sh` → bash, `.js` → node
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,14 @@
|
||||
# Codex Code Guidelines
|
||||
|
||||
## CLI Endpoints
|
||||
|
||||
- **CLI Tools Usage**: @~/.claude/workflows/cli-tools-usage.md
|
||||
- **CLI Endpoints Config**: @~/.claude/cli-tools.json
|
||||
|
||||
**Strictly follow the cli-tools.json configuration**
|
||||
|
||||
Available CLI endpoints are dynamically defined by the config file
|
||||
|
||||
## Code Quality Standards
|
||||
|
||||
### Code Quality
|
||||
|
||||
Reference in New Issue
Block a user