@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.
- package/README.md +37 -2
- package/bin/dev.js +0 -0
- package/dist/commands/agent/auth.d.ts +12 -2
- package/dist/commands/agent/auth.js +128 -4
- package/dist/commands/agent/list.js +16 -7
- package/dist/commands/agent/status.js +32 -4
- package/dist/commands/board/watch.js +6 -0
- package/dist/commands/branch/list.d.ts +1 -0
- package/dist/commands/branch/list.js +43 -12
- package/dist/commands/branch/where.js +9 -19
- package/dist/commands/category/list.d.ts +2 -1
- package/dist/commands/category/list.js +38 -13
- package/dist/commands/{claude.d.ts → claude/index.d.ts} +1 -1
- package/dist/commands/{claude.js → claude/index.js} +12 -12
- package/dist/commands/claude/open.d.ts +13 -0
- package/dist/commands/claude/open.js +175 -0
- package/dist/commands/diet.js +18 -2
- package/dist/commands/docker/logs.js +7 -3
- package/dist/commands/docker/shell.js +6 -0
- package/dist/commands/docker/start.js +20 -4
- package/dist/commands/docker/sync.d.ts +4 -0
- package/dist/commands/docker/sync.js +30 -2
- package/dist/commands/epic/show.d.ts +13 -0
- package/dist/commands/epic/show.js +16 -0
- package/dist/commands/epic/ticket.js +7 -24
- package/dist/commands/epic/view.js +27 -0
- package/dist/commands/execution/config.d.ts +0 -4
- package/dist/commands/execution/config.js +14 -46
- package/dist/commands/execution/index.js +2 -1
- package/dist/commands/execution/logs.js +7 -1
- package/dist/commands/execution/stop.js +2 -1
- package/dist/commands/execution/view.js +30 -26
- package/dist/commands/init.js +2 -19
- package/dist/commands/label/create.js +2 -1
- package/dist/commands/label/delete.js +2 -1
- package/dist/commands/label/group/create.js +2 -1
- package/dist/commands/label/group/list.js +2 -1
- package/dist/commands/label/list.js +2 -1
- package/dist/commands/mcp-server.js +27 -1
- package/dist/commands/phase/template/list.js +2 -1
- package/dist/commands/pmo/init.js +12 -40
- package/dist/commands/project/create.js +3 -4
- package/dist/commands/project/update.js +5 -6
- package/dist/commands/pull.js +24 -0
- package/dist/commands/qa/index.d.ts +54 -0
- package/dist/commands/qa/index.js +762 -0
- package/dist/commands/repo/view.js +2 -8
- package/dist/commands/session/attach.js +4 -4
- package/dist/commands/session/create.d.ts +19 -0
- package/dist/commands/session/create.js +102 -0
- package/dist/commands/session/health.js +4 -23
- package/dist/commands/session/index.js +14 -1
- package/dist/commands/session/list.js +9 -8
- package/dist/commands/session/peek.d.ts +38 -0
- package/dist/commands/session/peek.js +316 -0
- package/dist/commands/session/poke.d.ts +27 -0
- package/dist/commands/session/poke.js +219 -0
- package/dist/commands/spec/view.js +29 -0
- package/dist/commands/template/list.js +2 -1
- package/dist/commands/theme/add-names.d.ts +4 -0
- package/dist/commands/theme/add-names.js +11 -1
- package/dist/commands/theme/create.d.ts +2 -0
- package/dist/commands/theme/create.js +8 -0
- package/dist/commands/ticket/bulk.js +2 -2
- package/dist/commands/ticket/complete.js +2 -2
- package/dist/commands/ticket/create.js +21 -0
- package/dist/commands/ticket/delete.js +8 -0
- package/dist/commands/ticket/edit.js +25 -0
- package/dist/commands/ticket/epic.js +17 -43
- package/dist/commands/ticket/index.js +2 -2
- package/dist/commands/ticket/move.js +25 -2
- package/dist/commands/ticket/resolve.js +3 -4
- package/dist/commands/ticket/show.d.ts +13 -0
- package/dist/commands/ticket/show.js +16 -0
- package/dist/commands/ticket/template/list.js +2 -1
- package/dist/commands/ticket/view.d.ts +0 -1
- package/dist/commands/ticket/view.js +30 -1
- package/dist/commands/work/index.js +4 -0
- package/dist/commands/work/spawn-all.js +1 -1
- package/dist/commands/work/spawn.js +15 -4
- package/dist/commands/work/start.js +186 -103
- package/dist/commands/work/status.d.ts +14 -0
- package/dist/commands/work/status.js +60 -0
- package/dist/commands/work/watch.js +1 -1
- package/dist/commands/workflow/index.js +2 -1
- package/dist/commands/workflow/show.d.ts +13 -0
- package/dist/commands/workflow/show.js +16 -0
- package/dist/commands/workspace/add.js +15 -0
- package/dist/commands/workspace/list.js +2 -1
- package/dist/commands/workspace/prune.js +7 -7
- package/dist/hooks/init.js +10 -2
- package/dist/lib/agents/commands.d.ts +5 -0
- package/dist/lib/agents/commands.js +143 -97
- package/dist/lib/branch/index.d.ts +1 -0
- package/dist/lib/database/drizzle-schema.d.ts +465 -0
- package/dist/lib/database/drizzle-schema.js +53 -0
- package/dist/lib/database/index.d.ts +47 -1
- package/dist/lib/database/index.js +138 -20
- package/dist/lib/execution/config.d.ts +15 -1
- package/dist/lib/execution/config.js +28 -0
- package/dist/lib/execution/runners.d.ts +45 -0
- package/dist/lib/execution/runners.js +187 -26
- package/dist/lib/execution/session-utils.d.ts +16 -1
- package/dist/lib/execution/session-utils.js +71 -4
- package/dist/lib/execution/spawner.js +15 -2
- package/dist/lib/execution/storage.d.ts +6 -1
- package/dist/lib/execution/storage.js +35 -5
- package/dist/lib/execution/types.d.ts +3 -0
- package/dist/lib/mcp/tools/board.js +4 -6
- package/dist/lib/mcp/tools/cli-passthrough.js +25 -6
- package/dist/lib/mcp/tools/epic.js +8 -3
- package/dist/lib/mcp/tools/index.d.ts +1 -0
- package/dist/lib/mcp/tools/index.js +1 -0
- package/dist/lib/mcp/tools/spec.js +1 -1
- package/dist/lib/mcp/tools/ticket.js +11 -9
- package/dist/lib/mcp/tools/tmux.d.ts +16 -0
- package/dist/lib/mcp/tools/tmux.js +182 -0
- package/dist/lib/mcp/tools/work.js +148 -6
- package/dist/lib/mcp/types.d.ts +10 -0
- package/dist/lib/multiline-input.js +2 -1
- package/dist/lib/pmo/base-command.js +4 -4
- package/dist/lib/pmo/schema.d.ts +1 -1
- package/dist/lib/pmo/schema.js +1 -0
- package/dist/lib/pmo/storage/actions.js +1 -1
- package/dist/lib/pmo/storage/base.js +402 -50
- package/dist/lib/pmo/storage/dependencies.d.ts +1 -0
- package/dist/lib/pmo/storage/dependencies.js +11 -3
- package/dist/lib/pmo/storage/epics.js +1 -1
- package/dist/lib/pmo/storage/helpers.d.ts +4 -4
- package/dist/lib/pmo/storage/helpers.js +36 -26
- package/dist/lib/pmo/storage/projects.d.ts +2 -0
- package/dist/lib/pmo/storage/projects.js +207 -119
- package/dist/lib/pmo/storage/specs.d.ts +2 -0
- package/dist/lib/pmo/storage/specs.js +274 -188
- package/dist/lib/pmo/storage/tickets.d.ts +2 -0
- package/dist/lib/pmo/storage/tickets.js +350 -290
- package/dist/lib/pmo/storage/types.d.ts +1 -0
- package/dist/lib/pmo/storage/views.d.ts +2 -0
- package/dist/lib/pmo/storage/views.js +183 -130
- package/dist/lib/prompt-command.d.ts +20 -0
- package/dist/lib/prompt-command.js +38 -2
- package/dist/lib/prompt-json.d.ts +41 -4
- package/dist/lib/prompt-json.js +138 -7
- package/dist/lib/styles.d.ts +37 -0
- package/dist/lib/styles.js +73 -0
- package/oclif.manifest.json +4046 -3385
- package/package.json +11 -6
- 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
|
|
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
|
|
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
|
|
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
|
-
//
|
|
72
|
-
const db = this.storage.db;
|
|
71
|
+
// Helper to get ticket's epic ID via storage layer
|
|
73
72
|
const getTicketEpicId = (ticketId) => {
|
|
74
|
-
const
|
|
75
|
-
return
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
206
|
-
const
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
34
|
+
if (shouldOutputJson(flags)) {
|
|
34
35
|
this.log(JSON.stringify(templates, null, 2));
|
|
35
36
|
return;
|
|
36
37
|
}
|
|
@@ -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: '
|
|
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
|
-
|
|
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) {
|