@proletariat/cli 0.3.22 → 0.3.24

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 (173) hide show
  1. package/LICENSE +190 -21
  2. package/README.md +7 -7
  3. package/dist/commands/action/create.js +1 -1
  4. package/dist/commands/agent/{temp/cleanup.d.ts → cleanup.d.ts} +1 -1
  5. package/dist/commands/agent/{temp/cleanup.js → cleanup.js} +4 -4
  6. package/dist/commands/agent/index.js +8 -8
  7. package/dist/commands/branch/create.js +2 -2
  8. package/dist/commands/epic/create.d.ts +1 -0
  9. package/dist/commands/epic/create.js +39 -2
  10. package/dist/commands/epic/index.js +2 -2
  11. package/dist/commands/{epic/link/remove.d.ts → link/create.d.ts} +6 -7
  12. package/dist/commands/link/create.js +141 -0
  13. package/dist/commands/{epic/link/relates.d.ts → link/index.d.ts} +4 -5
  14. package/dist/commands/link/index.js +87 -0
  15. package/dist/commands/{epic/link/duplicates.d.ts → link/list.d.ts} +7 -4
  16. package/dist/commands/link/list.js +182 -0
  17. package/dist/commands/{spec/link → link}/remove.d.ts +4 -5
  18. package/dist/commands/link/remove.js +120 -0
  19. package/dist/commands/mcp-server.d.ts +22 -0
  20. package/dist/commands/mcp-server.js +98 -0
  21. package/dist/commands/phase/create.js +1 -1
  22. package/dist/commands/project/create.d.ts +1 -0
  23. package/dist/commands/project/create.js +38 -4
  24. package/dist/commands/spec/create.d.ts +1 -0
  25. package/dist/commands/spec/create.js +43 -2
  26. package/dist/commands/spec/index.js +2 -2
  27. package/dist/commands/{agent/staff → staff}/add.js +10 -10
  28. package/dist/commands/{agent/staff → staff}/index.d.ts +1 -1
  29. package/dist/commands/{agent/staff → staff}/index.js +7 -7
  30. package/dist/commands/{agent/staff → staff}/list.js +3 -3
  31. package/dist/commands/{agent/staff → staff}/remove.d.ts +1 -1
  32. package/dist/commands/{agent/staff → staff}/remove.js +8 -8
  33. package/dist/commands/{ticket/template → template}/apply.d.ts +8 -6
  34. package/dist/commands/template/apply.js +262 -0
  35. package/dist/commands/{ticket/template → template}/create.d.ts +5 -6
  36. package/dist/commands/template/create.js +238 -0
  37. package/dist/commands/template/index.js +48 -36
  38. package/dist/commands/{ticket/template → template}/save.d.ts +2 -2
  39. package/dist/commands/template/save.js +104 -0
  40. package/dist/commands/{phase/template → template}/update.d.ts +2 -2
  41. package/dist/commands/template/update.js +99 -0
  42. package/dist/commands/{agent/themes → theme}/add-names.d.ts +1 -1
  43. package/dist/commands/{agent/themes → theme}/add-names.js +6 -6
  44. package/dist/commands/{agent/themes → theme}/create.d.ts +1 -1
  45. package/dist/commands/{agent/themes → theme}/create.js +5 -5
  46. package/dist/commands/{agent/themes → theme}/index.d.ts +1 -1
  47. package/dist/commands/{agent/themes → theme}/index.js +10 -10
  48. package/dist/commands/{agent/themes → theme}/list.d.ts +1 -1
  49. package/dist/commands/{agent/themes → theme}/list.js +5 -5
  50. package/dist/commands/{agent/themes → theme}/set.d.ts +1 -1
  51. package/dist/commands/{agent/themes → theme}/set.js +7 -7
  52. package/dist/commands/ticket/create.d.ts +1 -0
  53. package/dist/commands/ticket/create.js +54 -2
  54. package/dist/commands/ticket/index.js +6 -6
  55. package/dist/commands/work/spawn.js +1 -1
  56. package/dist/lib/mcp/helpers.d.ts +43 -0
  57. package/dist/lib/mcp/helpers.js +57 -0
  58. package/dist/lib/mcp/index.d.ts +6 -0
  59. package/dist/lib/mcp/index.js +6 -0
  60. package/dist/lib/mcp/tools/action.d.ts +6 -0
  61. package/dist/lib/mcp/tools/action.js +88 -0
  62. package/dist/lib/mcp/tools/board.d.ts +6 -0
  63. package/dist/lib/mcp/tools/board.js +139 -0
  64. package/dist/lib/mcp/tools/category.d.ts +6 -0
  65. package/dist/lib/mcp/tools/category.js +84 -0
  66. package/dist/lib/mcp/tools/cli-passthrough.d.ts +15 -0
  67. package/dist/lib/mcp/tools/cli-passthrough.js +333 -0
  68. package/dist/lib/mcp/tools/epic.d.ts +6 -0
  69. package/dist/lib/mcp/tools/epic.js +178 -0
  70. package/dist/lib/mcp/tools/index.d.ts +18 -0
  71. package/dist/lib/mcp/tools/index.js +19 -0
  72. package/dist/lib/mcp/tools/phase.d.ts +6 -0
  73. package/dist/lib/mcp/tools/phase.js +131 -0
  74. package/dist/lib/mcp/tools/project.d.ts +6 -0
  75. package/dist/lib/mcp/tools/project.js +196 -0
  76. package/dist/lib/mcp/tools/roadmap.d.ts +6 -0
  77. package/dist/lib/mcp/tools/roadmap.js +123 -0
  78. package/dist/lib/mcp/tools/spec.d.ts +6 -0
  79. package/dist/lib/mcp/tools/spec.js +196 -0
  80. package/dist/lib/mcp/tools/status.d.ts +6 -0
  81. package/dist/lib/mcp/tools/status.js +109 -0
  82. package/dist/lib/mcp/tools/template.d.ts +6 -0
  83. package/dist/lib/mcp/tools/template.js +107 -0
  84. package/dist/lib/mcp/tools/ticket.d.ts +6 -0
  85. package/dist/lib/mcp/tools/ticket.js +393 -0
  86. package/dist/lib/mcp/tools/view.d.ts +6 -0
  87. package/dist/lib/mcp/tools/view.js +76 -0
  88. package/dist/lib/mcp/tools/work.d.ts +6 -0
  89. package/dist/lib/mcp/tools/work.js +132 -0
  90. package/dist/lib/mcp/tools/workflow.d.ts +6 -0
  91. package/dist/lib/mcp/tools/workflow.js +95 -0
  92. package/dist/lib/mcp/types.d.ts +17 -0
  93. package/dist/lib/mcp/types.js +4 -0
  94. package/dist/lib/prompt-json.d.ts +52 -1
  95. package/dist/lib/prompt-json.js +45 -0
  96. package/oclif.manifest.json +3553 -5457
  97. package/package.json +10 -7
  98. package/dist/commands/agent/temp/index.d.ts +0 -14
  99. package/dist/commands/agent/temp/index.js +0 -85
  100. package/dist/commands/agent/temp/list.d.ts +0 -7
  101. package/dist/commands/agent/temp/list.js +0 -108
  102. package/dist/commands/epic/link/block.d.ts +0 -14
  103. package/dist/commands/epic/link/block.js +0 -81
  104. package/dist/commands/epic/link/duplicates.js +0 -68
  105. package/dist/commands/epic/link/index.d.ts +0 -19
  106. package/dist/commands/epic/link/index.js +0 -272
  107. package/dist/commands/epic/link/relates.js +0 -68
  108. package/dist/commands/epic/link/remove.js +0 -93
  109. package/dist/commands/phase/template/apply.d.ts +0 -17
  110. package/dist/commands/phase/template/apply.js +0 -108
  111. package/dist/commands/phase/template/create.d.ts +0 -17
  112. package/dist/commands/phase/template/create.js +0 -104
  113. package/dist/commands/phase/template/delete.d.ts +0 -17
  114. package/dist/commands/phase/template/delete.js +0 -100
  115. package/dist/commands/phase/template/index.d.ts +0 -15
  116. package/dist/commands/phase/template/index.js +0 -130
  117. package/dist/commands/phase/template/list.d.ts +0 -16
  118. package/dist/commands/phase/template/list.js +0 -97
  119. package/dist/commands/phase/template/update.js +0 -89
  120. package/dist/commands/spec/link/depends.d.ts +0 -14
  121. package/dist/commands/spec/link/depends.js +0 -64
  122. package/dist/commands/spec/link/duplicates.d.ts +0 -14
  123. package/dist/commands/spec/link/duplicates.js +0 -63
  124. package/dist/commands/spec/link/index.d.ts +0 -19
  125. package/dist/commands/spec/link/index.js +0 -207
  126. package/dist/commands/spec/link/relates.d.ts +0 -14
  127. package/dist/commands/spec/link/relates.js +0 -63
  128. package/dist/commands/spec/link/remove.js +0 -96
  129. package/dist/commands/template/phase/apply.d.ts +0 -14
  130. package/dist/commands/template/phase/apply.js +0 -43
  131. package/dist/commands/template/phase/create.d.ts +0 -13
  132. package/dist/commands/template/phase/create.js +0 -38
  133. package/dist/commands/template/phase/delete.d.ts +0 -13
  134. package/dist/commands/template/phase/delete.js +0 -36
  135. package/dist/commands/template/phase/index.d.ts +0 -10
  136. package/dist/commands/template/phase/index.js +0 -63
  137. package/dist/commands/template/phase/list.d.ts +0 -11
  138. package/dist/commands/template/phase/list.js +0 -36
  139. package/dist/commands/template/phase/update.d.ts +0 -14
  140. package/dist/commands/template/phase/update.js +0 -43
  141. package/dist/commands/template/ticket/apply.d.ts +0 -17
  142. package/dist/commands/template/ticket/apply.js +0 -60
  143. package/dist/commands/template/ticket/create.d.ts +0 -20
  144. package/dist/commands/template/ticket/create.js +0 -89
  145. package/dist/commands/template/ticket/delete.d.ts +0 -13
  146. package/dist/commands/template/ticket/delete.js +0 -38
  147. package/dist/commands/template/ticket/index.d.ts +0 -10
  148. package/dist/commands/template/ticket/index.js +0 -63
  149. package/dist/commands/template/ticket/list.d.ts +0 -11
  150. package/dist/commands/template/ticket/list.js +0 -36
  151. package/dist/commands/template/ticket/save.d.ts +0 -15
  152. package/dist/commands/template/ticket/save.js +0 -46
  153. package/dist/commands/ticket/link/block.d.ts +0 -14
  154. package/dist/commands/ticket/link/block.js +0 -96
  155. package/dist/commands/ticket/link/duplicates.d.ts +0 -14
  156. package/dist/commands/ticket/link/duplicates.js +0 -95
  157. package/dist/commands/ticket/link/index.d.ts +0 -19
  158. package/dist/commands/ticket/link/index.js +0 -256
  159. package/dist/commands/ticket/link/relates.d.ts +0 -14
  160. package/dist/commands/ticket/link/relates.js +0 -95
  161. package/dist/commands/ticket/link/remove.d.ts +0 -16
  162. package/dist/commands/ticket/link/remove.js +0 -132
  163. package/dist/commands/ticket/template/apply.js +0 -252
  164. package/dist/commands/ticket/template/create.js +0 -386
  165. package/dist/commands/ticket/template/delete.d.ts +0 -17
  166. package/dist/commands/ticket/template/delete.js +0 -94
  167. package/dist/commands/ticket/template/index.d.ts +0 -15
  168. package/dist/commands/ticket/template/index.js +0 -120
  169. package/dist/commands/ticket/template/list.d.ts +0 -16
  170. package/dist/commands/ticket/template/list.js +0 -112
  171. package/dist/commands/ticket/template/save.js +0 -163
  172. /package/dist/commands/{agent/staff → staff}/add.d.ts +0 -0
  173. /package/dist/commands/{agent/staff → staff}/list.d.ts +0 -0
