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,250 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * GitHub Issue Creation Helper
5
- * Handles the limitation that gh issue create doesn't support --json flag
6
- * Provides methods to create issues and get JSON responses
7
- */
8
-
9
- const { execSync } = require('child_process');
10
- const fs = require('fs-extra');
11
- const path = require('path');
12
-
13
- class GitHubIssueCreator {
14
- constructor() {
15
- this.execSync = execSync;
16
- }
17
-
18
- /**
19
- * Set custom execSync for testing
20
- */
21
- _setExecSync(customExecSync) {
22
- this.execSync = customExecSync;
23
- }
24
-
25
- /**
26
- * Extract issue number from GitHub URL
27
- */
28
- _extractIssueNumber(url) {
29
- if (!url) return null;
30
-
31
- const match = url.toString().trim().match(/\/issues\/(\d+)/);
32
- return match ? parseInt(match[1], 10) : null;
33
- }
34
-
35
- /**
36
- * Validate required options
37
- */
38
- _validateOptions(options, requiredFields = ['title']) {
39
- for (const field of requiredFields) {
40
- if (!options[field]) {
41
- throw new Error(`${field} is required`);
42
- }
43
- }
44
- }
45
-
46
- /**
47
- * Build gh issue create command
48
- */
49
- _buildCreateCommand(options) {
50
- const parts = ['gh issue create'];
51
-
52
- // Title
53
- parts.push(`--title "${options.title.replace(/"/g, '\\"')}"`);
54
-
55
- // Body or body file
56
- if (options.bodyFile) {
57
- parts.push(`--body-file "${options.bodyFile}"`);
58
- } else if (options.body) {
59
- parts.push(`--body "${options.body.replace(/"/g, '\\"')}"`);
60
- }
61
-
62
- // Labels
63
- if (options.labels && options.labels.length > 0) {
64
- const labelStr = options.labels.join(',');
65
- parts.push(`--label "${labelStr}"`);
66
- }
67
-
68
- // Assignee
69
- if (options.assignee) {
70
- parts.push(`--assignee "${options.assignee}"`);
71
- }
72
-
73
- // Milestone
74
- if (options.milestone) {
75
- parts.push(`--milestone "${options.milestone}"`);
76
- }
77
-
78
- // Project
79
- if (options.project) {
80
- parts.push(`--project "${options.project}"`);
81
- }
82
-
83
- return parts.join(' ');
84
- }
85
-
86
- /**
87
- * Create issue using gh CLI and return issue details
88
- */
89
- async createIssue(options) {
90
- this._validateOptions(options, ['title']);
91
-
92
- try {
93
- // Create the issue
94
- const command = this._buildCreateCommand(options);
95
- const output = this.execSync(command, { encoding: 'utf8' });
96
-
97
- // Extract issue number from URL
98
- const number = this._extractIssueNumber(output);
99
-
100
- if (!number) {
101
- throw new Error(`Could not extract issue number from output: ${output}`);
102
- }
103
-
104
- return {
105
- number,
106
- url: output.trim()
107
- };
108
- } catch (error) {
109
- if (error.code === 'ENOENT') {
110
- throw new Error('GitHub CLI (gh) is not installed or not in PATH');
111
- }
112
- if (error.message && error.message.includes('not authenticated')) {
113
- throw new Error('GitHub CLI not authenticated. Run: gh auth login');
114
- }
115
- throw error;
116
- }
117
- }
118
-
119
- /**
120
- * Create issue using gh api for JSON response
121
- */
122
- async createIssueWithJson(options) {
123
- this._validateOptions(options, ['title']);
124
-
125
- try {
126
- // Prepare API payload
127
- const payload = {
128
- title: options.title,
129
- body: options.body || ''
130
- };
131
-
132
- // Read body from file if specified
133
- if (options.bodyFile && !options.body) {
134
- payload.body = await fs.readFile(options.bodyFile, 'utf8');
135
- }
136
-
137
- // Add labels
138
- if (options.labels && options.labels.length > 0) {
139
- payload.labels = options.labels;
140
- }
141
-
142
- // Add assignees
143
- if (options.assignee && options.assignee !== '@me') {
144
- payload.assignees = [options.assignee];
145
- }
146
-
147
- // Build gh api command
148
- const fieldArgs = Object.entries(payload)
149
- .map(([key, value]) => {
150
- if (Array.isArray(value)) {
151
- return `--field ${key}='${JSON.stringify(value)}'`;
152
- }
153
- return `--field ${key}="${value.replace(/"/g, '\\"')}"`;
154
- })
155
- .join(' ');
156
-
157
- const command = `gh api repos/:owner/:repo/issues --method POST ${fieldArgs}`;
158
- const output = this.execSync(command, { encoding: 'utf8' });
159
-
160
- return JSON.parse(output);
161
- } catch (error) {
162
- if (error.code === 'ENOENT') {
163
- throw new Error('GitHub CLI (gh) is not installed or not in PATH');
164
- }
165
- if (error.message && error.message.includes('not authenticated')) {
166
- throw new Error('GitHub CLI not authenticated. Run: gh auth login');
167
- }
168
- throw error;
169
- }
170
- }
171
-
172
- /**
173
- * Create an epic (issue with epic labels)
174
- */
175
- async createEpic(options) {
176
- const epicName = options.epicName || options.title?.replace(/^Epic:\s*/, '');
177
-
178
- if (!epicName) {
179
- throw new Error('epicName or title is required for epic creation');
180
- }
181
-
182
- // Prepare epic options
183
- const epicOptions = {
184
- title: options.title || `Epic: ${epicName}`,
185
- body: options.body || this._generateEpicBody(options),
186
- labels: ['epic', `epic:${epicName}`]
187
- };
188
-
189
- // Add additional labels
190
- if (options.additionalLabels) {
191
- epicOptions.labels.push(...options.additionalLabels);
192
- }
193
-
194
- // Use JSON method if we need the full response
195
- if (options.returnJson !== false) {
196
- return await this.createIssueWithJson(epicOptions);
197
- }
198
-
199
- return await this.createIssue(epicOptions);
200
- }
201
-
202
- /**
203
- * Generate epic body from template
204
- */
205
- _generateEpicBody(options) {
206
- let body = '## Epic Description\n\n';
207
-
208
- if (options.description) {
209
- body += `${options.description}\n\n`;
210
- }
211
-
212
- if (options.acceptanceCriteria && options.acceptanceCriteria.length > 0) {
213
- body += '## Acceptance Criteria\n\n';
214
- options.acceptanceCriteria.forEach(criteria => {
215
- body += `- [ ] ${criteria}\n`;
216
- });
217
- body += '\n';
218
- }
219
-
220
- if (options.tasks && options.tasks.length > 0) {
221
- body += '## Tasks\n\n';
222
- options.tasks.forEach(task => {
223
- body += `- [ ] ${task}\n`;
224
- });
225
- }
226
-
227
- return body;
228
- }
229
-
230
- /**
231
- * Helper method for PM commands - create epic and return just the number
232
- */
233
- async createEpicAndGetNumber(options) {
234
- const result = await this.createEpic({ ...options, returnJson: false });
235
- return result.number;
236
- }
237
- }
238
-
239
- // Create singleton instance
240
- const creator = new GitHubIssueCreator();
241
-
242
- // Export methods bound to instance
243
- module.exports = {
244
- createIssue: creator.createIssue.bind(creator),
245
- createIssueWithJson: creator.createIssueWithJson.bind(creator),
246
- createEpic: creator.createEpic.bind(creator),
247
- createEpicAndGetNumber: creator.createEpicAndGetNumber.bind(creator),
248
- _setExecSync: creator._setExecSync.bind(creator),
249
- _extractIssueNumber: creator._extractIssueNumber.bind(creator)
250
- };
@@ -1,336 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Interactive Prompt Helper
4
- * Provides interactive prompts for missing parameters
5
- */
6
-
7
- const readline = require('readline');
8
- const fs = require('fs').promises;
9
-
10
- class InteractivePrompt {
11
- constructor(options = {}) {
12
- this.rl = null;
13
- this.history = new Map();
14
- this.defaults = options.defaults || {};
15
- this.validation = options.validation || {};
16
- }
17
-
18
- /**
19
- * Initialize readline interface
20
- */
21
- init() {
22
- if (!this.rl) {
23
- this.rl = readline.createInterface({
24
- input: process.stdin,
25
- output: process.stdout,
26
- terminal: true
27
- });
28
- }
29
- }
30
-
31
- /**
32
- * Close readline interface
33
- */
34
- close() {
35
- if (this.rl) {
36
- this.rl.close();
37
- this.rl = null;
38
- }
39
- }
40
-
41
- /**
42
- * Prompt for input with validation
43
- */
44
- async prompt(question, options = {}) {
45
- this.init();
46
-
47
- const {
48
- defaultValue = this.defaults[question],
49
- required = false,
50
- type = 'string',
51
- choices = null,
52
- mask = false,
53
- validate = this.validation[question]
54
- } = options;
55
-
56
- let promptText = question;
57
-
58
- // Add choices if available
59
- if (choices && choices.length > 0) {
60
- promptText += '\nChoices:\n';
61
- choices.forEach((choice, index) => {
62
- promptText += ` ${index + 1}) ${choice}\n`;
63
- });
64
- promptText += 'Enter choice';
65
- }
66
-
67
- // Add default value indicator
68
- if (defaultValue !== undefined) {
69
- promptText += ` [${defaultValue}]`;
70
- }
71
-
72
- promptText += ': ';
73
-
74
- return new Promise((resolve) => {
75
- const askQuestion = () => {
76
- this.rl.question(promptText, async (answer) => {
77
- // Use default if no answer provided
78
- if (!answer && defaultValue !== undefined) {
79
- answer = defaultValue;
80
- }
81
-
82
- // Check required
83
- if (required && !answer) {
84
- console.log('This field is required.');
85
- return askQuestion();
86
- }
87
-
88
- // Handle choices
89
- if (choices && answer) {
90
- const choiceIndex = parseInt(answer) - 1;
91
- if (choiceIndex >= 0 && choiceIndex < choices.length) {
92
- answer = choices[choiceIndex];
93
- } else if (!choices.includes(answer)) {
94
- console.log('Invalid choice. Please select from the list.');
95
- return askQuestion();
96
- }
97
- }
98
-
99
- // Type conversion
100
- if (type === 'number' && answer) {
101
- answer = parseFloat(answer);
102
- if (isNaN(answer)) {
103
- console.log('Please enter a valid number.');
104
- return askQuestion();
105
- }
106
- } else if (type === 'boolean' && answer) {
107
- answer = ['yes', 'y', 'true', '1'].includes(answer.toLowerCase());
108
- }
109
-
110
- // Custom validation
111
- if (validate && answer) {
112
- const validationResult = await validate(answer);
113
- if (validationResult !== true) {
114
- console.log(validationResult || 'Invalid input.');
115
- return askQuestion();
116
- }
117
- }
118
-
119
- // Store in history
120
- this.history.set(question, answer);
121
-
122
- resolve(answer);
123
- });
124
- };
125
-
126
- askQuestion();
127
- });
128
- }
129
-
130
- /**
131
- * Prompt for multiple inputs
132
- */
133
- async promptMany(questions) {
134
- const answers = {};
135
-
136
- for (const question of questions) {
137
- if (typeof question === 'string') {
138
- answers[question] = await this.prompt(question);
139
- } else {
140
- const { name, ...options } = question;
141
- answers[name] = await this.prompt(name, options);
142
- }
143
- }
144
-
145
- return answers;
146
- }
147
-
148
- /**
149
- * Confirm action
150
- */
151
- async confirm(message, defaultValue = false) {
152
- const answer = await this.prompt(message, {
153
- type: 'boolean',
154
- defaultValue: defaultValue ? 'yes' : 'no'
155
- });
156
-
157
- return answer;
158
- }
159
-
160
- /**
161
- * Select from list
162
- */
163
- async select(message, choices, defaultIndex = 0) {
164
- return this.prompt(message, {
165
- choices,
166
- defaultValue: choices[defaultIndex]
167
- });
168
- }
169
-
170
- /**
171
- * Multi-select from list
172
- */
173
- async multiSelect(message, choices, defaults = []) {
174
- console.log(message);
175
- console.log('Select multiple (comma-separated numbers):');
176
- choices.forEach((choice, index) => {
177
- const selected = defaults.includes(choice) ? 'āœ“' : ' ';
178
- console.log(` [${selected}] ${index + 1}) ${choice}`);
179
- });
180
-
181
- const answer = await this.prompt('Enter choices', {
182
- defaultValue: defaults.map(d => choices.indexOf(d) + 1).join(',')
183
- });
184
-
185
- const indices = answer.split(',').map(s => parseInt(s.trim()) - 1);
186
- return indices
187
- .filter(i => i >= 0 && i < choices.length)
188
- .map(i => choices[i]);
189
- }
190
-
191
- /**
192
- * Password input (masked)
193
- */
194
- async password(message) {
195
- this.init();
196
-
197
- return new Promise((resolve) => {
198
- const stdin = process.stdin;
199
- const stdout = process.stdout;
200
-
201
- stdout.write(message + ': ');
202
-
203
- stdin.setRawMode(true);
204
- stdin.resume();
205
- stdin.setEncoding('utf8');
206
-
207
- let password = '';
208
-
209
- const onData = (char) => {
210
- if (char === '\n' || char === '\r') {
211
- stdin.setRawMode(false);
212
- stdin.pause();
213
- stdin.removeListener('data', onData);
214
- stdout.write('\n');
215
- resolve(password);
216
- } else if (char === '\u0003') {
217
- // Ctrl+C
218
- process.exit();
219
- } else if (char === '\u007f') {
220
- // Backspace
221
- if (password.length > 0) {
222
- password = password.slice(0, -1);
223
- stdout.clearLine();
224
- stdout.cursorTo(0);
225
- stdout.write(message + ': ' + '*'.repeat(password.length));
226
- }
227
- } else {
228
- password += char;
229
- stdout.write('*');
230
- }
231
- };
232
-
233
- stdin.on('data', onData);
234
- });
235
- }
236
-
237
- /**
238
- * Create wizard for complex configurations
239
- */
240
- async wizard(steps) {
241
- const results = {};
242
-
243
- console.log('\nšŸ§™ Configuration Wizard\n');
244
-
245
- for (const step of steps) {
246
- if (step.message) {
247
- console.log(`\n${step.message}\n`);
248
- }
249
-
250
- if (step.condition && !step.condition(results)) {
251
- continue;
252
- }
253
-
254
- const answer = await this.prompt(step.name, step.options);
255
- results[step.name] = answer;
256
-
257
- if (step.postProcess) {
258
- await step.postProcess(answer, results);
259
- }
260
- }
261
-
262
- console.log('\nāœ… Configuration complete!\n');
263
- return results;
264
- }
265
- }
266
-
267
- /**
268
- * Preset prompts for Azure DevOps
269
- */
270
- class AzurePrompts extends InteractivePrompt {
271
- constructor() {
272
- super({
273
- validation: {
274
- 'Work Item ID': (value) => {
275
- return /^\d+$/.test(value) || 'Please enter a valid work item ID';
276
- },
277
- 'Sprint Name': (value) => {
278
- return value.length > 0 || 'Sprint name cannot be empty';
279
- },
280
- 'Story Points': (value) => {
281
- const points = parseFloat(value);
282
- return (!isNaN(points) && points > 0 && points <= 100) ||
283
- 'Please enter a valid number between 1 and 100';
284
- }
285
- }
286
- });
287
- }
288
-
289
- /**
290
- * Prompt for missing work item details
291
- */
292
- async promptWorkItem(existing = {}) {
293
- const questions = [];
294
-
295
- if (!existing.title) {
296
- questions.push({
297
- name: 'title',
298
- options: { required: true }
299
- });
300
- }
301
-
302
- if (!existing.type) {
303
- questions.push({
304
- name: 'type',
305
- options: {
306
- choices: ['Task', 'User Story', 'Bug', 'Feature'],
307
- defaultValue: 'Task'
308
- }
309
- });
310
- }
311
-
312
- if (!existing.assignedTo) {
313
- questions.push({
314
- name: 'assignedTo',
315
- options: {
316
- defaultValue: process.env.AZURE_DEVOPS_USER || 'me'
317
- }
318
- });
319
- }
320
-
321
- if (!existing.priority && existing.type !== 'Feature') {
322
- questions.push({
323
- name: 'priority',
324
- options: {
325
- choices: ['1 - Critical', '2 - High', '3 - Medium', '4 - Low'],
326
- defaultValue: '3 - Medium'
327
- }
328
- });
329
- }
330
-
331
- const answers = await this.promptMany(questions);
332
- return { ...existing, ...answers };
333
- }
334
- }
335
-
336
- module.exports = { InteractivePrompt, AzurePrompts };