Files
Claude-Code-Workflow/.claude/commands/issue/plan.md
catlog22 cdf4833977 feat(issue): implement JSONL task generation and management for issue resolution
- Added `/issue:plan` command to generate a structured task plan from GitHub issues or descriptions, including delivery and pause criteria, and a dependency graph.
- Introduced JSONL schema for task entries to enforce structure and validation.
- Developed comprehensive issue command with functionalities for initializing, listing, adding, updating, and exporting tasks.
- Implemented error handling and user feedback for various operations within the issue management workflow.
- Enhanced task management with priority levels, phase results, and executor preferences.
2025-12-26 17:21:32 +08:00

11 KiB

name, description, argument-hint, allowed-tools
name description argument-hint allowed-tools
plan Plan issue resolution with JSONL task generation, delivery/pause criteria, and dependency graph "issue description"|github-url|file.md TodoWrite(*), Task(*), SlashCommand(*), AskUserQuestion(*), Bash(*), Read(*), Write(*)

Issue Plan Command (/issue:plan)

Overview

Generate a JSONL-based task plan from a GitHub issue or description. Each task includes delivery criteria, pause criteria, and dependency relationships. The plan is designed for progressive execution with the /issue:execute command.

Core capabilities:

  • Parse issue from URL, text description, or markdown file
  • Analyze codebase context for accurate task breakdown
  • Generate JSONL task file with DAG (Directed Acyclic Graph) dependencies
  • Define clear delivery criteria (what marks a task complete)
  • Define pause criteria (conditions to halt execution)
  • Interactive confirmation before finalizing

Usage

/issue:plan [FLAGS] <INPUT>

