@sparkleideas/claims 3.0.0-alpha.8-patch.16 → 3.0.0-alpha.8-patch.18

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 (69) hide show
  1. package/package.json +2 -2
  2. package/dist/api/cli-commands.d.ts +0 -84
  3. package/dist/api/cli-commands.d.ts.map +0 -1
  4. package/dist/api/cli-commands.js +0 -1223
  5. package/dist/api/cli-commands.js.map +0 -1
  6. package/dist/api/cli-types.d.ts +0 -82
  7. package/dist/api/cli-types.d.ts.map +0 -1
  8. package/dist/api/cli-types.js +0 -87
  9. package/dist/api/cli-types.js.map +0 -1
  10. package/dist/api/index.d.ts +0 -9
  11. package/dist/api/index.d.ts.map +0 -1
  12. package/dist/api/index.js +0 -11
  13. package/dist/api/index.js.map +0 -1
  14. package/dist/api/mcp-tools.d.ts +0 -376
  15. package/dist/api/mcp-tools.d.ts.map +0 -1
  16. package/dist/api/mcp-tools.js +0 -1409
  17. package/dist/api/mcp-tools.js.map +0 -1
  18. package/dist/application/claim-service.d.ts +0 -99
  19. package/dist/application/claim-service.d.ts.map +0 -1
  20. package/dist/application/claim-service.js +0 -440
  21. package/dist/application/claim-service.js.map +0 -1
  22. package/dist/application/index.d.ts +0 -15
  23. package/dist/application/index.d.ts.map +0 -1
  24. package/dist/application/index.js +0 -17
  25. package/dist/application/index.js.map +0 -1
  26. package/dist/application/load-balancer.d.ts +0 -353
  27. package/dist/application/load-balancer.d.ts.map +0 -1
  28. package/dist/application/load-balancer.js +0 -430
  29. package/dist/application/load-balancer.js.map +0 -1
  30. package/dist/application/work-stealing-service.d.ts +0 -149
  31. package/dist/application/work-stealing-service.d.ts.map +0 -1
  32. package/dist/application/work-stealing-service.js +0 -604
  33. package/dist/application/work-stealing-service.js.map +0 -1
  34. package/dist/domain/events.d.ts +0 -308
  35. package/dist/domain/events.d.ts.map +0 -1
  36. package/dist/domain/events.js +0 -241
  37. package/dist/domain/events.js.map +0 -1
  38. package/dist/domain/index.d.ts +0 -16
  39. package/dist/domain/index.d.ts.map +0 -1
  40. package/dist/domain/index.js +0 -34
  41. package/dist/domain/index.js.map +0 -1
  42. package/dist/domain/repositories.d.ts +0 -154
  43. package/dist/domain/repositories.d.ts.map +0 -1
  44. package/dist/domain/repositories.js +0 -9
  45. package/dist/domain/repositories.js.map +0 -1
  46. package/dist/domain/rules.d.ts +0 -105
  47. package/dist/domain/rules.d.ts.map +0 -1
  48. package/dist/domain/rules.js +0 -348
  49. package/dist/domain/rules.js.map +0 -1
  50. package/dist/domain/types.d.ts +0 -578
  51. package/dist/domain/types.d.ts.map +0 -1
  52. package/dist/domain/types.js +0 -99
  53. package/dist/domain/types.js.map +0 -1
  54. package/dist/index.d.ts +0 -33
  55. package/dist/index.d.ts.map +0 -1
  56. package/dist/index.js +0 -51
  57. package/dist/index.js.map +0 -1
  58. package/dist/infrastructure/claim-repository.d.ts +0 -46
  59. package/dist/infrastructure/claim-repository.d.ts.map +0 -1
  60. package/dist/infrastructure/claim-repository.js +0 -274
  61. package/dist/infrastructure/claim-repository.js.map +0 -1
  62. package/dist/infrastructure/event-store.d.ts +0 -62
  63. package/dist/infrastructure/event-store.d.ts.map +0 -1
  64. package/dist/infrastructure/event-store.js +0 -189
  65. package/dist/infrastructure/event-store.js.map +0 -1
  66. package/dist/infrastructure/index.d.ts +0 -10
  67. package/dist/infrastructure/index.d.ts.map +0 -1
  68. package/dist/infrastructure/index.js +0 -12
  69. package/dist/infrastructure/index.js.map +0 -1
