mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-03-11 17:21:03 +08:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
efbbaff834 | ||
|
|
1ada08f073 | ||
|
|
65ff5f54cb | ||
|
|
c50d9b21dc | ||
|
|
38d1987f41 | ||
|
|
d29dabf0a9 | ||
|
|
2d723644ea | ||
|
|
9fb13ed6b0 | ||
|
|
b4ad8c7b80 |
@@ -332,21 +332,22 @@ CONSTRAINTS: Focus on ${dimensions.join(', ')}
|
||||
|
||||
5. **Interactive Recommendation Review** (skip in auto mode):
|
||||
|
||||
Walk through each recommendation one-by-one for user confirmation:
|
||||
Present all recommendations, then batch-confirm via **single AskUserQuestion call** (up to 4 questions):
|
||||
|
||||
```
|
||||
For each recommendation (ordered by priority high→medium→low):
|
||||
1. Present: action, rationale, priority, steps[] (numbered sub-steps)
|
||||
2. AskUserQuestion (single-select, header: "建议#N"):
|
||||
1. Display all recommendations with numbering (action, rationale, priority, steps[])
|
||||
2. Single AskUserQuestion call — one question per recommendation (max 4, ordered by priority high→medium→low):
|
||||
Each question (single-select, header: "建议#N"):
|
||||
- **确认** (label: "确认", desc: "Accept as-is") → review_status = "accepted"
|
||||
- **修改** (label: "修改", desc: "Adjust scope/steps") → record modification → review_status = "modified"
|
||||
- **删除** (label: "删除", desc: "Not needed") → record reason → review_status = "rejected"
|
||||
- **跳过审议** (label: "跳过审议", desc: "Accept all remaining") → break loop
|
||||
3. Record review decision to discussion.md Decision Log
|
||||
4. Update conclusions.json recommendation.review_status
|
||||
- **修改** (label: "修改", desc: "Adjust scope/steps") → review_status = "modified"
|
||||
- **删除** (label: "删除", desc: "Not needed") → review_status = "rejected"
|
||||
3. If >4 recommendations: batch in groups of 4 with additional AskUserQuestion calls
|
||||
4. For "修改" selections: follow up to capture modification details
|
||||
5. Record all review decisions to discussion.md Decision Log
|
||||
6. Update conclusions.json recommendation.review_status for each
|
||||
```
|
||||
|
||||
**After review loop**: Display summary of reviewed recommendations:
|
||||
**After review**: Display summary of reviewed recommendations:
|
||||
- Accepted: N items | Modified: N items | Rejected: N items
|
||||
- Only accepted/modified recommendations proceed to next step
|
||||
|
||||
|
||||
@@ -289,7 +289,7 @@
|
||||
},
|
||||
{
|
||||
"name": "init-guidelines",
|
||||
"command": "/workflow:init-guidelines",
|
||||
"command": "/workflow:spec:setup -guidelines",
|
||||
"description": "Interactive wizard to fill specs/*.md based on project analysis",
|
||||
"arguments": "[--reset]",
|
||||
"category": "workflow",
|
||||
@@ -300,7 +300,7 @@
|
||||
},
|
||||
{
|
||||
"name": "init-specs",
|
||||
"command": "/workflow:init-specs",
|
||||
"command": "/workflow:spec:setup -specs",
|
||||
"description": "Interactive wizard to create individual specs or personal constraints with scope selection",
|
||||
"arguments": "[--scope <global|project>] [--dimension <specs|personal>] [--category <general|exploration|planning|execution>]",
|
||||
"category": "workflow",
|
||||
@@ -311,7 +311,7 @@
|
||||
},
|
||||
{
|
||||
"name": "init",
|
||||
"command": "/workflow:init",
|
||||
"command": "/workflow:spec:setup ",
|
||||
"description": "Initialize project-level state with intelligent project analysis using cli-explore-agent",
|
||||
"arguments": "[--regenerate] [--skip-specs]",
|
||||
"category": "workflow",
|
||||
|
||||
@@ -276,7 +276,7 @@
|
||||
},
|
||||
{
|
||||
"name": "init-guidelines",
|
||||
"command": "/workflow:init-guidelines",
|
||||
"command": "/workflow:spec:setup -guidelines",
|
||||
"description": "Interactive wizard to fill specs/*.md based on project analysis",
|
||||
"arguments": "[--reset]",
|
||||
"category": "workflow",
|
||||
@@ -287,7 +287,7 @@
|
||||
},
|
||||
{
|
||||
"name": "init-specs",
|
||||
"command": "/workflow:init-specs",
|
||||
"command": "/workflow:spec:setup -specs",
|
||||
"description": "Interactive wizard to create individual specs or personal constraints with scope selection",
|
||||
"arguments": "[--scope <global|project>] [--dimension <specs|personal>] [--category <general|exploration|planning|execution>]",
|
||||
"category": "workflow",
|
||||
@@ -298,7 +298,7 @@
|
||||
},
|
||||
{
|
||||
"name": "init",
|
||||
"command": "/workflow:init",
|
||||
"command": "/workflow:spec:setup ",
|
||||
"description": "Initialize project-level state with intelligent project analysis using cli-explore-agent",
|
||||
"arguments": "[--regenerate] [--skip-specs]",
|
||||
"category": "workflow",
|
||||
|
||||
@@ -298,7 +298,7 @@
|
||||
},
|
||||
{
|
||||
"name": "init-guidelines",
|
||||
"command": "/workflow:init-guidelines",
|
||||
"command": "/workflow:spec:setup -guidelines",
|
||||
"description": "Interactive wizard to fill specs/*.md based on project analysis",
|
||||
"arguments": "[--reset]",
|
||||
"category": "workflow",
|
||||
@@ -309,7 +309,7 @@
|
||||
},
|
||||
{
|
||||
"name": "init-specs",
|
||||
"command": "/workflow:init-specs",
|
||||
"command": "/workflow:spec:setup -specs",
|
||||
"description": "Interactive wizard to create individual specs or personal constraints with scope selection",
|
||||
"arguments": "[--scope <global|project>] [--dimension <specs|personal>] [--category <general|exploration|planning|execution>]",
|
||||
"category": "workflow",
|
||||
@@ -320,7 +320,7 @@
|
||||
},
|
||||
{
|
||||
"name": "init",
|
||||
"command": "/workflow:init",
|
||||
"command": "/workflow:spec:setup ",
|
||||
"description": "Initialize project-level state with intelligent project analysis using cli-explore-agent",
|
||||
"arguments": "[--regenerate] [--skip-specs]",
|
||||
"category": "workflow",
|
||||
|
||||
@@ -145,7 +145,7 @@
|
||||
},
|
||||
{
|
||||
"name": "init-guidelines",
|
||||
"command": "/workflow:init-guidelines",
|
||||
"command": "/workflow:spec:setup -guidelines",
|
||||
"description": "Interactive wizard to fill specs/*.md based on project analysis",
|
||||
"arguments": "[--reset]",
|
||||
"category": "workflow",
|
||||
@@ -156,7 +156,7 @@
|
||||
},
|
||||
{
|
||||
"name": "init-specs",
|
||||
"command": "/workflow:init-specs",
|
||||
"command": "/workflow:spec:setup -specs",
|
||||
"description": "Interactive wizard to create individual specs or personal constraints with scope selection",
|
||||
"arguments": "[--scope <global|project>] [--dimension <specs|personal>] [--category <general|exploration|planning|execution>]",
|
||||
"category": "workflow",
|
||||
@@ -167,7 +167,7 @@
|
||||
},
|
||||
{
|
||||
"name": "init",
|
||||
"command": "/workflow:init",
|
||||
"command": "/workflow:spec:setup ",
|
||||
"description": "Initialize project-level state with intelligent project analysis using cli-explore-agent",
|
||||
"arguments": "[--regenerate] [--skip-specs]",
|
||||
"category": "workflow",
|
||||
|
||||
382
.claude/skills/skill-iter-tune/SKILL.md
Normal file
382
.claude/skills/skill-iter-tune/SKILL.md
Normal file
@@ -0,0 +1,382 @@
|
||||
---
|
||||
name: skill-iter-tune
|
||||
description: Iterative skill tuning via execute-evaluate-improve feedback loop. Uses ccw cli Claude to execute skill, Gemini to evaluate quality, and Agent to apply improvements. Iterates until quality threshold or max iterations. Triggers on "skill iter tune", "iterative skill tuning", "tune skill".
|
||||
allowed-tools: Skill, Agent, AskUserQuestion, TaskCreate, TaskUpdate, TaskList, Read, Write, Edit, Bash, Glob, Grep
|
||||
---
|
||||
|
||||
# Skill Iter Tune
|
||||
|
||||
Iterative skill refinement through execute-evaluate-improve feedback loops. Each iteration runs the skill via Claude, evaluates output via Gemini, and applies improvements via Agent.
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────────────────┐
|
||||
│ Skill Iter Tune Orchestrator (SKILL.md) │
|
||||
│ → Parse input → Setup workspace → Iteration Loop → Final Report │
|
||||
└────────────────────────────┬─────────────────────────────────────────────┘
|
||||
│
|
||||
┌───────────────────┼───────────────────────────────────┐
|
||||
↓ ↓ ↓
|
||||
┌──────────┐ ┌─────────────────────────────┐ ┌──────────┐
|
||||
│ Phase 1 │ │ Iteration Loop (2→3→4) │ │ Phase 5 │
|
||||
│ Setup │ │ ┌─────┐ ┌─────┐ ┌─────┐ │ │ Report │
|
||||
│ │─────→│ │ P2 │→ │ P3 │→ │ P4 │ │────→│ │
|
||||
│ Backup + │ │ │Exec │ │Eval │ │Impr │ │ │ History │
|
||||
│ Init │ │ └─────┘ └─────┘ └─────┘ │ │ Summary │
|
||||
└──────────┘ │ ↑ │ │ └──────────┘
|
||||
│ └───────────────┘ │
|
||||
│ (if score < threshold │
|
||||
│ AND iter < max) │
|
||||
└─────────────────────────────┘
|
||||
```
|
||||
|
||||
### Chain Mode Extension
|
||||
|
||||
```
|
||||
Chain Mode (execution_mode === "chain"):
|
||||
|
||||
Phase 2 runs per-skill in chain_order:
|
||||
Skill A → ccw cli → artifacts/skill-A/
|
||||
↓ (artifacts as input)
|
||||
Skill B → ccw cli → artifacts/skill-B/
|
||||
↓ (artifacts as input)
|
||||
Skill C → ccw cli → artifacts/skill-C/
|
||||
|
||||
Phase 3 evaluates entire chain output + per-skill scores
|
||||
Phase 4 improves weakest skill(s) in chain
|
||||
```
|
||||
|
||||
## Key Design Principles
|
||||
|
||||
1. **Iteration Loop**: Phases 2-3-4 repeat until quality threshold, max iterations, or convergence
|
||||
2. **Two-Tool Pipeline**: Claude (write/execute) + Gemini (analyze/evaluate) = complementary perspectives
|
||||
3. **Pure Orchestrator**: SKILL.md coordinates only — execution detail lives in phase files
|
||||
4. **Progressive Phase Loading**: Phase docs read only when that phase executes
|
||||
5. **Skill Versioning**: Each iteration snapshots skill state before execution
|
||||
6. **Convergence Detection**: Stop early if score stalls (no improvement in 2 consecutive iterations)
|
||||
|
||||
## Interactive Preference Collection
|
||||
|
||||
```javascript
|
||||
// ★ Auto mode detection
|
||||
const autoYes = /\b(-y|--yes)\b/.test($ARGUMENTS)
|
||||
|
||||
if (autoYes) {
|
||||
workflowPreferences = {
|
||||
autoYes: true,
|
||||
maxIterations: 5,
|
||||
qualityThreshold: 80,
|
||||
executionMode: 'single'
|
||||
}
|
||||
} else {
|
||||
const prefResponse = AskUserQuestion({
|
||||
questions: [
|
||||
{
|
||||
question: "选择迭代调优配置:",
|
||||
header: "Tune Config",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{ label: "Quick (3 iter, 70)", description: "快速迭代,适合小幅改进" },
|
||||
{ label: "Standard (5 iter, 80) (Recommended)", description: "平衡方案,适合多数场景" },
|
||||
{ label: "Thorough (8 iter, 90)", description: "深度优化,适合生产级 skill" }
|
||||
]
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
const configMap = {
|
||||
"Quick": { maxIterations: 3, qualityThreshold: 70 },
|
||||
"Standard": { maxIterations: 5, qualityThreshold: 80 },
|
||||
"Thorough": { maxIterations: 8, qualityThreshold: 90 }
|
||||
}
|
||||
const selected = Object.keys(configMap).find(k =>
|
||||
prefResponse["Tune Config"].startsWith(k)
|
||||
) || "Standard"
|
||||
workflowPreferences = { autoYes: false, ...configMap[selected] }
|
||||
|
||||
// ★ Mode selection: chain vs single
|
||||
const modeResponse = AskUserQuestion({
|
||||
questions: [{
|
||||
question: "选择调优模式:",
|
||||
header: "Tune Mode",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{ label: "Single Skill (Recommended)", description: "独立调优每个 skill,适合单一 skill 优化" },
|
||||
{ label: "Skill Chain", description: "按链序执行,前一个 skill 的产出作为后一个的输入" }
|
||||
]
|
||||
}]
|
||||
});
|
||||
workflowPreferences.executionMode = modeResponse["Tune Mode"].startsWith("Skill Chain")
|
||||
? "chain" : "single";
|
||||
}
|
||||
```
|
||||
|
||||
## Input Processing
|
||||
|
||||
```
|
||||
$ARGUMENTS → Parse:
|
||||
├─ Skill path(s): first arg, comma-separated for multiple
|
||||
│ e.g., ".claude/skills/my-skill" or "my-skill" (auto-prefixed)
|
||||
│ Chain mode: order preserved as chain_order
|
||||
├─ Test scenario: --scenario "description" or remaining text
|
||||
└─ Flags: --max-iterations=N, --threshold=N, -y/--yes
|
||||
```
|
||||
|
||||
## Execution Flow
|
||||
|
||||
> **⚠️ COMPACT DIRECTIVE**: Context compression MUST check TodoWrite phase status.
|
||||
> The phase currently marked `in_progress` is the active execution phase — preserve its FULL content.
|
||||
> Only compress phases marked `completed` or `pending`.
|
||||
|
||||
### Phase 1: Setup (one-time)
|
||||
|
||||
Read and execute: `Ref: phases/01-setup.md`
|
||||
|
||||
- Parse skill paths, validate existence
|
||||
- Create workspace at `.workflow/.scratchpad/skill-iter-tune-{ts}/`
|
||||
- Backup original skill files
|
||||
- Initialize iteration-state.json
|
||||
|
||||
Output: `workDir`, `targetSkills[]`, `testScenario`, initialized state
|
||||
|
||||
### Iteration Loop
|
||||
|
||||
```javascript
|
||||
// Orchestrator iteration loop
|
||||
while (true) {
|
||||
// Increment iteration
|
||||
state.current_iteration++;
|
||||
state.iterations.push({
|
||||
round: state.current_iteration,
|
||||
status: 'pending',
|
||||
execution: null,
|
||||
evaluation: null,
|
||||
improvement: null
|
||||
});
|
||||
|
||||
// Update TodoWrite
|
||||
TaskUpdate(iterationTask, {
|
||||
subject: `Iteration ${state.current_iteration}/${state.max_iterations}`,
|
||||
status: 'in_progress',
|
||||
activeForm: `Running iteration ${state.current_iteration}`
|
||||
});
|
||||
|
||||
// === Phase 2: Execute ===
|
||||
// Read: phases/02-execute.md
|
||||
// Single mode: one ccw cli call for all skills
|
||||
// Chain mode: sequential ccw cli per skill in chain_order, passing artifacts
|
||||
// Snapshot skill → construct prompt → ccw cli --tool claude --mode write
|
||||
// Collect artifacts
|
||||
|
||||
// === Phase 3: Evaluate ===
|
||||
// Read: phases/03-evaluate.md
|
||||
// Construct eval prompt → ccw cli --tool gemini --mode analysis
|
||||
// Parse score → write iteration-N-eval.md → check termination
|
||||
|
||||
// Check termination
|
||||
if (shouldTerminate(state)) {
|
||||
break; // → Phase 5
|
||||
}
|
||||
|
||||
// === Phase 4: Improve ===
|
||||
// Read: phases/04-improve.md
|
||||
// Agent applies suggestions → write iteration-N-changes.md
|
||||
|
||||
// Update TodoWrite with score
|
||||
// Continue loop
|
||||
}
|
||||
```
|
||||
|
||||
### Phase 2: Execute Skill (per iteration)
|
||||
|
||||
Read and execute: `Ref: phases/02-execute.md`
|
||||
|
||||
- Snapshot skill → `iteration-{N}/skill-snapshot/`
|
||||
- Build execution prompt from skill content + test scenario
|
||||
- Execute: `ccw cli -p "..." --tool claude --mode write --cd "${iterDir}/artifacts"`
|
||||
- Collect artifacts
|
||||
|
||||
### Phase 3: Evaluate Quality (per iteration)
|
||||
|
||||
Read and execute: `Ref: phases/03-evaluate.md`
|
||||
|
||||
- Build evaluation prompt with skill + artifacts + criteria + history
|
||||
- Execute: `ccw cli -p "..." --tool gemini --mode analysis`
|
||||
- Parse 5-dimension score (Clarity, Completeness, Correctness, Effectiveness, Efficiency)
|
||||
- Write `iteration-{N}-eval.md`
|
||||
- Check termination: score >= threshold | iter >= max | convergence | error limit
|
||||
|
||||
### Phase 4: Apply Improvements (per iteration, skipped on termination)
|
||||
|
||||
Read and execute: `Ref: phases/04-improve.md`
|
||||
|
||||
- Read evaluation suggestions
|
||||
- Launch general-purpose Agent to apply changes
|
||||
- Write `iteration-{N}-changes.md`
|
||||
- Update state
|
||||
|
||||
### Phase 5: Final Report (one-time)
|
||||
|
||||
Read and execute: `Ref: phases/05-report.md`
|
||||
|
||||
- Generate comprehensive report with score progression table
|
||||
- Write `final-report.md`
|
||||
- Display summary to user
|
||||
|
||||
**Phase Reference Documents** (read on-demand when phase executes):
|
||||
|
||||
| Phase | Document | Purpose | Compact |
|
||||
|-------|----------|---------|---------|
|
||||
| 1 | [phases/01-setup.md](phases/01-setup.md) | Initialize workspace and state | TodoWrite 驱动 |
|
||||
| 2 | [phases/02-execute.md](phases/02-execute.md) | Execute skill via ccw cli Claude | TodoWrite 驱动 + 🔄 sentinel |
|
||||
| 3 | [phases/03-evaluate.md](phases/03-evaluate.md) | Evaluate via ccw cli Gemini | TodoWrite 驱动 + 🔄 sentinel |
|
||||
| 4 | [phases/04-improve.md](phases/04-improve.md) | Apply improvements via Agent | TodoWrite 驱动 + 🔄 sentinel |
|
||||
| 5 | [phases/05-report.md](phases/05-report.md) | Generate final report | TodoWrite 驱动 |
|
||||
|
||||
**Compact Rules**:
|
||||
1. **TodoWrite `in_progress`** → 保留完整内容,禁止压缩
|
||||
2. **TodoWrite `completed`** → 可压缩为摘要
|
||||
3. **🔄 sentinel fallback** → 若 compact 后仅存 sentinel 而无完整 Step 协议,立即 `Read()` 恢复
|
||||
|
||||
## Core Rules
|
||||
|
||||
1. **Start Immediately**: First action is preference collection → Phase 1 setup
|
||||
2. **Progressive Loading**: Read phase doc ONLY when that phase is about to execute
|
||||
3. **Snapshot Before Execute**: Always snapshot skill state before each iteration
|
||||
4. **Background CLI**: ccw cli runs in background, wait for hook callback before proceeding
|
||||
5. **Parse Every Output**: Extract structured JSON from CLI outputs for state updates
|
||||
6. **DO NOT STOP**: Continuous iteration until termination condition met
|
||||
7. **Single State Source**: `iteration-state.json` is the only source of truth
|
||||
|
||||
## Data Flow
|
||||
|
||||
```
|
||||
User Input (skill paths + test scenario)
|
||||
↓ (+ execution_mode + chain_order if chain mode)
|
||||
↓
|
||||
Phase 1: Setup
|
||||
↓ workDir, targetSkills[], testScenario, iteration-state.json
|
||||
↓
|
||||
┌─→ Phase 2: Execute (ccw cli claude)
|
||||
│ ↓ artifacts/ (skill execution output)
|
||||
│ ↓
|
||||
│ Phase 3: Evaluate (ccw cli gemini)
|
||||
│ ↓ score, dimensions[], suggestions[], iteration-N-eval.md
|
||||
│ ↓
|
||||
│ [Terminate?]─── YES ──→ Phase 5: Report → final-report.md
|
||||
│ ↓ NO
|
||||
│ ↓
|
||||
│ Phase 4: Improve (Agent)
|
||||
│ ↓ modified skill files, iteration-N-changes.md
|
||||
│ ↓
|
||||
└───┘ next iteration
|
||||
```
|
||||
|
||||
## TodoWrite Pattern
|
||||
|
||||
```javascript
|
||||
// Initial state
|
||||
TaskCreate({ subject: "Phase 1: Setup workspace", activeForm: "Setting up workspace" })
|
||||
TaskCreate({ subject: "Iteration Loop", activeForm: "Running iterations" })
|
||||
TaskCreate({ subject: "Phase 5: Final Report", activeForm: "Generating report" })
|
||||
|
||||
// Chain mode: create per-skill tracking tasks
|
||||
if (state.execution_mode === 'chain') {
|
||||
for (const skillName of state.chain_order) {
|
||||
TaskCreate({
|
||||
subject: `Chain: ${skillName}`,
|
||||
activeForm: `Tracking ${skillName}`,
|
||||
description: `Skill chain member position ${state.chain_order.indexOf(skillName) + 1}`
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// During iteration N
|
||||
// Single mode: one score per iteration (existing behavior)
|
||||
// Chain mode: per-skill status updates
|
||||
if (state.execution_mode === 'chain') {
|
||||
// After each skill executes in Phase 2:
|
||||
TaskUpdate(chainSkillTask, {
|
||||
subject: `Chain: ${skillName} — Iter ${N} executed`,
|
||||
activeForm: `${skillName} iteration ${N}`
|
||||
})
|
||||
// After Phase 3 evaluates:
|
||||
TaskUpdate(chainSkillTask, {
|
||||
subject: `Chain: ${skillName} — Score ${chainScores[skillName]}/100`,
|
||||
activeForm: `${skillName} scored`
|
||||
})
|
||||
} else {
|
||||
// Single mode (existing)
|
||||
TaskCreate({
|
||||
subject: `Iteration ${N}: Score ${score}/100`,
|
||||
activeForm: `Iteration ${N} complete`,
|
||||
description: `Strengths: ... | Weaknesses: ... | Suggestions: ${count}`
|
||||
})
|
||||
}
|
||||
|
||||
// Completed — collapse
|
||||
TaskUpdate(iterLoop, {
|
||||
subject: `Iteration Loop (${totalIters} iters, final: ${finalScore})`,
|
||||
status: 'completed'
|
||||
})
|
||||
```
|
||||
|
||||
## Termination Logic
|
||||
|
||||
```javascript
|
||||
function shouldTerminate(state) {
|
||||
// 1. Quality threshold met
|
||||
if (state.latest_score >= state.quality_threshold) {
|
||||
return { terminate: true, reason: 'quality_threshold_met' };
|
||||
}
|
||||
// 2. Max iterations reached
|
||||
if (state.current_iteration >= state.max_iterations) {
|
||||
return { terminate: true, reason: 'max_iterations_reached' };
|
||||
}
|
||||
// 3. Convergence: ≤2 points improvement over last 2 iterations
|
||||
if (state.score_trend.length >= 3) {
|
||||
const last3 = state.score_trend.slice(-3);
|
||||
if (last3[2] - last3[0] <= 2) {
|
||||
state.converged = true;
|
||||
return { terminate: true, reason: 'convergence_detected' };
|
||||
}
|
||||
}
|
||||
// 4. Error limit
|
||||
if (state.error_count >= state.max_errors) {
|
||||
return { terminate: true, reason: 'error_limit_reached' };
|
||||
}
|
||||
return { terminate: false };
|
||||
}
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
| Phase | Error | Recovery |
|
||||
|-------|-------|----------|
|
||||
| 2: Execute | CLI timeout/crash | Retry once with simplified prompt, then skip |
|
||||
| 3: Evaluate | CLI fails | Retry once, then use score 50 with warning |
|
||||
| 3: Evaluate | JSON parse fails | Extract score heuristically, save raw output |
|
||||
| 4: Improve | Agent fails | Rollback from `iteration-{N}/skill-snapshot/` |
|
||||
| Any | 3+ consecutive errors | Terminate with error report |
|
||||
|
||||
**Error Budget**: Each phase gets 1 retry. 3 consecutive failed iterations triggers termination.
|
||||
|
||||
## Coordinator Checklist
|
||||
|
||||
### Pre-Phase Actions
|
||||
- [ ] Read iteration-state.json for current state
|
||||
- [ ] Verify workspace directory exists
|
||||
- [ ] Check error count hasn't exceeded limit
|
||||
|
||||
### Per-Iteration Actions
|
||||
- [ ] Increment current_iteration in state
|
||||
- [ ] Create iteration-{N} subdirectory
|
||||
- [ ] Update TodoWrite with iteration status
|
||||
- [ ] After Phase 3: check termination before Phase 4
|
||||
- [ ] After Phase 4: write state, proceed to next iteration
|
||||
|
||||
### Post-Workflow Actions
|
||||
- [ ] Execute Phase 5 (Report)
|
||||
- [ ] Display final summary to user
|
||||
- [ ] Update all TodoWrite tasks to completed
|
||||
144
.claude/skills/skill-iter-tune/phases/01-setup.md
Normal file
144
.claude/skills/skill-iter-tune/phases/01-setup.md
Normal file
@@ -0,0 +1,144 @@
|
||||
# Phase 1: Setup
|
||||
|
||||
Initialize workspace, backup skills, parse inputs.
|
||||
|
||||
## Objective
|
||||
|
||||
- Parse skill path(s) and test scenario from user input
|
||||
- Validate all skill paths exist and contain SKILL.md
|
||||
- Create isolated workspace directory structure
|
||||
- Backup original skill files
|
||||
- Initialize iteration-state.json
|
||||
|
||||
## Execution
|
||||
|
||||
### Step 1.1: Parse Input
|
||||
|
||||
Parse `$ARGUMENTS` to extract skill paths and test scenario.
|
||||
|
||||
```javascript
|
||||
// Parse skill paths (first argument or comma-separated)
|
||||
const args = $ARGUMENTS.trim();
|
||||
const pathMatch = args.match(/^([^\s]+)/);
|
||||
const rawPaths = pathMatch ? pathMatch[1].split(',') : [];
|
||||
|
||||
// Parse test scenario
|
||||
const scenarioMatch = args.match(/(?:--scenario|--test)\s+"([^"]+)"/);
|
||||
const scenarioText = scenarioMatch ? scenarioMatch[1] : args.replace(rawPaths.join(','), '').trim();
|
||||
|
||||
// Record chain order (preserves input order for chain mode)
|
||||
const chainOrder = rawPaths.map(p => p.startsWith('.claude/') ? p.split('/').pop() : p);
|
||||
|
||||
// If no scenario, ask user
|
||||
if (!scenarioText) {
|
||||
const response = AskUserQuestion({
|
||||
questions: [{
|
||||
question: "Please describe the test scenario for evaluating this skill:",
|
||||
header: "Test Scenario",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{ label: "General quality test", description: "Evaluate overall skill quality with a generic task" },
|
||||
{ label: "Specific scenario", description: "I'll describe a specific test case" }
|
||||
]
|
||||
}]
|
||||
});
|
||||
// Use response to construct testScenario
|
||||
}
|
||||
```
|
||||
|
||||
### Step 1.2: Validate Skill Paths
|
||||
|
||||
```javascript
|
||||
const targetSkills = [];
|
||||
for (const rawPath of rawPaths) {
|
||||
const skillPath = rawPath.startsWith('.claude/') ? rawPath : `.claude/skills/${rawPath}`;
|
||||
|
||||
// Validate SKILL.md exists
|
||||
const skillFiles = Glob(`${skillPath}/SKILL.md`);
|
||||
if (skillFiles.length === 0) {
|
||||
throw new Error(`Skill not found at: ${skillPath} -- SKILL.md missing`);
|
||||
}
|
||||
|
||||
// Collect all skill files
|
||||
const allFiles = Glob(`${skillPath}/**/*.md`);
|
||||
targetSkills.push({
|
||||
name: skillPath.split('/').pop(),
|
||||
path: skillPath,
|
||||
files: allFiles.map(f => f.replace(skillPath + '/', '')),
|
||||
primary_file: 'SKILL.md'
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### Step 1.3: Create Workspace
|
||||
|
||||
```javascript
|
||||
const ts = Date.now();
|
||||
const workDir = `.workflow/.scratchpad/skill-iter-tune-${ts}`;
|
||||
|
||||
Bash(`mkdir -p "${workDir}/backups" "${workDir}/iterations"`);
|
||||
```
|
||||
|
||||
### Step 1.4: Backup Original Skills
|
||||
|
||||
```javascript
|
||||
for (const skill of targetSkills) {
|
||||
Bash(`cp -r "${skill.path}" "${workDir}/backups/${skill.name}"`);
|
||||
}
|
||||
```
|
||||
|
||||
### Step 1.5: Initialize State
|
||||
|
||||
Write `iteration-state.json` with initial state:
|
||||
|
||||
```javascript
|
||||
const initialState = {
|
||||
status: 'running',
|
||||
started_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString(),
|
||||
target_skills: targetSkills,
|
||||
test_scenario: {
|
||||
description: scenarioText,
|
||||
// Parse --requirements and --input-args from $ARGUMENTS if provided
|
||||
// e.g., --requirements "clear output,no errors" --input-args "my-skill --scenario test"
|
||||
requirements: parseListArg(args, '--requirements') || [],
|
||||
input_args: parseStringArg(args, '--input-args') || '',
|
||||
success_criteria: parseStringArg(args, '--success-criteria') || 'Produces correct, high-quality output'
|
||||
},
|
||||
execution_mode: workflowPreferences.executionMode || 'single',
|
||||
chain_order: workflowPreferences.executionMode === 'chain'
|
||||
? targetSkills.map(s => s.name)
|
||||
: [],
|
||||
current_iteration: 0,
|
||||
max_iterations: workflowPreferences.maxIterations,
|
||||
quality_threshold: workflowPreferences.qualityThreshold,
|
||||
latest_score: 0,
|
||||
score_trend: [],
|
||||
converged: false,
|
||||
iterations: [],
|
||||
errors: [],
|
||||
error_count: 0,
|
||||
max_errors: 3,
|
||||
work_dir: workDir,
|
||||
backup_dir: `${workDir}/backups`
|
||||
};
|
||||
|
||||
Write(`${workDir}/iteration-state.json`, JSON.stringify(initialState, null, 2));
|
||||
|
||||
// Chain mode: create per-skill tracking tasks
|
||||
if (initialState.execution_mode === 'chain') {
|
||||
for (const skill of targetSkills) {
|
||||
TaskCreate({
|
||||
subject: `Chain: ${skill.name}`,
|
||||
activeForm: `Tracking ${skill.name}`,
|
||||
description: `Skill chain member: ${skill.path} | Position: ${targetSkills.indexOf(skill) + 1}/${targetSkills.length}`
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Output
|
||||
|
||||
- **Variables**: `workDir`, `targetSkills[]`, `testScenario`, `chainOrder` (chain mode)
|
||||
- **Files**: `iteration-state.json`, `backups/` directory with skill copies
|
||||
- **TodoWrite**: Mark Phase 1 completed, start Iteration Loop. Chain mode: per-skill tracking tasks created
|
||||
292
.claude/skills/skill-iter-tune/phases/02-execute.md
Normal file
292
.claude/skills/skill-iter-tune/phases/02-execute.md
Normal file
@@ -0,0 +1,292 @@
|
||||
# Phase 2: Execute Skill
|
||||
|
||||
> **COMPACT SENTINEL [Phase 2: Execute]**
|
||||
> This phase contains 4 execution steps (Step 2.1 -- 2.4).
|
||||
> If you can read this sentinel but cannot find the full Step protocol below, context has been compressed.
|
||||
> Recovery: `Read("phases/02-execute.md")`
|
||||
|
||||
Execute the target skill against the test scenario using `ccw cli --tool claude --mode write`. Claude receives the full skill definition and simulates producing its expected output artifacts.
|
||||
|
||||
## Objective
|
||||
|
||||
- Snapshot current skill version before execution
|
||||
- Construct execution prompt with full skill content + test scenario
|
||||
- Execute via ccw cli Claude
|
||||
- Collect output artifacts
|
||||
|
||||
## Execution
|
||||
|
||||
### Step 2.1: Snapshot Current Skill
|
||||
|
||||
```javascript
|
||||
const N = state.current_iteration;
|
||||
const iterDir = `${state.work_dir}/iterations/iteration-${N}`;
|
||||
Bash(`mkdir -p "${iterDir}/skill-snapshot" "${iterDir}/artifacts"`);
|
||||
|
||||
// Chain mode: create per-skill artifact directories
|
||||
if (state.execution_mode === 'chain') {
|
||||
for (const skillName of state.chain_order) {
|
||||
Bash(`mkdir -p "${iterDir}/artifacts/${skillName}"`);
|
||||
}
|
||||
}
|
||||
|
||||
// Snapshot current skill state (so we can compare/rollback)
|
||||
for (const skill of state.target_skills) {
|
||||
Bash(`cp -r "${skill.path}" "${iterDir}/skill-snapshot/${skill.name}"`);
|
||||
}
|
||||
```
|
||||
|
||||
### Step 2.2: Construct Execution Prompt (Single Mode)
|
||||
|
||||
Read the execute-prompt template and substitute variables.
|
||||
|
||||
> Skip to Step 2.2b if `state.execution_mode === 'chain'`.
|
||||
|
||||
```javascript
|
||||
// Ref: templates/execute-prompt.md
|
||||
|
||||
// Build skillContent by reading only executable skill files (SKILL.md, phases/, specs/)
|
||||
// Exclude README.md, docs/, and other non-executable files to save tokens
|
||||
const skillContent = state.target_skills.map(skill => {
|
||||
const skillMd = Read(`${skill.path}/SKILL.md`);
|
||||
const phaseFiles = Glob(`${skill.path}/phases/*.md`).sort().map(f => ({
|
||||
relativePath: f.replace(skill.path + '/', ''),
|
||||
content: Read(f)
|
||||
}));
|
||||
const specFiles = Glob(`${skill.path}/specs/*.md`).map(f => ({
|
||||
relativePath: f.replace(skill.path + '/', ''),
|
||||
content: Read(f)
|
||||
}));
|
||||
|
||||
return `### File: SKILL.md\n${skillMd}\n\n` +
|
||||
phaseFiles.map(f => `### File: ${f.relativePath}\n${f.content}`).join('\n\n') +
|
||||
(specFiles.length > 0 ? '\n\n' + specFiles.map(f => `### File: ${f.relativePath}\n${f.content}`).join('\n\n') : '');
|
||||
}).join('\n\n---\n\n');
|
||||
|
||||
// Construct full prompt using template
|
||||
const executePrompt = `PURPOSE: Simulate executing the following workflow skill against a test scenario. Produce all expected output artifacts as if the skill were invoked with the given input.
|
||||
|
||||
SKILL CONTENT:
|
||||
${skillContent}
|
||||
|
||||
TEST SCENARIO:
|
||||
Description: ${state.test_scenario.description}
|
||||
Input Arguments: ${state.test_scenario.input_args}
|
||||
Requirements: ${state.test_scenario.requirements.join('; ')}
|
||||
Success Criteria: ${state.test_scenario.success_criteria}
|
||||
|
||||
TASK:
|
||||
1. Study the complete skill structure (SKILL.md + all phase files)
|
||||
2. Follow the skill execution flow sequentially
|
||||
3. For each phase, produce the artifacts that phase would generate
|
||||
4. Write all output artifacts to the current working directory
|
||||
5. Create a manifest.json listing all produced artifacts
|
||||
|
||||
MODE: write
|
||||
CONTEXT: @**/*
|
||||
EXPECTED: All artifacts written to disk + manifest.json
|
||||
CONSTRAINTS: Follow skill flow exactly, produce realistic output, not placeholders`;
|
||||
```
|
||||
|
||||
### Step 2.3: Execute via ccw cli
|
||||
|
||||
> **CHECKPOINT**: Before executing CLI, verify:
|
||||
> 1. This phase is TodoWrite `in_progress`
|
||||
> 2. `iterDir/artifacts/` directory exists
|
||||
> 3. Prompt is properly escaped
|
||||
|
||||
```javascript
|
||||
function escapeForShell(str) {
|
||||
return str.replace(/"/g, '\\"').replace(/\$/g, '\\$').replace(/`/g, '\\`');
|
||||
}
|
||||
|
||||
const cliCommand = `ccw cli -p "${escapeForShell(executePrompt)}" --tool claude --mode write --cd "${iterDir}/artifacts"`;
|
||||
|
||||
// Execute in background, wait for hook callback
|
||||
Bash({
|
||||
command: cliCommand,
|
||||
run_in_background: true,
|
||||
timeout: 600000 // 10 minutes max
|
||||
});
|
||||
|
||||
// STOP HERE -- wait for hook callback to resume
|
||||
// After callback, verify artifacts were produced
|
||||
```
|
||||
|
||||
### Step 2.2b: Chain Execution Path
|
||||
|
||||
> Skip this step if `state.execution_mode === 'single'`.
|
||||
|
||||
In chain mode, execute each skill sequentially. Each skill receives the previous skill's artifacts as input context.
|
||||
|
||||
```javascript
|
||||
// Chain execution: iterate through chain_order
|
||||
let previousArtifacts = ''; // Accumulates upstream output
|
||||
|
||||
for (let i = 0; i < state.chain_order.length; i++) {
|
||||
const skillName = state.chain_order[i];
|
||||
const skill = state.target_skills.find(s => s.name === skillName);
|
||||
const skillArtifactDir = `${iterDir}/artifacts/${skillName}`;
|
||||
|
||||
// Build this skill's content
|
||||
const skillMd = Read(`${skill.path}/SKILL.md`);
|
||||
const phaseFiles = Glob(`${skill.path}/phases/*.md`).sort().map(f => ({
|
||||
relativePath: f.replace(skill.path + '/', ''),
|
||||
content: Read(f)
|
||||
}));
|
||||
const specFiles = Glob(`${skill.path}/specs/*.md`).map(f => ({
|
||||
relativePath: f.replace(skill.path + '/', ''),
|
||||
content: Read(f)
|
||||
}));
|
||||
|
||||
const singleSkillContent = `### File: SKILL.md\n${skillMd}\n\n` +
|
||||
phaseFiles.map(f => `### File: ${f.relativePath}\n${f.content}`).join('\n\n') +
|
||||
(specFiles.length > 0 ? '\n\n' + specFiles.map(f => `### File: ${f.relativePath}\n${f.content}`).join('\n\n') : '');
|
||||
|
||||
// Build chain context from previous skill's artifacts
|
||||
const chainInputContext = previousArtifacts
|
||||
? `\nPREVIOUS CHAIN OUTPUT (from upstream skill "${state.chain_order[i - 1]}"):\n${previousArtifacts}\n\nIMPORTANT: Use the above output as input context for this skill's execution.\n`
|
||||
: '';
|
||||
|
||||
// Construct per-skill execution prompt
|
||||
// Ref: templates/execute-prompt.md
|
||||
const chainPrompt = `PURPOSE: Simulate executing the following workflow skill against a test scenario. Produce all expected output artifacts.
|
||||
|
||||
SKILL CONTENT (${skillName} — chain position ${i + 1}/${state.chain_order.length}):
|
||||
${singleSkillContent}
|
||||
${chainInputContext}
|
||||
TEST SCENARIO:
|
||||
Description: ${state.test_scenario.description}
|
||||
Input Arguments: ${state.test_scenario.input_args}
|
||||
Requirements: ${state.test_scenario.requirements.join('; ')}
|
||||
Success Criteria: ${state.test_scenario.success_criteria}
|
||||
|
||||
TASK:
|
||||
1. Study the complete skill structure
|
||||
2. Follow the skill execution flow sequentially
|
||||
3. Produce all expected artifacts
|
||||
4. Write output to the current working directory
|
||||
5. Create manifest.json listing all produced artifacts
|
||||
|
||||
MODE: write
|
||||
CONTEXT: @**/*
|
||||
CONSTRAINTS: Follow skill flow exactly, produce realistic output`;
|
||||
|
||||
function escapeForShell(str) {
|
||||
return str.replace(/"/g, '\\"').replace(/\$/g, '\\$').replace(/`/g, '\\`');
|
||||
}
|
||||
|
||||
const cliCommand = `ccw cli -p "${escapeForShell(chainPrompt)}" --tool claude --mode write --cd "${skillArtifactDir}"`;
|
||||
|
||||
// Execute in background
|
||||
Bash({
|
||||
command: cliCommand,
|
||||
run_in_background: true,
|
||||
timeout: 600000
|
||||
});
|
||||
|
||||
// STOP -- wait for hook callback
|
||||
|
||||
// After callback: collect artifacts for next skill in chain
|
||||
const artifacts = Glob(`${skillArtifactDir}/**/*`);
|
||||
const skillSuccess = artifacts.length > 0;
|
||||
|
||||
if (skillSuccess) {
|
||||
previousArtifacts = artifacts.slice(0, 10).map(f => {
|
||||
const relPath = f.replace(skillArtifactDir + '/', '');
|
||||
const content = Read(f, { limit: 100 });
|
||||
return `--- ${relPath} ---\n${content}`;
|
||||
}).join('\n\n');
|
||||
} else {
|
||||
// Mid-chain failure: keep previous artifacts for downstream skills
|
||||
// Log warning but continue chain — downstream skills receive last successful output
|
||||
state.errors.push({
|
||||
phase: 'execute',
|
||||
message: `Chain skill "${skillName}" (position ${i + 1}) produced no artifacts. Downstream skills will receive upstream output from "${state.chain_order[i - 1] || 'none'}" instead.`,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
state.error_count++;
|
||||
// previousArtifacts remains from last successful skill (or empty if first)
|
||||
}
|
||||
|
||||
// Update per-skill TodoWrite
|
||||
// TaskUpdate chain skill task with execution status
|
||||
|
||||
// Record per-skill execution
|
||||
if (!state.iterations[N - 1].execution.chain_executions) {
|
||||
state.iterations[N - 1].execution.chain_executions = [];
|
||||
}
|
||||
state.iterations[N - 1].execution.chain_executions.push({
|
||||
skill_name: skillName,
|
||||
cli_command: cliCommand,
|
||||
artifacts_dir: skillArtifactDir,
|
||||
success: skillSuccess
|
||||
});
|
||||
|
||||
// Check error budget: abort chain if too many consecutive failures
|
||||
if (state.error_count >= 3) {
|
||||
state.errors.push({
|
||||
phase: 'execute',
|
||||
message: `Chain execution aborted at skill "${skillName}" — error limit reached (${state.error_count} errors).`,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Step 2.4: Collect Artifacts
|
||||
|
||||
After CLI completes (hook callback received):
|
||||
|
||||
```javascript
|
||||
// List produced artifacts
|
||||
const artifactFiles = Glob(`${iterDir}/artifacts/**/*`);
|
||||
|
||||
// Chain mode: check per-skill artifacts
|
||||
if (state.execution_mode === 'chain') {
|
||||
const chainSuccess = state.iterations[N - 1].execution.chain_executions?.every(e => e.success) ?? false;
|
||||
state.iterations[N - 1].execution.success = chainSuccess;
|
||||
state.iterations[N - 1].execution.artifacts_dir = `${iterDir}/artifacts`;
|
||||
} else {
|
||||
|
||||
if (artifactFiles.length === 0) {
|
||||
// Execution produced nothing -- record error
|
||||
state.iterations[N - 1].execution = {
|
||||
cli_command: cliCommand,
|
||||
started_at: new Date().toISOString(),
|
||||
completed_at: new Date().toISOString(),
|
||||
artifacts_dir: `${iterDir}/artifacts`,
|
||||
success: false
|
||||
};
|
||||
state.error_count++;
|
||||
// Continue to Phase 3 anyway -- Gemini can evaluate the skill even without artifacts
|
||||
} else {
|
||||
state.iterations[N - 1].execution = {
|
||||
cli_command: cliCommand,
|
||||
started_at: new Date().toISOString(),
|
||||
completed_at: new Date().toISOString(),
|
||||
artifacts_dir: `${iterDir}/artifacts`,
|
||||
success: true
|
||||
};
|
||||
}
|
||||
|
||||
} // end single mode branch
|
||||
|
||||
// Update state
|
||||
Write(`${state.work_dir}/iteration-state.json`, JSON.stringify(state, null, 2));
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
| Error | Recovery |
|
||||
|-------|----------|
|
||||
| CLI timeout (10min) | Record failure, continue to Phase 3 without artifacts |
|
||||
| CLI crash | Retry once with simplified prompt (SKILL.md only, no phase files) |
|
||||
| No artifacts produced | Continue to Phase 3, evaluation focuses on skill definition quality |
|
||||
|
||||
## Output
|
||||
|
||||
- **Files**: `iteration-{N}/skill-snapshot/`, `iteration-{N}/artifacts/`
|
||||
- **State**: `iterations[N-1].execution` updated
|
||||
- **Next**: Phase 3 (Evaluate)
|
||||
312
.claude/skills/skill-iter-tune/phases/03-evaluate.md
Normal file
312
.claude/skills/skill-iter-tune/phases/03-evaluate.md
Normal file
@@ -0,0 +1,312 @@
|
||||
# Phase 3: Evaluate Quality
|
||||
|
||||
> **COMPACT SENTINEL [Phase 3: Evaluate]**
|
||||
> This phase contains 5 execution steps (Step 3.1 -- 3.5).
|
||||
> If you can read this sentinel but cannot find the full Step protocol below, context has been compressed.
|
||||
> Recovery: `Read("phases/03-evaluate.md")`
|
||||
|
||||
Evaluate skill quality using `ccw cli --tool gemini --mode analysis`. Gemini scores the skill across 5 dimensions and provides improvement suggestions.
|
||||
|
||||
## Objective
|
||||
|
||||
- Construct evaluation prompt with skill + artifacts + criteria
|
||||
- Execute via ccw cli Gemini
|
||||
- Parse multi-dimensional score
|
||||
- Write iteration-{N}-eval.md
|
||||
- Check termination conditions
|
||||
|
||||
## Execution
|
||||
|
||||
### Step 3.1: Prepare Evaluation Context
|
||||
|
||||
```javascript
|
||||
const N = state.current_iteration;
|
||||
const iterDir = `${state.work_dir}/iterations/iteration-${N}`;
|
||||
|
||||
// Read evaluation criteria
|
||||
// Ref: specs/evaluation-criteria.md
|
||||
const evaluationCriteria = Read('.claude/skills/skill-iter-tune/specs/evaluation-criteria.md');
|
||||
|
||||
// Build skillContent (same pattern as Phase 02 — only executable files)
|
||||
const skillContent = state.target_skills.map(skill => {
|
||||
const skillMd = Read(`${skill.path}/SKILL.md`);
|
||||
const phaseFiles = Glob(`${skill.path}/phases/*.md`).sort().map(f => ({
|
||||
relativePath: f.replace(skill.path + '/', ''),
|
||||
content: Read(f)
|
||||
}));
|
||||
const specFiles = Glob(`${skill.path}/specs/*.md`).map(f => ({
|
||||
relativePath: f.replace(skill.path + '/', ''),
|
||||
content: Read(f)
|
||||
}));
|
||||
return `### File: SKILL.md\n${skillMd}\n\n` +
|
||||
phaseFiles.map(f => `### File: ${f.relativePath}\n${f.content}`).join('\n\n') +
|
||||
(specFiles.length > 0 ? '\n\n' + specFiles.map(f => `### File: ${f.relativePath}\n${f.content}`).join('\n\n') : '');
|
||||
}).join('\n\n---\n\n');
|
||||
|
||||
// Build artifacts summary
|
||||
let artifactsSummary = 'No artifacts produced (execution may have failed)';
|
||||
|
||||
if (state.execution_mode === 'chain') {
|
||||
// Chain mode: group artifacts by skill
|
||||
const chainSummaries = state.chain_order.map(skillName => {
|
||||
const skillArtifactDir = `${iterDir}/artifacts/${skillName}`;
|
||||
const files = Glob(`${skillArtifactDir}/**/*`);
|
||||
if (files.length === 0) return `### ${skillName} (no artifacts)`;
|
||||
const filesSummary = files.map(f => {
|
||||
const relPath = f.replace(`${skillArtifactDir}/`, '');
|
||||
const content = Read(f, { limit: 200 });
|
||||
return `--- ${relPath} ---\n${content}`;
|
||||
}).join('\n\n');
|
||||
return `### ${skillName} (chain position ${state.chain_order.indexOf(skillName) + 1})\n${filesSummary}`;
|
||||
});
|
||||
artifactsSummary = chainSummaries.join('\n\n---\n\n');
|
||||
} else {
|
||||
// Single mode (existing)
|
||||
const artifactFiles = Glob(`${iterDir}/artifacts/**/*`);
|
||||
if (artifactFiles.length > 0) {
|
||||
artifactsSummary = artifactFiles.map(f => {
|
||||
const relPath = f.replace(`${iterDir}/artifacts/`, '');
|
||||
const content = Read(f, { limit: 200 });
|
||||
return `--- ${relPath} ---\n${content}`;
|
||||
}).join('\n\n');
|
||||
}
|
||||
}
|
||||
|
||||
// Build previous evaluation context
|
||||
const previousEvalContext = state.iterations.filter(i => i.evaluation).length > 0
|
||||
? `PREVIOUS ITERATIONS:\n` + state.iterations.filter(i => i.evaluation).map(iter =>
|
||||
`Iteration ${iter.round}: Score ${iter.evaluation.score}\n` +
|
||||
` Applied: ${iter.improvement?.changes_applied?.map(c => c.summary).join('; ') || 'none'}\n` +
|
||||
` Weaknesses: ${iter.evaluation.weaknesses?.slice(0, 3).join('; ') || 'none'}`
|
||||
).join('\n') + '\nIMPORTANT: Focus on NEW issues not yet addressed.'
|
||||
: '';
|
||||
```
|
||||
|
||||
### Step 3.2: Construct Evaluation Prompt
|
||||
|
||||
```javascript
|
||||
// Ref: templates/eval-prompt.md
|
||||
const evalPrompt = `PURPOSE: Evaluate the quality of a workflow skill by examining its definition and produced artifacts.
|
||||
|
||||
SKILL DEFINITION:
|
||||
${skillContent}
|
||||
|
||||
TEST SCENARIO:
|
||||
${state.test_scenario.description}
|
||||
Requirements: ${state.test_scenario.requirements.join('; ')}
|
||||
Success Criteria: ${state.test_scenario.success_criteria}
|
||||
|
||||
ARTIFACTS PRODUCED:
|
||||
${artifactsSummary}
|
||||
|
||||
EVALUATION CRITERIA:
|
||||
${evaluationCriteria}
|
||||
|
||||
${previousEvalContext}
|
||||
|
||||
${state.execution_mode === 'chain' ? `
|
||||
CHAIN CONTEXT:
|
||||
This skill chain contains ${state.chain_order.length} skills executed in order:
|
||||
${state.chain_order.map((s, i) => `${i+1}. ${s}`).join('\n')}
|
||||
Current evaluation covers the entire chain output.
|
||||
Please provide per-skill quality scores in an additional "chain_scores" field: { "${state.chain_order[0]}": <score>, ... }
|
||||
` : ''}
|
||||
|
||||
TASK:
|
||||
1. Score each dimension (Clarity 0.20, Completeness 0.25, Correctness 0.25, Effectiveness 0.20, Efficiency 0.10) on 0-100
|
||||
2. Calculate weighted composite score
|
||||
3. List top 3 strengths
|
||||
4. List top 3-5 weaknesses with file:section references
|
||||
5. Provide 3-5 prioritized improvement suggestions with concrete changes
|
||||
|
||||
EXPECTED OUTPUT (strict JSON, no markdown):
|
||||
{
|
||||
"composite_score": <0-100>,
|
||||
"dimensions": [
|
||||
{"name":"Clarity","id":"clarity","score":<0-100>,"weight":0.20,"feedback":"..."},
|
||||
{"name":"Completeness","id":"completeness","score":<0-100>,"weight":0.25,"feedback":"..."},
|
||||
{"name":"Correctness","id":"correctness","score":<0-100>,"weight":0.25,"feedback":"..."},
|
||||
{"name":"Effectiveness","id":"effectiveness","score":<0-100>,"weight":0.20,"feedback":"..."},
|
||||
{"name":"Efficiency","id":"efficiency","score":<0-100>,"weight":0.10,"feedback":"..."}
|
||||
],
|
||||
"strengths": ["...", "...", "..."],
|
||||
"weaknesses": ["...with file:section ref...", "..."],
|
||||
"suggestions": [
|
||||
{"priority":"high|medium|low","target_file":"...","description":"...","rationale":"...","code_snippet":"..."}
|
||||
]
|
||||
}
|
||||
|
||||
CONSTRAINTS: Be rigorous, reference exact files, focus on highest-impact changes, output ONLY JSON`;
|
||||
```
|
||||
|
||||
### Step 3.3: Execute via ccw cli Gemini
|
||||
|
||||
> **CHECKPOINT**: Verify evaluation prompt is properly constructed before CLI execution.
|
||||
|
||||
```javascript
|
||||
// Shell escape utility (same as Phase 02)
|
||||
function escapeForShell(str) {
|
||||
return str.replace(/"/g, '\\"').replace(/\$/g, '\\$').replace(/`/g, '\\`');
|
||||
}
|
||||
|
||||
const skillPath = state.target_skills[0].path; // Primary skill for --cd
|
||||
|
||||
const cliCommand = `ccw cli -p "${escapeForShell(evalPrompt)}" --tool gemini --mode analysis --cd "${skillPath}"`;
|
||||
|
||||
// Execute in background
|
||||
Bash({
|
||||
command: cliCommand,
|
||||
run_in_background: true,
|
||||
timeout: 300000 // 5 minutes
|
||||
});
|
||||
|
||||
// STOP -- wait for hook callback
|
||||
```
|
||||
|
||||
### Step 3.4: Parse Score and Write Eval File
|
||||
|
||||
After CLI completes:
|
||||
|
||||
```javascript
|
||||
// Parse JSON from Gemini output
|
||||
// The output may contain markdown wrapping -- extract JSON
|
||||
const rawOutput = /* CLI output from callback */;
|
||||
const jsonMatch = rawOutput.match(/\{[\s\S]*\}/);
|
||||
let evaluation;
|
||||
|
||||
if (jsonMatch) {
|
||||
try {
|
||||
evaluation = JSON.parse(jsonMatch[0]);
|
||||
// Extract chain_scores if present
|
||||
if (state.execution_mode === 'chain' && evaluation.chain_scores) {
|
||||
state.iterations[N - 1].evaluation.chain_scores = evaluation.chain_scores;
|
||||
}
|
||||
} catch (e) {
|
||||
// Fallback: try to extract score heuristically
|
||||
const scoreMatch = rawOutput.match(/"composite_score"\s*:\s*(\d+)/);
|
||||
evaluation = {
|
||||
composite_score: scoreMatch ? parseInt(scoreMatch[1]) : 50,
|
||||
dimensions: [],
|
||||
strengths: [],
|
||||
weaknesses: ['Evaluation output parsing failed -- raw output saved'],
|
||||
suggestions: []
|
||||
};
|
||||
}
|
||||
} else {
|
||||
evaluation = {
|
||||
composite_score: 50,
|
||||
dimensions: [],
|
||||
strengths: [],
|
||||
weaknesses: ['No structured evaluation output -- defaulting to 50'],
|
||||
suggestions: []
|
||||
};
|
||||
}
|
||||
|
||||
// Write iteration-N-eval.md
|
||||
const evalReport = `# Iteration ${N} Evaluation
|
||||
|
||||
**Composite Score**: ${evaluation.composite_score}/100
|
||||
**Date**: ${new Date().toISOString()}
|
||||
|
||||
## Dimension Scores
|
||||
|
||||
| Dimension | Score | Weight | Feedback |
|
||||
|-----------|-------|--------|----------|
|
||||
${(evaluation.dimensions || []).map(d =>
|
||||
`| ${d.name} | ${d.score} | ${d.weight} | ${d.feedback} |`
|
||||
).join('\n')}
|
||||
|
||||
${(state.execution_mode === 'chain' && evaluation.chain_scores) ? `
|
||||
## Chain Scores
|
||||
|
||||
| Skill | Score | Chain Position |
|
||||
|-------|-------|----------------|
|
||||
${state.chain_order.map((s, i) => `| ${s} | ${evaluation.chain_scores[s] || '-'} | ${i + 1} |`).join('\n')}
|
||||
` : ''}
|
||||
|
||||
## Strengths
|
||||
${(evaluation.strengths || []).map(s => `- ${s}`).join('\n')}
|
||||
|
||||
## Weaknesses
|
||||
${(evaluation.weaknesses || []).map(w => `- ${w}`).join('\n')}
|
||||
|
||||
## Improvement Suggestions
|
||||
${(evaluation.suggestions || []).map((s, i) =>
|
||||
`### ${i + 1}. [${s.priority}] ${s.description}\n- **Target**: ${s.target_file}\n- **Rationale**: ${s.rationale}\n${s.code_snippet ? `- **Suggested**:\n\`\`\`\n${s.code_snippet}\n\`\`\`` : ''}`
|
||||
).join('\n\n')}
|
||||
`;
|
||||
|
||||
Write(`${iterDir}/iteration-${N}-eval.md`, evalReport);
|
||||
|
||||
// Update state
|
||||
state.iterations[N - 1].evaluation = {
|
||||
score: evaluation.composite_score,
|
||||
dimensions: evaluation.dimensions || [],
|
||||
strengths: evaluation.strengths || [],
|
||||
weaknesses: evaluation.weaknesses || [],
|
||||
suggestions: evaluation.suggestions || [],
|
||||
chain_scores: evaluation.chain_scores || null,
|
||||
eval_file: `${iterDir}/iteration-${N}-eval.md`
|
||||
};
|
||||
state.latest_score = evaluation.composite_score;
|
||||
state.score_trend.push(evaluation.composite_score);
|
||||
|
||||
Write(`${state.work_dir}/iteration-state.json`, JSON.stringify(state, null, 2));
|
||||
```
|
||||
|
||||
### Step 3.5: Check Termination
|
||||
|
||||
```javascript
|
||||
function shouldTerminate(state) {
|
||||
// 1. Quality threshold met
|
||||
if (state.latest_score >= state.quality_threshold) {
|
||||
return { terminate: true, reason: 'quality_threshold_met' };
|
||||
}
|
||||
|
||||
// 2. Max iterations reached
|
||||
if (state.current_iteration >= state.max_iterations) {
|
||||
return { terminate: true, reason: 'max_iterations_reached' };
|
||||
}
|
||||
|
||||
// 3. Convergence: no improvement in last 2 iterations
|
||||
if (state.score_trend.length >= 3) {
|
||||
const last3 = state.score_trend.slice(-3);
|
||||
const improvement = last3[2] - last3[0];
|
||||
if (improvement <= 2) {
|
||||
state.converged = true;
|
||||
return { terminate: true, reason: 'convergence_detected' };
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Error limit
|
||||
if (state.error_count >= state.max_errors) {
|
||||
return { terminate: true, reason: 'error_limit_reached' };
|
||||
}
|
||||
|
||||
return { terminate: false };
|
||||
}
|
||||
|
||||
const termination = shouldTerminate(state);
|
||||
if (termination.terminate) {
|
||||
state.termination_reason = termination.reason;
|
||||
Write(`${state.work_dir}/iteration-state.json`, JSON.stringify(state, null, 2));
|
||||
// Skip Phase 4, go directly to Phase 5 (Report)
|
||||
} else {
|
||||
// Continue to Phase 4 (Improve)
|
||||
}
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
| Error | Recovery |
|
||||
|-------|----------|
|
||||
| CLI timeout | Retry once, if still fails use score 50 with warning |
|
||||
| JSON parse failure | Extract score heuristically, save raw output |
|
||||
| No output | Default score 50, note in weaknesses |
|
||||
|
||||
## Output
|
||||
|
||||
- **Files**: `iteration-{N}-eval.md`
|
||||
- **State**: `iterations[N-1].evaluation`, `latest_score`, `score_trend` updated
|
||||
- **Decision**: terminate -> Phase 5, continue -> Phase 4
|
||||
- **TodoWrite**: Update current iteration score display
|
||||
186
.claude/skills/skill-iter-tune/phases/04-improve.md
Normal file
186
.claude/skills/skill-iter-tune/phases/04-improve.md
Normal file
@@ -0,0 +1,186 @@
|
||||
# Phase 4: Apply Improvements
|
||||
|
||||
> **COMPACT SENTINEL [Phase 4: Improve]**
|
||||
> This phase contains 4 execution steps (Step 4.1 -- 4.4).
|
||||
> If you can read this sentinel but cannot find the full Step protocol below, context has been compressed.
|
||||
> Recovery: `Read("phases/04-improve.md")`
|
||||
|
||||
Apply targeted improvements to skill files based on evaluation suggestions. Uses a general-purpose Agent to make changes, ensuring only suggested modifications are applied.
|
||||
|
||||
## Objective
|
||||
|
||||
- Read evaluation suggestions from current iteration
|
||||
- Launch Agent to apply improvements in priority order
|
||||
- Document all changes made
|
||||
- Update iteration state
|
||||
|
||||
## Execution
|
||||
|
||||
### Step 4.1: Prepare Improvement Context
|
||||
|
||||
```javascript
|
||||
const N = state.current_iteration;
|
||||
const iterDir = `${state.work_dir}/iterations/iteration-${N}`;
|
||||
const evaluation = state.iterations[N - 1].evaluation;
|
||||
|
||||
// Verify we have suggestions to apply
|
||||
if (!evaluation.suggestions || evaluation.suggestions.length === 0) {
|
||||
// No suggestions -- skip improvement, mark iteration complete
|
||||
state.iterations[N - 1].improvement = {
|
||||
changes_applied: [],
|
||||
changes_file: null,
|
||||
improvement_rationale: 'No suggestions provided by evaluation'
|
||||
};
|
||||
state.iterations[N - 1].status = 'completed';
|
||||
Write(`${state.work_dir}/iteration-state.json`, JSON.stringify(state, null, 2));
|
||||
// -> Return to orchestrator for next iteration
|
||||
return;
|
||||
}
|
||||
|
||||
// Build file inventory for agent context
|
||||
const skillFileInventory = state.target_skills.map(skill => {
|
||||
return `Skill: ${skill.name} (${skill.path})\nFiles:\n` +
|
||||
skill.files.map(f => ` - ${f}`).join('\n');
|
||||
}).join('\n\n');
|
||||
|
||||
// Chain mode: add chain relationship context
|
||||
const chainContext = state.execution_mode === 'chain'
|
||||
? `\nChain Order: ${state.chain_order.join(' -> ')}\n` +
|
||||
`Chain Scores: ${state.chain_order.map(s =>
|
||||
`${s}: ${state.iterations[N-1].evaluation?.chain_scores?.[s] || 'N/A'}`
|
||||
).join(', ')}\n` +
|
||||
`Weakest Link: ${state.chain_order.reduce((min, s) => {
|
||||
const score = state.iterations[N-1].evaluation?.chain_scores?.[s] || 100;
|
||||
return score < (state.iterations[N-1].evaluation?.chain_scores?.[min] || 100) ? s : min;
|
||||
}, state.chain_order[0])}`
|
||||
: '';
|
||||
```
|
||||
|
||||
### Step 4.2: Launch Improvement Agent
|
||||
|
||||
> **CHECKPOINT**: Before launching agent, verify:
|
||||
> 1. evaluation.suggestions is non-empty
|
||||
> 2. All target_file paths in suggestions are valid
|
||||
|
||||
```javascript
|
||||
const suggestionsText = evaluation.suggestions.map((s, i) =>
|
||||
`${i + 1}. [${s.priority.toUpperCase()}] ${s.description}\n` +
|
||||
` Target: ${s.target_file}\n` +
|
||||
` Rationale: ${s.rationale}\n` +
|
||||
(s.code_snippet ? ` Suggested change:\n ${s.code_snippet}\n` : '')
|
||||
).join('\n');
|
||||
|
||||
Agent({
|
||||
subagent_type: 'general-purpose',
|
||||
run_in_background: false,
|
||||
description: `Apply skill improvements iteration ${N}`,
|
||||
prompt: `## Task: Apply Targeted Improvements to Skill Files
|
||||
|
||||
You are improving a workflow skill based on evaluation feedback. Apply ONLY the suggested changes -- do not refactor, add features, or "improve" beyond what is explicitly suggested.
|
||||
|
||||
## Current Score: ${evaluation.score}/100
|
||||
Dimension breakdown:
|
||||
${evaluation.dimensions.map(d => `- ${d.name}: ${d.score}/100`).join('\n')}
|
||||
|
||||
## Skill File Inventory
|
||||
${skillFileInventory}
|
||||
|
||||
${chainContext ? `## Chain Context\n${chainContext}\n\nPrioritize improvements on the weakest skill in the chain. Also consider interface compatibility between adjacent skills in the chain.\n` : ''}
|
||||
|
||||
## Improvement Suggestions (apply in priority order)
|
||||
${suggestionsText}
|
||||
|
||||
## Rules
|
||||
1. Read each target file BEFORE modifying it
|
||||
2. Apply ONLY the suggested changes -- no unsolicited modifications
|
||||
3. If a suggestion's target_file doesn't exist, skip it and note in summary
|
||||
4. If a suggestion conflicts with existing patterns, adapt it to fit (note adaptation)
|
||||
5. Preserve existing code style, naming conventions, and structure
|
||||
6. After all changes, write a change summary to: ${iterDir}/iteration-${N}-changes.md
|
||||
|
||||
## Changes Summary Format (write to ${iterDir}/iteration-${N}-changes.md)
|
||||
|
||||
# Iteration ${N} Changes
|
||||
|
||||
## Applied Suggestions
|
||||
- [high] description: what was changed in which file
|
||||
- [medium] description: what was changed in which file
|
||||
|
||||
## Files Modified
|
||||
- path/to/file.md: brief description of changes
|
||||
|
||||
## Skipped Suggestions (if any)
|
||||
- description: reason for skipping
|
||||
|
||||
## Notes
|
||||
- Any adaptations or considerations
|
||||
|
||||
## Success Criteria
|
||||
- All high-priority suggestions applied
|
||||
- Medium-priority suggestions applied if feasible
|
||||
- Low-priority suggestions applied if trivial
|
||||
- Changes summary written to ${iterDir}/iteration-${N}-changes.md
|
||||
`
|
||||
});
|
||||
```
|
||||
|
||||
### Step 4.3: Verify Changes
|
||||
|
||||
After agent completes:
|
||||
|
||||
```javascript
|
||||
// Verify changes summary was written
|
||||
const changesFile = `${iterDir}/iteration-${N}-changes.md`;
|
||||
const changesExist = Glob(changesFile).length > 0;
|
||||
|
||||
if (!changesExist) {
|
||||
// Agent didn't write summary -- create a minimal one
|
||||
Write(changesFile, `# Iteration ${N} Changes\n\n## Notes\nAgent completed but did not produce changes summary.\n`);
|
||||
}
|
||||
|
||||
// Read changes summary to extract applied changes
|
||||
const changesContent = Read(changesFile);
|
||||
|
||||
// Parse applied changes (heuristic: count lines starting with "- [")
|
||||
const appliedMatches = changesContent.match(/^- \[.+?\]/gm) || [];
|
||||
const changes_applied = appliedMatches.map(m => ({
|
||||
summary: m.replace(/^- /, ''),
|
||||
file: '' // Extracted from context
|
||||
}));
|
||||
```
|
||||
|
||||
### Step 4.4: Update State
|
||||
|
||||
```javascript
|
||||
state.iterations[N - 1].improvement = {
|
||||
changes_applied: changes_applied,
|
||||
changes_file: changesFile,
|
||||
improvement_rationale: `Applied ${changes_applied.length} improvements based on evaluation score ${evaluation.score}`
|
||||
};
|
||||
state.iterations[N - 1].status = 'completed';
|
||||
state.updated_at = new Date().toISOString();
|
||||
|
||||
// Also update the skill files list in case new files were created
|
||||
for (const skill of state.target_skills) {
|
||||
skill.files = Glob(`${skill.path}/**/*.md`).map(f => f.replace(skill.path + '/', ''));
|
||||
}
|
||||
|
||||
Write(`${state.work_dir}/iteration-state.json`, JSON.stringify(state, null, 2));
|
||||
|
||||
// -> Return to orchestrator for next iteration (Phase 2) or termination check
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
| Error | Recovery |
|
||||
|-------|----------|
|
||||
| Agent fails to complete | Rollback from skill-snapshot: `cp -r "${iterDir}/skill-snapshot/${skill.name}/*" "${skill.path}/"` |
|
||||
| Agent corrupts files | Same rollback from snapshot |
|
||||
| Changes summary missing | Create minimal summary, continue |
|
||||
| target_file not found | Agent skips suggestion, notes in summary |
|
||||
|
||||
## Output
|
||||
|
||||
- **Files**: `iteration-{N}-changes.md`, modified skill files
|
||||
- **State**: `iterations[N-1].improvement` and `.status` updated
|
||||
- **Next**: Return to orchestrator, begin next iteration (Phase 2) or terminate
|
||||
166
.claude/skills/skill-iter-tune/phases/05-report.md
Normal file
166
.claude/skills/skill-iter-tune/phases/05-report.md
Normal file
@@ -0,0 +1,166 @@
|
||||
# Phase 5: Final Report
|
||||
|
||||
> **COMPACT SENTINEL [Phase 5: Report]**
|
||||
> This phase contains 4 execution steps (Step 5.1 -- 5.4).
|
||||
> If you can read this sentinel but cannot find the full Step protocol below, context has been compressed.
|
||||
> Recovery: `Read("phases/05-report.md")`
|
||||
|
||||
Generate comprehensive iteration history report and display results to user.
|
||||
|
||||
## Objective
|
||||
|
||||
- Read complete iteration state
|
||||
- Generate formatted final report with score progression
|
||||
- Write final-report.md
|
||||
- Display summary to user
|
||||
|
||||
## Execution
|
||||
|
||||
### Step 5.1: Read Complete State
|
||||
|
||||
```javascript
|
||||
const state = JSON.parse(Read(`${state.work_dir}/iteration-state.json`));
|
||||
state.status = 'completed';
|
||||
state.updated_at = new Date().toISOString();
|
||||
```
|
||||
|
||||
### Step 5.2: Generate Report
|
||||
|
||||
```javascript
|
||||
// Determine outcome
|
||||
const outcomeMap = {
|
||||
quality_threshold_met: 'PASSED -- Quality threshold reached',
|
||||
max_iterations_reached: 'MAX ITERATIONS -- Threshold not reached',
|
||||
convergence_detected: 'CONVERGED -- Score stopped improving',
|
||||
error_limit_reached: 'FAILED -- Too many errors'
|
||||
};
|
||||
const outcome = outcomeMap[state.termination_reason] || 'COMPLETED';
|
||||
|
||||
// Build score progression table
|
||||
const scoreTable = state.iterations
|
||||
.filter(i => i.evaluation)
|
||||
.map(i => {
|
||||
const dims = i.evaluation.dimensions || [];
|
||||
const dimScores = ['clarity', 'completeness', 'correctness', 'effectiveness', 'efficiency']
|
||||
.map(id => {
|
||||
const dim = dims.find(d => d.id === id);
|
||||
return dim ? dim.score : '-';
|
||||
});
|
||||
return `| ${i.round} | ${i.evaluation.score} | ${dimScores.join(' | ')} |`;
|
||||
}).join('\n');
|
||||
|
||||
// Build iteration details
|
||||
const iterationDetails = state.iterations.map(iter => {
|
||||
const evalSection = iter.evaluation
|
||||
? `**Score**: ${iter.evaluation.score}/100\n` +
|
||||
`**Strengths**: ${iter.evaluation.strengths?.join(', ') || 'N/A'}\n` +
|
||||
`**Weaknesses**: ${iter.evaluation.weaknesses?.slice(0, 3).join(', ') || 'N/A'}`
|
||||
: '**Evaluation**: Skipped or failed';
|
||||
|
||||
const changesSection = iter.improvement
|
||||
? `**Changes Applied**: ${iter.improvement.changes_applied?.length || 0}\n` +
|
||||
(iter.improvement.changes_applied?.map(c => ` - ${c.summary}`).join('\n') || ' None')
|
||||
: '**Improvements**: None';
|
||||
|
||||
return `### Iteration ${iter.round}\n${evalSection}\n${changesSection}`;
|
||||
}).join('\n\n');
|
||||
|
||||
const report = `# Skill Iter Tune -- Final Report
|
||||
|
||||
## Summary
|
||||
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| **Target Skills** | ${state.target_skills.map(s => s.name).join(', ')} |
|
||||
| **Execution Mode** | ${state.execution_mode} |
|
||||
${state.execution_mode === 'chain' ? `| **Chain Order** | ${state.chain_order.join(' -> ')} |` : ''}
|
||||
| **Test Scenario** | ${state.test_scenario.description} |
|
||||
| **Iterations** | ${state.iterations.length} |
|
||||
| **Initial Score** | ${state.score_trend[0] || 'N/A'} |
|
||||
| **Final Score** | ${state.latest_score}/100 |
|
||||
| **Quality Threshold** | ${state.quality_threshold} |
|
||||
| **Outcome** | ${outcome} |
|
||||
| **Started** | ${state.started_at} |
|
||||
| **Completed** | ${state.updated_at} |
|
||||
|
||||
## Score Progression
|
||||
|
||||
| Iter | Composite | Clarity | Completeness | Correctness | Effectiveness | Efficiency |
|
||||
|------|-----------|---------|--------------|-------------|---------------|------------|
|
||||
${scoreTable}
|
||||
|
||||
**Trend**: ${state.score_trend.join(' -> ')}
|
||||
|
||||
${state.execution_mode === 'chain' ? `
|
||||
## Chain Score Progression
|
||||
|
||||
| Iter | ${state.chain_order.join(' | ')} |
|
||||
|------|${state.chain_order.map(() => '------').join('|')}|
|
||||
${state.iterations.filter(i => i.evaluation?.chain_scores).map(i => {
|
||||
const scores = state.chain_order.map(s => i.evaluation.chain_scores[s] || '-');
|
||||
return `| ${i.round} | ${scores.join(' | ')} |`;
|
||||
}).join('\n')}
|
||||
` : ''}
|
||||
|
||||
## Iteration Details
|
||||
|
||||
${iterationDetails}
|
||||
|
||||
## Remaining Weaknesses
|
||||
|
||||
${state.iterations.length > 0 && state.iterations[state.iterations.length - 1].evaluation
|
||||
? state.iterations[state.iterations.length - 1].evaluation.weaknesses?.map(w => `- ${w}`).join('\n') || 'None identified'
|
||||
: 'No evaluation data available'}
|
||||
|
||||
## Artifact Locations
|
||||
|
||||
| Path | Description |
|
||||
|------|-------------|
|
||||
| \`${state.work_dir}/iteration-state.json\` | Complete state history |
|
||||
| \`${state.work_dir}/iterations/iteration-{N}/iteration-{N}-eval.md\` | Per-iteration evaluations |
|
||||
| \`${state.work_dir}/iterations/iteration-{N}/iteration-{N}-changes.md\` | Per-iteration change logs |
|
||||
| \`${state.work_dir}/final-report.md\` | This report |
|
||||
| \`${state.backup_dir}/\` | Original skill backups |
|
||||
|
||||
## Restore Original
|
||||
|
||||
To revert all changes and restore the original skill files:
|
||||
|
||||
\`\`\`bash
|
||||
${state.target_skills.map(s => `cp -r "${state.backup_dir}/${s.name}"/* "${s.path}/"`).join('\n')}
|
||||
\`\`\`
|
||||
`;
|
||||
```
|
||||
|
||||
### Step 5.3: Write Report and Update State
|
||||
|
||||
```javascript
|
||||
Write(`${state.work_dir}/final-report.md`, report);
|
||||
|
||||
state.status = 'completed';
|
||||
Write(`${state.work_dir}/iteration-state.json`, JSON.stringify(state, null, 2));
|
||||
```
|
||||
|
||||
### Step 5.4: Display Summary to User
|
||||
|
||||
Output to user:
|
||||
|
||||
```
|
||||
Skill Iter Tune Complete!
|
||||
|
||||
Target: {skill names}
|
||||
Iterations: {count}
|
||||
Score: {initial} -> {final} ({outcome})
|
||||
Threshold: {threshold}
|
||||
|
||||
Score trend: {score1} -> {score2} -> ... -> {scoreN}
|
||||
|
||||
Full report: {workDir}/final-report.md
|
||||
Backups: {backupDir}/
|
||||
```
|
||||
|
||||
## Output
|
||||
|
||||
- **Files**: `final-report.md`
|
||||
- **State**: `status = completed`
|
||||
- **Next**: Workflow complete. Return control to user.
|
||||
63
.claude/skills/skill-iter-tune/specs/evaluation-criteria.md
Normal file
63
.claude/skills/skill-iter-tune/specs/evaluation-criteria.md
Normal file
@@ -0,0 +1,63 @@
|
||||
# Evaluation Criteria
|
||||
|
||||
Skill 质量评估标准,由 Phase 03 (Evaluate) 引用。Gemini 按此标准对 skill 产出物进行多维度评分。
|
||||
|
||||
## Dimensions
|
||||
|
||||
| Dimension | Weight | ID | Description |
|
||||
|-----------|--------|----|-------------|
|
||||
| Clarity | 0.20 | clarity | 指令清晰无歧义,结构良好,易于遵循。Phase 文件有明确的 Step 划分、输入输出说明 |
|
||||
| Completeness | 0.25 | completeness | 覆盖所有必要阶段、边界情况、错误处理。没有遗漏关键执行路径 |
|
||||
| Correctness | 0.25 | correctness | 逻辑正确,数据流一致,Phase 间无矛盾。State schema 与实际使用匹配 |
|
||||
| Effectiveness | 0.20 | effectiveness | 在给定测试场景下能产出高质量输出。产物满足用户需求和成功标准 |
|
||||
| Efficiency | 0.10 | efficiency | 无冗余内容,上下文使用合理,不浪费 token。Phase 职责清晰无重叠 |
|
||||
|
||||
## Scoring Guide
|
||||
|
||||
| Range | Level | Description |
|
||||
|-------|-------|-------------|
|
||||
| 90-100 | Excellent | 生产级别,几乎无改进空间 |
|
||||
| 80-89 | Good | 可投入使用,仅需微调 |
|
||||
| 70-79 | Adequate | 功能可用,有明显可改进区域 |
|
||||
| 60-69 | Needs Work | 存在影响产出质量的显著问题 |
|
||||
| 0-59 | Poor | 结构或逻辑存在根本性问题 |
|
||||
|
||||
## Composite Score Calculation
|
||||
|
||||
```
|
||||
composite = sum(dimension.score * dimension.weight)
|
||||
```
|
||||
|
||||
## Output JSON Schema
|
||||
|
||||
```json
|
||||
{
|
||||
"composite_score": 75,
|
||||
"dimensions": [
|
||||
{ "name": "Clarity", "id": "clarity", "score": 80, "weight": 0.20, "feedback": "..." },
|
||||
{ "name": "Completeness", "id": "completeness", "score": 70, "weight": 0.25, "feedback": "..." },
|
||||
{ "name": "Correctness", "id": "correctness", "score": 78, "weight": 0.25, "feedback": "..." },
|
||||
{ "name": "Effectiveness", "id": "effectiveness", "score": 72, "weight": 0.20, "feedback": "..." },
|
||||
{ "name": "Efficiency", "id": "efficiency", "score": 85, "weight": 0.10, "feedback": "..." }
|
||||
],
|
||||
"strengths": ["...", "...", "..."],
|
||||
"weaknesses": ["...", "...", "..."],
|
||||
"suggestions": [
|
||||
{
|
||||
"priority": "high",
|
||||
"target_file": "phases/02-execute.md",
|
||||
"description": "Add explicit error handling for CLI timeout",
|
||||
"rationale": "Current phase has no recovery path when CLI execution exceeds timeout",
|
||||
"code_snippet": "optional suggested replacement code"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Evaluation Focus by Iteration
|
||||
|
||||
| Iteration | Primary Focus |
|
||||
|-----------|--------------|
|
||||
| 1 | 全面评估,建立 baseline |
|
||||
| 2-3 | 重点关注上一轮 weaknesses 是否改善,避免重复已解决的问题 |
|
||||
| 4+ | 精细化改进,关注 Effectiveness 和 Efficiency |
|
||||
134
.claude/skills/skill-iter-tune/templates/eval-prompt.md
Normal file
134
.claude/skills/skill-iter-tune/templates/eval-prompt.md
Normal file
@@ -0,0 +1,134 @@
|
||||
# Evaluation Prompt Template
|
||||
|
||||
Phase 03 使用此模板构造 ccw cli 提示词,让 Gemini 按多维度评估 skill 质量。
|
||||
|
||||
## Template
|
||||
|
||||
```
|
||||
PURPOSE: Evaluate the quality of a workflow skill by examining both its definition files and the artifacts it produced when executed against a test scenario. Provide a structured multi-dimensional score with actionable improvement suggestions.
|
||||
|
||||
SKILL DEFINITION:
|
||||
${skillContent}
|
||||
|
||||
TEST SCENARIO:
|
||||
${testScenario.description}
|
||||
Requirements: ${testScenario.requirements}
|
||||
Success Criteria: ${testScenario.success_criteria}
|
||||
|
||||
ARTIFACTS PRODUCED:
|
||||
${artifactsSummary}
|
||||
|
||||
EVALUATION CRITERIA:
|
||||
${evaluationCriteria}
|
||||
|
||||
${previousEvalContext}
|
||||
|
||||
TASK:
|
||||
1. Read all skill definition files and produced artifacts carefully
|
||||
2. Score each dimension on 0-100 based on the evaluation criteria:
|
||||
- Clarity (weight 0.20): Instructions unambiguous, well-structured, easy to follow
|
||||
- Completeness (weight 0.25): All phases, edge cases, error handling covered
|
||||
- Correctness (weight 0.25): Logic sound, data flow consistent, no contradictions
|
||||
- Effectiveness (weight 0.20): Produces high-quality output for the test scenario
|
||||
- Efficiency (weight 0.10): Minimal redundancy, appropriate context usage
|
||||
3. Calculate weighted composite score
|
||||
4. List top 3 strengths
|
||||
5. List top 3-5 weaknesses with specific file:section references
|
||||
6. Provide 3-5 prioritized improvement suggestions with concrete changes
|
||||
|
||||
MODE: analysis
|
||||
|
||||
EXPECTED OUTPUT FORMAT (strict JSON, no markdown wrapping):
|
||||
{
|
||||
"composite_score": <number 0-100>,
|
||||
"dimensions": [
|
||||
{ "name": "Clarity", "id": "clarity", "score": <0-100>, "weight": 0.20, "feedback": "<specific feedback>" },
|
||||
{ "name": "Completeness", "id": "completeness", "score": <0-100>, "weight": 0.25, "feedback": "<specific feedback>" },
|
||||
{ "name": "Correctness", "id": "correctness", "score": <0-100>, "weight": 0.25, "feedback": "<specific feedback>" },
|
||||
{ "name": "Effectiveness", "id": "effectiveness", "score": <0-100>, "weight": 0.20, "feedback": "<specific feedback>" },
|
||||
{ "name": "Efficiency", "id": "efficiency", "score": <0-100>, "weight": 0.10, "feedback": "<specific feedback>" }
|
||||
],
|
||||
"strengths": ["<strength 1>", "<strength 2>", "<strength 3>"],
|
||||
"weaknesses": ["<weakness 1 with file:section reference>", "..."],
|
||||
"suggestions": [
|
||||
{
|
||||
"priority": "high|medium|low",
|
||||
"target_file": "<relative path to skill file>",
|
||||
"description": "<what to change>",
|
||||
"rationale": "<why this improves quality>",
|
||||
"code_snippet": "<optional: suggested replacement content>"
|
||||
}
|
||||
],
|
||||
"chain_scores": {
|
||||
"<skill_name>": "<number 0-100, per-skill score — only present in chain mode>"
|
||||
}
|
||||
}
|
||||
|
||||
CONSTRAINTS:
|
||||
- Be rigorous and specific — reference exact file paths and sections
|
||||
- Each suggestion MUST include a target_file that maps to a skill file
|
||||
- Focus suggestions on highest-impact changes first
|
||||
- Do NOT suggest changes already addressed in previous iterations
|
||||
- Output ONLY the JSON object, no surrounding text or markdown
|
||||
```
|
||||
|
||||
## Variable Substitution
|
||||
|
||||
| Variable | Source | Description |
|
||||
|----------|--------|-------------|
|
||||
| `${skillContent}` | Same as execute-prompt.md | 完整 skill 文件内容 |
|
||||
| `${testScenario.*}` | iteration-state.json | 测试场景信息 |
|
||||
| `${artifactsSummary}` | Phase 03 reads artifacts/ dir | 产出物文件列表 + 内容摘要 |
|
||||
| `${evaluationCriteria}` | specs/evaluation-criteria.md | 评分标准全文 |
|
||||
| `${previousEvalContext}` | 历史迭代记录 | 前几轮评估摘要(避免重复建议) |
|
||||
| `${chainContext}` | Phase 03 constructs | chain 模式下的链上下文信息 |
|
||||
|
||||
## previousEvalContext Construction
|
||||
|
||||
```javascript
|
||||
// Build context from prior iterations to avoid repeating suggestions
|
||||
const previousEvalContext = state.iterations.length > 0
|
||||
? `PREVIOUS ITERATIONS (context for avoiding duplicate suggestions):
|
||||
${state.iterations.map(iter => `
|
||||
Iteration ${iter.round}: Score ${iter.evaluation?.score || 'N/A'}
|
||||
Applied changes: ${iter.improvement?.changes_applied?.map(c => c.summary).join('; ') || 'none'}
|
||||
Remaining weaknesses: ${iter.evaluation?.weaknesses?.slice(0, 3).join('; ') || 'none'}
|
||||
`).join('')}
|
||||
IMPORTANT: Focus on NEW issues or issues NOT adequately addressed in previous improvements.`
|
||||
: '';
|
||||
```
|
||||
|
||||
## chainContext Construction
|
||||
|
||||
```javascript
|
||||
// Build chain context for evaluation (chain mode only)
|
||||
const chainContext = state.execution_mode === 'chain'
|
||||
? `CHAIN CONTEXT:
|
||||
This skill chain contains ${state.chain_order.length} skills executed in order:
|
||||
${state.chain_order.map((s, i) => `${i+1}. ${s}`).join('\n')}
|
||||
Current evaluation covers the entire chain output.
|
||||
Please provide per-skill quality scores in an additional "chain_scores" field.`
|
||||
: '';
|
||||
```
|
||||
|
||||
## artifactsSummary Construction
|
||||
|
||||
```javascript
|
||||
// Read manifest.json if available, otherwise list files
|
||||
const manifestPath = `${iterDir}/artifacts/manifest.json`;
|
||||
let artifactsSummary;
|
||||
|
||||
if (fileExists(manifestPath)) {
|
||||
const manifest = JSON.parse(Read(manifestPath));
|
||||
artifactsSummary = manifest.artifacts.map(a =>
|
||||
`- ${a.path}: ${a.description} (Phase ${a.phase})`
|
||||
).join('\n');
|
||||
} else {
|
||||
// Fallback: list all files with first 200 lines each
|
||||
const files = Glob(`${iterDir}/artifacts/**/*`);
|
||||
artifactsSummary = files.map(f => {
|
||||
const content = Read(f, { limit: 200 });
|
||||
return `--- ${f.replace(iterDir + '/artifacts/', '')} ---\n${content}`;
|
||||
}).join('\n\n');
|
||||
}
|
||||
```
|
||||
97
.claude/skills/skill-iter-tune/templates/execute-prompt.md
Normal file
97
.claude/skills/skill-iter-tune/templates/execute-prompt.md
Normal file
@@ -0,0 +1,97 @@
|
||||
# Execute Prompt Template
|
||||
|
||||
Phase 02 使用此模板构造 ccw cli 提示词,让 Claude 模拟执行 skill 并产出所有预期产物。
|
||||
|
||||
## Template
|
||||
|
||||
```
|
||||
PURPOSE: Simulate executing the following workflow skill against a test scenario. Produce all expected output artifacts as if the skill were invoked with the given input. This is for evaluating skill quality.
|
||||
|
||||
SKILL CONTENT:
|
||||
${skillContent}
|
||||
|
||||
TEST SCENARIO:
|
||||
Description: ${testScenario.description}
|
||||
Input Arguments: ${testScenario.input_args}
|
||||
Requirements: ${testScenario.requirements}
|
||||
Success Criteria: ${testScenario.success_criteria}
|
||||
|
||||
TASK:
|
||||
1. Study the complete skill structure (SKILL.md + all phase files)
|
||||
2. Follow the skill's execution flow sequentially (Phase 1 → Phase N)
|
||||
3. For each phase, produce the artifacts that phase would generate
|
||||
4. Write all output artifacts to the current working directory
|
||||
5. Create a manifest.json listing all produced artifacts with descriptions
|
||||
|
||||
MODE: write
|
||||
|
||||
CONTEXT: @**/*
|
||||
|
||||
EXPECTED:
|
||||
- All artifacts the skill would produce for this test scenario
|
||||
- Each artifact in its correct relative path
|
||||
- A manifest.json at root: { "artifacts": [{ "path": "...", "description": "...", "phase": N }] }
|
||||
|
||||
CONSTRAINTS:
|
||||
- Follow the skill execution flow exactly — do not skip or reorder phases
|
||||
- Produce realistic, high-quality output (not placeholder content)
|
||||
- If the skill requires user interaction (AskUserQuestion), use reasonable defaults
|
||||
- If the skill invokes external tools/CLIs, document what would be called but produce expected output directly
|
||||
```
|
||||
|
||||
## Variable Substitution
|
||||
|
||||
| Variable | Source | Description |
|
||||
|----------|--------|-------------|
|
||||
| `${skillContent}` | Phase 02 reads all skill files | 完整 SKILL.md + phase 文件内容,用 markdown headers 分隔 |
|
||||
| `${testScenario.description}` | iteration-state.json | 用户描述的测试场景 |
|
||||
| `${testScenario.input_args}` | iteration-state.json | 模拟传给 skill 的参数 |
|
||||
| `${testScenario.requirements}` | iteration-state.json | 质量要求列表 |
|
||||
| `${testScenario.success_criteria}` | iteration-state.json | 成功标准定义 |
|
||||
|
||||
## Chain Mode Extension
|
||||
|
||||
When running in chain mode, the template is invoked once per skill in `chain_order`. Each invocation includes:
|
||||
|
||||
### Additional Variable
|
||||
|
||||
| Variable | Source | Description |
|
||||
|----------|--------|-------------|
|
||||
| `${previousChainOutput}` | Phase 02 chain loop | 前序 skill 的 artifacts 摘要 (chain 模式下非首个 skill) |
|
||||
|
||||
### Chain Prompt Modification
|
||||
|
||||
When `execution_mode === 'chain'`, the prompt includes:
|
||||
|
||||
```
|
||||
PREVIOUS CHAIN OUTPUT (from upstream skill "${previousSkillName}"):
|
||||
${previousChainOutput}
|
||||
|
||||
IMPORTANT: Use the above output as input context for this skill's execution.
|
||||
```
|
||||
|
||||
This section is only added for skills at position 2+ in the chain. The first skill in the chain receives no upstream context.
|
||||
|
||||
## skillContent Construction
|
||||
|
||||
```javascript
|
||||
// Read only executable skill files and format with consistent headers
|
||||
const skillMd = Read(`${skill.path}/SKILL.md`);
|
||||
const phaseFiles = Glob(`${skill.path}/phases/*.md`).map(f => ({
|
||||
relativePath: f.replace(skill.path + '/', ''),
|
||||
content: Read(f)
|
||||
}));
|
||||
const specFiles = Glob(`${skill.path}/specs/*.md`).map(f => ({
|
||||
relativePath: f.replace(skill.path + '/', ''),
|
||||
content: Read(f)
|
||||
}));
|
||||
|
||||
const skillContent = `
|
||||
### File: SKILL.md
|
||||
${skillMd}
|
||||
|
||||
${phaseFiles.map(f => `### File: ${f.relativePath}\n${f.content}`).join('\n\n')}
|
||||
|
||||
${specFiles.length > 0 ? specFiles.map(f => `### File: ${f.relativePath}\n${f.content}`).join('\n\n') : ''}
|
||||
`.trim();
|
||||
```
|
||||
@@ -148,7 +148,7 @@ AskUserQuestion({
|
||||
|----------|------------|
|
||||
| Unknown --role value | Error with role registry list |
|
||||
| Role file not found | Error with expected path (roles/{name}/role.md) |
|
||||
| project-tech.json missing | Coordinator invokes /workflow:init |
|
||||
| project-tech.json missing | Coordinator invokes /workflow:spec:setup |
|
||||
| Phase verification fails with gaps | Coordinator triggers gap closure loop (max 3 iterations) |
|
||||
| Max gap closure iterations (3) | Report to user, ask for guidance |
|
||||
| Worker crash | Respawn worker, reassign task |
|
||||
|
||||
@@ -284,7 +284,7 @@ Delegate to `commands/monitor.md`:
|
||||
|
||||
| Scenario | Resolution |
|
||||
|----------|------------|
|
||||
| project-tech.json missing | Invoke /workflow:init automatically |
|
||||
| project-tech.json missing | Invoke /workflow:spec:setup automatically |
|
||||
| User cancels roadmap discussion | Save session state, exit gracefully |
|
||||
| Planner fails | Retry once, then ask user for guidance |
|
||||
| Executor fails on plan | Mark plan as failed, continue with next |
|
||||
|
||||
@@ -85,7 +85,7 @@
|
||||
"init_prerequisite": {
|
||||
"required_files": [".workflow/project-tech.json"],
|
||||
"optional_files": [".workflow/specs/*.md"],
|
||||
"init_command": "/workflow:init"
|
||||
"init_command": "/workflow:spec:setup "
|
||||
},
|
||||
"_metadata": {
|
||||
"created_at": "2026-02-24",
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 166 KiB |
BIN
assets/wechat-group-qr.png
Normal file
BIN
assets/wechat-group-qr.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 226 KiB |
@@ -85,7 +85,7 @@ function setupDefaultMocks() {
|
||||
vi.mocked(useNotifications).mockReturnValue({
|
||||
success: mockToastSuccess,
|
||||
error: mockToastError,
|
||||
} as ReturnType<typeof useNotifications>);
|
||||
} as unknown as ReturnType<typeof useNotifications>);
|
||||
}
|
||||
|
||||
describe('AdvancedTab', () => {
|
||||
|
||||
@@ -6,13 +6,10 @@ import { VersionCheckModal } from './VersionCheckModal';
|
||||
interface VersionData {
|
||||
currentVersion: string;
|
||||
latestVersion: string;
|
||||
updateAvailable: boolean;
|
||||
}
|
||||
|
||||
interface VersionCheckResponse {
|
||||
success: boolean;
|
||||
data?: VersionData;
|
||||
error?: string;
|
||||
hasUpdate: boolean;
|
||||
packageName?: string;
|
||||
updateCommand?: string;
|
||||
checkedAt?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -42,30 +39,26 @@ export function VersionCheck() {
|
||||
const checkVersion = async (silent = false) => {
|
||||
if (!silent) setChecking(true);
|
||||
try {
|
||||
const response = await fetch('/api/config/version');
|
||||
const response = await fetch('/api/version-check');
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||
}
|
||||
|
||||
const data: VersionCheckResponse = await response.json();
|
||||
const data: VersionData = await response.json();
|
||||
|
||||
// Validate response structure
|
||||
if (!data || typeof data !== 'object') {
|
||||
throw new Error('Invalid response format');
|
||||
}
|
||||
|
||||
if (!data.success) {
|
||||
throw new Error(data.error || 'Version check failed');
|
||||
}
|
||||
|
||||
if (!data.data || typeof data.data !== 'object') {
|
||||
if (!data.currentVersion) {
|
||||
throw new Error('Invalid version data in response');
|
||||
}
|
||||
|
||||
setVersionData(data.data);
|
||||
setVersionData(data);
|
||||
|
||||
if (data.data.updateAvailable && !silent) {
|
||||
toast.info('新版本可用: ' + data.data.latestVersion);
|
||||
if (data.hasUpdate && !silent) {
|
||||
toast.info('新版本可用: ' + data.latestVersion);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Version check failed:', error);
|
||||
@@ -98,7 +91,7 @@ export function VersionCheck() {
|
||||
localStorage.setItem('ccw.autoUpdate', JSON.stringify(enabled));
|
||||
};
|
||||
|
||||
if (!versionData?.updateAvailable) {
|
||||
if (!versionData?.hasUpdate) {
|
||||
return null; // Don't show anything if no update
|
||||
}
|
||||
|
||||
|
||||
@@ -102,6 +102,6 @@
|
||||
},
|
||||
"empty": {
|
||||
"title": "No Project Overview",
|
||||
"message": "Run /workflow:init to initialize project analysis"
|
||||
"message": "Run /workflow:spec:setup to initialize project analysis"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,6 +102,6 @@
|
||||
},
|
||||
"empty": {
|
||||
"title": "暂无项目概览",
|
||||
"message": "运行 /workflow:init 初始化项目分析"
|
||||
"message": "运行 /workflow:spec:setup 初始化项目分析"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -306,15 +306,17 @@ export async function installCommand(options: InstallOptions): Promise<void> {
|
||||
info(` Files in manifest: ${existingManifest.files?.length || 0}`);
|
||||
info(` Installed: ${new Date(existingManifest.installation_date).toLocaleDateString()}`);
|
||||
|
||||
const { backup } = await inquirer.prompt([{
|
||||
type: 'confirm',
|
||||
name: 'backup',
|
||||
message: 'Create backup before reinstalling?',
|
||||
default: true
|
||||
}]);
|
||||
if (!options.force) {
|
||||
const { backup } = await inquirer.prompt([{
|
||||
type: 'confirm',
|
||||
name: 'backup',
|
||||
message: 'Create backup before reinstalling?',
|
||||
default: true
|
||||
}]);
|
||||
|
||||
if (backup) {
|
||||
await createBackup(existingManifest);
|
||||
if (backup) {
|
||||
await createBackup(existingManifest);
|
||||
}
|
||||
}
|
||||
|
||||
// Clean based on manifest records
|
||||
@@ -495,7 +497,7 @@ export async function installCommand(options: InstallOptions): Promise<void> {
|
||||
});
|
||||
|
||||
// Install Git Bash fix on Windows
|
||||
if (platform() === 'win32') {
|
||||
if (platform() === 'win32' && !options.force) {
|
||||
console.log('');
|
||||
const { installFix } = await inquirer.prompt([{
|
||||
type: 'confirm',
|
||||
|
||||
@@ -35,29 +35,6 @@ import {
|
||||
saveConversation
|
||||
} from './cli-executor-state.js';
|
||||
|
||||
// Debug logging for history save investigation (Iteration 4)
|
||||
const DEBUG_SESSION_ID = 'DBG-parallel-ccw-cli-test-2026-03-07';
|
||||
const DEBUG_LOG_PATH = path.join(process.cwd(), '.workflow', '.debug', DEBUG_SESSION_ID, 'debug-save.log');
|
||||
|
||||
// Ensure debug log directory exists
|
||||
try {
|
||||
const debugDir = path.dirname(DEBUG_LOG_PATH);
|
||||
if (!fs.existsSync(debugDir)) {
|
||||
fs.mkdirSync(debugDir, { recursive: true });
|
||||
}
|
||||
} catch (err) {
|
||||
// Ignore directory creation errors
|
||||
}
|
||||
|
||||
function writeDebugLog(event: string, data: Record<string, any>): void {
|
||||
try {
|
||||
const logEntry = JSON.stringify({ event, ...data, timestamp: new Date().toISOString() }) + '\n';
|
||||
fs.appendFileSync(DEBUG_LOG_PATH, logEntry, 'utf8');
|
||||
} catch (err) {
|
||||
// Silently ignore logging errors
|
||||
}
|
||||
}
|
||||
|
||||
// Track all running child processes for cleanup on interruption (multi-process support)
|
||||
const runningChildProcesses = new Set<ChildProcess>();
|
||||
|
||||
@@ -1296,11 +1273,8 @@ async function executeCliTool(
|
||||
};
|
||||
// Try to save conversation to history
|
||||
try {
|
||||
writeDebugLog('BEFORE_SAVE_CONV', { conversationId: conversation.id, workingDir, tool });
|
||||
saveConversation(workingDir, conversation);
|
||||
writeDebugLog('AFTER_SAVE_CONV', { conversationId: conversation.id, workingDir, tool });
|
||||
} catch (err) {
|
||||
writeDebugLog('SAVE_CONV_OUTER_ERROR', { conversationId: conversation.id, workingDir, tool, error: (err as Error).message, stack: (err as Error).stack });
|
||||
// Non-fatal: continue even if history save fails
|
||||
console.error('[CLI Executor] Failed to save history:', (err as Error).message);
|
||||
}
|
||||
|
||||
@@ -9,29 +9,6 @@ import type { CliOutputUnit } from './cli-output-converter.js';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
// Debug logging for history save investigation (Iteration 4)
|
||||
const DEBUG_SESSION_ID = 'DBG-parallel-ccw-cli-test-2026-03-07';
|
||||
const DEBUG_LOG_PATH = path.join(process.cwd(), '.workflow', '.debug', DEBUG_SESSION_ID, 'debug-save.log');
|
||||
|
||||
// Ensure debug log directory exists
|
||||
try {
|
||||
const debugDir = path.dirname(DEBUG_LOG_PATH);
|
||||
if (!fs.existsSync(debugDir)) {
|
||||
fs.mkdirSync(debugDir, { recursive: true });
|
||||
}
|
||||
} catch (err) {
|
||||
// Ignore directory creation errors
|
||||
}
|
||||
|
||||
function writeDebugLog(event: string, data: Record<string, any>): void {
|
||||
try {
|
||||
const logEntry = JSON.stringify({ event, ...data, timestamp: new Date().toISOString() }) + '\n';
|
||||
fs.appendFileSync(DEBUG_LOG_PATH, logEntry, 'utf8');
|
||||
} catch (err) {
|
||||
// Silently ignore logging errors
|
||||
}
|
||||
}
|
||||
|
||||
// Lazy-loaded SQLite store module
|
||||
let sqliteStoreModule: typeof import('./cli-history-store.js') | null = null;
|
||||
|
||||
@@ -39,10 +16,8 @@ let sqliteStoreModule: typeof import('./cli-history-store.js') | null = null;
|
||||
* Get or initialize SQLite store (async)
|
||||
*/
|
||||
export async function getSqliteStore(baseDir: string) {
|
||||
writeDebugLog('GET_STORE', { baseDir, baseDirType: typeof baseDir, moduleInitialized: sqliteStoreModule !== null });
|
||||
if (!sqliteStoreModule) {
|
||||
sqliteStoreModule = await import('./cli-history-store.js');
|
||||
writeDebugLog('MODULE_LOADED', { baseDir });
|
||||
}
|
||||
return sqliteStoreModule.getHistoryStore(baseDir);
|
||||
}
|
||||
@@ -163,20 +138,15 @@ async function saveConversationAsync(baseDir: string, conversation: Conversation
|
||||
* @param baseDir - Project base directory (NOT historyDir)
|
||||
*/
|
||||
export function saveConversation(baseDir: string, conversation: ConversationRecord): void {
|
||||
writeDebugLog('SAVE_CONV_START', { baseDir, conversationId: conversation.id, moduleInitialized: sqliteStoreModule !== null });
|
||||
try {
|
||||
const store = getSqliteStoreSync(baseDir);
|
||||
writeDebugLog('SAVE_CONV_SYNC', { baseDir, conversationId: conversation.id });
|
||||
// Fire and forget - don't block on async save in sync context
|
||||
store.saveConversation(conversation).catch(err => {
|
||||
writeDebugLog('SAVE_CONV_ERROR', { baseDir, conversationId: conversation.id, error: err.message, stack: err.stack });
|
||||
console.error('[CLI Executor] Failed to save conversation:', err.message);
|
||||
});
|
||||
} catch (err) {
|
||||
writeDebugLog('SAVE_CONV_FALLBACK_ASYNC', { baseDir, conversationId: conversation.id, error: (err as Error).message });
|
||||
// If sync not available, queue for async save
|
||||
saveConversationAsync(baseDir, conversation).catch(err => {
|
||||
writeDebugLog('SAVE_CONV_ASYNC_ERROR', { baseDir, conversationId: conversation.id, error: err.message, stack: err.stack });
|
||||
console.error('[CLI Executor] Failed to save conversation:', err.message);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -11,29 +11,6 @@ import { getDiscoverer, getNativeSessions } from './native-session-discovery.js'
|
||||
import { StoragePaths, ensureStorageDir, getProjectId, getCCWHome } from '../config/storage-paths.js';
|
||||
import { createOutputParser, flattenOutputUnits, type CliOutputUnit } from './cli-output-converter.js';
|
||||
|
||||
// Debug logging for history save investigation (Iteration 4)
|
||||
const DEBUG_SESSION_ID = 'DBG-parallel-ccw-cli-test-2026-03-07';
|
||||
const DEBUG_LOG_PATH = join(process.cwd(), '.workflow', '.debug', DEBUG_SESSION_ID, 'debug-save.log');
|
||||
|
||||
// Ensure debug log directory exists
|
||||
try {
|
||||
const debugDir = dirname(DEBUG_LOG_PATH);
|
||||
if (!existsSync(debugDir)) {
|
||||
mkdirSync(debugDir, { recursive: true });
|
||||
}
|
||||
} catch (err) {
|
||||
// Ignore directory creation errors
|
||||
}
|
||||
|
||||
function writeDebugLog(event: string, data: Record<string, any>): void {
|
||||
try {
|
||||
const logEntry = JSON.stringify({ event, ...data, timestamp: new Date().toISOString() }) + '\n';
|
||||
appendFileSync(DEBUG_LOG_PATH, logEntry, 'utf8');
|
||||
} catch (err) {
|
||||
// Silently ignore logging errors
|
||||
}
|
||||
}
|
||||
|
||||
function reconstructFinalOutputFromStdout(rawStdout: string, canTrustStdout: boolean): string | undefined {
|
||||
if (!canTrustStdout || !rawStdout.trim()) {
|
||||
return undefined;
|
||||
@@ -154,29 +131,22 @@ export class CliHistoryStore {
|
||||
private projectPath: string;
|
||||
|
||||
constructor(baseDir: string) {
|
||||
writeDebugLog('STORE_CONSTRUCT_START', { baseDir });
|
||||
this.projectPath = baseDir;
|
||||
|
||||
// Use centralized storage path
|
||||
const paths = StoragePaths.project(baseDir);
|
||||
const historyDir = paths.cliHistory;
|
||||
writeDebugLog('STORAGE_PATHS', { baseDir, historyDir, historyDb: paths.historyDb });
|
||||
ensureStorageDir(historyDir);
|
||||
|
||||
this.dbPath = paths.historyDb;
|
||||
writeDebugLog('DB_INSTANCE_CREATE', { dbPath: this.dbPath });
|
||||
this.db = new Database(this.dbPath);
|
||||
writeDebugLog('DB_INSTANCE_CREATED', { dbPath: this.dbPath });
|
||||
this.db.pragma('journal_mode = WAL');
|
||||
this.db.pragma('synchronous = NORMAL');
|
||||
this.db.pragma('busy_timeout = 10000'); // Wait up to 10 seconds for locks (increased for write-heavy scenarios)
|
||||
this.db.pragma('wal_autocheckpoint = 1000'); // Optimize WAL checkpointing
|
||||
|
||||
writeDebugLog('INIT_SCHEMA_START', { dbPath: this.dbPath });
|
||||
this.initSchema();
|
||||
writeDebugLog('INIT_SCHEMA_COMPLETE', { dbPath: this.dbPath });
|
||||
this.migrateFromJson(historyDir);
|
||||
writeDebugLog('STORE_CONSTRUCT_COMPLETE', { baseDir, dbPath: this.dbPath });
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
| [`/workflow-multi-cli-plan`](./workflow.md#multi-cli-plan) | 多 CLI 协作规划 | Intermediate |
|
||||
| [`/workflow:review`](./workflow.md#review) | 实现后审查 | Intermediate |
|
||||
| [`/workflow:clean`](./workflow.md#clean) | 智能代码清理 | Intermediate |
|
||||
| [`/workflow:init`](./workflow.md#init) | 初始化项目状态 | Intermediate |
|
||||
| [`/workflow:spec:setup `](./workflow.md#init) | 初始化项目状态 | Intermediate |
|
||||
| [`/workflow:brainstorm-with-file`](./workflow.md#brainstorm-with-file) | 交互式头脑风暴 | Intermediate |
|
||||
| [`/workflow:analyze-with-file`](./workflow.md#analyze-with-file) | 交互式协作分析 | Beginner |
|
||||
| [`/workflow:debug-with-file`](./workflow.md#debug-with-file) | 交互式假设驱动调试 | Intermediate |
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
| [`/workflow-multi-cli-plan`](./workflow.md#multi-cli-plan) | Multi-CLI collaborative planning | Intermediate |
|
||||
| [`/workflow:review`](./workflow.md#review) | Post-implementation review | Intermediate |
|
||||
| [`/workflow:clean`](./workflow.md#clean) | Smart code cleanup | Intermediate |
|
||||
| [`/workflow:init`](./workflow.md#init) | Initialize project state | Intermediate |
|
||||
| [`/workflow:spec:setup `](./workflow.md#init) | Initialize project state | Intermediate |
|
||||
| [`/workflow:brainstorm-with-file`](./workflow.md#brainstorm-with-file) | Interactive brainstorming | Intermediate |
|
||||
| [`/workflow:analyze-with-file`](./workflow.md#analyze-with-file) | Interactive collaborative analysis | Beginner |
|
||||
| [`/workflow:debug-with-file`](./workflow.md#debug-with-file) | Interactive hypothesis-driven debugging | Intermediate |
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
| Command | Function | Syntax |
|
||||
|---------|----------|--------|
|
||||
| [`clean`](#clean) | Smart code cleanup | `/workflow:clean [-y] [--dry-run] ["focus area"]` |
|
||||
| [`init`](#init) | Initialize project state | `/workflow:init [--regenerate]` |
|
||||
| [`init`](#init) | Initialize project state | `/workflow:spec:setup [--regenerate]` |
|
||||
| [`plan-verify`](#plan-verify) | Verify planning consistency | `/workflow-plan-verify [--session session-id]` |
|
||||
|
||||
## Command Details
|
||||
|
||||
@@ -81,7 +81,7 @@ This creates a new workflow session. All subsequent operations will be performed
|
||||
### 2.2.2 Initialize Project Specs
|
||||
|
||||
```
|
||||
/workflow:init
|
||||
/workflow:spec:setup
|
||||
```
|
||||
|
||||
This creates the `project-tech.json` file, recording your project's technology stack information.
|
||||
@@ -89,7 +89,7 @@ This creates the `project-tech.json` file, recording your project's technology s
|
||||
### 2.2.3 Populate Project Specs
|
||||
|
||||
```
|
||||
/workflow:init-guidelines
|
||||
/workflow:spec:setup -guidelines
|
||||
```
|
||||
|
||||
Interactively populate project specifications, including coding style, architectural decisions, and other information.
|
||||
@@ -282,7 +282,7 @@ npm install
|
||||
/workflow:session:start
|
||||
|
||||
# 5. Initialize project
|
||||
/workflow:init
|
||||
/workflow:spec:setup
|
||||
```
|
||||
|
||||
### Common Commands
|
||||
|
||||
@@ -50,7 +50,7 @@ Create a simple workflow in under 5 minutes:
|
||||
# Prompt: "Fix the login timeout issue" # Execute without confirmation prompts
|
||||
|
||||
# Or use specific workflow commands
|
||||
/workflow:init # Initialize project state
|
||||
/workflow:spec:setup # Initialize project state
|
||||
/workflow-plan
|
||||
# Prompt: "Add OAuth2 authentication" # Create implementation plan
|
||||
/workflow-execute # Execute planned tasks
|
||||
|
||||
@@ -74,7 +74,7 @@ To check available CCW commands, you can list them:
|
||||
Available CCW Commands:
|
||||
- /ccw - Main workflow orchestrator
|
||||
- /ccw-coordinator - External CLI orchestration
|
||||
- /workflow:init - Initialize project configuration
|
||||
- /workflow:spec:setup - Initialize project configuration
|
||||
- /workflow:status - Generate project views
|
||||
- /issue:discover - Discover and plan issues
|
||||
- /brainstorm - Multi-perspective brainstorming
|
||||
@@ -145,7 +145,7 @@ Create `CLAUDE.md` in your project root to define project-specific instructions:
|
||||
Initialize CCW in your project:
|
||||
|
||||
```
|
||||
/workflow:init
|
||||
/workflow:spec:setup
|
||||
```
|
||||
|
||||
This creates `.workflow/project-tech.json` with your project's technology stack.
|
||||
@@ -343,7 +343,7 @@ After installation, try these commands to verify everything works:
|
||||
|
||||
```
|
||||
# 1. Initialize in your project
|
||||
/workflow:init
|
||||
/workflow:spec:setup
|
||||
|
||||
# 2. Try a simple analysis using the CLI tool
|
||||
Bash: ccw cli -p "Analyze the project structure" --tool gemini --mode analysis
|
||||
@@ -369,7 +369,7 @@ What would you like to accomplish? Please describe your task.
|
||||
|
||||
**Using workflow init:**
|
||||
```
|
||||
You: /workflow:init
|
||||
You: /workflow:spec:setup
|
||||
✔ Created .workflow/project-tech.json
|
||||
✔ Project configuration complete
|
||||
```
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
| [`/workflow-multi-cli-plan`](./workflow.md#multi-cli-plan) | 多 CLI 协作规划 | Intermediate |
|
||||
| [`/workflow:review`](./workflow.md#review) | 实现后审查 | Intermediate |
|
||||
| [`/workflow:clean`](./workflow.md#clean) | 智能代码清理 | Intermediate |
|
||||
| [`/workflow:init`](./workflow.md#init) | 初始化项目状态 | Intermediate |
|
||||
| [`/workflow:spec:setup `](./workflow.md#init) | 初始化项目状态 | Intermediate |
|
||||
| [`/workflow:brainstorm-with-file`](./workflow.md#brainstorm-with-file) | 交互式头脑风暴 | Intermediate |
|
||||
| [`/workflow:analyze-with-file`](./workflow.md#analyze-with-file) | 交互式协作分析 | Beginner |
|
||||
| [`/workflow:debug-with-file`](./workflow.md#debug-with-file) | 交互式假设驱动调试 | Intermediate |
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
| 命令 | 功能 | 语法 |
|
||||
| --- | --- | --- |
|
||||
| [`clean`](#clean) | 智能代码清理 | `/workflow:clean [-y] [--dry-run] ["焦点区域"]` |
|
||||
| [`init`](#init) | 初始化项目状态 | `/workflow:init [--regenerate]` |
|
||||
| [`init`](#init) | 初始化项目状态 | `/workflow:spec:setup [--regenerate]` |
|
||||
| [`plan-verify`](#plan-verify) | 验证规划一致性 | `/workflow-plan-verify [--session session-id]` |
|
||||
|
||||
## 命令详解
|
||||
|
||||
@@ -67,7 +67,7 @@ API Keys 也可以在项目级别配置 `.claude/settings.json`,项目级配
|
||||
### 2.2.2 初始化项目规范
|
||||
|
||||
```
|
||||
/workflow:init
|
||||
/workflow:spec:setup
|
||||
```
|
||||
|
||||
这会创建 `project-tech.json` 文件,记录项目的技术栈信息。
|
||||
@@ -75,7 +75,7 @@ API Keys 也可以在项目级别配置 `.claude/settings.json`,项目级配
|
||||
### 2.2.3 填充项目规范
|
||||
|
||||
```
|
||||
/workflow:init-guidelines
|
||||
/workflow:spec:setup -guidelines
|
||||
```
|
||||
|
||||
交互式填充项目规范,包括编码风格、架构决策等信息。
|
||||
@@ -272,7 +272,7 @@ npm install
|
||||
/workflow:session:start
|
||||
|
||||
# 5. 初始化项目
|
||||
/workflow:init
|
||||
/workflow:spec:setup
|
||||
```
|
||||
|
||||
### 常用命令
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "claude-code-workflow",
|
||||
"version": "7.2.5",
|
||||
"version": "7.2.6",
|
||||
"description": "JSON-driven multi-agent development framework with intelligent CLI orchestration (Gemini/Qwen/Codex), context-first architecture, and automated workflow execution",
|
||||
"type": "module",
|
||||
"main": "ccw/dist/index.js",
|
||||
|
||||
Reference in New Issue
Block a user