@proletariat/cli 0.3.25 → 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/index.js +2 -2
- 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/delete.js +4 -5
- package/dist/commands/feedback/submit.d.ts +2 -2
- package/dist/commands/feedback/submit.js +9 -9
- 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/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/spec/edit.js +2 -3
- 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/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 +34 -16
- package/dist/commands/ticket/delete.js +15 -13
- package/dist/commands/ticket/edit.js +20 -12
- package/dist/commands/ticket/epic.js +12 -10
- 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.js +154 -57
- package/dist/commands/work/start.d.ts +1 -0
- package/dist/commands/work/start.js +295 -173
- package/dist/hooks/init.js +4 -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 +72 -1
- package/dist/lib/prompt-json.js +46 -0
- package/oclif.manifest.json +1184 -1116
- 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)';
|
|
@@ -255,18 +254,78 @@ export default class WorkSpawn extends PMOCommand {
|
|
|
255
254
|
db.close();
|
|
256
255
|
return handleError('NO_VALID_TICKETS', 'No valid tickets found from provided IDs.');
|
|
257
256
|
}
|
|
258
|
-
// In JSON mode with explicit tickets,
|
|
257
|
+
// In JSON mode with explicit tickets, check if we should execute or return confirmation
|
|
259
258
|
if (jsonMode) {
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
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
|
+
}
|
|
270
329
|
}
|
|
271
330
|
this.log('');
|
|
272
331
|
this.log(styles.header(`🚀 Spawn: ${ticketsToSpawn.length} ticket(s)`));
|
|
@@ -396,23 +455,18 @@ export default class WorkSpawn extends PMOCommand {
|
|
|
396
455
|
}
|
|
397
456
|
ticketsByPriority.get(priority).push(ticket);
|
|
398
457
|
}
|
|
399
|
-
// Build choices
|
|
400
|
-
const choices = [];
|
|
401
|
-
// Also build flat choices for JSON mode (without separators)
|
|
458
|
+
// Build flat choices for ticket selection
|
|
402
459
|
const flatChoices = [];
|
|
403
460
|
for (const priority of PRIORITY_ORDER) {
|
|
404
461
|
const tickets = ticketsByPriority.get(priority) || [];
|
|
405
462
|
if (tickets.length === 0)
|
|
406
463
|
continue;
|
|
407
|
-
choices.push(new inquirer.Separator(`── ${priority} (${tickets.length}) ──`));
|
|
408
464
|
for (const ticket of tickets) {
|
|
409
465
|
const statusBadge = ticket.statusName ? ` [${ticket.statusName}]` : '';
|
|
410
|
-
|
|
466
|
+
flatChoices.push({
|
|
411
467
|
name: `[${priority}] ${ticket.id} - ${ticket.title}${statusBadge}`,
|
|
412
468
|
value: ticket.id,
|
|
413
|
-
};
|
|
414
|
-
choices.push(ticketChoice);
|
|
415
|
-
flatChoices.push(ticketChoice);
|
|
469
|
+
});
|
|
416
470
|
}
|
|
417
471
|
}
|
|
418
472
|
// Use FlagResolver for ticket selection (checkbox)
|
|
@@ -464,24 +518,25 @@ export default class WorkSpawn extends PMOCommand {
|
|
|
464
518
|
}
|
|
465
519
|
}
|
|
466
520
|
// Handle tickets with active sessions
|
|
521
|
+
const spawnJsonModeConfig = jsonMode ? { flags: flags, commandName: 'work spawn' } : null;
|
|
467
522
|
if (ticketsWithActiveSessions.length > 0 && !jsonMode) {
|
|
468
523
|
this.log(styles.warning(`Found ${ticketsWithActiveSessions.length} ticket(s) with active tmux sessions:`));
|
|
469
524
|
for (const { ticketId, agent } of ticketsWithActiveSessions) {
|
|
470
525
|
this.log(styles.muted(` ${ticketId} → ${agent}`));
|
|
471
526
|
}
|
|
472
527
|
this.log('');
|
|
473
|
-
const { sessionAction } = await
|
|
528
|
+
const { sessionAction } = await this.prompt([
|
|
474
529
|
{
|
|
475
530
|
type: 'list',
|
|
476
531
|
name: 'sessionAction',
|
|
477
532
|
message: 'What would you like to do with these tickets?',
|
|
478
533
|
choices: [
|
|
479
|
-
{ name: 'Skip them (only spawn tickets without active sessions)', value: 'skip' },
|
|
480
|
-
{ name: 'Kill sessions and respawn with new agents', value: 'kill' },
|
|
481
|
-
{ 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: '' },
|
|
482
537
|
],
|
|
483
538
|
},
|
|
484
|
-
]);
|
|
539
|
+
], spawnJsonModeConfig);
|
|
485
540
|
if (sessionAction === 'cancel') {
|
|
486
541
|
db.close();
|
|
487
542
|
this.log(styles.muted('Cancelled.'));
|
|
@@ -730,7 +785,7 @@ export default class WorkSpawn extends PMOCommand {
|
|
|
730
785
|
let environmentSelected = false;
|
|
731
786
|
while (!environmentSelected) {
|
|
732
787
|
// eslint-disable-next-line no-await-in-loop -- Interactive loop with retry on Docker check
|
|
733
|
-
const { selectedEnvironment } = await
|
|
788
|
+
const { selectedEnvironment } = await this.prompt([
|
|
734
789
|
{
|
|
735
790
|
type: 'list',
|
|
736
791
|
name: 'selectedEnvironment',
|
|
@@ -738,7 +793,7 @@ export default class WorkSpawn extends PMOCommand {
|
|
|
738
793
|
choices: envChoices,
|
|
739
794
|
default: devcontainerReady ? 'devcontainer' : 'host',
|
|
740
795
|
},
|
|
741
|
-
]);
|
|
796
|
+
], spawnJsonModeConfig);
|
|
742
797
|
if (selectedEnvironment === 'cancel') {
|
|
743
798
|
db.close();
|
|
744
799
|
this.log(styles.muted('Cancelled.'));
|
|
@@ -812,18 +867,18 @@ export default class WorkSpawn extends PMOCommand {
|
|
|
812
867
|
// For devcontainer, prompt for display mode
|
|
813
868
|
// Simplified: tmux is always used inside container for session persistence
|
|
814
869
|
// eslint-disable-next-line no-await-in-loop -- Follow-up prompt after selection
|
|
815
|
-
const { selectedDisplay } = await
|
|
870
|
+
const { selectedDisplay } = await this.prompt([
|
|
816
871
|
{
|
|
817
872
|
type: 'list',
|
|
818
873
|
name: 'selectedDisplay',
|
|
819
874
|
message: 'How should agent output be displayed?',
|
|
820
875
|
choices: [
|
|
821
|
-
{ name: '🖥️ New tab - Opens in new terminal tab (recommended)', value: 'terminal' },
|
|
822
|
-
{ 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' },
|
|
823
878
|
],
|
|
824
879
|
default: 'terminal',
|
|
825
880
|
},
|
|
826
|
-
]);
|
|
881
|
+
], spawnJsonModeConfig);
|
|
827
882
|
batchDisplayMode = selectedDisplay;
|
|
828
883
|
// Always use tmux inside container for session persistence
|
|
829
884
|
flags.session = 'tmux';
|
|
@@ -888,26 +943,33 @@ export default class WorkSpawn extends PMOCommand {
|
|
|
888
943
|
const actionModifiesCode = selectedActionDetails?.modifiesCode ?? true;
|
|
889
944
|
if (!batchCreatePr && !batchNoPr) {
|
|
890
945
|
if (actionModifiesCode) {
|
|
891
|
-
//
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
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
|
+
}
|
|
911
973
|
}
|
|
912
974
|
else {
|
|
913
975
|
// Non-code-modifying action - no PR needed
|
|
@@ -926,10 +988,14 @@ export default class WorkSpawn extends PMOCommand {
|
|
|
926
988
|
// Spawn each ticket - work:start will create ephemeral agents on-demand
|
|
927
989
|
let successCount = 0;
|
|
928
990
|
let failCount = 0;
|
|
991
|
+
// Track execution results for JSON output
|
|
992
|
+
const executionResults = [];
|
|
929
993
|
// Process sequentially for clear logging and resource management
|
|
930
994
|
for (const ticket of ticketsToSpawn) {
|
|
931
995
|
try {
|
|
932
|
-
|
|
996
|
+
if (!jsonMode) {
|
|
997
|
+
this.log(styles.muted(`Starting ${ticket.id} with ephemeral agent...`));
|
|
998
|
+
}
|
|
933
999
|
// Build args for work:start
|
|
934
1000
|
// IMPORTANT: Pass --project to avoid re-prompting for project selection
|
|
935
1001
|
// Pass --ephemeral to signal work:start should create an ephemeral agent
|
|
@@ -937,6 +1003,10 @@ export default class WorkSpawn extends PMOCommand {
|
|
|
937
1003
|
// Pass clone flag if specified
|
|
938
1004
|
if (flags.clone)
|
|
939
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
|
+
}
|
|
940
1010
|
if (flags['per-ticket']) {
|
|
941
1011
|
// Per-ticket mode: only pass display flag, let start prompt for the rest
|
|
942
1012
|
// batchDisplayMode is for devcontainer, batchDisplay is for host
|
|
@@ -996,16 +1066,43 @@ export default class WorkSpawn extends PMOCommand {
|
|
|
996
1066
|
// eslint-disable-next-line no-await-in-loop
|
|
997
1067
|
await this.config.runCommand('work:start', startArgs);
|
|
998
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
|
+
});
|
|
999
1076
|
}
|
|
1000
1077
|
catch (error) {
|
|
1001
1078
|
failCount++;
|
|
1002
|
-
|
|
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
|
+
});
|
|
1003
1089
|
}
|
|
1004
1090
|
}
|
|
1005
|
-
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
|
+
});
|
|
1006
1096
|
db.close();
|
|
1007
|
-
|
|
1008
|
-
|
|
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
|
+
}
|
|
1009
1106
|
}
|
|
1010
1107
|
catch (error) {
|
|
1011
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>;
|