@proletariat/cli 0.3.24 → 0.3.26
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.
- package/dist/commands/action/create.js +3 -3
- package/dist/commands/action/index.js +2 -2
- package/dist/commands/action/update.js +3 -3
- package/dist/commands/agent/auth.js +1 -1
- package/dist/commands/agent/cleanup.js +6 -6
- package/dist/commands/agent/discover.js +1 -1
- package/dist/commands/agent/remove.js +4 -4
- package/dist/commands/autocomplete/setup.d.ts +2 -2
- package/dist/commands/autocomplete/setup.js +5 -5
- package/dist/commands/branch/create.js +31 -30
- package/dist/commands/category/create.js +4 -5
- package/dist/commands/category/delete.js +2 -3
- package/dist/commands/category/rename.js +2 -3
- package/dist/commands/claude.d.ts +2 -8
- package/dist/commands/claude.js +26 -26
- package/dist/commands/commit.d.ts +2 -8
- package/dist/commands/commit.js +4 -26
- package/dist/commands/config/index.d.ts +2 -10
- package/dist/commands/config/index.js +8 -34
- package/dist/commands/docker/index.d.ts +2 -2
- package/dist/commands/docker/index.js +8 -8
- package/dist/commands/epic/activate.js +9 -17
- package/dist/commands/epic/archive.js +13 -24
- package/dist/commands/epic/create.js +7 -6
- package/dist/commands/epic/delete.js +4 -5
- package/dist/commands/epic/move.js +28 -47
- package/dist/commands/epic/progress.js +10 -14
- package/dist/commands/epic/project.js +42 -59
- package/dist/commands/epic/reorder.js +25 -30
- package/dist/commands/epic/spec.d.ts +1 -0
- package/dist/commands/epic/spec.js +39 -40
- package/dist/commands/epic/ticket.d.ts +2 -0
- package/dist/commands/epic/ticket.js +63 -37
- package/dist/commands/feedback/index.d.ts +10 -0
- package/dist/commands/feedback/index.js +60 -0
- package/dist/commands/feedback/list.d.ts +12 -0
- package/dist/commands/feedback/list.js +126 -0
- package/dist/commands/feedback/submit.d.ts +16 -0
- package/dist/commands/feedback/submit.js +220 -0
- package/dist/commands/feedback/view.d.ts +15 -0
- package/dist/commands/feedback/view.js +109 -0
- package/dist/commands/gh/index.js +4 -0
- package/dist/commands/link/index.js +2 -2
- package/dist/commands/pmo/init.d.ts +2 -2
- package/dist/commands/pmo/init.js +7 -7
- package/dist/commands/project/spec.js +6 -6
- package/dist/commands/repo/create.d.ts +38 -0
- package/dist/commands/repo/create.js +283 -0
- package/dist/commands/repo/index.js +7 -0
- package/dist/commands/roadmap/add-project.js +9 -22
- package/dist/commands/roadmap/create.d.ts +0 -1
- package/dist/commands/roadmap/create.js +46 -40
- package/dist/commands/roadmap/delete.js +10 -24
- package/dist/commands/roadmap/generate.d.ts +1 -0
- package/dist/commands/roadmap/generate.js +21 -22
- package/dist/commands/roadmap/remove-project.js +14 -34
- package/dist/commands/roadmap/reorder.js +19 -26
- package/dist/commands/roadmap/update.js +27 -26
- package/dist/commands/roadmap/view.js +5 -12
- package/dist/commands/session/attach.d.ts +1 -8
- package/dist/commands/session/attach.js +93 -59
- package/dist/commands/session/health.d.ts +29 -0
- package/dist/commands/session/health.js +495 -0
- package/dist/commands/session/index.js +4 -0
- package/dist/commands/session/list.d.ts +0 -8
- package/dist/commands/session/list.js +130 -81
- package/dist/commands/spec/create.js +1 -1
- package/dist/commands/spec/edit.js +64 -35
- package/dist/commands/staff/add.d.ts +2 -2
- package/dist/commands/staff/add.js +15 -14
- package/dist/commands/staff/index.js +2 -2
- package/dist/commands/staff/remove.js +4 -4
- package/dist/commands/status/index.js +6 -7
- package/dist/commands/support/book.d.ts +10 -0
- package/dist/commands/support/book.js +54 -0
- package/dist/commands/support/discord.d.ts +10 -0
- package/dist/commands/support/discord.js +54 -0
- package/dist/commands/support/docs.d.ts +10 -0
- package/dist/commands/support/docs.js +54 -0
- package/dist/commands/support/index.d.ts +19 -0
- package/dist/commands/support/index.js +81 -0
- package/dist/commands/support/issues.d.ts +11 -0
- package/dist/commands/support/issues.js +77 -0
- package/dist/commands/support/logs.d.ts +18 -0
- package/dist/commands/support/logs.js +247 -0
- package/dist/commands/template/apply.js +10 -11
- package/dist/commands/template/create.js +18 -17
- package/dist/commands/template/index.d.ts +2 -2
- package/dist/commands/template/index.js +6 -6
- package/dist/commands/template/save.js +8 -7
- package/dist/commands/template/update.js +6 -7
- package/dist/commands/terminal/title.d.ts +2 -26
- package/dist/commands/terminal/title.js +4 -33
- package/dist/commands/theme/index.d.ts +2 -2
- package/dist/commands/theme/index.js +19 -18
- package/dist/commands/theme/set.d.ts +2 -2
- package/dist/commands/theme/set.js +5 -5
- package/dist/commands/ticket/create.js +52 -26
- package/dist/commands/ticket/delete.js +15 -13
- package/dist/commands/ticket/edit.js +59 -20
- package/dist/commands/ticket/epic.js +12 -10
- package/dist/commands/ticket/move.d.ts +7 -0
- package/dist/commands/ticket/move.js +132 -0
- package/dist/commands/ticket/project.js +11 -9
- package/dist/commands/ticket/reassign.js +23 -19
- package/dist/commands/ticket/spec.js +7 -5
- package/dist/commands/ticket/update.js +55 -53
- package/dist/commands/whoami.js +1 -0
- package/dist/commands/work/ready.js +7 -7
- package/dist/commands/work/revise.js +13 -11
- package/dist/commands/work/spawn.d.ts +1 -0
- package/dist/commands/work/spawn.js +225 -64
- package/dist/commands/work/start.d.ts +1 -0
- package/dist/commands/work/start.js +301 -173
- package/dist/hooks/init.js +4 -0
- package/dist/lib/execution/runners.js +21 -17
- package/dist/lib/execution/session-utils.d.ts +60 -0
- package/dist/lib/execution/session-utils.js +162 -0
- package/dist/lib/execution/spawner.d.ts +2 -0
- package/dist/lib/execution/spawner.js +42 -0
- package/dist/lib/flags/resolver.d.ts +2 -2
- package/dist/lib/flags/resolver.js +15 -0
- package/dist/lib/init/index.js +18 -0
- package/dist/lib/multiline-input.d.ts +63 -0
- package/dist/lib/multiline-input.js +360 -0
- package/dist/lib/pr/index.d.ts +4 -0
- package/dist/lib/pr/index.js +32 -14
- package/dist/lib/prompt-command.d.ts +3 -0
- package/dist/lib/prompt-json.d.ts +77 -6
- package/dist/lib/prompt-json.js +46 -0
- package/dist/lib/repos/git.d.ts +7 -0
- package/dist/lib/repos/git.js +20 -0
- package/oclif.manifest.json +2913 -2246
- package/package.json +1 -1
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { Flags } from '@oclif/core';
|
|
2
2
|
import * as path from 'node:path';
|
|
3
|
-
import inquirer from 'inquirer';
|
|
4
3
|
import Database from 'better-sqlite3';
|
|
5
4
|
import { PMOCommand, pmoBaseFlags, autoExportToBoard } from '../../lib/pmo/index.js';
|
|
6
5
|
import { styles } from '../../lib/styles.js';
|
|
7
6
|
import { getWorkspaceInfo, getTicketTmuxSession, killTmuxSession } from '../../lib/agents/commands.js';
|
|
8
7
|
import { isDockerRunning, isGitHubTokenAvailable, isDevcontainerCliInstalled } from '../../lib/execution/runners.js';
|
|
9
|
-
import { shouldOutputJson, outputSuccessAsJson, outputErrorAsJson, createMetadata, } from '../../lib/prompt-json.js';
|
|
8
|
+
import { shouldOutputJson, outputSuccessAsJson, outputErrorAsJson, createMetadata, outputConfirmationNeededAsJson, outputExecutionResultAsJson, } from '../../lib/prompt-json.js';
|
|
10
9
|
import { FlagResolver } from '../../lib/flags/index.js';
|
|
11
10
|
export default class WorkSpawn extends PMOCommand {
|
|
12
11
|
static description = 'Spawn work for multiple tickets by column (batch mode)';
|
|
@@ -19,6 +18,7 @@ export default class WorkSpawn extends PMOCommand {
|
|
|
19
18
|
'<%= config.bin %> <%= command.id %> TKT-001 TKT-002 # Spawn specific tickets by ID',
|
|
20
19
|
'<%= config.bin %> <%= command.id %> --dry-run # Preview without executing',
|
|
21
20
|
'<%= config.bin %> <%= command.id %> --many --json # Output ticket choices as JSON (for agents)',
|
|
21
|
+
'<%= config.bin %> <%= command.id %> TKT-001 --action custom --message "Add unit tests" # Custom prompt',
|
|
22
22
|
];
|
|
23
23
|
static flags = {
|
|
24
24
|
...pmoBaseFlags,
|
|
@@ -101,7 +101,10 @@ export default class WorkSpawn extends PMOCommand {
|
|
|
101
101
|
default: false,
|
|
102
102
|
}),
|
|
103
103
|
action: Flags.string({
|
|
104
|
-
description: 'Action to perform (e.g., groom, implement, review). Prompts if not provided.',
|
|
104
|
+
description: 'Action to perform (e.g., groom, implement, review, custom). Prompts if not provided.',
|
|
105
|
+
}),
|
|
106
|
+
message: Flags.string({
|
|
107
|
+
description: 'Custom prompt/message for the agent (use with --action custom)',
|
|
105
108
|
}),
|
|
106
109
|
session: Flags.string({
|
|
107
110
|
description: 'Session manager inside container (tmux runs agent in tmux inside container)',
|
|
@@ -251,18 +254,78 @@ export default class WorkSpawn extends PMOCommand {
|
|
|
251
254
|
db.close();
|
|
252
255
|
return handleError('NO_VALID_TICKETS', 'No valid tickets found from provided IDs.');
|
|
253
256
|
}
|
|
254
|
-
// In JSON mode with explicit tickets,
|
|
257
|
+
// In JSON mode with explicit tickets, check if we should execute or return confirmation
|
|
255
258
|
if (jsonMode) {
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
259
|
+
// Check if all required flags for non-interactive execution are provided
|
|
260
|
+
const hasAction = !!flags.action;
|
|
261
|
+
const hasDisplay = !!flags.display || flags['run-on-host'];
|
|
262
|
+
const hasPermissions = flags['skip-permissions'] || flags['per-ticket']; // per-ticket mode prompts individually
|
|
263
|
+
const allFlagsProvided = hasAction && hasDisplay && hasPermissions;
|
|
264
|
+
if (allFlagsProvided && flags.yes) {
|
|
265
|
+
// All flags provided and --yes is set: fall through to execution loop
|
|
266
|
+
// Don't return here - continue to execution
|
|
267
|
+
}
|
|
268
|
+
else if (allFlagsProvided && !flags.yes) {
|
|
269
|
+
// All flags provided but no --yes: return confirmation_needed with plan
|
|
270
|
+
const metadata = createMetadata('work spawn', flags);
|
|
271
|
+
// Build the confirm command with --yes
|
|
272
|
+
const ticketIds = ticketsToSpawn.map(t => t.id).join(' ');
|
|
273
|
+
let confirmCmd = `prlt work spawn ${ticketIds}`;
|
|
274
|
+
if (flags.action)
|
|
275
|
+
confirmCmd += ` --action ${flags.action}`;
|
|
276
|
+
if (flags.display)
|
|
277
|
+
confirmCmd += ` --display ${flags.display}`;
|
|
278
|
+
if (flags['run-on-host'])
|
|
279
|
+
confirmCmd += ' --run-on-host';
|
|
280
|
+
if (flags['skip-permissions'])
|
|
281
|
+
confirmCmd += ' --skip-permissions';
|
|
282
|
+
if (flags.executor)
|
|
283
|
+
confirmCmd += ` --executor ${flags.executor}`;
|
|
284
|
+
if (flags.session)
|
|
285
|
+
confirmCmd += ` --session ${flags.session}`;
|
|
286
|
+
if (flags['create-pr'])
|
|
287
|
+
confirmCmd += ' --create-pr';
|
|
288
|
+
if (flags['no-pr'])
|
|
289
|
+
confirmCmd += ' --no-pr';
|
|
290
|
+
if (flags.clone)
|
|
291
|
+
confirmCmd += ' --clone';
|
|
292
|
+
if (flags.focus)
|
|
293
|
+
confirmCmd += ' --focus';
|
|
294
|
+
confirmCmd += ' --yes';
|
|
295
|
+
const plan = {
|
|
296
|
+
tickets: ticketsToSpawn.map(t => ({
|
|
297
|
+
id: t.id,
|
|
298
|
+
title: t.title,
|
|
299
|
+
status: t.statusName,
|
|
300
|
+
})),
|
|
301
|
+
action: flags.action,
|
|
302
|
+
display: flags.display || (flags['run-on-host'] ? 'host' : 'devcontainer'),
|
|
303
|
+
permissions: flags['skip-permissions'] ? 'danger' : 'safe',
|
|
304
|
+
count: ticketsToSpawn.length,
|
|
305
|
+
};
|
|
306
|
+
db.close();
|
|
307
|
+
outputConfirmationNeededAsJson(plan, confirmCmd, `Ready to spawn ${ticketsToSpawn.length} agent(s). Run with --yes to execute.`, metadata);
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
else {
|
|
311
|
+
// Missing required flags: return success with tickets selected (legacy behavior)
|
|
312
|
+
// This allows the calling agent to see what tickets are available
|
|
313
|
+
outputSuccessAsJson({
|
|
314
|
+
ticketsSelected: ticketsToSpawn.map(t => ({
|
|
315
|
+
id: t.id,
|
|
316
|
+
title: t.title,
|
|
317
|
+
status: t.statusName,
|
|
318
|
+
})),
|
|
319
|
+
count: ticketsToSpawn.length,
|
|
320
|
+
missingFlags: {
|
|
321
|
+
action: !hasAction,
|
|
322
|
+
display: !hasDisplay,
|
|
323
|
+
permissions: !hasPermissions,
|
|
324
|
+
},
|
|
325
|
+
}, createMetadata('work spawn', flags));
|
|
326
|
+
db.close();
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
266
329
|
}
|
|
267
330
|
this.log('');
|
|
268
331
|
this.log(styles.header(`🚀 Spawn: ${ticketsToSpawn.length} ticket(s)`));
|
|
@@ -392,23 +455,18 @@ export default class WorkSpawn extends PMOCommand {
|
|
|
392
455
|
}
|
|
393
456
|
ticketsByPriority.get(priority).push(ticket);
|
|
394
457
|
}
|
|
395
|
-
// Build choices
|
|
396
|
-
const choices = [];
|
|
397
|
-
// Also build flat choices for JSON mode (without separators)
|
|
458
|
+
// Build flat choices for ticket selection
|
|
398
459
|
const flatChoices = [];
|
|
399
460
|
for (const priority of PRIORITY_ORDER) {
|
|
400
461
|
const tickets = ticketsByPriority.get(priority) || [];
|
|
401
462
|
if (tickets.length === 0)
|
|
402
463
|
continue;
|
|
403
|
-
choices.push(new inquirer.Separator(`── ${priority} (${tickets.length}) ──`));
|
|
404
464
|
for (const ticket of tickets) {
|
|
405
465
|
const statusBadge = ticket.statusName ? ` [${ticket.statusName}]` : '';
|
|
406
|
-
|
|
466
|
+
flatChoices.push({
|
|
407
467
|
name: `[${priority}] ${ticket.id} - ${ticket.title}${statusBadge}`,
|
|
408
468
|
value: ticket.id,
|
|
409
|
-
};
|
|
410
|
-
choices.push(ticketChoice);
|
|
411
|
-
flatChoices.push(ticketChoice);
|
|
469
|
+
});
|
|
412
470
|
}
|
|
413
471
|
}
|
|
414
472
|
// Use FlagResolver for ticket selection (checkbox)
|
|
@@ -460,24 +518,25 @@ export default class WorkSpawn extends PMOCommand {
|
|
|
460
518
|
}
|
|
461
519
|
}
|
|
462
520
|
// Handle tickets with active sessions
|
|
521
|
+
const spawnJsonModeConfig = jsonMode ? { flags: flags, commandName: 'work spawn' } : null;
|
|
463
522
|
if (ticketsWithActiveSessions.length > 0 && !jsonMode) {
|
|
464
523
|
this.log(styles.warning(`Found ${ticketsWithActiveSessions.length} ticket(s) with active tmux sessions:`));
|
|
465
524
|
for (const { ticketId, agent } of ticketsWithActiveSessions) {
|
|
466
525
|
this.log(styles.muted(` ${ticketId} → ${agent}`));
|
|
467
526
|
}
|
|
468
527
|
this.log('');
|
|
469
|
-
const { sessionAction } = await
|
|
528
|
+
const { sessionAction } = await this.prompt([
|
|
470
529
|
{
|
|
471
530
|
type: 'list',
|
|
472
531
|
name: 'sessionAction',
|
|
473
532
|
message: 'What would you like to do with these tickets?',
|
|
474
533
|
choices: [
|
|
475
|
-
{ name: 'Skip them (only spawn tickets without active sessions)', value: 'skip' },
|
|
476
|
-
{ name: 'Kill sessions and respawn with new agents', value: 'kill' },
|
|
477
|
-
{ name: 'Cancel', value: 'cancel' },
|
|
534
|
+
{ name: 'Skip them (only spawn tickets without active sessions)', value: 'skip', command: 'prlt work spawn --json' },
|
|
535
|
+
{ name: 'Kill sessions and respawn with new agents', value: 'kill', command: 'prlt work spawn --json' },
|
|
536
|
+
{ name: 'Cancel', value: 'cancel', command: '' },
|
|
478
537
|
],
|
|
479
538
|
},
|
|
480
|
-
]);
|
|
539
|
+
], spawnJsonModeConfig);
|
|
481
540
|
if (sessionAction === 'cancel') {
|
|
482
541
|
db.close();
|
|
483
542
|
this.log(styles.muted('Cancelled.'));
|
|
@@ -522,6 +581,8 @@ export default class WorkSpawn extends PMOCommand {
|
|
|
522
581
|
let batchNoPr = flags['no-pr'];
|
|
523
582
|
let batchRunOnHost = flags['run-on-host'];
|
|
524
583
|
let batchAction = flags.action;
|
|
584
|
+
// Track custom message for custom action (needs to be outside the if block)
|
|
585
|
+
let batchCustomMessage = flags.message;
|
|
525
586
|
// Track display mode separately for devcontainer (needs to be outside the if block)
|
|
526
587
|
let batchDisplayMode;
|
|
527
588
|
// For ephemeral agents, we'll create devcontainers on-demand
|
|
@@ -542,12 +603,16 @@ export default class WorkSpawn extends PMOCommand {
|
|
|
542
603
|
name: `${a.id.padEnd(12)} - ${a.description || a.name}`,
|
|
543
604
|
value: a.id,
|
|
544
605
|
}));
|
|
545
|
-
// Add adhoc
|
|
606
|
+
// Add custom and adhoc options at the end
|
|
607
|
+
actionChoices.push({
|
|
608
|
+
name: 'custom - Enter a custom prompt/instruction',
|
|
609
|
+
value: '__custom__',
|
|
610
|
+
});
|
|
546
611
|
actionChoices.push({
|
|
547
612
|
name: 'adhoc - Unstructured exploration/debugging',
|
|
548
613
|
value: '__adhoc__',
|
|
549
614
|
});
|
|
550
|
-
// Use FlagResolver for action selection
|
|
615
|
+
// Use FlagResolver for action selection with optional custom input
|
|
551
616
|
const actionResolver = new FlagResolver({
|
|
552
617
|
commandName: 'work spawn',
|
|
553
618
|
baseCommand: 'prlt work spawn',
|
|
@@ -561,12 +626,54 @@ export default class WorkSpawn extends PMOCommand {
|
|
|
561
626
|
default: 'implement',
|
|
562
627
|
choices: () => actionChoices,
|
|
563
628
|
});
|
|
629
|
+
actionResolver.addPrompt({
|
|
630
|
+
flagName: 'customInput',
|
|
631
|
+
type: 'input',
|
|
632
|
+
message: 'Enter custom prompt for the agent:',
|
|
633
|
+
when: (ctx) => ctx.flags.selectedAction === '__custom__',
|
|
634
|
+
validate: (value) => value.trim() ? true : 'Prompt cannot be empty',
|
|
635
|
+
});
|
|
564
636
|
const actionResult = await actionResolver.resolve();
|
|
565
637
|
const selectedAction = actionResult.selectedAction;
|
|
566
|
-
|
|
638
|
+
if (selectedAction === '__custom__') {
|
|
639
|
+
batchAction = 'custom';
|
|
640
|
+
batchCustomMessage = actionResult.customInput.trim();
|
|
641
|
+
}
|
|
642
|
+
else if (selectedAction === '__adhoc__') {
|
|
643
|
+
batchAction = 'adhoc';
|
|
644
|
+
}
|
|
645
|
+
else {
|
|
646
|
+
batchAction = selectedAction;
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
else if (flags.action === 'custom') {
|
|
650
|
+
// Custom action specified via flag - require --message
|
|
651
|
+
if (!flags.message) {
|
|
652
|
+
db.close();
|
|
653
|
+
return handleError('MISSING_MESSAGE', '--action custom requires --message flag with the custom prompt');
|
|
654
|
+
}
|
|
655
|
+
batchAction = 'custom';
|
|
656
|
+
batchCustomMessage = flags.message;
|
|
657
|
+
}
|
|
658
|
+
else if (flags.message && flags.action !== 'custom') {
|
|
659
|
+
// --message provided without --action custom - warn user
|
|
660
|
+
this.warn('--message flag is only used with --action custom, ignoring');
|
|
567
661
|
}
|
|
568
662
|
// Now fetch action details after selection is made
|
|
569
|
-
if (batchAction === '
|
|
663
|
+
if (batchAction === 'custom') {
|
|
664
|
+
// Custom action - user provides their own prompt
|
|
665
|
+
selectedActionDetails = {
|
|
666
|
+
id: 'custom',
|
|
667
|
+
name: 'Custom',
|
|
668
|
+
description: 'Custom prompt/instruction',
|
|
669
|
+
prompt: batchCustomMessage || '',
|
|
670
|
+
modifiesCode: true, // Assume custom prompts may modify code
|
|
671
|
+
defaultMoveToCategory: 'started',
|
|
672
|
+
isBuiltin: false,
|
|
673
|
+
createdAt: new Date(),
|
|
674
|
+
};
|
|
675
|
+
}
|
|
676
|
+
else if (batchAction === 'adhoc') {
|
|
570
677
|
// Adhoc is a synthetic action, not stored in database
|
|
571
678
|
selectedActionDetails = {
|
|
572
679
|
id: 'adhoc',
|
|
@@ -678,7 +785,7 @@ export default class WorkSpawn extends PMOCommand {
|
|
|
678
785
|
let environmentSelected = false;
|
|
679
786
|
while (!environmentSelected) {
|
|
680
787
|
// eslint-disable-next-line no-await-in-loop -- Interactive loop with retry on Docker check
|
|
681
|
-
const { selectedEnvironment } = await
|
|
788
|
+
const { selectedEnvironment } = await this.prompt([
|
|
682
789
|
{
|
|
683
790
|
type: 'list',
|
|
684
791
|
name: 'selectedEnvironment',
|
|
@@ -686,7 +793,7 @@ export default class WorkSpawn extends PMOCommand {
|
|
|
686
793
|
choices: envChoices,
|
|
687
794
|
default: devcontainerReady ? 'devcontainer' : 'host',
|
|
688
795
|
},
|
|
689
|
-
]);
|
|
796
|
+
], spawnJsonModeConfig);
|
|
690
797
|
if (selectedEnvironment === 'cancel') {
|
|
691
798
|
db.close();
|
|
692
799
|
this.log(styles.muted('Cancelled.'));
|
|
@@ -760,18 +867,18 @@ export default class WorkSpawn extends PMOCommand {
|
|
|
760
867
|
// For devcontainer, prompt for display mode
|
|
761
868
|
// Simplified: tmux is always used inside container for session persistence
|
|
762
869
|
// eslint-disable-next-line no-await-in-loop -- Follow-up prompt after selection
|
|
763
|
-
const { selectedDisplay } = await
|
|
870
|
+
const { selectedDisplay } = await this.prompt([
|
|
764
871
|
{
|
|
765
872
|
type: 'list',
|
|
766
873
|
name: 'selectedDisplay',
|
|
767
874
|
message: 'How should agent output be displayed?',
|
|
768
875
|
choices: [
|
|
769
|
-
{ name: '🖥️ New tab - Opens in new terminal tab (recommended)', value: 'terminal' },
|
|
770
|
-
{ name: '📦 Background - Runs detached, reattach with: prlt session attach', value: 'background' },
|
|
876
|
+
{ name: '🖥️ New tab - Opens in new terminal tab (recommended)', value: 'terminal', command: 'prlt work spawn --display terminal --json' },
|
|
877
|
+
{ name: '📦 Background - Runs detached, reattach with: prlt session attach', value: 'background', command: 'prlt work spawn --display background --json' },
|
|
771
878
|
],
|
|
772
879
|
default: 'terminal',
|
|
773
880
|
},
|
|
774
|
-
]);
|
|
881
|
+
], spawnJsonModeConfig);
|
|
775
882
|
batchDisplayMode = selectedDisplay;
|
|
776
883
|
// Always use tmux inside container for session persistence
|
|
777
884
|
flags.session = 'tmux';
|
|
@@ -836,26 +943,33 @@ export default class WorkSpawn extends PMOCommand {
|
|
|
836
943
|
const actionModifiesCode = selectedActionDetails?.modifiesCode ?? true;
|
|
837
944
|
if (!batchCreatePr && !batchNoPr) {
|
|
838
945
|
if (actionModifiesCode) {
|
|
839
|
-
//
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
946
|
+
// In JSON mode with --yes, default to creating PRs for code-modifying actions
|
|
947
|
+
if (jsonMode && flags.yes) {
|
|
948
|
+
batchCreatePr = true;
|
|
949
|
+
batchNoPr = false;
|
|
950
|
+
}
|
|
951
|
+
else {
|
|
952
|
+
// Use FlagResolver for PR choice
|
|
953
|
+
const prResolver = new FlagResolver({
|
|
954
|
+
commandName: 'work spawn',
|
|
955
|
+
baseCommand: 'prlt work spawn',
|
|
956
|
+
jsonMode,
|
|
957
|
+
flags: {},
|
|
958
|
+
});
|
|
959
|
+
prResolver.addPrompt({
|
|
960
|
+
flagName: 'prChoice',
|
|
961
|
+
type: 'list',
|
|
962
|
+
message: 'Create pull requests when work is ready?',
|
|
963
|
+
default: 'yes',
|
|
964
|
+
choices: () => [
|
|
965
|
+
{ name: '✓ Yes - Create PR for each ticket', value: 'yes' },
|
|
966
|
+
{ name: '✗ No - Just move tickets to review', value: 'no' },
|
|
967
|
+
],
|
|
968
|
+
});
|
|
969
|
+
const prResult = await prResolver.resolve();
|
|
970
|
+
batchCreatePr = prResult.prChoice === 'yes';
|
|
971
|
+
batchNoPr = prResult.prChoice === 'no';
|
|
972
|
+
}
|
|
859
973
|
}
|
|
860
974
|
else {
|
|
861
975
|
// Non-code-modifying action - no PR needed
|
|
@@ -874,10 +988,14 @@ export default class WorkSpawn extends PMOCommand {
|
|
|
874
988
|
// Spawn each ticket - work:start will create ephemeral agents on-demand
|
|
875
989
|
let successCount = 0;
|
|
876
990
|
let failCount = 0;
|
|
991
|
+
// Track execution results for JSON output
|
|
992
|
+
const executionResults = [];
|
|
877
993
|
// Process sequentially for clear logging and resource management
|
|
878
994
|
for (const ticket of ticketsToSpawn) {
|
|
879
995
|
try {
|
|
880
|
-
|
|
996
|
+
if (!jsonMode) {
|
|
997
|
+
this.log(styles.muted(`Starting ${ticket.id} with ephemeral agent...`));
|
|
998
|
+
}
|
|
881
999
|
// Build args for work:start
|
|
882
1000
|
// IMPORTANT: Pass --project to avoid re-prompting for project selection
|
|
883
1001
|
// Pass --ephemeral to signal work:start should create an ephemeral agent
|
|
@@ -885,6 +1003,10 @@ export default class WorkSpawn extends PMOCommand {
|
|
|
885
1003
|
// Pass clone flag if specified
|
|
886
1004
|
if (flags.clone)
|
|
887
1005
|
startArgs.push('--clone');
|
|
1006
|
+
// In JSON mode with --yes, pass --yes to work:start to skip prompts there too
|
|
1007
|
+
if (jsonMode && flags.yes) {
|
|
1008
|
+
startArgs.push('--yes');
|
|
1009
|
+
}
|
|
888
1010
|
if (flags['per-ticket']) {
|
|
889
1011
|
// Per-ticket mode: only pass display flag, let start prompt for the rest
|
|
890
1012
|
// batchDisplayMode is for devcontainer, batchDisplay is for host
|
|
@@ -899,6 +1021,13 @@ export default class WorkSpawn extends PMOCommand {
|
|
|
899
1021
|
startArgs.push('--force');
|
|
900
1022
|
if (flags.focus)
|
|
901
1023
|
startArgs.push('--focus');
|
|
1024
|
+
// Pass action/prompt - custom action uses --prompt, others use --action
|
|
1025
|
+
if (batchAction === 'custom' && batchCustomMessage) {
|
|
1026
|
+
startArgs.push('--prompt', batchCustomMessage);
|
|
1027
|
+
}
|
|
1028
|
+
else if (batchAction) {
|
|
1029
|
+
startArgs.push('--action', batchAction);
|
|
1030
|
+
}
|
|
902
1031
|
}
|
|
903
1032
|
else {
|
|
904
1033
|
// Batch mode: pass all settings to skip prompts
|
|
@@ -920,8 +1049,13 @@ export default class WorkSpawn extends PMOCommand {
|
|
|
920
1049
|
startArgs.push('--create-pr');
|
|
921
1050
|
if (batchNoPr)
|
|
922
1051
|
startArgs.push('--no-pr');
|
|
923
|
-
// Pass action
|
|
924
|
-
|
|
1052
|
+
// Pass action/prompt - custom action uses --prompt, others use --action
|
|
1053
|
+
if (batchAction === 'custom' && batchCustomMessage) {
|
|
1054
|
+
startArgs.push('--prompt', batchCustomMessage);
|
|
1055
|
+
}
|
|
1056
|
+
else {
|
|
1057
|
+
startArgs.push('--action', batchAction || 'implement');
|
|
1058
|
+
}
|
|
925
1059
|
// Pass session manager (tmux inside container by default)
|
|
926
1060
|
if (flags.session)
|
|
927
1061
|
startArgs.push('--session', flags.session);
|
|
@@ -932,16 +1066,43 @@ export default class WorkSpawn extends PMOCommand {
|
|
|
932
1066
|
// eslint-disable-next-line no-await-in-loop
|
|
933
1067
|
await this.config.runCommand('work:start', startArgs);
|
|
934
1068
|
successCount++;
|
|
1069
|
+
// Track for JSON output
|
|
1070
|
+
executionResults.push({
|
|
1071
|
+
workId: `WORK-${ticket.id}`, // Placeholder - actual work ID comes from work:start
|
|
1072
|
+
ticketId: ticket.id,
|
|
1073
|
+
agent: 'ephemeral', // Ephemeral agent name determined by work:start
|
|
1074
|
+
status: 'running',
|
|
1075
|
+
});
|
|
935
1076
|
}
|
|
936
1077
|
catch (error) {
|
|
937
1078
|
failCount++;
|
|
938
|
-
|
|
1079
|
+
if (!jsonMode) {
|
|
1080
|
+
this.log(styles.error(`Failed to start ${ticket.id}: ${error instanceof Error ? error.message : error}`));
|
|
1081
|
+
}
|
|
1082
|
+
// Track failed executions for JSON output
|
|
1083
|
+
executionResults.push({
|
|
1084
|
+
workId: '',
|
|
1085
|
+
ticketId: ticket.id,
|
|
1086
|
+
agent: '',
|
|
1087
|
+
status: 'failed',
|
|
1088
|
+
});
|
|
939
1089
|
}
|
|
940
1090
|
}
|
|
941
|
-
await autoExportToBoard(this.pmoPath, this.storage, (msg) =>
|
|
1091
|
+
await autoExportToBoard(this.pmoPath, this.storage, (msg) => {
|
|
1092
|
+
if (!jsonMode) {
|
|
1093
|
+
this.log(styles.muted(msg));
|
|
1094
|
+
}
|
|
1095
|
+
});
|
|
942
1096
|
db.close();
|
|
943
|
-
|
|
944
|
-
|
|
1097
|
+
// Output results
|
|
1098
|
+
if (jsonMode) {
|
|
1099
|
+
// Output JSON execution results
|
|
1100
|
+
outputExecutionResultAsJson(executionResults, successCount, failCount, createMetadata('work spawn', flags));
|
|
1101
|
+
}
|
|
1102
|
+
else {
|
|
1103
|
+
this.log('');
|
|
1104
|
+
this.log(styles.success(`✓ Spawn results: ${successCount} started, ${failCount} failed`));
|
|
1105
|
+
}
|
|
945
1106
|
}
|
|
946
1107
|
catch (error) {
|
|
947
1108
|
db.close();
|
|
@@ -27,6 +27,7 @@ export default class WorkStart extends PMOCommand {
|
|
|
27
27
|
ephemeral: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
28
28
|
focus: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
29
29
|
clone: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
30
|
+
yes: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
30
31
|
project: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
31
32
|
};
|
|
32
33
|
execute(): Promise<void>;
|