@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,8 +1,7 @@
1
1
  import { Args, Flags } from '@oclif/core';
2
- import inquirer from 'inquirer';
3
2
  import { PMOCommand, pmoBaseFlags, autoExportToBoard } from '../../lib/pmo/index.js';
4
3
  import { styles } from '../../lib/styles.js';
5
- import { shouldOutputJson, outputPromptAsJson, outputErrorAsJson, createMetadata, buildPromptConfig, } from '../../lib/prompt-json.js';
4
+ import { shouldOutputJson, outputErrorAsJson, createMetadata, } from '../../lib/prompt-json.js';
6
5
  export default class EpicProject extends PMOCommand {
7
6
  static description = 'Move an epic to a different project (optionally with its tickets)';
8
7
  static examples = [
@@ -60,21 +59,18 @@ export default class EpicProject extends PMOCommand {
60
59
  this.log(styles.muted('\nNo epics found in this project.'));
61
60
  return;
62
61
  }
63
- // In JSON mode, output epic selection prompt
64
- if (jsonMode) {
65
- const epicChoices = epics.map(e => ({ name: `${e.id} - ${e.title} (${e.status})`, value: e.id }));
66
- outputPromptAsJson(buildPromptConfig('list', 'epicId', 'Select epic to move:', epicChoices), createMetadata('epic project', flags));
67
- return;
68
- }
69
- const { selected } = await inquirer.prompt([{
62
+ const epicChoices = epics.map(e => ({
63
+ name: `${e.id} - ${e.title} (${e.status})`,
64
+ value: e.id,
65
+ command: `prlt epic project ${e.id} --json`,
66
+ }));
67
+ const jsonModeConfig = jsonMode ? { flags, commandName: 'epic project' } : null;
68
+ const { selected } = await this.prompt([{
70
69
  type: 'list',
71
70
  name: 'selected',
72
71
  message: 'Select epic to move:',
73
- choices: epics.map(e => ({
74
- name: `${e.id} - ${e.title} (${e.status})`,
75
- value: e.id,
76
- })),
77
- }]);
72
+ choices: epicChoices,
73
+ }], jsonModeConfig);
78
74
  epicId = selected;
79
75
  }
80
76
  // Get epic details
@@ -86,24 +82,20 @@ export default class EpicProject extends PMOCommand {
86
82
  const projects = await this.storage.listProjects();
87
83
  const otherProjects = projects.filter(p => p.id !== sourceProjectId);
88
84
  if (otherProjects.length === 0) {
89
- if (jsonMode) {
90
- const actionChoices = [
91
- { name: 'Create a new project', value: 'create' },
92
- { name: 'Cancel', value: 'cancel' },
93
- ];
94
- outputPromptAsJson(buildPromptConfig('list', 'action', 'No other projects to move to. What would you like to do?', actionChoices), createMetadata('epic project', flags));
95
- return;
85
+ if (!jsonMode) {
86
+ this.log(styles.muted('\nNo other projects to move to.'));
96
87
  }
97
- this.log(styles.muted('\nNo other projects to move to.'));
98
- const { action } = await inquirer.prompt([{
88
+ const actionChoices = [
89
+ { name: 'Create a new project', value: 'create', command: 'prlt project create --json' },
90
+ { name: 'Cancel', value: 'cancel', command: '' },
91
+ ];
92
+ const jsonModeConfig = jsonMode ? { flags, commandName: 'epic project' } : null;
93
+ const { action } = await this.prompt([{
99
94
  type: 'list',
100
95
  name: 'action',
101
- message: 'What would you like to do?',
102
- choices: [
103
- { name: 'Create a new project', value: 'create' },
104
- { name: 'Cancel', value: 'cancel' },
105
- ],
106
- }]);
96
+ message: 'No other projects to move to. What would you like to do?',
97
+ choices: actionChoices,
98
+ }], jsonModeConfig);
107
99
  if (action === 'create') {
108
100
  await this.config.runCommand('project:create', []);
109
101
  }
@@ -112,21 +104,18 @@ export default class EpicProject extends PMOCommand {
112
104
  // Get target project
113
105
  let targetProjectId = args.targetProject;
114
106
  if (!targetProjectId) {
115
- // In JSON mode, output project selection prompt
116
- if (jsonMode) {
117
- const projectChoices = otherProjects.map(p => ({ name: `${p.id} - ${p.name} (${p.status})`, value: p.id }));
118
- outputPromptAsJson(buildPromptConfig('list', 'targetProject', 'Select target project:', projectChoices), createMetadata('epic project', flags));
119
- return;
120
- }
121
- const { selected } = await inquirer.prompt([{
107
+ const projectChoices = otherProjects.map(p => ({
108
+ name: `${p.id} - ${p.name} (${p.status})`,
109
+ value: p.id,
110
+ command: `prlt epic project ${epicId} ${p.id} --json`,
111
+ }));
112
+ const jsonModeConfig = jsonMode ? { flags, commandName: 'epic project' } : null;
113
+ const { selected } = await this.prompt([{
122
114
  type: 'list',
123
115
  name: 'selected',
124
116
  message: 'Select target project:',
125
- choices: otherProjects.map(p => ({
126
- name: `${p.id} - ${p.name} (${p.status})`,
127
- value: p.id,
128
- })),
129
- }]);
117
+ choices: projectChoices,
118
+ }], jsonModeConfig);
130
119
  targetProjectId = selected;
131
120
  }
132
121
  // Validate target project
@@ -145,27 +134,21 @@ export default class EpicProject extends PMOCommand {
145
134
  // Handle tickets
146
135
  let moveTickets = flags['with-tickets'];
147
136
  if (epicTickets.length > 0 && !flags['with-tickets']) {
148
- // In JSON mode, output ticket handling prompt
149
- if (jsonMode) {
150
- const ticketActionChoices = [
151
- { name: 'Move tickets with epic', value: 'move' },
152
- { name: 'Keep tickets in source project (unlink from epic)', value: 'unlink' },
153
- { name: 'Cancel', value: 'cancel' },
154
- ];
155
- outputPromptAsJson(buildPromptConfig('list', 'ticketAction', `Epic has ${epicTickets.length} ticket(s) assigned. How to handle tickets?`, ticketActionChoices), createMetadata('epic project', flags));
156
- return;
137
+ if (!jsonMode) {
138
+ this.log(styles.warning(`\nEpic has ${epicTickets.length} ticket(s) assigned.`));
157
139
  }
158
- this.log(styles.warning(`\nEpic has ${epicTickets.length} ticket(s) assigned.`));
159
- const { action } = await inquirer.prompt([{
140
+ const ticketActionChoices = [
141
+ { name: 'Move tickets with epic', value: 'move', command: `prlt epic project ${epicId} ${targetProjectId} --with-tickets --json` },
142
+ { name: 'Keep tickets in source project (unlink from epic)', value: 'unlink', command: `prlt epic project ${epicId} ${targetProjectId} --json` },
143
+ { name: 'Cancel', value: 'cancel', command: '' },
144
+ ];
145
+ const jsonModeConfig = jsonMode ? { flags, commandName: 'epic project' } : null;
146
+ const { action } = await this.prompt([{
160
147
  type: 'list',
161
148
  name: 'action',
162
- message: 'How to handle tickets?',
163
- choices: [
164
- { name: 'Move tickets with epic', value: 'move' },
165
- { name: 'Keep tickets in source project (unlink from epic)', value: 'unlink' },
166
- { name: 'Cancel', value: 'cancel' },
167
- ],
168
- }]);
149
+ message: `Epic has ${epicTickets.length} ticket(s) assigned. How to handle tickets?`,
150
+ choices: ticketActionChoices,
151
+ }], jsonModeConfig);
169
152
  if (action === 'cancel') {
170
153
  return;
171
154
  }
@@ -1,8 +1,7 @@
1
1
  import { Args, Flags } from '@oclif/core';
2
- import inquirer from 'inquirer';
3
2
  import { PMOCommand, pmoBaseFlags } from '../../lib/pmo/index.js';
4
3
  import { styles } from '../../lib/styles.js';
5
- import { shouldOutputJson, outputPromptAsJson, outputErrorAsJson, createMetadata, buildPromptConfig, } from '../../lib/prompt-json.js';
4
+ import { shouldOutputJson, outputErrorAsJson, createMetadata, } from '../../lib/prompt-json.js';
6
5
  export default class EpicReorder extends PMOCommand {
7
6
  static description = 'Reorder epic priority/rank';
8
7
  static examples = [
@@ -74,18 +73,15 @@ export default class EpicReorder extends PMOCommand {
74
73
  const choices = epics.map((e, i) => ({
75
74
  name: `#${i + 1} ${e.id} - ${e.title}`,
76
75
  value: e.id,
76
+ command: `prlt epic reorder ${e.id} --json`,
77
77
  }));
78
- // In JSON mode, output epic selection prompt
79
- if (jsonMode) {
80
- outputPromptAsJson(buildPromptConfig('list', 'id', 'Select epic to reorder:', choices), createMetadata('epic reorder', flags));
81
- return;
82
- }
83
- const { selected } = await inquirer.prompt([{
78
+ const jsonModeConfig = jsonMode ? { flags, commandName: 'epic reorder' } : null;
79
+ const { selected } = await this.prompt([{
84
80
  type: 'list',
85
81
  name: 'selected',
86
82
  message: 'Select epic to reorder:',
87
83
  choices,
88
- }]);
84
+ }], jsonModeConfig);
89
85
  epicId = selected;
90
86
  }
91
87
  const epic = await this.storage.getEpic(epicId);
@@ -123,30 +119,29 @@ export default class EpicReorder extends PMOCommand {
123
119
  newPosition = epics.length - 1;
124
120
  }
125
121
  else {
126
- // In JSON mode, output rank input prompt
127
- if (jsonMode) {
128
- outputPromptAsJson(buildPromptConfig('input', 'rank', `New rank for ${epicId} (1-${epics.length}):`, undefined, String(epic.position + 1)), createMetadata('epic reorder', flags));
129
- return;
130
- }
131
122
  // Interactive: show current order and ask for new position
132
- this.log(`\nCurrent order:`);
133
- epics.forEach((e, i) => {
134
- const marker = e.id === epicId ? ' ◀' : '';
135
- this.log(` #${i + 1} ${e.id} - ${e.title}${marker}`);
136
- });
137
- const { rank } = await inquirer.prompt([{
138
- type: 'number',
123
+ if (!jsonMode) {
124
+ this.log(`\nCurrent order:`);
125
+ epics.forEach((e, i) => {
126
+ const marker = e.id === epicId ? ' ◀' : '';
127
+ this.log(` #${i + 1} ${e.id} - ${e.title}${marker}`);
128
+ });
129
+ }
130
+ // Build position choices for rank selection
131
+ const rankChoices = epics.map((e, i) => ({
132
+ name: `#${i + 1}${e.id === epicId ? ' (current)' : ` - before ${e.title}`}`,
133
+ value: String(i + 1),
134
+ command: `prlt epic reorder ${epicId} ${i + 1} --json`,
135
+ }));
136
+ const jsonModeConfig = jsonMode ? { flags, commandName: 'epic reorder' } : null;
137
+ const { rank } = await this.prompt([{
138
+ type: 'list',
139
139
  name: 'rank',
140
140
  message: `New rank for ${epicId} (1-${epics.length}):`,
141
- default: epic.position + 1,
142
- validate: (input) => {
143
- if (input < 1 || input > epics.length) {
144
- return `Please enter a number between 1 and ${epics.length}`;
145
- }
146
- return true;
147
- },
148
- }]);
149
- newPosition = rank - 1;
141
+ choices: rankChoices,
142
+ default: String(epic.position + 1),
143
+ }], jsonModeConfig);
144
+ newPosition = parseInt(rank, 10) - 1;
150
145
  }
151
146
  // Perform reorder
152
147
  await this.storage.reorderEpic(projectId, epicId, newPosition);
@@ -9,6 +9,7 @@ export default class EpicSpec extends PMOCommand {
9
9
  static flags: {
10
10
  json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
11
  unlink: import("@oclif/core/interfaces").BooleanFlag<boolean>;
12
+ 'align-tickets': import("@oclif/core/interfaces").BooleanFlag<boolean>;
12
13
  project: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
14
  };
14
15
  execute(): Promise<void>;
@@ -1,8 +1,7 @@
1
1
  import { Args, Flags } from '@oclif/core';
2
- import inquirer from 'inquirer';
3
2
  import { PMOCommand, pmoBaseFlags } from '../../lib/pmo/index.js';
4
3
  import { styles } from '../../lib/styles.js';
5
- import { shouldOutputJson, outputPromptAsJson, outputErrorAsJson, createMetadata, buildPromptConfig, } from '../../lib/prompt-json.js';
4
+ import { shouldOutputJson, outputErrorAsJson, createMetadata, } from '../../lib/prompt-json.js';
6
5
  export default class EpicSpec extends PMOCommand {
7
6
  static description = 'Assign a spec to an epic (design document)';
8
7
  static examples = [
@@ -34,6 +33,10 @@ export default class EpicSpec extends PMOCommand {
34
33
  description: 'Remove spec from epic instead of adding',
35
34
  default: false,
36
35
  }),
36
+ 'align-tickets': Flags.boolean({
37
+ description: 'Also update all tickets in the epic to use the same spec',
38
+ default: false,
39
+ }),
37
40
  };
38
41
  async execute() {
39
42
  const { args, flags } = await this.parse(EpicSpec);
@@ -66,19 +69,16 @@ export default class EpicSpec extends PMOCommand {
66
69
  return {
67
70
  name: `${e.id} ${e.title} (${e.status})${specLabel}`,
68
71
  value: e.id,
72
+ command: `prlt epic spec ${e.id} --json`,
69
73
  };
70
74
  });
71
- // In JSON mode, output epic selection prompt
72
- if (jsonMode) {
73
- outputPromptAsJson(buildPromptConfig('list', 'epicId', 'Select epic:', epicChoices), createMetadata('epic spec', flags));
74
- return;
75
- }
76
- const { selected } = await inquirer.prompt([{
75
+ const jsonModeConfig = jsonMode ? { flags, commandName: 'epic spec' } : null;
76
+ const { selected } = await this.prompt([{
77
77
  type: 'list',
78
78
  name: 'selected',
79
79
  message: 'Select epic:',
80
80
  choices: epicChoices,
81
- }]);
81
+ }], jsonModeConfig);
82
82
  epicId = selected;
83
83
  }
84
84
  // Validate epic exists
@@ -114,18 +114,15 @@ export default class EpicSpec extends PMOCommand {
114
114
  const specChoices = specs.map(s => ({
115
115
  name: `${s.id} - ${s.title} (${s.status})`,
116
116
  value: s.id,
117
+ command: `prlt epic spec ${epicId} ${s.id} --json`,
117
118
  }));
118
- // In JSON mode, output spec selection prompt
119
- if (jsonMode) {
120
- outputPromptAsJson(buildPromptConfig('list', 'specId', `Select spec to link to ${epicId}:`, specChoices), createMetadata('epic spec', flags));
121
- return;
122
- }
123
- const { selected } = await inquirer.prompt([{
119
+ const jsonModeConfig = jsonMode ? { flags, commandName: 'epic spec' } : null;
120
+ const { selected } = await this.prompt([{
124
121
  type: 'list',
125
122
  name: 'selected',
126
123
  message: `Select spec to link to ${epicId}:`,
127
124
  choices: specChoices,
128
- }]);
125
+ }], jsonModeConfig);
129
126
  specId = selected;
130
127
  }
131
128
  // Validate spec exists
@@ -147,33 +144,35 @@ export default class EpicSpec extends PMOCommand {
147
144
  const epicTickets = await this.storage.getTicketsForEpic(projectId, epicId);
148
145
  const ticketsWithDifferentSpec = epicTickets.filter(t => t.specId && t.specId !== specId);
149
146
  if (ticketsWithDifferentSpec.length > 0) {
150
- // In JSON mode, output spec mismatch handling prompt
151
- if (jsonMode) {
147
+ // If --align-tickets flag provided, skip prompt and align all
148
+ let action;
149
+ if (flags['align-tickets']) {
150
+ action = 'align_all';
151
+ }
152
+ else {
153
+ if (!jsonMode) {
154
+ this.log(styles.warning(`\n⚠️ ${ticketsWithDifferentSpec.length} ticket(s) in this epic have different specs:`));
155
+ for (const t of ticketsWithDifferentSpec.slice(0, 5)) {
156
+ this.log(styles.muted(` - ${t.id}: spec "${t.specId}"`));
157
+ }
158
+ if (ticketsWithDifferentSpec.length > 5) {
159
+ this.log(styles.muted(` ... and ${ticketsWithDifferentSpec.length - 5} more`));
160
+ }
161
+ }
152
162
  const mismatchChoices = [
153
- { name: 'Update epic only (tickets keep their specs)', value: 'epic_only' },
154
- { name: `Update epic AND align all tickets to "${specId}"`, value: 'align_all' },
155
- { name: 'Cancel', value: 'cancel' },
163
+ { name: 'Update epic only (tickets keep their specs)', value: 'epic_only', command: `prlt epic spec ${epicId} ${specId} --json` },
164
+ { name: `Update epic AND align all tickets to "${specId}"`, value: 'align_all', command: `prlt epic spec ${epicId} ${specId} --align-tickets --json` },
165
+ { name: 'Cancel', value: 'cancel', command: '' },
156
166
  ];
157
- outputPromptAsJson(buildPromptConfig('list', 'specMismatchAction', `${ticketsWithDifferentSpec.length} ticket(s) in this epic have different specs. How to handle spec mismatch?`, mismatchChoices), createMetadata('epic spec', flags));
158
- return;
159
- }
160
- this.log(styles.warning(`\n⚠️ ${ticketsWithDifferentSpec.length} ticket(s) in this epic have different specs:`));
161
- for (const t of ticketsWithDifferentSpec.slice(0, 5)) {
162
- this.log(styles.muted(` - ${t.id}: spec "${t.specId}"`));
167
+ const jsonModeConfig = jsonMode ? { flags, commandName: 'epic spec' } : null;
168
+ const result = await this.prompt([{
169
+ type: 'list',
170
+ name: 'action',
171
+ message: `${ticketsWithDifferentSpec.length} ticket(s) in this epic have different specs. How to handle spec mismatch?`,
172
+ choices: mismatchChoices,
173
+ }], jsonModeConfig);
174
+ action = result.action;
163
175
  }
164
- if (ticketsWithDifferentSpec.length > 5) {
165
- this.log(styles.muted(` ... and ${ticketsWithDifferentSpec.length - 5} more`));
166
- }
167
- const { action } = await inquirer.prompt([{
168
- type: 'list',
169
- name: 'action',
170
- message: 'How to handle spec mismatch?',
171
- choices: [
172
- { name: 'Update epic only (tickets keep their specs)', value: 'epic_only' },
173
- { name: `Update epic AND align all tickets to "${specId}"`, value: 'align_all' },
174
- { name: 'Cancel', value: 'cancel' },
175
- ],
176
- }]);
177
176
  if (action === 'cancel') {
178
177
  return;
179
178
  }
@@ -12,6 +12,8 @@ export default class EpicTicket extends PMOCommand {
12
12
  unlink: import("@oclif/core/interfaces").BooleanFlag<boolean>;
13
13
  spec: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
14
14
  'unlink-spec': import("@oclif/core/interfaces").BooleanFlag<boolean>;
15
+ reconcile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
16
+ 'inherit-spec': import("@oclif/core/interfaces").BooleanFlag<boolean>;
15
17
  project: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
16
18
  };
17
19
  execute(): Promise<void>;
@@ -1,8 +1,7 @@
1
1
  import { Args, Flags } from '@oclif/core';
2
- import inquirer from 'inquirer';
3
2
  import { PMOCommand, pmoBaseFlags, autoExportToBoard } from '../../lib/pmo/index.js';
4
3
  import { styles } from '../../lib/styles.js';
5
- import { shouldOutputJson, outputPromptAsJson, outputErrorAsJson, createMetadata, buildPromptConfig, } from '../../lib/prompt-json.js';
4
+ import { shouldOutputJson, outputErrorAsJson, createMetadata, } from '../../lib/prompt-json.js';
6
5
  export default class EpicTicket extends PMOCommand {
7
6
  static description = 'Assign tickets to an epic, or link epic to a spec (parent-child)';
8
7
  static examples = [
@@ -45,6 +44,14 @@ export default class EpicTicket extends PMOCommand {
45
44
  description: 'Remove spec link from epic',
46
45
  default: false,
47
46
  }),
47
+ reconcile: Flags.string({
48
+ description: 'How to handle spec mismatch: keep (keep ticket spec), epic (use epic spec), skip',
49
+ options: ['keep', 'epic', 'skip'],
50
+ }),
51
+ 'inherit-spec': Flags.boolean({
52
+ description: 'Inherit spec from epic when ticket has no spec',
53
+ allowNo: true,
54
+ }),
48
55
  };
49
56
  async execute() {
50
57
  const { args, flags, argv } = await this.parse(EpicTicket);
@@ -100,18 +107,15 @@ export default class EpicTicket extends PMOCommand {
100
107
  const epicChoices = epics.map(e => ({
101
108
  name: `${e.id} ${e.title} (${e.status}) [${ticketCounts.get(e.id) || 0} tickets]`,
102
109
  value: e.id,
110
+ command: `prlt epic ticket ${e.id} --json`,
103
111
  }));
104
- // In JSON mode, output epic selection prompt
105
- if (jsonMode) {
106
- outputPromptAsJson(buildPromptConfig('list', 'id', 'Select epic to link tickets to:', epicChoices), createMetadata('epic ticket', flags));
107
- return;
108
- }
109
- const { selected } = await inquirer.prompt([{
112
+ const jsonModeConfig = jsonMode ? { flags, commandName: 'epic ticket' } : null;
113
+ const { selected } = await this.prompt([{
110
114
  type: 'list',
111
115
  name: 'selected',
112
116
  message: 'Select epic to link tickets to:',
113
117
  choices: epicChoices,
114
- }]);
118
+ }], jsonModeConfig);
115
119
  epicId = selected;
116
120
  }
117
121
  // Validate epic exists
@@ -169,19 +173,16 @@ export default class EpicTicket extends PMOCommand {
169
173
  name: `${t.id} - ${t.title} [${epicLabel}]`,
170
174
  value: t.id,
171
175
  checked: false,
176
+ command: `prlt epic ticket ${epicId} ${t.id} --json`,
172
177
  };
173
178
  });
174
- // In JSON mode, output ticket selection prompt
175
- if (jsonMode) {
176
- outputPromptAsJson(buildPromptConfig('checkbox', 'tickets', `Select tickets to ${flags.unlink ? 'unlink from' : 'link to'} ${epicId}:`, choices), createMetadata('epic ticket', flags));
177
- return;
178
- }
179
- const { selected } = await inquirer.prompt([{
179
+ const jsonModeConfig = jsonMode ? { flags, commandName: 'epic ticket' } : null;
180
+ const { selected } = await this.prompt([{
180
181
  type: 'checkbox',
181
182
  name: 'selected',
182
183
  message: `Select tickets to ${flags.unlink ? 'unlink from' : 'link to'} ${epicId}:`,
183
184
  choices,
184
- }]);
185
+ }], jsonModeConfig);
185
186
  ticketIds = selected;
186
187
  }
187
188
  if (ticketIds.length === 0) {
@@ -227,19 +228,31 @@ export default class EpicTicket extends PMOCommand {
227
228
  const ticketSpecId = ticket.specId;
228
229
  const epicSpecId = epic.specId;
229
230
  if (ticketSpecId && epicSpecId && ticketSpecId !== epicSpecId) {
230
- // Both have specs but they differ - warn user
231
- this.log(styles.warning(` ⚠️ Spec mismatch: ticket has "${ticketSpecId}", epic has "${epicSpecId}"`));
232
- // eslint-disable-next-line no-await-in-loop
233
- const { action } = await inquirer.prompt([{
234
- type: 'list',
235
- name: 'action',
236
- message: `How to reconcile spec for ${ticketId}?`,
237
- choices: [
238
- { name: `Keep ticket spec (${ticketSpecId})`, value: 'keep_ticket' },
239
- { name: `Use epic spec (${epicSpecId})`, value: 'use_epic' },
240
- { name: 'Skip this ticket', value: 'skip' },
241
- ],
242
- }]);
231
+ // Both have specs but they differ - determine action
232
+ let action;
233
+ // Check if --reconcile flag was provided
234
+ if (flags.reconcile) {
235
+ action = flags.reconcile === 'keep' ? 'keep_ticket' : flags.reconcile === 'epic' ? 'use_epic' : 'skip';
236
+ }
237
+ else {
238
+ if (!jsonMode) {
239
+ this.log(styles.warning(` ⚠️ Spec mismatch: ticket has "${ticketSpecId}", epic has "${epicSpecId}"`));
240
+ }
241
+ const specReconcileChoices = [
242
+ { name: `Keep ticket spec (${ticketSpecId})`, value: 'keep_ticket', command: `prlt epic ticket ${epicId} ${ticketId} --reconcile keep --json` },
243
+ { name: `Use epic spec (${epicSpecId})`, value: 'use_epic', command: `prlt epic ticket ${epicId} ${ticketId} --reconcile epic --json` },
244
+ { name: 'Skip this ticket', value: 'skip', command: `prlt epic ticket ${epicId} ${ticketId} --reconcile skip --json` },
245
+ ];
246
+ const jsonModeConfig = jsonMode ? { flags, commandName: 'epic ticket' } : null;
247
+ // eslint-disable-next-line no-await-in-loop
248
+ const result = await this.prompt([{
249
+ type: 'list',
250
+ name: 'action',
251
+ message: `Spec mismatch for ${ticketId}: ticket has "${ticketSpecId}", epic has "${epicSpecId}". How to reconcile?`,
252
+ choices: specReconcileChoices,
253
+ }], jsonModeConfig);
254
+ action = result.action;
255
+ }
243
256
  if (action === 'skip') {
244
257
  this.log(styles.muted(` Skipping ${ticketId}`));
245
258
  continue;
@@ -255,14 +268,27 @@ export default class EpicTicket extends PMOCommand {
255
268
  }
256
269
  }
257
270
  else if (!ticketSpecId && epicSpecId) {
258
- // Ticket has no spec but epic does - offer to inherit
259
- // eslint-disable-next-line no-await-in-loop
260
- const { inherit } = await inquirer.prompt([{
261
- type: 'confirm',
262
- name: 'inherit',
263
- message: `${ticketId} has no spec. Inherit epic's spec "${epicSpecId}"?`,
264
- default: true,
265
- }]);
271
+ // Ticket has no spec but epic does - determine if should inherit
272
+ let inherit;
273
+ // Check if --inherit-spec flag was provided
274
+ if (flags['inherit-spec'] !== undefined) {
275
+ inherit = flags['inherit-spec'];
276
+ }
277
+ else {
278
+ const inheritChoices = [
279
+ { name: 'Yes', value: true, command: `prlt epic ticket ${epicId} ${ticketId} --inherit-spec --json` },
280
+ { name: 'No', value: false, command: `prlt epic ticket ${epicId} ${ticketId} --no-inherit-spec --json` },
281
+ ];
282
+ const jsonModeConfig = jsonMode ? { flags, commandName: 'epic ticket' } : null;
283
+ // eslint-disable-next-line no-await-in-loop
284
+ const result = await this.prompt([{
285
+ type: 'list',
286
+ name: 'inherit',
287
+ message: `${ticketId} has no spec. Inherit epic's spec "${epicSpecId}"?`,
288
+ choices: inheritChoices,
289
+ }], jsonModeConfig);
290
+ inherit = result.inherit;
291
+ }
266
292
  if (inherit) {
267
293
  db.prepare(`
268
294
  UPDATE pmo_tickets
@@ -0,0 +1,10 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class Feedback extends Command {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ action: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
+ json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
8
+ };
9
+ run(): Promise<void>;
10
+ }
@@ -0,0 +1,60 @@
1
+ import { Command, Flags } from '@oclif/core';
2
+ import { isMachineOutput, } from '../../lib/prompt-json.js';
3
+ import { machineOutputFlags } from '../../lib/pmo/index.js';
4
+ import { FlagResolver } from '../../lib/flags/index.js';
5
+ export default class Feedback extends Command {
6
+ static description = 'Interactive menu for feedback and issue operations';
7
+ static examples = [
8
+ '<%= config.bin %> <%= command.id %>',
9
+ '<%= config.bin %> <%= command.id %> --action submit',
10
+ ];
11
+ static flags = {
12
+ ...machineOutputFlags,
13
+ action: Flags.string({
14
+ char: 'a',
15
+ description: 'Action to perform (submit, list, view)',
16
+ options: ['submit', 'list', 'view'],
17
+ }),
18
+ };
19
+ async run() {
20
+ const { flags } = await this.parse(Feedback);
21
+ // Check if JSON output mode is active
22
+ const jsonMode = isMachineOutput(flags);
23
+ // Use FlagResolver for action selection
24
+ const resolver = new FlagResolver({
25
+ commandName: 'feedback',
26
+ baseCommand: 'prlt feedback',
27
+ jsonMode,
28
+ flags: { action: flags.action },
29
+ });
30
+ resolver.addPrompt({
31
+ flagName: 'action',
32
+ type: 'list',
33
+ message: 'Feedback & Issues - What would you like to do?',
34
+ choices: () => [
35
+ { name: 'Submit feedback', value: 'submit', command: 'prlt feedback submit --json' },
36
+ { name: 'View my submissions', value: 'list', command: 'prlt feedback list --json' },
37
+ { name: 'Browse issues', value: 'view', command: 'prlt feedback list --json' },
38
+ { name: 'Cancel', value: 'cancel' },
39
+ ],
40
+ when: (ctx) => !ctx.flags.action,
41
+ skipAutoCommand: true,
42
+ });
43
+ const resolved = await resolver.resolve();
44
+ if (!resolved.action || resolved.action === 'cancel') {
45
+ return;
46
+ }
47
+ // Run the selected subcommand
48
+ switch (resolved.action) {
49
+ case 'submit':
50
+ await this.config.runCommand('feedback:submit', []);
51
+ break;
52
+ case 'list':
53
+ await this.config.runCommand('feedback:list', []);
54
+ break;
55
+ case 'view':
56
+ await this.config.runCommand('feedback:list', []);
57
+ break;
58
+ }
59
+ }
60
+ }
@@ -0,0 +1,12 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class FeedbackList extends Command {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ category: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
+ state: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
8
+ limit: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
9
+ json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
+ };
11
+ run(): Promise<void>;
12
+ }