claude-autopm 1.24.2 โ†’ 1.26.0

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 CHANGED
@@ -328,6 +328,117 @@ claude --dangerously-skip-permissions .
328
328
 
329
329
  ---
330
330
 
331
+ ## ๐Ÿ”ง Advanced Tools
332
+
333
+ ### Epic Sync (JavaScript)
334
+
335
+ Complete epic synchronization workflow in one command:
336
+
337
+ ```bash
338
+ # Full epic sync (create epic + tasks + update references)
339
+ node .claude/lib/commands/pm/epicSync.js sync fullstack/01-infrastructure
340
+
341
+ # Individual operations
342
+ node .claude/lib/commands/pm/epicSync.js create-epic fullstack/01-infrastructure
343
+ node .claude/lib/commands/pm/epicSync.js create-tasks fullstack/01-infrastructure 2
344
+ node .claude/lib/commands/pm/epicSync.js update-epic fullstack/01-infrastructure 2
345
+ ```
346
+
347
+ **Features:**
348
+ - Creates GitHub epic issue with labels and stats
349
+ - Creates task issues for all tasks in epic
350
+ - Updates epic file with GitHub URLs
351
+ - Renames task files to match issue numbers
352
+ - Updates all cross-references automatically
353
+
354
+ ### Issue Sync (JavaScript)
355
+
356
+ Synchronize local development progress with GitHub issues:
357
+
358
+ ```bash
359
+ # Full sync workflow
360
+ node .claude/lib/commands/pm/issueSync.js sync 123 .claude/epics/auth/updates/123
361
+
362
+ # Mark task as complete
363
+ node .claude/lib/commands/pm/issueSync.js sync 456 ./updates --complete
364
+
365
+ # Dry run (preview without posting)
366
+ node .claude/lib/commands/pm/issueSync.js sync 789 ./updates --dry-run
367
+
368
+ # Individual operations
369
+ node .claude/lib/commands/pm/issueSync.js gather 123 ./updates
370
+ node .claude/lib/commands/pm/issueSync.js format 123 ./updates
371
+ ```
372
+
373
+ **Features:**
374
+ - Gathers updates from multiple sources (progress, notes, commits)
375
+ - Formats professional GitHub comments
376
+ - Posts updates to issues
377
+ - Updates frontmatter with sync timestamps
378
+ - Preflight validation (auth, issue exists, etc.)
379
+ - Supports completion workflow
380
+
381
+ **What gets synced:**
382
+ - Progress updates and completion %
383
+ - Technical notes and decisions
384
+ - Recent commits (auto-detected or manual)
385
+ - Acceptance criteria updates
386
+ - Next steps and blockers
387
+
388
+ ### Epic Status (JavaScript)
389
+
390
+ Track epic progress with detailed status reporting:
391
+
392
+ ```bash
393
+ # Show epic status
394
+ node .claude/lib/commands/pm/epicStatus.js fullstack/01-infrastructure
395
+
396
+ # List available epics
397
+ node .claude/lib/commands/pm/epicStatus.js
398
+ ```
399
+
400
+ **Features:**
401
+ - Counts tasks by status (completed/in-progress/pending)
402
+ - Calculates progress percentage
403
+ - Visual progress bar
404
+ - Sub-epic breakdown
405
+ - Comprehensive status reporting
406
+
407
+ **Example output:**
408
+ ```
409
+ Epic: fullstack/01-infrastructure
410
+ ==================================
411
+
412
+ Total tasks: 12
413
+ Completed: 8 (67%)
414
+ In Progress: 2
415
+ Pending: 2
416
+
417
+ Progress: [=================================-------------] 67%
418
+
419
+ Sub-Epic Breakdown:
420
+ -------------------
421
+ backend 6 tasks (4 completed)
422
+ frontend 4 tasks (3 completed)
423
+ infrastructure 2 tasks (1 completed)
424
+ ```
425
+
426
+ ### Why JavaScript Tools?
427
+
428
+ **Replaced 10 Bash scripts** (~2600 lines) with **3 JavaScript tools** (~1500 lines):
429
+
430
+ **Benefits:**
431
+ - โœ… Zero parsing errors (no heredoc/awk/sed complexity)
432
+ - ๐Ÿงช Fully testable (all functions exported)
433
+ - ๐Ÿ“– More readable and maintainable
434
+ - ๐Ÿš€ 50% less code
435
+ - ๐Ÿ’พ Better error handling
436
+ - ๐Ÿ” Easier debugging
437
+
438
+ **Backward compatible:** Old Bash scripts still work, but new JS tools are recommended.
439
+
440
+ ---
441
+
331
442
  ## ๐Ÿค Contributing
