@proletariat/cli 0.3.34 → 0.3.35

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. package/dist/commands/agent/auth.d.ts +3 -1
  2. package/dist/commands/agent/auth.js +8 -11
  3. package/dist/commands/agent/index.js +11 -2
  4. package/dist/commands/agent/staff/add.d.ts +1 -0
  5. package/dist/commands/agent/staff/add.js +1 -0
  6. package/dist/commands/agent/staff/index.d.ts +15 -0
  7. package/dist/commands/agent/staff/index.js +83 -0
  8. package/dist/commands/agent/staff/list.d.ts +1 -0
  9. package/dist/commands/agent/staff/list.js +1 -0
  10. package/dist/commands/agent/staff/remove.d.ts +1 -0
  11. package/dist/commands/agent/staff/remove.js +1 -0
  12. package/dist/commands/agent/themes/add-names.d.ts +1 -0
  13. package/dist/commands/agent/themes/add-names.js +1 -0
  14. package/dist/commands/agent/themes/create.d.ts +1 -0
  15. package/dist/commands/agent/themes/create.js +1 -0
  16. package/dist/commands/agent/themes/index.d.ts +10 -0
  17. package/dist/commands/agent/themes/index.js +144 -0
  18. package/dist/commands/agent/themes/list.d.ts +1 -0
  19. package/dist/commands/agent/themes/list.js +1 -0
  20. package/dist/commands/agent/themes/set.d.ts +1 -0
  21. package/dist/commands/agent/themes/set.js +1 -0
  22. package/dist/commands/agents/themes/add-names.d.ts +1 -0
  23. package/dist/commands/agents/themes/add-names.js +1 -0
  24. package/dist/commands/agents/themes/create.d.ts +1 -0
  25. package/dist/commands/agents/themes/create.js +1 -0
  26. package/dist/commands/agents/themes/list.d.ts +1 -0
  27. package/dist/commands/agents/themes/list.js +1 -0
  28. package/dist/commands/category/list.js +1 -1
  29. package/dist/commands/label/create.d.ts +20 -0
  30. package/dist/commands/label/create.js +56 -0
  31. package/dist/commands/label/delete.d.ts +17 -0
  32. package/dist/commands/label/delete.js +31 -0
  33. package/dist/commands/label/group/create.d.ts +20 -0
  34. package/dist/commands/label/group/create.js +54 -0
  35. package/dist/commands/label/group/list.d.ts +14 -0
  36. package/dist/commands/label/group/list.js +51 -0
  37. package/dist/commands/label/index.d.ts +15 -0
  38. package/dist/commands/label/index.js +58 -0
  39. package/dist/commands/label/list.d.ts +16 -0
  40. package/dist/commands/label/list.js +82 -0
  41. package/dist/commands/link/list.js +3 -2
  42. package/dist/commands/mcp-server.js +2 -1
  43. package/dist/commands/phase/template/apply.d.ts +26 -0
  44. package/dist/commands/phase/template/apply.js +14 -0
  45. package/dist/commands/phase/template/create.d.ts +23 -0
  46. package/dist/commands/phase/template/create.js +14 -0
  47. package/dist/commands/phase/template/delete.d.ts +18 -0
  48. package/dist/commands/phase/template/delete.js +61 -0
  49. package/dist/commands/phase/template/list.d.ts +17 -0
  50. package/dist/commands/phase/template/list.js +88 -0
  51. package/dist/commands/phase/template/update.d.ts +1 -0
  52. package/dist/commands/phase/template/update.js +1 -0
  53. package/dist/commands/priority/add.js +1 -1
  54. package/dist/commands/project/update.js +0 -2
  55. package/dist/commands/roadmap/generate.js +1 -2
  56. package/dist/commands/session/health.js +1 -1
  57. package/dist/commands/spec/link/depends.d.ts +18 -0
  58. package/dist/commands/spec/link/depends.js +86 -0
  59. package/dist/commands/spec/link/index.d.ts +17 -0
  60. package/dist/commands/spec/link/index.js +92 -0
  61. package/dist/commands/spec/link/remove.d.ts +18 -0
  62. package/dist/commands/spec/link/remove.js +90 -0
  63. package/dist/commands/support/logs.js +2 -2
  64. package/dist/commands/template/apply.js +5 -4
  65. package/dist/commands/template/create.js +1 -1
  66. package/dist/commands/ticket/link/block.d.ts +15 -0
  67. package/dist/commands/ticket/link/block.js +95 -0
  68. package/dist/commands/ticket/link/index.d.ts +14 -0
  69. package/dist/commands/ticket/link/index.js +96 -0
  70. package/dist/commands/ticket/list.d.ts +1 -0
  71. package/dist/commands/ticket/list.js +6 -0
  72. package/dist/commands/ticket/resolve.js +1 -1
  73. package/dist/commands/ticket/template/apply.d.ts +26 -0
  74. package/dist/commands/ticket/template/apply.js +14 -0
  75. package/dist/commands/ticket/template/delete.d.ts +18 -0
  76. package/dist/commands/ticket/template/delete.js +61 -0
  77. package/dist/commands/ticket/template/list.d.ts +17 -0
  78. package/dist/commands/ticket/template/list.js +77 -0
  79. package/dist/commands/ticket/template/save.d.ts +17 -0
  80. package/dist/commands/ticket/template/save.js +97 -0
  81. package/dist/commands/ticket/view.d.ts +1 -0
  82. package/dist/commands/ticket/view.js +1 -0
  83. package/dist/commands/work/ready.js +17 -0
  84. package/dist/commands/work/resolve.js +1 -1
  85. package/dist/commands/work/spawn.js +4 -4
  86. package/dist/commands/work/start.d.ts +1 -0
  87. package/dist/commands/work/start.js +52 -17
  88. package/dist/lib/database/index.d.ts +1 -1
  89. package/dist/lib/database/index.js +20 -0
  90. package/dist/lib/execution/devcontainer.js +3 -1
  91. package/dist/lib/execution/runners.d.ts +7 -2
  92. package/dist/lib/execution/runners.js +18 -10
  93. package/dist/lib/execution/types.d.ts +1 -0
  94. package/dist/lib/flags/resolver.js +1 -0
  95. package/dist/lib/mcp/helpers.d.ts +1 -2
  96. package/dist/lib/mcp/tools/diet.js +1 -0
  97. package/dist/lib/mcp/tools/index.d.ts +1 -0
  98. package/dist/lib/mcp/tools/index.js +1 -0
  99. package/dist/lib/mcp/tools/label.d.ts +6 -0
  100. package/dist/lib/mcp/tools/label.js +338 -0
  101. package/dist/lib/mcp/tools/ticket.js +53 -17
  102. package/dist/lib/multiline-input.js +6 -18
  103. package/dist/lib/pmo/base-command.d.ts +0 -1
  104. package/dist/lib/pmo/base-command.js +0 -1
  105. package/dist/lib/pmo/schema.d.ts +6 -0
  106. package/dist/lib/pmo/schema.js +44 -0
  107. package/dist/lib/pmo/storage/base.d.ts +6 -0
  108. package/dist/lib/pmo/storage/base.js +116 -2
  109. package/dist/lib/pmo/storage/index.d.ts +23 -1
  110. package/dist/lib/pmo/storage/index.js +59 -1
  111. package/dist/lib/pmo/storage/labels.d.ts +55 -0
  112. package/dist/lib/pmo/storage/labels.js +346 -0
  113. package/dist/lib/pmo/storage/tickets.js +17 -0
  114. package/dist/lib/pmo/storage/types.d.ts +24 -0
  115. package/dist/lib/pmo/types.d.ts +44 -0
  116. package/dist/lib/pmo/utils.js +1 -1
  117. package/oclif.manifest.json +5702 -3660
  118. package/package.json +1 -1
