feat: add workflow-tune skill for multi-step workflow pipeline optimization

New skill that executes workflow step chains sequentially, inspects artifacts,
analyzes quality via ccw cli resume chain (Gemini), and generates optimization
reports. Supports 4 input formats: pipe-separated commands, comma-separated
skills, JSON file, and natural language with semantic decomposition.

Key features:
- Orchestrator + 5-phase progressive loading architecture
- Intent-to-tool mapping with ambiguity resolution for NL input
- Command document generation with pre-execution confirmation loop
- Per-step analysis + cross-step synthesis via resume chain
- Auto-fix with user confirmation safety gate

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
catlog22
2026-03-20 14:55:47 +08:00
parent 26a7371a20
commit d5b6480528
9 changed files with 2187 additions and 0 deletions

View File

@@ -0,0 +1,454 @@
# Phase 1: Setup
Initialize workspace, parse workflow definition, semantic decomposition for natural language, generate command document, user confirmation, create state and process log.
## Objective
- Parse workflow steps from user input (Format 1-3: direct parse, Format 4: semantic decomposition)
- Generate Command Document (formatted execution plan)
- User confirmation: Execute / Edit steps / Cancel
- Validate step commands/skill paths
- Create isolated workspace directory
- Initialize workflow-state.json and process-log.md
## Execution
### Step 1.1: Parse Input
```javascript
const args = $ARGUMENTS.trim();
// Detect input format
let steps = [];
let workflowName = 'unnamed-workflow';
let workflowContext = '';
// Format 1: JSON file (--file path)
const fileMatch = args.match(/--file\s+"?([^\s"]+)"?/);
if (fileMatch) {
const wfDef = JSON.parse(Read(fileMatch[1]));
workflowName = wfDef.name || 'unnamed-workflow';
workflowContext = wfDef.description || '';
steps = wfDef.steps;
}
// Format 2: Pipe-separated commands ("cmd1 | cmd2 | cmd3")
else if (args.includes('|')) {
const rawSteps = args.split(/(?:--context|--depth|-y|--yes|--auto-fix)\s+("[^"]*"|\S+)/)[0];
steps = rawSteps.split('|').map((cmd, i) => ({
name: `step-${i + 1}`,
type: cmd.trim().startsWith('/') ? 'skill'
: cmd.trim().startsWith('ccw cli') ? 'ccw-cli'
: 'command',
command: cmd.trim(),
expected_artifacts: [],
success_criteria: ''
}));
}
// Format 3: Comma-separated skill names (matches pattern: word,word or word-word,word-word)
else if (/^[\w-]+(,[\w-]+)+/.test(args.split(/\s/)[0])) {
const skillPart = args.match(/^([^\s]+)/);
const skillNames = skillPart ? skillPart[1].split(',') : [];
steps = skillNames.map((name, i) => {
const skillPath = name.startsWith('.claude/') ? name : `.claude/skills/${name}`;
return {
name: name.replace('.claude/skills/', ''),
type: 'skill',
command: `/${name.replace('.claude/skills/', '')}`,
skill_path: skillPath,
expected_artifacts: [],
success_criteria: ''
};
});
}
// Format 4: Natural language → semantic decomposition
else {
inputFormat = 'natural-language';
naturalLanguageInput = args.replace(/--\w+\s+"[^"]*"/g, '').replace(/--\w+\s+\S+/g, '').replace(/-y|--yes/g, '').trim();
// Steps will be populated in Step 1.1b
steps = [];
}
// Parse --context
const contextMatch = args.match(/--context\s+"([^"]+)"/);
workflowContext = contextMatch ? contextMatch[1] : workflowContext;
// Parse --depth
const depthMatch = args.match(/--depth\s+(quick|standard|deep)/);
if (depthMatch) {
workflowPreferences.analysisDepth = depthMatch[1];
}
// If no context provided, ask user
if (!workflowContext) {
const response = AskUserQuestion({
questions: [{
question: "请描述这个 workflow 的目标和预期效果:",
header: "Workflow Context",
multiSelect: false,
options: [
{ label: "General quality check", description: "通用质量检查,评估步骤间衔接" },
{ label: "Custom description", description: "自定义描述 workflow 目标" }
]
}]
});
workflowContext = response["Workflow Context"];
}
```
### Step 1.1b: Semantic Decomposition (Format 4 only)
> Skip this step if `inputFormat !== 'natural-language'`.
Decompose natural language input into a structured step chain by identifying intent verbs and mapping them to available tools/skills.
```javascript
if (inputFormat === 'natural-language') {
// Intent-to-tool mapping (regex patterns → tool config)
const intentMap = [
{ pattern: /分析|analyze|审查|inspect|scan/i, name: 'analyze', tool: 'gemini', mode: 'analysis', rule: 'analysis-analyze-code-patterns' },
{ pattern: /评审|review|code.?review/i, name: 'review', tool: 'gemini', mode: 'analysis', rule: 'analysis-review-code-quality' },
{ pattern: /诊断|debug|排查|diagnose/i, name: 'diagnose', tool: 'gemini', mode: 'analysis', rule: 'analysis-diagnose-bug-root-cause' },
{ pattern: /安全|security|漏洞|vulnerability/i, name: 'security-audit', tool: 'gemini', mode: 'analysis', rule: 'analysis-assess-security-risks' },
{ pattern: /性能|performance|perf/i, name: 'perf-analysis', tool: 'gemini', mode: 'analysis', rule: 'analysis-analyze-performance' },
{ pattern: /架构|architecture/i, name: 'arch-review', tool: 'gemini', mode: 'analysis', rule: 'analysis-review-architecture' },
{ pattern: /修复|fix|repair|解决/i, name: 'fix', tool: 'claude', mode: 'write', rule: 'development-debug-runtime-issues' },
{ pattern: /实现|implement|开发|create|新增/i, name: 'implement', tool: 'claude', mode: 'write', rule: 'development-implement-feature' },
{ pattern: /重构|refactor/i, name: 'refactor', tool: 'claude', mode: 'write', rule: 'development-refactor-codebase' },
{ pattern: /测试|test|generate.?test/i, name: 'test', tool: 'claude', mode: 'write', rule: 'development-generate-tests' },
{ pattern: /规划|plan|设计|design/i, name: 'plan', tool: 'gemini', mode: 'analysis', rule: 'planning-plan-architecture-design' },
{ pattern: /拆解|breakdown|分解/i, name: 'breakdown', tool: 'gemini', mode: 'analysis', rule: 'planning-breakdown-task-steps' },
];
// Segment input by Chinese/English delimiters: 、,,;然后/接着/最后/之后 etc.
const segments = naturalLanguageInput
.split(/[,;、]|(?:然后|接着|之后|最后|再|并|and then|then|finally|next)\s*/i)
.map(s => s.trim())
.filter(Boolean);
// Match each segment to an intent (with ambiguity resolution)
steps = segments.map((segment, i) => {
const allMatches = intentMap.filter(m => m.pattern.test(segment));
let matched = allMatches[0] || null;
// ★ Ambiguity resolution: if multiple intents match, ask user
if (allMatches.length > 1) {
const disambig = AskUserQuestion({
questions: [{
question: `"${segment}" 匹配到多个意图,请选择最符合的:`,
header: `Disambiguate Step ${i + 1}`,
multiSelect: false,
options: allMatches.map(m => ({
label: m.name,
description: `Tool: ${m.tool}, Mode: ${m.mode}, Rule: ${m.rule}`
}))
}]
});
const chosen = disambig[`Disambiguate Step ${i + 1}`];
matched = allMatches.find(m => m.name === chosen) || allMatches[0];
}
if (matched) {
// Extract target scope from segment (e.g., "分析 src 目录" → scope = "src")
const scopeMatch = segment.match(/(?:目录|文件|模块|directory|file|module)?\s*[:]?\s*(\S+)/);
const scope = scopeMatch ? scopeMatch[1].replace(/[的地得]$/, '') : '**/*';
return {
name: `${matched.name}`,
type: 'ccw-cli',
command: `ccw cli -p "${segment}" --tool ${matched.tool} --mode ${matched.mode} --rule ${matched.rule}`,
tool: matched.tool,
mode: matched.mode,
rule: matched.rule,
original_text: segment,
expected_artifacts: [],
success_criteria: ''
};
} else {
// Unmatched segment → generic analysis step
return {
name: `step-${i + 1}`,
type: 'ccw-cli',
command: `ccw cli -p "${segment}" --tool gemini --mode analysis`,
tool: 'gemini',
mode: 'analysis',
rule: 'universal-rigorous-style',
original_text: segment,
expected_artifacts: [],
success_criteria: ''
};
}
});
// Deduplicate: if same intent name appears twice, suffix with index
const nameCount = {};
steps.forEach(s => {
nameCount[s.name] = (nameCount[s.name] || 0) + 1;
if (nameCount[s.name] > 1) {
s.name = `${s.name}-${nameCount[s.name]}`;
}
});
// Set workflow context from the full natural language input
if (!workflowContext) {
workflowContext = naturalLanguageInput;
}
workflowName = 'nl-workflow'; // natural language derived
}
```
### Step 1.1c: Generate Command Document
Generate a formatted execution plan for user review. This runs for ALL input formats, not just Format 4.
```javascript
function generateCommandDoc(steps, workflowName, workflowContext, analysisDepth) {
const stepTable = steps.map((s, i) => {
const tool = s.tool || (s.type === 'skill' ? '-' : 'claude');
const mode = s.mode || (s.type === 'skill' ? '-' : 'write');
const cmdPreview = s.command.length > 60 ? s.command.substring(0, 57) + '...' : s.command;
return `| ${i + 1} | ${s.name} | ${s.type} | \`${cmdPreview}\` | ${tool} | ${mode} |`;
}).join('\n');
const flowDiagram = steps.map((s, i) => {
const arrow = i < steps.length - 1 ? '\n ↓' : '';
const feedsInto = i < steps.length - 1 ? `Feeds into: Step ${i + 2} (${steps[i + 1].name})` : 'Final step';
const originalText = s.original_text ? `\n Source: "${s.original_text}"` : '';
return `Step ${i + 1}: ${s.name}
Command: ${s.command}
Type: ${s.type} | Tool: ${s.tool || '-'} | Mode: ${s.mode || '-'}${originalText}
${feedsInto}${arrow}`;
}).join('\n');
const totalCli = steps.filter(s => s.type === 'ccw-cli').length;
const totalSkill = steps.filter(s => s.type === 'skill').length;
const totalCmd = steps.filter(s => s.type === 'command').length;
return `# Workflow Tune — Execution Plan
**Workflow**: ${workflowName}
**Goal**: ${workflowContext}
**Steps**: ${steps.length}
**Analysis Depth**: ${analysisDepth}
## Step Chain
| # | Name | Type | Command | Tool | Mode |
|---|------|------|---------|------|------|
${stepTable}
## Execution Flow
\`\`\`
${flowDiagram}
\`\`\`
## Estimated Scope
- CLI execute calls: ${totalCli}
- Skill invocations: ${totalSkill}
- Shell commands: ${totalCmd}
- Analysis calls (gemini --resume chain): ${steps.length} (per-step) + 1 (synthesis)
- Process documentation: process-log.md (accumulated)
- Final output: final-report.md with optimization recommendations
`;
}
const commandDoc = generateCommandDoc(steps, workflowName, workflowContext, workflowPreferences.analysisDepth);
// Output command document to user (direct text output)
// The orchestrator displays this as formatted text before confirmation
```
### Step 1.1d: Pre-Execution Confirmation
```javascript
// ★ Skip confirmation if -y/--yes auto mode
if (!workflowPreferences.autoYes) {
// Display commandDoc to user as formatted text output
// Then ask for confirmation
const confirmation = AskUserQuestion({
questions: [{
question: "确认执行以上 Workflow 调优计划?",
header: "Confirm Execution",
multiSelect: false,
options: [
{ label: "Execute (确认执行)", description: "按计划开始执行所有步骤" },
{ label: "Edit steps (修改步骤)", description: "调整步骤顺序、增删步骤、更换工具" },
{ label: "Cancel (取消)", description: "取消本次调优" }
]
}]
});
const choice = confirmation["Confirm Execution"];
if (choice.startsWith("Cancel")) {
// Abort: no workspace created, no state written
// Output: "Workflow tune cancelled."
return;
}
if (choice.startsWith("Edit")) {
// Enter edit loop: ask user what to change, apply, re-display, re-confirm
let editing = true;
while (editing) {
const editResponse = AskUserQuestion({
questions: [{
question: "请描述要修改的内容:\n" +
" - 删除步骤: '删除步骤2' 或 'remove step 2'\n" +
" - 添加步骤: '在步骤1后加入安全扫描' 或 'add security scan after step 1'\n" +
" - 修改工具: '步骤3改用codex' 或 'step 3 use codex'\n" +
" - 调换顺序: '步骤2和步骤3互换' 或 'swap step 2 and 3'\n" +
" - 修改命令: '步骤1命令改为 ccw cli -p \"...\" --tool gemini'",
header: "Edit Steps"
}]
});
const editText = editResponse["Edit Steps"];
// Apply edits to steps[] based on user instruction
// The orchestrator interprets the edit instruction and modifies steps:
//
// Delete: filter out the specified step, re-index
// Add: insert new step at specified position
// Modify tool: update the step's tool/mode/command
// Swap: exchange positions of two steps
// Modify command: replace command string
//
// After applying edits, re-generate command doc and re-display
const updatedCommandDoc = generateCommandDoc(steps, workflowName, workflowContext, workflowPreferences.analysisDepth);
// Display updatedCommandDoc to user
const reconfirm = AskUserQuestion({
questions: [{
question: "修改后的计划如上,是否确认?",
header: "Confirm Execution",
multiSelect: false,
options: [
{ label: "Execute (确认执行)", description: "按修改后的计划执行" },
{ label: "Edit more (继续修改)", description: "还需要调整" },
{ label: "Cancel (取消)", description: "取消本次调优" }
]
}]
});
const reChoice = reconfirm["Confirm Execution"];
if (reChoice.startsWith("Execute")) {
editing = false;
} else if (reChoice.startsWith("Cancel")) {
return; // Abort
}
// else: continue editing loop
}
}
// choice === "Execute" → proceed to workspace creation
}
// Save command doc for reference
// Will be written to workspace after Step 1.3
```
### Step 1.2: Validate Steps
```javascript
for (const step of steps) {
if (step.type === 'skill' && step.skill_path) {
const skillFiles = Glob(`${step.skill_path}/SKILL.md`);
if (skillFiles.length === 0) {
step.validation = 'warning';
step.validation_msg = `Skill not found: ${step.skill_path}`;
} else {
step.validation = 'ok';
}
} else {
// Command-type steps: basic validation (non-empty)
step.validation = step.command && step.command.trim() ? 'ok' : 'invalid';
}
}
const invalidSteps = steps.filter(s => s.validation === 'invalid');
if (invalidSteps.length > 0) {
throw new Error(`Invalid steps: ${invalidSteps.map(s => s.name).join(', ')}`);
}
```
### Step 1.3: Create Workspace
```javascript
const ts = Date.now();
const workDir = `.workflow/.scratchpad/workflow-tune-${ts}`;
Bash(`mkdir -p "${workDir}/steps"`);
// Create per-step directories
for (let i = 0; i < steps.length; i++) {
Bash(`mkdir -p "${workDir}/steps/step-${i + 1}/artifacts"`);
}
```
### Step 1.3b: Save Command Document
```javascript
// Save confirmed command doc to workspace for reference
Write(`${workDir}/command-doc.md`, commandDoc);
```
### Step 1.4: Initialize State
```javascript
const initialState = {
status: 'running',
started_at: new Date().toISOString(),
updated_at: new Date().toISOString(),
workflow_name: workflowName,
workflow_context: workflowContext,
analysis_depth: workflowPreferences.analysisDepth,
auto_fix: workflowPreferences.autoFix,
steps: steps.map((s, i) => ({
...s,
index: i,
status: 'pending',
execution: null,
analysis: null
})),
analysis_session_id: null, // ccw cli resume chain
process_log_entries: [],
synthesis: null,
errors: [],
error_count: 0,
max_errors: 3,
work_dir: workDir
};
Write(`${workDir}/workflow-state.json`, JSON.stringify(initialState, null, 2));
```
### Step 1.5: Initialize Process Log
```javascript
const processLog = `# Workflow Tune Process Log
**Workflow**: ${workflowName}
**Context**: ${workflowContext}
**Steps**: ${steps.length}
**Analysis Depth**: ${workflowPreferences.analysisDepth}
**Started**: ${new Date().toISOString()}
---
`;
Write(`${workDir}/process-log.md`, processLog);
```
## Output
- **Variables**: `workDir`, `steps[]`, `workflowContext`, `commandDoc`, initialized state
- **Files**: `workflow-state.json`, `process-log.md`, `command-doc.md`, per-step directories
- **User Confirmation**: Execution plan confirmed (or cancelled → abort)
- **TaskUpdate**: Mark Phase 1 completed, start Step Loop

View File

@@ -0,0 +1,197 @@
# Phase 2: Execute Step
> **COMPACT SENTINEL [Phase 2: Execute Step]**
> 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-step-execute.md")`
Execute a single workflow step and collect its output artifacts.
## Objective
- Determine step execution method (skill invoke / ccw cli / shell command)
- Execute step with appropriate tool
- Collect output artifacts into step directory
- Write artifacts manifest
## Execution
### Step 2.1: Prepare Step Directory
```javascript
const stepIdx = currentStepIndex; // from orchestrator loop
const step = state.steps[stepIdx];
const stepDir = `${state.work_dir}/steps/step-${stepIdx + 1}`;
const artifactsDir = `${stepDir}/artifacts`;
// Capture pre-execution state (git status, file timestamps)
const preGitStatus = Bash('git status --porcelain 2>/dev/null || echo "not a git repo"').stdout;
// ★ Warn if dirty git working directory (first step only)
if (stepIdx === 0 && preGitStatus.trim() && preGitStatus.trim() !== 'not a git repo') {
const dirtyLines = preGitStatus.trim().split('\n').length;
// Log warning — artifact collection via git diff may be unreliable
// This is informational; does not block execution
console.warn(`⚠ Dirty git working directory detected (${dirtyLines} changed files). Artifact collection via git diff may include pre-existing changes.`);
}
const preExecSnapshot = {
timestamp: new Date().toISOString(),
git_status: preGitStatus,
working_files: Glob('**/*.{ts,js,md,json}').slice(0, 50) // sample
};
Write(`${stepDir}/pre-exec-snapshot.json`, JSON.stringify(preExecSnapshot, null, 2));
```
### Step 2.2: Execute by Step Type
```javascript
let executionResult = { success: false, method: '', output: '', duration: 0 };
const startTime = Date.now();
switch (step.type) {
case 'skill': {
// Skill invocation — use Skill tool
// Extract skill name and arguments from command
const skillCmd = step.command.replace(/^\//, '');
const [skillName, ...skillArgs] = skillCmd.split(/\s+/);
// Execute skill (this runs synchronously within current context)
// Note: Skill execution produces artifacts in the working directory
// We capture changes by comparing pre/post state
Skill({
name: skillName,
arguments: skillArgs.join(' ')
});
executionResult.method = 'skill';
executionResult.success = true;
break;
}
case 'ccw-cli': {
// Direct ccw cli command
const cliCommand = step.command;
Bash({
command: cliCommand,
run_in_background: true,
timeout: 600000 // 10 minutes
});
// STOP — wait for hook callback
// After callback:
executionResult.method = 'ccw-cli';
executionResult.success = true;
break;
}
case 'command': {
// Generic shell command
const result = Bash({
command: step.command,
timeout: 300000 // 5 minutes
});
executionResult.method = 'command';
executionResult.output = result.stdout || '';
executionResult.success = result.exitCode === 0;
// Save command output
if (executionResult.output) {
Write(`${artifactsDir}/command-output.txt`, executionResult.output);
}
if (result.stderr) {
Write(`${artifactsDir}/command-stderr.txt`, result.stderr);
}
break;
}
}
executionResult.duration = Date.now() - startTime;
```
### Step 2.3: Collect Artifacts
```javascript
// Capture post-execution state
const postExecSnapshot = {
timestamp: new Date().toISOString(),
git_status: Bash('git status --porcelain 2>/dev/null || echo "not a git repo"').stdout,
working_files: Glob('**/*.{ts,js,md,json}').slice(0, 50)
};
// Detect changed/new files by comparing snapshots
const preFiles = new Set(preExecSnapshot.working_files);
const newOrChanged = postExecSnapshot.working_files.filter(f => !preFiles.has(f));
// Also check git diff for modified files
const gitDiff = Bash('git diff --name-only 2>/dev/null || true').stdout.trim().split('\n').filter(Boolean);
// Collect all artifacts (new files + git-changed files + declared expected_artifacts)
const declaredArtifacts = (step.expected_artifacts || []).filter(f => {
// Verify declared artifacts actually exist
const exists = Glob(f);
return exists.length > 0;
}).flatMap(f => Glob(f));
const allArtifacts = [...new Set([...newOrChanged, ...gitDiff, ...declaredArtifacts])];
// Copy detected artifacts to step artifacts dir (or record references)
const artifactManifest = {
step: step.name,
step_index: stepIdx,
execution_method: executionResult.method,
success: executionResult.success,
duration_ms: executionResult.duration,
artifacts: allArtifacts.map(f => ({
path: f,
type: f.endsWith('.md') ? 'markdown' : f.endsWith('.json') ? 'json' : 'other',
size: 'unknown' // Can be filled by stat if needed
})),
// For skill type: also check .workflow/.scratchpad for generated files
scratchpad_files: step.type === 'skill'
? Glob('.workflow/.scratchpad/**/*').filter(f => {
// Only include files created after step started
return true; // Heuristic: include recent scratchpad files
}).slice(0, 20)
: [],
collected_at: new Date().toISOString()
};
Write(`${stepDir}/artifacts-manifest.json`, JSON.stringify(artifactManifest, null, 2));
```
### Step 2.4: Update State
```javascript
state.steps[stepIdx].status = 'executed';
state.steps[stepIdx].execution = {
method: executionResult.method,
success: executionResult.success,
duration_ms: executionResult.duration,
artifacts_dir: artifactsDir,
manifest_path: `${stepDir}/artifacts-manifest.json`,
artifact_count: artifactManifest.artifacts.length,
started_at: preExecSnapshot.timestamp,
completed_at: new Date().toISOString()
};
state.updated_at = new Date().toISOString();
Write(`${state.work_dir}/workflow-state.json`, JSON.stringify(state, null, 2));
```
## Error Handling
| Error | Recovery |
|-------|----------|
| Skill not found | Record failure, set success=false, continue to Phase 3 |
| CLI timeout (10min) | Retry once with shorter timeout, then record failure |
| Command exit non-zero | Record stderr, set success=false, continue to Phase 3 |
| No artifacts detected | Continue to Phase 3 — analysis evaluates step definition quality |
## Output
- **Files**: `pre-exec-snapshot.json`, `artifacts-manifest.json`, `artifacts/` (if command type)
- **State**: `steps[stepIdx].execution` updated
- **Next**: Phase 3 (Analyze Step)

