@proletariat/cli 0.3.35 → 0.3.36
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/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 +3 -2
- 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/view.js +27 -0
- package/dist/commands/execution/config.d.ts +0 -4
- package/dist/commands/execution/config.js +10 -32
- package/dist/commands/execution/index.js +2 -1
- package/dist/commands/execution/logs.js +1 -1
- package/dist/commands/execution/stop.js +2 -1
- package/dist/commands/execution/view.js +22 -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 +25 -0
- package/dist/commands/phase/template/list.js +2 -1
- 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/session/create.d.ts +19 -0
- package/dist/commands/session/create.js +102 -0
- package/dist/commands/session/health.js +1 -20
- package/dist/commands/session/index.js +14 -1
- package/dist/commands/session/list.js +26 -7
- 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/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/start.js +169 -94
- package/dist/commands/work/status.d.ts +14 -0
- package/dist/commands/work/status.js +60 -0
- 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 +5 -5
- package/dist/lib/branch/index.d.ts +1 -0
- package/dist/lib/execution/config.d.ts +15 -1
- package/dist/lib/execution/config.js +28 -0
- package/dist/lib/execution/runners.d.ts +11 -0
- package/dist/lib/execution/runners.js +53 -19
- package/dist/lib/execution/session-utils.d.ts +11 -1
- package/dist/lib/execution/session-utils.js +26 -1
- package/dist/lib/execution/storage.d.ts +5 -0
- package/dist/lib/execution/storage.js +18 -3
- package/dist/lib/execution/types.d.ts +2 -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/spec.js +1 -1
- package/dist/lib/mcp/tools/ticket.js +11 -9
- package/dist/lib/mcp/tools/work.js +96 -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/storage/actions.js +1 -1
- package/dist/lib/pmo/storage/base.js +195 -50
- package/dist/lib/pmo/storage/types.d.ts +1 -0
- package/dist/lib/prompt-command.d.ts +20 -0
- package/dist/lib/prompt-command.js +38 -2
- package/dist/lib/prompt-json.d.ts +36 -4
- package/dist/lib/prompt-json.js +129 -7
- package/dist/lib/styles.d.ts +37 -0
- package/dist/lib/styles.js +73 -0
- package/oclif.manifest.json +3259 -2701
- package/package.json +1 -1
|
@@ -3,7 +3,6 @@ import { PMOCommand, pmoBaseFlags } from '../../lib/pmo/index.js';
|
|
|
3
3
|
import { styles } from '../../lib/styles.js';
|
|
4
4
|
import { shouldOutputJson, outputErrorAsJson, createMetadata, } from '../../lib/prompt-json.js';
|
|
5
5
|
export default class TicketView extends PMOCommand {
|
|
6
|
-
static aliases = ['ticket:show'];
|
|
7
6
|
static description = 'View detailed ticket information';
|
|
8
7
|
static examples = [
|
|
9
8
|
'<%= config.bin %> <%= command.id %> TICK-001',
|
|
@@ -58,6 +57,36 @@ export default class TicketView extends PMOCommand {
|
|
|
58
57
|
if (!ticket) {
|
|
59
58
|
this.error(`Ticket "${ticketId}" not found.`);
|
|
60
59
|
}
|
|
60
|
+
// JSON output mode
|
|
61
|
+
if (jsonMode) {
|
|
62
|
+
this.log(JSON.stringify({
|
|
63
|
+
success: true,
|
|
64
|
+
ticket: {
|
|
65
|
+
id: ticket.id,
|
|
66
|
+
title: ticket.title,
|
|
67
|
+
description: ticket.description,
|
|
68
|
+
priority: ticket.priority,
|
|
69
|
+
category: ticket.category,
|
|
70
|
+
statusName: ticket.statusName,
|
|
71
|
+
statusCategory: ticket.statusCategory,
|
|
72
|
+
projectId: ticket.projectId,
|
|
73
|
+
assignee: ticket.assignee,
|
|
74
|
+
owner: ticket.owner,
|
|
75
|
+
branch: ticket.branch,
|
|
76
|
+
epicId: ticket.epicId,
|
|
77
|
+
position: ticket.position,
|
|
78
|
+
subtasks: ticket.subtasks,
|
|
79
|
+
labels: ticket.labels,
|
|
80
|
+
metadata: ticket.metadata,
|
|
81
|
+
blockedBy: ticket.blockedBy,
|
|
82
|
+
acceptanceCriteria: ticket.acceptanceCriteria,
|
|
83
|
+
specId: ticket.specId,
|
|
84
|
+
createdAt: ticket.createdAt?.toISOString(),
|
|
85
|
+
updatedAt: ticket.updatedAt?.toISOString(),
|
|
86
|
+
},
|
|
87
|
+
}, null, 2));
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
61
90
|
// Get project board (may be null if project was deleted/orphaned)
|
|
62
91
|
const board = ticket.projectId ? await this.storage.getProjectBoard(ticket.projectId) : null;
|
|
63
92
|
const projectName = board?.name || ticket.projectId || 'Unknown';
|
|
@@ -22,6 +22,7 @@ export default class Work extends PMOCommand {
|
|
|
22
22
|
// Execution actions first (most common), then ownership
|
|
23
23
|
// Each choice includes the full command for AI agents to execute
|
|
24
24
|
const menuChoices = [
|
|
25
|
+
{ id: 'status', name: 'View work status (in-progress tickets)', command: `prlt work status -P ${projectId} --json` },
|
|
25
26
|
{ id: 'start', name: 'Start work (launch single agent)', command: `prlt work start -P ${projectId} --json` },
|
|
26
27
|
{ id: 'resolve', name: 'Resolve questions (agent-assisted)', command: `prlt work resolve -P ${projectId} --json` },
|
|
27
28
|
{ id: 'spawn', name: 'Spawn work (batch by column)', command: `prlt work spawn -P ${projectId} --json` },
|
|
@@ -46,6 +47,9 @@ export default class Work extends PMOCommand {
|
|
|
46
47
|
// Pass --project to avoid re-prompting for project selection
|
|
47
48
|
const projectArgs = ['--project', projectId];
|
|
48
49
|
switch (action) {
|
|
50
|
+
case 'status':
|
|
51
|
+
await this.config.runCommand('work:status', projectArgs);
|
|
52
|
+
break;
|
|
49
53
|
case 'start':
|
|
50
54
|
await this.config.runCommand('work:start', projectArgs);
|
|
51
55
|
break;
|
|
@@ -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';
|
|
@@ -810,11 +810,20 @@ export default class WorkStart extends PMOCommand {
|
|
|
810
810
|
flags: {},
|
|
811
811
|
});
|
|
812
812
|
envResolver.addPrompt({
|
|
813
|
-
flagName: '
|
|
813
|
+
flagName: 'environment',
|
|
814
814
|
type: 'list',
|
|
815
815
|
message: 'Where should the agent run?',
|
|
816
816
|
default: 'devcontainer',
|
|
817
817
|
choices: () => envChoices,
|
|
818
|
+
getCommand: (value) => {
|
|
819
|
+
const base = `prlt work start ${ticketId}`;
|
|
820
|
+
if (value === 'host')
|
|
821
|
+
return `${base} --run-on-host --json`;
|
|
822
|
+
if (value === 'cancel')
|
|
823
|
+
return '';
|
|
824
|
+
// devcontainer is the default when available
|
|
825
|
+
return `${base} --json`;
|
|
826
|
+
},
|
|
818
827
|
});
|
|
819
828
|
await envResolver.resolve();
|
|
820
829
|
// FlagResolver exits in JSON mode, so we never reach here
|
|
@@ -1008,109 +1017,171 @@ export default class WorkStart extends PMOCommand {
|
|
|
1008
1017
|
const outputMode = flags.output || DEFAULT_EXECUTION_CONFIG.outputMode;
|
|
1009
1018
|
// Track whether user explicitly chose to use API key instead of OAuth
|
|
1010
1019
|
let useApiKey = flags['use-api-key'] || false;
|
|
1011
|
-
//
|
|
1020
|
+
// Auth method resolution for devcontainer environment
|
|
1012
1021
|
if (environment === 'devcontainer' && !useApiKey) {
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
else {
|
|
1020
|
-
const hasApiKey = !!process.env.ANTHROPIC_API_KEY;
|
|
1022
|
+
// Check for saved auth method preference
|
|
1023
|
+
const savedAuthMethod = getAuthMethod(db);
|
|
1024
|
+
const hasApiKey = !!process.env.ANTHROPIC_API_KEY;
|
|
1025
|
+
if (savedAuthMethod === 'apikey') {
|
|
1026
|
+
// Saved preference: API key — validate it's still set
|
|
1027
|
+
if (!hasApiKey) {
|
|
1021
1028
|
this.log('');
|
|
1022
|
-
this.log(styles.warning('⚠️
|
|
1023
|
-
this.log(styles.muted('
|
|
1029
|
+
this.log(styles.warning('⚠️ Saved auth method is "apikey" but ANTHROPIC_API_KEY is not set in your environment.'));
|
|
1030
|
+
this.log(styles.muted(' Set the env var or run "' + this.config.bin + ' agent auth" to switch to OAuth.'));
|
|
1031
|
+
db.close();
|
|
1032
|
+
return;
|
|
1033
|
+
}
|
|
1034
|
+
useApiKey = true;
|
|
1035
|
+
}
|
|
1036
|
+
else if (savedAuthMethod === 'oauth') {
|
|
1037
|
+
// Saved preference: OAuth — validate credentials exist
|
|
1038
|
+
const hasCredentials = dockerCredentialsExist();
|
|
1039
|
+
if (!hasCredentials) {
|
|
1024
1040
|
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('');
|
|
1041
|
+
this.log(styles.warning('⚠️ Saved auth method is "oauth" but no OAuth credentials found.'));
|
|
1042
|
+
this.log(styles.muted(' Run "' + this.config.bin + ' agent auth" to authenticate.'));
|
|
1043
|
+
db.close();
|
|
1044
|
+
return;
|
|
1045
|
+
}
|
|
1046
|
+
// OAuth credentials valid — continue (useApiKey stays false)
|
|
1047
|
+
}
|
|
1048
|
+
else {
|
|
1049
|
+
// No saved preference — show auth method menu
|
|
1050
|
+
const hasCredentials = dockerCredentialsExist();
|
|
1051
|
+
if (hasCredentials) {
|
|
1052
|
+
// OAuth credentials exist, use them silently (no menu needed)
|
|
1053
|
+
// useApiKey stays false
|
|
1054
|
+
}
|
|
1055
|
+
else {
|
|
1056
|
+
// No saved preference and no OAuth credentials — prompt user
|
|
1057
|
+
// In JSON mode with --yes, continue anyway (agent can run /login)
|
|
1058
|
+
if (jsonMode && flags.yes) {
|
|
1059
|
+
// Continue without prompting - agent will need to handle auth
|
|
1062
1060
|
}
|
|
1063
|
-
else
|
|
1061
|
+
else {
|
|
1064
1062
|
this.log('');
|
|
1065
|
-
this.log(styles.
|
|
1063
|
+
this.log(styles.warning('⚠️ No Claude Code OAuth credentials found for Docker containers'));
|
|
1064
|
+
this.log(styles.muted(' Agents need credentials to authenticate with Claude.'));
|
|
1066
1065
|
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
|
-
'`);
|
|
1066
|
+
// Build auth method choices
|
|
1067
|
+
const authChoices = [
|
|
1068
|
+
{ name: `🔐 OAuth (recommended — uses Max subscription)`, value: 'oauth' },
|
|
1069
|
+
];
|
|
1070
|
+
if (hasApiKey) {
|
|
1071
|
+
authChoices.push({ name: '🔑 API key (uses API credits, not Max subscription)', value: 'apikey' });
|
|
1080
1072
|
}
|
|
1081
|
-
|
|
1082
|
-
|
|
1073
|
+
authChoices.push({ name: '💻 Switch to host environment instead', value: 'host' }, { name: '✗ Cancel', value: 'cancel' });
|
|
1074
|
+
// Use FlagResolver for auth method selection
|
|
1075
|
+
const authResolver = new FlagResolver({
|
|
1076
|
+
commandName: 'work start',
|
|
1077
|
+
baseCommand: `prlt work start ${ticketId}`,
|
|
1078
|
+
jsonMode,
|
|
1079
|
+
flags: {},
|
|
1080
|
+
});
|
|
1081
|
+
authResolver.addPrompt({
|
|
1082
|
+
flagName: 'authAction',
|
|
1083
|
+
type: 'list',
|
|
1084
|
+
message: 'How should the agent authenticate with Claude?',
|
|
1085
|
+
choices: () => authChoices,
|
|
1086
|
+
});
|
|
1087
|
+
const authResult = await authResolver.resolve();
|
|
1088
|
+
const authAction = authResult.authAction;
|
|
1089
|
+
if (authAction === 'cancel') {
|
|
1090
|
+
db.close();
|
|
1091
|
+
this.log(styles.muted('Cancelled.'));
|
|
1092
|
+
return;
|
|
1093
|
+
}
|
|
1094
|
+
if (authAction === 'host') {
|
|
1095
|
+
environment = 'host';
|
|
1096
|
+
this.log(styles.muted('Switched to host environment.'));
|
|
1097
|
+
}
|
|
1098
|
+
else if (authAction === 'apikey') {
|
|
1099
|
+
useApiKey = true;
|
|
1100
|
+
this.log(styles.warning('Using ANTHROPIC_API_KEY — this will consume API credits.'));
|
|
1101
|
+
this.log(styles.muted(`Run "${this.config.bin} agent auth" to set up OAuth and use your Max subscription instead.`));
|
|
1102
|
+
this.log('');
|
|
1103
|
+
}
|
|
1104
|
+
else if (authAction === 'oauth') {
|
|
1105
|
+
this.log('');
|
|
1106
|
+
this.log(styles.primary(`Opening ${this.config.bin} agent auth in new tab...`));
|
|
1107
|
+
this.log('');
|
|
1108
|
+
// Open auth in a new terminal tab
|
|
1109
|
+
const authCmd = `${process.argv[1]} agent auth`;
|
|
1083
1110
|
try {
|
|
1084
|
-
execSync(`osascript -e '
|
|
1111
|
+
execSync(`osascript -e '
|
|
1112
|
+
tell application "iTerm"
|
|
1113
|
+
tell current window
|
|
1114
|
+
create tab with default profile
|
|
1115
|
+
tell current session
|
|
1116
|
+
write text "${authCmd}"
|
|
1117
|
+
end tell
|
|
1118
|
+
end tell
|
|
1119
|
+
end tell
|
|
1120
|
+
'`);
|
|
1085
1121
|
}
|
|
1086
1122
|
catch {
|
|
1087
|
-
|
|
1088
|
-
|
|
1123
|
+
// Fallback: try Terminal.app
|
|
1124
|
+
try {
|
|
1125
|
+
execSync(`osascript -e 'tell application "Terminal" to do script "${authCmd}"'`);
|
|
1126
|
+
}
|
|
1127
|
+
catch {
|
|
1128
|
+
this.log(styles.warning('Could not open new terminal tab.'));
|
|
1129
|
+
this.log(styles.muted(`Please run manually: ${authCmd}`));
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
this.log(styles.muted('Complete the /login flow in the new tab, then press Enter here...'));
|
|
1133
|
+
this.log('');
|
|
1134
|
+
// Wait for user to complete auth
|
|
1135
|
+
await this.prompt([{
|
|
1136
|
+
type: 'input',
|
|
1137
|
+
name: 'done',
|
|
1138
|
+
message: 'Press Enter when authentication is complete:',
|
|
1139
|
+
}]);
|
|
1140
|
+
// Check if credentials now exist
|
|
1141
|
+
if (!dockerCredentialsExist()) {
|
|
1142
|
+
this.log('');
|
|
1143
|
+
this.log(styles.warning('Authentication did not complete. No credentials found.'));
|
|
1144
|
+
db.close();
|
|
1145
|
+
return;
|
|
1146
|
+
}
|
|
1147
|
+
const info = getDockerCredentialInfo();
|
|
1148
|
+
this.log('');
|
|
1149
|
+
this.log(styles.success('✓ Credentials configured'));
|
|
1150
|
+
if (info) {
|
|
1151
|
+
this.log(styles.muted(` Subscription: ${info.subscriptionType || 'unknown'}`));
|
|
1152
|
+
this.log(styles.muted(` Expires: ${info.expiresAt.toLocaleDateString()}`));
|
|
1089
1153
|
}
|
|
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
1154
|
this.log('');
|
|
1102
|
-
this.log(styles.warning('Authentication did not complete. No credentials found.'));
|
|
1103
|
-
db.close();
|
|
1104
|
-
return;
|
|
1105
1155
|
}
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1156
|
+
// Prompt "Save as default?" after a successful auth method choice
|
|
1157
|
+
// (only if they chose oauth or apikey, not host/cancel)
|
|
1158
|
+
if (authAction === 'oauth' || authAction === 'apikey') {
|
|
1159
|
+
const saveChoices = [
|
|
1160
|
+
{ name: 'Yes — skip this menu next time', value: true },
|
|
1161
|
+
{ name: 'No — ask me each time', value: false },
|
|
1162
|
+
];
|
|
1163
|
+
const saveMessage = 'Save as default auth method?';
|
|
1164
|
+
const saveResolver = new FlagResolver({
|
|
1165
|
+
commandName: 'work start',
|
|
1166
|
+
baseCommand: `prlt work start ${ticketId}`,
|
|
1167
|
+
jsonMode,
|
|
1168
|
+
flags: {},
|
|
1169
|
+
});
|
|
1170
|
+
saveResolver.addPrompt({
|
|
1171
|
+
flagName: 'saveDefault',
|
|
1172
|
+
type: 'list',
|
|
1173
|
+
message: saveMessage,
|
|
1174
|
+
default: true,
|
|
1175
|
+
choices: () => saveChoices,
|
|
1176
|
+
});
|
|
1177
|
+
const saveResult = await saveResolver.resolve();
|
|
1178
|
+
if (saveResult.saveDefault) {
|
|
1179
|
+
const methodToSave = authAction === 'apikey' ? 'apikey' : 'oauth';
|
|
1180
|
+
saveAuthMethod(db, methodToSave);
|
|
1181
|
+
this.log(styles.muted(`Auth method saved: ${methodToSave}. Will skip this menu next time.`));
|
|
1182
|
+
this.log('');
|
|
1183
|
+
}
|
|
1112
1184
|
}
|
|
1113
|
-
this.log('');
|
|
1114
1185
|
}
|
|
1115
1186
|
}
|
|
1116
1187
|
}
|
|
@@ -1161,9 +1232,13 @@ export default class WorkStart extends PMOCommand {
|
|
|
1161
1232
|
createPR = false;
|
|
1162
1233
|
}
|
|
1163
1234
|
else if (ghAvailable) {
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
createPR =
|
|
1235
|
+
if (context.modifiesCode === false) {
|
|
1236
|
+
// Non-code-modifying actions (groom, review, resolve) default to no PR
|
|
1237
|
+
createPR = false;
|
|
1238
|
+
}
|
|
1239
|
+
else if (jsonMode && flags.yes) {
|
|
1240
|
+
// In JSON mode with --yes, default to creating PR for code-modifying actions
|
|
1241
|
+
createPR = true;
|
|
1167
1242
|
}
|
|
1168
1243
|
else {
|
|
1169
1244
|
// Use FlagResolver for PR choice
|
|
@@ -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
|
+
}
|
|
@@ -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';
|
|
@@ -31,14 +31,14 @@ export default class WorkspacePrune extends PromptCommand {
|
|
|
31
31
|
const { flags } = await this.parse(WorkspacePrune);
|
|
32
32
|
// In non-TTY mode without --json (CI, scripts, piped), default to dry-run unless --force is set.
|
|
33
33
|
// In --json mode, we use confirmation_needed output instead of auto-dry-run so agents can review and confirm.
|
|
34
|
-
const
|
|
35
|
-
const effectiveDryRun = flags['dry-run'] || (!(flags
|
|
34
|
+
const nonTTY = isNonTTY();
|
|
35
|
+
const effectiveDryRun = flags['dry-run'] || (!shouldOutputJson(flags) && 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 {
|