claude-autopm 1.17.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 (76) 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/bin/commands/team.js +86 -0
  38. package/package.json +1 -1
  39. package/lib/agentExecutor.js.deprecated +0 -101
  40. package/lib/azure/cache.js +0 -80
  41. package/lib/azure/client.js +0 -77
  42. package/lib/azure/formatter.js +0 -177
  43. package/lib/commandHelpers.js +0 -177
  44. package/lib/context/manager.js +0 -290
  45. package/lib/documentation/manager.js +0 -528
  46. package/lib/github/workflow-manager.js +0 -546
  47. package/lib/helpers/azure-batch-api.js +0 -133
  48. package/lib/helpers/azure-cache-manager.js +0 -287
  49. package/lib/helpers/azure-parallel-processor.js +0 -158
  50. package/lib/helpers/azure-work-item-create.js +0 -278
  51. package/lib/helpers/gh-issue-create.js +0 -250
  52. package/lib/helpers/interactive-prompt.js +0 -336
  53. package/lib/helpers/output-manager.js +0 -335
  54. package/lib/helpers/progress-indicator.js +0 -258
  55. package/lib/performance/benchmarker.js +0 -429
  56. package/lib/pm/epic-decomposer.js +0 -273
  57. package/lib/pm/epic-syncer.js +0 -221
  58. package/lib/prdMetadata.js +0 -270
  59. package/lib/providers/azure/index.js +0 -234
  60. package/lib/providers/factory.js +0 -87
  61. package/lib/providers/github/index.js +0 -204
  62. package/lib/providers/interface.js +0 -73
  63. package/lib/python/scaffold-manager.js +0 -576
  64. package/lib/react/scaffold-manager.js +0 -745
  65. package/lib/regression/analyzer.js +0 -578
  66. package/lib/release/manager.js +0 -324
  67. package/lib/tailwind/manager.js +0 -486
  68. package/lib/traefik/manager.js +0 -484
  69. package/lib/utils/colors.js +0 -126
  70. package/lib/utils/config.js +0 -317
  71. package/lib/utils/filesystem.js +0 -316
  72. package/lib/utils/logger.js +0 -135
  73. package/lib/utils/prompts.js +0 -294
  74. package/lib/utils/shell.js +0 -237
  75. package/lib/validators/email-validator.js +0 -337
  76. 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;