332
443
 
333
444
  We welcome contributions! See [CONTRIBUTING.md](docs/development/contributing.md) for:
@@ -0,0 +1,263 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Epic Status - Complete epic progress tracking
4
+ *
5
+ * Replaces epic-status.sh with clean, testable JavaScript
6
+ * - Counts tasks by status (completed/in-progress/pending)
7
+ * - Calculates progress percentage
8
+ * - Shows progress bar visualization
9
+ * - Provides sub-epic breakdown
10
+ */
11
+
12
+ const fs = require('fs');
13
+ const path = require('path');
14
+
15
+ /**
16
+ * Parse frontmatter from markdown file
17
+ */
18
+ function parseFrontmatter(filePath) {
19
+ try {
20
+ const content = fs.readFileSync(filePath, 'utf8');
21
+ const lines = content.split('\n');
22
+
23
+ let inFrontmatter = false;
24
+ let frontmatterCount = 0;
25
+ const frontmatter = {};
26
+
27
+ for (const line of lines) {
28
+ if (line === '---') {
29
+ frontmatterCount++;
30
+ if (frontmatterCount === 1) {
31
+ inFrontmatter = true;
32
+ continue;
33
+ } else if (frontmatterCount === 2) {
34
+ break;
35
+ }
36
+ }
37
+
38
+ if (inFrontmatter) {
39
+ const match = line.match(/^(\w+):\s*(.+)$/);
40
+ if (match) {
41
+ const [, key, value] = match;
42
+ frontmatter[key] = value;
43
+ }
44
+ }
45
+ }
46
+
47
+ return frontmatter;
48
+ } catch (error) {
49
+ return {};
50
+ }
51
+ }
52
+
53
+ /**
54
+ * Find all task files in directory
55
+ */
56
+ function findTaskFiles(dir, maxDepth = 2, currentDepth = 0) {
57
+ if (!fs.existsSync(dir) || !fs.statSync(dir).isDirectory()) {
58
+ return [];
59
+ }
60
+
61
+ if (currentDepth >= maxDepth) {
62
+ return [];
63
+ }
64
+
65
+ const files = [];
66
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
67
+
68
+ for (const entry of entries) {
69
+ const fullPath = path.join(dir, entry.name);
70
+
71
+ if (entry.isFile() && /^\d+\.md$/.test(entry.name)) {
72
+ files.push(fullPath);
73
+ } else if (entry.isDirectory() && currentDepth < maxDepth - 1) {
74
+ files.push(...findTaskFiles(fullPath, maxDepth, currentDepth + 1));
75
+ }
76
+ }
77
+
78
+ return files;
79
+ }
80
+
81
+ /**
82
+ * Count tasks by status
83
+ */
84
+ function countTasksByStatus(taskFiles) {
85
+ const counts = {
86
+ completed: 0,
87
+ in_progress: 0,
88
+ pending: 0,
89
+ total: taskFiles.length
90
+ };
91
+
92
+ for (const taskFile of taskFiles) {
93
+ const frontmatter = parseFrontmatter(taskFile);
94
+ const status = frontmatter.status || '';
95
+
96
+ if (status === 'completed') {
97
+ counts.completed++;
98
+ } else if (status === 'in-progress' || status === 'in_progress') {
99
+ counts.in_progress++;
100
+ } else {
101
+ counts.pending++;
102
+ }
103
+ }
104
+
105
+ return counts;
106
+ }
107
+
108
+ /**
109
+ * Generate progress bar
110
+ */
111
+ function generateProgressBar(percentage, length = 50) {
112
+ const filled = Math.round((percentage * length) / 100);
113
+ const empty = length - filled;
114
+
115
+ const bar = '='.repeat(filled) + '-'.repeat(empty);
116
+ return `[${bar}] ${percentage}%`;
117
+ }
118
+
119
+ /**
120
+ * Get sub-epic breakdown
121
+ */
122
+ function getSubEpicBreakdown(epicDir) {
123
+ const breakdown = [];
124
+
125
+ if (!fs.existsSync(epicDir)) {
126
+ return breakdown;
127
+ }
128
+
129
+ const entries = fs.readdirSync(epicDir, { withFileTypes: true });
130
+
131
+ for (const entry of entries) {
132
+ if (entry.isDirectory()) {
133
+ const subDir = path.join(epicDir, entry.name);
134
+ const taskFiles = findTaskFiles(subDir, 1);
135
+
136
+ if (taskFiles.length > 0) {
137
+ const counts = countTasksByStatus(taskFiles);
138
+ breakdown.push({
139
+ name: entry.name,
140
+ total: counts.total,
141
+ completed: counts.completed
142
+ });
143
+ }
144
+ }
145
+ }
146
+
147
+ return breakdown;
148
+ }
149
+
150
+ /**
151
+ * Format epic status report
152
+ */
153
+ function formatEpicStatus(epicName, epicDir) {
154
+ // Find all tasks
155
+ const taskFiles = findTaskFiles(epicDir);
156
+ const counts = countTasksByStatus(taskFiles);
157
+
158
+ // Calculate progress
159
+ const progress = counts.total > 0
160
+ ? Math.round((counts.completed * 100) / counts.total)
161
+ : 0;
162
+
163
+ // Build report
164
+ const lines = [];
165
+ lines.push(`Epic: ${epicName}`);
166
+ lines.push('='.repeat(20 + epicName.length));
167
+ lines.push('');
168
+ lines.push(`Total tasks: ${counts.total}`);
169
+ lines.push(`Completed: ${counts.completed} (${progress}%)`);
170
+ lines.push(`In Progress: ${counts.in_progress}`);
171
+ lines.push(`Pending: ${counts.pending}`);
172
+ lines.push('');
173
+
174
+ // Progress bar
175
+ if (counts.total > 0) {
176
+ lines.push(`Progress: ${generateProgressBar(progress)}`);
177
+ lines.push('');
178
+ }
179
+
180
+ // Sub-epic breakdown
181
+ const breakdown = getSubEpicBreakdown(epicDir);
182
+ if (breakdown.length > 0) {
183
+ lines.push('Sub-Epic Breakdown:');
184
+ lines.push('-'.repeat(19));
185
+
186
+ for (const sub of breakdown) {
187
+ const name = sub.name.padEnd(30);
188
+ lines.push(` ${name} ${sub.total.toString().padStart(3)} tasks (${sub.completed} completed)`);
189
+ }
190
+ }
191
+
192
+ return lines.join('\n');
193
+ }
194
+
195
+ /**
196
+ * List available epics
197
+ */
198
+ function listAvailableEpics(epicsDir) {
199
+ if (!fs.existsSync(epicsDir)) {
200
+ return [];
201
+ }
202
+
203
+ const entries = fs.readdirSync(epicsDir, { withFileTypes: true });
204
+ return entries
205
+ .filter(entry => entry.isDirectory())
206
+ .map(entry => entry.name);
207
+ }
208
+
209
+ /**
210
+ * Main function
211
+ */
212
+ function main() {
213
+ const args = process.argv.slice(2);
214
+ const epicName = args[0];
215
+
216
+ const epicsDir = path.join(process.cwd(), '.claude/epics');
217
+
218
+ if (!epicName) {
219
+ console.log('Usage: epicStatus.js <epic-name>');
220
+ console.log('');
221
+ console.log('Available epics:');
222
+
223
+ const epics = listAvailableEpics(epicsDir);
224
+ if (epics.length > 0) {
225
+ epics.forEach(epic => console.log(` ${epic}`));
226
+ } else {
227
+ console.log(' No epics found');
228
+ }
229
+
230
+ process.exit(1);
231
+ }
232
+
233
+ const epicDir = path.join(epicsDir, epicName);
234
+
235
+ if (!fs.existsSync(epicDir)) {
236
+ console.error(`Error: Epic '${epicName}' not found`);
237
+ console.log('');
238
+ console.log('Available epics:');
239
+
240
+ const epics = listAvailableEpics(epicsDir);
241
+ epics.forEach(epic => console.log(` ${epic}`));
242
+
243
+ process.exit(1);
244
+ }
245
+
246
+ // Generate and display status
247
+ const status = formatEpicStatus(epicName, epicDir);
248
+ console.log(status);
249
+ }
250
+
251
+ if (require.main === module) {
252
+ main();
253
+ }
254
+
255
+ module.exports = {
256
+ parseFrontmatter,
257
+ findTaskFiles,
258
+ countTasksByStatus,
259
+ generateProgressBar,
260
+ getSubEpicBreakdown,
261
+ formatEpicStatus,
262
+ listAvailableEpics
263
+ };