claude-autopm 1.18.0 → 1.20.1

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 (108) hide show
  1. package/README.md +159 -0
  2. package/autopm/.claude/agents/README.md +1 -1
  3. package/autopm/.claude/agents/core/mcp-manager.md +1 -1
  4. package/autopm/.claude/agents/decision-matrices/python-backend-selection.md +25 -25
  5. package/autopm/.claude/agents/decision-matrices/ui-framework-selection.md +43 -43
  6. package/autopm/.claude/agents/devops/github-operations-specialist.md +1 -1
  7. package/autopm/.claude/agents/frameworks/README.md +5 -5
  8. package/autopm/.claude/agents/frameworks/e2e-test-engineer.md +1 -1
  9. package/autopm/.claude/agents/frameworks/nats-messaging-expert.md +1 -1
  10. package/autopm/.claude/agents/frameworks/react-frontend-engineer.md +1 -1
  11. package/autopm/.claude/agents/frameworks/react-ui-expert.md +3 -3
  12. package/autopm/.claude/agents/frameworks/tailwindcss-expert.md +3 -3
  13. package/autopm/.claude/agents/frameworks/ux-design-expert.md +3 -3
  14. package/autopm/.claude/commands/infrastructure/traefik-setup.md +1 -1
  15. package/autopm/.claude/commands/playwright/test-scaffold.md +1 -1
  16. package/autopm/.claude/commands/pm/context.md +11 -0
  17. package/autopm/.claude/commands/pm/epic-decompose.md +25 -2
  18. package/autopm/.claude/commands/pm/epic-oneshot.md +13 -0
  19. package/autopm/.claude/commands/pm/epic-start.md +19 -0
  20. package/autopm/.claude/commands/pm/epic-sync-modular.md +10 -10
  21. package/autopm/.claude/commands/pm/epic-sync.md +14 -14
  22. package/autopm/.claude/commands/pm/issue-start.md +50 -5
  23. package/autopm/.claude/commands/pm/issue-sync.md +15 -15
  24. package/autopm/.claude/commands/pm/what-next.md +11 -0
  25. package/autopm/.claude/commands/ui/bootstrap-scaffold.md +6 -5
  26. package/autopm/.claude/commands/ui/tailwind-system.md +1 -1
  27. package/autopm/.claude/examples/mcp/playwright-mcp.md +2 -2
  28. package/autopm/.claude/examples/mcp-servers.example.json +2 -2
  29. package/autopm/.claude/hooks/docker-first-enforcement.sh +1 -1
  30. package/autopm/.claude/mcp/MCP-REGISTRY.md +1 -1
  31. package/autopm/.claude/mcp/playwright-mcp.md +2 -2
  32. package/autopm/.claude/rules/agent-coordination.md +26 -24
  33. package/autopm/.claude/rules/docker-first-development.md +1 -1
  34. package/autopm/.claude/rules/infrastructure-pipeline.md +1 -1
  35. package/autopm/.claude/rules/ui-development-standards.md +1 -1
  36. package/autopm/.claude/rules/visual-testing.md +3 -3
  37. package/autopm/.claude/scripts/azure/active-work.js +2 -2
  38. package/autopm/.claude/scripts/azure/blocked.js +13 -13
  39. package/autopm/.claude/scripts/azure/daily.js +1 -1
  40. package/autopm/.claude/scripts/azure/dashboard.js +1 -1
  41. package/autopm/.claude/scripts/azure/feature-list.js +2 -2
  42. package/autopm/.claude/scripts/azure/feature-status.js +1 -1
  43. package/autopm/.claude/scripts/azure/next-task.js +1 -1
  44. package/autopm/.claude/scripts/azure/search.js +1 -1
  45. package/autopm/.claude/scripts/azure/setup.js +15 -15
  46. package/autopm/.claude/scripts/azure/sprint-report.js +2 -2
  47. package/autopm/.claude/scripts/azure/sync.js +1 -1
  48. package/autopm/.claude/scripts/azure/us-list.js +1 -1
  49. package/autopm/.claude/scripts/azure/us-status.js +1 -1
  50. package/autopm/.claude/scripts/azure/validate.js +13 -13
  51. package/autopm/.claude/scripts/lib/frontmatter-utils.sh +42 -7
  52. package/autopm/.claude/scripts/lib/logging-utils.sh +20 -16
  53. package/autopm/.claude/scripts/lib/validation-utils.sh +1 -1
  54. package/autopm/.claude/scripts/pm/context.js +338 -0
  55. package/autopm/.claude/scripts/pm/issue-sync/format-comment.sh +3 -3
  56. package/autopm/.claude/scripts/pm/lib/README.md +85 -0
  57. package/autopm/.claude/scripts/pm/lib/logger.js +78 -0
  58. package/autopm/.claude/scripts/pm/next.js +25 -1
  59. package/autopm/.claude/scripts/pm/what-next.js +660 -0
  60. package/autopm/.claude/teams.json +3 -5
  61. package/autopm/.claude/templates/claude-templates/addons/devops-agents.md +2 -2
  62. package/autopm/.claude/templates/claude-templates/addons/docker-agents.md +4 -4
  63. package/autopm/.claude/templates/claude-templates/addons/minimal-agents.md +1 -1
  64. package/autopm/.claude/templates/issue-decomposition/api.yaml +2 -2
  65. package/autopm/.claude/templates/issue-decomposition/auth.yaml +4 -4
  66. package/autopm/.claude/templates/issue-decomposition/crud.yaml +3 -3
  67. package/autopm/.claude/templates/issue-decomposition/default.yaml +1 -1
  68. package/autopm/.claude/templates/issue-decomposition/ui-feature.yaml +2 -2
  69. package/bin/autopm.js +25 -0
  70. package/package.json +1 -2
  71. package/lib/agentExecutor.js.deprecated +0 -101
  72. package/lib/azure/cache.js +0 -80
  73. package/lib/azure/client.js +0 -77
  74. package/lib/azure/formatter.js +0 -177
  75. package/lib/commandHelpers.js +0 -177
  76. package/lib/context/manager.js +0 -290
  77. package/lib/documentation/manager.js +0 -528
  78. package/lib/github/workflow-manager.js +0 -546
  79. package/lib/helpers/azure-batch-api.js +0 -133
  80. package/lib/helpers/azure-cache-manager.js +0 -287
  81. package/lib/helpers/azure-parallel-processor.js +0 -158
  82. package/lib/helpers/azure-work-item-create.js +0 -278
  83. package/lib/helpers/gh-issue-create.js +0 -250
  84. package/lib/helpers/interactive-prompt.js +0 -336
  85. package/lib/helpers/output-manager.js +0 -335
  86. package/lib/helpers/progress-indicator.js +0 -258
  87. package/lib/performance/benchmarker.js +0 -429
  88. package/lib/pm/epic-decomposer.js +0 -273
  89. package/lib/pm/epic-syncer.js +0 -221
  90. package/lib/prdMetadata.js +0 -270
  91. package/lib/providers/azure/index.js +0 -234
  92. package/lib/providers/factory.js +0 -87
  93. package/lib/providers/github/index.js +0 -204
  94. package/lib/providers/interface.js +0 -73
  95. package/lib/python/scaffold-manager.js +0 -576
  96. package/lib/react/scaffold-manager.js +0 -745
  97. package/lib/regression/analyzer.js +0 -578
  98. package/lib/release/manager.js +0 -324
  99. package/lib/tailwind/manager.js +0 -486
  100. package/lib/traefik/manager.js +0 -484
  101. package/lib/utils/colors.js +0 -126
  102. package/lib/utils/config.js +0 -317
  103. package/lib/utils/filesystem.js +0 -316
  104. package/lib/utils/logger.js +0 -135
  105. package/lib/utils/prompts.js +0 -294
  106. package/lib/utils/shell.js +0 -237
  107. package/lib/validators/email-validator.js +0 -337
  108. package/lib/workflow/manager.js +0 -449
