@proletariat/cli 0.3.19 → 0.3.21

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.
Files changed (81) hide show
  1. package/dist/commands/agent/login.js +2 -2
  2. package/dist/commands/agent/remove.d.ts +1 -0
  3. package/dist/commands/agent/remove.js +36 -28
  4. package/dist/commands/agent/shell.js +2 -2
  5. package/dist/commands/agent/staff/remove.d.ts +1 -0
  6. package/dist/commands/agent/staff/remove.js +36 -28
  7. package/dist/commands/agent/status.js +2 -2
  8. package/dist/commands/agent/temp/cleanup.js +10 -17
  9. package/dist/commands/agent/themes/add-names.d.ts +1 -0
  10. package/dist/commands/agent/themes/add-names.js +5 -1
  11. package/dist/commands/agent/visit.js +2 -2
  12. package/dist/commands/board/view.d.ts +15 -0
  13. package/dist/commands/board/view.js +136 -0
  14. package/dist/commands/config/index.js +6 -3
  15. package/dist/commands/epic/link/index.js +17 -0
  16. package/dist/commands/execution/config.d.ts +34 -0
  17. package/dist/commands/execution/config.js +433 -0
  18. package/dist/commands/execution/index.js +6 -1
  19. package/dist/commands/execution/kill.d.ts +12 -0
  20. package/dist/commands/execution/kill.js +17 -0
  21. package/dist/commands/execution/list.js +5 -4
  22. package/dist/commands/execution/logs.js +1 -0
  23. package/dist/commands/execution/view.d.ts +17 -0
  24. package/dist/commands/execution/view.js +288 -0
  25. package/dist/commands/phase/move.js +8 -0
  26. package/dist/commands/phase/template/apply.js +2 -2
  27. package/dist/commands/phase/template/create.js +67 -20
  28. package/dist/commands/phase/template/list.js +1 -1
  29. package/dist/commands/pr/index.js +6 -2
  30. package/dist/commands/pr/list.d.ts +17 -0
  31. package/dist/commands/pr/list.js +163 -0
  32. package/dist/commands/project/update.d.ts +19 -0
  33. package/dist/commands/project/update.js +163 -0
  34. package/dist/commands/roadmap/create.js +5 -0
  35. package/dist/commands/spec/delete.d.ts +18 -0
  36. package/dist/commands/spec/delete.js +111 -0
  37. package/dist/commands/spec/edit.d.ts +23 -0
  38. package/dist/commands/spec/edit.js +232 -0
  39. package/dist/commands/spec/index.js +5 -0
  40. package/dist/commands/status/create.js +38 -34
  41. package/dist/commands/status/list.js +5 -3
  42. package/dist/commands/template/phase/create.d.ts +1 -0
  43. package/dist/commands/template/phase/create.js +10 -1
  44. package/dist/commands/template/phase/index.js +4 -4
  45. package/dist/commands/template/ticket/create.d.ts +20 -0
  46. package/dist/commands/template/ticket/create.js +87 -0
  47. package/dist/commands/template/ticket/delete.d.ts +1 -1
  48. package/dist/commands/template/ticket/delete.js +4 -2
  49. package/dist/commands/template/ticket/save.d.ts +2 -0
  50. package/dist/commands/template/ticket/save.js +11 -0
  51. package/dist/commands/ticket/create.js +8 -1
  52. package/dist/commands/ticket/edit.js +1 -1
  53. package/dist/commands/ticket/list.d.ts +2 -0
  54. package/dist/commands/ticket/list.js +39 -2
  55. package/dist/commands/ticket/template/create.d.ts +9 -1
  56. package/dist/commands/ticket/template/create.js +224 -52
  57. package/dist/commands/ticket/template/save.d.ts +2 -1
  58. package/dist/commands/ticket/template/save.js +58 -7
  59. package/dist/commands/ticket/update.js +2 -2
  60. package/dist/commands/work/ready.js +8 -8
  61. package/dist/commands/work/spawn.js +32 -8
  62. package/dist/commands/work/watch.js +2 -0
  63. package/dist/lib/agents/commands.d.ts +7 -0
  64. package/dist/lib/agents/commands.js +11 -0
  65. package/dist/lib/agents/index.js +14 -4
  66. package/dist/lib/branch/index.js +24 -0
  67. package/dist/lib/execution/config.d.ts +2 -0
  68. package/dist/lib/execution/config.js +12 -0
  69. package/dist/lib/execution/runners.js +1 -2
  70. package/dist/lib/pmo/storage/epics.js +20 -10
  71. package/dist/lib/pmo/storage/helpers.d.ts +10 -0
  72. package/dist/lib/pmo/storage/helpers.js +59 -1
  73. package/dist/lib/pmo/storage/projects.js +20 -8
  74. package/dist/lib/pmo/storage/specs.js +23 -13
  75. package/dist/lib/pmo/storage/statuses.js +39 -18
  76. package/dist/lib/pmo/storage/subtasks.js +19 -8
  77. package/dist/lib/pmo/storage/tickets.js +27 -15
  78. package/dist/lib/pmo/utils.d.ts +4 -2
  79. package/dist/lib/pmo/utils.js +4 -2
  80. package/oclif.manifest.json +4037 -3234
  81. package/package.json +2 -4
