@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.
- package/dist/commands/action/index.js +2 -2
- package/dist/commands/action/show.js +7 -1
- package/dist/commands/agent/auth.js +1 -1
- package/dist/commands/agent/cleanup.js +6 -6
- package/dist/commands/agent/discover.js +1 -1
- package/dist/commands/agent/remove.js +4 -4
- package/dist/commands/autocomplete/setup.d.ts +2 -2
- package/dist/commands/autocomplete/setup.js +5 -5
- package/dist/commands/branch/create.js +31 -30
- package/dist/commands/branch/list.js +14 -11
- package/dist/commands/branch/validate.js +10 -1
- package/dist/commands/category/create.js +4 -5
- package/dist/commands/category/delete.js +2 -3
- package/dist/commands/category/rename.js +2 -3
- package/dist/commands/claude.d.ts +2 -8
- package/dist/commands/claude.js +26 -26
- package/dist/commands/commit.d.ts +2 -8
- package/dist/commands/commit.js +4 -26
- package/dist/commands/config/index.d.ts +2 -10
- package/dist/commands/config/index.js +8 -34
- package/dist/commands/docker/clean.js +7 -9
- package/dist/commands/docker/index.d.ts +2 -2
- package/dist/commands/docker/index.js +13 -12
- package/dist/commands/docker/list.d.ts +1 -0
- package/dist/commands/docker/list.js +31 -17
- package/dist/commands/docker/status.d.ts +3 -1
- package/dist/commands/docker/status.js +28 -2
- package/dist/commands/docker/sync.js +7 -6
- package/dist/commands/epic/delete.js +4 -5
- package/dist/commands/epic/list.js +17 -2
- package/dist/commands/execution/list.js +25 -17
- package/dist/commands/feedback/submit.d.ts +2 -2
- package/dist/commands/feedback/submit.js +9 -9
- package/dist/commands/link/index.js +2 -2
- package/dist/commands/pmo/init.d.ts +2 -2
- package/dist/commands/pmo/init.js +29 -10
- package/dist/commands/project/spec.js +6 -6
- package/dist/commands/repo/list.js +14 -8
- package/dist/commands/repo/view.js +2 -1
- package/dist/commands/roadmap/list.js +16 -1
- package/dist/commands/session/health.d.ts +29 -0
- package/dist/commands/session/health.js +496 -0
- package/dist/commands/session/index.js +4 -0
- package/dist/commands/session/list.js +15 -8
- package/dist/commands/spec/edit.js +2 -3
- package/dist/commands/staff/add.d.ts +2 -2
- package/dist/commands/staff/add.js +15 -14
- package/dist/commands/staff/index.js +2 -2
- package/dist/commands/staff/list.d.ts +3 -1
- package/dist/commands/staff/list.js +15 -1
- package/dist/commands/staff/remove.js +4 -4
- package/dist/commands/status/index.js +6 -7
- package/dist/commands/template/apply.js +10 -11
- package/dist/commands/template/create.js +18 -17
- package/dist/commands/template/index.d.ts +2 -2
- package/dist/commands/template/index.js +6 -6
- package/dist/commands/template/save.js +8 -7
- package/dist/commands/template/update.js +6 -7
- package/dist/commands/terminal/title.d.ts +2 -26
- package/dist/commands/terminal/title.js +4 -33
- package/dist/commands/theme/index.d.ts +2 -2
- package/dist/commands/theme/index.js +19 -18
- package/dist/commands/theme/list.d.ts +3 -0
- package/dist/commands/theme/list.js +25 -0
- package/dist/commands/theme/set.d.ts +2 -2
- package/dist/commands/theme/set.js +5 -5
- package/dist/commands/ticket/complete.js +4 -1
- package/dist/commands/ticket/create.d.ts +1 -0
- package/dist/commands/ticket/create.js +64 -16
- package/dist/commands/ticket/delete.js +18 -16
- package/dist/commands/ticket/edit.js +22 -14
- package/dist/commands/ticket/epic.js +12 -10
- package/dist/commands/ticket/list.js +24 -5
- package/dist/commands/ticket/move.js +4 -1
- package/dist/commands/ticket/project.js +11 -9
- package/dist/commands/ticket/reassign.js +23 -19
- package/dist/commands/ticket/spec.js +7 -5
- package/dist/commands/ticket/update.js +55 -53
- package/dist/commands/ticket/view.js +4 -2
- package/dist/commands/whoami.d.ts +3 -0
- package/dist/commands/whoami.js +22 -4
- package/dist/commands/work/complete.js +2 -2
- package/dist/commands/work/ready.js +9 -9
- package/dist/commands/work/revise.js +15 -13
- package/dist/commands/work/spawn.js +154 -57
- package/dist/commands/work/start.d.ts +1 -0
- package/dist/commands/work/start.js +299 -177
- package/dist/commands/workspace/prune.d.ts +3 -2
- package/dist/commands/workspace/prune.js +70 -10
- package/dist/hooks/init.js +4 -0
- package/dist/lib/agents/commands.js +4 -0
- package/dist/lib/agents/index.js +12 -0
- package/dist/lib/execution/devcontainer.d.ts +4 -0
- package/dist/lib/execution/devcontainer.js +63 -0
- package/dist/lib/mcp/helpers.d.ts +15 -0
- package/dist/lib/mcp/helpers.js +15 -0
- package/dist/lib/mcp/tools/action.js +5 -5
- package/dist/lib/mcp/tools/board.js +7 -7
- package/dist/lib/mcp/tools/category.js +5 -5
- package/dist/lib/mcp/tools/cli-passthrough.js +30 -30
- package/dist/lib/mcp/tools/epic.js +8 -8
- package/dist/lib/mcp/tools/phase.js +7 -7
- package/dist/lib/mcp/tools/project.js +10 -10
- package/dist/lib/mcp/tools/roadmap.js +7 -7
- package/dist/lib/mcp/tools/spec.js +9 -9
- package/dist/lib/mcp/tools/status.js +6 -6
- package/dist/lib/mcp/tools/template.js +6 -6
- package/dist/lib/mcp/tools/ticket.js +19 -19
- package/dist/lib/mcp/tools/view.js +4 -4
- package/dist/lib/mcp/tools/work.js +6 -6
- package/dist/lib/mcp/tools/workflow.js +5 -5
- package/dist/lib/pmo/index.js +4 -0
- package/dist/lib/pmo/storage/base.js +49 -0
- package/dist/lib/pr/index.d.ts +9 -0
- package/dist/lib/pr/index.js +101 -14
- package/dist/lib/prompt-command.d.ts +3 -0
- package/dist/lib/prompt-json.d.ts +72 -1
- package/dist/lib/prompt-json.js +46 -0
- package/dist/lib/repos/index.js +4 -0
- package/dist/lib/string-utils.d.ts +10 -0
- package/dist/lib/string-utils.js +16 -0
- package/oclif.manifest.json +594 -449
- package/package.json +3 -2
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
* MCP Template Tools
|
|
3
3
|
*/
|
|
4
4
|
import { z } from 'zod';
|
|
5
|
-
import { errorResponse } from '../helpers.js';
|
|
5
|
+
import { errorResponse, strictTool } from '../helpers.js';
|
|
6
6
|
export function registerTemplateTools(server, ctx) {
|
|
7
|
-
server
|
|
7
|
+
strictTool(server, 'ticket_template_list', 'List ticket templates', { include_builtin: z.boolean().optional() }, async (params) => {
|
|
8
8
|
try {
|
|
9
9
|
const templates = await ctx.storage.listTicketTemplates({
|
|
10
10
|
isBuiltin: params.include_builtin ? undefined : false,
|
|
@@ -28,7 +28,7 @@ export function registerTemplateTools(server, ctx) {
|
|
|
28
28
|
return errorResponse(error);
|
|
29
29
|
}
|
|
30
30
|
});
|
|
31
|
-
server
|
|
31
|
+
strictTool(server, 'ticket_template_show', 'Get ticket template details', { id: z.string().describe('Template ID') }, async (params) => {
|
|
32
32
|
try {
|
|
33
33
|
const template = await ctx.storage.getTicketTemplate(params.id);
|
|
34
34
|
if (!template)
|
|
@@ -44,7 +44,7 @@ export function registerTemplateTools(server, ctx) {
|
|
|
44
44
|
return errorResponse(error);
|
|
45
45
|
}
|
|
46
46
|
});
|
|
47
|
-
server
|
|
47
|
+
strictTool(server, 'ticket_template_create', 'Create a ticket template', {
|
|
48
48
|
name: z.string().describe('Template name'),
|
|
49
49
|
description: z.string().optional(),
|
|
50
50
|
title_pattern: z.string().optional(),
|
|
@@ -72,7 +72,7 @@ export function registerTemplateTools(server, ctx) {
|
|
|
72
72
|
return errorResponse(error);
|
|
73
73
|
}
|
|
74
74
|
});
|
|
75
|
-
server
|
|
75
|
+
strictTool(server, 'ticket_template_create_from_ticket', 'Create template from existing ticket', {
|
|
76
76
|
ticket_id: z.string().describe('Ticket ID'),
|
|
77
77
|
name: z.string().describe('Template name'),
|
|
78
78
|
description: z.string().optional(),
|
|
@@ -90,7 +90,7 @@ export function registerTemplateTools(server, ctx) {
|
|
|
90
90
|
return errorResponse(error);
|
|
91
91
|
}
|
|
92
92
|
});
|
|
93
|
-
server
|
|
93
|
+
strictTool(server, 'ticket_template_delete', 'Delete a ticket template', { id: z.string().describe('Template ID') }, async (params) => {
|
|
94
94
|
try {
|
|
95
95
|
await ctx.storage.deleteTicketTemplate(params.id);
|
|
96
96
|
return {
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
* MCP Ticket Tools
|
|
3
3
|
*/
|
|
4
4
|
import { z } from 'zod';
|
|
5
|
-
import { formatTicket, formatTicketFull, errorResponse } from '../helpers.js';
|
|
5
|
+
import { formatTicket, formatTicketFull, errorResponse, strictTool } from '../helpers.js';
|
|
6
6
|
export function registerTicketTools(server, ctx) {
|
|
7
|
-
server
|
|
7
|
+
strictTool(server, 'ticket_list', 'List tickets with optional filters', {
|
|
8
8
|
project: z.string().optional().describe('Project ID'),
|
|
9
9
|
column: z.string().optional().describe('Filter by column/status'),
|
|
10
10
|
priority: z.enum(['P0', 'P1', 'P2', 'P3']).optional().describe('Filter by priority'),
|
|
@@ -56,7 +56,7 @@ export function registerTicketTools(server, ctx) {
|
|
|
56
56
|
return errorResponse(error);
|
|
57
57
|
}
|
|
58
58
|
});
|
|
59
|
-
server
|
|
59
|
+
strictTool(server, 'ticket_create', 'Create a new ticket', {
|
|
60
60
|
title: z.string().describe('Ticket title (required)'),
|
|
61
61
|
project: z.string().optional().describe('Project ID'),
|
|
62
62
|
description: z.string().optional().describe('Ticket description'),
|
|
@@ -100,7 +100,7 @@ export function registerTicketTools(server, ctx) {
|
|
|
100
100
|
return errorResponse(error);
|
|
101
101
|
}
|
|
102
102
|
});
|
|
103
|
-
server
|
|
103
|
+
strictTool(server, 'ticket_show', 'Get detailed ticket information', { id: z.string().describe('Ticket ID') }, async (params) => {
|
|
104
104
|
try {
|
|
105
105
|
const ticket = await ctx.storage.getTicket(params.id);
|
|
106
106
|
if (!ticket)
|
|
@@ -116,7 +116,7 @@ export function registerTicketTools(server, ctx) {
|
|
|
116
116
|
return errorResponse(error);
|
|
117
117
|
}
|
|
118
118
|
});
|
|
119
|
-
server
|
|
119
|
+
strictTool(server, 'ticket_update', 'Update a ticket', {
|
|
120
120
|
id: z.string().describe('Ticket ID'),
|
|
121
121
|
title: z.string().optional().describe('New title'),
|
|
122
122
|
description: z.string().optional().describe('New description'),
|
|
@@ -154,7 +154,7 @@ export function registerTicketTools(server, ctx) {
|
|
|
154
154
|
return errorResponse(error);
|
|
155
155
|
}
|
|
156
156
|
});
|
|
157
|
-
server
|
|
157
|
+
strictTool(server, 'ticket_move', 'Move ticket to a different column/status', {
|
|
158
158
|
id: z.string().describe('Ticket ID'),
|
|
159
159
|
column: z.string().describe('Target column/status'),
|
|
160
160
|
position: z.number().optional().describe('Position in column'),
|
|
@@ -175,7 +175,7 @@ export function registerTicketTools(server, ctx) {
|
|
|
175
175
|
return errorResponse(error);
|
|
176
176
|
}
|
|
177
177
|
});
|
|
178
|
-
server
|
|
178
|
+
strictTool(server, 'ticket_delete', 'Delete a ticket', { id: z.string().describe('Ticket ID') }, async (params) => {
|
|
179
179
|
try {
|
|
180
180
|
await ctx.storage.deleteTicket(params.id);
|
|
181
181
|
return {
|
|
@@ -189,7 +189,7 @@ export function registerTicketTools(server, ctx) {
|
|
|
189
189
|
return errorResponse(error);
|
|
190
190
|
}
|
|
191
191
|
});
|
|
192
|
-
server
|
|
192
|
+
strictTool(server, 'ticket_move_to_project', 'Move ticket to a different project', {
|
|
193
193
|
id: z.string().describe('Ticket ID'),
|
|
194
194
|
project: z.string().describe('Target project ID'),
|
|
195
195
|
}, async (params) => {
|
|
@@ -206,7 +206,7 @@ export function registerTicketTools(server, ctx) {
|
|
|
206
206
|
return errorResponse(error);
|
|
207
207
|
}
|
|
208
208
|
});
|
|
209
|
-
server
|
|
209
|
+
strictTool(server, 'ticket_add_subtask', 'Add a subtask to a ticket', {
|
|
210
210
|
ticket_id: z.string().describe('Ticket ID'),
|
|
211
211
|
title: z.string().describe('Subtask title'),
|
|
212
212
|
}, async (params) => {
|
|
@@ -223,7 +223,7 @@ export function registerTicketTools(server, ctx) {
|
|
|
223
223
|
return errorResponse(error);
|
|
224
224
|
}
|
|
225
225
|
});
|
|
226
|
-
server
|
|
226
|
+
strictTool(server, 'ticket_toggle_subtask', 'Toggle subtask completion', {
|
|
227
227
|
ticket_id: z.string().describe('Ticket ID'),
|
|
228
228
|
subtask_id: z.string().describe('Subtask ID'),
|
|
229
229
|
}, async (params) => {
|
|
@@ -240,7 +240,7 @@ export function registerTicketTools(server, ctx) {
|
|
|
240
240
|
return errorResponse(error);
|
|
241
241
|
}
|
|
242
242
|
});
|
|
243
|
-
server
|
|
243
|
+
strictTool(server, 'ticket_remove_subtask', 'Remove a subtask', {
|
|
244
244
|
ticket_id: z.string().describe('Ticket ID'),
|
|
245
245
|
subtask_id: z.string().describe('Subtask ID'),
|
|
246
246
|
}, async (params) => {
|
|
@@ -257,7 +257,7 @@ export function registerTicketTools(server, ctx) {
|
|
|
257
257
|
return errorResponse(error);
|
|
258
258
|
}
|
|
259
259
|
});
|
|
260
|
-
server
|
|
260
|
+
strictTool(server, 'ticket_add_acceptance_criterion', 'Add acceptance criterion to a ticket', {
|
|
261
261
|
ticket_id: z.string().describe('Ticket ID'),
|
|
262
262
|
criterion: z.string().describe('Acceptance criterion text'),
|
|
263
263
|
}, async (params) => {
|
|
@@ -274,7 +274,7 @@ export function registerTicketTools(server, ctx) {
|
|
|
274
274
|
return errorResponse(error);
|
|
275
275
|
}
|
|
276
276
|
});
|
|
277
|
-
server
|
|
277
|
+
strictTool(server, 'ticket_remove_acceptance_criterion', 'Remove acceptance criterion', {
|
|
278
278
|
ticket_id: z.string().describe('Ticket ID'),
|
|
279
279
|
criterion_id: z.string().describe('Criterion ID'),
|
|
280
280
|
}, async (params) => {
|
|
@@ -291,7 +291,7 @@ export function registerTicketTools(server, ctx) {
|
|
|
291
291
|
return errorResponse(error);
|
|
292
292
|
}
|
|
293
293
|
});
|
|
294
|
-
server
|
|
294
|
+
strictTool(server, 'ticket_link_to_epic', 'Link ticket to an epic', {
|
|
295
295
|
ticket_id: z.string().describe('Ticket ID'),
|
|
296
296
|
epic_id: z.string().describe('Epic ID'),
|
|
297
297
|
}, async (params) => {
|
|
@@ -308,7 +308,7 @@ export function registerTicketTools(server, ctx) {
|
|
|
308
308
|
return errorResponse(error);
|
|
309
309
|
}
|
|
310
310
|
});
|
|
311
|
-
server
|
|
311
|
+
strictTool(server, 'ticket_unlink_from_epic', 'Unlink ticket from its epic', { ticket_id: z.string().describe('Ticket ID') }, async (params) => {
|
|
312
312
|
try {
|
|
313
313
|
await ctx.storage.unlinkTicketFromEpic(params.ticket_id);
|
|
314
314
|
return {
|
|
@@ -322,7 +322,7 @@ export function registerTicketTools(server, ctx) {
|
|
|
322
322
|
return errorResponse(error);
|
|
323
323
|
}
|
|
324
324
|
});
|
|
325
|
-
server
|
|
325
|
+
strictTool(server, 'ticket_link_to_spec', 'Link ticket to a spec', {
|
|
326
326
|
ticket_id: z.string().describe('Ticket ID'),
|
|
327
327
|
spec_id: z.string().describe('Spec ID'),
|
|
328
328
|
}, async (params) => {
|
|
@@ -339,7 +339,7 @@ export function registerTicketTools(server, ctx) {
|
|
|
339
339
|
return errorResponse(error);
|
|
340
340
|
}
|
|
341
341
|
});
|
|
342
|
-
server
|
|
342
|
+
strictTool(server, 'ticket_add_blocker', 'Add a blocking dependency', {
|
|
343
343
|
ticket_id: z.string().describe('Ticket that will be blocked'),
|
|
344
344
|
blocker_id: z.string().describe('Ticket that blocks'),
|
|
345
345
|
}, async (params) => {
|
|
@@ -356,7 +356,7 @@ export function registerTicketTools(server, ctx) {
|
|
|
356
356
|
return errorResponse(error);
|
|
357
357
|
}
|
|
358
358
|
});
|
|
359
|
-
server
|
|
359
|
+
strictTool(server, 'ticket_remove_blocker', 'Remove a blocking dependency', {
|
|
360
360
|
ticket_id: z.string().describe('Blocked ticket'),
|
|
361
361
|
blocker_id: z.string().describe('Blocking ticket'),
|
|
362
362
|
}, async (params) => {
|
|
@@ -373,7 +373,7 @@ export function registerTicketTools(server, ctx) {
|
|
|
373
373
|
return errorResponse(error);
|
|
374
374
|
}
|
|
375
375
|
});
|
|
376
|
-
server
|
|
376
|
+
strictTool(server, 'ticket_get_blockers', 'Get tickets blocking this ticket', { ticket_id: z.string().describe('Ticket ID') }, async (params) => {
|
|
377
377
|
try {
|
|
378
378
|
const blockers = await ctx.storage.getTicketBlockers(params.ticket_id);
|
|
379
379
|
return {
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
* MCP View Tools
|
|
3
3
|
*/
|
|
4
4
|
import { z } from 'zod';
|
|
5
|
-
import { errorResponse } from '../helpers.js';
|
|
5
|
+
import { errorResponse, strictTool } from '../helpers.js';
|
|
6
6
|
export function registerViewTools(server, ctx) {
|
|
7
|
-
server
|
|
7
|
+
strictTool(server, 'view_list', 'List board views for a project', { project: z.string().optional() }, async (params) => {
|
|
8
8
|
try {
|
|
9
9
|
const views = await ctx.storage.listBoardViews({ projectId: params.project });
|
|
10
10
|
return {
|
|
@@ -27,7 +27,7 @@ export function registerViewTools(server, ctx) {
|
|
|
27
27
|
return errorResponse(error);
|
|
28
28
|
}
|
|
29
29
|
});
|
|
30
|
-
server
|
|
30
|
+
strictTool(server, 'view_create', 'Create a board view', {
|
|
31
31
|
project: z.string().describe('Project ID'),
|
|
32
32
|
name: z.string().describe('View name'),
|
|
33
33
|
description: z.string().optional(),
|
|
@@ -59,7 +59,7 @@ export function registerViewTools(server, ctx) {
|
|
|
59
59
|
return errorResponse(error);
|
|
60
60
|
}
|
|
61
61
|
});
|
|
62
|
-
server
|
|
62
|
+
strictTool(server, 'view_delete', 'Delete a board view', { id: z.string().describe('View ID') }, async (params) => {
|
|
63
63
|
try {
|
|
64
64
|
await ctx.storage.deleteBoardView(params.id);
|
|
65
65
|
return {
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
* MCP Work Tools (Agent workflow)
|
|
3
3
|
*/
|
|
4
4
|
import { z } from 'zod';
|
|
5
|
-
import { formatTicket, errorResponse } from '../helpers.js';
|
|
5
|
+
import { formatTicket, errorResponse, strictTool } from '../helpers.js';
|
|
6
6
|
export function registerWorkTools(server, ctx) {
|
|
7
|
-
server
|
|
7
|
+
strictTool(server, 'work_status', 'Get current work status (in-progress tickets)', {}, async () => {
|
|
8
8
|
try {
|
|
9
9
|
const tickets = await ctx.storage.listTickets(undefined, { allProjects: true });
|
|
10
10
|
const inProgress = tickets.filter((t) => t.statusCategory === 'started' && t.assignee);
|
|
@@ -31,7 +31,7 @@ export function registerWorkTools(server, ctx) {
|
|
|
31
31
|
return errorResponse(error);
|
|
32
32
|
}
|
|
33
33
|
});
|
|
34
|
-
server
|
|
34
|
+
strictTool(server, 'work_start', 'Start working on a ticket (moves to In Progress)', {
|
|
35
35
|
ticket_id: z.string().describe('Ticket ID'),
|
|
36
36
|
assignee: z.string().optional().describe('Who is working'),
|
|
37
37
|
}, async (params) => {
|
|
@@ -60,7 +60,7 @@ export function registerWorkTools(server, ctx) {
|
|
|
60
60
|
return errorResponse(error);
|
|
61
61
|
}
|
|
62
62
|
});
|
|
63
|
-
server
|
|
63
|
+
strictTool(server, 'work_complete', 'Mark ticket complete (moves to Done)', { ticket_id: z.string().describe('Ticket ID') }, async (params) => {
|
|
64
64
|
try {
|
|
65
65
|
const ticket = await ctx.storage.getTicket(params.ticket_id);
|
|
66
66
|
if (!ticket)
|
|
@@ -83,7 +83,7 @@ export function registerWorkTools(server, ctx) {
|
|
|
83
83
|
return errorResponse(error);
|
|
84
84
|
}
|
|
85
85
|
});
|
|
86
|
-
server
|
|
86
|
+
strictTool(server, 'work_ready', 'Mark ticket ready for review', { ticket_id: z.string().describe('Ticket ID') }, async (params) => {
|
|
87
87
|
try {
|
|
88
88
|
const ticket = await ctx.storage.getTicket(params.ticket_id);
|
|
89
89
|
if (!ticket)
|
|
@@ -110,7 +110,7 @@ export function registerWorkTools(server, ctx) {
|
|
|
110
110
|
return errorResponse(error);
|
|
111
111
|
}
|
|
112
112
|
});
|
|
113
|
-
server
|
|
113
|
+
strictTool(server, 'work_revise', 'Send ticket back for revision', { ticket_id: z.string().describe('Ticket ID') }, async (params) => {
|
|
114
114
|
try {
|
|
115
115
|
const ticket = await ctx.storage.getTicket(params.ticket_id);
|
|
116
116
|
if (!ticket)
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
* MCP Workflow Tools
|
|
3
3
|
*/
|
|
4
4
|
import { z } from 'zod';
|
|
5
|
-
import { errorResponse } from '../helpers.js';
|
|
5
|
+
import { errorResponse, strictTool } from '../helpers.js';
|
|
6
6
|
export function registerWorkflowTools(server, ctx) {
|
|
7
|
-
server
|
|
7
|
+
strictTool(server, 'workflow_list', 'List all workflows', { include_builtin: z.boolean().optional() }, async (params) => {
|
|
8
8
|
try {
|
|
9
9
|
const workflows = await ctx.storage.listWorkflows({
|
|
10
10
|
isBuiltin: params.include_builtin ? undefined : false,
|
|
@@ -28,7 +28,7 @@ export function registerWorkflowTools(server, ctx) {
|
|
|
28
28
|
return errorResponse(error);
|
|
29
29
|
}
|
|
30
30
|
});
|
|
31
|
-
server
|
|
31
|
+
strictTool(server, 'workflow_show', 'Get workflow details with statuses', { id: z.string().describe('Workflow ID') }, async (params) => {
|
|
32
32
|
try {
|
|
33
33
|
const workflow = await ctx.storage.getWorkflow(params.id);
|
|
34
34
|
if (!workflow)
|
|
@@ -57,7 +57,7 @@ export function registerWorkflowTools(server, ctx) {
|
|
|
57
57
|
return errorResponse(error);
|
|
58
58
|
}
|
|
59
59
|
});
|
|
60
|
-
server
|
|
60
|
+
strictTool(server, 'workflow_create', 'Create a new workflow', {
|
|
61
61
|
name: z.string().describe('Workflow name'),
|
|
62
62
|
description: z.string().optional(),
|
|
63
63
|
statuses: z.array(z.string()).optional().describe('Status names'),
|
|
@@ -78,7 +78,7 @@ export function registerWorkflowTools(server, ctx) {
|
|
|
78
78
|
return errorResponse(error);
|
|
79
79
|
}
|
|
80
80
|
});
|
|
81
|
-
server
|
|
81
|
+
strictTool(server, 'workflow_delete', 'Delete a workflow', { id: z.string().describe('Workflow ID') }, async (params) => {
|
|
82
82
|
try {
|
|
83
83
|
await ctx.storage.deleteWorkflow(params.id);
|
|
84
84
|
return {
|
package/dist/lib/pmo/index.js
CHANGED
|
@@ -6,6 +6,7 @@ import { SQLiteStorage } from './storage-sqlite.js';
|
|
|
6
6
|
import { createSpecFolders } from './create-spec-folders.js';
|
|
7
7
|
import { slugify } from './utils.js';
|
|
8
8
|
import { createDevcontainerConfig } from '../execution/devcontainer.js';
|
|
9
|
+
import { getGitIdentity } from '../pr/index.js';
|
|
9
10
|
// Re-export new PMO modules
|
|
10
11
|
export * from './types.js';
|
|
11
12
|
export * from './utils.js';
|
|
@@ -383,10 +384,13 @@ ${columns.join(', ')}
|
|
|
383
384
|
// Create devcontainer for separate PMO (it's its own repo)
|
|
384
385
|
if (location === 'separate') {
|
|
385
386
|
console.log(chalk.blue('Creating devcontainer config for PMO...'));
|
|
387
|
+
const gitIdentity = getGitIdentity();
|
|
386
388
|
createDevcontainerConfig({
|
|
387
389
|
agentName: 'pmo',
|
|
388
390
|
agentDir: pmoPath,
|
|
389
391
|
repoWorktrees: [], // PMO is the repo itself, no nested worktrees
|
|
392
|
+
gitUserName: gitIdentity.name || undefined,
|
|
393
|
+
gitUserEmail: gitIdentity.email || undefined,
|
|
390
394
|
});
|
|
391
395
|
console.log(chalk.green(' ✓ Devcontainer config created'));
|
|
392
396
|
}
|
|
@@ -218,6 +218,55 @@ export function runMigrations(db) {
|
|
|
218
218
|
// Table may already be dropped
|
|
219
219
|
}
|
|
220
220
|
}
|
|
221
|
+
// Migration: Reassign orphaned tickets (TKT-940)
|
|
222
|
+
// Tickets with project_id that doesn't match any existing project are "orphaned".
|
|
223
|
+
// This can happen when a 'default' project never existed or was deleted.
|
|
224
|
+
// NOTE: This runs on every init but is idempotent — if no orphaned tickets exist, it's a no-op.
|
|
225
|
+
// Kept as a runtime check rather than a one-time migration since orphaned tickets could
|
|
226
|
+
// reappear if projects are deleted in the future.
|
|
227
|
+
if (tableExists(T.tickets) && tableExists(T.projects)) {
|
|
228
|
+
try {
|
|
229
|
+
// Find orphaned tickets (project_id doesn't match any project)
|
|
230
|
+
const orphanedTickets = db.prepare(`
|
|
231
|
+
SELECT t.id, t.project_id
|
|
232
|
+
FROM ${T.tickets} t
|
|
233
|
+
LEFT JOIN ${T.projects} p ON t.project_id = p.id
|
|
234
|
+
WHERE p.id IS NULL
|
|
235
|
+
`).all();
|
|
236
|
+
if (orphanedTickets.length > 0) {
|
|
237
|
+
// Get the first available project to reassign to
|
|
238
|
+
const firstProject = db.prepare(`
|
|
239
|
+
SELECT id FROM ${T.projects} ORDER BY created_at ASC LIMIT 1
|
|
240
|
+
`).get();
|
|
241
|
+
if (firstProject) {
|
|
242
|
+
// Get the default status for the target project's workflow
|
|
243
|
+
const project = db.prepare(`
|
|
244
|
+
SELECT workflow_id FROM ${T.projects} WHERE id = ?
|
|
245
|
+
`).get(firstProject.id);
|
|
246
|
+
const workflowId = project?.workflow_id || 'default';
|
|
247
|
+
const defaultStatus = db.prepare(`
|
|
248
|
+
SELECT id FROM ${T.workflow_statuses}
|
|
249
|
+
WHERE workflow_id = ? AND is_default = 1
|
|
250
|
+
`).get(workflowId);
|
|
251
|
+
// Reassign orphaned tickets to the first project
|
|
252
|
+
const updateStmt = defaultStatus
|
|
253
|
+
? db.prepare(`UPDATE ${T.tickets} SET project_id = ?, status_id = COALESCE(status_id, ?) WHERE id = ?`)
|
|
254
|
+
: db.prepare(`UPDATE ${T.tickets} SET project_id = ? WHERE id = ?`);
|
|
255
|
+
for (const ticket of orphanedTickets) {
|
|
256
|
+
if (defaultStatus) {
|
|
257
|
+
updateStmt.run(firstProject.id, defaultStatus.id, ticket.id);
|
|
258
|
+
}
|
|
259
|
+
else {
|
|
260
|
+
updateStmt.run(firstProject.id, ticket.id);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
catch {
|
|
267
|
+
// Non-critical migration - don't fail initialization
|
|
268
|
+
}
|
|
269
|
+
}
|
|
221
270
|
}
|
|
222
271
|
/**
|
|
223
272
|
* Seed built-in workflows from BUILTIN_TEMPLATES (single source of truth).
|
package/dist/lib/pr/index.d.ts
CHANGED
|
@@ -44,6 +44,11 @@ export declare function getGHUsername(): string | null;
|
|
|
44
44
|
* Check if GH_TOKEN or GITHUB_TOKEN is set in environment.
|
|
45
45
|
*/
|
|
46
46
|
export declare function isGHTokenInEnv(): boolean;
|
|
47
|
+
export interface GitIdentity {
|
|
48
|
+
name: string | null;
|
|
49
|
+
email: string | null;
|
|
50
|
+
}
|
|
51
|
+
export declare function getGitIdentity(cwd?: string): GitIdentity;
|
|
47
52
|
/**
|
|
48
53
|
* Get the GitHub repository from git remote.
|
|
49
54
|
* Returns format: owner/repo
|
|
@@ -51,6 +56,8 @@ export declare function isGHTokenInEnv(): boolean;
|
|
|
51
56
|
export declare function getGitHubRepo(cwd?: string): string | null;
|
|
52
57
|
/**
|
|
53
58
|
* Get the default base branch (main or master).
|
|
59
|
+
* Prefers origin/main over local main to avoid stale local branches
|
|
60
|
+
* in agent worktrees where local main is never updated.
|
|
54
61
|
*/
|
|
55
62
|
export declare function getDefaultBaseBranch(cwd?: string): string;
|
|
56
63
|
/**
|
|
@@ -71,6 +78,8 @@ export declare function pushBranch(branch: string, cwd?: string): boolean;
|
|
|
71
78
|
export declare function hasUnpushedCommits(branch: string, cwd?: string): boolean;
|
|
72
79
|
/**
|
|
73
80
|
* Get the commit log between base and head.
|
|
81
|
+
* Prefers origin/${base} over local ${base} to avoid stale local branches
|
|
82
|
+
* in agent worktrees where local main/master is never updated.
|
|
74
83
|
*/
|
|
75
84
|
export declare function getCommitLog(base: string, cwd?: string): string[];
|
|
76
85
|
/**
|
package/dist/lib/pr/index.js
CHANGED
|
@@ -53,6 +53,75 @@ export function getGHUsername() {
|
|
|
53
53
|
export function isGHTokenInEnv() {
|
|
54
54
|
return !!(process.env.GH_TOKEN || process.env.GITHUB_TOKEN);
|
|
55
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* Detect the user's git identity for commit attribution.
|
|
58
|
+
* Tries GitHub CLI first (gh api user), falls back to git config.
|
|
59
|
+
* Results are memoized so subprocess calls only run once per process.
|
|
60
|
+
*/
|
|
61
|
+
let _cachedGitIdentity;
|
|
62
|
+
export function getGitIdentity(cwd) {
|
|
63
|
+
if (_cachedGitIdentity)
|
|
64
|
+
return _cachedGitIdentity;
|
|
65
|
+
let name = null;
|
|
66
|
+
let email = null;
|
|
67
|
+
// Method 1: Try gh api user (most reliable for GitHub identity)
|
|
68
|
+
try {
|
|
69
|
+
const userJson = execSync('gh api user', {
|
|
70
|
+
encoding: 'utf-8',
|
|
71
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
72
|
+
});
|
|
73
|
+
const user = JSON.parse(userJson);
|
|
74
|
+
name = user.name || user.login || null;
|
|
75
|
+
email = user.email || null;
|
|
76
|
+
// If no public email, try GitHub emails API for primary email
|
|
77
|
+
if (!email) {
|
|
78
|
+
try {
|
|
79
|
+
const emailsJson = execSync('gh api user/emails', {
|
|
80
|
+
encoding: 'utf-8',
|
|
81
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
82
|
+
});
|
|
83
|
+
const emails = JSON.parse(emailsJson);
|
|
84
|
+
const primary = emails.find(e => e.primary);
|
|
85
|
+
if (primary) {
|
|
86
|
+
email = primary.email;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
// emails API may not be accessible with current token scope
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
// gh not available or not authenticated
|
|
96
|
+
}
|
|
97
|
+
// Method 2: Fall back to git config
|
|
98
|
+
if (!name) {
|
|
99
|
+
try {
|
|
100
|
+
name = execSync('git config user.name', {
|
|
101
|
+
cwd,
|
|
102
|
+
encoding: 'utf-8',
|
|
103
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
104
|
+
}).trim() || null;
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
// git config not set
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
if (!email) {
|
|
111
|
+
try {
|
|
112
|
+
email = execSync('git config user.email', {
|
|
113
|
+
cwd,
|
|
114
|
+
encoding: 'utf-8',
|
|
115
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
116
|
+
}).trim() || null;
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
// git config not set
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
_cachedGitIdentity = { name, email };
|
|
123
|
+
return _cachedGitIdentity;
|
|
124
|
+
}
|
|
56
125
|
// =============================================================================
|
|
57
126
|
// Git Remote Detection
|
|
58
127
|
// =============================================================================
|
|
@@ -85,29 +154,31 @@ export function getGitHubRepo(cwd) {
|
|
|
85
154
|
}
|
|
86
155
|
/**
|
|
87
156
|
* Get the default base branch (main or master).
|
|
157
|
+
* Prefers origin/main over local main to avoid stale local branches
|
|
158
|
+
* in agent worktrees where local main is never updated.
|
|
88
159
|
*/
|
|
89
160
|
export function getDefaultBaseBranch(cwd) {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
// Fall back to 'master'
|
|
161
|
+
// Check origin/main first (most reliable in worktree environments)
|
|
162
|
+
// then local main, then origin/master, then local master
|
|
163
|
+
const candidates = [
|
|
164
|
+
{ ref: 'origin/main', name: 'main' },
|
|
165
|
+
{ ref: 'main', name: 'main' },
|
|
166
|
+
{ ref: 'origin/master', name: 'master' },
|
|
167
|
+
{ ref: 'master', name: 'master' },
|
|
168
|
+
];
|
|
169
|
+
for (const { ref, name } of candidates) {
|
|
100
170
|
try {
|
|
101
|
-
execSync(
|
|
171
|
+
execSync(`git rev-parse --verify ${ref}`, {
|
|
102
172
|
cwd,
|
|
103
173
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
104
174
|
});
|
|
105
|
-
return
|
|
175
|
+
return name;
|
|
106
176
|
}
|
|
107
177
|
catch {
|
|
108
|
-
|
|
178
|
+
// Try next candidate
|
|
109
179
|
}
|
|
110
180
|
}
|
|
181
|
+
return 'main'; // Default to main even if not found
|
|
111
182
|
}
|
|
112
183
|
/**
|
|
113
184
|
* Get the current branch name.
|
|
@@ -173,10 +244,26 @@ export function hasUnpushedCommits(branch, cwd) {
|
|
|
173
244
|
}
|
|
174
245
|
/**
|
|
175
246
|
* Get the commit log between base and head.
|
|
247
|
+
* Prefers origin/${base} over local ${base} to avoid stale local branches
|
|
248
|
+
* in agent worktrees where local main/master is never updated.
|
|
176
249
|
*/
|
|
177
250
|
export function getCommitLog(base, cwd) {
|
|
251
|
+
// Prefer origin/${base} for accurate comparison (local branch may be stale)
|
|
252
|
+
let ref = base;
|
|
253
|
+
if (!base.startsWith('origin/')) {
|
|
254
|
+
try {
|
|
255
|
+
execSync(`git rev-parse --verify origin/${base}`, {
|
|
256
|
+
cwd,
|
|
257
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
258
|
+
});
|
|
259
|
+
ref = `origin/${base}`;
|
|
260
|
+
}
|
|
261
|
+
catch {
|
|
262
|
+
// Fall back to local ref
|
|
263
|
+
}
|
|
264
|
+
}
|
|
178
265
|
try {
|
|
179
|
-
const output = execSync(`git log ${
|
|
266
|
+
const output = execSync(`git log ${ref}..HEAD --oneline`, {
|
|
180
267
|
cwd,
|
|
181
268
|
encoding: 'utf-8',
|
|
182
269
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
@@ -83,6 +83,9 @@ export declare abstract class PromptCommand extends Command {
|
|
|
83
83
|
default?: unknown;
|
|
84
84
|
validate?: (input: unknown) => boolean | string;
|
|
85
85
|
when?: boolean | ((answers: Record<string, unknown>) => boolean);
|
|
86
|
+
filter?: (input: unknown) => unknown;
|
|
87
|
+
pageSize?: number;
|
|
88
|
+
[key: string]: unknown;
|
|
86
89
|
}>, jsonModeConfig?: {
|
|
87
90
|
flags: JsonFlags & Record<string, unknown>;
|
|
88
91
|
commandName: string;
|