Files
Claude-Code-Workflow/.claude/commands/issue/plan.md
catlog22 4da06864f8 feat: Enhance issue and solution management with new UI components and functionality
- Added internationalization support for new issue and solution-related strings in i18n.js.
- Implemented a solution detail modal in issue-manager.js to display solution information and bind/unbind actions.
- Enhanced the skill loading function to combine project and user skills in hook-manager.js.
- Improved queue rendering logic to handle empty states and display queue statistics in issue-manager.js.
- Introduced command modals for queue operations, allowing users to generate execution queues via CLI commands.
- Added functionality to auto-generate issue IDs and regenerate them in the create issue modal.
- Implemented detailed rendering of solution tasks, including acceptance criteria and modification points.
2025-12-27 11:27:45 +08:00

12 KiB

name, description, argument-hint, allowed-tools
name description argument-hint allowed-tools
plan Batch plan issue resolution using issue-plan-agent (explore + plan closed-loop) <issue-id>[,<issue-id>,...] [--batch-size 3] TodoWrite(*), Task(*), SlashCommand(*), AskUserQuestion(*), Bash(*), Read(*), Write(*)

Issue Plan Command (/issue:plan)

Overview

Unified planning command using issue-plan-agent that combines exploration and planning into a single closed-loop workflow. The agent handles ACE semantic search, solution generation, and task breakdown.

Core capabilities:

  • Closed-loop agent: issue-plan-agent combines explore + plan
  • Batch processing: 1 agent processes 1-3 issues
  • ACE semantic search integrated into planning
  • Solution with executable tasks and acceptance criteria
  • Automatic solution registration and binding

Storage Structure (Flat JSONL)

.workflow/issues/
├── issues.jsonl              # All issues (one per line)
├── queue.json                # Execution queue
└── solutions/
    ├── {issue-id}.jsonl      # Solutions for issue (one per line)
    └── ...

Usage

/issue:plan <issue-id>[,<issue-id>,...] [FLAGS]

# Examples
/issue:plan GH-123                    # Single issue
/issue:plan GH-123,GH-124,GH-125      # Batch (up to 3)
/issue:plan --all-pending             # All pending issues

# Flags
--batch-size <n>      Max issues per agent batch (default: 3)

Execution Process

Phase 1: Issue Loading
   ├─ Parse input (single, comma-separated, or --all-pending)
   ├─ Load issues from .workflow/issues/issues.jsonl
   ├─ Validate issues exist (create if needed)
   └─ Group into batches (max 3 per batch)

Phase 2: Unified Explore + Plan (issue-plan-agent)
   ├─ Launch issue-plan-agent per batch
   ├─ Agent performs:
   │   ├─ ACE semantic search for each issue
   │   ├─ Codebase exploration (files, patterns, dependencies)
   │   ├─ Solution generation with task breakdown
   │   └─ Conflict detection across issues
   └─ Output: solution JSON per issue

Phase 3: Solution Registration & Binding
   ├─ Append solutions to solutions/{issue-id}.jsonl
   ├─ Single solution per issue → auto-bind
   ├─ Multiple candidates → AskUserQuestion to select
   └─ Update issues.jsonl with bound_solution_id

Phase 4: Summary
   ├─ Display bound solutions
   ├─ Show task counts per issue
   └─ Display next steps (/issue:queue)

Implementation

Phase 1: Issue Loading

// Parse input
const issueIds = userInput.includes(',')
  ? userInput.split(',').map(s => s.trim())
  : [userInput.trim()];

// Read issues.jsonl
const issuesPath = '.workflow/issues/issues.jsonl';
const allIssues = Bash(`cat "${issuesPath}" 2>/dev/null || echo ''`)
  .split('\n')
  .filter(line => line.trim())
  .map(line => JSON.parse(line));

// Load and validate issues
const issues = [];
for (const id of issueIds) {
  let issue = allIssues.find(i => i.id === id);

  if (!issue) {
    console.log(`Issue ${id} not found. Creating...`);
    issue = {
      id,
      title: `Issue ${id}`,
      status: 'registered',
      priority: 3,
      context: '',
      created_at: new Date().toISOString(),
      updated_at: new Date().toISOString()
    };
    // Append to issues.jsonl
    Bash(`echo '${JSON.stringify(issue)}' >> "${issuesPath}"`);
  }

  issues.push(issue);
}

// Group into batches
const batchSize = flags.batchSize || 3;
const batches = [];
for (let i = 0; i < issues.length; i += batchSize) {
  batches.push(issues.slice(i, i + batchSize));
}

TodoWrite({
  todos: batches.flatMap((batch, i) => [
    { content: `Plan batch ${i+1}`, status: 'pending', activeForm: `Planning batch ${i+1}` }
  ])
});

