@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.
- package/LICENSE +190 -21
- package/README.md +7 -7
- 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 +3553 -5457
- package/package.json +10 -7
- 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
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
import { Args, Flags } from '@oclif/core';
|
|
2
|
-
import { autoExportToBoard, PMOCommand, pmoBaseFlags } from '../../../lib/pmo/index.js';
|
|
3
|
-
import { styles } from '../../../lib/styles.js';
|
|
4
|
-
import { shouldOutputJson, outputErrorAsJson, createMetadata, } from '../../../lib/prompt-json.js';
|
|
5
|
-
export default class TicketLinkRemove extends PMOCommand {
|
|
6
|
-
static description = 'Remove a dependency from a ticket';
|
|
7
|
-
static examples = [
|
|
8
|
-
'<%= config.bin %> <%= command.id %> TKT-001 TKT-002',
|
|
9
|
-
'<%= config.bin %> <%= command.id %> TKT-001 TKT-002 --type blocks',
|
|
10
|
-
'<%= config.bin %> <%= command.id %> TKT-001 --all',
|
|
11
|
-
'<%= config.bin %> <%= command.id %> TKT-001 # Interactive selection',
|
|
12
|
-
];
|
|
13
|
-
static args = {
|
|
14
|
-
id: Args.string({
|
|
15
|
-
description: 'Ticket ID',
|
|
16
|
-
required: true,
|
|
17
|
-
}),
|
|
18
|
-
target: Args.string({
|
|
19
|
-
description: 'Target ticket ID to unlink',
|
|
20
|
-
required: false,
|
|
21
|
-
}),
|
|
22
|
-
};
|
|
23
|
-
static flags = {
|
|
24
|
-
...pmoBaseFlags,
|
|
25
|
-
json: Flags.boolean({
|
|
26
|
-
char: 'm',
|
|
27
|
-
aliases: ['machine'],
|
|
28
|
-
description: 'Output prompt configuration as JSON (for AI agents/scripts)',
|
|
29
|
-
default: false,
|
|
30
|
-
}),
|
|
31
|
-
type: Flags.string({
|
|
32
|
-
char: 't',
|
|
33
|
-
description: 'Dependency type to remove',
|
|
34
|
-
options: ['blocks', 'relates_to', 'duplicates'],
|
|
35
|
-
}),
|
|
36
|
-
all: Flags.boolean({
|
|
37
|
-
char: 'a',
|
|
38
|
-
description: 'Remove all dependencies for this ticket',
|
|
39
|
-
default: false,
|
|
40
|
-
}),
|
|
41
|
-
};
|
|
42
|
-
async execute() {
|
|
43
|
-
const { args, flags } = await this.parse(TicketLinkRemove);
|
|
44
|
-
// Check if JSON output mode is active
|
|
45
|
-
const jsonMode = shouldOutputJson(flags);
|
|
46
|
-
// Helper to handle errors in JSON mode
|
|
47
|
-
const handleError = (code, message) => {
|
|
48
|
-
if (jsonMode) {
|
|
49
|
-
outputErrorAsJson(code, message, createMetadata('ticket link remove', flags));
|
|
50
|
-
this.exit(1);
|
|
51
|
-
}
|
|
52
|
-
this.error(message);
|
|
53
|
-
};
|
|
54
|
-
const ticket = await this.storage.getTicket(args.id);
|
|
55
|
-
if (!ticket) {
|
|
56
|
-
return handleError('TICKET_NOT_FOUND', `Ticket not found: ${args.id}`);
|
|
57
|
-
}
|
|
58
|
-
const dependencies = await this.storage.listTicketDependencies(args.id);
|
|
59
|
-
if (dependencies.length === 0) {
|
|
60
|
-
if (jsonMode) {
|
|
61
|
-
outputErrorAsJson('NO_DEPENDENCIES', `Ticket ${args.id} has no dependencies.`, createMetadata('ticket link remove', flags));
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
this.log(styles.muted(`\nTicket ${args.id} has no dependencies.`));
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
|
-
// If --all flag, remove all dependencies
|
|
68
|
-
if (flags.all) {
|
|
69
|
-
const confirmChoices = [
|
|
70
|
-
{ id: 'no', name: 'No, cancel' },
|
|
71
|
-
{ id: 'yes', name: `Yes, remove all ${dependencies.length} dependencies` },
|
|
72
|
-
];
|
|
73
|
-
const confirmed = await this.selectFromList({
|
|
74
|
-
message: `Remove all ${dependencies.length} dependencies from ${args.id}?`,
|
|
75
|
-
items: confirmChoices,
|
|
76
|
-
getName: (c) => c.name,
|
|
77
|
-
getValue: (c) => c.id,
|
|
78
|
-
getCommand: (c) => c.id === 'yes' ? `prlt ticket link remove ${args.id} --all --force --json` : '',
|
|
79
|
-
jsonMode: jsonMode ? { flags, commandName: 'ticket link remove' } : null,
|
|
80
|
-
});
|
|
81
|
-
if (confirmed !== 'yes') {
|
|
82
|
-
this.log(styles.muted('\nCancelled.'));
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
// Delete sequentially for data integrity
|
|
86
|
-
for (const dep of dependencies) {
|
|
87
|
-
// eslint-disable-next-line no-await-in-loop
|
|
88
|
-
await this.storage.deleteTicketDependency(args.id, dep.dependsOnTicketId, dep.dependencyType);
|
|
89
|
-
}
|
|
90
|
-
await autoExportToBoard(this.pmoPath, this.storage, (msg) => this.log(styles.muted(msg)));
|
|
91
|
-
this.log(styles.success(`\n✅ Removed ${dependencies.length} dependencies from ${args.id}`));
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
94
|
-
let targetId = args.target;
|
|
95
|
-
// If no target provided, prompt for selection
|
|
96
|
-
if (!targetId) {
|
|
97
|
-
const depChoices = await Promise.all(dependencies.map(async (dep) => {
|
|
98
|
-
const depTicket = await this.storage.getTicket(dep.dependsOnTicketId);
|
|
99
|
-
return {
|
|
100
|
-
id: dep.dependsOnTicketId,
|
|
101
|
-
name: `${dep.dependsOnTicketId} - ${depTicket?.title || 'Unknown'} (${dep.dependencyType})`,
|
|
102
|
-
type: dep.dependencyType,
|
|
103
|
-
};
|
|
104
|
-
}));
|
|
105
|
-
const selected = await this.selectFromList({
|
|
106
|
-
message: 'Select dependency to remove:',
|
|
107
|
-
items: depChoices,
|
|
108
|
-
getName: (d) => d.name,
|
|
109
|
-
getValue: (d) => d.id,
|
|
110
|
-
getCommand: (d) => `prlt ticket link remove ${args.id} ${d.id} --type ${d.type}${flags.project ? ` -P ${flags.project}` : ''} --json`,
|
|
111
|
-
jsonMode: jsonMode ? { flags, commandName: 'ticket link remove' } : null,
|
|
112
|
-
});
|
|
113
|
-
if (!selected) {
|
|
114
|
-
return;
|
|
115
|
-
}
|
|
116
|
-
targetId = selected;
|
|
117
|
-
}
|
|
118
|
-
// Find the dependency
|
|
119
|
-
const dep = dependencies.find(d => d.dependsOnTicketId === targetId);
|
|
120
|
-
if (!dep) {
|
|
121
|
-
this.error(`No dependency found from ${args.id} to ${targetId}`);
|
|
122
|
-
}
|
|
123
|
-
const dependencyType = flags.type;
|
|
124
|
-
await this.storage.deleteTicketDependency(args.id, targetId, dependencyType);
|
|
125
|
-
await autoExportToBoard(this.pmoPath, this.storage, (msg) => this.log(styles.muted(msg)));
|
|
126
|
-
const depTicket = await this.storage.getTicket(targetId);
|
|
127
|
-
this.log(styles.success(`\n✅ Removed dependency: ${args.id} → ${targetId}`));
|
|
128
|
-
if (depTicket) {
|
|
129
|
-
this.log(styles.muted(` ${ticket.title} no longer linked to ${depTicket.title}`));
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
}
|
|
@@ -1,252 +0,0 @@
|
|
|
1
|
-
import { Flags, Args } from '@oclif/core';
|
|
2
|
-
import inquirer from 'inquirer';
|
|
3
|
-
import { PMOCommand, pmoBaseFlags, autoExportToBoard } from '../../../lib/pmo/index.js';
|
|
4
|
-
import { PRIORITIES, PRIORITY_LABELS } from '../../../lib/pmo/types.js';
|
|
5
|
-
import { styles } from '../../../lib/styles.js';
|
|
6
|
-
import { shouldOutputJson, outputPromptAsJson, outputErrorAsJson, createMetadata, buildFormPromptConfig, } from '../../../lib/prompt-json.js';
|
|
7
|
-
export default class TicketTemplateApply extends PMOCommand {
|
|
8
|
-
static description = 'Create a new ticket from a template';
|
|
9
|
-
static examples = [
|
|
10
|
-
'<%= config.bin %> <%= command.id %> bug-report',
|
|
11
|
-
'<%= config.bin %> <%= command.id %> feature-request --title "Add dark mode"',
|
|
12
|
-
'<%= config.bin %> <%= command.id %> bug-report --project my-project',
|
|
13
|
-
'<%= config.bin %> <%= command.id %> bug-report -t "Login fails" -c Backlog',
|
|
14
|
-
];
|
|
15
|
-
static args = {
|
|
16
|
-
template: Args.string({
|
|
17
|
-
description: 'Template ID to use',
|
|
18
|
-
required: false,
|
|
19
|
-
}),
|
|
20
|
-
};
|
|
21
|
-
static flags = {
|
|
22
|
-
...pmoBaseFlags,
|
|
23
|
-
title: Flags.string({
|
|
24
|
-
char: 't',
|
|
25
|
-
description: 'Ticket title (overrides template pattern)',
|
|
26
|
-
}),
|
|
27
|
-
column: Flags.string({
|
|
28
|
-
char: 'c',
|
|
29
|
-
description: 'Column to place the ticket in',
|
|
30
|
-
}),
|
|
31
|
-
priority: Flags.string({
|
|
32
|
-
char: 'p',
|
|
33
|
-
description: 'Priority (overrides template default)',
|
|
34
|
-
options: [...PRIORITIES],
|
|
35
|
-
}),
|
|
36
|
-
category: Flags.string({
|
|
37
|
-
description: 'Category (overrides template default)',
|
|
38
|
-
}),
|
|
39
|
-
assignee: Flags.string({
|
|
40
|
-
char: 'a',
|
|
41
|
-
description: 'Assignee (overrides template default)',
|
|
42
|
-
}),
|
|
43
|
-
owner: Flags.string({
|
|
44
|
-
char: 'o',
|
|
45
|
-
description: 'Owner (overrides template default)',
|
|
46
|
-
}),
|
|
47
|
-
status: Flags.string({
|
|
48
|
-
char: 's',
|
|
49
|
-
description: 'Status ID (overrides template default)',
|
|
50
|
-
}),
|
|
51
|
-
labels: Flags.string({
|
|
52
|
-
char: 'l',
|
|
53
|
-
description: 'Labels (comma-separated, overrides template default)',
|
|
54
|
-
}),
|
|
55
|
-
description: Flags.string({
|
|
56
|
-
char: 'd',
|
|
57
|
-
description: 'Description (overrides template)',
|
|
58
|
-
}),
|
|
59
|
-
interactive: Flags.boolean({
|
|
60
|
-
char: 'i',
|
|
61
|
-
description: 'Interactive mode - prompt for values',
|
|
62
|
-
default: false,
|
|
63
|
-
}),
|
|
64
|
-
json: Flags.boolean({
|
|
65
|
-
char: 'm',
|
|
66
|
-
aliases: ['machine'],
|
|
67
|
-
description: 'Output prompt configuration as JSON (for AI agents/scripts)',
|
|
68
|
-
default: false,
|
|
69
|
-
}),
|
|
70
|
-
'no-subtasks': Flags.boolean({
|
|
71
|
-
description: 'Do not create suggested subtasks',
|
|
72
|
-
default: false,
|
|
73
|
-
}),
|
|
74
|
-
epic: Flags.string({
|
|
75
|
-
char: 'e',
|
|
76
|
-
description: 'Link ticket to an epic',
|
|
77
|
-
}),
|
|
78
|
-
};
|
|
79
|
-
async execute() {
|
|
80
|
-
const { args, flags } = await this.parse(TicketTemplateApply);
|
|
81
|
-
// This command requires project context - get projectId and board info
|
|
82
|
-
const projectId = await this.requireProject();
|
|
83
|
-
const board = await this.storage.getBoard(projectId);
|
|
84
|
-
const columns = board.columns.map(col => col.name);
|
|
85
|
-
const projectName = board.name;
|
|
86
|
-
// Check if JSON output mode is active
|
|
87
|
-
const jsonMode = shouldOutputJson(flags);
|
|
88
|
-
// Helper to handle errors in JSON mode
|
|
89
|
-
const handleError = (code, message) => {
|
|
90
|
-
if (jsonMode) {
|
|
91
|
-
outputErrorAsJson(code, message, createMetadata('ticket template apply', flags));
|
|
92
|
-
this.exit(1);
|
|
93
|
-
}
|
|
94
|
-
this.error(message);
|
|
95
|
-
};
|
|
96
|
-
// Get the template - prompt for selection if not provided
|
|
97
|
-
let templateId = args.template;
|
|
98
|
-
if (!templateId) {
|
|
99
|
-
const templates = await this.storage.listTicketTemplates();
|
|
100
|
-
if (templates.length === 0) {
|
|
101
|
-
return handleError('NO_TEMPLATES', `No ticket templates found.\nCreate one with: prlt ticket template save <ticket-id> "Template Name"`);
|
|
102
|
-
}
|
|
103
|
-
const { selectedTemplate } = await inquirer.prompt([{
|
|
104
|
-
type: 'list',
|
|
105
|
-
name: 'selectedTemplate',
|
|
106
|
-
message: 'Select a template:',
|
|
107
|
-
choices: templates.map(t => ({
|
|
108
|
-
name: `${t.name}${t.description ? ` - ${t.description}` : ''}`,
|
|
109
|
-
value: t.id,
|
|
110
|
-
})),
|
|
111
|
-
}]);
|
|
112
|
-
templateId = selectedTemplate;
|
|
113
|
-
}
|
|
114
|
-
const template = await this.storage.getTicketTemplate(templateId);
|
|
115
|
-
if (!template) {
|
|
116
|
-
return handleError('TEMPLATE_NOT_FOUND', `Template not found: ${templateId}\nRun 'prlt ticket template list' to see available templates.`);
|
|
117
|
-
}
|
|
118
|
-
// Validate epic if provided
|
|
119
|
-
if (flags.epic) {
|
|
120
|
-
const epic = await this.storage.getEpic(flags.epic);
|
|
121
|
-
if (!epic) {
|
|
122
|
-
this.error(`Epic not found: ${flags.epic}. Use 'prlt epic list' to see available epics.`);
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
// Determine ticket data
|
|
126
|
-
let title = flags.title || template.titlePattern || '';
|
|
127
|
-
let column = flags.column || columns[0];
|
|
128
|
-
let priority = flags.priority || template.defaultPriority;
|
|
129
|
-
let category = flags.category || template.defaultCategory;
|
|
130
|
-
let assignee = flags.assignee || template.defaultAssignee;
|
|
131
|
-
let owner = flags.owner || template.defaultOwner;
|
|
132
|
-
const statusId = flags.status || template.defaultStatusId;
|
|
133
|
-
const labels = flags.labels ? flags.labels.split(',').map(l => l.trim()).filter(Boolean) : template.defaultLabels;
|
|
134
|
-
let description = flags.description || template.descriptionTemplate;
|
|
135
|
-
// Interactive mode - prompt for values
|
|
136
|
-
if (flags.interactive || !title) {
|
|
137
|
-
// Build choices once - single source of truth
|
|
138
|
-
const columnChoices = columns.map(c => ({ name: c, value: c }));
|
|
139
|
-
const priorityChoices = [
|
|
140
|
-
{ name: 'None', value: '' },
|
|
141
|
-
...PRIORITIES.map(p => ({ name: PRIORITY_LABELS[p], value: p })),
|
|
142
|
-
];
|
|
143
|
-
// Define fields once - single source of truth for both JSON and interactive modes
|
|
144
|
-
const fields = [
|
|
145
|
-
{ type: 'input', name: 'title', message: 'Ticket title:', default: title || undefined },
|
|
146
|
-
{ type: 'list', name: 'column', message: 'Column:', choices: columnChoices, default: column },
|
|
147
|
-
{ type: 'list', name: 'priority', message: 'Priority:', choices: priorityChoices, default: priority },
|
|
148
|
-
{ type: 'input', name: 'category', message: 'Category:', default: category },
|
|
149
|
-
{ type: 'input', name: 'assignee', message: 'Assignee:', default: assignee },
|
|
150
|
-
{ type: 'input', name: 'owner', message: 'Owner:', default: owner },
|
|
151
|
-
{ type: 'editor', name: 'description', message: 'Description:', default: description },
|
|
152
|
-
];
|
|
153
|
-
// In JSON mode, output form prompts
|
|
154
|
-
if (jsonMode) {
|
|
155
|
-
outputPromptAsJson(buildFormPromptConfig(fields), createMetadata('ticket template apply', flags));
|
|
156
|
-
}
|
|
157
|
-
// Build inquirer prompts from fields, adding validators and inquirer-specific options
|
|
158
|
-
const answers = await inquirer.prompt(fields.map(field => ({
|
|
159
|
-
...field,
|
|
160
|
-
// Convert empty string to undefined for priority choices in inquirer
|
|
161
|
-
choices: field.name === 'priority' && field.choices
|
|
162
|
-
? field.choices.map(c => ({ ...c, value: c.value || undefined }))
|
|
163
|
-
: field.name === 'column'
|
|
164
|
-
? columns // Use simple array for column in interactive mode
|
|
165
|
-
: field.choices,
|
|
166
|
-
// Add validator for title
|
|
167
|
-
validate: field.name === 'title'
|
|
168
|
-
? ((input) => input.length > 0 || 'Title is required')
|
|
169
|
-
: undefined,
|
|
170
|
-
// Update editor message for interactive mode
|
|
171
|
-
message: field.name === 'description' ? 'Description (opens editor):' : field.message,
|
|
172
|
-
// Add waitForUseInput for editor
|
|
173
|
-
waitForUseInput: field.type === 'editor' ? false : undefined,
|
|
174
|
-
})));
|
|
175
|
-
title = answers.title;
|
|
176
|
-
column = answers.column;
|
|
177
|
-
priority = answers.priority;
|
|
178
|
-
category = answers.category || undefined;
|
|
179
|
-
assignee = answers.assignee || undefined;
|
|
180
|
-
owner = answers.owner || undefined;
|
|
181
|
-
description = answers.description || undefined;
|
|
182
|
-
}
|
|
183
|
-
// Validate column
|
|
184
|
-
if (!columns.includes(column)) {
|
|
185
|
-
this.error(`Invalid column "${column}". Available columns: ${columns.join(', ')}`);
|
|
186
|
-
}
|
|
187
|
-
// Validate status ID if provided
|
|
188
|
-
if (statusId) {
|
|
189
|
-
const status = await this.storage.getStatus(statusId);
|
|
190
|
-
if (!status) {
|
|
191
|
-
// Get project's workflow to list available statuses
|
|
192
|
-
const projectInfo = await this.storage.getProject(projectId);
|
|
193
|
-
const workflowId = projectInfo?.workflowId;
|
|
194
|
-
const statuses = workflowId ? await this.storage.listStatuses(workflowId) : [];
|
|
195
|
-
const statusNames = statuses.map(s => `${s.id} (${s.name})`).join(', ');
|
|
196
|
-
this.error(`Invalid status "${statusId}". Available statuses: ${statusNames}`);
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
// Create the ticket
|
|
200
|
-
const ticket = await this.storage.createTicket(projectId, {
|
|
201
|
-
title,
|
|
202
|
-
statusName: column,
|
|
203
|
-
priority,
|
|
204
|
-
category,
|
|
205
|
-
assignee,
|
|
206
|
-
owner,
|
|
207
|
-
statusId,
|
|
208
|
-
labels,
|
|
209
|
-
description,
|
|
210
|
-
epicId: flags.epic,
|
|
211
|
-
});
|
|
212
|
-
// Add subtasks from template (unless disabled) - sequential for ordering
|
|
213
|
-
if (!flags['no-subtasks'] && template.suggestedSubtasks.length > 0) {
|
|
214
|
-
for (const subtask of template.suggestedSubtasks) {
|
|
215
|
-
// eslint-disable-next-line no-await-in-loop
|
|
216
|
-
await this.storage.addSubtask(ticket.id, subtask.title);
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
// Auto-export to board.md
|
|
220
|
-
await autoExportToBoard(this.pmoPath, this.storage, (msg) => this.log(styles.muted(msg)));
|
|
221
|
-
this.log(styles.success(`\nCreated ticket ${styles.emphasis(ticket.id)} from template "${template.name}"`));
|
|
222
|
-
this.log(styles.muted(` Project: ${projectName}`));
|
|
223
|
-
this.log(styles.muted(` Title: ${ticket.title}`));
|
|
224
|
-
this.log(styles.muted(` Status: ${ticket.statusName}`));
|
|
225
|
-
if (priority) {
|
|
226
|
-
this.log(styles.muted(` Priority: ${priority}`));
|
|
227
|
-
}
|
|
228
|
-
if (category) {
|
|
229
|
-
this.log(styles.muted(` Category: ${category}`));
|
|
230
|
-
}
|
|
231
|
-
if (assignee) {
|
|
232
|
-
this.log(styles.muted(` Assignee: ${assignee}`));
|
|
233
|
-
}
|
|
234
|
-
if (owner) {
|
|
235
|
-
this.log(styles.muted(` Owner: ${owner}`));
|
|
236
|
-
}
|
|
237
|
-
if (statusId) {
|
|
238
|
-
this.log(styles.muted(` Status: ${statusId}`));
|
|
239
|
-
}
|
|
240
|
-
if (labels && labels.length > 0) {
|
|
241
|
-
this.log(styles.muted(` Labels: ${labels.join(', ')}`));
|
|
242
|
-
}
|
|
243
|
-
if (flags.epic) {
|
|
244
|
-
this.log(styles.muted(` Epic: ${flags.epic}`));
|
|
245
|
-
}
|
|
246
|
-
if (!flags['no-subtasks'] && template.suggestedSubtasks.length > 0) {
|
|
247
|
-
this.log(styles.muted(` Subtasks: ${template.suggestedSubtasks.length} created`));
|
|
248
|
-
}
|
|
249
|
-
this.log('');
|
|
250
|
-
this.log(styles.muted(`View ticket: prlt ticket view ${ticket.id}`));
|
|
251
|
-
}
|
|
252
|
-
}
|