@@ -0,0 +1,433 @@
1
+ import { Flags } from '@oclif/core';
2
+ import * as path from 'node:path';
3
+ import Database from 'better-sqlite3';
4
+ import inquirer from 'inquirer';
5
+ import { PMOCommand, pmoBaseFlags } from '../../lib/pmo/index.js';
6
+ import { styles } from '../../lib/styles.js';
7
+ import { getWorkspaceInfo } from '../../lib/agents/commands.js';
8
+ import { loadExecutionConfig, saveTerminalApp, saveTerminalOpenInBackground, saveTmuxControlMode, saveShell, saveExecutionSetting, } from '../../lib/execution/config.js';
9
+ import { shouldOutputJson, isAgentMode, outputPromptAsJson, outputSuccessAsJson, outputErrorAsJson, createMetadata, normalizeChoices, } from '../../lib/prompt-json.js';
10
+ export default class ExecutionConfig extends PMOCommand {
11
+ static description = 'View and update execution preferences';
12
+ static examples = [
13
+ '<%= config.bin %> execution config # Interactive menu',
14
+ '<%= config.bin %> execution config --json # Output current config as JSON',
15
+ '<%= config.bin %> execution config --list # Show all settings',
16
+ '<%= config.bin %> execution config --set defaultEnvironment host',
17
+ '<%= config.bin %> execution config --set outputMode interactive',
18
+ '<%= config.bin %> execution config --set sandboxed true',
19
+ '<%= config.bin %> execution config --setting outputMode --json # Show output mode choices',
20
+ ];
21
+ static flags = {
22
+ ...pmoBaseFlags,
23
+ json: Flags.boolean({
24
+ description: 'Output configuration as JSON (for AI agents/scripts)',
25
+ default: false,
26
+ }),
27
+ set: Flags.string({
28
+ char: 's',
29
+ description: 'Set a config value (format: key value)',
30
+ multiple: true,
31
+ }),
32
+ list: Flags.boolean({
33
+ char: 'l',
34
+ description: 'List all configuration values',
35
+ default: false,
36
+ }),
37
+ setting: Flags.string({
38
+ description: 'Navigate to a specific setting prompt (for agent navigation)',
39
+ }),
40
+ };
41
+ getPMOOptions() {
42
+ return { promptIfMultiple: false };
43
+ }
44
+ /**
45
+ * Prompt wrapper - drop-in replacement for inquirer.prompt
46
+ */
47
+ async promptUser(questions, jsonModeConfig) {
48
+ if (jsonModeConfig && isAgentMode(jsonModeConfig.flags)) {
49
+ const firstQuestion = questions[0];
50
+ if (firstQuestion) {
51
+ const choices = firstQuestion.choices
52
+ ? normalizeChoices(firstQuestion.choices)
53
+ : undefined;
54
+ outputPromptAsJson({
55
+ type: firstQuestion.type,
56
+ name: firstQuestion.name,
57
+ message: firstQuestion.message,
58
+ choices,
59
+ default: firstQuestion.default,
60
+ }, createMetadata(jsonModeConfig.commandName, jsonModeConfig.flags));
61
+ }
62
+ return {};
63
+ }
64
+ return inquirer.prompt(questions);
65
+ }
66
+ async execute() {
67
+ const { flags } = await this.parse(ExecutionConfig);
68
+ const jsonMode = shouldOutputJson(flags);
69
+ const jsonModeConfig = jsonMode ? { flags, commandName: 'execution config' } : null;
70
+ // Get workspace info
71
+ let workspaceInfo;
72
+ try {
73
+ workspaceInfo = getWorkspaceInfo();
74
+ }
75
+ catch {
76
+ if (jsonMode) {
77
+ outputErrorAsJson('NOT_IN_WORKSPACE', 'Not in a workspace. Run "prlt init" first.', createMetadata('execution config', flags));
78
+ this.exit(1);
79
+ }
80
+ this.error('Not in a workspace. Run "prlt init" first.');
81
+ }
82
+ // Open database
83
+ const dbPath = path.join(workspaceInfo.path, '.proletariat', 'workspace.db');
84
+ const db = new Database(dbPath);
85
+ try {
86
+ // Load current config
87
+ const config = loadExecutionConfig(db);
88
+ // Handle --set flag
89
+ if (flags.set && flags.set.length > 0) {
90
+ for (const setValue of flags.set) {
91
+ const [key, ...valueParts] = setValue.split(' ');
92
+ const value = valueParts.join(' ');
93
+ if (!key || !value) {
94
+ if (jsonMode) {
95
+ outputErrorAsJson('INVALID_SET_FORMAT', `Invalid format: "${setValue}". Use: --set "key value"`, createMetadata('execution config', flags));
96
+ }
97
+ else {
98
+ this.error(`Invalid format: "${setValue}". Use: --set "key value"`);
99
+ }
100
+ continue;
101
+ }
102
+ this.setConfigValue(db, key, value, jsonMode);
103
+ }
104
+ db.close();
105
+ return;
106
+ }
107
+ // Handle --list or --json flag without --setting (just show config)
108
+ if ((flags.list || flags.json) && !flags.setting) {
109
+ if (jsonMode) {
110
+ outputSuccessAsJson({
111
+ terminal: {
112
+ app: config.terminal.app,
113
+ openInBackground: config.terminal.openInBackground,
114
+ },
115
+ shell: config.shell,
116
+ tmux: {
117
+ controlMode: config.tmux.controlMode,
118
+ },
119
+ defaultEnvironment: config.defaultEnvironment,
120
+ defaultExecutor: config.defaultExecutor,
121
+ outputMode: config.outputMode,
122
+ sandboxed: config.sandboxed,
123
+ }, createMetadata('execution config', flags));
124
+ }
125
+ else {
126
+ this.log('');
127
+ this.log(styles.header('Execution Configuration'));
128
+ this.log('═'.repeat(50));
129
+ this.log('');
130
+ this.log(styles.emphasis('Environment'));
131
+ this.log(` defaultEnvironment: ${config.defaultEnvironment}`);
132
+ this.log(` defaultExecutor: ${config.defaultExecutor}`);
133
+ this.log('');
134
+ this.log(styles.emphasis('Output'));
135
+ this.log(` outputMode: ${config.outputMode}`);
136
+ this.log(` sandboxed: ${config.sandboxed}`);
137
+ this.log('');
138
+ this.log(styles.emphasis('Terminal'));
139
+ this.log(` app: ${config.terminal.app}`);
140
+ this.log(` openInBackground: ${config.terminal.openInBackground}`);
141
+ this.log('');
142
+ this.log(styles.emphasis('Shell'));
143
+ this.log(` shell: ${config.shell}`);
144
+ this.log('');
145
+ this.log(styles.emphasis('Tmux'));
146
+ this.log(` controlMode: ${config.tmux.controlMode}`);
147
+ this.log('');
148
+ }
149
+ db.close();
150
+ return;
151
+ }
152
+ // Handle --setting flag (navigate directly to a sub-prompt)
153
+ if (flags.setting) {
154
+ await this.handleSettingPrompt(db, config, flags.setting, jsonModeConfig);
155
+ db.close();
156
+ return;
157
+ }
158
+ // Interactive menu
159
+ const settingChoices = [
160
+ { name: `Default Environment: ${config.defaultEnvironment}`, value: 'defaultEnvironment', command: 'prlt execution config --setting defaultEnvironment --json' },
161
+ { name: `Output Mode: ${config.outputMode}`, value: 'outputMode', command: 'prlt execution config --setting outputMode --json' },
162
+ { name: `Permission Mode: ${config.sandboxed ? 'safe' : 'danger'}`, value: 'sandboxed', command: 'prlt execution config --setting sandboxed --json' },
163
+ { name: `Terminal App: ${config.terminal.app}`, value: 'terminal.app', command: 'prlt execution config --setting terminal.app --json' },
164
+ { name: `Open Tabs in Background: ${config.terminal.openInBackground}`, value: 'terminal.openInBackground', command: 'prlt execution config --setting terminal.openInBackground --json' },
165
+ { name: `Shell: ${config.shell}`, value: 'shell', command: 'prlt execution config --setting shell --json' },
166
+ { name: `Tmux Control Mode: ${config.tmux.controlMode}`, value: 'tmux.controlMode', command: 'prlt execution config --setting tmux.controlMode --json' },
167
+ ];
168
+ const { setting } = await this.promptUser([
169
+ {
170
+ type: 'list',
171
+ name: 'setting',
172
+ message: 'Select setting to configure:',
173
+ choices: [
174
+ new inquirer.Separator('── Execution ──'),
175
+ ...settingChoices.slice(0, 3),
176
+ new inquirer.Separator('── Terminal ──'),
177
+ ...settingChoices.slice(3, 5),
178
+ new inquirer.Separator('── Shell ──'),
179
+ settingChoices[5],
180
+ new inquirer.Separator('── Tmux ──'),
181
+ settingChoices[6],
182
+ new inquirer.Separator(),
183
+ { name: 'Exit', value: '__exit__' },
184
+ ],
185
+ },
186
+ ], jsonModeConfig);
187
+ if (setting === '__exit__') {
188
+ db.close();
189
+ return;
190
+ }
191
+ // Handle the selected setting
192
+ await this.handleSettingPrompt(db, config, setting, jsonModeConfig);
193
+ db.close();
194
+ }
195
+ catch (error) {
196
+ db.close();
197
+ throw error;
198
+ }
199
+ }
200
+ /**
201
+ * Handle a specific setting's sub-prompt
202
+ */
203
+ async handleSettingPrompt(db, config, setting, jsonModeConfig) {
204
+ switch (setting) {
205
+ case 'defaultEnvironment': {
206
+ const envChoices = [
207
+ { name: 'host - Run directly on host machine', value: 'host', command: 'prlt execution config --set "defaultEnvironment host" --json' },
208
+ { name: 'devcontainer - Run in a devcontainer (sandboxed)', value: 'devcontainer', command: 'prlt execution config --set "defaultEnvironment devcontainer" --json' },
209
+ { name: 'docker - Run in a Docker container', value: 'docker', command: 'prlt execution config --set "defaultEnvironment docker" --json' },
210
+ { name: 'vm - Run on a remote VM', value: 'vm', command: 'prlt execution config --set "defaultEnvironment vm" --json' },
211
+ ];
212
+ const { newEnv } = await this.promptUser([
213
+ {
214
+ type: 'list',
215
+ name: 'newEnv',
216
+ message: 'Select default execution environment:',
217
+ choices: envChoices,
218
+ default: config.defaultEnvironment,
219
+ },
220
+ ], jsonModeConfig);
221
+ saveExecutionSetting(db, 'defaultMode', newEnv);
222
+ this.log(styles.success(`Default environment set to: ${newEnv}`));
223
+ break;
224
+ }
225
+ case 'outputMode': {
226
+ const outputChoices = [
227
+ { name: 'interactive - Watch Claude work in real-time (streaming UI)', value: 'interactive', command: 'prlt execution config --set "outputMode interactive" --json' },
228
+ { name: 'print - Show final result only (better for logs)', value: 'print', command: 'prlt execution config --set "outputMode print" --json' },
229
+ ];
230
+ const { newOutput } = await this.promptUser([
231
+ {
232
+ type: 'list',
233
+ name: 'newOutput',
234
+ message: 'Select output mode:',
235
+ choices: outputChoices,
236
+ default: config.outputMode,
237
+ },
238
+ ], jsonModeConfig);
239
+ this.setConfigValue(db, 'outputMode', newOutput, false);
240
+ this.log(styles.success(`Output mode set to: ${newOutput}`));
241
+ break;
242
+ }
243
+ case 'sandboxed': {
244
+ const permChoices = [
245
+ { name: 'safe - Requires approval for dangerous operations (recommended)', value: 'true', command: 'prlt execution config --set "sandboxed true" --json' },
246
+ { name: 'danger - Skip permission checks (--dangerously-skip-permissions)', value: 'false', command: 'prlt execution config --set "sandboxed false" --json' },
247
+ ];
248
+ const { newPerm } = await this.promptUser([
249
+ {
250
+ type: 'list',
251
+ name: 'newPerm',
252
+ message: 'Select permission mode:',
253
+ choices: permChoices,
254
+ default: String(config.sandboxed),
255
+ },
256
+ ], jsonModeConfig);
257
+ this.setConfigValue(db, 'sandboxed', newPerm, false);
258
+ this.log(styles.success(`Permission mode set to: ${newPerm === 'true' ? 'safe' : 'danger'}`));
259
+ break;
260
+ }
261
+ case 'terminal.app': {
262
+ const appChoices = [
263
+ { name: 'iTerm', value: 'iTerm', command: 'prlt execution config --set "terminal.app iTerm" --json' },
264
+ { name: 'Terminal.app (macOS default)', value: 'Terminal', command: 'prlt execution config --set "terminal.app Terminal" --json' },
265
+ { name: 'Ghostty', value: 'Ghostty', command: 'prlt execution config --set "terminal.app Ghostty" --json' },
266
+ { name: 'Alacritty', value: 'Alacritty', command: 'prlt execution config --set "terminal.app Alacritty" --json' },
267
+ { name: 'Kitty', value: 'Kitty', command: 'prlt execution config --set "terminal.app Kitty" --json' },
268
+ { name: 'WezTerm', value: 'WezTerm', command: 'prlt execution config --set "terminal.app WezTerm" --json' },
269
+ { name: 'Warp', value: 'Warp', command: 'prlt execution config --set "terminal.app Warp" --json' },
270
+ { name: 'tmux', value: 'tmux', command: 'prlt execution config --set "terminal.app tmux" --json' },
271
+ ];
272
+ const { newApp } = await this.promptUser([
273
+ {
274
+ type: 'list',
275
+ name: 'newApp',
276
+ message: 'Select terminal app:',
277
+ choices: appChoices,
278
+ default: config.terminal.app,
279
+ },
280
+ ], jsonModeConfig);
281
+ saveTerminalApp(db, newApp);
282
+ this.log(styles.success(`Terminal app set to: ${newApp}`));
283
+ break;
284
+ }
285
+ case 'terminal.openInBackground': {
286
+ const bgChoices = [
287
+ { name: 'Yes - Open tabs in background (don\'t steal focus)', value: 'true', command: 'prlt execution config --set "terminal.openInBackground true" --json' },
288
+ { name: 'No - Bring terminal to foreground when opening tabs', value: 'false', command: 'prlt execution config --set "terminal.openInBackground false" --json' },
289
+ ];
290
+ const { openInBg } = await this.promptUser([
291
+ {
292
+ type: 'list',
293
+ name: 'openInBg',
294
+ message: 'Open terminal tabs in background?',
295
+ choices: bgChoices,
296
+ default: String(config.terminal.openInBackground),
297
+ },
298
+ ], jsonModeConfig);
299
+ saveTerminalOpenInBackground(db, openInBg === 'true');
300
+ this.log(styles.success(`Open in background set to: ${openInBg}`));
301
+ break;
302
+ }
303
+ case 'shell': {
304
+ const shellChoices = [
305
+ { name: 'zsh (macOS default)', value: 'zsh', command: 'prlt execution config --set "shell zsh" --json' },
306
+ { name: 'bash', value: 'bash', command: 'prlt execution config --set "shell bash" --json' },
307
+ { name: 'fish', value: 'fish', command: 'prlt execution config --set "shell fish" --json' },
308
+ ];
309
+ const { newShell } = await this.promptUser([
310
+ {
311
+ type: 'list',
312
+ name: 'newShell',
313
+ message: 'Select shell:',
314
+ choices: shellChoices,
315
+ default: config.shell,
316
+ },
317
+ ], jsonModeConfig);
318
+ saveShell(db, newShell);
319
+ this.log(styles.success(`Shell set to: ${newShell}`));
320
+ break;
321
+ }
322
+ case 'tmux.controlMode': {
323
+ const ccChoices = [
324
+ { name: 'Yes - Use tmux -CC for native iTerm integration', value: 'true', command: 'prlt execution config --set "tmux.controlMode true" --json' },
325
+ { name: 'No - Standard tmux interface', value: 'false', command: 'prlt execution config --set "tmux.controlMode false" --json' },
326
+ ];
327
+ const { controlMode } = await this.promptUser([
328
+ {
329
+ type: 'list',
330
+ name: 'controlMode',
331
+ message: 'Enable tmux control mode (-CC)?',
332
+ choices: ccChoices,
333
+ default: String(config.tmux.controlMode),
334
+ },
335
+ ], jsonModeConfig);
336
+ saveTmuxControlMode(db, controlMode === 'true');
337
+ this.log(styles.success(`Tmux control mode set to: ${controlMode}`));
338
+ break;
339
+ }
340
+ default: {
341
+ const jsonMode = shouldOutputJson(jsonModeConfig?.flags ?? {});
342
+ if (jsonMode) {
343
+ outputErrorAsJson('UNKNOWN_SETTING', `Unknown setting: ${setting}`, createMetadata('execution config', jsonModeConfig?.flags ?? {}));
344
+ }
345
+ this.error(`Unknown setting: ${setting}`);
346
+ }
347
+ }
348
+ }
349
+ setConfigValue(db, key, value, jsonMode) {
350
+ const normalizedKey = key.toLowerCase();
351
+ // Define valid values for each config key
352
+ const VALID_VALUES = {
353
+ defaultenvironment: ['host', 'devcontainer', 'docker', 'vm'],
354
+ outputmode: ['interactive', 'print'],
355
+ sandboxed: ['true', 'false'],
356
+ 'terminal.app': ['Terminal', 'iTerm', 'Alacritty', 'Ghostty', 'Kitty', 'tmux', 'Warp', 'WezTerm'],
357
+ 'terminal.openinbackground': ['true', 'false'],
358
+ shell: ['bash', 'zsh', 'fish'],
359
+ 'tmux.controlmode': ['true', 'false'],
360
+ };
361
+ // Validate value against allowed options
362
+ const validValues = VALID_VALUES[normalizedKey];
363
+ if (validValues && !validValues.includes(value)) {
364
+ const errorMsg = `Invalid value "${value}" for ${key}. Valid options: ${validValues.join(', ')}`;
365
+ if (jsonMode) {
366
+ outputErrorAsJson('INVALID_VALUE', errorMsg, createMetadata('execution config', {}));
367
+ }
368
+ else {
369
+ this.error(errorMsg);
370
+ }
371
+ return;
372
+ }
373
+ switch (normalizedKey) {
374
+ case 'defaultenvironment':
375
+ saveExecutionSetting(db, 'defaultMode', value);
376
+ break;
377
+ case 'outputmode':
378
+ // Store output mode - need to add this to the config storage
379
+ this.setOutputMode(db, value);
380
+ break;
381
+ case 'sandboxed':
382
+ // Store sandboxed preference
383
+ this.setSandboxed(db, value.toLowerCase() === 'true');
384
+ break;
385
+ case 'terminal.app':
386
+ saveTerminalApp(db, value);
387
+ break;
388
+ case 'terminal.openinbackground':
389
+ saveTerminalOpenInBackground(db, value.toLowerCase() === 'true');
390
+ break;
391
+ case 'shell':
392
+ saveShell(db, value);
393
+ break;
394
+ case 'tmux.controlmode':
395
+ saveTmuxControlMode(db, value.toLowerCase() === 'true');
396
+ break;
397
+ default:
398
+ if (jsonMode) {
399
+ outputErrorAsJson('UNKNOWN_KEY', `Unknown config key: ${key}`, createMetadata('execution config', {}));
400
+ }
401
+ else {
402
+ this.warn(`Unknown config key: ${key}`);
403
+ }
404
+ return;
405
+ }
406
+ if (jsonMode) {
407
+ outputSuccessAsJson({ key, value }, createMetadata('execution config', {}));
408
+ }
409
+ else {
410
+ this.log(styles.success(`Set ${key} = ${value}`));
411
+ }
412
+ }
413
+ /**
414
+ * Save output mode to workspace settings
415
+ */
416
+ setOutputMode(db, mode) {
417
+ db.prepare(`
418
+ INSERT INTO workspace_settings (key, value)
419
+ VALUES (?, ?)
420
+ ON CONFLICT(key) DO UPDATE SET value = excluded.value
421
+ `).run('execution.output_mode', mode);
422
+ }
423
+ /**
424
+ * Save sandboxed preference to workspace settings
425
+ */
426
+ setSandboxed(db, sandboxed) {
427
+ db.prepare(`
428
+ INSERT INTO workspace_settings (key, value)
429
+ VALUES (?, ?)
430
+ ON CONFLICT(key) DO UPDATE SET value = excluded.value
431
+ `).run('execution.sandboxed', sandboxed.toString());
432
+ }
433
+ }
@@ -2,9 +2,10 @@ import { Flags } from '@oclif/core';
2
2
  import inquirer from 'inquirer';
