@proletariat/cli 0.3.23 → 0.3.25

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 (235) hide show
  1. package/dist/commands/action/create.js +4 -4
  2. package/dist/commands/action/update.js +3 -3
  3. package/dist/commands/agent/{temp/cleanup.d.ts → cleanup.d.ts} +1 -1
  4. package/dist/commands/agent/{temp/cleanup.js → cleanup.js} +4 -4
  5. package/dist/commands/agent/index.js +8 -8
  6. package/dist/commands/branch/create.js +2 -2
  7. package/dist/commands/epic/activate.js +9 -17
  8. package/dist/commands/epic/archive.js +13 -24
  9. package/dist/commands/epic/create.d.ts +1 -0
  10. package/dist/commands/epic/create.js +46 -8
  11. package/dist/commands/epic/index.js +2 -2
  12. package/dist/commands/epic/move.js +28 -47
  13. package/dist/commands/epic/progress.js +10 -14
  14. package/dist/commands/epic/project.js +42 -59
  15. package/dist/commands/epic/reorder.js +25 -30
  16. package/dist/commands/epic/spec.d.ts +1 -0
  17. package/dist/commands/epic/spec.js +39 -40
  18. package/dist/commands/epic/ticket.d.ts +2 -0
  19. package/dist/commands/epic/ticket.js +63 -37
  20. package/dist/commands/feedback/index.d.ts +10 -0
  21. package/dist/commands/feedback/index.js +60 -0
  22. package/dist/commands/feedback/list.d.ts +12 -0
  23. package/dist/commands/feedback/list.js +126 -0
  24. package/dist/commands/feedback/submit.d.ts +16 -0
  25. package/dist/commands/feedback/submit.js +220 -0
  26. package/dist/commands/{template/phase/delete.d.ts → feedback/view.d.ts} +7 -5
  27. package/dist/commands/feedback/view.js +109 -0
  28. package/dist/commands/gh/index.js +4 -0
  29. package/dist/commands/{epic/link/remove.d.ts → link/create.d.ts} +6 -7
  30. package/dist/commands/link/create.js +141 -0
  31. package/dist/commands/{epic/link/relates.d.ts → link/index.d.ts} +4 -5
  32. package/dist/commands/link/index.js +87 -0
  33. package/dist/commands/{epic/link/duplicates.d.ts → link/list.d.ts} +7 -4
  34. package/dist/commands/link/list.js +182 -0
  35. package/dist/commands/{spec/link → link}/remove.d.ts +4 -5
  36. package/dist/commands/link/remove.js +120 -0
  37. package/dist/commands/mcp-server.d.ts +22 -0
  38. package/dist/commands/mcp-server.js +98 -0
  39. package/dist/commands/phase/create.js +1 -1
  40. package/dist/commands/project/create.d.ts +1 -0
  41. package/dist/commands/project/create.js +38 -4
  42. package/dist/commands/repo/create.d.ts +38 -0
  43. package/dist/commands/repo/create.js +283 -0
  44. package/dist/commands/repo/index.js +7 -0
  45. package/dist/commands/roadmap/add-project.js +9 -22
  46. package/dist/commands/roadmap/create.d.ts +0 -1
  47. package/dist/commands/roadmap/create.js +46 -40
  48. package/dist/commands/roadmap/delete.js +10 -24
  49. package/dist/commands/roadmap/generate.d.ts +1 -0
  50. package/dist/commands/roadmap/generate.js +21 -22
  51. package/dist/commands/roadmap/remove-project.js +14 -34
  52. package/dist/commands/roadmap/reorder.js +19 -26
  53. package/dist/commands/roadmap/update.js +27 -26
  54. package/dist/commands/roadmap/view.js +5 -12
  55. package/dist/commands/session/attach.d.ts +1 -8
  56. package/dist/commands/session/attach.js +93 -59
  57. package/dist/commands/session/list.d.ts +0 -8
  58. package/dist/commands/session/list.js +130 -81
  59. package/dist/commands/spec/create.d.ts +1 -0
  60. package/dist/commands/spec/create.js +44 -3
  61. package/dist/commands/spec/edit.js +63 -33
  62. package/dist/commands/spec/index.js +2 -2
  63. package/dist/commands/{agent/staff → staff}/add.js +10 -10
  64. package/dist/commands/{agent/staff → staff}/index.d.ts +1 -1
  65. package/dist/commands/{agent/staff → staff}/index.js +7 -7
  66. package/dist/commands/{agent/staff → staff}/list.js +3 -3
  67. package/dist/commands/{agent/staff → staff}/remove.d.ts +1 -1
  68. package/dist/commands/{agent/staff → staff}/remove.js +8 -8
  69. package/dist/commands/{template/phase/index.d.ts → support/book.d.ts} +2 -2
  70. package/dist/commands/support/book.js +54 -0
  71. package/dist/commands/{template/ticket/index.d.ts → support/discord.d.ts} +2 -2
  72. package/dist/commands/support/discord.js +54 -0
  73. package/dist/commands/support/docs.d.ts +10 -0
  74. package/dist/commands/support/docs.js +54 -0
  75. package/dist/commands/support/index.d.ts +19 -0
  76. package/dist/commands/support/index.js +81 -0
  77. package/dist/commands/support/issues.d.ts +11 -0
  78. package/dist/commands/support/issues.js +77 -0
  79. package/dist/commands/support/logs.d.ts +18 -0
  80. package/dist/commands/support/logs.js +247 -0
  81. package/dist/commands/{ticket/template → template}/apply.d.ts +8 -6
  82. package/dist/commands/template/apply.js +262 -0
  83. package/dist/commands/{ticket/template → template}/create.d.ts +5 -6
  84. package/dist/commands/template/create.js +238 -0
  85. package/dist/commands/template/index.js +48 -36
  86. package/dist/commands/{ticket/template → template}/save.d.ts +2 -2
  87. package/dist/commands/template/save.js +104 -0
  88. package/dist/commands/{phase/template → template}/update.d.ts +2 -2
  89. package/dist/commands/template/update.js +99 -0
  90. package/dist/commands/{agent/themes → theme}/add-names.d.ts +1 -1
  91. package/dist/commands/{agent/themes → theme}/add-names.js +6 -6
  92. package/dist/commands/{agent/themes → theme}/create.d.ts +1 -1
  93. package/dist/commands/{agent/themes → theme}/create.js +5 -5
  94. package/dist/commands/{agent/themes → theme}/index.d.ts +1 -1
  95. package/dist/commands/{agent/themes → theme}/index.js +10 -10
  96. package/dist/commands/{agent/themes → theme}/list.d.ts +1 -1
  97. package/dist/commands/{agent/themes → theme}/list.js +5 -5
  98. package/dist/commands/{agent/themes → theme}/set.d.ts +1 -1
  99. package/dist/commands/{agent/themes → theme}/set.js +7 -7
  100. package/dist/commands/ticket/create.d.ts +1 -0
  101. package/dist/commands/ticket/create.js +75 -15
  102. package/dist/commands/ticket/edit.js +44 -13
  103. package/dist/commands/ticket/index.js +6 -6
  104. package/dist/commands/ticket/move.d.ts +7 -0
  105. package/dist/commands/ticket/move.js +132 -0
  106. package/dist/commands/work/spawn.d.ts +1 -0
  107. package/dist/commands/work/spawn.js +72 -8
  108. package/dist/commands/work/start.js +6 -0
  109. package/dist/lib/execution/runners.js +21 -17
  110. package/dist/lib/execution/session-utils.d.ts +60 -0
  111. package/dist/lib/execution/session-utils.js +162 -0
  112. package/dist/lib/execution/spawner.d.ts +2 -0
  113. package/dist/lib/execution/spawner.js +42 -0
  114. package/dist/lib/flags/resolver.d.ts +2 -2
  115. package/dist/lib/flags/resolver.js +15 -0
  116. package/dist/lib/init/index.js +18 -0
  117. package/dist/lib/mcp/helpers.d.ts +43 -0
  118. package/dist/lib/mcp/helpers.js +57 -0
  119. package/dist/lib/mcp/index.d.ts +6 -0
  120. package/dist/lib/mcp/index.js +6 -0
  121. package/dist/lib/mcp/tools/action.d.ts +6 -0
  122. package/dist/lib/mcp/tools/action.js +88 -0
  123. package/dist/lib/mcp/tools/board.d.ts +6 -0
  124. package/dist/lib/mcp/tools/board.js +139 -0
  125. package/dist/lib/mcp/tools/category.d.ts +6 -0
  126. package/dist/lib/mcp/tools/category.js +84 -0
  127. package/dist/lib/mcp/tools/cli-passthrough.d.ts +15 -0
  128. package/dist/lib/mcp/tools/cli-passthrough.js +333 -0
  129. package/dist/lib/mcp/tools/epic.d.ts +6 -0
  130. package/dist/lib/mcp/tools/epic.js +178 -0
  131. package/dist/lib/mcp/tools/index.d.ts +18 -0
  132. package/dist/lib/mcp/tools/index.js +19 -0
  133. package/dist/lib/mcp/tools/phase.d.ts +6 -0
  134. package/dist/lib/mcp/tools/phase.js +131 -0
  135. package/dist/lib/mcp/tools/project.d.ts +6 -0
  136. package/dist/lib/mcp/tools/project.js +196 -0
  137. package/dist/lib/mcp/tools/roadmap.d.ts +6 -0
  138. package/dist/lib/mcp/tools/roadmap.js +123 -0
  139. package/dist/lib/mcp/tools/spec.d.ts +6 -0
  140. package/dist/lib/mcp/tools/spec.js +196 -0
  141. package/dist/lib/mcp/tools/status.d.ts +6 -0
  142. package/dist/lib/mcp/tools/status.js +109 -0
  143. package/dist/lib/mcp/tools/template.d.ts +6 -0
  144. package/dist/lib/mcp/tools/template.js +107 -0
  145. package/dist/lib/mcp/tools/ticket.d.ts +6 -0
  146. package/dist/lib/mcp/tools/ticket.js +393 -0
  147. package/dist/lib/mcp/tools/view.d.ts +6 -0
  148. package/dist/lib/mcp/tools/view.js +76 -0
  149. package/dist/lib/mcp/tools/work.d.ts +6 -0
  150. package/dist/lib/mcp/tools/work.js +132 -0
  151. package/dist/lib/mcp/tools/workflow.d.ts +6 -0
  152. package/dist/lib/mcp/tools/workflow.js +95 -0
  153. package/dist/lib/mcp/types.d.ts +17 -0
  154. package/dist/lib/mcp/types.js +4 -0
  155. package/dist/lib/multiline-input.d.ts +63 -0
  156. package/dist/lib/multiline-input.js +360 -0
  157. package/dist/lib/prompt-json.d.ts +57 -6
  158. package/dist/lib/prompt-json.js +45 -0
  159. package/dist/lib/repos/git.d.ts +7 -0
  160. package/dist/lib/repos/git.js +20 -0
  161. package/oclif.manifest.json +3690 -4995
  162. package/package.json +6 -4
  163. package/dist/commands/agent/temp/index.d.ts +0 -14
  164. package/dist/commands/agent/temp/index.js +0 -85
  165. package/dist/commands/agent/temp/list.d.ts +0 -7
  166. package/dist/commands/agent/temp/list.js +0 -108
  167. package/dist/commands/epic/link/block.d.ts +0 -14
  168. package/dist/commands/epic/link/block.js +0 -81
  169. package/dist/commands/epic/link/duplicates.js +0 -68
  170. package/dist/commands/epic/link/index.d.ts +0 -19
  171. package/dist/commands/epic/link/index.js +0 -272
  172. package/dist/commands/epic/link/relates.js +0 -68
  173. package/dist/commands/epic/link/remove.js +0 -93
  174. package/dist/commands/phase/template/apply.d.ts +0 -17
  175. package/dist/commands/phase/template/apply.js +0 -108
  176. package/dist/commands/phase/template/create.d.ts +0 -17
  177. package/dist/commands/phase/template/create.js +0 -104
  178. package/dist/commands/phase/template/delete.d.ts +0 -17
  179. package/dist/commands/phase/template/delete.js +0 -100
  180. package/dist/commands/phase/template/index.d.ts +0 -15
  181. package/dist/commands/phase/template/index.js +0 -130
  182. package/dist/commands/phase/template/list.d.ts +0 -16
  183. package/dist/commands/phase/template/list.js +0 -97
  184. package/dist/commands/phase/template/update.js +0 -89
  185. package/dist/commands/spec/link/depends.d.ts +0 -14
  186. package/dist/commands/spec/link/depends.js +0 -64
  187. package/dist/commands/spec/link/duplicates.d.ts +0 -14
  188. package/dist/commands/spec/link/duplicates.js +0 -63
  189. package/dist/commands/spec/link/index.d.ts +0 -19
  190. package/dist/commands/spec/link/index.js +0 -207
  191. package/dist/commands/spec/link/relates.d.ts +0 -14
  192. package/dist/commands/spec/link/relates.js +0 -63
  193. package/dist/commands/spec/link/remove.js +0 -96
  194. package/dist/commands/template/phase/apply.d.ts +0 -14
  195. package/dist/commands/template/phase/apply.js +0 -43
  196. package/dist/commands/template/phase/create.d.ts +0 -13
  197. package/dist/commands/template/phase/create.js +0 -38
  198. package/dist/commands/template/phase/delete.js +0 -36
  199. package/dist/commands/template/phase/index.js +0 -63
  200. package/dist/commands/template/phase/list.d.ts +0 -11
  201. package/dist/commands/template/phase/list.js +0 -36
  202. package/dist/commands/template/phase/update.d.ts +0 -14
  203. package/dist/commands/template/phase/update.js +0 -43
  204. package/dist/commands/template/ticket/apply.d.ts +0 -17
  205. package/dist/commands/template/ticket/apply.js +0 -60
  206. package/dist/commands/template/ticket/create.d.ts +0 -20
  207. package/dist/commands/template/ticket/create.js +0 -89
  208. package/dist/commands/template/ticket/delete.d.ts +0 -13
  209. package/dist/commands/template/ticket/delete.js +0 -38
  210. package/dist/commands/template/ticket/index.js +0 -63
  211. package/dist/commands/template/ticket/list.d.ts +0 -11
  212. package/dist/commands/template/ticket/list.js +0 -36
  213. package/dist/commands/template/ticket/save.d.ts +0 -15
  214. package/dist/commands/template/ticket/save.js +0 -46
  215. package/dist/commands/ticket/link/block.d.ts +0 -14
  216. package/dist/commands/ticket/link/block.js +0 -96
  217. package/dist/commands/ticket/link/duplicates.d.ts +0 -14
  218. package/dist/commands/ticket/link/duplicates.js +0 -95
  219. package/dist/commands/ticket/link/index.d.ts +0 -19
  220. package/dist/commands/ticket/link/index.js +0 -256
  221. package/dist/commands/ticket/link/relates.d.ts +0 -14
  222. package/dist/commands/ticket/link/relates.js +0 -95
  223. package/dist/commands/ticket/link/remove.d.ts +0 -16
  224. package/dist/commands/ticket/link/remove.js +0 -132
  225. package/dist/commands/ticket/template/apply.js +0 -252
  226. package/dist/commands/ticket/template/create.js +0 -386
  227. package/dist/commands/ticket/template/delete.d.ts +0 -17
  228. package/dist/commands/ticket/template/delete.js +0 -94
  229. package/dist/commands/ticket/template/index.d.ts +0 -15
  230. package/dist/commands/ticket/template/index.js +0 -120
  231. package/dist/commands/ticket/template/list.d.ts +0 -16
  232. package/dist/commands/ticket/template/list.js +0 -112
  233. package/dist/commands/ticket/template/save.js +0 -163
  234. /package/dist/commands/{agent/staff → staff}/add.d.ts +0 -0
  235. /package/dist/commands/{agent/staff → staff}/list.d.ts +0 -0
