@proletariat/cli 0.3.9 → 0.3.11

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 (152) hide show
  1. package/README.md +25 -0
  2. package/bin/dev.js +0 -0
  3. package/dist/commands/action/index.js +1 -1
  4. package/dist/commands/action/run.js +8 -12
  5. package/dist/commands/agent/auth.d.ts +30 -0
  6. package/dist/commands/agent/auth.js +172 -0
  7. package/dist/commands/agent/discover.d.ts +9 -0
  8. package/dist/commands/agent/discover.js +67 -0
  9. package/dist/commands/agent/index.js +47 -12
  10. package/dist/commands/agent/list.d.ts +4 -1
  11. package/dist/commands/agent/list.js +78 -16
  12. package/dist/commands/agent/login.js +35 -31
  13. package/dist/commands/agent/restart.js +2 -0
  14. package/dist/commands/agent/shell.js +78 -19
  15. package/dist/commands/agent/staff/add.js +1 -12
  16. package/dist/commands/agent/staff/remove.js +9 -7
  17. package/dist/commands/agent/status.js +17 -4
  18. package/dist/commands/agent/temp/cleanup.js +7 -3
  19. package/dist/commands/agent/themes/index.js +4 -5
  20. package/dist/commands/agent/themes/list.js +5 -5
  21. package/dist/commands/agent/visit.js +17 -4
  22. package/dist/commands/branch/create.d.ts +4 -0
  23. package/dist/commands/branch/create.js +16 -8
  24. package/dist/commands/branch/index.js +1 -1
  25. package/dist/commands/branch/where.js +1 -0
  26. package/dist/commands/claude.d.ts +38 -0
  27. package/dist/commands/claude.js +899 -0
  28. package/dist/commands/commit.js +1 -1
  29. package/dist/commands/config/index.d.ts +12 -0
  30. package/dist/commands/config/index.js +271 -0
  31. package/dist/commands/docker/clean.js +2 -2
  32. package/dist/commands/docker/index.js +2 -2
  33. package/dist/commands/docker/list.js +3 -8
  34. package/dist/commands/docker/logs.js +2 -2
  35. package/dist/commands/docker/prune.js +1 -1
  36. package/dist/commands/docker/restart.js +2 -2
  37. package/dist/commands/docker/shell.js +2 -2
  38. package/dist/commands/docker/start.js +2 -2
  39. package/dist/commands/docker/status.js +1 -1
  40. package/dist/commands/docker/stop.js +2 -2
  41. package/dist/commands/docker/sync.js +2 -2
  42. package/dist/commands/epic/index.js +1 -1
  43. package/dist/commands/epic/link/index.js +25 -14
  44. package/dist/commands/epic/link/remove.js +2 -0
  45. package/dist/commands/epic/list.js +5 -5
  46. package/dist/commands/epic/progress.js +10 -4
  47. package/dist/commands/epic/spec.js +2 -0
  48. package/dist/commands/epic/ticket.js +3 -0
  49. package/dist/commands/execution/stop.js +1 -0
  50. package/dist/commands/init.js +4 -4
  51. package/dist/commands/project/index.js +1 -1
  52. package/dist/commands/project/spec.js +7 -0
  53. package/dist/commands/repo/add.js +1 -0
  54. package/dist/commands/repo/remove.js +1 -0
  55. package/dist/commands/roadmap/add-project.d.ts +18 -0
  56. package/dist/commands/roadmap/add-project.js +135 -0
  57. package/dist/commands/roadmap/create.d.ts +22 -0
  58. package/dist/commands/roadmap/create.js +156 -0
  59. package/dist/commands/roadmap/delete.d.ts +17 -0
  60. package/dist/commands/roadmap/delete.js +104 -0
  61. package/dist/commands/roadmap/generate.d.ts +22 -0
  62. package/dist/commands/roadmap/generate.js +201 -0
  63. package/dist/commands/roadmap/index.d.ts +13 -0
  64. package/dist/commands/roadmap/index.js +61 -0
  65. package/dist/commands/roadmap/list.d.ts +12 -0
  66. package/dist/commands/roadmap/list.js +42 -0
  67. package/dist/commands/roadmap/remove-project.d.ts +18 -0
  68. package/dist/commands/roadmap/remove-project.js +147 -0
  69. package/dist/commands/roadmap/reorder.d.ts +17 -0
  70. package/dist/commands/roadmap/reorder.js +157 -0
  71. package/dist/commands/roadmap/update.d.ts +19 -0
  72. package/dist/commands/roadmap/update.js +136 -0
  73. package/dist/commands/roadmap/view.d.ts +16 -0
  74. package/dist/commands/roadmap/view.js +103 -0
  75. package/dist/commands/spec/index.js +1 -1
  76. package/dist/commands/spec/link/index.js +24 -13
  77. package/dist/commands/spec/link/remove.js +2 -0
  78. package/dist/commands/status/index.js +1 -1
  79. package/dist/commands/status/list.js +0 -8
  80. package/dist/commands/template/delete.js +2 -0
  81. package/dist/commands/terminal/title.d.ts +12 -0
  82. package/dist/commands/terminal/title.js +48 -0
  83. package/dist/commands/ticket/complete.js +2 -0
  84. package/dist/commands/ticket/create.js +4 -2
  85. package/dist/commands/ticket/delete.js +2 -0
  86. package/dist/commands/ticket/edit.js +8 -2
  87. package/dist/commands/ticket/link/index.js +17 -3
  88. package/dist/commands/ticket/link/remove.js +2 -0
  89. package/dist/commands/ticket/list.js +1 -2
  90. package/dist/commands/ticket/move.js +2 -0
  91. package/dist/commands/ticket/project.js +3 -1
  92. package/dist/commands/ticket/reassign.js +2 -0
  93. package/dist/commands/ticket/spec.js +4 -2
  94. package/dist/commands/ticket/template/apply.js +4 -3
  95. package/dist/commands/ticket/template/create.js +2 -0
  96. package/dist/commands/ticket/template/index.js +1 -1
  97. package/dist/commands/ticket/update.js +2 -0
  98. package/dist/commands/work/index.js +1 -1
  99. package/dist/commands/work/revise.js +7 -1
  100. package/dist/commands/work/spawn.d.ts +2 -1
  101. package/dist/commands/work/spawn.js +131 -36
  102. package/dist/commands/work/start.d.ts +2 -1
  103. package/dist/commands/work/start.js +349 -69
  104. package/dist/commands/work/watch.js +10 -2
  105. package/dist/commands/workflow/create.js +3 -3
  106. package/dist/commands/workflow/switch.js +2 -1
  107. package/dist/commands/workspace/remove.js +0 -8
  108. package/dist/commands/workspace/use.js +1 -9
  109. package/dist/lib/agents/commands.js +18 -13
  110. package/dist/lib/database/index.d.ts +19 -12
  111. package/dist/lib/database/index.js +158 -42
  112. package/dist/lib/docker/resolve.js +1 -1
  113. package/dist/lib/execution/config.d.ts +6 -0
  114. package/dist/lib/execution/config.js +15 -2
  115. package/dist/lib/execution/devcontainer.d.ts +2 -0
  116. package/dist/lib/execution/devcontainer.js +41 -9
  117. package/dist/lib/execution/runners.d.ts +85 -3
  118. package/dist/lib/execution/runners.js +925 -228
  119. package/dist/lib/execution/spawner.d.ts +2 -2
  120. package/dist/lib/execution/spawner.js +4 -3
  121. package/dist/lib/execution/storage.d.ts +2 -1
  122. package/dist/lib/execution/storage.js +9 -13
  123. package/dist/lib/execution/types.d.ts +10 -1
  124. package/dist/lib/execution/types.js +3 -1
  125. package/dist/lib/init/index.js +1 -0
  126. package/dist/lib/machine-config.js +1 -1
  127. package/dist/lib/pmo/base-command.js +5 -9
  128. package/dist/lib/pmo/index.js +2 -0
  129. package/dist/lib/pmo/schema.d.ts +6 -0
  130. package/dist/lib/pmo/schema.js +36 -0
  131. package/dist/lib/pmo/storage/base.js +3 -3
  132. package/dist/lib/pmo/storage/index.d.ts +16 -1
  133. package/dist/lib/pmo/storage/index.js +45 -0
  134. package/dist/lib/pmo/storage/roadmaps.d.ts +62 -0
  135. package/dist/lib/pmo/storage/roadmaps.js +301 -0
  136. package/dist/lib/pmo/storage/specs.js +2 -0
  137. package/dist/lib/pmo/storage/types.d.ts +14 -0
  138. package/dist/lib/pmo/sync-manager.d.ts +1 -1
  139. package/dist/lib/pmo/sync-manager.js +1 -1
  140. package/dist/lib/pmo/types.d.ts +41 -0
  141. package/dist/lib/pmo/utils.d.ts +2 -0
  142. package/dist/lib/pmo/utils.js +22 -1
  143. package/dist/lib/repos/index.js +7 -1
  144. package/dist/lib/terminal.d.ts +31 -0
  145. package/dist/lib/terminal.js +48 -0
  146. package/dist/lib/themes.d.ts +21 -3
  147. package/dist/lib/themes.js +80 -23
  148. package/dist/lib/workspace-config.d.ts +80 -0
  149. package/dist/lib/workspace-config.js +100 -0
  150. package/oclif.manifest.json +4065 -3225
  151. package/package.json +10 -6
  152. package/LICENSE +0 -21
