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,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 };