@@ -1,96 +0,0 @@
1
- import { Args, Flags } from '@oclif/core';
2
- import { autoExportToBoard, PMOCommand, pmoBaseFlags } from '../../../lib/pmo/index.js';
3
- import { styles } from '../../../lib/styles.js';
4
- import { shouldOutputJson, outputErrorAsJson, createMetadata, } from '../../../lib/prompt-json.js';
5
- export default class TicketLinkBlock extends PMOCommand {
6
- static description = 'Add a blocking dependency (ticket is blocked by another)';
7
- static examples = [
8
- '<%= config.bin %> <%= command.id %> TKT-001 TKT-002 # TKT-001 is blocked by TKT-002',
9
- '<%= config.bin %> <%= command.id %> TKT-001 # Interactive selection',
10
- ];
11
- static args = {
12
- id: Args.string({
13
- description: 'Ticket ID that will be blocked',
14
- required: true,
15
- }),
16
- blocker: Args.string({
17
- description: 'Ticket ID that blocks this ticket',
18
- required: false,
19
- }),
20
- };
21
- static flags = {
22
- ...pmoBaseFlags,
23
- json: Flags.boolean({
24
- char: 'm',
25
- aliases: ['machine'],
26
- description: 'Output prompt configuration as JSON (for AI agents/scripts)',
27
- default: false,
28
- }),
29
- };
30
- async execute() {
31
- const { args, flags } = await this.parse(TicketLinkBlock);
32
- // Check if JSON output mode is active
33
- const jsonMode = shouldOutputJson(flags);
34
- // Helper to handle errors in JSON mode
35
- const handleError = (code, message) => {
36
- if (jsonMode) {
37
- outputErrorAsJson(code, message, createMetadata('ticket link block', flags));
38
- this.exit(1);
39
- }
40
- this.error(message);
41
- };
42
- const ticket = await this.storage.getTicket(args.id);
43
- if (!ticket) {
44
- return handleError('TICKET_NOT_FOUND', `Ticket not found: ${args.id}`);
45
- }
46
- let blockerId = args.blocker;
47
- // If no blocker provided, prompt for selection
48
- if (!blockerId) {
49
- const projectId = flags.project;
50
- const allTickets = await this.storage.listTickets(projectId);
51
- const otherTickets = allTickets.filter(t => t.id !== args.id);
52
- if (otherTickets.length === 0) {
53
- if (jsonMode) {
54
- outputErrorAsJson('NO_OTHER_TICKETS', 'No other tickets to create dependency with.', createMetadata('ticket link block', flags));
55
- return;
56
- }
57
- this.log(styles.muted('\nNo other tickets to create dependency with.'));
58
- return;
59
- }
60
- const selected = await this.selectFromList({
61
- message: `Select ticket that blocks ${args.id}:`,
62
- items: otherTickets,
63
- getName: (t) => `${t.id} - ${t.title} (${t.statusName || t.status})`,
64
- getValue: (t) => t.id,
65
- getCommand: (t) => `prlt ticket link block ${args.id} ${t.id}${projectId ? ` -P ${projectId}` : ''} --json`,
66
- jsonMode: jsonMode ? { flags, commandName: 'ticket link block' } : null,
67
- });
68
- if (!selected) {
69
- return;
70
- }
71
- blockerId = selected;
72
- }
73
- const blockerTicket = await this.storage.getTicket(blockerId);
74
- if (!blockerTicket) {
75
- this.error(`Ticket not found: ${blockerId}`);
76
- }
77
- try {
78
- await this.storage.createTicketDependency(args.id, blockerId, 'blocks');
79
- await autoExportToBoard(this.pmoPath, this.storage, (msg) => this.log(styles.muted(msg)));
80
- this.log(styles.success(`\n✅ ${styles.emphasis(args.id)} is blocked by ${styles.emphasis(blockerId)}`));
81
- this.log(styles.muted(` ${ticket.title}`));
82
- this.log(styles.muted(` blocked by: ${blockerTicket.title}`));
83
- }
84
- catch (error) {
85
- if (error instanceof Error) {
86
- if (error.message.includes('already exists')) {
87
- this.error('Dependency already exists');
88
- }
89
- if (error.message.includes('self-dependency')) {
90
- this.error('Cannot create self-dependency');
91
- }
92
- }
93
- throw error;
94
- }
95
- }
96
- }
@@ -1,14 +0,0 @@
1
- import { PMOCommand } from '../../../lib/pmo/index.js';
2
- export default class TicketLinkDuplicates extends PMOCommand {
3
- static description: string;
4
- static examples: string[];
5
- static args: {
6
- id: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
7
- original: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
8
- };
9
- static flags: {
10
- json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
- project: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
- };
13
- execute(): Promise<void>;
14
- }
@@ -1,95 +0,0 @@
1
- import { Args, Flags } from '@oclif/core';
2
- import { autoExportToBoard, PMOCommand, pmoBaseFlags } from '../../../lib/pmo/index.js';
3
- import { styles } from '../../../lib/styles.js';
4
- import { shouldOutputJson, outputErrorAsJson, createMetadata, } from '../../../lib/prompt-json.js';
5
- export default class TicketLinkDuplicates extends PMOCommand {
6
- static description = 'Mark a ticket as duplicate of another';
7
- static examples = [
8
- '<%= config.bin %> <%= command.id %> TKT-001 TKT-002 # TKT-001 duplicates TKT-002',
9
- '<%= config.bin %> <%= command.id %> TKT-001 # Interactive selection',
10
- ];
11
- static args = {
12
- id: Args.string({
13
- description: 'Duplicate ticket ID',
14
- required: true,
15
- }),
16
- original: Args.string({
17
- description: 'Original ticket ID',
18
- required: false,
19
- }),
20
- };
21
- static flags = {
22
- ...pmoBaseFlags,
23
- json: Flags.boolean({
24
- char: 'm',
25
- aliases: ['machine'],
26
- description: 'Output prompt configuration as JSON (for AI agents/scripts)',
27
- default: false,
28
- }),
29
- };
30
- async execute() {
31
- const { args, flags } = await this.parse(TicketLinkDuplicates);
32
- // Check if JSON output mode is active
33
- const jsonMode = shouldOutputJson(flags);
34
- // Helper to handle errors in JSON mode
35
- const handleError = (code, message) => {
36
- if (jsonMode) {
37
- outputErrorAsJson(code, message, createMetadata('ticket link duplicates', flags));
38
- this.exit(1);
39
- }
40
- this.error(message);
41
- };
42
- const ticket = await this.storage.getTicket(args.id);
43
- if (!ticket) {
44
- return handleError('TICKET_NOT_FOUND', `Ticket not found: ${args.id}`);
45
- }
46
- let originalId = args.original;
47
- if (!originalId) {
48
- const projectId = flags.project;
49
- const allTickets = await this.storage.listTickets(projectId);
50
- const otherTickets = allTickets.filter(t => t.id !== args.id);
51
- if (otherTickets.length === 0) {
52
- if (jsonMode) {
53
- outputErrorAsJson('NO_OTHER_TICKETS', 'No other tickets.', createMetadata('ticket link duplicates', flags));
54
- return;
55
- }
56
- this.log(styles.muted('\nNo other tickets.'));
57
- return;
58
- }
59
- const selected = await this.selectFromList({
60
- message: `Select the original ticket (${args.id} is a duplicate of):`,
61
- items: otherTickets,
62
- getName: (t) => `${t.id} - ${t.title} (${t.statusName || t.status})`,
63
- getValue: (t) => t.id,
64
- getCommand: (t) => `prlt ticket link duplicates ${args.id} ${t.id}${projectId ? ` -P ${projectId}` : ''} --json`,
65
- jsonMode: jsonMode ? { flags, commandName: 'ticket link duplicates' } : null,
66
- });
67
- if (!selected) {
68
- return;
69
- }
70
- originalId = selected;
71
- }
72
- const originalTicket = await this.storage.getTicket(originalId);
73
- if (!originalTicket) {
74
- this.error(`Ticket not found: ${originalId}`);
75
- }
76
- try {
77
- await this.storage.createTicketDependency(args.id, originalId, 'duplicates');
78
- await autoExportToBoard(this.pmoPath, this.storage, (msg) => this.log(styles.muted(msg)));
79
- this.log(styles.success(`\n✅ ${styles.emphasis(args.id)} duplicates ${styles.emphasis(originalId)}`));
80
- this.log(styles.muted(` ${ticket.title}`));
81
- this.log(styles.muted(` duplicates: ${originalTicket.title}`));
82
- }
83
- catch (error) {
84
- if (error instanceof Error) {
85
- if (error.message.includes('already exists')) {
86
- this.error('Dependency already exists');
87
- }
88
- if (error.message.includes('self-dependency')) {
89
- this.error('Cannot create self-dependency');
90
- }
91
- }
92
- throw error;
93
- }
94
- }
95
- }
@@ -1,19 +0,0 @@
1
- import { PMOCommand } from '../../../lib/pmo/index.js';
2
- export default class TicketLink extends PMOCommand {
3
- static description: string;
4
- static examples: string[];
5
- static args: {
6
- id: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
7
- };
8
- static flags: {
9
- json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
- blocks: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
- relates: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
- duplicates: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
- all: import("@oclif/core/interfaces").BooleanFlag<boolean>;
14
- project: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
15
- };
16
- execute(): Promise<void>;
17
- private addDependency;
18
- private viewDependencies;
19
- }
@@ -1,256 +0,0 @@
1
- import { Args, Flags } from '@oclif/core';
2
- import { autoExportToBoard, PMOCommand, pmoBaseFlags } from '../../../lib/pmo/index.js';
3
- import { styles } from '../../../lib/styles.js';
4
- import { shouldOutputJson, outputErrorAsJson, createMetadata, } from '../../../lib/prompt-json.js';
5
- export default class TicketLink extends PMOCommand {
6
- static description = 'Manage ticket dependencies (links)';
7
- static examples = [
8
- '<%= config.bin %> <%= command.id %> TKT-001 # List dependencies',
9
- '<%= config.bin %> <%= command.id %> TKT-001 --blocks TKT-002 # TKT-001 is blocked by TKT-002',
10
- '<%= config.bin %> <%= command.id %> TKT-001 --relates TKT-002 # TKT-001 relates to TKT-002',
11
- '<%= config.bin %> <%= command.id %> TKT-001 --duplicates TKT-002',
12
- '<%= config.bin %> <%= command.id %> TKT-001 --all # Show all (blockers + blocking)',
13
- ];
14
- static args = {
15
- id: Args.string({
16
- description: 'Ticket ID',
17
- required: false,
18
- }),
19
- };
20
- static flags = {
21
- ...pmoBaseFlags,
22
- json: Flags.boolean({
23
- char: 'm',
24
- aliases: ['machine'],
25
- description: 'Output prompt configuration as JSON (for AI agents/scripts)',
26
- default: false,
27
- }),
28
- blocks: Flags.string({
29
- char: 'b',
30
- description: 'Add blocking dependency: this ticket is blocked by TARGET',
31
- }),
32
- relates: Flags.string({
33
- char: 'r',
34
- description: 'Add relates_to dependency: this ticket relates to TARGET',
35
- }),
36
- duplicates: Flags.string({
37
- char: 'd',
38
- description: 'Add duplicates dependency: this ticket duplicates TARGET',
39
- }),
40
- all: Flags.boolean({
41
- char: 'a',
42
- description: 'Show all dependencies (blockers and tickets blocked by this)',
43
- default: false,
44
- }),
45
- };
46
- async execute() {
47
- const { args, flags } = await this.parse(TicketLink);
48
- // Check if JSON output mode is active
49
- const jsonMode = shouldOutputJson(flags);
50
- const projectId = flags.project;
51
- let ticketId = args.id;
52
- if (!ticketId) {
53
- const tickets = await this.storage.listTickets(projectId);
54
- if (tickets.length === 0) {
55
- if (jsonMode) {
56
- outputErrorAsJson('NO_TICKETS', 'No tickets found.', createMetadata('ticket link', flags));
57
- return;
58
- }
59
- this.log(styles.muted('\nNo tickets found.'));
60
- return;
61
- }
62
- const selected = await this.selectFromList({
63
- message: 'Select ticket to manage dependencies:',
64
- items: tickets,
65
- getName: (t) => `${t.id} - ${t.title}`,
66
- getValue: (t) => t.id,
67
- getCommand: (t) => `prlt ticket link ${t.id}${projectId ? ` -P ${projectId}` : ''} --json`,
68
- jsonMode: jsonMode ? { flags, commandName: 'ticket link' } : null,
69
- });
70
- if (!selected) {
71
- return;
72
- }
73
- ticketId = selected;
74
- }
75
- const ticket = await this.storage.getTicket(ticketId);
76
- if (!ticket) {
77
- this.error(`Ticket not found: ${ticketId}`);
78
- }
79
- // If a dependency flag is provided, add the dependency directly
80
- if (flags.blocks || flags.relates || flags.duplicates) {
81
- const targetId = flags.blocks || flags.relates || flags.duplicates;
82
- const dependencyType = flags.blocks ? 'blocks' :
83
- flags.relates ? 'relates_to' : 'duplicates';
84
- await this.addDependency(this.storage, this.pmoPath, ticketId, targetId, dependencyType, ticket.title);
85
- return;
86
- }
87
- // Interactive mode: show menu in a loop - user interaction requires sequential processing
88
- let continueLoop = true;
89
- while (continueLoop) {
90
- // eslint-disable-next-line no-await-in-loop
91
- const allTickets = await this.storage.listTickets(projectId);
92
- const otherTickets = allTickets.filter(t => t.id !== ticketId);
93
- const menuChoices = [
94
- { id: 'view', name: 'View dependencies' },
95
- { id: 'blocks', name: 'Add blocking dependency (blocked by...)' },
96
- { id: 'relates_to', name: 'Add relates_to dependency' },
97
- { id: 'duplicates', name: 'Add duplicates dependency' },
98
- { id: 'remove', name: 'Remove dependency' },
99
- { id: 'done', name: 'Done' },
100
- ];
101
- // eslint-disable-next-line no-await-in-loop
102
- const action = await this.selectFromList({
103
- message: `Dependencies for ${ticket.id}:`,
104
- items: menuChoices,
105
- getName: (c) => c.name,
106
- getValue: (c) => c.id,
107
- getCommand: (c) => {
108
- const pFlag = projectId ? ` -P ${projectId}` : '';
109
- switch (c.id) {
110
- case 'view': return `prlt ticket link ${ticketId}${pFlag} --all`;
111
- case 'blocks': return `prlt ticket link block ${ticketId}${pFlag} --json`;
112
- case 'relates_to': return `prlt ticket link relates ${ticketId}${pFlag} --json`;
113
- case 'duplicates': return `prlt ticket link duplicates ${ticketId}${pFlag} --json`;
114
- case 'remove': return `prlt ticket link remove ${ticketId}${pFlag} --json`;
115
- default: return '';
116
- }
117
- },
118
- jsonMode: jsonMode ? { flags, commandName: 'ticket link' } : null,
119
- });
120
- if (action === 'done' || !action) {
121
- continueLoop = false;
122
- continue;
123
- }
124
- if (action === 'view') {
125
- // eslint-disable-next-line no-await-in-loop
126
- await this.viewDependencies(this.storage, ticketId, ticket, flags.all);
127
- continue;
128
- }
129
- if (action === 'remove') {
130
- // eslint-disable-next-line no-await-in-loop
131
- const dependencies = await this.storage.listTicketDependencies(ticketId);
132
- if (dependencies.length === 0) {
133
- this.log(styles.muted('\nNo dependencies to remove.'));
134
- continue;
135
- }
136
- // eslint-disable-next-line no-await-in-loop
137
- const depChoices = await Promise.all(dependencies.map(async (dep) => {
138
- const depTicket = await this.storage.getTicket(dep.dependsOnTicketId);
139
- return {
140
- id: dep.dependsOnTicketId,
141
- name: `${dep.dependsOnTicketId} - ${depTicket?.title || 'Unknown'} (${dep.dependencyType})`,
142
- type: dep.dependencyType
143
- };
144
- }));
145
- // eslint-disable-next-line no-await-in-loop
146
- const selected = await this.selectFromList({
147
- message: 'Select dependency to remove:',
148
- items: depChoices,
149
- getName: (d) => d.name,
150
- getValue: (d) => d.id,
151
- getCommand: (d) => `prlt ticket link remove ${ticketId} ${d.id} --type ${d.type}${projectId ? ` -P ${projectId}` : ''} --json`,
152
- jsonMode: jsonMode ? { flags, commandName: 'ticket link' } : null,
153
- });
154
- if (!selected) {
155
- continue;
156
- }
157
- const selectedDep = depChoices.find(d => d.id === selected);
158
- if (selectedDep) {
159
- // eslint-disable-next-line no-await-in-loop
160
- await this.storage.deleteTicketDependency(ticketId, selected, selectedDep.type);
161
- // eslint-disable-next-line no-await-in-loop
162
- await autoExportToBoard(this.pmoPath, this.storage, (msg) => this.log(styles.muted(msg)));
163
- this.log(styles.success(`\n✅ Removed dependency: ${ticketId} → ${selected}`));
164
- }
165
- continue;
166
- }
167
- // Add dependency
168
- if (otherTickets.length === 0) {
169
- this.log(styles.muted('\nNo other tickets to link to.'));
170
- continue;
171
- }
172
- // eslint-disable-next-line no-await-in-loop
173
- const targetId = await this.selectFromList({
174
- message: `Select ticket that ${ticketId} ${action === 'blocks' ? 'is blocked by' : action === 'relates_to' ? 'relates to' : 'duplicates'}:`,
175
- items: otherTickets,
176
- getName: (t) => `${t.id} - ${t.title}`,
177
- getValue: (t) => t.id,
178
- getCommand: (t) => `prlt ticket link ${action === 'blocks' ? 'block' : action} ${ticketId} ${t.id}${projectId ? ` -P ${projectId}` : ''} --json`,
179
- jsonMode: jsonMode ? { flags, commandName: 'ticket link' } : null,
180
- });
181
- if (targetId) {
182
- // eslint-disable-next-line no-await-in-loop
183
- await this.addDependency(this.storage, this.pmoPath, ticketId, targetId, action, ticket.title);
184
- }
185
- }
186
- }
187
- async addDependency(storage, pmoPath, ticketId, targetId, dependencyType, ticketTitle) {
188
- const targetTicket = await this.storage.getTicket(targetId);
189
- if (!targetTicket) {
190
- this.error(`Ticket not found: ${targetId}`);
191
- }
192
- try {
193
- await this.storage.createTicketDependency(ticketId, targetId, dependencyType);
194
- await autoExportToBoard(this.pmoPath, this.storage, (msg) => this.log(styles.muted(msg)));
195
- const typeLabel = dependencyType === 'blocks' ? 'is blocked by' :
196
- dependencyType === 'relates_to' ? 'relates to' : 'duplicates';
197
- this.log(styles.success(`\n✅ ${styles.emphasis(ticketId)} ${typeLabel} ${styles.emphasis(targetId)}`));
198
- this.log(styles.muted(` ${ticketTitle}`));
199
- this.log(styles.muted(` ${typeLabel} ${targetTicket.title}`));
200
- }
201
- catch (error) {
202
- if (error instanceof Error) {
203
- if (error.message.includes('already exists')) {
204
- this.error('Dependency already exists');
205
- }
206
- if (error.message.includes('self-dependency')) {
207
- this.error('Cannot create self-dependency');
208
- }
209
- }
210
- throw error;
211
- }
212
- }
213
- async viewDependencies(storage, ticketId, ticket, showAll) {
214
- const dependencies = await this.storage.listTicketDependencies(ticketId);
215
- const blockers = await this.storage.getTicketBlockers(ticketId);
216
- const isBlocked = await this.storage.isTicketBlocked(ticketId);
217
- this.log(`\n${styles.emphasis(ticket.id)}: ${ticket.title}`);
218
- if (isBlocked) {
219
- this.log(styles.warning(' Status: BLOCKED'));
220
- }
221
- if (blockers.length > 0) {
222
- this.log(styles.muted('\n Blocked by:'));
223
- for (const blocker of blockers) {
224
- const status = blocker.status === 'done' ? styles.success('done') : styles.warning(blocker.status);
225
- this.log(` - ${blocker.id}: ${blocker.title} (${status})`);
226
- }
227
- }
228
- const otherDeps = dependencies.filter(d => d.dependencyType !== 'blocks');
229
- if (otherDeps.length > 0) {
230
- this.log(styles.muted('\n Related:'));
231
- // Fetch all related tickets in parallel
232
- const relatedTickets = await Promise.all(otherDeps.map(async (dep) => ({
233
- dep,
234
- ticket: await this.storage.getTicket(dep.dependsOnTicketId)
235
- })));
236
- for (const { dep, ticket: relatedTicket } of relatedTickets) {
237
- if (relatedTicket) {
238
- this.log(` - ${dep.dependencyType}: ${relatedTicket.id} - ${relatedTicket.title}`);
239
- }
240
- }
241
- }
242
- if (showAll) {
243
- const blockedBy = await this.storage.getTicketsBlockedBy(ticketId);
244
- if (blockedBy.length > 0) {
245
- this.log(styles.muted('\n Blocking:'));
246
- for (const blocked of blockedBy) {
247
- this.log(` - ${blocked.id}: ${blocked.title} (${blocked.status})`);
248
- }
249
- }
250
- }
251
- if (dependencies.length === 0 && blockers.length === 0) {
252
- this.log(styles.muted('\n No dependencies.'));
253
- }
254
- this.log('');
255
- }
256
- }
@@ -1,14 +0,0 @@
1
- import { PMOCommand } from '../../../lib/pmo/index.js';
2
- export default class TicketLinkRelates extends PMOCommand {
3
- static description: string;
4
- static examples: string[];
5
- static args: {
6
- id: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
7
- target: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
8
- };
9
- static flags: {
10
- json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
- project: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
- };
13
- execute(): Promise<void>;
14
- }
@@ -1,95 +0,0 @@
1
- import { Args, Flags } from '@oclif/core';
2
- import { autoExportToBoard, PMOCommand, pmoBaseFlags } from '../../../lib/pmo/index.js';
3
- import { styles } from '../../../lib/styles.js';
4
- import { shouldOutputJson, outputErrorAsJson, createMetadata, } from '../../../lib/prompt-json.js';
5
- export default class TicketLinkRelates extends PMOCommand {
6
- static description = 'Add a relates_to dependency (informational link)';
7
- static examples = [
8
- '<%= config.bin %> <%= command.id %> TKT-001 TKT-002 # TKT-001 relates to TKT-002',
9
- '<%= config.bin %> <%= command.id %> TKT-001 # Interactive selection',
10
- ];
11
- static args = {
12
- id: Args.string({
13
- description: 'Ticket ID',
14
- required: true,
15
- }),
16
- target: Args.string({
17
- description: 'Related ticket ID',
18
- required: false,
19
- }),
20
- };
21
- static flags = {
22
- ...pmoBaseFlags,
23
- json: Flags.boolean({
24
- char: 'm',
25
- aliases: ['machine'],
26
- description: 'Output prompt configuration as JSON (for AI agents/scripts)',
27
- default: false,
28
- }),
29
- };
30
- async execute() {
31
- const { args, flags } = await this.parse(TicketLinkRelates);
32
- // Check if JSON output mode is active
33
- const jsonMode = shouldOutputJson(flags);
34
- // Helper to handle errors in JSON mode
35
- const handleError = (code, message) => {
36
- if (jsonMode) {
37
- outputErrorAsJson(code, message, createMetadata('ticket link relates', flags));
38
- this.exit(1);
39
- }
40
- this.error(message);
41
- };
42
- const ticket = await this.storage.getTicket(args.id);
43
- if (!ticket) {
44
- return handleError('TICKET_NOT_FOUND', `Ticket not found: ${args.id}`);
45
- }
46
- let targetId = args.target;
47
- if (!targetId) {
48
- const projectId = flags.project;
49
- const allTickets = await this.storage.listTickets(projectId);
50
- const otherTickets = allTickets.filter(t => t.id !== args.id);
51
- if (otherTickets.length === 0) {
52
- if (jsonMode) {
53
- outputErrorAsJson('NO_OTHER_TICKETS', 'No other tickets to relate to.', createMetadata('ticket link relates', flags));
54
- return;
55
- }
56
- this.log(styles.muted('\nNo other tickets to relate to.'));
57
- return;
58
- }
59
- const selected = await this.selectFromList({
60
- message: `Select ticket that ${args.id} relates to:`,
61
- items: otherTickets,
62
- getName: (t) => `${t.id} - ${t.title} (${t.statusName || t.status})`,
63
- getValue: (t) => t.id,
64
- getCommand: (t) => `prlt ticket link relates ${args.id} ${t.id}${projectId ? ` -P ${projectId}` : ''} --json`,
65
- jsonMode: jsonMode ? { flags, commandName: 'ticket link relates' } : null,
66
- });
67
- if (!selected) {
68
- return;
69
- }
70
- targetId = selected;
71
- }
72
- const targetTicket = await this.storage.getTicket(targetId);
73
- if (!targetTicket) {
74
- this.error(`Ticket not found: ${targetId}`);
75
- }
76
- try {
77
- await this.storage.createTicketDependency(args.id, targetId, 'relates_to');
78
- await autoExportToBoard(this.pmoPath, this.storage, (msg) => this.log(styles.muted(msg)));
79
- this.log(styles.success(`\n✅ ${styles.emphasis(args.id)} relates to ${styles.emphasis(targetId)}`));
80
- this.log(styles.muted(` ${ticket.title}`));
81
- this.log(styles.muted(` relates to: ${targetTicket.title}`));
82
- }
83
- catch (error) {
84
- if (error instanceof Error) {
85
- if (error.message.includes('already exists')) {
86
- this.error('Dependency already exists');
87
- }
88
- if (error.message.includes('self-dependency')) {
89
- this.error('Cannot create self-dependency');
90
- }
91
- }
92
- throw error;
93
- }
94
- }
95
- }
@@ -1,16 +0,0 @@
1
- import { PMOCommand } from '../../../lib/pmo/index.js';
2
- export default class TicketLinkRemove extends PMOCommand {
3
- static description: string;
4
- static examples: string[];
5
- static args: {
6
- id: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
7
- target: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
8
- };
9
- static flags: {
10
- json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
- type: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
- all: import("@oclif/core/interfaces").BooleanFlag<boolean>;
13
- project: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
14
- };
15
- execute(): Promise<void>;
16
- }