@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
@@ -21,7 +21,7 @@ export default class ActionCreate extends PMOCommand {
21
21
  ...pmoBaseFlags,
22
22
  prompt: Flags.string({
23
23
  char: 'p',
24
- description: 'The prompt to send to the agent',
24
+ description: 'The prompt to send to the agent [required for non-interactive]',
25
25
  }),
26
26
  description: Flags.string({
27
27
  char: 'd',
@@ -85,11 +85,11 @@ export default class ActionCreate extends PMOCommand {
85
85
  requiredFields: ['name (as first argument)', '--prompt'],
86
86
  },
87
87
  });
88
- // Prompt input (editor type in interactive, but input for JSON mode context)
88
+ // Prompt input (multiline for inline text input)
89
89
  resolver.addPrompt({
90
90
  flagName: 'prompt',
91
- type: 'editor',
92
- message: 'Prompt (opens editor):',
91
+ type: 'multiline',
92
+ message: 'Prompt (agent instructions):',
93
93
  default: prompt || 'Enter the prompt that will be sent to the agent...',
94
94
  when: (ctx) => !ctx.flags.prompt && ctx.flags.name !== undefined,
95
95
  validate: (value) => value.trim() ? true : 'Prompt is required',
@@ -113,11 +113,11 @@ export default class ActionUpdate extends PMOCommand {
113
113
  currentValue: existingAction.description || '',
114
114
  },
115
115
  });
116
- // Prompt input (editor)
116
+ // Prompt input (multiline)
117
117
  resolver.addPrompt({
118
118
  flagName: 'prompt',
119
- type: 'editor',
120
- message: 'Prompt (opens editor):',
119
+ type: 'multiline',
120
+ message: 'Prompt (agent instructions):',
121
121
  default: existingAction.prompt,
122
122
  when: (ctx) => ctx.flags.name !== undefined && ctx.flags.description !== undefined,
123
123
  context: {
@@ -1,4 +1,4 @@
1
- import { PMOCommand } from '../../../lib/pmo/index.js';
1
+ import { PMOCommand } from '../../lib/pmo/index.js';
2
2
  export default class Cleanup extends PMOCommand {
3
3
  static description: string;
4
4
  static examples: string[];
@@ -1,9 +1,9 @@
1
1
  import { Args, Flags } from '@oclif/core';
2
2
  import inquirer from 'inquirer';
3
- import { colors, format } from '../../../lib/colors.js';
4
- import { getWorkspaceInfo, cleanupAgent, getCleanableAgents, getAgentTmuxSessions } from '../../../lib/agents/commands.js';
5
- import { PMOCommand, pmoBaseFlags } from '../../../lib/pmo/index.js';
6
- import { shouldOutputJson, outputPromptAsJson, outputErrorAsJson, outputSuccessAsJson, createMetadata, buildPromptConfig, } from '../../../lib/prompt-json.js';
3
+ import { colors, format } from '../../lib/colors.js';
4
+ import { getWorkspaceInfo, cleanupAgent, getCleanableAgents, getAgentTmuxSessions } from '../../lib/agents/commands.js';
5
+ import { PMOCommand, pmoBaseFlags } from '../../lib/pmo/index.js';
6
+ import { shouldOutputJson, outputPromptAsJson, outputErrorAsJson, outputSuccessAsJson, createMetadata, buildPromptConfig, } from '../../lib/prompt-json.js';
7
7
  export default class Cleanup extends PMOCommand {
8
8
  static description = 'Clean up agent resources (containers, directories, tmux sessions)';
9
9
  static examples = [
@@ -10,7 +10,7 @@ export default class Agent extends PMOCommand {
10
10
  '<%= config.bin %> <%= command.id %> visit tacoma',
11
11
  '<%= config.bin %> <%= command.id %> staff add',
12
12
  '<%= config.bin %> <%= command.id %> staff remove camry',
13
- '<%= config.bin %> <%= command.id %> temp cleanup --temp',
13
+ '<%= config.bin %> <%= command.id %> cleanup --temp',
14
14
  '<%= config.bin %> <%= command.id %> restart altman',
15
15
  '<%= config.bin %> <%= command.id %> rebuild altman',
16
16
  '<%= config.bin %> <%= command.id %> shell altman',
@@ -52,7 +52,7 @@ export default class Agent extends PMOCommand {
52
52
  { name: '🗑️ Remove agent', value: 'remove', command: 'prlt agent remove --machine' },
53
53
  // Management group
54
54
  { name: '👔 Manage staff agents', value: 'staff', command: 'prlt agent staff --machine' },
55
- { name: '⏱️ Manage temp agents', value: 'temp', command: 'prlt agent temp --machine' },
55
+ { name: '🧹 Cleanup agents', value: 'cleanup', command: 'prlt agent cleanup --machine' },
56
56
  { name: '🎨 Manage themes', value: 'themes', command: 'prlt agent themes --machine' },
57
57
  // Operations group
58
58
  { name: '🐚 Open shell', value: 'shell', command: 'prlt agent shell --machine' },
@@ -96,20 +96,20 @@ export default class Agent extends PMOCommand {
96
96
  break;
97
97
  }
98
98
  case 'staff': {
99
- const { default: StaffCommand } = await import('./staff/index.js');
99
+ const { default: StaffCommand } = await import('../staff/index.js');
100
100
  const cmd = new StaffCommand([], this.config);
101
101
  await cmd.run();
102
102
  break;
103
103
  }
104
- case 'temp': {
105
- const { default: TempCommand } = await import('./temp/index.js');
106
- const cmd = new TempCommand([], this.config);
104
+ case 'cleanup': {
105
+ const { default: CleanupCommand } = await import('./cleanup.js');
106
+ const cmd = new CleanupCommand([], this.config);
107
107
  await cmd.run();
108
108
  break;
109
109
  }
110
110
  case 'themes': {
111
- const { default: ThemesCommand } = await import('./themes/index.js');
112
- const cmd = new ThemesCommand([], this.config);
111
+ const { default: ThemeCommand } = await import('../theme/index.js');
112
+ const cmd = new ThemeCommand([], this.config);
113
113
  await cmd.run();
114
114
  break;
115
115
  }
@@ -38,7 +38,7 @@ export default class BranchCreate extends PMOCommand {
38
38
  }),
39
39
  type: Flags.string({
40
40
  char: 't',
41
- description: 'Branch type',
41
+ description: 'Branch type [required for non-interactive with -d]',
42
42
  options: Object.keys(BRANCH_TYPES),
43
43
  }),
44
44
  owner: Flags.string({
@@ -47,7 +47,7 @@ export default class BranchCreate extends PMOCommand {
47
47
  }),
48
48
  description: Flags.string({
49
49
  char: 'd',
50
- description: 'Branch description (kebab-case)',
50
+ description: 'Branch description (kebab-case) [required for non-interactive with -t]',
51
51
  }),
52
52
  'empty-commit': Flags.boolean({
53
53
  char: 'e',
@@ -1,9 +1,8 @@
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
4
  import { moveEpicFile, getRelativeEpicPath } from '../../lib/pmo/epic-files.js';
6
- import { shouldOutputJson, outputPromptAsJson, outputErrorAsJson, createMetadata, buildPromptConfig, } from '../../lib/prompt-json.js';
5
+ import { shouldOutputJson, outputErrorAsJson, createMetadata, } from '../../lib/prompt-json.js';
7
6
  export default class EpicActivate extends PMOCommand {
8
7
  static description = 'Activate a draft or archived epic';
9
8
  static examples = [
@@ -76,26 +75,19 @@ export default class EpicActivate extends PMOCommand {
76
75
  if (epic.status === 'complete') {
77
76
  const tickets = await this.storage.getTicketsForEpic(projectId, epicId);
78
77
  const doneTickets = tickets.filter((t) => t.status === 'done').length;
79
- // In JSON mode, output confirmation prompt
80
- if (jsonMode) {
81
- const confirmChoices = [
82
- { name: 'No', value: 'false' },
83
- { name: 'Yes', value: 'true' },
84
- ];
85
- outputPromptAsJson(buildPromptConfig('list', 'confirm', `This epic was previously completed (${doneTickets}/${tickets.length} tickets done). Reactivate this epic?`, confirmChoices), createMetadata('epic activate', flags));
86
- return;
78
+ if (!jsonMode) {
79
+ this.log(styles.warning(`\n⚠️ This epic was previously completed (${doneTickets}/${tickets.length} tickets done)`));
87
80
  }
88
- this.log(styles.warning(`\n⚠️ This epic was previously completed (${doneTickets}/${tickets.length} tickets done)`));
89
- const { confirm } = await inquirer.prompt([{
81
+ const jsonModeConfig = jsonMode ? { flags, commandName: 'epic activate' } : null;
82
+ const { confirm } = await this.prompt([{
90
83
  type: 'list',
91
84
  name: 'confirm',
92
- message: 'Reactivate this epic?',
85
+ message: `This epic was previously completed (${doneTickets}/${tickets.length} tickets done). Reactivate this epic?`,
93
86
  choices: [
94
- { name: 'No', value: false },
95
- { name: 'Yes', value: true },
87
+ { name: 'No', value: false, command: '' },
88
+ { name: 'Yes', value: true, command: `prlt epic activate ${epicId} --json` },
96
89
  ],
97
- default: false,
98
- }]);
90
+ }], jsonModeConfig);
99
91
  if (!confirm) {
100
92
  this.log(styles.muted('Cancelled.'));
101
93
  return;
@@ -1,9 +1,8 @@
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
4
  import { moveEpicFile, getRelativeEpicPath } from '../../lib/pmo/epic-files.js';
6
- import { shouldOutputJson, outputPromptAsJson, outputErrorAsJson, createMetadata, buildPromptConfig, } from '../../lib/prompt-json.js';
5
+ import { shouldOutputJson, outputErrorAsJson, createMetadata, } from '../../lib/prompt-json.js';
7
6
  export default class EpicArchive extends PMOCommand {
8
7
  static description = 'Archive a completed epic';
9
8
  static examples = [
@@ -64,19 +63,16 @@ export default class EpicArchive extends PMOCommand {
64
63
  return {
65
64
  name: `${e.id} ${e.title} (${e.status}) [${done}/${tickets.length} tickets complete]${complete ? ' ✅' : ''}`,
66
65
  value: e.id,
66
+ command: `prlt epic archive ${e.id} --json`,
67
67
  };
68
68
  }));
69
- // In JSON mode, output epic selection prompt
70
- if (jsonMode) {
71
- outputPromptAsJson(buildPromptConfig('list', 'id', 'Select epic to archive:', choices), createMetadata('epic archive', flags));
72
- return;
73
- }
74
- const { selected } = await inquirer.prompt([{
69
+ const jsonModeConfig = jsonMode ? { flags, commandName: 'epic archive' } : null;
70
+ const { selected } = await this.prompt([{
75
71
  type: 'list',
76
72
  name: 'selected',
77
73
  message: 'Select epic to archive:',
78
74
  choices,
79
- }]);
75
+ }], jsonModeConfig);
80
76
  epicId = selected;
81
77
  }
82
78
  const epic = await this.storage.getEpic(epicId);
@@ -92,26 +88,19 @@ export default class EpicArchive extends PMOCommand {
92
88
  const doneTickets = tickets.filter((t) => t.status === 'done').length;
93
89
  const allComplete = doneTickets === tickets.length;
94
90
  if (!allComplete && !flags.force) {
95
- // In JSON mode, output confirmation prompt
96
- if (jsonMode) {
97
- const confirmChoices = [
98
- { name: 'No', value: 'false' },
99
- { name: 'Yes', value: 'true' },
100
- ];
101
- outputPromptAsJson(buildPromptConfig('list', 'confirm', `Not all tickets are complete (${doneTickets}/${tickets.length} done). Continue archiving anyway?`, confirmChoices), createMetadata('epic archive', flags));
102
- return;
91
+ if (!jsonMode) {
92
+ this.log(styles.warning(`\n⚠️ Not all tickets are complete (${doneTickets}/${tickets.length} done)`));
103
93
  }
104
- this.log(styles.warning(`\n⚠️ Not all tickets are complete (${doneTickets}/${tickets.length} done)`));
105
- const { confirm } = await inquirer.prompt([{
94
+ const jsonModeConfig = jsonMode ? { flags, commandName: 'epic archive' } : null;
95
+ const { confirm } = await this.prompt([{
106
96
  type: 'list',
107
97
  name: 'confirm',
108
- message: 'Continue archiving anyway?',
98
+ message: `Not all tickets are complete (${doneTickets}/${tickets.length} done). Continue archiving anyway?`,
109
99
  choices: [
110
- { name: 'No', value: false },
111
- { name: 'Yes', value: true },
100
+ { name: 'No', value: false, command: '' },
101
+ { name: 'Yes', value: true, command: `prlt epic archive ${epicId} --force --json` },
112
102
  ],
113
- default: false,
114
- }]);
103
+ }], jsonModeConfig);
115
104
  if (!confirm) {
116
105
  this.log(styles.muted('Cancelled.'));
117
106
  return;
@@ -8,6 +8,7 @@ export default class EpicCreate extends PMOCommand {
8
8
  description: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
9
  spec: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
10
  json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
+ 'dry-run': import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
12
  project: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
13
  };
13
14
  execute(): Promise<void>;
@@ -1,9 +1,8 @@
1
1
  import { 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
4
  import { createEpicFile, getRelativeEpicPath } from '../../lib/pmo/epic-files.js';
6
- import { shouldOutputJson, outputPromptAsJson, createMetadata, buildFormPromptConfig, } from '../../lib/prompt-json.js';
5
+ import { shouldOutputJson, outputPromptAsJson, outputDryRunSuccessAsJson, outputDryRunErrorsAsJson, createMetadata, buildFormPromptConfig, } from '../../lib/prompt-json.js';
7
6
  export default class EpicCreate extends PMOCommand {
8
7
  static description = 'Create a new epic';
9
8
  static examples = [
@@ -11,12 +10,13 @@ export default class EpicCreate extends PMOCommand {
11
10
  '<%= config.bin %> <%= command.id %> --title "User Authentication System"',
12
11
  '<%= config.bin %> <%= command.id %> -t "API Design" --status draft',
13
12
  '<%= config.bin %> <%= command.id %> -t "Implement Auth" --spec SPEC-001',
13
+ '<%= config.bin %> <%= command.id %> --title "Test" -P PROJ-001 --dry-run --json # Validate without creating',
14
14
  ];
15
15
  static flags = {
16
16
  ...pmoBaseFlags,
17
17
  title: Flags.string({
18
18
  char: 't',
19
- description: 'Epic title',
19
+ description: 'Epic title [required for non-interactive]',
20
20
  }),
21
21
  status: Flags.string({
22
22
  char: 's',
@@ -37,6 +37,10 @@ export default class EpicCreate extends PMOCommand {
37
37
  description: 'Output prompt configuration as JSON (for AI agents/scripts)',
38
38
  default: false,
39
39
  }),
40
+ 'dry-run': Flags.boolean({
41
+ description: 'Validate inputs without creating epic (use with --json for structured output)',
42
+ default: false,
43
+ }),
40
44
  };
41
45
  async execute() {
42
46
  const { flags } = await this.parse(EpicCreate);
@@ -76,7 +80,7 @@ export default class EpicCreate extends PMOCommand {
76
80
  if (jsonMode) {
77
81
  outputPromptAsJson(buildFormPromptConfig(fields), createMetadata('epic create', flags));
78
82
  }
79
- epicData = await this.promptEpicData(fields, specChoices.length > 1);
83
+ epicData = await this.promptEpicData(fields, specChoices.length > 1, jsonMode ? { flags, commandName: 'epic create' } : null);
80
84
  }
81
85
  else {
82
86
  epicData = {
@@ -90,9 +94,41 @@ export default class EpicCreate extends PMOCommand {
90
94
  if (epicData.specId) {
91
95
  const spec = await this.storage.getSpec(epicData.specId);
92
96
  if (!spec) {
97
+ if (flags['dry-run']) {
98
+ if (jsonMode) {
99
+ outputDryRunErrorsAsJson([{ field: 'spec', error: `Spec not found: ${epicData.specId}` }], createMetadata('epic create', flags));
100
+ }
101
+ }
93
102
  this.error(`Spec not found: ${epicData.specId}`);
94
103
  }
95
104
  }
105
+ // Handle dry-run: show what would be created without actually creating
106
+ if (flags['dry-run']) {
107
+ const projectName = await this.getProjectName(projectId);
108
+ const wouldCreate = {
109
+ title: epicData.title,
110
+ project: projectId,
111
+ status: epicData.status,
112
+ ...(epicData.description && { description: epicData.description }),
113
+ ...(epicData.specId && { spec: epicData.specId }),
114
+ };
115
+ if (jsonMode) {
116
+ outputDryRunSuccessAsJson('epic', wouldCreate, createMetadata('epic create', flags));
117
+ }
118
+ // Human-readable dry-run output
119
+ this.log(styles.warning('\n[DRY RUN] Would create epic:'));
120
+ this.log(styles.muted(` Title: ${epicData.title}`));
121
+ this.log(styles.muted(` Project: ${projectName}`));
122
+ this.log(styles.muted(` Status: ${epicData.status}`));
123
+ if (epicData.description) {
124
+ this.log(styles.muted(` Description: ${epicData.description}`));
125
+ }
126
+ if (epicData.specId) {
127
+ this.log(styles.muted(` Spec: ${epicData.specId}`));
128
+ }
129
+ this.log(styles.muted('\n(No epic was created)'));
130
+ return;
131
+ }
96
132
  const epic = await this.storage.createEpic(projectId, {
97
133
  title: epicData.title,
98
134
  status: epicData.status,
@@ -120,15 +156,17 @@ export default class EpicCreate extends PMOCommand {
120
156
  this.log(styles.muted(` prlt ticket create --epic ${epic.id} "Design auth flow"`));
121
157
  this.log(styles.muted(` 3. View progress: prlt epic progress ${epic.id}`));
122
158
  }
123
- async promptEpicData(fields, hasSpecs) {
159
+ async promptEpicData(fields, hasSpecs, jsonModeConfig) {
124
160
  // Build inquirer prompts from fields, adding validators and conditionals
125
- const answers = await inquirer.prompt(fields.map(field => ({
161
+ const promptFields = fields
162
+ .filter(field => field.name !== 'specId' || hasSpecs)
163
+ .map(field => ({
126
164
  ...field,
127
165
  validate: field.name === 'title'
128
166
  ? ((input) => input.trim() ? true : 'Title cannot be empty')
129
167
  : undefined,
130
- when: field.name === 'specId' ? () => hasSpecs : undefined,
131
- })));
168
+ }));
169
+ const answers = await this.prompt(promptFields, jsonModeConfig);
132
170
  return {
133
171
  title: answers.title,
134
172
  status: answers.status,
@@ -31,7 +31,7 @@ export default class Epic extends PMOCommand {
31
31
  { id: 'progress', name: 'Show progress', command: 'prlt epic progress --json' },
32
32
  { id: 'ticket', name: 'Assign tickets to epic', command: 'prlt epic ticket --json' },
33
33
  { id: 'spec', name: 'Assign spec to epic', command: 'prlt epic spec --json' },
34
- { id: 'link', name: 'Manage dependencies', command: 'prlt epic link --json' },
34
+ { id: 'link', name: 'Manage dependencies', command: 'prlt link list --json' },
35
35
  { id: 'archive', name: 'Archive epic (complete)', command: 'prlt epic archive --json' },
36
36
  { id: 'activate', name: 'Activate epic', command: 'prlt epic activate --json' },
37
37
  { id: 'move', name: 'Reorder epic', command: 'prlt epic move --json' },
@@ -72,7 +72,7 @@ export default class Epic extends PMOCommand {
72
72
  await this.config.runCommand('epic:spec', []);
73
73
  break;
74
74
  case 'link':
75
- await this.config.runCommand('epic:link', []);
75
+ await this.config.runCommand('link', []);
76
76
  break;
77
77
  case 'archive':
78
78
  await this.config.runCommand('epic:archive', []);
@@ -1,9 +1,8 @@
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
4
  import { moveEpicFile, getRelativeEpicPath } from '../../lib/pmo/epic-files.js';
6
- import { shouldOutputJson, outputPromptAsJson, outputErrorAsJson, createMetadata, buildPromptConfig, } from '../../lib/prompt-json.js';
5
+ import { shouldOutputJson, outputErrorAsJson, createMetadata, } from '../../lib/prompt-json.js';
7
6
  const STATUS_CHOICES = [
8
7
  { name: 'active (currently working on)', value: 'active' },
9
8
  { name: 'draft (planning phase)', value: 'draft' },
@@ -75,19 +74,16 @@ export default class EpicMove extends PMOCommand {
75
74
  return {
76
75
  name: `${e.id} ${e.title} (${e.status}) [${done}/${tickets.length} complete]`,
77
76
  value: e.id,
77
+ command: `prlt epic move ${e.id} --json`,
78
78
  };
79
79
  }));
80
- // In JSON mode, output epic selection prompt
81
- if (jsonMode) {
82
- outputPromptAsJson(buildPromptConfig('list', 'id', 'Select epic to move:', choices), createMetadata('epic move', flags));
83
- return;
84
- }
85
- const { selected } = await inquirer.prompt([{
80
+ const jsonModeConfig = jsonMode ? { flags, commandName: 'epic move' } : null;
81
+ const { selected } = await this.prompt([{
86
82
  type: 'list',
87
83
  name: 'selected',
88
84
  message: 'Select epic to move:',
89
85
  choices,
90
- }]);
86
+ }], jsonModeConfig);
91
87
  epicId = selected;
92
88
  }
93
89
  const epic = await this.storage.getEpic(epicId);
@@ -96,18 +92,17 @@ export default class EpicMove extends PMOCommand {
96
92
  }
97
93
  // If no status provided, prompt for selection
98
94
  if (!targetStatus) {
99
- const statusChoices = STATUS_CHOICES.filter(c => c.value !== epic.status);
100
- // In JSON mode, output status selection prompt
101
- if (jsonMode) {
102
- outputPromptAsJson(buildPromptConfig('list', 'status', 'Move to which status?', statusChoices), createMetadata('epic move', flags));
103
- return;
104
- }
105
- const { selected } = await inquirer.prompt([{
95
+ const statusChoices = STATUS_CHOICES.filter(c => c.value !== epic.status).map(c => ({
96
+ ...c,
97
+ command: `prlt epic move ${epicId} ${c.value} --json`,
98
+ }));
99
+ const jsonModeConfig = jsonMode ? { flags, commandName: 'epic move' } : null;
100
+ const { selected } = await this.prompt([{
106
101
  type: 'list',
107
102
  name: 'selected',
108
103
  message: 'Move to which status?',
109
104
  choices: statusChoices,
110
- }]);
105
+ }], jsonModeConfig);
111
106
  targetStatus = selected;
112
107
  }
113
108
  if (targetStatus === epic.status) {
@@ -120,26 +115,19 @@ export default class EpicMove extends PMOCommand {
120
115
  const allComplete = doneTickets === tickets.length;
121
116
  // Moving to complete - check ticket completion
122
117
  if (targetStatus === 'complete' && !allComplete && !flags.force) {
123
- // In JSON mode, output confirmation prompt
124
- if (jsonMode) {
125
- const confirmChoices = [
126
- { name: 'No', value: 'false' },
127
- { name: 'Yes', value: 'true' },
128
- ];
129
- outputPromptAsJson(buildPromptConfig('list', 'confirm', `Not all tickets are complete (${doneTickets}/${tickets.length} done). Continue moving to complete anyway?`, confirmChoices), createMetadata('epic move', flags));
130
- return;
118
+ if (!jsonMode) {
119
+ this.log(styles.warning(`\n⚠️ Not all tickets are complete (${doneTickets}/${tickets.length} done)`));
131
120
  }
132
- this.log(styles.warning(`\n⚠️ Not all tickets are complete (${doneTickets}/${tickets.length} done)`));
133
- const { confirm } = await inquirer.prompt([{
121
+ const jsonModeConfig = jsonMode ? { flags, commandName: 'epic move' } : null;
122
+ const { confirm } = await this.prompt([{
134
123
  type: 'list',
135
124
  name: 'confirm',
136
- message: 'Continue moving to complete anyway?',
125
+ message: `Not all tickets are complete (${doneTickets}/${tickets.length} done). Continue moving to complete anyway?`,
137
126
  choices: [
138
- { name: 'No', value: false },
139
- { name: 'Yes', value: true },
127
+ { name: 'No', value: false, command: '' },
128
+ { name: 'Yes', value: true, command: `prlt epic move ${epicId} complete --force --json` },
140
129
  ],
141
- default: false,
142
- }]);
130
+ }], jsonModeConfig);
143
131
  if (!confirm) {
144
132
  this.log(styles.muted('Cancelled.'));
145
133
  return;
@@ -147,26 +135,19 @@ export default class EpicMove extends PMOCommand {
147
135
  }
148
136
  // Moving to dropped - confirm cancellation
149
137
  if (targetStatus === 'dropped' && !flags.force) {
150
- // In JSON mode, output confirmation prompt
151
- if (jsonMode) {
152
- const confirmChoices = [
153
- { name: 'No', value: 'false' },
154
- { name: 'Yes', value: 'true' },
155
- ];
156
- outputPromptAsJson(buildPromptConfig('list', 'confirmDrop', 'This will mark the epic as dropped/cancelled. Continue?', confirmChoices), createMetadata('epic move', flags));
157
- return;
138
+ if (!jsonMode) {
139
+ this.log(styles.warning('\n⚠️ This will mark the epic as dropped/cancelled'));
158
140
  }
159
- this.log(styles.warning('\n⚠️ This will mark the epic as dropped/cancelled'));
160
- const { confirm } = await inquirer.prompt([{
141
+ const jsonModeConfig = jsonMode ? { flags, commandName: 'epic move' } : null;
142
+ const { confirm } = await this.prompt([{
161
143
  type: 'list',
162
144
  name: 'confirm',
163
- message: 'Continue?',
145
+ message: 'This will mark the epic as dropped/cancelled. Continue?',
164
146
  choices: [
165
- { name: 'No', value: false },
166
- { name: 'Yes', value: true },
147
+ { name: 'No', value: false, command: '' },
148
+ { name: 'Yes', value: true, command: `prlt epic move ${epicId} dropped --force --json` },
167
149
  ],
168
- default: false,
169
- }]);
150
+ }], jsonModeConfig);
170
151
  if (!confirm) {
171
152
  this.log(styles.muted('Cancelled.'));
172
153
  return;
@@ -1,9 +1,8 @@
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
4
  import { getRelativeEpicPath } from '../../lib/pmo/epic-files.js';
6
- import { shouldOutputJson, outputPromptAsJson, outputErrorAsJson, createMetadata, buildPromptConfig, } from '../../lib/prompt-json.js';
5
+ import { shouldOutputJson, outputErrorAsJson, createMetadata, } from '../../lib/prompt-json.js';
7
6
  // Progress bar helper
8
7
  function progressBar(percent, width = 20) {
9
8
  const filled = Math.round((percent / 100) * width);
@@ -57,21 +56,18 @@ export default class EpicProgress extends PMOCommand {
57
56
  this.log(styles.muted('\nNo epics found.'));
58
57
  return;
59
58
  }
60
- // In JSON mode, output epic selection prompt
61
- if (jsonMode) {
62
- const epicChoices = epics.map(e => ({ name: `${e.id} ${e.title} (${e.status})`, value: e.id }));
63
- outputPromptAsJson(buildPromptConfig('list', 'id', 'Select epic to view progress:', epicChoices), createMetadata('epic progress', flags));
64
- return;
65
- }
66
- const { selected } = await inquirer.prompt([{
59
+ const epicChoices = epics.map(e => ({
60
+ name: `${e.id} ${e.title} (${e.status})`,
61
+ value: e.id,
62
+ command: `prlt epic progress ${e.id} --json`,
63
+ }));
64
+ const jsonModeConfig = jsonMode ? { flags, commandName: 'epic progress' } : null;
65
+ const { selected } = await this.prompt([{
67
66
  type: 'list',
68
67
  name: 'selected',
69
68
  message: 'Select epic to view progress:',
70
- choices: epics.map(e => ({
71
- name: `${e.id} ${e.title} (${e.status})`,
72
- value: e.id,
73
- })),
74
- }]);
69
+ choices: epicChoices,
70
+ }], jsonModeConfig);
75
71
  epicId = selected;
76
72
  }
77
73
  await this.showSingleProgress(projectId, epicId);