@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
|
@@ -2,10 +2,9 @@ import { Args, Flags } from '@oclif/core';
|
|
|
2
2
|
import * as fs from 'node:fs';
|
|
3
3
|
import * as path from 'node:path';
|
|
4
4
|
import { execSync } from 'node:child_process';
|
|
5
|
-
import inquirer from 'inquirer';
|
|
6
5
|
import Database from 'better-sqlite3';
|
|
7
6
|
import { PMOCommand, pmoBaseFlags, autoExportToBoard } from '../../lib/pmo/index.js';
|
|
8
|
-
import { shouldOutputJson, outputErrorAsJson, createMetadata, } from '../../lib/prompt-json.js';
|
|
7
|
+
import { shouldOutputJson, outputErrorAsJson, createMetadata, outputConfirmationNeededAsJson, outputExecutionResultAsJson, } from '../../lib/prompt-json.js';
|
|
9
8
|
import { FlagResolver } from '../../lib/flags/index.js';
|
|
10
9
|
import { getWorkColumnSetting, findColumnByName } from '../../lib/pmo/utils.js';
|
|
11
10
|
import { styles } from '../../lib/styles.js';
|
|
@@ -178,6 +177,11 @@ export default class WorkStart extends PMOCommand {
|
|
|
178
177
|
description: 'Use independent git clone instead of worktree (more isolation, no real-time sync)',
|
|
179
178
|
default: false,
|
|
180
179
|
}),
|
|
180
|
+
yes: Flags.boolean({
|
|
181
|
+
char: 'y',
|
|
182
|
+
description: 'Skip confirmation prompt (for non-TTY/scripted execution)',
|
|
183
|
+
default: false,
|
|
184
|
+
}),
|
|
181
185
|
};
|
|
182
186
|
async execute() {
|
|
183
187
|
const { args, flags } = await this.parse(WorkStart);
|
|
@@ -198,6 +202,7 @@ export default class WorkStart extends PMOCommand {
|
|
|
198
202
|
}
|
|
199
203
|
// Check if JSON output mode is active
|
|
200
204
|
const jsonMode = shouldOutputJson(flags);
|
|
205
|
+
const jsonModeConfig = jsonMode ? { flags: flags, commandName: 'work start' } : null;
|
|
201
206
|
// Helper to handle errors in JSON mode
|
|
202
207
|
const handleError = (code, message) => {
|
|
203
208
|
if (jsonMode) {
|
|
@@ -253,6 +258,68 @@ export default class WorkStart extends PMOCommand {
|
|
|
253
258
|
db.close();
|
|
254
259
|
return handleError('TICKET_NOT_FOUND', `Ticket "${ticketId}" not found.`);
|
|
255
260
|
}
|
|
261
|
+
// In JSON mode with explicit flags, implement two-step confirm-then-execute protocol
|
|
262
|
+
if (jsonMode) {
|
|
263
|
+
// Check if all required flags for non-interactive execution are provided
|
|
264
|
+
const hasAction = !!(flags.action || flags.prompt);
|
|
265
|
+
const hasDisplay = !!(flags.display || flags['run-on-host']);
|
|
266
|
+
const hasPermissions = !!(flags['permission-mode'] || flags['skip-permissions']);
|
|
267
|
+
const hasAgent = !!(flags.ephemeral || flags.agent);
|
|
268
|
+
const allFlagsProvided = hasAction && hasDisplay && hasPermissions && hasAgent;
|
|
269
|
+
if (allFlagsProvided && !flags.yes) {
|
|
270
|
+
// All flags provided but no --yes: return confirmation_needed with plan
|
|
271
|
+
const metadata = createMetadata('work start', flags);
|
|
272
|
+
// Build the confirm command with --yes
|
|
273
|
+
let confirmCmd = `prlt work start ${ticketId}`;
|
|
274
|
+
if (flags.action)
|
|
275
|
+
confirmCmd += ` --action ${flags.action}`;
|
|
276
|
+
if (flags.prompt)
|
|
277
|
+
confirmCmd += ` --prompt "${flags.prompt}"`;
|
|
278
|
+
if (flags.display)
|
|
279
|
+
confirmCmd += ` --display ${flags.display}`;
|
|
280
|
+
if (flags['run-on-host'])
|
|
281
|
+
confirmCmd += ' --run-on-host';
|
|
282
|
+
if (flags['permission-mode'])
|
|
283
|
+
confirmCmd += ` --permission-mode ${flags['permission-mode']}`;
|
|
284
|
+
if (flags['skip-permissions'])
|
|
285
|
+
confirmCmd += ' --skip-permissions';
|
|
286
|
+
if (flags.ephemeral)
|
|
287
|
+
confirmCmd += ' --ephemeral';
|
|
288
|
+
if (flags.agent)
|
|
289
|
+
confirmCmd += ` --agent ${flags.agent}`;
|
|
290
|
+
if (flags.executor)
|
|
291
|
+
confirmCmd += ` --executor ${flags.executor}`;
|
|
292
|
+
if (flags.session)
|
|
293
|
+
confirmCmd += ` --session ${flags.session}`;
|
|
294
|
+
if (flags['create-pr'])
|
|
295
|
+
confirmCmd += ' --create-pr';
|
|
296
|
+
if (flags['no-pr'])
|
|
297
|
+
confirmCmd += ' --no-pr';
|
|
298
|
+
if (flags.clone)
|
|
299
|
+
confirmCmd += ' --clone';
|
|
300
|
+
if (flags.focus)
|
|
301
|
+
confirmCmd += ' --focus';
|
|
302
|
+
if (flags.force)
|
|
303
|
+
confirmCmd += ' --force';
|
|
304
|
+
confirmCmd += ' --yes';
|
|
305
|
+
const plan = {
|
|
306
|
+
ticket: {
|
|
307
|
+
id: ticket.id,
|
|
308
|
+
title: ticket.title,
|
|
309
|
+
status: ticket.statusName,
|
|
310
|
+
},
|
|
311
|
+
action: flags.action || 'custom',
|
|
312
|
+
display: flags.display || (flags['run-on-host'] ? 'host' : 'devcontainer'),
|
|
313
|
+
permissions: (flags['permission-mode'] || (flags['skip-permissions'] ? 'danger' : 'safe')),
|
|
314
|
+
agent: flags.agent || 'ephemeral',
|
|
315
|
+
};
|
|
316
|
+
db.close();
|
|
317
|
+
outputConfirmationNeededAsJson(plan, confirmCmd, `Ready to start work on ${ticketId}. Run with --yes to execute.`, metadata);
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
// If --yes is set with all flags, continue to execution (don't return)
|
|
321
|
+
// If missing flags, continue and let FlagResolver handle prompts
|
|
322
|
+
}
|
|
256
323
|
// Check if ticket is blocked by dependencies
|
|
257
324
|
const isBlocked = await this.storage.isTicketBlocked(ticketId);
|
|
258
325
|
if (isBlocked && !flags.force) {
|
|
@@ -336,15 +403,22 @@ export default class WorkStart extends PMOCommand {
|
|
|
336
403
|
let isEphemeralAgent = flags.ephemeral;
|
|
337
404
|
if (flags.ephemeral) {
|
|
338
405
|
// Create ephemeral agent on-demand
|
|
339
|
-
|
|
406
|
+
if (!jsonMode) {
|
|
407
|
+
this.log(styles.muted('Creating ephemeral agent...'));
|
|
408
|
+
}
|
|
340
409
|
const ephemeralResult = await createEphemeralAgent(workspaceInfo, {
|
|
341
410
|
skipDevcontainer: flags['run-on-host'],
|
|
342
|
-
log: (msg) =>
|
|
411
|
+
log: (msg) => {
|
|
412
|
+
if (!jsonMode)
|
|
413
|
+
this.log(msg);
|
|
414
|
+
},
|
|
343
415
|
mountMode: flags.clone ? 'clone' : 'worktree',
|
|
344
416
|
});
|
|
345
417
|
agentName = ephemeralResult.name;
|
|
346
418
|
agentWorktreePath = ephemeralResult.worktreePath;
|
|
347
|
-
|
|
419
|
+
if (!jsonMode) {
|
|
420
|
+
this.log(styles.success(`Created ephemeral agent: ${agentName}`));
|
|
421
|
+
}
|
|
348
422
|
}
|
|
349
423
|
else if (flags.agent) {
|
|
350
424
|
// Agent specified via flag
|
|
@@ -737,7 +811,7 @@ export default class WorkStart extends PMOCommand {
|
|
|
737
811
|
let environmentSelected = false;
|
|
738
812
|
while (!environmentSelected) {
|
|
739
813
|
// eslint-disable-next-line no-await-in-loop -- Interactive loop with retry on Docker check
|
|
740
|
-
const { selectedEnvironment } = await
|
|
814
|
+
const { selectedEnvironment } = await this.prompt([
|
|
741
815
|
{
|
|
742
816
|
type: 'list',
|
|
743
817
|
name: 'selectedEnvironment',
|
|
@@ -745,7 +819,7 @@ export default class WorkStart extends PMOCommand {
|
|
|
745
819
|
choices: envChoices,
|
|
746
820
|
default: devcontainerReady ? 'devcontainer' : 'host',
|
|
747
821
|
},
|
|
748
|
-
]);
|
|
822
|
+
], jsonModeConfig);
|
|
749
823
|
if (selectedEnvironment === 'cancel') {
|
|
750
824
|
db.close();
|
|
751
825
|
this.log(styles.muted('Cancelled.'));
|
|
@@ -812,19 +886,19 @@ export default class WorkStart extends PMOCommand {
|
|
|
812
886
|
environment = 'host';
|
|
813
887
|
// Skip to host mode prompts
|
|
814
888
|
// eslint-disable-next-line no-await-in-loop -- Follow-up prompt after user selection
|
|
815
|
-
const { selectedDisplay } = await
|
|
889
|
+
const { selectedDisplay } = await this.prompt([
|
|
816
890
|
{
|
|
817
891
|
type: 'list',
|
|
818
892
|
name: 'selectedDisplay',
|
|
819
893
|
message: 'How should the agent output be displayed?',
|
|
820
894
|
choices: [
|
|
821
|
-
{ name: '🖥️ New tab - Opens in new terminal tab (recommended)', value: 'terminal' },
|
|
822
|
-
{ name: '▶️ Foreground - Run in current terminal (blocking)', value: 'foreground' },
|
|
823
|
-
{ name: '📦 Background - Runs detached, reattach with: prlt session attach', value: 'background' },
|
|
895
|
+
{ name: '🖥️ New tab - Opens in new terminal tab (recommended)', value: 'terminal', command: `prlt work start ${ticketId} --display terminal --run-on-host --json` },
|
|
896
|
+
{ name: '▶️ Foreground - Run in current terminal (blocking)', value: 'foreground', command: `prlt work start ${ticketId} --display foreground --run-on-host --json` },
|
|
897
|
+
{ name: '📦 Background - Runs detached, reattach with: prlt session attach', value: 'background', command: `prlt work start ${ticketId} --display background --run-on-host --json` },
|
|
824
898
|
],
|
|
825
899
|
default: 'terminal',
|
|
826
900
|
},
|
|
827
|
-
]);
|
|
901
|
+
], jsonModeConfig);
|
|
828
902
|
displayMode = selectedDisplay;
|
|
829
903
|
environmentSelected = true;
|
|
830
904
|
continue;
|
|
@@ -834,19 +908,19 @@ export default class WorkStart extends PMOCommand {
|
|
|
834
908
|
environment = 'devcontainer';
|
|
835
909
|
// Pick display mode for devcontainer
|
|
836
910
|
// eslint-disable-next-line no-await-in-loop -- Follow-up prompt after selection
|
|
837
|
-
const { selectedDisplay } = await
|
|
911
|
+
const { selectedDisplay } = await this.prompt([
|
|
838
912
|
{
|
|
839
913
|
type: 'list',
|
|
840
914
|
name: 'selectedDisplay',
|
|
841
915
|
message: 'How should the agent output be displayed?',
|
|
842
916
|
choices: [
|
|
843
|
-
{ name: '🖥️ New tab - Opens in new terminal tab (recommended)', value: 'terminal' },
|
|
844
|
-
{ name: '▶️ Foreground - Run in current terminal (blocking)', value: 'foreground' },
|
|
845
|
-
{ name: '📦 Background - Runs detached, reattach with: prlt session attach', value: 'background' },
|
|
917
|
+
{ name: '🖥️ New tab - Opens in new terminal tab (recommended)', value: 'terminal', command: `prlt work start ${ticketId} --display terminal --json` },
|
|
918
|
+
{ name: '▶️ Foreground - Run in current terminal (blocking)', value: 'foreground', command: `prlt work start ${ticketId} --display foreground --json` },
|
|
919
|
+
{ name: '📦 Background - Runs detached, reattach with: prlt session attach', value: 'background', command: `prlt work start ${ticketId} --display background --json` },
|
|
846
920
|
],
|
|
847
921
|
default: 'terminal',
|
|
848
922
|
},
|
|
849
|
-
]);
|
|
923
|
+
], jsonModeConfig);
|
|
850
924
|
displayMode = selectedDisplay;
|
|
851
925
|
environment = 'devcontainer';
|
|
852
926
|
environmentSelected = true;
|
|
@@ -855,19 +929,19 @@ export default class WorkStart extends PMOCommand {
|
|
|
855
929
|
// User chose host
|
|
856
930
|
environment = 'host';
|
|
857
931
|
// eslint-disable-next-line no-await-in-loop -- Follow-up prompt after selection
|
|
858
|
-
const { selectedDisplay } = await
|
|
932
|
+
const { selectedDisplay } = await this.prompt([
|
|
859
933
|
{
|
|
860
934
|
type: 'list',
|
|
861
935
|
name: 'selectedDisplay',
|
|
862
936
|
message: 'How should the agent output be displayed?',
|
|
863
937
|
choices: [
|
|
864
|
-
{ name: '🖥️ New tab - Opens in new terminal tab (recommended)', value: 'terminal' },
|
|
865
|
-
{ name: '▶️ Foreground - Run in current terminal (blocking)', value: 'foreground' },
|
|
866
|
-
{ name: '📦 Background - Runs detached, reattach with: prlt session attach', value: 'background' },
|
|
938
|
+
{ name: '🖥️ New tab - Opens in new terminal tab (recommended)', value: 'terminal', command: `prlt work start ${ticketId} --display terminal --run-on-host --json` },
|
|
939
|
+
{ name: '▶️ Foreground - Run in current terminal (blocking)', value: 'foreground', command: `prlt work start ${ticketId} --display foreground --run-on-host --json` },
|
|
940
|
+
{ name: '📦 Background - Runs detached, reattach with: prlt session attach', value: 'background', command: `prlt work start ${ticketId} --display background --run-on-host --json` },
|
|
867
941
|
],
|
|
868
942
|
default: 'terminal',
|
|
869
943
|
},
|
|
870
|
-
]);
|
|
944
|
+
], jsonModeConfig);
|
|
871
945
|
displayMode = selectedDisplay;
|
|
872
946
|
environmentSelected = true;
|
|
873
947
|
}
|
|
@@ -924,92 +998,98 @@ export default class WorkStart extends PMOCommand {
|
|
|
924
998
|
if (environment === 'devcontainer') {
|
|
925
999
|
const hasCredentials = dockerCredentialsExist();
|
|
926
1000
|
if (!hasCredentials) {
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
this.log('');
|
|
931
|
-
// Use FlagResolver for auth action
|
|
932
|
-
const authResolver = new FlagResolver({
|
|
933
|
-
commandName: 'work start',
|
|
934
|
-
baseCommand: `prlt work start ${ticketId}`,
|
|
935
|
-
jsonMode,
|
|
936
|
-
flags: {},
|
|
937
|
-
});
|
|
938
|
-
authResolver.addPrompt({
|
|
939
|
-
flagName: 'authAction',
|
|
940
|
-
type: 'list',
|
|
941
|
-
message: 'What would you like to do?',
|
|
942
|
-
choices: () => [
|
|
943
|
-
{ name: `🔐 Run ${this.config.bin} agent auth now (one-time setup)`, value: 'auth' },
|
|
944
|
-
{ name: '💻 Switch to host environment instead', value: 'host' },
|
|
945
|
-
{ name: '⏩ Continue anyway (must run /login in first agent)', value: 'continue' },
|
|
946
|
-
{ name: '✗ Cancel', value: 'cancel' },
|
|
947
|
-
],
|
|
948
|
-
});
|
|
949
|
-
const authResult = await authResolver.resolve();
|
|
950
|
-
const authAction = authResult.authAction;
|
|
951
|
-
if (authAction === 'cancel') {
|
|
952
|
-
db.close();
|
|
953
|
-
this.log(styles.muted('Cancelled.'));
|
|
954
|
-
return;
|
|
1001
|
+
// In JSON mode with --yes, continue anyway (agent can run /login)
|
|
1002
|
+
if (jsonMode && flags.yes) {
|
|
1003
|
+
// Continue without prompting - agent will need to handle auth
|
|
955
1004
|
}
|
|
956
|
-
|
|
957
|
-
environment = 'host';
|
|
958
|
-
this.log(styles.muted('Switched to host environment.'));
|
|
959
|
-
}
|
|
960
|
-
else if (authAction === 'auth') {
|
|
1005
|
+
else {
|
|
961
1006
|
this.log('');
|
|
962
|
-
this.log(styles.
|
|
1007
|
+
this.log(styles.warning('⚠️ No Claude Code credentials found for Docker containers'));
|
|
1008
|
+
this.log(styles.muted(' Agents will fail with 401 authentication errors without credentials.'));
|
|
963
1009
|
this.log('');
|
|
964
|
-
//
|
|
965
|
-
const
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
1010
|
+
// Use FlagResolver for auth action
|
|
1011
|
+
const authResolver = new FlagResolver({
|
|
1012
|
+
commandName: 'work start',
|
|
1013
|
+
baseCommand: `prlt work start ${ticketId}`,
|
|
1014
|
+
jsonMode,
|
|
1015
|
+
flags: {},
|
|
1016
|
+
});
|
|
1017
|
+
authResolver.addPrompt({
|
|
1018
|
+
flagName: 'authAction',
|
|
1019
|
+
type: 'list',
|
|
1020
|
+
message: 'What would you like to do?',
|
|
1021
|
+
choices: () => [
|
|
1022
|
+
{ name: `🔐 Run ${this.config.bin} agent auth now (one-time setup)`, value: 'auth' },
|
|
1023
|
+
{ name: '💻 Switch to host environment instead', value: 'host' },
|
|
1024
|
+
{ name: '⏩ Continue anyway (must run /login in first agent)', value: 'continue' },
|
|
1025
|
+
{ name: '✗ Cancel', value: 'cancel' },
|
|
1026
|
+
],
|
|
1027
|
+
});
|
|
1028
|
+
const authResult = await authResolver.resolve();
|
|
1029
|
+
const authAction = authResult.authAction;
|
|
1030
|
+
if (authAction === 'cancel') {
|
|
1031
|
+
db.close();
|
|
1032
|
+
this.log(styles.muted('Cancelled.'));
|
|
1033
|
+
return;
|
|
977
1034
|
}
|
|
978
|
-
|
|
979
|
-
|
|
1035
|
+
if (authAction === 'host') {
|
|
1036
|
+
environment = 'host';
|
|
1037
|
+
this.log(styles.muted('Switched to host environment.'));
|
|
1038
|
+
}
|
|
1039
|
+
else if (authAction === 'auth') {
|
|
1040
|
+
this.log('');
|
|
1041
|
+
this.log(styles.primary(`Opening ${this.config.bin} agent auth in new tab...`));
|
|
1042
|
+
this.log('');
|
|
1043
|
+
// Open auth in a new terminal tab
|
|
1044
|
+
const authCmd = `${process.argv[1]} agent auth`;
|
|
980
1045
|
try {
|
|
981
|
-
execSync(`osascript -e '
|
|
1046
|
+
execSync(`osascript -e '
|
|
1047
|
+
tell application "iTerm"
|
|
1048
|
+
tell current window
|
|
1049
|
+
create tab with default profile
|
|
1050
|
+
tell current session
|
|
1051
|
+
write text "${authCmd}"
|
|
1052
|
+
end tell
|
|
1053
|
+
end tell
|
|
1054
|
+
end tell
|
|
1055
|
+
'`);
|
|
982
1056
|
}
|
|
983
1057
|
catch {
|
|
984
|
-
|
|
985
|
-
|
|
1058
|
+
// Fallback: try Terminal.app
|
|
1059
|
+
try {
|
|
1060
|
+
execSync(`osascript -e 'tell application "Terminal" to do script "${authCmd}"'`);
|
|
1061
|
+
}
|
|
1062
|
+
catch {
|
|
1063
|
+
this.log(styles.warning('Could not open new terminal tab.'));
|
|
1064
|
+
this.log(styles.muted(`Please run manually: ${authCmd}`));
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
this.log(styles.muted('Complete the /login flow in the new tab, then press Enter here...'));
|
|
1068
|
+
this.log('');
|
|
1069
|
+
// Wait for user to complete auth
|
|
1070
|
+
await this.prompt([{
|
|
1071
|
+
type: 'input',
|
|
1072
|
+
name: 'done',
|
|
1073
|
+
message: 'Press Enter when authentication is complete:',
|
|
1074
|
+
}]);
|
|
1075
|
+
// Check if credentials now exist
|
|
1076
|
+
if (!dockerCredentialsExist()) {
|
|
1077
|
+
this.log('');
|
|
1078
|
+
this.log(styles.warning('Authentication did not complete. No credentials found.'));
|
|
1079
|
+
db.close();
|
|
1080
|
+
return;
|
|
1081
|
+
}
|
|
1082
|
+
const info = getDockerCredentialInfo();
|
|
1083
|
+
this.log('');
|
|
1084
|
+
this.log(styles.success('✓ Credentials configured'));
|
|
1085
|
+
if (info) {
|
|
1086
|
+
this.log(styles.muted(` Subscription: ${info.subscriptionType || 'unknown'}`));
|
|
1087
|
+
this.log(styles.muted(` Expires: ${info.expiresAt.toLocaleDateString()}`));
|
|
986
1088
|
}
|
|
987
|
-
}
|
|
988
|
-
this.log(styles.muted('Complete the /login flow in the new tab, then press Enter here...'));
|
|
989
|
-
this.log('');
|
|
990
|
-
// Wait for user to complete auth
|
|
991
|
-
await inquirer.prompt([{
|
|
992
|
-
type: 'input',
|
|
993
|
-
name: 'done',
|
|
994
|
-
message: 'Press Enter when authentication is complete:',
|
|
995
|
-
}]);
|
|
996
|
-
// Check if credentials now exist
|
|
997
|
-
if (!dockerCredentialsExist()) {
|
|
998
1089
|
this.log('');
|
|
999
|
-
this.log(styles.warning('Authentication did not complete. No credentials found.'));
|
|
1000
|
-
db.close();
|
|
1001
|
-
return;
|
|
1002
|
-
}
|
|
1003
|
-
const info = getDockerCredentialInfo();
|
|
1004
|
-
this.log('');
|
|
1005
|
-
this.log(styles.success('✓ Credentials configured'));
|
|
1006
|
-
if (info) {
|
|
1007
|
-
this.log(styles.muted(` Subscription: ${info.subscriptionType || 'unknown'}`));
|
|
1008
|
-
this.log(styles.muted(` Expires: ${info.expiresAt.toLocaleDateString()}`));
|
|
1009
1090
|
}
|
|
1010
|
-
|
|
1091
|
+
// authAction === 'continue' falls through
|
|
1011
1092
|
}
|
|
1012
|
-
// authAction === 'continue' falls through
|
|
1013
1093
|
}
|
|
1014
1094
|
}
|
|
1015
1095
|
// Prompt for permissions mode (all environments)
|
|
@@ -1054,50 +1134,58 @@ export default class WorkStart extends PMOCommand {
|
|
|
1054
1134
|
createPR = false;
|
|
1055
1135
|
}
|
|
1056
1136
|
else if (ghAvailable) {
|
|
1057
|
-
//
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
this.log(styles.muted(` Executor: ${executor}`));
|
|
1083
|
-
// Environment info
|
|
1084
|
-
const envIcon = environment === 'devcontainer' ? '🐳' : '💻';
|
|
1085
|
-
this.log(styles.muted(` Environment: ${envIcon} ${environment}`));
|
|
1086
|
-
this.log(styles.muted(` Display: ${displayMode}`));
|
|
1087
|
-
// Permissions info
|
|
1088
|
-
if (sandboxed) {
|
|
1089
|
-
this.log(styles.success(` Permissions: 🔒 safe`));
|
|
1090
|
-
}
|
|
1091
|
-
else {
|
|
1092
|
-
this.log(styles.warning(` Permissions: ⚠️ danger (--dangerously-skip-permissions)`));
|
|
1137
|
+
// In JSON mode with --yes, default to creating PR for code-modifying actions
|
|
1138
|
+
if (jsonMode && flags.yes) {
|
|
1139
|
+
createPR = context.modifiesCode !== false;
|
|
1140
|
+
}
|
|
1141
|
+
else {
|
|
1142
|
+
// Use FlagResolver for PR choice
|
|
1143
|
+
const prResolver = new FlagResolver({
|
|
1144
|
+
commandName: 'work start',
|
|
1145
|
+
baseCommand: `prlt work start ${ticketId}`,
|
|
1146
|
+
jsonMode,
|
|
1147
|
+
flags: {},
|
|
1148
|
+
});
|
|
1149
|
+
prResolver.addPrompt({
|
|
1150
|
+
flagName: 'prChoice',
|
|
1151
|
+
type: 'list',
|
|
1152
|
+
message: 'Create a pull request when work is ready?',
|
|
1153
|
+
default: 'yes',
|
|
1154
|
+
choices: () => [
|
|
1155
|
+
{ name: '✓ Yes - Create PR when running `prlt work ready`', value: 'yes' },
|
|
1156
|
+
{ name: '✗ No - Just move ticket to review (can create PR later)', value: 'no' },
|
|
1157
|
+
],
|
|
1158
|
+
});
|
|
1159
|
+
const prResult = await prResolver.resolve();
|
|
1160
|
+
createPR = prResult.prChoice === 'yes';
|
|
1161
|
+
}
|
|
1093
1162
|
}
|
|
1094
|
-
|
|
1095
|
-
if (
|
|
1096
|
-
this.log(
|
|
1163
|
+
// Show execution info (skip in JSON mode)
|
|
1164
|
+
if (!jsonMode) {
|
|
1165
|
+
this.log('');
|
|
1166
|
+
this.log(styles.header(`🚀 Starting work: ${ticket.id}: ${ticket.title}`));
|
|
1167
|
+
this.log(styles.muted(` Agent: ${assignedAgent}`));
|
|
1168
|
+
this.log(styles.muted(` Action: ${context.actionName || 'None'}`));
|
|
1169
|
+
this.log(styles.muted(` Executor: ${executor}`));
|
|
1170
|
+
// Environment info
|
|
1171
|
+
const envIcon = environment === 'devcontainer' ? '🐳' : '💻';
|
|
1172
|
+
this.log(styles.muted(` Environment: ${envIcon} ${environment}`));
|
|
1173
|
+
this.log(styles.muted(` Display: ${displayMode}`));
|
|
1174
|
+
// Permissions info
|
|
1175
|
+
if (sandboxed) {
|
|
1176
|
+
this.log(styles.success(` Permissions: 🔒 safe`));
|
|
1177
|
+
}
|
|
1178
|
+
else {
|
|
1179
|
+
this.log(styles.warning(` Permissions: ⚠️ danger (--dangerously-skip-permissions)`));
|
|
1180
|
+
}
|
|
1181
|
+
this.log(styles.muted(` Output: ${outputMode === 'interactive' ? 'streaming (watch Claude work)' : 'print (final result only)'}`));
|
|
1182
|
+
if (ghAvailable) {
|
|
1183
|
+
this.log(styles.muted(` Create PR: ${createPR ? 'yes (when work is ready)' : 'no'}`));
|
|
1184
|
+
}
|
|
1185
|
+
this.log(styles.muted(` Worktree: ${worktreePath}`));
|
|
1186
|
+
this.log(styles.muted(` Branch: ${branch}`));
|
|
1187
|
+
this.log('');
|
|
1097
1188
|
}
|
|
1098
|
-
this.log(styles.muted(` Worktree: ${worktreePath}`));
|
|
1099
|
-
this.log(styles.muted(` Branch: ${branch}`));
|
|
1100
|
-
this.log('');
|
|
1101
1189
|
// Add createPR to context
|
|
1102
1190
|
context.createPR = createPR;
|
|
1103
1191
|
// Handle git operations
|
|
@@ -1149,14 +1237,14 @@ export default class WorkStart extends PMOCommand {
|
|
|
1149
1237
|
const branchChoice = branchResult.branchChoice;
|
|
1150
1238
|
if (branchChoice === 'enter') {
|
|
1151
1239
|
// User enters existing branch name
|
|
1152
|
-
const { enteredBranch } = await
|
|
1240
|
+
const { enteredBranch } = await this.prompt([
|
|
1153
1241
|
{
|
|
1154
1242
|
type: 'input',
|
|
1155
1243
|
name: 'enteredBranch',
|
|
1156
1244
|
message: 'Enter branch name:',
|
|
1157
1245
|
validate: (input) => input.trim() ? true : 'Branch name required',
|
|
1158
1246
|
},
|
|
1159
|
-
]);
|
|
1247
|
+
], jsonModeConfig);
|
|
1160
1248
|
finalBranch = enteredBranch.trim();
|
|
1161
1249
|
// Validate branch exists (locally or in origin)
|
|
1162
1250
|
try {
|
|
@@ -1190,18 +1278,17 @@ export default class WorkStart extends PMOCommand {
|
|
|
1190
1278
|
}
|
|
1191
1279
|
if (remoteBranches.length > 0) {
|
|
1192
1280
|
const branchChoices = [
|
|
1193
|
-
...remoteBranches.map(b => ({ name: b, value: b.replace('origin/', '') })),
|
|
1194
|
-
new
|
|
1195
|
-
{ name: 'None of these, create new branch', value: '__create__' },
|
|
1281
|
+
...remoteBranches.map(b => ({ name: b, value: b.replace('origin/', ''), command: `prlt work start ${ticketId} --json` })),
|
|
1282
|
+
{ name: '── None of these, create new branch ──', value: '__create__', command: `prlt work start ${ticketId} --json` },
|
|
1196
1283
|
];
|
|
1197
|
-
const { selectedBranch } = await
|
|
1284
|
+
const { selectedBranch } = await this.prompt([
|
|
1198
1285
|
{
|
|
1199
1286
|
type: 'list',
|
|
1200
1287
|
name: 'selectedBranch',
|
|
1201
1288
|
message: `Found ${remoteBranches.length} matching branch(es):`,
|
|
1202
1289
|
choices: branchChoices,
|
|
1203
1290
|
},
|
|
1204
|
-
]);
|
|
1291
|
+
], jsonModeConfig);
|
|
1205
1292
|
if (selectedBranch !== '__create__') {
|
|
1206
1293
|
finalBranch = selectedBranch;
|
|
1207
1294
|
// Fetch and checkout the selected branch
|
|
@@ -1285,8 +1372,10 @@ export default class WorkStart extends PMOCommand {
|
|
|
1285
1372
|
sandboxed,
|
|
1286
1373
|
branch,
|
|
1287
1374
|
});
|
|
1288
|
-
|
|
1289
|
-
|
|
1375
|
+
if (!jsonMode) {
|
|
1376
|
+
this.log(styles.muted(` Work ID: ${execution.id}`));
|
|
1377
|
+
this.log('');
|
|
1378
|
+
}
|
|
1290
1379
|
// Note: Ticket status update moved to after successful spawn (see below)
|
|
1291
1380
|
// Load execution config from database
|
|
1292
1381
|
const executionConfig = loadExecutionConfig(db);
|
|
@@ -1327,7 +1416,9 @@ export default class WorkStart extends PMOCommand {
|
|
|
1327
1416
|
executionConfig.terminal.openInBackground = false;
|
|
1328
1417
|
}
|
|
1329
1418
|
// Run execution
|
|
1330
|
-
|
|
1419
|
+
if (!jsonMode) {
|
|
1420
|
+
this.log(styles.muted('Starting agent...'));
|
|
1421
|
+
}
|
|
1331
1422
|
const sessionManager = (flags.session || 'tmux');
|
|
1332
1423
|
const result = await runExecution(environment, context, executor, executionConfig, {
|
|
1333
1424
|
host: flags['vm-host'],
|
|
@@ -1381,18 +1472,47 @@ export default class WorkStart extends PMOCommand {
|
|
|
1381
1472
|
this.warn(`Could not move ticket to "${targetColumnName}": ${moveError instanceof Error ? moveError.message : moveError}`);
|
|
1382
1473
|
}
|
|
1383
1474
|
}
|
|
1384
|
-
await autoExportToBoard(this.pmoPath, this.storage, (msg) =>
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1475
|
+
await autoExportToBoard(this.pmoPath, this.storage, (msg) => {
|
|
1476
|
+
if (!jsonMode) {
|
|
1477
|
+
this.log(styles.muted(msg));
|
|
1478
|
+
}
|
|
1479
|
+
});
|
|
1480
|
+
// Output results
|
|
1481
|
+
if (jsonMode) {
|
|
1482
|
+
// Output JSON execution result
|
|
1483
|
+
outputExecutionResultAsJson([{
|
|
1484
|
+
workId: execution.id,
|
|
1485
|
+
ticketId: ticket.id,
|
|
1486
|
+
agent: assignedAgent,
|
|
1487
|
+
sessionId: result.sessionId,
|
|
1488
|
+
containerId: result.containerId,
|
|
1489
|
+
status: 'running',
|
|
1490
|
+
}], 1, 0, createMetadata('work start', flags));
|
|
1491
|
+
}
|
|
1492
|
+
else {
|
|
1493
|
+
this.log('');
|
|
1494
|
+
this.log(styles.success(`✓ Work started (${execution.id})`));
|
|
1495
|
+
this.log('');
|
|
1496
|
+
this.log(styles.muted('Commands:'));
|
|
1497
|
+
this.log(styles.muted(` prlt work status View work status`));
|
|
1498
|
+
this.log(styles.muted(` prlt work ready ${ticketId} Mark ready for review`));
|
|
1499
|
+
this.log(styles.muted(` prlt work stop ${execution.id} Stop work`));
|
|
1500
|
+
}
|
|
1392
1501
|
}
|
|
1393
1502
|
else {
|
|
1394
1503
|
executionStorage.updateStatus(execution.id, 'failed');
|
|
1395
|
-
|
|
1504
|
+
if (jsonMode) {
|
|
1505
|
+
// Output JSON failure result
|
|
1506
|
+
outputExecutionResultAsJson([{
|
|
1507
|
+
workId: execution.id,
|
|
1508
|
+
ticketId: ticket.id,
|
|
1509
|
+
agent: assignedAgent,
|
|
1510
|
+
status: 'failed',
|
|
1511
|
+
}], 0, 1, createMetadata('work start', flags));
|
|
1512
|
+
}
|
|
1513
|
+
else {
|
|
1514
|
+
this.error(`Failed to start work: ${result.error}`);
|
|
1515
|
+
}
|
|
1396
1516
|
}
|
|
1397
1517
|
db.close();
|
|
1398
1518
|
}
|
|
@@ -1405,6 +1525,8 @@ export default class WorkStart extends PMOCommand {
|
|
|
1405
1525
|
* Run batch mode: spawn work for all unassigned backlog tickets
|
|
1406
1526
|
*/
|
|
1407
1527
|
async runBatchMode(workspaceInfo, db, executionStorage, flags) {
|
|
1528
|
+
const batchJsonMode = shouldOutputJson(flags);
|
|
1529
|
+
const batchJsonModeConfig = batchJsonMode ? { flags: flags, commandName: 'work start' } : null;
|
|
1408
1530
|
// Get all tickets and filter to backlog/unstarted (not in progress)
|
|
1409
1531
|
// Note: In batch mode, we get all tickets across all projects (pass undefined for projectId)
|
|
1410
1532
|
// eslint-disable-next-line unicorn/no-useless-undefined
|
|
@@ -1441,17 +1563,17 @@ export default class WorkStart extends PMOCommand {
|
|
|
1441
1563
|
this.log(styles.muted(`Tickets to spawn: ${backlogTickets.map(t => t.id).join(', ')}`));
|
|
1442
1564
|
this.log('');
|
|
1443
1565
|
// Confirm before batch spawning
|
|
1444
|
-
const { confirm } = await
|
|
1566
|
+
const { confirm } = await this.prompt([
|
|
1445
1567
|
{
|
|
1446
1568
|
type: 'list',
|
|
1447
1569
|
name: 'confirm',
|
|
1448
1570
|
message: `Start work on ${backlogTickets.length} tickets using ${availableAgents.length} available agents?`,
|
|
1449
1571
|
choices: [
|
|
1450
|
-
{ name: 'Yes', value: true },
|
|
1451
|
-
{ name: 'No', value: false },
|
|
1572
|
+
{ name: 'Yes', value: true, command: 'prlt work start --all --json' },
|
|
1573
|
+
{ name: 'No', value: false, command: '' },
|
|
1452
1574
|
],
|
|
1453
1575
|
},
|
|
1454
|
-
]);
|
|
1576
|
+
], batchJsonModeConfig);
|
|
1455
1577
|
if (!confirm) {
|
|
1456
1578
|
db.close();
|
|
1457
1579
|
this.log(styles.muted('Cancelled.'));
|
|
@@ -1460,18 +1582,18 @@ export default class WorkStart extends PMOCommand {
|
|
|
1460
1582
|
// Prompt for permissions mode once for all tickets (TKT-513)
|
|
1461
1583
|
let batchPermissionMode = flags['permission-mode'];
|
|
1462
1584
|
if (!batchPermissionMode) {
|
|
1463
|
-
const { permissionMode } = await
|
|
1585
|
+
const { permissionMode } = await this.prompt([
|
|
1464
1586
|
{
|
|
1465
1587
|
type: 'list',
|
|
1466
1588
|
name: 'permissionMode',
|
|
1467
1589
|
message: 'Permission mode for Claude Code:',
|
|
1468
1590
|
choices: [
|
|
1469
|
-
{ name: '⚠️ danger - Skip permission checks (faster, container provides isolation)', value: 'danger' },
|
|
1470
|
-
{ name: '🔒 safe - Requires approval for dangerous operations', value: 'safe' },
|
|
1591
|
+
{ name: '⚠️ danger - Skip permission checks (faster, container provides isolation)', value: 'danger', command: 'prlt work start --all --permission-mode danger --json' },
|
|
1592
|
+
{ name: '🔒 safe - Requires approval for dangerous operations', value: 'safe', command: 'prlt work start --all --permission-mode safe --json' },
|
|
1471
1593
|
],
|
|
1472
1594
|
default: 'danger',
|
|
1473
1595
|
},
|
|
1474
|
-
]);
|
|
1596
|
+
], batchJsonModeConfig);
|
|
1475
1597
|
batchPermissionMode = permissionMode;
|
|
1476
1598
|
}
|
|
1477
1599
|
// Check Docker credentials if any agents use devcontainers
|
|
@@ -1486,18 +1608,18 @@ export default class WorkStart extends PMOCommand {
|
|
|
1486
1608
|
this.log(styles.warning('⚠️ No Claude Code credentials found for Docker containers'));
|
|
1487
1609
|
this.log(styles.muted(' Agents will fail with 401 authentication errors without credentials.'));
|
|
1488
1610
|
this.log('');
|
|
1489
|
-
const { authAction } = await
|
|
1611
|
+
const { authAction } = await this.prompt([
|
|
1490
1612
|
{
|
|
1491
1613
|
type: 'list',
|
|
1492
1614
|
name: 'authAction',
|
|
1493
1615
|
message: 'What would you like to do?',
|
|
1494
1616
|
choices: [
|
|
1495
|
-
{ name: `🔐 Run ${this.config.bin} agent auth now (one-time setup)`, value: 'auth' },
|
|
1496
|
-
{ name: '💻 Run all agents on host instead (--run-on-host)', value: 'host' },
|
|
1497
|
-
{ name: '✗ Cancel', value: 'cancel' },
|
|
1617
|
+
{ name: `🔐 Run ${this.config.bin} agent auth now (one-time setup)`, value: 'auth', command: `${this.config.bin} agent auth` },
|
|
1618
|
+
{ name: '💻 Run all agents on host instead (--run-on-host)', value: 'host', command: 'prlt work start --all --run-on-host --json' },
|
|
1619
|
+
{ name: '✗ Cancel', value: 'cancel', command: '' },
|
|
1498
1620
|
],
|
|
1499
1621
|
},
|
|
1500
|
-
]);
|
|
1622
|
+
], batchJsonModeConfig);
|
|
1501
1623
|
if (authAction === 'cancel') {
|
|
1502
1624
|
db.close();
|
|
1503
1625
|
this.log(styles.muted('Cancelled.'));
|
|
@@ -1538,11 +1660,11 @@ export default class WorkStart extends PMOCommand {
|
|
|
1538
1660
|
this.log(styles.muted('Complete the /login flow in the new tab, then press Enter here...'));
|
|
1539
1661
|
this.log('');
|
|
1540
1662
|
// Wait for user to complete auth
|
|
1541
|
-
await
|
|
1663
|
+
await this.prompt([{
|
|
1542
1664
|
type: 'input',
|
|
1543
1665
|
name: 'done',
|
|
1544
1666
|
message: 'Press Enter when authentication is complete:',
|
|
1545
|
-
}]);
|
|
1667
|
+
}], batchJsonModeConfig);
|
|
1546
1668
|
// Check if credentials now exist
|
|
1547
1669
|
if (!dockerCredentialsExist()) {
|
|
1548
1670
|
this.log('');
|