@proletariat/cli 0.3.25 → 0.3.27

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 (123) hide show
  1. package/dist/commands/action/index.js +2 -2
  2. package/dist/commands/action/show.js +7 -1
  3. package/dist/commands/agent/auth.js +1 -1
  4. package/dist/commands/agent/cleanup.js +6 -6
  5. package/dist/commands/agent/discover.js +1 -1
  6. package/dist/commands/agent/remove.js +4 -4
  7. package/dist/commands/autocomplete/setup.d.ts +2 -2
  8. package/dist/commands/autocomplete/setup.js +5 -5
  9. package/dist/commands/branch/create.js +31 -30
  10. package/dist/commands/branch/list.js +14 -11
  11. package/dist/commands/branch/validate.js +10 -1
  12. package/dist/commands/category/create.js +4 -5
  13. package/dist/commands/category/delete.js +2 -3
  14. package/dist/commands/category/rename.js +2 -3
  15. package/dist/commands/claude.d.ts +2 -8
  16. package/dist/commands/claude.js +26 -26
  17. package/dist/commands/commit.d.ts +2 -8
  18. package/dist/commands/commit.js +4 -26
  19. package/dist/commands/config/index.d.ts +2 -10
  20. package/dist/commands/config/index.js +8 -34
  21. package/dist/commands/docker/clean.js +7 -9
  22. package/dist/commands/docker/index.d.ts +2 -2
  23. package/dist/commands/docker/index.js +13 -12
  24. package/dist/commands/docker/list.d.ts +1 -0
  25. package/dist/commands/docker/list.js +31 -17
  26. package/dist/commands/docker/status.d.ts +3 -1
  27. package/dist/commands/docker/status.js +28 -2
  28. package/dist/commands/docker/sync.js +7 -6
  29. package/dist/commands/epic/delete.js +4 -5
  30. package/dist/commands/epic/list.js +17 -2
  31. package/dist/commands/execution/list.js +25 -17
  32. package/dist/commands/feedback/submit.d.ts +2 -2
  33. package/dist/commands/feedback/submit.js +9 -9
  34. package/dist/commands/link/index.js +2 -2
  35. package/dist/commands/pmo/init.d.ts +2 -2
  36. package/dist/commands/pmo/init.js +29 -10
  37. package/dist/commands/project/spec.js +6 -6
  38. package/dist/commands/repo/list.js +14 -8
  39. package/dist/commands/repo/view.js +2 -1
  40. package/dist/commands/roadmap/list.js +16 -1
  41. package/dist/commands/session/health.d.ts +29 -0
  42. package/dist/commands/session/health.js +496 -0
  43. package/dist/commands/session/index.js +4 -0
  44. package/dist/commands/session/list.js +15 -8
  45. package/dist/commands/spec/edit.js +2 -3
  46. package/dist/commands/staff/add.d.ts +2 -2
  47. package/dist/commands/staff/add.js +15 -14
  48. package/dist/commands/staff/index.js +2 -2
  49. package/dist/commands/staff/list.d.ts +3 -1
  50. package/dist/commands/staff/list.js +15 -1
  51. package/dist/commands/staff/remove.js +4 -4
  52. package/dist/commands/status/index.js +6 -7
  53. package/dist/commands/template/apply.js +10 -11
  54. package/dist/commands/template/create.js +18 -17
  55. package/dist/commands/template/index.d.ts +2 -2
  56. package/dist/commands/template/index.js +6 -6
  57. package/dist/commands/template/save.js +8 -7
  58. package/dist/commands/template/update.js +6 -7
  59. package/dist/commands/terminal/title.d.ts +2 -26
  60. package/dist/commands/terminal/title.js +4 -33
  61. package/dist/commands/theme/index.d.ts +2 -2
  62. package/dist/commands/theme/index.js +19 -18
  63. package/dist/commands/theme/list.d.ts +3 -0
  64. package/dist/commands/theme/list.js +25 -0
  65. package/dist/commands/theme/set.d.ts +2 -2
  66. package/dist/commands/theme/set.js +5 -5
  67. package/dist/commands/ticket/complete.js +4 -1
  68. package/dist/commands/ticket/create.d.ts +1 -0
  69. package/dist/commands/ticket/create.js +64 -16
  70. package/dist/commands/ticket/delete.js +18 -16
  71. package/dist/commands/ticket/edit.js +22 -14
  72. package/dist/commands/ticket/epic.js +12 -10
  73. package/dist/commands/ticket/list.js +24 -5
  74. package/dist/commands/ticket/move.js +4 -1
  75. package/dist/commands/ticket/project.js +11 -9
  76. package/dist/commands/ticket/reassign.js +23 -19
  77. package/dist/commands/ticket/spec.js +7 -5
  78. package/dist/commands/ticket/update.js +55 -53
  79. package/dist/commands/ticket/view.js +4 -2
  80. package/dist/commands/whoami.d.ts +3 -0
  81. package/dist/commands/whoami.js +22 -4
  82. package/dist/commands/work/complete.js +2 -2
  83. package/dist/commands/work/ready.js +9 -9
  84. package/dist/commands/work/revise.js +15 -13
  85. package/dist/commands/work/spawn.js +154 -57
  86. package/dist/commands/work/start.d.ts +1 -0
  87. package/dist/commands/work/start.js +299 -177
  88. package/dist/commands/workspace/prune.d.ts +3 -2
  89. package/dist/commands/workspace/prune.js +70 -10
  90. package/dist/hooks/init.js +4 -0
  91. package/dist/lib/agents/commands.js +4 -0
  92. package/dist/lib/agents/index.js +12 -0
  93. package/dist/lib/execution/devcontainer.d.ts +4 -0
  94. package/dist/lib/execution/devcontainer.js +63 -0
  95. package/dist/lib/mcp/helpers.d.ts +15 -0
  96. package/dist/lib/mcp/helpers.js +15 -0
  97. package/dist/lib/mcp/tools/action.js +5 -5
  98. package/dist/lib/mcp/tools/board.js +7 -7
  99. package/dist/lib/mcp/tools/category.js +5 -5
  100. package/dist/lib/mcp/tools/cli-passthrough.js +30 -30
  101. package/dist/lib/mcp/tools/epic.js +8 -8
  102. package/dist/lib/mcp/tools/phase.js +7 -7
  103. package/dist/lib/mcp/tools/project.js +10 -10
  104. package/dist/lib/mcp/tools/roadmap.js +7 -7
  105. package/dist/lib/mcp/tools/spec.js +9 -9
  106. package/dist/lib/mcp/tools/status.js +6 -6
  107. package/dist/lib/mcp/tools/template.js +6 -6
  108. package/dist/lib/mcp/tools/ticket.js +19 -19
  109. package/dist/lib/mcp/tools/view.js +4 -4
  110. package/dist/lib/mcp/tools/work.js +6 -6
  111. package/dist/lib/mcp/tools/workflow.js +5 -5
  112. package/dist/lib/pmo/index.js +4 -0
  113. package/dist/lib/pmo/storage/base.js +49 -0
  114. package/dist/lib/pr/index.d.ts +9 -0
  115. package/dist/lib/pr/index.js +101 -14
  116. package/dist/lib/prompt-command.d.ts +3 -0
  117. package/dist/lib/prompt-json.d.ts +72 -1
  118. package/dist/lib/prompt-json.js +46 -0
  119. package/dist/lib/repos/index.js +4 -0
  120. package/dist/lib/string-utils.d.ts +10 -0
  121. package/dist/lib/string-utils.js +16 -0
  122. package/oclif.manifest.json +594 -449
  123. package/package.json +3 -2
