@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,11 +1,11 @@
|
|
|
1
1
|
import { Command, Flags } from '@oclif/core';
|
|
2
2
|
import inquirer from 'inquirer';
|
|
3
3
|
import chalk from 'chalk';
|
|
4
|
-
import { getWorkspaceInfo } from '
|
|
5
|
-
import { ensureBuiltinThemes } from '
|
|
6
|
-
import { getThemes, getAvailableThemeNames } from '
|
|
7
|
-
import { shouldOutputJson, outputPromptAsJson, createMetadata, buildPromptConfig, } from '
|
|
8
|
-
export default class
|
|
4
|
+
import { getWorkspaceInfo } from '../../lib/agents/commands.js';
|
|
5
|
+
import { ensureBuiltinThemes } from '../../lib/themes.js';
|
|
6
|
+
import { getThemes, getAvailableThemeNames } from '../../lib/database/index.js';
|
|
7
|
+
import { shouldOutputJson, outputPromptAsJson, createMetadata, buildPromptConfig, } from '../../lib/prompt-json.js';
|
|
8
|
+
export default class Theme extends Command {
|
|
9
9
|
static description = 'Manage agent naming themes';
|
|
10
10
|
static examples = [
|
|
11
11
|
'<%= config.bin %> <%= command.id %> list',
|
|
@@ -21,15 +21,15 @@ export default class Themes extends Command {
|
|
|
21
21
|
}),
|
|
22
22
|
};
|
|
23
23
|
async run() {
|
|
24
|
-
const { flags } = await this.parse(
|
|
24
|
+
const { flags } = await this.parse(Theme);
|
|
25
25
|
// Check if JSON output mode is active
|
|
26
26
|
const jsonMode = shouldOutputJson(flags);
|
|
27
27
|
// Define choices once, use for both JSON and interactive modes
|
|
28
28
|
// Each choice includes the full command for AI agents to execute
|
|
29
29
|
const menuChoices = [
|
|
30
|
-
{ id: 'list', name: 'List themes', command: 'prlt
|
|
31
|
-
{ id: 'create', name: 'Create a new theme', command: 'prlt
|
|
32
|
-
{ id: 'add-names', name: 'Add names to a theme', command: 'prlt
|
|
30
|
+
{ id: 'list', name: 'List themes', command: 'prlt theme list --format json' },
|
|
31
|
+
{ id: 'create', name: 'Create a new theme', command: 'prlt theme create --machine' },
|
|
32
|
+
{ id: 'add-names', name: 'Add names to a theme', command: 'prlt theme add-names --machine' },
|
|
33
33
|
{ id: 'cancel', name: 'Cancel', command: '' },
|
|
34
34
|
];
|
|
35
35
|
const message = 'What would you like to do?';
|
|
@@ -39,7 +39,7 @@ export default class Themes extends Command {
|
|
|
39
39
|
name: c.name,
|
|
40
40
|
value: c.id,
|
|
41
41
|
command: c.command,
|
|
42
|
-
}))), createMetadata('
|
|
42
|
+
}))), createMetadata('theme', flags));
|
|
43
43
|
return;
|
|
44
44
|
}
|
|
45
45
|
this.log(chalk.bold('\nAgent Themes'));
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { Command } from '@oclif/core';
|
|
2
2
|
import chalk from 'chalk';
|
|
3
|
-
import { getWorkspaceInfo } from '
|
|
4
|
-
import { ensureBuiltinThemes } from '
|
|
5
|
-
import { getThemes, getThemeNames, getAvailableThemeNames } from '
|
|
6
|
-
export default class
|
|
3
|
+
import { getWorkspaceInfo } from '../../lib/agents/commands.js';
|
|
4
|
+
import { ensureBuiltinThemes } from '../../lib/themes.js';
|
|
5
|
+
import { getThemes, getThemeNames, getAvailableThemeNames } from '../../lib/database/index.js';
|
|
6
|
+
export default class ThemeList extends Command {
|
|
7
7
|
static description = 'List available agent themes';
|
|
8
8
|
static examples = [
|
|
9
9
|
'<%= config.bin %> <%= command.id %>',
|
|
@@ -32,7 +32,7 @@ export default class ThemesList extends Command {
|
|
|
32
32
|
this.log(chalk.gray(` Names: ${chalk.green(availableNames.length + ' available')}, ${chalk.yellow(inUse + ' in use')}`));
|
|
33
33
|
this.log('');
|
|
34
34
|
}
|
|
35
|
-
this.log(chalk.blue('Use: prlt
|
|
35
|
+
this.log(chalk.blue('Use: prlt staff add --theme <theme-id>'));
|
|
36
36
|
}
|
|
37
37
|
catch (error) {
|
|
38
38
|
this.error(error instanceof Error ? error.message : String(error));
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { Command, Args, Flags } from '@oclif/core';
|
|
2
2
|
import chalk from 'chalk';
|
|
3
3
|
import inquirer from 'inquirer';
|
|
4
|
-
import { getWorkspaceInfo } from '
|
|
5
|
-
import { ensureBuiltinThemes } from '
|
|
6
|
-
import { getThemes, getAvailableThemeNames, setActiveTheme, getActiveTheme } from '
|
|
7
|
-
import { shouldOutputJson, outputPromptAsJson, createMetadata, buildPromptConfig, } from '
|
|
8
|
-
export default class
|
|
4
|
+
import { getWorkspaceInfo } from '../../lib/agents/commands.js';
|
|
5
|
+
import { ensureBuiltinThemes } from '../../lib/themes.js';
|
|
6
|
+
import { getThemes, getAvailableThemeNames, setActiveTheme, getActiveTheme } from '../../lib/database/index.js';
|
|
7
|
+
import { shouldOutputJson, outputPromptAsJson, createMetadata, buildPromptConfig, } from '../../lib/prompt-json.js';
|
|
8
|
+
export default class ThemeSet extends Command {
|
|
9
9
|
static description = 'Set the active theme for this workspace';
|
|
10
10
|
static examples = [
|
|
11
11
|
'<%= config.bin %> <%= command.id %> billionaires',
|
|
@@ -26,7 +26,7 @@ export default class ThemesSet extends Command {
|
|
|
26
26
|
}),
|
|
27
27
|
};
|
|
28
28
|
async run() {
|
|
29
|
-
const { args, flags } = await this.parse(
|
|
29
|
+
const { args, flags } = await this.parse(ThemeSet);
|
|
30
30
|
// Check if JSON output mode is active
|
|
31
31
|
const jsonMode = shouldOutputJson(flags);
|
|
32
32
|
try {
|
|
@@ -47,7 +47,7 @@ export default class ThemesSet extends Command {
|
|
|
47
47
|
value: t.id
|
|
48
48
|
};
|
|
49
49
|
});
|
|
50
|
-
outputPromptAsJson(buildPromptConfig('list', 'theme', 'Select theme for this workspace:', themeChoices), createMetadata('
|
|
50
|
+
outputPromptAsJson(buildPromptConfig('list', 'theme', 'Select theme for this workspace:', themeChoices), createMetadata('theme set', flags));
|
|
51
51
|
return;
|
|
52
52
|
}
|
|
53
53
|
const choices = themes.map(t => {
|
|
@@ -14,6 +14,7 @@ export default class TicketCreate extends PMOCommand {
|
|
|
14
14
|
epic: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
15
15
|
template: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
16
16
|
labels: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
17
|
+
'dry-run': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
17
18
|
project: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
18
19
|
};
|
|
19
20
|
execute(): Promise<void>;
|
|
@@ -4,8 +4,9 @@ import { autoExportToBoard, PMOCommand, pmoBaseFlags } from '../../lib/pmo/index
|
|
|
4
4
|
import { styles } from '../../lib/styles.js';
|
|
5
5
|
import { updateEpicTicketsSection } from '../../lib/pmo/epic-files.js';
|
|
6
6
|
import { PRIORITIES, PRIORITY_LABELS } from '../../lib/pmo/types.js';
|
|
7
|
-
import { shouldOutputJson, outputErrorAsJson, createMetadata, } from '../../lib/prompt-json.js';
|
|
7
|
+
import { shouldOutputJson, outputErrorAsJson, outputDryRunSuccessAsJson, outputDryRunErrorsAsJson, createMetadata, } from '../../lib/prompt-json.js';
|
|
8
8
|
import { FlagResolver } from '../../lib/flags/index.js';
|
|
9
|
+
import { multiLineInput } from '../../lib/multiline-input.js';
|
|
9
10
|
export default class TicketCreate extends PMOCommand {
|
|
10
11
|
static description = 'Create a new ticket on the PMO board';
|
|
11
12
|
static examples = [
|
|
@@ -15,6 +16,7 @@ export default class TicketCreate extends PMOCommand {
|
|
|
15
16
|
'<%= config.bin %> <%= command.id %> --project mobile-app -t "New feature"',
|
|
16
17
|
'<%= config.bin %> <%= command.id %> --epic EPIC-001 -t "Implement auth flow"',
|
|
17
18
|
'<%= config.bin %> <%= command.id %> --json # Output column choices as JSON',
|
|
19
|
+
'<%= config.bin %> <%= command.id %> --title "Test" -P PROJ-001 --dry-run --json # Validate without creating',
|
|
18
20
|
];
|
|
19
21
|
static flags = {
|
|
20
22
|
...pmoBaseFlags,
|
|
@@ -26,7 +28,7 @@ export default class TicketCreate extends PMOCommand {
|
|
|
26
28
|
}),
|
|
27
29
|
title: Flags.string({
|
|
28
30
|
char: 't',
|
|
29
|
-
description: 'Ticket title',
|
|
31
|
+
description: 'Ticket title [required for non-interactive]',
|
|
30
32
|
}),
|
|
31
33
|
column: Flags.string({
|
|
32
34
|
char: 'c',
|
|
@@ -64,6 +66,10 @@ export default class TicketCreate extends PMOCommand {
|
|
|
64
66
|
char: 'l',
|
|
65
67
|
description: 'Labels (comma-separated)',
|
|
66
68
|
}),
|
|
69
|
+
'dry-run': Flags.boolean({
|
|
70
|
+
description: 'Validate inputs without creating ticket (use with --json for structured output)',
|
|
71
|
+
default: false,
|
|
72
|
+
}),
|
|
67
73
|
};
|
|
68
74
|
async execute() {
|
|
69
75
|
const { flags } = await this.parse(TicketCreate);
|
|
@@ -171,8 +177,55 @@ export default class TicketCreate extends PMOCommand {
|
|
|
171
177
|
}
|
|
172
178
|
// Validate status/column
|
|
173
179
|
if (!columns.includes(ticketData.statusName)) {
|
|
180
|
+
if (flags['dry-run']) {
|
|
181
|
+
if (jsonMode) {
|
|
182
|
+
outputDryRunErrorsAsJson([{ field: 'column', error: `Invalid column "${ticketData.statusName}". Available: ${columns.join(', ')}` }], createMetadata('ticket create', flags));
|
|
183
|
+
}
|
|
184
|
+
this.error(`Invalid column "${ticketData.statusName}". Available columns: ${columns.join(', ')}`);
|
|
185
|
+
}
|
|
174
186
|
this.error(`Invalid column "${ticketData.statusName}". Available columns: ${columns.join(', ')}`);
|
|
175
187
|
}
|
|
188
|
+
// Handle dry-run: show what would be created without actually creating
|
|
189
|
+
if (flags['dry-run']) {
|
|
190
|
+
const wouldCreate = {
|
|
191
|
+
title: ticketData.title,
|
|
192
|
+
project: projectId,
|
|
193
|
+
column: ticketData.statusName,
|
|
194
|
+
...(ticketData.priority && { priority: ticketData.priority }),
|
|
195
|
+
...(ticketData.category && { category: ticketData.category }),
|
|
196
|
+
...(ticketData.description && { description: ticketData.description }),
|
|
197
|
+
...(ticketData.epicId && { epic: ticketData.epicId }),
|
|
198
|
+
...(ticketData.labels && ticketData.labels.length > 0 && { labels: ticketData.labels }),
|
|
199
|
+
};
|
|
200
|
+
if (jsonMode) {
|
|
201
|
+
outputDryRunSuccessAsJson('ticket', wouldCreate, createMetadata('ticket create', flags));
|
|
202
|
+
}
|
|
203
|
+
// Human-readable dry-run output
|
|
204
|
+
this.log(styles.warning('\n[DRY RUN] Would create ticket:'));
|
|
205
|
+
this.log(styles.muted(` Title: ${ticketData.title}`));
|
|
206
|
+
this.log(styles.muted(` Project: ${projectName}`));
|
|
207
|
+
this.log(styles.muted(` Column: ${ticketData.statusName}`));
|
|
208
|
+
if (ticketData.priority) {
|
|
209
|
+
this.log(styles.muted(` Priority: ${ticketData.priority}`));
|
|
210
|
+
}
|
|
211
|
+
if (ticketData.category) {
|
|
212
|
+
this.log(styles.muted(` Category: ${ticketData.category}`));
|
|
213
|
+
}
|
|
214
|
+
if (ticketData.epicId) {
|
|
215
|
+
this.log(styles.muted(` Epic: ${ticketData.epicId}`));
|
|
216
|
+
}
|
|
217
|
+
if (ticketData.labels && ticketData.labels.length > 0) {
|
|
218
|
+
this.log(styles.muted(` Labels: ${ticketData.labels.join(', ')}`));
|
|
219
|
+
}
|
|
220
|
+
if (template) {
|
|
221
|
+
this.log(styles.muted(` Template: ${template.name}`));
|
|
222
|
+
if (template.suggestedSubtasks.length > 0) {
|
|
223
|
+
this.log(styles.muted(` Subtasks: ${template.suggestedSubtasks.length} would be created`));
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
this.log(styles.muted('\n(No ticket was created)'));
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
176
229
|
const ticket = await this.storage.createTicket(projectId, {
|
|
177
230
|
id: ticketData.id,
|
|
178
231
|
title: ticketData.title,
|
|
@@ -348,18 +401,25 @@ export default class TicketCreate extends PMOCommand {
|
|
|
348
401
|
return existingDescription;
|
|
349
402
|
}
|
|
350
403
|
this.log(styles.muted('\n─── Ticket Description (for agent execution) ───'));
|
|
351
|
-
|
|
404
|
+
// Prompt for "What" - the main outcome
|
|
405
|
+
const { what } = await inquirer.prompt([
|
|
352
406
|
{
|
|
353
407
|
type: 'input',
|
|
354
408
|
name: 'what',
|
|
355
409
|
message: 'What is the concrete outcome? (one sentence):',
|
|
356
410
|
validate: (input) => input.trim() ? true : 'Outcome cannot be empty - what does success look like?',
|
|
357
411
|
},
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
412
|
+
]);
|
|
413
|
+
// Prompt for acceptance criteria using multiline input
|
|
414
|
+
const doneWhenResult = await multiLineInput({
|
|
415
|
+
message: 'Done when (acceptance criteria):',
|
|
416
|
+
hint: 'Enter each criterion on a new line. Ctrl+D to finish, Ctrl+C to cancel',
|
|
417
|
+
});
|
|
418
|
+
if (doneWhenResult.cancelled) {
|
|
419
|
+
throw new Error('Ticket creation cancelled');
|
|
420
|
+
}
|
|
421
|
+
// Continue with remaining prompts
|
|
422
|
+
const { context, notInScope } = await inquirer.prompt([
|
|
363
423
|
{
|
|
364
424
|
type: 'input',
|
|
365
425
|
name: 'context',
|
|
@@ -375,10 +435,10 @@ export default class TicketCreate extends PMOCommand {
|
|
|
375
435
|
]);
|
|
376
436
|
// Build structured description
|
|
377
437
|
const parts = [];
|
|
378
|
-
parts.push(`## What\n${
|
|
379
|
-
if (
|
|
438
|
+
parts.push(`## What\n${what}`);
|
|
439
|
+
if (doneWhenResult.value.trim()) {
|
|
380
440
|
// Ensure each line in doneWhen starts with - [ ] if it doesn't already
|
|
381
|
-
const criteria =
|
|
441
|
+
const criteria = doneWhenResult.value
|
|
382
442
|
.split('\n')
|
|
383
443
|
.map(line => line.trim())
|
|
384
444
|
.filter(line => line.length > 0)
|
|
@@ -394,11 +454,11 @@ export default class TicketCreate extends PMOCommand {
|
|
|
394
454
|
.join('\n');
|
|
395
455
|
parts.push(`## Done when\n${criteria}`);
|
|
396
456
|
}
|
|
397
|
-
if (
|
|
398
|
-
parts.push(`## Context\n${
|
|
457
|
+
if (context.trim()) {
|
|
458
|
+
parts.push(`## Context\n${context}`);
|
|
399
459
|
}
|
|
400
|
-
if (
|
|
401
|
-
parts.push(`## Not in scope\n${
|
|
460
|
+
if (notInScope.trim()) {
|
|
461
|
+
parts.push(`## Not in scope\n${notInScope}`);
|
|
402
462
|
}
|
|
403
463
|
return parts.join('\n\n');
|
|
404
464
|
}
|
|
@@ -4,6 +4,7 @@ import { autoExportToBoard, PMOCommand, pmoBaseFlags } from '../../lib/pmo/index
|
|
|
4
4
|
import { PRIORITIES, PRIORITY_LABELS } from '../../lib/pmo/types.js';
|
|
5
5
|
import { styles } from '../../lib/styles.js';
|
|
6
6
|
import { shouldOutputJson, outputErrorAsJson, createMetadata, } from '../../lib/prompt-json.js';
|
|
7
|
+
import { multiLineInput } from '../../lib/multiline-input.js';
|
|
7
8
|
export default class TicketEdit extends PMOCommand {
|
|
8
9
|
static description = 'Edit an existing ticket';
|
|
9
10
|
static examples = [
|
|
@@ -128,6 +129,29 @@ export default class TicketEdit extends PMOCommand {
|
|
|
128
129
|
flags.owner || flags.assignee || flags['add-subtask'] || flags['clear-subtasks'] ||
|
|
129
130
|
flags['add-label'] || flags['remove-label'] || flags['add-ac'] || flags['clear-ac'];
|
|
130
131
|
if (flags.interactive || !hasFlags) {
|
|
132
|
+
// In JSON mode without flags, output a form prompt instead of interactive prompts
|
|
133
|
+
if (jsonMode) {
|
|
134
|
+
const { outputPromptAsJson, buildFormPromptConfig } = await import('../../lib/prompt-json.js');
|
|
135
|
+
const formConfig = buildFormPromptConfig([
|
|
136
|
+
{ type: 'input', name: 'title', message: 'Title:', default: ticket.title },
|
|
137
|
+
{ type: 'multiline', name: 'description', message: 'Description:', default: ticket.description || '' },
|
|
138
|
+
{ type: 'list', name: 'priority', message: 'Priority:', choices: [
|
|
139
|
+
{ name: 'None', value: '' },
|
|
140
|
+
{ name: 'P0 - Critical', value: 'P0' },
|
|
141
|
+
{ name: 'P1 - High', value: 'P1' },
|
|
142
|
+
{ name: 'P2 - Medium', value: 'P2' },
|
|
143
|
+
{ name: 'P3 - Low', value: 'P3' },
|
|
144
|
+
], default: ticket.priority || '' },
|
|
145
|
+
{ type: 'input', name: 'category', message: 'Category:', default: ticket.category || '' },
|
|
146
|
+
]);
|
|
147
|
+
formConfig.context = {
|
|
148
|
+
hint: `Edit ticket with: prlt ticket edit ${ticketId} --title "..." --description "..." --priority P0 --json`,
|
|
149
|
+
ticketId,
|
|
150
|
+
currentValues: { title: ticket.title, description: ticket.description, priority: ticket.priority, category: ticket.category },
|
|
151
|
+
};
|
|
152
|
+
outputPromptAsJson(formConfig, createMetadata('ticket edit', flags));
|
|
153
|
+
return; // outputPromptAsJson exits, but TypeScript doesn't know
|
|
154
|
+
}
|
|
131
155
|
// Interactive mode - prompt for all editable fields
|
|
132
156
|
const board = await this.storage.getBoard(ticket.projectId);
|
|
133
157
|
const columns = board.columns.map(col => col.name);
|
|
@@ -244,7 +268,8 @@ export default class TicketEdit extends PMOCommand {
|
|
|
244
268
|
this.log('');
|
|
245
269
|
}
|
|
246
270
|
async promptForEdits(ticket, _columns) {
|
|
247
|
-
|
|
271
|
+
// First prompt for title
|
|
272
|
+
const { title } = await inquirer.prompt([
|
|
248
273
|
{
|
|
249
274
|
type: 'input',
|
|
250
275
|
name: 'title',
|
|
@@ -252,13 +277,18 @@ export default class TicketEdit extends PMOCommand {
|
|
|
252
277
|
default: ticket.title,
|
|
253
278
|
validate: (input) => input.trim() ? true : 'Title cannot be empty',
|
|
254
279
|
},
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
280
|
+
]);
|
|
281
|
+
// Prompt for description using inline multiline input
|
|
282
|
+
const descResult = await multiLineInput({
|
|
283
|
+
message: 'Description:',
|
|
284
|
+
default: ticket.description || '',
|
|
285
|
+
hint: 'Ctrl+D to finish, Ctrl+C to cancel',
|
|
286
|
+
});
|
|
287
|
+
if (descResult.cancelled) {
|
|
288
|
+
throw new Error('Edit cancelled');
|
|
289
|
+
}
|
|
290
|
+
// Continue with remaining prompts
|
|
291
|
+
const answers = await inquirer.prompt([
|
|
262
292
|
{
|
|
263
293
|
type: 'list',
|
|
264
294
|
name: 'priority',
|
|
@@ -304,17 +334,18 @@ export default class TicketEdit extends PMOCommand {
|
|
|
304
334
|
type: 'input',
|
|
305
335
|
name: 'customCategory',
|
|
306
336
|
message: 'Enter custom category:',
|
|
307
|
-
when: (
|
|
337
|
+
when: (promptAnswers) => promptAnswers.categoryChoice === '__custom__',
|
|
308
338
|
validate: (input) => input.trim() ? true : 'Category cannot be empty',
|
|
309
339
|
},
|
|
310
340
|
]);
|
|
311
341
|
// Build updates object with only changed fields
|
|
312
342
|
const updates = {};
|
|
313
|
-
if (
|
|
314
|
-
updates.title =
|
|
343
|
+
if (title !== ticket.title) {
|
|
344
|
+
updates.title = title;
|
|
315
345
|
}
|
|
316
|
-
if (
|
|
317
|
-
|
|
346
|
+
if (descResult.value !== (ticket.description || '')) {
|
|
347
|
+
// Preserve empty string to allow clearing the description
|
|
348
|
+
updates.description = descResult.value;
|
|
318
349
|
}
|
|
319
350
|
if (answers.priority !== (ticket.priority || '')) {
|
|
320
351
|
updates.priority = answers.priority || undefined;
|
|
@@ -27,7 +27,7 @@ export default class Ticket extends PMOCommand {
|
|
|
27
27
|
// Each choice includes the full command for AI agents to execute
|
|
28
28
|
const menuChoices = [
|
|
29
29
|
{ name: 'Create new ticket', value: 'create', command: 'prlt ticket create --json' },
|
|
30
|
-
{ name: 'Create from template', value: 'template', command: 'prlt
|
|
30
|
+
{ name: 'Create from template', value: 'template', command: 'prlt template apply --type ticket --json' },
|
|
31
31
|
{ name: 'List all tickets', value: 'list', command: 'prlt ticket list --format json' },
|
|
32
32
|
{ name: 'View ticket details', value: 'view', command: 'prlt ticket view --json' },
|
|
33
33
|
{ name: 'Edit ticket', value: 'edit', command: 'prlt ticket edit --json' },
|
|
@@ -35,8 +35,8 @@ export default class Ticket extends PMOCommand {
|
|
|
35
35
|
{ name: 'Move to different project', value: 'project', command: 'prlt ticket project --json' },
|
|
36
36
|
{ name: 'Assign to epic', value: 'epic', command: 'prlt ticket epic --json' },
|
|
37
37
|
{ name: 'Assign to spec', value: 'spec', command: 'prlt ticket spec --json' },
|
|
38
|
-
{ name: 'Manage dependencies', value: 'link', command: 'prlt
|
|
39
|
-
{ name: 'Manage templates', value: 'templates', command: 'prlt
|
|
38
|
+
{ name: 'Manage dependencies', value: 'link', command: 'prlt link list --json' },
|
|
39
|
+
{ name: 'Manage templates', value: 'templates', command: 'prlt template --json' },
|
|
40
40
|
{ name: 'Delete ticket', value: 'delete', command: 'prlt ticket delete --json' },
|
|
41
41
|
{ name: 'Cancel', value: 'cancel' },
|
|
42
42
|
];
|
|
@@ -68,7 +68,7 @@ export default class Ticket extends PMOCommand {
|
|
|
68
68
|
await this.config.runCommand('ticket:create', []);
|
|
69
69
|
break;
|
|
70
70
|
case 'template':
|
|
71
|
-
await this.config.runCommand('
|
|
71
|
+
await this.config.runCommand('template:apply', ['--type', 'ticket']);
|
|
72
72
|
break;
|
|
73
73
|
case 'list':
|
|
74
74
|
await this.config.runCommand('ticket:list', []);
|
|
@@ -92,10 +92,10 @@ export default class Ticket extends PMOCommand {
|
|
|
92
92
|
await this.config.runCommand('ticket:spec', []);
|
|
93
93
|
break;
|
|
94
94
|
case 'link':
|
|
95
|
-
await this.config.runCommand('
|
|
95
|
+
await this.config.runCommand('link', []);
|
|
96
96
|
break;
|
|
97
97
|
case 'templates':
|
|
98
|
-
await this.config.runCommand('
|
|
98
|
+
await this.config.runCommand('template', []);
|
|
99
99
|
break;
|
|
100
100
|
case 'delete':
|
|
101
101
|
await this.config.runCommand('ticket:delete', []);
|
|
@@ -9,10 +9,17 @@ export default class TicketMove extends PMOCommand {
|
|
|
9
9
|
static flags: {
|
|
10
10
|
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
11
11
|
position: import("@oclif/core/interfaces").OptionFlag<number | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
+
'to-project': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
13
|
bulk: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
13
14
|
force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
14
15
|
project: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
15
16
|
};
|
|
16
17
|
execute(): Promise<void>;
|
|
17
18
|
private executeBulk;
|
|
19
|
+
/**
|
|
20
|
+
* Move a ticket to a different project.
|
|
21
|
+
* If a target column is specified and exists in the target project, move to that column.
|
|
22
|
+
* Otherwise, use the default/backlog column.
|
|
23
|
+
*/
|
|
24
|
+
private executeCrossProjectMove;
|
|
18
25
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Args, Flags } from '@oclif/core';
|
|
2
2
|
import { autoExportToBoard, PMOCommand, pmoBaseFlags, } from '../../lib/pmo/index.js';
|
|
3
|
+
import { PMOError } from '../../lib/pmo/types.js';
|
|
3
4
|
import { styles } from '../../lib/styles.js';
|
|
4
5
|
import { shouldOutputJson, outputErrorAsJson, createMetadata, } from '../../lib/prompt-json.js';
|
|
5
6
|
export default class TicketMove extends PMOCommand {
|
|
@@ -8,6 +9,7 @@ export default class TicketMove extends PMOCommand {
|
|
|
8
9
|
'<%= config.bin %> <%= command.id %> my-ticket "In Progress"',
|
|
9
10
|
'<%= config.bin %> <%= command.id %> implement-auth Done',
|
|
10
11
|
'<%= config.bin %> <%= command.id %> fix-bug "In Review" --position 0',
|
|
12
|
+
'<%= config.bin %> <%= command.id %> TKT-123 --to-project PROJ-002',
|
|
11
13
|
'<%= config.bin %> <%= command.id %> --bulk',
|
|
12
14
|
];
|
|
13
15
|
static args = {
|
|
@@ -31,6 +33,9 @@ export default class TicketMove extends PMOCommand {
|
|
|
31
33
|
position: Flags.integer({
|
|
32
34
|
description: 'Position within the column (0 = top)',
|
|
33
35
|
}),
|
|
36
|
+
'to-project': Flags.string({
|
|
37
|
+
description: 'Move ticket to a different project (uses Backlog/default column)',
|
|
38
|
+
}),
|
|
34
39
|
bulk: Flags.boolean({
|
|
35
40
|
char: 'b',
|
|
36
41
|
description: 'Enable bulk mode to move multiple tickets',
|
|
@@ -54,6 +59,16 @@ export default class TicketMove extends PMOCommand {
|
|
|
54
59
|
}
|
|
55
60
|
this.error(message);
|
|
56
61
|
};
|
|
62
|
+
// Cross-project move: if ticketId and --to-project are provided, skip project context
|
|
63
|
+
// The source project is determined from the ticket itself
|
|
64
|
+
if (args.ticketId && flags['to-project']) {
|
|
65
|
+
const ticket = await this.storage.getTicket(args.ticketId);
|
|
66
|
+
if (!ticket) {
|
|
67
|
+
return handleError('TICKET_NOT_FOUND', `Ticket "${args.ticketId}" not found.`);
|
|
68
|
+
}
|
|
69
|
+
await this.executeCrossProjectMove(ticket, flags['to-project'], args.column, jsonMode, flags);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
57
72
|
// This command requires project context - get projectId (with JSON mode support)
|
|
58
73
|
const projectId = await this.requireProject({
|
|
59
74
|
jsonMode: {
|
|
@@ -94,9 +109,69 @@ export default class TicketMove extends PMOCommand {
|
|
|
94
109
|
if (!ticket) {
|
|
95
110
|
this.error(`Ticket "${ticketId}" not found.`);
|
|
96
111
|
}
|
|
112
|
+
// Cross-project move (when --to-project flag is provided)
|
|
113
|
+
if (flags['to-project']) {
|
|
114
|
+
await this.executeCrossProjectMove(ticket, flags['to-project'], args.column, jsonMode, flags);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
97
117
|
// Get target column - prompt if not provided
|
|
98
118
|
let targetColumn = args.column;
|
|
99
119
|
if (!targetColumn) {
|
|
120
|
+
// Check if there are other projects to move to
|
|
121
|
+
const allProjects = await this.storage.listProjects();
|
|
122
|
+
const otherProjects = allProjects.filter(p => p.id !== projectId);
|
|
123
|
+
// If there are other projects, ask user what type of move they want
|
|
124
|
+
if (otherProjects.length > 0) {
|
|
125
|
+
const moveTypeChoices = [
|
|
126
|
+
{ id: 'column', name: 'Different column (same project)' },
|
|
127
|
+
{ id: 'project', name: 'Different project' },
|
|
128
|
+
];
|
|
129
|
+
const moveType = await this.selectFromList({
|
|
130
|
+
message: 'Move to:',
|
|
131
|
+
items: moveTypeChoices,
|
|
132
|
+
getName: (choice) => choice.name,
|
|
133
|
+
getValue: (choice) => choice.id,
|
|
134
|
+
getCommand: (choice) => choice.id === 'column'
|
|
135
|
+
? `prlt ticket move ${ticketId} -P ${projectId} --json`
|
|
136
|
+
: `prlt ticket project ${ticketId} -P ${projectId} --json`,
|
|
137
|
+
jsonMode: jsonMode ? { flags, commandName: 'ticket move' } : null,
|
|
138
|
+
});
|
|
139
|
+
if (!moveType) {
|
|
140
|
+
return; // Cancelled or JSON mode
|
|
141
|
+
}
|
|
142
|
+
// If user chose different project, handle cross-project move
|
|
143
|
+
if (moveType === 'project') {
|
|
144
|
+
const targetProjectId = await this.selectFromList({
|
|
145
|
+
message: 'Select target project:',
|
|
146
|
+
items: otherProjects,
|
|
147
|
+
getName: (p) => `${p.name} (${p.id})`,
|
|
148
|
+
getValue: (p) => p.id,
|
|
149
|
+
getCommand: (p) => `prlt ticket move ${ticketId} --to-project ${p.id} --json`,
|
|
150
|
+
jsonMode: jsonMode ? { flags, commandName: 'ticket move' } : null,
|
|
151
|
+
});
|
|
152
|
+
if (!targetProjectId) {
|
|
153
|
+
return; // Cancelled or JSON mode
|
|
154
|
+
}
|
|
155
|
+
// Get columns from target project and ask which column to move to
|
|
156
|
+
const targetProjectBoard = await this.storage.getProjectBoard(targetProjectId);
|
|
157
|
+
if (!targetProjectBoard) {
|
|
158
|
+
this.error('Target project not found.');
|
|
159
|
+
}
|
|
160
|
+
const targetColumnName = await this.selectFromList({
|
|
161
|
+
message: 'Move to column:',
|
|
162
|
+
items: targetProjectBoard.columns,
|
|
163
|
+
getName: (col) => col.name,
|
|
164
|
+
getValue: (col) => col.name,
|
|
165
|
+
getCommand: (col) => `prlt ticket move ${ticketId} "${col.name}" --to-project ${targetProjectId} --json`,
|
|
166
|
+
jsonMode: jsonMode ? { flags, commandName: 'ticket move' } : null,
|
|
167
|
+
});
|
|
168
|
+
if (!targetColumnName) {
|
|
169
|
+
return; // Cancelled or JSON mode
|
|
170
|
+
}
|
|
171
|
+
await this.executeCrossProjectMove(ticket, targetProjectId, targetColumnName, jsonMode, flags);
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
100
175
|
// Get columns from the database (not config.json) to ensure accuracy
|
|
101
176
|
const project = await this.storage.getProjectBoard(projectId);
|
|
102
177
|
if (!project) {
|
|
@@ -221,4 +296,61 @@ export default class TicketMove extends PMOCommand {
|
|
|
221
296
|
this.log(styles.error(`Failed to move ${failCount} ticket(s)`));
|
|
222
297
|
}
|
|
223
298
|
}
|
|
299
|
+
/**
|
|
300
|
+
* Move a ticket to a different project.
|
|
301
|
+
* If a target column is specified and exists in the target project, move to that column.
|
|
302
|
+
* Otherwise, use the default/backlog column.
|
|
303
|
+
*/
|
|
304
|
+
async executeCrossProjectMove(ticket, targetProjectId, targetColumn, jsonMode, flags) {
|
|
305
|
+
const ticketId = ticket.id;
|
|
306
|
+
const sourceProjectId = ticket.projectId;
|
|
307
|
+
// Check if target project exists
|
|
308
|
+
const projects = await this.storage.listProjects();
|
|
309
|
+
const targetProject = projects.find(p => p.id === targetProjectId ||
|
|
310
|
+
p.id.toLowerCase() === targetProjectId.toLowerCase() ||
|
|
311
|
+
p.name.toLowerCase() === targetProjectId.toLowerCase());
|
|
312
|
+
if (!targetProject) {
|
|
313
|
+
if (jsonMode) {
|
|
314
|
+
outputErrorAsJson('PROJECT_NOT_FOUND', `Project not found: ${targetProjectId}`, createMetadata('ticket move', flags));
|
|
315
|
+
this.exit(1);
|
|
316
|
+
}
|
|
317
|
+
this.error(`Project not found: ${targetProjectId}`);
|
|
318
|
+
}
|
|
319
|
+
// Check if moving to the same project
|
|
320
|
+
if (targetProject.id === sourceProjectId) {
|
|
321
|
+
this.log(styles.warning(`Ticket "${ticketId}" is already in project "${targetProject.id}".`));
|
|
322
|
+
this.log(styles.muted(`To move to a different column, use: prlt ticket move ${ticketId} <column>`));
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
// Move ticket to the new project
|
|
326
|
+
const movedTicket = await this.storage.moveTicketToProject(ticketId, targetProject.id);
|
|
327
|
+
// If a target column was specified, try to move to that column in the new project
|
|
328
|
+
if (targetColumn) {
|
|
329
|
+
try {
|
|
330
|
+
await this.storage.moveTicket(targetProject.id, ticketId, targetColumn);
|
|
331
|
+
// Refresh ticket to get updated status
|
|
332
|
+
const updatedTicket = await this.storage.getTicket(ticketId);
|
|
333
|
+
await autoExportToBoard(this.pmoPath, this.storage, (msg) => this.log(styles.muted(msg)));
|
|
334
|
+
this.log(styles.success(`\n✅ Moved ticket ${styles.emphasis(ticketId)} to project ${styles.emphasis(targetProject.id)}`));
|
|
335
|
+
this.log(styles.muted(` From project: ${sourceProjectId}`));
|
|
336
|
+
this.log(styles.muted(` To project: ${targetProject.id}`));
|
|
337
|
+
this.log(styles.muted(` Column: ${updatedTicket?.statusName || targetColumn}`));
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
catch (error) {
|
|
341
|
+
// Only catch "status not found" errors - re-throw unexpected errors
|
|
342
|
+
if (error instanceof PMOError && error.code === 'NOT_FOUND') {
|
|
343
|
+
this.log(styles.muted(`Note: Column "${targetColumn}" not found in target project, using default column.`));
|
|
344
|
+
}
|
|
345
|
+
else {
|
|
346
|
+
throw error;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
await autoExportToBoard(this.pmoPath, this.storage, (msg) => this.log(styles.muted(msg)));
|
|
351
|
+
this.log(styles.success(`\n✅ Moved ticket ${styles.emphasis(ticketId)} to project ${styles.emphasis(targetProject.id)}`));
|
|
352
|
+
this.log(styles.muted(` From project: ${sourceProjectId}`));
|
|
353
|
+
this.log(styles.muted(` To project: ${targetProject.id}`));
|
|
354
|
+
this.log(styles.muted(` Column: ${movedTicket.statusName || 'default'}`));
|
|
355
|
+
}
|
|
224
356
|
}
|
|
@@ -22,6 +22,7 @@ export default class WorkSpawn extends PMOCommand {
|
|
|
22
22
|
'create-pr': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
23
23
|
'no-pr': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
24
24
|
action: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
25
|
+
message: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
25
26
|
session: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
26
27
|
focus: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
27
28
|
clone: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|