3
3
  import { PMOCommand, pmoBaseFlags } from '../../lib/pmo/index.js';
4
4
  export default class Execution extends PMOCommand {
5
- static description = 'Single execution operations (logs, stop)';
5
+ static description = 'Single execution operations (view, logs, stop)';
6
6
  static examples = [
7
7
  '<%= config.bin %> <%= command.id %>',
8
+ '<%= config.bin %> <%= command.id %> view WORK-001',
8
9
  '<%= config.bin %> <%= command.id %> logs WORK-001',
9
10
  '<%= config.bin %> <%= command.id %> stop WORK-001',
10
11
  ];
@@ -28,9 +29,11 @@ export default class Execution extends PMOCommand {
28
29
  message: 'What would you like to do?',
29
30
  choices: [
30
31
  { name: '📋 List all executions', value: 'list', command: 'prlt execution list --json' },
32
+ { name: '🔍 View execution details', value: 'view', command: 'prlt execution view --json' },
31
33
  { name: '📜 View logs for an execution', value: 'logs', command: 'prlt execution logs --json' },
32
34
  { name: '🛑 Stop an execution', value: 'stop', command: 'prlt execution stop --json' },
33
35
  { name: '🛑 Stop all running', value: 'stop-all', command: 'prlt execution stop --all --json' },
36
+ { name: '⚙️ Configure execution preferences', value: 'config', command: 'prlt execution config --json' },
34
37
  new inquirer.Separator(),
35
38
  { name: '❌ Cancel', value: 'cancel', command: '' },
36
39
  ],
@@ -42,9 +45,11 @@ export default class Execution extends PMOCommand {
42
45
  // Run the selected subcommand
43
46
  const commands = {
44
47
  list: { cmd: 'execution:list', args: [] },
48
+ view: { cmd: 'execution:view', args: [] },
45
49
  logs: { cmd: 'execution:logs', args: [] },
46
50
  stop: { cmd: 'execution:stop', args: [] },
47
51
  'stop-all': { cmd: 'execution:stop', args: ['--all'] },
52
+ config: { cmd: 'execution:config', args: [] },
48
53
  };
49
54
  const command = commands[action];
50
55
  if (command) {
@@ -0,0 +1,12 @@
1
+ import ExecutionStop from './stop.js';
2
+ /**
3
+ * Alias for 'execution stop' command.
4
+ * Users intuitively try 'execution kill' so this provides a familiar interface.
5
+ */
6
+ export default class ExecutionKill extends ExecutionStop {
7
+ static description: string;
8
+ static args: {
9
+ id: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
10
+ };
11
+ static examples: string[];
12
+ }
@@ -0,0 +1,17 @@
1
+ import ExecutionStop from './stop.js';
2
+ /**
3
+ * Alias for 'execution stop' command.
4
+ * Users intuitively try 'execution kill' so this provides a familiar interface.
5
+ */
6
+ export default class ExecutionKill extends ExecutionStop {
7
+ static description = 'Stop running execution(s) (alias for "execution stop")';
8
+ static args = ExecutionStop.args;
9
+ static examples = [
10
+ '<%= config.bin %> <%= command.id %> WORK-001',
11
+ '<%= config.bin %> <%= command.id %> WORK-001 --force',
12
+ '<%= config.bin %> <%= command.id %> # Interactive mode',
13
+ '<%= config.bin %> <%= command.id %> --all',
14
+ '<%= config.bin %> <%= command.id %> --all --force',
15
+ '<%= config.bin %> <%= command.id %> --agent altman',
16
+ ];
17
+ }
@@ -28,6 +28,7 @@ export default class ExecutionList extends PMOCommand {
28
28
  char: 'l',
29
29
  description: 'Number of results',
30
30
  default: 20,
31
+ min: 1,
31
32
  }),
32
33
  };
33
34
  getPMOOptions() {
@@ -61,10 +62,10 @@ export default class ExecutionList extends PMOCommand {
61
62
  this.log('');
62
63
  this.log(styles.header('🚀 Agent Work'));
63
64
  this.log('═'.repeat(100));
64
- this.log(styles.muted(padEnd('ID', 11) +
65
+ this.log(styles.muted(padEnd('ID', 14) +
65
66
  padEnd('Ticket', 9) +
66
67
  padEnd('Agent', 10) +
67
- padEnd('Env', 12) +
68
+ padEnd('Env', 13) +
68
69
  padEnd('Display', 11) +
69
70
  padEnd('Perms', 8) +
70
71
  padEnd('Status', 10) +
@@ -78,10 +79,10 @@ export default class ExecutionList extends PMOCommand {
78
79
  const envStr = `${envIcon} ${exec.environment}`;
79
80
  const permsStr = exec.sandboxed ? 'safe' : 'danger';
80
81
  const permsColor = exec.sandboxed ? styles.success : styles.warning;
81
- this.log(padEnd(exec.id, 11) +
82
+ this.log(padEnd(exec.id, 14) +
82
83
  padEnd(exec.ticketId, 9) +
83
84
  padEnd(exec.agentName, 10) +
84
- padEnd(envStr, 12) +
85
+ padEnd(envStr, 13) +
85
86
  padEnd(exec.displayMode, 11) +
86
87
  permsColor(padEnd(permsStr, 8)) +
87
88
  statusColor(padEnd(exec.status, 10)) +
@@ -32,6 +32,7 @@ export default class ExecutionLogs extends PMOCommand {
32
32
  tail: Flags.integer({
33
33
  char: 'n',
34
34
  description: 'Show last n lines',
35
+ min: 1,
35
36
  }),
36
37
  json: Flags.boolean({
37
38
  description: 'Output prompt configuration as JSON (for AI agents/scripts)',
@@ -0,0 +1,17 @@
1
+ import { PMOCommand } from '../../lib/pmo/index.js';
2
+ export default class ExecutionView extends PMOCommand {
3
+ static description: string;
4
+ static examples: string[];
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
+ protected getPMOOptions(): {
14
+ promptIfMultiple: boolean;
15
+ };
16
+ execute(): Promise<void>;
17
+ }