@proletariat/cli 0.3.45 → 0.3.47

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 (77) hide show
  1. package/bin/validate-better-sqlite3.cjs +55 -0
  2. package/dist/commands/config/index.js +39 -1
  3. package/dist/commands/linear/auth.d.ts +14 -0
  4. package/dist/commands/linear/auth.js +211 -0
  5. package/dist/commands/linear/import.d.ts +21 -0
  6. package/dist/commands/linear/import.js +260 -0
  7. package/dist/commands/linear/status.d.ts +11 -0
  8. package/dist/commands/linear/status.js +88 -0
  9. package/dist/commands/linear/sync.d.ts +15 -0
  10. package/dist/commands/linear/sync.js +233 -0
  11. package/dist/commands/orchestrator/attach.d.ts +10 -1
  12. package/dist/commands/orchestrator/attach.js +102 -18
  13. package/dist/commands/orchestrator/index.js +22 -7
  14. package/dist/commands/orchestrator/start.d.ts +13 -1
  15. package/dist/commands/orchestrator/start.js +96 -25
  16. package/dist/commands/orchestrator/status.d.ts +1 -0
  17. package/dist/commands/orchestrator/status.js +10 -5
  18. package/dist/commands/orchestrator/stop.d.ts +1 -0
  19. package/dist/commands/orchestrator/stop.js +9 -4
  20. package/dist/commands/session/attach.js +32 -9
  21. package/dist/commands/ticket/link/duplicates.d.ts +15 -0
  22. package/dist/commands/ticket/link/duplicates.js +95 -0
  23. package/dist/commands/ticket/link/index.js +14 -0
  24. package/dist/commands/ticket/link/relates.d.ts +15 -0
  25. package/dist/commands/ticket/link/relates.js +95 -0
  26. package/dist/commands/work/index.js +4 -0
  27. package/dist/commands/work/review.d.ts +45 -0
  28. package/dist/commands/work/review.js +401 -0
  29. package/dist/commands/work/revise.js +4 -3
  30. package/dist/commands/work/spawn.d.ts +5 -0
  31. package/dist/commands/work/spawn.js +195 -14
  32. package/dist/commands/work/start.js +75 -19
  33. package/dist/hooks/init.js +18 -5
  34. package/dist/lib/database/native-validation.d.ts +21 -0
  35. package/dist/lib/database/native-validation.js +49 -0
  36. package/dist/lib/execution/config.d.ts +15 -0
  37. package/dist/lib/execution/config.js +54 -0
  38. package/dist/lib/execution/devcontainer.d.ts +6 -3
  39. package/dist/lib/execution/devcontainer.js +39 -12
  40. package/dist/lib/execution/runners.d.ts +28 -32
  41. package/dist/lib/execution/runners.js +353 -277
  42. package/dist/lib/execution/spawner.js +62 -5
  43. package/dist/lib/execution/types.d.ts +4 -0
  44. package/dist/lib/execution/types.js +3 -0
  45. package/dist/lib/external-issues/adapters.d.ts +26 -0
  46. package/dist/lib/external-issues/adapters.js +251 -0
  47. package/dist/lib/external-issues/index.d.ts +10 -0
  48. package/dist/lib/external-issues/index.js +14 -0
  49. package/dist/lib/external-issues/mapper.d.ts +21 -0
  50. package/dist/lib/external-issues/mapper.js +86 -0
  51. package/dist/lib/external-issues/types.d.ts +144 -0
  52. package/dist/lib/external-issues/types.js +26 -0
  53. package/dist/lib/external-issues/validation.d.ts +34 -0
  54. package/dist/lib/external-issues/validation.js +219 -0
  55. package/dist/lib/linear/client.d.ts +55 -0
  56. package/dist/lib/linear/client.js +254 -0
  57. package/dist/lib/linear/config.d.ts +37 -0
  58. package/dist/lib/linear/config.js +100 -0
  59. package/dist/lib/linear/index.d.ts +11 -0
  60. package/dist/lib/linear/index.js +10 -0
  61. package/dist/lib/linear/mapper.d.ts +67 -0
  62. package/dist/lib/linear/mapper.js +219 -0
  63. package/dist/lib/linear/sync.d.ts +37 -0
  64. package/dist/lib/linear/sync.js +89 -0
  65. package/dist/lib/linear/types.d.ts +139 -0
  66. package/dist/lib/linear/types.js +34 -0
  67. package/dist/lib/mcp/helpers.d.ts +8 -0
  68. package/dist/lib/mcp/helpers.js +10 -0
  69. package/dist/lib/mcp/tools/board.js +63 -11
  70. package/dist/lib/mcp/tools/work.js +36 -0
  71. package/dist/lib/pmo/schema.d.ts +2 -0
  72. package/dist/lib/pmo/schema.js +20 -0
  73. package/dist/lib/pmo/storage/base.js +92 -13
  74. package/dist/lib/pmo/storage/dependencies.js +15 -0
  75. package/dist/lib/prompt-json.d.ts +4 -0
  76. package/oclif.manifest.json +3205 -2537
  77. package/package.json +3 -2
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Linear Integration Types
3
+ *
4
+ * Type definitions for the Linear ↔ PMO integration layer.
5
+ */
6
+ /**
7
+ * Status mapping between Linear state types and PMO state categories.
8
+ */
9
+ export const LINEAR_STATE_TO_PMO_CATEGORY = {
10
+ triage: 'triage',
11
+ backlog: 'backlog',
12
+ unstarted: 'unstarted',
13
+ started: 'started',
14
+ completed: 'completed',
15
+ canceled: 'canceled',
16
+ };
17
+ /**
18
+ * Priority mapping between Linear (1-4) and PMO (P0-P3).
19
+ * Linear: 0=None, 1=Urgent, 2=High, 3=Medium, 4=Low
20
+ * PMO: P0=Critical, P1=High, P2=Medium, P3=Low
21
+ */
22
+ export const LINEAR_PRIORITY_TO_PMO = {
23
+ 0: 'P3', // No priority → Low
24
+ 1: 'P0', // Urgent → Critical
25
+ 2: 'P1', // High → High
26
+ 3: 'P2', // Medium → Medium
27
+ 4: 'P3', // Low → Low
28
+ };
29
+ export const PMO_PRIORITY_TO_LINEAR = {
30
+ P0: 1, // Critical → Urgent
31
+ P1: 2, // High → High
32
+ P2: 3, // Medium → Medium
33
+ P3: 4, // Low → Low
34
+ };
@@ -31,6 +31,14 @@ export declare function formatTicket(t: Ticket): {
31
31
  epicId: string | undefined;
32
32
  position: number | undefined;
33
33
  };
