mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-03-21 19:08:17 +08:00
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>
198 lines
6.4 KiB
Markdown
198 lines
6.4 KiB
Markdown
# 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)
|