claude-cli-advanced-starter-pack 1.8.3 → 1.8.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -2
- package/bin/gtask.js +24 -0
- package/package.json +1 -1
- package/src/cli/menu.js +171 -1
- package/src/cli/mobile-menu.js +230 -0
- package/src/commands/global-reinstall.js +243 -0
- package/src/commands/global-uninstall.js +229 -0
- package/src/commands/init.js +224 -0
- package/src/commands/panel.js +84 -0
- package/src/commands/uninstall.js +8 -1
- package/src/data/releases.json +30 -0
- package/src/utils/global-registry.js +230 -0
- package/src/utils/happy-detect.js +66 -0
- package/src/utils/paths.js +146 -0
- package/templates/commands/create-task-list-for-issue.template.md +216 -0
- package/templates/commands/create-task-list.template.md +280 -82
- package/templates/commands/menu-for-happy-ui.template.md +109 -0
- package/templates/commands/menu-issues-list.template.md +288 -0
- package/templates/hooks/github-progress-hook.template.cjs +248 -0
- package/templates/hooks/github-progress-hook.template.js +0 -197
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Mobile-friendly menu of open GitHub issues sorted by date
|
|
3
|
+
type: utility
|
|
4
|
+
complexity: simple
|
|
5
|
+
model: haiku
|
|
6
|
+
allowed-tools:
|
|
7
|
+
- Bash
|
|
8
|
+
- AskUserQuestion
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# /menu-issues-list - Quick Issues View
|
|
12
|
+
|
|
13
|
+
**Mobile-friendly list of open GitHub issues with single-character selection.**
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## EXECUTION
|
|
18
|
+
|
|
19
|
+
### Step 1: Fetch and Display Issues
|
|
20
|
+
|
|
21
|
+
Run this command to get open issues:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
gh issue list --state open --json number,title,createdAt,labels --limit 20
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Step 2: Format for Mobile Display
|
|
28
|
+
|
|
29
|
+
Display issues in this compact format (sorted newest to oldest):
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
╔═══════════════════════════════════════╗
|
|
33
|
+
║ 📋 Open Issues ║
|
|
34
|
+
╠═══════════════════════════════════════╣
|
|
35
|
+
║ ║
|
|
36
|
+
║ [A] #123 - Fix login redirect bug ║
|
|
37
|
+
║ 01/30 • P1 • frontend ║
|
|
38
|
+
║ ║
|
|
39
|
+
║ [B] #122 - Add dark mode toggle ║
|
|
40
|
+
║ 01/29 • P2 • feature ║
|
|
41
|
+
║ ║
|
|
42
|
+
║ [C] #121 - Update API docs ║
|
|
43
|
+
║ 01/28 • P3 • docs ║
|
|
44
|
+
║ ║
|
|
45
|
+
║ [D] #120 - Refactor auth module ║
|
|
46
|
+
║ 01/27 • P2 • backend ║
|
|
47
|
+
║ ║
|
|
48
|
+
╠═══════════════════════════════════════╣
|
|
49
|
+
║ [R] Refresh [X] Exit ║
|
|
50
|
+
╚═══════════════════════════════════════╝
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**Format Rules:**
|
|
54
|
+
- Title: Max 30 chars, truncate with `...` if longer
|
|
55
|
+
- Date: `MM/DD` format (createdAt)
|
|
56
|
+
- Priority: Extract from labels (P0, P1, P2, P3) or show `-`
|
|
57
|
+
- Labels: Show first non-priority label, max 10 chars
|
|
58
|
+
- Sort: Newest first (by createdAt descending)
|
|
59
|
+
|
|
60
|
+
### Step 3: Ask User Selection
|
|
61
|
+
|
|
62
|
+
Use AskUserQuestion with single-letter options:
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
header: "Select"
|
|
66
|
+
question: "Pick an issue (A-Z) or action:"
|
|
67
|
+
options:
|
|
68
|
+
- label: "A"
|
|
69
|
+
description: "#123 - Fix login redirect bug"
|
|
70
|
+
- label: "B"
|
|
71
|
+
description: "#122 - Add dark mode toggle"
|
|
72
|
+
- label: "C"
|
|
73
|
+
description: "#121 - Update API docs"
|
|
74
|
+
- label: "R"
|
|
75
|
+
description: "Refresh list"
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Step 4: Handle Selection
|
|
79
|
+
|
|
80
|
+
**If user selects an issue (A-Z):**
|
|
81
|
+
|
|
82
|
+
First, fetch full issue details including body:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
gh issue view [NUMBER] --json number,title,body,createdAt,labels,url
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
**Detect Issue Format:**
|
|
89
|
+
|
|
90
|
+
Check if the issue body contains BOTH of these indicators of `/github-task` format:
|
|
91
|
+
- `## Acceptance Criteria` OR `## Task Checklist` section
|
|
92
|
+
- `## Suggested Implementation` OR `## Implementation Approach` section
|
|
93
|
+
|
|
94
|
+
**If PROPERLY FORMATTED** (created by `/github-task`):
|
|
95
|
+
|
|
96
|
+
Show issue details with format indicator:
|
|
97
|
+
|
|
98
|
+
```
|
|
99
|
+
╔═══════════════════════════════════════╗
|
|
100
|
+
║ Issue #123 ✓ Task-Ready ║
|
|
101
|
+
╠═══════════════════════════════════════╣
|
|
102
|
+
║ ║
|
|
103
|
+
║ Fix login redirect bug ║
|
|
104
|
+
║ ║
|
|
105
|
+
║ Created: 01/30/2026 ║
|
|
106
|
+
║ Labels: P1, frontend, bug ║
|
|
107
|
+
║ URL: github.com/.../issues/123 ║
|
|
108
|
+
║ ║
|
|
109
|
+
║ Format: Task-ready (has checklist) ║
|
|
110
|
+
║ ║
|
|
111
|
+
╠═══════════════════════════════════════╣
|
|
112
|
+
║ [S] Start (use existing tasks) ║
|
|
113
|
+
║ [V] View details [B] Back [X] Exit ║
|
|
114
|
+
╚═══════════════════════════════════════╝
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Then ask:
|
|
118
|
+
|
|
119
|
+
```
|
|
120
|
+
header: "Action"
|
|
121
|
+
question: "What would you like to do?"
|
|
122
|
+
options:
|
|
123
|
+
- label: "S - Start working"
|
|
124
|
+
description: "Execute task checklist from issue"
|
|
125
|
+
- label: "V - View details"
|
|
126
|
+
description: "Show full issue body"
|
|
127
|
+
- label: "B - Back"
|
|
128
|
+
description: "Return to issues list"
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
**If NOT PROPERLY FORMATTED** (generic issue):
|
|
132
|
+
|
|
133
|
+
Show issue details with exploration indicator:
|
|
134
|
+
|
|
135
|
+
```
|
|
136
|
+
╔═══════════════════════════════════════╗
|
|
137
|
+
║ Issue #123 ⚠ Needs Analysis ║
|
|
138
|
+
╠═══════════════════════════════════════╣
|
|
139
|
+
║ ║
|
|
140
|
+
║ Fix login redirect bug ║
|
|
141
|
+
║ ║
|
|
142
|
+
║ Created: 01/30/2026 ║
|
|
143
|
+
║ Labels: P1, frontend, bug ║
|
|
144
|
+
║ URL: github.com/.../issues/123 ║
|
|
145
|
+
║ ║
|
|
146
|
+
║ Format: Needs task list generation ║
|
|
147
|
+
║ ║
|
|
148
|
+
╠═══════════════════════════════════════╣
|
|
149
|
+
║ [S] Start (explore + generate tasks) ║
|
|
150
|
+
║ [V] View details [B] Back [X] Exit ║
|
|
151
|
+
╚═══════════════════════════════════════╝
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Then ask:
|
|
155
|
+
|
|
156
|
+
```
|
|
157
|
+
header: "Action"
|
|
158
|
+
question: "What would you like to do?"
|
|
159
|
+
options:
|
|
160
|
+
- label: "S - Start working"
|
|
161
|
+
description: "Run /create-task-list to analyze and generate tasks"
|
|
162
|
+
- label: "V - View details"
|
|
163
|
+
description: "Show full issue body"
|
|
164
|
+
- label: "B - Back"
|
|
165
|
+
description: "Return to issues list"
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Step 5: Handle "Start Working" - ALWAYS Run Full /create-task-list Process
|
|
169
|
+
|
|
170
|
+
**BOTH PATHS run the full `/create-task-list` workflow**, including:
|
|
171
|
+
- Agent-based codebase exploration
|
|
172
|
+
- Testing options (Ralph loop, E2E framework selection)
|
|
173
|
+
- Environment configuration from tech-stack.json
|
|
174
|
+
- Workflow options (branch, worktree, project board)
|
|
175
|
+
|
|
176
|
+
The only difference is whether we **generate** a new task list or **use** the existing one from the issue.
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
**For PROPERLY FORMATTED issues (S action):**
|
|
181
|
+
|
|
182
|
+
Run `/create-task-list for issue #[NUMBER] --use-existing-tasks`
|
|
183
|
+
|
|
184
|
+
This executes the full `/create-task-list` process but:
|
|
185
|
+
1. **Skips task generation** - uses the existing `## Task Checklist` from issue body
|
|
186
|
+
2. **Runs codebase exploration** - agents analyze relevant files for context
|
|
187
|
+
3. **Asks testing questions** - Ralph loop, E2E framework, test environment
|
|
188
|
+
4. **Asks workflow questions** - branch creation, worktree, project board sync
|
|
189
|
+
5. **Creates TodoWrite entries** from the issue's existing checklist
|
|
190
|
+
6. **Begins implementation** with full context
|
|
191
|
+
|
|
192
|
+
```
|
|
193
|
+
╔═══════════════════════════════════════════════════════════════╗
|
|
194
|
+
║ 📋 Starting Issue #[NUMBER] (Task-Ready) ║
|
|
195
|
+
╠═══════════════════════════════════════════════════════════════╣
|
|
196
|
+
║ Using existing task checklist from issue ║
|
|
197
|
+
║ Running full /create-task-list workflow for: ║
|
|
198
|
+
║ • Codebase exploration & context ║
|
|
199
|
+
║ • Testing configuration (Ralph loop, E2E) ║
|
|
200
|
+
║ • Workflow setup (branch, board sync) ║
|
|
201
|
+
╚═══════════════════════════════════════════════════════════════╝
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
**For NOT PROPERLY FORMATTED issues (S action):**
|
|
207
|
+
|
|
208
|
+
Run `/create-task-list for issue #[NUMBER]`
|
|
209
|
+
|
|
210
|
+
This executes the full `/create-task-list` process:
|
|
211
|
+
1. **Generates task list** - agents explore codebase and create tasks
|
|
212
|
+
2. **Runs codebase exploration** - deep analysis of relevant files
|
|
213
|
+
3. **Asks testing questions** - Ralph loop, E2E framework, test environment
|
|
214
|
+
4. **Asks workflow questions** - branch creation, worktree, project board sync
|
|
215
|
+
5. **Creates TodoWrite entries** from generated tasks
|
|
216
|
+
6. **Offers to update GitHub issue** with the generated task list
|
|
217
|
+
7. **Begins implementation** with full context
|
|
218
|
+
|
|
219
|
+
```
|
|
220
|
+
╔═══════════════════════════════════════════════════════════════╗
|
|
221
|
+
║ 📋 Starting Issue #[NUMBER] (Needs Analysis) ║
|
|
222
|
+
╠═══════════════════════════════════════════════════════════════╣
|
|
223
|
+
║ Running full /create-task-list workflow for: ║
|
|
224
|
+
║ • Task generation via codebase exploration ║
|
|
225
|
+
║ • Testing configuration (Ralph loop, E2E) ║
|
|
226
|
+
║ • Workflow setup (branch, board sync) ║
|
|
227
|
+
║ • Option to update issue with generated tasks ║
|
|
228
|
+
╚═══════════════════════════════════════════════════════════════╝
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
After task list is generated, ask:
|
|
232
|
+
|
|
233
|
+
```
|
|
234
|
+
header: "Update"
|
|
235
|
+
question: "Update GitHub issue with generated task list?"
|
|
236
|
+
options:
|
|
237
|
+
- label: "Y - Yes, update issue"
|
|
238
|
+
description: "Add task checklist to issue body"
|
|
239
|
+
- label: "N - No, just implement"
|
|
240
|
+
description: "Keep issue as-is, start work"
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
If user selects Y:
|
|
244
|
+
```bash
|
|
245
|
+
# Append task checklist to issue body
|
|
246
|
+
gh issue edit [NUMBER] --body "$(gh issue view [NUMBER] --json body -q .body)
|
|
247
|
+
|
|
248
|
+
## Task Checklist (Auto-generated)
|
|
249
|
+
|
|
250
|
+
- [ ] Task 1
|
|
251
|
+
- [ ] Task 2
|
|
252
|
+
..."
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
**Other Actions:**
|
|
256
|
+
- **V (View)**: Run `gh issue view [NUMBER]` and display full body
|
|
257
|
+
- **B (Back)**: Return to Step 2
|
|
258
|
+
- **R (Refresh)**: Re-fetch issues and display
|
|
259
|
+
- **X (Exit)**: End command
|
|
260
|
+
|
|
261
|
+
---
|
|
262
|
+
|
|
263
|
+
## MOBILE OPTIMIZATION
|
|
264
|
+
|
|
265
|
+
- Single character inputs (A, B, C, S, V, X)
|
|
266
|
+
- Compact display fits small screens
|
|
267
|
+
- No scrolling needed for main list
|
|
268
|
+
- Clear visual hierarchy with boxes
|
|
269
|
+
- Truncated titles prevent overflow
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
## ERROR HANDLING
|
|
274
|
+
|
|
275
|
+
| Error | Action |
|
|
276
|
+
|-------|--------|
|
|
277
|
+
| No issues found | Display "No open issues" message |
|
|
278
|
+
| gh not authenticated | Show `gh auth login` instructions |
|
|
279
|
+
| Network error | Show retry option |
|
|
280
|
+
|
|
281
|
+
---
|
|
282
|
+
|
|
283
|
+
## RELATED COMMANDS
|
|
284
|
+
|
|
285
|
+
- `/create-task-list-for-issue` - Start issue by number directly
|
|
286
|
+
- `/create-task-list` - Create task list from issue
|
|
287
|
+
- `/github-task-start` - Start working on issue
|
|
288
|
+
- `/github-update` - Sync with project board
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub Progress Hook
|
|
3
|
+
*
|
|
4
|
+
* Automatically updates GitHub issues as tasks are completed.
|
|
5
|
+
* Monitors TodoWrite calls and syncs progress to linked GitHub issues.
|
|
6
|
+
*
|
|
7
|
+
* Event: PostToolUse
|
|
8
|
+
* Matcher: TodoWrite
|
|
9
|
+
*
|
|
10
|
+
* This hook reads from stdin (Claude Code passes tool info there)
|
|
11
|
+
* and updates the linked GitHub issue with progress comments.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const { execSync } = require('child_process');
|
|
15
|
+
const fs = require('fs');
|
|
16
|
+
const path = require('path');
|
|
17
|
+
|
|
18
|
+
// Configuration - parse from tech-stack.json dynamically
|
|
19
|
+
function loadConfig() {
|
|
20
|
+
try {
|
|
21
|
+
const techStackPath = path.join(process.cwd(), '.claude', 'config', 'tech-stack.json');
|
|
22
|
+
if (fs.existsSync(techStackPath)) {
|
|
23
|
+
const techStack = JSON.parse(fs.readFileSync(techStackPath, 'utf8'));
|
|
24
|
+
const vc = techStack.versionControl || {};
|
|
25
|
+
const repo = vc.repository || '';
|
|
26
|
+
const [owner, repoName] = repo.includes('/') ? repo.split('/') : ['', ''];
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
owner: owner,
|
|
30
|
+
repo: repoName,
|
|
31
|
+
projectNumber: vc.projectBoard?.number || null,
|
|
32
|
+
enabled: !!vc.projectBoard?.type && !!owner && !!repoName,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
} catch (e) {
|
|
36
|
+
// Silent fail - config not available
|
|
37
|
+
}
|
|
38
|
+
return { owner: '', repo: '', projectNumber: null, enabled: false };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const PROGRESS_FILE = '.claude/hooks/cache/github-progress.json';
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Load progress tracking data
|
|
45
|
+
*/
|
|
46
|
+
function loadProgress() {
|
|
47
|
+
const progressPath = path.join(process.cwd(), PROGRESS_FILE);
|
|
48
|
+
|
|
49
|
+
if (fs.existsSync(progressPath)) {
|
|
50
|
+
try {
|
|
51
|
+
return JSON.parse(fs.readFileSync(progressPath, 'utf8'));
|
|
52
|
+
} catch (error) {
|
|
53
|
+
// Could not parse, return default
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
linkedIssue: null,
|
|
59
|
+
tasks: [],
|
|
60
|
+
completedTasks: [],
|
|
61
|
+
lastUpdate: null,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Save progress tracking data
|
|
67
|
+
*/
|
|
68
|
+
function saveProgress(progress) {
|
|
69
|
+
const progressPath = path.join(process.cwd(), PROGRESS_FILE);
|
|
70
|
+
const progressDir = path.dirname(progressPath);
|
|
71
|
+
|
|
72
|
+
if (!fs.existsSync(progressDir)) {
|
|
73
|
+
fs.mkdirSync(progressDir, { recursive: true });
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
progress.lastUpdate = new Date().toISOString();
|
|
77
|
+
fs.writeFileSync(progressPath, JSON.stringify(progress, null, 2), 'utf8');
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Check if gh CLI is available
|
|
82
|
+
*/
|
|
83
|
+
function hasGhCli() {
|
|
84
|
+
try {
|
|
85
|
+
execSync('gh --version', { stdio: 'ignore' });
|
|
86
|
+
return true;
|
|
87
|
+
} catch {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Update GitHub issue with progress comment
|
|
94
|
+
*/
|
|
95
|
+
function updateGitHubIssue(issueNumber, completedCount, totalCount, latestTask, config) {
|
|
96
|
+
if (!hasGhCli()) {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
try {
|
|
101
|
+
// Create progress comment
|
|
102
|
+
const percentage = totalCount > 0 ? Math.round((completedCount / totalCount) * 100) : 0;
|
|
103
|
+
const progressBar = '█'.repeat(Math.floor(percentage / 10)) + '░'.repeat(10 - Math.floor(percentage / 10));
|
|
104
|
+
|
|
105
|
+
const comment = `### Progress Update
|
|
106
|
+
|
|
107
|
+
${progressBar} ${percentage}% (${completedCount}/${totalCount} tasks)
|
|
108
|
+
|
|
109
|
+
**Latest completed:** ${latestTask || 'N/A'}
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
*Auto-updated by Claude Code github-progress-hook*`;
|
|
113
|
+
|
|
114
|
+
// Add comment to issue - use file to avoid escaping issues
|
|
115
|
+
const cacheDir = path.join(process.cwd(), '.claude', 'hooks', 'cache');
|
|
116
|
+
if (!fs.existsSync(cacheDir)) {
|
|
117
|
+
fs.mkdirSync(cacheDir, { recursive: true });
|
|
118
|
+
}
|
|
119
|
+
const tmpFile = path.join(cacheDir, 'tmp-comment.md');
|
|
120
|
+
fs.writeFileSync(tmpFile, comment, 'utf8');
|
|
121
|
+
|
|
122
|
+
execSync(
|
|
123
|
+
`gh issue comment ${issueNumber} --repo ${config.owner}/${config.repo} --body-file "${tmpFile}"`,
|
|
124
|
+
{ stdio: 'pipe' }
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
// Clean up temp file
|
|
128
|
+
try { fs.unlinkSync(tmpFile); } catch {}
|
|
129
|
+
|
|
130
|
+
console.log(`[github-progress] Updated issue #${issueNumber}: ${percentage}% complete`);
|
|
131
|
+
return true;
|
|
132
|
+
} catch (error) {
|
|
133
|
+
// Silent fail
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Extract linked issue number from todos
|
|
140
|
+
*/
|
|
141
|
+
function findLinkedIssue(todos, progress) {
|
|
142
|
+
// Check if issue is already linked
|
|
143
|
+
if (progress.linkedIssue) {
|
|
144
|
+
return progress.linkedIssue;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Look for issue reference in todo content (e.g., "Issue #11" or "#11")
|
|
148
|
+
for (const todo of todos) {
|
|
149
|
+
const content = todo.content || '';
|
|
150
|
+
const issueMatch = content.match(/(?:Issue\s*)?#(\d+)/i);
|
|
151
|
+
if (issueMatch) {
|
|
152
|
+
return parseInt(issueMatch[1], 10);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Main function - reads from stdin and processes TodoWrite
|
|
161
|
+
*/
|
|
162
|
+
async function main() {
|
|
163
|
+
const config = loadConfig();
|
|
164
|
+
|
|
165
|
+
// Skip if not configured
|
|
166
|
+
if (!config.enabled) {
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Read stdin to get tool information
|
|
171
|
+
let input = '';
|
|
172
|
+
try {
|
|
173
|
+
input = fs.readFileSync(0, 'utf8');
|
|
174
|
+
} catch {
|
|
175
|
+
// No stdin available
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
let toolData;
|
|
180
|
+
try {
|
|
181
|
+
toolData = JSON.parse(input);
|
|
182
|
+
} catch {
|
|
183
|
+
// Invalid JSON
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Check if this is a TodoWrite tool
|
|
188
|
+
const toolName = toolData.tool_name || toolData.name;
|
|
189
|
+
if (toolName !== 'TodoWrite') {
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Get todos from input
|
|
194
|
+
const todos = toolData.tool_input?.todos || toolData.input?.todos || [];
|
|
195
|
+
if (todos.length === 0) {
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Load progress tracking
|
|
200
|
+
const progress = loadProgress();
|
|
201
|
+
|
|
202
|
+
// Check for linked issue
|
|
203
|
+
const issueNumber = findLinkedIssue(todos, progress);
|
|
204
|
+
|
|
205
|
+
if (issueNumber && !progress.linkedIssue) {
|
|
206
|
+
progress.linkedIssue = issueNumber;
|
|
207
|
+
console.log(`[github-progress] Linked to issue #${issueNumber}`);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Skip if no linked issue
|
|
211
|
+
if (!progress.linkedIssue) {
|
|
212
|
+
saveProgress(progress);
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Count completed vs total tasks (excluding CONTEXT task)
|
|
217
|
+
const actualTodos = todos.filter(t => !t.content?.startsWith('CONTEXT:'));
|
|
218
|
+
const completedTodos = actualTodos.filter(t => t.status === 'completed');
|
|
219
|
+
const totalCount = actualTodos.length;
|
|
220
|
+
const completedCount = completedTodos.length;
|
|
221
|
+
|
|
222
|
+
// Find latest completed task
|
|
223
|
+
const latestCompleted = completedTodos[completedTodos.length - 1];
|
|
224
|
+
const latestTask = latestCompleted?.content || null;
|
|
225
|
+
|
|
226
|
+
// Check if progress changed (more tasks completed than before)
|
|
227
|
+
const previousCompleted = progress.completedTasks?.length || 0;
|
|
228
|
+
|
|
229
|
+
if (completedCount > previousCompleted) {
|
|
230
|
+
// Update GitHub issue
|
|
231
|
+
updateGitHubIssue(progress.linkedIssue, completedCount, totalCount, latestTask, config);
|
|
232
|
+
|
|
233
|
+
// Update progress tracking
|
|
234
|
+
progress.completedTasks = completedTodos.map(t => t.content);
|
|
235
|
+
progress.tasks = actualTodos.map(t => ({ content: t.content, status: t.status }));
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Save progress
|
|
239
|
+
saveProgress(progress);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Run the hook
|
|
243
|
+
main().catch(err => {
|
|
244
|
+
// Silently fail - don't break Claude Code
|
|
245
|
+
if (process.env.DEBUG) {
|
|
246
|
+
console.error('github-progress hook error:', err);
|
|
247
|
+
}
|
|
248
|
+
});
|