claude-autopm 1.18.0 → 1.20.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.
Files changed (75) hide show
  1. package/README.md +159 -0
  2. package/autopm/.claude/agents/core/mcp-manager.md +1 -1
  3. package/autopm/.claude/commands/pm/context.md +11 -0
  4. package/autopm/.claude/commands/pm/epic-decompose.md +25 -2
  5. package/autopm/.claude/commands/pm/epic-oneshot.md +13 -0
  6. package/autopm/.claude/commands/pm/epic-start.md +19 -0
  7. package/autopm/.claude/commands/pm/epic-sync-modular.md +10 -10
  8. package/autopm/.claude/commands/pm/epic-sync.md +14 -14
  9. package/autopm/.claude/commands/pm/issue-start.md +50 -5
  10. package/autopm/.claude/commands/pm/issue-sync.md +15 -15
  11. package/autopm/.claude/commands/pm/what-next.md +11 -0
  12. package/autopm/.claude/mcp/MCP-REGISTRY.md +1 -1
  13. package/autopm/.claude/scripts/azure/active-work.js +2 -2
  14. package/autopm/.claude/scripts/azure/blocked.js +13 -13
  15. package/autopm/.claude/scripts/azure/daily.js +1 -1
  16. package/autopm/.claude/scripts/azure/dashboard.js +1 -1
  17. package/autopm/.claude/scripts/azure/feature-list.js +2 -2
  18. package/autopm/.claude/scripts/azure/feature-status.js +1 -1
  19. package/autopm/.claude/scripts/azure/next-task.js +1 -1
  20. package/autopm/.claude/scripts/azure/search.js +1 -1
  21. package/autopm/.claude/scripts/azure/setup.js +15 -15
  22. package/autopm/.claude/scripts/azure/sprint-report.js +2 -2
  23. package/autopm/.claude/scripts/azure/sync.js +1 -1
  24. package/autopm/.claude/scripts/azure/us-list.js +1 -1
  25. package/autopm/.claude/scripts/azure/us-status.js +1 -1
  26. package/autopm/.claude/scripts/azure/validate.js +13 -13
  27. package/autopm/.claude/scripts/lib/frontmatter-utils.sh +42 -7
  28. package/autopm/.claude/scripts/lib/logging-utils.sh +20 -16
  29. package/autopm/.claude/scripts/lib/validation-utils.sh +1 -1
  30. package/autopm/.claude/scripts/pm/context.js +338 -0
  31. package/autopm/.claude/scripts/pm/issue-sync/format-comment.sh +3 -3
  32. package/autopm/.claude/scripts/pm/lib/README.md +85 -0
  33. package/autopm/.claude/scripts/pm/lib/logger.js +78 -0
  34. package/autopm/.claude/scripts/pm/next.js +25 -1
  35. package/autopm/.claude/scripts/pm/what-next.js +660 -0
  36. package/bin/autopm.js +25 -0
  37. package/package.json +1 -1
  38. package/lib/agentExecutor.js.deprecated +0 -101
  39. package/lib/azure/cache.js +0 -80
  40. package/lib/azure/client.js +0 -77
  41. package/lib/azure/formatter.js +0 -177
  42. package/lib/commandHelpers.js +0 -177
  43. package/lib/context/manager.js +0 -290
  44. package/lib/documentation/manager.js +0 -528
  45. package/lib/github/workflow-manager.js +0 -546
  46. package/lib/helpers/azure-batch-api.js +0 -133
  47. package/lib/helpers/azure-cache-manager.js +0 -287
  48. package/lib/helpers/azure-parallel-processor.js +0 -158
  49. package/lib/helpers/azure-work-item-create.js +0 -278
  50. package/lib/helpers/gh-issue-create.js +0 -250
  51. package/lib/helpers/interactive-prompt.js +0 -336
  52. package/lib/helpers/output-manager.js +0 -335
  53. package/lib/helpers/progress-indicator.js +0 -258
  54. package/lib/performance/benchmarker.js +0 -429
  55. package/lib/pm/epic-decomposer.js +0 -273
  56. package/lib/pm/epic-syncer.js +0 -221
  57. package/lib/prdMetadata.js +0 -270
  58. package/lib/providers/azure/index.js +0 -234
  59. package/lib/providers/factory.js +0 -87
  60. package/lib/providers/github/index.js +0 -204
  61. package/lib/providers/interface.js +0 -73
  62. package/lib/python/scaffold-manager.js +0 -576
  63. package/lib/react/scaffold-manager.js +0 -745
  64. package/lib/regression/analyzer.js +0 -578
  65. package/lib/release/manager.js +0 -324
  66. package/lib/tailwind/manager.js +0 -486
  67. package/lib/traefik/manager.js +0 -484
  68. package/lib/utils/colors.js +0 -126
  69. package/lib/utils/config.js +0 -317
  70. package/lib/utils/filesystem.js +0 -316
  71. package/lib/utils/logger.js +0 -135
  72. package/lib/utils/prompts.js +0 -294
  73. package/lib/utils/shell.js +0 -237
  74. package/lib/validators/email-validator.js +0 -337
  75. package/lib/workflow/manager.js +0 -449
