@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,272 +0,0 @@
1
- import { Args, Flags } from '@oclif/core';
2
- import inquirer from 'inquirer';
3
- import { PMOCommand, pmoBaseFlags, autoExportToBoard } from '../../../lib/pmo/index.js';
4
- import { styles } from '../../../lib/styles.js';
5
- import { shouldOutputJson, outputPromptAsJson, outputErrorAsJson, createMetadata, buildPromptConfig, } from '../../../lib/prompt-json.js';
6
- export default class EpicLink extends PMOCommand {
7
- static description = 'Manage epic dependencies (links)';
8
- static examples = [
9
- '<%= config.bin %> <%= command.id %> EPIC-001 # List dependencies',
10
- '<%= config.bin %> <%= command.id %> EPIC-001 --blocks EPIC-002 # EPIC-001 is blocked by EPIC-002',
11
- '<%= config.bin %> <%= command.id %> EPIC-001 --relates EPIC-002 # EPIC-001 relates to EPIC-002',
12
- '<%= config.bin %> <%= command.id %> EPIC-001 --duplicates EPIC-002',
13
- '<%= config.bin %> <%= command.id %> EPIC-001 --all # Show all links',
14
- ];
15
- static args = {
16
- id: Args.string({
17
- description: 'Epic ID',
18
- required: false,
19
- }),
20
- };
21
- static flags = {
22
- ...pmoBaseFlags,
23
- project: Flags.string({
24
- char: 'P',
25
- description: 'Project ID (default: "default")',
26
- }),
27
- blocks: Flags.string({
28
- char: 'b',
29
- description: 'Add blocking dependency: this epic is blocked by TARGET',
30
- }),
31
- relates: Flags.string({
32
- char: 'r',
33
- description: 'Add relates_to dependency',
34
- }),
35
- duplicates: Flags.string({
36
- char: 'd',
37
- description: 'Add duplicates dependency',
38
- }),
39
- all: Flags.boolean({
40
- char: 'a',
41
- description: 'Show all dependencies (blockers and blocking)',
42
- default: false,
43
- }),
44
- json: Flags.boolean({
45
- char: 'm',
46
- aliases: ['machine'],
47
- description: 'Output prompt configuration as JSON (for AI agents/scripts)',
48
- default: false,
49
- }),
50
- };
51
- async execute() {
52
- const { args, flags } = await this.parse(EpicLink);
53
- // Check if JSON output mode is active
54
- const jsonMode = shouldOutputJson(flags);
55
- // Helper to handle errors in JSON mode
56
- const handleError = (code, message) => {
57
- if (jsonMode) {
58
- outputErrorAsJson(code, message, createMetadata('epic link', flags));
59
- this.exit(1);
60
- }
61
- this.error(message);
62
- };
63
- const projectId = await this.requireProject();
64
- let epicId = args.id;
65
- if (!epicId) {
66
- const epics = await this.storage.listEpics(projectId);
67
- if (epics.length === 0) {
68
- if (jsonMode) {
69
- outputErrorAsJson('NO_EPICS', 'No epics found.', createMetadata('epic link', flags));
70
- return;
71
- }
72
- this.log(styles.muted('\nNo epics found.'));
73
- return;
74
- }
75
- // In JSON mode, output epic selection prompt
76
- if (jsonMode) {
77
- const epicChoices = epics.map(e => ({ name: `${e.id} - ${e.title}`, value: e.id }));
78
- outputPromptAsJson(buildPromptConfig('list', 'id', 'Select epic to manage dependencies:', epicChoices), createMetadata('epic link', flags));
79
- return;
80
- }
81
- const { selected } = await inquirer.prompt([{
82
- type: 'list',
83
- name: 'selected',
84
- message: 'Select epic to manage dependencies:',
85
- choices: epics.map(e => ({ name: `${e.id} - ${e.title}`, value: e.id })),
86
- }]);
87
- epicId = selected;
88
- }
89
- const epic = await this.storage.getEpic(epicId);
90
- if (!epic) {
91
- return handleError('EPIC_NOT_FOUND', `Epic not found: ${epicId}`);
92
- }
93
- // If a dependency flag is provided, add the dependency directly
94
- if (flags.blocks || flags.relates || flags.duplicates) {
95
- const targetId = flags.blocks || flags.relates || flags.duplicates;
96
- const dependencyType = flags.blocks ? 'blocks' :
97
- flags.relates ? 'relates_to' : 'duplicates';
98
- await this.addDependency(epicId, targetId, dependencyType, epic.title);
99
- return;
100
- }
101
- // Interactive mode: show menu in a loop
102
- // In JSON mode, output the interactive menu config instead of prompting
103
- if (jsonMode) {
104
- const menuChoices = [
105
- { name: 'View dependencies', value: 'view' },
106
- { name: 'Add blocking dependency (blocked by...)', value: 'blocks' },
107
- { name: 'Add relates_to dependency', value: 'relates_to' },
108
- { name: 'Add duplicates dependency', value: 'duplicates' },
109
- { name: 'Remove dependency', value: 'remove' },
110
- { name: 'Done', value: 'done' },
111
- ];
112
- outputPromptAsJson(buildPromptConfig('list', 'action', `Dependencies for ${epicId}:`, menuChoices), createMetadata('epic link', flags));
113
- return;
114
- }
115
- // Check for TTY before showing interactive menu
116
- if (!process.stdin.isTTY) {
117
- this.error('Interactive mode requires a TTY. Use --json for scripted usage or provide flags like --blocks, --relates, or --duplicates.');
118
- }
119
- let continueLoop = true;
120
- while (continueLoop) {
121
- // eslint-disable-next-line no-await-in-loop -- Interactive user loop
122
- const allEpics = await this.storage.listEpics(projectId);
123
- const otherEpics = allEpics.filter(e => e.id !== epicId);
124
- // eslint-disable-next-line no-await-in-loop -- Interactive user prompt
125
- const { action } = await inquirer.prompt([{
126
- type: 'list',
127
- name: 'action',
128
- message: `Dependencies for ${epic.id}:`,
129
- choices: [
130
- { name: 'View dependencies', value: 'view' },
131
- { name: 'Add blocking dependency (blocked by...)', value: 'blocks' },
132
- { name: 'Add relates_to dependency', value: 'relates_to' },
133
- { name: 'Add duplicates dependency', value: 'duplicates' },
134
- new inquirer.Separator(),
135
- { name: 'Remove dependency', value: 'remove' },
136
- { name: 'Done', value: 'done' },
137
- ],
138
- }]);
139
- if (action === 'done') {
140
- continueLoop = false;
141
- continue;
142
- }
143
- if (action === 'view') {
144
- // eslint-disable-next-line no-await-in-loop -- User action handling
145
- await this.viewDependencies(epicId, epic, flags.all);
146
- continue;
147
- }
148
- if (action === 'remove') {
149
- // eslint-disable-next-line no-await-in-loop -- User action handling
150
- const dependencies = await this.storage.listEpicDependencies(epicId);
151
- if (dependencies.length === 0) {
152
- this.log(styles.muted('\nNo dependencies to remove.'));
153
- continue;
154
- }
155
- // eslint-disable-next-line no-await-in-loop -- Building choices for current interaction
156
- const choices = await Promise.all(dependencies.map(async (dep) => {
157
- const depEpic = await this.storage.getEpic(dep.dependsOnEpicId);
158
- return {
159
- name: `${dep.dependsOnEpicId} - ${depEpic?.title || 'Unknown'} (${dep.dependencyType})`,
160
- value: { targetId: dep.dependsOnEpicId, type: dep.dependencyType }
161
- };
162
- }));
163
- // eslint-disable-next-line no-await-in-loop -- User selection prompt
164
- const { selected } = await inquirer.prompt([{
165
- type: 'list',
166
- name: 'selected',
167
- message: 'Select dependency to remove:',
168
- choices,
169
- }]);
170
- // eslint-disable-next-line no-await-in-loop -- Action after user selection
171
- await this.storage.deleteEpicDependency(epicId, selected.targetId, selected.type);
172
- // eslint-disable-next-line no-await-in-loop
173
- await autoExportToBoard(this.pmoPath, this.storage, (msg) => this.log(styles.muted(msg)));
174
- this.log(styles.success(`\n✅ Removed dependency: ${epicId} → ${selected.targetId}`));
175
- continue;
176
- }
177
- // Add dependency
178
- if (otherEpics.length === 0) {
179
- this.log(styles.muted('\nNo other epics to link to.'));
180
- continue;
181
- }
182
- // eslint-disable-next-line no-await-in-loop -- User selection prompt
183
- const { targetId } = await inquirer.prompt([{
184
- type: 'list',
185
- name: 'targetId',
186
- message: `Select epic that ${epicId} ${action === 'blocks' ? 'is blocked by' : action === 'relates_to' ? 'relates to' : 'duplicates'}:`,
187
- choices: otherEpics.map(e => ({ name: `${e.id} - ${e.title}`, value: e.id })),
188
- }]);
189
- // eslint-disable-next-line no-await-in-loop -- Action after user selection
190
- await this.addDependency(epicId, targetId, action, epic.title);
191
- }
192
- }
193
- async addDependency(epicId, targetId, dependencyType, epicTitle) {
194
- const targetEpic = await this.storage.getEpic(targetId);
195
- if (!targetEpic) {
196
- this.error(`Epic not found: ${targetId}`);
197
- }
198
- try {
199
- await this.storage.createEpicDependency(epicId, targetId, dependencyType);
200
- await autoExportToBoard(this.pmoPath, this.storage, (msg) => this.log(styles.muted(msg)));
201
- const typeLabel = dependencyType === 'blocks' ? 'is blocked by' :
202
- dependencyType === 'relates_to' ? 'relates to' : 'duplicates';
203
- this.log(styles.success(`\n✅ ${styles.emphasis(epicId)} ${typeLabel} ${styles.emphasis(targetId)}`));
204
- this.log(styles.muted(` ${epicTitle}`));
205
- this.log(styles.muted(` ${typeLabel} ${targetEpic.title}`));
206
- }
207
- catch (error) {
208
- if (error instanceof Error) {
209
- if (error.message.includes('already exists')) {
210
- this.error('Dependency already exists');
211
- }
212
- if (error.message.includes('self-dependency')) {
213
- this.error('Cannot create self-dependency');
214
- }
215
- }
216
- throw error;
217
- }
218
- }
219
- async viewDependencies(epicId, epic, showAll) {
220
- const dependencies = await this.storage.listEpicDependencies(epicId);
221
- const isBlocked = await this.storage.isEpicBlocked(epicId);
222
- this.log(`\n${styles.emphasis(epic.id)}: ${epic.title}`);
223
- if (isBlocked) {
224
- this.log(styles.warning(' Status: BLOCKED'));
225
- }
226
- const blockers = dependencies.filter(d => d.dependencyType === 'blocks');
227
- if (blockers.length > 0) {
228
- this.log(styles.muted('\n Blocked by:'));
229
- // Fetch all blocker epics in parallel
230
- const blockerEpics = await Promise.all(blockers.map(dep => this.storage.getEpic(dep.dependsOnEpicId)));
231
- for (const blockerEpic of blockerEpics) {
232
- if (blockerEpic) {
233
- const status = blockerEpic.status === 'complete' ? styles.success('complete') : styles.warning(blockerEpic.status);
234
- this.log(` - ${blockerEpic.id}: ${blockerEpic.title} (${status})`);
235
- }
236
- }
237
- }
238
- const otherDeps = dependencies.filter(d => d.dependencyType !== 'blocks');
239
- if (otherDeps.length > 0) {
240
- this.log(styles.muted('\n Related:'));
241
- // Fetch all related epics in parallel
242
- const relatedEpics = await Promise.all(otherDeps.map(async (dep) => ({ dep, epic: await this.storage.getEpic(dep.dependsOnEpicId) })));
243
- for (const { dep, epic: relatedEpic } of relatedEpics) {
244
- if (relatedEpic) {
245
- this.log(` - ${dep.dependencyType}: ${relatedEpic.id} - ${relatedEpic.title}`);
246
- }
247
- }
248
- }
249
- if (showAll) {
250
- const allEpics = await this.storage.listEpics(epic.projectId);
251
- // Find all epics that depend on this epic in parallel
252
- const blockingResults = await Promise.all(allEpics
253
- .filter(otherEpic => otherEpic.id !== epicId)
254
- .map(async (otherEpic) => {
255
- const otherDeps = await this.storage.listEpicDependencies(otherEpic.id);
256
- const blockingDep = otherDeps.find(d => d.dependsOnEpicId === epicId);
257
- return blockingDep ? { epic: otherEpic, type: blockingDep.dependencyType } : null;
258
- }));
259
- const blocking = blockingResults.filter((b) => b !== null);
260
- if (blocking.length > 0) {
261
- this.log(styles.muted('\n Blocking:'));
262
- for (const item of blocking) {
263
- this.log(` - ${item.epic.id}: ${item.epic.title} (${item.type})`);
264
- }
265
- }
266
- }
267
- if (dependencies.length === 0) {
268
- this.log(styles.muted('\n No dependencies.'));
269
- }
270
- this.log('');
271
- }
272
- }
@@ -1,68 +0,0 @@
1
- import { Args, Flags } from '@oclif/core';
2
- import inquirer from 'inquirer';
3
- import { PMOCommand, pmoBaseFlags, autoExportToBoard } from '../../../lib/pmo/index.js';
4
- import { styles } from '../../../lib/styles.js';
5
- import { shouldOutputJson, outputPromptAsJson, outputErrorAsJson, createMetadata, buildPromptConfig, } from '../../../lib/prompt-json.js';
6
- export default class EpicLinkRelates extends PMOCommand {
7
- static description = 'Add a relates_to dependency (informational link)';
8
- static examples = ['<%= config.bin %> <%= command.id %> EPIC-001 EPIC-002'];
9
- static args = {
10
- id: Args.string({ description: 'Epic ID', required: true }),
11
- target: Args.string({ description: 'Related epic ID', required: false }),
12
- };
13
- static flags = {
14
- ...pmoBaseFlags,
15
- project: Flags.string({ char: 'P', description: 'Project ID' }),
16
- json: Flags.boolean({
17
- char: 'm',
18
- aliases: ['machine'],
19
- description: 'Output prompt configuration as JSON (for AI agents/scripts)',
20
- default: false,
21
- }),
22
- };
23
- async execute() {
24
- const { args, flags } = await this.parse(EpicLinkRelates);
25
- // Check if JSON output mode is active
26
- const jsonMode = shouldOutputJson(flags);
27
- // Helper to handle errors in JSON mode
28
- const handleError = (code, message) => {
29
- if (jsonMode) {
30
- outputErrorAsJson(code, message, createMetadata('epic link relates', flags));
31
- this.exit(1);
32
- }
33
- this.error(message);
34
- };
35
- const epic = await this.storage.getEpic(args.id);
36
- if (!epic)
37
- return handleError('EPIC_NOT_FOUND', `Epic not found: ${args.id}`);
38
- const projectId = epic.projectId;
39
- let targetId = args.target;
40
- if (!targetId) {
41
- const allEpics = await this.storage.listEpics(projectId);
42
- const otherEpics = allEpics.filter(e => e.id !== args.id);
43
- if (otherEpics.length === 0) {
44
- if (jsonMode) {
45
- outputErrorAsJson('NO_OTHER_EPICS', 'No other epics.', createMetadata('epic link relates', flags));
46
- return;
47
- }
48
- this.log(styles.muted('\nNo other epics.'));
49
- return;
50
- }
51
- // In JSON mode, output target epic selection prompt
52
- if (jsonMode) {
53
- const epicChoices = otherEpics.map(e => ({ name: `${e.id} - ${e.title}`, value: e.id }));
54
- outputPromptAsJson(buildPromptConfig('list', 'target', `Select epic that ${args.id} relates to:`, epicChoices), createMetadata('epic link relates', flags));
55
- return;
56
- }
57
- const { selected } = await inquirer.prompt([{ type: 'list', name: 'selected', message: `Select epic that ${args.id} relates to:`,
58
- choices: otherEpics.map(e => ({ name: `${e.id} - ${e.title}`, value: e.id })) }]);
59
- targetId = selected;
60
- }
61
- const targetEpic = await this.storage.getEpic(targetId);
62
- if (!targetEpic)
63
- return handleError('TARGET_EPIC_NOT_FOUND', `Epic not found: ${targetId}`);
64
- await this.storage.createEpicDependency(args.id, targetId, 'relates_to');
65
- await autoExportToBoard(this.pmoPath, this.storage, (msg) => this.log(styles.muted(msg)));
66
- this.log(styles.success(`\n✅ ${styles.emphasis(args.id)} relates to ${styles.emphasis(targetId)}`));
67
- }
68
- }
@@ -1,93 +0,0 @@
1
- import { Args, Flags } from '@oclif/core';
2
- import inquirer from 'inquirer';
3
- import { PMOCommand, pmoBaseFlags, autoExportToBoard } from '../../../lib/pmo/index.js';
4
- import { styles } from '../../../lib/styles.js';
5
- import { shouldOutputJson, outputPromptAsJson, outputErrorAsJson, createMetadata, buildPromptConfig, } from '../../../lib/prompt-json.js';
6
- export default class EpicLinkRemove extends PMOCommand {
7
- static description = 'Remove a dependency from an epic';
8
- static examples = [
9
- '<%= config.bin %> <%= command.id %> EPIC-001 EPIC-002',
10
- '<%= config.bin %> <%= command.id %> EPIC-001 --all',
11
- ];
12
- static args = {
13
- id: Args.string({ description: 'Epic ID', required: true }),
14
- target: Args.string({ description: 'Target epic ID to unlink', required: false }),
15
- };
16
- static flags = {
17
- ...pmoBaseFlags,
18
- project: Flags.string({ char: 'P', description: 'Project ID' }),
19
- type: Flags.string({ char: 't', description: 'Dependency type', options: ['blocks', 'relates_to', 'duplicates'] }),
20
- all: Flags.boolean({ char: 'a', description: 'Remove all dependencies', default: false }),
21
- json: Flags.boolean({
22
- char: 'm',
23
- aliases: ['machine'],
24
- description: 'Output prompt configuration as JSON (for AI agents/scripts)',
25
- default: false,
26
- }),
27
- };
28
- async execute() {
29
- const { args, flags } = await this.parse(EpicLinkRemove);
30
- // Check if JSON output mode is active
31
- const jsonMode = shouldOutputJson(flags);
32
- // Helper to handle errors in JSON mode
33
- const handleError = (code, message) => {
34
- if (jsonMode) {
35
- outputErrorAsJson(code, message, createMetadata('epic link remove', flags));
36
- this.exit(1);
37
- }
38
- this.error(message);
39
- };
40
- const epic = await this.storage.getEpic(args.id);
41
- if (!epic)
42
- return handleError('EPIC_NOT_FOUND', `Epic not found: ${args.id}`);
43
- const dependencies = await this.storage.listEpicDependencies(args.id);
44
- if (dependencies.length === 0) {
45
- if (jsonMode) {
46
- outputErrorAsJson('NO_DEPENDENCIES', `Epic ${args.id} has no dependencies.`, createMetadata('epic link remove', flags));
47
- return;
48
- }
49
- this.log(styles.muted(`\nEpic ${args.id} has no dependencies.`));
50
- return;
51
- }
52
- if (flags.all) {
53
- // In JSON mode, output confirmation prompt
54
- if (jsonMode) {
55
- const confirmChoices = [
56
- { name: 'No', value: 'false' },
57
- { name: 'Yes', value: 'true' },
58
- ];
59
- outputPromptAsJson(buildPromptConfig('list', 'confirmed', `Remove all ${dependencies.length} dependencies?`, confirmChoices), createMetadata('epic link remove', flags));
60
- return;
61
- }
62
- const { confirmed } = await inquirer.prompt([{ type: 'confirm', name: 'confirmed', message: `Remove all ${dependencies.length} dependencies?`, default: false }]);
63
- if (!confirmed) {
64
- this.log(styles.muted('\nCancelled.'));
65
- return;
66
- }
67
- // Delete sequentially to maintain data integrity
68
- // eslint-disable-next-line no-await-in-loop
69
- for (const dep of dependencies)
70
- await this.storage.deleteEpicDependency(args.id, dep.dependsOnEpicId, dep.dependencyType);
71
- await autoExportToBoard(this.pmoPath, this.storage, (msg) => this.log(styles.muted(msg)));
72
- this.log(styles.success(`\n✅ Removed ${dependencies.length} dependencies from ${args.id}`));
73
- return;
74
- }
75
- let targetId = args.target;
76
- if (!targetId) {
77
- const choices = await Promise.all(dependencies.map(async (dep) => {
78
- const depEpic = await this.storage.getEpic(dep.dependsOnEpicId);
79
- return { name: `${dep.dependsOnEpicId} - ${depEpic?.title || 'Unknown'} (${dep.dependencyType})`, value: dep.dependsOnEpicId };
80
- }));
81
- // In JSON mode, output dependency selection prompt
82
- if (jsonMode) {
83
- outputPromptAsJson(buildPromptConfig('list', 'target', 'Select dependency to remove:', choices), createMetadata('epic link remove', flags));
84
- return;
85
- }
86
- const { selected } = await inquirer.prompt([{ type: 'list', name: 'selected', message: 'Select dependency to remove:', choices }]);
87
- targetId = selected;
88
- }
89
- await this.storage.deleteEpicDependency(args.id, targetId, flags.type);
90
- await autoExportToBoard(this.pmoPath, this.storage, (msg) => this.log(styles.muted(msg)));
91
- this.log(styles.success(`\n✅ Removed dependency: ${args.id} → ${targetId}`));
92
- }
93
- }
@@ -1,17 +0,0 @@
1
- import { PMOCommand } from '../../../lib/pmo/index.js';
2
- export default class PhaseTemplateApply extends PMOCommand {
3
- static description: string;
4
- static examples: string[];
5
- static args: {
6
- template: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
7
- };
8
- static flags: {
9
- force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
- json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
- project: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
- };
13
- protected getPMOOptions(): {
14
- promptIfMultiple: boolean;
15
- };
16
- execute(): Promise<void>;
17
- }
@@ -1,108 +0,0 @@
1
- import { Flags, Args } from '@oclif/core';
2
- import inquirer from 'inquirer';
3
- import { PMOCommand, pmoBaseFlags } from '../../../lib/pmo/index.js';
4
- import { styles } from '../../../lib/styles.js';
5
- import { shouldOutputJson, outputPromptAsJson, outputErrorAsJson, createMetadata, buildPromptConfig, } from '../../../lib/prompt-json.js';
6
- export default class PhaseTemplateApply extends PMOCommand {
7
- static description = 'Apply a phase template to the workspace';
8
- static examples = [
9
- '<%= config.bin %> <%= command.id %> default',
10
- '<%= config.bin %> <%= command.id %> agile',
11
- '<%= config.bin %> <%= command.id %> product --force # Skip confirmation',
12
- ];
13
- static args = {
14
- template: Args.string({
15
- description: 'Phase template ID to apply',
16
- required: false,
17
- }),
18
- };
19
- static flags = {
20
- ...pmoBaseFlags,
21
- force: Flags.boolean({
22
- char: 'f',
23
- description: 'Skip confirmation prompt (will replace existing phases)',
24
- default: false,
25
- }),
26
- json: Flags.boolean({
27
- char: 'm',
28
- aliases: ['machine'],
29
- description: 'Output prompt configuration as JSON (for AI agents/scripts)',
30
- default: false,
31
- }),
32
- };
33
- getPMOOptions() {
34
- return { promptIfMultiple: false };
35
- }
36
- async execute() {
37
- const { args, flags } = await this.parse(PhaseTemplateApply);
38
- // Check if JSON output mode is active
39
- const jsonMode = shouldOutputJson(flags);
40
- // Helper to handle errors in JSON mode
41
- const handleError = (code, message) => {
42
- if (jsonMode) {
43
- outputErrorAsJson(code, message, createMetadata('phase template apply', flags));
44
- this.exit(1);
45
- }
46
- this.error(message);
47
- };
48
- // Get template - prompt for selection if not provided
49
- let templateId = args.template;
50
- if (!templateId) {
51
- const templates = await this.storage.listPhaseTemplates();
52
- if (templates.length === 0) {
53
- return handleError('NO_TEMPLATES', `No phase templates found.\nCreate one with: prlt template phase create "Template Name"`);
54
- }
55
- const { selectedTemplate } = await inquirer.prompt([{
56
- type: 'list',
57
- name: 'selectedTemplate',
58
- message: 'Select a phase template:',
59
- choices: templates.map(t => ({
60
- name: `${t.name}${t.description ? ` - ${t.description}` : ''}`,
61
- value: t.id,
62
- })),
63
- }]);
64
- templateId = selectedTemplate;
65
- }
66
- // Verify template exists
67
- const template = await this.storage.getPhaseTemplate(templateId);
68
- if (!template) {
69
- return handleError('TEMPLATE_NOT_FOUND', `Phase template not found: ${templateId}. Run 'prlt template phase list' to see available templates.`);
70
- }
71
- // Check if workspace has existing phases
72
- const existingPhases = await this.storage.listPhases();
73
- if (existingPhases.length > 0 && !flags.force) {
74
- // In JSON mode, output confirmation prompt
75
- if (jsonMode) {
76
- const confirmChoices = [
77
- { name: 'No', value: 'false' },
78
- { name: 'Yes', value: 'true' },
79
- ];
80
- outputPromptAsJson(buildPromptConfig('list', 'confirmed', `Workspace has ${existingPhases.length} existing phase(s). Applying will REPLACE all. Apply template "${template.name}"?`, confirmChoices), createMetadata('phase template apply', flags));
81
- return;
82
- }
83
- this.log(styles.warning(`\nWorkspace has ${existingPhases.length} existing phase(s).`));
84
- this.log(styles.warning('Applying a template will REPLACE all existing phases.'));
85
- this.log('');
86
- const { confirm } = await inquirer.prompt([
87
- {
88
- type: 'confirm',
89
- name: 'confirm',
90
- message: `Apply template "${template.name}" and replace existing phases?`,
91
- default: false,
92
- },
93
- ]);
94
- if (!confirm) {
95
- this.log(styles.muted('Cancelled'));
96
- return;
97
- }
98
- }
99
- // Apply template
100
- const phases = await this.storage.applyPhaseTemplate(templateId);
101
- this.log(styles.success(`\nApplied phase template "${styles.emphasis(template.name)}"`));
102
- this.log(styles.muted(`Created ${phases.length} phases:`));
103
- for (const phase of phases) {
104
- const defaultBadge = phase.isDefault ? ' (default)' : '';
105
- this.log(styles.muted(` • ${phase.name} [${phase.category}]${defaultBadge}`));
106
- }
107
- }
108
- }
@@ -1,17 +0,0 @@
1
- import { PMOCommand } from '../../../lib/pmo/index.js';
2
- export default class PhaseTemplateCreate extends PMOCommand {
3
- static description: string;
4
- static examples: string[];
5
- static args: {
6
- name: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
7
- };
8
- static flags: {
9
- description: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
- json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
- project: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
- };
13
- protected getPMOOptions(): {
14
- promptIfMultiple: boolean;
15
- };
16
- execute(): Promise<void>;
17
- }