diff --git a/.claude/commands/issue/new.md b/.claude/commands/issue/new.md index 00d6a411..34825e8c 100644 --- a/.claude/commands/issue/new.md +++ b/.claude/commands/issue/new.md @@ -35,16 +35,11 @@ interface Issue { affected_components?: string[];// Files/modules affected reproduction_steps?: string[]; // Steps to reproduce - // Discovery context (when source='discovery') - discovery_context?: { - discovery_id: string; // Source discovery session - perspective: string; // bug, test, quality, etc. - category: string; // Finding category - file: string; // Primary affected file - line: number; // Line number - snippet?: string; // Code snippet - confidence: number; // Agent confidence (0-1) + // Extended context (minimal, for planning hints) + extended_context?: { + location?: string; // file:line when specific line matters suggested_fix?: string; // Suggested remediation + notes?: string; // Additional notes (user clarifications or discovery hints) }; // Closed-loop requirements (guide plan generation) @@ -290,6 +285,108 @@ const lifecycle = { issueData.lifecycle_requirements = lifecycle; ``` +### Phase 4.5: Follow-up Questions (Intelligent Suggestions) + +```javascript +// Analyze parsed data and suggest follow-up questions for missing/unclear fields +const suggestions = []; + +// Check for missing critical fields +if (!issueData.expected_behavior) { + suggestions.push({ + field: 'expected_behavior', + question: 'What is the expected behavior?', + hint: 'Describe what should happen when working correctly' + }); +} + +if (!issueData.actual_behavior && issueData.source !== 'discovery') { + suggestions.push({ + field: 'actual_behavior', + question: 'What is the actual behavior?', + hint: 'Describe what currently happens (error message, wrong output, etc.)' + }); +} + +if (!issueData.affected_components?.length) { + suggestions.push({ + field: 'affected_components', + question: 'Which files or modules are affected?', + hint: 'e.g., src/auth/login.ts, src/api/users.ts' + }); +} + +if (!issueData.reproduction_steps?.length && issueData.source === 'text') { + suggestions.push({ + field: 'reproduction_steps', + question: 'How can this issue be reproduced?', + hint: 'Step-by-step instructions to trigger the issue' + }); +} + +// Ask follow-up questions if any suggestions exist +let userNotes = ''; +if (suggestions.length > 0) { + console.log(` +## Suggested Clarifications + +The following information would help with issue resolution: +${suggestions.map((s, i) => `${i+1}. **${s.question}** - ${s.hint}`).join('\n')} +`); + + const followUpAnswer = AskUserQuestion({ + questions: [{ + question: 'Would you like to provide additional details?', + header: 'Clarify', + multiSelect: false, + options: [ + { label: 'Add details', description: 'Provide clarifications for suggested questions' }, + { label: 'Skip', description: 'Continue without additional details (Recommended)' } + ] + }] + }); + + if (followUpAnswer.includes('Add details')) { + // Collect additional notes via "Other" input + const notesAnswer = AskUserQuestion({ + questions: [{ + question: 'Enter additional details (address any of the suggested questions):', + header: 'Details', + multiSelect: false, + options: [ + { label: 'Use template', description: 'Expected: ... | Actual: ... | Files: ...' } + ] + }] + }); + + if (notesAnswer.customText) { + userNotes = notesAnswer.customText; + + // Parse structured input if provided + const expectedMatch = userNotes.match(/expected:\s*([^|]+)/i); + const actualMatch = userNotes.match(/actual:\s*([^|]+)/i); + const filesMatch = userNotes.match(/files?:\s*([^|]+)/i); + + if (expectedMatch && !issueData.expected_behavior) { + issueData.expected_behavior = expectedMatch[1].trim(); + } + if (actualMatch && !issueData.actual_behavior) { + issueData.actual_behavior = actualMatch[1].trim(); + } + if (filesMatch && !issueData.affected_components?.length) { + issueData.affected_components = filesMatch[1].split(/[,\s]+/).filter(f => f.includes('/') || f.includes('.')); + } + } + } +} + +// Store user notes in extended context +issueData.extended_context = { + ...(issueData.extended_context || {}), + notes: userNotes || null +}; +``` + ### Phase 5: User Confirmation ```javascript @@ -377,6 +474,9 @@ const newIssue = { affected_components: issueData.affected_components || [], reproduction_steps: issueData.reproduction_steps || [], + // Extended context (universal) + extended_context: issueData.extended_context || null, + // Closed-loop lifecycle requirements lifecycle_requirements: issueData.lifecycle_requirements || { test_strategy: 'auto', @@ -395,9 +495,11 @@ const newIssue = { // Ensure directory exists Bash('mkdir -p .workflow/issues'); -// Append to issues.jsonl +// Append to issues.jsonl with proper newline handling const issuesPath = '.workflow/issues/issues.jsonl'; -Bash(`echo '${JSON.stringify(newIssue)}' >> "${issuesPath}"`); +const jsonLine = JSON.stringify(newIssue); +// Ensure file ends with newline before appending, then append with trailing newline +Bash(`[ -s "${issuesPath}" ] && [ -n "$(tail -c 1 "${issuesPath}")" ] && printf '\\n' >> "${issuesPath}"; printf '%s\\n' '${jsonLine}' >> "${issuesPath}"`); console.log(` ## Issue Created diff --git a/.claude/commands/issue/plan.md b/.claude/commands/issue/plan.md index 42ca0734..c55f6150 100644 --- a/.claude/commands/issue/plan.md +++ b/.claude/commands/issue/plan.md @@ -207,7 +207,7 @@ ${issueList} ### Steps 1. Fetch: \`ccw issue status --json\` 2. Load project context (project-tech.json + project-guidelines.json) -3. **If source=discovery**: Use discovery_context (file, line, snippet, suggested_fix) as planning hints +3. **If extended_context exists**: Use extended_context (location, suggested_fix, notes) as planning hints 4. Explore (ACE) → Plan solution (respecting guidelines) 5. Register & bind: \`ccw issue bind --solution \` diff --git a/.claude/workflows/cli-templates/schemas/issues-jsonl-schema.json b/.claude/workflows/cli-templates/schemas/issues-jsonl-schema.json index e31bffd0..91d630e5 100644 --- a/.claude/workflows/cli-templates/schemas/issues-jsonl-schema.json +++ b/.claude/workflows/cli-templates/schemas/issues-jsonl-schema.json @@ -42,43 +42,21 @@ "items": { "type": "string" }, "description": "Issue labels/tags" }, - "discovery_context": { + "extended_context": { "type": "object", - "description": "Enriched context from issue:discover (only when source=discovery)", + "description": "Minimal extended context for planning hints", "properties": { - "discovery_id": { + "location": { "type": "string", - "description": "Source discovery session ID" - }, - "perspective": { - "type": "string", - "enum": ["bug", "ux", "test", "quality", "security", "performance", "maintainability", "best-practices"] - }, - "category": { - "type": "string", - "description": "Finding category (e.g., edge-case, race-condition)" - }, - "file": { - "type": "string", - "description": "Primary affected file" - }, - "line": { - "type": "integer", - "description": "Line number in primary file" - }, - "snippet": { - "type": "string", - "description": "Code snippet showing the issue" - }, - "confidence": { - "type": "number", - "minimum": 0, - "maximum": 1, - "description": "Agent confidence score" + "description": "file:line format (e.g., 'src/auth.ts:42')" }, "suggested_fix": { "type": "string", - "description": "Suggested remediation from discovery" + "description": "Suggested remediation" + }, + "notes": { + "type": "string", + "description": "Additional notes (user clarifications or discovery hints)" } } }, @@ -143,15 +121,10 @@ "context": "Connection pool cleanup only happens when MAX_POOL_SIZE is reached...", "source": "discovery", "labels": ["bug", "resource-leak", "critical"], - "discovery_context": { - "discovery_id": "DSC-20251228-182237", - "perspective": "bug", - "category": "resource-leak", - "file": "storage/sqlite_store.py", - "line": 59, - "snippet": "if len(self._pool) >= self.MAX_POOL_SIZE:\n self._cleanup_stale_connections()", - "confidence": 0.85, - "suggested_fix": "Implement periodic cleanup or weak references" + "extended_context": { + "location": "storage/sqlite_store.py:59", + "suggested_fix": "Implement periodic cleanup or weak references", + "notes": null }, "affected_components": ["storage/sqlite_store.py"], "lifecycle_requirements": {