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,578 +0,0 @@
1
- /**
2
- * Regression Test Analyzer
3
- * Centralized regression testing and analysis functionality
4
- */
5
-
6
- const fs = require('fs').promises;
7
- const path = require('path');
8
- const { exec } = require('child_process');
9
- const { promisify } = require('util');
10
- const execAsync = promisify(exec);
11
-
12
- /**
13
- * Configuration
14
- */
15
- const CONFIG = {
16
- directories: {
17
- regression: '.claude/regression'
18
- },
19
- defaults: {
20
- coverageThreshold: 80,
21
- slowTestThreshold: 1000, // ms
22
- maxHistorySize: 100
23
- },
24
- filePatterns: {
25
- baseline: 'baseline.json',
26
- history: 'history.json',
27
- report: 'report-{timestamp}.json'
28
- }
29
- };
30
-
31
- class RegressionAnalyzer {
32
- constructor(projectRoot = process.cwd()) {
33
- this.projectRoot = projectRoot;
34
- this.regressionDir = path.join(projectRoot, CONFIG.directories.regression);
35
- }
36
-
37
- /**
38
- * Runs the test suite
39
- */
40
- async runTests() {
41
- const packagePath = path.join(this.projectRoot, 'package.json');
42
-
43
- try {
44
- // Check if package.json exists
45
- const packageJson = JSON.parse(await fs.readFile(packagePath, 'utf8'));
46
-
47
- // Try to run npm test
48
- if (packageJson.scripts && packageJson.scripts.test) {
49
- const startTime = Date.now();
50
-
51
- try {
52
- const { stdout, stderr } = await execAsync('npm test', {
53
- cwd: this.projectRoot,
54
- env: { ...process.env, CI: 'true' }
55
- });
56
- const duration = Date.now() - startTime;
57
-
58
- return this.parseTestResults(stdout, duration, true);
59
- } catch (error) {
60
- const duration = Date.now() - startTime;
61
- return this.parseTestResults(error.stdout || error.message, duration, false);
62
- }
63
- }
64
- } catch (error) {
65
- // No package.json or no test script
66
- }
67
-
68
- // Fallback: look for test files
69
- const testFiles = await this.findTestFiles();
70
- const results = {
71
- success: true,
72
- tests: { total: testFiles.length, passed: 0, failed: 0 },
73
- timestamp: new Date().toISOString(),
74
- duration: 0
75
- };
76
-
77
- const startTime = Date.now();
78
- for (const testFile of testFiles) {
79
- try {
80
- await execAsync(`node ${testFile}`, { cwd: this.projectRoot });
81
- results.tests.passed++;
82
- } catch (error) {
83
- results.tests.failed++;
84
- results.success = false;
85
- }
86
- }
87
- results.duration = Date.now() - startTime;
88
-
89
- return results;
90
- }
91
-
92
- /**
93
- * Parses test results from output
94
- */
95
- parseTestResults(output, duration, success) {
96
- const results = {
97
- success,
98
- duration,
99
- output: output.substring(0, 1000), // Limit output size
100
- timestamp: new Date().toISOString()
101
- };
102
-
103
- // Try to parse different test output formats
104
- const patterns = [
105
- /(\d+)\s+(pass|passed|passing)/i,
106
- /(\d+)\s+(fail|failed|failing)/i,
107
- /(\d+)\s+tests?/i,
108
- /✓\s+(\d+)/,
109
- /✗\s+(\d+)/
110
- ];
111
-
112
- const passMatch = output.match(patterns[0]);
113
- const failMatch = output.match(patterns[1]);
114
- const totalMatch = output.match(patterns[2]);
115
-
116
- if (passMatch || failMatch || totalMatch) {
117
- results.tests = {
118
- total: parseInt(totalMatch?.[1] || 0) ||
119
- (parseInt(passMatch?.[1] || 0) + parseInt(failMatch?.[1] || 0)),
120
- passed: parseInt(passMatch?.[1] || 0),
121
- failed: parseInt(failMatch?.[1] || 0)
122
- };
123
- }
124
-
125
- return results;
126
- }
127
-
128
- /**
129
- * Finds test files in the project
130
- */
131
- async findTestFiles() {
132
- const testFiles = [];
133
- const testDirs = ['test', 'tests', '__tests__', 'spec'];
134
-
135
- for (const dir of testDirs) {
136
- const testDir = path.join(this.projectRoot, dir);
137
-
138
- try {
139
- await this.findTestFilesInDir(testDir, testFiles);
140
- } catch (error) {
141
- // Directory doesn't exist
142
- }
143
- }
144
-
145
- return testFiles;
146
- }
147
-
148
- /**
149
- * Recursively finds test files in a directory
150
- */
151
- async findTestFilesInDir(dir, testFiles) {
152
- const entries = await fs.readdir(dir, { withFileTypes: true });
153
-
154
- for (const entry of entries) {
155
- const fullPath = path.join(dir, entry.name);
156
-
157
- if (entry.isDirectory() && !entry.name.startsWith('.')) {
158
- await this.findTestFilesInDir(fullPath, testFiles);
159
- } else if (entry.isFile()) {
160
- if (entry.name.match(/\.(test|spec)\.(js|ts|jsx|tsx)$/)) {
161
- testFiles.push(fullPath);
162
- }
163
- }
164
- }
165
- }
166
-
167
- /**
168
- * Gets coverage information
169
- */
170
- async getCoverage() {
171
- // Try to get coverage from common locations
172
- const coveragePaths = [
173
- path.join(this.projectRoot, 'coverage', 'coverage-summary.json'),
174
- path.join(this.projectRoot, '.nyc_output', 'coverage-summary.json'),
175
- path.join(this.projectRoot, 'coverage', 'coverage-final.json'),
176
- path.join(this.projectRoot, 'coverage.json')
177
- ];
178
-
179
- for (const coveragePath of coveragePaths) {
180
- try {
181
- const content = await fs.readFile(coveragePath, 'utf8');
182
- const coverage = JSON.parse(content);
183
-
184
- // Extract coverage metrics
185
- if (coverage.total) {
186
- return {
187
- lines: Math.round(coverage.total.lines?.pct || 0),
188
- branches: Math.round(coverage.total.branches?.pct || 0),
189
- functions: Math.round(coverage.total.functions?.pct || 0),
190
- statements: Math.round(coverage.total.statements?.pct || 0)
191
- };
192
- }
193
-
194
- // Try aggregate format
195
- if (coverage.aggregate) {
196
- return {
197
- lines: Math.round(coverage.aggregate.line?.pct || 0),
198
- branches: Math.round(coverage.aggregate.branch?.pct || 0),
199
- functions: Math.round(coverage.aggregate.function?.pct || 0),
200
- statements: Math.round(coverage.aggregate.statement?.pct || 0)
201
- };
202
- }
203
- } catch (error) {
204
- // Coverage file not found or invalid
205
- }
206
- }
207
-
208
- // Fallback: simulated coverage for testing
209
- return {
210
- lines: 75 + Math.floor(Math.random() * 20),
211
- branches: 70 + Math.floor(Math.random() * 20),
212
- functions: 80 + Math.floor(Math.random() * 15),
213
- statements: 75 + Math.floor(Math.random() * 20)
214
- };
215
- }
216
-
217
- /**
218
- * Captures baseline for regression testing
219
- */
220
- async captureBaseline() {
221
- // Run tests to get baseline
222
- const testResults = await this.runTests();
223
-
224
- // Get coverage if available
225
- const coverage = await this.getCoverage();
226
-
227
- const baseline = {
228
- timestamp: new Date().toISOString(),
229
- tests: testResults.tests || { total: 0, passed: 0, failed: 0 },
230
- coverage: coverage,
231
- performance: {
232
- duration: testResults.duration || 0
233
- },
234
- environment: {
235
- node: process.version,
236
- platform: process.platform,
237
- arch: process.arch
238
- }
239
- };
240
-
241
- // Save baseline
242
- await fs.mkdir(this.regressionDir, { recursive: true });
243
- const baselinePath = path.join(this.regressionDir, CONFIG.filePatterns.baseline);
244
- await fs.writeFile(baselinePath, JSON.stringify(baseline, null, 2));
245
-
246
- return { baseline, path: baselinePath };
247
- }
248
-
249
- /**
250
- * Loads baseline
251
- */
252
- async loadBaseline() {
253
- const baselinePath = path.join(this.regressionDir, CONFIG.filePatterns.baseline);
254
-
255
- try {
256
- const content = await fs.readFile(baselinePath, 'utf8');
257
- return JSON.parse(content);
258
- } catch (error) {
259
- return null;
260
- }
261
- }
262
-
263
- /**
264
- * Compares current state with baseline
265
- */
266
- async compareWithBaseline() {
267
- const baseline = await this.loadBaseline();
268
-
269
- if (!baseline) {
270
- throw new Error('No baseline found. Run "regression:suite baseline" first.');
271
- }
272
-
273
- // Run current tests
274
- const currentResults = await this.runTests();
275
- const currentCoverage = await this.getCoverage();
276
-
277
- const comparison = {
278
- baseline: {
279
- timestamp: baseline.timestamp,
280
- tests: baseline.tests,
281
- coverage: baseline.coverage,
282
- performance: baseline.performance
283
- },
284
- current: {
285
- timestamp: new Date().toISOString(),
286
- tests: currentResults.tests || { total: 0, passed: 0, failed: 0 },
287
- coverage: currentCoverage,
288
- performance: { duration: currentResults.duration || 0 }
289
- },
290
- regressions: [],
291
- improvements: []
292
- };
293
-
294
- // Compare tests
295
- if (baseline.tests && currentResults.tests) {
296
- const testDiff = currentResults.tests.passed - baseline.tests.passed;
297
- if (testDiff < 0) {
298
- comparison.regressions.push({
299
- type: 'tests',
300
- message: `${Math.abs(testDiff)} fewer tests passing`
301
- });
302
- } else if (testDiff > 0) {
303
- comparison.improvements.push({
304
- type: 'tests',
305
- message: `${testDiff} more tests passing`
306
- });
307
- }
308
- }
309
-
310
- // Compare coverage
311
- if (baseline.coverage && currentCoverage) {
312
- const coverageDiff = currentCoverage.lines - baseline.coverage.lines;
313
- if (coverageDiff < -5) {
314
- comparison.regressions.push({
315
- type: 'coverage',
316
- message: `${Math.abs(coverageDiff)}% coverage decrease`
317
- });
318
- } else if (coverageDiff > 5) {
319
- comparison.improvements.push({
320
- type: 'coverage',
321
- message: `${coverageDiff}% coverage increase`
322
- });
323
- }
324
- }
325
-
326
- // Compare performance
327
- if (baseline.performance && currentResults.duration) {
328
- const perfChange = ((currentResults.duration - baseline.performance.duration) /
329
- baseline.performance.duration) * 100;
330
- if (perfChange > 20) {
331
- comparison.regressions.push({
332
- type: 'performance',
333
- message: `${Math.round(perfChange)}% slower`
334
- });
335
- } else if (perfChange < -10) {
336
- comparison.improvements.push({
337
- type: 'performance',
338
- message: `${Math.abs(Math.round(perfChange))}% faster`
339
- });
340
- }
341
- }
342
-
343
- return comparison;
344
- }
345
-
346
- /**
347
- * Analyzes test history for patterns
348
- */
349
- async analyzePatterns(options = {}) {
350
- const historyPath = path.join(this.regressionDir, CONFIG.filePatterns.history);
351
-
352
- // Load history
353
- let history = [];
354
- try {
355
- const content = await fs.readFile(historyPath, 'utf8');
356
- history = JSON.parse(content);
357
- } catch (error) {
358
- // No history yet
359
- }
360
-
361
- const analysis = {
362
- flaky: [],
363
- slow: [],
364
- trends: {}
365
- };
366
-
367
- if (options.flaky) {
368
- // Analyze for flaky tests
369
- const testRuns = {};
370
- for (const run of history) {
371
- if (run.test) {
372
- if (!testRuns[run.test]) {
373
- testRuns[run.test] = [];
374
- }
375
- testRuns[run.test].push(run.passed);
376
- }
377
- }
378
-
379
- for (const [test, results] of Object.entries(testRuns)) {
380
- if (results.length < 3) continue;
381
-
382
- const passCount = results.filter(r => r).length;
383
- const failCount = results.filter(r => !r).length;
384
-
385
- if (passCount > 0 && failCount > 0) {
386
- const passRate = (passCount / results.length) * 100;
387
- if (passRate < 95 && passRate > 5) {
388
- analysis.flaky.push({
389
- test,
390
- passRate: Math.round(passRate),
391
- runs: results.length
392
- });
393
- }
394
- }
395
- }
396
- }
397
-
398
- if (options.slow) {
399
- // Identify slow tests (simulated)
400
- const threshold = options.slowThreshold || CONFIG.defaults.slowTestThreshold;
401
- analysis.slow = [
402
- { name: 'integration test', duration: 2500 },
403
- { name: 'database test', duration: 1800 }
404
- ].filter(t => t.duration > threshold);
405
- }
406
-
407
- return analysis;
408
- }
409
-
410
- /**
411
- * Adds entry to history
412
- */
413
- async addToHistory(entry) {
414
- const historyPath = path.join(this.regressionDir, CONFIG.filePatterns.history);
415
-
416
- let history = [];
417
- try {
418
- const content = await fs.readFile(historyPath, 'utf8');
419
- history = JSON.parse(content);
420
- } catch (error) {
421
- // No history yet
422
- }
423
-
424
- history.push(entry);
425
-
426
- // Limit history size
427
- if (history.length > CONFIG.defaults.maxHistorySize) {
428
- history = history.slice(-CONFIG.defaults.maxHistorySize);
429
- }
430
-
431
- await fs.mkdir(this.regressionDir, { recursive: true });
432
- await fs.writeFile(historyPath, JSON.stringify(history, null, 2));
433
-
434
- return history;
435
- }
436
-
437
- /**
438
- * Gets test trends from history
439
- */
440
- async getTrends(limit = 10) {
441
- const historyPath = path.join(this.regressionDir, CONFIG.filePatterns.history);
442
-
443
- let history = [];
444
- try {
445
- const content = await fs.readFile(historyPath, 'utf8');
446
- history = JSON.parse(content);
447
- } catch (error) {
448
- return { history: [], statistics: {} };
449
- }
450
-
451
- const recentRuns = history.slice(-limit);
452
-
453
- // Calculate statistics
454
- const statistics = {
455
- totalRuns: history.length,
456
- avgSuccessRate: 0,
457
- avgDuration: 0,
458
- avgCoverage: 0
459
- };
460
-
461
- let successCount = 0;
462
- let durationCount = 0;
463
- let coverageCount = 0;
464
-
465
- for (const run of history) {
466
- if (run.tests && run.tests.total > 0) {
467
- statistics.avgSuccessRate += (run.tests.passed / run.tests.total);
468
- successCount++;
469
- }
470
- if (run.duration) {
471
- statistics.avgDuration += run.duration;
472
- durationCount++;
473
- }
474
- if (run.coverage && run.coverage.lines) {
475
- statistics.avgCoverage += run.coverage.lines;
476
- coverageCount++;
477
- }
478
- }
479
-
480
- if (successCount > 0) {
481
- statistics.avgSuccessRate = Math.round((statistics.avgSuccessRate / successCount) * 100);
482
- }
483
- if (durationCount > 0) {
484
- statistics.avgDuration = Math.round(statistics.avgDuration / durationCount);
485
- }
486
- if (coverageCount > 0) {
487
- statistics.avgCoverage = Math.round(statistics.avgCoverage / coverageCount);
488
- }
489
-
490
- return { history: recentRuns, statistics };
491
- }
492
-
493
- /**
494
- * Generates comprehensive report
495
- */
496
- async generateReport() {
497
- // Gather all data
498
- const testResults = await this.runTests();
499
- const coverage = await this.getCoverage();
500
- const baseline = await this.loadBaseline();
501
- const { history, statistics } = await this.getTrends();
502
-
503
- // Create report
504
- const report = {
505
- timestamp: new Date().toISOString(),
506
- summary: {
507
- tests: testResults.tests || { total: 0, passed: 0, failed: 0 },
508
- coverage: coverage,
509
- duration: testResults.duration,
510
- success: testResults.success
511
- },
512
- baseline: baseline ? {
513
- timestamp: baseline.timestamp,
514
- comparison: await this.compareWithBaseline()
515
- } : null,
516
- trends: statistics,
517
- environment: {
518
- node: process.version,
519
- platform: process.platform,
520
- arch: process.arch,
521
- cwd: this.projectRoot
522
- }
523
- };
524
-
525
- // Save report
526
- await fs.mkdir(this.regressionDir, { recursive: true });
527
- const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
528
- const reportPath = path.join(
529
- this.regressionDir,
530
- CONFIG.filePatterns.report.replace('{timestamp}', timestamp)
531
- );
532
-
533
- await fs.writeFile(reportPath, JSON.stringify(report, null, 2));
534
-
535
- // Add to history
536
- await this.addToHistory({
537
- timestamp: report.timestamp,
538
- tests: report.summary.tests,
539
- coverage: report.summary.coverage,
540
- duration: report.summary.duration
541
- });
542
-
543
- return { report, path: reportPath };
544
- }
545
-
546
- /**
547
- * Checks coverage against threshold
548
- */
549
- checkCoverageThreshold(coverage, threshold) {
550
- const failures = [];
551
-
552
- if (coverage.lines < threshold) {
553
- failures.push({
554
- metric: 'lines',
555
- actual: coverage.lines,
556
- threshold
557
- });
558
- }
559
- if (coverage.branches < threshold) {
560
- failures.push({
561
- metric: 'branches',
562
- actual: coverage.branches,
563
- threshold
564
- });
565
- }
566
- if (coverage.functions < threshold) {
567
- failures.push({
568
- metric: 'functions',
569
- actual: coverage.functions,
570
- threshold
571
- });
572
- }
573
-
574
- return failures;
575
- }
576
- }
577
-
578
- module.exports = RegressionAnalyzer;