@@ -100,6 +100,7 @@ export default class ExecutionStop extends PMOCommand {
100
100
  let failed = 0;
101
101
  for (const execution of activeExecutions) {
102
102
  try {
103
+ // eslint-disable-next-line no-await-in-loop -- Sequential stop with user feedback
103
104
  const success = await this.stopExecution(execution, flags.force || false);
104
105
  if (success) {
105
106
  executionStorage.updateStatus(execution.id, 'stopped');
@@ -161,10 +161,10 @@ export default class Init extends Command {
161
161
  storageType: 'sqlite',
162
162
  },
163
163
  };
164
+ // Suppress console output in JSON mode
165
+ const originalLog = console.log;
166
+ console.log = () => { };
164
167
  try {
165
- // Suppress console output in JSON mode
166
- const originalLog = console.log;
167
- console.log = () => { };
168
168
  // Initialize the HQ
169
169
  await initializeHQ(options);
170
170
  // Restore console.log
@@ -183,7 +183,7 @@ export default class Init extends Command {
183
183
  }
184
184
  catch (error) {
185
185
  // Restore console.log on error
186
- console.log = console.log;
186
+ console.log = originalLog;
187
187
  this.outputJson({
188
188
  success: false,
189
189
  error: error instanceof Error ? error.message : 'Unknown error',
@@ -1,6 +1,6 @@
1
1
  import { Flags } from '@oclif/core';
2
2
  import { PMOCommand, pmoBaseFlags } from '../../lib/pmo/index.js';
3
- import { shouldOutputJson, } from '../../lib/prompt-json.js';
3
+ import { shouldOutputJson } from '../../lib/prompt-json.js';
4
4
  export default class Project extends PMOCommand {
5
5
  static description = 'Interactive menu for project operations';
6
6
  static examples = [
@@ -133,6 +133,7 @@ export default class ProjectSpec extends PMOCommand {
133
133
  let continueLoop = true;
134
134
  while (continueLoop) {
135
135
  // Refresh project specs each iteration
136
+ // eslint-disable-next-line no-await-in-loop -- Interactive user loop
136
137
  const currentSpecs = await this.storage.getSpecsForProject(projectId);
137
138
  const currentSpecIds = new Set(currentSpecs.map(s => s.id));
138
139
  this.log(styles.title(`\nSpecs for project "${project.name}" (${projectId})`));
@@ -144,6 +145,7 @@ export default class ProjectSpec extends PMOCommand {
144
145
  this.log(` ${styles.code(spec.id)} - ${spec.title} (${spec.status})`);
145
146
  }
146
147
  }
148
+ // eslint-disable-next-line no-await-in-loop -- Interactive user prompt
147
149
  const { action } = await inquirer.prompt([{
148
150
  type: 'list',
149
151
  name: 'action',
@@ -161,12 +163,14 @@ export default class ProjectSpec extends PMOCommand {
161
163
  }
162
164
  if (action === 'add') {
163
165
  // Get all specs not already associated
166
+ // eslint-disable-next-line no-await-in-loop -- User action handling
164
167
  const allSpecs = await this.storage.listSpecs();
165
168
  const availableSpecs = allSpecs.filter(s => !currentSpecIds.has(s.id));
166
169
  if (availableSpecs.length === 0) {
167
170
  this.log(styles.muted('\n All specs are already associated with this project.'));
168
171
  continue;
169
172
  }
173
+ // eslint-disable-next-line no-await-in-loop -- User selection prompt
170
174
  const { selected } = await inquirer.prompt([{
171
175
  type: 'checkbox',
172
176
  name: 'selected',
@@ -181,6 +185,7 @@ export default class ProjectSpec extends PMOCommand {
181
185
  continue;
182
186
  }
183
187
  for (const specId of selected) {
188
+ // eslint-disable-next-line no-await-in-loop -- Sequential updates with feedback
184
189
  await this.storage.linkProjectToSpec(projectId, specId);
185
190
  this.log(styles.success(` Added ${specId}`));
186
191
  }
@@ -191,6 +196,7 @@ export default class ProjectSpec extends PMOCommand {
191
196
  this.log(styles.muted('\n No specs to remove.'));
192
197
  continue;
193
198
  }
199
+ // eslint-disable-next-line no-await-in-loop -- User selection prompt
194
200
  const { selected } = await inquirer.prompt([{
195
201
  type: 'checkbox',
196
202
  name: 'selected',
@@ -205,6 +211,7 @@ export default class ProjectSpec extends PMOCommand {
205
211
  continue;
206
212
  }
207
213
  for (const specId of selected) {
214
+ // eslint-disable-next-line no-await-in-loop -- Sequential updates with feedback
208
215
  await this.storage.unlinkProjectFromSpec(projectId, specId);
209
216
  this.log(styles.success(` Removed ${specId}`));
210
217
  }
@@ -96,6 +96,7 @@ export default class Add extends PMOCommand {
96
96
  let successCount = 0;
97
97
  let failCount = 0;
98
98
  for (const repo of reposToAdd) {
99
+ // eslint-disable-next-line no-await-in-loop -- Sequential add with user feedback
99
100
  const result = await addRepository(hqPath, repo.path, repo.action);
100
101
  if (result.success) {
101
102
  this.log(format.success(`Repository ${result.name} added`));
@@ -195,6 +195,7 @@ export default class Remove extends PMOCommand {
195
195
  let failCount = 0;
196
196
  for (const repoName of selectedRepos) {
197
197
  this.log(colors.textMuted(`Removing ${repoName}...`));
198
+ // eslint-disable-next-line no-await-in-loop -- Sequential remove with user feedback
198
199
  const result = await removeRepository(hqPath, repoName, keepFiles);
199
200
  if (result.success) {
200
201
  this.log(format.success(`Removed ${repoName}`));
@@ -0,0 +1,18 @@
1
+ import { PMOCommand } from '../../lib/pmo/index.js';
2
+ export default class RoadmapAddProject extends PMOCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static args: {
6
+ roadmap: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
7
+ project: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
8
+ };
9
+ static flags: {
10
+ position: import("@oclif/core/interfaces").OptionFlag<number | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
+ json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
12
+ project: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
+ };
14
+ protected getPMOOptions(): {
15
+ promptIfMultiple: boolean;
16
+ };
17
+ execute(): Promise<void>;
18
+ }
@@ -0,0 +1,135 @@
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 RoadmapAddProject extends PMOCommand {
7
+ static description = 'Add a project to a roadmap';
8
+ static examples = [
9
+ '<%= config.bin %> <%= command.id %> my-roadmap my-project',
10
+ '<%= config.bin %> <%= command.id %> my-roadmap # Interactive project selection',
11
+ '<%= config.bin %> <%= command.id %> # Interactive selection for both',
12
+ ];
13
+ static args = {
14
+ roadmap: Args.string({
15
+ description: 'Roadmap ID',
16
+ required: false,
17
+ }),
18
+ project: Args.string({
19
+ description: 'Project ID to add',
20
+ required: false,
21
+ }),
22
+ };
23
+ static flags = {
24
+ ...pmoBaseFlags,
25
+ position: Flags.integer({
26
+ char: 'p',
27
+ description: 'Position in the roadmap (0-indexed)',
28
+ }),
29
+ json: Flags.boolean({
30
+ description: 'Output prompt configuration as JSON (for AI agents/scripts)',
31
+ default: false,
32
+ }),
33
+ };
34
+ getPMOOptions() {
35
+ return { promptIfMultiple: false };
36
+ }
37
+ async execute() {
38
+ const { args, flags } = await this.parse(RoadmapAddProject);
39
+ const jsonMode = shouldOutputJson(flags);
40
+ // Select roadmap
41
+ let roadmapId = args.roadmap;
42
+ if (!roadmapId) {
43
+ const roadmaps = await this.storage.listRoadmaps();
44
+ if (roadmaps.length === 0) {
45
+ if (jsonMode) {
46
+ outputErrorAsJson('NO_ROADMAPS', 'No roadmaps found', createMetadata('roadmap add-project', flags));
47
+ return;
48
+ }
49
+ this.error('No roadmaps found. Create one with: prlt roadmap create');
50
+ }
51
+ if (jsonMode) {
52
+ const choices = roadmaps.map(r => ({
53
+ name: `${r.name}${r.isDefault ? ' (default)' : ''}`,
54
+ value: r.id,
55
+ }));
56
+ outputPromptAsJson(buildPromptConfig('list', 'roadmap', 'Select roadmap:', choices), createMetadata('roadmap add-project', flags));
57
+ return;
58
+ }
59
+ const { selected } = await inquirer.prompt([{
60
+ type: 'list',
61
+ name: 'selected',
62
+ message: 'Select roadmap:',
63
+ choices: roadmaps.map(r => ({
64
+ name: `${r.name}${r.isDefault ? ' (default)' : ''}`,
65
+ value: r.id,
66
+ })),
67
+ }]);
68
+ roadmapId = selected;
69
+ }
70
+ const roadmap = await this.storage.getRoadmap(roadmapId);
71
+ if (!roadmap) {
72
+ if (jsonMode) {
73
+ outputErrorAsJson('NOT_FOUND', `Roadmap not found: ${roadmapId}`, createMetadata('roadmap add-project', flags));
74
+ return;
75
+ }
76
+ this.error(`Roadmap not found: ${roadmapId}`);
77
+ }
78
+ // Get projects already in roadmap
79
+ const existingProjects = await this.storage.listRoadmapProjects(roadmapId);
80
+ const existingProjectIds = new Set(existingProjects.map(p => p.id));
81
+ // Get all projects and filter out ones already in roadmap
82
+ const allProjects = await this.storage.listProjects({ isArchived: false });
83
+ const availableProjects = allProjects.filter(p => !existingProjectIds.has(p.id));
84
+ if (availableProjects.length === 0) {
85
+ if (jsonMode) {
86
+ outputErrorAsJson('NO_AVAILABLE_PROJECTS', 'All projects are already in this roadmap', createMetadata('roadmap add-project', flags));
87
+ return;
88
+ }
89
+ this.error('All projects are already in this roadmap');
90
+ }
91
+ // Select project
92
+ let projectId = args.project;
93
+ if (!projectId) {
94
+ if (jsonMode) {
95
+ const choices = availableProjects.map(p => ({
96
+ name: p.name,
97
+ value: p.id,
98
+ }));
99
+ outputPromptAsJson(buildPromptConfig('list', 'project', 'Select project to add:', choices), createMetadata('roadmap add-project', flags));
100
+ return;
101
+ }
102
+ const { selected } = await inquirer.prompt([{
103
+ type: 'list',
104
+ name: 'selected',
105
+ message: 'Select project to add:',
106
+ choices: availableProjects.map(p => ({
107
+ name: p.name,
108
+ value: p.id,
109
+ })),
110
+ }]);
111
+ projectId = selected;
112
+ }
113
+ // Verify project exists and isn't already in roadmap
114
+ const project = await this.storage.getProject(projectId);
115
+ if (!project) {
116
+ if (jsonMode) {
117
+ outputErrorAsJson('NOT_FOUND', `Project not found: ${projectId}`, createMetadata('roadmap add-project', flags));
118
+ return;
119
+ }
120
+ this.error(`Project not found: ${projectId}`);
121
+ }
122
+ if (existingProjectIds.has(projectId)) {
123
+ if (jsonMode) {
124
+ outputErrorAsJson('ALREADY_IN_ROADMAP', `Project "${project.name}" is already in this roadmap`, createMetadata('roadmap add-project', flags));
125
+ return;
126
+ }
127
+ this.error(`Project "${project.name}" is already in this roadmap`);
128
+ }
129
+ // Add project to roadmap
130
+ await this.storage.addProjectToRoadmap(roadmapId, projectId, flags.position);
131
+ const projects = await this.storage.listRoadmapProjects(roadmapId);
132
+ const position = projects.findIndex(p => p.id === projectId) + 1;
133
+ this.log(styles.success(`Added "${project.name}" to "${roadmap.name}" at position ${position}`));
134
+ }
135
+ }
@@ -0,0 +1,22 @@
1
+ import { PMOCommand } from '../../lib/pmo/index.js';
2
+ export default class RoadmapCreate extends PMOCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static args: {
6
+ name: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
7
+ };
8
+ static flags: {
9
+ name: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
+ id: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
+ description: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
+ default: import("@oclif/core/interfaces").BooleanFlag<boolean>;
13
+ interactive: import("@oclif/core/interfaces").BooleanFlag<boolean>;
14
+ json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
15
+ project: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
16
+ };
17
+ protected getPMOOptions(): {
18
+ promptIfMultiple: boolean;
19
+ };
20
+ execute(): Promise<void>;
21
+ private promptRoadmapData;
22
+ }
@@ -0,0 +1,156 @@
1
+ import { Flags, Args } from '@oclif/core';
2
+ import inquirer from 'inquirer';
3
+ import { PMOCommand, pmoBaseFlags } from '../../lib/pmo/index.js';
4
+ import { styles } from '../../lib/styles.js';
5
+ import { slugify } from '../../lib/pmo/utils.js';
6
+ import { shouldOutputJson, outputPromptAsJson, createMetadata, buildFormPromptConfig, } from '../../lib/prompt-json.js';
7
+ export default class RoadmapCreate extends PMOCommand {
8
+ static description = 'Create a new roadmap';
9
+ static examples = [
10
+ '<%= config.bin %> <%= command.id %> "Public Roadmap"',
11
+ '<%= config.bin %> <%= command.id %> --name "Internal Roadmap" --description "Full project list"',
12
+ '<%= config.bin %> <%= command.id %> -i # Interactive mode',
13
+ ];
14
+ static args = {
15
+ name: Args.string({
16
+ description: 'Roadmap name',
17
+ required: false,
18
+ }),
19
+ };
20
+ static flags = {
21
+ ...pmoBaseFlags,
22
+ name: Flags.string({
23
+ char: 'n',
24
+ description: 'Roadmap name',
25
+ }),
26
+ id: Flags.string({
27
+ description: 'Custom roadmap ID (auto-generated from name if not provided)',
28
+ }),
29
+ description: Flags.string({
30
+ char: 'd',
31
+ description: 'Roadmap description',
32
+ }),
33
+ default: Flags.boolean({
34
+ description: 'Set as the default roadmap',
35
+ default: false,
36
+ }),
37
+ interactive: Flags.boolean({
38
+ char: 'i',
39
+ description: 'Interactive mode',
40
+ default: false,
41
+ }),
42
+ json: Flags.boolean({
43
+ description: 'Output prompt configuration as JSON (for AI agents/scripts)',
44
+ default: false,
45
+ }),
46
+ };
47
+ getPMOOptions() {
48
+ return { promptIfMultiple: false };
49
+ }
50
+ async execute() {
51
+ const { args, flags } = await this.parse(RoadmapCreate);
52
+ const jsonMode = shouldOutputJson(flags);
53
+ let roadmapData;
54
+ if (flags.interactive || (!args.name && !flags.name)) {
55
+ const fields = [
56
+ { type: 'input', name: 'name', message: 'Roadmap name:', default: flags.name },
57
+ { type: 'input', name: 'id', message: 'Roadmap ID (leave blank to auto-generate):' },
58
+ { type: 'input', name: 'description', message: 'Description (optional):', default: flags.description },
59
+ {
60
+ type: 'list',
61
+ name: 'isDefault',
62
+ message: 'Set as default roadmap?',
63
+ choices: [
64
+ { name: 'No', value: 'false' },
65
+ { name: 'Yes', value: 'true' },
66
+ ],
67
+ default: flags.default ? 'true' : 'false',
68
+ },
69
+ ];
70
+ if (jsonMode) {
71
+ outputPromptAsJson(buildFormPromptConfig(fields), createMetadata('roadmap create', flags));
72
+ return;
73
+ }
74
+ roadmapData = await this.promptRoadmapData(fields);
75
+ }
76
+ else {
77
+ roadmapData = {
78
+ name: args.name || flags.name,
79
+ id: flags.id,
80
+ description: flags.description,
81
+ isDefault: flags.default,
82
+ };
83
+ }
84
+ const roadmapId = roadmapData.id || `roadmap-${slugify(roadmapData.name)}`;
85
+ // Check if roadmap already exists
86
+ const existing = await this.storage.getRoadmap(roadmapId);
87
+ if (existing) {
88
+ this.error(`Roadmap "${roadmapId}" already exists.`);
89
+ }
90
+ // Create roadmap in database
91
+ const roadmap = await this.storage.createRoadmap({
92
+ id: roadmapId,
93
+ name: roadmapData.name,
94
+ description: roadmapData.description,
95
+ isDefault: roadmapData.isDefault,
96
+ });
97
+ this.log(styles.success(`\nCreated roadmap "${styles.emphasis(roadmap.name)}"`));
98
+ this.log(styles.muted(` ID: ${roadmap.id}`));
99
+ if (roadmap.description) {
100
+ this.log(styles.muted(` Description: ${roadmap.description}`));
101
+ }
102
+ if (roadmap.isDefault) {
103
+ this.log(styles.muted(` Default: Yes`));
104
+ }
105
+ // Prompt to add projects
106
+ const projects = await this.storage.listProjects({ isArchived: false });
107
+ if (projects.length > 0) {
108
+ this.log('');
109
+ const { addProjects } = await inquirer.prompt([{
110
+ type: 'list',
111
+ name: 'addProjects',
112
+ message: 'Add projects to this roadmap now?',
113
+ choices: [
114
+ { name: 'Yes', value: true },
115
+ { name: 'No, I\'ll add them later', value: false },
116
+ ],
117
+ }]);
118
+ if (addProjects) {
119
+ const { selectedProjects } = await inquirer.prompt([{
120
+ type: 'checkbox',
121
+ name: 'selectedProjects',
122
+ message: 'Select projects to include:',
123
+ choices: projects.map(p => ({ name: p.name, value: p.id })),
124
+ }]);
125
+ for (const projectId of selectedProjects) {
126
+ // eslint-disable-next-line no-await-in-loop -- Sequential updates
127
+ await this.storage.addProjectToRoadmap(roadmap.id, projectId);
128
+ }
129
+ if (selectedProjects.length > 0) {
130
+ this.log(styles.success(`Added ${selectedProjects.length} project(s) to roadmap`));
131
+ }
132
+ }
133
+ }
134
+ this.log(styles.muted(`\nNext steps:`));
135
+ this.log(styles.muted(` prlt roadmap view ${roadmap.id}`));
136
+ this.log(styles.muted(` prlt roadmap add-project ${roadmap.id}`));
137
+ this.log(styles.muted(` prlt roadmap generate ${roadmap.id}`));
138
+ }
139
+ async promptRoadmapData(fields) {
140
+ const answers = await inquirer.prompt(fields.map(field => ({
141
+ ...field,
142
+ validate: field.name === 'name'
143
+ ? ((input) => input.length > 0 || 'Name is required')
144
+ : undefined,
145
+ default: field.name === 'id'
146
+ ? ((answers) => `roadmap-${slugify(answers.name)}`)
147
+ : field.default,
148
+ })));
149
+ return {
150
+ name: answers.name,
151
+ id: answers.id || undefined,
152
+ description: answers.description || undefined,
153
+ isDefault: answers.isDefault === true || answers.isDefault === 'true',
154
+ };
155
+ }
156
+ }
@@ -0,0 +1,17 @@
1
+ import { PMOCommand } from '../../lib/pmo/index.js';
2
+ export default class RoadmapDelete 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
+ force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
+ json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
+ project: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
+ };
13
+ protected getPMOOptions(): {
14
+ promptIfMultiple: boolean;
15
+ };
16
+ execute(): Promise<void>;
17
+ }
@@ -0,0 +1,104 @@
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 RoadmapDelete extends PMOCommand {
7
+ static description = 'Delete a roadmap';
8
+ static examples = [
9
+ '<%= config.bin %> <%= command.id %> my-roadmap',
10
+ '<%= config.bin %> <%= command.id %> my-roadmap --force',
11
+ '<%= config.bin %> <%= command.id %> # Interactive selection',
12
+ ];
13
+ static args = {
14
+ id: Args.string({
15
+ description: 'Roadmap ID to delete',
16
+ required: false,
17
+ }),
18
+ };
19
+ static flags = {
20
+ ...pmoBaseFlags,
21
+ force: Flags.boolean({
22
+ char: 'f',
23
+ description: 'Skip confirmation',
24
+ default: false,
25
+ }),
26
+ json: Flags.boolean({
27
+ description: 'Output prompt configuration as JSON (for AI agents/scripts)',
28
+ default: false,
29
+ }),
30
+ };
31
+ getPMOOptions() {
32
+ return { promptIfMultiple: false };
33
+ }
34
+ async execute() {
35
+ const { args, flags } = await this.parse(RoadmapDelete);
36
+ const jsonMode = shouldOutputJson(flags);
37
+ let roadmapId = args.id;
38
+ if (!roadmapId) {
39
+ const roadmaps = await this.storage.listRoadmaps();
40
+ if (roadmaps.length === 0) {
41
+ if (jsonMode) {
42
+ outputErrorAsJson('NO_ROADMAPS', 'No roadmaps found', createMetadata('roadmap delete', flags));
43
+ return;
44
+ }
45
+ this.error('No roadmaps found.');
46
+ }
47
+ if (jsonMode) {
48
+ const choices = roadmaps.map(r => ({
49
+ name: `${r.name}${r.isDefault ? ' (default)' : ''}`,
50
+ value: r.id,
51
+ }));
52
+ outputPromptAsJson(buildPromptConfig('list', 'id', 'Select roadmap to delete:', choices), createMetadata('roadmap delete', flags));
53
+ return;
54
+ }
55
+ const { selected } = await inquirer.prompt([{
56
+ type: 'list',
57
+ name: 'selected',
58
+ message: 'Select roadmap to delete:',
59
+ choices: roadmaps.map(r => ({
60
+ name: `${r.name}${r.isDefault ? ' (default)' : ''}`,
61
+ value: r.id,
62
+ })),
63
+ }]);
64
+ roadmapId = selected;
65
+ }
66
+ const roadmap = await this.storage.getRoadmap(roadmapId);
67
+ if (!roadmap) {
68
+ if (jsonMode) {
69
+ outputErrorAsJson('NOT_FOUND', `Roadmap not found: ${roadmapId}`, createMetadata('roadmap delete', flags));
70
+ return;
71
+ }
72
+ this.error(`Roadmap not found: ${roadmapId}`);
73
+ }
74
+ // Get project count for context
75
+ const projects = await this.storage.listRoadmapProjects(roadmapId);
76
+ // Confirm deletion
77
+ if (!flags.force) {
78
+ const message = `Delete roadmap "${roadmap.name}"${projects.length > 0 ? ` (contains ${projects.length} project reference${projects.length > 1 ? 's' : ''})` : ''}?`;
79
+ if (jsonMode) {
80
+ const confirmChoices = [
81
+ { name: 'No, cancel', value: 'false' },
82
+ { name: 'Yes, delete', value: 'true' },
83
+ ];
84
+ outputPromptAsJson(buildPromptConfig('list', 'confirmed', message, confirmChoices), createMetadata('roadmap delete', flags));
85
+ return;
86
+ }
87
+ const { confirm } = await inquirer.prompt([{
88
+ type: 'list',
89
+ name: 'confirm',
90
+ message,
91
+ choices: [
92
+ { name: 'No, cancel', value: false },
93
+ { name: 'Yes, delete', value: true },
94
+ ],
95
+ }]);
96
+ if (!confirm) {
97
+ this.log(styles.muted('Cancelled.'));
98
+ return;
99
+ }
100
+ }
101
+ await this.storage.deleteRoadmap(roadmapId);
102
+ this.log(styles.success(`Deleted roadmap "${roadmap.name}"`));
103
+ }
104
+ }
@@ -0,0 +1,22 @@
1
+ import { PMOCommand } from '../../lib/pmo/index.js';
2
+ export default class RoadmapGenerate 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
+ all: import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
+ output: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
+ json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
12
+ project: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
+ };
14
+ protected getPMOOptions(): {
15
+ promptIfMultiple: boolean;
16
+ };
17
+ execute(): Promise<void>;
18
+ private generateRoadmap;
19
+ private getPriorityLabel;
20
+ private cleanForTable;
21
+ private truncateDescription;
22
+ }