View File

@@ -0,0 +1,318 @@
# Phase 3: Analyze Step
> **COMPACT SENTINEL [Phase 3: Analyze Step]**
> 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-step-analyze.md")`
Analyze a completed step's artifacts and quality using `ccw cli --tool gemini --mode analysis`. Uses `--resume` to maintain context across step analyses, building a continuous analysis chain.
## Objective
- Inspect step artifacts (file list, content, quality signals)
- Build analysis prompt with step context + prior process log
- Execute via ccw cli Gemini with resume chain
- Parse analysis results → write step-{N}-analysis.md
- Append findings to process-log.md
- Return updated session ID for resume chain
## Execution
### Step 3.1: Inspect Artifacts
```javascript
const stepIdx = currentStepIndex;
const step = state.steps[stepIdx];
const stepDir = `${state.work_dir}/steps/step-${stepIdx + 1}`;
// Read artifacts manifest
const manifest = JSON.parse(Read(`${stepDir}/artifacts-manifest.json`));
// Build artifact summary based on analysis depth
let artifactSummary = '';
if (state.analysis_depth === 'quick') {
// Quick: just file list and sizes
artifactSummary = `Artifacts (${manifest.artifacts.length} files):\n` +
manifest.artifacts.map(a => `- ${a.path} (${a.type})`).join('\n');
} else {
// Standard/Deep: include file content summaries
artifactSummary = manifest.artifacts.map(a => {
const maxLines = state.analysis_depth === 'deep' ? 300 : 150;
try {
const content = Read(a.path, { limit: maxLines });
return `--- ${a.path} (${a.type}) ---\n${content}`;
} catch {
return `--- ${a.path} --- [unreadable]`;
}
}).join('\n\n');
// Deep: also include scratchpad files
if (state.analysis_depth === 'deep' && manifest.scratchpad_files?.length > 0) {
artifactSummary += '\n\n--- Scratchpad Files ---\n' +
manifest.scratchpad_files.slice(0, 5).map(f => {
const content = Read(f, { limit: 100 });
return `--- ${f} ---\n${content}`;
}).join('\n\n');
}
}
// Execution result summary
const execSummary = `Execution: ${step.execution.method} | ` +
`Success: ${step.execution.success} | ` +
`Duration: ${step.execution.duration_ms}ms | ` +
`Artifacts: ${manifest.artifacts.length} files`;
```
### Step 3.2: Build Prior Context
```javascript
// Build accumulated process log context for this analysis
const priorProcessLog = Read(`${state.work_dir}/process-log.md`);
// Build step chain context (what came before, what comes after)
const stepChainContext = state.steps.map((s, i) => {
const status = i < stepIdx ? 'completed' : i === stepIdx ? 'CURRENT' : 'pending';
const score = s.analysis?.quality_score || '-';
return `${i + 1}. [${status}] ${s.name} (${s.type}) — Quality: ${score}`;
}).join('\n');
// Previous step handoff context (if not first step)
let handoffContext = '';
if (stepIdx > 0) {
const prevStep = state.steps[stepIdx - 1];
const prevAnalysis = prevStep.analysis;
if (prevAnalysis) {
handoffContext = `PREVIOUS STEP OUTPUT SUMMARY:
Step "${prevStep.name}" produced ${prevStep.execution?.artifact_count || 0} artifacts.
Quality: ${prevAnalysis.quality_score}/100
Key outputs: ${prevAnalysis.key_outputs?.join(', ') || 'unknown'}
Handoff notes: ${prevAnalysis.handoff_notes || 'none'}`;
}
}
```
### Step 3.3: Construct Analysis Prompt
```javascript
// Ref: templates/step-analysis-prompt.md
const depthInstructions = {
quick: 'Provide brief assessment (3-5 bullet points). Focus on: execution success, output completeness, obvious issues.',
standard: 'Provide detailed assessment. Cover: execution quality, output completeness, artifact quality, step-to-step handoff readiness, potential issues.',
deep: 'Provide exhaustive assessment. Cover: execution quality, output completeness and correctness, artifact quality and structure, step-to-step handoff integrity, error handling, performance signals, architecture implications, edge cases.'
};
const analysisPrompt = `PURPOSE: Analyze the output of workflow step "${step.name}" (step ${stepIdx + 1}/${state.steps.length}) to assess quality, identify issues, and evaluate handoff readiness for the next step.
WORKFLOW CONTEXT:
Name: ${state.workflow_name}
Goal: ${state.workflow_context}
Step Chain:
${stepChainContext}
CURRENT STEP:
Name: ${step.name}
Type: ${step.type}
Command: ${step.command}
${step.success_criteria ? `Success Criteria: ${step.success_criteria}` : ''}
EXECUTION RESULT:
${execSummary}
${handoffContext}
STEP ARTIFACTS:
${artifactSummary}
ANALYSIS DEPTH: ${state.analysis_depth}
${depthInstructions[state.analysis_depth]}
TASK:
1. Assess step execution quality (did it succeed? complete output?)
2. Evaluate artifact quality (content correctness, completeness, format)
3. Check handoff readiness (can the next step consume this output?)
4. Identify issues, risks, or optimization opportunities
5. Rate overall step quality 0-100
EXPECTED OUTPUT (strict JSON, no markdown):
{
"quality_score": <0-100>,
"execution_assessment": {
"success": <true|false>,
"completeness": "<complete|partial|failed>",
"notes": "<brief assessment>"
},
"artifact_assessment": {
"count": <number>,
"quality": "<high|medium|low>",
"key_outputs": ["<main output 1>", "<main output 2>"],
"missing_outputs": ["<expected but missing>"]
},
"handoff_assessment": {
"ready": <true|false>,
"next_step_compatible": <true|false|null>,
"handoff_notes": "<what next step should know>"
},
"issues": [
{ "severity": "high|medium|low", "description": "<issue>", "suggestion": "<fix>" }
],
"optimization_opportunities": [
{ "area": "<area>", "description": "<opportunity>", "impact": "high|medium|low" }
],
"step_summary": "<1-2 sentence summary for process log>"
}
CONSTRAINTS: Be specific, reference artifact content where possible, output ONLY JSON`;
```
### Step 3.4: Execute via ccw cli Gemini with Resume
```javascript
function escapeForShell(str) {
return str.replace(/"/g, '\\"').replace(/\$/g, '\\$').replace(/`/g, '\\`');
}
// Build CLI command with optional resume
let cliCommand = `ccw cli -p "${escapeForShell(analysisPrompt)}" --tool gemini --mode analysis`;
// Resume from previous step's analysis session (maintains context chain)
if (state.analysis_session_id) {
cliCommand += ` --resume ${state.analysis_session_id}`;
}
Bash({
command: cliCommand,
run_in_background: true,
timeout: 300000 // 5 minutes
});
// STOP — wait for hook callback
```
### Step 3.5: Parse Results and Update Process Log
After CLI completes:
```javascript
const rawOutput = /* CLI output from callback */;
// Extract session ID from CLI output for resume chain
const sessionIdMatch = rawOutput.match(/\[CCW_EXEC_ID=([^\]]+)\]/);
if (sessionIdMatch) {
state.analysis_session_id = sessionIdMatch[1];
}
// Parse JSON
const jsonMatch = rawOutput.match(/\{[\s\S]*\}/);
let analysis;
if (jsonMatch) {
try {
analysis = JSON.parse(jsonMatch[0]);
} catch (e) {
// Fallback: extract score heuristically
const scoreMatch = rawOutput.match(/"quality_score"\s*:\s*(\d+)/);
analysis = {
quality_score: scoreMatch ? parseInt(scoreMatch[1]) : 50,
execution_assessment: { success: step.execution.success, completeness: 'unknown', notes: 'Parse failed' },
artifact_assessment: { count: manifest.artifacts.length, quality: 'unknown', key_outputs: [], missing_outputs: [] },
handoff_assessment: { ready: true, next_step_compatible: null, handoff_notes: '' },
issues: [{ severity: 'low', description: 'Analysis output parsing failed', suggestion: 'Review raw output' }],
optimization_opportunities: [],
step_summary: 'Analysis parsing failed — raw output saved for manual review'
};
}
} else {
analysis = {
quality_score: 50,
step_summary: 'No structured analysis output received'
};
}
// Write step analysis file
const stepAnalysisReport = `# Step ${stepIdx + 1} Analysis: ${step.name}
**Quality Score**: ${analysis.quality_score}/100
**Date**: ${new Date().toISOString()}
## Execution
- Success: ${analysis.execution_assessment?.success}
- Completeness: ${analysis.execution_assessment?.completeness}
- Notes: ${analysis.execution_assessment?.notes}
## Artifacts
- Count: ${analysis.artifact_assessment?.count}
- Quality: ${analysis.artifact_assessment?.quality}
- Key Outputs: ${analysis.artifact_assessment?.key_outputs?.join(', ') || 'N/A'}
- Missing: ${analysis.artifact_assessment?.missing_outputs?.join(', ') || 'None'}
## Handoff Readiness
- Ready: ${analysis.handoff_assessment?.ready}
- Next Step Compatible: ${analysis.handoff_assessment?.next_step_compatible}
- Notes: ${analysis.handoff_assessment?.handoff_notes}
## Issues
${(analysis.issues || []).map(i => `- [${i.severity}] ${i.description}${i.suggestion}`).join('\n') || 'None'}
## Optimization Opportunities
${(analysis.optimization_opportunities || []).map(o => `- [${o.impact}] ${o.area}: ${o.description}`).join('\n') || 'None'}
`;
Write(`${stepDir}/step-${stepIdx + 1}-analysis.md`, stepAnalysisReport);
// Append to process log
const processLogEntry = `
## Step ${stepIdx + 1}: ${step.name} — Score: ${analysis.quality_score}/100
**Command**: \`${step.command}\`
**Result**: ${analysis.execution_assessment?.completeness || 'unknown'} | ${analysis.artifact_assessment?.count || 0} artifacts
**Summary**: ${analysis.step_summary || 'No summary'}
**Issues**: ${(analysis.issues || []).filter(i => i.severity === 'high').map(i => i.description).join('; ') || 'None critical'}
**Handoff**: ${analysis.handoff_assessment?.handoff_notes || 'Ready'}
---
`;
// Append to process-log.md
const currentLog = Read(`${state.work_dir}/process-log.md`);
Write(`${state.work_dir}/process-log.md`, currentLog + processLogEntry);
// Update state
state.steps[stepIdx].analysis = {
quality_score: analysis.quality_score,
key_outputs: analysis.artifact_assessment?.key_outputs || [],
handoff_notes: analysis.handoff_assessment?.handoff_notes || '',
issue_count: (analysis.issues || []).length,
high_issues: (analysis.issues || []).filter(i => i.severity === 'high').length,
optimization_count: (analysis.optimization_opportunities || []).length,
analysis_file: `${stepDir}/step-${stepIdx + 1}-analysis.md`
};
state.steps[stepIdx].status = 'analyzed';
state.process_log_entries.push({
step_index: stepIdx,
step_name: step.name,
quality_score: analysis.quality_score,
summary: analysis.step_summary,
timestamp: new Date().toISOString()
});
state.updated_at = new Date().toISOString();
Write(`${state.work_dir}/workflow-state.json`, JSON.stringify(state, null, 2));
```
## Error Handling
| Error | Recovery |
|-------|----------|
| CLI timeout | Retry once without --resume (fresh session) |
| Resume session not found | Start fresh analysis session, continue |
| JSON parse fails | Extract score heuristically, save raw output |
| No output | Default score 50, minimal process log entry |
## Output
- **Files**: `step-{N}-analysis.md`, updated `process-log.md`
- **State**: `steps[stepIdx].analysis` updated, `analysis_session_id` updated
- **Next**: Phase 2 for next step, or Phase 4 (Synthesize) if all steps done

View File

@@ -0,0 +1,257 @@
# Phase 4: Synthesize
> **COMPACT SENTINEL [Phase 4: Synthesize]**
> 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-synthesize.md")`
Synthesize all step analyses into cross-step insights. Evaluates the workflow as a whole: step ordering, handoff quality, redundancy, bottlenecks, and overall coherence.
## Objective
- Read complete process-log.md and all step analyses
- Build synthesis prompt with full workflow context
- Execute via ccw cli Gemini with resume chain
- Generate cross-step optimization insights
- Write synthesis.md
## Execution
### Step 4.1: Gather All Analyses
```javascript
// Read process log
const processLog = Read(`${state.work_dir}/process-log.md`);
// Read all step analysis files
const stepAnalyses = state.steps.map((step, i) => {
const analysisFile = `${state.work_dir}/steps/step-${i + 1}/step-${i + 1}-analysis.md`;
try {
return { step: step.name, index: i, content: Read(analysisFile) };
} catch {
return { step: step.name, index: i, content: '[Analysis not available]' };
}
});
// Build score summary
const scoreSummary = state.steps.map((s, i) =>
`Step ${i + 1} (${s.name}): ${s.analysis?.quality_score || '-'}/100 | Issues: ${s.analysis?.issue_count || 0} (${s.analysis?.high_issues || 0} high)`
).join('\n');
// Compute aggregate stats
const scores = state.steps.map(s => s.analysis?.quality_score).filter(Boolean);
const avgScore = scores.length > 0 ? Math.round(scores.reduce((a, b) => a + b, 0) / scores.length) : 0;
const minScore = scores.length > 0 ? Math.min(...scores) : 0;
const totalIssues = state.steps.reduce((sum, s) => sum + (s.analysis?.issue_count || 0), 0);
const totalHighIssues = state.steps.reduce((sum, s) => sum + (s.analysis?.high_issues || 0), 0);
```
### Step 4.2: Construct Synthesis Prompt
```javascript
// Ref: templates/synthesis-prompt.md
const synthesisPrompt = `PURPOSE: Synthesize all step analyses into a holistic workflow optimization assessment. Evaluate cross-step concerns: ordering, handoff quality, redundancy, bottlenecks, and overall workflow coherence.
WORKFLOW OVERVIEW:
Name: ${state.workflow_name}
Goal: ${state.workflow_context}
Steps: ${state.steps.length}
Average Quality: ${avgScore}/100
Weakest Step: ${minScore}/100
Total Issues: ${totalIssues} (${totalHighIssues} high severity)
SCORE SUMMARY:
${scoreSummary}
COMPLETE PROCESS LOG:
${processLog}
DETAILED STEP ANALYSES:
${stepAnalyses.map(a => `### ${a.step} (Step ${a.index + 1})\n${a.content}`).join('\n\n---\n\n')}
TASK:
1. **Workflow Coherence**: Do steps form a logical sequence? Any missing steps?
2. **Handoff Quality**: Are step outputs well-consumed by subsequent steps? Data format mismatches?
3. **Redundancy Detection**: Do any steps duplicate work? Overlapping concerns?
4. **Bottleneck Identification**: Which steps are bottlenecks (lowest quality, longest duration)?
5. **Step Ordering**: Would reordering steps improve outcomes?
6. **Missing Steps**: Are there gaps in the pipeline that need additional steps?
7. **Per-Step Optimization**: Top 3 improvements per underperforming step
8. **Workflow-Level Optimization**: Structural changes to the overall pipeline
EXPECTED OUTPUT (strict JSON, no markdown):
{
"workflow_score": <0-100>,
"coherence": {
"score": <0-100>,
"assessment": "<logical flow evaluation>",
"gaps": ["<missing step or transition>"]
},
"handoff_quality": {
"score": <0-100>,
"issues": [
{ "from_step": "<step name>", "to_step": "<step name>", "issue": "<description>", "fix": "<suggestion>" }
]
},
"redundancy": {
"found": <true|false>,
"items": [
{ "steps": ["<step1>", "<step2>"], "description": "<what overlaps>", "recommendation": "<merge or remove>" }
]
},
"bottlenecks": [
{ "step": "<step name>", "reason": "<why it's a bottleneck>", "impact": "high|medium|low", "fix": "<suggestion>" }
],
"ordering_suggestions": [
{ "current": "<current order description>", "proposed": "<new order>", "rationale": "<why>" }
],
"per_step_improvements": [
{ "step": "<step name>", "improvements": [
{ "priority": "high|medium|low", "description": "<what to change>", "rationale": "<why>" }
]}
],
"workflow_improvements": [
{ "priority": "high|medium|low", "category": "structure|handoff|performance|quality", "description": "<change>", "rationale": "<why>", "affected_steps": ["<step names>"] }
],
"summary": "<2-3 sentence executive summary of workflow health and top recommendations>"
}
CONSTRAINTS: Be specific, reference step names and artifact details, output ONLY JSON`;
```
### Step 4.3: Execute via ccw cli Gemini with Resume
```javascript
function escapeForShell(str) {
return str.replace(/"/g, '\\"').replace(/\$/g, '\\$').replace(/`/g, '\\`');
}
let cliCommand = `ccw cli -p "${escapeForShell(synthesisPrompt)}" --tool gemini --mode analysis`;
// Resume from the last step's analysis session
if (state.analysis_session_id) {
cliCommand += ` --resume ${state.analysis_session_id}`;
}
Bash({
command: cliCommand,
run_in_background: true,
timeout: 300000
});
// STOP — wait for hook callback
```
### Step 4.4: Parse Results and Write Synthesis
After CLI completes:
```javascript
const rawOutput = /* CLI output from callback */;
const jsonMatch = rawOutput.match(/\{[\s\S]*\}/);
let synthesis;
if (jsonMatch) {
try {
synthesis = JSON.parse(jsonMatch[0]);
} catch {
synthesis = {
workflow_score: avgScore,
summary: 'Synthesis parsing failed — individual step analyses available',
workflow_improvements: [],
per_step_improvements: [],
bottlenecks: [],
handoff_quality: { score: 0, issues: [] },
coherence: { score: 0, assessment: 'Parse error' },
redundancy: { found: false, items: [] },
ordering_suggestions: []
};
}
} else {
synthesis = {
workflow_score: avgScore,
summary: 'No synthesis output received',
workflow_improvements: [],
per_step_improvements: []
};
}
// Write synthesis report
const synthesisReport = `# Workflow Synthesis
**Workflow Score**: ${synthesis.workflow_score}/100
**Date**: ${new Date().toISOString()}
## Executive Summary
${synthesis.summary}
## Coherence (${synthesis.coherence?.score || '-'}/100)
${synthesis.coherence?.assessment || 'N/A'}
${(synthesis.coherence?.gaps || []).length > 0 ? '\n### Gaps\n' + synthesis.coherence.gaps.map(g => `- ${g}`).join('\n') : ''}
## Handoff Quality (${synthesis.handoff_quality?.score || '-'}/100)
${(synthesis.handoff_quality?.issues || []).map(i =>
`- **${i.from_step}${i.to_step}**: ${i.issue}\n Fix: ${i.fix}`
).join('\n') || 'No handoff issues'}
## Redundancy
${synthesis.redundancy?.found ? (synthesis.redundancy.items || []).map(r =>
`- Steps ${r.steps.join(', ')}: ${r.description}${r.recommendation}`
).join('\n') : 'No redundancy detected'}
## Bottlenecks
${(synthesis.bottlenecks || []).map(b =>
`- **${b.step}** [${b.impact}]: ${b.reason}\n Fix: ${b.fix}`
).join('\n') || 'No bottlenecks'}
## Ordering Suggestions
${(synthesis.ordering_suggestions || []).map(o =>
`- Current: ${o.current}\n Proposed: ${o.proposed}\n Rationale: ${o.rationale}`
).join('\n') || 'Current ordering is optimal'}
## Per-Step Improvements
${(synthesis.per_step_improvements || []).map(s =>
`### ${s.step}\n` + (s.improvements || []).map(i =>
`- [${i.priority}] ${i.description}${i.rationale}`
).join('\n')
).join('\n\n') || 'No per-step improvements'}
## Workflow-Level Improvements
${(synthesis.workflow_improvements || []).map((w, i) =>
`### ${i + 1}. [${w.priority}] ${w.description}\n- Category: ${w.category}\n- Rationale: ${w.rationale}\n- Affected: ${(w.affected_steps || []).join(', ')}`
).join('\n\n') || 'No workflow-level improvements'}
`;
Write(`${state.work_dir}/synthesis.md`, synthesisReport);
// Update state
state.synthesis = {
workflow_score: synthesis.workflow_score,
summary: synthesis.summary,
improvement_count: (synthesis.workflow_improvements || []).length +
(synthesis.per_step_improvements || []).reduce((sum, s) => sum + (s.improvements || []).length, 0),
high_priority_count: (synthesis.workflow_improvements || []).filter(w => w.priority === 'high').length,
bottleneck_count: (synthesis.bottlenecks || []).length,
handoff_issue_count: (synthesis.handoff_quality?.issues || []).length,
synthesis_file: `${state.work_dir}/synthesis.md`,
raw_data: synthesis
};
state.updated_at = new Date().toISOString();
Write(`${state.work_dir}/workflow-state.json`, JSON.stringify(state, null, 2));
```
## Error Handling
| Error | Recovery |
|-------|----------|
| CLI timeout | Generate synthesis from individual step analyses only (no cross-step) |
| Resume fails | Start fresh analysis session |
| JSON parse fails | Use step-level data to construct minimal synthesis |
## Output
- **Files**: `synthesis.md`
- **State**: `state.synthesis` updated
- **Next**: Phase 5 (Optimization Report)

