@proletariat/cli 0.3.35 → 0.3.40

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (148) hide show
  1. package/README.md +37 -2
  2. package/bin/dev.js +0 -0
  3. package/dist/commands/agent/auth.d.ts +12 -2
  4. package/dist/commands/agent/auth.js +128 -4
  5. package/dist/commands/agent/list.js +16 -7
  6. package/dist/commands/agent/status.js +32 -4
  7. package/dist/commands/board/watch.js +6 -0
  8. package/dist/commands/branch/list.d.ts +1 -0
  9. package/dist/commands/branch/list.js +43 -12
  10. package/dist/commands/branch/where.js +9 -19
  11. package/dist/commands/category/list.d.ts +2 -1
  12. package/dist/commands/category/list.js +38 -13
  13. package/dist/commands/{claude.d.ts → claude/index.d.ts} +1 -1
  14. package/dist/commands/{claude.js → claude/index.js} +12 -12
  15. package/dist/commands/claude/open.d.ts +13 -0
  16. package/dist/commands/claude/open.js +175 -0
  17. package/dist/commands/diet.js +18 -2
  18. package/dist/commands/docker/logs.js +7 -3
  19. package/dist/commands/docker/shell.js +6 -0
  20. package/dist/commands/docker/start.js +20 -4
  21. package/dist/commands/docker/sync.d.ts +4 -0
  22. package/dist/commands/docker/sync.js +30 -2
  23. package/dist/commands/epic/show.d.ts +13 -0
  24. package/dist/commands/epic/show.js +16 -0
  25. package/dist/commands/epic/ticket.js +7 -24
  26. package/dist/commands/epic/view.js +27 -0
  27. package/dist/commands/execution/config.d.ts +0 -4
  28. package/dist/commands/execution/config.js +14 -46
  29. package/dist/commands/execution/index.js +2 -1
  30. package/dist/commands/execution/logs.js +7 -1
  31. package/dist/commands/execution/stop.js +2 -1
  32. package/dist/commands/execution/view.js +30 -26
  33. package/dist/commands/init.js +2 -19
  34. package/dist/commands/label/create.js +2 -1
  35. package/dist/commands/label/delete.js +2 -1
  36. package/dist/commands/label/group/create.js +2 -1
  37. package/dist/commands/label/group/list.js +2 -1
  38. package/dist/commands/label/list.js +2 -1
  39. package/dist/commands/mcp-server.js +27 -1
  40. package/dist/commands/phase/template/list.js +2 -1
  41. package/dist/commands/pmo/init.js +12 -40
  42. package/dist/commands/project/create.js +3 -4
  43. package/dist/commands/project/update.js +5 -6
  44. package/dist/commands/pull.js +24 -0
  45. package/dist/commands/qa/index.d.ts +54 -0
  46. package/dist/commands/qa/index.js +762 -0
  47. package/dist/commands/repo/view.js +2 -8
  48. package/dist/commands/session/attach.js +4 -4
  49. package/dist/commands/session/create.d.ts +19 -0
  50. package/dist/commands/session/create.js +102 -0
  51. package/dist/commands/session/health.js +4 -23
  52. package/dist/commands/session/index.js +14 -1
  53. package/dist/commands/session/list.js +9 -8
  54. package/dist/commands/session/peek.d.ts +38 -0
  55. package/dist/commands/session/peek.js +316 -0
  56. package/dist/commands/session/poke.d.ts +27 -0
  57. package/dist/commands/session/poke.js +219 -0
  58. package/dist/commands/spec/view.js +29 -0
  59. package/dist/commands/template/list.js +2 -1
  60. package/dist/commands/theme/add-names.d.ts +4 -0
  61. package/dist/commands/theme/add-names.js +11 -1
  62. package/dist/commands/theme/create.d.ts +2 -0
  63. package/dist/commands/theme/create.js +8 -0
  64. package/dist/commands/ticket/bulk.js +2 -2
  65. package/dist/commands/ticket/complete.js +2 -2
  66. package/dist/commands/ticket/create.js +21 -0
  67. package/dist/commands/ticket/delete.js +8 -0
  68. package/dist/commands/ticket/edit.js +25 -0
  69. package/dist/commands/ticket/epic.js +17 -43
  70. package/dist/commands/ticket/index.js +2 -2
  71. package/dist/commands/ticket/move.js +25 -2
  72. package/dist/commands/ticket/resolve.js +3 -4
  73. package/dist/commands/ticket/show.d.ts +13 -0
  74. package/dist/commands/ticket/show.js +16 -0
  75. package/dist/commands/ticket/template/list.js +2 -1
  76. package/dist/commands/ticket/view.d.ts +0 -1
  77. package/dist/commands/ticket/view.js +30 -1
  78. package/dist/commands/work/index.js +4 -0
  79. package/dist/commands/work/spawn-all.js +1 -1
  80. package/dist/commands/work/spawn.js +15 -4
  81. package/dist/commands/work/start.js +186 -103
  82. package/dist/commands/work/status.d.ts +14 -0
  83. package/dist/commands/work/status.js +60 -0
  84. package/dist/commands/work/watch.js +1 -1
  85. package/dist/commands/workflow/index.js +2 -1
  86. package/dist/commands/workflow/show.d.ts +13 -0
  87. package/dist/commands/workflow/show.js +16 -0
  88. package/dist/commands/workspace/add.js +15 -0
  89. package/dist/commands/workspace/list.js +2 -1
  90. package/dist/commands/workspace/prune.js +7 -7
  91. package/dist/hooks/init.js +10 -2
  92. package/dist/lib/agents/commands.d.ts +5 -0
  93. package/dist/lib/agents/commands.js +143 -97
  94. package/dist/lib/branch/index.d.ts +1 -0
  95. package/dist/lib/database/drizzle-schema.d.ts +465 -0
  96. package/dist/lib/database/drizzle-schema.js +53 -0
  97. package/dist/lib/database/index.d.ts +47 -1
  98. package/dist/lib/database/index.js +138 -20
  99. package/dist/lib/execution/config.d.ts +15 -1
  100. package/dist/lib/execution/config.js +28 -0
  101. package/dist/lib/execution/runners.d.ts +45 -0
  102. package/dist/lib/execution/runners.js +187 -26
  103. package/dist/lib/execution/session-utils.d.ts +16 -1
  104. package/dist/lib/execution/session-utils.js +71 -4
  105. package/dist/lib/execution/spawner.js +15 -2
  106. package/dist/lib/execution/storage.d.ts +6 -1
  107. package/dist/lib/execution/storage.js +35 -5
  108. package/dist/lib/execution/types.d.ts +3 -0
  109. package/dist/lib/mcp/tools/board.js +4 -6
  110. package/dist/lib/mcp/tools/cli-passthrough.js +25 -6
  111. package/dist/lib/mcp/tools/epic.js +8 -3
  112. package/dist/lib/mcp/tools/index.d.ts +1 -0
  113. package/dist/lib/mcp/tools/index.js +1 -0
  114. package/dist/lib/mcp/tools/spec.js +1 -1
  115. package/dist/lib/mcp/tools/ticket.js +11 -9
  116. package/dist/lib/mcp/tools/tmux.d.ts +16 -0
  117. package/dist/lib/mcp/tools/tmux.js +182 -0
  118. package/dist/lib/mcp/tools/work.js +148 -6
  119. package/dist/lib/mcp/types.d.ts +10 -0
  120. package/dist/lib/multiline-input.js +2 -1
  121. package/dist/lib/pmo/base-command.js +4 -4
  122. package/dist/lib/pmo/schema.d.ts +1 -1
  123. package/dist/lib/pmo/schema.js +1 -0
  124. package/dist/lib/pmo/storage/actions.js +1 -1
  125. package/dist/lib/pmo/storage/base.js +402 -50
  126. package/dist/lib/pmo/storage/dependencies.d.ts +1 -0
  127. package/dist/lib/pmo/storage/dependencies.js +11 -3
  128. package/dist/lib/pmo/storage/epics.js +1 -1
  129. package/dist/lib/pmo/storage/helpers.d.ts +4 -4
  130. package/dist/lib/pmo/storage/helpers.js +36 -26
  131. package/dist/lib/pmo/storage/projects.d.ts +2 -0
  132. package/dist/lib/pmo/storage/projects.js +207 -119
  133. package/dist/lib/pmo/storage/specs.d.ts +2 -0
  134. package/dist/lib/pmo/storage/specs.js +274 -188
  135. package/dist/lib/pmo/storage/tickets.d.ts +2 -0
  136. package/dist/lib/pmo/storage/tickets.js +350 -290
  137. package/dist/lib/pmo/storage/types.d.ts +1 -0
  138. package/dist/lib/pmo/storage/views.d.ts +2 -0
  139. package/dist/lib/pmo/storage/views.js +183 -130
  140. package/dist/lib/prompt-command.d.ts +20 -0
  141. package/dist/lib/prompt-command.js +38 -2
  142. package/dist/lib/prompt-json.d.ts +41 -4
  143. package/dist/lib/prompt-json.js +138 -7
  144. package/dist/lib/styles.d.ts +37 -0
  145. package/dist/lib/styles.js +73 -0
  146. package/oclif.manifest.json +4046 -3385
  147. package/package.json +11 -6
  148. package/LICENSE +0 -190