Phase 2: Unified Explore + Plan (issue-plan-agent)

for (const [batchIndex, batch] of batches.entries()) {
  updateTodo(`Plan batch ${batchIndex + 1}`, 'in_progress');

  // Build issue prompt for agent with lifecycle requirements
  const issuePrompt = `
## Issues to Plan (Closed-Loop Tasks Required)

${batch.map((issue, i) => `
### Issue ${i + 1}: ${issue.id}
**Title**: ${issue.title}
**Context**: ${issue.context || 'No context provided'}
**Affected Components**: ${issue.affected_components?.join(', ') || 'Not specified'}

**Lifecycle Requirements**:
- Test Strategy: ${issue.lifecycle_requirements?.test_strategy || 'auto'}
- Regression Scope: ${issue.lifecycle_requirements?.regression_scope || 'affected'}
- Commit Strategy: ${issue.lifecycle_requirements?.commit_strategy || 'per-task'}
`).join('\n')}

## Project Root
${process.cwd()}

## Requirements - CLOSED-LOOP TASKS

Each task MUST include ALL lifecycle phases:

### 1. Implementation
- implementation: string[] (2-7 concrete steps)
- modification_points: { file, target, change }[]

### 2. Test
- test.unit: string[] (unit test requirements)
- test.integration: string[] (integration test requirements if needed)
- test.commands: string[] (actual test commands to run)
- test.coverage_target: number (minimum coverage %)

### 3. Regression
- regression: string[] (commands to run for regression check)
- Based on issue's regression_scope setting

### 4. Acceptance
- acceptance.criteria: string[] (testable acceptance criteria)
- acceptance.verification: string[] (how to verify each criterion)
- acceptance.manual_checks: string[] (manual checks if needed)

### 5. Commit
- commit.type: feat|fix|refactor|test|docs|chore
- commit.scope: string (module name)
- commit.message_template: string (full commit message)
- commit.breaking: boolean

## Additional Requirements
1. Use ACE semantic search (mcp__ace-tool__search_context) for exploration
2. Detect file conflicts if multiple issues
3. Generate executable test commands based on project's test framework
4. Infer commit scope from affected files
`;

  // Launch issue-plan-agent (combines explore + plan)
  const result = Task(
    subagent_type="issue-plan-agent",
    run_in_background=false,
    description=`Explore & plan ${batch.length} issues`,
    prompt=issuePrompt
  );

  // Parse agent output
  const agentOutput = JSON.parse(result);

  // Register solutions for each issue (append to solutions/{issue-id}.jsonl)
  for (const item of agentOutput.solutions) {
    const solutionPath = `.workflow/issues/solutions/${item.issue_id}.jsonl`;

    // Ensure solutions directory exists
    Bash(`mkdir -p .workflow/issues/solutions`);

    // Append solution as new line
    Bash(`echo '${JSON.stringify(item.solution)}' >> "${solutionPath}"`);
  }

  // Handle conflicts if any
  if (agentOutput.conflicts?.length > 0) {
    console.log(`\n⚠ File conflicts detected:`);
    agentOutput.conflicts.forEach(c => {
      console.log(`  ${c.file}: ${c.issues.join(', ')} → suggested: ${c.suggested_order.join(' → ')}`);
    });
  }

  updateTodo(`Plan batch ${batchIndex + 1}`, 'completed');
}

Phase 3: Solution Binding

// Re-read issues.jsonl
let allIssuesUpdated = Bash(`cat "${issuesPath}"`)
  .split('\n')
  .filter(line => line.trim())
  .map(line => JSON.parse(line));

for (const issue of issues) {
  const solPath = `.workflow/issues/solutions/${issue.id}.jsonl`;
  const solutions = Bash(`cat "${solPath}" 2>/dev/null || echo ''`)
    .split('\n')
    .filter(line => line.trim())
    .map(line => JSON.parse(line));

  if (solutions.length === 0) {
    console.log(`⚠ No solutions for ${issue.id}`);
    continue;
  }

  let selectedSolId;

  if (solutions.length === 1) {
    // Auto-bind single solution
    selectedSolId = solutions[0].id;
    console.log(`✓ Auto-bound ${selectedSolId} to ${issue.id} (${solutions[0].tasks?.length || 0} tasks)`);
  } else {
    // Multiple solutions - ask user
    const answer = AskUserQuestion({
      questions: [{
        question: `Select solution for ${issue.id}:`,
        header: issue.id,
        multiSelect: false,
        options: solutions.map(s => ({
          label: `${s.id}: ${s.description || 'Solution'}`,
          description: `${s.tasks?.length || 0} tasks`
        }))
      }]
    });

    selectedSolId = extractSelectedSolutionId(answer);
    console.log(`✓ Bound ${selectedSolId} to ${issue.id}`);
  }

  // Update issue in allIssuesUpdated
  const issueIndex = allIssuesUpdated.findIndex(i => i.id === issue.id);
  if (issueIndex !== -1) {
    allIssuesUpdated[issueIndex].bound_solution_id = selectedSolId;
    allIssuesUpdated[issueIndex].status = 'planned';
    allIssuesUpdated[issueIndex].planned_at = new Date().toISOString();
    allIssuesUpdated[issueIndex].updated_at = new Date().toISOString();
  }

  // Mark solution as bound in solutions file
  const updatedSolutions = solutions.map(s => ({
    ...s,
    is_bound: s.id === selectedSolId,
    bound_at: s.id === selectedSolId ? new Date().toISOString() : s.bound_at
  }));
  Write(solPath, updatedSolutions.map(s => JSON.stringify(s)).join('\n'));
}