@@ -0,0 +1,104 @@
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 { shouldOutputJson, outputPromptAsJson, outputSuccessAsJson, outputErrorAsJson, createMetadata, buildPromptConfig, } from '../../lib/prompt-json.js';
6
+ export default class TemplateSave extends PMOCommand {
7
+ static description = 'Create a ticket template from an existing ticket';
8
+ static examples = [
9
+ '<%= config.bin %> <%= command.id %> TKT-001 "Bug Report Template"',
10
+ '<%= config.bin %> <%= command.id %> TKT-042 "Feature Request" -d "Standard feature request"',
11
+ ];
12
+ static args = {
13
+ ticket: Args.string({
14
+ description: 'Ticket ID to create template from',
15
+ required: false,
16
+ }),
17
+ name: Args.string({
18
+ description: 'Template name',
19
+ required: false,
20
+ }),
21
+ };
22
+ static flags = {
23
+ ...pmoBaseFlags,
24
+ 'template-name': Flags.string({
25
+ char: 'n',
26
+ description: 'Template name (alternative to positional arg)',
27
+ }),
28
+ description: Flags.string({
29
+ char: 'd',
30
+ description: 'Template description',
31
+ }),
32
+ json: Flags.boolean({
33
+ char: 'm',
34
+ aliases: ['machine'],
35
+ description: 'Output as JSON for AI agents/scripts',
36
+ default: false,
37
+ }),
38
+ };
39
+ async execute() {
40
+ const { args, flags } = await this.parse(TemplateSave);
41
+ const jsonMode = shouldOutputJson(flags);
42
+ const handleError = (code, message) => {
43
+ if (jsonMode) {
44
+ outputErrorAsJson(code, message, createMetadata('template save', flags));
45
+ }
46
+ this.error(message);
47
+ };
48
+ // Get ticket ID
49
+ let ticketId = args.ticket;
50
+ if (!ticketId) {
51
+ const projectId = await this.requireProject();
52
+ const tickets = await this.storage.listTickets(projectId);
53
+ if (tickets.length === 0) {
54
+ return handleError('NO_TICKETS', 'No tickets found. Create a ticket first.');
55
+ }
56
+ if (jsonMode) {
57
+ const choices = tickets.slice(0, 20).map(t => ({ name: `${t.id} - ${t.title}`, value: t.id }));
58
+ outputPromptAsJson(buildPromptConfig('list', 'ticket', 'Select ticket:', choices), createMetadata('template save', flags));
59
+ return;
60
+ }
61
+ const { selected } = await inquirer.prompt([{
62
+ type: 'list',
63
+ name: 'selected',
64
+ message: 'Select ticket:',
65
+ choices: tickets.slice(0, 20).map(t => ({ name: `${t.id} - ${t.title}`, value: t.id })),
66
+ }]);
67
+ ticketId = selected;
68
+ }
69
+ const ticket = await this.storage.getTicket(ticketId);
70
+ if (!ticket) {
71
+ return handleError('TICKET_NOT_FOUND', `Ticket not found: ${ticketId}`);
72
+ }
73
+ // Get template name
74
+ let templateName = flags['template-name'] || args.name;
75
+ if (!templateName) {
76
+ if (jsonMode) {
77
+ outputPromptAsJson(buildPromptConfig('input', 'name', 'Template name:', undefined, ticket.category || ticket.title.split(' ')[0]), createMetadata('template save', flags));
78
+ return;
79
+ }
80
+ const { name } = await inquirer.prompt([{
81
+ type: 'input',
82
+ name: 'name',
83
+ message: 'Template name:',
84
+ default: ticket.category || ticket.title.split(' ')[0],
85
+ validate: (i) => i.length > 0 || 'Required',
86
+ }]);
87
+ templateName = name;
88
+ }
89
+ // Get description
90
+ let description = flags.description;
91
+ if (description === undefined && !jsonMode) {
92
+ const { desc } = await inquirer.prompt([{ type: 'input', name: 'desc', message: 'Description (optional):' }]);
93
+ description = desc || undefined;
94
+ }
95
+ const template = await this.storage.createTicketTemplateFromTicket(ticketId, templateName, description);
96
+ if (jsonMode) {
97
+ outputSuccessAsJson({ template, sourceTicketId: ticketId }, createMetadata('template save', flags));
98
+ return;
99
+ }
100
+ this.log(styles.success(`\nCreated template "${styles.emphasis(template.name)}" from ${ticketId}`));
101
+ this.log(styles.muted(` ID: ${template.id}`));
102
+ this.log(styles.muted(`\nApply with: prlt template apply --type ticket ${template.id}`));
103
+ }
104
+ }
@@ -1,5 +1,5 @@
1
- import { PMOCommand } from '../../../lib/pmo/index.js';
2
- export default class PhaseTemplateUpdate extends PMOCommand {
1
+ import { PMOCommand } from '../../lib/pmo/index.js';
2
+ export default class TemplateUpdate extends PMOCommand {
3
3
  static description: string;
4
4
  static examples: string[];
5
5
  static args: {
@@ -0,0 +1,99 @@
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 { shouldOutputJson, outputPromptAsJson, outputSuccessAsJson, outputErrorAsJson, createMetadata, buildPromptConfig, } from '../../lib/prompt-json.js';
6
+ export default class TemplateUpdate extends PMOCommand {
7
+ static description = 'Update a phase template';
8
+ static examples = [
9
+ '<%= config.bin %> <%= command.id %> my-template --name "New Name"',
10
+ '<%= config.bin %> <%= command.id %> my-template -d "Updated description"',
11
+ ];
12
+ static args = {
13
+ id: Args.string({
14
+ description: 'Template ID to update',
15
+ required: false,
16
+ }),
17
+ };
18
+ static flags = {
19
+ ...pmoBaseFlags,
20
+ name: Flags.string({
21
+ char: 'n',
22
+ description: 'New template name',
23
+ }),
24
+ description: Flags.string({
25
+ char: 'd',
26
+ description: 'New template description',
27
+ }),
28
+ json: Flags.boolean({
29
+ char: 'm',
30
+ aliases: ['machine'],
31
+ description: 'Output as JSON for AI agents/scripts',
32
+ default: false,
33
+ }),
34
+ };
35
+ getPMOOptions() {
36
+ return { promptIfMultiple: false };
37
+ }
38
+ async execute() {
39
+ const { args, flags } = await this.parse(TemplateUpdate);
40
+ const jsonMode = shouldOutputJson(flags);
41
+ const handleError = (code, message) => {
42
+ if (jsonMode) {
43
+ outputErrorAsJson(code, message, createMetadata('template update', flags));
44
+ }
45
+ this.error(message);
46
+ };
47
+ // Get template ID
48
+ let templateId = args.id;
49
+ if (!templateId) {
50
+ const templates = await this.storage.listPhaseTemplates();
51
+ const editable = templates.filter(t => !t.isBuiltin);
52
+ if (editable.length === 0) {
53
+ return handleError('NO_TEMPLATES', 'No editable phase templates (built-in cannot be updated).');
54
+ }
55
+ if (jsonMode) {
56
+ const choices = editable.map(t => ({ name: `${t.name}${t.description ? ` - ${t.description}` : ''}`, value: t.id }));
57
+ outputPromptAsJson(buildPromptConfig('list', 'id', 'Select template:', choices), createMetadata('template update', flags));
58
+ return;
59
+ }
60
+ const { selected } = await inquirer.prompt([{
61
+ type: 'list',
62
+ name: 'selected',
63
+ message: 'Select template:',
64
+ choices: editable.map(t => ({ name: `${t.name}${t.description ? ` - ${t.description}` : ''}`, value: t.id })),
65
+ }]);
66
+ templateId = selected;
67
+ }
68
+ // Get updates
69
+ let newName = flags.name;
70
+ let newDescription = flags.description;
71
+ if (!newName && newDescription === undefined && !jsonMode) {
72
+ const { updateName } = await inquirer.prompt([{
73
+ type: 'input', name: 'updateName', message: 'New name (leave empty to keep):',
74
+ }]);
75
+ if (updateName)
76
+ newName = updateName;
77
+ const { updateDesc } = await inquirer.prompt([{
78
+ type: 'input', name: 'updateDesc', message: 'New description (leave empty to keep):',
79
+ }]);
80
+ if (updateDesc)
81
+ newDescription = updateDesc;
82
+ if (!newName && !newDescription) {
83
+ this.log(styles.muted('No changes.'));
84
+ return;
85
+ }
86
+ }
87
+ const changes = {};
88
+ if (newName)
89
+ changes.name = newName;
90
+ if (newDescription !== undefined)
91
+ changes.description = newDescription;
92
+ const template = await this.storage.updatePhaseTemplate(templateId, changes);
93
+ if (jsonMode) {
94
+ outputSuccessAsJson({ template }, createMetadata('template update', flags));
95
+ return;
96
+ }
97
+ this.log(styles.success(`\nUpdated template "${styles.emphasis(template.name)}"`));
98
+ }
99
+ }
@@ -1,5 +1,5 @@
1
1
  import { Command } from '@oclif/core';
2
- export default class ThemesAddNames extends Command {
2
+ export default class ThemeAddNames extends Command {
3
3
  static description: string;
4
4
  static examples: string[];
5
5
  static args: {
@@ -1,9 +1,9 @@
1
1
  import { Command, Args } from '@oclif/core';
2
2
  import chalk from 'chalk';
3
- import { getWorkspaceInfo } from '../../../lib/agents/commands.js';
4
- import { isValidAgentName, normalizeAgentName } from '../../../lib/themes.js';
5
- import { getTheme, addThemeNames, getThemeNames } from '../../../lib/database/index.js';
6
- export default class ThemesAddNames extends Command {
3
+ import { getWorkspaceInfo } from '../../lib/agents/commands.js';
4
+ import { isValidAgentName, normalizeAgentName } from '../../lib/themes.js';
5
+ import { getTheme, addThemeNames, getThemeNames } from '../../lib/database/index.js';
6
+ export default class ThemeAddNames extends Command {
7
7
  static description = 'Add names to a theme';
8
8
  static examples = [
9
9
  '<%= config.bin %> <%= command.id %> greek-gods zeus athena poseidon',
@@ -21,13 +21,13 @@ export default class ThemesAddNames extends Command {
21
21
  };
22
22
  static strict = false; // Allow multiple name arguments
23
23
  async run() {
24
- const { args, argv } = await this.parse(ThemesAddNames);
24
+ const { args, argv } = await this.parse(ThemeAddNames);
25
25
  try {
26
26
  const workspaceInfo = getWorkspaceInfo();
27
27
  // Validate theme exists
28
28
  const theme = getTheme(workspaceInfo.path, args.theme);
29
29
  if (!theme) {
30
- this.error(`Theme "${args.theme}" not found. Run "prlt agent themes list" to see available themes.`);
30
+ this.error(`Theme "${args.theme}" not found. Run "prlt theme list" to see available themes.`);
31
31
  }
32
32
  // Get names from remaining arguments (skip the theme arg)
33
33
  const rawNames = argv.slice(1);
@@ -1,5 +1,5 @@
1
1
  import { Command } from '@oclif/core';
2
- export default class ThemesCreate extends Command {
2
+ export default class ThemeCreate extends Command {
3
3
  static description: string;
4
4
  static examples: string[];
5
5
  static args: {
@@ -1,8 +1,8 @@
1
1
  import { Command, Args, Flags } from '@oclif/core';
2
2
  import chalk from 'chalk';
3
- import { getWorkspaceInfo } from '../../../lib/agents/commands.js';
4
- import { createTheme, getTheme } from '../../../lib/database/index.js';
5
- export default class ThemesCreate extends Command {
3
+ import { getWorkspaceInfo } from '../../lib/agents/commands.js';
4
+ import { createTheme, getTheme } from '../../lib/database/index.js';
5
+ export default class ThemeCreate extends Command {
6
6
  static description = 'Create a custom agent theme';
7
7
  static examples = [
8
8
  '<%= config.bin %> <%= command.id %> greek-gods',
@@ -25,7 +25,7 @@ export default class ThemesCreate extends Command {
25
25
  }),
26
26
  };
27
27
  async run() {
28
- const { args, flags } = await this.parse(ThemesCreate);
28
+ const { args, flags } = await this.parse(ThemeCreate);
29
29
  try {
30
30
  const workspaceInfo = getWorkspaceInfo();
31
31
  // Validate theme name format
@@ -57,7 +57,7 @@ export default class ThemesCreate extends Command {
57
57
  this.log(chalk.gray(` ${theme.description}`));
58
58
  }
59
59
  this.log('');
60
- this.log(chalk.blue('Add names with: prlt agent themes add-names ' + name + ' <names...>'));
60
+ this.log(chalk.blue('Add names with: prlt theme add-names ' + name + ' <names...>'));
61
61
  }
62
62
  catch (error) {
63
63
  this.error(error instanceof Error ? error.message : String(error));
@@ -1,5 +1,5 @@
1
1
  import { Command } from '@oclif/core';
2
- export default class Themes extends Command {
2
+ export default class Theme extends Command {
3
3
  static description: string;
4
4
  static examples: string[];
5
5
  static flags: {
@@ -1,11 +1,11 @@
1
1
  import { Command, Flags } from '@oclif/core';
2
2
  import inquirer from 'inquirer';
3
3
  import chalk from 'chalk';
4
- import { getWorkspaceInfo } from '../../../lib/agents/commands.js';
5
- import { ensureBuiltinThemes } from '../../../lib/themes.js';
6
- import { getThemes, getAvailableThemeNames } from '../../../lib/database/index.js';
7
- import { shouldOutputJson, outputPromptAsJson, createMetadata, buildPromptConfig, } from '../../../lib/prompt-json.js';
8
- export default class Themes extends Command {
4
+ import { getWorkspaceInfo } from '../../lib/agents/commands.js';
5
+ import { ensureBuiltinThemes } from '../../lib/themes.js';
6
+ import { getThemes, getAvailableThemeNames } from '../../lib/database/index.js';
7
+ import { shouldOutputJson, outputPromptAsJson, createMetadata, buildPromptConfig, } from '../../lib/prompt-json.js';
8
+ export default class Theme extends Command {
9
9
  static description = 'Manage agent naming themes';
10
10
  static examples = [
11
11
  '<%= config.bin %> <%= command.id %> list',
@@ -21,15 +21,15 @@ export default class Themes extends Command {
21
21
  }),
22
22
  };
23
23
  async run() {
24
- const { flags } = await this.parse(Themes);
24
+ const { flags } = await this.parse(Theme);
25
25
  // Check if JSON output mode is active
26
26
  const jsonMode = shouldOutputJson(flags);
27
27
  // Define choices once, use for both JSON and interactive modes
28
28
  // Each choice includes the full command for AI agents to execute
29
29
  const menuChoices = [
30
- { id: 'list', name: 'List themes', command: 'prlt agent themes list --format json' },
31
- { id: 'create', name: 'Create a new theme', command: 'prlt agent themes create --machine' },
32
- { id: 'add-names', name: 'Add names to a theme', command: 'prlt agent themes add-names --machine' },
30
+ { id: 'list', name: 'List themes', command: 'prlt theme list --format json' },
31
+ { id: 'create', name: 'Create a new theme', command: 'prlt theme create --machine' },
32
+ { id: 'add-names', name: 'Add names to a theme', command: 'prlt theme add-names --machine' },
33
33
  { id: 'cancel', name: 'Cancel', command: '' },
34
34
  ];
35
35
  const message = 'What would you like to do?';
@@ -39,7 +39,7 @@ export default class Themes extends Command {
39
39
  name: c.name,
40
40
  value: c.id,
41
41
  command: c.command,
42
- }))), createMetadata('agent themes', flags));
42
+ }))), createMetadata('theme', flags));
43
43
  return;
44
44
  }
45
45
  this.log(chalk.bold('\nAgent Themes'));
@@ -1,5 +1,5 @@
1
1
  import { Command } from '@oclif/core';
2
- export default class ThemesList extends Command {
2
+ export default class ThemeList extends Command {
3
3
  static description: string;
4
4
  static examples: string[];
5
5
  run(): Promise<void>;
@@ -1,9 +1,9 @@
1
1
  import { Command } from '@oclif/core';
2
2
  import chalk from 'chalk';
3
- import { getWorkspaceInfo } from '../../../lib/agents/commands.js';
4
- import { ensureBuiltinThemes } from '../../../lib/themes.js';
5
- import { getThemes, getThemeNames, getAvailableThemeNames } from '../../../lib/database/index.js';
6
- export default class ThemesList extends Command {
3
+ import { getWorkspaceInfo } from '../../lib/agents/commands.js';
4
+ import { ensureBuiltinThemes } from '../../lib/themes.js';
5
+ import { getThemes, getThemeNames, getAvailableThemeNames } from '../../lib/database/index.js';
6
+ export default class ThemeList extends Command {
7
7
  static description = 'List available agent themes';
8
8
  static examples = [
9
9
  '<%= config.bin %> <%= command.id %>',
@@ -32,7 +32,7 @@ export default class ThemesList extends Command {
32
32
  this.log(chalk.gray(` Names: ${chalk.green(availableNames.length + ' available')}, ${chalk.yellow(inUse + ' in use')}`));
33
33
  this.log('');
34
34
  }
35
- this.log(chalk.blue('Use: prlt agent add --theme <theme-id>'));
35
+ this.log(chalk.blue('Use: prlt staff add --theme <theme-id>'));
36
36
  }
37
37
  catch (error) {
38
38
  this.error(error instanceof Error ? error.message : String(error));
@@ -1,5 +1,5 @@
1
1
  import { Command } from '@oclif/core';
2
- export default class ThemesSet extends Command {
2
+ export default class ThemeSet extends Command {
3
3
  static description: string;
4
4
  static examples: string[];
5
5
  static args: {
@@ -1,11 +1,11 @@
1
1
  import { Command, Args, Flags } from '@oclif/core';
2
2
  import chalk from 'chalk';
3
3
  import inquirer from 'inquirer';
4
- import { getWorkspaceInfo } from '../../../lib/agents/commands.js';
5
- import { ensureBuiltinThemes } from '../../../lib/themes.js';
6
- import { getThemes, getAvailableThemeNames, setActiveTheme, getActiveTheme } from '../../../lib/database/index.js';
7
- import { shouldOutputJson, outputPromptAsJson, createMetadata, buildPromptConfig, } from '../../../lib/prompt-json.js';
8
- export default class ThemesSet extends Command {
4
+ import { getWorkspaceInfo } from '../../lib/agents/commands.js';
5
+ import { ensureBuiltinThemes } from '../../lib/themes.js';
6
+ import { getThemes, getAvailableThemeNames, setActiveTheme, getActiveTheme } from '../../lib/database/index.js';
7
+ import { shouldOutputJson, outputPromptAsJson, createMetadata, buildPromptConfig, } from '../../lib/prompt-json.js';
8
+ export default class ThemeSet extends Command {
9
9
  static description = 'Set the active theme for this workspace';
10
10
  static examples = [
11
11
  '<%= config.bin %> <%= command.id %> billionaires',
@@ -26,7 +26,7 @@ export default class ThemesSet extends Command {
26
26
  }),
27
27
  };
28
28
  async run() {
29
- const { args, flags } = await this.parse(ThemesSet);
29
+ const { args, flags } = await this.parse(ThemeSet);
30
30
  // Check if JSON output mode is active
31
31
  const jsonMode = shouldOutputJson(flags);
32
32
  try {
@@ -47,7 +47,7 @@ export default class ThemesSet extends Command {
47
47
  value: t.id
48
48
  };
49
49
  });
50
- outputPromptAsJson(buildPromptConfig('list', 'theme', 'Select theme for this workspace:', themeChoices), createMetadata('agent themes set', flags));
50
+ outputPromptAsJson(buildPromptConfig('list', 'theme', 'Select theme for this workspace:', themeChoices), createMetadata('theme set', flags));
51
51
  return;
52
52
  }
53
53
  const choices = themes.map(t => {
@@ -14,6 +14,7 @@ export default class TicketCreate extends PMOCommand {
14
14
  epic: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
15
15
  template: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
16
16
  labels: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
17
+ 'dry-run': import("@oclif/core/interfaces").BooleanFlag<boolean>;
17
18
  project: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
18
19
  };
19
20
  execute(): Promise<void>;
@@ -4,7 +4,7 @@ import { autoExportToBoard, PMOCommand, pmoBaseFlags } from '../../lib/pmo/index
4
4
  import { styles } from '../../lib/styles.js';
5
5
  import { updateEpicTicketsSection } from '../../lib/pmo/epic-files.js';
6
6
  import { PRIORITIES, PRIORITY_LABELS } from '../../lib/pmo/types.js';
7
- import { shouldOutputJson, outputErrorAsJson, createMetadata, } from '../../lib/prompt-json.js';
7
+ import { shouldOutputJson, outputErrorAsJson, outputDryRunSuccessAsJson, outputDryRunErrorsAsJson, createMetadata, } from '../../lib/prompt-json.js';
8
8
  import { FlagResolver } from '../../lib/flags/index.js';
9
9
  export default class TicketCreate extends PMOCommand {
10
10
  static description = 'Create a new ticket on the PMO board';
@@ -15,6 +15,7 @@ export default class TicketCreate extends PMOCommand {
15
15
  '<%= config.bin %> <%= command.id %> --project mobile-app -t "New feature"',
16
16
  '<%= config.bin %> <%= command.id %> --epic EPIC-001 -t "Implement auth flow"',
17
17
  '<%= config.bin %> <%= command.id %> --json # Output column choices as JSON',
18
+ '<%= config.bin %> <%= command.id %> --title "Test" -P PROJ-001 --dry-run --json # Validate without creating',
18
19
  ];
19
20
  static flags = {
20
21
  ...pmoBaseFlags,
@@ -26,7 +27,7 @@ export default class TicketCreate extends PMOCommand {
26
27
  }),
27
28
  title: Flags.string({
28
29
  char: 't',
29
- description: 'Ticket title',
30
+ description: 'Ticket title [required for non-interactive]',
30
31
  }),
31
32
  column: Flags.string({
32
33
  char: 'c',
@@ -64,6 +65,10 @@ export default class TicketCreate extends PMOCommand {
64
65
  char: 'l',
65
66
  description: 'Labels (comma-separated)',
66
67
  }),
68
+ 'dry-run': Flags.boolean({
69
+ description: 'Validate inputs without creating ticket (use with --json for structured output)',
70
+ default: false,
71
+ }),
67
72
  };
68
73
  async execute() {
69
74
  const { flags } = await this.parse(TicketCreate);
@@ -171,8 +176,55 @@ export default class TicketCreate extends PMOCommand {
171
176
  }
172
177
  // Validate status/column
173
178
  if (!columns.includes(ticketData.statusName)) {
179
+ if (flags['dry-run']) {
180
+ if (jsonMode) {
181
+ outputDryRunErrorsAsJson([{ field: 'column', error: `Invalid column "${ticketData.statusName}". Available: ${columns.join(', ')}` }], createMetadata('ticket create', flags));
182
+ }
183
+ this.error(`Invalid column "${ticketData.statusName}". Available columns: ${columns.join(', ')}`);
184
+ }
174
185
  this.error(`Invalid column "${ticketData.statusName}". Available columns: ${columns.join(', ')}`);
175
186
  }
187
+ // Handle dry-run: show what would be created without actually creating
188
+ if (flags['dry-run']) {
189
+ const wouldCreate = {
190
+ title: ticketData.title,
191
+ project: projectId,
192
+ column: ticketData.statusName,
193
+ ...(ticketData.priority && { priority: ticketData.priority }),
194
+ ...(ticketData.category && { category: ticketData.category }),
195
+ ...(ticketData.description && { description: ticketData.description }),
196
+ ...(ticketData.epicId && { epic: ticketData.epicId }),
197
+ ...(ticketData.labels && ticketData.labels.length > 0 && { labels: ticketData.labels }),
198
+ };
199
+ if (jsonMode) {
200
+ outputDryRunSuccessAsJson('ticket', wouldCreate, createMetadata('ticket create', flags));
201
+ }
202
+ // Human-readable dry-run output
203
+ this.log(styles.warning('\n[DRY RUN] Would create ticket:'));
204
+ this.log(styles.muted(` Title: ${ticketData.title}`));
205
+ this.log(styles.muted(` Project: ${projectName}`));
206
+ this.log(styles.muted(` Column: ${ticketData.statusName}`));
207
+ if (ticketData.priority) {
208
+ this.log(styles.muted(` Priority: ${ticketData.priority}`));
209
+ }
210
+ if (ticketData.category) {
211
+ this.log(styles.muted(` Category: ${ticketData.category}`));
212
+ }
213
+ if (ticketData.epicId) {
214
+ this.log(styles.muted(` Epic: ${ticketData.epicId}`));
215
+ }
216
+ if (ticketData.labels && ticketData.labels.length > 0) {
217
+ this.log(styles.muted(` Labels: ${ticketData.labels.join(', ')}`));
218
+ }
219
+ if (template) {
220
+ this.log(styles.muted(` Template: ${template.name}`));
221
+ if (template.suggestedSubtasks.length > 0) {
222
+ this.log(styles.muted(` Subtasks: ${template.suggestedSubtasks.length} would be created`));
223
+ }
224
+ }
225
+ this.log(styles.muted('\n(No ticket was created)'));
226
+ return;
227
+ }
176
228
  const ticket = await this.storage.createTicket(projectId, {
177
229
  id: ticketData.id,
178
230
  title: ticketData.title,
@@ -27,7 +27,7 @@ export default class Ticket extends PMOCommand {
27
27
  // Each choice includes the full command for AI agents to execute
28
28
  const menuChoices = [
29
29
  { name: 'Create new ticket', value: 'create', command: 'prlt ticket create --json' },
30
- { name: 'Create from template', value: 'template', command: 'prlt ticket template apply --json' },
30
+ { name: 'Create from template', value: 'template', command: 'prlt template apply --type ticket --json' },
31
31
  { name: 'List all tickets', value: 'list', command: 'prlt ticket list --format json' },
32
32
  { name: 'View ticket details', value: 'view', command: 'prlt ticket view --json' },
33
33
  { name: 'Edit ticket', value: 'edit', command: 'prlt ticket edit --json' },
@@ -35,8 +35,8 @@ export default class Ticket extends PMOCommand {
35
35
  { name: 'Move to different project', value: 'project', command: 'prlt ticket project --json' },
36
36
  { name: 'Assign to epic', value: 'epic', command: 'prlt ticket epic --json' },
37
37
  { name: 'Assign to spec', value: 'spec', command: 'prlt ticket spec --json' },
38
- { name: 'Manage dependencies', value: 'link', command: 'prlt ticket link --json' },
39
- { name: 'Manage templates', value: 'templates', command: 'prlt ticket template --json' },
38
+ { name: 'Manage dependencies', value: 'link', command: 'prlt link list --json' },
39
+ { name: 'Manage templates', value: 'templates', command: 'prlt template --json' },
40
40
  { name: 'Delete ticket', value: 'delete', command: 'prlt ticket delete --json' },
41
41
  { name: 'Cancel', value: 'cancel' },
42
42
  ];
@@ -68,7 +68,7 @@ export default class Ticket extends PMOCommand {
68
68
  await this.config.runCommand('ticket:create', []);
69
69
  break;
70
70
  case 'template':
71
- await this.config.runCommand('ticket:template:apply', []);
71
+ await this.config.runCommand('template:apply', ['--type', 'ticket']);
72
72
  break;
73
73
  case 'list':
74
74
  await this.config.runCommand('ticket:list', []);
@@ -92,10 +92,10 @@ export default class Ticket extends PMOCommand {
92
92
  await this.config.runCommand('ticket:spec', []);
93
93
  break;
94
94
  case 'link':
95
- await this.config.runCommand('ticket:link', []);
95
+ await this.config.runCommand('link', []);
96
96
  break;
97
97
  case 'templates':
98
- await this.config.runCommand('ticket:template', []);
98
+ await this.config.runCommand('template', []);
99
99
  break;
100
100
  case 'delete':
101
101
  await this.config.runCommand('ticket:delete', []);
@@ -39,7 +39,7 @@ export default class WorkSpawn extends PMOCommand {
39
39
  }),
40
40
  column: Flags.string({
41
41
  char: 'c',
42
- description: 'Column name to spawn tickets from (used with --all)',
42
+ description: 'Column name to spawn tickets from [required for non-interactive with --all]',
43
43
  }),
44
44
  strategy: Flags.string({
45
45
  char: 's',
@@ -0,0 +1,43 @@
1
+ /**
2
+ * MCP Helper Functions
3
+ */
4
+ import type { Ticket } from '../pmo/types.js';
5
+ import type { McpToolResult } from './types.js';
6
+ export declare function formatTicket(t: Ticket): {
7
+ id: string;
8
+ title: string;
9
+ priority: string | undefined;
10
+ category: string | undefined;
11
+ statusName: string | undefined;
12
+ statusCategory: import("../pmo/types.js").StateCategory | undefined;
13
+ projectId: string | undefined;
14
+ assignee: string | undefined;
15
+ owner: string | undefined;
16
+ branch: string | undefined;
17
+ epicId: string | undefined;
18
+ };
19
+ export declare function formatTicketFull(t: Ticket): {
20
+ description: string | undefined;
21
+ subtasks: import("../pmo/types.js").Subtask[];
22
+ labels: string[];
23
+ metadata: Record<string, string>;
24
+ blockedBy: string[] | undefined;
25
+ acceptanceCriteria: import("../pmo/types.js").AcceptanceCriterion[] | undefined;
26
+ specId: string | undefined;
27
+ createdAt: string;
28
+ updatedAt: string;
29
+ id: string;
30
+ title: string;
31
+ priority: string | undefined;
32
+ category: string | undefined;
33
+ statusName: string | undefined;
34
+ statusCategory: import("../pmo/types.js").StateCategory | undefined;
35
+ projectId: string | undefined;
36
+ assignee: string | undefined;
37
+ owner: string | undefined;
38
+ branch: string | undefined;
39
+ epicId: string | undefined;
40
+ };
41
+ export declare function successResponse(data: Record<string, unknown>): McpToolResult;
42
+ export declare function errorResponse(error: unknown): McpToolResult;
43
+ export declare function textResponse(text: string): McpToolResult;