34
+ export declare function formatTicketCompact(t: Ticket): {
35
+ id: string;
36
+ title: string;
37
+ priority: string | undefined;
38
+ category: string | undefined;
39
+ assignee: string | undefined;
40
+ statusName: string | undefined;
41
+ };
34
42
  export declare function formatTicketFull(t: Ticket): {
35
43
  description: string | undefined;
36
44
  subtasks: import("../pmo/types.js").Subtask[];
@@ -32,6 +32,16 @@ export function formatTicket(t) {
32
32
  position: t.position,
33
33
  };
34
34
  }
35
+ export function formatTicketCompact(t) {
36
+ return {
37
+ id: t.id,
38
+ title: t.title,
39
+ priority: t.priority,
40
+ category: t.category,
41
+ assignee: t.assignee,
42
+ statusName: t.statusName,
43
+ };
44
+ }
35
45
  export function formatTicketFull(t) {
36
46
  return {
37
47
  ...formatTicket(t),
@@ -2,9 +2,18 @@
2
2
  * MCP Board Tools
3
3
  */
4
4
  import { z } from 'zod';
5
- import { formatTicket, errorResponse, strictTool } from '../helpers.js';
5
+ import { formatTicketCompact, errorResponse, strictTool } from '../helpers.js';
6
+ /** Status categories considered "done" and excluded by default */
7
+ const DONE_CATEGORIES = new Set(['completed', 'canceled']);
6
8
  export function registerBoardTools(server, ctx) {
7
- strictTool(server, 'board_view', 'Show the kanban board', { project: z.string().optional().describe('Project ID') }, async (params) => {
9
+ strictTool(server, 'board_view', 'Show the kanban board. Returns compact ticket format by default (id, title, priority, category, assignee, statusName). Use ticket_show for full details. Excludes Done/Canceled columns by default.', {
10
+ project: z.string().optional().describe('Project ID'),
11
+ summary: z.boolean().optional().describe('Summary mode: return only column names and ticket counts, no ticket details'),
12
+ column: z.string().optional().describe('Filter to a specific column by name (case-insensitive partial match)'),
13
+ include_done: z.boolean().optional().describe('Include completed/canceled columns (excluded by default)'),
14
+ limit: z.number().int().nonnegative().optional().describe('Max tickets to return per column'),
15
+ offset: z.number().int().nonnegative().optional().describe('Number of tickets to skip per column (for pagination)'),
16
+ }, async (params) => {
8
17
  try {
9
18
  let projectId = params.project;
10
19
  if (!projectId) {
@@ -14,6 +23,40 @@ export function registerBoardTools(server, ctx) {
14
23
  projectId = projects[0].id;
15
24
  }
16
25
  const board = await ctx.storage.getBoard(projectId);
26
+ // Filter out done columns unless explicitly requested
27
+ let columns = board.columns;
28
+ if (!params.include_done) {
29
+ columns = columns.filter((col) => !DONE_CATEGORIES.has(col.status || ''));
30
+ }
31
+ // Filter to specific column by name (case-insensitive partial match)
32
+ if (params.column) {
33
+ const columnFilter = params.column.toLowerCase();
34
+ columns = columns.filter((col) => col.name.toLowerCase().includes(columnFilter));
35
+ }
36
+ // Summary mode: just column names and ticket counts
37
+ if (params.summary) {
38
+ const totalTickets = columns.reduce((sum, col) => sum + col.tickets.length, 0);
39
+ return {
40
+ content: [{
41
+ type: 'text',
42
+ text: JSON.stringify({
43
+ success: true,
44
+ board: {
45
+ id: board.id,
46
+ name: board.name,
47
+ totalTickets,
48
+ columns: columns.map((col) => ({
49
+ name: col.name,
50
+ position: col.position,
51
+ ticketCount: col.tickets.length,
52
+ })),
53
+ updatedAt: board.updatedAt.toISOString(),
54
+ },
55
+ }, null, 2),
56
+ }],
57
+ };
58
+ }
59
+ // Compact format (default): return compact ticket objects with pagination
17
60
  return {
18
61
  content: [{
19
62
  type: 'text',
@@ -22,15 +65,24 @@ export function registerBoardTools(server, ctx) {
22
65
  board: {
23
66
  id: board.id,
24
67
  name: board.name,
25
- columns: board.columns.map((col) => ({
26
- name: col.name,
27
- position: col.position,
28
- ticketCount: col.tickets.length,
29
- tickets: col.tickets.map((t) => ({
30
- ...formatTicket(t),
31
- labels: t.labels,
32
- })),
33
- })),
68
+ columns: columns.map((col) => {
69
+ const total = col.tickets.length;
70
+ let tickets = col.tickets;
71
+ const offset = params.offset || 0;
72
+ if (offset > 0) {
73
+ tickets = tickets.slice(offset);
74
+ }
75
+ if (params.limit !== undefined) {
76
+ tickets = tickets.slice(0, params.limit);
77
+ }
78
+ return {
79
+ name: col.name,
80
+ position: col.position,
81
+ ticketCount: total,
82
+ ...(offset > 0 || params.limit !== undefined ? { showing: tickets.length, offset } : {}),
83
+ tickets: tickets.map((t) => formatTicketCompact(t)),
84
+ };
85
+ }),
34
86
  updatedAt: board.updatedAt.toISOString(),
35
87
  },
36
88
  }, null, 2),
@@ -219,6 +219,42 @@ export function registerWorkTools(server, ctx) {
219
219
  return errorResponse(error);
220
220
  }
221
221
  });
222
+ strictTool(server, 'work_review', 'Run automated review-fix pipeline on a ticket: spawns review agent, checks results, auto-spawns fix agent if issues found, re-reviews until clean or max cycles reached. Requires ticket to have a PR.', {
223
+ ticket_id: z.string().describe('Ticket ID to review'),
224
+ max_cycles: z.number().optional().describe('Maximum review-fix cycles (default: 3)'),
225
+ skip_permissions: z.boolean().optional().describe('Skip permission prompts (default: false)'),
226
+ environment: z.enum(['devcontainer', 'host']).optional().describe('Execution environment (default: devcontainer)'),
227
+ }, async (params) => {
228
+ try {
229
+ const args = [params.ticket_id, '--auto'];
230
+ if (params.max_cycles) {
231
+ args.push('--max-cycles', String(params.max_cycles));
232
+ }
233
+ if (params.skip_permissions) {
234
+ args.push('--skip-permissions');
235
+ }
236
+ if (params.environment === 'host') {
237
+ args.push('--run-on-host');
238
+ }
239
+ const cmd = `prlt work review ${args.join(' ')}`;
240
+ const output = ctx.runCommand(cmd);
241
+ return {
242
+ content: [{
243
+ type: 'text',
244
+ text: JSON.stringify({
245
+ success: true,
246
+ ticketId: params.ticket_id,
247
+ command: cmd,
248
+ output,
249
+ message: `Review pipeline started for ${params.ticket_id}`,
250
+ }, null, 2),
251
+ }],
252
+ };
253
+ }
254
+ catch (error) {
255
+ return errorResponse(error);
256
+ }
257
+ });
222
258
  strictTool(server, 'work_spawn', 'Spawn work on a ticket using the full CLI pipeline (agent selection, Docker build, container creation, branch setup, tmux session). Shells out to "prlt work spawn" — works whenever prlt is installed, no workspace context needed in-process.', {
223
259
  ticket_id: z.string().describe('Ticket ID to spawn work for'),
224
260
  action: z.string().optional().describe('Action to perform (e.g., implement, groom, review, custom). Defaults to implement.'),
@@ -38,6 +38,7 @@ export declare const PMO_TABLES: {
38
38
  readonly label_groups: "pmo_label_groups";
39
39
  readonly labels: "pmo_labels";
40
40
  readonly ticket_labels: "pmo_ticket_labels";
41
+ readonly linear_issue_map: "pmo_linear_issue_map";
41
42
  readonly columns: "pmo_columns";
42
43
  readonly board_tickets: "pmo_board_tickets";
43
44
  readonly statuses: "pmo_statuses";
@@ -79,6 +80,7 @@ export declare const PMO_TABLE_SCHEMAS: {
79
80
  readonly ticket_labels: "\n CREATE TABLE IF NOT EXISTS pmo_ticket_labels (\n ticket_id TEXT NOT NULL REFERENCES pmo_tickets(id) ON DELETE CASCADE,\n label_id TEXT NOT NULL REFERENCES pmo_labels(id) ON DELETE CASCADE,\n PRIMARY KEY (ticket_id, label_id)\n )";
80
81
  readonly roadmaps: "\n CREATE TABLE IF NOT EXISTS pmo_roadmaps (\n id TEXT PRIMARY KEY,\n name TEXT NOT NULL UNIQUE,\n description TEXT,\n is_default INTEGER NOT NULL DEFAULT 0,\n created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n )";
81
82
  readonly roadmap_projects: "\n CREATE TABLE IF NOT EXISTS pmo_roadmap_projects (\n roadmap_id TEXT NOT NULL REFERENCES pmo_roadmaps(id) ON DELETE CASCADE,\n project_id TEXT NOT NULL REFERENCES pmo_projects(id) ON DELETE CASCADE,\n position INTEGER NOT NULL DEFAULT 0,\n created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n PRIMARY KEY (roadmap_id, project_id)\n )";
83
+ readonly linear_issue_map: "\n CREATE TABLE IF NOT EXISTS pmo_linear_issue_map (\n pmo_ticket_id TEXT NOT NULL REFERENCES pmo_tickets(id) ON DELETE CASCADE,\n linear_issue_id TEXT NOT NULL,\n linear_identifier TEXT NOT NULL,\n linear_team_key TEXT NOT NULL,\n linear_url TEXT NOT NULL,\n sync_direction TEXT NOT NULL DEFAULT 'inbound',\n last_synced_at TIMESTAMP,\n created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n PRIMARY KEY (pmo_ticket_id),\n UNIQUE (linear_issue_id)\n )";
82
84
  };
83
85
  export declare const PMO_INDEXES: string;
84
86
  /**
@@ -45,6 +45,8 @@ export const PMO_TABLES = {
45
45
  label_groups: 'pmo_label_groups',
46
46
  labels: 'pmo_labels',
47
47
  ticket_labels: 'pmo_ticket_labels',
48
+ // Linear integration tables
49
+ linear_issue_map: 'pmo_linear_issue_map', // Linear issue ↔ PMO ticket mapping
48
50
  // Legacy tables (deprecated, kept for migration)
49
51
  columns: 'pmo_columns', // DEPRECATED: use workflow_statuses
50
52
  board_tickets: 'pmo_board_tickets', // DEPRECATED: tickets now use status_id directly
@@ -482,6 +484,20 @@ export const PMO_TABLE_SCHEMAS = {
482
484
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
483
485
  PRIMARY KEY (roadmap_id, project_id)
484
486
  )`,
487
+ // Linear integration: issue ↔ PMO ticket mapping
488
+ linear_issue_map: `
489
+ CREATE TABLE IF NOT EXISTS ${PMO_TABLES.linear_issue_map} (
490
+ pmo_ticket_id TEXT NOT NULL REFERENCES ${PMO_TABLES.tickets}(id) ON DELETE CASCADE,
491
+ linear_issue_id TEXT NOT NULL,
492
+ linear_identifier TEXT NOT NULL,
493
+ linear_team_key TEXT NOT NULL,
494
+ linear_url TEXT NOT NULL,
495
+ sync_direction TEXT NOT NULL DEFAULT 'inbound',
496
+ last_synced_at TIMESTAMP,
497
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
498
+ PRIMARY KEY (pmo_ticket_id),
499
+ UNIQUE (linear_issue_id)
500
+ )`,
485
501
  };
486
502
  // =============================================================================
487
503
  // Indexes
@@ -544,6 +560,9 @@ export const PMO_INDEXES = `
544
560
  CREATE INDEX IF NOT EXISTS idx_pmo_labels_builtin ON ${PMO_TABLES.labels}(is_builtin);
545
561
  CREATE INDEX IF NOT EXISTS idx_pmo_ticket_labels_ticket ON ${PMO_TABLES.ticket_labels}(ticket_id);
546
562
  CREATE INDEX IF NOT EXISTS idx_pmo_ticket_labels_label ON ${PMO_TABLES.ticket_labels}(label_id);
563
+ CREATE INDEX IF NOT EXISTS idx_pmo_linear_issue_map_linear_id ON ${PMO_TABLES.linear_issue_map}(linear_issue_id);
564
+ CREATE INDEX IF NOT EXISTS idx_pmo_linear_issue_map_identifier ON ${PMO_TABLES.linear_issue_map}(linear_identifier);
565
+ CREATE INDEX IF NOT EXISTS idx_pmo_linear_issue_map_team ON ${PMO_TABLES.linear_issue_map}(linear_team_key);
547
566
  `;
548
567
  // =============================================================================
549
568
  // Combined Schema
@@ -587,6 +606,7 @@ export const PMO_SCHEMA_SQL = [
587
606
  PMO_TABLE_SCHEMAS.ticket_labels, // Ticket-label junction table
588
607
  PMO_TABLE_SCHEMAS.roadmaps, // Named roadmap definitions
589
608
  PMO_TABLE_SCHEMAS.roadmap_projects, // Roadmap-to-project associations
609
+ PMO_TABLE_SCHEMAS.linear_issue_map, // Linear issue ↔ PMO ticket mapping
590
610
  // Legacy tables (kept for migration, will be dropped after data migrated)
591
611
  PMO_TABLE_SCHEMAS.columns, // DEPRECATED
592
612
  PMO_TABLE_SCHEMAS.board_tickets, // DEPRECATED
@@ -184,7 +184,7 @@ export function runMigrations(db) {
184
184
  const depsColumnNames = new Set(depsColumns.map(c => c.name));
185
185
  if (depsColumnNames.has('blocked_by_ticket_id') && !depsColumnNames.has('depends_on_ticket_id')) {
186
186
  // Old schema detected - migrate to new format
187
- try {
187
+ const migrate = db.transaction(() => {
188
188
  // Create new table with correct schema
189
189
  db.exec(`
190
190
  CREATE TABLE pmo_ticket_dependencies_new (
@@ -205,10 +205,8 @@ export function runMigrations(db) {
205
205
  // Replace old table with new one
206
206
  db.exec(`DROP TABLE ${T.ticket_dependencies}`);
207
207
  db.exec(`ALTER TABLE pmo_ticket_dependencies_new RENAME TO ${T.ticket_dependencies}`);
208
- }
209
- catch {
210
- // Migration may have already been applied partially
211
- }
208
+ });
209
+ migrate();
212
210
  }
213
211
  }
214
212
  // Migration: Convert legacy priority values (URGENT/HIGH/MEDIUM/LOW) to P0-P3
@@ -490,6 +488,46 @@ Example (WRONG - do not do this):
490
488
  ./apps/cli/bin/run.js ticket edit TKT-123 ...
491
489
  \`\`\`
492
490
  `.trim();
491
+ /**
492
+ * prlt CLI command reference sections for action prompts.
493
+ * Each action gets a tailored set of commands relevant to its workflow.
494
+ */
495
+ const PRLT_COMMANDS_COMMON = `
496
+ ## prlt CLI Reference
497
+
498
+ Use these commands to interact with the ticket system. Always use the global \`prlt\` command.
499
+
500
+ | Command | Description |
501
+ |---------|-------------|
502
+ | \`prlt ticket show <id>\` | View full ticket details (description, AC, subtasks, labels) |
503
+ | \`prlt ticket edit <id> [flags]\` | Update ticket fields (see flags below) |
504
+ | \`prlt ticket list\` | List tickets on the board |
505
+ | \`prlt whoami\` | Show current agent/user identity |
506
+
507
+ ### Common \`ticket edit\` flags
508
+ | Flag | Description |
509
+ |------|-------------|
510
+ | \`--title "..."\` | Update title |
511
+ | \`--description "..."\` | Update description |
512
+ | \`--priority P0\\|P1\\|P2\\|P3\` | Set priority |
513
+ | \`--category <type>\` | Set category (feature, bug, refactor, etc.) |
514
+ | \`--add-ac "..."\` | Add acceptance criterion |
515
+ | \`--clear-ac\` | Clear all acceptance criteria |
516
+ | \`--add-subtask "..."\` | Add a subtask |
517
+ | \`--clear-subtasks\` | Clear all subtasks |
518
+ | \`--add-label "..."\` | Add a label |
519
+ | \`--remove-label "..."\` | Remove a label |
520
+ | \`--assignee <name>\` | Set assignee |
521
+ | \`--owner <name>\` | Set owner |`;
522
+ const PRLT_COMMANDS_CODE = `
523
+ | \`prlt commit "message"\` | Create a commit with ticket ID from branch name |
524
+ | \`prlt work ready <id> --pr\` | Mark ticket ready for review and create a PR |`;
525
+ const PRLT_COMMANDS_REVIEW = `
526
+ | \`prlt ticket move <id>\` | Move ticket to a different column |
527
+ | \`prlt ticket complete <id>\` | Mark ticket as complete (move to Done) |`;
528
+ const PRLT_COMMANDS_RESOLVE = `
529
+ | \`prlt ticket resolve <id>\` | Interactive resolution of ambiguity questions |
530
+ | \`prlt work resolve <id>\` | Agent-assisted resolution of ambiguity questions |`;
493
531
  /**
494
532
  * Seed built-in work actions.
495
533
  */
@@ -532,6 +570,15 @@ If NO ambiguities are found, add the \`ready\` label instead.
532
570
 
533
571
  **AI Agent Tip:** When running \`prlt\` commands without all required arguments, use \`--json\` to receive interactive prompts as structured JSON.
534
572
 
573
+ ### Additional prlt Commands
574
+ | Command | Description |
575
+ |---------|-------------|
576
+ | \`prlt ticket show <id>\` | View full ticket details |
577
+ | \`prlt ticket list\` | List tickets on the board |
578
+ | \`prlt whoami\` | Show current agent/user identity |
579
+ | \`prlt ticket resolve <id>\` | Interactive resolution of ambiguity questions |
580
+ | \`prlt work resolve <id>\` | Agent-assisted resolution of ambiguity questions |
581
+
535
582
  ## Ticket Schema Reference
536
583
 
537
584
  | Field | Type | Valid Values | CLI Flag |
@@ -628,7 +675,10 @@ For each question:
628
675
  ## Important
629
676
  - This is an INTERACTIVE session - always ask the human before writing answers
630
677
  - If the ticket has acceptance criteria or subtasks that need updating based on answers, update those too
631
- - After resolving, move the ticket to the Ready column if all questions are answered`,
678
+ - After resolving, move the ticket to the Ready column if all questions are answered
679
+
680
+ ${PRLT_COMMANDS_COMMON}
681
+ ${PRLT_COMMANDS_RESOLVE}`,
632
682
  endPrompt: `After resolving all questions:
633
683
  1. Update the ticket description with answers below each question
634
684
  2. Remove the \`needs-clarification\` label and add \`ready\` label
@@ -671,7 +721,10 @@ prlt commit "implement user validation"
671
721
  git push
672
722
  \`\`\`
673
723
 
674
- When complete, the ticket should be ready for code review.`,
724
+ When complete, the ticket should be ready for code review.
725
+
726
+ ${PRLT_COMMANDS_COMMON}
727
+ ${PRLT_COMMANDS_CODE}`,
675
728
  endPrompt: `When complete:
676
729
  1. **Commit your work** in each repository directory you modified:
677
730
  \`\`\`bash
@@ -717,7 +770,10 @@ Continue working on this ticket from where you left off.
717
770
 
718
771
  \`\`\`bash
719
772
  git add -A && prlt commit "your change" && git push
720
- \`\`\``,
773
+ \`\`\`
774
+
775
+ ${PRLT_COMMANDS_COMMON}
776
+ ${PRLT_COMMANDS_CODE}`,
721
777
  endPrompt: `When complete:
722
778
  1. **Commit your work** in each repository directory you modified:
723
779
  \`\`\`bash
@@ -761,9 +817,15 @@ After reviewing, determine your verdict:
761
817
  - **REQUEST_CHANGES**: There are issues that must be fixed before merging
762
818
  - **COMMENT**: General feedback, no blocking issues but some suggestions
763
819
 
764
- Do NOT modify any code. This is a read-only review.`,
820
+ Do NOT modify any code. Do NOT attempt to fix any issues. This is a read-only review — report your findings only.
821
+ If you identify issues that need fixing, describe them in your review. A separate action (Review & Fix) will handle fixes.
822
+
823
+ ${PRLT_COMMANDS_COMMON}
824
+ ${PRLT_COMMANDS_REVIEW}`,
765
825
  endPrompt: `When you have finished reviewing, post your review on the PR using \`gh pr review\`.
766
826
 
827
+ **CRITICAL: Do NOT modify any code. Do NOT attempt to fix any issues you find. Your ONLY job is to report findings.**
828
+
767
829
  Choose the appropriate command based on your verdict:
768
830
 
769
831
  **If approving:**
@@ -810,7 +872,14 @@ COMMENT - Some suggestions but no blocking issues."
810
872
 
811
873
  Format the body with: what looks good, concerns (if any), suggested improvements (if any), and your verdict.
812
874
 
813
- No commits are needed for code review.`,
875
+ No commits are needed for code review.
876
+
877
+ **After posting your review**, if you found issues that need fixing, log them on the ticket so another action can address them:
878
+ \`\`\`bash
879
+ prlt ticket edit <TICKET_ID> --add-subtask "Fix: <description of issue>"
880
+ \`\`\`
881
+
882
+ **STOP:** After posting your review and logging any findings, your task is complete. Do not take any further actions, do not attempt to fix any issues, do not type additional instructions, and do not continue the conversation. Simply output your summary and use \`/exit\` to end the session.`,
814
883
  suggestedForCategories: ['started', 'completed'],
815
884
  modifiesCode: false,
816
885
  position: 4,
@@ -838,7 +907,11 @@ Review this ticket's implementation thoroughly and fix any issues found:
838
907
 
839
908
  \`\`\`bash
840
909
  git add -A && prlt commit "fix: address code review findings" && git push
841
- \`\`\``,
910
+ \`\`\`
911
+
912
+ ${PRLT_COMMANDS_COMMON}
913
+ ${PRLT_COMMANDS_CODE}
914
+ ${PRLT_COMMANDS_REVIEW}`,
842
915
  endPrompt: `When you have finished reviewing and fixing:
843
916
 
844
917
  1. **If issues were found and fixed**, post a review summary and push your fixes:
@@ -908,7 +981,10 @@ Address the feedback on this ticket's pull request:
908
981
 
909
982
  \`\`\`bash
910
983
  git add -A && prlt commit "fix: address PR review feedback" && git push
911
- \`\`\``,
984
+ \`\`\`
985
+
986
+ ${PRLT_COMMANDS_COMMON}
987
+ ${PRLT_COMMANDS_CODE}`,
912
988
  endPrompt: `After addressing all feedback:
913
989
 
914
990
  1. **Commit your changes**:
@@ -1117,7 +1193,10 @@ Write comprehensive tests for this ticket's implementation:
1117
1193
 
1118
1194
  \`\`\`bash
1119
1195
  git add -A && prlt commit "add tests for X" && git push
1120
- \`\`\``,
1196
+ \`\`\`
1197
+
1198
+ ${PRLT_COMMANDS_COMMON}
1199
+ ${PRLT_COMMANDS_CODE}`,
1121
1200
  endPrompt: `When complete:
1122
1201
  1. **Commit your tests**:
1123
1202
  \`\`\`bash
@@ -58,6 +58,21 @@ export class DependencyStorage {
58
58
  }
59
59
  const result = this.ctx.db.prepare(query).run(...params);
60
60
  if (result.changes === 0) {
61
+ // For relates_to, try the reverse direction since the relationship is symmetric
62
+ const reverseType = dependencyType ?? 'relates_to';
63
+ if (reverseType === 'relates_to') {
64
+ let reverseQuery = `DELETE FROM ${T.ticket_dependencies} WHERE ticket_id = ? AND depends_on_ticket_id = ?`;
65
+ const reverseParams = [dependsOnTicketId, ticketId];
66
+ if (dependencyType) {
67
+ reverseQuery += ' AND dependency_type = ?';
68
+ reverseParams.push(dependencyType);
69
+ }
70
+ const reverseResult = this.ctx.db.prepare(reverseQuery).run(...reverseParams);
71
+ if (reverseResult.changes === 0) {
72
+ throw new PMOError('NOT_FOUND', 'Dependency not found');
73
+ }
74
+ return;
75
+ }
61
76
  throw new PMOError('NOT_FOUND', 'Dependency not found');
62
77
  }
63
78
  }
@@ -88,6 +88,10 @@ export interface OutputMetadata {
88
88
  timestamp?: string;
89
89
  /** Resolved PR mode after flag precedence ('create-pr' | 'no-pr') */
90
90
  resolvedPRMode?: string;
91
+ /** Source of the resolved PR mode (e.g., 'flag --create-pr', 'workspace config', 'interactive prompt') */
92
+ prModeSource?: string;
93
+ /** Warning message when PR creation is disabled for code-modifying actions */
94
+ prWarning?: string;
91
95
  }
92
96
  /**
93
97
  * JSON output when a prompt would be shown