// Write updated issues.jsonl
Write(issuesPath, allIssuesUpdated.map(i => JSON.stringify(i)).join('\n'));

Phase 4: Summary

console.log(`
## Planning Complete

**Issues Planned**: ${issues.length}

### Bound Solutions
${issues.map(i => {
  const issue = allIssuesUpdated.find(a => a.id === i.id);
  return issue?.bound_solution_id
    ? `✓ ${i.id}: ${issue.bound_solution_id}`
    : `○ ${i.id}: No solution bound`;
}).join('\n')}

### Next Steps
1. Review: \`ccw issue status <issue-id>\`
2. Form queue: \`/issue:queue\`
3. Execute: \`/issue:execute\`
`);

Solution Format (Closed-Loop Tasks)

Each solution line in solutions/{issue-id}.jsonl:

{
  "id": "SOL-20251226-001",
  "description": "Direct Implementation",
  "tasks": [
    {
      "id": "T1",
      "title": "Create auth middleware",
      "scope": "src/middleware/",
      "action": "Create",
      "description": "Create JWT validation middleware",
      "modification_points": [
        { "file": "src/middleware/auth.ts", "target": "new file", "change": "Create middleware" }
      ],

      "implementation": [
        "Create auth.ts file in src/middleware/",
        "Implement JWT token validation using jsonwebtoken",
        "Add error handling for invalid/expired tokens",
        "Export middleware function"
      ],

      "test": {
        "unit": [
          "Test valid token passes through",
          "Test invalid token returns 401",
          "Test expired token returns 401",
          "Test missing token returns 401"
        ],
        "commands": [
          "npm test -- --grep 'auth middleware'",
          "npm run test:coverage -- src/middleware/auth.ts"
        ],
        "coverage_target": 80
      },

      "regression": [
        "npm test -- --grep 'protected routes'",
        "npm run test:integration -- auth"
      ],

      "acceptance": {
        "criteria": [
          "Middleware validates JWT tokens successfully",
          "Returns 401 for invalid or missing tokens",
          "Passes decoded token to request context"
        ],
        "verification": [
          "curl -H 'Authorization: Bearer valid_token' /api/protected → 200",
          "curl /api/protected → 401",
          "curl -H 'Authorization: Bearer invalid' /api/protected → 401"
        ]
      },

      "commit": {
        "type": "feat",
        "scope": "auth",
        "message_template": "feat(auth): add JWT validation middleware\n\n- Implement token validation\n- Add error handling for invalid tokens\n- Export for route protection",
        "breaking": false
      },

      "depends_on": [],
      "estimated_minutes": 30,
      "executor": "codex"
    }
  ],
  "exploration_context": {
    "relevant_files": ["src/config/auth.ts"],
    "patterns": "Follow existing middleware pattern"
  },
  "is_bound": true,
  "created_at": "2025-12-26T10:00:00Z",
  "bound_at": "2025-12-26T10:05:00Z"
}

Error Handling

Error Resolution
Issue not found Auto-create in issues.jsonl
ACE search fails Agent falls back to ripgrep
No solutions generated Display error, suggest manual planning
User cancels selection Skip issue, continue with others
File conflicts Agent detects and suggests resolution order

Agent Integration

The command uses issue-plan-agent which:

  1. Performs ACE semantic search per issue
  2. Identifies modification points and patterns
  3. Generates task breakdown with dependencies
  4. Detects cross-issue file conflicts
  5. Outputs solution JSON for registration

See .claude/agents/issue-plan-agent.md for agent specification.

  • /issue:queue - Form execution queue from bound solutions
  • /issue:execute - Execute queue with codex
  • ccw issue list - List all issues
  • ccw issue status - View issue and solution details