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.
This commit is contained in:
catlog22
2025-12-26 17:21:32 +08:00
parent c8a914aeca
commit cdf4833977
5 changed files with 1830 additions and 0 deletions

View File

@@ -0,0 +1,591 @@
---
name: execute
description: Execute issue tasks with closed-loop methodology (analyze→implement→test→optimize→commit)
argument-hint: "<issue-id> [--task <task-id>] [--batch <n>]"
allowed-tools: TodoWrite(*), Task(*), Bash(*), Read(*), Write(*), Edit(*), AskUserQuestion(*)
---
# Issue Execute Command (/issue:execute)
## Overview
Execute tasks from a planned issue using closed-loop methodology. Each task goes through 5 phases: **Analyze → Implement → Test → Optimize → Commit**. Tasks are loaded progressively based on dependency satisfaction.
**Core capabilities:**
- Progressive task loading (only load ready tasks)
- Closed-loop execution with 5 phases per task
- Automatic retry on test failures (up to 3 attempts)
- Pause on defined pause_criteria conditions
- Delivery criteria verification before completion
- Automatic git commit per task
## Usage
```bash
/issue:execute <ISSUE_ID> [FLAGS]
# Arguments
<issue-id> Issue ID (e.g., GH-123, TEXT-1735200000)
# Flags
--task <id> Execute specific task only
--batch <n> Max concurrent tasks (default: 1)
--skip-commit Skip git commit phase
--dry-run Simulate execution without changes
--continue Continue from paused/failed state
```
## Execution Process
```
Initialization:
├─ Load state.json and tasks.jsonl
├─ Build completed task index
└─ Identify ready tasks (dependencies satisfied)
Task Loop:
└─ For each ready task:
├─ Phase 1: ANALYZE
│ ├─ Verify task requirements
│ ├─ Check file existence
│ ├─ Validate preconditions
│ └─ Check pause_criteria (halt if triggered)
├─ Phase 2: IMPLEMENT
│ ├─ Execute code changes
│ ├─ Write/modify files
│ └─ Track modified files
├─ Phase 3: TEST
│ ├─ Run relevant tests
│ ├─ Verify functionality
│ └─ Retry loop (max 3) on failure → back to IMPLEMENT
├─ Phase 4: OPTIMIZE
│ ├─ Code quality check
│ ├─ Lint/format verification
│ └─ Apply minor improvements
├─ Phase 5: COMMIT
│ ├─ Stage modified files
│ ├─ Create commit with task reference
│ └─ Update task status to 'completed'
└─ Update state.json
Completion:
└─ Return execution summary
```
## Implementation
### Initialization
```javascript
// Load issue context
const issueDir = `.workflow/issues/${issueId}`
const state = JSON.parse(Read(`${issueDir}/state.json`))
const tasks = readJsonl(`${issueDir}/tasks.jsonl`)
// Build completed index
const completedIds = new Set(
tasks.filter(t => t.status === 'completed').map(t => t.id)
)
// Get ready tasks (dependencies satisfied)
function getReadyTasks() {
return tasks.filter(task =>
task.status === 'pending' &&
task.depends_on.every(dep => completedIds.has(dep))
)
}
let readyTasks = getReadyTasks()
if (readyTasks.length === 0) {
if (tasks.every(t => t.status === 'completed')) {
console.log('✓ All tasks completed!')
return
}
console.log('⚠ No ready tasks. Check dependencies or blocked tasks.')
return
}
// Initialize TodoWrite for tracking
TodoWrite({
todos: readyTasks.slice(0, batchSize).map(t => ({
content: `[${t.id}] ${t.title}`,
status: 'pending',
activeForm: `Executing ${t.id}`
}))
})
```
### Task Execution Loop
```javascript
for (const task of readyTasks.slice(0, batchSize)) {
console.log(`\n## Executing: ${task.id} - ${task.title}`)
// Update state
updateTaskStatus(task.id, 'in_progress', 'analyze')
try {
// Phase 1: ANALYZE
const analyzeResult = await executePhase_Analyze(task)
if (analyzeResult.paused) {
console.log(`⏸ Task paused: ${analyzeResult.reason}`)
updateTaskStatus(task.id, 'paused', 'analyze')
continue
}
// Phase 2-5: Closed Loop
let implementRetries = 0
const maxRetries = 3
while (implementRetries < maxRetries) {
// Phase 2: IMPLEMENT
const implementResult = await executePhase_Implement(task, analyzeResult)
updateTaskStatus(task.id, 'in_progress', 'test')
// Phase 3: TEST
const testResult = await executePhase_Test(task, implementResult)
if (testResult.passed) {
// Phase 4: OPTIMIZE
await executePhase_Optimize(task, implementResult)
// Phase 5: COMMIT
if (!flags.skipCommit) {
await executePhase_Commit(task, implementResult)
}
// Mark completed
updateTaskStatus(task.id, 'completed', 'done')
completedIds.add(task.id)
break
} else {
implementRetries++
console.log(`⚠ Test failed, retry ${implementRetries}/${maxRetries}`)
if (implementRetries >= maxRetries) {
updateTaskStatus(task.id, 'failed', 'test')
console.log(`✗ Task failed after ${maxRetries} retries`)
}
}
}
} catch (error) {
updateTaskStatus(task.id, 'failed', task.current_phase)
console.log(`✗ Task failed: ${error.message}`)
}
}
```
### Phase 1: ANALYZE
```javascript
async function executePhase_Analyze(task) {
console.log('### Phase 1: ANALYZE')
// Check pause criteria first
for (const criterion of task.pause_criteria || []) {
const shouldPause = await evaluatePauseCriterion(criterion, task)
if (shouldPause) {
return { paused: true, reason: criterion }
}
}
// Execute analysis via CLI
const analysisResult = await Task(
subagent_type="cli-explore-agent",
run_in_background=false,
description=`Analyze: ${task.id}`,
prompt=`
## Analysis Task
ID: ${task.id}
Title: ${task.title}
Description: ${task.description}
## File Context
${task.file_context.join('\n')}
## Delivery Criteria (to be achieved)
${task.delivery_criteria.map((c, i) => `${i+1}. ${c}`).join('\n')}
## Required Analysis
1. Verify all referenced files exist
2. Identify exact modification points
3. Check for potential conflicts
4. Validate approach feasibility
## Output
Return JSON:
{
"files_to_modify": ["path1", "path2"],
"integration_points": [...],
"potential_risks": [...],
"implementation_notes": "..."
}
`
)
// Parse and return
const analysis = JSON.parse(analysisResult)
// Update phase results
updatePhaseResult(task.id, 'analyze', {
status: 'completed',
findings: analysis.potential_risks,
timestamp: new Date().toISOString()
})
return { paused: false, analysis }
}
```
### Phase 2: IMPLEMENT
```javascript
async function executePhase_Implement(task, analyzeResult) {
console.log('### Phase 2: IMPLEMENT')
updateTaskStatus(task.id, 'in_progress', 'implement')
// Determine executor
const executor = task.executor === 'auto'
? (task.type === 'test' ? 'agent' : 'codex')
: task.executor
// Build implementation prompt
const prompt = `
## Implementation Task
ID: ${task.id}
Title: ${task.title}
Type: ${task.type}
## Description
${task.description}
## Analysis Results
${JSON.stringify(analyzeResult.analysis, null, 2)}
## Files to Modify
${analyzeResult.analysis.files_to_modify.join('\n')}
## Delivery Criteria (MUST achieve all)
${task.delivery_criteria.map((c, i) => `- [ ] ${c}`).join('\n')}
## Implementation Notes
${analyzeResult.analysis.implementation_notes}
## Rules
- Follow existing code patterns
- Maintain backward compatibility
- Add appropriate error handling
- Document significant changes
`
let result
if (executor === 'codex') {
result = Bash(
`ccw cli -p "${escapePrompt(prompt)}" --tool codex --mode write`,
timeout=3600000
)
} else if (executor === 'gemini') {
result = Bash(
`ccw cli -p "${escapePrompt(prompt)}" --tool gemini --mode write`,
timeout=1800000
)
} else {
result = await Task(
subagent_type="code-developer",
run_in_background=false,
description=`Implement: ${task.id}`,
prompt=prompt
)
}
// Track modified files
const modifiedFiles = extractModifiedFiles(result)
updatePhaseResult(task.id, 'implement', {
status: 'completed',
files_modified: modifiedFiles,
timestamp: new Date().toISOString()
})
return { modifiedFiles, output: result }
}
```
### Phase 3: TEST
```javascript
async function executePhase_Test(task, implementResult) {
console.log('### Phase 3: TEST')
updateTaskStatus(task.id, 'in_progress', 'test')
// Determine test command based on project
const testCommand = detectTestCommand(task.file_context)
// e.g., 'npm test', 'pytest', 'go test', etc.
// Run tests
const testResult = Bash(testCommand, timeout=300000)
const passed = testResult.exitCode === 0
// Verify delivery criteria
let criteriaVerified = passed
if (passed) {
for (const criterion of task.delivery_criteria) {
const verified = await verifyCriterion(criterion, implementResult)
if (!verified) {
criteriaVerified = false
console.log(`⚠ Criterion not met: ${criterion}`)
}
}
}
updatePhaseResult(task.id, 'test', {
status: passed && criteriaVerified ? 'passed' : 'failed',
test_results: testResult.output.substring(0, 1000),
retry_count: implementResult.retryCount || 0,
timestamp: new Date().toISOString()
})
return { passed: passed && criteriaVerified, output: testResult }
}
```
### Phase 4: OPTIMIZE
```javascript
async function executePhase_Optimize(task, implementResult) {
console.log('### Phase 4: OPTIMIZE')
updateTaskStatus(task.id, 'in_progress', 'optimize')
// Run linting/formatting
const lintResult = Bash('npm run lint:fix || true', timeout=60000)
// Quick code review
const reviewResult = await Task(
subagent_type="universal-executor",
run_in_background=false,
description=`Review: ${task.id}`,
prompt=`
Quick code review for task ${task.id}
## Modified Files
${implementResult.modifiedFiles.join('\n')}
## Check
1. Code follows project conventions
2. No obvious security issues
3. Error handling is appropriate
4. No dead code or console.logs
## Output
If issues found, apply fixes directly. Otherwise confirm OK.
`
)
updatePhaseResult(task.id, 'optimize', {
status: 'completed',
improvements: extractImprovements(reviewResult),
timestamp: new Date().toISOString()
})
return { lintResult, reviewResult }
}
```
### Phase 5: COMMIT
```javascript
async function executePhase_Commit(task, implementResult) {
console.log('### Phase 5: COMMIT')
updateTaskStatus(task.id, 'in_progress', 'commit')
// Stage modified files
for (const file of implementResult.modifiedFiles) {
Bash(`git add "${file}"`)
}
// Create commit message
const typePrefix = {
'feature': 'feat',
'bug': 'fix',
'refactor': 'refactor',
'test': 'test',
'chore': 'chore',
'docs': 'docs'
}[task.type] || 'feat'
const commitMessage = `${typePrefix}(${task.id}): ${task.title}
${task.description.substring(0, 200)}
Delivery Criteria:
${task.delivery_criteria.map(c => `- [x] ${c}`).join('\n')}
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>`
// Commit
const commitResult = Bash(`git commit -m "$(cat <<'EOF'
${commitMessage}
EOF
)"`)
// Get commit hash
const commitHash = Bash('git rev-parse HEAD').trim()
updatePhaseResult(task.id, 'commit', {
status: 'completed',
commit_hash: commitHash,
message: `${typePrefix}(${task.id}): ${task.title}`,
timestamp: new Date().toISOString()
})
console.log(`✓ Committed: ${commitHash.substring(0, 7)}`)
return { commitHash }
}
```
### State Management
```javascript
// Update task status in JSONL (append-style with compaction)
function updateTaskStatus(taskId, status, phase) {
const tasks = readJsonl(`${issueDir}/tasks.jsonl`)
const taskIndex = tasks.findIndex(t => t.id === taskId)
if (taskIndex >= 0) {
tasks[taskIndex].status = status
tasks[taskIndex].current_phase = phase
tasks[taskIndex].updated_at = new Date().toISOString()
// Rewrite JSONL (compact)
const jsonlContent = tasks.map(t => JSON.stringify(t)).join('\n')
Write(`${issueDir}/tasks.jsonl`, jsonlContent)
}
// Update state.json
const state = JSON.parse(Read(`${issueDir}/state.json`))
state.current_task = status === 'in_progress' ? taskId : null
state.completed_count = tasks.filter(t => t.status === 'completed').length
state.updated_at = new Date().toISOString()
Write(`${issueDir}/state.json`, JSON.stringify(state, null, 2))
}
// Update phase result
function updatePhaseResult(taskId, phase, result) {
const tasks = readJsonl(`${issueDir}/tasks.jsonl`)
const taskIndex = tasks.findIndex(t => t.id === taskId)
if (taskIndex >= 0) {
tasks[taskIndex].phase_results = tasks[taskIndex].phase_results || {}
tasks[taskIndex].phase_results[phase] = result
const jsonlContent = tasks.map(t => JSON.stringify(t)).join('\n')
Write(`${issueDir}/tasks.jsonl`, jsonlContent)
}
}
```
## Progressive Loading
For memory efficiency with large task lists:
```javascript
// Stream JSONL and only load ready tasks
function* getReadyTasksStream(issueDir, completedIds) {
const filePath = `${issueDir}/tasks.jsonl`
const lines = readFileLines(filePath)
for (const line of lines) {
if (!line.trim()) continue
const task = JSON.parse(line)
if (task.status === 'pending' &&
task.depends_on.every(dep => completedIds.has(dep))) {
yield task
}
}
}
// Usage: Only load what's needed
const iterator = getReadyTasksStream(issueDir, completedIds)
const batch = []
for (let i = 0; i < batchSize; i++) {
const { value, done } = iterator.next()
if (done) break
batch.push(value)
}
```
## Pause Criteria Evaluation
```javascript
async function evaluatePauseCriterion(criterion, task) {
// Pattern matching for common pause conditions
const patterns = [
{ match: /unclear|undefined|missing/i, action: 'ask_user' },
{ match: /security review/i, action: 'require_approval' },
{ match: /migration required/i, action: 'check_migration' },
{ match: /external (api|service)/i, action: 'verify_external' }
]
for (const pattern of patterns) {
if (pattern.match.test(criterion)) {
// Check if condition is resolved
const resolved = await checkCondition(pattern.action, criterion, task)
if (!resolved) return true // Pause
}
}
return false // Don't pause
}
```
## Error Handling
| Error | Resolution |
|-------|------------|
| Task not found | List available tasks, suggest correct ID |
| Dependencies unsatisfied | Show blocking tasks, suggest running those first |
| Test failure (3x) | Mark failed, save state, suggest manual intervention |
| Pause triggered | Save state, display pause reason, await user action |
| Commit conflict | Stash changes, report conflict, await resolution |
## Output
```
## Execution Complete
**Issue**: GH-123
**Tasks Executed**: 3/5
**Completed**: 3
**Failed**: 0
**Pending**: 2 (dependencies not met)
### Task Status
| ID | Title | Status | Phase | Commit |
|----|-------|--------|-------|--------|
| TASK-001 | Setup auth middleware | ✓ | done | a1b2c3d |
| TASK-002 | Protect API routes | ✓ | done | e4f5g6h |
| TASK-003 | Add login endpoint | ✓ | done | i7j8k9l |
| TASK-004 | Add logout endpoint | ⏳ | pending | - |
| TASK-005 | Integration tests | ⏳ | pending | - |
### Next Steps
Run `/issue:execute GH-123` to continue with remaining tasks.
```
## Related Commands
- `/issue:plan` - Create issue plan with JSONL tasks
- `ccw issue status` - Check issue execution status
- `ccw issue retry` - Retry failed tasks

View File

@@ -0,0 +1,361 @@
---
name: plan
description: Plan issue resolution with JSONL task generation, delivery/pause criteria, and dependency graph
argument-hint: "\"issue description\"|github-url|file.md"
allowed-tools: 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
```bash
/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
```javascript
// 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
```javascript
// 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
```javascript
// 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
```javascript
// 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
```javascript
// 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:
```json
{"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:
```javascript
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 |
## Related Commands
- `/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

View File

@@ -0,0 +1,136 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Issue Task JSONL Schema",
"description": "Schema for individual task entries in tasks.jsonl file",
"type": "object",
"required": ["id", "title", "type", "description", "depends_on", "delivery_criteria", "status", "current_phase", "executor"],
"properties": {
"id": {
"type": "string",
"description": "Unique task identifier (e.g., TASK-001)",
"pattern": "^TASK-[0-9]+$"
},
"title": {
"type": "string",
"description": "Short summary of the task",
"maxLength": 100
},
"type": {
"type": "string",
"enum": ["feature", "bug", "refactor", "test", "chore", "docs"],
"description": "Task category"
},
"description": {
"type": "string",
"description": "Detailed instructions for the task"
},
"file_context": {
"type": "array",
"items": { "type": "string" },
"description": "List of relevant files/globs",
"default": []
},
"depends_on": {
"type": "array",
"items": { "type": "string" },
"description": "Array of Task IDs that must complete first",
"default": []
},
"delivery_criteria": {
"type": "array",
"items": { "type": "string" },
"description": "Checklist items that define task completion",
"minItems": 1
},
"pause_criteria": {
"type": "array",
"items": { "type": "string" },
"description": "Conditions that should halt execution (e.g., 'API spec unclear')",
"default": []
},
"status": {
"type": "string",
"enum": ["pending", "ready", "in_progress", "completed", "failed", "paused", "skipped"],
"description": "Current task status",
"default": "pending"
},
"current_phase": {
"type": "string",
"enum": ["analyze", "implement", "test", "optimize", "commit", "done"],
"description": "Current execution phase within the task lifecycle",
"default": "analyze"
},
"executor": {
"type": "string",
"enum": ["agent", "codex", "gemini", "auto"],
"description": "Preferred executor for this task",
"default": "auto"
},
"priority": {
"type": "integer",
"minimum": 1,
"maximum": 5,
"description": "Task priority (1=highest, 5=lowest)",
"default": 3
},
"phase_results": {
"type": "object",
"description": "Results from each execution phase",
"properties": {
"analyze": {
"type": "object",
"properties": {
"status": { "type": "string" },
"findings": { "type": "array", "items": { "type": "string" } },
"timestamp": { "type": "string", "format": "date-time" }
}
},
"implement": {
"type": "object",
"properties": {
"status": { "type": "string" },
"files_modified": { "type": "array", "items": { "type": "string" } },
"timestamp": { "type": "string", "format": "date-time" }
}
},
"test": {
"type": "object",
"properties": {
"status": { "type": "string" },
"test_results": { "type": "string" },
"retry_count": { "type": "integer" },
"timestamp": { "type": "string", "format": "date-time" }
}
},
"optimize": {
"type": "object",
"properties": {
"status": { "type": "string" },
"improvements": { "type": "array", "items": { "type": "string" } },
"timestamp": { "type": "string", "format": "date-time" }
}
},
"commit": {
"type": "object",
"properties": {
"status": { "type": "string" },
"commit_hash": { "type": "string" },
"message": { "type": "string" },
"timestamp": { "type": "string", "format": "date-time" }
}
}
}
},
"created_at": {
"type": "string",
"format": "date-time",
"description": "Task creation timestamp"
},
"updated_at": {
"type": "string",
"format": "date-time",
"description": "Last update timestamp"
}
},
"additionalProperties": false
}