@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,63 +0,0 @@
1
- import { 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 SpecLinkDuplicates extends PMOCommand {
7
- static description = 'Mark a spec as duplicate of another';
8
- static examples = ['<%= config.bin %> <%= command.id %> my-feature other-spec'];
9
- static args = {
10
- id: Args.string({ description: 'Duplicate spec ID', required: true }),
11
- original: Args.string({ description: 'Original spec ID', required: false }),
12
- };
13
- static flags = {
14
- ...pmoBaseFlags,
15
- };
16
- async execute() {
17
- const { args, flags } = await this.parse(SpecLinkDuplicates);
18
- // Check if JSON output mode is active
19
- const jsonMode = shouldOutputJson(flags);
20
- // Helper to handle errors in JSON mode
21
- const handleError = (code, message) => {
22
- if (jsonMode) {
23
- outputErrorAsJson(code, message, createMetadata('spec link duplicates', flags));
24
- this.exit(1);
25
- }
26
- this.error(message);
27
- };
28
- const spec = await this.storage.getSpec(args.id);
29
- if (!spec)
30
- return handleError('SPEC_NOT_FOUND', `Spec not found: ${args.id}`);
31
- let originalId = args.original;
32
- if (!originalId) {
33
- const allSpecs = await this.storage.listSpecs();
34
- const otherSpecs = allSpecs.filter(s => s.id !== args.id);
35
- if (otherSpecs.length === 0) {
36
- if (jsonMode) {
37
- outputErrorAsJson('NO_OTHER_SPECS', 'No other specs.', createMetadata('spec link duplicates', flags));
38
- return;
39
- }
40
- this.log(styles.muted('\nNo other specs.'));
41
- return;
42
- }
43
- // In JSON mode, output spec selection prompt
44
- if (jsonMode) {
45
- const specChoices = otherSpecs.map(s => ({
46
- name: `${s.id} - ${s.title}`,
47
- value: s.id,
48
- command: `prlt spec link duplicates ${args.id} ${s.id} --json`,
49
- }));
50
- outputPromptAsJson(buildPromptConfig('list', 'original', `Select the original spec (${args.id} is a duplicate of):`, specChoices), createMetadata('spec link duplicates', flags));
51
- return;
52
- }
53
- const { selected } = await inquirer.prompt([{ type: 'list', name: 'selected', message: `Select the original spec (${args.id} is a duplicate of):`,
54
- choices: otherSpecs.map(s => ({ name: `${s.id} - ${s.title}`, value: s.id })) }]);
55
- originalId = selected;
56
- }
57
- const originalSpec = await this.storage.getSpec(originalId);
58
- if (!originalSpec)
59
- this.error(`Spec not found: ${originalId}`);
60
- await this.storage.createSpecDependency(args.id, originalId, 'duplicates');
61
- this.log(styles.success(`\n✅ ${styles.emphasis(args.id)} duplicates ${styles.emphasis(originalId)}`));
62
- }
63
- }
@@ -1,19 +0,0 @@
1
- import { PMOCommand } from '../../../lib/pmo/index.js';
2
- export default class SpecLink extends PMOCommand {
3
- static description: string;
4
- static examples: string[];
5
- static args: {
6
- id: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
7
- };
8
- static flags: {
9
- depends: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
- relates: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
- duplicates: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
- all: import("@oclif/core/interfaces").BooleanFlag<boolean>;
13
- json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
14
- project: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
15
- };
16
- execute(): Promise<void>;
17
- private addDependency;
18
- private viewDependencies;
19
- }
@@ -1,207 +0,0 @@
1
- import { Args, Flags } 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 SpecLink extends PMOCommand {
7
- static description = 'Manage spec dependencies (links)';
8
- static examples = [
9
- '<%= config.bin %> <%= command.id %> my-feature # List dependencies',
10
- '<%= config.bin %> <%= command.id %> my-feature --depends other-spec # my-feature depends on other-spec',
11
- '<%= config.bin %> <%= command.id %> my-feature --relates other-spec # my-feature relates to other-spec',
12
- '<%= config.bin %> <%= command.id %> my-feature --duplicates other-spec',
13
- '<%= config.bin %> <%= command.id %> my-feature --all # Show all links',
14
- ];
15
- static args = {
16
- id: Args.string({ description: 'Spec ID', required: false }),
17
- };
18
- static flags = {
19
- ...pmoBaseFlags,
20
- depends: Flags.string({ char: 'd', description: 'Add depends_on dependency' }),
21
- relates: Flags.string({ char: 'r', description: 'Add relates_to dependency' }),
22
- duplicates: Flags.string({ description: 'Add duplicates dependency' }),
23
- all: Flags.boolean({ char: 'a', description: 'Show all dependencies', default: false }),
24
- };
25
- async execute() {
26
- const { args, flags } = await this.parse(SpecLink);
27
- // Check if JSON output mode is active
28
- const jsonMode = shouldOutputJson(flags);
29
- let specId = args.id;
30
- if (!specId) {
31
- const specs = await this.storage.listSpecs();
32
- if (specs.length === 0) {
33
- if (jsonMode) {
34
- outputErrorAsJson('NO_SPECS', 'No specs found.', createMetadata('spec link', flags));
35
- return;
36
- }
37
- this.log(styles.muted('\nNo specs found.'));
38
- return;
39
- }
40
- // In JSON mode, output spec selection prompt
41
- if (jsonMode) {
42
- const specChoices = specs.map(s => ({ name: `${s.id} - ${s.title}`, value: s.id }));
43
- outputPromptAsJson(buildPromptConfig('list', 'id', 'Select spec to manage dependencies:', specChoices), createMetadata('spec link', flags));
44
- return;
45
- }
46
- const { selected } = await inquirer.prompt([{
47
- type: 'list',
48
- name: 'selected',
49
- message: 'Select spec to manage dependencies:',
50
- choices: specs.map(s => ({ name: `${s.id} - ${s.title}`, value: s.id })),
51
- }]);
52
- specId = selected;
53
- }
54
- const spec = await this.storage.getSpec(specId);
55
- if (!spec)
56
- this.error(`Spec not found: ${specId}`);
57
- // If a dependency flag is provided, add the dependency directly
58
- if (flags.depends || flags.relates || flags.duplicates) {
59
- const targetId = flags.depends || flags.relates || flags.duplicates;
60
- const dependencyType = flags.depends ? 'depends_on' :
61
- flags.relates ? 'relates_to' : 'duplicates';
62
- await this.addDependency(specId, targetId, dependencyType, spec.title);
63
- return;
64
- }
65
- // Interactive mode: show menu in a loop
66
- let continueLoop = true;
67
- while (continueLoop) {
68
- // eslint-disable-next-line no-await-in-loop -- Interactive user loop
69
- const allSpecs = await this.storage.listSpecs();
70
- const otherSpecs = allSpecs.filter(s => s.id !== specId);
71
- // eslint-disable-next-line no-await-in-loop -- Interactive user prompt
72
- const { action } = await inquirer.prompt([{
73
- type: 'list',
74
- name: 'action',
75
- message: `Dependencies for ${spec.id}:`,
76
- choices: [
77
- { name: 'View dependencies', value: 'view' },
78
- { name: 'Add depends_on dependency', value: 'depends_on' },
79
- { name: 'Add relates_to dependency', value: 'relates_to' },
80
- { name: 'Add duplicates dependency', value: 'duplicates' },
81
- new inquirer.Separator(),
82
- { name: 'Remove dependency', value: 'remove' },
83
- { name: 'Done', value: 'done' },
84
- ],
85
- }]);
86
- if (action === 'done') {
87
- continueLoop = false;
88
- continue;
89
- }
90
- if (action === 'view') {
91
- // eslint-disable-next-line no-await-in-loop -- User action handling
92
- await this.viewDependencies(specId, spec, flags.all);
93
- continue;
94
- }
95
- if (action === 'remove') {
96
- // eslint-disable-next-line no-await-in-loop -- User action handling
97
- const dependencies = await this.storage.listSpecDependencies(specId);
98
- if (dependencies.length === 0) {
99
- this.log(styles.muted('\nNo dependencies to remove.'));
100
- continue;
101
- }
102
- // eslint-disable-next-line no-await-in-loop -- Building choices for current interaction
103
- const choices = await Promise.all(dependencies.map(async (dep) => {
104
- const depSpec = await this.storage.getSpec(dep.dependsOnSpecId);
105
- return {
106
- name: `${dep.dependsOnSpecId} - ${depSpec?.title || 'Unknown'} (${dep.dependencyType})`,
107
- value: { targetId: dep.dependsOnSpecId, type: dep.dependencyType }
108
- };
109
- }));
110
- // eslint-disable-next-line no-await-in-loop -- User selection prompt
111
- const { selected } = await inquirer.prompt([{
112
- type: 'list',
113
- name: 'selected',
114
- message: 'Select dependency to remove:',
115
- choices,
116
- }]);
117
- // eslint-disable-next-line no-await-in-loop -- Action after user selection
118
- await this.storage.deleteSpecDependency(specId, selected.targetId, selected.type);
119
- this.log(styles.success(`\n✅ Removed dependency: ${specId} → ${selected.targetId}`));
120
- continue;
121
- }
122
- // Add dependency
123
- if (otherSpecs.length === 0) {
124
- this.log(styles.muted('\nNo other specs to link to.'));
125
- continue;
126
- }
127
- // eslint-disable-next-line no-await-in-loop -- User selection prompt
128
- const { targetId } = await inquirer.prompt([{
129
- type: 'list',
130
- name: 'targetId',
131
- message: `Select spec that ${specId} ${action === 'depends_on' ? 'depends on' : action === 'relates_to' ? 'relates to' : 'duplicates'}:`,
132
- choices: otherSpecs.map(s => ({ name: `${s.id} - ${s.title}`, value: s.id })),
133
- }]);
134
- // eslint-disable-next-line no-await-in-loop -- Action after user selection
135
- await this.addDependency(specId, targetId, action, spec.title);
136
- }
137
- }
138
- async addDependency(specId, targetId, dependencyType, specTitle) {
139
- const targetSpec = await this.storage.getSpec(targetId);
140
- if (!targetSpec) {
141
- this.error(`Spec not found: ${targetId}`);
142
- }
143
- try {
144
- await this.storage.createSpecDependency(specId, targetId, dependencyType);
145
- const typeLabel = dependencyType === 'depends_on' ? 'depends on' :
146
- dependencyType === 'relates_to' ? 'relates to' : 'duplicates';
147
- this.log(styles.success(`\n✅ ${styles.emphasis(specId)} ${typeLabel} ${styles.emphasis(targetId)}`));
148
- this.log(styles.muted(` ${specTitle}`));
149
- this.log(styles.muted(` ${typeLabel} ${targetSpec.title}`));
150
- }
151
- catch (error) {
152
- if (error instanceof Error) {
153
- if (error.message.includes('already exists')) {
154
- this.error('Dependency already exists');
155
- }
156
- if (error.message.includes('self-dependency')) {
157
- this.error('Cannot create self-dependency');
158
- }
159
- }
160
- throw error;
161
- }
162
- }
163
- async viewDependencies(specId, spec, showAll) {
164
- const dependencies = await this.storage.listSpecDependencies(specId);
165
- this.log(`\n${styles.emphasis(spec.id)}: ${spec.title}`);
166
- const dependsOn = dependencies.filter(d => d.dependencyType === 'depends_on');
167
- if (dependsOn.length > 0) {
168
- this.log(styles.muted('\n Depends on:'));
169
- // Fetch all dependency specs in parallel
170
- const depSpecs = await Promise.all(dependsOn.map(dep => this.storage.getSpec(dep.dependsOnSpecId)));
171
- for (const depSpec of depSpecs) {
172
- if (depSpec)
173
- this.log(` - ${depSpec.id}: ${depSpec.title}`);
174
- }
175
- }
176
- const otherDeps = dependencies.filter(d => d.dependencyType !== 'depends_on');
177
- if (otherDeps.length > 0) {
178
- this.log(styles.muted('\n Related:'));
179
- // Fetch all related specs in parallel
180
- const relatedSpecs = await Promise.all(otherDeps.map(async (dep) => ({ dep, spec: await this.storage.getSpec(dep.dependsOnSpecId) })));
181
- for (const { dep, spec: relatedSpec } of relatedSpecs) {
182
- if (relatedSpec)
183
- this.log(` - ${dep.dependencyType}: ${relatedSpec.id} - ${relatedSpec.title}`);
184
- }
185
- }
186
- if (showAll) {
187
- const allSpecs = await this.storage.listSpecs();
188
- // Find all specs that depend on this spec in parallel
189
- const dependedByResults = await Promise.all(allSpecs
190
- .filter(otherSpec => otherSpec.id !== specId)
191
- .map(async (otherSpec) => {
192
- const otherDeps = await this.storage.listSpecDependencies(otherSpec.id);
193
- const dep = otherDeps.find(d => d.dependsOnSpecId === specId);
194
- return dep ? { spec: otherSpec, type: dep.dependencyType } : null;
195
- }));
196
- const dependedBy = dependedByResults.filter((d) => d !== null);
197
- if (dependedBy.length > 0) {
198
- this.log(styles.muted('\n Depended by:'));
199
- for (const item of dependedBy)
200
- this.log(` - ${item.spec.id}: ${item.spec.title} (${item.type})`);
201
- }
202
- }
203
- if (dependencies.length === 0)
204
- this.log(styles.muted('\n No dependencies.'));
205
- this.log('');
206
- }
207
- }
@@ -1,14 +0,0 @@
1
- import { PMOCommand } from '../../../lib/pmo/index.js';
2
- export default class SpecLinkRelates extends PMOCommand {
3
- static description: string;
4
- static examples: string[];
5
- static args: {
6
- id: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
7
- target: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
8
- };
9
- static flags: {
10
- json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
- project: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
- };
13
- execute(): Promise<void>;
14
- }
@@ -1,63 +0,0 @@
1
- import { 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 SpecLinkRelates extends PMOCommand {
7
- static description = 'Add a relates_to dependency (informational link)';
8
- static examples = ['<%= config.bin %> <%= command.id %> my-feature other-spec'];
9
- static args = {
10
- id: Args.string({ description: 'Spec ID', required: true }),
11
- target: Args.string({ description: 'Related spec ID', required: false }),
12
- };
13
- static flags = {
14
- ...pmoBaseFlags,
15
- };
16
- async execute() {
17
- const { args, flags } = await this.parse(SpecLinkRelates);
18
- // Check if JSON output mode is active
19
- const jsonMode = shouldOutputJson(flags);
20
- // Helper to handle errors in JSON mode
21
- const handleError = (code, message) => {
22
- if (jsonMode) {
23
- outputErrorAsJson(code, message, createMetadata('spec link relates', flags));
24
- this.exit(1);
25
- }
26
- this.error(message);
27
- };
28
- const spec = await this.storage.getSpec(args.id);
29
- if (!spec)
30
- return handleError('SPEC_NOT_FOUND', `Spec not found: ${args.id}`);
31
- let targetId = args.target;
32
- if (!targetId) {
33
- const allSpecs = await this.storage.listSpecs();
34
- const otherSpecs = allSpecs.filter(s => s.id !== args.id);
35
- if (otherSpecs.length === 0) {
36
- if (jsonMode) {
37
- outputErrorAsJson('NO_OTHER_SPECS', 'No other specs.', createMetadata('spec link relates', flags));
38
- return;
39
- }
40
- this.log(styles.muted('\nNo other specs.'));
41
- return;
42
- }
43
- // In JSON mode, output spec selection prompt
44
- if (jsonMode) {
45
- const specChoices = otherSpecs.map(s => ({
46
- name: `${s.id} - ${s.title}`,
47
- value: s.id,
48
- command: `prlt spec link relates ${args.id} ${s.id} --json`,
49
- }));
50
- outputPromptAsJson(buildPromptConfig('list', 'target', `Select spec that ${args.id} relates to:`, specChoices), createMetadata('spec link relates', flags));
51
- return;
52
- }
53
- const { selected } = await inquirer.prompt([{ type: 'list', name: 'selected', message: `Select spec that ${args.id} relates to:`,
54
- choices: otherSpecs.map(s => ({ name: `${s.id} - ${s.title}`, value: s.id })) }]);
55
- targetId = selected;
56
- }
57
- const targetSpec = await this.storage.getSpec(targetId);
58
- if (!targetSpec)
59
- this.error(`Spec not found: ${targetId}`);
60
- await this.storage.createSpecDependency(args.id, targetId, 'relates_to');
61
- this.log(styles.success(`\n✅ ${styles.emphasis(args.id)} relates to ${styles.emphasis(targetId)}`));
62
- }
63
- }
@@ -1,96 +0,0 @@
1
- import { Args, Flags } 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 SpecLinkRemove extends PMOCommand {
7
- static description = 'Remove a dependency from a spec';
8
- static examples = [
9
- '<%= config.bin %> <%= command.id %> my-feature other-spec',
10
- '<%= config.bin %> <%= command.id %> my-feature --all',
11
- ];
12
- static args = {
13
- id: Args.string({ description: 'Spec ID', required: true }),
14
- target: Args.string({ description: 'Target spec ID to unlink', required: false }),
15
- };
16
- static flags = {
17
- ...pmoBaseFlags,
18
- type: Flags.string({ char: 't', description: 'Dependency type', options: ['depends_on', 'relates_to', 'duplicates'] }),
19
- all: Flags.boolean({ char: 'a', description: 'Remove all dependencies', default: false }),
20
- };
21
- async execute() {
22
- const { args, flags } = await this.parse(SpecLinkRemove);
23
- // Check if JSON output mode is active
24
- const jsonMode = shouldOutputJson(flags);
25
- // Helper to handle errors in JSON mode
26
- const handleError = (code, message) => {
27
- if (jsonMode) {
28
- outputErrorAsJson(code, message, createMetadata('spec link remove', flags));
29
- this.exit(1);
30
- }
31
- this.error(message);
32
- };
33
- const spec = await this.storage.getSpec(args.id);
34
- if (!spec)
35
- return handleError('SPEC_NOT_FOUND', `Spec not found: ${args.id}`);
36
- const dependencies = await this.storage.listSpecDependencies(args.id);
37
- if (dependencies.length === 0) {
38
- if (jsonMode) {
39
- outputErrorAsJson('NO_DEPENDENCIES', `Spec ${args.id} has no dependencies.`, createMetadata('spec link remove', flags));
40
- return;
41
- }
42
- this.log(styles.muted(`\nSpec ${args.id} has no dependencies.`));
43
- return;
44
- }
45
- if (flags.all) {
46
- // In JSON mode, output confirmation prompt
47
- if (jsonMode) {
48
- const confirmChoices = [
49
- { name: 'No', value: 'false' },
50
- { name: 'Yes', value: 'true' },
51
- ];
52
- outputPromptAsJson(buildPromptConfig('list', 'confirmed', `Remove all ${dependencies.length} dependencies?`, confirmChoices), createMetadata('spec link remove', flags));
53
- return;
54
- }
55
- const { confirmed } = await inquirer.prompt([{
56
- type: 'list',
57
- name: 'confirmed',
58
- message: `Remove all ${dependencies.length} dependencies?`,
59
- choices: [
60
- { name: 'No', value: false },
61
- { name: 'Yes', value: true },
62
- ],
63
- }]);
64
- if (!confirmed) {
65
- this.log(styles.muted('\nCancelled.'));
66
- return;
67
- }
68
- // Delete sequentially for data integrity
69
- // eslint-disable-next-line no-await-in-loop
70
- for (const dep of dependencies)
71
- await this.storage.deleteSpecDependency(args.id, dep.dependsOnSpecId, dep.dependencyType);
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 depSpec = await this.storage.getSpec(dep.dependsOnSpecId);
79
- return { name: `${dep.dependsOnSpecId} - ${depSpec?.title || 'Unknown'} (${dep.dependencyType})`, value: dep.dependsOnSpecId };
80
- }));
81
- // In JSON mode, output dependency selection prompt with command fields
82
- if (jsonMode) {
83
- const choicesWithCommands = choices.map(c => ({
84
- ...c,
85
- command: `prlt spec link remove ${args.id} ${c.value} --json`,
86
- }));
87
- outputPromptAsJson(buildPromptConfig('list', 'target', 'Select dependency to remove:', choicesWithCommands), createMetadata('spec link remove', flags));
88
- return;
89
- }
90
- const { selected } = await inquirer.prompt([{ type: 'list', name: 'selected', message: 'Select dependency to remove:', choices }]);
91
- targetId = selected;
92
- }
93
- await this.storage.deleteSpecDependency(args.id, targetId, flags.type);
94
- this.log(styles.success(`\n✅ Removed dependency: ${args.id} → ${targetId}`));
95
- }
96
- }
@@ -1,14 +0,0 @@
1
- import { Command } from '@oclif/core';
2
- export default class TemplatePhaseApply extends Command {
3
- static description: string;
4
- static examples: string[];
5
- static args: {
6
- template: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
7
- };
8
- static flags: {
9
- project: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
- force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
- json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
12
- };
13
- run(): Promise<void>;
14
- }
@@ -1,43 +0,0 @@
1
- import { Args, Command, Flags } from '@oclif/core';
2
- export default class TemplatePhaseApply extends Command {
3
- static description = 'Apply a phase template to a project';
4
- static examples = [
5
- '<%= config.bin %> <%= command.id %> agile',
6
- '<%= config.bin %> <%= command.id %> waterfall --project my-project',
7
- '<%= config.bin %> <%= command.id %> agile --force',
8
- ];
9
- static args = {
10
- template: Args.string({
11
- description: 'Phase template ID to apply',
12
- required: true,
13
- }),
14
- };
15
- static flags = {
16
- project: Flags.string({
17
- char: 'p',
18
- description: 'Project ID or name',
19
- }),
20
- force: Flags.boolean({
21
- char: 'f',
22
- description: 'Skip confirmation prompt (will replace existing phases)',
23
- default: false,
24
- }),
25
- json: Flags.boolean({
26
- char: 'm',
27
- aliases: ['machine'],
28
- description: 'Output prompt configuration as JSON (for AI agents/scripts)',
29
- default: false,
30
- }),
31
- };
32
- async run() {
33
- const { args, flags } = await this.parse(TemplatePhaseApply);
34
- const cmdArgs = [args.template];
35
- if (flags.project)
36
- cmdArgs.push('--project', flags.project);
37
- if (flags.force)
38
- cmdArgs.push('--force');
39
- if (flags.json)
40
- cmdArgs.push('--json');
41
- await this.config.runCommand('phase:template:apply', cmdArgs);
42
- }
43
- }
@@ -1,13 +0,0 @@
1
- import { Command } from '@oclif/core';
2
- export default class TemplatePhaseCreate extends Command {
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
- };
12
- run(): Promise<void>;
13
- }
@@ -1,38 +0,0 @@
1
- import { Args, Command, Flags } from '@oclif/core';
2
- export default class TemplatePhaseCreate extends Command {
3
- static description = 'Create a new phase template';
4
- static examples = [
5
- '<%= config.bin %> <%= command.id %> "My Phases"',
6
- '<%= config.bin %> <%= command.id %> "Sprint Phases" --description "Agile sprint phases"',
7
- '<%= config.bin %> <%= command.id %> "My Phases" --description "Custom phases" --json',
8
- ];
9
- static args = {
10
- name: Args.string({
11
- description: 'Name for the new template',
12
- required: false,
13
- }),
14
- };
15
- static flags = {
16
- description: Flags.string({
17
- char: 'd',
18
- description: 'Template description',
19
- }),
20
- json: Flags.boolean({
21
- char: 'm',
22
- aliases: ['machine'],
23
- description: 'Output as JSON for AI agents/scripts',
24
- default: false,
25
- }),
26
- };
27
- async run() {
28
- const { args, flags } = await this.parse(TemplatePhaseCreate);
29
- const cmdArgs = [];
30
- if (args.name)
31
- cmdArgs.push(args.name);
32
- if (flags.description)
33
- cmdArgs.push('--description', flags.description);
34
- if (flags.json)
35
- cmdArgs.push('--json');
36
- await this.config.runCommand('phase:template:create', cmdArgs);
37
- }
38
- }
@@ -1,36 +0,0 @@
1
- import { Args, Command, Flags } from '@oclif/core';
2
- export default class TemplatePhaseDelete extends Command {
3
- static description = 'Delete a phase template';
4
- static examples = [
5
- '<%= config.bin %> <%= command.id %> my-template',
6
- '<%= config.bin %> <%= command.id %> my-template --force',
7
- ];
8
- static args = {
9
- id: Args.string({
10
- description: 'Template ID to delete',
11
- required: true,
12
- }),
13
- };
14
- static flags = {
15
- force: Flags.boolean({
16
- char: 'f',
17
- description: 'Skip confirmation',
18
- default: false,
19
- }),
20
- json: Flags.boolean({
21
- char: 'm',
22
- aliases: ['machine'],
23
- description: 'Output prompt configuration as JSON (for AI agents/scripts)',
24
- default: false,
25
- }),
26
- };
27
- async run() {
28
- const { args, flags } = await this.parse(TemplatePhaseDelete);
29
- const cmdArgs = [args.id];
30
- if (flags.force)
31
- cmdArgs.push('--force');
32
- if (flags.json)
33
- cmdArgs.push('--json');
34
- await this.config.runCommand('phase:template:delete', cmdArgs);
35
- }
36
- }