@@ -1,1223 +0,0 @@
1
- // @ts-nocheck - CLI integration requires the full @sparkleideas/cli package
2
- /**
3
- * V3 CLI Claims Command
4
- * Issue claiming and work distribution management
5
- *
6
- * Implements:
7
- * - Core claiming commands (list, claim, release, handoff, status)
8
- * - Work stealing commands (stealable, steal, mark-stealable, contest)
9
- * - Load balancing commands (load, rebalance)
10
- */
11
- import { output, confirm, input, callMCPTool, MCPClientError } from './cli-types.js';
12
- // ============================================
13
- // Formatting Helpers
14
- // ============================================
15
- function formatClaimStatus(status) {
16
- switch (status) {
17
- case 'active':
18
- return output.success(status);
19
- case 'blocked':
20
- return output.error(status);
21
- case 'review-requested':
22
- return output.warning(status);
23
- case 'stealable':
24
- return output.warning(status);
25
- case 'completed':
26
- return output.dim(status);
27
- default:
28
- return status;
29
- }
30
- }
31
- function formatClaimantType(type) {
32
- switch (type) {
33
- case 'agent':
34
- return output.info(type);
35
- case 'human':
36
- return output.highlight(type);
37
- default:
38
- return type;
39
- }
40
- }
41
- function formatAgentStatus(status) {
42
- switch (status) {
43
- case 'healthy':
44
- return output.success(status);
45
- case 'overloaded':
46
- return output.error(status);
47
- case 'idle':
48
- return output.dim(status);
49
- default:
50
- return status;
51
- }
52
- }
53
- function formatProgress(progress) {
54
- if (progress >= 75) {
55
- return output.success(`${progress}%`);
56
- }
57
- else if (progress >= 25) {
58
- return output.warning(`${progress}%`);
59
- }
60
- return output.dim(`${progress}%`);
61
- }
62
- function formatTimeRemaining(expiresAt) {
63
- if (!expiresAt)
64
- return output.dim('N/A');
65
- const expiry = new Date(expiresAt);
66
- const now = new Date();
67
- const diffMs = expiry.getTime() - now.getTime();
68
- if (diffMs <= 0) {
69
- return output.error('EXPIRED');
70
- }
71
- const hours = Math.floor(diffMs / (1000 * 60 * 60));
72
- const minutes = Math.floor((diffMs % (1000 * 60 * 60)) / (1000 * 60));
73
- if (hours < 1) {
74
- return output.warning(`${minutes}m`);
75
- }
76
- else if (hours < 4) {
77
- return output.warning(`${hours}h ${minutes}m`);
78
- }
79
- return output.dim(`${hours}h ${minutes}m`);
80
- }
81
- function parseTarget(target) {
82
- // Format: agent:coder-1 or human:alice
83
- const [type, id] = target.split(':');
84
- if (!type || !id || (type !== 'agent' && type !== 'human')) {
85
- throw new Error(`Invalid target format: ${target}. Use agent:<id> or human:<id>`);
86
- }
87
- return { id, type: type };
88
- }
89
- // ============================================
90
- // List Subcommand
91
- // ============================================
92
- const listCommand = {
93
- name: 'list',
94
- aliases: ['ls'],
95
- description: 'List issues',
96
- options: [
97
- {
98
- name: 'available',
99
- short: 'a',
100
- description: 'Show only unclaimed issues',
101
- type: 'boolean',
102
- default: false
103
- },
104
- {
105
- name: 'mine',
106
- short: 'm',
107
- description: 'Show only my claims',
108
- type: 'boolean',
109
- default: false
110
- },
111
- {
112
- name: 'status',
113
- short: 's',
114
- description: 'Filter by status',
115
- type: 'string',
116
- choices: ['active', 'blocked', 'review-requested', 'stealable', 'completed']
117
- },
118
- {
119
- name: 'limit',
120
- short: 'l',
121
- description: 'Maximum number of issues to show',
122
- type: 'number',
123
- default: 20
124
- }
125
- ],
126
- action: async (ctx) => {
127
- const available = ctx.flags.available;
128
- const mine = ctx.flags.mine;
129
- const status = ctx.flags.status;
130
- const limit = ctx.flags.limit;
131
- try {
132
- const result = await callMCPTool('claims/list', {
133
- available,
134
- mine,
135
- status,
136
- limit
137
- });
138
- if (ctx.flags.format === 'json') {
139
- output.printJson(result);
140
- return { success: true, data: result };
141
- }
142
- output.writeln();
143
- if (available) {
144
- output.writeln(output.bold('Available Issues (Unclaimed)'));
145
- }
146
- else if (mine) {
147
- output.writeln(output.bold('My Claims'));
148
- }
149
- else {
150
- output.writeln(output.bold('All Claims'));
151
- }
152
- output.writeln();
153
- if (result.claims.length === 0) {
154
- if (available) {
155
- output.printInfo('No unclaimed issues available');
156
- }
157
- else if (mine) {
158
- output.printInfo('You have no active claims');
159
- }
160
- else {
161
- output.printInfo('No claims found matching criteria');
162
- }
163
- return { success: true, data: result };
164
- }
165
- output.printTable({
166
- columns: [
167
- { key: 'issueId', header: 'Issue', width: 12 },
168
- { key: 'claimant', header: 'Claimant', width: 15 },
169
- { key: 'type', header: 'Type', width: 8 },
170
- { key: 'status', header: 'Status', width: 16 },
171
- { key: 'progress', header: 'Progress', width: 10 },
172
- { key: 'time', header: 'Time Left', width: 12 }
173
- ],
174
- data: result.claims.map(c => ({
175
- issueId: c.issueId,
176
- claimant: c.claimantId || output.dim('unclaimed'),
177
- type: c.claimantType ? formatClaimantType(c.claimantType) : '-',
178
- status: formatClaimStatus(c.status),
179
- progress: formatProgress(c.progress),
180
- time: formatTimeRemaining(c.expiresAt)
181
- }))
182
- });
183
- output.writeln();
184
- output.printInfo(`Showing ${result.claims.length} of ${result.total} issues (${result.available} available)`);
185
- return { success: true, data: result };
186
- }
187
- catch (error) {
188
- if (error instanceof MCPClientError) {
189
- output.printError(`Failed to list issues: ${error.message}`);
190
- }
191
- else {
192
- output.printError(`Unexpected error: ${String(error)}`);
193
- }
194
- return { success: false, exitCode: 1 };
195
- }
196
- }
197
- };
198
- // ============================================
199
- // Claim Subcommand
200
- // ============================================
201
- const claimCommand = {
202
- name: 'claim',
203
- description: 'Claim an issue to work on',
204
- options: [
205
- {
206
- name: 'as',
207
- description: 'Claim as specific identity (agent:id or human:id)',
208
- type: 'string'
209
- }
210
- ],
211
- action: async (ctx) => {
212
- const issueId = ctx.args[0];
213
- const asIdentity = ctx.flags.as;
214
- if (!issueId) {
215
- output.printError('Issue ID is required');
216
- output.printInfo('Usage: claude-flow issues claim <issueId>');
217
- return { success: false, exitCode: 1 };
218
- }
219
- let claimantId = 'current-agent';
220
- let claimantType = 'agent';
221
- if (asIdentity) {
222
- const parsed = parseTarget(asIdentity);
223
- claimantId = parsed.id;
224
- claimantType = parsed.type;
225
- }
226
- output.writeln();
227
- output.printInfo(`Claiming issue ${output.highlight(issueId)}...`);
228
- try {
229
- const result = await callMCPTool('claims/claim', {
230
- issueId,
231
- claimantId,
232
- claimantType
233
- });
234
- output.writeln();
235
- output.printSuccess(`Issue ${issueId} claimed successfully`);
236
- output.writeln();
237
- output.printTable({
238
- columns: [
239
- { key: 'property', header: 'Property', width: 15 },
240
- { key: 'value', header: 'Value', width: 35 }
241
- ],
242
- data: [
243
- { property: 'Issue ID', value: result.issueId },
244
- { property: 'Claimant', value: result.claimantId },
245
- { property: 'Type', value: formatClaimantType(result.claimantType) },
246
- { property: 'Status', value: formatClaimStatus(result.status) },
247
- { property: 'Claimed At', value: new Date(result.claimedAt).toLocaleString() },
248
- { property: 'Expires At', value: result.expiresAt ? new Date(result.expiresAt).toLocaleString() : 'N/A' }
249
- ]
250
- });
251
- if (ctx.flags.format === 'json') {
252
- output.printJson(result);
253
- }
254
- return { success: true, data: result };
255
- }
256
- catch (error) {
257
- if (error instanceof MCPClientError) {
258
- output.printError(`Failed to claim issue: ${error.message}`);
259
- }
260
- else {
261
- output.printError(`Unexpected error: ${String(error)}`);
262
- }
263
- return { success: false, exitCode: 1 };
264
- }
265
- }
266
- };
267
- // ============================================
268
- // Release Subcommand
269
- // ============================================
270
- const releaseCommand = {
271
- name: 'release',
272
- aliases: ['unclaim'],
273
- description: 'Release a claim on an issue',
274
- options: [
275
- {
276
- name: 'force',
277
- short: 'f',
278
- description: 'Force release without confirmation',
279
- type: 'boolean',
280
- default: false
281
- },
282
- {
283
- name: 'reason',
284
- short: 'r',
285
- description: 'Reason for releasing the claim',
286
- type: 'string'
287
- }
288
- ],
289
- action: async (ctx) => {
290
- const issueId = ctx.args[0];
291
- const force = ctx.flags.force;
292
- const reason = ctx.flags.reason;
293
- if (!issueId) {
294
- output.printError('Issue ID is required');
295
- output.printInfo('Usage: claude-flow issues release <issueId>');
296
- return { success: false, exitCode: 1 };
297
- }
298
- if (!force && ctx.interactive) {
299
- const confirmed = await confirm({
300
- message: `Release your claim on issue ${issueId}?`,
301
- default: false
302
- });
303
- if (!confirmed) {
304
- output.printInfo('Operation cancelled');
305
- return { success: true };
306
- }
307
- }
308
- output.writeln();
309
- output.printInfo(`Releasing claim on issue ${output.highlight(issueId)}...`);
310
- try {
311
- await callMCPTool('claims/release', {
312
- issueId,
313
- reason: reason || 'Released by user via CLI'
314
- });
315
- output.writeln();
316
- output.printSuccess(`Claim on issue ${issueId} released`);
317
- if (reason) {
318
- output.printInfo(`Reason: ${reason}`);
319
- }
320
- return { success: true };
321
- }
322
- catch (error) {
323
- if (error instanceof MCPClientError) {
324
- output.printError(`Failed to release claim: ${error.message}`);
325
- }
326
- else {
327
- output.printError(`Unexpected error: ${String(error)}`);
328
- }
329
- return { success: false, exitCode: 1 };
330
- }
331
- }
332
- };
333
- // ============================================
334
- // Handoff Subcommand
335
- // ============================================
336
- const handoffCommand = {
337
- name: 'handoff',
338
- description: 'Request handoff of an issue to another agent or human',
339
- options: [
340
- {
341
- name: 'to',
342
- short: 't',
343
- description: 'Target for handoff (agent:id or human:id)',
344
- type: 'string',
345
- required: true
346
- },
347
- {
348
- name: 'reason',
349
- short: 'r',
350
- description: 'Reason for handoff',
351
- type: 'string'
352
- }
353
- ],
354
- action: async (ctx) => {
355
- const issueId = ctx.args[0];
356
- let target = ctx.flags.to;
357
- const reason = ctx.flags.reason;
358
- if (!issueId) {
359
- output.printError('Issue ID is required');
360
- output.printInfo('Usage: claude-flow issues handoff <issueId> --to <target>');
361
- return { success: false, exitCode: 1 };
362
- }
363
- if (!target && ctx.interactive) {
364
- target = await input({
365
- message: 'Handoff to (agent:id or human:id):',
366
- validate: (v) => {
367
- try {
368
- parseTarget(v);
369
- return true;
370
- }
371
- catch {
372
- return 'Invalid format. Use agent:<id> or human:<id>';
373
- }
374
- }
375
- });
376
- }
377
- if (!target) {
378
- output.printError('Target is required. Use --to flag (e.g., --to agent:coder-1)');
379
- return { success: false, exitCode: 1 };
380
- }
381
- const parsedTarget = parseTarget(target);
382
- output.writeln();
383
- output.printInfo(`Requesting handoff of ${output.highlight(issueId)} to ${output.highlight(target)}...`);
384
- try {
385
- const result = await callMCPTool('claims/handoff', {
386
- issueId,
387
- targetId: parsedTarget.id,
388
- targetType: parsedTarget.type,
389
- reason
390
- });
391
- output.writeln();
392
- output.printSuccess(`Handoff requested for issue ${issueId}`);
393
- output.writeln();
394
- output.printTable({
395
- columns: [
396
- { key: 'property', header: 'Property', width: 15 },
397
- { key: 'value', header: 'Value', width: 35 }
398
- ],
399
- data: [
400
- { property: 'Issue ID', value: result.issueId },
401
- { property: 'From', value: result.fromId },
402
- { property: 'To', value: `${result.toType}:${result.toId}` },
403
- { property: 'Status', value: output.warning(result.status) },
404
- { property: 'Requested At', value: new Date(result.requestedAt).toLocaleString() }
405
- ]
406
- });
407
- if (ctx.flags.format === 'json') {
408
- output.printJson(result);
409
- }
410
- return { success: true, data: result };
411
- }
412
- catch (error) {
413
- if (error instanceof MCPClientError) {
414
- output.printError(`Failed to request handoff: ${error.message}`);
415
- }
416
- else {
417
- output.printError(`Unexpected error: ${String(error)}`);
418
- }
419
- return { success: false, exitCode: 1 };
420
- }
421
- }
422
- };
423
- // ============================================
424
- // Status Subcommand
425
- // ============================================
426
- const statusCommand = {
427
- name: 'status',
428
- description: 'Update or view issue claim status',
429
- options: [
430
- {
431
- name: 'blocked',
432
- short: 'b',
433
- description: 'Mark issue as blocked with reason',
434
- type: 'string'
435
- },
436
- {
437
- name: 'review-requested',
438
- short: 'r',
439
- description: 'Request review for the issue',
440
- type: 'boolean',
441
- default: false
442
- },
443
- {
444
- name: 'active',
445
- short: 'a',
446
- description: 'Mark issue as active (unblock)',
447
- type: 'boolean',
448
- default: false
449
- },
450
- {
451
- name: 'progress',
452
- short: 'p',
453
- description: 'Update progress percentage (0-100)',
454
- type: 'number'
455
- }
456
- ],
457
- action: async (ctx) => {
458
- const issueId = ctx.args[0];
459
- if (!issueId) {
460
- output.printError('Issue ID is required');
461
- output.printInfo('Usage: claude-flow issues status <issueId> [options]');
462
- return { success: false, exitCode: 1 };
463
- }
464
- const blocked = ctx.flags.blocked;
465
- const reviewRequested = ctx.flags['review-requested'];
466
- const active = ctx.flags.active;
467
- const progress = ctx.flags.progress;
468
- // If no update flags, show current status
469
- if (!blocked && !reviewRequested && !active && progress === undefined) {
470
- try {
471
- const result = await callMCPTool('claims/status', { issueId });
472
- if (ctx.flags.format === 'json') {
473
- output.printJson(result);
474
- return { success: true, data: result };
475
- }
476
- output.writeln();
477
- output.printBox([
478
- `Claimant: ${result.claimantId || 'unclaimed'}`,
479
- `Type: ${formatClaimantType(result.claimantType)}`,
480
- `Status: ${formatClaimStatus(result.status)}`,
481
- `Progress: ${formatProgress(result.progress)}`,
482
- '',
483
- `Claimed At: ${result.claimedAt ? new Date(result.claimedAt).toLocaleString() : 'N/A'}`,
484
- `Expires At: ${result.expiresAt ? new Date(result.expiresAt).toLocaleString() : 'N/A'}`,
485
- '',
486
- result.blockedReason ? `Blocked: ${result.blockedReason}` : '',
487
- result.stealableReason ? `Stealable: ${result.stealableReason}` : ''
488
- ].filter(Boolean).join('\n'), `Issue: ${issueId}`);
489
- return { success: true, data: result };
490
- }
491
- catch (error) {
492
- if (error instanceof MCPClientError) {
493
- output.printError(`Failed to get status: ${error.message}`);
494
- }
495
- else {
496
- output.printError(`Unexpected error: ${String(error)}`);
497
- }
498
- return { success: false, exitCode: 1 };
499
- }
500
- }
501
- // Update status
502
- let newStatus;
503
- let reason;
504
- if (blocked) {
505
- newStatus = 'blocked';
506
- reason = blocked;
507
- }
508
- else if (reviewRequested) {
509
- newStatus = 'review-requested';
510
- }
511
- else if (active) {
512
- newStatus = 'active';
513
- }
514
- output.writeln();
515
- output.printInfo(`Updating issue ${output.highlight(issueId)}...`);
516
- try {
517
- const result = await callMCPTool('claims/update', {
518
- issueId,
519
- status: newStatus,
520
- reason,
521
- progress
522
- });
523
- output.writeln();
524
- output.printSuccess(`Issue ${issueId} updated`);
525
- output.writeln();
526
- const updates = [];
527
- if (newStatus) {
528
- updates.push({ property: 'Status', value: formatClaimStatus(result.status) });
529
- }
530
- if (reason) {
531
- updates.push({ property: 'Reason', value: reason });
532
- }
533
- if (progress !== undefined) {
534
- updates.push({ property: 'Progress', value: formatProgress(result.progress) });
535
- }
536
- output.printTable({
537
- columns: [
538
- { key: 'property', header: 'Property', width: 15 },
539
- { key: 'value', header: 'Value', width: 35 }
540
- ],
541
- data: updates
542
- });
543
- if (ctx.flags.format === 'json') {
544
- output.printJson(result);
545
- }
546
- return { success: true, data: result };
547
- }
548
- catch (error) {
549
- if (error instanceof MCPClientError) {
550
- output.printError(`Failed to update status: ${error.message}`);
551
- }
552
- else {
553
- output.printError(`Unexpected error: ${String(error)}`);
554
- }
555
- return { success: false, exitCode: 1 };
556
- }
557
- }
558
- };
559
- // ============================================
560
- // Board Subcommand
561
- // ============================================
562
- const boardCommand = {
563
- name: 'board',
564
- description: 'View who is working on what',
565
- options: [
566
- {
567
- name: 'all',
568
- short: 'a',
569
- description: 'Show all issues including completed',
570
- type: 'boolean',
571
- default: false
572
- },
573
- {
574
- name: 'group',
575
- short: 'g',
576
- description: 'Group by claimant type',
577
- type: 'boolean',
578
- default: false
579
- }
580
- ],
581
- action: async (ctx) => {
582
- const showAll = ctx.flags.all;
583
- const groupBy = ctx.flags.group;
584
- try {
585
- const result = await callMCPTool('claims/board', {
586
- includeCompleted: showAll
587
- });
588
- if (ctx.flags.format === 'json') {
589
- output.printJson(result);
590
- return { success: true, data: result };
591
- }
592
- output.writeln();
593
- output.writeln(output.bold('Issue Claims Board'));
594
- output.writeln();
595
- // Stats summary
596
- output.printList([
597
- `Total Claimed: ${output.highlight(String(result.stats.totalClaimed))}`,
598
- `Available: ${output.success(String(result.stats.totalAvailable))}`,
599
- `By Agents: ${output.info(String(result.stats.agentClaims))}`,
600
- `By Humans: ${output.highlight(String(result.stats.humanClaims))}`
601
- ]);
602
- output.writeln();
603
- if (result.claims.length === 0) {
604
- output.printInfo('No active claims');
605
- return { success: true, data: result };
606
- }
607
- if (groupBy) {
608
- // Group by claimant type
609
- const agents = result.claims.filter(c => c.claimantType === 'agent');
610
- const humans = result.claims.filter(c => c.claimantType === 'human');
611
- if (agents.length > 0) {
612
- output.writeln(output.bold('Agent Claims'));
613
- printBoardTable(agents);
614
- output.writeln();
615
- }
616
- if (humans.length > 0) {
617
- output.writeln(output.bold('Human Claims'));
618
- printBoardTable(humans);
619
- output.writeln();
620
- }
621
- }
622
- else {
623
- printBoardTable(result.claims);
624
- }
625
- return { success: true, data: result };
626
- }
627
- catch (error) {
628
- if (error instanceof MCPClientError) {
629
- output.printError(`Failed to load board: ${error.message}`);
630
- }
631
- else {
632
- output.printError(`Unexpected error: ${String(error)}`);
633
- }
634
- return { success: false, exitCode: 1 };
635
- }
636
- }
637
- };
638
- function printBoardTable(claims) {
639
- output.printTable({
640
- columns: [
641
- { key: 'issue', header: 'Issue', width: 12 },
642
- { key: 'claimant', header: 'Claimant', width: 15 },
643
- { key: 'type', header: 'Type', width: 8 },
644
- { key: 'status', header: 'Status', width: 16 },
645
- { key: 'progress', header: 'Progress', width: 10 },
646
- { key: 'time', header: 'Time', width: 12 }
647
- ],
648
- data: claims.map(c => ({
649
- issue: c.issueId,
650
- claimant: c.claimantId,
651
- type: formatClaimantType(c.claimantType),
652
- status: formatClaimStatus(c.status),
653
- progress: formatProgress(c.progress),
654
- time: formatTimeRemaining(c.expiresAt)
655
- }))
656
- });
657
- }
658
- // ============================================
659
- // Work Stealing Commands
660
- // ============================================
661
- const stealableCommand = {
662
- name: 'stealable',
663
- description: 'List stealable issues',
664
- options: [
665
- {
666
- name: 'limit',
667
- short: 'l',
668
- description: 'Maximum number of issues to show',
669
- type: 'number',
670
- default: 20
671
- }
672
- ],
673
- action: async (ctx) => {
674
- const limit = ctx.flags.limit;
675
- try {
676
- const result = await callMCPTool('claims/stealable', { limit });
677
- if (ctx.flags.format === 'json') {
678
- output.printJson(result);
679
- return { success: true, data: result };
680
- }
681
- output.writeln();
682
- output.writeln(output.bold('Stealable Issues'));
683
- output.writeln();
684
- if (result.claims.length === 0) {
685
- output.printInfo('No stealable issues available');
686
- return { success: true, data: result };
687
- }
688
- output.printTable({
689
- columns: [
690
- { key: 'issue', header: 'Issue', width: 12 },
691
- { key: 'claimant', header: 'Current Owner', width: 15 },
692
- { key: 'progress', header: 'Progress', width: 10 },
693
- { key: 'reason', header: 'Stealable Reason', width: 30 }
694
- ],
695
- data: result.claims.map(c => ({
696
- issue: c.issueId,
697
- claimant: c.claimantId,
698
- progress: formatProgress(c.progress),
699
- reason: c.stealableReason || output.dim('No reason provided')
700
- }))
701
- });
702
- output.writeln();
703
- output.printInfo(`Showing ${result.claims.length} of ${result.total} stealable issues`);
704
- output.writeln();
705
- output.printInfo('Use "claude-flow issues steal <issueId>" to take over an issue');
706
- return { success: true, data: result };
707
- }
708
- catch (error) {
709
- if (error instanceof MCPClientError) {
710
- output.printError(`Failed to list stealable issues: ${error.message}`);
711
- }
712
- else {
713
- output.printError(`Unexpected error: ${String(error)}`);
714
- }
715
- return { success: false, exitCode: 1 };
716
- }
717
- }
718
- };
719
- const stealCommand = {
720
- name: 'steal',
721
- description: 'Steal an issue from another agent',
722
- options: [
723
- {
724
- name: 'force',
725
- short: 'f',
726
- description: 'Force steal without confirmation',
727
- type: 'boolean',
728
- default: false
729
- },
730
- {
731
- name: 'reason',
732
- short: 'r',
733
- description: 'Reason for stealing',
734
- type: 'string'
735
- }
736
- ],
737
- action: async (ctx) => {
738
- const issueId = ctx.args[0];
739
- const force = ctx.flags.force;
740
- const reason = ctx.flags.reason;
741
- if (!issueId) {
742
- output.printError('Issue ID is required');
743
- output.printInfo('Usage: claude-flow issues steal <issueId>');
744
- return { success: false, exitCode: 1 };
745
- }
746
- if (!force && ctx.interactive) {
747
- output.writeln();
748
- output.printWarning('Work stealing should be used responsibly.');
749
- output.printInfo('This action will reassign the issue to you.');
750
- output.writeln();
751
- const confirmed = await confirm({
752
- message: `Steal issue ${issueId}?`,
753
- default: false
754
- });
755
- if (!confirmed) {
756
- output.printInfo('Operation cancelled');
757
- return { success: true };
758
- }
759
- }
760
- output.writeln();
761
- output.printInfo(`Stealing issue ${output.highlight(issueId)}...`);
762
- try {
763
- const result = await callMCPTool('claims/steal', {
764
- issueId,
765
- reason
766
- });
767
- output.writeln();
768
- output.printSuccess(`Issue ${issueId} stolen successfully`);
769
- output.writeln();
770
- output.printTable({
771
- columns: [
772
- { key: 'property', header: 'Property', width: 15 },
773
- { key: 'value', header: 'Value', width: 35 }
774
- ],
775
- data: [
776
- { property: 'Issue ID', value: result.issueId },
777
- { property: 'New Claimant', value: result.claimantId },
778
- { property: 'Status', value: formatClaimStatus(result.status) },
779
- { property: 'Progress', value: formatProgress(result.progress) }
780
- ]
781
- });
782
- if (ctx.flags.format === 'json') {
783
- output.printJson(result);
784
- }
785
- return { success: true, data: result };
786
- }
787
- catch (error) {
788
- if (error instanceof MCPClientError) {
789
- output.printError(`Failed to steal issue: ${error.message}`);
790
- }
791
- else {
792
- output.printError(`Unexpected error: ${String(error)}`);
793
- }
794
- return { success: false, exitCode: 1 };
795
- }
796
- }
797
- };
798
- const markStealableCommand = {
799
- name: 'mark-stealable',
800
- description: 'Mark my claim as stealable',
801
- options: [
802
- {
803
- name: 'reason',
804
- short: 'r',
805
- description: 'Reason for marking as stealable',
806
- type: 'string'
807
- }
808
- ],
809
- action: async (ctx) => {
810
- const issueId = ctx.args[0];
811
- let reason = ctx.flags.reason;
812
- if (!issueId) {
813
- output.printError('Issue ID is required');
814
- output.printInfo('Usage: claude-flow issues mark-stealable <issueId>');
815
- return { success: false, exitCode: 1 };
816
- }
817
- if (!reason && ctx.interactive) {
818
- reason = await input({
819
- message: 'Reason for marking as stealable (optional):',
820
- default: ''
821
- });
822
- }
823
- output.writeln();
824
- output.printInfo(`Marking issue ${output.highlight(issueId)} as stealable...`);
825
- try {
826
- const result = await callMCPTool('claims/mark-stealable', {
827
- issueId,
828
- reason: reason || undefined
829
- });
830
- output.writeln();
831
- output.printSuccess(`Issue ${issueId} marked as stealable`);
832
- if (reason) {
833
- output.printInfo(`Reason: ${reason}`);
834
- }
835
- output.writeln();
836
- output.printWarning('Other agents can now claim this issue');
837
- if (ctx.flags.format === 'json') {
838
- output.printJson(result);
839
- }
840
- return { success: true, data: result };
841
- }
842
- catch (error) {
843
- if (error instanceof MCPClientError) {
844
- output.printError(`Failed to mark as stealable: ${error.message}`);
845
- }
846
- else {
847
- output.printError(`Unexpected error: ${String(error)}`);
848
- }
849
- return { success: false, exitCode: 1 };
850
- }
851
- }
852
- };
853
- const contestCommand = {
854
- name: 'contest',
855
- description: 'Contest a steal action',
856
- options: [
857
- {
858
- name: 'reason',
859
- short: 'r',
860
- description: 'Reason for contesting (required)',
861
- type: 'string',
862
- required: true
863
- }
864
- ],
865
- action: async (ctx) => {
866
- const issueId = ctx.args[0];
867
- let reason = ctx.flags.reason;
868
- if (!issueId) {
869
- output.printError('Issue ID is required');
870
- output.printInfo('Usage: claude-flow issues contest <issueId> --reason "..."');
871
- return { success: false, exitCode: 1 };
872
- }
873
- if (!reason && ctx.interactive) {
874
- reason = await input({
875
- message: 'Reason for contesting (required):',
876
- validate: (v) => v.length > 0 || 'Reason is required'
877
- });
878
- }
879
- if (!reason) {
880
- output.printError('Reason is required for contesting');
881
- return { success: false, exitCode: 1 };
882
- }
883
- output.writeln();
884
- output.printInfo(`Contesting steal on issue ${output.highlight(issueId)}...`);
885
- try {
886
- const result = await callMCPTool('claims/contest', {
887
- issueId,
888
- reason
889
- });
890
- output.writeln();
891
- switch (result.resolution) {
892
- case 'steal-reverted':
893
- output.printSuccess('Contest successful - steal reverted');
894
- output.printInfo(`Issue ${issueId} returned to original claimant: ${result.originalClaimantId}`);
895
- break;
896
- case 'steal-upheld':
897
- output.printWarning('Contest denied - steal upheld');
898
- output.printInfo(`Issue ${issueId} remains with: ${result.contesterId}`);
899
- break;
900
- case 'pending-review':
901
- output.printWarning('Contest submitted for review');
902
- output.printInfo('A coordinator will review this contest');
903
- break;
904
- }
905
- if (ctx.flags.format === 'json') {
906
- output.printJson(result);
907
- }
908
- return { success: true, data: result };
909
- }
910
- catch (error) {
911
- if (error instanceof MCPClientError) {
912
- output.printError(`Failed to contest steal: ${error.message}`);
913
- }
914
- else {
915
- output.printError(`Unexpected error: ${String(error)}`);
916
- }
917
- return { success: false, exitCode: 1 };
918
- }
919
- }
920
- };
921
- // ============================================
922
- // Load Balancing Commands
923
- // ============================================
924
- const loadCommand = {
925
- name: 'load',
926
- description: 'View agent load distribution',
927
- options: [
928
- {
929
- name: 'agent',
930
- short: 'a',
931
- description: 'View specific agent load',
932
- type: 'string'
933
- },
934
- {
935
- name: 'sort',
936
- short: 's',
937
- description: 'Sort by field',
938
- type: 'string',
939
- choices: ['utilization', 'issues', 'agent'],
940
- default: 'utilization'
941
- }
942
- ],
943
- action: async (ctx) => {
944
- const agentId = ctx.flags.agent;
945
- const sortBy = ctx.flags.sort;
946
- try {
947
- const result = await callMCPTool('claims/load', {
948
- agentId,
949
- sortBy
950
- });
951
- if (ctx.flags.format === 'json') {
952
- output.printJson(result);
953
- return { success: true, data: result };
954
- }
955
- output.writeln();
956
- output.writeln(output.bold('Agent Load Distribution'));
957
- output.writeln();
958
- // Summary
959
- output.printList([
960
- `Total Agents: ${result.summary.totalAgents}`,
961
- `Active Issues: ${result.summary.totalIssues}`,
962
- `Avg Utilization: ${result.summary.avgUtilization.toFixed(1)}%`,
963
- `Overloaded: ${output.error(String(result.summary.overloadedCount))}`,
964
- `Idle: ${output.dim(String(result.summary.idleCount))}`
965
- ]);
966
- output.writeln();
967
- if (agentId) {
968
- // Single agent detail
969
- const agent = result.agents[0];
970
- if (!agent) {
971
- output.printError(`Agent ${agentId} not found`);
972
- return { success: false, exitCode: 1 };
973
- }
974
- output.printBox([
975
- `Type: ${agent.agentType}`,
976
- `Status: ${formatAgentStatus(agent.status)}`,
977
- `Active Issues: ${agent.activeIssues}`,
978
- `Capacity: ${agent.totalCapacity}`,
979
- `Utilization: ${output.progressBar(agent.utilizationPercent, 100, 30)}`,
980
- `Avg Completion: ${agent.avgCompletionTime}`
981
- ].join('\n'), `Agent: ${agent.agentId}`);
982
- }
983
- else {
984
- // All agents table
985
- output.printTable({
986
- columns: [
987
- { key: 'agent', header: 'Agent', width: 15 },
988
- { key: 'type', header: 'Type', width: 12 },
989
- { key: 'issues', header: 'Issues', width: 8, align: 'right' },
990
- { key: 'capacity', header: 'Cap', width: 6, align: 'right' },
991
- { key: 'utilization', header: 'Utilization', width: 15 },
992
- { key: 'status', header: 'Status', width: 12 }
993
- ],
994
- data: result.agents.map(a => ({
995
- agent: a.agentId,
996
- type: a.agentType,
997
- issues: a.activeIssues,
998
- capacity: a.totalCapacity,
999
- utilization: `${a.utilizationPercent.toFixed(0)}%`,
1000
- status: formatAgentStatus(a.status)
1001
- }))
1002
- });
1003
- }
1004
- return { success: true, data: result };
1005
- }
1006
- catch (error) {
1007
- if (error instanceof MCPClientError) {
1008
- output.printError(`Failed to get load info: ${error.message}`);
1009
- }
1010
- else {
1011
- output.printError(`Unexpected error: ${String(error)}`);
1012
- }
1013
- return { success: false, exitCode: 1 };
1014
- }
1015
- }
1016
- };
1017
- const rebalanceCommand = {
1018
- name: 'rebalance',
1019
- description: 'Trigger swarm rebalancing',
1020
- options: [
1021
- {
1022
- name: 'dry-run',
1023
- short: 'd',
1024
- description: 'Preview rebalancing without making changes',
1025
- type: 'boolean',
1026
- default: false
1027
- },
1028
- {
1029
- name: 'force',
1030
- short: 'f',
1031
- description: 'Force rebalancing without confirmation',
1032
- type: 'boolean',
1033
- default: false
1034
- },
1035
- {
1036
- name: 'threshold',
1037
- short: 't',
1038
- description: 'Utilization threshold for rebalancing (0-100)',
1039
- type: 'number',
1040
- default: 80
1041
- }
1042
- ],
1043
- action: async (ctx) => {
1044
- const dryRun = ctx.flags['dry-run'];
1045
- const force = ctx.flags.force;
1046
- const threshold = ctx.flags.threshold;
1047
- if (!dryRun && !force && ctx.interactive) {
1048
- output.writeln();
1049
- output.printWarning('This will reassign issues between agents to balance load.');
1050
- const confirmed = await confirm({
1051
- message: 'Proceed with rebalancing?',
1052
- default: false
1053
- });
1054
- if (!confirmed) {
1055
- output.printInfo('Operation cancelled');
1056
- return { success: true };
1057
- }
1058
- }
1059
- output.writeln();
1060
- if (dryRun) {
1061
- output.printInfo('Analyzing rebalancing options (dry run)...');
1062
- }
1063
- else {
1064
- output.printInfo('Rebalancing swarm workload...');
1065
- }
1066
- const spinner = output.createSpinner({ text: 'Calculating optimal distribution...', spinner: 'dots' });
1067
- spinner.start();
1068
- try {
1069
- const result = await callMCPTool('claims/rebalance', {
1070
- dryRun,
1071
- threshold
1072
- });
1073
- spinner.stop();
1074
- output.writeln();
1075
- if (dryRun) {
1076
- output.printSuccess('Rebalancing Analysis Complete (Dry Run)');
1077
- }
1078
- else {
1079
- output.printSuccess('Rebalancing Complete');
1080
- }
1081
- output.writeln();
1082
- // Summary stats
1083
- output.printList([
1084
- `Issues to move: ${output.highlight(String(result.moved))}`,
1085
- `Issues skipped: ${output.dim(String(result.skipped))}`,
1086
- `Mode: ${dryRun ? output.warning('DRY RUN') : output.success('APPLIED')}`
1087
- ]);
1088
- if (result.reassignments.length > 0) {
1089
- output.writeln();
1090
- output.writeln(output.bold('Reassignments'));
1091
- output.printTable({
1092
- columns: [
1093
- { key: 'issue', header: 'Issue', width: 12 },
1094
- { key: 'from', header: 'From', width: 15 },
1095
- { key: 'to', header: 'To', width: 15 },
1096
- { key: 'reason', header: 'Reason', width: 30 }
1097
- ],
1098
- data: result.reassignments.map(r => ({
1099
- issue: r.issueId,
1100
- from: r.fromAgent,
1101
- to: r.toAgent,
1102
- reason: r.reason
1103
- }))
1104
- });
1105
- if (dryRun) {
1106
- output.writeln();
1107
- output.printInfo('Run without --dry-run to apply these changes');
1108
- }
1109
- }
1110
- else {
1111
- output.writeln();
1112
- output.printInfo('No reassignments needed - workload is balanced');
1113
- }
1114
- if (ctx.flags.format === 'json') {
1115
- output.printJson(result);
1116
- }
1117
- return { success: true, data: result };
1118
- }
1119
- catch (error) {
1120
- spinner.fail('Rebalancing failed');
1121
- if (error instanceof MCPClientError) {
1122
- output.printError(`Failed to rebalance: ${error.message}`);
1123
- }
1124
- else {
1125
- output.printError(`Unexpected error: ${String(error)}`);
1126
- }
1127
- return { success: false, exitCode: 1 };
1128
- }
1129
- }
1130
- };
1131
- // ============================================
1132
- // Main Issues Command
1133
- // ============================================
1134
- export const issuesCommand = {
1135
- name: 'issues',
1136
- description: 'Manage issue claims and work distribution',
1137
- subcommands: [
1138
- // Core claiming
1139
- listCommand,
1140
- claimCommand,
1141
- releaseCommand,
1142
- handoffCommand,
1143
- statusCommand,
1144
- boardCommand,
1145
- // Work stealing
1146
- stealableCommand,
1147
- stealCommand,
1148
- markStealableCommand,
1149
- contestCommand,
1150
- // Load balancing
1151
- loadCommand,
1152
- rebalanceCommand
1153
- ],
1154
- options: [],
1155
- examples: [
1156
- { command: 'claude-flow issues list --available', description: 'List unclaimed issues' },
1157
- { command: 'claude-flow issues list --mine', description: 'List my claims' },
1158
- { command: 'claude-flow issues claim GH-123', description: 'Claim an issue' },
1159
- { command: 'claude-flow issues release GH-123', description: 'Release a claim' },
1160
- { command: 'claude-flow issues handoff GH-123 --to agent:coder-1', description: 'Request handoff to agent' },
1161
- { command: 'claude-flow issues handoff GH-123 --to human:alice', description: 'Request handoff to human' },
1162
- { command: 'claude-flow issues status GH-123 --blocked "Waiting for API"', description: 'Mark as blocked' },
1163
- { command: 'claude-flow issues status GH-123 --review-requested', description: 'Request review' },
1164
- { command: 'claude-flow issues board', description: 'View who is working on what' },
1165
- { command: 'claude-flow issues stealable', description: 'List stealable issues' },
1166
- { command: 'claude-flow issues steal GH-123', description: 'Steal an issue' },
1167
- { command: 'claude-flow issues mark-stealable GH-123', description: 'Mark my claim as stealable' },
1168
- { command: 'claude-flow issues contest GH-123 -r "I was actively working on it"', description: 'Contest a steal' },
1169
- { command: 'claude-flow issues load', description: 'View agent load distribution' },
1170
- { command: 'claude-flow issues load --agent coder-1', description: 'View specific agent load' },
1171
- { command: 'claude-flow issues rebalance --dry-run', description: 'Preview rebalancing' },
1172
- { command: 'claude-flow issues rebalance', description: 'Trigger swarm rebalancing' }
1173
- ],
1174
- action: async (ctx) => {
1175
- // Show help if no subcommand
1176
- output.writeln();
1177
- output.writeln(output.bold('Issue Claims Management'));
1178
- output.writeln();
1179
- output.writeln('Usage: claude-flow issues <subcommand> [options]');
1180
- output.writeln();
1181
- output.writeln(output.bold('Core Commands'));
1182
- output.printList([
1183
- `${output.highlight('list')} - List issues (--available, --mine)`,
1184
- `${output.highlight('claim')} - Claim an issue to work on`,
1185
- `${output.highlight('release')} - Release a claim on an issue`,
1186
- `${output.highlight('handoff')} - Request handoff to another agent/human`,
1187
- `${output.highlight('status')} - Update or view issue claim status`,
1188
- `${output.highlight('board')} - View who is working on what`
1189
- ]);
1190
- output.writeln();
1191
- output.writeln(output.bold('Work Stealing Commands'));
1192
- output.printList([
1193
- `${output.highlight('stealable')} - List stealable issues`,
1194
- `${output.highlight('steal')} - Steal an issue from another agent`,
1195
- `${output.highlight('mark-stealable')} - Mark my claim as stealable`,
1196
- `${output.highlight('contest')} - Contest a steal action`
1197
- ]);
1198
- output.writeln();
1199
- output.writeln(output.bold('Load Balancing Commands'));
1200
- output.printList([
1201
- `${output.highlight('load')} - View agent load distribution`,
1202
- `${output.highlight('rebalance')} - Trigger swarm rebalancing`
1203
- ]);
1204
- output.writeln();
1205
- output.writeln('Run "claude-flow issues <subcommand> --help" for subcommand help');
1206
- return { success: true };
1207
- }
1208
- };
1209
- // ============================================
1210
- // Factory Function (for dependency injection)
1211
- // ============================================
1212
- /**
1213
- * Create issues command with injected services
1214
- * This allows for testing with mock services
1215
- */
1216
- export function createIssuesCommand(services) {
1217
- // The command structure remains the same, but actions would use
1218
- // the injected services instead of callMCPTool
1219
- // For now, we return the default command which uses MCP tools
1220
- return issuesCommand;
1221
- }
1222
- export default issuesCommand;
1223
- //# sourceMappingURL=cli-commands.js.map