@proletariat/cli 0.3.25 → 0.3.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/dist/commands/action/index.js +2 -2
  2. package/dist/commands/agent/auth.js +1 -1
  3. package/dist/commands/agent/cleanup.js +6 -6
  4. package/dist/commands/agent/discover.js +1 -1
  5. package/dist/commands/agent/remove.js +4 -4
  6. package/dist/commands/autocomplete/setup.d.ts +2 -2
  7. package/dist/commands/autocomplete/setup.js +5 -5
  8. package/dist/commands/branch/create.js +31 -30
  9. package/dist/commands/category/create.js +4 -5
  10. package/dist/commands/category/delete.js +2 -3
  11. package/dist/commands/category/rename.js +2 -3
  12. package/dist/commands/claude.d.ts +2 -8
  13. package/dist/commands/claude.js +26 -26
  14. package/dist/commands/commit.d.ts +2 -8
  15. package/dist/commands/commit.js +4 -26
  16. package/dist/commands/config/index.d.ts +2 -10
  17. package/dist/commands/config/index.js +8 -34
  18. package/dist/commands/docker/index.d.ts +2 -2
  19. package/dist/commands/docker/index.js +8 -8
  20. package/dist/commands/epic/delete.js +4 -5
  21. package/dist/commands/feedback/submit.d.ts +2 -2
  22. package/dist/commands/feedback/submit.js +9 -9
  23. package/dist/commands/link/index.js +2 -2
  24. package/dist/commands/pmo/init.d.ts +2 -2
  25. package/dist/commands/pmo/init.js +7 -7
  26. package/dist/commands/project/spec.js +6 -6
  27. package/dist/commands/session/health.d.ts +29 -0
  28. package/dist/commands/session/health.js +495 -0
  29. package/dist/commands/session/index.js +4 -0
  30. package/dist/commands/spec/edit.js +2 -3
  31. package/dist/commands/staff/add.d.ts +2 -2
  32. package/dist/commands/staff/add.js +15 -14
  33. package/dist/commands/staff/index.js +2 -2
  34. package/dist/commands/staff/remove.js +4 -4
  35. package/dist/commands/status/index.js +6 -7
  36. package/dist/commands/template/apply.js +10 -11
  37. package/dist/commands/template/create.js +18 -17
  38. package/dist/commands/template/index.d.ts +2 -2
  39. package/dist/commands/template/index.js +6 -6
  40. package/dist/commands/template/save.js +8 -7
  41. package/dist/commands/template/update.js +6 -7
  42. package/dist/commands/terminal/title.d.ts +2 -26
  43. package/dist/commands/terminal/title.js +4 -33
  44. package/dist/commands/theme/index.d.ts +2 -2
  45. package/dist/commands/theme/index.js +19 -18
  46. package/dist/commands/theme/set.d.ts +2 -2
  47. package/dist/commands/theme/set.js +5 -5
  48. package/dist/commands/ticket/create.js +34 -16
  49. package/dist/commands/ticket/delete.js +15 -13
  50. package/dist/commands/ticket/edit.js +20 -12
  51. package/dist/commands/ticket/epic.js +12 -10
  52. package/dist/commands/ticket/project.js +11 -9
  53. package/dist/commands/ticket/reassign.js +23 -19
  54. package/dist/commands/ticket/spec.js +7 -5
  55. package/dist/commands/ticket/update.js +55 -53
  56. package/dist/commands/whoami.js +1 -0
  57. package/dist/commands/work/ready.js +7 -7
  58. package/dist/commands/work/revise.js +13 -11
  59. package/dist/commands/work/spawn.js +154 -57
  60. package/dist/commands/work/start.d.ts +1 -0
  61. package/dist/commands/work/start.js +295 -173
  62. package/dist/hooks/init.js +4 -0
  63. package/dist/lib/pr/index.d.ts +4 -0
  64. package/dist/lib/pr/index.js +32 -14
  65. package/dist/lib/prompt-command.d.ts +3 -0
  66. package/dist/lib/prompt-json.d.ts +72 -1
  67. package/dist/lib/prompt-json.js +46 -0
  68. package/oclif.manifest.json +1184 -1116
  69. package/package.json +1 -1
@@ -1,11 +1,11 @@
1
- import { Command, Args, Flags } from '@oclif/core';
1
+ import { Args, Flags } from '@oclif/core';
2
2
  import chalk from 'chalk';
