From bf06f4ddcc416ef871afe70900265690042391dd Mon Sep 17 00:00:00 2001 From: catlog22 Date: Tue, 13 Jan 2026 09:58:55 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0GitHub=E5=9B=9E?= =?UTF-8?q?=E5=A4=8D=E4=BB=BB=E5=8A=A1=EF=BC=8C=E6=94=AF=E6=8C=81=E5=9C=A8?= =?UTF-8?q?=E9=97=AE=E9=A2=98=E5=AD=98=E5=9C=A8github=5Furl=E6=97=B6?= =?UTF-8?q?=E8=87=AA=E5=8A=A8=E8=AF=84=E8=AE=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .claude/agents/issue-plan-agent.md | 33 ++++++++++- .claude/commands/issue/new.md | 94 +++++++++++++++++++++++++++++- .claude/commands/issue/plan.md | 5 +- 3 files changed, 126 insertions(+), 6 deletions(-) diff --git a/.claude/agents/issue-plan-agent.md b/.claude/agents/issue-plan-agent.md index 2efa5986..62e61eee 100644 --- a/.claude/agents/issue-plan-agent.md +++ b/.claude/agents/issue-plan-agent.md @@ -138,7 +138,7 @@ Generate multiple candidate solutions when: **Task Decomposition** following schema: ```javascript function decomposeTasks(issue, exploration) { - return groups.map(group => ({ + const tasks = groups.map(group => ({ id: `T${taskId++}`, // Pattern: ^T[0-9]+$ title: group.title, scope: inferScope(group), // Module path @@ -161,7 +161,35 @@ function decomposeTasks(issue, exploration) { }, depends_on: inferDependencies(group, tasks), priority: calculatePriority(group) // 1-5 (1=highest) - })) + })); + + // GitHub Reply Task: Add final task if issue has github_url + if (issue.github_url || issue.github_number) { + const lastTaskId = tasks[tasks.length - 1]?.id; + tasks.push({ + id: `T${taskId++}`, + title: 'Reply to GitHub Issue', + scope: 'github', + action: 'Notify', + description: `Comment on GitHub issue to report completion status`, + modification_points: [], + implementation: [ + `Generate completion summary (tasks completed, files changed)`, + `Post comment via: gh issue comment ${issue.github_number || extractNumber(issue.github_url)} --body "..."`, + `Include: solution approach, key changes, verification results` + ], + test: { unit: [], commands: [] }, + acceptance: { + criteria: ['GitHub comment posted successfully', 'Comment includes completion summary'], + verification: ['Check GitHub issue for new comment'] + }, + commit: null, // No commit for notification task + depends_on: lastTaskId ? [lastTaskId] : [], // Depends on last implementation task + priority: 5 // Lowest priority (run last) + }); + } + + return tasks; } ``` @@ -284,6 +312,7 @@ Each line is a solution JSON containing tasks. Schema: `cat .claude/workflows/cl 7. Write solutions to `.workflow/issues/solutions/{issue-id}.jsonl` (append mode) 8. For HIGH complexity: generate 2-3 candidate solutions 9. **Solution ID format**: `SOL-{issue-id}-{N}` (e.g., `SOL-GH-123-1`, `SOL-GH-123-2`) +10. **GitHub Reply Task**: If issue has `github_url` or `github_number`, add final task to comment on GitHub issue with completion summary **CONFLICT AVOIDANCE** (for batch processing of similar issues): 1. **File isolation**: Each issue's solution should target distinct files when possible diff --git a/.claude/commands/issue/new.md b/.claude/commands/issue/new.md index 2af2d118..cd45c5ac 100644 --- a/.claude/commands/issue/new.md +++ b/.claude/commands/issue/new.md @@ -29,6 +29,10 @@ interface Issue { source_url?: string; labels?: string[]; + // GitHub binding (for non-GitHub sources that publish to GitHub) + github_url?: string; // https://github.com/owner/repo/issues/123 + github_number?: number; // 123 + // Optional structured fields expected_behavior?: string; actual_behavior?: string; @@ -165,7 +169,30 @@ if (clarityScore < 2 && (!issueData.context || issueData.context.length < 20)) { } ``` -### Phase 5: Create Issue +### Phase 5: GitHub Publishing Decision (Non-GitHub Sources) + +```javascript +// For non-GitHub sources, ask if user wants to publish to GitHub +let publishToGitHub = false; + +if (issueData.source !== 'github') { + const publishAnswer = AskUserQuestion({ + questions: [{ + question: 'Would you like to publish this issue to GitHub?', + header: 'Publish', + multiSelect: false, + options: [ + { label: 'Yes, publish to GitHub', description: 'Create issue on GitHub and link it' }, + { label: 'No, keep local only', description: 'Store as local issue without GitHub sync' } + ] + }] + }); + + publishToGitHub = publishAnswer.answers?.['Publish']?.includes('Yes'); +} +``` + +### Phase 6: Create Issue **Summary Display:** - Show ID, title, source, affected files (if any) @@ -220,8 +247,64 @@ EOF } ``` +**GitHub Publishing** (if user opted in): +```javascript +// Step 1: Create local issue FIRST +const localIssue = createLocalIssue(issueData); // ccw issue create + +// Step 2: Publish to GitHub if requested +if (publishToGitHub) { + const ghResult = Bash(`gh issue create --title "${issueData.title}" --body "${issueData.context}"`); + // Parse GitHub URL from output + const ghUrl = ghResult.match(/https:\/\/github\.com\/[\w-]+\/[\w-]+\/issues\/\d+/)?.[0]; + const ghNumber = parseInt(ghUrl?.match(/\/issues\/(\d+)/)?.[1]); + + if (ghNumber) { + // Step 3: Update local issue with GitHub binding + Bash(`ccw issue update ${localIssue.id} --github-url "${ghUrl}" --github-number ${ghNumber}`); + // Or via pipe: + // echo '{"github_url":"${ghUrl}","github_number":${ghNumber}}' | ccw issue update ${localIssue.id} + } +} +``` + +**Workflow:** +``` +1. Create local issue (ISS-YYYYMMDD-NNN) → stored in .workflow/issues.jsonl +2. If publishToGitHub: + a. gh issue create → returns GitHub URL + b. Update local issue with github_url + github_number binding +3. Both local and GitHub issues exist, linked together +``` + +**Example with GitHub Publishing:** +```bash +# User creates text issue +/issue:new "Login fails with special chars. Expected: success. Actual: 500" + +# System asks: "Would you like to publish this issue to GitHub?" +# User selects: "Yes, publish to GitHub" + +# Output: +# ✓ Local issue created: ISS-20251229-001 +# ✓ Published to GitHub: https://github.com/org/repo/issues/123 +# ✓ GitHub binding saved to local issue +# → Next step: /issue:plan ISS-20251229-001 + +# Resulting issue JSON: +{ + "id": "ISS-20251229-001", + "title": "Login fails with special chars", + "source": "text", + "github_url": "https://github.com/org/repo/issues/123", + "github_number": 123, + ... +} +``` + **Completion:** - Display created issue ID +- Show GitHub URL (if published) - Show next step: `/issue:plan ` ## Execution Flow @@ -240,9 +323,16 @@ Phase 2: Data Extraction (branched by clarity) │ │ (3 files max) │ → feedback │ └────────────┴─────────────────┴──────────────┘ -Phase 3: Create Issue +Phase 3: GitHub Publishing Decision (non-GitHub only) + ├─ Source = github: Skip (already from GitHub) + └─ Source ≠ github: AskUserQuestion + ├─ Yes → publishToGitHub = true + └─ No → publishToGitHub = false + +Phase 4: Create Issue ├─ Score ≥ 2: Direct creation └─ Score < 2: Confirm first → Create + └─ If publishToGitHub: gh issue create → link URL Note: Deep exploration & lifecycle deferred to /issue:plan ``` diff --git a/.claude/commands/issue/plan.md b/.claude/commands/issue/plan.md index 1b79bd6b..9dc32294 100644 --- a/.claude/commands/issue/plan.md +++ b/.claude/commands/issue/plan.md @@ -198,8 +198,9 @@ ${issueList} 2. Load project context files 3. Explore codebase (ACE semantic search) 4. Plan solution with tasks (schema: solution-schema.json) -5. Write solution to: .workflow/issues/solutions/{issue-id}.jsonl -6. Single solution → auto-bind; Multiple → return for selection +5. **If github_url exists**: Add final task to comment on GitHub issue +6. Write solution to: .workflow/issues/solutions/{issue-id}.jsonl +7. Single solution → auto-bind; Multiple → return for selection ### Rules - Solution ID format: SOL-{issue-id}-{seq}