@@ -1,11 +1,12 @@
1
- import { Command, Args, Flags } from '@oclif/core';
1
+ import { Args, Flags } from '@oclif/core';
2
2
  import chalk from 'chalk';
3
3
  import inquirer from 'inquirer';
4
+ import { PromptCommand } from '../../lib/prompt-command.js';
4
5
  import { getWorkspaceInfo, validateAgentNames, addAgentsToWorkspace } from '../../lib/agents/commands.js';
5
6
  import { ensureBuiltinThemes, BUILTIN_THEMES, isValidAgentName, normalizeAgentName } from '../../lib/themes.js';
6
7
  import { getTheme, getThemes, getAvailableThemeNames, getActiveTheme } from '../../lib/database/index.js';
7
8
  import { shouldOutputJson, outputPromptAsJson, outputErrorAsJson, createMetadata, buildPromptConfig, } from '../../lib/prompt-json.js';
8
- export default class Add extends Command {
9
+ export default class Add extends PromptCommand {
9
10
  static description = 'Add new agents to the workspace';
10
11
  static examples = [
11
12
  '<%= config.bin %> <%= command.id %> zeus',
@@ -82,13 +83,13 @@ export default class Add extends Command {
82
83
  return;
83
84
  }
84
85
  // Interactive selection from theme
85
- const { selected } = await inquirer.prompt([{
86
+ const { selected } = await this.prompt([{
86
87
  type: 'checkbox',
87
88
  name: 'selected',
88
89
  message: selectMessage,
89
90
  choices: nameChoices,
90
91
  validate: (input) => input.length > 0 || 'Please select at least one name'
91
- }]);
92
+ }], null);
92
93
  agentNames = selected;
93
94
  }
94
95
  // Interactive mode: show names from workspace's active theme
@@ -127,19 +128,19 @@ export default class Add extends Command {
127
128
  new inquirer.Separator(),
128
129
  { name: chalk.blue(nameChoices[nameChoices.length - 1].name), value: nameChoices[nameChoices.length - 1].value }
129
130
  ];
130
- const { selected } = await inquirer.prompt([{
131
+ const { selected } = await this.prompt([{
131
132
  type: 'checkbox',
132
133
  name: 'selected',
133
134
  message: selectMessage,
134
135
  choices,
135
136
  pageSize: 20,
136
137
  validate: (input) => input.length > 0 || 'Please select at least one name'
137
- }]);
138
+ }], null);
138
139
  // Check if custom was selected
139
140
  const hasCustom = selected.includes('__custom__');
140
141
  const themedSelections = selected.filter((s) => s !== '__custom__');
141
142
  if (hasCustom) {
142
- const { customNames } = await inquirer.prompt([{
143
+ const { customNames } = await this.prompt([{
143
144
  type: 'input',
144
145
  name: 'customNames',
145
146
  message: 'Enter custom agent names (space-separated):',
@@ -148,7 +149,7 @@ export default class Add extends Command {
148
149
  return 'Please enter at least one name';
149
150
  return true;
150
151
  }
151
- }]);
152
+ }], null);
152
153
  const rawNames = customNames.trim().split(/\s+/);
153
154
  const normalizedCustom = rawNames.map((n) => normalizeAgentName(n)).filter((n) => n && isValidAgentName(n));
154
155
  agentNames.push(...normalizedCustom);
@@ -187,14 +188,14 @@ export default class Add extends Command {
187
188
  new inquirer.Separator(),
188
189
  { name: chalk.blue(themeChoices[themeChoices.length - 1].name), value: themeChoices[themeChoices.length - 1].value }
189
190
  ];
190
- const { selectedTheme } = await inquirer.prompt([{
191
+ const { selectedTheme } = await this.prompt([{
191
192
  type: 'list',
192
193
  name: 'selectedTheme',
193
194
  message: selectMessage,
194
195
  choices: interactiveChoices
195
- }]);
196
+ }], null);
196
197
  if (selectedTheme === '__custom__') {
197
- const { customNames } = await inquirer.prompt([{
198
+ const { customNames } = await this.prompt([{
198
199
  type: 'input',
199
200
  name: 'customNames',
200
201
  message: 'Enter custom agent names (space-separated):',
@@ -203,7 +204,7 @@ export default class Add extends Command {
203
204
  return 'Please enter at least one name';
204
205
  return true;
205
206
  }
206
- }]);
207
+ }], null);
207
208
  const rawNames = customNames.trim().split(/\s+/);
208
209
  const normalizedCustom = rawNames.map((n) => ({
209
210
  original: n,
@@ -222,14 +223,14 @@ export default class Add extends Command {
222
223
  themeId = selectedTheme;
223
224
  const theme = getTheme(workspaceInfo.path, selectedTheme);
224
225
  const availableNames = getAvailableThemeNames(workspaceInfo.path, selectedTheme);
225
- const { selected } = await inquirer.prompt([{
226
+ const { selected } = await this.prompt([{
226
227
  type: 'checkbox',
227
228
  name: 'selected',
228
229
  message: `Select agents from ${theme?.display_name}:`,
229
230
  choices: availableNames.map(name => ({ name, value: name })),
230
231
  pageSize: 20,
231
232
  validate: (input) => input.length > 0 || 'Please select at least one name'
232
- }]);
233
+ }], null);
233
234
  agentNames = selected;
234
235
  }
235
236
  }
@@ -46,7 +46,7 @@ export default class Staff extends PMOCommand {
46
46
  }
47
47
  this.log(colors.primary('Staff Agents'));
48
48
  this.log(colors.textMuted('Persistent agents with dedicated worktrees.\n'));
49
- const { action } = await inquirer.prompt([{
49
+ const { action } = await this.prompt([{
50
50
  type: 'list',
51
51
  name: 'action',
52
52
  message,
@@ -57,7 +57,7 @@ export default class Staff extends PMOCommand {
57
57
  new inquirer.Separator(),
58
58
  { name: '❌ ' + menuChoices[3].name, value: menuChoices[3].value },
59
59
  ]
60
- }]);
60
+ }], null);
61
61
  if (action === 'cancel') {
62
62
  this.log(colors.textMuted('Operation cancelled.'));
63
63
  return;
@@ -2,6 +2,8 @@ import { Command } from '@oclif/core';
2
2
  export default class List extends Command {
3
3
  static description: string;
4
4
  static examples: string[];
5
- static flags: {};
5
+ static flags: {
6
+ json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
7
+ };
6
8
  run(): Promise<void>;
7
9
  }
@@ -3,25 +3,39 @@ import chalk from 'chalk';
3
3
  import * as path from 'node:path';
4
4
  import * as fs from 'node:fs';
5
5
  import { getWorkspaceInfo, getAllAgentsStatus } from '../../lib/agents/commands.js';
6
+ import { shouldOutputJson } from '../../lib/prompt-json.js';
7
+ import { machineOutputFlags } from '../../lib/pmo/index.js';
6
8
  export default class List extends Command {
7
9
  static description = 'List all staff (persistent) agents and their status';
8
10
  static examples = [
9
11
  '<%= config.bin %> <%= command.id %>',
10
12
  ];
11
- static flags = {};
13
+ static flags = {
14
+ ...machineOutputFlags,
15
+ };
12
16
  async run() {
17
+ const { flags } = await this.parse(List);
18
+ const jsonMode = shouldOutputJson(flags);
13
19
  try {
14
20
  // Get workspace information
15
21
  const workspaceInfo = getWorkspaceInfo();
16
22
  // Filter to staff agents only
17
23
  const staffAgents = workspaceInfo.agents.filter(a => a.type === 'persistent');
18
24
  if (staffAgents.length === 0) {
25
+ if (jsonMode) {
26
+ this.log(JSON.stringify([], null, 2));
27
+ return;
28
+ }
19
29
  this.log(chalk.yellow('No staff agents found. Add staff agents with "prlt staff add"'));
20
30
  return;
21
31
  }
22
32
  // Get status for all agents and filter to staff
23
33
  const agentsStatus = getAllAgentsStatus(workspaceInfo);
24
34
  const staffStatus = agentsStatus.filter(a => staffAgents.some(s => s.name === a.name));
35
+ if (jsonMode) {
36
+ this.log(JSON.stringify(staffStatus, null, 2));
37
+ return;
38
+ }
25
39
  this.log(chalk.bold.cyan('\n Staff Agents:\n'));
26
40
  for (const agentStatus of staffStatus) {
27
41
  // Agent info line
@@ -71,7 +71,7 @@ export default class Remove extends PMOCommand {
71
71
  outputPromptAsJson(buildPromptConfig('list', 'name', selectMessage, agentChoices), createMetadata('staff remove', flags));
72
72
  return;
73
73
  }
74
- const { selected } = await inquirer.prompt([
74
+ const { selected } = await this.prompt([
75
75
  {
76
76
  type: 'list',
77
77
  name: 'selected',
@@ -82,7 +82,7 @@ export default class Remove extends PMOCommand {
82
82
  { name: '❌ ' + agentChoices[agentChoices.length - 1].name, value: agentChoices[agentChoices.length - 1].value }
83
83
  ]
84
84
  }
85
- ]);
85
+ ], null);
86
86
  if (selected === 'cancel') {
87
87
  this.log(colors.textMuted('Operation cancelled.'));
88
88
  return;
@@ -109,7 +109,7 @@ export default class Remove extends PMOCommand {
109
109
  outputPromptAsJson(buildPromptConfig('list', 'confirmed', confirmMessage, confirmChoices), createMetadata('staff remove', flags));
110
110
  return;
111
111
  }
112
- const { confirm } = await inquirer.prompt([
112
+ const { confirm } = await this.prompt([
113
113
  {
114
114
  type: 'list',
115
115
  name: 'confirm',
@@ -120,7 +120,7 @@ export default class Remove extends PMOCommand {
120
120
  ],
121
121
  default: 0 // Default to "No, cancel"
122
122
  }
123
- ]);
123
+ ], null);
124
124
  if (!confirm) {
125
125
  this.log(colors.textMuted('Removal cancelled.'));
126
126
  return;
@@ -1,4 +1,3 @@
1
- import inquirer from 'inquirer';
2
1
  import { PMOCommand, pmoBaseFlags } from '../../lib/pmo/index.js';
3
2
  import { FlagResolver, shouldOutputJson } from '../../lib/flags/index.js';
4
3
  export default class Status extends PMOCommand {
@@ -62,34 +61,34 @@ export default class Status extends PMOCommand {
62
61
  case 'update': {
63
62
  // First list statuses, then prompt for selection
64
63
  await this.config.runCommand('status:list', []);
65
- const { statusId } = await inquirer.prompt([{
64
+ const { statusId } = await this.prompt([{
66
65
  type: 'input',
67
66
  name: 'statusId',
68
67
  message: 'Status ID to update:',
69
68
  validate: (input) => input.length > 0 || 'Status ID is required',
70
- }]);
69
+ }], null);
71
70
  await this.config.runCommand('status:update', [statusId]);
72
71
  break;
73
72
  }
74
73
  case 'move': {
75
74
  await this.config.runCommand('status:list', []);
76
- const { statusId } = await inquirer.prompt([{
75
+ const { statusId } = await this.prompt([{
77
76
  type: 'input',
78
77
  name: 'statusId',
79
78
  message: 'Status ID to move:',
80
79
  validate: (input) => input.length > 0 || 'Status ID is required',
81
- }]);
80
+ }], null);
82
81
  await this.config.runCommand('status:move', [statusId]);
83
82
  break;
84
83
  }
85
84
  case 'delete': {
86
85
  await this.config.runCommand('status:list', []);
87
- const { statusId } = await inquirer.prompt([{
86
+ const { statusId } = await this.prompt([{
88
87
  type: 'input',
89
88
  name: 'statusId',
90
89
  message: 'Status ID to delete:',
91
90
  validate: (input) => input.length > 0 || 'Status ID is required',
92
- }]);
91
+ }], null);
93
92
  await this.config.runCommand('status:delete', [statusId]);
94
93
  break;
95
94
  }
@@ -1,5 +1,4 @@
1
1
  import { Flags, Args } from '@oclif/core';
2
- import inquirer from 'inquirer';
3
2
  import { PMOCommand, pmoBaseFlags, autoExportToBoard } from '../../lib/pmo/index.js';
4
3
  import { PRIORITIES, PRIORITY_LABELS } from '../../lib/pmo/types.js';
5
4
  import { styles } from '../../lib/styles.js';
@@ -92,7 +91,7 @@ export default class TemplateApply extends PMOCommand {
92
91
  ]), createMetadata('template apply', flags));
93
92
  return;
94
93
  }
95
- const { selectedType } = await inquirer.prompt([{
94
+ const { selectedType } = await this.prompt([{
96
95
  type: 'list',
97
96
  name: 'selectedType',
98
97
  message: 'What type of template?',
@@ -100,7 +99,7 @@ export default class TemplateApply extends PMOCommand {
100
99
  { name: 'Ticket template', value: 'ticket' },
101
100
  { name: 'Phase template', value: 'phase' },
102
101
  ],
103
- }]);
102
+ }], null);
104
103
  templateType = selectedType;
105
104
  }
106
105
  if (templateType === 'ticket') {
@@ -127,12 +126,12 @@ export default class TemplateApply extends PMOCommand {
127
126
  if (templates.length === 0) {
128
127
  return handleError('NO_TEMPLATES', 'No ticket templates. Create with: prlt template create --type ticket');
129
128
  }
130
- const { selected } = await inquirer.prompt([{
129
+ const { selected } = await this.prompt([{
131
130
  type: 'list',
132
131
  name: 'selected',
133
132
  message: 'Select template:',
134
133
  choices: templates.map(t => ({ name: `${t.name}${t.description ? ` - ${t.description}` : ''}`, value: t.id })),
135
- }]);
134
+ }], null);
136
135
  templateId = selected;
137
136
  }
138
137
  const template = await this.storage.getTicketTemplate(templateId);
@@ -159,10 +158,10 @@ export default class TemplateApply extends PMOCommand {
159
158
  outputPromptAsJson(buildFormPromptConfig(fields), createMetadata('template apply', flags));
160
159
  return;
161
160
  }
162
- const answers = await inquirer.prompt(fields.map(f => ({
161
+ const answers = await this.prompt(fields.map(f => ({
163
162
  ...f,
164
163
  validate: f.name === 'title' ? ((i) => i.length > 0 || 'Required') : undefined,
165
- })));
164
+ })), null);
166
165
  title = answers.title;
167
166
  column = answers.column;
168
167
  priority = answers.priority || undefined;
@@ -215,12 +214,12 @@ export default class TemplateApply extends PMOCommand {
215
214
  if (templates.length === 0) {
216
215
  return handleError('NO_TEMPLATES', 'No phase templates. Create with: prlt template create --type phase');
217
216
  }
218
- const { selected } = await inquirer.prompt([{
217
+ const { selected } = await this.prompt([{
219
218
  type: 'list',
220
219
  name: 'selected',
221
220
  message: 'Select phase template:',
222
221
  choices: templates.map(t => ({ name: `${t.name}${t.description ? ` - ${t.description}` : ''}`, value: t.id })),
223
- }]);
222
+ }], null);
224
223
  templateId = selected;
225
224
  }
226
225
  const template = await this.storage.getPhaseTemplate(templateId);
@@ -238,7 +237,7 @@ export default class TemplateApply extends PMOCommand {
238
237
  return;
239
238
  }
240
239
  this.log(styles.warning(`\nThis will REPLACE ${existingPhases.length} existing phase(s).`));
241
- const { confirm } = await inquirer.prompt([{
240
+ const { confirm } = await this.prompt([{
242
241
  type: 'list',
243
242
  name: 'confirm',
244
243
  message: `Apply template "${template.name}"?`,
@@ -246,7 +245,7 @@ export default class TemplateApply extends PMOCommand {
246
245
  { name: 'No', value: false },
247
246
  { name: 'Yes', value: true },
248
247
  ],
249
- }]);
248
+ }], null);
250
249
  if (!confirm) {
251
250
  this.log(styles.muted('Cancelled'));
252
251
  return;
@@ -1,5 +1,4 @@
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 { PRIORITIES, PRIORITY_LABELS, TICKET_CATEGORIES } from '../../lib/pmo/types.js';
5
4
  import { styles } from '../../lib/styles.js';
@@ -82,15 +81,15 @@ export default class TemplateCreate extends PMOCommand {
82
81
  ]), createMetadata('template create', flags));
83
82
  return;
84
83
  }
85
- const { selectedType } = await inquirer.prompt([{
84
+ const { selectedType } = await this.prompt([{
86
85
  type: 'list',
87
86
  name: 'selectedType',
88
87
  message: 'What type of template?',
89
88
  choices: [
90
- { name: 'Ticket template', value: 'ticket' },
91
- { name: 'Phase template', value: 'phase' },
89
+ { name: 'Ticket template', value: 'ticket', command: 'prlt template create --type ticket --json' },
90
+ { name: 'Phase template', value: 'phase', command: 'prlt template create --type phase --json' },
92
91
  ],
93
- }]);
92
+ }], jsonMode ? { flags, commandName: 'template create' } : null);
94
93
  templateType = selectedType;
95
94
  }
96
95
  if (templateType === 'ticket') {
@@ -129,12 +128,13 @@ export default class TemplateCreate extends PMOCommand {
129
128
  outputPromptAsJson(buildFormPromptConfig(fields), createMetadata('template create', flags));
130
129
  return;
131
130
  }
132
- const { templateName } = await inquirer.prompt([{
131
+ const jsonModeConfig = jsonMode ? { flags: flags, commandName: 'template create' } : null;
132
+ const { templateName } = await this.prompt([{
133
133
  type: 'input',
134
134
  name: 'templateName',
135
135
  message: 'Template name:',
136
136
  validate: (input) => input.trim() ? true : 'Name required',
137
- }]);
137
+ }], jsonModeConfig);
138
138
  name = templateName;
139
139
  }
140
140
  // Get optional values
@@ -148,13 +148,13 @@ export default class TemplateCreate extends PMOCommand {
148
148
  let descriptionTemplate = flags['description-template'];
149
149
  // Interactive prompts for missing optional values
150
150
  if (!jsonMode && description === undefined) {
151
- const { desc } = await inquirer.prompt([{
151
+ const { desc } = await this.prompt([{
152
152
  type: 'input', name: 'desc', message: 'Description (optional):',
153
- }]);
153
+ }], null);
154
154
  description = desc || undefined;
155
155
  }
156
156
  if (!jsonMode && priority === undefined) {
157
- const { p } = await inquirer.prompt([{
157
+ const { p } = await this.prompt([{
158
158
  type: 'list',
159
159
  name: 'p',
160
160
  message: 'Default priority:',
@@ -162,11 +162,11 @@ export default class TemplateCreate extends PMOCommand {
162
162
  { name: 'None', value: '' },
163
163
  ...PRIORITIES.map(pr => ({ name: PRIORITY_LABELS[pr], value: pr })),
164
164
  ],
165
- }]);
165
+ }], null);
166
166
  priority = p || undefined;
167
167
  }
168
168
  if (!jsonMode && category === undefined) {
169
- const { c } = await inquirer.prompt([{
169
+ const { c } = await this.prompt([{
170
170
  type: 'list',
171
171
  name: 'c',
172
172
  message: 'Default category:',
@@ -174,7 +174,7 @@ export default class TemplateCreate extends PMOCommand {
174
174
  { name: 'None', value: '' },
175
175
  ...TICKET_CATEGORIES.map(cat => ({ name: cat, value: cat })),
176
176
  ],
177
- }]);
177
+ }], null);
178
178
  category = c || undefined;
179
179
  }
180
180
  // Build description template with ACs
@@ -207,19 +207,20 @@ export default class TemplateCreate extends PMOCommand {
207
207
  outputErrorAsJson('NAME_REQUIRED', 'Name required: prlt template create --type phase "Name"', createMetadata('template create', flags));
208
208
  return;
209
209
  }
210
- const { templateName } = await inquirer.prompt([{
210
+ const jsonModeConfig = jsonMode ? { flags: flags, commandName: 'template create' } : null;
211
+ const { templateName } = await this.prompt([{
211
212
  type: 'input',
212
213
  name: 'templateName',
213
214
  message: 'Template name:',
214
215
  validate: (input) => input.trim() ? true : 'Name required',
215
- }]);
216
+ }], jsonModeConfig);
216
217
  name = templateName;
217
218
  }
218
219
  let description = flags.description;
219
220
  if (!jsonMode && description === undefined) {
220
- const { desc } = await inquirer.prompt([{
221
+ const { desc } = await this.prompt([{
221
222
  type: 'input', name: 'desc', message: 'Description (optional):',
222
- }]);
223
+ }], null);
223
224
  description = desc || undefined;
224
225
  }
225
226
  const template = await this.storage.savePhaseTemplate(name, description);
@@ -1,5 +1,5 @@
1
- import { Command } from '@oclif/core';
2
- export default class Template extends Command {
1
+ import { PromptCommand } from '../../lib/prompt-command.js';
2
+ export default class Template extends PromptCommand {
3
3
  static description: string;
4
4
  static aliases: string[];
5
5
  static examples: string[];
@@ -1,8 +1,8 @@
1
- import { Command, Flags } from '@oclif/core';
2
- import inquirer from 'inquirer';
1
+ import { Flags } from '@oclif/core';
3
2
  import { styles } from '../../lib/styles.js';
4
3
  import { shouldOutputJson, outputPromptAsJson, buildPromptConfig, createMetadata } from '../../lib/prompt-json.js';
5
- export default class Template extends Command {
4
+ import { PromptCommand } from '../../lib/prompt-command.js';
5
+ export default class Template extends PromptCommand {
6
6
  static description = 'Manage templates (ticket and phase)';
7
7
  static aliases = ['templates'];
8
8
  static examples = [
@@ -37,12 +37,12 @@ export default class Template extends Command {
37
37
  }
38
38
  this.log(`\n${styles.emphasis('Templates')}`);
39
39
  this.log(styles.muted('Manage ticket and phase templates\n'));
40
- const { action } = await inquirer.prompt([{
40
+ const { action } = await this.prompt([{
41
41
  type: 'list',
42
42
  name: 'action',
43
43
  message: 'What would you like to do?',
44
- choices: menuChoices.map(c => ({ name: c.name, value: c.value })),
45
- }]);
44
+ choices: menuChoices.map(c => ({ name: c.name, value: c.value, command: c.command })),
45
+ }], null);
46
46
  if (action === 'cancel')
47
47
  return;
48
48
  switch (action) {
@@ -1,5 +1,4 @@
1
1
  import { Flags, Args } 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 { shouldOutputJson, outputPromptAsJson, outputSuccessAsJson, outputErrorAsJson, createMetadata, buildPromptConfig, } from '../../lib/prompt-json.js';
@@ -58,12 +57,13 @@ export default class TemplateSave extends PMOCommand {
58
57
  outputPromptAsJson(buildPromptConfig('list', 'ticket', 'Select ticket:', choices), createMetadata('template save', flags));
59
58
  return;
60
59
  }
61
- const { selected } = await inquirer.prompt([{
60
+ const jsonModeConfig = jsonMode ? { flags, commandName: 'template save' } : null;
61
+ const { selected } = await this.prompt([{
62
62
  type: 'list',
63
63
  name: 'selected',
64
64
  message: 'Select ticket:',
65
- choices: tickets.slice(0, 20).map(t => ({ name: `${t.id} - ${t.title}`, value: t.id })),
66
- }]);
65
+ choices: tickets.slice(0, 20).map(t => ({ name: `${t.id} - ${t.title}`, value: t.id, command: `prlt template save ${t.id} --json` })),
66
+ }], jsonModeConfig);
67
67
  ticketId = selected;
68
68
  }
69
69
  const ticket = await this.storage.getTicket(ticketId);
@@ -77,19 +77,20 @@ export default class TemplateSave extends PMOCommand {
77
77
  outputPromptAsJson(buildPromptConfig('input', 'name', 'Template name:', undefined, ticket.category || ticket.title.split(' ')[0]), createMetadata('template save', flags));
78
78
  return;
79
79
  }
80
- const { name } = await inquirer.prompt([{
80
+ const jsonModeConfig2 = jsonMode ? { flags, commandName: 'template save' } : null;
81
+ const { name } = await this.prompt([{
81
82
  type: 'input',
82
83
  name: 'name',
83
84
  message: 'Template name:',
84
85
  default: ticket.category || ticket.title.split(' ')[0],
85
86
  validate: (i) => i.length > 0 || 'Required',
86
- }]);
87
+ }], jsonModeConfig2);
87
88
  templateName = name;
88
89
  }
89
90
  // Get description
90
91
  let description = flags.description;
91
92
  if (description === undefined && !jsonMode) {
92
- const { desc } = await inquirer.prompt([{ type: 'input', name: 'desc', message: 'Description (optional):' }]);
93
+ const { desc } = await this.prompt([{ type: 'input', name: 'desc', message: 'Description (optional):' }], null);
93
94
  description = desc || undefined;
94
95
  }
95
96
  const template = await this.storage.createTicketTemplateFromTicket(ticketId, templateName, description);
@@ -1,5 +1,4 @@
1
1
  import { Flags, Args } 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 { shouldOutputJson, outputPromptAsJson, outputSuccessAsJson, outputErrorAsJson, createMetadata, buildPromptConfig, } from '../../lib/prompt-json.js';
@@ -57,26 +56,26 @@ export default class TemplateUpdate extends PMOCommand {
57
56
  outputPromptAsJson(buildPromptConfig('list', 'id', 'Select template:', choices), createMetadata('template update', flags));
58
57
  return;
59
58
  }
60
- const { selected } = await inquirer.prompt([{
59
+ const { selected } = await this.prompt([{
61
60
  type: 'list',
62
61
  name: 'selected',
63
62
  message: 'Select template:',
64
63
  choices: editable.map(t => ({ name: `${t.name}${t.description ? ` - ${t.description}` : ''}`, value: t.id })),
65
- }]);
64
+ }], null);
66
65
  templateId = selected;
67
66
  }
68
67
  // Get updates
69
68
  let newName = flags.name;
70
69
  let newDescription = flags.description;
71
70
  if (!newName && newDescription === undefined && !jsonMode) {
72
- const { updateName } = await inquirer.prompt([{
71
+ const { updateName } = await this.prompt([{
73
72
  type: 'input', name: 'updateName', message: 'New name (leave empty to keep):',
74
- }]);
73
+ }], null);
75
74
  if (updateName)
76
75
  newName = updateName;
77
- const { updateDesc } = await inquirer.prompt([{
76
+ const { updateDesc } = await this.prompt([{
78
77
  type: 'input', name: 'updateDesc', message: 'New description (leave empty to keep):',
79
- }]);
78
+ }], null);
80
79
  if (updateDesc)
81
80
  newDescription = updateDesc;
82
81
  if (!newName && !newDescription) {
@@ -1,6 +1,5 @@
1
- import { Command } from '@oclif/core';
2
- import { type JsonFlags } from '../../lib/prompt-json.js';
3
- export default class TerminalTitle extends Command {
1
+ import { PromptCommand } from '../../lib/prompt-command.js';
2
+ export default class TerminalTitle extends PromptCommand {
4
3
  static description: string;
5
4
  static examples: string[];
6
5
  static args: {
@@ -10,28 +9,5 @@ export default class TerminalTitle extends Command {
10
9
  json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
10
  reset: import("@oclif/core/interfaces").BooleanFlag<boolean>;
12
11
  };
13
- /**
14
- * Prompt wrapper - drop-in replacement for inquirer.prompt with JSON mode support.
15
- * Matches the pattern from PMOCommand.prompt().
16
- *
17
- * In JSON mode: outputs prompt config as JSON and exits
18
- * In interactive mode: shows normal inquirer prompt
19
- */
20
- protected prompt<T extends Record<string, unknown>>(questions: Array<{
21
- type: string;
22
- name: string;
23
- message: string;
24
- choices?: Array<string | {
25
- name: string;
26
- value: unknown;
27
- disabled?: boolean | string;
28
- command?: string;
29
- } | unknown>;
30
- default?: unknown;
31
- validate?: (input: unknown) => boolean | string;
32
- }>, jsonModeConfig?: {
33
- flags: JsonFlags & Record<string, unknown>;
34
- commandName: string;
35
- } | null): Promise<T>;
36
12
  run(): Promise<void>;
37
13
  }