3
- import inquirer from 'inquirer';
3
+ import { PromptCommand } from '../../lib/prompt-command.js';
4
4
  import { getWorkspaceInfo } from '../../lib/agents/commands.js';
5
5
  import { ensureBuiltinThemes } from '../../lib/themes.js';
6
6
  import { getThemes, getAvailableThemeNames, setActiveTheme, getActiveTheme } from '../../lib/database/index.js';
7
7
  import { shouldOutputJson, outputPromptAsJson, createMetadata, buildPromptConfig, } from '../../lib/prompt-json.js';
8
- export default class ThemeSet extends Command {
8
+ export default class ThemeSet extends PromptCommand {
9
9
  static description = 'Set the active theme for this workspace';
10
10
  static examples = [
11
11
  '<%= config.bin %> <%= command.id %> billionaires',
@@ -58,12 +58,12 @@ export default class ThemeSet extends Command {
58
58
  value: t.id
59
59
  };
60
60
  });
61
- const { selected } = await inquirer.prompt([{
61
+ const { selected } = await this.prompt([{
62
62
  type: 'list',
63
63
  name: 'selected',
64
64
  message: 'Select theme for this workspace:',
65
65
  choices
66
- }]);
66
+ }], null);
67
67
  themeId = selected;
68
68
  }
69
69
  setActiveTheme(workspaceInfo.path, themeId);
@@ -1,6 +1,7 @@
1
1
  import { Flags } from '@oclif/core';
2
2
  import inquirer from 'inquirer';
3
3
  import { autoExportToBoard, PMOCommand, pmoBaseFlags } from '../../lib/pmo/index.js';
4
+ // Note: inquirer import kept for inquirer.Separator usage in interactive mode
4
5
  import { styles } from '../../lib/styles.js';
5
6
  import { updateEpicTicketsSection } from '../../lib/pmo/epic-files.js';
6
7
  import { PRIORITIES, PRIORITY_LABELS } from '../../lib/pmo/types.js';
@@ -290,7 +291,7 @@ export default class TicketCreate extends PMOCommand {
290
291
  if (!template && !flags.template) {
291
292
  const templates = await storage.listTicketTemplates();
292
293
  if (templates.length > 0) {
293
- const { selectedTemplate } = await inquirer.prompt([
294
+ const { selectedTemplate } = await this.prompt([
294
295
  {
295
296
  type: 'list',
296
297
  name: 'selectedTemplate',
@@ -304,13 +305,14 @@ export default class TicketCreate extends PMOCommand {
304
305
  })),
305
306
  ],
306
307
  },
307
- ]);
308
+ ], null);
308
309
  if (selectedTemplate) {
309
310
  template = templates.find(t => t.id === selectedTemplate) || null;
310
311
  }
311
312
  }
312
313
  }
