@proletariat/cli 0.3.23 → 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.
- package/dist/commands/action/create.js +1 -1
- package/dist/commands/agent/{temp/cleanup.d.ts → cleanup.d.ts} +1 -1
- package/dist/commands/agent/{temp/cleanup.js → cleanup.js} +4 -4
- package/dist/commands/agent/index.js +8 -8
- package/dist/commands/branch/create.js +2 -2
- package/dist/commands/epic/create.d.ts +1 -0
- package/dist/commands/epic/create.js +39 -2
- package/dist/commands/epic/index.js +2 -2
- package/dist/commands/{epic/link/remove.d.ts → link/create.d.ts} +6 -7
- package/dist/commands/link/create.js +141 -0
- package/dist/commands/{epic/link/relates.d.ts → link/index.d.ts} +4 -5
- package/dist/commands/link/index.js +87 -0
- package/dist/commands/{epic/link/duplicates.d.ts → link/list.d.ts} +7 -4
- package/dist/commands/link/list.js +182 -0
- package/dist/commands/{spec/link → link}/remove.d.ts +4 -5
- package/dist/commands/link/remove.js +120 -0
- package/dist/commands/mcp-server.d.ts +22 -0
- package/dist/commands/mcp-server.js +98 -0
- package/dist/commands/phase/create.js +1 -1
- package/dist/commands/project/create.d.ts +1 -0
- package/dist/commands/project/create.js +38 -4
- package/dist/commands/spec/create.d.ts +1 -0
- package/dist/commands/spec/create.js +43 -2
- package/dist/commands/spec/index.js +2 -2
- package/dist/commands/{agent/staff → staff}/add.js +10 -10
- package/dist/commands/{agent/staff → staff}/index.d.ts +1 -1
- package/dist/commands/{agent/staff → staff}/index.js +7 -7
- package/dist/commands/{agent/staff → staff}/list.js +3 -3
- package/dist/commands/{agent/staff → staff}/remove.d.ts +1 -1
- package/dist/commands/{agent/staff → staff}/remove.js +8 -8
- package/dist/commands/{ticket/template → template}/apply.d.ts +8 -6
- package/dist/commands/template/apply.js +262 -0
- package/dist/commands/{ticket/template → template}/create.d.ts +5 -6
- package/dist/commands/template/create.js +238 -0
- package/dist/commands/template/index.js +48 -36
- package/dist/commands/{ticket/template → template}/save.d.ts +2 -2
- package/dist/commands/template/save.js +104 -0
- package/dist/commands/{phase/template → template}/update.d.ts +2 -2
- package/dist/commands/template/update.js +99 -0
- package/dist/commands/{agent/themes → theme}/add-names.d.ts +1 -1
- package/dist/commands/{agent/themes → theme}/add-names.js +6 -6
- package/dist/commands/{agent/themes → theme}/create.d.ts +1 -1
- package/dist/commands/{agent/themes → theme}/create.js +5 -5
- package/dist/commands/{agent/themes → theme}/index.d.ts +1 -1
- package/dist/commands/{agent/themes → theme}/index.js +10 -10
- package/dist/commands/{agent/themes → theme}/list.d.ts +1 -1
- package/dist/commands/{agent/themes → theme}/list.js +5 -5
- package/dist/commands/{agent/themes → theme}/set.d.ts +1 -1
- package/dist/commands/{agent/themes → theme}/set.js +7 -7
- package/dist/commands/ticket/create.d.ts +1 -0
- package/dist/commands/ticket/create.js +54 -2
- package/dist/commands/ticket/index.js +6 -6
- package/dist/commands/work/spawn.js +1 -1
- package/dist/lib/mcp/helpers.d.ts +43 -0
- package/dist/lib/mcp/helpers.js +57 -0
- package/dist/lib/mcp/index.d.ts +6 -0
- package/dist/lib/mcp/index.js +6 -0
- package/dist/lib/mcp/tools/action.d.ts +6 -0
- package/dist/lib/mcp/tools/action.js +88 -0
- package/dist/lib/mcp/tools/board.d.ts +6 -0
- package/dist/lib/mcp/tools/board.js +139 -0
- package/dist/lib/mcp/tools/category.d.ts +6 -0
- package/dist/lib/mcp/tools/category.js +84 -0
- package/dist/lib/mcp/tools/cli-passthrough.d.ts +15 -0
- package/dist/lib/mcp/tools/cli-passthrough.js +333 -0
- package/dist/lib/mcp/tools/epic.d.ts +6 -0
- package/dist/lib/mcp/tools/epic.js +178 -0
- package/dist/lib/mcp/tools/index.d.ts +18 -0
- package/dist/lib/mcp/tools/index.js +19 -0
- package/dist/lib/mcp/tools/phase.d.ts +6 -0
- package/dist/lib/mcp/tools/phase.js +131 -0
- package/dist/lib/mcp/tools/project.d.ts +6 -0
- package/dist/lib/mcp/tools/project.js +196 -0
- package/dist/lib/mcp/tools/roadmap.d.ts +6 -0
- package/dist/lib/mcp/tools/roadmap.js +123 -0
- package/dist/lib/mcp/tools/spec.d.ts +6 -0
- package/dist/lib/mcp/tools/spec.js +196 -0
- package/dist/lib/mcp/tools/status.d.ts +6 -0
- package/dist/lib/mcp/tools/status.js +109 -0
- package/dist/lib/mcp/tools/template.d.ts +6 -0
- package/dist/lib/mcp/tools/template.js +107 -0
- package/dist/lib/mcp/tools/ticket.d.ts +6 -0
- package/dist/lib/mcp/tools/ticket.js +393 -0
- package/dist/lib/mcp/tools/view.d.ts +6 -0
- package/dist/lib/mcp/tools/view.js +76 -0
- package/dist/lib/mcp/tools/work.d.ts +6 -0
- package/dist/lib/mcp/tools/work.js +132 -0
- package/dist/lib/mcp/tools/workflow.d.ts +6 -0
- package/dist/lib/mcp/tools/workflow.js +95 -0
- package/dist/lib/mcp/types.d.ts +17 -0
- package/dist/lib/mcp/types.js +4 -0
- package/dist/lib/prompt-json.d.ts +52 -1
- package/dist/lib/prompt-json.js +45 -0
- package/oclif.manifest.json +3660 -5564
- package/package.json +6 -4
- package/dist/commands/agent/temp/index.d.ts +0 -14
- package/dist/commands/agent/temp/index.js +0 -85
- package/dist/commands/agent/temp/list.d.ts +0 -7
- package/dist/commands/agent/temp/list.js +0 -108
- package/dist/commands/epic/link/block.d.ts +0 -14
- package/dist/commands/epic/link/block.js +0 -81
- package/dist/commands/epic/link/duplicates.js +0 -68
- package/dist/commands/epic/link/index.d.ts +0 -19
- package/dist/commands/epic/link/index.js +0 -272
- package/dist/commands/epic/link/relates.js +0 -68
- package/dist/commands/epic/link/remove.js +0 -93
- package/dist/commands/phase/template/apply.d.ts +0 -17
- package/dist/commands/phase/template/apply.js +0 -108
- package/dist/commands/phase/template/create.d.ts +0 -17
- package/dist/commands/phase/template/create.js +0 -104
- package/dist/commands/phase/template/delete.d.ts +0 -17
- package/dist/commands/phase/template/delete.js +0 -100
- package/dist/commands/phase/template/index.d.ts +0 -15
- package/dist/commands/phase/template/index.js +0 -130
- package/dist/commands/phase/template/list.d.ts +0 -16
- package/dist/commands/phase/template/list.js +0 -97
- package/dist/commands/phase/template/update.js +0 -89
- package/dist/commands/spec/link/depends.d.ts +0 -14
- package/dist/commands/spec/link/depends.js +0 -64
- package/dist/commands/spec/link/duplicates.d.ts +0 -14
- package/dist/commands/spec/link/duplicates.js +0 -63
- package/dist/commands/spec/link/index.d.ts +0 -19
- package/dist/commands/spec/link/index.js +0 -207
- package/dist/commands/spec/link/relates.d.ts +0 -14
- package/dist/commands/spec/link/relates.js +0 -63
- package/dist/commands/spec/link/remove.js +0 -96
- package/dist/commands/template/phase/apply.d.ts +0 -14
- package/dist/commands/template/phase/apply.js +0 -43
- package/dist/commands/template/phase/create.d.ts +0 -13
- package/dist/commands/template/phase/create.js +0 -38
- package/dist/commands/template/phase/delete.d.ts +0 -13
- package/dist/commands/template/phase/delete.js +0 -36
- package/dist/commands/template/phase/index.d.ts +0 -10
- package/dist/commands/template/phase/index.js +0 -63
- package/dist/commands/template/phase/list.d.ts +0 -11
- package/dist/commands/template/phase/list.js +0 -36
- package/dist/commands/template/phase/update.d.ts +0 -14
- package/dist/commands/template/phase/update.js +0 -43
- package/dist/commands/template/ticket/apply.d.ts +0 -17
- package/dist/commands/template/ticket/apply.js +0 -60
- package/dist/commands/template/ticket/create.d.ts +0 -20
- package/dist/commands/template/ticket/create.js +0 -89
- package/dist/commands/template/ticket/delete.d.ts +0 -13
- package/dist/commands/template/ticket/delete.js +0 -38
- package/dist/commands/template/ticket/index.d.ts +0 -10
- package/dist/commands/template/ticket/index.js +0 -63
- package/dist/commands/template/ticket/list.d.ts +0 -11
- package/dist/commands/template/ticket/list.js +0 -36
- package/dist/commands/template/ticket/save.d.ts +0 -15
- package/dist/commands/template/ticket/save.js +0 -46
- package/dist/commands/ticket/link/block.d.ts +0 -14
- package/dist/commands/ticket/link/block.js +0 -96
- package/dist/commands/ticket/link/duplicates.d.ts +0 -14
- package/dist/commands/ticket/link/duplicates.js +0 -95
- package/dist/commands/ticket/link/index.d.ts +0 -19
- package/dist/commands/ticket/link/index.js +0 -256
- package/dist/commands/ticket/link/relates.d.ts +0 -14
- package/dist/commands/ticket/link/relates.js +0 -95
- package/dist/commands/ticket/link/remove.d.ts +0 -16
- package/dist/commands/ticket/link/remove.js +0 -132
- package/dist/commands/ticket/template/apply.js +0 -252
- package/dist/commands/ticket/template/create.js +0 -386
- package/dist/commands/ticket/template/delete.d.ts +0 -17
- package/dist/commands/ticket/template/delete.js +0 -94
- package/dist/commands/ticket/template/index.d.ts +0 -15
- package/dist/commands/ticket/template/index.js +0 -120
- package/dist/commands/ticket/template/list.d.ts +0 -16
- package/dist/commands/ticket/template/list.js +0 -112
- package/dist/commands/ticket/template/save.js +0 -163
- /package/dist/commands/{agent/staff → staff}/add.d.ts +0 -0
- /package/dist/commands/{agent/staff → staff}/list.d.ts +0 -0
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import { Args, Flags } from '@oclif/core';
|
|
2
|
+
import inquirer from 'inquirer';
|
|
3
|
+
import { PMOCommand, pmoBaseFlags } from '../../lib/pmo/index.js';
|
|
4
|
+
import { PRIORITIES, PRIORITY_LABELS, TICKET_CATEGORIES } from '../../lib/pmo/types.js';
|
|
5
|
+
import { styles } from '../../lib/styles.js';
|
|
6
|
+
import { shouldOutputJson, outputSuccessAsJson, outputPromptAsJson, outputErrorAsJson, buildFormPromptConfig, createMetadata, buildPromptConfig, } from '../../lib/prompt-json.js';
|
|
7
|
+
export default class TemplateCreate extends PMOCommand {
|
|
8
|
+
static description = 'Create a new template';
|
|
9
|
+
static examples = [
|
|
10
|
+
'<%= config.bin %> <%= command.id %> --type ticket "Bug Report"',
|
|
11
|
+
'<%= config.bin %> <%= command.id %> --type ticket "Feature" -d "For new features"',
|
|
12
|
+
'<%= config.bin %> <%= command.id %> --type phase "My Phases"',
|
|
13
|
+
'<%= config.bin %> <%= command.id %> --type phase "Enterprise" --description "Enterprise lifecycle"',
|
|
14
|
+
];
|
|
15
|
+
static args = {
|
|
16
|
+
name: Args.string({
|
|
17
|
+
description: 'Template name',
|
|
18
|
+
required: false,
|
|
19
|
+
}),
|
|
20
|
+
};
|
|
21
|
+
static flags = {
|
|
22
|
+
...pmoBaseFlags,
|
|
23
|
+
type: Flags.string({
|
|
24
|
+
char: 't',
|
|
25
|
+
description: 'Template type',
|
|
26
|
+
options: ['ticket', 'phase'],
|
|
27
|
+
}),
|
|
28
|
+
description: Flags.string({
|
|
29
|
+
char: 'd',
|
|
30
|
+
description: 'Template description',
|
|
31
|
+
}),
|
|
32
|
+
// Ticket-specific flags
|
|
33
|
+
'title-pattern': Flags.string({
|
|
34
|
+
description: 'Default title prefix/pattern (ticket only)',
|
|
35
|
+
}),
|
|
36
|
+
'description-template': Flags.string({
|
|
37
|
+
description: 'Default description template (ticket only)',
|
|
38
|
+
}),
|
|
39
|
+
priority: Flags.string({
|
|
40
|
+
char: 'p',
|
|
41
|
+
description: 'Default priority (ticket only)',
|
|
42
|
+
options: [...PRIORITIES],
|
|
43
|
+
}),
|
|
44
|
+
category: Flags.string({
|
|
45
|
+
char: 'c',
|
|
46
|
+
description: 'Default category (ticket only)',
|
|
47
|
+
options: [...TICKET_CATEGORIES],
|
|
48
|
+
}),
|
|
49
|
+
subtask: Flags.string({
|
|
50
|
+
description: 'Add a suggested subtask (ticket only, can repeat)',
|
|
51
|
+
multiple: true,
|
|
52
|
+
}),
|
|
53
|
+
ac: Flags.string({
|
|
54
|
+
description: 'Add an acceptance criterion (ticket only, can repeat)',
|
|
55
|
+
multiple: true,
|
|
56
|
+
}),
|
|
57
|
+
label: Flags.string({
|
|
58
|
+
char: 'l',
|
|
59
|
+
description: 'Add a default label (ticket only, can repeat)',
|
|
60
|
+
multiple: true,
|
|
61
|
+
}),
|
|
62
|
+
json: Flags.boolean({
|
|
63
|
+
char: 'm',
|
|
64
|
+
aliases: ['machine'],
|
|
65
|
+
description: 'Output as JSON for AI agents/scripts',
|
|
66
|
+
default: false,
|
|
67
|
+
}),
|
|
68
|
+
};
|
|
69
|
+
getPMOOptions() {
|
|
70
|
+
return { promptIfMultiple: false };
|
|
71
|
+
}
|
|
72
|
+
async execute() {
|
|
73
|
+
const { args, flags } = await this.parse(TemplateCreate);
|
|
74
|
+
const jsonMode = shouldOutputJson(flags);
|
|
75
|
+
// Determine template type
|
|
76
|
+
let templateType = flags.type;
|
|
77
|
+
if (!templateType) {
|
|
78
|
+
if (jsonMode) {
|
|
79
|
+
outputPromptAsJson(buildPromptConfig('list', 'type', 'What type of template?', [
|
|
80
|
+
{ name: 'Ticket template', value: 'ticket' },
|
|
81
|
+
{ name: 'Phase template', value: 'phase' },
|
|
82
|
+
]), createMetadata('template create', flags));
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
const { selectedType } = await inquirer.prompt([{
|
|
86
|
+
type: 'list',
|
|
87
|
+
name: 'selectedType',
|
|
88
|
+
message: 'What type of template?',
|
|
89
|
+
choices: [
|
|
90
|
+
{ name: 'Ticket template', value: 'ticket' },
|
|
91
|
+
{ name: 'Phase template', value: 'phase' },
|
|
92
|
+
],
|
|
93
|
+
}]);
|
|
94
|
+
templateType = selectedType;
|
|
95
|
+
}
|
|
96
|
+
if (templateType === 'ticket') {
|
|
97
|
+
await this.createTicketTemplate(args.name, flags, jsonMode);
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
await this.createPhaseTemplate(args.name, flags, jsonMode);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
async createTicketTemplate(name, flags, jsonMode) {
|
|
104
|
+
// Check if we have required data
|
|
105
|
+
if (!name) {
|
|
106
|
+
if (jsonMode) {
|
|
107
|
+
const fields = [
|
|
108
|
+
{ type: 'input', name: 'name', message: 'Template name:' },
|
|
109
|
+
{ type: 'input', name: 'description', message: 'Description (optional):' },
|
|
110
|
+
{
|
|
111
|
+
type: 'list',
|
|
112
|
+
name: 'priority',
|
|
113
|
+
message: 'Default priority:',
|
|
114
|
+
choices: [
|
|
115
|
+
{ name: 'None', value: '' },
|
|
116
|
+
...PRIORITIES.map(p => ({ name: PRIORITY_LABELS[p], value: p })),
|
|
117
|
+
],
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
type: 'list',
|
|
121
|
+
name: 'category',
|
|
122
|
+
message: 'Default category:',
|
|
123
|
+
choices: [
|
|
124
|
+
{ name: 'None', value: '' },
|
|
125
|
+
...TICKET_CATEGORIES.map(c => ({ name: c, value: c })),
|
|
126
|
+
],
|
|
127
|
+
},
|
|
128
|
+
];
|
|
129
|
+
outputPromptAsJson(buildFormPromptConfig(fields), createMetadata('template create', flags));
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
const { templateName } = await inquirer.prompt([{
|
|
133
|
+
type: 'input',
|
|
134
|
+
name: 'templateName',
|
|
135
|
+
message: 'Template name:',
|
|
136
|
+
validate: (input) => input.trim() ? true : 'Name required',
|
|
137
|
+
}]);
|
|
138
|
+
name = templateName;
|
|
139
|
+
}
|
|
140
|
+
// Get optional values
|
|
141
|
+
let description = flags.description;
|
|
142
|
+
let titlePattern = flags['title-pattern'];
|
|
143
|
+
let priority = flags.priority;
|
|
144
|
+
let category = flags.category;
|
|
145
|
+
const subtasks = flags.subtask || [];
|
|
146
|
+
const acs = flags.ac || [];
|
|
147
|
+
const labels = flags.label || [];
|
|
148
|
+
let descriptionTemplate = flags['description-template'];
|
|
149
|
+
// Interactive prompts for missing optional values
|
|
150
|
+
if (!jsonMode && description === undefined) {
|
|
151
|
+
const { desc } = await inquirer.prompt([{
|
|
152
|
+
type: 'input', name: 'desc', message: 'Description (optional):',
|
|
153
|
+
}]);
|
|
154
|
+
description = desc || undefined;
|
|
155
|
+
}
|
|
156
|
+
if (!jsonMode && priority === undefined) {
|
|
157
|
+
const { p } = await inquirer.prompt([{
|
|
158
|
+
type: 'list',
|
|
159
|
+
name: 'p',
|
|
160
|
+
message: 'Default priority:',
|
|
161
|
+
choices: [
|
|
162
|
+
{ name: 'None', value: '' },
|
|
163
|
+
...PRIORITIES.map(pr => ({ name: PRIORITY_LABELS[pr], value: pr })),
|
|
164
|
+
],
|
|
165
|
+
}]);
|
|
166
|
+
priority = p || undefined;
|
|
167
|
+
}
|
|
168
|
+
if (!jsonMode && category === undefined) {
|
|
169
|
+
const { c } = await inquirer.prompt([{
|
|
170
|
+
type: 'list',
|
|
171
|
+
name: 'c',
|
|
172
|
+
message: 'Default category:',
|
|
173
|
+
choices: [
|
|
174
|
+
{ name: 'None', value: '' },
|
|
175
|
+
...TICKET_CATEGORIES.map(cat => ({ name: cat, value: cat })),
|
|
176
|
+
],
|
|
177
|
+
}]);
|
|
178
|
+
category = c || undefined;
|
|
179
|
+
}
|
|
180
|
+
// Build description template with ACs
|
|
181
|
+
if (acs.length > 0 && !descriptionTemplate) {
|
|
182
|
+
descriptionTemplate = '## Description\n\n## Acceptance Criteria\n' +
|
|
183
|
+
acs.map(ac => `- [ ] ${ac}`).join('\n') + '\n';
|
|
184
|
+
}
|
|
185
|
+
// Create the template
|
|
186
|
+
const template = await this.storage.createTicketTemplate({
|
|
187
|
+
name: name,
|
|
188
|
+
description,
|
|
189
|
+
titlePattern,
|
|
190
|
+
defaultPriority: priority,
|
|
191
|
+
defaultCategory: category,
|
|
192
|
+
descriptionTemplate,
|
|
193
|
+
suggestedSubtasks: subtasks.map(title => ({ title })),
|
|
194
|
+
defaultLabels: labels,
|
|
195
|
+
});
|
|
196
|
+
if (jsonMode) {
|
|
197
|
+
outputSuccessAsJson({ template }, createMetadata('template create', flags));
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
this.log(styles.success(`\nCreated ticket template "${styles.emphasis(template.name)}"`));
|
|
201
|
+
this.log(styles.muted(` ID: ${template.id}`));
|
|
202
|
+
this.log(styles.muted(`\nApply with: prlt template apply --type ticket ${template.id}`));
|
|
203
|
+
}
|
|
204
|
+
async createPhaseTemplate(name, flags, jsonMode) {
|
|
205
|
+
if (!name) {
|
|
206
|
+
if (jsonMode) {
|
|
207
|
+
outputErrorAsJson('NAME_REQUIRED', 'Name required: prlt template create --type phase "Name"', createMetadata('template create', flags));
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
const { templateName } = await inquirer.prompt([{
|
|
211
|
+
type: 'input',
|
|
212
|
+
name: 'templateName',
|
|
213
|
+
message: 'Template name:',
|
|
214
|
+
validate: (input) => input.trim() ? true : 'Name required',
|
|
215
|
+
}]);
|
|
216
|
+
name = templateName;
|
|
217
|
+
}
|
|
218
|
+
let description = flags.description;
|
|
219
|
+
if (!jsonMode && description === undefined) {
|
|
220
|
+
const { desc } = await inquirer.prompt([{
|
|
221
|
+
type: 'input', name: 'desc', message: 'Description (optional):',
|
|
222
|
+
}]);
|
|
223
|
+
description = desc || undefined;
|
|
224
|
+
}
|
|
225
|
+
const template = await this.storage.savePhaseTemplate(name, description);
|
|
226
|
+
if (jsonMode) {
|
|
227
|
+
outputSuccessAsJson({
|
|
228
|
+
id: template.id,
|
|
229
|
+
name: template.name,
|
|
230
|
+
phasesCount: template.phases.length,
|
|
231
|
+
}, createMetadata('template create', flags));
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
this.log(styles.success(`\nCreated phase template "${styles.emphasis(template.name)}" (${template.id})`));
|
|
235
|
+
this.log(styles.muted(`Saved ${template.phases.length} phases`));
|
|
236
|
+
this.log(styles.muted(`\nApply with: prlt template apply --type phase ${template.id}`));
|
|
237
|
+
}
|
|
238
|
+
}
|
|
@@ -1,56 +1,68 @@
|
|
|
1
|
-
import { Command } from '@oclif/core';
|
|
1
|
+
import { Command, Flags } from '@oclif/core';
|
|
2
|
+
import inquirer from 'inquirer';
|
|
2
3
|
import { styles } from '../../lib/styles.js';
|
|
3
|
-
import {
|
|
4
|
-
import { machineOutputFlags } from '../../lib/pmo/index.js';
|
|
4
|
+
import { shouldOutputJson, outputPromptAsJson, buildPromptConfig, createMetadata } from '../../lib/prompt-json.js';
|
|
5
5
|
export default class Template extends Command {
|
|
6
6
|
static description = 'Manage templates (ticket and phase)';
|
|
7
7
|
static aliases = ['templates'];
|
|
8
8
|
static examples = [
|
|
9
|
-
'<%= config.bin %> <%= command.id %>',
|
|
10
9
|
'<%= config.bin %> <%= command.id %> list',
|
|
11
10
|
'<%= config.bin %> <%= command.id %> list --type ticket',
|
|
12
|
-
'<%= config.bin %> <%= command.id %> ticket',
|
|
13
|
-
'<%= config.bin %> <%= command.id %> phase',
|
|
11
|
+
'<%= config.bin %> <%= command.id %> create --type ticket "Bug Report"',
|
|
12
|
+
'<%= config.bin %> <%= command.id %> apply --type phase agile',
|
|
14
13
|
];
|
|
15
14
|
static flags = {
|
|
16
|
-
|
|
15
|
+
json: Flags.boolean({
|
|
16
|
+
char: 'm',
|
|
17
|
+
aliases: ['machine'],
|
|
18
|
+
description: 'Output as JSON for AI agents/scripts',
|
|
19
|
+
default: false,
|
|
20
|
+
}),
|
|
17
21
|
};
|
|
18
22
|
async run() {
|
|
19
23
|
const { flags } = await this.parse(Template);
|
|
20
24
|
const jsonMode = shouldOutputJson(flags);
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
switch (resolved.action) {
|
|
25
|
+
const menuChoices = [
|
|
26
|
+
{ name: 'List templates', value: 'list', command: 'prlt template list --json' },
|
|
27
|
+
{ name: 'Create template', value: 'create', command: 'prlt template create --json' },
|
|
28
|
+
{ name: 'Apply template', value: 'apply', command: 'prlt template apply --json' },
|
|
29
|
+
{ name: 'Save ticket as template', value: 'save', command: 'prlt template save --json' },
|
|
30
|
+
{ name: 'Update phase template', value: 'update', command: 'prlt template update --json' },
|
|
31
|
+
{ name: 'Delete template', value: 'delete', command: 'prlt template delete --json' },
|
|
32
|
+
{ name: 'Cancel', value: 'cancel', command: '' },
|
|
33
|
+
];
|
|
34
|
+
if (jsonMode) {
|
|
35
|
+
outputPromptAsJson(buildPromptConfig('list', 'action', 'What would you like to do?', menuChoices), createMetadata('template', flags));
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
this.log(`\n${styles.emphasis('Templates')}`);
|
|
39
|
+
this.log(styles.muted('Manage ticket and phase templates\n'));
|
|
40
|
+
const { action } = await inquirer.prompt([{
|
|
41
|
+
type: 'list',
|
|
42
|
+
name: 'action',
|
|
43
|
+
message: 'What would you like to do?',
|
|
44
|
+
choices: menuChoices.map(c => ({ name: c.name, value: c.value })),
|
|
45
|
+
}]);
|
|
46
|
+
if (action === 'cancel')
|
|
47
|
+
return;
|
|
48
|
+
switch (action) {
|
|
46
49
|
case 'list':
|
|
47
50
|
await this.config.runCommand('template:list', []);
|
|
48
51
|
break;
|
|
49
|
-
case '
|
|
50
|
-
await this.config.runCommand('template:
|
|
52
|
+
case 'create':
|
|
53
|
+
await this.config.runCommand('template:create', []);
|
|
54
|
+
break;
|
|
55
|
+
case 'apply':
|
|
56
|
+
await this.config.runCommand('template:apply', []);
|
|
57
|
+
break;
|
|
58
|
+
case 'save':
|
|
59
|
+
await this.config.runCommand('template:save', []);
|
|
60
|
+
break;
|
|
61
|
+
case 'update':
|
|
62
|
+
await this.config.runCommand('template:update', []);
|
|
51
63
|
break;
|
|
52
|
-
case '
|
|
53
|
-
await this.config.runCommand('template:
|
|
64
|
+
case 'delete':
|
|
65
|
+
await this.config.runCommand('template:delete', []);
|
|
54
66
|
break;
|
|
55
67
|
}
|
|
56
68
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { PMOCommand } from '
|
|
2
|
-
export default class
|
|
1
|
+
import { PMOCommand } from '../../lib/pmo/index.js';
|
|
2
|
+
export default class TemplateSave extends PMOCommand {
|
|
3
3
|
static description: string;
|
|
4
4
|
static examples: string[];
|
|
5
5
|
static args: {
|
|
@@ -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 '
|
|
2
|
-
export default class
|
|
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,9 +1,9 @@
|
|
|
1
1
|
import { Command, Args } from '@oclif/core';
|
|
2
2
|
import chalk from 'chalk';
|
|
3
|
-
import { getWorkspaceInfo } from '
|
|
4
|
-
import { isValidAgentName, normalizeAgentName } from '
|
|
5
|
-
import { getTheme, addThemeNames, getThemeNames } from '
|
|
6
|
-
export default class
|
|
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(
|
|
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
|
|
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,8 +1,8 @@
|
|
|
1
1
|
import { Command, Args, Flags } from '@oclif/core';
|
|
2
2
|
import chalk from 'chalk';
|
|
3
|
-
import { getWorkspaceInfo } from '
|
|
4
|
-
import { createTheme, getTheme } from '
|
|
5
|
-
export default class
|
|
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(
|
|
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
|
|
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));
|