claude-autopm 2.7.0 ā 2.8.2
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 +307 -56
- package/autopm/.claude/.env +158 -0
- package/autopm/.claude/settings.local.json +9 -0
- package/bin/autopm.js +11 -2
- package/bin/commands/epic.js +23 -3
- package/bin/commands/plugin.js +395 -0
- package/bin/commands/team.js +184 -10
- package/install/install.js +223 -4
- package/lib/cli/commands/issue.js +360 -20
- package/lib/plugins/PluginManager.js +1328 -0
- package/lib/plugins/PluginManager.old.js +400 -0
- package/lib/providers/AzureDevOpsProvider.js +575 -0
- package/lib/providers/GitHubProvider.js +475 -0
- package/lib/services/EpicService.js +1092 -3
- package/lib/services/IssueService.js +991 -0
- package/package.json +9 -1
- package/scripts/publish-plugins.sh +166 -0
- package/autopm/.claude/agents/cloud/README.md +0 -55
- package/autopm/.claude/agents/cloud/aws-cloud-architect.md +0 -521
- package/autopm/.claude/agents/cloud/azure-cloud-architect.md +0 -436
- package/autopm/.claude/agents/cloud/gcp-cloud-architect.md +0 -385
- package/autopm/.claude/agents/cloud/gcp-cloud-functions-engineer.md +0 -306
- package/autopm/.claude/agents/cloud/gemini-api-expert.md +0 -880
- package/autopm/.claude/agents/cloud/kubernetes-orchestrator.md +0 -566
- package/autopm/.claude/agents/cloud/openai-python-expert.md +0 -1087
- package/autopm/.claude/agents/cloud/terraform-infrastructure-expert.md +0 -454
- package/autopm/.claude/agents/core/agent-manager.md +0 -296
- package/autopm/.claude/agents/core/code-analyzer.md +0 -131
- package/autopm/.claude/agents/core/file-analyzer.md +0 -162
- package/autopm/.claude/agents/core/test-runner.md +0 -200
- package/autopm/.claude/agents/data/airflow-orchestration-expert.md +0 -52
- package/autopm/.claude/agents/data/kedro-pipeline-expert.md +0 -50
- package/autopm/.claude/agents/data/langgraph-workflow-expert.md +0 -520
- package/autopm/.claude/agents/databases/README.md +0 -50
- package/autopm/.claude/agents/databases/bigquery-expert.md +0 -392
- package/autopm/.claude/agents/databases/cosmosdb-expert.md +0 -368
- package/autopm/.claude/agents/databases/mongodb-expert.md +0 -398
- package/autopm/.claude/agents/databases/postgresql-expert.md +0 -321
- package/autopm/.claude/agents/databases/redis-expert.md +0 -52
- package/autopm/.claude/agents/devops/README.md +0 -52
- package/autopm/.claude/agents/devops/azure-devops-specialist.md +0 -308
- package/autopm/.claude/agents/devops/docker-containerization-expert.md +0 -298
- package/autopm/.claude/agents/devops/github-operations-specialist.md +0 -335
- package/autopm/.claude/agents/devops/mcp-context-manager.md +0 -319
- package/autopm/.claude/agents/devops/observability-engineer.md +0 -574
- package/autopm/.claude/agents/devops/ssh-operations-expert.md +0 -1093
- package/autopm/.claude/agents/devops/traefik-proxy-expert.md +0 -444
- package/autopm/.claude/agents/frameworks/README.md +0 -64
- package/autopm/.claude/agents/frameworks/e2e-test-engineer.md +0 -360
- package/autopm/.claude/agents/frameworks/nats-messaging-expert.md +0 -254
- package/autopm/.claude/agents/frameworks/react-frontend-engineer.md +0 -217
- package/autopm/.claude/agents/frameworks/react-ui-expert.md +0 -226
- package/autopm/.claude/agents/frameworks/tailwindcss-expert.md +0 -770
- package/autopm/.claude/agents/frameworks/ux-design-expert.md +0 -244
- package/autopm/.claude/agents/integration/message-queue-engineer.md +0 -794
- package/autopm/.claude/agents/languages/README.md +0 -50
- package/autopm/.claude/agents/languages/bash-scripting-expert.md +0 -541
- package/autopm/.claude/agents/languages/javascript-frontend-engineer.md +0 -197
- package/autopm/.claude/agents/languages/nodejs-backend-engineer.md +0 -226
- package/autopm/.claude/agents/languages/python-backend-engineer.md +0 -214
- package/autopm/.claude/agents/languages/python-backend-expert.md +0 -289
- package/autopm/.claude/agents/testing/frontend-testing-engineer.md +0 -395
- package/autopm/.claude/commands/ai/langgraph-workflow.md +0 -65
- package/autopm/.claude/commands/ai/openai-chat.md +0 -65
- package/autopm/.claude/commands/azure/COMMANDS.md +0 -107
- package/autopm/.claude/commands/azure/COMMAND_MAPPING.md +0 -252
- package/autopm/.claude/commands/azure/INTEGRATION_FIX.md +0 -103
- package/autopm/.claude/commands/azure/README.md +0 -246
- package/autopm/.claude/commands/azure/active-work.md +0 -198
- package/autopm/.claude/commands/azure/aliases.md +0 -143
- package/autopm/.claude/commands/azure/blocked-items.md +0 -287
- package/autopm/.claude/commands/azure/clean.md +0 -93
- package/autopm/.claude/commands/azure/docs-query.md +0 -48
- package/autopm/.claude/commands/azure/feature-decompose.md +0 -380
- package/autopm/.claude/commands/azure/feature-list.md +0 -61
- package/autopm/.claude/commands/azure/feature-new.md +0 -115
- package/autopm/.claude/commands/azure/feature-show.md +0 -205
- package/autopm/.claude/commands/azure/feature-start.md +0 -130
- package/autopm/.claude/commands/azure/fix-integration-example.md +0 -93
- package/autopm/.claude/commands/azure/help.md +0 -150
- package/autopm/.claude/commands/azure/import-us.md +0 -269
- package/autopm/.claude/commands/azure/init.md +0 -211
- package/autopm/.claude/commands/azure/next-task.md +0 -262
- package/autopm/.claude/commands/azure/search.md +0 -160
- package/autopm/.claude/commands/azure/sprint-status.md +0 -235
- package/autopm/.claude/commands/azure/standup.md +0 -260
- package/autopm/.claude/commands/azure/sync-all.md +0 -99
- package/autopm/.claude/commands/azure/task-analyze.md +0 -186
- package/autopm/.claude/commands/azure/task-close.md +0 -329
- package/autopm/.claude/commands/azure/task-edit.md +0 -145
- package/autopm/.claude/commands/azure/task-list.md +0 -263
- package/autopm/.claude/commands/azure/task-new.md +0 -84
- package/autopm/.claude/commands/azure/task-reopen.md +0 -79
- package/autopm/.claude/commands/azure/task-show.md +0 -126
- package/autopm/.claude/commands/azure/task-start.md +0 -301
- package/autopm/.claude/commands/azure/task-status.md +0 -65
- package/autopm/.claude/commands/azure/task-sync.md +0 -67
- package/autopm/.claude/commands/azure/us-edit.md +0 -164
- package/autopm/.claude/commands/azure/us-list.md +0 -202
- package/autopm/.claude/commands/azure/us-new.md +0 -265
- package/autopm/.claude/commands/azure/us-parse.md +0 -253
- package/autopm/.claude/commands/azure/us-show.md +0 -188
- package/autopm/.claude/commands/azure/us-status.md +0 -320
- package/autopm/.claude/commands/azure/validate.md +0 -86
- package/autopm/.claude/commands/azure/work-item-sync.md +0 -47
- package/autopm/.claude/commands/cloud/infra-deploy.md +0 -38
- package/autopm/.claude/commands/github/workflow-create.md +0 -42
- package/autopm/.claude/commands/infrastructure/ssh-security.md +0 -65
- package/autopm/.claude/commands/infrastructure/traefik-setup.md +0 -65
- package/autopm/.claude/commands/kubernetes/deploy.md +0 -37
- package/autopm/.claude/commands/playwright/test-scaffold.md +0 -38
- package/autopm/.claude/commands/pm/blocked.md +0 -28
- package/autopm/.claude/commands/pm/clean.md +0 -119
- package/autopm/.claude/commands/pm/context-create.md +0 -136
- package/autopm/.claude/commands/pm/context-prime.md +0 -170
- package/autopm/.claude/commands/pm/context-update.md +0 -292
- package/autopm/.claude/commands/pm/context.md +0 -28
- package/autopm/.claude/commands/pm/epic-close.md +0 -86
- package/autopm/.claude/commands/pm/epic-decompose.md +0 -370
- package/autopm/.claude/commands/pm/epic-edit.md +0 -83
- package/autopm/.claude/commands/pm/epic-list.md +0 -30
- package/autopm/.claude/commands/pm/epic-merge.md +0 -222
- package/autopm/.claude/commands/pm/epic-oneshot.md +0 -119
- package/autopm/.claude/commands/pm/epic-refresh.md +0 -119
- package/autopm/.claude/commands/pm/epic-show.md +0 -28
- package/autopm/.claude/commands/pm/epic-split.md +0 -120
- package/autopm/.claude/commands/pm/epic-start.md +0 -195
- package/autopm/.claude/commands/pm/epic-status.md +0 -28
- package/autopm/.claude/commands/pm/epic-sync-modular.md +0 -338
- package/autopm/.claude/commands/pm/epic-sync-original.md +0 -473
- package/autopm/.claude/commands/pm/epic-sync.md +0 -486
- package/autopm/.claude/commands/pm/help.md +0 -28
- package/autopm/.claude/commands/pm/import.md +0 -115
- package/autopm/.claude/commands/pm/in-progress.md +0 -28
- package/autopm/.claude/commands/pm/init.md +0 -28
- package/autopm/.claude/commands/pm/issue-analyze.md +0 -202
- package/autopm/.claude/commands/pm/issue-close.md +0 -119
- package/autopm/.claude/commands/pm/issue-edit.md +0 -93
- package/autopm/.claude/commands/pm/issue-reopen.md +0 -87
- package/autopm/.claude/commands/pm/issue-show.md +0 -41
- package/autopm/.claude/commands/pm/issue-start.md +0 -234
- package/autopm/.claude/commands/pm/issue-status.md +0 -95
- package/autopm/.claude/commands/pm/issue-sync.md +0 -411
- package/autopm/.claude/commands/pm/next.md +0 -28
- package/autopm/.claude/commands/pm/prd-edit.md +0 -82
- package/autopm/.claude/commands/pm/prd-list.md +0 -28
- package/autopm/.claude/commands/pm/prd-new.md +0 -55
- package/autopm/.claude/commands/pm/prd-parse.md +0 -42
- package/autopm/.claude/commands/pm/prd-status.md +0 -28
- package/autopm/.claude/commands/pm/search.md +0 -28
- package/autopm/.claude/commands/pm/standup.md +0 -28
- package/autopm/.claude/commands/pm/status.md +0 -28
- package/autopm/.claude/commands/pm/sync.md +0 -99
- package/autopm/.claude/commands/pm/test-reference-update.md +0 -151
- package/autopm/.claude/commands/pm/validate.md +0 -28
- package/autopm/.claude/commands/pm/what-next.md +0 -28
- package/autopm/.claude/commands/python/api-scaffold.md +0 -50
- package/autopm/.claude/commands/python/docs-query.md +0 -48
- package/autopm/.claude/commands/react/app-scaffold.md +0 -50
- package/autopm/.claude/commands/testing/prime.md +0 -314
- package/autopm/.claude/commands/testing/run.md +0 -125
- package/autopm/.claude/commands/ui/bootstrap-scaffold.md +0 -65
- package/autopm/.claude/commands/ui/tailwind-system.md +0 -64
- package/autopm/.claude/rules/ai-integration-patterns.md +0 -219
- package/autopm/.claude/rules/ci-cd-kubernetes-strategy.md +0 -25
- package/autopm/.claude/rules/database-management-strategy.md +0 -17
- package/autopm/.claude/rules/database-pipeline.md +0 -94
- package/autopm/.claude/rules/devops-troubleshooting-playbook.md +0 -450
- package/autopm/.claude/rules/docker-first-development.md +0 -404
- package/autopm/.claude/rules/infrastructure-pipeline.md +0 -128
- package/autopm/.claude/rules/performance-guidelines.md +0 -403
- package/autopm/.claude/rules/ui-development-standards.md +0 -281
- package/autopm/.claude/rules/ui-framework-rules.md +0 -151
- package/autopm/.claude/rules/ux-design-rules.md +0 -209
- package/autopm/.claude/rules/visual-testing.md +0 -223
- package/autopm/.claude/scripts/azure/README.md +0 -192
- package/autopm/.claude/scripts/azure/active-work.js +0 -524
- package/autopm/.claude/scripts/azure/active-work.sh +0 -20
- package/autopm/.claude/scripts/azure/blocked.js +0 -520
- package/autopm/.claude/scripts/azure/blocked.sh +0 -20
- package/autopm/.claude/scripts/azure/daily.js +0 -533
- package/autopm/.claude/scripts/azure/daily.sh +0 -20
- package/autopm/.claude/scripts/azure/dashboard.js +0 -970
- package/autopm/.claude/scripts/azure/dashboard.sh +0 -20
- package/autopm/.claude/scripts/azure/feature-list.js +0 -254
- package/autopm/.claude/scripts/azure/feature-list.sh +0 -20
- package/autopm/.claude/scripts/azure/feature-show.js +0 -7
- package/autopm/.claude/scripts/azure/feature-show.sh +0 -20
- package/autopm/.claude/scripts/azure/feature-status.js +0 -604
- package/autopm/.claude/scripts/azure/feature-status.sh +0 -20
- package/autopm/.claude/scripts/azure/help.js +0 -342
- package/autopm/.claude/scripts/azure/help.sh +0 -20
- package/autopm/.claude/scripts/azure/next-task.js +0 -508
- package/autopm/.claude/scripts/azure/next-task.sh +0 -20
- package/autopm/.claude/scripts/azure/search.js +0 -469
- package/autopm/.claude/scripts/azure/search.sh +0 -20
- package/autopm/.claude/scripts/azure/setup.js +0 -745
- package/autopm/.claude/scripts/azure/setup.sh +0 -20
- package/autopm/.claude/scripts/azure/sprint-report.js +0 -1012
- package/autopm/.claude/scripts/azure/sprint-report.sh +0 -20
- package/autopm/.claude/scripts/azure/sync.js +0 -563
- package/autopm/.claude/scripts/azure/sync.sh +0 -20
- package/autopm/.claude/scripts/azure/us-list.js +0 -210
- package/autopm/.claude/scripts/azure/us-list.sh +0 -20
- package/autopm/.claude/scripts/azure/us-status.js +0 -238
- package/autopm/.claude/scripts/azure/us-status.sh +0 -20
- package/autopm/.claude/scripts/azure/validate.js +0 -626
- package/autopm/.claude/scripts/azure/validate.sh +0 -20
- package/autopm/.claude/scripts/azure/wrapper-template.sh +0 -20
- package/autopm/.claude/scripts/github/dependency-tracker.js +0 -554
- package/autopm/.claude/scripts/github/dependency-validator.js +0 -545
- package/autopm/.claude/scripts/github/dependency-visualizer.js +0 -477
- package/autopm/.claude/scripts/pm/analytics.js +0 -425
- package/autopm/.claude/scripts/pm/blocked.js +0 -164
- package/autopm/.claude/scripts/pm/blocked.sh +0 -78
- package/autopm/.claude/scripts/pm/clean.js +0 -464
- package/autopm/.claude/scripts/pm/context-create.js +0 -216
- package/autopm/.claude/scripts/pm/context-prime.js +0 -335
- package/autopm/.claude/scripts/pm/context-update.js +0 -344
- package/autopm/.claude/scripts/pm/context.js +0 -338
- package/autopm/.claude/scripts/pm/epic-close.js +0 -347
- package/autopm/.claude/scripts/pm/epic-edit.js +0 -382
- package/autopm/.claude/scripts/pm/epic-list.js +0 -273
- package/autopm/.claude/scripts/pm/epic-list.sh +0 -109
- package/autopm/.claude/scripts/pm/epic-show.js +0 -291
- package/autopm/.claude/scripts/pm/epic-show.sh +0 -105
- package/autopm/.claude/scripts/pm/epic-split.js +0 -522
- package/autopm/.claude/scripts/pm/epic-start/epic-start.js +0 -183
- package/autopm/.claude/scripts/pm/epic-start/epic-start.sh +0 -94
- package/autopm/.claude/scripts/pm/epic-status.js +0 -291
- package/autopm/.claude/scripts/pm/epic-status.sh +0 -104
- package/autopm/.claude/scripts/pm/epic-sync/README.md +0 -208
- package/autopm/.claude/scripts/pm/epic-sync/create-epic-issue.sh +0 -77
- package/autopm/.claude/scripts/pm/epic-sync/create-task-issues.sh +0 -86
- package/autopm/.claude/scripts/pm/epic-sync/update-epic-file.sh +0 -79
- package/autopm/.claude/scripts/pm/epic-sync/update-references.sh +0 -89
- package/autopm/.claude/scripts/pm/epic-sync.sh +0 -137
- package/autopm/.claude/scripts/pm/help.js +0 -92
- package/autopm/.claude/scripts/pm/help.sh +0 -90
- package/autopm/.claude/scripts/pm/in-progress.js +0 -178
- package/autopm/.claude/scripts/pm/in-progress.sh +0 -93
- package/autopm/.claude/scripts/pm/init.js +0 -321
- package/autopm/.claude/scripts/pm/init.sh +0 -178
- package/autopm/.claude/scripts/pm/issue-close.js +0 -232
- package/autopm/.claude/scripts/pm/issue-edit.js +0 -310
- package/autopm/.claude/scripts/pm/issue-show.js +0 -272
- package/autopm/.claude/scripts/pm/issue-start.js +0 -181
- package/autopm/.claude/scripts/pm/issue-sync/format-comment.sh +0 -468
- package/autopm/.claude/scripts/pm/issue-sync/gather-updates.sh +0 -460
- package/autopm/.claude/scripts/pm/issue-sync/post-comment.sh +0 -330
- package/autopm/.claude/scripts/pm/issue-sync/preflight-validation.sh +0 -348
- package/autopm/.claude/scripts/pm/issue-sync/update-frontmatter.sh +0 -387
- package/autopm/.claude/scripts/pm/lib/README.md +0 -85
- package/autopm/.claude/scripts/pm/lib/epic-discovery.js +0 -119
- package/autopm/.claude/scripts/pm/lib/logger.js +0 -78
- package/autopm/.claude/scripts/pm/next.js +0 -189
- package/autopm/.claude/scripts/pm/next.sh +0 -72
- package/autopm/.claude/scripts/pm/optimize.js +0 -407
- package/autopm/.claude/scripts/pm/pr-create.js +0 -337
- package/autopm/.claude/scripts/pm/pr-list.js +0 -257
- package/autopm/.claude/scripts/pm/prd-list.js +0 -242
- package/autopm/.claude/scripts/pm/prd-list.sh +0 -103
- package/autopm/.claude/scripts/pm/prd-new.js +0 -684
- package/autopm/.claude/scripts/pm/prd-parse.js +0 -547
- package/autopm/.claude/scripts/pm/prd-status.js +0 -152
- package/autopm/.claude/scripts/pm/prd-status.sh +0 -63
- package/autopm/.claude/scripts/pm/release.js +0 -460
- package/autopm/.claude/scripts/pm/search.js +0 -192
- package/autopm/.claude/scripts/pm/search.sh +0 -89
- package/autopm/.claude/scripts/pm/standup.js +0 -362
- package/autopm/.claude/scripts/pm/standup.sh +0 -95
- package/autopm/.claude/scripts/pm/status.js +0 -148
- package/autopm/.claude/scripts/pm/status.sh +0 -59
- package/autopm/.claude/scripts/pm/sync-batch.js +0 -337
- package/autopm/.claude/scripts/pm/sync.js +0 -343
- package/autopm/.claude/scripts/pm/template-list.js +0 -141
- package/autopm/.claude/scripts/pm/template-new.js +0 -366
- package/autopm/.claude/scripts/pm/validate.js +0 -274
- package/autopm/.claude/scripts/pm/validate.sh +0 -106
- package/autopm/.claude/scripts/pm/what-next.js +0 -660
- package/bin/node/azure-feature-show.js +0 -7
|
@@ -1,382 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Epic Edit - Edit epic details
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
const fs = require('fs');
|
|
7
|
-
const path = require('path');
|
|
8
|
-
const readline = require('readline');
|
|
9
|
-
|
|
10
|
-
class EpicEditor {
|
|
11
|
-
constructor() {
|
|
12
|
-
this.epicsDir = path.join('.claude', 'epics');
|
|
13
|
-
this.prdsDir = path.join('.claude', 'prds');
|
|
14
|
-
this.activeWorkFile = path.join('.claude', 'active-work.json');
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
findEpicFile(epicName) {
|
|
18
|
-
// Check epics directory
|
|
19
|
-
let epicPath = path.join(this.epicsDir, `${epicName}.md`);
|
|
20
|
-
if (fs.existsSync(epicPath)) {
|
|
21
|
-
return epicPath;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// Check PRDs directory
|
|
25
|
-
epicPath = path.join(this.prdsDir, `${epicName}.md`);
|
|
26
|
-
if (fs.existsSync(epicPath)) {
|
|
27
|
-
return epicPath;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
return null;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
async editEpic(epicName) {
|
|
34
|
-
console.log(`\nš Editing Epic: ${epicName}`);
|
|
35
|
-
console.log(`${'ā'.repeat(50)}\n`);
|
|
36
|
-
|
|
37
|
-
// Find epic file
|
|
38
|
-
const epicFile = this.findEpicFile(epicName);
|
|
39
|
-
if (!epicFile) {
|
|
40
|
-
console.error(`ā Epic not found: ${epicName}`);
|
|
41
|
-
console.log('\nš” Available epics:');
|
|
42
|
-
this.listAvailableEpics();
|
|
43
|
-
return false;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// Read current content
|
|
47
|
-
const currentContent = fs.readFileSync(epicFile, 'utf8');
|
|
48
|
-
console.log('Current epic content:');
|
|
49
|
-
console.log(`${'ā'.repeat(50)}`);
|
|
50
|
-
console.log(currentContent.substring(0, 500));
|
|
51
|
-
if (currentContent.length > 500) {
|
|
52
|
-
console.log('... (truncated)');
|
|
53
|
-
}
|
|
54
|
-
console.log(`${'ā'.repeat(50)}\n`);
|
|
55
|
-
|
|
56
|
-
// Create interface for editing
|
|
57
|
-
const rl = readline.createInterface({
|
|
58
|
-
input: process.stdin,
|
|
59
|
-
output: process.stdout
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
const prompt = (question) => new Promise((resolve) => {
|
|
63
|
-
rl.question(question, resolve);
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
try {
|
|
67
|
-
// Edit menu
|
|
68
|
-
console.log('What would you like to edit?');
|
|
69
|
-
console.log(' 1. Title');
|
|
70
|
-
console.log(' 2. Description');
|
|
71
|
-
console.log(' 3. Add milestone');
|
|
72
|
-
console.log(' 4. Mark milestone complete');
|
|
73
|
-
console.log(' 5. Add technical requirement');
|
|
74
|
-
console.log(' 6. Update metadata');
|
|
75
|
-
console.log(' 7. Open in editor');
|
|
76
|
-
console.log(' 0. Cancel');
|
|
77
|
-
|
|
78
|
-
const choice = await prompt('\nEnter choice (0-7): ');
|
|
79
|
-
|
|
80
|
-
switch (choice) {
|
|
81
|
-
case '1':
|
|
82
|
-
await this.editTitle(epicFile, currentContent, rl, prompt);
|
|
83
|
-
break;
|
|
84
|
-
case '2':
|
|
85
|
-
await this.editDescription(epicFile, currentContent, rl, prompt);
|
|
86
|
-
break;
|
|
87
|
-
case '3':
|
|
88
|
-
await this.addMilestone(epicFile, currentContent, rl, prompt);
|
|
89
|
-
break;
|
|
90
|
-
case '4':
|
|
91
|
-
await this.markMilestoneComplete(epicFile, currentContent, rl, prompt);
|
|
92
|
-
break;
|
|
93
|
-
case '5':
|
|
94
|
-
await this.addTechnicalRequirement(epicFile, currentContent, rl, prompt);
|
|
95
|
-
break;
|
|
96
|
-
case '6':
|
|
97
|
-
await this.updateMetadata(epicFile, currentContent, rl, prompt);
|
|
98
|
-
break;
|
|
99
|
-
case '7':
|
|
100
|
-
await this.openInEditor(epicFile);
|
|
101
|
-
break;
|
|
102
|
-
case '0':
|
|
103
|
-
console.log('Edit cancelled.');
|
|
104
|
-
break;
|
|
105
|
-
default:
|
|
106
|
-
console.log('Invalid choice.');
|
|
107
|
-
}
|
|
108
|
-
} finally {
|
|
109
|
-
rl.close();
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// Show updated content
|
|
113
|
-
if (choice !== '0' && choice !== '7') {
|
|
114
|
-
const updatedContent = fs.readFileSync(epicFile, 'utf8');
|
|
115
|
-
console.log('\nā
Epic updated successfully!');
|
|
116
|
-
console.log('\nUpdated content preview:');
|
|
117
|
-
console.log(`${'ā'.repeat(50)}`);
|
|
118
|
-
console.log(updatedContent.substring(0, 300));
|
|
119
|
-
if (updatedContent.length > 300) {
|
|
120
|
-
console.log('... (truncated)');
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
return true;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
async editTitle(epicFile, content, rl, prompt) {
|
|
128
|
-
const newTitle = await prompt('Enter new title: ');
|
|
129
|
-
if (newTitle) {
|
|
130
|
-
const updated = content.replace(/^# .+$/m, `# ${newTitle}`);
|
|
131
|
-
fs.writeFileSync(epicFile, updated);
|
|
132
|
-
console.log('ā
Title updated');
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
async editDescription(epicFile, content, rl, prompt) {
|
|
137
|
-
console.log('Enter new description (end with empty line):');
|
|
138
|
-
let description = '';
|
|
139
|
-
let line;
|
|
140
|
-
while ((line = await prompt('')) !== '') {
|
|
141
|
-
description += line + '\n';
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
if (description) {
|
|
145
|
-
const updated = content.replace(
|
|
146
|
-
/(## Overview\n)([\s\S]*?)(\n##|$)/,
|
|
147
|
-
`$1${description}$3`
|
|
148
|
-
);
|
|
149
|
-
fs.writeFileSync(epicFile, updated);
|
|
150
|
-
console.log('ā
Description updated');
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
async addMilestone(epicFile, content, rl, prompt) {
|
|
155
|
-
const milestone = await prompt('Enter new milestone: ');
|
|
156
|
-
if (milestone) {
|
|
157
|
-
let updated = content;
|
|
158
|
-
|
|
159
|
-
if (!content.includes('## Milestones')) {
|
|
160
|
-
// Add milestones section
|
|
161
|
-
updated = content.replace(
|
|
162
|
-
/(## Implementation Plan|## Tasks)/,
|
|
163
|
-
`## Milestones\n- [ ] ${milestone}\n\n$1`
|
|
164
|
-
);
|
|
165
|
-
} else {
|
|
166
|
-
// Add to existing milestones
|
|
167
|
-
updated = content.replace(
|
|
168
|
-
/(## Milestones\n([\s\S]*?))\n(\n##|$)/,
|
|
169
|
-
`$1\n- [ ] ${milestone}\n$3`
|
|
170
|
-
);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
fs.writeFileSync(epicFile, updated);
|
|
174
|
-
console.log('ā
Milestone added');
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
async markMilestoneComplete(epicFile, content, rl, prompt) {
|
|
179
|
-
// Extract milestones
|
|
180
|
-
const milestoneMatches = content.match(/- \[ \] .+/g) || [];
|
|
181
|
-
|
|
182
|
-
if (milestoneMatches.length === 0) {
|
|
183
|
-
console.log('No incomplete milestones found.');
|
|
184
|
-
return;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
console.log('\nIncomplete milestones:');
|
|
188
|
-
milestoneMatches.forEach((milestone, index) => {
|
|
189
|
-
console.log(` ${index + 1}. ${milestone.replace('- [ ] ', '')}`);
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
const milestoneNum = await prompt(`\nSelect milestone to complete (1-${milestoneMatches.length}): `);
|
|
193
|
-
const index = parseInt(milestoneNum) - 1;
|
|
194
|
-
|
|
195
|
-
if (index >= 0 && index < milestoneMatches.length) {
|
|
196
|
-
const milestoneToComplete = milestoneMatches[index];
|
|
197
|
-
const updated = content.replace(
|
|
198
|
-
milestoneToComplete,
|
|
199
|
-
milestoneToComplete.replace('- [ ]', '- [x]')
|
|
200
|
-
);
|
|
201
|
-
fs.writeFileSync(epicFile, updated);
|
|
202
|
-
console.log('ā
Milestone marked as complete');
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
async addTechnicalRequirement(epicFile, content, rl, prompt) {
|
|
207
|
-
const requirement = await prompt('Enter technical requirement: ');
|
|
208
|
-
if (requirement) {
|
|
209
|
-
let updated = content;
|
|
210
|
-
|
|
211
|
-
if (!content.includes('## Technical Requirements')) {
|
|
212
|
-
// Add technical requirements section
|
|
213
|
-
updated = content.replace(
|
|
214
|
-
/(## Implementation Plan|## Tasks)/,
|
|
215
|
-
`## Technical Requirements\n- ${requirement}\n\n$1`
|
|
216
|
-
);
|
|
217
|
-
} else {
|
|
218
|
-
// Add to existing requirements
|
|
219
|
-
updated = content.replace(
|
|
220
|
-
/(## Technical Requirements\n([\s\S]*?))\n(\n##|$)/,
|
|
221
|
-
`$1\n- ${requirement}\n$3`
|
|
222
|
-
);
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
fs.writeFileSync(epicFile, updated);
|
|
226
|
-
console.log('ā
Technical requirement added');
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
async updateMetadata(epicFile, content, rl, prompt) {
|
|
231
|
-
console.log('\nSelect metadata to update:');
|
|
232
|
-
console.log(' 1. Status');
|
|
233
|
-
console.log(' 2. Priority');
|
|
234
|
-
console.log(' 3. Estimated effort');
|
|
235
|
-
console.log(' 4. Tags');
|
|
236
|
-
|
|
237
|
-
const choice = await prompt('Enter choice (1-4): ');
|
|
238
|
-
|
|
239
|
-
switch (choice) {
|
|
240
|
-
case '1':
|
|
241
|
-
const status = await prompt('Enter new status (planning/in-progress/review/completed): ');
|
|
242
|
-
if (status) {
|
|
243
|
-
const updated = this.updateMetadataField(content, 'status', status);
|
|
244
|
-
fs.writeFileSync(epicFile, updated);
|
|
245
|
-
console.log('ā
Status updated');
|
|
246
|
-
}
|
|
247
|
-
break;
|
|
248
|
-
case '2':
|
|
249
|
-
const priority = await prompt('Enter new priority (P0/P1/P2/P3): ');
|
|
250
|
-
if (priority) {
|
|
251
|
-
const updated = this.updateMetadataField(content, 'priority', priority);
|
|
252
|
-
fs.writeFileSync(epicFile, updated);
|
|
253
|
-
console.log('ā
Priority updated');
|
|
254
|
-
}
|
|
255
|
-
break;
|
|
256
|
-
case '3':
|
|
257
|
-
const effort = await prompt('Enter estimated effort (e.g., 2d, 1w, 3w): ');
|
|
258
|
-
if (effort) {
|
|
259
|
-
const updated = this.updateMetadataField(content, 'effort', effort);
|
|
260
|
-
fs.writeFileSync(epicFile, updated);
|
|
261
|
-
console.log('ā
Effort estimate updated');
|
|
262
|
-
}
|
|
263
|
-
break;
|
|
264
|
-
case '4':
|
|
265
|
-
const tags = await prompt('Enter tags (comma-separated): ');
|
|
266
|
-
if (tags) {
|
|
267
|
-
const updated = this.updateMetadataField(content, 'tags', tags);
|
|
268
|
-
fs.writeFileSync(epicFile, updated);
|
|
269
|
-
console.log('ā
Tags updated');
|
|
270
|
-
}
|
|
271
|
-
break;
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
updateMetadataField(content, field, value) {
|
|
276
|
-
const lines = content.split('\n');
|
|
277
|
-
const updatedLines = [];
|
|
278
|
-
let inMetadata = false;
|
|
279
|
-
let fieldFound = false;
|
|
280
|
-
|
|
281
|
-
for (const line of lines) {
|
|
282
|
-
if (line.startsWith('---') && !inMetadata) {
|
|
283
|
-
inMetadata = true;
|
|
284
|
-
updatedLines.push(line);
|
|
285
|
-
} else if (line.startsWith('---') && inMetadata) {
|
|
286
|
-
if (!fieldFound) {
|
|
287
|
-
// Add field before closing metadata
|
|
288
|
-
updatedLines.push(`${field}: ${value}`);
|
|
289
|
-
}
|
|
290
|
-
updatedLines.push(line);
|
|
291
|
-
inMetadata = false;
|
|
292
|
-
} else if (inMetadata && line.startsWith(`${field}:`)) {
|
|
293
|
-
updatedLines.push(`${field}: ${value}`);
|
|
294
|
-
fieldFound = true;
|
|
295
|
-
} else {
|
|
296
|
-
updatedLines.push(line);
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
// If no metadata section exists, add one
|
|
301
|
-
if (!fieldFound && !content.includes('---')) {
|
|
302
|
-
const titleIndex = updatedLines.findIndex(line => line.startsWith('# '));
|
|
303
|
-
if (titleIndex !== -1) {
|
|
304
|
-
updatedLines.splice(titleIndex + 1, 0,
|
|
305
|
-
'',
|
|
306
|
-
'---',
|
|
307
|
-
`${field}: ${value}`,
|
|
308
|
-
'---',
|
|
309
|
-
''
|
|
310
|
-
);
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
return updatedLines.join('\n');
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
async openInEditor(epicFile) {
|
|
318
|
-
const { exec } = require('child_process');
|
|
319
|
-
const editor = process.env.EDITOR || 'vi';
|
|
320
|
-
|
|
321
|
-
console.log(`Opening in ${editor}...`);
|
|
322
|
-
exec(`${editor} "${epicFile}"`, (error) => {
|
|
323
|
-
if (error) {
|
|
324
|
-
console.error(`Failed to open editor: ${error.message}`);
|
|
325
|
-
} else {
|
|
326
|
-
console.log('ā
Editor closed');
|
|
327
|
-
}
|
|
328
|
-
});
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
listAvailableEpics() {
|
|
332
|
-
const epics = new Set();
|
|
333
|
-
|
|
334
|
-
// List from epics directory
|
|
335
|
-
if (fs.existsSync(this.epicsDir)) {
|
|
336
|
-
fs.readdirSync(this.epicsDir)
|
|
337
|
-
.filter(file => file.endsWith('.md'))
|
|
338
|
-
.forEach(file => epics.add(file.replace('.md', '')));
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
// List from PRDs directory
|
|
342
|
-
if (fs.existsSync(this.prdsDir)) {
|
|
343
|
-
fs.readdirSync(this.prdsDir)
|
|
344
|
-
.filter(file => file.endsWith('.md'))
|
|
345
|
-
.forEach(file => epics.add(file.replace('.md', '')));
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
if (epics.size > 0) {
|
|
349
|
-
Array.from(epics).forEach(epic => {
|
|
350
|
-
console.log(` ⢠${epic}`);
|
|
351
|
-
});
|
|
352
|
-
} else {
|
|
353
|
-
console.log(' No epics found');
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
async run(args) {
|
|
358
|
-
const epicName = args[0];
|
|
359
|
-
|
|
360
|
-
if (!epicName) {
|
|
361
|
-
console.error('ā Error: Epic name required');
|
|
362
|
-
console.error('Usage: /pm:epic-edit <epic-name>');
|
|
363
|
-
console.log('\nAvailable epics:');
|
|
364
|
-
this.listAvailableEpics();
|
|
365
|
-
process.exit(1);
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
const success = await this.editEpic(epicName);
|
|
369
|
-
process.exit(success ? 0 : 1);
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
// Main execution
|
|
374
|
-
if (require.main === module) {
|
|
375
|
-
const editor = new EpicEditor();
|
|
376
|
-
editor.run(process.argv.slice(2)).catch(error => {
|
|
377
|
-
console.error('ā Error:', error.message);
|
|
378
|
-
process.exit(1);
|
|
379
|
-
});
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
module.exports = EpicEditor;
|
|
@@ -1,273 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* PM Epic List Script - Node.js Implementation
|
|
5
|
-
*
|
|
6
|
-
* Migrated from epic-list.sh to provide epic listing functionality
|
|
7
|
-
* Maintains full compatibility with the original bash implementation
|
|
8
|
-
*
|
|
9
|
-
* Features:
|
|
10
|
-
* - Lists epics categorized by status (planning, in-progress, completed)
|
|
11
|
-
* - Extracts metadata from frontmatter (name, status, progress, github)
|
|
12
|
-
* - Counts tasks per epic
|
|
13
|
-
* - Provides summary statistics
|
|
14
|
-
* - Handles various status formats
|
|
15
|
-
* - Extracts GitHub issue numbers
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
const fs = require('fs');
|
|
19
|
-
const path = require('path');
|
|
20
|
-
|
|
21
|
-
function parseMetadata(content) {
|
|
22
|
-
const metadata = {
|
|
23
|
-
name: '',
|
|
24
|
-
status: '',
|
|
25
|
-
progress: '',
|
|
26
|
-
github: '',
|
|
27
|
-
created: ''
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
// Handle YAML frontmatter (between --- lines)
|
|
31
|
-
const yamlMatch = content.match(/^---\s*\n([\s\S]*?)\n---/);
|
|
32
|
-
if (yamlMatch) {
|
|
33
|
-
const yamlContent = yamlMatch[1];
|
|
34
|
-
const lines = yamlContent.split('\n');
|
|
35
|
-
|
|
36
|
-
for (const line of lines) {
|
|
37
|
-
const trimmedLine = line.trim();
|
|
38
|
-
if (trimmedLine.includes(':')) {
|
|
39
|
-
const [key, ...valueParts] = trimmedLine.split(':');
|
|
40
|
-
const value = valueParts.join(':').trim();
|
|
41
|
-
const cleanKey = key.trim().toLowerCase();
|
|
42
|
-
|
|
43
|
-
if (Object.prototype.hasOwnProperty.call(metadata, cleanKey)) {
|
|
44
|
-
metadata[cleanKey] = value;
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
} else {
|
|
49
|
-
// Handle simple key-value format (key: value lines at start of file)
|
|
50
|
-
const lines = content.split('\n');
|
|
51
|
-
for (const line of lines) {
|
|
52
|
-
const trimmedLine = line.trim();
|
|
53
|
-
if (trimmedLine.includes(':') && !trimmedLine.startsWith('#')) {
|
|
54
|
-
const [key, ...valueParts] = trimmedLine.split(':');
|
|
55
|
-
const value = valueParts.join(':').trim();
|
|
56
|
-
const cleanKey = key.trim().toLowerCase();
|
|
57
|
-
|
|
58
|
-
if (Object.prototype.hasOwnProperty.call(metadata, cleanKey)) {
|
|
59
|
-
metadata[cleanKey] = value;
|
|
60
|
-
}
|
|
61
|
-
} else if (trimmedLine.startsWith('#') || trimmedLine === '') {
|
|
62
|
-
// Stop parsing when we hit content
|
|
63
|
-
break;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
return metadata;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function categorizeStatus(status) {
|
|
72
|
-
const lowerStatus = (status || '').toLowerCase();
|
|
73
|
-
|
|
74
|
-
// Planning statuses
|
|
75
|
-
if (['planning', 'draft', ''].includes(lowerStatus)) {
|
|
76
|
-
return 'planning';
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// In-progress statuses
|
|
80
|
-
if (['in-progress', 'in_progress', 'active', 'started'].includes(lowerStatus)) {
|
|
81
|
-
return 'inProgress';
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// Completed statuses
|
|
85
|
-
if (['completed', 'complete', 'done', 'closed', 'finished'].includes(lowerStatus)) {
|
|
86
|
-
return 'completed';
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// Default to planning for unknown statuses
|
|
90
|
-
return 'planning';
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
function extractGitHubIssue(githubUrl) {
|
|
94
|
-
if (!githubUrl) return null;
|
|
95
|
-
|
|
96
|
-
const match = githubUrl.match(/\/(\d+)$/);
|
|
97
|
-
return match ? match[1] : null;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
function countTasks(epicDir) {
|
|
101
|
-
try {
|
|
102
|
-
const files = fs.readdirSync(epicDir);
|
|
103
|
-
// Count files that match pattern [0-9]*.md (same as bash: ls "$dir"[0-9]*.md)
|
|
104
|
-
return files.filter(file => /^\d+\.md$/.test(file)).length;
|
|
105
|
-
} catch (error) {
|
|
106
|
-
return 0;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
function listEpics() {
|
|
111
|
-
const result = {
|
|
112
|
-
planning: [],
|
|
113
|
-
inProgress: [],
|
|
114
|
-
completed: [],
|
|
115
|
-
summary: {
|
|
116
|
-
totalEpics: 0,
|
|
117
|
-
totalTasks: 0
|
|
118
|
-
}
|
|
119
|
-
};
|
|
120
|
-
|
|
121
|
-
// Check if epics directory exists
|
|
122
|
-
if (!fs.existsSync('.claude/epics')) {
|
|
123
|
-
return result;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
let epicDirs;
|
|
127
|
-
try {
|
|
128
|
-
epicDirs = fs.readdirSync('.claude/epics', { withFileTypes: true })
|
|
129
|
-
.filter(dirent => dirent.isDirectory())
|
|
130
|
-
.map(dirent => dirent.name);
|
|
131
|
-
} catch (error) {
|
|
132
|
-
return result;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
let totalTasks = 0;
|
|
136
|
-
|
|
137
|
-
for (const epicDir of epicDirs) {
|
|
138
|
-
const epicPath = path.join('.claude/epics', epicDir);
|
|
139
|
-
const epicFilePath = path.join(epicPath, 'epic.md');
|
|
140
|
-
|
|
141
|
-
// Skip directories without epic.md file
|
|
142
|
-
if (!fs.existsSync(epicFilePath)) {
|
|
143
|
-
continue;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
let metadata;
|
|
147
|
-
try {
|
|
148
|
-
const content = fs.readFileSync(epicFilePath, 'utf8');
|
|
149
|
-
metadata = parseMetadata(content);
|
|
150
|
-
} catch (error) {
|
|
151
|
-
// Skip files that can't be read, or use defaults
|
|
152
|
-
metadata = {
|
|
153
|
-
name: '',
|
|
154
|
-
status: '',
|
|
155
|
-
progress: '',
|
|
156
|
-
github: '',
|
|
157
|
-
created: ''
|
|
158
|
-
};
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
// Apply defaults
|
|
162
|
-
const name = metadata.name || epicDir;
|
|
163
|
-
const progress = metadata.progress || '0%';
|
|
164
|
-
const status = metadata.status || 'planning';
|
|
165
|
-
const github = metadata.github || '';
|
|
166
|
-
const created = metadata.created || '';
|
|
167
|
-
|
|
168
|
-
// Count tasks
|
|
169
|
-
const taskCount = countTasks(epicPath);
|
|
170
|
-
totalTasks += taskCount;
|
|
171
|
-
|
|
172
|
-
// Extract GitHub issue number
|
|
173
|
-
const githubIssue = extractGitHubIssue(github);
|
|
174
|
-
|
|
175
|
-
const epicData = {
|
|
176
|
-
name,
|
|
177
|
-
status,
|
|
178
|
-
progress,
|
|
179
|
-
github,
|
|
180
|
-
githubIssue,
|
|
181
|
-
created,
|
|
182
|
-
taskCount,
|
|
183
|
-
epicDir,
|
|
184
|
-
epicPath: `${epicPath}/epic.md`
|
|
185
|
-
};
|
|
186
|
-
|
|
187
|
-
// Categorize by status
|
|
188
|
-
const category = categorizeStatus(status);
|
|
189
|
-
result[category].push(epicData);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
result.summary.totalEpics = epicDirs.length;
|
|
193
|
-
result.summary.totalTasks = totalTasks;
|
|
194
|
-
|
|
195
|
-
return result;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
function formatEpicList(data) {
|
|
199
|
-
let output = 'Getting epics...\n\n\n';
|
|
200
|
-
|
|
201
|
-
// Check if no epics directory or empty
|
|
202
|
-
if (data.summary.totalEpics === 0) {
|
|
203
|
-
if (!fs.existsSync('.claude/epics')) {
|
|
204
|
-
output += 'š No epics directory found. Create your first epic with: /pm:prd-parse <feature-name>\n';
|
|
205
|
-
return output;
|
|
206
|
-
} else {
|
|
207
|
-
output += 'š No epics found. Create your first epic with: /pm:prd-parse <feature-name>\n';
|
|
208
|
-
return output;
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
output += 'š Project Epics\n';
|
|
213
|
-
output += '================\n\n';
|
|
214
|
-
|
|
215
|
-
// Planning section
|
|
216
|
-
output += 'š Planning:\n';
|
|
217
|
-
if (data.planning.length > 0) {
|
|
218
|
-
for (const epic of data.planning) {
|
|
219
|
-
const issueText = epic.githubIssue ? ` (#${epic.githubIssue})` : '';
|
|
220
|
-
output += ` š ${epic.epicPath}${issueText} - ${epic.progress} complete (${epic.taskCount} tasks)\n`;
|
|
221
|
-
}
|
|
222
|
-
} else {
|
|
223
|
-
output += ' (none)\n';
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
output += '\n';
|
|
227
|
-
|
|
228
|
-
// In Progress section
|
|
229
|
-
output += 'š In Progress:\n';
|
|
230
|
-
if (data.inProgress.length > 0) {
|
|
231
|
-
for (const epic of data.inProgress) {
|
|
232
|
-
const issueText = epic.githubIssue ? ` (#${epic.githubIssue})` : '';
|
|
233
|
-
output += ` š ${epic.epicPath}${issueText} - ${epic.progress} complete (${epic.taskCount} tasks)\n`;
|
|
234
|
-
}
|
|
235
|
-
} else {
|
|
236
|
-
output += ' (none)\n';
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
output += '\n';
|
|
240
|
-
|
|
241
|
-
// Completed section
|
|
242
|
-
output += 'ā
Completed:\n';
|
|
243
|
-
if (data.completed.length > 0) {
|
|
244
|
-
for (const epic of data.completed) {
|
|
245
|
-
const issueText = epic.githubIssue ? ` (#${epic.githubIssue})` : '';
|
|
246
|
-
output += ` š ${epic.epicPath}${issueText} - ${epic.progress} complete (${epic.taskCount} tasks)\n`;
|
|
247
|
-
}
|
|
248
|
-
} else {
|
|
249
|
-
output += ' (none)\n';
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
// Summary
|
|
253
|
-
output += '\nš Summary\n';
|
|
254
|
-
output += ` Total epics: ${data.summary.totalEpics}\n`;
|
|
255
|
-
output += ` Total tasks: ${data.summary.totalTasks}\n`;
|
|
256
|
-
|
|
257
|
-
return output;
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
// CommonJS export for testing
|
|
261
|
-
module.exports = listEpics;
|
|
262
|
-
|
|
263
|
-
// CLI execution
|
|
264
|
-
if (require.main === module) {
|
|
265
|
-
try {
|
|
266
|
-
const data = listEpics();
|
|
267
|
-
console.log(formatEpicList(data));
|
|
268
|
-
process.exit(0);
|
|
269
|
-
} catch (error) {
|
|
270
|
-
console.error('ā Error listing epics:', error.message);
|
|
271
|
-
process.exit(1);
|
|
272
|
-
}
|
|
273
|
-
}
|