@@ -1,546 +0,0 @@
1
- /**
2
- * GitHub Workflow Manager
3
- * Centralized GitHub Actions workflow management functionality
4
- */
5
-
6
- const fs = require('fs').promises;
7
- const path = require('path');
8
-
9
- /**
10
- * Configuration
11
- */
12
- const CONFIG = {
13
- directories: {
14
- workflows: '.github/workflows'
15
- },
16
- defaults: {
17
- os: 'ubuntu-latest',
18
- nodeVersion: '18.x'
19
- },
20
- fileExtensions: ['.yml', '.yaml']
21
- };
22
-
23
- /**
24
- * Workflow templates
25
- */
26
- const TEMPLATES = {
27
- node: {
28
- name: 'Node.js CI',
29
- content: `name: Node.js CI
30
-
31
- on:
32
- push:
33
- branches: [ main, develop ]
34
- pull_request:
35
- branches: [ main ]
36
-
37
- jobs:
38
- test:
39
- runs-on: ubuntu-latest
40
-
41
- strategy:
42
- matrix:
43
- node-version: [16.x, 18.x, 20.x]
44
-
45
- steps:
46
- - uses: actions/checkout@v3
47
- - name: Use Node.js \${{ matrix.node-version }}
48
- uses: actions/setup-node@v3
49
- with:
50
- node-version: \${{ matrix.node-version }}
51
- cache: 'npm'
52
- - run: npm ci
53
- - run: npm test
54
- - run: npm run build --if-present`
55
- },
56
- python: {
57
- name: 'Python CI',
58
- content: `name: Python CI
59
-
60
- on:
61
- push:
62
- branches: [ main, develop ]
63
- pull_request:
64
- branches: [ main ]
65
-
66
- jobs:
67
- test:
68
- runs-on: ubuntu-latest
69
-
70
- strategy:
71
- matrix:
72
- python-version: ["3.8", "3.9", "3.10", "3.11"]
73
-
74
- steps:
75
- - uses: actions/checkout@v3
76
- - name: Set up Python \${{ matrix.python-version }}
77
- uses: actions/setup-python@v4
78
- with:
79
- python-version: \${{ matrix.python-version }}
80
- - name: Install dependencies
81
- run: |
82
- python -m pip install --upgrade pip
83
- pip install pytest
84
- if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
85
- - name: Test with pytest
86
- run: |
87
- pytest`
88
- },
89
- docker: {
90
- name: 'Docker Build',
91
- content: `name: Docker Build
92
-
93
- on:
94
- push:
95
- branches: [ main ]
96
- tags: [ 'v*' ]
97
-
98
- jobs:
99
- docker:
100
- runs-on: ubuntu-latest
101
-
102
- steps:
103
- - uses: actions/checkout@v3
104
-
105
- - name: Set up Docker Buildx
106
- uses: docker/setup-buildx-action@v2
107
-
108
- - name: Log in to Docker Hub
109
- uses: docker/login-action@v2
110
- with:
111
- username: \${{ secrets.DOCKER_USERNAME }}
112
- password: \${{ secrets.DOCKER_TOKEN }}
113
-
114
- - name: Build and push
115
- uses: docker/build-push-action@v4
116
- with:
117
- context: .
118
- push: true
119
- tags: user/app:latest`
120
- }
121
- };
122
-
123
- class GitHubWorkflowManager {
124
- constructor(projectRoot = process.cwd()) {
125
- this.projectRoot = projectRoot;
126
- this.workflowsDir = path.join(projectRoot, CONFIG.directories.workflows);
127
- }
128
-
129
- /**
130
- * Creates a new workflow
131
- */
132
- async createWorkflow(name, options = {}) {
133
- await fs.mkdir(this.workflowsDir, { recursive: true });
134
-
135
- const workflow = {
136
- name: name.charAt(0).toUpperCase() + name.slice(1),
137
- on: options.trigger || ['push', 'pull_request'],
138
- jobs: {
139
- build: {
140
- 'runs-on': options.os || CONFIG.defaults.os,
141
- steps: [
142
- { uses: 'actions/checkout@v3' },
143
- { name: 'Setup', run: 'echo "Setting up..."' },
144
- { name: 'Build', run: 'echo "Building..."' },
145
- { name: 'Test', run: 'echo "Testing..."' }
146
- ]
147
- }
148
- }
149
- };
150
-
151
- const workflowPath = path.join(this.workflowsDir, `${name}.yml`);
152
- await fs.writeFile(workflowPath, this.generateYAML(workflow));
153
-
154
- return {
155
- path: workflowPath,
156
- name: workflow.name,
157
- triggers: Array.isArray(workflow.on) ? workflow.on : [workflow.on]
158
- };
159
- }
160
-
161
- /**
162
- * Creates a test workflow
163
- */
164
- async createTestWorkflow(options = {}) {
165
- await fs.mkdir(this.workflowsDir, { recursive: true });
166
-
167
- const testWorkflow = {
168
- name: 'Tests',
169
- on: {
170
- push: {
171
- branches: ['main', 'develop']
172
- },
173
- pull_request: {
174
- branches: ['main']
175
- }
176
- },
177
- jobs: {
178
- test: {
179
- 'runs-on': options.os || CONFIG.defaults.os,
180
- steps: [
181
- { uses: 'actions/checkout@v3' },
182
- {
183
- name: 'Setup Node.js',
184
- uses: 'actions/setup-node@v3',
185
- with: {
186
- 'node-version': options.nodeVersion || CONFIG.defaults.nodeVersion,
187
- cache: 'npm'
188
- }
189
- },
190
- { name: 'Install dependencies', run: 'npm ci' },
191
- { name: 'Run tests', run: 'npm test' },
192
- {
193
- name: 'Upload coverage',
194
- if: 'success()',
195
- uses: 'codecov/codecov-action@v3'
196
- }
197
- ]
198
- }
199
- }
200
- };
201
-
202
- const workflowPath = path.join(this.workflowsDir, 'test.yml');
203
- await fs.writeFile(workflowPath, this.generateYAML(testWorkflow));
204
-
205
- return {
206
- path: workflowPath,
207
- name: testWorkflow.name
208
- };
209
- }
210
-
211
- /**
212
- * Creates a release workflow
213
- */
214
- async createReleaseWorkflow(options = {}) {
215
- await fs.mkdir(this.workflowsDir, { recursive: true });
216
-
217
- let trigger = {};
218
- if (options.trigger === 'tag') {
219
- trigger = {
220
- push: {
221
- tags: ['v*']
222
- }
223
- };
224
- } else {
225
- trigger = {
226
- release: {
227
- types: ['created']
228
- }
229
- };
230
- }
231
-
232
- const releaseWorkflow = {
233
- name: 'Release',
234
- on: trigger,
235
- jobs: {
236
- release: {
237
- 'runs-on': CONFIG.defaults.os,
238
- steps: [
239
- { uses: 'actions/checkout@v3' },
240
- {
241
- name: 'Setup Node.js',
242
- uses: 'actions/setup-node@v3',
243
- with: {
244
- 'node-version': CONFIG.defaults.nodeVersion,
245
- 'registry-url': 'https://registry.npmjs.org/'
246
- }
247
- },
248
- { name: 'Install dependencies', run: 'npm ci' },
249
- { name: 'Build', run: 'npm run build --if-present' },
250
- { name: 'Test', run: 'npm test' },
251
- {
252
- name: 'Publish to npm',
253
- if: options.publish !== false,
254
- run: 'npm publish',
255
- env: {
256
- NODE_AUTH_TOKEN: '${{ secrets.NPM_TOKEN }}'
257
- }
258
- },
259
- {
260
- name: 'Create GitHub Release',
261
- uses: 'softprops/action-gh-release@v1',
262
- if: "startsWith(github.ref, 'refs/tags/')",
263
- with: {
264
- files: 'dist/*'
265
- }
266
- }
267
- ]
268
- }
269
- }
270
- };
271
-
272
- const workflowPath = path.join(this.workflowsDir, 'release.yml');
273
- await fs.writeFile(workflowPath, this.generateYAML(releaseWorkflow));
274
-
275
- return {
276
- path: workflowPath,
277
- name: releaseWorkflow.name,
278
- trigger: options.trigger
279
- };
280
- }
281
-
282
- /**
283
- * Applies a template
284
- */
285
- async applyTemplate(templateName) {
286
- const template = TEMPLATES[templateName];
287
-
288
- if (!template) {
289
- throw new Error(`Unknown template: ${templateName}`);
290
- }
291
-
292
- await fs.mkdir(this.workflowsDir, { recursive: true });
293
-
294
- const filename = `${templateName}.yml`;
295
- const workflowPath = path.join(this.workflowsDir, filename);
296
- await fs.writeFile(workflowPath, template.content);
297
-
298
- return {
299
- path: workflowPath,
300
- template: templateName,
301
- name: template.name
302
- };
303
- }
304
-
305
- /**
306
- * Lists available templates
307
- */
308
- getTemplates() {
309
- return Object.entries(TEMPLATES).map(([key, template]) => ({
310
- key,
311
- name: template.name,
312
- description: this.getTemplateDescription(key)
313
- }));
314
- }
315
-
316
- /**
317
- * Lists existing workflows
318
- */
319
- async listWorkflows() {
320
- try {
321
- const files = await fs.readdir(this.workflowsDir);
322
- const workflows = [];
323
-
324
- for (const file of files) {
325
- if (CONFIG.fileExtensions.some(ext => file.endsWith(ext))) {
326
- const content = await fs.readFile(path.join(this.workflowsDir, file), 'utf8');
327
- workflows.push({
328
- file,
329
- name: this.extractWorkflowName(content),
330
- triggers: this.extractTriggers(content)
331
- });
332
- }
333
- }
334
-
335
- return workflows;
336
- } catch (error) {
337
- return [];
338
- }
339
- }
340
-
341
- /**
342
- * Validates workflows
343
- */
344
- async validateWorkflows() {
345
- try {
346
- const files = await fs.readdir(this.workflowsDir);
347
- const results = [];
348
-
349
- for (const file of files) {
350
- if (CONFIG.fileExtensions.some(ext => file.endsWith(ext))) {
351
- const content = await fs.readFile(path.join(this.workflowsDir, file), 'utf8');
352
- const issues = this.validateWorkflow(content);
353
- results.push({
354
- file,
355
- valid: issues.length === 0,
356
- issues
357
- });
358
- }
359
- }
360
-
361
- return results;
362
- } catch (error) {
363
- return [];
364
- }
365
- }
366
-
367
- /**
368
- * Updates a workflow
369
- */
370
- async updateWorkflow(workflowName, updates = {}) {
371
- const workflowPath = path.join(this.workflowsDir, `${workflowName}.yml`);
372
- let content = await fs.readFile(workflowPath, 'utf8');
373
-
374
- // Add job if requested
375
- if (updates.addJob) {
376
- const jobName = updates.addJob;
377
- const jobDefinition = `
378
- ${jobName}:
379
- runs-on: ubuntu-latest
380
- steps:
381
- - uses: actions/checkout@v3
382
- - name: ${jobName}
383
- run: echo "Running ${jobName}"`;
384
-
385
- // Add before the last line
386
- const lines = content.split('\n');
387
- const jobsIndex = lines.findIndex(l => l.startsWith('jobs:'));
388
- if (jobsIndex >= 0) {
389
- // Find the end of jobs section
390
- let insertIndex = lines.length;
391
- for (let i = jobsIndex + 1; i < lines.length; i++) {
392
- if (!lines[i].startsWith(' ')) {
393
- insertIndex = i;
394
- break;
395
- }
396
- }
397
- lines.splice(insertIndex, 0, jobDefinition);
398
- content = lines.join('\n');
399
- }
400
- }
401
-
402
- await fs.writeFile(workflowPath, content);
403
-
404
- return {
405
- path: workflowPath,
406
- updated: true,
407
- changes: updates
408
- };
409
- }
410
-
411
- /**
412
- * Validates workflow content
413
- */
414
- validateWorkflow(content) {
415
- const issues = [];
416
- const lines = content.split('\n');
417
-
418
- // Check for required fields
419
- if (!content.includes('name:')) {
420
- issues.push('Missing workflow name');
421
- }
422
-
423
- if (!content.includes('on:')) {
424
- issues.push('Missing trigger events (on:)');
425
- }
426
-
427
- if (!content.includes('jobs:')) {
428
- issues.push('Missing jobs section');
429
- }
430
-
431
- // Check each job has runs-on
432
- const jobsIndex = lines.findIndex(l => l.trim().startsWith('jobs:'));
433
- if (jobsIndex >= 0) {
434
- let currentJob = null;
435
- let hasRunsOn = false;
436
-
437
- for (let i = jobsIndex + 1; i < lines.length; i++) {
438
- const line = lines[i];
439
- const trimmed = line.trim();
440
-
441
- // New job starts
442
- if (line.match(/^ \w+:/)) {
443
- if (currentJob && !hasRunsOn) {
444
- issues.push(`Job '${currentJob}' missing runs-on`);
445
- }
446
- currentJob = trimmed.replace(':', '');
447
- hasRunsOn = false;
448
- }
449
-
450
- // Check for runs-on
451
- if (trimmed.startsWith('runs-on:')) {
452
- hasRunsOn = true;
453
- }
454
-
455
- // End of jobs section
456
- if (line.length > 0 && !line.startsWith(' ')) {
457
- break;
458
- }
459
- }
460
-
461
- // Check last job
462
- if (currentJob && !hasRunsOn) {
463
- issues.push(`Job '${currentJob}' missing runs-on`);
464
- }
465
- }
466
-
467
- return issues;
468
- }
469
-
470
- /**
471
- * Extracts workflow name from content
472
- */
473
- extractWorkflowName(content) {
474
- const match = content.match(/name:\s*(.+)/);
475
- return match ? match[1].trim() : 'Unnamed';
476
- }
477
-
478
- /**
479
- * Extracts triggers from workflow content
480
- */
481
- extractTriggers(content) {
482
- const match = content.match(/on:\s*(.+)/);
483
- if (!match) return 'None';
484
-
485
- const triggerLine = match[1].trim();
486
- if (triggerLine.startsWith('[')) {
487
- return triggerLine.slice(1, -1);
488
- }
489
- return triggerLine;
490
- }
491
-
492
- /**
493
- * Gets template description
494
- */
495
- getTemplateDescription(name) {
496
- const descriptions = {
497
- node: 'Node.js CI/CD with matrix testing',
498
- python: 'Python testing with pytest',
499
- docker: 'Docker build and push to registry'
500
- };
501
- return descriptions[name] || 'Workflow template';
502
- }
503
-
504
- /**
505
- * Generates simple YAML
506
- */
507
- generateYAML(obj, indent = 0) {
508
- let yaml = '';
509
- const spaces = ' '.repeat(indent);
510
-
511
- for (const [key, value] of Object.entries(obj)) {
512
- if (value === null || value === undefined) {
513
- yaml += `${spaces}${key}:\n`;
514
- } else if (typeof value === 'string') {
515
- // Handle multi-line strings
516
- if (value.includes('\n')) {
517
- yaml += `${spaces}${key}: |\n`;
518
- value.split('\n').forEach(line => {
519
- yaml += `${spaces} ${line}\n`;
520
- });
521
- } else {
522
- yaml += `${spaces}${key}: ${value}\n`;
523
- }
524
- } else if (typeof value === 'boolean') {
525
- yaml += `${spaces}${key}: ${value}\n`;
526
- } else if (typeof value === 'object' && !Array.isArray(value)) {
527
- yaml += `${spaces}${key}:\n${this.generateYAML(value, indent + 1)}`;
528
- } else if (Array.isArray(value)) {
529
- yaml += `${spaces}${key}:\n`;
530
- for (const item of value) {
531
- if (typeof item === 'object') {
532
- yaml += `${spaces}- ${this.generateYAML(item, indent + 1).trim()}\n`;
533
- } else {
534
- yaml += `${spaces} - ${item}\n`;
535
- }
536
- }
537
- } else {
538
- yaml += `${spaces}${key}: ${value}\n`;
539
- }
540
- }
541
-
542
- return yaml;
543
- }
544
- }
545
-
546
- module.exports = GitHubWorkflowManager;
@@ -1,133 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Azure DevOps Batch API Helper
4
- * Optimizes API calls by batching multiple requests
5
- */
6
-
7
- const https = require('https');
8
-
9
- class AzureBatchAPI {
10
- constructor(org, project, pat) {
11
- this.org = org;
12
- this.project = project;
13
- this.pat = pat;
14
- this.baseUrl = `dev.azure.com/${org}/${project}/_apis`;
15
- this.batchSize = 200; // Azure DevOps batch limit
16
- }
17
-
18
- /**
19
- * Execute batch API requests
20
- * @param {Array} requests - Array of request objects
21
- * @returns {Promise<Array>} Array of responses
22
- */
23
- async executeBatch(requests) {
24
- if (!requests || requests.length === 0) {
25
- return [];
26
- }
27
-
28
- // Split into chunks if needed
29
- const chunks = this.chunkRequests(requests, this.batchSize);
30
- const results = [];
31
-
32
- for (const chunk of chunks) {
33
- const batchResults = await this.executeBatchChunk(chunk);
34
- results.push(...batchResults);
35
- }
36
-
37
- return results;
38
- }
39
-
40
- /**
41
- * Execute a single batch chunk
42
- * @private
43
- */
44
- async executeBatchChunk(requests) {
45
- const batchRequest = {
46
- requests: requests.map((req, index) => ({
47
- id: index.toString(),
48
- method: req.method || 'GET',
49
- url: `/${this.baseUrl}/${req.endpoint}?api-version=7.0`,
50
- headers: req.headers || {},
51
- body: req.body
52
- }))
53
- };
54
-
55
- return new Promise((resolve, reject) => {
56
- const options = {
57
- hostname: 'dev.azure.com',
58
- path: `/${this.org}/_apis/$batch?api-version=7.0`,
59
- method: 'POST',
60
- headers: {
61
- 'Authorization': `Basic ${Buffer.from(`:${this.pat}`).toString('base64')}`,
62
- 'Content-Type': 'application/json'
63
- }
64
- };
65
-
66
- const req = https.request(options, (res) => {
67
- let data = '';
68
- res.on('data', chunk => data += chunk);
69
- res.on('end', () => {
70
- try {
71
- const batchResponse = JSON.parse(data);
72
- const results = batchResponse.responses.map(r => {
73
- try {
74
- return JSON.parse(r.body);
75
- } catch {
76
- return r.body;
77
- }
78
- });
79
- resolve(results);
80
- } catch (error) {
81
- reject(error);
82
- }
83
- });
84
- });
85
-
86
- req.on('error', reject);
87
- req.write(JSON.stringify(batchRequest));
88
- req.end();
89
- });
90
- }
91
-
92
- /**
93
- * Chunk requests into batches
94
- * @private
95
- */
96
- chunkRequests(requests, size) {
97
- const chunks = [];
98
- for (let i = 0; i < requests.length; i += size) {
99
- chunks.push(requests.slice(i, i + size));
100
- }
101
- return chunks;
102
- }
103
-
104
- /**
105
- * Batch fetch work items
106
- */
107
- async batchFetchWorkItems(ids) {
108
- const requests = ids.map(id => ({
109
- endpoint: `wit/workitems/${id}`,
110
- method: 'GET'
111
- }));
112
-
113
- return this.executeBatch(requests);
114
- }
115
-
116
- /**
117
- * Batch update work items
118
- */
119
- async batchUpdateWorkItems(updates) {
120
- const requests = updates.map(update => ({
121
- endpoint: `wit/workitems/${update.id}`,
122
- method: 'PATCH',
123
- headers: {
124
- 'Content-Type': 'application/json-patch+json'
125
- },
126
- body: update.operations
127
- }));
128
-
129
- return this.executeBatch(requests);
130
- }
131
- }
132
-
133
- module.exports = AzureBatchAPI;