@proletariat/cli 0.3.25 → 0.3.27

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 (123) hide show
  1. package/dist/commands/action/index.js +2 -2
  2. package/dist/commands/action/show.js +7 -1
  3. package/dist/commands/agent/auth.js +1 -1
  4. package/dist/commands/agent/cleanup.js +6 -6
  5. package/dist/commands/agent/discover.js +1 -1
  6. package/dist/commands/agent/remove.js +4 -4
  7. package/dist/commands/autocomplete/setup.d.ts +2 -2
  8. package/dist/commands/autocomplete/setup.js +5 -5
  9. package/dist/commands/branch/create.js +31 -30
  10. package/dist/commands/branch/list.js +14 -11
  11. package/dist/commands/branch/validate.js +10 -1
  12. package/dist/commands/category/create.js +4 -5
  13. package/dist/commands/category/delete.js +2 -3
  14. package/dist/commands/category/rename.js +2 -3
  15. package/dist/commands/claude.d.ts +2 -8
  16. package/dist/commands/claude.js +26 -26
  17. package/dist/commands/commit.d.ts +2 -8
  18. package/dist/commands/commit.js +4 -26
  19. package/dist/commands/config/index.d.ts +2 -10
  20. package/dist/commands/config/index.js +8 -34
  21. package/dist/commands/docker/clean.js +7 -9
  22. package/dist/commands/docker/index.d.ts +2 -2
  23. package/dist/commands/docker/index.js +13 -12
  24. package/dist/commands/docker/list.d.ts +1 -0
  25. package/dist/commands/docker/list.js +31 -17
  26. package/dist/commands/docker/status.d.ts +3 -1
  27. package/dist/commands/docker/status.js +28 -2
  28. package/dist/commands/docker/sync.js +7 -6
  29. package/dist/commands/epic/delete.js +4 -5
  30. package/dist/commands/epic/list.js +17 -2
  31. package/dist/commands/execution/list.js +25 -17
  32. package/dist/commands/feedback/submit.d.ts +2 -2
  33. package/dist/commands/feedback/submit.js +9 -9
  34. package/dist/commands/link/index.js +2 -2
  35. package/dist/commands/pmo/init.d.ts +2 -2
  36. package/dist/commands/pmo/init.js +29 -10
  37. package/dist/commands/project/spec.js +6 -6
  38. package/dist/commands/repo/list.js +14 -8
  39. package/dist/commands/repo/view.js +2 -1
  40. package/dist/commands/roadmap/list.js +16 -1
  41. package/dist/commands/session/health.d.ts +29 -0
  42. package/dist/commands/session/health.js +496 -0
  43. package/dist/commands/session/index.js +4 -0
  44. package/dist/commands/session/list.js +15 -8
  45. package/dist/commands/spec/edit.js +2 -3
  46. package/dist/commands/staff/add.d.ts +2 -2
  47. package/dist/commands/staff/add.js +15 -14
  48. package/dist/commands/staff/index.js +2 -2
  49. package/dist/commands/staff/list.d.ts +3 -1
  50. package/dist/commands/staff/list.js +15 -1
  51. package/dist/commands/staff/remove.js +4 -4
  52. package/dist/commands/status/index.js +6 -7
  53. package/dist/commands/template/apply.js +10 -11
  54. package/dist/commands/template/create.js +18 -17
  55. package/dist/commands/template/index.d.ts +2 -2
  56. package/dist/commands/template/index.js +6 -6
  57. package/dist/commands/template/save.js +8 -7
  58. package/dist/commands/template/update.js +6 -7
  59. package/dist/commands/terminal/title.d.ts +2 -26
  60. package/dist/commands/terminal/title.js +4 -33
  61. package/dist/commands/theme/index.d.ts +2 -2
  62. package/dist/commands/theme/index.js +19 -18
  63. package/dist/commands/theme/list.d.ts +3 -0
  64. package/dist/commands/theme/list.js +25 -0
  65. package/dist/commands/theme/set.d.ts +2 -2
  66. package/dist/commands/theme/set.js +5 -5
  67. package/dist/commands/ticket/complete.js +4 -1
  68. package/dist/commands/ticket/create.d.ts +1 -0
  69. package/dist/commands/ticket/create.js +64 -16
  70. package/dist/commands/ticket/delete.js +18 -16
  71. package/dist/commands/ticket/edit.js +22 -14
  72. package/dist/commands/ticket/epic.js +12 -10
  73. package/dist/commands/ticket/list.js +24 -5
  74. package/dist/commands/ticket/move.js +4 -1
  75. package/dist/commands/ticket/project.js +11 -9
  76. package/dist/commands/ticket/reassign.js +23 -19
  77. package/dist/commands/ticket/spec.js +7 -5
  78. package/dist/commands/ticket/update.js +55 -53
  79. package/dist/commands/ticket/view.js +4 -2
  80. package/dist/commands/whoami.d.ts +3 -0
  81. package/dist/commands/whoami.js +22 -4
  82. package/dist/commands/work/complete.js +2 -2
  83. package/dist/commands/work/ready.js +9 -9
  84. package/dist/commands/work/revise.js +15 -13
  85. package/dist/commands/work/spawn.js +154 -57
  86. package/dist/commands/work/start.d.ts +1 -0
  87. package/dist/commands/work/start.js +299 -177
  88. package/dist/commands/workspace/prune.d.ts +3 -2
  89. package/dist/commands/workspace/prune.js +70 -10
  90. package/dist/hooks/init.js +4 -0
  91. package/dist/lib/agents/commands.js +4 -0
  92. package/dist/lib/agents/index.js +12 -0
  93. package/dist/lib/execution/devcontainer.d.ts +4 -0
  94. package/dist/lib/execution/devcontainer.js +63 -0
  95. package/dist/lib/mcp/helpers.d.ts +15 -0
  96. package/dist/lib/mcp/helpers.js +15 -0
  97. package/dist/lib/mcp/tools/action.js +5 -5
  98. package/dist/lib/mcp/tools/board.js +7 -7
  99. package/dist/lib/mcp/tools/category.js +5 -5
  100. package/dist/lib/mcp/tools/cli-passthrough.js +30 -30
  101. package/dist/lib/mcp/tools/epic.js +8 -8
  102. package/dist/lib/mcp/tools/phase.js +7 -7
  103. package/dist/lib/mcp/tools/project.js +10 -10
  104. package/dist/lib/mcp/tools/roadmap.js +7 -7
  105. package/dist/lib/mcp/tools/spec.js +9 -9
  106. package/dist/lib/mcp/tools/status.js +6 -6
  107. package/dist/lib/mcp/tools/template.js +6 -6
  108. package/dist/lib/mcp/tools/ticket.js +19 -19
  109. package/dist/lib/mcp/tools/view.js +4 -4
  110. package/dist/lib/mcp/tools/work.js +6 -6
  111. package/dist/lib/mcp/tools/workflow.js +5 -5
  112. package/dist/lib/pmo/index.js +4 -0
  113. package/dist/lib/pmo/storage/base.js +49 -0
  114. package/dist/lib/pr/index.d.ts +9 -0
  115. package/dist/lib/pr/index.js +101 -14
  116. package/dist/lib/prompt-command.d.ts +3 -0
  117. package/dist/lib/prompt-json.d.ts +72 -1
  118. package/dist/lib/prompt-json.js +46 -0
  119. package/dist/lib/repos/index.js +4 -0
  120. package/dist/lib/string-utils.d.ts +10 -0
  121. package/dist/lib/string-utils.js +16 -0
  122. package/oclif.manifest.json +594 -449
  123. package/package.json +3 -2