@@ -1,221 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * Epic Syncer
5
- * Syncs Epic structure to provider (GitHub/Azure DevOps)
6
- */
7
-
8
- const fs = require('fs-extra');
9
- const path = require('path');
10
- const ProviderFactory = require('../providers/factory');
11
-
12
- class EpicSyncer {
13
- constructor(options = {}) {
14
- this.provider = options.provider || this.detectProvider();
15
- this.client = options.client || this.getDefaultClient();
16
- this.providerInstance = this.getProviderInstance();
17
- }
18
-
19
- /**
20
- * Detect provider from config
21
- */
22
- detectProvider() {
23
- try {
24
- const configPath = path.join(process.cwd(), '.claude', 'config.json');
25
- if (fs.existsSync(configPath)) {
26
- const config = fs.readJsonSync(configPath);
27
- return config.provider || 'github';
28
- }
29
- } catch (error) {
30
- console.error(`Error detecting provider: ${error.message}`);
31
- }
32
- return 'github';
33
- }
34
-
35
- /**
36
- * Get default client for provider
37
- */
38
- getDefaultClient() {
39
- if (this.provider === 'azure') {
40
- return require('../../lib/azure/client').getClient();
41
- } else {
42
- // Mock GitHub client for now
43
- return {
44
- createIssue: async (data) => ({ number: Date.now(), ...data }),
45
- updateIssue: async (number, data) => ({ number, ...data }),
46
- getIssue: async (number) => ({ number, body: 'Issue body' })
47
- };
48
- }
49
- }
50
-
51
- /**
52
- * Get provider instance
53
- */
54
- getProviderInstance() {
55
- if (this.client) {
56
- return ProviderFactory.create(this.provider, this.client);
57
- }
58
- return ProviderFactory.autoDetect();
59
- }
60
-
61
- /**
62
- * Sync epic to provider
63
- */
64
- async sync(epicName) {
65
- const epicJsonPath = path.join(process.cwd(), '.claude', 'epics', `${epicName}.json`);
66
-
67
- if (!await fs.pathExists(epicJsonPath)) {
68
- throw new Error(`Epic not found: ${epicName}`);
69
- }
70
-
71
- const epicData = await fs.readJson(epicJsonPath);
72
-
73
- let result;
74
- if (this.provider === 'azure') {
75
- result = await this.syncToAzure(epicData);
76
- } else {
77
- result = await this.syncToGitHub(epicData);
78
- }
79
-
80
- // Update epic file with provider IDs
81
- await this.updateEpicFile(epicName, epicData, result);
82
-
83
- return result;
84
- }
85
-
86
- /**
87
- * Sync to Azure DevOps
88
- */
89
- async syncToAzure(epicData) {
90
- try {
91
- const result = await this.providerInstance.syncEpic(epicData);
92
-
93
- // Add Azure-specific URLs and IDs
94
- if (result.hierarchy) {
95
- result.epic = result.hierarchy.epic;
96
- result.userStories = result.hierarchy.userStories;
97
- }
98
-
99
- return result;
100
- } catch (error) {
101
- throw new Error(`Failed to sync epic: ${error.message}`);
102
- }
103
- }
104
-
105
- /**
106
- * Sync to GitHub
107
- */
108
- async syncToGitHub(epicData) {
109
- try {
110
- const result = await this.providerInstance.syncEpic(epicData);
111
-
112
- // GitHub returns a simpler structure
113
- return result;
114
- } catch (error) {
115
- throw new Error(`Failed to sync epic: ${error.message}`);
116
- }
117
- }
118
-
119
- /**
120
- * Update epic file with provider IDs
121
- */
122
- async updateEpicFile(epicName, epicData, syncResult) {
123
- const epicJsonPath = path.join(process.cwd(), '.claude', 'epics', `${epicName}.json`);
124
-
125
- // Add provider sync information
126
- if (this.provider === 'azure') {
127
- epicData.azureDevOps = {
128
- epicId: syncResult.epic.id,
129
- url: syncResult.epic.url,
130
- syncedAt: new Date().toISOString()
131
- };
132
-
133
- // Add IDs to user stories
134
- if (epicData.userStories && syncResult.userStories) {
135
- epicData.userStories.forEach((story, index) => {
136
- const syncedStory = syncResult.userStories[index];
137
- if (syncedStory) {
138
- story.azureDevOps = {
139
- storyId: syncedStory.id,
140
- url: syncedStory.url
141
- };
142
-
143
- // Add IDs to tasks
144
- if (story.tasks && syncedStory.tasks) {
145
- story.tasks.forEach((task, taskIndex) => {
146
- const syncedTask = syncedStory.tasks[taskIndex];
147
- if (syncedTask) {
148
- task.azureDevOps = {
149
- taskId: syncedTask.id,
150
- url: syncedTask.url
151
- };
152
- }
153
- });
154
- }
155
- }
156
- });
157
- }
158
- } else if (this.provider === 'github') {
159
- epicData.github = {
160
- epicNumber: syncResult.epic.number,
161
- url: syncResult.epic.url,
162
- syncedAt: new Date().toISOString()
163
- };
164
-
165
- // Add issue numbers
166
- if (epicData.issues && syncResult.tasks) {
167
- epicData.issues.forEach((issue, index) => {
168
- const syncedIssue = syncResult.tasks[index];
169
- if (syncedIssue) {
170
- issue.github = {
171
- issueNumber: syncedIssue.number,
172
- url: syncedIssue.url
173
- };
174
- }
175
- });
176
- }
177
- }
178
-
179
- // Save updated epic
180
- await fs.writeJson(epicJsonPath, epicData, { spaces: 2 });
181
-
182
- // Also update markdown file
183
- await this.updateEpicMarkdown(epicName, epicData);
184
- }
185
-
186
- /**
187
- * Update epic markdown file
188
- */
189
- async updateEpicMarkdown(epicName, epicData) {
190
- const epicMdPath = path.join(process.cwd(), '.claude', 'epics', `${epicName}.md`);
191
-
192
- if (!await fs.pathExists(epicMdPath)) {
193
- return;
194
- }
195
-
196
- let markdown = await fs.readFile(epicMdPath, 'utf8');
197
-
198
- // Add sync status to frontmatter
199
- const frontmatterEnd = markdown.indexOf('---', 3);
200
- if (frontmatterEnd > 0) {
201
- const frontmatter = markdown.substring(0, frontmatterEnd);
202
- const content = markdown.substring(frontmatterEnd);
203
-
204
- let syncInfo = '';
205
- if (this.provider === 'azure' && epicData.azureDevOps) {
206
- syncInfo = `\nazure_epic_id: ${epicData.azureDevOps.epicId}`;
207
- syncInfo += `\nazure_url: ${epicData.azureDevOps.url}`;
208
- } else if (this.provider === 'github' && epicData.github) {
209
- syncInfo = `\ngithub_epic_number: ${epicData.github.epicNumber}`;
210
- syncInfo += `\ngithub_url: ${epicData.github.url}`;
211
- }
212
-
213
- if (syncInfo) {
214
- markdown = frontmatter + syncInfo + '\nsynced: true' + content;
215
- await fs.writeFile(epicMdPath, markdown);
216
- }
217
- }
218
- }
219
- }
220
-
221
- module.exports = EpicSyncer;
@@ -1,270 +0,0 @@
1
- /**
2
- * PRD Metadata Management System
3
- * Handles progress tracking, validation, and section dependencies
4
- */
5
-
6
- const fs = require('fs');
7
- const path = require('path');
8
-
9
- class PRDMetadata {
10
- constructor(featureName) {
11
- this.featureName = featureName;
12
- this.prdsDir = '.claude/prds';
13
- this.draftsDir = path.join(this.prdsDir, 'drafts');
14
- this.metaDir = path.join(this.prdsDir, 'meta');
15
- this.prdPath = path.join(this.draftsDir, `${featureName}.md`);
16
- this.metaPath = path.join(this.metaDir, `${featureName}.json`);
17
- }
18
-
19
- /**
20
- * Load metadata from file
21
- */
22
- load() {
23
- if (!fs.existsSync(this.metaPath)) {
24
- return null;
25
- }
26
- return JSON.parse(fs.readFileSync(this.metaPath, 'utf-8'));
27
- }
28
-
29
- /**
30
- * Save metadata to file
31
- */
32
- save(metadata) {
33
- // Ensure directories exist
34
- if (!fs.existsSync(this.metaDir)) {
35
- fs.mkdirSync(this.metaDir, { recursive: true });
36
- }
37
-
38
- fs.writeFileSync(this.metaPath, JSON.stringify(metadata, null, 2));
39
- }
40
-
41
- /**
42
- * Update section content and metadata
43
- */
44
- updateSection(sectionName, content) {
45
- const metadata = this.load();
46
- if (!metadata) {
47
- throw new Error(`PRD metadata not found for ${this.featureName}`);
48
- }
49
-
50
- const sectionKey = sectionName.toLowerCase().replace(/\s+/g, '-');
51
- const timestamp = new Date().toISOString();
52
- const wordCount = content.trim().split(/\s+/).length;
53
-
54
- // Update section metadata
55
- if (!metadata.sections[sectionKey]) {
56
- metadata.sections[sectionKey] = {};
57
- }
58
-
59
- metadata.sections[sectionKey] = {
60
- ...metadata.sections[sectionKey],
61
- status: content.trim() ? 'completed' : 'empty',
62
- word_count: wordCount,
63
- last_edited: timestamp,
64
- dependencies_met: this.checkDependencies(sectionKey, metadata)
65
- };
66
-
67
- // Update overall progress
68
- metadata.last_activity = timestamp;
69
- this.updateProgress(metadata);
70
-
71
- // Update PRD file
72
- this.updatePRDContent(sectionName, content);
73
-
74
- // Save metadata
75
- this.save(metadata);
76
-
77
- return metadata;
78
- }
79
-
80
- /**
81
- * Update PRD file content for specific section
82
- */
83
- updatePRDContent(sectionName, content) {
84
- if (!fs.existsSync(this.prdPath)) {
85
- throw new Error(`PRD file not found: ${this.prdPath}`);
86
- }
87
-
88
- let prdContent = fs.readFileSync(this.prdPath, 'utf-8');
89
-
90
- // Find section boundaries
91
- const sectionHeaderRegex = new RegExp(`^## ${sectionName}$`, 'm');
92
- const nextSectionRegex = /^## /gm;
93
-
94
- const headerMatch = prdContent.match(sectionHeaderRegex);
95
- if (!headerMatch) {
96
- throw new Error(`Section '${sectionName}' not found in PRD`);
97
- }
98
-
99
- const headerIndex = headerMatch.index;
100
- const headerEnd = headerIndex + headerMatch[0].length;
101
-
102
- // Find next section
103
- nextSectionRegex.lastIndex = headerEnd;
104
- const nextMatch = nextSectionRegex.exec(prdContent);
105
-
106
- const sectionEnd = nextMatch ? nextMatch.index : prdContent.length;
107
-
108
- // Replace section content
109
- const beforeSection = prdContent.slice(0, headerEnd);
110
- const afterSection = prdContent.slice(sectionEnd);
111
- const newContent = content.trim() ? `\n\n${content.trim()}\n\n` : '\n\n<!-- This section will be filled by AI during conversation -->\n\n';
112
-
113
- prdContent = beforeSection + newContent + afterSection;
114
-
115
- fs.writeFileSync(this.prdPath, prdContent);
116
- }
117
-
118
- /**
119
- * Check if section dependencies are met
120
- */
121
- checkDependencies(sectionKey, metadata) {
122
- const sectionTemplatesDir = path.join(__dirname, '../autopm/.claude/prds/templates/sections');
123
- const templatePath = path.join(sectionTemplatesDir, `${sectionKey}.md`);
124
-
125
- if (!fs.existsSync(templatePath)) {
126
- return true; // No template means no dependencies
127
- }
128
-
129
- const templateContent = fs.readFileSync(templatePath, 'utf-8');
130
- const yamlMatch = templateContent.match(/^---\n([\s\S]*?)\n---/);
131
-
132
- if (!yamlMatch) {
133
- return true; // No YAML frontmatter means no dependencies
134
- }
135
-
136
- const yaml = require('js-yaml');
137
- const config = yaml.load(yamlMatch[1]);
138
-
139
- if (!config.dependencies || config.dependencies.length === 0) {
140
- return true;
141
- }
142
-
143
- return config.dependencies.every(dep => {
144
- const depKey = dep.toLowerCase().replace(/\s+/g, '-');
145
- return metadata.sections[depKey] &&
146
- metadata.sections[depKey].status === 'completed';
147
- });
148
- }
149
-
150
- /**
151
- * Update overall progress statistics
152
- */
153
- updateProgress(metadata) {
154
- const sections = Object.values(metadata.sections);
155
- const totalSections = sections.length;
156
- const completedSections = sections.filter(s => s.status === 'completed').length;
157
- const inProgressSections = sections.filter(s => s.status === 'in_progress').length;
158
- const emptySections = sections.filter(s => s.status === 'empty').length;
159
-
160
- metadata.progress = {
161
- total_sections: totalSections,
162
- completed_sections: completedSections,
163
- in_progress_sections: inProgressSections,
164
- empty_sections: emptySections,
165
- completion_percentage: Math.round((completedSections / totalSections) * 100)
166
- };
167
-
168
- // Update validation status
169
- this.updateValidationStatus(metadata);
170
- }
171
-
172
- /**
173
- * Update validation status
174
- */
175
- updateValidationStatus(metadata) {
176
- const timestamp = new Date().toISOString();
177
- const issues = [];
178
-
179
- // Check for empty required sections
180
- Object.entries(metadata.sections).forEach(([key, section]) => {
181
- if (section.status === 'empty') {
182
- issues.push(`Section '${key}' is empty`);
183
- }
184
- });
185
-
186
- // Check word counts
187
- const lowWordCountSections = Object.entries(metadata.sections)
188
- .filter(([key, section]) => section.status === 'completed' && section.word_count < 50)
189
- .map(([key]) => key);
190
-
191
- if (lowWordCountSections.length > 0) {
192
- issues.push(`Low word count in: ${lowWordCountSections.join(', ')}`);
193
- }
194
-
195
- // Calculate overall score
196
- const completionScore = metadata.progress.completion_percentage;
197
- const qualityScore = Math.max(0, 100 - (lowWordCountSections.length * 20));
198
- const overallScore = Math.round((completionScore + qualityScore) / 2);
199
-
200
- metadata.validation = {
201
- last_check: timestamp,
202
- overall_score: overallScore,
203
- issues: issues,
204
- ready_for_review: issues.length === 0 && completionScore >= 80,
205
- ready_for_publish: issues.length === 0 && completionScore === 100 && overallScore >= 90
206
- };
207
- }
208
-
209
- /**
210
- * Get next recommended section to work on
211
- */
212
- getNextSection(metadata) {
213
- // Define logical section order (recommended workflow)
214
- const sectionPriority = [
215
- 'problem-statement',
216
- 'success-criteria',
217
- 'user-stories',
218
- 'acceptance-criteria',
219
- 'out-of-scope',
220
- 'executive-summary'
221
- ];
222
-
223
- // Find empty sections with met dependencies
224
- const emptySections = Object.entries(metadata.sections)
225
- .filter(([key, section]) => section.status === 'empty')
226
- .map(([key, section]) => ({
227
- key,
228
- section,
229
- dependencies_met: this.checkDependencies(key, metadata),
230
- priority: sectionPriority.indexOf(key)
231
- }))
232
- .sort((a, b) => a.priority - b.priority); // Sort by priority
233
-
234
- // Prioritize sections with met dependencies
235
- const readySections = emptySections.filter(s => s.dependencies_met);
236
- if (readySections.length > 0) {
237
- return readySections[0].key;
238
- }
239
-
240
- // Return first empty section if none are ready
241
- if (emptySections.length > 0) {
242
- return emptySections[0].key;
243
- }
244
-
245
- return null;
246
- }
247
-
248
- /**
249
- * Convert section key to display name
250
- */
251
- static keyToDisplayName(key) {
252
- return key.split('-')
253
- .map(word => word.charAt(0).toUpperCase() + word.slice(1))
254
- .join(' ');
255
- }
256
-
257
- /**
258
- * Get section status emoji
259
- */
260
- static getStatusEmoji(status) {
261
- switch (status) {
262
- case 'completed': return '✅';
263
- case 'in_progress': return '🔄';
264
- case 'empty': return '⭕';
265
- default: return '❓';
266
- }
267
- }
268
- }
269
-
270
- module.exports = PRDMetadata;