diff --git a/.claude/skills/codex-skill-designer/SKILL.md b/.claude/skills/codex-skill-designer/SKILL.md new file mode 100644 index 00000000..b501d407 --- /dev/null +++ b/.claude/skills/codex-skill-designer/SKILL.md @@ -0,0 +1,353 @@ +--- +name: codex-skill-designer +description: Meta-skill for designing Codex-native skills with subagent orchestration (spawn_agent/wait/send_input/close_agent). Supports new skill creation and Claude→Codex conversion. Triggers on "design codex skill", "create codex skill", "codex skill designer", "convert to codex". +allowed-tools: Task, AskUserQuestion, TodoWrite, Read, Write, Edit, Bash, Glob, Grep +--- + +# Codex Skill Designer + +Meta-skill for creating Codex-native skills that use the subagent API (`spawn_agent`/`wait`/`send_input`/`close_agent`). Generates complete skill packages with orchestrator coordination and agent role definitions. + +## Architecture Overview + +``` +┌──────────────────────────────────────────────────────────────┐ +│ Codex Skill Designer │ +│ → Analyze requirements → Design orchestrator → Design agents│ +└───────────────┬──────────────────────────────────────────────┘ + │ + ┌───────────┼───────────┬───────────┐ + ↓ ↓ ↓ ↓ +┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ +│ Phase 1 │ │ Phase 2 │ │ Phase 3 │ │ Phase 4 │ +│ Require │ │ Orch │ │ Agent │ │ Valid │ +│ Analysis│ │ Design │ │ Design │ │ & Integ │ +└─────────┘ └─────────┘ └─────────┘ └─────────┘ + ↓ ↓ ↓ ↓ + codexSkill orchestrator agents/ Complete + Config .md generated *.md skill pkg +``` + +## Target Output Structure + +The skill this meta-skill produces follows this structure: + +### Mode A: Structured Skill Package (multi-agent orchestration) + +``` +.codex/skills/{skill-name}/ +├── orchestrator.md # Main Codex orchestrator +│ ├── Frontmatter (name, description) +│ ├── Architecture (spawn/wait/close flow) +│ ├── Agent Registry (role → path mapping) +│ ├── Phase Execution (spawn_agent patterns) +│ ├── Result Aggregation (wait + merge) +│ └── Lifecycle Management (close_agent cleanup) +├── agents/ # Skill-specific agent definitions +│ ├── {agent-1}.md # → deploy to ~/.codex/agents/ +│ └── {agent-2}.md # → deploy to ~/.codex/agents/ +└── phases/ # [Optional] Phase execution detail + ├── 01-{phase}.md + └── 02-{phase}.md +``` + +### Mode B: Single Prompt (simple or self-contained skills) + +``` +~/.codex/prompts/{skill-name}.md # Self-contained Codex prompt +``` + +## Key Design Principles — Codex-Native Patterns + +### Pattern 1: Explicit Lifecycle Management + +Every agent has a complete lifecycle: `spawn_agent` → `wait` → [`send_input`] → `close_agent`. + +```javascript +// Standard lifecycle +const agentId = spawn_agent({ message: taskMessage }) +const result = wait({ ids: [agentId], timeout_ms: 300000 }) +// [Optional: send_input for multi-round] +close_agent({ id: agentId }) +``` + +**Key Rules**: +- Use `wait()` to get results, NEVER depend on `close_agent` return +- `close_agent` is irreversible — no further `wait`/`send_input` possible +- Delay `close_agent` until certain no more interaction is needed + +### Pattern 2: Role Loading via Path Reference + +Codex subagents cannot auto-load roles. Use MANDATORY FIRST STEPS pattern: + +```javascript +spawn_agent({ + message: ` +## TASK ASSIGNMENT + +### MANDATORY FIRST STEPS (Agent Execute) +1. **Read role definition**: ~/.codex/agents/{agent-type}.md (MUST read first) +2. Read: .workflow/project-tech.json +3. Read: .workflow/project-guidelines.json + +## TASK CONTEXT +${taskContext} + +## DELIVERABLES +${deliverables} +` +}) +``` + +### Pattern 3: Parallel Fan-out with Batch Wait + +Multiple independent agents → batch `wait({ ids: [...] })`: + +```javascript +const agentIds = tasks.map(task => + spawn_agent({ message: buildTaskMessage(task) }) +) +const results = wait({ ids: agentIds, timeout_ms: 600000 }) +agentIds.forEach(id => close_agent({ id })) +``` + +### Pattern 4: Deep Interaction (send_input Multi-round) + +Single agent, multi-phase with context preservation: + +```javascript +const agent = spawn_agent({ message: explorePrompt }) +const round1 = wait({ ids: [agent] }) + +// Continue with clarification +send_input({ id: agent, message: clarificationAnswers }) +const round2 = wait({ ids: [agent] }) + +close_agent({ id: agent }) // Only after all rounds complete +``` + +### Pattern 5: Two-Phase Workflow (Clarify → Execute) + +``` +Phase 1: spawn_agent → output Open Questions only + ↓ +Phase 2: send_input (answers) → output full solution +``` + +### Pattern 6: Structured Output Template + +All agents produce uniform output: + +```text +Summary: +- One-sentence completion status + +Findings: +- Finding 1: specific description +- Finding 2: specific description + +Proposed changes: +- File: path/to/file +- Change: specific modification +- Risk: potential impact + +Tests: +- New/updated test cases needed +- Test commands to run + +Open questions: +1. Question needing clarification +2. Question needing clarification +``` + +## Execution Flow + +``` +Phase 1: Requirements Analysis + └─ Ref: phases/01-requirements-analysis.md + ├─ Input: text description / Claude skill / requirements doc / existing codex prompt + └─ Output: codexSkillConfig (agents, phases, patterns, interaction model) + +Phase 2: Orchestrator Design + └─ Ref: phases/02-orchestrator-design.md + ├─ Input: codexSkillConfig + └─ Output: .codex/skills/{name}/orchestrator.md (or ~/.codex/prompts/{name}.md) + +Phase 3: Agent Design + └─ Ref: phases/03-agent-design.md + ├─ Input: codexSkillConfig + source content + └─ Output: .codex/skills/{name}/agents/*.md + optional phases/*.md + +Phase 4: Validation & Delivery + └─ Ref: phases/04-validation.md + └─ Output: Validated skill package + deployment instructions +``` + +**Phase Reference Documents** (read on-demand when phase executes): + +| Phase | Document | Purpose | +|-------|----------|---------| +| 1 | [phases/01-requirements-analysis.md](phases/01-requirements-analysis.md) | Analyze inputs, determine skill config | +| 2 | [phases/02-orchestrator-design.md](phases/02-orchestrator-design.md) | Generate Codex-native orchestrator | +| 3 | [phases/03-agent-design.md](phases/03-agent-design.md) | Generate agent roles & command patterns | +| 4 | [phases/04-validation.md](phases/04-validation.md) | Validate structure, patterns, quality | + +## Input Sources + +| Source | Description | Example | +|--------|-------------|---------| +| **Text description** | User describes desired Codex skill | "Create a 3-agent code review skill for Codex" | +| **Claude skill** | Convert existing Claude skill to Codex | `.claude/skills/workflow-plan/SKILL.md` | +| **Requirements doc** | Structured requirements file | `requirements.md` with agents/phases/outputs | +| **Existing Codex prompt** | Refactor/enhance a Codex prompt | `~/.codex/prompts/plan.md` | + +## Conversion Mode (Claude → Codex) + +When source is a Claude skill, apply conversion rules: + +| Claude Pattern | Codex Equivalent | +|----------------|-----------------| +| `Task({ subagent_type, prompt })` | `spawn_agent({ message })` + `wait()` | +| `Task({ run_in_background: false })` | `spawn_agent()` + immediate `wait()` | +| `Task({ resume: agentId })` | `send_input({ id: agentId })` | +| `TaskOutput({ task_id, block })` | `wait({ ids: [id], timeout_ms })` | +| Automatic agent cleanup | Explicit `close_agent({ id })` | +| `subagent_type` auto-loads role | MANDATORY FIRST STEPS role path | +| Multiple parallel `Task()` calls | Multiple `spawn_agent()` + batch `wait({ ids })` | + +**Full conversion spec**: Ref: specs/conversion-rules.md + +## Data Flow + +``` +Phase 1 → codexSkillConfig: + { + name, description, outputMode (structured|single), + agents: [{ name, role_file, responsibility, patterns }], + phases: [{ name, agents_involved, interaction_model }], + parallelSplits: [{ strategy, agents }], + conversionSource: null | { type, path } + } + +Phase 2 → orchestrator.md: + Generated Codex orchestrator with spawn/wait/close patterns + +Phase 3 → agents/*.md: + Per-agent role definitions with Codex-native conventions + +Phase 4 → validated package: + Structural completeness + pattern compliance + quality score +``` + +## TodoWrite Pattern + +``` +Phase starts: + → Sub-tasks ATTACHED to TodoWrite (in_progress + pending) + → Designer executes sub-tasks sequentially + +Phase ends: + → Sub-tasks COLLAPSED back to high-level summary (completed) + → Next phase begins +``` + +## Interactive Preference Collection + +Collect preferences via AskUserQuestion before dispatching to phases: + +```javascript +const prefResponse = AskUserQuestion({ + questions: [ + { + question: "What is the output mode for this Codex skill?", + header: "Output Mode", + multiSelect: false, + options: [ + { label: "Structured Package (Recommended)", description: "Multi-file: orchestrator.md + agents/*.md + phases/*.md" }, + { label: "Single Prompt", description: "Self-contained ~/.codex/prompts/{name}.md" } + ] + }, + { + question: "What is the input source?", + header: "Input Source", + multiSelect: false, + options: [ + { label: "Text Description", description: "Describe the desired Codex skill in natural language" }, + { label: "Claude Skill (Convert)", description: "Convert existing .claude/skills/ to Codex-native" }, + { label: "Requirements Doc", description: "Structured requirements file" }, + { label: "Existing Codex Prompt", description: "Refactor/enhance existing ~/.codex/prompts/" } + ] + } + ] +}) + +const workflowPreferences = { + outputMode: prefResponse["Output Mode"].includes("Structured") ? "structured" : "single", + inputSource: prefResponse["Input Source"] +} +``` + +## Specification Documents + +Read specs on-demand for pattern guidance: + +| Spec | Document | Purpose | +|------|----------|---------| +| Agent Patterns | [specs/codex-agent-patterns.md](specs/codex-agent-patterns.md) | Core Codex subagent API patterns | +| Conversion Rules | [specs/conversion-rules.md](specs/conversion-rules.md) | Claude → Codex mapping rules | +| Quality Standards | [specs/quality-standards.md](specs/quality-standards.md) | Quality gates & validation criteria | + +## Generation Templates + +Apply templates during generation: + +| Template | Document | Purpose | +|----------|----------|---------| +| Orchestrator | [templates/orchestrator-template.md](templates/orchestrator-template.md) | Codex orchestrator output template | +| Agent Role | [templates/agent-role-template.md](templates/agent-role-template.md) | Agent role definition template | +| Command Patterns | [templates/command-pattern-template.md](templates/command-pattern-template.md) | Pre-built Codex command patterns | + +## Error Handling + +| Scenario | Resolution | +|----------|------------| +| Source Claude skill has unsupported patterns | Log warning, provide manual conversion guidance | +| Agent role file path conflict | Append skill-name prefix to agent file | +| Output directory exists | Ask user: overwrite or new name | +| Validation score < 70% | Block delivery, report issues | + +## Post-Phase Updates + +After each phase, update accumulated state: + +```javascript +// After Phase 1 +codexSkillConfig = { ...requirements analysis output } + +// After Phase 2 +generatedFiles.orchestrator = "path/to/orchestrator.md" + +// After Phase 3 +generatedFiles.agents = ["path/to/agent1.md", "path/to/agent2.md"] +generatedFiles.phases = ["path/to/phase1.md"] // optional + +// After Phase 4 +validationResult = { score, issues, passed } +``` + +## Coordinator Checklist + +### Pre-Phase Actions +- [ ] Verify input source exists and is readable +- [ ] Collect preferences via AskUserQuestion +- [ ] Read relevant specs based on input source + +### Post-Phase Actions +- [ ] Verify phase output completeness +- [ ] Update TodoWrite status +- [ ] Pass accumulated state to next phase + +### Final Delivery +- [ ] All generated files written to target directory +- [ ] Deployment instructions provided +- [ ] Agent files include `~/.codex/agents/` deployment paths diff --git a/.claude/skills/codex-skill-designer/phases/01-requirements-analysis.md b/.claude/skills/codex-skill-designer/phases/01-requirements-analysis.md new file mode 100644 index 00000000..66f48b1f --- /dev/null +++ b/.claude/skills/codex-skill-designer/phases/01-requirements-analysis.md @@ -0,0 +1,167 @@ +# Phase 1: Requirements Analysis + +Analyze input source and extract Codex skill configuration. + +## Objective + +- Parse input source (text / Claude skill / requirements doc / Codex prompt) +- Identify agents, phases, interaction patterns +- Determine output mode (structured package vs single prompt) +- Produce codexSkillConfig for downstream phases + +## Pre-Requisites + +Read specification documents based on input source: +- **Always**: Read `specs/codex-agent-patterns.md` for available patterns +- **Claude conversion**: Also read `specs/conversion-rules.md` +- **Quality reference**: Read `specs/quality-standards.md` for target criteria + +## Execution + +### Step 1.1: Input Source Detection + +```javascript +// Determine input type from workflowPreferences +const inputSource = workflowPreferences.inputSource + +if (inputSource.includes("Claude Skill")) { + // Read source Claude skill + const sourceSkillPath = AskUserQuestion({ + questions: [{ + question: "Path to the Claude skill to convert?", + header: "Skill Path", + multiSelect: false, + options: [ + { label: "Browse", description: "I'll provide the path" } + ] + }] + }) + // Read SKILL.md + phases/*.md from source + const skillContent = Read(sourceSkillPath) + const phaseFiles = Glob(`${sourceSkillDir}/phases/*.md`) +} else if (inputSource.includes("Text Description")) { + // Collect description via user interaction +} else if (inputSource.includes("Requirements Doc")) { + // Read requirements file +} else if (inputSource.includes("Existing Codex")) { + // Read existing Codex prompt for refactoring +} +``` + +### Step 1.2: Skill Structure Extraction + +For each input type, extract: + +**From Text Description**: +```javascript +const codexSkillConfig = { + name: extractSkillName(userDescription), + description: extractDescription(userDescription), + outputMode: workflowPreferences.outputMode, + agents: inferAgents(userDescription), + phases: inferPhases(userDescription), + parallelSplits: inferParallelism(userDescription), + interactionModel: inferInteractionModel(userDescription), + conversionSource: null +} +``` + +**From Claude Skill** (conversion): +```javascript +// Parse Claude SKILL.md +const claudeConfig = { + phases: extractPhases(skillContent), + agents: extractTaskCalls(skillContent), // Find Task() invocations + dataFlow: extractDataFlow(skillContent), + todoPattern: extractTodoPattern(skillContent), + resumePatterns: findResumePatterns(skillContent) // For send_input mapping +} + +const codexSkillConfig = { + name: claudeConfig.name, + description: claudeConfig.description, + outputMode: workflowPreferences.outputMode, + agents: claudeConfig.agents.map(a => ({ + name: a.subagent_type, + role_file: mapToCodexRolePath(a.subagent_type), + responsibility: a.description, + patterns: determinePatterns(a) + })), + phases: claudeConfig.phases.map(p => ({ + name: p.name, + agents_involved: p.agentCalls.map(a => a.subagent_type), + interaction_model: hasResume(p) ? "deep_interaction" : "standard" + })), + parallelSplits: detectParallelPatterns(claudeConfig), + conversionSource: { type: "claude_skill", path: sourceSkillPath } +} +``` + +### Step 1.3: Agent Inventory Check + +Verify agent roles exist in `~/.codex/agents/`: + +```javascript +const existingAgents = Glob("~/.codex/agents/*.md") +const requiredAgents = codexSkillConfig.agents.map(a => a.role_file) + +const missingAgents = requiredAgents.filter(r => + !existingAgents.includes(r) +) + +if (missingAgents.length > 0) { + // Mark as "needs new agent role definition" + codexSkillConfig.newAgentDefinitions = missingAgents +} +``` + +### Step 1.4: Interaction Model Selection + +Based on agent relationships, select interaction patterns: + +| Pattern | Condition | Result | +|---------|-----------|--------| +| **Standard** | Single agent, single task | `spawn → wait → close` | +| **Parallel Fan-out** | Multiple independent agents | `spawn[] → batch wait → close[]` | +| **Deep Interaction** | Multi-phase with context | `spawn → wait → send_input → wait → close` | +| **Two-Phase** | Needs clarification first | `spawn(clarify) → wait → send_input(answers) → wait → close` | +| **Pipeline** | Sequential agent chain | `spawn(A) → wait → spawn(B, with A result) → wait → close` | + +```javascript +codexSkillConfig.phases.forEach(phase => { + if (phase.agents_involved.length > 1) { + phase.interaction_model = "parallel_fanout" + } else if (phase.interaction_model === "deep_interaction") { + // Already set from resume pattern detection + } else { + phase.interaction_model = "standard" + } +}) +``` + +### Step 1.5: User Confirmation + +Present extracted configuration for user review: + +```javascript +AskUserQuestion({ + questions: [{ + question: `Skill "${codexSkillConfig.name}" will have ${codexSkillConfig.agents.length} agent(s) and ${codexSkillConfig.phases.length} phase(s). ${codexSkillConfig.newAgentDefinitions?.length || 0} new agent definitions needed. Proceed?`, + header: "Confirm", + multiSelect: false, + options: [ + { label: "Proceed", description: "Generate Codex skill package" }, + { label: "Adjust", description: "Modify configuration first" } + ] + }] +}) +``` + +## Output + +- **Variable**: `codexSkillConfig` — complete skill configuration +- **TodoWrite**: Mark Phase 1 completed, Phase 2 in_progress + +## Next Phase + +Return to orchestrator, then auto-continue to [Phase 2: Orchestrator Design](02-orchestrator-design.md). diff --git a/.claude/skills/codex-skill-designer/phases/02-orchestrator-design.md b/.claude/skills/codex-skill-designer/phases/02-orchestrator-design.md new file mode 100644 index 00000000..2f4da7f0 --- /dev/null +++ b/.claude/skills/codex-skill-designer/phases/02-orchestrator-design.md @@ -0,0 +1,291 @@ +# Phase 2: Orchestrator Design + +Generate the main Codex orchestrator document using codexSkillConfig. + +## Objective + +- Generate orchestrator.md (structured mode) or {skill-name}.md (single mode) +- Apply Codex-native patterns: spawn_agent, wait, send_input, close_agent +- Include agent registry, phase execution, lifecycle management +- Preserve source content faithfully when converting from Claude + +## Pre-Requisites + +- Read `templates/orchestrator-template.md` for output structure +- Read `specs/codex-agent-patterns.md` for pattern reference +- If converting: Read `specs/conversion-rules.md` for mapping rules + +## Execution + +### Step 2.1: Determine Output Path + +```javascript +const outputPath = codexSkillConfig.outputMode === "structured" + ? `.codex/skills/${codexSkillConfig.name}/orchestrator.md` + : `~/.codex/prompts/${codexSkillConfig.name}.md` +``` + +### Step 2.2: Generate Frontmatter + +```markdown +--- +name: {{skill_name}} +description: {{description}} +agents: {{agent_count}} +phases: {{phase_count}} +output_template: structured # or "open_questions" for clarification-first +--- +``` + +### Step 2.3: Generate Architecture Diagram + +Map phases and agents to ASCII flow: + +```javascript +// For parallel fan-out: +const diagram = ` +┌──────────────────────────────────────────┐ +│ ${codexSkillConfig.name} Orchestrator │ +└──────────────┬───────────────────────────┘ + │ + ┌───────────┼───────────┬────────────┐ + ↓ ↓ ↓ ↓ +┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ +│Agent1│ │Agent2│ │Agent3│ │AgentN│ +│spawn │ │spawn │ │spawn │ │spawn │ +└──┬───┘ └──┬───┘ └──┬───┘ └──┬───┘ + └──────────┼───────────┘ │ + ↓ │ + batch wait({ids}) ←──────────┘ + ↓ + Aggregate Results + ↓ + close_agent (all) +` +``` + +### Step 2.4: Generate Agent Registry + +```javascript +const agentRegistry = codexSkillConfig.agents.map(agent => ({ + name: agent.name, + role_file: agent.role_file, // e.g., ~/.codex/agents/cli-explore-agent.md + responsibility: agent.responsibility, + is_new: agent.role_file.startsWith('.codex/skills/') // skill-specific new agent +})) +``` + +Output as registry table in orchestrator: + +```markdown +## Agent Registry + +| Agent | Role File | Responsibility | Status | +|-------|-----------|----------------|--------| +{{#each agents}} +| `{{name}}` | `{{role_file}}` | {{responsibility}} | {{#if is_new}}NEW{{else}}existing{{/if}} | +{{/each}} +``` + +### Step 2.5: Generate Phase Execution Blocks + +For each phase in codexSkillConfig.phases, generate the appropriate pattern: + +**Standard Pattern** (single agent, single task): +```javascript +// Phase N: {{phase.name}} +const agentId = spawn_agent({ + message: ` +## TASK ASSIGNMENT + +### MANDATORY FIRST STEPS (Agent Execute) +1. **Read role definition**: {{agent.role_file}} (MUST read first) +2. Read: .workflow/project-tech.json +3. Read: .workflow/project-guidelines.json + +--- + +Goal: {{phase.goal}} + +Scope: +- 可做: {{phase.scope.include}} +- 不可做: {{phase.scope.exclude}} + +Context: +{{phase.context}} + +Deliverables: +- {{phase.deliverables}} + +Quality bar: +- {{phase.quality_criteria}} +` +}) + +const result = wait({ ids: [agentId], timeout_ms: {{phase.timeout_ms || 300000}} }) +close_agent({ id: agentId }) +``` + +**Parallel Fan-out Pattern** (multiple independent agents): +```javascript +// Phase N: {{phase.name}} (Parallel) +const agentIds = {{phase.agents}}.map(agentConfig => { + return spawn_agent({ + message: ` +## TASK ASSIGNMENT + +### MANDATORY FIRST STEPS (Agent Execute) +1. **Read role definition**: ${agentConfig.role_file} (MUST read first) +2. Read: .workflow/project-tech.json + +--- + +Goal: ${agentConfig.specific_goal} +Scope: ${agentConfig.scope} +Deliverables: ${agentConfig.deliverables} +` + }) +}) + +// Batch wait for all agents +const results = wait({ + ids: agentIds, + timeout_ms: {{phase.timeout_ms || 600000}} +}) + +// Handle timeout +if (results.timed_out) { + const completed = agentIds.filter(id => results.status[id].completed) + const pending = agentIds.filter(id => !results.status[id].completed) + // Decision: continue waiting or use partial results +} + +// Aggregate results +const aggregated = agentIds.map(id => results.status[id].completed) + +// Cleanup +agentIds.forEach(id => close_agent({ id })) +``` + +**Deep Interaction Pattern** (multi-round with send_input): +```javascript +// Phase N: {{phase.name}} (Deep Interaction) +const agent = spawn_agent({ + message: ` +## TASK ASSIGNMENT + +### MANDATORY FIRST STEPS (Agent Execute) +1. **Read role definition**: {{agent.role_file}} (MUST read first) + +--- + +### Phase A: {{phase.initial_goal}} +Goal: {{phase.initial_goal}} +Output: Findings + Open Questions (if any) + +Output format for questions: +\`\`\` +CLARIFICATION_NEEDED: +Q1: [question] | Options: [A, B, C] | Recommended: [A] +\`\`\` + +### Phase B: {{phase.followup_goal}} +Trigger: Receive answers via send_input +Output: Complete deliverable +` +}) + +// Round 1: Initial exploration +const round1 = wait({ ids: [agent], timeout_ms: {{phase.timeout_ms || 600000}} }) + +// Check for clarification needs +const needsClarification = round1.status[agent].completed.includes('CLARIFICATION_NEEDED') + +if (needsClarification) { + // Collect user answers (orchestrator responsibility) + const answers = collectUserAnswers(round1) + + // Continue interaction + send_input({ + id: agent, + message: ` +## CLARIFICATION ANSWERS +${answers} + +## NEXT STEP +Proceed with Phase B: {{phase.followup_goal}} +` + }) + + const round2 = wait({ ids: [agent], timeout_ms: {{phase.timeout_ms || 900000}} }) +} + +close_agent({ id: agent }) +``` + +**Pipeline Pattern** (sequential agent chain): +```javascript +// Phase N: {{phase.name}} (Pipeline) + +// Stage 1 +const agent1 = spawn_agent({ message: stage1Prompt }) +const result1 = wait({ ids: [agent1] }) +close_agent({ id: agent1 }) + +// Stage 2 (uses Stage 1 output) +const agent2 = spawn_agent({ + message: ` +## TASK ASSIGNMENT +... +## PREVIOUS STAGE OUTPUT +${result1.status[agent1].completed} +... +` +}) +const result2 = wait({ ids: [agent2] }) +close_agent({ id: agent2 }) +``` + +### Step 2.6: Generate Lifecycle Management Section + +```markdown +## Lifecycle Management + +### Timeout Handling + +| Timeout | Action | +|---------|--------| +| Agent completes within timeout | Process result, close_agent | +| Agent times out (partial) | Option 1: continue wait / Option 2: send_input to urge convergence / Option 3: close_agent and use partial | +| All agents timeout | Log warning, retry with extended timeout or abort | + +### Cleanup Protocol + +After ALL phases complete or on error: +1. Verify all agent IDs have been closed +2. Report any agents still running +3. Force close remaining agents + +\`\`\`javascript +const allAgentIds = [] // accumulated during execution +allAgentIds.forEach(id => { + try { close_agent({ id }) } catch { /* already closed */ } +}) +\`\`\` +``` + +### Step 2.7: Write Orchestrator File + +Apply template from `templates/orchestrator-template.md` with generated content. + +Write the complete orchestrator to the output path. + +## Output + +- **File**: `{outputPath}` — generated Codex orchestrator +- **Variable**: `generatedFiles.orchestrator` = outputPath +- **TodoWrite**: Mark Phase 2 completed, Phase 3 in_progress + +## Next Phase + +Return to orchestrator, then auto-continue to [Phase 3: Agent Design](03-agent-design.md). diff --git a/.claude/skills/codex-skill-designer/phases/03-agent-design.md b/.claude/skills/codex-skill-designer/phases/03-agent-design.md new file mode 100644 index 00000000..af647b6b --- /dev/null +++ b/.claude/skills/codex-skill-designer/phases/03-agent-design.md @@ -0,0 +1,277 @@ +# Phase 3: Agent Design + +Generate agent role definitions and optional phase execution detail files. + +## Objective + +- Generate agent role files for `~/.codex/agents/` or `.codex/skills/{name}/agents/` +- Apply Codex-native conventions (MANDATORY FIRST STEPS, structured output) +- Preserve source content when converting from Claude +- Generate optional phase detail files for complex orchestrations + +## Pre-Requisites + +- Read `templates/agent-role-template.md` for role file structure +- Read `templates/command-pattern-template.md` for pre-built command patterns +- Read `specs/codex-agent-patterns.md` for API patterns + +## Execution + +### Step 3.1: Identify Agents to Generate + +```javascript +// From codexSkillConfig +const agentsToGenerate = codexSkillConfig.agents.filter(a => + a.role_file.startsWith('.codex/skills/') // new skill-specific agents + || codexSkillConfig.newAgentDefinitions?.includes(a.role_file) +) + +// Existing agents (already in ~/.codex/agents/) — skip generation +const existingAgents = codexSkillConfig.agents.filter(a => + !agentsToGenerate.includes(a) +) +``` + +### Step 3.2: Generate Agent Role Files + +For each agent to generate, apply the agent-role-template: + +```javascript +for (const agent of agentsToGenerate) { + const roleContent = applyTemplate('templates/agent-role-template.md', { + agent_name: agent.name, + description: agent.responsibility, + capabilities: agent.capabilities || inferCapabilities(agent), + execution_process: agent.workflow || inferWorkflow(agent), + output_format: codexSkillConfig.outputTemplate || "structured", + key_reminders: generateReminders(agent) + }) + + const outputPath = agent.role_file.startsWith('~/') + ? agent.role_file + : `.codex/skills/${codexSkillConfig.name}/agents/${agent.name}.md` + + Write(outputPath, roleContent) + generatedFiles.agents.push(outputPath) +} +``` + +### Step 3.3: Agent Role File Content Structure + +Each generated agent role file follows this structure: + +```markdown +--- +name: {{agent_name}} +description: | + {{description}} +color: {{color}} +skill: {{parent_skill_name}} +--- + +# {{agent_display_name}} + +{{description_paragraph}} + +## Core Capabilities + +1. **{{capability_1}}**: {{description}} +2. **{{capability_2}}**: {{description}} +3. **{{capability_3}}**: {{description}} + +## Execution Process + +### Step 1: Context Loading +- Read role-specific configuration files +- Load project context (.workflow/project-tech.json) +- Understand task scope from TASK ASSIGNMENT + +### Step 2: {{primary_action}} +{{primary_action_detail}} + +### Step 3: {{secondary_action}} +{{secondary_action_detail}} + +### Step 4: Output Delivery +Produce structured output following the template: + +\`\`\`text +Summary: +- {{summary_format}} + +Findings: +- {{findings_format}} + +Proposed changes: +- {{changes_format}} + +Tests: +- {{tests_format}} + +Open questions: +- {{questions_format}} +\`\`\` + +## Key Reminders + +**ALWAYS**: +- Read role definition file as FIRST action +- Follow structured output template +- Stay within assigned scope +- Report open questions instead of guessing + +**NEVER**: +- Modify files outside assigned scope +- Skip role definition loading +- Produce unstructured output +- Make assumptions about unclear requirements +``` + +### Step 3.4: Conversion from Claude Agent Definitions + +When converting from Claude skill, extract agent behavior from: + +1. **Task() prompts**: The `prompt` parameter contains the agent's task instructions +2. **Phase files**: Phase execution detail contains the full agent interaction +3. **subagent_type**: Maps to existing `~/.codex/agents/` roles + +```javascript +// For each Task() call found in Claude source +for (const taskCall of claudeConfig.agents) { + const existingRole = roleMapping[taskCall.subagent_type] + + if (existingRole) { + // Map to existing Codex agent — no new file needed + // Just reference in orchestrator's MANDATORY FIRST STEPS + codexSkillConfig.agents.push({ + name: taskCall.subagent_type, + role_file: `~/.codex/agents/${taskCall.subagent_type}.md`, + responsibility: taskCall.description, + is_new: false + }) + } else { + // Extract agent behavior from Claude prompt and create new role + const newRole = extractRoleFromPrompt(taskCall.prompt) + // Generate new role file + } +} +``` + +### Step 3.5: Command Pattern Selection + +For agents that need specific command patterns, select from pre-built templates: + +| Pattern | Use When | Template | +|---------|----------|----------| +| **Explore** | Agent needs codebase exploration | Parallel fan-out spawn_agent | +| **Analyze** | Agent performs multi-perspective analysis | Parallel spawn + merge | +| **Implement** | Agent writes code | Sequential spawn + validate | +| **Validate** | Agent runs tests | Iterative spawn + send_input fix cycle | +| **Review** | Agent reviews code/artifacts | Parallel spawn + aggregate | +| **Deep Interact** | Agent needs multi-round conversation | spawn + wait + send_input loop | +| **Two-Phase** | Agent needs clarification first | spawn(clarify) + send_input(execute) | + +Read `templates/command-pattern-template.md` for full pattern implementations. + +### Step 3.6: Generate Phase Detail Files (Optional) + +For structured mode with complex phases, generate phase detail files: + +```javascript +if (codexSkillConfig.outputMode === "structured") { + for (const phase of codexSkillConfig.phases) { + if (phase.complexity === "high" || phase.agents_involved.length > 2) { + const phaseContent = generatePhaseDetail(phase, codexSkillConfig) + const phasePath = `.codex/skills/${codexSkillConfig.name}/phases/${phase.index}-${phase.slug}.md` + Write(phasePath, phaseContent) + generatedFiles.phases.push(phasePath) + } + } +} +``` + +Phase detail structure: + +```markdown +# Phase {{N}}: {{Phase Name}} + +{{One-sentence description}} + +## Agents Involved + +| Agent | Role | Interaction Model | +|-------|------|-------------------| +{{#each phase.agents}} +| {{name}} | {{role_file}} | {{interaction_model}} | +{{/each}} + +## Execution + +### spawn_agent Configuration + +\`\`\`javascript +const agent = spawn_agent({ + message: ` +## TASK ASSIGNMENT + +### MANDATORY FIRST STEPS (Agent Execute) +1. **Read role definition**: {{role_file}} (MUST read first) +... + +--- + +Goal: {{goal}} +Scope: {{scope}} +Context: {{context}} +Deliverables: {{deliverables}} +Quality bar: {{quality}} +` +}) +\`\`\` + +### Wait & Result Processing + +\`\`\`javascript +const result = wait({ ids: [agent], timeout_ms: {{timeout}} }) +// Process: {{result_processing}} +close_agent({ id: agent }) +\`\`\` + +## Output + +- **Result**: {{output_description}} +- **Passed to**: Phase {{N+1}} +``` + +### Step 3.7: Deployment Mapping + +Generate deployment instructions: + +```javascript +const deploymentMap = { + // Existing agents — no action needed + existing: existingAgents.map(a => ({ + name: a.name, + path: a.role_file, + action: "already deployed" + })), + // New agents — need deployment + new: agentsToGenerate.map(a => ({ + name: a.name, + sourcePath: `.codex/skills/${codexSkillConfig.name}/agents/${a.name}.md`, + targetPath: `~/.codex/agents/${a.name}.md`, + action: "copy to ~/.codex/agents/" + })) +} +``` + +## Output + +- **Files**: `generatedFiles.agents[]` — agent role files +- **Files**: `generatedFiles.phases[]` — optional phase detail files +- **Variable**: `deploymentMap` — deployment instructions +- **TodoWrite**: Mark Phase 3 completed, Phase 4 in_progress + +## Next Phase + +Return to orchestrator, then auto-continue to [Phase 4: Validation & Delivery](04-validation.md). diff --git a/.claude/skills/codex-skill-designer/phases/04-validation.md b/.claude/skills/codex-skill-designer/phases/04-validation.md new file mode 100644 index 00000000..9792b761 --- /dev/null +++ b/.claude/skills/codex-skill-designer/phases/04-validation.md @@ -0,0 +1,254 @@ +# Phase 4: Validation & Delivery + +Validate the generated Codex skill package and deliver to target location. + +## Objective + +- Verify structural completeness of all generated files +- Validate Codex pattern compliance (lifecycle, role loading, output format) +- Score quality against standards +- Deploy to target location with instructions + +## Pre-Requisites + +- Read `specs/quality-standards.md` for validation criteria +- Access `generatedFiles` from previous phases +- Access `codexSkillConfig` for expected structure + +## Execution + +### Step 4.1: Structural Completeness Check + +```javascript +const structuralChecks = { + // Orchestrator exists + orchestrator: { + exists: fileExists(generatedFiles.orchestrator), + hasFrontmatter: checkFrontmatter(generatedFiles.orchestrator), + hasArchitecture: checkSection(generatedFiles.orchestrator, "Architecture"), + hasAgentRegistry: checkSection(generatedFiles.orchestrator, "Agent Registry"), + hasPhaseExecution: checkSection(generatedFiles.orchestrator, "Phase"), + hasLifecycleManagement: checkSection(generatedFiles.orchestrator, "Lifecycle"), + hasTimeoutHandling: checkSection(generatedFiles.orchestrator, "Timeout"), + passed: 0, total: 7 + }, + + // Agent files exist and are well-formed + agents: codexSkillConfig.agents.map(agent => ({ + name: agent.name, + exists: fileExists(agent.role_file) || fileExists(generatedFiles.agents.find(f => f.includes(agent.name))), + hasFrontmatter: checkFrontmatter(agentFile), + hasCapabilities: checkSection(agentFile, "Core Capabilities"), + hasExecution: checkSection(agentFile, "Execution Process"), + hasReminders: checkSection(agentFile, "Key Reminders"), + passed: 0, total: 5 + })), + + // Phase files (if structured mode) + phases: generatedFiles.phases?.map(phasePath => ({ + path: phasePath, + exists: fileExists(phasePath), + hasAgentTable: checkSection(phasePath, "Agents Involved"), + hasSpawnConfig: checkSection(phasePath, "spawn_agent"), + hasWaitProcessing: checkSection(phasePath, "Wait"), + passed: 0, total: 4 + })) || [] +} + +// Count passes +let totalPassed = 0, totalChecks = 0 +// ... count logic +``` + +### Step 4.2: Codex Pattern Compliance + +Verify all Codex-native patterns are correctly applied: + +```javascript +const patternChecks = { + // Lifecycle: every spawn has a close + lifecycle: { + spawnCount: countPattern(orchestratorContent, /spawn_agent/g), + closeCount: countPattern(orchestratorContent, /close_agent/g), + balanced: spawnCount <= closeCount, // close >= spawn (batch close is OK) + description: "Every spawn_agent must have matching close_agent" + }, + + // Role loading: MANDATORY FIRST STEPS present + roleLoading: { + hasPattern: orchestratorContent.includes("MANDATORY FIRST STEPS"), + allAgentsReferenced: codexSkillConfig.agents.every(a => + orchestratorContent.includes(a.role_file) + ), + usesPathNotInline: !orchestratorContent.includes("## ROLE DEFINITION"), + description: "Role files loaded via path reference, not inline content" + }, + + // Wait pattern: uses wait() not close_agent for results + waitPattern: { + usesWaitForResults: countPattern(orchestratorContent, /wait\(\s*\{/) > 0, + noCloseForResults: !hasPatternSequence(orchestratorContent, "close_agent", "result"), + description: "Results obtained via wait(), not close_agent" + }, + + // Batch wait: parallel agents use batch wait + batchWait: { + applicable: codexSkillConfig.parallelSplits?.length > 0, + usesBatchIds: orchestratorContent.includes("ids: [") || + orchestratorContent.includes("ids: agentIds"), + description: "Parallel agents use batch wait({ ids: [...] })" + }, + + // Timeout handling: timeout_ms specified + timeout: { + hasTimeout: orchestratorContent.includes("timeout_ms"), + hasTimeoutHandling: orchestratorContent.includes("timed_out"), + description: "Timeout specified and timeout scenarios handled" + }, + + // Structured output: agents produce uniform output + structuredOutput: { + hasSummary: agentContents.every(c => c.includes("Summary:")), + hasDeliverables: agentContents.every(c => c.includes("Deliverables") || c.includes("Findings")), + description: "All agents produce structured output template" + }, + + // No Claude patterns: no Task(), no TaskOutput(), no resume + noClaudePatterns: { + noTask: !orchestratorContent.includes("Task("), + noTaskOutput: !orchestratorContent.includes("TaskOutput("), + noResume: !orchestratorContent.includes("resume:") && !orchestratorContent.includes("resume ="), + description: "No Claude-specific patterns remain" + } +} + +const patternScore = calculatePatternScore(patternChecks) +``` + +### Step 4.3: Content Quality Check + +```javascript +const qualityChecks = { + // Orchestrator quality + orchestratorQuality: { + hasDescription: orchestratorContent.length > 500, + hasCodeBlocks: countPattern(orchestratorContent, /```/g) >= 4, + hasErrorHandling: orchestratorContent.includes("Error") || orchestratorContent.includes("error"), + noPlaceholders: !orchestratorContent.includes("{{") || !orchestratorContent.includes("TODO"), + description: "Orchestrator is complete and production-ready" + }, + + // Agent quality + agentQuality: agentContents.map(content => ({ + hasSubstantiveContent: content.length > 300, + hasActionableSteps: countPattern(content, /Step \d/g) >= 2, + hasOutputFormat: content.includes("Output") || content.includes("Deliverables"), + noPlaceholders: !content.includes("{{") || !content.includes("TODO") + })), + + // Conversion quality (if applicable) + conversionQuality: codexSkillConfig.conversionSource ? { + allTasksConverted: true, // verify all Claude Task() calls are mapped + noLostFunctionality: true, // verify no features dropped + interactionPreserved: true // verify resume → send_input mapping + } : null +} + +const qualityScore = calculateQualityScore(qualityChecks) +``` + +### Step 4.4: Quality Gate + +```javascript +const overallScore = ( + structuralScore * 0.30 + + patternScore * 0.40 + + qualityScore * 0.30 +) + +const verdict = overallScore >= 80 ? "PASS" : + overallScore >= 60 ? "REVIEW" : "FAIL" +``` + +| Verdict | Score | Action | +|---------|-------|--------| +| **PASS** | >= 80% | Deliver to target location | +| **REVIEW** | 60-79% | Report issues, ask user to proceed or fix | +| **FAIL** | < 60% | Block delivery, list critical issues | + +### Step 4.5: Validation Report + +```javascript +const validationReport = { + skill: codexSkillConfig.name, + outputMode: codexSkillConfig.outputMode, + scores: { + structural: structuralScore, + pattern: patternScore, + quality: qualityScore, + overall: overallScore + }, + verdict: verdict, + issues: collectIssues(structuralChecks, patternChecks, qualityChecks), + generatedFiles: generatedFiles, + deploymentMap: deploymentMap +} +``` + +### Step 4.6: Delivery + +If verdict is PASS or user approves REVIEW: + +```javascript +// For structured mode — files already in .codex/skills/{name}/ +// Report deployment instructions for agent files + +const deploymentInstructions = ` +## Deployment Instructions + +### Generated Files +${generatedFiles.orchestrator} +${generatedFiles.agents.join('\n')} +${generatedFiles.phases?.join('\n') || '(no phase files)'} + +### Agent Deployment +${deploymentMap.new.map(a => + `Copy: ${a.sourcePath} → ${a.targetPath}` +).join('\n')} + +### Existing Agents (no action needed) +${deploymentMap.existing.map(a => + `✓ ${a.name}: ${a.path}` +).join('\n')} + +### Usage +Invoke the generated orchestrator via Codex: +- Read the orchestrator.md and follow its phase execution +- Or register as a Codex prompt in ~/.codex/prompts/ + +### Validation Score +Overall: ${overallScore}% (${verdict}) +- Structural: ${structuralScore}% +- Pattern Compliance: ${patternScore}% +- Content Quality: ${qualityScore}% +` +``` + +### Step 4.7: Final Summary to User + +Present: +1. Generated file list with paths +2. Validation scores +3. Deployment instructions +4. Any issues or warnings +5. Next steps (e.g., "test the skill by running the orchestrator") + +## Output + +- **Report**: Validation report with scores +- **Deployment**: Instructions for agent file deployment +- **TodoWrite**: Mark Phase 4 completed + +## Completion + +Skill package generation complete. All files written and validated. diff --git a/.claude/skills/codex-skill-designer/specs/codex-agent-patterns.md b/.claude/skills/codex-skill-designer/specs/codex-agent-patterns.md new file mode 100644 index 00000000..d0371cc0 --- /dev/null +++ b/.claude/skills/codex-skill-designer/specs/codex-agent-patterns.md @@ -0,0 +1,406 @@ +# Codex Agent Patterns + +Core Codex subagent API patterns reference for skill generation. + +## Purpose + +| Phase | Usage | +|-------|-------| +| Phase 0 | Read to understand available Codex patterns | +| Phase 2 | Reference when generating orchestrator patterns | +| Phase 3 | Reference when designing agent interactions | + +--- + +## 1. API Reference + +### 1.1 spawn_agent + +Creates a new subagent with independent context. + +```javascript +const agentId = spawn_agent({ + message: "task message", // Required: task assignment + agent_type: "type" // Optional: preset baseline +}) +// Returns: agent_id (string) +``` + +**Key Facts**: +- Each agent has isolated context (no shared state) +- `agent_type` selects preset behavior baseline +- Role definition must be loaded via MANDATORY FIRST STEPS +- Returns immediately — use `wait()` for results + +### 1.2 wait + +Retrieves results from one or more agents. + +```javascript +const result = wait({ + ids: [agentId1, agentId2], // Required: agent IDs to wait for + timeout_ms: 300000 // Optional: max wait time (ms) +}) +// Returns: { timed_out: boolean, status: { [id]: { completed: string } } } +``` + +**Key Facts**: +- Primary result retrieval method (NOT close_agent) +- Supports batch wait for multiple agents +- `timed_out: true` means some agents haven't finished — can re-wait +- Can be called multiple times on same agent + +### 1.3 send_input + +Continues interaction with an active agent. + +```javascript +send_input({ + id: agentId, // Required: target agent + message: "follow-up", // Required: continuation message + interrupt: false // Optional: interrupt current processing +}) +``` + +**Key Facts**: +- Agent must NOT be closed +- Preserves full conversation context +- Use for: clarification answers, phase transitions, iterative refinement +- `interrupt: true` — use with caution (stops current processing) + +### 1.4 close_agent + +Permanently terminates an agent. + +```javascript +close_agent({ id: agentId }) +``` + +**Key Facts**: +- Irreversible — no further wait/send_input possible +- Do NOT use to retrieve results (use wait instead) +- Delay until certain no more interaction needed +- Call for ALL agents at end of workflow (cleanup) + +## 2. Interaction Patterns + +### 2.1 Standard (Single Agent, Single Task) + +``` +spawn_agent → wait → close_agent +``` + +**Use When**: Simple, one-shot tasks with clear deliverables. + +```javascript +const agent = spawn_agent({ message: taskPrompt }) +const result = wait({ ids: [agent], timeout_ms: 300000 }) +close_agent({ id: agent }) +``` + +### 2.2 Parallel Fan-out (Multiple Independent Agents) + +``` +spawn_agent × N → batch wait({ ids: [...] }) → close_agent × N +``` + +**Use When**: Multiple independent tasks that can run concurrently. + +```javascript +const agents = tasks.map(t => spawn_agent({ message: buildPrompt(t) })) +const results = wait({ ids: agents, timeout_ms: 600000 }) +// Aggregate results +const merged = agents.map(id => results.status[id].completed) +// Cleanup all +agents.forEach(id => close_agent({ id })) +``` + +**Split Strategies**: + +| Strategy | Description | Example | +|----------|-------------|---------| +| By responsibility | Each agent has different role | Research / Plan / Test | +| By module | Each agent handles different code area | auth / api / database | +| By perspective | Each agent analyzes from different angle | security / performance / maintainability | + +### 2.3 Deep Interaction (Multi-round with send_input) + +``` +spawn_agent → wait (round 1) → send_input → wait (round 2) → ... → close_agent +``` + +**Use When**: Tasks needing iterative refinement or multi-phase execution within single agent context. + +```javascript +const agent = spawn_agent({ message: initialPrompt }) + +// Round 1 +const r1 = wait({ ids: [agent], timeout_ms: 300000 }) + +// Round 2 (refine based on r1) +send_input({ id: agent, message: refinementPrompt }) +const r2 = wait({ ids: [agent], timeout_ms: 300000 }) + +// Round 3 (finalize) +send_input({ id: agent, message: finalizationPrompt }) +const r3 = wait({ ids: [agent], timeout_ms: 300000 }) + +close_agent({ id: agent }) +``` + +### 2.4 Two-Phase (Clarify → Execute) + +``` +spawn_agent → wait (questions) → send_input (answers) → wait (solution) → close_agent +``` + +**Use When**: Complex tasks where requirements need clarification before execution. + +```javascript +const agent = spawn_agent({ + message: ` +## TASK ASSIGNMENT +... +### Phase A: Exploration & Clarification +Output findings + Open Questions (CLARIFICATION_NEEDED format) + +### Phase B: Full Solution (after receiving answers) +Output complete deliverable +` +}) + +// Phase A +const exploration = wait({ ids: [agent], timeout_ms: 600000 }) + +if (exploration.status[agent].completed.includes('CLARIFICATION_NEEDED')) { + // Collect answers + const answers = getUserAnswers(exploration) + + // Phase B + send_input({ + id: agent, + message: `## CLARIFICATION ANSWERS\n${answers}\n\n## PROCEED\nGenerate full solution.` + }) + const solution = wait({ ids: [agent], timeout_ms: 900000 }) +} + +close_agent({ id: agent }) +``` + +### 2.5 Pipeline (Sequential Agent Chain) + +``` +spawn(A) → wait(A) → close(A) → spawn(B, with A's output) → wait(B) → close(B) +``` + +**Use When**: Tasks where each stage depends on the previous stage's output. + +```javascript +// Stage 1: Research +const researcher = spawn_agent({ message: researchPrompt }) +const research = wait({ ids: [researcher] }) +close_agent({ id: researcher }) + +// Stage 2: Plan (uses research output) +const planner = spawn_agent({ + message: `${planPrompt}\n\n## RESEARCH CONTEXT\n${research.status[researcher].completed}` +}) +const plan = wait({ ids: [planner] }) +close_agent({ id: planner }) + +// Stage 3: Execute (uses plan output) +const executor = spawn_agent({ + message: `${executePrompt}\n\n## PLAN\n${plan.status[planner].completed}` +}) +const execution = wait({ ids: [executor] }) +close_agent({ id: executor }) +``` + +### 2.6 Merged Exploration (Explore + Clarify + Plan in Single Agent) + +``` +spawn(dual-role) → wait(explore) → send_input(clarify) → wait(plan) → close +``` + +**Use When**: Exploration and planning are tightly coupled and benefit from shared context. + +**Advantages over Pipeline**: +- 60-80% fewer agent creations +- No context loss between phases +- Higher result consistency + +```javascript +const agent = spawn_agent({ + message: ` +## DUAL ROLE ASSIGNMENT + +### Role A: Explorer +Explore codebase, identify patterns, generate questions + +### Role B: Planner (activated after clarification) +Generate implementation plan based on exploration + answers + +### Phase 1: Explore +Output: Findings + CLARIFICATION_NEEDED questions + +### Phase 2: Plan (triggered by send_input) +Output: plan.json +` +}) + +const explore = wait({ ids: [agent] }) +// ... handle clarification ... +send_input({ id: agent, message: answers }) +const plan = wait({ ids: [agent] }) +close_agent({ id: agent }) +``` + +## 3. Message Design + +### 3.1 TASK ASSIGNMENT Structure + +```text +## TASK ASSIGNMENT + +### MANDATORY FIRST STEPS (Agent Execute) +1. **Read role definition**: ~/.codex/agents/{agent-type}.md (MUST read first) +2. Read: .workflow/project-tech.json +3. Read: .workflow/project-guidelines.json + +--- + +Goal: One-sentence objective + +Scope: +- Include: allowed operations +- Exclude: forbidden operations +- Directory: target paths +- Dependencies: dependency constraints + +Context: +- Key paths: relevant file paths +- Current state: system status +- Constraints: must-follow rules + +Deliverables: +- Output structured following template + +Quality bar: +- Criterion 1 +- Criterion 2 +``` + +### 3.2 Structured Output Template + +```text +Summary: +- One-sentence completion status + +Findings: +- Finding 1: description +- Finding 2: description + +Proposed changes: +- File: path/to/file +- Change: modification detail +- Risk: impact assessment + +Tests: +- Test cases needed +- Commands to run + +Open questions: +1. Unresolved question 1 +2. Unresolved question 2 +``` + +### 3.3 Clarification Format + +```text +CLARIFICATION_NEEDED: +Q1: [question] | Options: [A, B, C] | Recommended: [A] +Q2: [question] | Options: [A, B] | Recommended: [B] +``` + +## 4. Error Handling + +### 4.1 Timeout + +```javascript +const result = wait({ ids: [agent], timeout_ms: 30000 }) +if (result.timed_out) { + // Option 1: Continue waiting + const retry = wait({ ids: [agent], timeout_ms: 60000 }) + + // Option 2: Urge convergence + send_input({ id: agent, message: "Please wrap up and output current findings." }) + const urged = wait({ ids: [agent], timeout_ms: 30000 }) + + // Option 3: Abort + close_agent({ id: agent }) +} +``` + +### 4.2 Agent Recovery (post close_agent) + +```javascript +// Cannot recover closed agent — must recreate +const newAgent = spawn_agent({ + message: `${originalPrompt}\n\n## PREVIOUS ATTEMPT OUTPUT\n${previousOutput}` +}) +``` + +### 4.3 Partial Results (parallel fan-out) + +```javascript +const results = wait({ ids: agents, timeout_ms: 300000 }) +const completed = agents.filter(id => results.status[id]?.completed) +const pending = agents.filter(id => !results.status[id]?.completed) + +if (completed.length >= Math.ceil(agents.length * 0.7)) { + // 70%+ complete — proceed with partial results + pending.forEach(id => close_agent({ id })) +} +``` + +## 5. Role Loading + +### 5.1 Path Reference Pattern (Recommended) + +```javascript +spawn_agent({ + message: ` +### MANDATORY FIRST STEPS (Agent Execute) +1. **Read role definition**: ~/.codex/agents/${agentType}.md (MUST read first) +... +` +}) +``` + +**Why**: Keeps message lean, agent loads its own role context. + +### 5.2 Role Mapping + +| Agent Type | Role File | +|------------|-----------| +| cli-explore-agent | ~/.codex/agents/cli-explore-agent.md | +| cli-lite-planning-agent | ~/.codex/agents/cli-lite-planning-agent.md | +| code-developer | ~/.codex/agents/code-developer.md | +| context-search-agent | ~/.codex/agents/context-search-agent.md | +| debug-explore-agent | ~/.codex/agents/debug-explore-agent.md | +| doc-generator | ~/.codex/agents/doc-generator.md | +| action-planning-agent | ~/.codex/agents/action-planning-agent.md | +| test-fix-agent | ~/.codex/agents/test-fix-agent.md | +| universal-executor | ~/.codex/agents/universal-executor.md | +| tdd-developer | ~/.codex/agents/tdd-developer.md | +| ui-design-agent | ~/.codex/agents/ui-design-agent.md | + +## 6. Design Principles + +1. **Delay close_agent**: Only close when certain no more interaction needed +2. **Batch wait over sequential**: Use `wait({ ids: [...] })` for parallel agents +3. **Merge phases when context-dependent**: Use send_input over new agents +4. **Structured output always**: Enforce uniform output template +5. **Minimal message size**: Pass role file paths, not inline content +6. **Explicit lifecycle**: Every spawn must have a close (balanced) +7. **Timeout handling**: Always specify timeout_ms, always handle timed_out diff --git a/.claude/skills/codex-skill-designer/specs/conversion-rules.md b/.claude/skills/codex-skill-designer/specs/conversion-rules.md new file mode 100644 index 00000000..1c9be95d --- /dev/null +++ b/.claude/skills/codex-skill-designer/specs/conversion-rules.md @@ -0,0 +1,228 @@ +# Claude → Codex Conversion Rules + +Comprehensive mapping rules for converting Claude Code skills to Codex-native skills. + +## Purpose + +| Phase | Usage | +|-------|-------| +| Phase 1 | Reference when analyzing Claude source skill | +| Phase 2 | Apply when generating Codex orchestrator | +| Phase 3 | Apply when converting agent definitions | + +--- + +## 1. API Mapping + +### 1.1 Core API Conversion + +| Claude Pattern | Codex Equivalent | Notes | +|----------------|-----------------|-------| +| `Task({ subagent_type, prompt })` | `spawn_agent({ message })` + `wait()` | Split create and result retrieval | +| `Task({ run_in_background: false })` | `spawn_agent()` + immediate `wait()` | Synchronous equivalent | +| `Task({ run_in_background: true })` | `spawn_agent()` (wait later) | Deferred wait | +| `Task({ resume: agentId })` | `send_input({ id: agentId })` | Agent must not be closed | +| `TaskOutput({ task_id, block: true })` | `wait({ ids: [id] })` | Blocking wait | +| `TaskOutput({ task_id, block: false })` | `wait({ ids: [id], timeout_ms: 1000 })` | Polling with short timeout | +| Agent auto-cleanup | `close_agent({ id })` | Must be explicit | + +### 1.2 Parallel Task Conversion + +**Claude**: +```javascript +// Multiple Task() calls in single message (parallel) +const result1 = Task({ subagent_type: "agent-a", prompt: promptA }) +const result2 = Task({ subagent_type: "agent-b", prompt: promptB }) +const result3 = Task({ subagent_type: "agent-c", prompt: promptC }) +``` + +**Codex**: +```javascript +// Explicit parallel: spawn all, then batch wait +const idA = spawn_agent({ message: promptA_with_role }) +const idB = spawn_agent({ message: promptB_with_role }) +const idC = spawn_agent({ message: promptC_with_role }) + +const results = wait({ ids: [idA, idB, idC], timeout_ms: 600000 }) + +// Process results +const resultA = results.status[idA].completed +const resultB = results.status[idB].completed +const resultC = results.status[idC].completed + +// Cleanup +;[idA, idB, idC].forEach(id => close_agent({ id })) +``` + +### 1.3 Resume/Continue Conversion + +**Claude**: +```javascript +// Resume a previous agent +Task({ subagent_type: "agent-a", resume: previousAgentId, prompt: "Continue..." }) +``` + +**Codex**: +```javascript +// send_input to continue (agent must still be alive) +send_input({ + id: previousAgentId, + message: "Continue..." +}) +const continued = wait({ ids: [previousAgentId] }) +``` + +### 1.4 TaskOutput Polling Conversion + +**Claude**: +```javascript +while (!done) { + const output = TaskOutput({ task_id: id, block: false }) + if (output.status === 'completed') done = true + sleep(1000) +} +``` + +**Codex**: +```javascript +let result = wait({ ids: [id], timeout_ms: 30000 }) +while (result.timed_out) { + result = wait({ ids: [id], timeout_ms: 30000 }) +} +``` + +## 2. Role Loading Conversion + +### 2.1 subagent_type → MANDATORY FIRST STEPS + +**Claude**: Role automatically loaded via `subagent_type` parameter. + +**Codex**: Role must be explicitly loaded by agent as first action. + +**Conversion**: +```javascript +// Claude +Task({ + subagent_type: "cli-explore-agent", + prompt: "Explore the codebase for authentication patterns" +}) + +// Codex +spawn_agent({ + message: ` +## TASK ASSIGNMENT + +### MANDATORY FIRST STEPS (Agent Execute) +1. **Read role definition**: ~/.codex/agents/cli-explore-agent.md (MUST read first) +2. Read: .workflow/project-tech.json +3. Read: .workflow/project-guidelines.json + +--- + +Goal: Explore the codebase for authentication patterns +Deliverables: Structured findings following output template +` +}) +``` + +### 2.2 Role Mapping Table + +| Claude subagent_type | Codex Role Path | +|----------------------|-----------------| +| `Explore` | `~/.codex/agents/cli-explore-agent.md` | +| `Plan` | `~/.codex/agents/cli-lite-planning-agent.md` | +| `code-developer` | `~/.codex/agents/code-developer.md` | +| `context-search-agent` | `~/.codex/agents/context-search-agent.md` | +| `debug-explore-agent` | `~/.codex/agents/debug-explore-agent.md` | +| `doc-generator` | `~/.codex/agents/doc-generator.md` | +| `action-planning-agent` | `~/.codex/agents/action-planning-agent.md` | +| `test-fix-agent` | `~/.codex/agents/test-fix-agent.md` | +| `universal-executor` | `~/.codex/agents/universal-executor.md` | +| `tdd-developer` | `~/.codex/agents/tdd-developer.md` | +| `general-purpose` | `~/.codex/agents/universal-executor.md` | +| `Bash` | Direct shell execution (no agent needed) | +| `haiku` / `sonnet` / `opus` | Model selection via agent_type parameter | + +## 3. Structural Conversion + +### 3.1 SKILL.md → orchestrator.md + +| Claude SKILL.md Section | Codex orchestrator.md Section | +|--------------------------|-------------------------------| +| Frontmatter (name, description, allowed-tools) | Frontmatter (name, description, agents, phases) | +| Architecture Overview | Architecture Overview (spawn/wait/close flow) | +| Execution Flow (Ref: markers) | Phase Execution (spawn_agent code blocks) | +| Data Flow (variables, files) | Data Flow (wait results, context passing) | +| TodoWrite Pattern | update_plan tracking (Codex convention) | +| Interactive Preference Collection | User interaction via orchestrator prompts | +| Error Handling | Timeout + Lifecycle error handling | +| Phase Reference Documents table | Agent Registry + Phase detail files | + +### 3.2 Phase Files → Phase Detail or Inline + +**Simple phases** (single agent, no branching): Inline in orchestrator.md + +**Complex phases** (multi-agent, conditional): Separate `phases/0N-{name}.md` + +### 3.3 Pattern-Level Conversion + +| Claude Pattern | Codex Pattern | +|----------------|---------------| +| Orchestrator + Progressive Loading | Orchestrator + Agent Registry + on-demand phase loading | +| TodoWrite Attachment/Collapse | update_plan pending → in_progress → completed | +| Inter-Phase Data Flow (variables) | wait() result passing between phases | +| Conditional Phase Execution | if/else on wait() results | +| Direct Phase Handoff (Read phase doc) | Inline execution or separate phase files | +| AskUserQuestion | Direct user interaction in orchestrator | + +## 4. Content Preservation Rules + +When converting Claude skills: + +1. **Agent prompts**: Preserve task descriptions, goals, scope, deliverables VERBATIM +2. **Bash commands**: Preserve all shell commands unchanged +3. **Code blocks**: Preserve implementation code unchanged +4. **Validation logic**: Preserve quality checks and success criteria +5. **Error handling**: Convert to Codex timeout/lifecycle patterns, preserve intent + +**Transform** (structure changes): +- `Task()` calls → `spawn_agent()` + `wait()` + `close_agent()` +- `subagent_type` → MANDATORY FIRST STEPS role path +- Synchronous returns → Explicit `wait()` calls +- Auto-cleanup → Explicit `close_agent()` calls + +**Preserve** (content unchanged): +- Task descriptions and goals +- Scope definitions +- Quality criteria +- File paths and patterns +- Shell commands +- Business logic + +## 5. Anti-Patterns to Avoid + +| Anti-Pattern | Why | Correct Pattern | +|-------------|-----|-----------------| +| Using close_agent for results | Returns are unreliable | Use wait() for results | +| Inline role content in message | Bloats message, wastes tokens | Pass role file path in MANDATORY FIRST STEPS | +| Early close_agent before potential follow-up | Cannot resume closed agent | Delay close until certain no more interaction | +| Sequential wait for parallel agents | Wasted time | Batch wait({ ids: [...] }) | +| No timeout_ms | Indefinite hang risk | Always specify timeout_ms | +| No timed_out handling | Silent failures | Always check result.timed_out | +| Claude Task() remaining in output | Runtime incompatibility | Convert all Task() to spawn_agent | +| Claude resume: in output | Runtime incompatibility | Convert to send_input() | + +## 6. Conversion Checklist + +Before delivering converted skill: + +- [ ] All `Task()` calls converted to `spawn_agent()` + `wait()` + `close_agent()` +- [ ] All `subagent_type` mapped to MANDATORY FIRST STEPS role paths +- [ ] All `resume` converted to `send_input()` +- [ ] All `TaskOutput` polling converted to `wait()` with timeout +- [ ] No Claude-specific patterns remain (Task, TaskOutput, resume, subagent_type) +- [ ] Timeout handling added for all `wait()` calls +- [ ] Lifecycle balanced (spawn count ≤ close count) +- [ ] Structured output template enforced for all agents +- [ ] Agent prompts/goals/scope preserved verbatim +- [ ] Error handling converted to Codex patterns diff --git a/.claude/skills/codex-skill-designer/specs/quality-standards.md b/.claude/skills/codex-skill-designer/specs/quality-standards.md new file mode 100644 index 00000000..46daea63 --- /dev/null +++ b/.claude/skills/codex-skill-designer/specs/quality-standards.md @@ -0,0 +1,163 @@ +# Quality Standards + +Quality criteria and validation gates for generated Codex skills. + +## Purpose + +| Phase | Usage | +|-------|-------| +| Phase 3 | Reference during generation | +| Phase 4 | Apply during validation | + +--- + +## 1. Quality Dimensions + +### 1.1 Structural Completeness (30%) + +| Check | Weight | Criteria | +|-------|--------|----------| +| Orchestrator exists | 5 | File present at expected path | +| Frontmatter valid | 3 | Contains name, description | +| Architecture diagram | 3 | ASCII flow showing spawn/wait/close | +| Agent Registry | 4 | Table with all agents, role paths, responsibilities | +| Phase Execution blocks | 5 | Code blocks for each phase with spawn/wait/close | +| Lifecycle Management | 5 | Timeout handling + cleanup protocol | +| Agent files complete | 5 | All new agent roles have complete role files | + +**Scoring**: Each check passes (full weight) or fails (0). Total = sum / max. + +### 1.2 Pattern Compliance (40%) + +| Check | Weight | Criteria | +|-------|--------|----------| +| Lifecycle balanced | 6 | Every spawn_agent has matching close_agent | +| Role loading correct | 6 | MANDATORY FIRST STEPS pattern used (not inline content) | +| Wait for results | 5 | wait() used for results (not close_agent) | +| Batch wait for parallel | 5 | Parallel agents use wait({ ids: [...] }) | +| Timeout specified | 4 | All wait() calls have timeout_ms | +| Timeout handled | 4 | timed_out checked after every wait() | +| Structured output | 5 | Agents produce Summary/Findings/Changes/Tests/Questions | +| No Claude patterns | 5 | No Task(), TaskOutput(), resume: remaining | + +**Scoring**: Each check passes (full weight) or fails (0). Total = sum / max. + +### 1.3 Content Quality (30%) + +| Check | Weight | Criteria | +|-------|--------|----------| +| Orchestrator substantive | 4 | Content > 500 chars, not boilerplate | +| Code blocks present | 3 | >= 4 code blocks with executable patterns | +| Error handling | 3 | Timeout + recovery + partial results handling | +| No placeholders | 4 | No `{{...}}` or `TODO` remaining in output | +| Agent roles substantive | 4 | Each agent role > 300 chars with actionable steps | +| Output format defined | 3 | Structured output template in each agent | +| Goals/scope clear | 4 | Every spawn_agent has Goal + Scope + Deliverables | +| Conversion faithful | 5 | Source content preserved (if converting) | + +**Scoring**: Each check passes (full weight) or fails (0). Total = sum / max. + +## 2. Quality Gates + +| Verdict | Score | Action | +|---------|-------|--------| +| **PASS** | >= 80% | Deliver to target location | +| **REVIEW** | 60-79% | Report issues, user decides | +| **FAIL** | < 60% | Block delivery, list critical issues | + +### 2.1 Critical Failures (Auto-FAIL) + +These issues force FAIL regardless of overall score: + +1. **No orchestrator file** — skill has no entry point +2. **Task() calls in output** — runtime incompatible with Codex +3. **No agent registry** — agents cannot be identified +4. **Missing close_agent** — resource leak risk +5. **Inline role content** — violates Codex pattern (message bloat) + +### 2.2 Warnings (Non-blocking) + +1. **Missing timeout handling** — degraded reliability +2. **No error handling section** — reduced robustness +3. **Placeholder text remaining** — needs manual completion +4. **Phase files missing** — acceptable for simple skills + +## 3. Validation Process + +### 3.1 Automated Checks + +```javascript +function validateSkill(generatedFiles, codexSkillConfig) { + const checks = [] + + // Structural + checks.push(checkFileExists(generatedFiles.orchestrator)) + checks.push(checkFrontmatter(generatedFiles.orchestrator)) + checks.push(checkSection(generatedFiles.orchestrator, "Architecture")) + checks.push(checkSection(generatedFiles.orchestrator, "Agent Registry")) + // ... + + // Pattern compliance + const content = Read(generatedFiles.orchestrator) + checks.push(checkBalancedLifecycle(content)) + checks.push(checkRoleLoading(content)) + checks.push(checkWaitPattern(content)) + // ... + + // Content quality + checks.push(checkNoPlaceholders(content)) + checks.push(checkSubstantiveContent(content)) + // ... + + // Critical failures + const criticals = checkCriticalFailures(content, generatedFiles) + if (criticals.length > 0) return { verdict: "FAIL", criticals } + + // Score + const score = calculateWeightedScore(checks) + const verdict = score >= 80 ? "PASS" : score >= 60 ? "REVIEW" : "FAIL" + + return { score, verdict, checks, issues: checks.filter(c => !c.passed) } +} +``` + +### 3.2 Manual Review Points + +For REVIEW verdict, highlight these for user attention: + +1. Agent role completeness — are all capabilities covered? +2. Interaction model appropriateness — right pattern for use case? +3. Timeout values — appropriate for expected task duration? +4. Scope definitions — clear boundaries for each agent? +5. Output format — suitable for downstream consumers? + +## 4. Scoring Formula + +``` +Overall = Structural × 0.30 + PatternCompliance × 0.40 + ContentQuality × 0.30 +``` + +Pattern compliance weighted highest because Codex runtime correctness is critical. + +## 5. Quality Improvement Guidance + +### Low Structural Score + +- Add missing sections to orchestrator +- Create missing agent role files +- Add frontmatter to all files + +### Low Pattern Score + +- Add MANDATORY FIRST STEPS to all spawn_agent messages +- Replace inline role content with path references +- Add close_agent for every spawn_agent +- Add timeout_ms and timed_out handling to all wait calls +- Remove any remaining Claude patterns + +### Low Content Score + +- Expand agent role definitions with more specific steps +- Add concrete Goal/Scope/Deliverables to spawn messages +- Replace placeholders with actual content +- Add error handling for each phase diff --git a/.claude/skills/codex-skill-designer/templates/agent-role-template.md b/.claude/skills/codex-skill-designer/templates/agent-role-template.md new file mode 100644 index 00000000..9e558c52 --- /dev/null +++ b/.claude/skills/codex-skill-designer/templates/agent-role-template.md @@ -0,0 +1,215 @@ +# Agent Role Template + +Template for generating per-agent role definition files. + +## Purpose + +| Phase | Usage | +|-------|-------| +| Phase 0 | Read to understand agent role file structure | +| Phase 3 | Apply with agent-specific content | + +--- + +## Template + +```markdown +--- +name: {{agent_name}} +description: | + {{description}} +color: {{color}} +skill: {{parent_skill_name}} +--- + +# {{agent_display_name}} + +{{description_paragraph}} + +## Core Capabilities + +{{#each capabilities}} +{{@index}}. **{{this.name}}**: {{this.description}} +{{/each}} + +## Execution Process + +### Step 1: Context Loading + +**MANDATORY**: Execute these steps FIRST before any other action. + +1. Read this role definition file (already done if you're reading this) +2. Read: `.workflow/project-tech.json` — understand project technology stack +3. Read: `.workflow/project-guidelines.json` — understand project conventions +4. Parse the TASK ASSIGNMENT from the spawn message for: + - **Goal**: What to achieve + - **Scope**: What's allowed and forbidden + - **Context**: Relevant background information + - **Deliverables**: Expected output format + - **Quality bar**: Success criteria + +### Step 2: {{primary_action_name}} + +{{primary_action_detail}} + +\`\`\`javascript +// {{primary_action_description}} +{{primary_action_code}} +\`\`\` + +### Step 3: {{secondary_action_name}} + +{{secondary_action_detail}} + +\`\`\`javascript +// {{secondary_action_description}} +{{secondary_action_code}} +\`\`\` + +### Step 4: Output Delivery + +Produce structured output following this EXACT template: + +\`\`\`text +Summary: +- One-sentence completion summary + +Findings: +- Finding 1: [specific description with file:line references] +- Finding 2: [specific description] + +Proposed changes: +- File: [path/to/file] +- Change: [specific modification description] +- Risk: [low/medium/high] - [impact description] + +Tests: +- Test cases: [list of needed test cases] +- Commands: [test commands to verify] + +Open questions: +1. [Question needing clarification, if any] +2. [Question needing clarification, if any] +\`\`\` + +**Important**: If there are open questions that block progress, prepend output with: +\`\`\` +CLARIFICATION_NEEDED: +Q1: [question] | Options: [A, B, C] | Recommended: [A] +Q2: [question] | Options: [A, B] | Recommended: [B] +\`\`\` + +## Key Reminders + +**ALWAYS**: +- Read role definition file as FIRST action (Step 1) +- Follow structured output template EXACTLY +- Stay within the assigned Scope boundaries +- Include file:line references in Findings +- Report open questions via CLARIFICATION_NEEDED format +- Provide actionable, specific deliverables + +**NEVER**: +- Modify files outside the assigned Scope +- Skip context loading (Step 1) +- Produce unstructured or free-form output +- Make assumptions about unclear requirements (ask instead) +- Exceed the defined Quality bar without explicit approval +- Ignore the Goal/Scope/Deliverables from TASK ASSIGNMENT + +## Error Handling + +| Scenario | Action | +|----------|--------| +| Cannot access required file | Report in Open questions, continue with available data | +| Task scope unclear | Output CLARIFICATION_NEEDED, provide best-effort findings | +| Unexpected error | Report error details in Summary, include partial results | +| Quality bar not achievable | Report gap in Summary, explain constraints | +``` + +--- + +## Template Variants by Responsibility Type + +### Exploration Agent + +**Step 2**: Codebase Discovery +```javascript +// Search for relevant code patterns +const files = Glob("src/**/*.{ts,js,tsx,jsx}") +const matches = Grep(targetPattern, files) +// Trace call chains, identify entry points +``` + +**Step 3**: Pattern Analysis +```javascript +// Analyze discovered patterns +// Cross-reference with project conventions +// Identify similar implementations +``` + +### Implementation Agent + +**Step 2**: Code Implementation +```javascript +// Implement changes according to plan +// Follow existing code patterns +// Maintain backward compatibility +``` + +**Step 3**: Self-Validation +```javascript +// Run relevant tests +// Check for syntax/type errors +// Verify changes match acceptance criteria +``` + +### Analysis Agent + +**Step 2**: Multi-Dimensional Analysis +```javascript +// Analyze from assigned perspective (security/perf/quality/etc.) +// Collect evidence with file:line references +// Classify findings by severity +``` + +**Step 3**: Recommendation Generation +```javascript +// Propose fixes for each finding +// Assess risk and effort +// Prioritize by impact +``` + +### Testing Agent + +**Step 2**: Test Design +```javascript +// Identify test scenarios from requirements +// Design test cases with expected results +// Map to test frameworks +``` + +**Step 3**: Test Execution & Validation +```javascript +// Run tests +// Collect pass/fail results +// Iterate on failures +``` + +--- + +## Variable Reference + +| Variable | Source | Description | +|----------|--------|-------------| +| `{{agent_name}}` | config.name | Agent identifier (lowercase, hyphenated) | +| `{{agent_display_name}}` | Derived from name | Human-readable title | +| `{{description}}` | config.description | Short description (1-3 lines) | +| `{{description_paragraph}}` | config.description | Full paragraph description | +| `{{color}}` | Auto-assigned | Terminal color for output | +| `{{parent_skill_name}}` | codexSkillConfig.name | Parent skill identifier | +| `{{capabilities}}` | Inferred from responsibility | Array of capability objects | +| `{{primary_action_name}}` | Derived from responsibility | Step 2 title | +| `{{primary_action_detail}}` | Generated or from source | Step 2 content | +| `{{secondary_action_name}}` | Derived from responsibility | Step 3 title | +| `{{secondary_action_detail}}` | Generated or from source | Step 3 content | diff --git a/.claude/skills/codex-skill-designer/templates/command-pattern-template.md b/.claude/skills/codex-skill-designer/templates/command-pattern-template.md new file mode 100644 index 00000000..2a981eee --- /dev/null +++ b/.claude/skills/codex-skill-designer/templates/command-pattern-template.md @@ -0,0 +1,414 @@ +# Command Pattern Template + +Pre-built Codex command patterns for common agent interaction scenarios. + +## Purpose + +| Phase | Usage | +|-------|-------| +| Phase 0 | Read to understand available command patterns | +| Phase 2 | Select appropriate patterns for orchestrator | +| Phase 3 | Apply patterns to agent definitions | + +--- + +## Pattern 1: Explore (Parallel Fan-out) + +**Use When**: Multi-angle codebase exploration needed. + +```javascript +// ==================== Explore Pattern ==================== + +// Step 1: Define exploration angles +const angles = ["architecture", "dependencies", "patterns", "testing"] + +// Step 2: Create parallel exploration agents +const agents = angles.map(angle => + spawn_agent({ + message: ` +## TASK ASSIGNMENT + +### MANDATORY FIRST STEPS (Agent Execute) +1. **Read role definition**: ~/.codex/agents/cli-explore-agent.md (MUST read first) +2. Read: .workflow/project-tech.json + +--- + +Goal: Execute ${angle} exploration for ${task_description} + +Scope: +- Include: All source files relevant to ${angle} +- Exclude: node_modules, dist, build artifacts + +Context: +- Task: ${task_description} +- Angle: ${angle} + +Deliverables: +- Structured findings following output template +- File:line references for key discoveries +- Open questions for unclear areas + +Quality bar: +- At least 3 relevant files identified +- Findings backed by concrete evidence +` + }) +) + +// Step 3: Batch wait +const results = wait({ ids: agents, timeout_ms: 600000 }) + +// Step 4: Aggregate +const findings = agents.map((id, i) => ({ + angle: angles[i], + result: results.status[id].completed +})) + +// Step 5: Cleanup +agents.forEach(id => close_agent({ id })) +``` + +## Pattern 2: Analyze (Multi-Perspective) + +**Use When**: Code analysis from multiple dimensions needed. + +```javascript +// ==================== Analyze Pattern ==================== + +const perspectives = [ + { name: "security", focus: "OWASP Top 10, injection, auth bypass" }, + { name: "performance", focus: "O(n²), memory leaks, blocking I/O" }, + { name: "maintainability", focus: "complexity, coupling, duplication" } +] + +const agents = perspectives.map(p => + spawn_agent({ + message: ` +## TASK ASSIGNMENT + +### MANDATORY FIRST STEPS (Agent Execute) +1. **Read role definition**: ~/.codex/agents/cli-explore-agent.md (MUST read first) + +--- + +Goal: Analyze ${targetModule} from ${p.name} perspective +Focus: ${p.focus} + +Scope: +- Include: ${targetPaths} +- Exclude: Test files, generated code + +Deliverables: +- Severity-classified findings (Critical/High/Medium/Low) +- File:line references for each finding +- Remediation recommendations + +Quality bar: +- Every finding must have evidence (code reference) +- Remediation must be actionable +` + }) +) + +const results = wait({ ids: agents, timeout_ms: 600000 }) + +// Merge findings by severity +const merged = { + critical: [], high: [], medium: [], low: [] +} +agents.forEach((id, i) => { + const parsed = parseFindings(results.status[id].completed) + Object.keys(merged).forEach(sev => merged[sev].push(...(parsed[sev] || []))) +}) + +agents.forEach(id => close_agent({ id })) +``` + +## Pattern 3: Implement (Sequential Delegation) + +**Use When**: Code implementation following a plan. + +```javascript +// ==================== Implement Pattern ==================== + +const implementAgent = spawn_agent({ + message: ` +## TASK ASSIGNMENT + +### MANDATORY FIRST STEPS (Agent Execute) +1. **Read role definition**: ~/.codex/agents/code-developer.md (MUST read first) +2. Read: .workflow/project-tech.json +3. Read: .workflow/project-guidelines.json + +--- + +Goal: Implement ${featureDescription} + +Scope: +- Include: ${targetPaths} +- Exclude: Unrelated modules +- Constraints: No breaking changes, follow existing patterns + +Context: +- Plan: ${planContent} +- Dependencies: ${dependencies} +- Existing patterns: ${patterns} + +Deliverables: +- Working implementation following plan +- Updated/new test files +- Summary of changes with file:line references + +Quality bar: +- All existing tests pass +- New code follows project conventions +- No TypeScript errors +- Backward compatible +` +}) + +const result = wait({ ids: [implementAgent], timeout_ms: 900000 }) + +// Check for open questions (might need clarification) +if (result.status[implementAgent].completed.includes('CLARIFICATION_NEEDED')) { + // Handle clarification via send_input + const answers = getUserAnswers(result) + send_input({ id: implementAgent, message: `## ANSWERS\n${answers}\n\n## CONTINUE\nProceed with implementation.` }) + const final = wait({ ids: [implementAgent], timeout_ms: 900000 }) +} + +close_agent({ id: implementAgent }) +``` + +## Pattern 4: Validate (Test-Fix Cycle) + +**Use When**: Running tests and fixing failures iteratively. + +```javascript +// ==================== Validate Pattern ==================== + +const validateAgent = spawn_agent({ + message: ` +## TASK ASSIGNMENT + +### MANDATORY FIRST STEPS (Agent Execute) +1. **Read role definition**: ~/.codex/agents/test-fix-agent.md (MUST read first) + +--- + +Goal: Validate ${component} — run tests, fix failures, iterate + +Scope: +- Include: ${testPaths} +- Exclude: Unrelated test suites + +Context: +- Recent changes: ${changedFiles} +- Test framework: ${testFramework} + +Deliverables: +- All tests passing (or documented blocked tests) +- Fix summary with file:line references +- Coverage report + +Quality bar: +- Pass rate >= 95% +- No new test regressions +- Max 5 fix iterations +` +}) + +const round1 = wait({ ids: [validateAgent], timeout_ms: 600000 }) + +// Check if more iterations needed +let iteration = 1 +while ( + iteration < 5 && + round1.status[validateAgent].completed.includes('TESTS_FAILING') +) { + send_input({ + id: validateAgent, + message: `## ITERATION ${iteration + 1}\nContinue fixing remaining failures. Focus on:\n${remainingFailures}` + }) + const roundN = wait({ ids: [validateAgent], timeout_ms: 300000 }) + iteration++ +} + +close_agent({ id: validateAgent }) +``` + +## Pattern 5: Review (Multi-Dimensional) + +**Use When**: Code review from multiple dimensions. + +```javascript +// ==================== Review Pattern ==================== + +const dimensions = [ + { name: "correctness", agent: "cli-explore-agent" }, + { name: "security", agent: "cli-explore-agent" }, + { name: "performance", agent: "cli-explore-agent" }, + { name: "style", agent: "cli-explore-agent" } +] + +const agents = dimensions.map(d => + spawn_agent({ + message: ` +## TASK ASSIGNMENT + +### MANDATORY FIRST STEPS (Agent Execute) +1. **Read role definition**: ~/.codex/agents/${d.agent}.md (MUST read first) + +--- + +Goal: Review ${targetCode} for ${d.name} +Scope: ${changedFiles} +Deliverables: Findings with severity, file:line, remediation +` + }) +) + +const results = wait({ ids: agents, timeout_ms: 600000 }) + +// Aggregate review findings +const review = { + approved: true, + findings: [], + blockers: [] +} + +agents.forEach((id, i) => { + const parsed = parseReview(results.status[id].completed) + review.findings.push(...parsed.findings) + if (parsed.blockers.length > 0) { + review.approved = false + review.blockers.push(...parsed.blockers) + } +}) + +agents.forEach(id => close_agent({ id })) +``` + +## Pattern 6: Deep Interact (Merged Explore + Plan) + +**Use When**: Exploration and planning are tightly coupled. + +```javascript +// ==================== Deep Interact Pattern ==================== + +const agent = spawn_agent({ + message: ` +## TASK ASSIGNMENT + +### MANDATORY FIRST STEPS (Agent Execute) +1. **Read role definition**: ~/.codex/agents/cli-explore-agent.md (MUST read first) +2. **Also read**: ~/.codex/agents/cli-lite-planning-agent.md (dual role) + +--- + +### Phase A: Exploration +Goal: Explore codebase for ${task_description} +Output: Structured findings + CLARIFICATION_NEEDED questions (if any) + +### Phase B: Planning (activated after clarification) +Goal: Generate implementation plan based on exploration + answers +Output: Structured plan following plan schema + +Deliverables: +- Phase A: exploration findings (Summary/Findings/Open questions) +- Phase B: implementation plan (after receiving clarification answers) +` +}) + +// Phase A: Exploration +const exploration = wait({ ids: [agent], timeout_ms: 600000 }) + +if (exploration.status[agent].completed.includes('CLARIFICATION_NEEDED')) { + const answers = getUserAnswers(exploration) + + // Phase B: Planning (same agent, preserved context) + send_input({ + id: agent, + message: ` +## CLARIFICATION ANSWERS +${answers} + +## PROCEED TO PHASE B +Generate implementation plan based on your exploration findings and these answers. +` + }) + + const plan = wait({ ids: [agent], timeout_ms: 900000 }) +} + +close_agent({ id: agent }) +``` + +## Pattern 7: Two-Phase (Clarify → Execute) + +**Use When**: Task requires explicit clarification before execution. + +```javascript +// ==================== Two-Phase Pattern ==================== + +// Phase 1: Clarification +const agent = spawn_agent({ + message: ` +## TASK ASSIGNMENT + +### MANDATORY FIRST STEPS (Agent Execute) +1. **Read role definition**: ~/.codex/agents/${agentType}.md (MUST read first) + +--- + +### PHASE: CLARIFICATION ONLY + +Goal: Understand ${task_description} and identify unclear points + +Output ONLY: +1. Your understanding of the task (2-3 sentences) +2. CLARIFICATION_NEEDED questions (if any) +3. Recommended approach (1-2 sentences) + +DO NOT execute any changes yet. +` +}) + +const clarification = wait({ ids: [agent], timeout_ms: 300000 }) + +// Collect user confirmation/answers +const userResponse = processUserInput(clarification) + +// Phase 2: Execution +send_input({ + id: agent, + message: ` +## USER CONFIRMATION +${userResponse} + +## PROCEED TO EXECUTION +Now execute the task with full implementation. +Output: Complete deliverable following structured output template. +` +}) + +const execution = wait({ ids: [agent], timeout_ms: 900000 }) + +close_agent({ id: agent }) +``` + +--- + +## Pattern Selection Guide + +| Scenario | Recommended Pattern | Reason | +|----------|-------------------|--------| +| Explore codebase from N angles | Pattern 1: Explore | Parallel fan-out, independent angles | +| Analyze code quality | Pattern 2: Analyze | Multi-perspective, severity classification | +| Implement from plan | Pattern 3: Implement | Sequential, plan-driven | +| Run tests + fix | Pattern 4: Validate | Iterative send_input loop | +| Code review | Pattern 5: Review | Multi-dimensional, aggregated verdict | +| Explore then plan | Pattern 6: Deep Interact | Context preservation, merged phases | +| Complex/unclear task | Pattern 7: Two-Phase | Clarify first, reduce rework | +| Simple one-shot task | Standard (no pattern) | spawn → wait → close | diff --git a/.claude/skills/codex-skill-designer/templates/orchestrator-template.md b/.claude/skills/codex-skill-designer/templates/orchestrator-template.md new file mode 100644 index 00000000..68759ffb --- /dev/null +++ b/.claude/skills/codex-skill-designer/templates/orchestrator-template.md @@ -0,0 +1,306 @@ +# Orchestrator Template + +Template for the generated Codex orchestrator document. + +## Purpose + +| Phase | Usage | +|-------|-------| +| Phase 0 | Read to understand orchestrator output structure | +| Phase 2 | Apply with skill-specific content | + +--- + +## Template + +```markdown +--- +name: {{skill_name}} +description: | + {{description}} +agents: {{agent_count}} +phases: {{phase_count}} +--- + +# {{skill_display_name}} + +{{one_paragraph_description}} + +## Architecture Overview + +\`\`\` +{{architecture_diagram}} +\`\`\` + +## Agent Registry + +| Agent | Role File | Responsibility | New/Existing | +|-------|-----------|----------------|--------------| +{{#each agents}} +| `{{this.name}}` | `{{this.role_file}}` | {{this.responsibility}} | {{this.status}} | +{{/each}} + +## Phase Execution + +{{#each phases}} +### Phase {{this.index}}: {{this.name}} + +{{this.description}} + +{{#if this.is_parallel}} +#### Parallel Fan-out + +\`\`\`javascript +// Create parallel agents +const agentIds = [ +{{#each this.agents}} + spawn_agent({ + message: \` +## TASK ASSIGNMENT + +### MANDATORY FIRST STEPS (Agent Execute) +1. **Read role definition**: {{this.role_file}} (MUST read first) +2. Read: .workflow/project-tech.json +3. Read: .workflow/project-guidelines.json + +--- + +Goal: {{this.goal}} + +Scope: +- Include: {{this.scope_include}} +- Exclude: {{this.scope_exclude}} + +Context: +{{this.context}} + +Deliverables: +{{this.deliverables}} + +Quality bar: +{{this.quality_bar}} +\` + }), +{{/each}} +] + +// Batch wait +const results = wait({ + ids: agentIds, + timeout_ms: {{this.timeout_ms}} +}) + +// Handle timeout +if (results.timed_out) { + const completed = agentIds.filter(id => results.status[id]?.completed) + const pending = agentIds.filter(id => !results.status[id]?.completed) + // Use completed results, log pending +} + +// Aggregate results +const phaseResults = agentIds.map(id => results.status[id].completed) + +// Cleanup +agentIds.forEach(id => close_agent({ id })) +\`\`\` +{{/if}} + +{{#if this.is_standard}} +#### Standard Execution + +\`\`\`javascript +const agentId = spawn_agent({ + message: \` +## TASK ASSIGNMENT + +### MANDATORY FIRST STEPS (Agent Execute) +1. **Read role definition**: {{this.agent.role_file}} (MUST read first) +2. Read: .workflow/project-tech.json +3. Read: .workflow/project-guidelines.json + +--- + +Goal: {{this.goal}} + +Scope: +- Include: {{this.scope_include}} +- Exclude: {{this.scope_exclude}} + +Context: +{{this.context}} + +Deliverables: +{{this.deliverables}} + +Quality bar: +{{this.quality_bar}} +\` +}) + +const result = wait({ ids: [agentId], timeout_ms: {{this.timeout_ms}} }) + +if (result.timed_out) { + // Timeout handling: continue wait or urge convergence + send_input({ id: agentId, message: "Please finalize and output current findings." }) + const retry = wait({ ids: [agentId], timeout_ms: 60000 }) +} + +close_agent({ id: agentId }) +\`\`\` +{{/if}} + +{{#if this.is_deep_interaction}} +#### Deep Interaction (Multi-round) + +\`\`\`javascript +const agent = spawn_agent({ + message: \` +## TASK ASSIGNMENT + +### MANDATORY FIRST STEPS (Agent Execute) +1. **Read role definition**: {{this.agent.role_file}} (MUST read first) + +--- + +### Phase A: {{this.initial_goal}} +Output: Findings + Open Questions (CLARIFICATION_NEEDED format) + +### Phase B: {{this.followup_goal}} (after clarification) +Output: Complete deliverable +\` +}) + +// Round 1: Initial exploration +const round1 = wait({ ids: [agent], timeout_ms: {{this.timeout_ms}} }) + +// Check for clarification needs +if (round1.status[agent].completed.includes('CLARIFICATION_NEEDED')) { + // Parse questions, collect user answers + const answers = collectUserAnswers(round1.status[agent].completed) + + // Round 2: Continue with answers + send_input({ + id: agent, + message: \` +## CLARIFICATION ANSWERS +\${answers} + +## NEXT STEP +Proceed with Phase B. +\` + }) + + const round2 = wait({ ids: [agent], timeout_ms: {{this.followup_timeout_ms}} }) +} + +close_agent({ id: agent }) +\`\`\` +{{/if}} + +{{#if this.is_pipeline}} +#### Pipeline (Sequential Chain) + +\`\`\`javascript +{{#each this.stages}} +// Stage {{this.index}}: {{this.name}} +const stage{{this.index}}Agent = spawn_agent({ + message: \` +## TASK ASSIGNMENT + +### MANDATORY FIRST STEPS (Agent Execute) +1. **Read role definition**: {{this.role_file}} (MUST read first) + +--- + +Goal: {{this.goal}} +{{#if this.previous_output}} +## PREVIOUS STAGE OUTPUT +\${stage{{this.previous_index}}Result} +{{/if}} + +Deliverables: {{this.deliverables}} +\` +}) + +const stage{{this.index}}Result = wait({ ids: [stage{{this.index}}Agent], timeout_ms: {{this.timeout_ms}} }) +close_agent({ id: stage{{this.index}}Agent }) +{{/each}} +\`\`\` +{{/if}} + +{{/each}} + +## Result Aggregation + +\`\`\`javascript +// Merge results from all phases +const finalResult = { +{{#each phases}} + phase{{this.index}}: phase{{this.index}}Results, +{{/each}} +} + +// Output summary +console.log(\` +## Skill Execution Complete + +{{#each phases}} +### Phase {{this.index}}: {{this.name}} +Status: \${phase{{this.index}}Results.status} +{{/each}} +\`) +\`\`\` + +## Lifecycle Management + +### Timeout Handling + +| Timeout Scenario | Action | +|-----------------|--------| +| Single agent timeout | send_input to urge convergence, retry wait | +| Parallel partial timeout | Use completed results if >= 70%, close pending | +| All agents timeout | Log error, abort with partial state | + +### Cleanup Protocol + +\`\`\`javascript +// Track all agents created during execution +const allAgentIds = [] + +// ... (agents added during phase execution) ... + +// Final cleanup (end of orchestrator or on error) +allAgentIds.forEach(id => { + try { close_agent({ id }) } catch { /* already closed */ } +}) +\`\`\` + +## Error Handling + +| Scenario | Resolution | +|----------|------------| +| Agent produces invalid output | Retry with clarified instructions via send_input | +| Agent timeout | Urge convergence, retry, or abort | +| Missing role file | Log error, skip agent or use fallback | +| Partial results | Proceed with available data, log gaps | +``` + +--- + +## Variable Reference + +| Variable | Source | Description | +|----------|--------|-------------| +| `{{skill_name}}` | codexSkillConfig.name | Skill identifier | +| `{{skill_display_name}}` | Derived from name | Human-readable title | +| `{{description}}` | codexSkillConfig.description | Skill description | +| `{{agent_count}}` | codexSkillConfig.agents.length | Number of agents | +| `{{phase_count}}` | codexSkillConfig.phases.length | Number of phases | +| `{{architecture_diagram}}` | Generated from phase/agent topology | ASCII flow diagram | +| `{{agents}}` | codexSkillConfig.agents | Array of agent configs | +| `{{phases}}` | codexSkillConfig.phases | Array of phase configs | +| `{{phases[].is_parallel}}` | phase.interaction_model === "parallel_fanout" | Boolean | +| `{{phases[].is_standard}}` | phase.interaction_model === "standard" | Boolean | +| `{{phases[].is_deep_interaction}}` | phase.interaction_model === "deep_interaction" | Boolean | +| `{{phases[].is_pipeline}}` | phase.interaction_model === "pipeline" | Boolean | +| `{{phases[].timeout_ms}}` | Phase-specific timeout | Default: 300000 | diff --git a/.claude/skills/team-issue/roles/coordinator.md b/.claude/skills/team-issue/roles/coordinator.md index e70cb1cd..1b99d976 100644 --- a/.claude/skills/team-issue/roles/coordinator.md +++ b/.claude/skills/team-issue/roles/coordinator.md @@ -89,6 +89,36 @@ if (issueIds.length === 0) { // Auto-detect mode const mode = detectMode(issueIds, explicitMode) + +// Execution method selection (for BUILD phase) +const execSelection = AskUserQuestion({ + questions: [ + { + question: "选择代码执行方式:", + header: "Execution", + multiSelect: false, + options: [ + { label: "Agent", description: "code-developer agent(同步,适合简单任务)" }, + { label: "Codex", description: "Codex CLI(后台,适合复杂任务)" }, + { label: "Gemini", description: "Gemini CLI(后台,适合分析类任务)" }, + { label: "Auto", description: "根据 solution task_count 自动选择(默认)" } + ] + }, + { + question: "实现后是否进行代码审查?", + header: "Code Review", + multiSelect: false, + options: [ + { label: "Skip", description: "不审查" }, + { label: "Gemini Review", description: "Gemini CLI 审查" }, + { label: "Codex Review", description: "Git-aware review(--uncommitted)" } + ] + } + ] +}) + +const executionMethod = execSelection.Execution || 'Auto' +const codeReviewTool = execSelection['Code Review'] || 'Skip' ``` **Mode Auto-Detection**: @@ -171,7 +201,7 @@ for (const issueId of issueIds) { TaskCreate({ subject: `BUILD-001: Implement solution for ${issueId}`, - description: `Implement solution for issue ${issueId}. Load via ccw issue detail , execute tasks, report via ccw issue done.`, + description: `Implement solution for issue ${issueId}. Load via ccw issue detail , execute tasks, report via ccw issue done.\nexecution_method: ${executionMethod}\ncode_review: ${codeReviewTool}`, activeForm: `Implementing ${issueId}`, owner: "implementer", addBlockedBy: [marshalId] @@ -215,7 +245,7 @@ for (const issueId of issueIds) { TaskCreate({ subject: `BUILD-001: Implement solution for ${issueId}`, - description: `Implement approved solution for issue ${issueId}.`, + description: `Implement approved solution for issue ${issueId}.\nexecution_method: ${executionMethod}\ncode_review: ${codeReviewTool}`, activeForm: `Implementing ${issueId}`, owner: "implementer", addBlockedBy: [marshalId] @@ -282,6 +312,9 @@ const marshalId = TaskCreate({ }) // BUILD tasks created dynamically after MARSHAL completes (based on DAG) +// Each BUILD-* task description MUST include: +// execution_method: ${executionMethod} +// code_review: ${codeReviewTool} ``` ### Phase 4: Coordination Loop diff --git a/.claude/skills/team-lifecycle/roles/coordinator.md b/.claude/skills/team-lifecycle/roles/coordinator.md index db6d94e7..c55794b1 100644 --- a/.claude/skills/team-lifecycle/roles/coordinator.md +++ b/.claude/skills/team-lifecycle/roles/coordinator.md @@ -160,6 +160,43 @@ if (mode === 'spec-only' || mode === 'full-lifecycle') { Simple tasks can skip clarification. +#### Execution Method Selection (impl/full-lifecycle modes) + +When mode includes implementation, select execution backend before team creation: + +```javascript +if (mode === 'impl-only' || mode === 'full-lifecycle') { + const execSelection = AskUserQuestion({ + questions: [ + { + question: "选择代码执行方式:", + header: "Execution", + multiSelect: false, + options: [ + { label: "Agent", description: "code-developer agent(同步,适合简单任务)" }, + { label: "Codex", description: "Codex CLI(后台,适合复杂任务)" }, + { label: "Gemini", description: "Gemini CLI(后台,适合分析类任务)" }, + { label: "Auto", description: "根据任务复杂度自动选择(默认)" } + ] + }, + { + question: "实现后是否进行代码审查?", + header: "Code Review", + multiSelect: false, + options: [ + { label: "Skip", description: "不审查(Reviewer 角色独立负责)" }, + { label: "Gemini Review", description: "Gemini CLI 审查" }, + { label: "Codex Review", description: "Git-aware review(--uncommitted)" } + ] + } + ] + }) + + var executionMethod = execSelection.Execution || 'Auto' + var codeReviewTool = execSelection['Code Review'] || 'Skip' +} +``` + ### Phase 2: Create Team + Spawn Workers ```javascript @@ -277,7 +314,7 @@ TaskCreate({ subject: "PLAN-001: 探索和规划实现", description: `${taskDes TaskUpdate({ taskId: planId, owner: "planner" }) // IMPL-001 (blockedBy PLAN-001) -TaskCreate({ subject: "IMPL-001: 实现已批准的计划", description: `${taskDescription}\n\nSession: ${sessionFolder}\nPlan: ${sessionFolder}/plan/plan.json`, activeForm: "实现中" }) +TaskCreate({ subject: "IMPL-001: 实现已批准的计划", description: `${taskDescription}\n\nSession: ${sessionFolder}\nPlan: ${sessionFolder}/plan/plan.json\nexecution_method: ${executionMethod || 'Auto'}\ncode_review: ${codeReviewTool || 'Skip'}`, activeForm: "实现中" }) TaskUpdate({ taskId: implId, owner: "executor", addBlockedBy: [planId] }) // TEST-001 (blockedBy IMPL-001) diff --git a/.claude/skills/team-lifecycle/roles/executor.md b/.claude/skills/team-lifecycle/roles/executor.md index 1b2a9cd7..a8dc4d36 100644 --- a/.claude/skills/team-lifecycle/roles/executor.md +++ b/.claude/skills/team-lifecycle/roles/executor.md @@ -1,12 +1,12 @@ # Role: executor -Code implementation following approved plans. Reads plan files, implements changes, self-validates, and reports completion. +Code implementation following approved plans. Reads plan files, routes to selected execution backend (Agent/Codex/Gemini), self-validates, and reports completion. ## Role Identity - **Name**: `executor` - **Task Prefix**: `IMPL-*` -- **Responsibility**: Load plan → Implement code → Self-validate → Report completion +- **Responsibility**: Load plan → Route to backend → Implement code → Self-validate → Report completion - **Communication**: SendMessage to coordinator only ## Message Types @@ -40,6 +40,35 @@ When `mcp__ccw-tools__team_msg` MCP is unavailable: Bash(`ccw team log --team "${teamName}" --from "executor" --to "coordinator" --type "impl_complete" --summary "IMPL-001 complete: 5 files changed" --json`) ``` +## Execution Backends + +| Backend | Tool | Invocation | Mode | +|---------|------|------------|------| +| `agent` | code-developer subagent | `Task({ subagent_type: "code-developer" })` | 同步 | +| `codex` | Codex CLI | `ccw cli --tool codex --mode write` | 后台 | +| `gemini` | Gemini CLI | `ccw cli --tool gemini --mode write` | 后台 | + +## Execution Method Resolution + +从 IMPL-* 任务 description 中解析执行方式(coordinator 在创建任务时已写入): + +```javascript +function resolveExecutor(taskDesc, taskCount) { + const methodMatch = taskDesc.match(/execution_method:\s*(Agent|Codex|Gemini|Auto)/i) + const method = methodMatch ? methodMatch[1] : 'Auto' + + if (method.toLowerCase() === 'auto') { + return taskCount <= 3 ? 'agent' : 'codex' + } + return method.toLowerCase() // 'agent' | 'codex' | 'gemini' +} + +function resolveCodeReview(taskDesc) { + const reviewMatch = taskDesc.match(/code_review:\s*(\S+)/i) + return reviewMatch ? reviewMatch[1] : 'Skip' +} +``` + ## Execution (5-Phase) ### Phase 1: Task & Plan Loading @@ -69,6 +98,10 @@ if (!planPath) { } const plan = JSON.parse(Read(planPath)) + +// Resolve execution method +const executor = resolveExecutor(task.description, plan.task_count || plan.task_ids?.length || 0) +const codeReview = resolveCodeReview(task.description) ``` ### Phase 2: Task Grouping @@ -103,7 +136,7 @@ const planTasks = plan.task_ids.map(id => JSON.parse(Read(`${planPath.replace('p const batches = createBatches(planTasks) ``` -### Phase 3: Code Implementation +### Phase 3: Code Implementation (Multi-Backend Routing) ```javascript // Unified Task Prompt Builder @@ -130,33 +163,55 @@ ${(planTask.convergence?.criteria || []).map(c => `- [ ] ${c}`).join('\n')} ` } +function buildBatchPrompt(batch) { + const taskPrompts = batch.tasks.map(buildExecutionPrompt).join('\n\n---\n') + return `## Goal\n${plan.summary}\n\n## Tasks\n${taskPrompts}\n\n## Context\n### Project Guidelines\n@.workflow/project-guidelines.json\n\nComplete each task according to its "Done when" checklist.` +} + const changedFiles = [] +const sessionId = task.description.match(/TLS-[\w-]+/)?.[0] || 'lifecycle' for (const batch of batches) { - if (batch.tasks.length === 1 && isSimpleTask(batch.tasks[0])) { - // Simple task: direct file editing + const batchPrompt = buildBatchPrompt(batch) + const batchId = `${sessionId}-B${batches.indexOf(batch) + 1}` + + if (batch.tasks.length === 1 && isSimpleTask(batch.tasks[0]) && executor === 'agent') { + // Simple task + Agent mode: direct file editing const t = batch.tasks[0] for (const f of (t.files || [])) { const content = Read(f.path) Edit({ file_path: f.path, old_string: "...", new_string: "..." }) changedFiles.push(f.path) } - } else { - // Complex task(s): delegate to code-developer sub-agent - const prompt = batch.tasks.map(buildExecutionPrompt).join('\n\n---\n') - + } else if (executor === 'agent') { + // Agent execution (synchronous) Task({ subagent_type: "code-developer", run_in_background: false, description: batch.tasks.map(t => t.title).join(' | '), - prompt: `## Goal\n${plan.summary}\n\n## Tasks\n${prompt}\n\n## Context\n### Project Guidelines\n@.workflow/project-guidelines.json\n\nComplete each task according to its "Done when" checklist.` + prompt: batchPrompt }) - + batch.tasks.forEach(t => (t.files || []).forEach(f => changedFiles.push(f.path))) + } else if (executor === 'codex') { + // Codex CLI execution (background) + Bash( + `ccw cli -p "${batchPrompt}" --tool codex --mode write --id ${batchId}`, + { run_in_background: true } + ) + // STOP — CLI 后台执行,等待 task hook callback + batch.tasks.forEach(t => (t.files || []).forEach(f => changedFiles.push(f.path))) + } else if (executor === 'gemini') { + // Gemini CLI execution (background) + Bash( + `ccw cli -p "${batchPrompt}" --tool gemini --mode write --id ${batchId}`, + { run_in_background: true } + ) + // STOP — CLI 后台执行,等待 task hook callback batch.tasks.forEach(t => (t.files || []).forEach(f => changedFiles.push(f.path))) } // Progress update - mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "executor", to: "coordinator", type: "impl_progress", summary: `Batch完成: ${changedFiles.length}个文件已变更` }) + mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "executor", to: "coordinator", type: "impl_progress", summary: `Batch完成 (${executor}): ${changedFiles.length}个文件已变更` }) } function isSimpleTask(task) { @@ -183,6 +238,22 @@ const testFiles = changedFiles .map(f => f.replace(/\/src\//, '/tests/').replace(/\.(ts|js)$/, '.test.$1')) .filter(f => Bash(`test -f ${f} && echo exists || true`).includes('exists')) if (testFiles.length > 0) Bash(`npx jest ${testFiles.join(' ')} --passWithNoTests 2>&1 || true`) + +// Optional: Code review (if configured by coordinator) +if (codeReview !== 'Skip') { + if (codeReview === 'Gemini Review' || codeReview === 'Gemini') { + Bash(`ccw cli -p "PURPOSE: Code review for IMPL changes against plan convergence criteria +TASK: • Verify convergence criteria • Check test coverage • Analyze code quality +MODE: analysis +CONTEXT: @**/* | Memory: Review lifecycle IMPL execution +EXPECTED: Quality assessment with issue identification +CONSTRAINTS: analysis=READ-ONLY" --tool gemini --mode analysis --id ${sessionId}-review`, + { run_in_background: true }) + } else if (codeReview === 'Codex Review' || codeReview === 'Codex') { + Bash(`ccw cli --tool codex --mode review --uncommitted`, + { run_in_background: true }) + } +} ``` ### Phase 5: Report to Coordinator @@ -192,7 +263,7 @@ mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "executor", to: "coordinator", type: "impl_complete", - summary: `IMPL完成: ${[...new Set(changedFiles)].length}个文件变更, syntax=${hasSyntaxErrors ? 'errors' : 'clean'}` + summary: `IMPL完成 (${executor}): ${[...new Set(changedFiles)].length}个文件变更, syntax=${hasSyntaxErrors ? 'errors' : 'clean'}` }) SendMessage({ @@ -201,6 +272,8 @@ SendMessage({ content: `## Implementation Complete **Task**: ${task.subject} +**Executor**: ${executor} +**Code Review**: ${codeReview} ### Changed Files ${[...new Set(changedFiles)].map(f => '- ' + f).join('\n')} @@ -211,9 +284,10 @@ ${acceptanceStatus.map(t => '**' + t.title + '**: ' + (t.criteria.every(c => c.m ### Validation - Syntax: ${hasSyntaxErrors ? 'Has errors (attempted fix)' : 'Clean'} - Tests: ${testFiles.length > 0 ? 'Ran' : 'N/A'} +${executor !== 'agent' ? `- CLI Resume ID: ${sessionId}-B*` : ''} Implementation is ready for testing and review.`, - summary: `IMPL complete: ${[...new Set(changedFiles)].length} files changed` + summary: `IMPL complete (${executor}): ${[...new Set(changedFiles)].length} files changed` }) TaskUpdate({ taskId: task.id, status: 'completed' }) @@ -227,8 +301,11 @@ TaskUpdate({ taskId: task.id, status: 'completed' }) |----------|------------| | No IMPL-* tasks available | Idle, wait for coordinator assignment | | Plan file not found | Notify coordinator, request plan location | +| Unknown execution_method | Fallback to `agent` with warning | | Syntax errors after implementation | Attempt auto-fix, report remaining errors | -| Sub-agent failure | Retry once, then attempt direct implementation | +| Agent (code-developer) failure | Retry once, then attempt direct implementation | +| CLI (Codex/Gemini) failure | Provide resume command with fixed ID, report error | +| CLI timeout | Use fixed ID `${sessionId}-B*` for resume | | File conflict / merge issue | Notify coordinator, request guidance | | Test failures in self-validation | Report in completion message, let tester handle | | Circular dependencies in plan | Execute in plan order, ignore dependency chain | diff --git a/.claude/skills/team-planex/SKILL.md b/.claude/skills/team-planex/SKILL.md index 5d3f13ad..79a6579f 100644 --- a/.claude/skills/team-planex/SKILL.md +++ b/.claude/skills/team-planex/SKILL.md @@ -180,6 +180,83 @@ Final: planner 发送 all_planned → executor 完成剩余 EXEC-* → 结束 - executor 持续轮询并消费可用的 EXEC-* 任务 - 当 planner 发送 `all_planned` 消息后,executor 完成所有剩余任务即可结束 +## Execution Method Selection + +在编排模式或直接调用 executor 前,**必须先确定执行方式**。支持 3 种执行后端: + +| Executor | 后端 | 适用场景 | +|----------|------|----------| +| `agent` | code-developer subagent | 简单任务、同步执行 | +| `codex` | `ccw cli --tool codex --mode write` | 复杂任务、后台执行 | +| `gemini` | `ccw cli --tool gemini --mode write` | 分析类任务、后台执行 | + +### 选择逻辑 + +```javascript +const autoYes = args.includes('--auto') || args.includes('-y') +const explicitExec = args.match(/--exec[=\s]+(agent|codex|gemini|auto)/i)?.[1] + +let executionConfig + +if (explicitExec) { + // 显式指定 + executionConfig = { + executionMethod: explicitExec.charAt(0).toUpperCase() + explicitExec.slice(1), + codeReviewTool: "Skip" + } +} else if (autoYes) { + // Auto 模式:默认 Agent + Skip review + executionConfig = { executionMethod: "Auto", codeReviewTool: "Skip" } +} else { + // 交互选择 + executionConfig = AskUserQuestion({ + questions: [ + { + question: "选择执行方式:", + header: "Execution", + multiSelect: false, + options: [ + { label: "Agent", description: "code-developer agent(同步,适合简单任务)" }, + { label: "Codex", description: "Codex CLI(后台,适合复杂任务)" }, + { label: "Gemini", description: "Gemini CLI(后台,适合分析类任务)" }, + { label: "Auto", description: "根据任务复杂度自动选择" } + ] + }, + { + question: "执行后是否进行代码审查?", + header: "Code Review", + multiSelect: false, + options: [ + { label: "Skip", description: "不审查" }, + { label: "Gemini Review", description: "Gemini CLI 审查" }, + { label: "Codex Review", description: "Git-aware review(--uncommitted)" }, + { label: "Agent Review", description: "当前 agent 审查" } + ] + } + ] + }) +} + +// Auto 解析:根据 solution task_count 决定 +function resolveExecutor(taskCount) { + if (executionConfig.executionMethod === 'Auto') { + return taskCount <= 3 ? 'agent' : 'codex' + } + return executionConfig.executionMethod.toLowerCase() +} +``` + +### 通过 args 指定 + +```bash +# 显式指定 +Skill(skill="team-planex", args="--exec=codex ISS-xxx") +Skill(skill="team-planex", args="--exec=agent --text '简单功能'") + +# Auto 模式(跳过交互) +Skill(skill="team-planex", args="-y --text '添加日志'") +``` + ## Orchestration Mode 当不带 `--role` 调用时,SKILL.md 进入轻量编排模式: @@ -195,7 +272,10 @@ const planMatch = args.match(/--plan\s+(\S+)/) let plannerInput = args // 透传给 planner -// 3. 创建初始 PLAN-* 任务 +// 3. 执行方式选择(见上方 Execution Method Selection) +// executionConfig 已确定: { executionMethod, codeReviewTool } + +// 4. 创建初始 PLAN-* 任务 TaskCreate({ subject: "PLAN-001: 初始规划", description: `规划任务。输入: ${plannerInput}`, @@ -203,7 +283,7 @@ TaskCreate({ owner: "planner" }) -// 4. Spawn planner agent +// 5. Spawn planner agent Task({ subagent_type: "general-purpose", team_name: teamName, @@ -212,10 +292,17 @@ Task({ 当你收到 PLAN-* 任务时,调用 Skill(skill="team-planex", args="--role=planner") 执行。 当前输入: ${plannerInput} +## 执行配置 +executor 的执行方式已确定: ${executionConfig.executionMethod} +创建 EXEC-* 任务时,在 description 中包含: + execution_method: ${executionConfig.executionMethod} + code_review: ${executionConfig.codeReviewTool} + ## 角色准则(强制) - 你只能处理 PLAN-* 前缀的任务 - 所有输出必须带 [planner] 标识前缀 - 完成每个 wave 后立即创建 EXEC-* 任务供 executor 消费 +- EXEC-* 任务 description 中必须包含 execution_method 字段 ## 消息总线(必须) 每次 SendMessage 前,先调用 mcp__ccw-tools__team_msg 记录。 @@ -227,7 +314,7 @@ Task({ 4. TaskUpdate completed → 检查下一个任务` }) -// 5. Spawn executor agent +// 6. Spawn executor agent Task({ subagent_type: "general-purpose", team_name: teamName, @@ -235,9 +322,15 @@ Task({ prompt: `你是 team "${teamName}" 的 EXECUTOR。 当你收到 EXEC-* 任务时,调用 Skill(skill="team-planex", args="--role=executor") 执行。 +## 执行配置 +默认执行方式: ${executionConfig.executionMethod} +代码审查: ${executionConfig.codeReviewTool} +(每个 EXEC-* 任务 description 中可能包含 execution_method 覆盖) + ## 角色准则(强制) - 你只能处理 EXEC-* 前缀的任务 - 所有输出必须带 [executor] 标识前缀 +- 根据 execution_method 选择执行后端(Agent/Codex/Gemini) - 每个 solution 完成后通知 planner ## 消息总线(必须) diff --git a/.claude/skills/team-planex/roles/executor.md b/.claude/skills/team-planex/roles/executor.md new file mode 100644 index 00000000..2fe6e2c3 --- /dev/null +++ b/.claude/skills/team-planex/roles/executor.md @@ -0,0 +1,402 @@ +# Role: executor + +加载 solution → 根据 execution_method 路由到对应后端(Agent/Codex/Gemini)→ 测试验证 → 提交。支持多种 CLI 执行后端,执行方式在 skill 启动前已确定(见 SKILL.md Execution Method Selection)。 + +## Role Identity + +- **Name**: `executor` +- **Task Prefix**: `EXEC-*` +- **Responsibility**: Code implementation (solution → route to backend → test → commit) +- **Communication**: SendMessage to planner only +- **Output Tag**: `[executor]` + +## Role Boundaries + +### MUST + +- 仅处理 `EXEC-*` 前缀的任务 +- 所有输出必须带 `[executor]` 标识 +- 按照 EXEC-* 任务中的 `execution_method` 字段选择执行后端 +- 每个 issue 完成后通知 planner +- 持续轮询新的 EXEC-* 任务(planner 可能随时创建新 wave) + +### MUST NOT + +- ❌ 创建 issue(planner 职责) +- ❌ 修改 solution 或 queue(planner 职责) +- ❌ 调用 issue-plan-agent 或 issue-queue-agent +- ❌ 直接与用户交互(AskUserQuestion) +- ❌ 为 planner 创建 PLAN-* 任务 + +## Message Types + +| Type | Direction | Trigger | Description | +|------|-----------|---------|-------------| +| `impl_complete` | executor → planner | Implementation and tests pass | 单个 issue 实现完成 | +| `impl_failed` | executor → planner | Implementation failed after retries | 实现失败 | +| `wave_done` | executor → planner | All EXEC tasks in a wave completed | 整个 wave 完成 | +| `error` | executor → planner | Blocking error | 执行错误 | + +## Toolbox + +### Execution Backends + +| Backend | Tool | Invocation | Mode | +|---------|------|------------|------| +| `agent` | code-developer subagent | `Task({ subagent_type: "code-developer" })` | 同步 | +| `codex` | Codex CLI | `ccw cli --tool codex --mode write` | 后台 | +| `gemini` | Gemini CLI | `ccw cli --tool gemini --mode write` | 后台 | + +### Direct Capabilities + +| Tool | Purpose | +|------|---------| +| `Read` | 读取 solution plan 和队列文件 | +| `Write` | 写入实现产物 | +| `Edit` | 编辑源代码 | +| `Bash` | 运行测试、git 操作、CLI 调用 | + +### CLI Capabilities + +| CLI Command | Purpose | +|-------------|---------| +| `ccw issue status --json` | 查看 issue 状态 | +| `ccw issue solutions --json` | 加载 bound solution | +| `ccw issue update --status in-progress` | 更新 issue 状态为进行中 | +| `ccw issue update --status resolved` | 标记 issue 已解决 | + +## Execution Method Resolution + +从 EXEC-* 任务的 description 中解析执行方式: + +```javascript +// 从任务描述中解析 execution_method +function resolveExecutor(taskDesc, solutionTaskCount) { + const methodMatch = taskDesc.match(/execution_method:\s*(Agent|Codex|Gemini|Auto)/i) + const method = methodMatch ? methodMatch[1] : 'Auto' + + if (method.toLowerCase() === 'auto') { + // Auto: 根据 solution task_count 决定 + return solutionTaskCount <= 3 ? 'agent' : 'codex' + } + return method.toLowerCase() // 'agent' | 'codex' | 'gemini' +} + +// 从任务描述中解析 code_review 配置 +function resolveCodeReview(taskDesc) { + const reviewMatch = taskDesc.match(/code_review:\s*(\S+)/i) + return reviewMatch ? reviewMatch[1] : 'Skip' +} +``` + +## Execution Prompt Builder + +统一的 prompt 构建,所有后端共用: + +```javascript +function buildExecutionPrompt(issueId, solution) { + return ` +## Issue +ID: ${issueId} +Title: ${solution.bound.title || 'N/A'} + +## Solution Plan +${JSON.stringify(solution.bound, null, 2)} + +## Implementation Requirements + +1. Follow the solution plan tasks in order +2. Write clean, minimal code following existing patterns +3. Run tests after each significant change +4. Ensure all existing tests still pass +5. Do NOT over-engineer — implement exactly what the solution specifies + +## Quality Checklist +- [ ] All solution tasks implemented +- [ ] No TypeScript/linting errors +- [ ] Existing tests pass +- [ ] New tests added where appropriate +- [ ] No security vulnerabilities introduced + +## Project Guidelines +@.workflow/project-guidelines.json +` +} +``` + +## Execution (5-Phase) + +### Phase 1: Task Discovery + +```javascript +const tasks = TaskList() +const myTasks = tasks.filter(t => + t.subject.startsWith('EXEC-') && + t.owner === 'executor' && + t.status === 'pending' && + t.blockedBy.length === 0 +) + +if (myTasks.length === 0) return // idle — wait for planner to create EXEC tasks + +const task = TaskGet({ taskId: myTasks[0].id }) +TaskUpdate({ taskId: task.id, status: 'in_progress' }) +``` + +### Phase 2: Load Solution & Resolve Executor + +```javascript +// Extract issue ID from task description +const issueIdMatch = task.description.match(/ISS-\d{8}-\d{6}/) +const issueId = issueIdMatch ? issueIdMatch[0] : null + +if (!issueId) { + mcp__ccw-tools__team_msg({ + operation: "log", team: "planex", from: "executor", to: "planner", + type: "error", + summary: "[executor] No issue ID found in task" + }) + SendMessage({ + type: "message", recipient: "planner", + content: "## [executor] Error\nNo issue ID in task description", + summary: "[executor] error: no issue ID" + }) + TaskUpdate({ taskId: task.id, status: 'completed' }) + return +} + +// Load solution plan +const solJson = Bash(`ccw issue solutions ${issueId} --json`) +const solution = JSON.parse(solJson) + +if (!solution.bound) { + mcp__ccw-tools__team_msg({ + operation: "log", team: "planex", from: "executor", to: "planner", + type: "error", + summary: `[executor] No bound solution for ${issueId}` + }) + SendMessage({ + type: "message", recipient: "planner", + content: `## [executor] Error\nNo bound solution for ${issueId}`, + summary: `[executor] error: no solution for ${issueId}` + }) + TaskUpdate({ taskId: task.id, status: 'completed' }) + return +} + +// Resolve execution method from task description +const taskCount = solution.bound.task_count || solution.bound.tasks?.length || 0 +const executor = resolveExecutor(task.description, taskCount) +const codeReview = resolveCodeReview(task.description) + +// Update issue status +Bash(`ccw issue update ${issueId} --status in-progress`) +``` + +### Phase 3: Implementation (Multi-Backend Routing) + +根据 `executor` 变量路由到对应后端: + +#### Option A: Agent Execution (`executor === 'agent'`) + +同步调用 code-developer subagent,适合简单任务(task_count ≤ 3)。 + +```javascript +if (executor === 'agent') { + const implResult = Task({ + subagent_type: "code-developer", + run_in_background: false, + description: `Implement solution for ${issueId}`, + prompt: buildExecutionPrompt(issueId, solution) + }) +} +``` + +#### Option B: Codex CLI Execution (`executor === 'codex'`) + +后台调用 Codex CLI,适合复杂任务。使用固定 ID 支持 resume。 + +```javascript +if (executor === 'codex') { + const fixedId = `planex-${issueId}` + + Bash( + `ccw cli -p "${buildExecutionPrompt(issueId, solution)}" --tool codex --mode write --id ${fixedId}`, + { run_in_background: true } + ) + // STOP — CLI 后台执行,等待 task hook callback 通知完成 + + // 失败时 resume: + // ccw cli -p "Continue implementation" --resume ${fixedId} --tool codex --mode write --id ${fixedId}-retry +} +``` + +#### Option C: Gemini CLI Execution (`executor === 'gemini'`) + +后台调用 Gemini CLI,适合需要分析的复合任务。 + +```javascript +if (executor === 'gemini') { + const fixedId = `planex-${issueId}` + + Bash( + `ccw cli -p "${buildExecutionPrompt(issueId, solution)}" --tool gemini --mode write --id ${fixedId}`, + { run_in_background: true } + ) + // STOP — CLI 后台执行,等待 task hook callback 通知完成 +} +``` + +### Phase 4: Verify & Commit + +```javascript +// Detect test command from package.json or project config +let testCmd = 'npm test' +try { + const pkgJson = JSON.parse(Read('package.json')) + if (pkgJson.scripts?.test) testCmd = 'npm test' + else if (pkgJson.scripts?.['test:unit']) testCmd = 'npm run test:unit' +} catch { + // Fallback: try common test runners +} + +// Verify implementation +const testResult = Bash(`${testCmd} 2>&1 || echo "TEST_FAILED"`) +const testPassed = !testResult.includes('TEST_FAILED') && !testResult.includes('FAIL') + +if (!testPassed) { + // Implementation failed — report to planner + mcp__ccw-tools__team_msg({ + operation: "log", team: "planex", from: "executor", to: "planner", + type: "impl_failed", + summary: `[executor] Tests failing for ${issueId} after implementation (via ${executor})` + }) + + SendMessage({ + type: "message", recipient: "planner", + content: `## [executor] Implementation Failed + +**Issue**: ${issueId} +**Executor**: ${executor} +**Status**: Tests failing after implementation +**Test Output** (truncated): +${testResult.slice(0, 500)} + +**Action**: May need solution revision or manual intervention. +${executor !== 'agent' ? `**Resume**: \`ccw cli -p "Fix failing tests" --resume planex-${issueId} --tool ${executor} --mode write --id planex-${issueId}-fix\`` : ''}`, + summary: `[executor] impl_failed: ${issueId} (${executor})` + }) + + TaskUpdate({ taskId: task.id, status: 'completed' }) + return +} + +// Optional: Code review (if configured) +if (codeReview !== 'Skip') { + executeCodeReview(codeReview, issueId) +} + +// Update issue status to resolved +Bash(`ccw issue update ${issueId} --status resolved`) +``` + +### Code Review (Optional) + +```javascript +function executeCodeReview(reviewTool, issueId) { + const reviewPrompt = `PURPOSE: Code review for ${issueId} implementation against solution plan +TASK: • Verify solution convergence criteria • Check test coverage • Analyze code quality • Identify issues +MODE: analysis +CONTEXT: @**/* | Memory: Review planex execution for ${issueId} +EXPECTED: Quality assessment with issue identification and recommendations +CONSTRAINTS: Focus on solution adherence and code quality | analysis=READ-ONLY` + + if (reviewTool === 'Gemini Review') { + Bash(`ccw cli -p "${reviewPrompt}" --tool gemini --mode analysis --id planex-review-${issueId}`, + { run_in_background: true }) + } else if (reviewTool === 'Codex Review') { + // Codex review: --uncommitted flag only (no prompt with target flags) + Bash(`ccw cli --tool codex --mode review --uncommitted`, + { run_in_background: true }) + } else if (reviewTool === 'Agent Review') { + // Current agent performs review inline + // Read solution convergence criteria and verify against implementation + } +} +``` + +### Phase 5: Report + Loop + +```javascript +mcp__ccw-tools__team_msg({ + operation: "log", + team: "planex", + from: "executor", + to: "planner", + type: "impl_complete", + summary: `[executor] Implementation complete for ${issueId} via ${executor}, tests passing` +}) + +SendMessage({ + type: "message", + recipient: "planner", + content: `## [executor] Implementation Complete + +**Issue**: ${issueId} +**Executor**: ${executor} +**Solution**: ${solution.bound.id} +**Code Review**: ${codeReview} +**Status**: All tests passing +**Issue Status**: Updated to resolved`, + summary: `[executor] EXEC complete: ${issueId} (${executor})` +}) + +TaskUpdate({ taskId: task.id, status: 'completed' }) + +// Check for next EXEC-* task (may include new wave tasks from planner) +const nextTasks = TaskList().filter(t => + t.subject.startsWith('EXEC-') && + t.owner === 'executor' && + t.status === 'pending' && + t.blockedBy.length === 0 +) + +if (nextTasks.length > 0) { + // Continue with next task → back to Phase 1 +} else { + // Check if planner has sent all_planned signal + // If yes and no more tasks → send wave_done and exit + mcp__ccw-tools__team_msg({ + operation: "log", + team: "planex", + from: "executor", + to: "planner", + type: "wave_done", + summary: "[executor] All EXEC tasks completed" + }) + + SendMessage({ + type: "message", + recipient: "planner", + content: `## [executor] All Tasks Done + +All EXEC-* tasks have been completed. Pipeline finished.`, + summary: "[executor] wave_done: all complete" + }) +} +``` + +## Error Handling + +| Scenario | Resolution | +|----------|------------| +| No EXEC-* tasks available | Idle, wait for planner to create tasks | +| Solution plan not found | Report error to planner | +| Unknown execution_method | Fallback to `agent` with warning | +| Agent (code-developer) failure | Retry once, then report impl_failed | +| CLI (Codex/Gemini) failure | Provide resume command with fixed ID, report impl_failed | +| CLI timeout | Use fixed ID `planex-{issueId}` for resume | +| Tests failing after implementation | Report impl_failed with test output + resume info | +| Issue status update failure | Log warning, continue with report | +| Dependency not yet complete | Wait — task is blocked by blockedBy | +| All tasks done but planner still planning | Send wave_done, then idle for more | diff --git a/.claude/skills/team-planex/roles/planner.md b/.claude/skills/team-planex/roles/planner.md new file mode 100644 index 00000000..cfca5792 --- /dev/null +++ b/.claude/skills/team-planex/roles/planner.md @@ -0,0 +1,373 @@ +# Role: planner + +需求拆解 → issue 创建 → 方案设计 → 队列编排 → EXEC 任务派发。内部调用 issue-plan-agent 和 issue-queue-agent,并通过 Wave Pipeline 持续推进。planner 同时承担 lead 角色(无独立 coordinator)。 + +## Role Identity + +- **Name**: `planner` +- **Task Prefix**: `PLAN-*` +- **Responsibility**: Planning lead (requirement → issues → solutions → queue → dispatch) +- **Communication**: SendMessage to executor; 需要时 AskUserQuestion +- **Output Tag**: `[planner]` + +## Role Boundaries + +### MUST + +- 仅处理 `PLAN-*` 前缀的任务 +- 所有输出必须带 `[planner]` 标识 +- 完成每个 wave 的 queue 后**立即创建 EXEC-\* 任务** +- 不等待 executor 完成当前 wave,直接进入下一 wave 规划 + +### MUST NOT + +- ❌ 直接编写/修改业务代码(executor 职责) +- ❌ 调用 code-developer agent +- ❌ 运行项目测试 +- ❌ git commit 代码变更 + +## Message Types + +| Type | Direction | Trigger | Description | +|------|-----------|---------|-------------| +| `wave_ready` | planner → executor | Wave queue 完成 + EXEC 任务已创建 | 新 wave 可执行 | +| `queue_ready` | planner → executor | 单个 issue 的 queue 就绪 | 增量通知 | +| `all_planned` | planner → executor | 所有 wave 规划完毕 | 最终信号 | +| `error` | planner → executor | 阻塞性错误 | 规划失败 | + +## Toolbox + +### Subagent Capabilities + +| Agent Type | Purpose | +|------------|---------| +| `issue-plan-agent` | Closed-loop planning: ACE exploration + solution generation + binding | +| `issue-queue-agent` | Solution ordering + conflict detection → execution queue | + +### CLI Capabilities + +| CLI Command | Purpose | +|-------------|---------| +| `ccw issue new --text '...' --json` | 从文本创建 issue | +| `ccw issue status --json` | 查看 issue 状态 | +| `ccw issue solutions --json` | 查看已绑定 solution | +| `ccw issue bind ` | 绑定 solution 到 issue | + +### Skill Capabilities + +| Skill | Purpose | +|-------|---------| +| `Skill(skill="issue:new", args="--text '...'")` | 从文本创建 issue | + +## Execution (5-Phase) + +### Phase 1: Task Discovery + +```javascript +const tasks = TaskList() +const myTasks = tasks.filter(t => + t.subject.startsWith('PLAN-') && + t.owner === 'planner' && + t.status === 'pending' && + t.blockedBy.length === 0 +) + +if (myTasks.length === 0) return // idle + +const task = TaskGet({ taskId: myTasks[0].id }) +TaskUpdate({ taskId: task.id, status: 'in_progress' }) +``` + +### Phase 2: Input Parsing + +解析任务描述中的输入类型,确定处理方式。 + +```javascript +const desc = task.description +const args = "$ARGUMENTS" + +// 1) 已有 Issue IDs +const issueIds = (desc + ' ' + args).match(/ISS-\d{8}-\d{6}/g) || [] + +// 2) 文本输入 +const textMatch = (desc + ' ' + args).match(/--text\s+['"]([^'"]+)['"]/) +const inputText = textMatch ? textMatch[1] : null + +// 3) Plan 文件输入 +const planMatch = (desc + ' ' + args).match(/--plan\s+(\S+)/) +const planFile = planMatch ? planMatch[1] : null + +// Determine input type +let inputType = 'unknown' +if (issueIds.length > 0) inputType = 'issue_ids' +else if (inputText) inputType = 'text' +else if (planFile) inputType = 'plan_file' +else { + // 任务描述本身可能就是需求文本 + inputType = 'text_from_description' +} +``` + +### Phase 3: Issue Processing Pipeline + +根据输入类型执行不同的处理路径: + +#### Path A: 文本输入 → 创建 Issue + +```javascript +if (inputType === 'text' || inputType === 'text_from_description') { + const text = inputText || desc + + // 使用 issue:new skill 创建 issue + Skill(skill="issue:new", args=`--text '${text}'`) + + // 获取新创建的 issue ID + // issue:new 会输出创建的 issue ID + // 将其加入 issueIds 列表 + issueIds.push(newIssueId) +} +``` + +#### Path B: Plan 文件 → 批量创建 Issues + +```javascript +if (inputType === 'plan_file') { + const planContent = Read(planFile) + + // 解析 Plan 文件中的 Phase/步骤 + // 每个 Phase 或独立步骤创建一个 issue + const phases = parsePlanPhases(planContent) + + for (const phase of phases) { + Skill(skill="issue:new", args=`--text '${phase.title}: ${phase.description}'`) + issueIds.push(newIssueId) + } +} +``` + +#### Path C: Issue IDs → 直接进入规划 + +Issue IDs 已就绪,直接进入 solution 规划。 + +#### Wave 规划(所有路径汇聚) + +将 issueIds 按波次分组规划: + +```javascript +const projectRoot = Bash('cd . && pwd').trim() + +// 按批次分组(每 wave 最多 5 个 issues) +const WAVE_SIZE = 5 +const waves = [] +for (let i = 0; i < issueIds.length; i += WAVE_SIZE) { + waves.push(issueIds.slice(i, i + WAVE_SIZE)) +} + +let waveNum = 0 +for (const waveIssues of waves) { + waveNum++ + + // Step 1: 调用 issue-plan-agent 生成 solutions + const planResult = Task({ + subagent_type: "issue-plan-agent", + run_in_background: false, + description: `Plan solutions for wave ${waveNum}`, + prompt: ` +issue_ids: ${JSON.stringify(waveIssues)} +project_root: "${projectRoot}" + +## Requirements +- Generate solutions for each issue +- Auto-bind single solutions +- For multiple solutions, select the most pragmatic one +` + }) + + // Step 2: 调用 issue-queue-agent 形成 queue + const queueResult = Task({ + subagent_type: "issue-queue-agent", + run_in_background: false, + description: `Form queue for wave ${waveNum}`, + prompt: ` +issue_ids: ${JSON.stringify(waveIssues)} +project_root: "${projectRoot}" + +## Requirements +- Order solutions by dependency (DAG) +- Detect conflicts between solutions +- Output execution queue +` + }) + + // Step 3: → Phase 4 (Wave Dispatch) +} +``` + +### Phase 4: Wave Dispatch + +每个 wave 的 queue 完成后,**立即创建 EXEC-\* 任务**供 executor 消费。 + +```javascript +// Read the generated queue +const queuePath = `.workflow/issues/queue/execution-queue.json` +const queue = JSON.parse(Read(queuePath)) + +// Create EXEC-* tasks from queue entries +const execTasks = [] +for (const entry of queue.queue) { + const execTask = TaskCreate({ + subject: `EXEC-W${waveNum}-${entry.issue_id}: 实现 ${entry.title || entry.issue_id}`, + description: `## 执行任务 + +**Wave**: ${waveNum} +**Issue**: ${entry.issue_id} +**Solution**: ${entry.solution_id} +**Priority**: ${entry.priority || 'normal'} +**Dependencies**: ${entry.depends_on?.join(', ') || 'none'} + +加载 solution plan 并实现代码。完成后运行测试、提交。`, + activeForm: `实现 ${entry.issue_id}`, + owner: "executor" + }) + execTasks.push(execTask) +} + +// Set up dependency chains between EXEC tasks (based on queue DAG) +for (const entry of queue.queue) { + if (entry.depends_on?.length > 0) { + const thisTask = execTasks.find(t => t.subject.includes(entry.issue_id)) + const depTasks = entry.depends_on.map(depId => + execTasks.find(t => t.subject.includes(depId)) + ).filter(Boolean) + + if (thisTask && depTasks.length > 0) { + TaskUpdate({ + taskId: thisTask.id, + addBlockedBy: depTasks.map(t => t.id) + }) + } + } +} + +// Notify executor: wave ready +mcp__ccw-tools__team_msg({ + operation: "log", + team: "planex", + from: "planner", + to: "executor", + type: "wave_ready", + summary: `[planner] Wave ${waveNum} ready: ${execTasks.length} EXEC tasks created` +}) + +SendMessage({ + type: "message", + recipient: "executor", + content: `## [planner] Wave ${waveNum} Ready + +**Issues**: ${waveIssues.join(', ')} +**EXEC Tasks Created**: ${execTasks.length} +**Queue**: ${queuePath} + +Executor 可以开始实现。`, + summary: `[planner] wave_ready: wave ${waveNum}` +}) + +// 不等待 executor 完成,继续下一 wave → back to Phase 3 loop +``` + +### Phase 5: Report + Finalize + +所有 wave 规划完毕后,发送最终信号。 + +```javascript +// All waves planned +mcp__ccw-tools__team_msg({ + operation: "log", + team: "planex", + from: "planner", + to: "executor", + type: "all_planned", + summary: `[planner] All ${waveNum} waves planned, ${issueIds.length} issues total` +}) + +SendMessage({ + type: "message", + recipient: "executor", + content: `## [planner] All Waves Planned + +**Total Waves**: ${waveNum} +**Total Issues**: ${issueIds.length} +**Status**: 所有规划完毕,等待 executor 完成剩余 EXEC-* 任务 + +Pipeline 完成后请 executor 发送 wave_done 确认。`, + summary: `[planner] all_planned: ${waveNum} waves, ${issueIds.length} issues` +}) + +TaskUpdate({ taskId: task.id, status: 'completed' }) + +// Check for next PLAN-* task (e.g., user added more requirements) +const nextTasks = TaskList().filter(t => + t.subject.startsWith('PLAN-') && + t.owner === 'planner' && + t.status === 'pending' && + t.blockedBy.length === 0 +) + +if (nextTasks.length > 0) { + // Continue with next task → back to Phase 1 +} +``` + +## Plan File Parsing + +解析 Plan 文件为 phases 列表: + +```javascript +function parsePlanPhases(planContent) { + const phases = [] + + // 匹配 ## Phase N: Title 或 ## Step N: Title 或 ### N. Title + const phaseRegex = /^#{2,3}\s+(?:Phase|Step|阶段)\s*\d*[:.:]\s*(.+?)$/gm + let match + let lastIndex = 0 + let lastTitle = null + + while ((match = phaseRegex.exec(planContent)) !== null) { + if (lastTitle !== null) { + const description = planContent.slice(lastIndex, match.index).trim() + phases.push({ title: lastTitle, description }) + } + lastTitle = match[1].trim() + lastIndex = match.index + match[0].length + } + + // Last phase + if (lastTitle !== null) { + const description = planContent.slice(lastIndex).trim() + phases.push({ title: lastTitle, description }) + } + + // Fallback: 如果没有匹配到 Phase 结构,将整个内容作为单个 issue + if (phases.length === 0) { + const titleMatch = planContent.match(/^#\s+(.+)$/m) + phases.push({ + title: titleMatch ? titleMatch[1] : 'Plan Implementation', + description: planContent.slice(0, 500) + }) + } + + return phases +} +``` + +## Error Handling + +| Scenario | Resolution | +|----------|------------| +| No PLAN-* tasks available | Idle, wait for orchestrator | +| Issue creation failure | Retry once with simplified text, then report error | +| issue-plan-agent failure | Retry once, then report error and skip to next issue | +| issue-queue-agent failure | Retry once, then create EXEC tasks without DAG ordering | +| Plan file not found | Report error with expected path | +| Empty input (no issues, no text, no plan) | AskUserQuestion for clarification | +| Wave partially failed | Report partial success, continue with successful issues | diff --git a/.codex/skills/ccw-loop/SKILL.md b/.codex/skills/ccw-loop/SKILL.md deleted file mode 100644 index d8ccb948..00000000 --- a/.codex/skills/ccw-loop/SKILL.md +++ /dev/null @@ -1,465 +0,0 @@ ---- -name: ccw-loop -description: Stateless iterative development loop with single-agent deep interaction. Supports develop, debug, validate phases with file-based state tracking. Triggers on "ccw-loop", "dev loop", "development loop", "开发循环", "迭代开发". -allowed-tools: spawn_agent, wait, send_input, close_agent, AskUserQuestion, Read, Write, Edit, Bash, Glob, Grep ---- - -# CCW Loop - Stateless Iterative Development Workflow - -Stateless iterative development loop using Codex single-agent deep interaction pattern. One agent handles all phases (develop → debug → validate → complete) via `send_input`, with file-based state tracking and Dashboard integration. - -## Architecture Overview - -``` -+-------------------------------------------------------------+ -| Dashboard (UI) | -| [Create] [Start] [Pause] [Resume] [Stop] [View Progress] | -+-------------------------------------------------------------+ - | - v -+-------------------------------------------------------------+ -| loop-v2-routes.ts (Control Plane) | -| | -| State: {projectRoot}/.workflow/.loop/{loopId}.json (MASTER) | -| Tasks: {projectRoot}/.workflow/.loop/{loopId}/.task/*.json | -| | -| /start -> Trigger ccw-loop skill with --loop-id | -| /pause -> Set status='paused' (skill checks before action) | -| /stop -> Set status='failed' (skill terminates) | -| /resume -> Set status='running' (skill continues) | -+-------------------------------------------------------------+ - | - v -+-------------------------------------------------------------+ -| ccw-loop Skill (Execution Plane) | -| | -| Single Agent Deep Interaction: | -| spawn_agent -> wait -> send_input -> ... -> close_agent | -| | -| Actions: INIT -> DEVELOP -> DEBUG -> VALIDATE -> COMPLETE | -+-------------------------------------------------------------+ -``` - -## Key Design Principles - -1. **Single Agent Deep Interaction**: One agent handles entire loop lifecycle via `send_input` (no multi-agent overhead) -2. **Unified State**: API and Skill share `{projectRoot}/.workflow/.loop/{loopId}.json` state file -3. **Control Signals**: Skill checks `status` field before each action (paused/stopped → graceful exit) -4. **File-Driven Progress**: All progress documented in `{projectRoot}/.workflow/.loop/{loopId}.progress/` -5. **Resumable**: Continue any loop with `--loop-id` -6. **Dual Trigger**: Supports API trigger (`--loop-id`) and direct call (task description) - -## Arguments - -| Arg | Required | Description | -|-----|----------|-------------| -| TASK | One of TASK or --loop-id | Task description (for new loop) | -| --loop-id | One of TASK or --loop-id | Existing loop ID to continue | -| --auto | No | Auto-cycle mode (develop → debug → validate → complete) | - -## Prep Package Integration - -When `prep-package.json` exists at `{projectRoot}/.workflow/.loop/prep-package.json`, Phase 1 consumes it to: -- Load pre-built task list from `.task/*.json` files instead of generating tasks from scratch -- Apply auto-loop config (max_iterations, timeout) -- Preserve source provenance and convergence criteria from upstream planning/analysis skills - -Prep packages are generated by the interactive prompt `/prompts:prep-loop`, which accepts JSONL from: -- `collaborative-plan-with-file` (.task/*.json) -- `analyze-with-file` (.task/*.json) -- `brainstorm-to-cycle` (cycle-task.md → converted to task format) - -See [phases/00-prep-checklist.md](phases/00-prep-checklist.md) for schema and validation rules. - -## Execution Modes - -### Mode 1: Interactive - -User manually selects each action via menu. - -``` -User -> MENU -> Select action -> Execute -> View results -> MENU -> ... -``` - -### Mode 2: Auto-Loop - -Automatic execution using `selectNextAction` logic. - -``` -INIT -> DEVELOP -> ... -> VALIDATE -> (if fail) -> DEBUG -> VALIDATE -> COMPLETE -``` - -## Execution Flow - -``` -Input Parsing: - └─ Parse arguments (TASK | --loop-id + --auto) - └─ Convert to structured context (loopId, state, progressDir) - -Phase 1: Session Initialization - └─ Ref: phases/01-session-init.md - ├─ Create new loop OR resume existing loop - ├─ Initialize state file and directory structure - └─ Output: loopId, state, progressDir - -Phase 2: Orchestration Loop - └─ Ref: phases/02-orchestration-loop.md - ├─ Spawn single executor agent - ├─ Main while loop: wait → parse → dispatch → send_input - ├─ Handle: COMPLETED / PAUSED / STOPPED / WAITING_INPUT / next action - ├─ Update iteration count per cycle - └─ close_agent on exit -``` - -**Phase Reference Documents** (read on-demand when phase executes): - -| Phase | Document | Purpose | -|-------|----------|---------| -| 0 | [phases/00-prep-checklist.md](phases/00-prep-checklist.md) | Prep package schema and validation rules | -| 1 | [phases/01-session-init.md](phases/01-session-init.md) | Argument parsing, state creation/resume, directory init | -| 2 | [phases/02-orchestration-loop.md](phases/02-orchestration-loop.md) | Agent spawn, main loop, result parsing, send_input dispatch | - -## Data Flow - -``` -User Input (TASK | --loop-id + --auto) - ↓ -[Parse Arguments] - ↓ loopId, state, progressDir - -Phase 1: Session Initialization - ↓ loopId, state (initialized/resumed), progressDir - -Phase 2: Orchestration Loop - ↓ spawn agent → [INIT] → wait → parse - ↓ - ┌─── Main Loop (while iteration < max) ──────────┐ - │ wait() → parseActionResult(output) │ - │ ├─ COMPLETED → close_agent, return │ - │ ├─ PAUSED → close_agent, return │ - │ ├─ STOPPED → close_agent, return │ - │ ├─ WAITING_INPUT → collect input → send_input │ - │ └─ next_action → send_input(continue) │ - │ Update iteration in state file │ - └──────────────────────────────────────────────────┘ - ↓ -close_agent → return finalState -``` - -## Session Structure - -``` -{projectRoot}/.workflow/.loop/ -├── {loopId}.json # Master state file (API + Skill shared) -├── {loopId}/.task/ # Task files directory (API managed) -│ └── TASK-{id}.json # Individual task files (task-schema.json) -└── {loopId}.progress/ # Skill progress files - ├── develop.md # Development progress timeline - ├── debug.md # Understanding evolution document - ├── validate.md # Validation report - ├── changes.log # Code changes log (NDJSON) - ├── debug.log # Debug log (NDJSON) - ├── hypotheses.json # Debug hypotheses tracking - ├── test-results.json # Test execution results - ├── coverage.json # Coverage data - └── summary.md # Completion summary -``` - -## State Management - -Master state file: `{projectRoot}/.workflow/.loop/{loopId}.json` - -```json -{ - "loop_id": "loop-v2-20260122T100000-abc123", - "title": "Task title", - "description": "Full task description", - "max_iterations": 10, - "status": "created | running | paused | completed | failed | user_exit", - "current_iteration": 0, - "created_at": "ISO8601", - "updated_at": "ISO8601", - "completed_at": "ISO8601 (optional)", - "failure_reason": "string (optional)", - - "skill_state": { - "current_action": "init | develop | debug | validate | complete | null", - "last_action": "string | null", - "completed_actions": [], - "mode": "interactive | auto", - - "develop": { - "total": 0, "completed": 0, "current_task": null, - "tasks": [{ "id": "task-001", "description": "...", "tool": "gemini", "mode": "write", "status": "pending", "files_changed": [], "created_at": "ISO8601", "completed_at": null }], - "last_progress_at": null - }, - - "debug": { - "active_bug": null, "hypotheses_count": 0, - "hypotheses": [{ "id": "H1", "description": "...", "testable_condition": "...", "logging_point": "file:func:line", "evidence_criteria": { "confirm": "...", "reject": "..." }, "likelihood": 1, "status": "pending", "evidence": null, "verdict_reason": null }], - "confirmed_hypothesis": null, "iteration": 0, "last_analysis_at": null - }, - - "validate": { - "pass_rate": 0, "coverage": 0, - "test_results": [{ "test_name": "...", "suite": "...", "status": "passed | failed | skipped", "duration_ms": 0, "error_message": null, "stack_trace": null }], - "passed": false, "failed_tests": [], "last_run_at": null - }, - - "errors": [{ "action": "...", "message": "...", "timestamp": "ISO8601" }], - - "summary": { "duration": 0, "iterations": 0, "develop": {}, "debug": {}, "validate": {} } - } -} -``` - -**Control Signal Checking**: Agent checks `state.status` before every action: -- `running` → continue -- `paused` → exit gracefully, wait for resume -- `failed` → terminate -- Other → stop - -**Recovery**: If state corrupted, rebuild `skill_state` from `{projectRoot}/.workflow/.loop/{loopId}.progress/` markdown files and logs. - -## Action Catalog - -| Action | Purpose | Preconditions | Output Files | Trigger | -|--------|---------|---------------|--------------|---------| -| [INIT](actions/action-init.md) | Initialize session | status=running, skill_state=null | develop.md, state.json | First run | -| [DEVELOP](actions/action-develop.md) | Execute dev task | pending tasks > 0 | develop.md, changes.log | Has pending tasks | -| [DEBUG](actions/action-debug.md) | Hypothesis debug | needs debugging | debug.md, debug.log | Test failures | -| [VALIDATE](actions/action-validate.md) | Run tests | needs validation | validate.md, test-results.json | After develop/debug | -| [COMPLETE](actions/action-complete.md) | Finish loop | all done | summary.md | All tasks done | -| [MENU](actions/action-menu.md) | Display menu | interactive mode | - | Interactive mode | - -### Action Flow - -``` -spawn_agent (ccw-loop-executor) - | - v - +-------+ - | INIT | (if skill_state is null) - +-------+ - | - v - +-------+ send_input - | MENU | <------------- (user selection in interactive mode) - +-------+ - | - +---+---+---+---+ - | | | | | - v v v v v - DEV DBG VAL CMP EXIT - | - v - wait() -> get result - | - v - [Loop continues via send_input] - | - v - close_agent() -``` - -### Action Dependencies - -| Action | Depends On | Leads To | -|--------|------------|----------| -| INIT | - | MENU or DEVELOP | -| MENU | INIT | User selection | -| DEVELOP | INIT | DEVELOP, DEBUG, VALIDATE | -| DEBUG | INIT | DEVELOP, VALIDATE | -| VALIDATE | DEVELOP or DEBUG | COMPLETE, DEBUG, DEVELOP | -| COMPLETE | - | Terminal | - -### Action Sequences - -``` -Happy Path (Auto): INIT → DEVELOP → DEVELOP → VALIDATE (pass) → COMPLETE -Debug Iteration: INIT → DEVELOP → VALIDATE (fail) → DEBUG → VALIDATE (pass) → COMPLETE -Interactive Path: INIT → MENU → DEVELOP → MENU → VALIDATE → MENU → COMPLETE -``` - -## Auto Mode Selection Logic - -```javascript -function selectNextAction(state) { - const skillState = state.skill_state - - // 1. Terminal conditions - if (state.status === 'completed') return null - if (state.status === 'failed') return null - if (state.current_iteration >= state.max_iterations) return 'COMPLETE' - - // 2. Initialization check - if (!skillState) return 'INIT' - - // 3. Auto selection based on state - const hasPendingDevelop = skillState.develop.tasks.some(t => t.status === 'pending') - - if (hasPendingDevelop) return 'DEVELOP' - - if (skillState.last_action === 'DEVELOP') { - if (skillState.develop.completed < skillState.develop.total) return 'DEBUG' - } - - if (skillState.last_action === 'DEBUG' || skillState.debug.confirmed_hypothesis) { - return 'VALIDATE' - } - - if (skillState.last_action === 'VALIDATE') { - if (!skillState.validate.passed) return 'DEVELOP' - } - - if (skillState.validate.passed && !hasPendingDevelop) return 'COMPLETE' - - return 'DEVELOP' -} -``` - -## Coordination Protocol - -### Agent → Orchestrator (ACTION_RESULT) - -Every action MUST output: - -``` -ACTION_RESULT: -- action: {ACTION_NAME} -- status: success | failed | needs_input -- message: {user-facing message} -- state_updates: { ... } - -FILES_UPDATED: -- {file_path}: {description} - -NEXT_ACTION_NEEDED: {ACTION_NAME} | WAITING_INPUT | COMPLETED | PAUSED -``` - -### Orchestrator → Agent (send_input) - -Auto mode continuation: - -``` -## CONTINUE EXECUTION - -Previous action completed: {action} -Result: {status} - -## EXECUTE NEXT ACTION - -Continue with: {next_action} -Read action instructions and execute. -Output ACTION_RESULT when complete. -``` - -Interactive mode user input: - -``` -## USER INPUT RECEIVED - -Action selected: {action} - -## EXECUTE SELECTED ACTION - -Read action instructions and execute: {action} -Update state and progress files accordingly. -Output ACTION_RESULT when complete. -``` - -### Codex Subagent API - -| API | Purpose | -|-----|---------| -| `spawn_agent({ message })` | Create subagent, returns `agent_id` | -| `wait({ ids, timeout_ms })` | Wait for results (only way to get output) | -| `send_input({ id, message })` | Continue interaction / follow-up | -| `close_agent({ id })` | Close and reclaim (irreversible) | - -**Rules**: Single agent for entire loop. `send_input` for multi-phase. `close_agent` only after confirming no more interaction needed. - -## TodoWrite Pattern - -### Phase-Level Tracking (Attached) - -```json -[ - {"content": "Phase 1: Session Initialization", "status": "completed"}, - {"content": "Phase 2: Orchestration Loop", "status": "in_progress"}, - {"content": " → Action: INIT", "status": "completed"}, - {"content": " → Action: DEVELOP (task 1/3)", "status": "in_progress"}, - {"content": " → Action: VALIDATE", "status": "pending"}, - {"content": " → Action: COMPLETE", "status": "pending"} -] -``` - -### Iteration Tracking (Collapsed) - -```json -[ - {"content": "Phase 1: Session Initialization", "status": "completed"}, - {"content": "Iteration 1: DEVELOP x3 + VALIDATE (pass)", "status": "completed"}, - {"content": "Phase 2: COMPLETE", "status": "in_progress"} -] -``` - -## Core Rules - -1. **Start Immediately**: First action is TodoWrite initialization, then Phase 1 execution -2. **Progressive Phase Loading**: Read phase docs ONLY when that phase is about to execute -3. **Parse Every Output**: Extract ACTION_RESULT from agent output for next decision -4. **Auto-Continue**: After each action, execute next pending action automatically (auto mode) -5. **Track Progress**: Update TodoWrite dynamically with attachment/collapse pattern -6. **Single Writer**: Orchestrator updates master state file; agent writes to progress files -7. **File References**: Pass file paths between orchestrator and agent, not content -8. **DO NOT STOP**: Continuous execution until COMPLETED, PAUSED, STOPPED, or max iterations - -## Error Handling - -| Error Type | Recovery | -|------------|----------| -| Agent timeout | send_input requesting convergence, then retry | -| State corrupted | Rebuild from progress markdown files and logs | -| Agent closed unexpectedly | Re-spawn with previous output in message | -| Action failed | Log error, continue or prompt user | -| Tests fail | Loop back to DEVELOP or DEBUG | -| Max iterations reached | Generate summary with remaining issues documented | -| Session not found | Create new session | - -## Coordinator Checklist - -### Before Each Phase - -- [ ] Read phase reference document -- [ ] Check current state for dependencies -- [ ] Update TodoWrite with phase tasks - -### After Each Phase - -- [ ] Parse agent outputs (ACTION_RESULT) -- [ ] Update master state file (iteration count, updated_at) -- [ ] Collapse TodoWrite sub-tasks -- [ ] Determine next action (continue / iterate / complete) - -## Reference Documents - -| Document | Purpose | -|----------|---------| -| [actions/](actions/) | Action definitions (INIT, DEVELOP, DEBUG, VALIDATE, COMPLETE, MENU) | - -## Usage - -```bash -# Start new loop (direct call) -/ccw-loop TASK="Implement user authentication" - -# Auto-cycle mode -/ccw-loop --auto TASK="Fix login bug and add tests" - -# Continue existing loop -/ccw-loop --loop-id=loop-v2-20260122-abc123 - -# API triggered auto-cycle -/ccw-loop --loop-id=loop-v2-20260122-abc123 --auto -``` diff --git a/.codex/skills/ccw-loop/actions/action-complete.md b/.codex/skills/ccw-loop/actions/action-complete.md deleted file mode 100644 index 5ea58707..00000000 --- a/.codex/skills/ccw-loop/actions/action-complete.md +++ /dev/null @@ -1,269 +0,0 @@ -# Action: COMPLETE - -Complete CCW Loop session and generate summary report. - -## Purpose - -- Generate completion report -- Aggregate all phase results -- Provide follow-up recommendations -- Offer expansion to issues -- Mark status as completed - -## Preconditions - -- [ ] state.status === 'running' -- [ ] state.skill_state !== null - -## Execution Steps - -### Step 1: Verify Control Signals - -```javascript -const state = JSON.parse(Read(`${projectRoot}/.workflow/.loop/${loopId}.json`)) - -if (state.status !== 'running') { - return { - action: 'COMPLETE', - status: 'failed', - message: `Cannot complete: status is ${state.status}`, - next_action: state.status === 'paused' ? 'PAUSED' : 'STOPPED' - } -} -``` - -### Step 2: Aggregate Statistics - -```javascript -const stats = { - // Time statistics - duration: Date.now() - new Date(state.created_at).getTime(), - iterations: state.current_iteration, - - // Development statistics - develop: { - total_tasks: state.skill_state.develop.total, - completed_tasks: state.skill_state.develop.completed, - completion_rate: state.skill_state.develop.total > 0 - ? ((state.skill_state.develop.completed / state.skill_state.develop.total) * 100).toFixed(1) - : 0 - }, - - // Debug statistics - debug: { - iterations: state.skill_state.debug.iteration, - hypotheses_tested: state.skill_state.debug.hypotheses.length, - root_cause_found: state.skill_state.debug.confirmed_hypothesis !== null - }, - - // Validation statistics - validate: { - runs: state.skill_state.validate.test_results.length, - passed: state.skill_state.validate.passed, - coverage: state.skill_state.validate.coverage, - failed_tests: state.skill_state.validate.failed_tests.length - } -} -``` - -### Step 3: Generate Summary Report - -```javascript -const timestamp = getUtc8ISOString() - -const summaryReport = `# CCW Loop Session Summary - -**Loop ID**: ${loopId} -**Task**: ${state.description} -**Started**: ${state.created_at} -**Completed**: ${timestamp} -**Duration**: ${formatDuration(stats.duration)} - ---- - -## Executive Summary - -${state.skill_state.validate.passed - ? 'All tests passed, validation successful' - : state.skill_state.develop.completed === state.skill_state.develop.total - ? 'Development complete, validation not passed - needs debugging' - : 'Task partially complete - pending items remain'} - ---- - -## Development Phase - -| Metric | Value | -|--------|-------| -| Total Tasks | ${stats.develop.total_tasks} | -| Completed | ${stats.develop.completed_tasks} | -| Completion Rate | ${stats.develop.completion_rate}% | - -### Completed Tasks - -${state.skill_state.develop.tasks.filter(t => t.status === 'completed').map(t => ` -- ${t.description} - - Files: ${t.files_changed?.join(', ') || 'N/A'} - - Completed: ${t.completed_at} -`).join('\n')} - -### Pending Tasks - -${state.skill_state.develop.tasks.filter(t => t.status !== 'completed').map(t => ` -- ${t.description} -`).join('\n') || '_None_'} - ---- - -## Debug Phase - -| Metric | Value | -|--------|-------| -| Iterations | ${stats.debug.iterations} | -| Hypotheses Tested | ${stats.debug.hypotheses_tested} | -| Root Cause Found | ${stats.debug.root_cause_found ? 'Yes' : 'No'} | - -${stats.debug.root_cause_found ? ` -### Confirmed Root Cause - -${state.skill_state.debug.confirmed_hypothesis}: ${state.skill_state.debug.hypotheses.find(h => h.id === state.skill_state.debug.confirmed_hypothesis)?.description} -` : ''} - ---- - -## Validation Phase - -| Metric | Value | -|--------|-------| -| Test Runs | ${stats.validate.runs} | -| Status | ${stats.validate.passed ? 'PASSED' : 'FAILED'} | -| Coverage | ${stats.validate.coverage || 'N/A'}% | -| Failed Tests | ${stats.validate.failed_tests} | - ---- - -## Recommendations - -${generateRecommendations(stats, state)} - ---- - -## Session Artifacts - -| File | Description | -|------|-------------| -| \`develop.md\` | Development progress timeline | -| \`debug.md\` | Debug exploration and learnings | -| \`validate.md\` | Validation report | -| \`test-results.json\` | Test execution results | - ---- - -*Generated by CCW Loop at ${timestamp}* -` - -Write(`${progressDir}/summary.md`, summaryReport) -``` - -### Step 4: Update State to Completed - -```javascript -state.status = 'completed' -state.completed_at = timestamp -state.updated_at = timestamp -state.skill_state.last_action = 'COMPLETE' -state.skill_state.summary = stats - -Write(`${projectRoot}/.workflow/.loop/${loopId}.json`, JSON.stringify(state, null, 2)) -``` - -## Output Format - -``` -ACTION_RESULT: -- action: COMPLETE -- status: success -- message: Loop completed. Duration: {duration}, Iterations: {N} -- state_updates: { - "status": "completed", - "completed_at": "{timestamp}" - } - -FILES_UPDATED: -- {projectRoot}/.workflow/.loop/{loopId}.json: Status set to completed -- {projectRoot}/.workflow/.loop/{loopId}.progress/summary.md: Summary report generated - -NEXT_ACTION_NEEDED: COMPLETED -``` - -## Expansion Options - -After completion, offer expansion to issues: - -``` -## Expansion Options - -Would you like to create follow-up issues? - -1. [test] Add more test cases -2. [enhance] Feature enhancements -3. [refactor] Code refactoring -4. [doc] Documentation updates -5. [none] No expansion needed - -Select options (comma-separated) or 'none': -``` - -## Helper Functions - -```javascript -function formatDuration(ms) { - const seconds = Math.floor(ms / 1000) - const minutes = Math.floor(seconds / 60) - const hours = Math.floor(minutes / 60) - - if (hours > 0) { - return `${hours}h ${minutes % 60}m` - } else if (minutes > 0) { - return `${minutes}m ${seconds % 60}s` - } else { - return `${seconds}s` - } -} - -function generateRecommendations(stats, state) { - const recommendations = [] - - if (stats.develop.completion_rate < 100) { - recommendations.push('- Complete remaining development tasks') - } - - if (!stats.validate.passed) { - recommendations.push('- Fix failing tests') - } - - if (stats.validate.coverage && stats.validate.coverage < 80) { - recommendations.push(`- Improve test coverage (current: ${stats.validate.coverage}%)`) - } - - if (recommendations.length === 0) { - recommendations.push('- Consider code review') - recommendations.push('- Update documentation') - recommendations.push('- Prepare for deployment') - } - - return recommendations.join('\n') -} -``` - -## Error Handling - -| Error Type | Recovery | -|------------|----------| -| Report generation failed | Show basic stats, skip file write | -| Issue creation failed | Log error, continue completion | - -## Next Actions - -- None (terminal state) -- To continue: Use `/ccw-loop --loop-id={loopId}` to reopen (will set status back to running) diff --git a/.codex/skills/ccw-loop/actions/action-debug.md b/.codex/skills/ccw-loop/actions/action-debug.md deleted file mode 100644 index 97a86d7a..00000000 --- a/.codex/skills/ccw-loop/actions/action-debug.md +++ /dev/null @@ -1,286 +0,0 @@ -# Action: DEBUG - -Hypothesis-driven debugging with understanding evolution documentation. - -## Purpose - -- Locate error source -- Generate testable hypotheses -- Add NDJSON instrumentation -- Analyze log evidence -- Correct understanding based on evidence -- Apply fixes - -## Preconditions - -- [ ] state.status === 'running' -- [ ] state.skill_state !== null - -## Mode Detection - -```javascript -const understandingPath = `${progressDir}/debug.md` -const debugLogPath = `${progressDir}/debug.log` - -const understandingExists = fs.existsSync(understandingPath) -const logHasContent = fs.existsSync(debugLogPath) && fs.statSync(debugLogPath).size > 0 - -const debugMode = logHasContent ? 'analyze' : (understandingExists ? 'continue' : 'explore') -``` - -## Execution Steps - -### Mode: Explore (First Debug) - -#### Step E1: Get Bug Description - -```javascript -// From test failures or user input -const bugDescription = state.skill_state.validate?.failed_tests?.[0] - || await getUserInput('Describe the bug:') -``` - -#### Step E2: Search Codebase - -```javascript -// Use ACE search_context to find related code -const searchResults = mcp__ace-tool__search_context({ - project_root_path: '.', - query: `code related to: ${bugDescription}` -}) -``` - -#### Step E3: Generate Hypotheses - -```javascript -const hypotheses = [ - { - id: 'H1', - description: 'Most likely cause', - testable_condition: 'What to check', - logging_point: 'file.ts:functionName:42', - evidence_criteria: { - confirm: 'If we see X, hypothesis confirmed', - reject: 'If we see Y, hypothesis rejected' - }, - likelihood: 1, - status: 'pending', - evidence: null, - verdict_reason: null - }, - // H2, H3... -] -``` - -#### Step E4: Create Understanding Document - -```javascript -const initialUnderstanding = `# Understanding Document - -**Loop ID**: ${loopId} -**Bug Description**: ${bugDescription} -**Started**: ${getUtc8ISOString()} - ---- - -## Exploration Timeline - -### Iteration 1 - Initial Exploration (${getUtc8ISOString()}) - -#### Current Understanding - -Based on bug description and code search: - -- Error pattern: [identified pattern] -- Affected areas: [files/modules] -- Initial hypothesis: [first thoughts] - -#### Evidence from Code Search - -[Search results summary] - -#### Hypotheses - -${hypotheses.map(h => ` -**${h.id}**: ${h.description} -- Testable condition: ${h.testable_condition} -- Logging point: ${h.logging_point} -- Likelihood: ${h.likelihood} -`).join('\n')} - ---- - -## Current Consolidated Understanding - -[Summary of what we know so far] -` - -Write(understandingPath, initialUnderstanding) -Write(`${progressDir}/hypotheses.json`, JSON.stringify({ hypotheses, iteration: 1 }, null, 2)) -``` - -#### Step E5: Add NDJSON Logging Points - -```javascript -// For each hypothesis, add instrumentation -for (const hypothesis of hypotheses) { - const [file, func, line] = hypothesis.logging_point.split(':') - - const logStatement = `console.log(JSON.stringify({ - hid: "${hypothesis.id}", - ts: Date.now(), - func: "${func}", - data: { /* relevant context */ } - }))` - - // Add to file using Edit tool -} -``` - -### Mode: Analyze (Has Logs) - -#### Step A1: Parse Debug Log - -```javascript -const logContent = Read(debugLogPath) -const entries = logContent.split('\n') - .filter(l => l.trim()) - .map(l => JSON.parse(l)) - -// Group by hypothesis ID -const byHypothesis = entries.reduce((acc, e) => { - acc[e.hid] = acc[e.hid] || [] - acc[e.hid].push(e) - return acc -}, {}) -``` - -#### Step A2: Evaluate Evidence - -```javascript -const hypothesesData = JSON.parse(Read(`${progressDir}/hypotheses.json`)) - -for (const hypothesis of hypothesesData.hypotheses) { - const evidence = byHypothesis[hypothesis.id] || [] - - // Evaluate against criteria - if (matchesConfirmCriteria(evidence, hypothesis.evidence_criteria.confirm)) { - hypothesis.status = 'confirmed' - hypothesis.evidence = evidence - hypothesis.verdict_reason = 'Evidence matches confirm criteria' - } else if (matchesRejectCriteria(evidence, hypothesis.evidence_criteria.reject)) { - hypothesis.status = 'rejected' - hypothesis.evidence = evidence - hypothesis.verdict_reason = 'Evidence matches reject criteria' - } else { - hypothesis.status = 'inconclusive' - hypothesis.evidence = evidence - hypothesis.verdict_reason = 'Insufficient evidence' - } -} -``` - -#### Step A3: Update Understanding - -```javascript -const iteration = hypothesesData.iteration + 1 -const timestamp = getUtc8ISOString() - -const analysisEntry = ` -### Iteration ${iteration} - Evidence Analysis (${timestamp}) - -#### Log Analysis Results - -${hypothesesData.hypotheses.map(h => ` -**${h.id}**: ${h.status.toUpperCase()} -- Evidence: ${JSON.stringify(h.evidence?.slice(0, 3))} -- Reasoning: ${h.verdict_reason} -`).join('\n')} - -#### Corrected Understanding - -[Any corrections to previous assumptions] - -${confirmedHypothesis ? ` -#### Root Cause Identified - -**${confirmedHypothesis.id}**: ${confirmedHypothesis.description} -` : ` -#### Next Steps - -[What to investigate next] -`} - ---- -` - -const existingUnderstanding = Read(understandingPath) -Write(understandingPath, existingUnderstanding + analysisEntry) -``` - -### Step: Update State - -```javascript -state.skill_state.debug.active_bug = bugDescription -state.skill_state.debug.hypotheses = hypothesesData.hypotheses -state.skill_state.debug.hypotheses_count = hypothesesData.hypotheses.length -state.skill_state.debug.iteration = iteration -state.skill_state.debug.last_analysis_at = timestamp - -if (confirmedHypothesis) { - state.skill_state.debug.confirmed_hypothesis = confirmedHypothesis.id -} - -state.skill_state.last_action = 'DEBUG' -state.updated_at = timestamp -Write(`${projectRoot}/.workflow/.loop/${loopId}.json`, JSON.stringify(state, null, 2)) -``` - -## Output Format - -``` -ACTION_RESULT: -- action: DEBUG -- status: success -- message: {Mode description} - {result summary} -- state_updates: { - "debug.iteration": {N}, - "debug.confirmed_hypothesis": "{id or null}" - } - -FILES_UPDATED: -- {projectRoot}/.workflow/.loop/{loopId}.progress/debug.md: Understanding updated -- {projectRoot}/.workflow/.loop/{loopId}.progress/hypotheses.json: Hypotheses updated -- [Source files]: Instrumentation added - -NEXT_ACTION_NEEDED: {DEBUG | VALIDATE | DEVELOP | MENU} -``` - -## Next Action Selection - -```javascript -if (confirmedHypothesis) { - // Root cause found, apply fix and validate - return 'VALIDATE' -} else if (allRejected) { - // Generate new hypotheses - return 'DEBUG' -} else { - // Need more evidence - prompt user to reproduce bug - return 'WAITING_INPUT' // User needs to trigger bug -} -``` - -## Error Handling - -| Error Type | Recovery | -|------------|----------| -| Empty debug.log | Prompt user to reproduce bug | -| All hypotheses rejected | Generate new hypotheses | -| >5 iterations | Suggest escalation | - -## Next Actions - -- Root cause found: `VALIDATE` -- Need more evidence: `DEBUG` (after reproduction) -- All rejected: `DEBUG` (new hypotheses) diff --git a/.codex/skills/ccw-loop/actions/action-develop.md b/.codex/skills/ccw-loop/actions/action-develop.md deleted file mode 100644 index 41814a41..00000000 --- a/.codex/skills/ccw-loop/actions/action-develop.md +++ /dev/null @@ -1,183 +0,0 @@ -# Action: DEVELOP - -Execute development task and record progress to develop.md. - -## Purpose - -- Execute next pending development task -- Implement code changes -- Record progress to Markdown file -- Update task status in state - -## Preconditions - -- [ ] state.status === 'running' -- [ ] state.skill_state !== null -- [ ] state.skill_state.develop.tasks.some(t => t.status === 'pending') - -## Execution Steps - -### Step 1: Verify Control Signals - -```javascript -const state = JSON.parse(Read(`${projectRoot}/.workflow/.loop/${loopId}.json`)) - -if (state.status !== 'running') { - return { - action: 'DEVELOP', - status: 'failed', - message: `Cannot develop: status is ${state.status}`, - next_action: state.status === 'paused' ? 'PAUSED' : 'STOPPED' - } -} -``` - -### Step 2: Find Next Pending Task - -```javascript -const tasks = state.skill_state.develop.tasks -const currentTask = tasks.find(t => t.status === 'pending') - -if (!currentTask) { - return { - action: 'DEVELOP', - status: 'success', - message: 'All development tasks completed', - next_action: mode === 'auto' ? 'VALIDATE' : 'MENU' - } -} - -// Mark as in_progress -currentTask.status = 'in_progress' -``` - -### Step 3: Execute Development Task - -```javascript -console.log(`Executing task: ${currentTask.description}`) - -// Use appropriate tools based on task type -// - ACE search_context for finding patterns -// - Read for loading files -// - Edit/Write for making changes - -// Record files changed -const filesChanged = [] - -// Implementation logic... -``` - -### Step 4: Record Changes to Log (NDJSON) - -```javascript -const changesLogPath = `${progressDir}/changes.log` -const timestamp = getUtc8ISOString() - -const changeEntry = { - timestamp: timestamp, - task_id: currentTask.id, - description: currentTask.description, - files_changed: filesChanged, - result: 'success' -} - -// Append to NDJSON log -const existingLog = Read(changesLogPath) || '' -Write(changesLogPath, existingLog + JSON.stringify(changeEntry) + '\n') -``` - -### Step 5: Update Progress Document - -```javascript -const progressPath = `${progressDir}/develop.md` -const iteration = state.skill_state.develop.completed + 1 - -const progressEntry = ` -### Iteration ${iteration} - ${currentTask.description} (${timestamp}) - -#### Task Details - -- **ID**: ${currentTask.id} -- **Tool**: ${currentTask.tool} -- **Mode**: ${currentTask.mode} - -#### Implementation Summary - -[Implementation description] - -#### Files Changed - -${filesChanged.map(f => `- \`${f}\``).join('\n') || '- No files changed'} - -#### Status: COMPLETED - ---- - -` - -const existingProgress = Read(progressPath) -Write(progressPath, existingProgress + progressEntry) -``` - -### Step 6: Update State - -```javascript -currentTask.status = 'completed' -currentTask.completed_at = timestamp -currentTask.files_changed = filesChanged - -state.skill_state.develop.completed += 1 -state.skill_state.develop.current_task = null -state.skill_state.develop.last_progress_at = timestamp -state.skill_state.last_action = 'DEVELOP' -state.skill_state.completed_actions.push('DEVELOP') -state.updated_at = timestamp - -Write(`${projectRoot}/.workflow/.loop/${loopId}.json`, JSON.stringify(state, null, 2)) -``` - -## Output Format - -``` -ACTION_RESULT: -- action: DEVELOP -- status: success -- message: Task completed: {task_description} -- state_updates: { - "develop.completed": {N}, - "develop.last_progress_at": "{timestamp}" - } - -FILES_UPDATED: -- {projectRoot}/.workflow/.loop/{loopId}.json: Task status updated -- {projectRoot}/.workflow/.loop/{loopId}.progress/develop.md: Progress entry added -- {projectRoot}/.workflow/.loop/{loopId}.progress/changes.log: Change entry added - -NEXT_ACTION_NEEDED: {DEVELOP | DEBUG | VALIDATE | MENU} -``` - -## Auto Mode Next Action Selection - -```javascript -const pendingTasks = tasks.filter(t => t.status === 'pending') - -if (pendingTasks.length > 0) { - return 'DEVELOP' // More tasks to do -} else { - return 'DEBUG' // All done, check for issues -} -``` - -## Error Handling - -| Error Type | Recovery | -|------------|----------| -| Task execution failed | Mark task as failed, continue to next | -| File write failed | Retry once, then report error | -| All tasks done | Move to DEBUG or VALIDATE | - -## Next Actions - -- More pending tasks: `DEVELOP` -- All tasks complete: `DEBUG` (auto) or `MENU` (interactive) -- Task failed: `DEVELOP` (retry) or `DEBUG` (investigate) diff --git a/.codex/skills/ccw-loop/actions/action-init.md b/.codex/skills/ccw-loop/actions/action-init.md deleted file mode 100644 index 0e5c910c..00000000 --- a/.codex/skills/ccw-loop/actions/action-init.md +++ /dev/null @@ -1,174 +0,0 @@ -# Action: INIT - -Initialize CCW Loop session, create directory structure and initial state. - -## Purpose - -- Create session directory structure -- Initialize state file with skill_state -- Analyze task description to generate development tasks -- Prepare execution environment - -## Preconditions - -- [ ] state.status === 'running' -- [ ] state.skill_state === null - -## Execution Steps - -### Step 1: Verify Control Signals - -```javascript -const state = JSON.parse(Read(`${projectRoot}/.workflow/.loop/${loopId}.json`)) - -if (state.status !== 'running') { - return { - action: 'INIT', - status: 'failed', - message: `Cannot init: status is ${state.status}`, - next_action: state.status === 'paused' ? 'PAUSED' : 'STOPPED' - } -} -``` - -### Step 2: Create Directory Structure - -```javascript -const progressDir = `${projectRoot}/.workflow/.loop/${loopId}.progress` - -// Directories created by orchestrator, verify they exist -// mkdir -p ${progressDir} -``` - -### Step 3: Analyze Task and Generate Tasks - -```javascript -// Check if prep tasks already loaded by orchestrator (from prep-package) -// If skill_state already has tasks (pre-populated by Phase 1), skip generation -const existingTasks = state.skill_state?.develop?.tasks -if (existingTasks && existingTasks.length > 0) { - console.log(`✓ Using ${existingTasks.length} pre-built tasks from prep-package`) - console.log(` Source: ${state.prep_source?.tool || 'unknown'}`) - // Skip to Step 4 — tasks already available - tasks = existingTasks -} else { - // No prep tasks — analyze task description and generate 3-7 development tasks - const taskDescription = state.description - - // Generate 3-7 development tasks based on analysis - // Use ACE search or smart_search to find relevant patterns - - tasks = [ - { - id: 'task-001', - description: 'Task description based on analysis', - tool: 'gemini', - mode: 'write', - status: 'pending', - priority: 1, - files: [], - created_at: getUtc8ISOString(), - completed_at: null - } - // ... more tasks - ] -} -``` - -### Step 4: Initialize Progress Document - -```javascript -const progressPath = `${progressDir}/develop.md` - -const progressInitial = `# Development Progress - -**Loop ID**: ${loopId} -**Task**: ${taskDescription} -**Started**: ${getUtc8ISOString()} - ---- - -## Task List - -${tasks.map((t, i) => `${i + 1}. [ ] ${t.description}`).join('\n')} - ---- - -## Progress Timeline - -` - -Write(progressPath, progressInitial) -``` - -### Step 5: Update State - -```javascript -const skillState = { - current_action: 'init', - last_action: null, - completed_actions: [], - mode: mode, - - develop: { - total: tasks.length, - completed: 0, - current_task: null, - tasks: tasks, - last_progress_at: null - }, - - debug: { - active_bug: null, - hypotheses_count: 0, - hypotheses: [], - confirmed_hypothesis: null, - iteration: 0, - last_analysis_at: null - }, - - validate: { - pass_rate: 0, - coverage: 0, - test_results: [], - passed: false, - failed_tests: [], - last_run_at: null - }, - - errors: [] -} - -state.skill_state = skillState -state.updated_at = getUtc8ISOString() -Write(`${projectRoot}/.workflow/.loop/${loopId}.json`, JSON.stringify(state, null, 2)) -``` - -## Output Format - -``` -ACTION_RESULT: -- action: INIT -- status: success -- message: Session initialized with {N} development tasks - -FILES_UPDATED: -- {projectRoot}/.workflow/.loop/{loopId}.json: skill_state initialized -- {projectRoot}/.workflow/.loop/{loopId}.progress/develop.md: Progress document created - -NEXT_ACTION_NEEDED: {DEVELOP (auto) | MENU (interactive)} -``` - -## Error Handling - -| Error Type | Recovery | -|------------|----------| -| Directory creation failed | Report error, stop | -| Task analysis failed | Create single generic task | -| State write failed | Retry once, then stop | - -## Next Actions - -- Success (auto mode): `DEVELOP` -- Success (interactive): `MENU` -- Failed: Report error diff --git a/.codex/skills/ccw-loop/actions/action-menu.md b/.codex/skills/ccw-loop/actions/action-menu.md deleted file mode 100644 index 85e555f8..00000000 --- a/.codex/skills/ccw-loop/actions/action-menu.md +++ /dev/null @@ -1,205 +0,0 @@ -# Action: MENU - -Display interactive action menu for user selection. - -## Purpose - -- Show current state summary -- Display available actions -- Wait for user selection -- Return selected action - -## Preconditions - -- [ ] state.status === 'running' -- [ ] state.skill_state !== null -- [ ] mode === 'interactive' - -## Execution Steps - -### Step 1: Verify Control Signals - -```javascript -const state = JSON.parse(Read(`${projectRoot}/.workflow/.loop/${loopId}.json`)) - -if (state.status !== 'running') { - return { - action: 'MENU', - status: 'failed', - message: `Cannot show menu: status is ${state.status}`, - next_action: state.status === 'paused' ? 'PAUSED' : 'STOPPED' - } -} -``` - -### Step 2: Generate Status Summary - -```javascript -// Development progress -const developProgress = state.skill_state.develop.total > 0 - ? `${state.skill_state.develop.completed}/${state.skill_state.develop.total} (${((state.skill_state.develop.completed / state.skill_state.develop.total) * 100).toFixed(0)}%)` - : 'Not started' - -// Debug status -const debugStatus = state.skill_state.debug.confirmed_hypothesis - ? 'Root cause found' - : state.skill_state.debug.iteration > 0 - ? `Iteration ${state.skill_state.debug.iteration}` - : 'Not started' - -// Validation status -const validateStatus = state.skill_state.validate.passed - ? 'PASSED' - : state.skill_state.validate.test_results.length > 0 - ? `FAILED (${state.skill_state.validate.failed_tests.length} failures)` - : 'Not run' -``` - -### Step 3: Display Menu - -```javascript -const menuDisplay = ` -================================================================ - CCW Loop - ${loopId} -================================================================ - - Task: ${state.description} - Iteration: ${state.current_iteration} - - +-----------------------------------------------------+ - | Phase | Status | - +-----------------------------------------------------+ - | Develop | ${developProgress.padEnd(35)}| - | Debug | ${debugStatus.padEnd(35)}| - | Validate | ${validateStatus.padEnd(35)}| - +-----------------------------------------------------+ - -================================================================ - -MENU_OPTIONS: -1. [develop] Continue Development - ${state.skill_state.develop.total - state.skill_state.develop.completed} tasks pending -2. [debug] Start Debugging - ${debugStatus} -3. [validate] Run Validation - ${validateStatus} -4. [status] View Detailed Status -5. [complete] Complete Loop -6. [exit] Exit (save and quit) -` - -console.log(menuDisplay) -``` - -## Output Format - -``` -ACTION_RESULT: -- action: MENU -- status: success -- message: ${menuDisplay} - -MENU_OPTIONS: -1. [develop] Continue Development - {N} tasks pending -2. [debug] Start Debugging - {status} -3. [validate] Run Validation - {status} -4. [status] View Detailed Status -5. [complete] Complete Loop -6. [exit] Exit (save and quit) - -NEXT_ACTION_NEEDED: WAITING_INPUT -``` - -## User Input Handling - -When user provides input, orchestrator sends it back via `send_input`: - -```javascript -// User selects "develop" -send_input({ - id: agent, - message: ` -## USER INPUT RECEIVED - -Action selected: develop - -## EXECUTE SELECTED ACTION - -Execute DEVELOP action. -` -}) -``` - -## Status Detail View - -If user selects "status": - -```javascript -const detailView = ` -## Detailed Status - -### Development Progress - -${Read(`${progressDir}/develop.md`)?.substring(0, 1000) || 'No progress recorded'} - -### Debug Status - -${state.skill_state.debug.hypotheses.length > 0 - ? state.skill_state.debug.hypotheses.map(h => ` ${h.id}: ${h.status} - ${h.description.substring(0, 50)}...`).join('\n') - : ' No debugging started'} - -### Validation Results - -${state.skill_state.validate.test_results.length > 0 - ? ` Last run: ${state.skill_state.validate.last_run_at} - Pass rate: ${state.skill_state.validate.pass_rate}%` - : ' No validation run yet'} -` - -console.log(detailView) - -// Return to menu -return { - action: 'MENU', - status: 'success', - message: detailView, - next_action: 'MENU' // Show menu again -} -``` - -## Exit Handling - -If user selects "exit": - -```javascript -// Save current state -state.status = 'user_exit' -state.updated_at = getUtc8ISOString() -Write(`${projectRoot}/.workflow/.loop/${loopId}.json`, JSON.stringify(state, null, 2)) - -return { - action: 'MENU', - status: 'success', - message: 'Session saved. Use --loop-id to resume.', - next_action: 'COMPLETED' -} -``` - -## Action Mapping - -| User Selection | Next Action | -|----------------|-------------| -| develop | DEVELOP | -| debug | DEBUG | -| validate | VALIDATE | -| status | MENU (after showing details) | -| complete | COMPLETE | -| exit | COMPLETED (save and exit) | - -## Error Handling - -| Error Type | Recovery | -|------------|----------| -| Invalid selection | Show menu again | -| User cancels | Return to menu | - -## Next Actions - -Based on user selection - forwarded via `send_input` by orchestrator. diff --git a/.codex/skills/ccw-loop/actions/action-validate.md b/.codex/skills/ccw-loop/actions/action-validate.md deleted file mode 100644 index 0c957b40..00000000 --- a/.codex/skills/ccw-loop/actions/action-validate.md +++ /dev/null @@ -1,250 +0,0 @@ -# Action: VALIDATE - -Run tests and verify implementation, record results to validate.md. - -## Purpose - -- Run unit tests -- Run integration tests -- Check code coverage -- Generate validation report -- Determine pass/fail status - -## Preconditions - -- [ ] state.status === 'running' -- [ ] state.skill_state !== null -- [ ] (develop.completed > 0) OR (debug.confirmed_hypothesis !== null) - -## Execution Steps - -### Step 1: Verify Control Signals - -```javascript -const state = JSON.parse(Read(`${projectRoot}/.workflow/.loop/${loopId}.json`)) - -if (state.status !== 'running') { - return { - action: 'VALIDATE', - status: 'failed', - message: `Cannot validate: status is ${state.status}`, - next_action: state.status === 'paused' ? 'PAUSED' : 'STOPPED' - } -} -``` - -### Step 2: Detect Test Framework - -```javascript -const packageJson = JSON.parse(Read('package.json') || '{}') -const testScript = packageJson.scripts?.test || 'npm test' -const coverageScript = packageJson.scripts?.['test:coverage'] -``` - -### Step 3: Run Tests - -```javascript -const testResult = await Bash({ - command: testScript, - timeout: 300000 // 5 minutes -}) - -// Parse test output based on framework -const testResults = parseTestOutput(testResult.stdout, testResult.stderr) -``` - -### Step 4: Run Coverage (if available) - -```javascript -let coverageData = null - -if (coverageScript) { - const coverageResult = await Bash({ - command: coverageScript, - timeout: 300000 - }) - - coverageData = parseCoverageReport(coverageResult.stdout) - Write(`${progressDir}/coverage.json`, JSON.stringify(coverageData, null, 2)) -} -``` - -### Step 5: Generate Validation Report - -```javascript -const timestamp = getUtc8ISOString() -const iteration = (state.skill_state.validate.test_results?.length || 0) + 1 - -const validationReport = `# Validation Report - -**Loop ID**: ${loopId} -**Task**: ${state.description} -**Validated**: ${timestamp} - ---- - -## Iteration ${iteration} - Validation Run - -### Test Execution Summary - -| Metric | Value | -|--------|-------| -| Total Tests | ${testResults.total} | -| Passed | ${testResults.passed} | -| Failed | ${testResults.failed} | -| Skipped | ${testResults.skipped} | -| Duration | ${testResults.duration_ms}ms | -| **Pass Rate** | **${((testResults.passed / testResults.total) * 100).toFixed(1)}%** | - -### Coverage Report - -${coverageData ? ` -| File | Statements | Branches | Functions | Lines | -|------|------------|----------|-----------|-------| -${coverageData.files.map(f => `| ${f.path} | ${f.statements}% | ${f.branches}% | ${f.functions}% | ${f.lines}% |`).join('\n')} - -**Overall Coverage**: ${coverageData.overall.statements}% -` : '_No coverage data available_'} - -### Failed Tests - -${testResults.failed > 0 ? testResults.failures.map(f => ` -#### ${f.test_name} - -- **Suite**: ${f.suite} -- **Error**: ${f.error_message} -`).join('\n') : '_All tests passed_'} - ---- - -## Validation Decision - -**Result**: ${testResults.failed === 0 ? 'PASS' : 'FAIL'} - -${testResults.failed > 0 ? ` -### Next Actions - -1. Review failed tests -2. Debug failures using DEBUG action -3. Fix issues and re-run validation -` : ` -### Next Actions - -1. Consider code review -2. Complete loop -`} -` - -Write(`${progressDir}/validate.md`, validationReport) -``` - -### Step 6: Save Test Results - -```javascript -const testResultsData = { - iteration, - timestamp, - summary: { - total: testResults.total, - passed: testResults.passed, - failed: testResults.failed, - skipped: testResults.skipped, - pass_rate: ((testResults.passed / testResults.total) * 100).toFixed(1), - duration_ms: testResults.duration_ms - }, - tests: testResults.tests, - failures: testResults.failures, - coverage: coverageData?.overall || null -} - -Write(`${progressDir}/test-results.json`, JSON.stringify(testResultsData, null, 2)) -``` - -### Step 7: Update State - -```javascript -const validationPassed = testResults.failed === 0 && testResults.passed > 0 - -state.skill_state.validate.test_results.push(testResultsData) -state.skill_state.validate.pass_rate = parseFloat(testResultsData.summary.pass_rate) -state.skill_state.validate.coverage = coverageData?.overall?.statements || 0 -state.skill_state.validate.passed = validationPassed -state.skill_state.validate.failed_tests = testResults.failures.map(f => f.test_name) -state.skill_state.validate.last_run_at = timestamp - -state.skill_state.last_action = 'VALIDATE' -state.updated_at = timestamp -Write(`${projectRoot}/.workflow/.loop/${loopId}.json`, JSON.stringify(state, null, 2)) -``` - -## Output Format - -``` -ACTION_RESULT: -- action: VALIDATE -- status: success -- message: Validation {PASSED | FAILED} - {pass_count}/{total_count} tests passed -- state_updates: { - "validate.passed": {true | false}, - "validate.pass_rate": {N}, - "validate.failed_tests": [{list}] - } - -FILES_UPDATED: -- {projectRoot}/.workflow/.loop/{loopId}.progress/validate.md: Validation report created -- {projectRoot}/.workflow/.loop/{loopId}.progress/test-results.json: Test results saved -- {projectRoot}/.workflow/.loop/{loopId}.progress/coverage.json: Coverage data saved (if available) - -NEXT_ACTION_NEEDED: {COMPLETE | DEBUG | DEVELOP | MENU} -``` - -## Next Action Selection - -```javascript -if (validationPassed) { - const pendingTasks = state.skill_state.develop.tasks.filter(t => t.status === 'pending') - if (pendingTasks.length === 0) { - return 'COMPLETE' - } else { - return 'DEVELOP' - } -} else { - // Tests failed - need debugging - return 'DEBUG' -} -``` - -## Test Output Parsers - -### Jest/Vitest Parser - -```javascript -function parseJestOutput(stdout) { - const summaryMatch = stdout.match(/Tests:\s+(\d+)\s+passed.*?(\d+)\s+failed.*?(\d+)\s+total/) - // ... implementation -} -``` - -### Pytest Parser - -```javascript -function parsePytestOutput(stdout) { - const summaryMatch = stdout.match(/(\d+)\s+passed.*?(\d+)\s+failed/) - // ... implementation -} -``` - -## Error Handling - -| Error Type | Recovery | -|------------|----------| -| Tests don't run | Check test script config, report error | -| All tests fail | Suggest DEBUG action | -| Coverage tool missing | Skip coverage, run tests only | -| Timeout | Increase timeout or split tests | - -## Next Actions - -- Validation passed, no pending: `COMPLETE` -- Validation passed, has pending: `DEVELOP` -- Validation failed: `DEBUG` diff --git a/.codex/skills/ccw-loop/phases/00-prep-checklist.md b/.codex/skills/ccw-loop/phases/00-prep-checklist.md deleted file mode 100644 index 10ce1cf8..00000000 --- a/.codex/skills/ccw-loop/phases/00-prep-checklist.md +++ /dev/null @@ -1,116 +0,0 @@ -# Phase 0: Prep Package Schema & Integration - -Schema reference for `prep-package.json` consumed by ccw-loop Phase 1. Generated by interactive prompt `/prompts:prep-loop`. - -## prep-package.json Schema - -```json -{ - "version": "1.0.0", - "generated_at": "ISO8601 (UTC+8)", - "prep_status": "ready | cancelled | needs_refinement", - "target_skill": "ccw-loop", - - "environment": { - "project_root": "absolute path", - "tech_stack": "string", - "test_framework": "string" - }, - - "source": { - "tool": "collaborative-plan-with-file | analyze-with-file | brainstorm-to-cycle | manual", - "session_id": "string", - "task_dir": "absolute path to source .task/ directory", - "task_count": "number", - "tasks_with_convergence": "number" - }, - - "tasks": { - "total": "number", - "by_priority": { "high": 0, "medium": 0, "low": 0 }, - "by_type": { "feature": 0, "fix": 0, "refactor": 0, "enhancement": 0, "testing": 0 } - }, - - "auto_loop": { - "enabled": true, - "no_confirmation": true, - "max_iterations": 10, - "timeout_per_action_ms": 600000 - } -} -``` - -## .task/*.json Schema (task-schema.json) - -One task per file in `.task/` directory, each following `task-schema.json` with ccw-loop extended fields: - -```json -{ - "id": "task-001", - "description": "Title: detailed description", - "tool": "gemini", - "mode": "write", - "status": "pending", - "priority": 1, - "files_changed": ["path/to/file.ts"], - "created_at": "ISO8601", - "completed_at": null, - "_source": { "tool": "collaborative-plan-with-file", "session_id": "...", "original_id": "TASK-001" }, - "_convergence": { "criteria": ["..."], "verification": "...", "definition_of_done": "..." }, - "_type": "feature", - "_effort": "medium", - "_depends_on": [] -} -``` - -## Validation Rules - -| # | Check | Condition | On Failure | -|---|-------|-----------|------------| -| 1 | prep_status | `=== "ready"` | Skip prep, use default INIT | -| 2 | target_skill | `=== "ccw-loop"` | Skip prep, use default INIT | -| 3 | project_root | Matches current `projectRoot` | Skip prep, warn mismatch | -| 4 | freshness | `generated_at` within 24h | Skip prep, warn stale | -| 5 | tasks dir | `.task/` directory exists with *.json files | Skip prep, use default INIT | -| 6 | tasks content | At least 1 valid task JSON in `.task/` | Skip prep, use default INIT | - -## Integration Points - -### Phase 1: Session Initialization - -```javascript -// Load prep-package.json (generated by /prompts:prep-loop) -let prepPackage = null -const prepPath = `${projectRoot}/.workflow/.loop/prep-package.json` - -if (fs.existsSync(prepPath)) { - const raw = JSON.parse(Read(prepPath)) - const checks = validateLoopPrepPackage(raw, projectRoot) - - if (checks.valid) { - prepPackage = raw - // Load pre-built tasks from .task/*.json - const taskDir = `${projectRoot}/.workflow/.loop/.task` - const prepTasks = loadPrepTasks(taskDir) - // → Inject into state.skill_state.develop.tasks - // → Set max_iterations from auto_loop config - } else { - console.warn(`⚠ Prep package failed validation, using default INIT`) - prepPackage = null - } -} -``` - -### INIT Action (action-init.md) - -When prep tasks are loaded: -- **Skip** Step 3 (Analyze Task and Generate Tasks) — tasks already provided -- **Use** prep tasks directly in Step 5 (Update State) -- **Preserve** `_convergence` fields for VALIDATE action reference - -### VALIDATE Action - -When `_convergence` exists on a task: -- Use `convergence.verification` as validation command/steps -- Use `convergence.criteria` as pass/fail conditions -- Fall back to default test validation if `_convergence` is null diff --git a/.codex/skills/ccw-loop/phases/01-session-init.md b/.codex/skills/ccw-loop/phases/01-session-init.md deleted file mode 100644 index 64c2f508..00000000 --- a/.codex/skills/ccw-loop/phases/01-session-init.md +++ /dev/null @@ -1,300 +0,0 @@ -# Phase 1: Session Initialization - -Create or resume a development loop, initialize state file and directory structure. - -## Objective - -- Parse user arguments (TASK, --loop-id, --auto) -- Create new loop with unique ID OR resume existing loop -- Initialize directory structure for progress files -- Create master state file -- Output: loopId, state, progressDir - -## Execution - -### Step 0: Determine Project Root - -```javascript -// Step 0: Determine Project Root -const projectRoot = Bash('git rev-parse --show-toplevel 2>/dev/null || pwd').trim() -``` - -### Step 1.1: Parse Arguments & Load Prep Package - -```javascript -const { loopId: existingLoopId, task, mode = 'interactive' } = options - -// Validate mutual exclusivity -if (!existingLoopId && !task) { - console.error('Either --loop-id or task description is required') - return { status: 'error', message: 'Missing loopId or task' } -} - -// Determine mode -const executionMode = options['--auto'] ? 'auto' : 'interactive' - -// ── Prep Package: Detect → Validate → Consume ── -let prepPackage = null -let prepTasks = null -const prepPath = `${projectRoot}/.workflow/.loop/prep-package.json` - -if (fs.existsSync(prepPath)) { - const raw = JSON.parse(Read(prepPath)) - const checks = validateLoopPrepPackage(raw, projectRoot) - - if (checks.valid) { - prepPackage = raw - - // Load pre-built tasks - const taskDir = `${projectRoot}/.workflow/.loop/.task` - prepTasks = loadPrepTasks(taskDir) - - if (prepTasks && prepTasks.length > 0) { - console.log(`✓ Prep package loaded: ${prepTasks.length} tasks from ${prepPackage.source.tool}`) - console.log(` Checks passed: ${checks.passed.join(', ')}`) - } else { - console.warn(`Warning: Prep tasks directory empty or invalid, falling back to default INIT`) - prepPackage = null - prepTasks = null - } - } else { - console.warn(`⚠ Prep package found but failed validation:`) - checks.failures.forEach(f => console.warn(` ✗ ${f}`)) - console.warn(` → Falling back to default behavior (prep-package ignored)`) - prepPackage = null - } -} - -/** - * Validate prep-package.json integrity before consumption. - * Returns { valid: bool, passed: string[], failures: string[] } - */ -function validateLoopPrepPackage(prep, projectRoot) { - const passed = [] - const failures = [] - - // Check 1: prep_status must be "ready" - if (prep.prep_status === 'ready') { - passed.push('status=ready') - } else { - failures.push(`prep_status is "${prep.prep_status}", expected "ready"`) - } - - // Check 2: target_skill must match - if (prep.target_skill === 'ccw-loop') { - passed.push('target_skill match') - } else { - failures.push(`target_skill is "${prep.target_skill}", expected "ccw-loop"`) - } - - // Check 3: project_root must match current project - if (prep.environment?.project_root === projectRoot) { - passed.push('project_root match') - } else { - failures.push(`project_root mismatch: prep="${prep.environment?.project_root}", current="${projectRoot}"`) - } - - // Check 4: generated_at must be within 24 hours - const generatedAt = new Date(prep.generated_at) - const hoursSince = (Date.now() - generatedAt.getTime()) / (1000 * 60 * 60) - if (hoursSince <= 24) { - passed.push(`age=${Math.round(hoursSince)}h`) - } else { - failures.push(`prep-package is ${Math.round(hoursSince)}h old (max 24h), may be stale`) - } - - // Check 5: .task/ directory must exist with task files - const taskDir = `${projectRoot}/.workflow/.loop/.task` - const taskFiles = Glob(`${taskDir}/*.json`) - if (fs.existsSync(taskDir) && taskFiles.length > 0) { - passed.push(`.task/ exists (${taskFiles.length} files)`) - } else { - failures.push('.task/ directory not found or empty') - } - - // Check 6: task count > 0 - if ((prep.tasks?.total || 0) > 0) { - passed.push(`tasks=${prep.tasks.total}`) - } else { - failures.push('task count is 0') - } - - return { - valid: failures.length === 0, - passed, - failures - } -} - -/** - * Load pre-built tasks from .task/*.json directory. - * Returns array of task objects or null on failure. - */ -function loadPrepTasks(taskDir) { - if (!fs.existsSync(taskDir)) return null - - const taskFiles = Glob(`${taskDir}/*.json`).sort() - const tasks = [] - - for (const filePath of taskFiles) { - try { - const content = Read(filePath) - const task = JSON.parse(content) - if (task.id && task.description) { - tasks.push(task) - } - } catch (e) { - console.warn(`Warning: Skipping invalid task file ${filePath}: ${e.message}`) - } - } - - return tasks.length > 0 ? tasks : null -} -``` - -### Step 1.2: Utility Functions - -```javascript -const getUtc8ISOString = () => new Date(Date.now() + 8 * 60 * 60 * 1000).toISOString() - -function readLoopState(loopId) { - const stateFile = `${projectRoot}/.workflow/.loop/${loopId}.json` - if (!fs.existsSync(stateFile)) { - return null - } - return JSON.parse(Read(stateFile)) -} -``` - -### Step 1.3: New Loop Creation - -When `TASK` is provided (no `--loop-id`): - -```javascript -// Generate unique loop ID -const timestamp = getUtc8ISOString().replace(/[-:]/g, '').split('.')[0] -const random = Math.random().toString(36).substring(2, 10) -const loopId = `loop-v2-${timestamp}-${random}` - -console.log(`Creating new loop: ${loopId}`) -``` - -#### Create Directory Structure - -```bash -mkdir -p ${projectRoot}/.workflow/.loop/${loopId}.progress -``` - -#### Initialize State File - -```javascript -function createLoopState(loopId, taskDescription) { - const stateFile = `${projectRoot}/.workflow/.loop/${loopId}.json` - const now = getUtc8ISOString() - - const state = { - // API compatible fields - loop_id: loopId, - title: taskDescription.substring(0, 100), - description: taskDescription, - max_iterations: prepPackage?.auto_loop?.max_iterations || 10, - status: 'running', - current_iteration: 0, - created_at: now, - updated_at: now, - - // Skill extension fields - // When prep tasks available, pre-populate skill_state instead of null - skill_state: prepTasks ? { - current_action: 'init', - last_action: null, - completed_actions: [], - mode: executionMode, - - develop: { - total: prepTasks.length, - completed: 0, - current_task: null, - tasks: prepTasks, - last_progress_at: null - }, - - debug: { - active_bug: null, - hypotheses_count: 0, - hypotheses: [], - confirmed_hypothesis: null, - iteration: 0, - last_analysis_at: null - }, - - validate: { - pass_rate: 0, - coverage: 0, - test_results: [], - passed: false, - failed_tests: [], - last_run_at: null - }, - - errors: [] - } : null, - - // Prep package metadata (for traceability) - prep_source: prepPackage?.source || null - } - - Write(stateFile, JSON.stringify(state, null, 2)) - return state -} -``` - -### Step 1.4: Resume Existing Loop - -When `--loop-id` is provided: - -```javascript -const loopId = existingLoopId -const state = readLoopState(loopId) - -if (!state) { - console.error(`Loop not found: ${loopId}`) - return { status: 'error', message: 'Loop not found' } -} - -console.log(`Resuming loop: ${loopId}`) -console.log(`Status: ${state.status}`) -``` - -### Step 1.5: Control Signal Check - -Before proceeding, verify loop status allows continuation: - -```javascript -function checkControlSignals(loopId) { - const state = readLoopState(loopId) - - switch (state?.status) { - case 'paused': - return { continue: false, action: 'pause_exit' } - case 'failed': - return { continue: false, action: 'stop_exit' } - case 'running': - return { continue: true, action: 'continue' } - default: - return { continue: false, action: 'stop_exit' } - } -} -``` - -## Output - -- **Variable**: `loopId` - Unique loop identifier -- **Variable**: `state` - Initialized or resumed loop state object -- **Variable**: `progressDir` - `${projectRoot}/.workflow/.loop/${loopId}.progress` -- **Variable**: `mode` - `'interactive'` or `'auto'` -- **TodoWrite**: Mark Phase 1 completed, Phase 2 in_progress - -## Next Phase - -Return to orchestrator, then auto-continue to [Phase 2: Orchestration Loop](02-orchestration-loop.md). diff --git a/.codex/skills/ccw-loop/phases/02-orchestration-loop.md b/.codex/skills/ccw-loop/phases/02-orchestration-loop.md deleted file mode 100644 index b0cd125c..00000000 --- a/.codex/skills/ccw-loop/phases/02-orchestration-loop.md +++ /dev/null @@ -1,309 +0,0 @@ -# Phase 2: Orchestration Loop - -Spawn single executor agent and run main orchestration loop until completion, pause, or max iterations. - -## Objective - -- Spawn single executor agent with loop context -- Run main while loop: wait → parse → dispatch → send_input -- Handle terminal conditions (COMPLETED, PAUSED, STOPPED) -- Handle interactive mode (WAITING_INPUT → user choice → send_input) -- Handle auto mode (next action → send_input) -- Update iteration count per cycle -- Close agent on exit - -## Execution - -### Step 2.1: Spawn Executor Agent - -```javascript -const agent = spawn_agent({ - message: ` -## TASK ASSIGNMENT - -### MANDATORY FIRST STEPS (Agent Execute) -1. **Read role definition**: ~/.codex/agents/ccw-loop-executor.md (MUST read first) -2. Read: ${projectRoot}/.workflow/project-tech.json (if exists) -3. Read: ${projectRoot}/.workflow/project-guidelines.json (if exists) - ---- - -## LOOP CONTEXT - -- **Loop ID**: ${loopId} -- **State File**: ${projectRoot}/.workflow/.loop/${loopId}.json -- **Progress Dir**: ${progressDir} -- **Mode**: ${mode} - -## CURRENT STATE - -${JSON.stringify(state, null, 2)} - -## TASK DESCRIPTION - -${state.description || task} - -## EXECUTION INSTRUCTIONS - -You are executing CCW Loop orchestrator. Your job: - -1. **Check Control Signals** - - Read ${projectRoot}/.workflow/.loop/${loopId}.json - - If status === 'paused' -> Output "PAUSED" and stop - - If status === 'failed' -> Output "STOPPED" and stop - - If status === 'running' -> Continue - -2. **Select Next Action** - Based on skill_state: - - If not initialized -> Execute INIT - - If mode === 'interactive' -> Output MENU and wait for input - - If mode === 'auto' -> Auto-select based on state - -3. **Execute Action** - - Follow action instructions from ~/.codex/skills/ccw-loop/actions/ - - Update progress files in ${progressDir}/ - - Update state in ${projectRoot}/.workflow/.loop/${loopId}.json - -4. **Output Format** - \`\`\` - ACTION_RESULT: - - action: {action_name} - - status: success | failed | needs_input - - message: {user message} - - state_updates: {JSON of skill_state updates} - - NEXT_ACTION_NEEDED: {action_name} | WAITING_INPUT | COMPLETED | PAUSED - \`\`\` - -## FIRST ACTION - -${!state.skill_state ? 'Execute: INIT' : mode === 'auto' ? 'Auto-select next action' : 'Show MENU'} -` -}) -``` - -### Step 2.2: Main Orchestration Loop - -```javascript -let iteration = state.current_iteration || 0 -const maxIterations = state.max_iterations || 10 -let continueLoop = true - -while (continueLoop && iteration < maxIterations) { - iteration++ - - // Wait for agent output - const result = wait({ ids: [agent], timeout_ms: 600000 }) - - // Handle timeout - if (result.timed_out) { - console.log('Agent timeout, requesting convergence...') - send_input({ - id: agent, - message: ` -## TIMEOUT NOTIFICATION - -Execution timeout reached. Please: -1. Output current progress -2. Save any pending state updates -3. Return ACTION_RESULT with current status -` - }) - continue - } - - const output = result.status[agent].completed - - // Parse action result - const actionResult = parseActionResult(output) - - console.log(`\n[Iteration ${iteration}] Action: ${actionResult.action}, Status: ${actionResult.status}`) - - // Update iteration in state - state = readLoopState(loopId) - state.current_iteration = iteration - state.updated_at = getUtc8ISOString() - Write(`${projectRoot}/.workflow/.loop/${loopId}.json`, JSON.stringify(state, null, 2)) - - // Handle different outcomes - switch (actionResult.next_action) { - case 'COMPLETED': - console.log('Loop completed successfully') - continueLoop = false - break - - case 'PAUSED': - console.log('Loop paused by API, exiting gracefully') - continueLoop = false - break - - case 'STOPPED': - console.log('Loop stopped by API') - continueLoop = false - break - - case 'WAITING_INPUT': - // Interactive mode: display menu, get user choice - if (mode === 'interactive') { - const userChoice = await displayMenuAndGetChoice(actionResult) - - send_input({ - id: agent, - message: ` -## USER INPUT RECEIVED - -Action selected: ${userChoice.action} -${userChoice.data ? `Additional data: ${JSON.stringify(userChoice.data)}` : ''} - -## EXECUTE SELECTED ACTION - -Read action instructions and execute: ${userChoice.action} -Update state and progress files accordingly. -Output ACTION_RESULT when complete. -` - }) - } - break - - default: - // Continue with next action - if (actionResult.next_action && actionResult.next_action !== 'NONE') { - send_input({ - id: agent, - message: ` -## CONTINUE EXECUTION - -Previous action completed: ${actionResult.action} -Result: ${actionResult.status} -${actionResult.message ? `Message: ${actionResult.message}` : ''} - -## EXECUTE NEXT ACTION - -Continue with: ${actionResult.next_action} -Read action instructions and execute. -Output ACTION_RESULT when complete. -` - }) - } else { - if (actionResult.status === 'failed') { - console.log(`Action failed: ${actionResult.message}`) - } - continueLoop = false - } - } -} -``` - -### Step 2.3: Iteration Limit Check - -```javascript -if (iteration >= maxIterations) { - console.log(`\nReached maximum iterations (${maxIterations})`) - console.log('Consider breaking down the task or taking a break.') -} -``` - -### Step 2.4: Cleanup - -```javascript -close_agent({ id: agent }) - -console.log('\n=== CCW Loop Orchestrator Finished ===') - -const finalState = readLoopState(loopId) -return { - status: finalState.status, - loop_id: loopId, - iterations: iteration, - final_state: finalState -} -``` - -## Helper Functions - -### parseActionResult - -```javascript -function parseActionResult(output) { - const result = { - action: 'unknown', - status: 'unknown', - message: '', - state_updates: {}, - next_action: 'NONE' - } - - // Parse ACTION_RESULT block - const actionMatch = output.match(/ACTION_RESULT:\s*([\s\S]*?)(?:FILES_UPDATED:|NEXT_ACTION_NEEDED:|$)/) - if (actionMatch) { - const lines = actionMatch[1].split('\n') - for (const line of lines) { - const match = line.match(/^-\s*(\w+):\s*(.+)$/) - if (match) { - const [, key, value] = match - if (key === 'state_updates') { - try { - result.state_updates = JSON.parse(value) - } catch (e) { - // Try parsing multi-line JSON - } - } else { - result[key] = value.trim() - } - } - } - } - - // Parse NEXT_ACTION_NEEDED - const nextMatch = output.match(/NEXT_ACTION_NEEDED:\s*(\S+)/) - if (nextMatch) { - result.next_action = nextMatch[1] - } - - return result -} -``` - -### displayMenuAndGetChoice - -```javascript -async function displayMenuAndGetChoice(actionResult) { - const menuMatch = actionResult.message.match(/MENU_OPTIONS:\s*([\s\S]*?)(?:WAITING_INPUT:|$)/) - - if (menuMatch) { - console.log('\n' + menuMatch[1]) - } - - const response = await ASK_USER([{ - id: "Action", type: "select", - prompt: "Select next action:", - options: [ - { label: "develop", description: "Continue development" }, - { label: "debug", description: "Start debugging" }, - { label: "validate", description: "Run validation" }, - { label: "complete", description: "Complete loop" }, - { label: "exit", description: "Exit and save" } - ] - }]) // BLOCKS (wait for user response) - - return { action: response["Action"] } -} -``` - -## Termination Conditions - -1. **API Paused**: `state.status === 'paused'` (Skill exits, wait for resume) -2. **API Stopped**: `state.status === 'failed'` (Skill terminates) -3. **Task Complete**: `NEXT_ACTION_NEEDED === 'COMPLETED'` -4. **Iteration Limit**: `current_iteration >= max_iterations` -5. **User Exit**: User selects 'exit' in interactive mode - -## Output - -- **Variable**: `finalState` - Final loop state after all iterations -- **Return**: `{ status, loop_id, iterations, final_state }` -- **TodoWrite**: Mark Phase 2 completed - -## Next Phase - -None. Phase 2 is the terminal phase of the orchestrator. diff --git a/ccw/frontend/src/components/terminal-dashboard/DashboardToolbar.tsx b/ccw/frontend/src/components/terminal-dashboard/DashboardToolbar.tsx index 147da134..7b1393ff 100644 --- a/ccw/frontend/src/components/terminal-dashboard/DashboardToolbar.tsx +++ b/ccw/frontend/src/components/terminal-dashboard/DashboardToolbar.tsx @@ -11,6 +11,7 @@ import { AlertCircle, ListChecks, Info, + FolderOpen, LayoutGrid, Columns2, Rows2, @@ -47,7 +48,7 @@ import { CliConfigModal, type CliSessionConfig } from './CliConfigModal'; // ========== Types ========== -export type PanelId = 'issues' | 'queue' | 'inspector'; +export type PanelId = 'issues' | 'queue' | 'inspector' | 'files'; interface DashboardToolbarProps { activePanel: PanelId | null; @@ -292,6 +293,12 @@ export function DashboardToolbar({ activePanel, onTogglePanel }: DashboardToolba onClick={() => onTogglePanel('inspector')} dot={hasChain} /> + onTogglePanel('files')} + /> {/* Separator */}
diff --git a/ccw/frontend/src/components/terminal-dashboard/FloatingFileBrowser.tsx b/ccw/frontend/src/components/terminal-dashboard/FloatingFileBrowser.tsx index 58158ff1..e6c4512b 100644 --- a/ccw/frontend/src/components/terminal-dashboard/FloatingFileBrowser.tsx +++ b/ccw/frontend/src/components/terminal-dashboard/FloatingFileBrowser.tsx @@ -20,6 +20,7 @@ export interface FloatingFileBrowserProps { rootPath: string; onInsertPath?: (path: string) => void; initialSelectedPath?: string | null; + width?: number | string; } export function FloatingFileBrowser({ @@ -28,6 +29,7 @@ export function FloatingFileBrowser({ rootPath, onInsertPath, initialSelectedPath = null, + width = 400, }: FloatingFileBrowserProps) { const { formatMessage } = useIntl(); @@ -89,7 +91,7 @@ export function FloatingFileBrowser({ onClose={onClose} title={formatMessage({ id: 'terminalDashboard.fileBrowser.title' })} side="right" - width={400} + width={width} >
{/* Toolbar */} @@ -148,7 +150,7 @@ export function FloatingFileBrowser({ {/* Body */}
{/* Tree */} -
+
{isLoading ? (
diff --git a/ccw/frontend/src/components/terminal-dashboard/FloatingPanel.tsx b/ccw/frontend/src/components/terminal-dashboard/FloatingPanel.tsx index 56b248a1..9b4d5516 100644 --- a/ccw/frontend/src/components/terminal-dashboard/FloatingPanel.tsx +++ b/ccw/frontend/src/components/terminal-dashboard/FloatingPanel.tsx @@ -15,7 +15,7 @@ interface FloatingPanelProps { onClose: () => void; title: string; side?: 'left' | 'right'; - width?: number; + width?: number | string; children: React.ReactNode; } @@ -77,7 +77,7 @@ export function FloatingPanel({ style={{ top: '40px', // Below toolbar height: 'calc(100vh - 40px)', // Full height below toolbar - width: `${width}px`, + width: typeof width === 'number' ? `${width}px` : width, }} > {/* Panel header */} diff --git a/ccw/frontend/src/components/terminal-dashboard/IssuePanel.tsx b/ccw/frontend/src/components/terminal-dashboard/IssuePanel.tsx index 9061975c..5d6fafda 100644 --- a/ccw/frontend/src/components/terminal-dashboard/IssuePanel.tsx +++ b/ccw/frontend/src/components/terminal-dashboard/IssuePanel.tsx @@ -82,8 +82,9 @@ function IssueItem({ ); return ( - +
); } diff --git a/ccw/frontend/src/locales/en/terminal-dashboard.json b/ccw/frontend/src/locales/en/terminal-dashboard.json index 7748d37e..c0ed4098 100644 --- a/ccw/frontend/src/locales/en/terminal-dashboard.json +++ b/ccw/frontend/src/locales/en/terminal-dashboard.json @@ -73,6 +73,7 @@ "issues": "Issues", "queue": "Queue", "inspector": "Inspector", + "files": "Files", "layoutSingle": "Single", "layoutSplitH": "Split Horizontal", "layoutSplitV": "Split Vertical", diff --git a/ccw/frontend/src/locales/zh/terminal-dashboard.json b/ccw/frontend/src/locales/zh/terminal-dashboard.json index c7fb2bdf..a90645ec 100644 --- a/ccw/frontend/src/locales/zh/terminal-dashboard.json +++ b/ccw/frontend/src/locales/zh/terminal-dashboard.json @@ -73,6 +73,7 @@ "issues": "问题", "queue": "队列", "inspector": "检查器", + "files": "文件", "layoutSingle": "单窗格", "layoutSplitH": "左右分割", "layoutSplitV": "上下分割", diff --git a/ccw/frontend/src/pages/TerminalDashboardPage.tsx b/ccw/frontend/src/pages/TerminalDashboardPage.tsx index 8d06b63e..1529835e 100644 --- a/ccw/frontend/src/pages/TerminalDashboardPage.tsx +++ b/ccw/frontend/src/pages/TerminalDashboardPage.tsx @@ -18,6 +18,10 @@ import { AgentList } from '@/components/terminal-dashboard/AgentList'; import { IssuePanel } from '@/components/terminal-dashboard/IssuePanel'; import { QueuePanel } from '@/components/terminal-dashboard/QueuePanel'; import { InspectorContent } from '@/components/terminal-dashboard/BottomInspector'; +import { FloatingFileBrowser } from '@/components/terminal-dashboard/FloatingFileBrowser'; +import { useWorkflowStore, selectProjectPath } from '@/stores/workflowStore'; +import { useTerminalGridStore, selectTerminalGridFocusedPaneId } from '@/stores/terminalGridStore'; +import { sendCliSessionText } from '@/lib/api'; // ========== Main Page Component ========== @@ -25,6 +29,10 @@ export function TerminalDashboardPage() { const { formatMessage } = useIntl(); const [activePanel, setActivePanel] = useState(null); + const projectPath = useWorkflowStore(selectProjectPath); + const focusedPaneId = useTerminalGridStore(selectTerminalGridFocusedPaneId); + const panes = useTerminalGridStore((s) => s.panes); + const togglePanel = useCallback((panelId: PanelId) => { setActivePanel((prev) => (prev === panelId ? null : panelId)); }, []); @@ -33,6 +41,20 @@ export function TerminalDashboardPage() { setActivePanel(null); }, []); + const handleInsertPath = useCallback( + (path: string) => { + if (!focusedPaneId) return; + const sessionId = panes[focusedPaneId]?.sessionId; + if (!sessionId) return; + sendCliSessionText( + sessionId, + { text: path, appendNewline: false }, + projectPath ?? undefined + ).catch((err) => console.error('[TerminalDashboard] insert path failed:', err)); + }, + [focusedPaneId, panes, projectPath] + ); + return (
@@ -90,6 +112,15 @@ export function TerminalDashboardPage() { > + + {/* File browser (half screen, right side) */} +
); diff --git a/ccw/frontend/src/stores/configStore.ts b/ccw/frontend/src/stores/configStore.ts index 973df156..c3e75358 100644 --- a/ccw/frontend/src/stores/configStore.ts +++ b/ccw/frontend/src/stores/configStore.ts @@ -235,6 +235,10 @@ export const useConfigStore = create()( secondaryModel: t.secondaryModel || '', tags: t.tags || [], type: t.type || 'builtin', + // Load additional fields from backend (fixes cross-browser config sync) + envFile: t.envFile, + settingsFile: t.settingsFile, + availableModels: t.availableModels, }; } if (Object.keys(cliTools).length > 0) { diff --git a/codex-lens/tests/parsers/test_comparison.py b/codex-lens/tests/parsers/test_comparison.py index 06fa2b47..9c9840a0 100644 --- a/codex-lens/tests/parsers/test_comparison.py +++ b/codex-lens/tests/parsers/test_comparison.py @@ -442,9 +442,8 @@ import sys def test_imports_inside_function(self) -> None: """Test simple import inside a function scope is recorded. - Note: tree-sitter parser requires a scope to record imports. - Module-level imports without any function/class are not recorded - because scope_stack is empty at module level. + Note: module-level imports are recorded under a synthetic "" scope. + This test ensures imports inside a function scope are also recorded. """ code = """ def my_function():