313
- const answers = await inquirer.prompt([
314
+ // Prompt for title
315
+ const { title: answerTitle } = await this.prompt([
314
316
  {
315
317
  type: 'input',
316
318
  name: 'title',
@@ -318,13 +320,19 @@ export default class TicketCreate extends PMOCommand {
318
320
  default: flags.title || template?.titlePattern,
319
321
  validate: (input) => input.trim() ? true : 'Title cannot be empty',
320
322
  },
323
+ ], null);
324
+ // Prompt for column
325
+ const { column: answerColumn } = await this.prompt([
321
326
  {
322
327
  type: 'list',
323
328
  name: 'column',
324
329
  message: 'Column:',
325
- choices: columns,
330
+ choices: columns.map(c => ({ name: c, value: c })),
326
331
  default: flags.column || columns[0],
327
332
  },
333
+ ], null);
334
+ // Prompt for priority
335
+ const { priority: answerPriority } = await this.prompt([
328
336
  {
329
337
  type: 'list',
330
338
  name: 'priority',
@@ -335,6 +343,9 @@ export default class TicketCreate extends PMOCommand {
335
343
  ],
336
344
  default: flags.priority || template?.defaultPriority,
337
345
  },
346
+ ], null);
347
+ // Prompt for category
348
+ const { categoryChoice } = await this.prompt([
338
349
  {
339
350
  type: 'list',
340
351
  name: 'categoryChoice',
@@ -366,14 +377,19 @@ export default class TicketCreate extends PMOCommand {
366
377
  ],
367
378
  default: flags.category || template?.defaultCategory || '',
368
379
  },
369
- {
370
- type: 'input',
371
- name: 'customCategory',
372
- message: 'Enter custom category:',
373
- when: (answers) => answers.categoryChoice === '__custom__',
374
- validate: (input) => input.trim() ? true : 'Category cannot be empty',
375
- },
376
- ]);
380
+ ], null);
381
+ // Custom category prompt if needed
382
+ let customCategory;
383
+ if (categoryChoice === '__custom__') {
384
+ const result = await this.prompt([{
385
+ type: 'input',
386
+ name: 'customCategory',
387
+ message: 'Enter custom category:',
388
+ validate: (input) => input.trim() ? true : 'Category cannot be empty',
389
+ }], null);
390
+ customCategory = result.customCategory;
391
+ }
392
+ const answers = { title: answerTitle, column: answerColumn, priority: answerPriority, categoryChoice, customCategory };
377
393
  // Resolve category from choice or custom input
378
394
  const category = answers.categoryChoice === '__custom__'
379
395
  ? answers.customCategory
@@ -402,14 +418,14 @@ export default class TicketCreate extends PMOCommand {
402
418
  }
403
419
  this.log(styles.muted('\n─── Ticket Description (for agent execution) ───'));
404
420
  // Prompt for "What" - the main outcome
405
- const { what } = await inquirer.prompt([
421
+ const { what } = await this.prompt([
406
422
  {
407
423
  type: 'input',
408
424
  name: 'what',
409
425
  message: 'What is the concrete outcome? (one sentence):',
410
426
  validate: (input) => input.trim() ? true : 'Outcome cannot be empty - what does success look like?',
411
427
  },
412
- ]);
428
+ ], null);
413
429
  // Prompt for acceptance criteria using multiline input
414
430
  const doneWhenResult = await multiLineInput({
415
431
  message: 'Done when (acceptance criteria):',
@@ -419,20 +435,22 @@ export default class TicketCreate extends PMOCommand {
419
435
  throw new Error('Ticket creation cancelled');
420
436
  }
421
437
  // Continue with remaining prompts
422
- const { context, notInScope } = await inquirer.prompt([
438
+ const { context } = await this.prompt([
423
439
  {
424
440
  type: 'input',
425
441
  name: 'context',
426
442
  message: 'Context (files, patterns, hints - optional):',
427
443
  default: '',
428
444
  },
445
+ ], null);
446
+ const { notInScope } = await this.prompt([
429
447
  {
430
448
  type: 'input',
431
449
  name: 'notInScope',
432
450
  message: 'Not in scope (explicit exclusions - optional):',
433
451
  default: '',
434
452
  },
435
- ]);
453
+ ], null);
436
454
  // Build structured description
437
455
  const parts = [];
438
456
  parts.push(`## What\n${what}`);
@@ -1,5 +1,4 @@
1
1
  import { Args, Flags } from '@oclif/core';
2
- import inquirer from 'inquirer';
3
2
  import { autoExportToBoard, PMOCommand, pmoBaseFlags, } from '../../lib/pmo/index.js';
4
3
  import { styles } from '../../lib/styles.js';
5
4
  import { shouldOutputJson, outputErrorAsJson, createMetadata, } from '../../lib/prompt-json.js';
@@ -57,7 +56,7 @@ export default class TicketDelete extends PMOCommand {
57
56
  }
58
57
  // Bulk mode
59
58
  if (flags.bulk) {
60
- await this.executeBulk(allTickets, flags.force);
59
+ await this.executeBulk(allTickets, flags.force, flags);
61
60
  return;
62
61
  }
63
62
  // Single ticket mode
@@ -90,16 +89,17 @@ export default class TicketDelete extends PMOCommand {
90
89
  this.log(` Title: ${ticket.title}`);
91
90
  this.log(` Project: ${board.name}`);
92
91
  this.log(` Status: ${ticket.statusName}`);
93
- const { confirmed } = await inquirer.prompt([{
92
+ const jsonModeConfig = jsonMode ? { flags, commandName: 'ticket delete' } : null;
93
+ const { confirmed } = await this.prompt([{
94
94
  type: 'list',
95
95
  name: 'confirmed',
96
96
  message: 'Are you sure?',
97
97
  choices: [
98
- { name: 'No, cancel', value: false },
99
- { name: 'Yes, delete', value: true },
98
+ { name: 'No, cancel', value: false, command: '' },
99
+ { name: 'Yes, delete', value: true, command: `prlt ticket delete ${ticketId} --force --json` },
100
100
  ],
101
101
  default: 0,
102
- }]);
102
+ }], jsonModeConfig);
103
103
  if (!confirmed) {
104
104
  this.log(styles.warning('Deletion cancelled.'));
105
105
  return;
@@ -112,10 +112,12 @@ export default class TicketDelete extends PMOCommand {
112
112
  this.log(styles.success(`\n✅ Ticket ${styles.emphasis(ticketId)} deleted`));
113
113
  this.log(styles.muted(' Removed from database and board'));
114
114
  }
115
- async executeBulk(allTickets, force) {
115
+ async executeBulk(allTickets, force, flags) {
116
+ const jsonMode = flags ? shouldOutputJson(flags) : false;
117
+ const jsonModeConfig = jsonMode ? { flags: flags, commandName: 'ticket delete' } : null;
116
118
  this.log(styles.emphasis('🗑️ Delete Multiple Tickets\n'));
117
119
  // Select tickets to delete
118
- const { selectedTickets } = await inquirer.prompt([{
120
+ const { selectedTickets } = await this.prompt([{
119
121
  type: 'checkbox',
120
122
  name: 'selectedTickets',
121
123
  message: 'Select tickets to DELETE:',
@@ -123,7 +125,7 @@ export default class TicketDelete extends PMOCommand {
123
125
  name: `${t.id} - ${t.title} (${t.statusName})`,
124
126
  value: t.id,
125
127
  })),
126
- }]);
128
+ }], jsonModeConfig);
127
129
  if (selectedTickets.length === 0) {
128
130
  this.log(styles.muted('No tickets selected.'));
129
131
  return;
@@ -136,16 +138,16 @@ export default class TicketDelete extends PMOCommand {
136
138
  this.log(styles.primary(` • ${ticketId}: ${ticket?.title}`));
137
139
  }
138
140
  this.log('');
139
- const { confirm } = await inquirer.prompt([{
141
+ const { confirm } = await this.prompt([{
140
142
  type: 'list',
141
143
  name: 'confirm',
142
144
  message: 'Are you sure? This cannot be undone.',
143
145
  choices: [
144
- { name: 'No, cancel', value: false },
145
- { name: 'Yes, DELETE tickets', value: true }
146
+ { name: 'No, cancel', value: false, command: '' },
147
+ { name: 'Yes, DELETE tickets', value: true, command: 'prlt ticket delete --bulk --force --json' }
146
148
  ],
147
149
  default: 0
148
- }]);
150
+ }], jsonModeConfig);
149
151
  if (!confirm) {
150
152
  this.log(styles.muted('Deletion cancelled.'));
151
153
  return;
@@ -269,7 +269,7 @@ export default class TicketEdit extends PMOCommand {
269
269
  }
270
270
  async promptForEdits(ticket, _columns) {
271
271
  // First prompt for title
272
- const { title } = await inquirer.prompt([
272
+ const { title } = await this.prompt([
273
273
  {
274
274
  type: 'input',
275
275
  name: 'title',
@@ -277,7 +277,7 @@ export default class TicketEdit extends PMOCommand {
277
277
  default: ticket.title,
278
278
  validate: (input) => input.trim() ? true : 'Title cannot be empty',
279
279
  },
280
- ]);
280
+ ], null);
281
281
  // Prompt for description using inline multiline input
282
282
  const descResult = await multiLineInput({
283
283
  message: 'Description:',
@@ -287,8 +287,8 @@ export default class TicketEdit extends PMOCommand {
287
287
  if (descResult.cancelled) {
288
288
  throw new Error('Edit cancelled');
289
289
  }
290
- // Continue with remaining prompts
291
- const answers = await inquirer.prompt([
290
+ // Continue with remaining prompts - priority first
291
+ const { priority } = await this.prompt([
292
292
  {
293
293
  type: 'list',
294
294
  name: 'priority',
@@ -299,6 +299,9 @@ export default class TicketEdit extends PMOCommand {
299
299
  ],
300
300
  default: ticket.priority || '',
301
301
  },
302
+ ], null);
303
+ // Then category
304
+ const { categoryChoice } = await this.prompt([
302
305
  {
303
306
  type: 'list',
304
307
  name: 'categoryChoice',
@@ -330,14 +333,19 @@ export default class TicketEdit extends PMOCommand {
330
333
  ],
331
334
  default: ticket.category || '',
332
335
  },
333
- {
334
- type: 'input',
335
- name: 'customCategory',
336
- message: 'Enter custom category:',
337
- when: (promptAnswers) => promptAnswers.categoryChoice === '__custom__',
338
- validate: (input) => input.trim() ? true : 'Category cannot be empty',
339
- },
340
- ]);
336
+ ], null);
337
+ // Custom category prompt if needed
338
+ let customCategory;
339
+ if (categoryChoice === '__custom__') {
340
+ const result = await this.prompt([{
341
+ type: 'input',
342
+ name: 'customCategory',
343
+ message: 'Enter custom category:',
344
+ validate: (input) => input.trim() ? true : 'Category cannot be empty',
345
+ }], null);
346
+ customCategory = result.customCategory;
347
+ }
348
+ const answers = { priority, categoryChoice, customCategory };
341
349
  // Build updates object with only changed fields
342
350
  const updates = {};
343
351
  if (title !== ticket.title) {
@@ -1,5 +1,4 @@
1
1
  import { Args, Flags } from '@oclif/core';
2
- import inquirer from 'inquirer';
3
2
  import { PMOCommand, pmoBaseFlags, autoExportToBoard } from '../../lib/pmo/index.js';
4
3
  import { styles } from '../../lib/styles.js';
5
4
  import { shouldOutputJson, outputErrorAsJson, createMetadata, } from '../../lib/prompt-json.js';
@@ -198,6 +197,8 @@ export default class TicketEpic extends PMOCommand {
198
197
  this.log(styles.muted(` Epic: ${epic.title}`));
199
198
  }
200
199
  async executeBulk(flags) {
200
+ const jsonMode = shouldOutputJson(flags);
201
+ const jsonModeConfig = jsonMode ? { flags: flags, commandName: 'ticket epic' } : null;
201
202
  this.log(styles.emphasis('🔗 Link Tickets to Epic\n'));
202
203
  // Get project first
203
204
  const projectId = await this.requireProject();
@@ -238,7 +239,7 @@ export default class TicketEpic extends PMOCommand {
238
239
  ticketEpics.set(ticket.id, row?.epic_id || null);
239
240
  }
240
241
  // Select tickets to link
241
- const { selectedTickets } = await inquirer.prompt([{
242
+ const { selectedTickets } = await this.prompt([{
242
243
  type: 'checkbox',
243
244
  name: 'selectedTickets',
244
245
  message: 'Select tickets to link:',
@@ -250,7 +251,7 @@ export default class TicketEpic extends PMOCommand {
250
251
  value: t.id,
251
252
  };
252
253
  }),
253
- }]);
254
+ }], jsonModeConfig);
254
255
  if (selectedTickets.length === 0) {
255
256
  this.log(styles.muted('No tickets selected.'));
256
257
  return;
@@ -258,18 +259,19 @@ export default class TicketEpic extends PMOCommand {
258
259
  // Select target epic
259
260
  let targetEpic = flags['to-epic'];
260
261
  if (targetEpic === undefined) {
261
- const { epic } = await inquirer.prompt([{
262
+ const { epic } = await this.prompt([{
262
263
  type: 'list',
263
264
  name: 'epic',
264
265
  message: 'Link to which epic?',
265
266
  choices: [
266
- { name: 'None (remove epic link)', value: null },
267
+ { name: 'None (remove epic link)', value: null, command: 'prlt ticket epic --bulk --unlink --json' },
267
268
  ...epics.map(e => ({
268
269
  name: `${e.title} (${e.status})`,
269
270
  value: e.id,
271
+ command: `prlt ticket epic --bulk --to-epic ${e.id} --json`,
270
272
  })),
271
273
  ],
272
- }]);
274
+ }], jsonModeConfig);
273
275
  targetEpic = epic;
274
276
  }
275
277
  // Confirmation
@@ -285,16 +287,16 @@ export default class TicketEpic extends PMOCommand {
285
287
  this.log(styles.primary(` • ${ticketId}: ${ticket?.title}`));
286
288
  }
287
289
  this.log('');
288
- const { confirm } = await inquirer.prompt([{
290
+ const { confirm } = await this.prompt([{
289
291
  type: 'list',
290
292
  name: 'confirm',
291
293
  message: 'Continue?',
292
294
  choices: [
293
- { name: 'No, cancel', value: false },
294
- { name: 'Yes, link tickets', value: true }
295
+ { name: 'No, cancel', value: false, command: '' },
296
+ { name: 'Yes, link tickets', value: true, command: `prlt ticket epic --bulk --to-epic ${targetEpic || 'none'} --force --json` }
295
297
  ],
296
298
  default: 0
297
- }]);
299
+ }], jsonModeConfig);
298
300
  if (!confirm) {
299
301
  this.log(styles.muted('Operation cancelled.'));
300
302
  return;
@@ -1,5 +1,4 @@
1
1
  import { Args, Flags } from '@oclif/core';
2
- import inquirer from 'inquirer';
3
2
  import { PMOCommand, pmoBaseFlags, autoExportToBoard } from '../../lib/pmo/index.js';
4
3
  import { styles } from '../../lib/styles.js';
5
4
  import { shouldOutputJson, outputErrorAsJson, createMetadata, } from '../../lib/prompt-json.js';
@@ -166,6 +165,8 @@ export default class TicketProject extends PMOCommand {
166
165
  this.log(styles.muted(`\nView ticket: prlt ticket view ${ticketId}`));
167
166
  }
168
167
  async executeBulk(flags) {
168
+ const jsonMode = shouldOutputJson(flags);
169
+ const jsonModeConfig = jsonMode ? { flags: flags, commandName: 'ticket project' } : null;
169
170
  this.log(styles.emphasis('📁 Bulk Move Tickets to Project\n'));
170
171
  // Get source project ID
171
172
  const sourceProjectId = await this.requireProject();
@@ -176,7 +177,7 @@ export default class TicketProject extends PMOCommand {
176
177
  return;
177
178
  }
178
179
  // Select tickets
179
- const { selectedTickets } = await inquirer.prompt([{
180
+ const { selectedTickets } = await this.prompt([{
180
181
  type: 'checkbox',
181
182
  name: 'selectedTickets',
182
183
  message: 'Select tickets to move:',
@@ -187,7 +188,7 @@ export default class TicketProject extends PMOCommand {
187
188
  value: t.id,
188
189
  };
189
190
  }),
190
- }]);
191
+ }], jsonModeConfig);
191
192
  if (selectedTickets.length === 0) {
192
193
  this.log(styles.muted('No tickets selected.'));
193
194
  return;
@@ -201,15 +202,16 @@ export default class TicketProject extends PMOCommand {
201
202
  }
202
203
  let targetProjectId = flags.target;
203
204
  if (!targetProjectId) {
204
- const { selected } = await inquirer.prompt([{
205
+ const { selected } = await this.prompt([{
205
206
  type: 'list',
206
207
  name: 'selected',
207
208
  message: 'Select target project:',
208
209
  choices: otherProjects.map(p => ({
209
210
  name: `${p.id} - ${p.name}`,
210
211
  value: p.id,
212
+ command: `prlt ticket project --bulk --target ${p.id} --json`,
211
213
  })),
212
- }]);
214
+ }], jsonModeConfig);
213
215
  targetProjectId = selected;
214
216
  }
215
217
  // Validate target project
@@ -224,15 +226,15 @@ export default class TicketProject extends PMOCommand {
224
226
  });
225
227
  if (ticketsWithEpics.length > 0) {
226
228
  this.log(styles.warning(`\n${ticketsWithEpics.length} ticket(s) are assigned to epics.`));
227
- const { action } = await inquirer.prompt([{
229
+ const { action } = await this.prompt([{
228
230
  type: 'list',
229
231
  name: 'action',
230
232
  message: 'How to handle epic assignments?',
231
233
  choices: [
232
- { name: 'Unlink from epics and move', value: 'unlink' },
233
- { name: 'Cancel', value: 'cancel' },
234
+ { name: 'Unlink from epics and move', value: 'unlink', command: `prlt ticket project --bulk --target ${targetProjectId} --json` },
235
+ { name: 'Cancel', value: 'cancel', command: '' },
234
236
  ],
235
- }]);
237
+ }], jsonModeConfig);
236
238
  if (action === 'cancel') {
237
239
  return;
238
240
  }
@@ -1,5 +1,4 @@
1
1
  import { Args, Flags } from '@oclif/core';
2
- import inquirer from 'inquirer';
3
2
  import { PMOCommand, pmoBaseFlags, autoExportToBoard } from '../../lib/pmo/index.js';
4
3
  import { styles } from '../../lib/styles.js';
5
4
  import { shouldOutputJson, outputErrorAsJson, createMetadata, } from '../../lib/prompt-json.js';
@@ -110,26 +109,28 @@ export default class TicketReassign extends PMOCommand {
110
109
  // Get target assignee
111
110
  let targetAssignee = args.assignee || flags.to;
112
111
  if (!targetAssignee) {
113
- const { assignee } = await inquirer.prompt([{
112
+ const jsonModeConfig = jsonMode ? { flags, commandName: 'ticket reassign' } : null;
113
+ const { assignee } = await this.prompt([{
114
114
  type: 'list',
115
115
  name: 'assignee',
116
116
  message: `Reassign ${ticketId} to:`,
117
117
  choices: [
118
- { name: 'None (unassign)', value: '__none__' },
118
+ { name: 'None (unassign)', value: '__none__', command: `prlt ticket reassign ${ticketId} none${projectId ? ` -P ${projectId}` : ''} --json` },
119
119
  ...Array.from(assignees).sort().map(a => ({
120
120
  name: a === ticket.assignee ? `${a} (current)` : a,
121
121
  value: a,
122
+ command: `prlt ticket reassign ${ticketId} ${a}${projectId ? ` -P ${projectId}` : ''} --json`,
122
123
  })),
123
- { name: '── Enter custom name ──', value: '__custom__' },
124
+ { name: '── Enter custom name ──', value: '__custom__', command: `prlt ticket reassign ${ticketId} <name>${projectId ? ` -P ${projectId}` : ''} --json` },
124
125
  ],
125
- }]);
126
+ }], jsonModeConfig);
126
127
  if (assignee === '__custom__') {
127
- const { customAssignee } = await inquirer.prompt([{
128
+ const { customAssignee } = await this.prompt([{
128
129
  type: 'input',
129
130
  name: 'customAssignee',
130
131
  message: 'Enter agent/assignee name:',
131
132
  validate: (input) => input.length > 0 || 'Name is required',
132
- }]);
133
+ }], jsonModeConfig);
133
134
  targetAssignee = customAssignee;
134
135
  }
135
136
  else if (assignee === '__none__') {
@@ -173,6 +174,7 @@ export default class TicketReassign extends PMOCommand {
173
174
  }
174
175
  }
175
176
  async executeBulk(allTickets, assigneesList, flags) {
177
+ const jsonMode = shouldOutputJson(flags);
176
178
  this.log(styles.emphasis('👤 Reassign Tickets\n'));
177
179
  // Filter tickets if --from specified
178
180
  let filteredTickets = allTickets;
@@ -189,7 +191,8 @@ export default class TicketReassign extends PMOCommand {
189
191
  return;
190
192
  }
191
193
  // Select tickets to reassign
192
- const { selectedTickets } = await inquirer.prompt([{
194
+ const jsonModeConfig = jsonMode ? { flags: flags, commandName: 'ticket reassign' } : null;
195
+ const { selectedTickets } = await this.prompt([{
193
196
  type: 'checkbox',
194
197
  name: 'selectedTickets',
195
198
  message: 'Select tickets to reassign:',
@@ -197,7 +200,7 @@ export default class TicketReassign extends PMOCommand {
197
200
  name: `${t.id} - ${t.title} [Assignee: ${t.assignee || '(none)'}]`,
198
201
  value: t.id,
199
202
  })),
200
- }]);
203
+ }], jsonModeConfig);
201
204
  if (selectedTickets.length === 0) {
202
205
  this.log(styles.muted('No tickets selected.'));
203
206
  return;
@@ -205,26 +208,27 @@ export default class TicketReassign extends PMOCommand {
205
208
  // Select target assignee
206
209
  let targetAssignee = flags.to;
207
210
  if (targetAssignee === undefined) {
208
- const { assignee } = await inquirer.prompt([{
211
+ const { assignee } = await this.prompt([{
209
212
  type: 'list',
210
213
  name: 'assignee',
211
214
  message: 'Reassign to which agent?',
212
215
  choices: [
213
- { name: 'None (unassign)', value: '__none__' },
216
+ { name: 'None (unassign)', value: '__none__', command: '' },
214
217
  ...assigneesList.sort().map(a => ({
215
218
  name: a,
216
219
  value: a,
220
+ command: `prlt ticket reassign --bulk --to ${a} --json`,
217
221
  })),
218
- { name: '── Enter custom name ──', value: '__custom__' },
222
+ { name: '── Enter custom name ──', value: '__custom__', command: '' },
219
223
  ],
220
- }]);
224
+ }], jsonModeConfig);
221
225
  if (assignee === '__custom__') {
222
- const { customAssignee } = await inquirer.prompt([{
226
+ const { customAssignee } = await this.prompt([{
223
227
  type: 'input',
224
228
  name: 'customAssignee',
225
229
  message: 'Enter agent/assignee name:',
226
230
  validate: (input) => input.length > 0 || 'Name is required',
227
- }]);
231
+ }], jsonModeConfig);
228
232
  targetAssignee = customAssignee;
229
233
  }
230
234
  else if (assignee === '__none__') {
@@ -262,16 +266,16 @@ export default class TicketReassign extends PMOCommand {
262
266
  this.log(styles.primary(` • ${ticketId}: ${ticket?.title}`));
263
267
  }
264
268
  this.log('');
265
- const { confirm } = await inquirer.prompt([{
269
+ const { confirm } = await this.prompt([{
266
270
  type: 'list',
267
271
  name: 'confirm',
268
272
  message: 'Continue?',
269
273
  choices: [
270
- { name: 'No, cancel', value: false },
271
- { name: 'Yes, reassign tickets', value: true }
274
+ { name: 'No, cancel', value: false, command: '' },
275
+ { name: 'Yes, reassign tickets', value: true, command: `prlt ticket reassign --bulk --to ${targetAssignee || 'none'} --force --json` }
272
276
  ],
273
277
  default: 0
274
- }]);
278
+ }], jsonModeConfig);
275
279
  if (!confirm) {
276
280
  this.log(styles.muted('Operation cancelled.'));
277
281
  return;
@@ -1,5 +1,4 @@
1
1
  import { Args, Flags } from '@oclif/core';
2
- import inquirer from 'inquirer';
3
2
  import { PMOCommand, pmoBaseFlags, autoExportToBoard } from '../../lib/pmo/index.js';
4
3
  import { styles } from '../../lib/styles.js';
5
4
  import { shouldOutputJson, outputErrorAsJson, createMetadata, } from '../../lib/prompt-json.js';
@@ -192,6 +191,8 @@ export default class TicketSpec extends PMOCommand {
192
191
  this.log(styles.muted(`\nView ticket: prlt ticket view ${ticketId}`));
193
192
  }
194
193
  async executeBulk(flags, projectId) {
194
+ const jsonMode = shouldOutputJson(flags);
195
+ const jsonModeConfig = jsonMode ? { flags: flags, commandName: 'ticket spec' } : null;
195
196
  this.log(styles.emphasis('📄 Bulk Assign Spec to Tickets\n'));
196
197
  // Get all tickets
197
198
  const tickets = await this.storage.listTickets(projectId);
@@ -200,7 +201,7 @@ export default class TicketSpec extends PMOCommand {
200
201
  return;
201
202
  }
202
203
  // Select tickets
203
- const { selectedTickets } = await inquirer.prompt([{
204
+ const { selectedTickets } = await this.prompt([{
204
205
  type: 'checkbox',
205
206
  name: 'selectedTickets',
206
207
  message: 'Select tickets to update:',
@@ -211,7 +212,7 @@ export default class TicketSpec extends PMOCommand {
211
212
  value: t.id,
212
213
  };
213
214
  }),
214
- }]);
215
+ }], jsonModeConfig);
215
216
  if (selectedTickets.length === 0) {
216
217
  this.log(styles.muted('No tickets selected.'));
217
218
  return;
@@ -235,15 +236,16 @@ export default class TicketSpec extends PMOCommand {
235
236
  this.log(styles.muted('\nNo specs found. Create one with: prlt spec create'));
236
237
  return;
237
238
  }
238
- const { selected } = await inquirer.prompt([{
239
+ const { selected } = await this.prompt([{
239
240
  type: 'list',
240
241
  name: 'selected',
241
242
  message: 'Select spec to assign:',
242
243
  choices: specs.map(s => ({
243
244
  name: `${s.id} - ${s.title} (${s.status})`,
244
245
  value: s.id,
246
+ command: `prlt ticket spec --bulk --spec ${s.id} --json`,
245
247
  })),
246
- }]);
248
+ }], jsonModeConfig);
247
249
  specId = selected;
248
250
  }
249
251
  // Validate spec