feat(skills): implement enable/disable functionality for skills

- Added new API endpoints to enable and disable skills.
- Introduced logic to manage disabled skills, including loading and saving configurations.
- Enhanced skills routes to return lists of disabled skills.
- Updated frontend to display disabled skills and allow toggling their status.
- Added internationalization support for new skill status messages.
- Created JSON schemas for plan verification agent and findings.
- Defined new types for skill management in TypeScript.
This commit is contained in:
catlog22
2026-01-28 00:49:39 +08:00
parent 8d178feaac
commit 7a40f16235
35 changed files with 1123 additions and 2016 deletions

View File

@@ -1,474 +0,0 @@
# State Schema
CCW Loop 的状态结构定义(统一版本)。
## 状态文件
**位置**: `.loop/{loopId}.json` (统一位置API + Skill 共享)
**旧版本位置** (仅向后兼容): `.workflow/.loop/{session-id}/state.json`
## 结构定义
### 统一状态接口 (Unified Loop State)
```typescript
/**
* Unified Loop State - API 和 Skill 共享的状态结构
* API (loop-v2-routes.ts) 拥有状态的主控权
* Skill (ccw-loop) 读取和更新此状态
*/
interface LoopState {
// =====================================================
// API FIELDS (from loop-v2-routes.ts)
// 这些字段由 API 管理Skill 只读
// =====================================================
loop_id: string // Loop ID, e.g., "loop-v2-20260122-abc123"
title: string // Loop 标题
description: string // Loop 描述
max_iterations: number // 最大迭代次数
status: 'created' | 'running' | 'paused' | 'completed' | 'failed'
current_iteration: number // 当前迭代次数
created_at: string // 创建时间 (ISO8601)
updated_at: string // 最后更新时间 (ISO8601)
completed_at?: string // 完成时间 (ISO8601)
failure_reason?: string // 失败原因
// =====================================================
// SKILL EXTENSION FIELDS
// 这些字段由 Skill 管理API 只读
// =====================================================
skill_state?: {
// 当前执行动作
current_action: 'init' | 'develop' | 'debug' | 'validate' | 'complete' | null
last_action: string | null
completed_actions: string[]
mode: 'interactive' | 'auto'
// === 开发阶段 ===
develop: {
total: number
completed: number
current_task?: string
tasks: DevelopTask[]
last_progress_at: string | null
}
// === 调试阶段 ===
debug: {
active_bug?: string
hypotheses_count: number
hypotheses: Hypothesis[]
confirmed_hypothesis: string | null
iteration: number
last_analysis_at: string | null
}
// === 验证阶段 ===
validate: {
pass_rate: number // 测试通过率 (0-100)
coverage: number // 覆盖率 (0-100)
test_results: TestResult[]
passed: boolean
failed_tests: string[]
last_run_at: string | null
}
// === 错误追踪 ===
errors: Array<{
action: string
message: string
timestamp: string
}>
}
}
interface DevelopTask {
id: string
description: string
tool: 'gemini' | 'qwen' | 'codex' | 'bash'
mode: 'analysis' | 'write'
status: 'pending' | 'in_progress' | 'completed' | 'failed'
files_changed: string[]
created_at: string
completed_at: string | null
}
interface Hypothesis {
id: string // H1, H2, ...
description: string
testable_condition: string
logging_point: string
evidence_criteria: {
confirm: string
reject: string
}
likelihood: number // 1 = 最可能
status: 'pending' | 'confirmed' | 'rejected' | 'inconclusive'
evidence: Record<string, any> | null
verdict_reason: string | null
}
interface TestResult {
test_name: string
suite: string
status: 'passed' | 'failed' | 'skipped'
duration_ms: number
error_message: string | null
stack_trace: string | null
}
```
## 初始状态
### 由 API 创建时 (Dashboard 触发)
```json
{
"loop_id": "loop-v2-20260122-abc123",
"title": "Implement user authentication",
"description": "Add login/logout functionality",
"max_iterations": 10,
"status": "created",
"current_iteration": 0,
"created_at": "2026-01-22T10:00:00+08:00",
"updated_at": "2026-01-22T10:00:00+08:00"
}
```
### 由 Skill 初始化后 (action-init)
```json
{
"loop_id": "loop-v2-20260122-abc123",
"title": "Implement user authentication",
"description": "Add login/logout functionality",
"max_iterations": 10,
"status": "running",
"current_iteration": 0,
"created_at": "2026-01-22T10:00:00+08:00",
"updated_at": "2026-01-22T10:00:05+08:00",
"skill_state": {
"current_action": "init",
"last_action": null,
"completed_actions": [],
"mode": "auto",
"develop": {
"total": 3,
"completed": 0,
"current_task": null,
"tasks": [
{ "id": "task-001", "description": "Create auth component", "status": "pending" }
],
"last_progress_at": null
},
"debug": {
"active_bug": null,
"hypotheses_count": 0,
"hypotheses": [],
"confirmed_hypothesis": null,
"iteration": 0,
"last_analysis_at": null
},
"validate": {
"pass_rate": 0,
"coverage": 0,
"test_results": [],
"passed": false,
"failed_tests": [],
"last_run_at": null
},
"errors": []
}
}
```
## 控制信号检查 (Control Signals)
Skill 在每个 Action 开始前必须检查控制信号:
```javascript
/**
* 检查 API 控制信号
* @returns { continue: boolean, action: 'pause_exit' | 'stop_exit' | 'continue' }
*/
function checkControlSignals(loopId) {
const state = JSON.parse(Read(`.loop/${loopId}.json`))
switch (state.status) {
case 'paused':
// API 暂停了循环Skill 应退出等待 resume
return { continue: false, action: 'pause_exit' }
case 'failed':
// API 停止了循环 (用户手动停止)
return { continue: false, action: 'stop_exit' }
case 'running':
// 正常继续
return { continue: true, action: 'continue' }
default:
// 异常状态
return { continue: false, action: 'stop_exit' }
}
}
```
### 在 Action 中使用
```markdown
## Execution
### Step 1: Check Control Signals
\`\`\`javascript
const control = checkControlSignals(loopId)
if (!control.continue) {
// 输出退出原因
console.log(`Loop ${control.action}: status = ${state.status}`)
// 如果是 pause_exit保存当前进度
if (control.action === 'pause_exit') {
updateSkillState(loopId, { current_action: 'paused' })
}
return // 退出 Action
}
\`\`\`
### Step 2: Execute Action Logic
...
```
## 状态转换规则
### 1. 初始化 (action-init)
```javascript
// Skill 初始化后
{
// API 字段更新
status: 'created' 'running', // 或保持 'running' 如果 API 已设置
updated_at: timestamp,
// Skill 字段初始化
skill_state: {
current_action: 'init',
mode: 'auto',
develop: {
tasks: [...parsed_tasks],
total: N,
completed: 0
}
}
}
```
### 2. 开发进行中 (action-develop-with-file)
```javascript
// 开发任务执行后
{
updated_at: timestamp,
current_iteration: state.current_iteration + 1,
skill_state: {
current_action: 'develop',
last_action: 'action-develop-with-file',
completed_actions: [...state.skill_state.completed_actions, 'action-develop-with-file'],
develop: {
current_task: 'task-xxx',
completed: N+1,
last_progress_at: timestamp
}
}
}
```
### 3. 调试进行中 (action-debug-with-file)
```javascript
// 调试执行后
{
updated_at: timestamp,
current_iteration: state.current_iteration + 1,
skill_state: {
current_action: 'debug',
last_action: 'action-debug-with-file',
debug: {
active_bug: '...',
hypotheses_count: N,
hypotheses: [...new_hypotheses],
iteration: N+1,
last_analysis_at: timestamp
}
}
}
```
### 4. 验证完成 (action-validate-with-file)
```javascript
// 验证执行后
{
updated_at: timestamp,
current_iteration: state.current_iteration + 1,
skill_state: {
current_action: 'validate',
last_action: 'action-validate-with-file',
validate: {
test_results: [...results],
pass_rate: 95.5,
coverage: 85.0,
passed: true | false,
failed_tests: ['test1', 'test2'],
last_run_at: timestamp
}
}
}
```
### 5. 完成 (action-complete)
```javascript
// 循环完成后
{
status: 'running' 'completed',
completed_at: timestamp,
updated_at: timestamp,
skill_state: {
current_action: 'complete',
last_action: 'action-complete'
}
}
```
## 状态派生字段
以下字段可从状态计算得出,不需要存储:
```javascript
// 开发完成度
const developProgress = state.develop.total_count > 0
? (state.develop.completed_count / state.develop.total_count) * 100
: 0
// 是否有待开发任务
const hasPendingDevelop = state.develop.tasks.some(t => t.status === 'pending')
// 调试是否完成
const debugCompleted = state.debug.confirmed_hypothesis !== null
// 验证是否通过
const validationPassed = state.validate.passed && state.validate.test_results.length > 0
// 整体进度
const overallProgress = (
(developProgress * 0.5) +
(debugCompleted ? 25 : 0) +
(validationPassed ? 25 : 0)
)
```
## 文件同步
### 统一位置 (Unified Location)
状态与文件的对应关系:
| 状态字段 | 同步文件 | 同步时机 |
|----------|----------|----------|
| 整个 LoopState | `.loop/{loopId}.json` | 每次状态变更 (主文件) |
| `skill_state.develop` | `.loop/{loopId}.progress/develop.md` | 每次开发操作后 |
| `skill_state.debug` | `.loop/{loopId}.progress/debug.md` | 每次调试操作后 |
| `skill_state.validate` | `.loop/{loopId}.progress/validate.md` | 每次验证操作后 |
| 代码变更日志 | `.loop/{loopId}.progress/changes.log` | 每次文件修改 (NDJSON) |
| 调试日志 | `.loop/{loopId}.progress/debug.log` | 每次调试日志 (NDJSON) |
### 文件结构示例
```
.loop/
├── loop-v2-20260122-abc123.json # 主状态文件 (API + Skill)
├── loop-v2-20260122-abc123.tasks.jsonl # 任务列表 (API 管理)
└── loop-v2-20260122-abc123.progress/ # Skill 进度文件
├── develop.md # 开发进度
├── debug.md # 调试理解
├── validate.md # 验证报告
├── changes.log # 代码变更 (NDJSON)
└── debug.log # 调试日志 (NDJSON)
```
## 状态恢复
如果主状态文件 `.loop/{loopId}.json` 损坏,可以从进度文件重建 skill_state:
```javascript
function rebuildSkillStateFromProgress(loopId) {
const progressDir = `.loop/${loopId}.progress`
// 尝试从进度文件解析状态
const skill_state = {
develop: parseProgressFile(`${progressDir}/develop.md`),
debug: parseProgressFile(`${progressDir}/debug.md`),
validate: parseProgressFile(`${progressDir}/validate.md`)
}
return skill_state
}
// 解析进度 Markdown 文件
function parseProgressFile(filePath) {
const content = Read(filePath)
if (!content) return null
// 从 Markdown 表格和结构中提取数据
// ... implementation
}
```
### 恢复策略
1. **API 字段**: 无法恢复 - 需要从 API 重新获取或用户手动输入
2. **skill_state 字段**: 可以从 `.progress/` 目录的 Markdown 文件解析
3. **任务列表**: 从 `.loop/{loopId}.tasks.jsonl` 恢复
## 状态验证
```javascript
function validateState(state) {
const errors = []
// 必需字段
if (!state.session_id) errors.push('Missing session_id')
if (!state.task_description) errors.push('Missing task_description')
// 状态一致性
if (state.initialized && state.status === 'pending') {
errors.push('Inconsistent: initialized but status is pending')
}
if (state.status === 'completed' && !state.validate.passed) {
errors.push('Inconsistent: completed but validation not passed')
}
// 开发任务一致性
const completedTasks = state.develop.tasks.filter(t => t.status === 'completed').length
if (completedTasks !== state.develop.completed_count) {
errors.push('Inconsistent: completed_count mismatch')
}
return { valid: errors.length === 0, errors }
}
```