@@ -2,6 +2,8 @@ import { Command, Args, Flags } from '@oclif/core';
2
2
  import chalk from 'chalk';
3
3
  import { getWorkspaceInfo } from '../../lib/agents/commands.js';
4
4
  import { createTheme, getTheme } from '../../lib/database/index.js';
5
+ import { machineOutputFlags } from '../../lib/pmo/index.js';
6
+ import { shouldOutputJson } from '../../lib/prompt-json.js';
5
7
  export default class ThemeCreate extends Command {
6
8
  static description = 'Create a custom agent theme';
7
9
  static examples = [
@@ -16,6 +18,7 @@ export default class ThemeCreate extends Command {
16
18
  }),
17
19
  };
18
20
  static flags = {
21
+ ...machineOutputFlags,
19
22
  description: Flags.string({
20
23
  char: 'd',
21
24
  description: 'Theme description',
@@ -26,6 +29,7 @@ export default class ThemeCreate extends Command {
26
29
  };
27
30
  async run() {
28
31
  const { args, flags } = await this.parse(ThemeCreate);
32
+ const jsonMode = shouldOutputJson(flags);
29
33
  try {
30
34
  const workspaceInfo = getWorkspaceInfo();
31
35
  // Validate theme name format
@@ -51,6 +55,10 @@ export default class ThemeCreate extends Command {
51
55
  description: flags.description,
52
56
  builtin: false,
53
57
  });
58
+ if (jsonMode) {
59
+ this.log(JSON.stringify({ type: 'success', result: { id: theme.id, displayName: theme.display_name, description: theme.description } }, null, 2));
60
+ return;
61
+ }
54
62
  this.log(chalk.green(`\n Created theme: ${theme.display_name}`));
55
63
  this.log(chalk.gray(` ID: ${theme.id}`));
56
64
  if (theme.description) {
@@ -45,7 +45,7 @@ export default class TicketBulk extends PMOCommand {
45
45
  const choice = menuChoices.find(c => c.value === value);
46
46
  return { name: choice.emoji ? `${choice.emoji} ${choice.name}` : choice.name, value: choice.value };
47
47
  };
48
- const { action } = await inquirer.prompt([{
48
+ const { action } = await this.prompt([{
49
49
  type: 'list',
50
50
  name: 'action',
51
51
  message: 'What would you like to do?',
@@ -64,7 +64,7 @@ export default class TicketBulk extends PMOCommand {
64
64
  new inquirer.Separator(),
65
65
  withEmoji('cancel'),
66
66
  ]
67
- }]);
67
+ }], null);
68
68
  if (action === 'cancel') {
69
69
  this.log(styles.muted('Operation cancelled.'));
70
70
  return;
@@ -98,11 +98,11 @@ export default class TicketComplete extends PMOCommand {
98
98
  }
99
99
  async executeBulk(incompleteTickets, doneColumnName, flags) {
100
100
  // Only show header in interactive mode
101
- if (!(flags.json || flags.machine)) {
101
+ if (!shouldOutputJson(flags)) {
102
102
  this.log(styles.emphasis('✅ Complete Multiple Tickets\n'));
103
103
  }
104
104
  // Agent mode config for prompts
105
- const jsonModeConfig = (flags.json || flags.machine) ? { flags, commandName: 'ticket complete --bulk' } : null;
105
+ const jsonModeConfig = shouldOutputJson(flags) ? { flags, commandName: 'ticket complete --bulk' } : null;
106
106
  // Select tickets to complete (now agent-compatible!)
107
107
  const { selectedTickets } = await this.prompt([{
108
108
  type: 'checkbox',
@@ -284,6 +284,27 @@ export default class TicketCreate extends PMOCommand {
284
284
  updateEpicTicketsSection(this.pmoPath, ticketData.epicId, epic.status, ticketInfos, projectId);
285
285
  }
286
286
  }
287
+ // JSON output mode - match MCP tool response shape
288
+ if (jsonMode) {
289
+ this.log(JSON.stringify({
290
+ success: true,
291
+ ticket: {
292
+ id: ticket.id,
293
+ title: ticket.title,
294
+ priority: ticket.priority,
295
+ category: ticket.category,
296
+ statusName: ticket.statusName,
297
+ statusCategory: ticket.statusCategory,
298
+ projectId: ticket.projectId,
299
+ assignee: ticket.assignee,
300
+ owner: ticket.owner,
301
+ branch: ticket.branch,
302
+ epicId: ticket.epicId,
303
+ position: ticket.position,
304
+ },
305
+ }, null, 2));
306
+ return;
307
+ }
287
308
  this.log(styles.success(`\n✅ Created ticket ${styles.emphasis(ticket.id)} in project ${styles.emphasis(projectName)}`));
288
309
  if (template) {
289
310
  this.log(styles.muted(` Template: ${template.name}`));
@@ -103,6 +103,14 @@ export default class TicketDelete extends PMOCommand {
103
103
  await this.storage.deleteTicket(ticketId);
104
104
  // Auto-export to board.md after write
105
105
  await autoExportToBoard(this.pmoPath, this.storage, (msg) => this.log(styles.muted(msg)));
106
+ // JSON output mode - match MCP tool response shape
107
+ if (jsonMode) {
108
+ this.log(JSON.stringify({
109
+ success: true,
110
+ message: `Deleted ${ticketId}`,
111
+ }, null, 2));
112
+ return;
113
+ }
106
114
  this.log(styles.success(`\n✅ Ticket ${styles.emphasis(ticketId)} deleted`));
107
115
  this.log(styles.muted(' Removed from database and board'));
108
116
  }
@@ -227,6 +227,31 @@ export default class TicketEdit extends PMOCommand {
227
227
  const updatedTicket = await this.storage.updateTicket(ticketId, updates);
228
228
  // Auto-export to board.md
229
229
  await autoExportToBoard(this.pmoPath, this.storage, (msg) => this.log(styles.muted(msg)));
230
+ // JSON output mode - match MCP tool response shape
231
+ if (jsonMode) {
232
+ // Re-fetch to get latest state including subtasks/AC changes
233
+ const finalTicket = (subtasksChanged || acChanged)
234
+ ? await this.storage.getTicket(ticketId) ?? updatedTicket
235
+ : updatedTicket;
236
+ this.log(JSON.stringify({
237
+ success: true,
238
+ ticket: {
239
+ id: finalTicket.id,
240
+ title: finalTicket.title,
241
+ priority: finalTicket.priority,
242
+ category: finalTicket.category,
243
+ statusName: finalTicket.statusName,
244
+ statusCategory: finalTicket.statusCategory,
245
+ projectId: finalTicket.projectId,
246
+ assignee: finalTicket.assignee,
247
+ owner: finalTicket.owner,
248
+ branch: finalTicket.branch,
249
+ epicId: finalTicket.epicId,
250
+ position: finalTicket.position,
251
+ },
252
+ }, null, 2));
253
+ return;
254
+ }
230
255
  // Display updated ticket
231
256
  this.log(styles.success(`\n✅ Updated ticket ${styles.emphasis(updatedTicket.id)}`));
232
257
  const changedFields = [];
@@ -68,11 +68,10 @@ export default class TicketEpic extends PMOCommand {
68
68
  const projectId = await this.requireProject();
69
69
  // Get all epics
70
70
  const epics = await this.storage.listEpics(projectId);
71
- // Get epic_id for each ticket via direct DB query
72
- const db = this.storage.db;
71
+ // Helper to get ticket's epic ID via storage layer
73
72
  const getTicketEpicId = (ticketId) => {
74
- const row = db.prepare(`SELECT epic_id FROM pmo_tickets WHERE id = ?`).get(ticketId);
75
- return row?.epic_id || null;
73
+ const ticket = allTickets.find((t) => t.id === ticketId);
74
+ return ticket?.epicId ?? null;
76
75
  };
77
76
  let ticketId = args.id;
78
77
  let epicId = args['epic-id'];
@@ -119,11 +118,7 @@ export default class TicketEpic extends PMOCommand {
119
118
  }
120
119
  const currentEpic = epics.find(e => e.id === currentEpicId);
121
120
  // Update the ticket
122
- db.prepare(`
123
- UPDATE pmo_tickets
124
- SET epic_id = NULL, updated_at = ?
125
- WHERE id = ?
126
- `).run(Date.now(), ticketId);
121
+ await this.storage.unlinkTicketFromEpic(ticketId);
127
122
  await autoExportToBoard(this.pmoPath, this.storage, (msg) => this.log(styles.muted(msg)));
128
123
  this.log(styles.success(`\n✅ Unlinked ${styles.emphasis(ticketId)} from ${currentEpic?.title || currentEpicId}`));
129
124
  this.log(styles.muted(` Title: ${ticket.title}`));
@@ -156,11 +151,7 @@ export default class TicketEpic extends PMOCommand {
156
151
  }
157
152
  if (selected === '__none__') {
158
153
  // Unlink
159
- db.prepare(`
160
- UPDATE pmo_tickets
161
- SET epic_id = NULL, updated_at = ?
162
- WHERE id = ?
163
- `).run(Date.now(), ticketId);
154
+ await this.storage.unlinkTicketFromEpic(ticketId);
164
155
  const currentEpic = epics.find(e => e.id === currentEpicId);
165
156
  await autoExportToBoard(this.pmoPath, this.storage, (msg) => this.log(styles.muted(msg)));
166
157
  this.log(styles.success(`\n✅ Unlinked ${styles.emphasis(ticketId)} from ${currentEpic?.title || currentEpicId}`));
@@ -180,11 +171,7 @@ export default class TicketEpic extends PMOCommand {
180
171
  return;
181
172
  }
182
173
  // Update the ticket
183
- db.prepare(`
184
- UPDATE pmo_tickets
185
- SET epic_id = ?, updated_at = ?
186
- WHERE id = ?
187
- `).run(epicId, Date.now(), ticketId);
174
+ await this.storage.linkTicketToEpic(ticketId, epicId);
188
175
  await autoExportToBoard(this.pmoPath, this.storage, (msg) => this.log(styles.muted(msg)));
189
176
  this.log(styles.success(`\n✅ Linked ${styles.emphasis(ticketId)} to ${styles.emphasis(epicId)}`));
190
177
  this.log(styles.muted(` Title: ${ticket.title}`));
@@ -202,35 +189,21 @@ export default class TicketEpic extends PMOCommand {
202
189
  this.log(styles.warning('No tickets found.'));
203
190
  return;
204
191
  }
205
- // Get epics from database
206
- const db = this.storage.db;
207
- const epics = db.prepare(`
208
- SELECT id, title, status FROM pmo_epics
209
- WHERE project_id = ?
210
- ORDER BY status, title
211
- `).all(projectId);
192
+ // Get epics via storage layer
193
+ const epics = await this.storage.listEpics(projectId);
212
194
  // Filter tickets if --from-epic specified
213
195
  let filteredTickets = allTickets;
214
196
  if (flags['from-epic']) {
215
- // Get tickets with matching epic_id via metadata or direct query
216
- const epicTickets = db.prepare(`
217
- SELECT id FROM pmo_tickets
218
- WHERE project_id = ? AND epic_id = ?
219
- `).all(projectId, flags['from-epic']);
220
- const epicTicketIds = new Set(epicTickets.map(t => t.id));
221
- filteredTickets = allTickets.filter(t => epicTicketIds.has(t.id));
197
+ filteredTickets = allTickets.filter(t => t.epicId === flags['from-epic']);
222
198
  }
223
199
  if (filteredTickets.length === 0) {
224
200
  this.log(styles.warning('No tickets found matching filter.'));
225
201
  return;
226
202
  }
227
- // Get current epic for each ticket
203
+ // Get current epic for each ticket (already available on Ticket objects)
228
204
  const ticketEpics = new Map();
229
205
  for (const ticket of filteredTickets) {
230
- const row = db.prepare(`
231
- SELECT epic_id FROM pmo_tickets WHERE id = ?
232
- `).get(ticket.id);
233
- ticketEpics.set(ticket.id, row?.epic_id || null);
206
+ ticketEpics.set(ticket.id, ticket.epicId ?? null);
234
207
  }
235
208
  // Select tickets to link
236
209
  const { selectedTickets } = await this.prompt([{
@@ -302,11 +275,12 @@ export default class TicketEpic extends PMOCommand {
302
275
  let failCount = 0;
303
276
  for (const ticketId of selectedTickets) {
304
277
  try {
305
- db.prepare(`
306
- UPDATE pmo_tickets
307
- SET epic_id = ?, updated_at = ?
308
- WHERE id = ?
309
- `).run(targetEpic, Date.now(), ticketId);
278
+ if (targetEpic) {
279
+ await this.storage.linkTicketToEpic(ticketId, targetEpic);
280
+ }
281
+ else {
282
+ await this.storage.unlinkTicketFromEpic(ticketId);
283
+ }
310
284
  const action = targetEpic ? `Linked to ${targetEpic}` : 'Removed epic link';
311
285
  this.log(styles.success(`${ticketId}: ${action}`));
312
286
  successCount++;
@@ -41,7 +41,7 @@ export default class Ticket extends PMOCommand {
41
41
  return;
42
42
  }
43
43
  // Show interactive menu
44
- const { action } = await inquirer.prompt([{
44
+ const { action } = await this.prompt([{
45
45
  type: 'list',
46
46
  name: 'action',
47
47
  message: '🎫 ' + message,
@@ -52,7 +52,7 @@ export default class Ticket extends PMOCommand {
52
52
  menuChoices[12],
53
53
  menuChoices[13],
54
54
  ],
55
- }]);
55
+ }], null);
56
56
  if (action === 'cancel') {
57
57
  return;
58
58
  }
@@ -3,6 +3,7 @@ import { autoExportToBoard, PMOCommand, pmoBaseFlags, } from '../../lib/pmo/inde
3
3
  import { PMOError } from '../../lib/pmo/types.js';
4
4
  import { styles } from '../../lib/styles.js';
5
5
  import { shouldOutputJson, outputErrorAsJson, createMetadata, } from '../../lib/prompt-json.js';
6
+ import { formatTicket } from '../../lib/mcp/helpers.js';
6
7
  export default class TicketMove extends PMOCommand {
7
8
  static description = 'Move ticket(s) to a different column';
8
9
  static examples = [
@@ -195,6 +196,14 @@ export default class TicketMove extends PMOCommand {
195
196
  const moved = await this.storage.moveTicket(projectId, ticketId, targetColumn, flags.position);
196
197
  // Auto-export to board.md after write
197
198
  await autoExportToBoard(this.pmoPath, this.storage, (msg) => this.log(styles.muted(msg)));
199
+ // JSON output mode - match MCP tool response shape
200
+ if (jsonMode) {
201
+ this.log(JSON.stringify({
202
+ success: true,
203
+ ticket: formatTicket(moved),
204
+ }, null, 2));
205
+ return;
206
+ }
198
207
  this.log(styles.success(`\n✅ Moved ticket ${styles.emphasis(moved.id)}`));
199
208
  if (targetColumn !== ticket.statusName) {
200
209
  this.log(styles.muted(` From: ${ticket.statusName}`));
@@ -206,7 +215,7 @@ export default class TicketMove extends PMOCommand {
206
215
  }
207
216
  async executeBulk(allTickets, flags, projectId) {
208
217
  // Only show header in interactive mode
209
- if (!(flags.json || flags.machine)) {
218
+ if (!shouldOutputJson(flags)) {
210
219
  this.log(styles.emphasis('📦 Move Multiple Tickets\n'));
211
220
  }
212
221
  // Get columns
@@ -216,7 +225,7 @@ export default class TicketMove extends PMOCommand {
216
225
  }
217
226
  const columns = board.columns.map(col => col.name);
218
227
  // Agent mode config for prompts
219
- const jsonModeConfig = (flags.json || flags.machine) ? { flags, commandName: 'ticket move --bulk' } : null;
228
+ const jsonModeConfig = shouldOutputJson(flags) ? { flags, commandName: 'ticket move --bulk' } : null;
220
229
  // Select tickets to move (now agent-compatible!)
221
230
  const { selectedTickets } = await this.prompt([{
222
231
  type: 'checkbox',
@@ -328,6 +337,13 @@ export default class TicketMove extends PMOCommand {
328
337
  // Refresh ticket to get updated status
329
338
  const updatedTicket = await this.storage.getTicket(ticketId);
330
339
  await autoExportToBoard(this.pmoPath, this.storage, (msg) => this.log(styles.muted(msg)));
340
+ if (jsonMode && updatedTicket) {
341
+ this.log(JSON.stringify({
342
+ success: true,
343
+ ticket: formatTicket(updatedTicket),
344
+ }, null, 2));
345
+ return;
346
+ }
331
347
  this.log(styles.success(`\n✅ Moved ticket ${styles.emphasis(ticketId)} to project ${styles.emphasis(targetProject.id)}`));
332
348
  this.log(styles.muted(` From project: ${sourceProjectId}`));
333
349
  this.log(styles.muted(` To project: ${targetProject.id}`));
@@ -345,6 +361,13 @@ export default class TicketMove extends PMOCommand {
345
361
  }
346
362
  }
347
363
  await autoExportToBoard(this.pmoPath, this.storage, (msg) => this.log(styles.muted(msg)));
364
+ if (jsonMode) {
365
+ this.log(JSON.stringify({
366
+ success: true,
367
+ ticket: formatTicket(movedTicket),
368
+ }, null, 2));
369
+ return;
370
+ }
348
371
  this.log(styles.success(`\n✅ Moved ticket ${styles.emphasis(ticketId)} to project ${styles.emphasis(targetProject.id)}`));
349
372
  this.log(styles.muted(` From project: ${sourceProjectId}`));
350
373
  this.log(styles.muted(` To project: ${targetProject.id}`));
@@ -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, outputSuccessAsJson, createMetadata, } from '../../lib/prompt-json.js';
@@ -223,18 +222,18 @@ export default class TicketResolve extends PMOCommand {
223
222
  for (const question of unanswered) {
224
223
  this.log(styles.emphasis(` Q${question.number}: ${question.text}`));
225
224
  // eslint-disable-next-line no-await-in-loop
226
- const { answer } = await inquirer.prompt([
225
+ const { answer } = await this.prompt([
227
226
  {
228
227
  type: 'input',
229
228
  name: 'answer',
230
229
  message: ` A${question.number}:`,
231
230
  validate: (input) => {
232
- if (!input.trim())
231
+ if (!String(input).trim())
233
232
  return 'Please provide an answer (or type "skip" to skip)';
234
233
  return true;
235
234
  },
236
235
  },
237
- ]);
236
+ ], null);
238
237
  if (answer.toLowerCase() === 'cancel') {
239
238
  cancelled = true;
240
239
  this.log(styles.warning('\nResolution cancelled.'));
@@ -0,0 +1,13 @@
1
+ import TicketView from './view.js';
2
+ export default class TicketShow extends TicketView {
3
+ static description: string;
4
+ static hidden: boolean;
5
+ static args: {
6
+ ticketId: 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 TicketView from './view.js';
4
+ export default class TicketShow extends TicketView {
5
+ static description = 'View detailed ticket information (alias for ticket view)';
6
+ static hidden = true;
7
+ static args = {
8
+ ticketId: Args.string({
9
+ description: 'Ticket ID to view - prompts with dropdown if not provided',
10
+ required: false,
11
+ }),
12
+ };
13
+ static flags = {
14
+ ...pmoBaseFlags,
15
+ };
16
+ }
@@ -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 TicketTemplateList extends PMOCommand {
5
6
  static description = 'List ticket templates';
@@ -30,7 +31,7 @@ export default class TicketTemplateList extends PMOCommand {
30
31
  if (flags.custom)
31
32
  builtinFilter = { isBuiltin: false };
32
33
  const templates = await this.storage.listTicketTemplates(builtinFilter);
33
- if (flags.json) {
34
+ if (shouldOutputJson(flags)) {
34
35
  this.log(JSON.stringify(templates, null, 2));
35
36
  return;
36
37
  }
@@ -1,6 +1,5 @@
1
1
  import { PMOCommand } from '../../lib/pmo/index.js';
2
2
  export default class TicketView extends PMOCommand {
3
- static aliases: string[];
4
3
  static description: string;
5
4
  static examples: string[];
6
5
  static flags: {
@@ -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;
@@ -24,7 +24,7 @@ export default class WorkSpawnAll extends PMOCommand {
24
24
  default: false,
25
25
  }),
26
26
  'create-pr': Flags.boolean({
27
- description: 'Create PR when work is ready',
27
+ description: 'Create PR when work is ready (canonical flag for PR behavior)',
28
28
  default: false,
29
29
  }),
30
30
  executor: Flags.string({
@@ -25,6 +25,7 @@ export default class WorkSpawn extends PMOCommand {
25
25
  '<%= config.bin %> <%= command.id %> --count 5 --category ship --action implement # Filtered by category',
26
26
  '<%= config.bin %> <%= command.id %> --count 5 --priority P0 --action implement # Filtered by priority',
27
27
  '<%= config.bin %> <%= command.id %> --count 10 --diet --category ship,grow --action groom # Combined',
28
+ '<%= config.bin %> <%= command.id %> TKT-001 TKT-002 --create-pr # Create PR when work is ready',
28
29
  ];
29
30
  static flags = {
30
31
  ...pmoBaseFlags,
@@ -93,11 +94,11 @@ export default class WorkSpawn extends PMOCommand {
93
94
  default: false,
94
95
  }),
95
96
  'create-pr': Flags.boolean({
96
- description: 'Create PR when work is ready (batch mode only)',
97
+ description: 'Create PR when work is ready (canonical flag for PR behavior, batch mode only)',
97
98
  default: false,
98
99
  }),
99
100
  'no-pr': Flags.boolean({
100
- description: 'Do not create PR when work is ready (batch mode only)',
101
+ description: '[deprecated: use --create-pr instead] Skip PR creation (batch mode only)',
101
102
  default: false,
102
103
  }),
103
104
  action: Flags.string({
@@ -153,6 +154,10 @@ export default class WorkSpawn extends PMOCommand {
153
154
  }
154
155
  this.error(message);
155
156
  };
157
+ // Deprecation guidance for --no-pr
158
+ if (flags['no-pr']) {
159
+ this.warn('--no-pr is deprecated. Omit --create-pr instead (PR creation is off by default). --no-pr will continue to work.');
160
+ }
156
161
  // Parse ticket IDs from args (everything after flags)
157
162
  const ticketIdArgs = argv;
158
163
  // Try to infer project from ticket IDs if provided
@@ -294,6 +299,7 @@ export default class WorkSpawn extends PMOCommand {
294
299
  else if (allFlagsProvided && !flags.yes) {
295
300
  // All flags provided but no --yes: return confirmation_needed with plan
296
301
  const metadata = createMetadata('work spawn', flags);
302
+ metadata.resolvedPRMode = flags['create-pr'] ? 'create-pr' : 'no-pr';
297
303
  // Build the confirm command with --yes
298
304
  const ticketIds = ticketsToSpawn.map(t => t.id).join(' ');
299
305
  let confirmCmd = `prlt work spawn ${ticketIds}`;
@@ -594,6 +600,7 @@ export default class WorkSpawn extends PMOCommand {
594
600
  // In JSON mode without --yes, return confirmation_needed
595
601
  if (jsonMode && !flags.yes) {
596
602
  const metadata = createMetadata('work spawn', flags);
603
+ metadata.resolvedPRMode = flags['create-pr'] ? 'create-pr' : 'no-pr';
597
604
  const ticketIds = ticketsToSpawn.map(t => t.id).join(' ');
598
605
  let confirmCmd = `prlt work spawn ${ticketIds}`;
599
606
  if (flags.action)
@@ -1394,12 +1401,16 @@ export default class WorkSpawn extends PMOCommand {
1394
1401
  db.close();
1395
1402
  // Output results
1396
1403
  if (jsonMode) {
1397
- // Output JSON execution results
1398
- outputExecutionResultAsJson(executionResults, successCount, failCount, createMetadata('work spawn', flags));
1404
+ // Output JSON execution results with resolved PR mode
1405
+ const spawnMetadata = createMetadata('work spawn', flags);
1406
+ spawnMetadata.resolvedPRMode = flags['create-pr'] ? 'create-pr' : 'no-pr';
1407
+ outputExecutionResultAsJson(executionResults, successCount, failCount, spawnMetadata);
1399
1408
  }
1400
1409
  else {
1410
+ const resolvedPRMode = flags['create-pr'] ? 'create-pr' : 'no-pr';
1401
1411
  this.log('');
1402
1412
  this.log(styles.success(`✓ Spawn results: ${successCount} started, ${failCount} failed`));
1413
+ this.log(styles.muted(` PR mode: ${resolvedPRMode}`));
1403
1414
  }
1404
1415
  }
1405
1416
  catch (error) {