# Input Formats
<issue-url>           GitHub issue URL (e.g., https://github.com/owner/repo/issues/123)
<description>         Text description of the issue
<file.md>             Markdown file with issue details

# Flags
-e, --explore         Force code exploration phase
--executor <type>     Default executor: agent|codex|gemini|auto (default: auto)

Execution Process

Phase 1: Input Parsing & Context
   ├─ Parse input (URL → fetch issue, text → use directly, file → read content)
   ├─ Extract: title, description, labels, acceptance criteria
   └─ Store as issueContext

Phase 2: Exploration (if needed)
   ├─ Complexity assessment (Low/Medium/High)
   ├─ Launch cli-explore-agent for codebase understanding
   └─ Identify: relevant files, patterns, integration points

Phase 3: Task Breakdown
   ├─ Agent generates JSONL task list
   ├─ Each task includes:
   │   ├─ delivery_criteria (completion checklist)
   │   ├─ pause_criteria (halt conditions)
   │   └─ depends_on (dependency graph)
   └─ Validate DAG (no circular dependencies)

Phase 4: User Confirmation
   ├─ Display task summary table
   ├─ Show dependency graph
   └─ AskUserQuestion: Approve / Refine / Cancel

Phase 5: Persistence
   ├─ Write tasks.jsonl to .workflow/issues/{issue-id}/
   ├─ Initialize state.json for status tracking
   └─ Return summary and next steps

Implementation

Phase 1: Input Parsing

// Helper: Get UTC+8 ISO string
const getUtc8ISOString = () => new Date(Date.now() + 8 * 60 * 60 * 1000).toISOString()

// Parse input type
function parseInput(input) {
  if (input.startsWith('https://github.com/')) {
    const match = input.match(/github\.com\/(.+?)\/(.+?)\/issues\/(\d+)/)
    if (match) {
      return { type: 'github', owner: match[1], repo: match[2], number: match[3] }
    }
  }
  if (input.endsWith('.md') && fileExists(input)) {
    return { type: 'file', path: input }
  }
  return { type: 'text', content: input }
}

// Generate issue ID
const inputType = parseInput(userInput)
let issueId, issueTitle, issueContent

if (inputType.type === 'github') {
  // Fetch via gh CLI
  const issueData = Bash(`gh issue view ${inputType.number} --repo ${inputType.owner}/${inputType.repo} --json title,body,labels`)
  const parsed = JSON.parse(issueData)
  issueId = `GH-${inputType.number}`
  issueTitle = parsed.title
  issueContent = parsed.body
} else if (inputType.type === 'file') {
  issueContent = Read(inputType.path)
  issueId = `FILE-${Date.now()}`
  issueTitle = extractTitle(issueContent) // First # heading
} else {
  issueContent = inputType.content
  issueId = `TEXT-${Date.now()}`
  issueTitle = issueContent.substring(0, 50)
}

// Create issue directory
const issueDir = `.workflow/issues/${issueId}`
Bash(`mkdir -p ${issueDir}`)

// Save issue context
Write(`${issueDir}/context.md`, `# ${issueTitle}\n\n${issueContent}`)

Phase 2: Exploration

// Complexity assessment
const complexity = analyzeComplexity(issueContent)
// Low: Single file change, isolated
// Medium: Multiple files, some dependencies
// High: Cross-module, architectural

const needsExploration = (
  flags.includes('--explore') ||
  complexity !== 'Low' ||
  issueContent.mentions_specific_files
)

if (needsExploration) {
  Task(
    subagent_type="cli-explore-agent",
    run_in_background=false,
    description="Explore codebase for issue context",
    prompt=`
## Task Objective
Analyze codebase to understand context for issue resolution.

## Issue Context
Title: ${issueTitle}
Content: ${issueContent}

## Required Analysis
1. Identify files that need modification
2. Find relevant patterns and conventions
3. Map dependencies and integration points
4. Identify potential risks or blockers

## Output
Write exploration results to: ${issueDir}/exploration.json
`
  )
}

Phase 3: Task Breakdown

// Load schema reference
const schema = Read('~/.claude/workflows/cli-templates/schemas/issue-task-jsonl-schema.json')

// Generate tasks via CLI
Task(
  subagent_type="cli-lite-planning-agent",
  run_in_background=false,
  description="Generate JSONL task breakdown",
  prompt=`
## Objective
Break down the issue into executable tasks in JSONL format.

## Issue Context
ID: ${issueId}
Title: ${issueTitle}
Content: ${issueContent}

## Exploration Results
${explorationResults || 'No exploration performed'}

## Task Schema
${schema}

## Requirements
1. Generate 2-10 tasks depending on complexity
2. Each task MUST include:
   - delivery_criteria: Specific, verifiable conditions for completion (2-5 items)
   - pause_criteria: Conditions that should halt execution (0-3 items)
   - depends_on: Task IDs that must complete first (ensure DAG)
3. Task execution phases: analyze → implement → test → optimize → commit
4. Assign executor based on task nature (analysis=gemini, implementation=codex)

## Delivery Criteria Examples
Good: "User login endpoint returns JWT token with 24h expiry"
Bad: "Authentication works" (too vague)

## Pause Criteria Examples
- "API spec for external service unclear"
- "Database schema migration required"
- "Security review needed before implementation"

## Output Format
Write JSONL file (one JSON object per line):
${issueDir}/tasks.jsonl

## Validation
- Ensure no circular dependencies
- Ensure all depends_on references exist
- Ensure at least one task has empty depends_on (entry point)
`
)

// Validate DAG
const tasks = readJsonl(`${issueDir}/tasks.jsonl`)
validateDAG(tasks) // Throws if circular dependency detected

Phase 4: User Confirmation

// Display task summary
const tasks = readJsonl(`${issueDir}/tasks.jsonl`)

console.log(`
## Issue Plan: ${issueId}

**Title**: ${issueTitle}
**Tasks**: ${tasks.length}
**Complexity**: ${complexity}

### Task Breakdown

| ID | Title | Type | Dependencies | Delivery Criteria |
|----|-------|------|--------------|-------------------|
${tasks.map(t => `| ${t.id} | ${t.title} | ${t.type} | ${t.depends_on.join(', ') || '-'} | ${t.delivery_criteria.length} items |`).join('\n')}

### Dependency Graph
${generateDependencyGraph(tasks)}
`)

// User confirmation
AskUserQuestion({
  questions: [
    {
      question: `Approve issue plan? (${tasks.length} tasks)`,
      header: "Confirm",
      multiSelect: false,
      options: [
        { label: "Approve", description: "Proceed with this plan" },
        { label: "Refine", description: "Modify tasks before proceeding" },
        { label: "Cancel", description: "Discard plan" }
      ]
    }
  ]
})

if (answer === "Refine") {
  // Allow editing specific tasks
  AskUserQuestion({
    questions: [
      {
        question: "What would you like to refine?",
        header: "Refine",
        multiSelect: true,
        options: [
          { label: "Add Task", description: "Add a new task" },
          { label: "Remove Task", description: "Remove an existing task" },
          { label: "Modify Dependencies", description: "Change task dependencies" },
          { label: "Regenerate", description: "Regenerate entire plan" }
        ]
      }
    ]
  })
}

Phase 5: Persistence

// Initialize state.json for status tracking
const state = {
  issue_id: issueId,
  title: issueTitle,
  status: 'planned',
  created_at: getUtc8ISOString(),
  updated_at: getUtc8ISOString(),
  task_count: tasks.length,
  completed_count: 0,
  current_task: null,
  executor_default: flags.executor || 'auto'
}

Write(`${issueDir}/state.json`, JSON.stringify(state, null, 2))

console.log(`
## Plan Created

**Issue**: ${issueId}
**Location**: ${issueDir}/
**Tasks**: ${tasks.length}

### Files Created
- tasks.jsonl (task definitions)
- state.json (execution state)
- context.md (issue context)
${explorationResults ? '- exploration.json (codebase analysis)' : ''}

### Next Steps
1. Review: \`ccw issue list ${issueId}\`
2. Execute: \`/issue:execute ${issueId}\`
3. Monitor: \`ccw issue status ${issueId}\`
`)

JSONL Task Format

Each line in tasks.jsonl is a complete JSON object:

{"id":"TASK-001","title":"Setup auth middleware","type":"feature","description":"Implement JWT verification middleware","file_context":["src/middleware/","src/config/auth.ts"],"depends_on":[],"delivery_criteria":["Middleware validates JWT tokens","Returns 401 for invalid tokens","Passes existing auth tests"],"pause_criteria":["JWT secret configuration unclear"],"status":"pending","current_phase":"analyze","executor":"auto","priority":1,"created_at":"2025-12-26T10:00:00Z","updated_at":"2025-12-26T10:00:00Z"}
{"id":"TASK-002","title":"Protect API routes","type":"feature","description":"Apply auth middleware to /api/v1/* routes","file_context":["src/routes/api/"],"depends_on":["TASK-001"],"delivery_criteria":["All /api/v1/* routes require auth","Public routes excluded","Integration tests pass"],"pause_criteria":[],"status":"pending","current_phase":"analyze","executor":"auto","priority":2,"created_at":"2025-12-26T10:00:00Z","updated_at":"2025-12-26T10:00:00Z"}

Progressive Loading Algorithm

For large task lists, only load tasks with satisfied dependencies:

function getReadyTasks(tasks, completedIds) {
  return tasks.filter(task =>
    task.status === 'pending' &&
    task.depends_on.every(dep => completedIds.has(dep))
  )
}

// Stream JSONL line-by-line for memory efficiency
function* streamJsonl(filePath) {
  const lines = readLines(filePath)
  for (const line of lines) {
    if (line.trim()) yield JSON.parse(line)
  }
}

Error Handling

Error Resolution
Invalid GitHub URL Display correct format, ask for valid URL
Circular dependency List cycle, ask user to resolve
No tasks generated Suggest simpler breakdown or manual entry
Exploration timeout Proceed without exploration, warn user
  • /issue:execute - Execute planned tasks with closed-loop methodology
  • ccw issue list - List all issues and their status
  • ccw issue status - Show detailed issue status