@@ -0,0 +1,86 @@
1
+ import { Args } from '@oclif/core';
2
+ import { PMOCommand, pmoBaseFlags } from '../../../lib/pmo/index.js';
3
+ import { styles } from '../../../lib/styles.js';
4
+ import { shouldOutputJson, outputPromptAsJson, outputErrorAsJson, createMetadata, buildPromptConfig, } from '../../../lib/prompt-json.js';
5
+ export default class SpecLinkDepends extends PMOCommand {
6
+ static description = 'Add a dependency between specs';
7
+ static examples = [
8
+ '<%= config.bin %> <%= command.id %> SPEC-001 SPEC-002',
9
+ '<%= config.bin %> <%= command.id %> SPEC-001 --machine',
10
+ ];
11
+ static args = {
12
+ spec: Args.string({
13
+ description: 'Source spec ID (the one that depends on another)',
14
+ required: true,
15
+ }),
16
+ target: Args.string({
17
+ description: 'Target spec ID (the one depended upon)',
18
+ required: false,
19
+ }),
20
+ };
21
+ static flags = {
22
+ ...pmoBaseFlags,
23
+ };
24
+ getPMOOptions() {
25
+ return { promptIfMultiple: false };
26
+ }
27
+ async execute() {
28
+ const { args, flags } = await this.parse(SpecLinkDepends);
29
+ const jsonMode = shouldOutputJson(flags);
30
+ const handleError = (code, message) => {
31
+ if (jsonMode) {
32
+ outputErrorAsJson(code, message, createMetadata('spec link depends', flags));
33
+ this.exit(1);
34
+ }
35
+ this.error(message);
36
+ };
37
+ // Verify source spec exists
38
+ const sourceSpec = await this.storage.getSpec(args.spec);
39
+ if (!sourceSpec) {
40
+ return handleError('SPEC_NOT_FOUND', `Spec not found: ${args.spec}`);
41
+ }
42
+ // If target not provided, prompt for selection
43
+ if (!args.target) {
44
+ const specs = await this.storage.listSpecs();
45
+ const otherSpecs = specs.filter(s => s.id !== args.spec);
46
+ if (otherSpecs.length === 0) {
47
+ return handleError('NO_SPECS', 'No other specs to select as dependency.');
48
+ }
49
+ const choices = otherSpecs.map(s => ({
50
+ name: `${s.id} - ${s.title}`,
51
+ value: s.id,
52
+ command: `prlt spec link depends ${args.spec} ${s.id} --json`,
53
+ }));
54
+ const message = `Select spec that ${args.spec} depends on:`;
55
+ if (jsonMode) {
56
+ outputPromptAsJson(buildPromptConfig('list', 'target', message, choices), createMetadata('spec link depends', flags));
57
+ return;
58
+ }
59
+ const { selected } = await this.prompt([{
60
+ type: 'list',
61
+ name: 'selected',
62
+ message,
63
+ choices,
64
+ }], null);
65
+ args.target = selected;
66
+ }
67
+ // Verify target spec exists
68
+ const targetSpec = await this.storage.getSpec(args.target);
69
+ if (!targetSpec) {
70
+ return handleError('TARGET_NOT_FOUND', `Spec not found: ${args.target}`);
71
+ }
72
+ // Create the dependency
73
+ try {
74
+ await this.storage.createSpecDependency(args.spec, args.target, 'depends_on');
75
+ this.log(styles.success(`\n${args.spec} now depends on ${args.target}`));
76
+ this.log(styles.muted(` ${sourceSpec.title}`));
77
+ this.log(styles.muted(` depends on: ${targetSpec.title}`));
78
+ }
79
+ catch (error) {
80
+ if (error instanceof Error && error.message.includes('already exists')) {
81
+ return handleError('ALREADY_EXISTS', 'Dependency already exists.');
82
+ }
83
+ throw error;
84
+ }
85
+ }
86
+ }
@@ -0,0 +1,17 @@
1
+ import { PMOCommand } from '../../../lib/pmo/index.js';
2
+ export default class SpecLink extends PMOCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static args: {
6
+ spec: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
7
+ };
8
+ static flags: {
9
+ json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
+ machine: import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
+ project: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
+ };
13
+ protected getPMOOptions(): {
14
+ promptIfMultiple: boolean;
15
+ };
16
+ execute(): Promise<void>;
17
+ }
@@ -0,0 +1,92 @@
1
+ import { Args } from '@oclif/core';
2
+ import { PMOCommand, pmoBaseFlags } from '../../../lib/pmo/index.js';
3
+ import { styles } from '../../../lib/styles.js';
4
+ import { shouldOutputJson, outputPromptAsJson, createMetadata, buildPromptConfig, } from '../../../lib/prompt-json.js';
5
+ export default class SpecLink extends PMOCommand {
6
+ static description = 'Manage links (dependencies) for a spec';
7
+ static examples = [
8
+ '<%= config.bin %> <%= command.id %>',
9
+ '<%= config.bin %> <%= command.id %> SPEC-001',
10
+ '<%= config.bin %> <%= command.id %> --machine',
11
+ ];
12
+ static args = {
13
+ spec: Args.string({
14
+ description: 'Spec ID',
15
+ required: false,
16
+ }),
17
+ };
18
+ static flags = {
19
+ ...pmoBaseFlags,
20
+ };
21
+ getPMOOptions() {
22
+ return { promptIfMultiple: false };
23
+ }
24
+ async execute() {
25
+ const { args, flags } = await this.parse(SpecLink);
26
+ const jsonMode = shouldOutputJson(flags);
27
+ // If no spec ID provided, prompt for selection
28
+ if (!args.spec) {
29
+ const specs = await this.storage.listSpecs();
30
+ if (specs.length === 0) {
31
+ this.log(styles.muted('No specs found.'));
32
+ return;
33
+ }
34
+ const choices = specs.map(s => ({
35
+ name: `${s.id} - ${s.title}`,
36
+ value: s.id,
37
+ command: `prlt spec link ${s.id} --machine`,
38
+ }));
39
+ const message = 'Select a spec to manage links:';
40
+ if (jsonMode) {
41
+ outputPromptAsJson(buildPromptConfig('list', 'id', message, choices), createMetadata('spec link', flags));
42
+ return;
43
+ }
44
+ const { selected } = await this.prompt([{
45
+ type: 'list',
46
+ name: 'selected',
47
+ message,
48
+ choices,
49
+ }], null);
50
+ args.spec = selected;
51
+ }
52
+ const spec = await this.storage.getSpec(args.spec);
53
+ if (!spec) {
54
+ this.error(`Spec not found: ${args.spec}`);
55
+ }
56
+ // Show link actions submenu
57
+ const menuChoices = [
58
+ { name: 'Add dependency', value: 'depends', command: `prlt spec link depends ${args.spec} --machine` },
59
+ { name: 'Remove dependency', value: 'remove', command: `prlt spec link remove ${args.spec} --machine` },
60
+ { name: 'Cancel', value: 'cancel', command: '' },
61
+ ];
62
+ const message = `Manage links for ${args.spec}: ${spec.title}`;
63
+ if (jsonMode) {
64
+ outputPromptAsJson(buildPromptConfig('list', 'action', message, menuChoices), createMetadata('spec link', flags));
65
+ return;
66
+ }
67
+ const { action } = await this.prompt([{
68
+ type: 'list',
69
+ name: 'action',
70
+ message,
71
+ choices: menuChoices,
72
+ }], null);
73
+ if (action === 'cancel') {
74
+ this.log(styles.muted('Cancelled.'));
75
+ return;
76
+ }
77
+ switch (action) {
78
+ case 'depends': {
79
+ const { default: DependsCommand } = await import('./depends.js');
80
+ const cmd = new DependsCommand([args.spec], this.config);
81
+ await cmd.run();
82
+ break;
83
+ }
84
+ case 'remove': {
85
+ const { default: RemoveCommand } = await import('./remove.js');
86
+ const cmd = new RemoveCommand([args.spec], this.config);
87
+ await cmd.run();
88
+ break;
89
+ }
90
+ }
91
+ }
92
+ }
@@ -0,0 +1,18 @@
1
+ import { PMOCommand } from '../../../lib/pmo/index.js';
2
+ export default class SpecLinkRemove extends PMOCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static args: {
6
+ spec: 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
+ machine: import("@oclif/core/interfaces").BooleanFlag<boolean>;
12
+ project: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
+ };
14
+ protected getPMOOptions(): {
15
+ promptIfMultiple: boolean;
16
+ };
17
+ execute(): Promise<void>;
18
+ }
@@ -0,0 +1,90 @@
1
+ import { Args } from '@oclif/core';
2
+ import { PMOCommand, pmoBaseFlags } from '../../../lib/pmo/index.js';
3
+ import { styles } from '../../../lib/styles.js';
4
+ import { shouldOutputJson, outputPromptAsJson, outputErrorAsJson, createMetadata, buildPromptConfig, } from '../../../lib/prompt-json.js';
5
+ export default class SpecLinkRemove extends PMOCommand {
6
+ static description = 'Remove a dependency between specs';
7
+ static examples = [
8
+ '<%= config.bin %> <%= command.id %> SPEC-001 SPEC-002',
9
+ '<%= config.bin %> <%= command.id %> SPEC-001 --machine',
10
+ ];
11
+ static args = {
12
+ spec: Args.string({
13
+ description: 'Source spec ID',
14
+ required: true,
15
+ }),
16
+ target: Args.string({
17
+ description: 'Target spec ID to remove dependency from',
18
+ required: false,
19
+ }),
20
+ };
21
+ static flags = {
22
+ ...pmoBaseFlags,
23
+ };
24
+ getPMOOptions() {
25
+ return { promptIfMultiple: false };
26
+ }
27
+ async execute() {
28
+ const { args, flags } = await this.parse(SpecLinkRemove);
29
+ const jsonMode = shouldOutputJson(flags);
30
+ const handleError = (code, message) => {
31
+ if (jsonMode) {
32
+ outputErrorAsJson(code, message, createMetadata('spec link remove', flags));
33
+ this.exit(1);
34
+ }
35
+ this.error(message);
36
+ };
37
+ // Verify source spec exists
38
+ const sourceSpec = await this.storage.getSpec(args.spec);
39
+ if (!sourceSpec) {
40
+ return handleError('SPEC_NOT_FOUND', `Spec not found: ${args.spec}`);
41
+ }
42
+ // If target not provided, show existing dependencies for selection
43
+ if (!args.target) {
44
+ const dependencies = await this.storage.listSpecDependencies(args.spec);
45
+ if (dependencies.length === 0) {
46
+ if (jsonMode) {
47
+ outputErrorAsJson('NO_DEPENDENCIES', `No dependencies found for ${args.spec}.`, createMetadata('spec link remove', flags));
48
+ return;
49
+ }
50
+ this.log(styles.muted(`No dependencies found for ${args.spec}.`));
51
+ return;
52
+ }
53
+ // Get spec details for dependency targets
54
+ const choices = [];
55
+ for (const dep of dependencies) {
56
+ // eslint-disable-next-line no-await-in-loop
57
+ const targetSpec = await this.storage.getSpec(dep.dependsOnSpecId);
58
+ const name = targetSpec ? `${dep.dependsOnSpecId} - ${targetSpec.title} (${dep.dependencyType})` : `${dep.dependsOnSpecId} (${dep.dependencyType})`;
59
+ choices.push({
60
+ name,
61
+ value: dep.dependsOnSpecId,
62
+ command: `prlt spec link remove ${args.spec} ${dep.dependsOnSpecId} --json`,
63
+ });
64
+ }
65
+ const message = `Select dependency to remove from ${args.spec}:`;
66
+ if (jsonMode) {
67
+ outputPromptAsJson(buildPromptConfig('list', 'target', message, choices), createMetadata('spec link remove', flags));
68
+ return;
69
+ }
70
+ const { selected } = await this.prompt([{
71
+ type: 'list',
72
+ name: 'selected',
73
+ message,
74
+ choices,
75
+ }], null);
76
+ args.target = selected;
77
+ }
78
+ // Remove the dependency
79
+ try {
80
+ await this.storage.deleteSpecDependency(args.spec, args.target);
81
+ this.log(styles.success(`\nRemoved dependency: ${args.spec} no longer depends on ${args.target}`));
82
+ }
83
+ catch (error) {
84
+ if (error instanceof Error && error.message.includes('not found')) {
85
+ return handleError('NOT_FOUND', `Dependency not found between ${args.spec} and ${args.target}`);
86
+ }
87
+ throw error;
88
+ }
89
+ }
90
+ }
@@ -210,8 +210,8 @@ export default class SupportLogs extends PMOCommand {
210
210
  copyToClipboard(text) {
211
211
  const platform = process.platform;
212
212
  // Strip ANSI codes for clipboard
213
- // biome-ignore lint/suspicious/noControlCharactersInRegex: intentional ANSI escape code stripping
214
- const plainText = text.replace(/\x1b\[[0-9;]*m/g, '');
213
+ // eslint-disable-next-line no-control-regex -- intentional ANSI escape code stripping
214
+ const plainText = text.replace(/\u001B\[[0-9;]*m/g, '');
215
215
  try {
216
216
  if (platform === 'darwin') {
217
217
  execSync('pbcopy', { input: plainText, encoding: 'utf-8' });
@@ -135,10 +135,10 @@ export default class TemplateApply extends PMOCommand {
135
135
  let title = flags.title || template.titlePattern || '';
136
136
  let column = flags.column || columns[0];
137
137
  let priority = flags.priority || template.defaultPriority;
138
- let category = flags.category || template.defaultCategory;
139
- let assignee = flags.assignee || template.defaultAssignee;
140
- let owner = flags.owner || template.defaultOwner;
141
- let description = flags.description || template.descriptionTemplate;
138
+ const category = flags.category || template.defaultCategory;
139
+ const assignee = flags.assignee || template.defaultAssignee;
140
+ const owner = flags.owner || template.defaultOwner;
141
+ const description = flags.description || template.descriptionTemplate;
142
142
  const labels = template.defaultLabels;
143
143
  // Interactive mode
144
144
  if (flags.interactive || !title) {
@@ -182,6 +182,7 @@ export default class TemplateApply extends PMOCommand {
182
182
  // Add subtasks
183
183
  if (!flags['no-subtasks'] && template.suggestedSubtasks.length > 0) {
184
184
  for (const subtask of template.suggestedSubtasks) {
185
+ // eslint-disable-next-line no-await-in-loop
185
186
  await this.storage.addSubtask(ticket.id, subtask.title);
186
187
  }
187
188
  }
@@ -135,7 +135,7 @@ export default class TemplateCreate extends PMOCommand {
135
135
  }
136
136
  // Get optional values
137
137
  let description = flags.description;
138
- let titlePattern = flags['title-pattern'];
138
+ const titlePattern = flags['title-pattern'];
139
139
  let priority = flags.priority;
140
140
  let category = flags.category;
141
141
  const subtasks = flags.subtask || [];
@@ -0,0 +1,15 @@
1
+ import { PMOCommand } from '../../../lib/pmo/index.js';
2
+ export default class TicketLinkBlock extends PMOCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static args: {
6
+ ticket: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
7
+ blocker: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
8
+ };
9
+ static flags: {
10
+ json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
+ machine: import("@oclif/core/interfaces").BooleanFlag<boolean>;
12
+ project: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
+ };
14
+ execute(): Promise<void>;
15
+ }
@@ -0,0 +1,95 @@
1
+ import { Args } from '@oclif/core';
2
+ import { autoExportToBoard, PMOCommand, pmoBaseFlags } from '../../../lib/pmo/index.js';
3
+ import { styles } from '../../../lib/styles.js';
4
+ import { shouldOutputJson, outputPromptAsJson, outputSuccessAsJson, outputErrorAsJson, createMetadata, buildPromptConfig, } from '../../../lib/prompt-json.js';
5
+ export default class TicketLinkBlock extends PMOCommand {
6
+ static description = 'Add a blocking dependency to a ticket';
7
+ static examples = [
8
+ '<%= config.bin %> <%= command.id %> TKT-001',
9
+ '<%= config.bin %> <%= command.id %> TKT-001 TKT-002',
10
+ '<%= config.bin %> <%= command.id %> TKT-001 --json',
11
+ ];
12
+ static args = {
13
+ ticket: Args.string({
14
+ description: 'Ticket that will be blocked',
15
+ required: true,
16
+ }),
17
+ blocker: Args.string({
18
+ description: 'Ticket that blocks (the blocker)',
19
+ required: false,
20
+ }),
21
+ };
22
+ static flags = {
23
+ ...pmoBaseFlags,
24
+ };
25
+ async execute() {
26
+ const { args, flags } = await this.parse(TicketLinkBlock);
27
+ const jsonMode = shouldOutputJson(flags);
28
+ const projectId = await this.requireProject();
29
+ const handleError = (code, message) => {
30
+ if (jsonMode) {
31
+ outputErrorAsJson(code, message, createMetadata('ticket link block', flags));
32
+ this.exit(1);
33
+ }
34
+ this.error(message);
35
+ };
36
+ // Verify the target ticket exists
37
+ const ticket = await this.storage.getTicket(args.ticket);
38
+ if (!ticket) {
39
+ return handleError('TICKET_NOT_FOUND', `Ticket not found: ${args.ticket}`);
40
+ }
41
+ // If blocker not provided, prompt for selection
42
+ if (!args.blocker) {
43
+ const tickets = await this.storage.listTickets(projectId);
44
+ const otherTickets = tickets.filter(t => t.id !== args.ticket);
45
+ if (otherTickets.length === 0) {
46
+ return handleError('NO_TICKETS', 'No other tickets to select as blocker.');
47
+ }
48
+ const projectFlag = flags.project ? ` -P ${flags.project}` : '';
49
+ const choices = otherTickets.map(t => ({
50
+ name: `${t.id} - ${t.title}`,
51
+ value: t.id,
52
+ command: `prlt ticket link block ${args.ticket} ${t.id}${projectFlag} --json`,
53
+ }));
54
+ const message = `Select ticket that blocks ${args.ticket}:`;
55
+ if (jsonMode) {
56
+ outputPromptAsJson(buildPromptConfig('list', 'blocker', message, choices), createMetadata('ticket link block', flags));
57
+ return;
58
+ }
59
+ const { selected } = await this.prompt([{
60
+ type: 'list',
61
+ name: 'selected',
62
+ message,
63
+ choices,
64
+ }], null);
65
+ args.blocker = selected;
66
+ }
67
+ // Verify blocker exists
68
+ const blockerTicket = await this.storage.getTicket(args.blocker);
69
+ if (!blockerTicket) {
70
+ return handleError('BLOCKER_NOT_FOUND', `Blocker ticket not found: ${args.blocker}`);
71
+ }
72
+ // Create the blocking dependency
73
+ try {
74
+ await this.storage.createTicketDependency(args.ticket, args.blocker, 'blocks');
75
+ await autoExportToBoard(this.pmoPath, this.storage, (msg) => this.log(styles.muted(msg)));
76
+ if (jsonMode) {
77
+ outputSuccessAsJson({
78
+ ticketId: args.ticket,
79
+ blockerId: args.blocker,
80
+ type: 'blocks',
81
+ }, createMetadata('ticket link block', flags));
82
+ return;
83
+ }
84
+ this.log(styles.success(`\n${args.ticket} is now blocked by ${args.blocker}`));
85
+ this.log(styles.muted(` ${ticket.title}`));
86
+ this.log(styles.muted(` blocked by: ${blockerTicket.title}`));
87
+ }
88
+ catch (error) {
89
+ if (error instanceof Error && error.message.includes('already exists')) {
90
+ return handleError('ALREADY_EXISTS', 'Blocking dependency already exists.');
91
+ }
92
+ throw error;
93
+ }
94
+ }
95
+ }
@@ -0,0 +1,14 @@
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
+ ticket: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
7
+ };
8
+ static flags: {
9
+ json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
+ machine: 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
+ }
@@ -0,0 +1,96 @@
1
+ import { Args } from '@oclif/core';
2
+ import { PMOCommand, pmoBaseFlags } from '../../../lib/pmo/index.js';
3
+ import { styles } from '../../../lib/styles.js';
4
+ import { shouldOutputJson, outputPromptAsJson, createMetadata, buildPromptConfig, } from '../../../lib/prompt-json.js';
5
+ export default class TicketLink extends PMOCommand {
6
+ static description = 'Manage links (dependencies) for a ticket';
7
+ static examples = [
8
+ '<%= config.bin %> <%= command.id %>',
9
+ '<%= config.bin %> <%= command.id %> TKT-001',
10
+ '<%= config.bin %> <%= command.id %> TKT-001 --json',
11
+ ];
12
+ static args = {
13
+ ticket: Args.string({
14
+ description: 'Ticket ID',
15
+ required: false,
16
+ }),
17
+ };
18
+ static flags = {
19
+ ...pmoBaseFlags,
20
+ };
21
+ async execute() {
22
+ const { args, flags } = await this.parse(TicketLink);
23
+ const jsonMode = shouldOutputJson(flags);
24
+ const projectId = await this.requireProject();
25
+ // If no ticket ID provided, prompt for selection
26
+ if (!args.ticket) {
27
+ const tickets = await this.storage.listTickets(projectId);
28
+ if (tickets.length === 0) {
29
+ this.log(styles.muted('No tickets found.'));
30
+ return;
31
+ }
32
+ const choices = tickets.map(t => ({
33
+ name: `${t.id} - ${t.title}`,
34
+ value: t.id,
35
+ command: `prlt ticket link ${t.id}${flags.project ? ` -P ${flags.project}` : ''} --json`,
36
+ }));
37
+ const message = 'Select a ticket to manage links:';
38
+ if (jsonMode) {
39
+ outputPromptAsJson(buildPromptConfig('list', 'ticket', message, choices), createMetadata('ticket link', flags));
40
+ return;
41
+ }
42
+ const { selected } = await this.prompt([{
43
+ type: 'list',
44
+ name: 'selected',
45
+ message,
46
+ choices,
47
+ }], null);
48
+ args.ticket = selected;
49
+ }
50
+ const ticket = await this.storage.getTicket(args.ticket);
51
+ if (!ticket) {
52
+ this.error(`Ticket not found: ${args.ticket}`);
53
+ }
54
+ // Show link actions submenu
55
+ const projectFlag = flags.project ? ` -P ${flags.project}` : '';
56
+ const menuChoices = [
57
+ { name: 'Add blocker', value: 'block', command: `prlt ticket link block ${args.ticket}${projectFlag} --json` },
58
+ { name: 'View links', value: 'view', command: `prlt link list ${args.ticket}${projectFlag} --json` },
59
+ { name: 'Remove link', value: 'remove', command: `prlt link remove ${args.ticket}${projectFlag} --json` },
60
+ { name: 'Cancel', value: 'cancel', command: '' },
61
+ ];
62
+ const message = `Manage links for ${args.ticket}: ${ticket.title}`;
63
+ if (jsonMode) {
64
+ outputPromptAsJson(buildPromptConfig('list', 'action', message, menuChoices), createMetadata('ticket link', flags));
65
+ return;
66
+ }
67
+ const { action } = await this.prompt([{
68
+ type: 'list',
69
+ name: 'action',
70
+ message,
71
+ choices: menuChoices,
72
+ }], null);
73
+ if (action === 'cancel') {
74
+ this.log(styles.muted('Cancelled.'));
75
+ return;
76
+ }
77
+ switch (action) {
78
+ case 'block': {
79
+ const { default: BlockCommand } = await import('./block.js');
80
+ const cmd = new BlockCommand([args.ticket, ...(flags.project ? ['-P', flags.project] : [])], this.config);
81
+ await cmd.run();
82
+ break;
83
+ }
84
+ case 'view': {
85
+ const { default: LinkListCommand } = await import('../../link/list.js');
86
+ const cmd = new LinkListCommand([args.ticket, ...(flags.project ? ['-P', flags.project] : [])], this.config);
87
+ await cmd.run();
88
+ break;
89
+ }
90
+ case 'remove': {
91
+ this.log(styles.muted('Use: prlt link remove <from> <to>'));
92
+ break;
93
+ }
94
+ }
95
+ }
96
+ }
@@ -11,6 +11,7 @@ export default class TicketList extends Command {
11
11
  search: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
12
  format: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
13
13
  all: import("@oclif/core/interfaces").BooleanFlag<boolean>;
14
+ label: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
14
15
  'group-by': import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
15
16
  limit: import("@oclif/core/interfaces").OptionFlag<number | undefined, import("@oclif/core/interfaces").CustomOptions>;
16
17
  offset: import("@oclif/core/interfaces").OptionFlag<number | undefined, import("@oclif/core/interfaces").CustomOptions>;
@@ -55,6 +55,9 @@ export default class TicketList extends Command {
55
55
  description: 'Show tickets across all projects',
56
56
  default: false,
57
57
  }),
58
+ label: Flags.string({
59
+ description: 'Filter by label name',
60
+ }),
58
61
  'group-by': Flags.string({
59
62
  char: 'g',
60
63
  description: 'Group tickets by field',
@@ -106,6 +109,9 @@ export default class TicketList extends Command {
106
109
  if (flags.search) {
107
110
  filter.search = flags.search;
108
111
  }
112
+ if (flags.label) {
113
+ filter.label = flags.label;
114
+ }
109
115
  // Determine projectId for the query
110
116
  const projectId = flags.all ? undefined : (filter.projectId || undefined);
111
117
  // Validate project if specified (not in --all mode)
@@ -137,7 +137,7 @@ export default class TicketResolve extends PMOCommand {
137
137
  }),
138
138
  };
139
139
  async execute() {
140
- const { args, flags, argv } = await this.parse(TicketResolve);
140
+ const { flags, argv } = await this.parse(TicketResolve);
141
141
  const projectId = flags.project;
142
142
  const jsonMode = shouldOutputJson(flags);
143
143
  const handleError = (code, message) => {
@@ -0,0 +1,26 @@
1
+ import TemplateApply from '../../template/apply.js';
2
+ export default class TicketTemplateApply extends TemplateApply {
3
+ static description: string;
4
+ static args: {
5
+ template: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
6
+ };
7
+ static flags: {
8
+ type: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
+ title: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
+ column: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
+ priority: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
+ category: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
+ assignee: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
14
+ owner: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
15
+ description: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
16
+ epic: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
17
+ 'no-subtasks': import("@oclif/core/interfaces").BooleanFlag<boolean>;
18
+ interactive: import("@oclif/core/interfaces").BooleanFlag<boolean>;
19
+ force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
20
+ json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
21
+ machine: import("@oclif/core/interfaces").BooleanFlag<boolean>;
22
+ project: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
23
+ };
24
+ static examples: string[];
25
+ run(): Promise<void>;
26
+ }
@@ -0,0 +1,14 @@
1
+ import TemplateApply from '../../template/apply.js';
2
+ export default class TicketTemplateApply extends TemplateApply {
3
+ static description = 'Apply a ticket template to create a new ticket';
4
+ static args = TemplateApply.args;
5
+ static flags = TemplateApply.flags;
6
+ static examples = TemplateApply.examples;
7
+ async run() {
8
+ const argv = this.argv;
9
+ if (!argv.includes('--type') && !argv.includes('-t')) {
10
+ argv.push('--type', 'ticket');
11
+ }
12
+ return super.run();
13
+ }
14
+ }