@proletariat/cli 0.3.23 → 0.3.25
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 +4 -4
- package/dist/commands/action/update.js +3 -3
- 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/activate.js +9 -17
- package/dist/commands/epic/archive.js +13 -24
- package/dist/commands/epic/create.d.ts +1 -0
- package/dist/commands/epic/create.js +46 -8
- package/dist/commands/epic/index.js +2 -2
- package/dist/commands/epic/move.js +28 -47
- package/dist/commands/epic/progress.js +10 -14
- package/dist/commands/epic/project.js +42 -59
- package/dist/commands/epic/reorder.js +25 -30
- package/dist/commands/epic/spec.d.ts +1 -0
- package/dist/commands/epic/spec.js +39 -40
- package/dist/commands/epic/ticket.d.ts +2 -0
- package/dist/commands/epic/ticket.js +63 -37
- package/dist/commands/feedback/index.d.ts +10 -0
- package/dist/commands/feedback/index.js +60 -0
- package/dist/commands/feedback/list.d.ts +12 -0
- package/dist/commands/feedback/list.js +126 -0
- package/dist/commands/feedback/submit.d.ts +16 -0
- package/dist/commands/feedback/submit.js +220 -0
- package/dist/commands/{template/phase/delete.d.ts → feedback/view.d.ts} +7 -5
- package/dist/commands/feedback/view.js +109 -0
- package/dist/commands/gh/index.js +4 -0
- 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/repo/create.d.ts +38 -0
- package/dist/commands/repo/create.js +283 -0
- package/dist/commands/repo/index.js +7 -0
- package/dist/commands/roadmap/add-project.js +9 -22
- package/dist/commands/roadmap/create.d.ts +0 -1
- package/dist/commands/roadmap/create.js +46 -40
- package/dist/commands/roadmap/delete.js +10 -24
- package/dist/commands/roadmap/generate.d.ts +1 -0
- package/dist/commands/roadmap/generate.js +21 -22
- package/dist/commands/roadmap/remove-project.js +14 -34
- package/dist/commands/roadmap/reorder.js +19 -26
- package/dist/commands/roadmap/update.js +27 -26
- package/dist/commands/roadmap/view.js +5 -12
- package/dist/commands/session/attach.d.ts +1 -8
- package/dist/commands/session/attach.js +93 -59
- package/dist/commands/session/list.d.ts +0 -8
- package/dist/commands/session/list.js +130 -81
- package/dist/commands/spec/create.d.ts +1 -0
- package/dist/commands/spec/create.js +44 -3
- package/dist/commands/spec/edit.js +63 -33
- 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/{template/phase/index.d.ts → support/book.d.ts} +2 -2
- package/dist/commands/support/book.js +54 -0
- package/dist/commands/{template/ticket/index.d.ts → support/discord.d.ts} +2 -2
- package/dist/commands/support/discord.js +54 -0
- package/dist/commands/support/docs.d.ts +10 -0
- package/dist/commands/support/docs.js +54 -0
- package/dist/commands/support/index.d.ts +19 -0
- package/dist/commands/support/index.js +81 -0
- package/dist/commands/support/issues.d.ts +11 -0
- package/dist/commands/support/issues.js +77 -0
- package/dist/commands/support/logs.d.ts +18 -0
- package/dist/commands/support/logs.js +247 -0
- 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 +75 -15
- package/dist/commands/ticket/edit.js +44 -13
- package/dist/commands/ticket/index.js +6 -6
- package/dist/commands/ticket/move.d.ts +7 -0
- package/dist/commands/ticket/move.js +132 -0
- package/dist/commands/work/spawn.d.ts +1 -0
- package/dist/commands/work/spawn.js +72 -8
- package/dist/commands/work/start.js +6 -0
- package/dist/lib/execution/runners.js +21 -17
- package/dist/lib/execution/session-utils.d.ts +60 -0
- package/dist/lib/execution/session-utils.js +162 -0
- package/dist/lib/execution/spawner.d.ts +2 -0
- package/dist/lib/execution/spawner.js +42 -0
- package/dist/lib/flags/resolver.d.ts +2 -2
- package/dist/lib/flags/resolver.js +15 -0
- package/dist/lib/init/index.js +18 -0
- 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/multiline-input.d.ts +63 -0
- package/dist/lib/multiline-input.js +360 -0
- package/dist/lib/prompt-json.d.ts +57 -6
- package/dist/lib/prompt-json.js +45 -0
- package/dist/lib/repos/git.d.ts +7 -0
- package/dist/lib/repos/git.js +20 -0
- package/oclif.manifest.json +3690 -4995
- 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.js +0 -36
- 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.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,96 +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 TicketLinkBlock extends PMOCommand {
|
|
6
|
-
static description = 'Add a blocking dependency (ticket is blocked by another)';
|
|
7
|
-
static examples = [
|
|
8
|
-
'<%= config.bin %> <%= command.id %> TKT-001 TKT-002 # TKT-001 is blocked by TKT-002',
|
|
9
|
-
'<%= config.bin %> <%= command.id %> TKT-001 # Interactive selection',
|
|
10
|
-
];
|
|
11
|
-
static args = {
|
|
12
|
-
id: Args.string({
|
|
13
|
-
description: 'Ticket ID that will be blocked',
|
|
14
|
-
required: true,
|
|
15
|
-
}),
|
|
16
|
-
blocker: Args.string({
|
|
17
|
-
description: 'Ticket ID that blocks this ticket',
|
|
18
|
-
required: false,
|
|
19
|
-
}),
|
|
20
|
-
};
|
|
21
|
-
static flags = {
|
|
22
|
-
...pmoBaseFlags,
|
|
23
|
-
json: Flags.boolean({
|
|
24
|
-
char: 'm',
|
|
25
|
-
aliases: ['machine'],
|
|
26
|
-
description: 'Output prompt configuration as JSON (for AI agents/scripts)',
|
|
27
|
-
default: false,
|
|
28
|
-
}),
|
|
29
|
-
};
|
|
30
|
-
async execute() {
|
|
31
|
-
const { args, flags } = await this.parse(TicketLinkBlock);
|
|
32
|
-
// Check if JSON output mode is active
|
|
33
|
-
const jsonMode = shouldOutputJson(flags);
|
|
34
|
-
// Helper to handle errors in JSON mode
|
|
35
|
-
const handleError = (code, message) => {
|
|
36
|
-
if (jsonMode) {
|
|
37
|
-
outputErrorAsJson(code, message, createMetadata('ticket link block', flags));
|
|
38
|
-
this.exit(1);
|
|
39
|
-
}
|
|
40
|
-
this.error(message);
|
|
41
|
-
};
|
|
42
|
-
const ticket = await this.storage.getTicket(args.id);
|
|
43
|
-
if (!ticket) {
|
|
44
|
-
return handleError('TICKET_NOT_FOUND', `Ticket not found: ${args.id}`);
|
|
45
|
-
}
|
|
46
|
-
let blockerId = args.blocker;
|
|
47
|
-
// If no blocker provided, prompt for selection
|
|
48
|
-
if (!blockerId) {
|
|
49
|
-
const projectId = flags.project;
|
|
50
|
-
const allTickets = await this.storage.listTickets(projectId);
|
|
51
|
-
const otherTickets = allTickets.filter(t => t.id !== args.id);
|
|
52
|
-
if (otherTickets.length === 0) {
|
|
53
|
-
if (jsonMode) {
|
|
54
|
-
outputErrorAsJson('NO_OTHER_TICKETS', 'No other tickets to create dependency with.', createMetadata('ticket link block', flags));
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
|
-
this.log(styles.muted('\nNo other tickets to create dependency with.'));
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
const selected = await this.selectFromList({
|
|
61
|
-
message: `Select ticket that blocks ${args.id}:`,
|
|
62
|
-
items: otherTickets,
|
|
63
|
-
getName: (t) => `${t.id} - ${t.title} (${t.statusName || t.status})`,
|
|
64
|
-
getValue: (t) => t.id,
|
|
65
|
-
getCommand: (t) => `prlt ticket link block ${args.id} ${t.id}${projectId ? ` -P ${projectId}` : ''} --json`,
|
|
66
|
-
jsonMode: jsonMode ? { flags, commandName: 'ticket link block' } : null,
|
|
67
|
-
});
|
|
68
|
-
if (!selected) {
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
blockerId = selected;
|
|
72
|
-
}
|
|
73
|
-
const blockerTicket = await this.storage.getTicket(blockerId);
|
|
74
|
-
if (!blockerTicket) {
|
|
75
|
-
this.error(`Ticket not found: ${blockerId}`);
|
|
76
|
-
}
|
|
77
|
-
try {
|
|
78
|
-
await this.storage.createTicketDependency(args.id, blockerId, 'blocks');
|
|
79
|
-
await autoExportToBoard(this.pmoPath, this.storage, (msg) => this.log(styles.muted(msg)));
|
|
80
|
-
this.log(styles.success(`\n✅ ${styles.emphasis(args.id)} is blocked by ${styles.emphasis(blockerId)}`));
|
|
81
|
-
this.log(styles.muted(` ${ticket.title}`));
|
|
82
|
-
this.log(styles.muted(` blocked by: ${blockerTicket.title}`));
|
|
83
|
-
}
|
|
84
|
-
catch (error) {
|
|
85
|
-
if (error instanceof Error) {
|
|
86
|
-
if (error.message.includes('already exists')) {
|
|
87
|
-
this.error('Dependency already exists');
|
|
88
|
-
}
|
|
89
|
-
if (error.message.includes('self-dependency')) {
|
|
90
|
-
this.error('Cannot create self-dependency');
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
throw error;
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { PMOCommand } from '../../../lib/pmo/index.js';
|
|
2
|
-
export default class TicketLinkDuplicates extends PMOCommand {
|
|
3
|
-
static description: string;
|
|
4
|
-
static examples: string[];
|
|
5
|
-
static args: {
|
|
6
|
-
id: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
7
|
-
original: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
|
|
8
|
-
};
|
|
9
|
-
static flags: {
|
|
10
|
-
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
11
|
-
project: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
-
};
|
|
13
|
-
execute(): Promise<void>;
|
|
14
|
-
}
|
|
@@ -1,95 +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 TicketLinkDuplicates extends PMOCommand {
|
|
6
|
-
static description = 'Mark a ticket as duplicate of another';
|
|
7
|
-
static examples = [
|
|
8
|
-
'<%= config.bin %> <%= command.id %> TKT-001 TKT-002 # TKT-001 duplicates TKT-002',
|
|
9
|
-
'<%= config.bin %> <%= command.id %> TKT-001 # Interactive selection',
|
|
10
|
-
];
|
|
11
|
-
static args = {
|
|
12
|
-
id: Args.string({
|
|
13
|
-
description: 'Duplicate ticket ID',
|
|
14
|
-
required: true,
|
|
15
|
-
}),
|
|
16
|
-
original: Args.string({
|
|
17
|
-
description: 'Original ticket ID',
|
|
18
|
-
required: false,
|
|
19
|
-
}),
|
|
20
|
-
};
|
|
21
|
-
static flags = {
|
|
22
|
-
...pmoBaseFlags,
|
|
23
|
-
json: Flags.boolean({
|
|
24
|
-
char: 'm',
|
|
25
|
-
aliases: ['machine'],
|
|
26
|
-
description: 'Output prompt configuration as JSON (for AI agents/scripts)',
|
|
27
|
-
default: false,
|
|
28
|
-
}),
|
|
29
|
-
};
|
|
30
|
-
async execute() {
|
|
31
|
-
const { args, flags } = await this.parse(TicketLinkDuplicates);
|
|
32
|
-
// Check if JSON output mode is active
|
|
33
|
-
const jsonMode = shouldOutputJson(flags);
|
|
34
|
-
// Helper to handle errors in JSON mode
|
|
35
|
-
const handleError = (code, message) => {
|
|
36
|
-
if (jsonMode) {
|
|
37
|
-
outputErrorAsJson(code, message, createMetadata('ticket link duplicates', flags));
|
|
38
|
-
this.exit(1);
|
|
39
|
-
}
|
|
40
|
-
this.error(message);
|
|
41
|
-
};
|
|
42
|
-
const ticket = await this.storage.getTicket(args.id);
|
|
43
|
-
if (!ticket) {
|
|
44
|
-
return handleError('TICKET_NOT_FOUND', `Ticket not found: ${args.id}`);
|
|
45
|
-
}
|
|
46
|
-
let originalId = args.original;
|
|
47
|
-
if (!originalId) {
|
|
48
|
-
const projectId = flags.project;
|
|
49
|
-
const allTickets = await this.storage.listTickets(projectId);
|
|
50
|
-
const otherTickets = allTickets.filter(t => t.id !== args.id);
|
|
51
|
-
if (otherTickets.length === 0) {
|
|
52
|
-
if (jsonMode) {
|
|
53
|
-
outputErrorAsJson('NO_OTHER_TICKETS', 'No other tickets.', createMetadata('ticket link duplicates', flags));
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
this.log(styles.muted('\nNo other tickets.'));
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
const selected = await this.selectFromList({
|
|
60
|
-
message: `Select the original ticket (${args.id} is a duplicate of):`,
|
|
61
|
-
items: otherTickets,
|
|
62
|
-
getName: (t) => `${t.id} - ${t.title} (${t.statusName || t.status})`,
|
|
63
|
-
getValue: (t) => t.id,
|
|
64
|
-
getCommand: (t) => `prlt ticket link duplicates ${args.id} ${t.id}${projectId ? ` -P ${projectId}` : ''} --json`,
|
|
65
|
-
jsonMode: jsonMode ? { flags, commandName: 'ticket link duplicates' } : null,
|
|
66
|
-
});
|
|
67
|
-
if (!selected) {
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
originalId = selected;
|
|
71
|
-
}
|
|
72
|
-
const originalTicket = await this.storage.getTicket(originalId);
|
|
73
|
-
if (!originalTicket) {
|
|
74
|
-
this.error(`Ticket not found: ${originalId}`);
|
|
75
|
-
}
|
|
76
|
-
try {
|
|
77
|
-
await this.storage.createTicketDependency(args.id, originalId, 'duplicates');
|
|
78
|
-
await autoExportToBoard(this.pmoPath, this.storage, (msg) => this.log(styles.muted(msg)));
|
|
79
|
-
this.log(styles.success(`\n✅ ${styles.emphasis(args.id)} duplicates ${styles.emphasis(originalId)}`));
|
|
80
|
-
this.log(styles.muted(` ${ticket.title}`));
|
|
81
|
-
this.log(styles.muted(` duplicates: ${originalTicket.title}`));
|
|
82
|
-
}
|
|
83
|
-
catch (error) {
|
|
84
|
-
if (error instanceof Error) {
|
|
85
|
-
if (error.message.includes('already exists')) {
|
|
86
|
-
this.error('Dependency already exists');
|
|
87
|
-
}
|
|
88
|
-
if (error.message.includes('self-dependency')) {
|
|
89
|
-
this.error('Cannot create self-dependency');
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
throw error;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { PMOCommand } from '../../../lib/pmo/index.js';
|
|
2
|
-
export default class TicketLink extends PMOCommand {
|
|
3
|
-
static description: string;
|
|
4
|
-
static examples: string[];
|
|
5
|
-
static args: {
|
|
6
|
-
id: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
|
|
7
|
-
};
|
|
8
|
-
static flags: {
|
|
9
|
-
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
10
|
-
blocks: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
-
relates: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
-
duplicates: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
|
-
all: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
14
|
-
project: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
15
|
-
};
|
|
16
|
-
execute(): Promise<void>;
|
|
17
|
-
private addDependency;
|
|
18
|
-
private viewDependencies;
|
|
19
|
-
}
|
|
@@ -1,256 +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 TicketLink extends PMOCommand {
|
|
6
|
-
static description = 'Manage ticket dependencies (links)';
|
|
7
|
-
static examples = [
|
|
8
|
-
'<%= config.bin %> <%= command.id %> TKT-001 # List dependencies',
|
|
9
|
-
'<%= config.bin %> <%= command.id %> TKT-001 --blocks TKT-002 # TKT-001 is blocked by TKT-002',
|
|
10
|
-
'<%= config.bin %> <%= command.id %> TKT-001 --relates TKT-002 # TKT-001 relates to TKT-002',
|
|
11
|
-
'<%= config.bin %> <%= command.id %> TKT-001 --duplicates TKT-002',
|
|
12
|
-
'<%= config.bin %> <%= command.id %> TKT-001 --all # Show all (blockers + blocking)',
|
|
13
|
-
];
|
|
14
|
-
static args = {
|
|
15
|
-
id: Args.string({
|
|
16
|
-
description: 'Ticket ID',
|
|
17
|
-
required: false,
|
|
18
|
-
}),
|
|
19
|
-
};
|
|
20
|
-
static flags = {
|
|
21
|
-
...pmoBaseFlags,
|
|
22
|
-
json: Flags.boolean({
|
|
23
|
-
char: 'm',
|
|
24
|
-
aliases: ['machine'],
|
|
25
|
-
description: 'Output prompt configuration as JSON (for AI agents/scripts)',
|
|
26
|
-
default: false,
|
|
27
|
-
}),
|
|
28
|
-
blocks: Flags.string({
|
|
29
|
-
char: 'b',
|
|
30
|
-
description: 'Add blocking dependency: this ticket is blocked by TARGET',
|
|
31
|
-
}),
|
|
32
|
-
relates: Flags.string({
|
|
33
|
-
char: 'r',
|
|
34
|
-
description: 'Add relates_to dependency: this ticket relates to TARGET',
|
|
35
|
-
}),
|
|
36
|
-
duplicates: Flags.string({
|
|
37
|
-
char: 'd',
|
|
38
|
-
description: 'Add duplicates dependency: this ticket duplicates TARGET',
|
|
39
|
-
}),
|
|
40
|
-
all: Flags.boolean({
|
|
41
|
-
char: 'a',
|
|
42
|
-
description: 'Show all dependencies (blockers and tickets blocked by this)',
|
|
43
|
-
default: false,
|
|
44
|
-
}),
|
|
45
|
-
};
|
|
46
|
-
async execute() {
|
|
47
|
-
const { args, flags } = await this.parse(TicketLink);
|
|
48
|
-
// Check if JSON output mode is active
|
|
49
|
-
const jsonMode = shouldOutputJson(flags);
|
|
50
|
-
const projectId = flags.project;
|
|
51
|
-
let ticketId = args.id;
|
|
52
|
-
if (!ticketId) {
|
|
53
|
-
const tickets = await this.storage.listTickets(projectId);
|
|
54
|
-
if (tickets.length === 0) {
|
|
55
|
-
if (jsonMode) {
|
|
56
|
-
outputErrorAsJson('NO_TICKETS', 'No tickets found.', createMetadata('ticket link', flags));
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
this.log(styles.muted('\nNo tickets found.'));
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
const selected = await this.selectFromList({
|
|
63
|
-
message: 'Select ticket to manage dependencies:',
|
|
64
|
-
items: tickets,
|
|
65
|
-
getName: (t) => `${t.id} - ${t.title}`,
|
|
66
|
-
getValue: (t) => t.id,
|
|
67
|
-
getCommand: (t) => `prlt ticket link ${t.id}${projectId ? ` -P ${projectId}` : ''} --json`,
|
|
68
|
-
jsonMode: jsonMode ? { flags, commandName: 'ticket link' } : null,
|
|
69
|
-
});
|
|
70
|
-
if (!selected) {
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
ticketId = selected;
|
|
74
|
-
}
|
|
75
|
-
const ticket = await this.storage.getTicket(ticketId);
|
|
76
|
-
if (!ticket) {
|
|
77
|
-
this.error(`Ticket not found: ${ticketId}`);
|
|
78
|
-
}
|
|
79
|
-
// If a dependency flag is provided, add the dependency directly
|
|
80
|
-
if (flags.blocks || flags.relates || flags.duplicates) {
|
|
81
|
-
const targetId = flags.blocks || flags.relates || flags.duplicates;
|
|
82
|
-
const dependencyType = flags.blocks ? 'blocks' :
|
|
83
|
-
flags.relates ? 'relates_to' : 'duplicates';
|
|
84
|
-
await this.addDependency(this.storage, this.pmoPath, ticketId, targetId, dependencyType, ticket.title);
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
// Interactive mode: show menu in a loop - user interaction requires sequential processing
|
|
88
|
-
let continueLoop = true;
|
|
89
|
-
while (continueLoop) {
|
|
90
|
-
// eslint-disable-next-line no-await-in-loop
|
|
91
|
-
const allTickets = await this.storage.listTickets(projectId);
|
|
92
|
-
const otherTickets = allTickets.filter(t => t.id !== ticketId);
|
|
93
|
-
const menuChoices = [
|
|
94
|
-
{ id: 'view', name: 'View dependencies' },
|
|
95
|
-
{ id: 'blocks', name: 'Add blocking dependency (blocked by...)' },
|
|
96
|
-
{ id: 'relates_to', name: 'Add relates_to dependency' },
|
|
97
|
-
{ id: 'duplicates', name: 'Add duplicates dependency' },
|
|
98
|
-
{ id: 'remove', name: 'Remove dependency' },
|
|
99
|
-
{ id: 'done', name: 'Done' },
|
|
100
|
-
];
|
|
101
|
-
// eslint-disable-next-line no-await-in-loop
|
|
102
|
-
const action = await this.selectFromList({
|
|
103
|
-
message: `Dependencies for ${ticket.id}:`,
|
|
104
|
-
items: menuChoices,
|
|
105
|
-
getName: (c) => c.name,
|
|
106
|
-
getValue: (c) => c.id,
|
|
107
|
-
getCommand: (c) => {
|
|
108
|
-
const pFlag = projectId ? ` -P ${projectId}` : '';
|
|
109
|
-
switch (c.id) {
|
|
110
|
-
case 'view': return `prlt ticket link ${ticketId}${pFlag} --all`;
|
|
111
|
-
case 'blocks': return `prlt ticket link block ${ticketId}${pFlag} --json`;
|
|
112
|
-
case 'relates_to': return `prlt ticket link relates ${ticketId}${pFlag} --json`;
|
|
113
|
-
case 'duplicates': return `prlt ticket link duplicates ${ticketId}${pFlag} --json`;
|
|
114
|
-
case 'remove': return `prlt ticket link remove ${ticketId}${pFlag} --json`;
|
|
115
|
-
default: return '';
|
|
116
|
-
}
|
|
117
|
-
},
|
|
118
|
-
jsonMode: jsonMode ? { flags, commandName: 'ticket link' } : null,
|
|
119
|
-
});
|
|
120
|
-
if (action === 'done' || !action) {
|
|
121
|
-
continueLoop = false;
|
|
122
|
-
continue;
|
|
123
|
-
}
|
|
124
|
-
if (action === 'view') {
|
|
125
|
-
// eslint-disable-next-line no-await-in-loop
|
|
126
|
-
await this.viewDependencies(this.storage, ticketId, ticket, flags.all);
|
|
127
|
-
continue;
|
|
128
|
-
}
|
|
129
|
-
if (action === 'remove') {
|
|
130
|
-
// eslint-disable-next-line no-await-in-loop
|
|
131
|
-
const dependencies = await this.storage.listTicketDependencies(ticketId);
|
|
132
|
-
if (dependencies.length === 0) {
|
|
133
|
-
this.log(styles.muted('\nNo dependencies to remove.'));
|
|
134
|
-
continue;
|
|
135
|
-
}
|
|
136
|
-
// eslint-disable-next-line no-await-in-loop
|
|
137
|
-
const depChoices = await Promise.all(dependencies.map(async (dep) => {
|
|
138
|
-
const depTicket = await this.storage.getTicket(dep.dependsOnTicketId);
|
|
139
|
-
return {
|
|
140
|
-
id: dep.dependsOnTicketId,
|
|
141
|
-
name: `${dep.dependsOnTicketId} - ${depTicket?.title || 'Unknown'} (${dep.dependencyType})`,
|
|
142
|
-
type: dep.dependencyType
|
|
143
|
-
};
|
|
144
|
-
}));
|
|
145
|
-
// eslint-disable-next-line no-await-in-loop
|
|
146
|
-
const selected = await this.selectFromList({
|
|
147
|
-
message: 'Select dependency to remove:',
|
|
148
|
-
items: depChoices,
|
|
149
|
-
getName: (d) => d.name,
|
|
150
|
-
getValue: (d) => d.id,
|
|
151
|
-
getCommand: (d) => `prlt ticket link remove ${ticketId} ${d.id} --type ${d.type}${projectId ? ` -P ${projectId}` : ''} --json`,
|
|
152
|
-
jsonMode: jsonMode ? { flags, commandName: 'ticket link' } : null,
|
|
153
|
-
});
|
|
154
|
-
if (!selected) {
|
|
155
|
-
continue;
|
|
156
|
-
}
|
|
157
|
-
const selectedDep = depChoices.find(d => d.id === selected);
|
|
158
|
-
if (selectedDep) {
|
|
159
|
-
// eslint-disable-next-line no-await-in-loop
|
|
160
|
-
await this.storage.deleteTicketDependency(ticketId, selected, selectedDep.type);
|
|
161
|
-
// eslint-disable-next-line no-await-in-loop
|
|
162
|
-
await autoExportToBoard(this.pmoPath, this.storage, (msg) => this.log(styles.muted(msg)));
|
|
163
|
-
this.log(styles.success(`\n✅ Removed dependency: ${ticketId} → ${selected}`));
|
|
164
|
-
}
|
|
165
|
-
continue;
|
|
166
|
-
}
|
|
167
|
-
// Add dependency
|
|
168
|
-
if (otherTickets.length === 0) {
|
|
169
|
-
this.log(styles.muted('\nNo other tickets to link to.'));
|
|
170
|
-
continue;
|
|
171
|
-
}
|
|
172
|
-
// eslint-disable-next-line no-await-in-loop
|
|
173
|
-
const targetId = await this.selectFromList({
|
|
174
|
-
message: `Select ticket that ${ticketId} ${action === 'blocks' ? 'is blocked by' : action === 'relates_to' ? 'relates to' : 'duplicates'}:`,
|
|
175
|
-
items: otherTickets,
|
|
176
|
-
getName: (t) => `${t.id} - ${t.title}`,
|
|
177
|
-
getValue: (t) => t.id,
|
|
178
|
-
getCommand: (t) => `prlt ticket link ${action === 'blocks' ? 'block' : action} ${ticketId} ${t.id}${projectId ? ` -P ${projectId}` : ''} --json`,
|
|
179
|
-
jsonMode: jsonMode ? { flags, commandName: 'ticket link' } : null,
|
|
180
|
-
});
|
|
181
|
-
if (targetId) {
|
|
182
|
-
// eslint-disable-next-line no-await-in-loop
|
|
183
|
-
await this.addDependency(this.storage, this.pmoPath, ticketId, targetId, action, ticket.title);
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
async addDependency(storage, pmoPath, ticketId, targetId, dependencyType, ticketTitle) {
|
|
188
|
-
const targetTicket = await this.storage.getTicket(targetId);
|
|
189
|
-
if (!targetTicket) {
|
|
190
|
-
this.error(`Ticket not found: ${targetId}`);
|
|
191
|
-
}
|
|
192
|
-
try {
|
|
193
|
-
await this.storage.createTicketDependency(ticketId, targetId, dependencyType);
|
|
194
|
-
await autoExportToBoard(this.pmoPath, this.storage, (msg) => this.log(styles.muted(msg)));
|
|
195
|
-
const typeLabel = dependencyType === 'blocks' ? 'is blocked by' :
|
|
196
|
-
dependencyType === 'relates_to' ? 'relates to' : 'duplicates';
|
|
197
|
-
this.log(styles.success(`\n✅ ${styles.emphasis(ticketId)} ${typeLabel} ${styles.emphasis(targetId)}`));
|
|
198
|
-
this.log(styles.muted(` ${ticketTitle}`));
|
|
199
|
-
this.log(styles.muted(` ${typeLabel} ${targetTicket.title}`));
|
|
200
|
-
}
|
|
201
|
-
catch (error) {
|
|
202
|
-
if (error instanceof Error) {
|
|
203
|
-
if (error.message.includes('already exists')) {
|
|
204
|
-
this.error('Dependency already exists');
|
|
205
|
-
}
|
|
206
|
-
if (error.message.includes('self-dependency')) {
|
|
207
|
-
this.error('Cannot create self-dependency');
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
throw error;
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
async viewDependencies(storage, ticketId, ticket, showAll) {
|
|
214
|
-
const dependencies = await this.storage.listTicketDependencies(ticketId);
|
|
215
|
-
const blockers = await this.storage.getTicketBlockers(ticketId);
|
|
216
|
-
const isBlocked = await this.storage.isTicketBlocked(ticketId);
|
|
217
|
-
this.log(`\n${styles.emphasis(ticket.id)}: ${ticket.title}`);
|
|
218
|
-
if (isBlocked) {
|
|
219
|
-
this.log(styles.warning(' Status: BLOCKED'));
|
|
220
|
-
}
|
|
221
|
-
if (blockers.length > 0) {
|
|
222
|
-
this.log(styles.muted('\n Blocked by:'));
|
|
223
|
-
for (const blocker of blockers) {
|
|
224
|
-
const status = blocker.status === 'done' ? styles.success('done') : styles.warning(blocker.status);
|
|
225
|
-
this.log(` - ${blocker.id}: ${blocker.title} (${status})`);
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
const otherDeps = dependencies.filter(d => d.dependencyType !== 'blocks');
|
|
229
|
-
if (otherDeps.length > 0) {
|
|
230
|
-
this.log(styles.muted('\n Related:'));
|
|
231
|
-
// Fetch all related tickets in parallel
|
|
232
|
-
const relatedTickets = await Promise.all(otherDeps.map(async (dep) => ({
|
|
233
|
-
dep,
|
|
234
|
-
ticket: await this.storage.getTicket(dep.dependsOnTicketId)
|
|
235
|
-
})));
|
|
236
|
-
for (const { dep, ticket: relatedTicket } of relatedTickets) {
|
|
237
|
-
if (relatedTicket) {
|
|
238
|
-
this.log(` - ${dep.dependencyType}: ${relatedTicket.id} - ${relatedTicket.title}`);
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
if (showAll) {
|
|
243
|
-
const blockedBy = await this.storage.getTicketsBlockedBy(ticketId);
|
|
244
|
-
if (blockedBy.length > 0) {
|
|
245
|
-
this.log(styles.muted('\n Blocking:'));
|
|
246
|
-
for (const blocked of blockedBy) {
|
|
247
|
-
this.log(` - ${blocked.id}: ${blocked.title} (${blocked.status})`);
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
if (dependencies.length === 0 && blockers.length === 0) {
|
|
252
|
-
this.log(styles.muted('\n No dependencies.'));
|
|
253
|
-
}
|
|
254
|
-
this.log('');
|
|
255
|
-
}
|
|
256
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { PMOCommand } from '../../../lib/pmo/index.js';
|
|
2
|
-
export default class TicketLinkRelates extends PMOCommand {
|
|
3
|
-
static description: string;
|
|
4
|
-
static examples: string[];
|
|
5
|
-
static args: {
|
|
6
|
-
id: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
7
|
-
target: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
|
|
8
|
-
};
|
|
9
|
-
static flags: {
|
|
10
|
-
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
11
|
-
project: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
-
};
|
|
13
|
-
execute(): Promise<void>;
|
|
14
|
-
}
|
|
@@ -1,95 +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 TicketLinkRelates extends PMOCommand {
|
|
6
|
-
static description = 'Add a relates_to dependency (informational link)';
|
|
7
|
-
static examples = [
|
|
8
|
-
'<%= config.bin %> <%= command.id %> TKT-001 TKT-002 # TKT-001 relates to TKT-002',
|
|
9
|
-
'<%= config.bin %> <%= command.id %> TKT-001 # Interactive selection',
|
|
10
|
-
];
|
|
11
|
-
static args = {
|
|
12
|
-
id: Args.string({
|
|
13
|
-
description: 'Ticket ID',
|
|
14
|
-
required: true,
|
|
15
|
-
}),
|
|
16
|
-
target: Args.string({
|
|
17
|
-
description: 'Related ticket ID',
|
|
18
|
-
required: false,
|
|
19
|
-
}),
|
|
20
|
-
};
|
|
21
|
-
static flags = {
|
|
22
|
-
...pmoBaseFlags,
|
|
23
|
-
json: Flags.boolean({
|
|
24
|
-
char: 'm',
|
|
25
|
-
aliases: ['machine'],
|
|
26
|
-
description: 'Output prompt configuration as JSON (for AI agents/scripts)',
|
|
27
|
-
default: false,
|
|
28
|
-
}),
|
|
29
|
-
};
|
|
30
|
-
async execute() {
|
|
31
|
-
const { args, flags } = await this.parse(TicketLinkRelates);
|
|
32
|
-
// Check if JSON output mode is active
|
|
33
|
-
const jsonMode = shouldOutputJson(flags);
|
|
34
|
-
// Helper to handle errors in JSON mode
|
|
35
|
-
const handleError = (code, message) => {
|
|
36
|
-
if (jsonMode) {
|
|
37
|
-
outputErrorAsJson(code, message, createMetadata('ticket link relates', flags));
|
|
38
|
-
this.exit(1);
|
|
39
|
-
}
|
|
40
|
-
this.error(message);
|
|
41
|
-
};
|
|
42
|
-
const ticket = await this.storage.getTicket(args.id);
|
|
43
|
-
if (!ticket) {
|
|
44
|
-
return handleError('TICKET_NOT_FOUND', `Ticket not found: ${args.id}`);
|
|
45
|
-
}
|
|
46
|
-
let targetId = args.target;
|
|
47
|
-
if (!targetId) {
|
|
48
|
-
const projectId = flags.project;
|
|
49
|
-
const allTickets = await this.storage.listTickets(projectId);
|
|
50
|
-
const otherTickets = allTickets.filter(t => t.id !== args.id);
|
|
51
|
-
if (otherTickets.length === 0) {
|
|
52
|
-
if (jsonMode) {
|
|
53
|
-
outputErrorAsJson('NO_OTHER_TICKETS', 'No other tickets to relate to.', createMetadata('ticket link relates', flags));
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
this.log(styles.muted('\nNo other tickets to relate to.'));
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
const selected = await this.selectFromList({
|
|
60
|
-
message: `Select ticket that ${args.id} relates to:`,
|
|
61
|
-
items: otherTickets,
|
|
62
|
-
getName: (t) => `${t.id} - ${t.title} (${t.statusName || t.status})`,
|
|
63
|
-
getValue: (t) => t.id,
|
|
64
|
-
getCommand: (t) => `prlt ticket link relates ${args.id} ${t.id}${projectId ? ` -P ${projectId}` : ''} --json`,
|
|
65
|
-
jsonMode: jsonMode ? { flags, commandName: 'ticket link relates' } : null,
|
|
66
|
-
});
|
|
67
|
-
if (!selected) {
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
targetId = selected;
|
|
71
|
-
}
|
|
72
|
-
const targetTicket = await this.storage.getTicket(targetId);
|
|
73
|
-
if (!targetTicket) {
|
|
74
|
-
this.error(`Ticket not found: ${targetId}`);
|
|
75
|
-
}
|
|
76
|
-
try {
|
|
77
|
-
await this.storage.createTicketDependency(args.id, targetId, 'relates_to');
|
|
78
|
-
await autoExportToBoard(this.pmoPath, this.storage, (msg) => this.log(styles.muted(msg)));
|
|
79
|
-
this.log(styles.success(`\n✅ ${styles.emphasis(args.id)} relates to ${styles.emphasis(targetId)}`));
|
|
80
|
-
this.log(styles.muted(` ${ticket.title}`));
|
|
81
|
-
this.log(styles.muted(` relates to: ${targetTicket.title}`));
|
|
82
|
-
}
|
|
83
|
-
catch (error) {
|
|
84
|
-
if (error instanceof Error) {
|
|
85
|
-
if (error.message.includes('already exists')) {
|
|
86
|
-
this.error('Dependency already exists');
|
|
87
|
-
}
|
|
88
|
-
if (error.message.includes('self-dependency')) {
|
|
89
|
-
this.error('Cannot create self-dependency');
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
throw error;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { PMOCommand } from '../../../lib/pmo/index.js';
|
|
2
|
-
export default class TicketLinkRemove extends PMOCommand {
|
|
3
|
-
static description: string;
|
|
4
|
-
static examples: string[];
|
|
5
|
-
static args: {
|
|
6
|
-
id: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
7
|
-
target: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
|
|
8
|
-
};
|
|
9
|
-
static flags: {
|
|
10
|
-
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
11
|
-
type: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
-
all: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
13
|
-
project: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
|
-
};
|
|
15
|
-
execute(): Promise<void>;
|
|
16
|
-
}
|