View File

@@ -0,0 +1,244 @@
# Phase 5: Optimization 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-optimize-report.md")`
Generate the final optimization report and optionally apply high-priority fixes.
## Objective
- Read complete state, process log, synthesis
- Generate structured final report
- Optionally apply auto-fix (if enabled)
- Write final-report.md
- Display summary to user
## Execution
### Step 5.1: Read Complete State
```javascript
const state = JSON.parse(Read(`${state.work_dir}/workflow-state.json`));
const processLog = Read(`${state.work_dir}/process-log.md`);
const synthesis = state.synthesis;
state.status = 'completed';
state.updated_at = new Date().toISOString();
```
### Step 5.2: Generate Report
```javascript
// Compute stats
const scores = state.steps.map(s => s.analysis?.quality_score).filter(Boolean);
const avgScore = scores.length > 0 ? Math.round(scores.reduce((a, b) => a + b, 0) / scores.length) : 0;
const minStep = state.steps.reduce((min, s) =>
(s.analysis?.quality_score || 100) < (min.analysis?.quality_score || 100) ? s : min
, state.steps[0]);
const totalIssues = state.steps.reduce((sum, s) => sum + (s.analysis?.issue_count || 0), 0);
const totalHighIssues = state.steps.reduce((sum, s) => sum + (s.analysis?.high_issues || 0), 0);
// Step quality table
const stepTable = state.steps.map((s, i) =>
`| ${i + 1} | ${s.name} | ${s.type} | ${s.execution?.success ? 'OK' : 'FAIL'} | ${s.analysis?.quality_score || '-'} | ${s.analysis?.issue_count || 0} | ${s.analysis?.high_issues || 0} |`
).join('\n');
// Collect all improvements (workflow-level + per-step)
const allImprovements = [];
if (synthesis?.raw_data?.workflow_improvements) {
synthesis.raw_data.workflow_improvements.forEach(w => {
allImprovements.push({
scope: 'workflow',
priority: w.priority,
description: w.description,
rationale: w.rationale,
category: w.category,
affected: w.affected_steps || []
});
});
}
if (synthesis?.raw_data?.per_step_improvements) {
synthesis.raw_data.per_step_improvements.forEach(s => {
(s.improvements || []).forEach(imp => {
allImprovements.push({
scope: s.step,
priority: imp.priority,
description: imp.description,
rationale: imp.rationale,
category: 'step',
affected: [s.step]
});
});
});
}
// Sort by priority
const priorityOrder = { high: 0, medium: 1, low: 2 };
allImprovements.sort((a, b) => (priorityOrder[a.priority] || 2) - (priorityOrder[b.priority] || 2));
const report = `# Workflow Tune — Final Report
## Summary
| Field | Value |
|-------|-------|
| **Workflow** | ${state.workflow_name} |
| **Goal** | ${state.workflow_context} |
| **Steps** | ${state.steps.length} |
| **Workflow Score** | ${synthesis?.workflow_score || avgScore}/100 |
| **Average Step Quality** | ${avgScore}/100 |
| **Weakest Step** | ${minStep.name} (${minStep.analysis?.quality_score || '-'}/100) |
| **Total Issues** | ${totalIssues} (${totalHighIssues} high severity) |
| **Analysis Depth** | ${state.analysis_depth} |
| **Started** | ${state.started_at} |
| **Completed** | ${state.updated_at} |
## Step Quality Matrix
| # | Step | Type | Exec | Quality | Issues | High |
|---|------|------|------|---------|--------|------|
${stepTable}
## Workflow Flow Assessment
### Coherence: ${synthesis?.raw_data?.coherence?.score || '-'}/100
${synthesis?.raw_data?.coherence?.assessment || 'Not evaluated'}
### Handoff Quality: ${synthesis?.raw_data?.handoff_quality?.score || '-'}/100
${(synthesis?.raw_data?.handoff_quality?.issues || []).map(i =>
`- **${i.from_step}${i.to_step}**: ${i.issue}`
).join('\n') || 'No handoff issues'}
### Bottlenecks
${(synthesis?.raw_data?.bottlenecks || []).map(b =>
`- **${b.step}** [${b.impact}]: ${b.reason}`
).join('\n') || 'No bottlenecks identified'}
## Optimization Recommendations
### Priority: HIGH
${allImprovements.filter(i => i.priority === 'high').map((i, idx) =>
`${idx + 1}. **[${i.scope}]** ${i.description}\n - Rationale: ${i.rationale}\n - Affected: ${i.affected.join(', ')}`
).join('\n') || 'None'}
### Priority: MEDIUM
${allImprovements.filter(i => i.priority === 'medium').map((i, idx) =>
`${idx + 1}. **[${i.scope}]** ${i.description}\n - Rationale: ${i.rationale}`
).join('\n') || 'None'}
### Priority: LOW
${allImprovements.filter(i => i.priority === 'low').map((i, idx) =>
`${idx + 1}. **[${i.scope}]** ${i.description}`
).join('\n') || 'None'}
## Process Documentation
Full process log: \`${state.work_dir}/process-log.md\`
Synthesis: \`${state.work_dir}/synthesis.md\`
### Per-Step Analysis Files
| Step | Analysis File |
|------|---------------|
${state.steps.map((s, i) =>
`| ${s.name} | \`${state.work_dir}/steps/step-${i + 1}/step-${i + 1}-analysis.md\` |`
).join('\n')}
## Artifact Locations
| Path | Description |
|------|-------------|
| \`${state.work_dir}/workflow-state.json\` | Complete state |
| \`${state.work_dir}/process-log.md\` | Accumulated process log |
| \`${state.work_dir}/synthesis.md\` | Cross-step synthesis |
| \`${state.work_dir}/final-report.md\` | This report |
`;
Write(`${state.work_dir}/final-report.md`, report);
```
### Step 5.3: Optional Auto-Fix
```javascript
if (state.auto_fix && allImprovements.filter(i => i.priority === 'high').length > 0) {
const highPriorityFixes = allImprovements.filter(i => i.priority === 'high');
// ★ Safety: confirm with user before applying auto-fixes
const fixList = highPriorityFixes.map((f, i) =>
`${i + 1}. [${f.scope}] ${f.description}\n Affected: ${f.affected.join(', ')}`
).join('\n');
const autoFixConfirm = AskUserQuestion({
questions: [{
question: `以下 ${highPriorityFixes.length} 项高优先级优化将被自动应用:\n\n${fixList}\n\n确认应用?`,
header: "Auto-Fix Confirmation",
multiSelect: false,
options: [
{ label: "Apply (应用)", description: "自动应用以上高优先级修复" },
{ label: "Skip (跳过)", description: "跳过自动修复,仅保留报告" }
]
}]
});
if (autoFixConfirm["Auto-Fix Confirmation"].startsWith("Skip")) {
// Skip auto-fix, just log it
state.auto_fix_skipped = true;
} else {
Agent({
subagent_type: 'general-purpose',
run_in_background: false,
description: 'Apply high-priority workflow optimizations',
prompt: `## Task: Apply High-Priority Workflow Optimizations
You are applying the top optimization suggestions from a workflow analysis.
## Improvements to Apply (HIGH priority only)
${highPriorityFixes.map((f, i) =>
`${i + 1}. [${f.scope}] ${f.description}\n Rationale: ${f.rationale}\n Affected: ${f.affected.join(', ')}`
).join('\n')}
## Workflow Steps
${state.steps.map((s, i) => `${i + 1}. ${s.name} (${s.type}): ${s.command}`).join('\n')}
## Rules
1. Read each affected file BEFORE modifying
2. Apply ONLY the high-priority suggestions
3. Preserve existing code style
4. Write a changes summary to: ${state.work_dir}/auto-fix-changes.md
`
});
} // end Apply branch
}
```
### Step 5.4: Display Summary
Output to user:
```
Workflow Tune Complete!
Workflow: {name}
Steps: {count}
Workflow Score: {score}/100
Average Step Quality: {avgScore}/100
Weakest Step: {name} ({score}/100)
Step Scores: {step1}={score1} → {step2}={score2} → ... → {stepN}={scoreN}
Issues: {total} ({high} high priority)
Improvements: {count} ({highCount} high priority)
Full report: {workDir}/final-report.md
Process log: {workDir}/process-log.md
```
## Output
- **Files**: `final-report.md`, optionally `auto-fix-changes.md`
- **State**: `status = completed`
- **Next**: Workflow complete. Return control to user.