@proletariat/cli 0.3.35 → 0.3.40
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/README.md +37 -2
- package/bin/dev.js +0 -0
- package/dist/commands/agent/auth.d.ts +12 -2
- package/dist/commands/agent/auth.js +128 -4
- package/dist/commands/agent/list.js +16 -7
- package/dist/commands/agent/status.js +32 -4
- package/dist/commands/board/watch.js +6 -0
- package/dist/commands/branch/list.d.ts +1 -0
- package/dist/commands/branch/list.js +43 -12
- package/dist/commands/branch/where.js +9 -19
- package/dist/commands/category/list.d.ts +2 -1
- package/dist/commands/category/list.js +38 -13
- package/dist/commands/{claude.d.ts → claude/index.d.ts} +1 -1
- package/dist/commands/{claude.js → claude/index.js} +12 -12
- package/dist/commands/claude/open.d.ts +13 -0
- package/dist/commands/claude/open.js +175 -0
- package/dist/commands/diet.js +18 -2
- package/dist/commands/docker/logs.js +7 -3
- package/dist/commands/docker/shell.js +6 -0
- package/dist/commands/docker/start.js +20 -4
- package/dist/commands/docker/sync.d.ts +4 -0
- package/dist/commands/docker/sync.js +30 -2
- package/dist/commands/epic/show.d.ts +13 -0
- package/dist/commands/epic/show.js +16 -0
- package/dist/commands/epic/ticket.js +7 -24
- package/dist/commands/epic/view.js +27 -0
- package/dist/commands/execution/config.d.ts +0 -4
- package/dist/commands/execution/config.js +14 -46
- package/dist/commands/execution/index.js +2 -1
- package/dist/commands/execution/logs.js +7 -1
- package/dist/commands/execution/stop.js +2 -1
- package/dist/commands/execution/view.js +30 -26
- package/dist/commands/init.js +2 -19
- package/dist/commands/label/create.js +2 -1
- package/dist/commands/label/delete.js +2 -1
- package/dist/commands/label/group/create.js +2 -1
- package/dist/commands/label/group/list.js +2 -1
- package/dist/commands/label/list.js +2 -1
- package/dist/commands/mcp-server.js +27 -1
- package/dist/commands/phase/template/list.js +2 -1
- package/dist/commands/pmo/init.js +12 -40
- package/dist/commands/project/create.js +3 -4
- package/dist/commands/project/update.js +5 -6
- package/dist/commands/pull.js +24 -0
- package/dist/commands/qa/index.d.ts +54 -0
- package/dist/commands/qa/index.js +762 -0
- package/dist/commands/repo/view.js +2 -8
- package/dist/commands/session/attach.js +4 -4
- package/dist/commands/session/create.d.ts +19 -0
- package/dist/commands/session/create.js +102 -0
- package/dist/commands/session/health.js +4 -23
- package/dist/commands/session/index.js +14 -1
- package/dist/commands/session/list.js +9 -8
- package/dist/commands/session/peek.d.ts +38 -0
- package/dist/commands/session/peek.js +316 -0
- package/dist/commands/session/poke.d.ts +27 -0
- package/dist/commands/session/poke.js +219 -0
- package/dist/commands/spec/view.js +29 -0
- package/dist/commands/template/list.js +2 -1
- package/dist/commands/theme/add-names.d.ts +4 -0
- package/dist/commands/theme/add-names.js +11 -1
- package/dist/commands/theme/create.d.ts +2 -0
- package/dist/commands/theme/create.js +8 -0
- package/dist/commands/ticket/bulk.js +2 -2
- package/dist/commands/ticket/complete.js +2 -2
- package/dist/commands/ticket/create.js +21 -0
- package/dist/commands/ticket/delete.js +8 -0
- package/dist/commands/ticket/edit.js +25 -0
- package/dist/commands/ticket/epic.js +17 -43
- package/dist/commands/ticket/index.js +2 -2
- package/dist/commands/ticket/move.js +25 -2
- package/dist/commands/ticket/resolve.js +3 -4
- package/dist/commands/ticket/show.d.ts +13 -0
- package/dist/commands/ticket/show.js +16 -0
- package/dist/commands/ticket/template/list.js +2 -1
- package/dist/commands/ticket/view.d.ts +0 -1
- package/dist/commands/ticket/view.js +30 -1
- package/dist/commands/work/index.js +4 -0
- package/dist/commands/work/spawn-all.js +1 -1
- package/dist/commands/work/spawn.js +15 -4
- package/dist/commands/work/start.js +186 -103
- package/dist/commands/work/status.d.ts +14 -0
- package/dist/commands/work/status.js +60 -0
- package/dist/commands/work/watch.js +1 -1
- package/dist/commands/workflow/index.js +2 -1
- package/dist/commands/workflow/show.d.ts +13 -0
- package/dist/commands/workflow/show.js +16 -0
- package/dist/commands/workspace/add.js +15 -0
- package/dist/commands/workspace/list.js +2 -1
- package/dist/commands/workspace/prune.js +7 -7
- package/dist/hooks/init.js +10 -2
- package/dist/lib/agents/commands.d.ts +5 -0
- package/dist/lib/agents/commands.js +143 -97
- package/dist/lib/branch/index.d.ts +1 -0
- package/dist/lib/database/drizzle-schema.d.ts +465 -0
- package/dist/lib/database/drizzle-schema.js +53 -0
- package/dist/lib/database/index.d.ts +47 -1
- package/dist/lib/database/index.js +138 -20
- package/dist/lib/execution/config.d.ts +15 -1
- package/dist/lib/execution/config.js +28 -0
- package/dist/lib/execution/runners.d.ts +45 -0
- package/dist/lib/execution/runners.js +187 -26
- package/dist/lib/execution/session-utils.d.ts +16 -1
- package/dist/lib/execution/session-utils.js +71 -4
- package/dist/lib/execution/spawner.js +15 -2
- package/dist/lib/execution/storage.d.ts +6 -1
- package/dist/lib/execution/storage.js +35 -5
- package/dist/lib/execution/types.d.ts +3 -0
- package/dist/lib/mcp/tools/board.js +4 -6
- package/dist/lib/mcp/tools/cli-passthrough.js +25 -6
- package/dist/lib/mcp/tools/epic.js +8 -3
- package/dist/lib/mcp/tools/index.d.ts +1 -0
- package/dist/lib/mcp/tools/index.js +1 -0
- package/dist/lib/mcp/tools/spec.js +1 -1
- package/dist/lib/mcp/tools/ticket.js +11 -9
- package/dist/lib/mcp/tools/tmux.d.ts +16 -0
- package/dist/lib/mcp/tools/tmux.js +182 -0
- package/dist/lib/mcp/tools/work.js +148 -6
- package/dist/lib/mcp/types.d.ts +10 -0
- package/dist/lib/multiline-input.js +2 -1
- package/dist/lib/pmo/base-command.js +4 -4
- package/dist/lib/pmo/schema.d.ts +1 -1
- package/dist/lib/pmo/schema.js +1 -0
- package/dist/lib/pmo/storage/actions.js +1 -1
- package/dist/lib/pmo/storage/base.js +402 -50
- package/dist/lib/pmo/storage/dependencies.d.ts +1 -0
- package/dist/lib/pmo/storage/dependencies.js +11 -3
- package/dist/lib/pmo/storage/epics.js +1 -1
- package/dist/lib/pmo/storage/helpers.d.ts +4 -4
- package/dist/lib/pmo/storage/helpers.js +36 -26
- package/dist/lib/pmo/storage/projects.d.ts +2 -0
- package/dist/lib/pmo/storage/projects.js +207 -119
- package/dist/lib/pmo/storage/specs.d.ts +2 -0
- package/dist/lib/pmo/storage/specs.js +274 -188
- package/dist/lib/pmo/storage/tickets.d.ts +2 -0
- package/dist/lib/pmo/storage/tickets.js +350 -290
- package/dist/lib/pmo/storage/types.d.ts +1 -0
- package/dist/lib/pmo/storage/views.d.ts +2 -0
- package/dist/lib/pmo/storage/views.js +183 -130
- package/dist/lib/prompt-command.d.ts +20 -0
- package/dist/lib/prompt-command.js +38 -2
- package/dist/lib/prompt-json.d.ts +41 -4
- package/dist/lib/prompt-json.js +138 -7
- package/dist/lib/styles.d.ts +37 -0
- package/dist/lib/styles.js +73 -0
- package/oclif.manifest.json +4046 -3385
- package/package.json +11 -6
- package/LICENSE +0 -190
|
@@ -13,7 +13,7 @@ import { getWorkspaceInfo, createEphemeralAgent, getTicketTmuxSession, killTmuxS
|
|
|
13
13
|
import { generateBranchName, DEFAULT_EXECUTION_CONFIG, } from '../../lib/execution/types.js';
|
|
14
14
|
import { runExecution, isDockerRunning, isGitHubTokenAvailable, isDevcontainerCliInstalled, dockerCredentialsExist, getDockerCredentialInfo } from '../../lib/execution/runners.js';
|
|
15
15
|
import { ExecutionStorage, ContainerStorage } from '../../lib/execution/storage.js';
|
|
16
|
-
import { loadExecutionConfig, getTerminalApp, promptTerminalPreference, getShell, promptShellPreference, hasTerminalPreference, hasShellPreference, getOrPromptCoderName } from '../../lib/execution/config.js';
|
|
16
|
+
import { loadExecutionConfig, getTerminalApp, promptTerminalPreference, getShell, promptShellPreference, hasTerminalPreference, hasShellPreference, getOrPromptCoderName, getAuthMethod, saveAuthMethod } from '../../lib/execution/config.js';
|
|
17
17
|
import { hasDevcontainerConfig } from '../../lib/execution/devcontainer.js';
|
|
18
18
|
import { detectRepoWorktrees, resolveWorktreePath } from '../../lib/execution/context.js';
|
|
19
19
|
import { isGHInstalled, isGHAuthenticated } from '../../lib/pr/index.js';
|
|
@@ -71,6 +71,7 @@ export default class WorkStart extends PMOCommand {
|
|
|
71
71
|
static description = 'Start work on a ticket (launches an agent to implement it)';
|
|
72
72
|
static examples = [
|
|
73
73
|
'<%= config.bin %> <%= command.id %> TKT-001',
|
|
74
|
+
'<%= config.bin %> <%= command.id %> TKT-001 --create-pr # Create PR when work is ready',
|
|
74
75
|
'<%= config.bin %> <%= command.id %> TKT-001 --mode foreground',
|
|
75
76
|
'<%= config.bin %> <%= command.id %> TKT-001 --mode tmux',
|
|
76
77
|
'<%= config.bin %> <%= command.id %> TKT-001 --mode terminal',
|
|
@@ -137,11 +138,11 @@ export default class WorkStart extends PMOCommand {
|
|
|
137
138
|
default: false,
|
|
138
139
|
}),
|
|
139
140
|
'create-pr': Flags.boolean({
|
|
140
|
-
description: 'Create PR when work is ready',
|
|
141
|
+
description: 'Create PR when work is ready (canonical flag for PR behavior)',
|
|
141
142
|
default: false,
|
|
142
143
|
}),
|
|
143
144
|
'no-pr': Flags.boolean({
|
|
144
|
-
description: '
|
|
145
|
+
description: '[deprecated: use --create-pr instead] Skip PR creation when work is ready',
|
|
145
146
|
default: false,
|
|
146
147
|
}),
|
|
147
148
|
output: Flags.string({
|
|
@@ -193,6 +194,10 @@ export default class WorkStart extends PMOCommand {
|
|
|
193
194
|
if (flags['create-pr'] && flags['no-pr']) {
|
|
194
195
|
this.error('--create-pr and --no-pr are mutually exclusive');
|
|
195
196
|
}
|
|
197
|
+
// Deprecation guidance for --no-pr
|
|
198
|
+
if (flags['no-pr']) {
|
|
199
|
+
this.warn('--no-pr is deprecated. Omit --create-pr instead (PR creation is off by default). --no-pr will continue to work.');
|
|
200
|
+
}
|
|
196
201
|
// Handle --skip-permissions flag (alias for --permission-mode danger)
|
|
197
202
|
// Check for conflicting flags first
|
|
198
203
|
if (flags['skip-permissions'] && flags['permission-mode']) {
|
|
@@ -272,6 +277,7 @@ export default class WorkStart extends PMOCommand {
|
|
|
272
277
|
if (allFlagsProvided && !flags.yes) {
|
|
273
278
|
// All flags provided but no --yes: return confirmation_needed with plan
|
|
274
279
|
const metadata = createMetadata('work start', flags);
|
|
280
|
+
metadata.resolvedPRMode = flags['create-pr'] ? 'create-pr' : 'no-pr';
|
|
275
281
|
// Build the confirm command with --yes
|
|
276
282
|
let confirmCmd = `prlt work start ${ticketId}`;
|
|
277
283
|
if (flags.action)
|
|
@@ -810,11 +816,20 @@ export default class WorkStart extends PMOCommand {
|
|
|
810
816
|
flags: {},
|
|
811
817
|
});
|
|
812
818
|
envResolver.addPrompt({
|
|
813
|
-
flagName: '
|
|
819
|
+
flagName: 'environment',
|
|
814
820
|
type: 'list',
|
|
815
821
|
message: 'Where should the agent run?',
|
|
816
822
|
default: 'devcontainer',
|
|
817
823
|
choices: () => envChoices,
|
|
824
|
+
getCommand: (value) => {
|
|
825
|
+
const base = `prlt work start ${ticketId}`;
|
|
826
|
+
if (value === 'host')
|
|
827
|
+
return `${base} --run-on-host --json`;
|
|
828
|
+
if (value === 'cancel')
|
|
829
|
+
return '';
|
|
830
|
+
// devcontainer is the default when available
|
|
831
|
+
return `${base} --json`;
|
|
832
|
+
},
|
|
818
833
|
});
|
|
819
834
|
await envResolver.resolve();
|
|
820
835
|
// FlagResolver exits in JSON mode, so we never reach here
|
|
@@ -1008,109 +1023,171 @@ export default class WorkStart extends PMOCommand {
|
|
|
1008
1023
|
const outputMode = flags.output || DEFAULT_EXECUTION_CONFIG.outputMode;
|
|
1009
1024
|
// Track whether user explicitly chose to use API key instead of OAuth
|
|
1010
1025
|
let useApiKey = flags['use-api-key'] || false;
|
|
1011
|
-
//
|
|
1026
|
+
// Auth method resolution for devcontainer environment
|
|
1012
1027
|
if (environment === 'devcontainer' && !useApiKey) {
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
else {
|
|
1020
|
-
const hasApiKey = !!process.env.ANTHROPIC_API_KEY;
|
|
1028
|
+
// Check for saved auth method preference
|
|
1029
|
+
const savedAuthMethod = getAuthMethod(db);
|
|
1030
|
+
const hasApiKey = !!process.env.ANTHROPIC_API_KEY;
|
|
1031
|
+
if (savedAuthMethod === 'apikey') {
|
|
1032
|
+
// Saved preference: API key — validate it's still set
|
|
1033
|
+
if (!hasApiKey) {
|
|
1021
1034
|
this.log('');
|
|
1022
|
-
this.log(styles.warning('⚠️
|
|
1023
|
-
this.log(styles.muted('
|
|
1035
|
+
this.log(styles.warning('⚠️ Saved auth method is "apikey" but ANTHROPIC_API_KEY is not set in your environment.'));
|
|
1036
|
+
this.log(styles.muted(' Set the env var or run "' + this.config.bin + ' agent auth" to switch to OAuth.'));
|
|
1037
|
+
db.close();
|
|
1038
|
+
return;
|
|
1039
|
+
}
|
|
1040
|
+
useApiKey = true;
|
|
1041
|
+
}
|
|
1042
|
+
else if (savedAuthMethod === 'oauth') {
|
|
1043
|
+
// Saved preference: OAuth — validate credentials exist
|
|
1044
|
+
const hasCredentials = dockerCredentialsExist();
|
|
1045
|
+
if (!hasCredentials) {
|
|
1024
1046
|
this.log('');
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
choices: () => authChoices,
|
|
1045
|
-
});
|
|
1046
|
-
const authResult = await authResolver.resolve();
|
|
1047
|
-
const authAction = authResult.authAction;
|
|
1048
|
-
if (authAction === 'cancel') {
|
|
1049
|
-
db.close();
|
|
1050
|
-
this.log(styles.muted('Cancelled.'));
|
|
1051
|
-
return;
|
|
1052
|
-
}
|
|
1053
|
-
if (authAction === 'host') {
|
|
1054
|
-
environment = 'host';
|
|
1055
|
-
this.log(styles.muted('Switched to host environment.'));
|
|
1056
|
-
}
|
|
1057
|
-
else if (authAction === 'apikey') {
|
|
1058
|
-
useApiKey = true;
|
|
1059
|
-
this.log(styles.warning('Using ANTHROPIC_API_KEY — this will consume API credits.'));
|
|
1060
|
-
this.log(styles.muted(`Run "${this.config.bin} agent auth" to set up OAuth and use your Max subscription instead.`));
|
|
1061
|
-
this.log('');
|
|
1047
|
+
this.log(styles.warning('⚠️ Saved auth method is "oauth" but no OAuth credentials found.'));
|
|
1048
|
+
this.log(styles.muted(' Run "' + this.config.bin + ' agent auth" to authenticate.'));
|
|
1049
|
+
db.close();
|
|
1050
|
+
return;
|
|
1051
|
+
}
|
|
1052
|
+
// OAuth credentials valid — continue (useApiKey stays false)
|
|
1053
|
+
}
|
|
1054
|
+
else {
|
|
1055
|
+
// No saved preference — show auth method menu
|
|
1056
|
+
const hasCredentials = dockerCredentialsExist();
|
|
1057
|
+
if (hasCredentials) {
|
|
1058
|
+
// OAuth credentials exist, use them silently (no menu needed)
|
|
1059
|
+
// useApiKey stays false
|
|
1060
|
+
}
|
|
1061
|
+
else {
|
|
1062
|
+
// No saved preference and no OAuth credentials — prompt user
|
|
1063
|
+
// In JSON mode with --yes, continue anyway (agent can run /login)
|
|
1064
|
+
if (jsonMode && flags.yes) {
|
|
1065
|
+
// Continue without prompting - agent will need to handle auth
|
|
1062
1066
|
}
|
|
1063
|
-
else
|
|
1067
|
+
else {
|
|
1064
1068
|
this.log('');
|
|
1065
|
-
this.log(styles.
|
|
1069
|
+
this.log(styles.warning('⚠️ No Claude Code OAuth credentials found for Docker containers'));
|
|
1070
|
+
this.log(styles.muted(' Agents need credentials to authenticate with Claude.'));
|
|
1066
1071
|
this.log('');
|
|
1067
|
-
//
|
|
1068
|
-
const
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
create tab with default profile
|
|
1074
|
-
tell current session
|
|
1075
|
-
write text "${authCmd}"
|
|
1076
|
-
end tell
|
|
1077
|
-
end tell
|
|
1078
|
-
end tell
|
|
1079
|
-
'`);
|
|
1072
|
+
// Build auth method choices
|
|
1073
|
+
const authChoices = [
|
|
1074
|
+
{ name: `🔐 OAuth (recommended — uses Max subscription)`, value: 'oauth' },
|
|
1075
|
+
];
|
|
1076
|
+
if (hasApiKey) {
|
|
1077
|
+
authChoices.push({ name: '🔑 API key (uses API credits, not Max subscription)', value: 'apikey' });
|
|
1080
1078
|
}
|
|
1081
|
-
|
|
1082
|
-
|
|
1079
|
+
authChoices.push({ name: '💻 Switch to host environment instead', value: 'host' }, { name: '✗ Cancel', value: 'cancel' });
|
|
1080
|
+
// Use FlagResolver for auth method selection
|
|
1081
|
+
const authResolver = new FlagResolver({
|
|
1082
|
+
commandName: 'work start',
|
|
1083
|
+
baseCommand: `prlt work start ${ticketId}`,
|
|
1084
|
+
jsonMode,
|
|
1085
|
+
flags: {},
|
|
1086
|
+
});
|
|
1087
|
+
authResolver.addPrompt({
|
|
1088
|
+
flagName: 'authAction',
|
|
1089
|
+
type: 'list',
|
|
1090
|
+
message: 'How should the agent authenticate with Claude?',
|
|
1091
|
+
choices: () => authChoices,
|
|
1092
|
+
});
|
|
1093
|
+
const authResult = await authResolver.resolve();
|
|
1094
|
+
const authAction = authResult.authAction;
|
|
1095
|
+
if (authAction === 'cancel') {
|
|
1096
|
+
db.close();
|
|
1097
|
+
this.log(styles.muted('Cancelled.'));
|
|
1098
|
+
return;
|
|
1099
|
+
}
|
|
1100
|
+
if (authAction === 'host') {
|
|
1101
|
+
environment = 'host';
|
|
1102
|
+
this.log(styles.muted('Switched to host environment.'));
|
|
1103
|
+
}
|
|
1104
|
+
else if (authAction === 'apikey') {
|
|
1105
|
+
useApiKey = true;
|
|
1106
|
+
this.log(styles.warning('Using ANTHROPIC_API_KEY — this will consume API credits.'));
|
|
1107
|
+
this.log(styles.muted(`Run "${this.config.bin} agent auth" to set up OAuth and use your Max subscription instead.`));
|
|
1108
|
+
this.log('');
|
|
1109
|
+
}
|
|
1110
|
+
else if (authAction === 'oauth') {
|
|
1111
|
+
this.log('');
|
|
1112
|
+
this.log(styles.primary(`Opening ${this.config.bin} agent auth in new tab...`));
|
|
1113
|
+
this.log('');
|
|
1114
|
+
// Open auth in a new terminal tab
|
|
1115
|
+
const authCmd = `${process.argv[1]} agent auth`;
|
|
1083
1116
|
try {
|
|
1084
|
-
execSync(`osascript -e '
|
|
1117
|
+
execSync(`osascript -e '
|
|
1118
|
+
tell application "iTerm"
|
|
1119
|
+
tell current window
|
|
1120
|
+
create tab with default profile
|
|
1121
|
+
tell current session
|
|
1122
|
+
write text "${authCmd}"
|
|
1123
|
+
end tell
|
|
1124
|
+
end tell
|
|
1125
|
+
end tell
|
|
1126
|
+
'`);
|
|
1085
1127
|
}
|
|
1086
1128
|
catch {
|
|
1087
|
-
|
|
1088
|
-
|
|
1129
|
+
// Fallback: try Terminal.app
|
|
1130
|
+
try {
|
|
1131
|
+
execSync(`osascript -e 'tell application "Terminal" to do script "${authCmd}"'`);
|
|
1132
|
+
}
|
|
1133
|
+
catch {
|
|
1134
|
+
this.log(styles.warning('Could not open new terminal tab.'));
|
|
1135
|
+
this.log(styles.muted(`Please run manually: ${authCmd}`));
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
this.log(styles.muted('Complete the /login flow in the new tab, then press Enter here...'));
|
|
1139
|
+
this.log('');
|
|
1140
|
+
// Wait for user to complete auth
|
|
1141
|
+
await this.prompt([{
|
|
1142
|
+
type: 'input',
|
|
1143
|
+
name: 'done',
|
|
1144
|
+
message: 'Press Enter when authentication is complete:',
|
|
1145
|
+
}]);
|
|
1146
|
+
// Check if credentials now exist
|
|
1147
|
+
if (!dockerCredentialsExist()) {
|
|
1148
|
+
this.log('');
|
|
1149
|
+
this.log(styles.warning('Authentication did not complete. No credentials found.'));
|
|
1150
|
+
db.close();
|
|
1151
|
+
return;
|
|
1152
|
+
}
|
|
1153
|
+
const info = getDockerCredentialInfo();
|
|
1154
|
+
this.log('');
|
|
1155
|
+
this.log(styles.success('✓ Credentials configured'));
|
|
1156
|
+
if (info) {
|
|
1157
|
+
this.log(styles.muted(` Subscription: ${info.subscriptionType || 'unknown'}`));
|
|
1158
|
+
this.log(styles.muted(` Expires: ${info.expiresAt.toLocaleDateString()}`));
|
|
1089
1159
|
}
|
|
1090
|
-
}
|
|
1091
|
-
this.log(styles.muted('Complete the /login flow in the new tab, then press Enter here...'));
|
|
1092
|
-
this.log('');
|
|
1093
|
-
// Wait for user to complete auth
|
|
1094
|
-
await this.prompt([{
|
|
1095
|
-
type: 'input',
|
|
1096
|
-
name: 'done',
|
|
1097
|
-
message: 'Press Enter when authentication is complete:',
|
|
1098
|
-
}]);
|
|
1099
|
-
// Check if credentials now exist
|
|
1100
|
-
if (!dockerCredentialsExist()) {
|
|
1101
1160
|
this.log('');
|
|
1102
|
-
this.log(styles.warning('Authentication did not complete. No credentials found.'));
|
|
1103
|
-
db.close();
|
|
1104
|
-
return;
|
|
1105
1161
|
}
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1162
|
+
// Prompt "Save as default?" after a successful auth method choice
|
|
1163
|
+
// (only if they chose oauth or apikey, not host/cancel)
|
|
1164
|
+
if (authAction === 'oauth' || authAction === 'apikey') {
|
|
1165
|
+
const saveChoices = [
|
|
1166
|
+
{ name: 'Yes — skip this menu next time', value: true },
|
|
1167
|
+
{ name: 'No — ask me each time', value: false },
|
|
1168
|
+
];
|
|
1169
|
+
const saveMessage = 'Save as default auth method?';
|
|
1170
|
+
const saveResolver = new FlagResolver({
|
|
1171
|
+
commandName: 'work start',
|
|
1172
|
+
baseCommand: `prlt work start ${ticketId}`,
|
|
1173
|
+
jsonMode,
|
|
1174
|
+
flags: {},
|
|
1175
|
+
});
|
|
1176
|
+
saveResolver.addPrompt({
|
|
1177
|
+
flagName: 'saveDefault',
|
|
1178
|
+
type: 'list',
|
|
1179
|
+
message: saveMessage,
|
|
1180
|
+
default: true,
|
|
1181
|
+
choices: () => saveChoices,
|
|
1182
|
+
});
|
|
1183
|
+
const saveResult = await saveResolver.resolve();
|
|
1184
|
+
if (saveResult.saveDefault) {
|
|
1185
|
+
const methodToSave = authAction === 'apikey' ? 'apikey' : 'oauth';
|
|
1186
|
+
saveAuthMethod(db, methodToSave);
|
|
1187
|
+
this.log(styles.muted(`Auth method saved: ${methodToSave}. Will skip this menu next time.`));
|
|
1188
|
+
this.log('');
|
|
1189
|
+
}
|
|
1112
1190
|
}
|
|
1113
|
-
this.log('');
|
|
1114
1191
|
}
|
|
1115
1192
|
}
|
|
1116
1193
|
}
|
|
@@ -1161,9 +1238,13 @@ export default class WorkStart extends PMOCommand {
|
|
|
1161
1238
|
createPR = false;
|
|
1162
1239
|
}
|
|
1163
1240
|
else if (ghAvailable) {
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
createPR =
|
|
1241
|
+
if (context.modifiesCode === false) {
|
|
1242
|
+
// Non-code-modifying actions (groom, review, resolve) default to no PR
|
|
1243
|
+
createPR = false;
|
|
1244
|
+
}
|
|
1245
|
+
else if (jsonMode && flags.yes) {
|
|
1246
|
+
// In JSON mode with --yes, default to creating PR for code-modifying actions
|
|
1247
|
+
createPR = true;
|
|
1167
1248
|
}
|
|
1168
1249
|
else {
|
|
1169
1250
|
// Use FlagResolver for PR choice
|
|
@@ -1206,9 +1287,7 @@ export default class WorkStart extends PMOCommand {
|
|
|
1206
1287
|
this.log(styles.warning(` Permissions: ⚠️ danger (--dangerously-skip-permissions)`));
|
|
1207
1288
|
}
|
|
1208
1289
|
this.log(styles.muted(` Output: ${outputMode === 'interactive' ? 'streaming (watch Claude work)' : 'print (final result only)'}`));
|
|
1209
|
-
|
|
1210
|
-
this.log(styles.muted(` Create PR: ${createPR ? 'yes (when work is ready)' : 'no'}`));
|
|
1211
|
-
}
|
|
1290
|
+
this.log(styles.muted(` PR mode: ${createPR ? 'create-pr' : 'no-pr'}${ghAvailable ? '' : ' (gh CLI not available)'}`));
|
|
1212
1291
|
this.log(styles.muted(` Worktree: ${worktreePath}`));
|
|
1213
1292
|
this.log(styles.muted(` Branch: ${branch}`));
|
|
1214
1293
|
this.log('');
|
|
@@ -1510,7 +1589,9 @@ export default class WorkStart extends PMOCommand {
|
|
|
1510
1589
|
});
|
|
1511
1590
|
// Output results
|
|
1512
1591
|
if (jsonMode) {
|
|
1513
|
-
// Output JSON execution result
|
|
1592
|
+
// Output JSON execution result with resolved PR mode
|
|
1593
|
+
const metadata = createMetadata('work start', flags);
|
|
1594
|
+
metadata.resolvedPRMode = createPR ? 'create-pr' : 'no-pr';
|
|
1514
1595
|
outputExecutionResultAsJson([{
|
|
1515
1596
|
workId: execution.id,
|
|
1516
1597
|
ticketId: ticket.id,
|
|
@@ -1518,7 +1599,7 @@ export default class WorkStart extends PMOCommand {
|
|
|
1518
1599
|
sessionId: result.sessionId,
|
|
1519
1600
|
containerId: result.containerId,
|
|
1520
1601
|
status: 'running',
|
|
1521
|
-
}], 1, 0,
|
|
1602
|
+
}], 1, 0, metadata);
|
|
1522
1603
|
}
|
|
1523
1604
|
else {
|
|
1524
1605
|
this.log('');
|
|
@@ -1533,13 +1614,15 @@ export default class WorkStart extends PMOCommand {
|
|
|
1533
1614
|
else {
|
|
1534
1615
|
executionStorage.updateStatus(execution.id, 'failed');
|
|
1535
1616
|
if (jsonMode) {
|
|
1536
|
-
// Output JSON failure result
|
|
1617
|
+
// Output JSON failure result with resolved PR mode
|
|
1618
|
+
const failMetadata = createMetadata('work start', flags);
|
|
1619
|
+
failMetadata.resolvedPRMode = createPR ? 'create-pr' : 'no-pr';
|
|
1537
1620
|
outputExecutionResultAsJson([{
|
|
1538
1621
|
workId: execution.id,
|
|
1539
1622
|
ticketId: ticket.id,
|
|
1540
1623
|
agent: assignedAgent,
|
|
1541
1624
|
status: 'failed',
|
|
1542
|
-
}], 0, 1,
|
|
1625
|
+
}], 0, 1, failMetadata);
|
|
1543
1626
|
}
|
|
1544
1627
|
else {
|
|
1545
1628
|
this.error(`Failed to start work: ${result.error}`);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { PMOCommand } from '../../lib/pmo/index.js';
|
|
2
|
+
export default class WorkStatus extends PMOCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static flags: {
|
|
6
|
+
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
7
|
+
machine: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
8
|
+
project: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
|
+
};
|
|
10
|
+
protected getPMOOptions(): {
|
|
11
|
+
promptIfMultiple: boolean;
|
|
12
|
+
};
|
|
13
|
+
execute(): Promise<void>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { PMOCommand, pmoBaseFlags } from '../../lib/pmo/index.js';
|
|
2
|
+
import { styles, formatPriority } from '../../lib/styles.js';
|
|
3
|
+
import { shouldOutputJson, outputSuccessAsJson, createMetadata, } from '../../lib/prompt-json.js';
|
|
4
|
+
export default class WorkStatus extends PMOCommand {
|
|
5
|
+
static description = 'Show current work status (in-progress tickets)';
|
|
6
|
+
static examples = [
|
|
7
|
+
'<%= config.bin %> <%= command.id %>',
|
|
8
|
+
'<%= config.bin %> <%= command.id %> --json',
|
|
9
|
+
];
|
|
10
|
+
static flags = {
|
|
11
|
+
...pmoBaseFlags,
|
|
12
|
+
};
|
|
13
|
+
getPMOOptions() {
|
|
14
|
+
return { promptIfMultiple: false };
|
|
15
|
+
}
|
|
16
|
+
async execute() {
|
|
17
|
+
const { flags } = await this.parse(WorkStatus);
|
|
18
|
+
const projectId = flags.project;
|
|
19
|
+
const jsonMode = shouldOutputJson(flags);
|
|
20
|
+
// List all tickets, optionally filtered by project
|
|
21
|
+
const tickets = await this.storage.listTickets(projectId, { allProjects: !projectId });
|
|
22
|
+
const inProgress = tickets.filter(t => t.statusCategory === 'started' && t.assignee);
|
|
23
|
+
if (jsonMode) {
|
|
24
|
+
outputSuccessAsJson({
|
|
25
|
+
inProgressCount: inProgress.length,
|
|
26
|
+
tickets: inProgress.map(t => ({
|
|
27
|
+
id: t.id,
|
|
28
|
+
title: t.title,
|
|
29
|
+
assignee: t.assignee,
|
|
30
|
+
statusName: t.statusName,
|
|
31
|
+
priority: t.priority,
|
|
32
|
+
projectId: t.projectId,
|
|
33
|
+
branch: t.branch,
|
|
34
|
+
})),
|
|
35
|
+
}, createMetadata('work status', flags));
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
// Interactive mode
|
|
39
|
+
if (inProgress.length === 0) {
|
|
40
|
+
this.log(styles.info('No in-progress work found.'));
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
this.log(styles.title(`\nWork In Progress (${inProgress.length})`));
|
|
44
|
+
this.log(styles.muted('─'.repeat(60)));
|
|
45
|
+
for (const ticket of inProgress) {
|
|
46
|
+
const priority = formatPriority(ticket.priority);
|
|
47
|
+
this.log(` ${styles.code(ticket.id)} ${ticket.title} ${priority}`);
|
|
48
|
+
const details = [
|
|
49
|
+
ticket.assignee ? `Assignee: ${ticket.assignee}` : null,
|
|
50
|
+
ticket.statusName ? `Status: ${ticket.statusName}` : null,
|
|
51
|
+
ticket.projectId ? `Project: ${ticket.projectId}` : null,
|
|
52
|
+
ticket.branch ? `Branch: ${ticket.branch}` : null,
|
|
53
|
+
].filter(Boolean).join(' | ');
|
|
54
|
+
if (details) {
|
|
55
|
+
this.log(styles.muted(` ${details}`));
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
this.log('');
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -60,7 +60,7 @@ export default class WorkWatch extends PMOCommand {
|
|
|
60
60
|
default: false,
|
|
61
61
|
}),
|
|
62
62
|
'create-pr': Flags.boolean({
|
|
63
|
-
description: 'Create PR when work is ready',
|
|
63
|
+
description: 'Create PR when work is ready (canonical flag for PR behavior)',
|
|
64
64
|
default: false,
|
|
65
65
|
}),
|
|
66
66
|
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Flags } from '@oclif/core';
|
|
2
2
|
import { PMOCommand, pmoBaseFlags } from '../../lib/pmo/index.js';
|
|
3
|
+
import { shouldOutputJson } from '../../lib/prompt-json.js';
|
|
3
4
|
import { styles } from '../../lib/styles.js';
|
|
4
5
|
export default class Workflow extends PMOCommand {
|
|
5
6
|
static description = 'List all available workflows (alias for workflow list)';
|
|
@@ -31,7 +32,7 @@ export default class Workflow extends PMOCommand {
|
|
|
31
32
|
if (flags.custom)
|
|
32
33
|
filter.isBuiltin = false;
|
|
33
34
|
const workflows = await this.storage.listWorkflows(Object.keys(filter).length > 0 ? filter : undefined);
|
|
34
|
-
if (flags
|
|
35
|
+
if (shouldOutputJson(flags)) {
|
|
35
36
|
this.log(JSON.stringify(workflows, null, 2));
|
|
36
37
|
return;
|
|
37
38
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import WorkflowView from './view.js';
|
|
2
|
+
export default class WorkflowShow extends WorkflowView {
|
|
3
|
+
static description: string;
|
|
4
|
+
static hidden: boolean;
|
|
5
|
+
static args: {
|
|
6
|
+
id: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
|
|
7
|
+
};
|
|
8
|
+
static flags: {
|
|
9
|
+
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
10
|
+
machine: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
11
|
+
project: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Args } from '@oclif/core';
|
|
2
|
+
import { pmoBaseFlags } from '../../lib/pmo/index.js';
|
|
3
|
+
import WorkflowView from './view.js';
|
|
4
|
+
export default class WorkflowShow extends WorkflowView {
|
|
5
|
+
static description = 'View details of a workflow (alias for workflow view)';
|
|
6
|
+
static hidden = true;
|
|
7
|
+
static args = {
|
|
8
|
+
id: Args.string({
|
|
9
|
+
description: 'Workflow ID - prompts with dropdown if not provided',
|
|
10
|
+
required: false,
|
|
11
|
+
}),
|
|
12
|
+
};
|
|
13
|
+
static flags = {
|
|
14
|
+
...pmoBaseFlags,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
@@ -4,6 +4,7 @@ import * as fs from 'node:fs';
|
|
|
4
4
|
import * as path from 'node:path';
|
|
5
5
|
import { registerWorkspace, normalizePath, isWorkspaceRegistered, getWorkspaceNameFromPath, } from '../../lib/machine-config.js';
|
|
6
6
|
import { machineOutputFlags } from '../../lib/pmo/index.js';
|
|
7
|
+
import { shouldOutputJson } from '../../lib/prompt-json.js';
|
|
7
8
|
export default class WorkspaceAdd extends Command {
|
|
8
9
|
static description = 'Register an existing workspace in the machine config';
|
|
9
10
|
static examples = [
|
|
@@ -26,6 +27,7 @@ export default class WorkspaceAdd extends Command {
|
|
|
26
27
|
};
|
|
27
28
|
async run() {
|
|
28
29
|
const { args, flags } = await this.parse(WorkspaceAdd);
|
|
30
|
+
const jsonMode = shouldOutputJson(flags);
|
|
29
31
|
// Normalize the path
|
|
30
32
|
const workspacePath = normalizePath(args.path);
|
|
31
33
|
// Check if path exists
|
|
@@ -56,6 +58,10 @@ export default class WorkspaceAdd extends Command {
|
|
|
56
58
|
}
|
|
57
59
|
// Check if already registered
|
|
58
60
|
if (isWorkspaceRegistered(workspacePath)) {
|
|
61
|
+
if (jsonMode) {
|
|
62
|
+
this.log(JSON.stringify({ type: 'success', result: { path: workspacePath, status: 'already_registered' } }));
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
59
65
|
this.log(chalk.yellow(`Workspace is already registered: ${workspacePath}`));
|
|
60
66
|
this.log(chalk.gray('Use "prlt workspace use" to set it as active.'));
|
|
61
67
|
return;
|
|
@@ -65,11 +71,20 @@ export default class WorkspaceAdd extends Command {
|
|
|
65
71
|
// Register the workspace
|
|
66
72
|
try {
|
|
67
73
|
const entry = registerWorkspace(workspacePath, workspaceName, true);
|
|
74
|
+
if (jsonMode) {
|
|
75
|
+
this.log(JSON.stringify({ type: 'success', result: { name: entry.name, path: entry.path, registeredAt: entry.registeredAt, status: 'registered' } }));
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
68
78
|
this.log(chalk.green(`Registered workspace: ${entry.name}`));
|
|
69
79
|
this.log(chalk.gray(` Path: ${entry.path}`));
|
|
70
80
|
this.log(chalk.gray(` Registered at: ${entry.registeredAt}`));
|
|
71
81
|
}
|
|
72
82
|
catch (error) {
|
|
83
|
+
if (jsonMode) {
|
|
84
|
+
this.log(JSON.stringify({ type: 'error', error: { code: 'REGISTER_FAILED', message: error.message } }));
|
|
85
|
+
this.exit(1);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
73
88
|
this.error(`Failed to register workspace: ${error.message}`);
|
|
74
89
|
}
|
|
75
90
|
}
|
|
@@ -5,6 +5,7 @@ import { machineOutputFlags } from '../../lib/pmo/index.js';
|
|
|
5
5
|
import { getRegisteredWorkspaces, getActiveWorkspace, } from '../../lib/machine-config.js';
|
|
6
6
|
import * as fs from 'node:fs';
|
|
7
7
|
import * as path from 'node:path';
|
|
8
|
+
import { shouldOutputJson } from '../../lib/prompt-json.js';
|
|
8
9
|
export default class WorkspaceList extends Command {
|
|
9
10
|
static description = 'List all registered and discovered HQ workspaces';
|
|
10
11
|
static examples = [
|
|
@@ -78,7 +79,7 @@ export default class WorkspaceList extends Command {
|
|
|
78
79
|
});
|
|
79
80
|
}
|
|
80
81
|
// JSON output
|
|
81
|
-
if (flags
|
|
82
|
+
if (shouldOutputJson(flags)) {
|
|
82
83
|
const output = {
|
|
83
84
|
workspaces: workspaces.map((w) => ({
|
|
84
85
|
name: w.name,
|
|
@@ -5,7 +5,7 @@ import { PromptCommand } from '../../lib/prompt-command.js';
|
|
|
5
5
|
import { styles } from '../../lib/styles.js';
|
|
6
6
|
import { getRegisteredHeadquarters, unregisterHeadquarters, } from '../../lib/machine-config.js';
|
|
7
7
|
import { getWorkspaceAgents, removeAgentsFromDatabase, getDatabasePath, } from '../../lib/database/index.js';
|
|
8
|
-
import { outputConfirmationNeededAsJson, createMetadata, } from '../../lib/prompt-json.js';
|
|
8
|
+
import { outputConfirmationNeededAsJson, createMetadata, shouldOutputJson, isNonTTY, } from '../../lib/prompt-json.js';
|
|
9
9
|
import { machineOutputFlags } from '../../lib/pmo/index.js';
|
|
10
10
|
export default class WorkspacePrune extends PromptCommand {
|
|
11
11
|
static description = 'Remove stale workspace entries and agents with deleted worktrees';
|
|
@@ -29,16 +29,16 @@ export default class WorkspacePrune extends PromptCommand {
|
|
|
29
29
|
};
|
|
30
30
|
async run() {
|
|
31
31
|
const { flags } = await this.parse(WorkspacePrune);
|
|
32
|
-
// In non-TTY mode
|
|
33
|
-
//
|
|
34
|
-
const
|
|
35
|
-
const effectiveDryRun = flags['dry-run'] || (
|
|
32
|
+
// In non-TTY mode (CI, scripts, piped), default to dry-run unless --force is set.
|
|
33
|
+
// This applies regardless of output format (text or JSON).
|
|
34
|
+
const nonTTY = isNonTTY();
|
|
35
|
+
const effectiveDryRun = flags['dry-run'] || (nonTTY && !flags.force);
|
|
36
36
|
// Find stale entries
|
|
37
37
|
const staleWorkspaces = this.findStaleWorkspaces();
|
|
38
38
|
const staleAgents = this.findStaleAgents();
|
|
39
39
|
const totalStale = staleWorkspaces.length + staleAgents.length;
|
|
40
40
|
// JSON output
|
|
41
|
-
if (flags
|
|
41
|
+
if (shouldOutputJson(flags)) {
|
|
42
42
|
const output = {
|
|
43
43
|
dryRun: effectiveDryRun,
|
|
44
44
|
staleWorkspaces: staleWorkspaces.map(w => ({
|
|
@@ -112,7 +112,7 @@ export default class WorkspacePrune extends PromptCommand {
|
|
|
112
112
|
this.log(styles.muted(` • ${staleAgents.length} agent record(s)`));
|
|
113
113
|
}
|
|
114
114
|
this.log('');
|
|
115
|
-
if (
|
|
115
|
+
if (nonTTY) {
|
|
116
116
|
this.log(styles.muted('Non-TTY environment detected. Run with --force to remove these entries.'));
|
|
117
117
|
}
|
|
118
118
|
else {
|