@@ -153,8 +153,8 @@ export default class TicketEdit extends PMOCommand {
153
153
  return; // outputPromptAsJson exits, but TypeScript doesn't know
154
154
  }
155
155
  // Interactive mode - prompt for all editable fields
156
- const board = await this.storage.getBoard(ticket.projectId);
157
- const columns = board.columns.map(col => col.name);
156
+ const board = ticket.projectId ? await this.storage.getProjectBoard(ticket.projectId) : null;
157
+ const columns = board ? board.columns.map(col => col.name) : [];
158
158
  updates = await this.promptForEdits(ticket, columns);
159
159
  }
160
160
  else {
@@ -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;
@@ -3,6 +3,7 @@ import { pmoBaseFlags } from '../../lib/pmo/index.js';
3
3
  import { PRIORITIES } from '../../lib/pmo/types.js';
4
4
  import { getPMOContext } from '../../lib/pmo/pmo-context.js';
5
5
  import { styles, formatPriority, formatCategory, getColumnStyle, getColumnEmoji, divider, getPriorityStyle, } from '../../lib/styles.js';
6
+ import { isNonTTY } from '../../lib/prompt-json.js';
6
7
  // Priority order for grouping: P0, P1, P2, P3, None
7
8
  const PRIORITY_ORDER = ['P0', 'P1', 'P2', 'P3', 'None'];
8
9
  export default class TicketList extends Command {
@@ -67,6 +68,10 @@ export default class TicketList extends Command {
67
68
  };
68
69
  async run() {
69
70
  const { flags } = await this.parse(TicketList);
71
+ // Default format to 'json' in non-TTY environments (piped output, CI, agents)
72
+ if (flags.format === 'table' && isNonTTY()) {
73
+ flags.format = 'json';
74
+ }
70
75
  // When --all is set, we don't need to select a specific project
71
76
  // Otherwise, use the normal project selection flow
72
77
  // Get PMO context - no project selection needed
@@ -110,10 +115,12 @@ export default class TicketList extends Command {
110
115
  // Get the project board to validate the column
111
116
  const targetProjectId = projectId || (await pmoContext.storage.listProjectSummaries())[0]?.id;
112
117
  if (targetProjectId) {
113
- const board = await pmoContext.storage.getBoard(targetProjectId);
114
- const validColumns = board.columns.map(c => c.name);
115
- if (!validColumns.includes(flags.column)) {
116
- this.error(`Column "${flags.column}" not found. Valid columns: ${validColumns.join(', ')}`);
118
+ const board = await pmoContext.storage.getProjectBoard(targetProjectId);
119
+ if (board) {
120
+ const validColumns = board.columns.map(c => c.name);
121
+ if (!validColumns.includes(flags.column)) {
122
+ this.error(`Column "${flags.column}" not found. Valid columns: ${validColumns.join(', ')}`);
123
+ }
117
124
  }
118
125
  }
119
126
  }
@@ -149,7 +156,19 @@ export default class TicketList extends Command {
149
156
  this.log(styles.warning('No project found.'));
150
157
  return;
151
158
  }
152
- const board = await pmoContext.storage.getBoard(actualProjectId);
159
+ const board = await pmoContext.storage.getProjectBoard(actualProjectId);
160
+ if (!board) {
161
+ // Project doesn't exist (orphaned tickets) - fall back to cross-project view
162
+ this.log(styles.warning(`Project "${actualProjectId}" not found. Showing tickets without board layout.`));
163
+ switch (flags.format) {
164
+ case 'json':
165
+ this.log(JSON.stringify(tickets, null, 2));
166
+ break;
167
+ default:
168
+ this.outputCrossProjectTable(tickets, groupBy);
169
+ }
170
+ return;
171
+ }
153
172
  const columns = board.columns.map(col => col.name);
154
173
  switch (flags.format) {
155
174
  case 'json':
@@ -216,7 +216,10 @@ export default class TicketMove extends PMOCommand {
216
216
  this.log(styles.emphasis('📦 Move Multiple Tickets\n'));
217
217
  }
218
218
  // Get columns
219
- const board = await this.storage.getBoard(projectId);
219
+ const board = await this.storage.getProjectBoard(projectId);
220
+ if (!board) {
221
+ this.error(`Project "${projectId}" not found. The ticket may belong to an orphaned project.`);
222
+ }
220
223
  const columns = board.columns.map(col => col.name);
221
224
  // Agent mode config for prompts
222
225
  const jsonModeConfig = flags.json ? { flags, commandName: 'ticket move --bulk' } : null;
@@ -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