jira-pilot 2.1.2 → 2.1.3
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/README.md +23 -0
- package/bin/jira.ts +2 -0
- package/dist/bin/jira.js +2 -0
- package/dist/bin/jira.js.map +1 -1
- package/dist/src/commands/ai-actions/plan.js +1 -1
- package/dist/src/commands/ai-actions/plan.js.map +1 -1
- package/dist/src/commands/ai-actions/review.js +5 -4
- package/dist/src/commands/ai-actions/review.js.map +1 -1
- package/dist/src/commands/ai-actions/standup.js +1 -1
- package/dist/src/commands/ai-actions/standup.js.map +1 -1
- package/dist/src/commands/ai.js +1 -1
- package/dist/src/commands/ai.js.map +1 -1
- package/dist/src/commands/board.js +10 -5
- package/dist/src/commands/board.js.map +1 -1
- package/dist/src/commands/bulk.js +1 -1
- package/dist/src/commands/bulk.js.map +1 -1
- package/dist/src/commands/config.js +1 -1
- package/dist/src/commands/config.js.map +1 -1
- package/dist/src/commands/dashboard.js +11 -5
- package/dist/src/commands/dashboard.js.map +1 -1
- package/dist/src/commands/filter.js +7 -4
- package/dist/src/commands/filter.js.map +1 -1
- package/dist/src/commands/git.js +1 -1
- package/dist/src/commands/git.js.map +1 -1
- package/dist/src/commands/issue-attach.js +1 -1
- package/dist/src/commands/issue-attach.js.map +1 -1
- package/dist/src/commands/issue-pr.js +1 -1
- package/dist/src/commands/issue-pr.js.map +1 -1
- package/dist/src/commands/issue-worklog.js +10 -5
- package/dist/src/commands/issue-worklog.js.map +1 -1
- package/dist/src/commands/issue.js +142 -87
- package/dist/src/commands/issue.js.map +1 -1
- package/dist/src/commands/project.js +10 -5
- package/dist/src/commands/project.js.map +1 -1
- package/dist/src/commands/sprint.js +19 -8
- package/dist/src/commands/sprint.js.map +1 -1
- package/dist/src/commands/tui.d.ts +2 -0
- package/dist/src/commands/tui.js +10 -0
- package/dist/src/commands/tui.js.map +1 -0
- package/dist/src/services/ai-service.js +7 -4
- package/dist/src/services/ai-service.js.map +1 -1
- package/dist/src/services/api-service.d.ts +2 -0
- package/dist/src/services/api-service.js +29 -20
- package/dist/src/services/api-service.js.map +1 -1
- package/dist/src/tui/App.d.ts +1 -0
- package/dist/src/tui/App.js +26 -0
- package/dist/src/tui/App.js.map +1 -0
- package/dist/src/tui/index.d.ts +1 -0
- package/dist/src/tui/index.js +8 -0
- package/dist/src/tui/index.js.map +1 -0
- package/dist/src/tui/screens/BoardList.d.ts +1 -0
- package/dist/src/tui/screens/BoardList.js +71 -0
- package/dist/src/tui/screens/BoardList.js.map +1 -0
- package/dist/src/tui/screens/Dashboard.d.ts +1 -0
- package/dist/src/tui/screens/Dashboard.js +41 -0
- package/dist/src/tui/screens/Dashboard.js.map +1 -0
- package/dist/src/tui/screens/IssueDetail.d.ts +6 -0
- package/dist/src/tui/screens/IssueDetail.js +40 -0
- package/dist/src/tui/screens/IssueDetail.js.map +1 -0
- package/dist/src/tui/screens/IssueList.d.ts +1 -0
- package/dist/src/tui/screens/IssueList.js +72 -0
- package/dist/src/tui/screens/IssueList.js.map +1 -0
- package/dist/src/tui/screens/KanbanBoard.d.ts +6 -0
- package/dist/src/tui/screens/KanbanBoard.js +86 -0
- package/dist/src/tui/screens/KanbanBoard.js.map +1 -0
- package/dist/src/tui/utils/adf-render.d.ts +1 -0
- package/dist/src/tui/utils/adf-render.js +29 -0
- package/dist/src/tui/utils/adf-render.js.map +1 -0
- package/dist/src/utils/error-handler.d.ts +2 -2
- package/dist/src/utils/error-handler.js.map +1 -1
- package/dist/src/utils/http.d.ts +27 -0
- package/dist/src/utils/http.js +95 -0
- package/dist/src/utils/http.js.map +1 -0
- package/dist/src/utils/spinner.d.ts +21 -0
- package/dist/src/utils/spinner.js +79 -0
- package/dist/src/utils/spinner.js.map +1 -0
- package/package.json +10 -5
- package/src/commands/ai-actions/plan.ts +1 -1
- package/src/commands/ai-actions/review.ts +5 -4
- package/src/commands/ai-actions/standup.ts +1 -1
- package/src/commands/ai.ts +1 -1
- package/src/commands/board.ts +10 -5
- package/src/commands/bulk.ts +1 -1
- package/src/commands/config.ts +1 -1
- package/src/commands/dashboard.ts +11 -5
- package/src/commands/filter.ts +8 -5
- package/src/commands/git.ts +1 -1
- package/src/commands/issue-attach.ts +1 -1
- package/src/commands/issue-pr.ts +1 -1
- package/src/commands/issue-worklog.ts +10 -5
- package/src/commands/issue.ts +149 -89
- package/src/commands/project.ts +10 -5
- package/src/commands/sprint.ts +19 -8
- package/src/commands/tui.ts +11 -0
- package/src/services/ai-service.ts +7 -4
- package/src/services/api-service.ts +29 -21
- package/src/tui/App.tsx +61 -0
- package/src/tui/index.tsx +8 -0
- package/src/tui/screens/BoardList.tsx +102 -0
- package/src/tui/screens/Dashboard.tsx +75 -0
- package/src/tui/screens/IssueDetail.tsx +93 -0
- package/src/tui/screens/IssueList.tsx +116 -0
- package/src/tui/screens/KanbanBoard.tsx +133 -0
- package/src/tui/utils/adf-render.ts +30 -0
- package/src/utils/error-handler.ts +2 -2
- package/src/utils/http.ts +128 -0
- package/src/utils/spinner.ts +87 -0
package/src/commands/board.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
import chalk from 'chalk';
|
|
3
|
-
import Table from '
|
|
3
|
+
import { Table } from 'cmd-table';
|
|
4
4
|
import { api } from '../services/api-service.js';
|
|
5
|
-
import ora from '
|
|
5
|
+
import ora from '../utils/spinner.js';
|
|
6
6
|
import { handleCommandError } from '../utils/error-handler.js';
|
|
7
7
|
|
|
8
8
|
export function registerBoardCommand(program: Command) {
|
|
@@ -51,11 +51,16 @@ Common Actions:
|
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
const table = new Table({
|
|
54
|
-
|
|
54
|
+
columns: [
|
|
55
|
+
{ name: chalk.bold('ID') },
|
|
56
|
+
{ name: chalk.bold('Name') },
|
|
57
|
+
{ name: chalk.bold('Type') },
|
|
58
|
+
{ name: chalk.bold('Project') }
|
|
59
|
+
]
|
|
55
60
|
});
|
|
56
61
|
|
|
57
62
|
data.values.forEach((b: any) => {
|
|
58
|
-
table.
|
|
63
|
+
table.addRow([
|
|
59
64
|
b.id,
|
|
60
65
|
b.name,
|
|
61
66
|
b.type,
|
|
@@ -63,7 +68,7 @@ Common Actions:
|
|
|
63
68
|
]);
|
|
64
69
|
});
|
|
65
70
|
|
|
66
|
-
console.log(table.
|
|
71
|
+
console.log(table.render());
|
|
67
72
|
console.log(chalk.grey(`Showing ${data.values.length} board(s)`));
|
|
68
73
|
|
|
69
74
|
} catch (e: any) {
|
package/src/commands/bulk.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
import chalk from 'chalk';
|
|
3
|
-
import ora from '
|
|
3
|
+
import ora from '../utils/spinner.js';
|
|
4
4
|
import enquirer from 'enquirer';
|
|
5
5
|
import { api } from '../services/api-service.js';
|
|
6
6
|
import { handleCommandError } from '../utils/error-handler.js';
|
package/src/commands/config.ts
CHANGED
|
@@ -3,7 +3,7 @@ import chalk from 'chalk';
|
|
|
3
3
|
import enquirer from 'enquirer';
|
|
4
4
|
import { setCredentials, getCredentials, clearCredentials, saveProfile, loadProfile, deleteProfile, listProfiles, getActiveProfile } from '../utils/config.js';
|
|
5
5
|
import ConfigStore from '../utils/config-store.js';
|
|
6
|
-
import ora from '
|
|
6
|
+
import ora from '../utils/spinner.js';
|
|
7
7
|
import { api } from '../services/api-service.js';
|
|
8
8
|
|
|
9
9
|
export function registerConfigCommand(program: Command) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
import chalk from 'chalk';
|
|
3
|
-
import Table from '
|
|
4
|
-
import ora from '
|
|
3
|
+
import { Table } from 'cmd-table';
|
|
4
|
+
import ora from '../utils/spinner.js';
|
|
5
5
|
import enquirer from 'enquirer';
|
|
6
6
|
import { api } from '../services/api-service.js';
|
|
7
7
|
import { handleCommandError } from '../utils/error-handler.js';
|
|
@@ -96,11 +96,17 @@ export function registerDashboardCommand(program: Command) {
|
|
|
96
96
|
|
|
97
97
|
if (issues.length > 0) {
|
|
98
98
|
const table = new Table({
|
|
99
|
-
|
|
99
|
+
columns: [
|
|
100
|
+
{ name: chalk.bold('Key') },
|
|
101
|
+
{ name: chalk.bold('Type') },
|
|
102
|
+
{ name: chalk.bold('Summary') },
|
|
103
|
+
{ name: chalk.bold('Status') },
|
|
104
|
+
{ name: chalk.bold('Priority') }
|
|
105
|
+
]
|
|
100
106
|
});
|
|
101
107
|
|
|
102
108
|
issues.forEach((i: any) => {
|
|
103
|
-
table.
|
|
109
|
+
table.addRow([
|
|
104
110
|
chalk.cyan(i.key),
|
|
105
111
|
i.fields.issuetype?.name || '',
|
|
106
112
|
i.fields.summary ? (i.fields.summary.length > 40 ? i.fields.summary.substring(0, 37) + '...' : i.fields.summary) : '',
|
|
@@ -108,7 +114,7 @@ export function registerDashboardCommand(program: Command) {
|
|
|
108
114
|
getPriorityColor(i.fields.priority?.name || '', i.fields.priority?.name || '')
|
|
109
115
|
]);
|
|
110
116
|
});
|
|
111
|
-
console.log(table.
|
|
117
|
+
console.log(table.render());
|
|
112
118
|
} else {
|
|
113
119
|
console.log(chalk.green(' 🎉 No open issues — nice work!'));
|
|
114
120
|
}
|
package/src/commands/filter.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
import chalk from 'chalk';
|
|
3
|
-
import Table from '
|
|
3
|
+
import { Table } from 'cmd-table';
|
|
4
4
|
import { api } from '../services/api-service.js';
|
|
5
|
-
import ora from '
|
|
5
|
+
import ora from '../utils/spinner.js';
|
|
6
6
|
import enquirer from 'enquirer';
|
|
7
7
|
import { handleCommandError } from '../utils/error-handler.js';
|
|
8
8
|
import { ConfigService } from '../services/config-service.js';
|
|
@@ -30,14 +30,17 @@ Examples:
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
const table = new Table({
|
|
33
|
-
|
|
33
|
+
columns: [
|
|
34
|
+
{ name: chalk.bold('Name') },
|
|
35
|
+
{ name: chalk.bold('JQL') }
|
|
36
|
+
]
|
|
34
37
|
});
|
|
35
38
|
|
|
36
39
|
for (const [name, jql] of Object.entries(filters)) {
|
|
37
|
-
table.
|
|
40
|
+
table.addRow([chalk.cyan(name), jql as string] as any);
|
|
38
41
|
}
|
|
39
42
|
|
|
40
|
-
console.log(table.
|
|
43
|
+
console.log(table.render());
|
|
41
44
|
});
|
|
42
45
|
|
|
43
46
|
filterCmd
|
package/src/commands/git.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { Command } from 'commander';
|
|
|
2
2
|
import chalk from 'chalk';
|
|
3
3
|
import { execSync } from 'child_process';
|
|
4
4
|
import { api } from '../services/api-service.js';
|
|
5
|
-
import ora from '
|
|
5
|
+
import ora from '../utils/spinner.js';
|
|
6
6
|
import enquirer from 'enquirer';
|
|
7
7
|
import { validateIssueKey } from '../utils/validators.js';
|
|
8
8
|
import { handleCommandError } from '../utils/error-handler.js';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
import chalk from 'chalk';
|
|
3
3
|
import { api } from '../services/api-service.js';
|
|
4
|
-
import ora from '
|
|
4
|
+
import ora from '../utils/spinner.js';
|
|
5
5
|
import fs from 'fs';
|
|
6
6
|
import path from 'path';
|
|
7
7
|
import { validateIssueKey } from '../utils/validators.js';
|
package/src/commands/issue-pr.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { Command } from 'commander';
|
|
|
2
2
|
import chalk from 'chalk';
|
|
3
3
|
import enquirer from 'enquirer';
|
|
4
4
|
import { api } from '../services/api-service.js';
|
|
5
|
-
import ora from '
|
|
5
|
+
import ora from '../utils/spinner.js';
|
|
6
6
|
import { exec } from 'child_process';
|
|
7
7
|
import { promisify } from 'util';
|
|
8
8
|
import { validateIssueKey } from '../utils/validators.js';
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
import chalk from 'chalk';
|
|
3
|
-
import Table from '
|
|
3
|
+
import { Table } from 'cmd-table';
|
|
4
4
|
import { api } from '../services/api-service.js';
|
|
5
|
-
import ora from '
|
|
5
|
+
import ora from '../utils/spinner.js';
|
|
6
6
|
import enquirer from 'enquirer';
|
|
7
7
|
import { textToADF } from '../utils/text-to-adf.js';
|
|
8
8
|
import { validateIssueKey } from '../utils/validators.js';
|
|
@@ -67,11 +67,16 @@ Examples:
|
|
|
67
67
|
console.log(chalk.bold(`\nWorklogs for ${chalk.cyan(issueKey)}:`));
|
|
68
68
|
|
|
69
69
|
const table = new Table({
|
|
70
|
-
|
|
70
|
+
columns: [
|
|
71
|
+
{ name: chalk.bold('Author') },
|
|
72
|
+
{ name: chalk.bold('Time Spent') },
|
|
73
|
+
{ name: chalk.bold('Date') },
|
|
74
|
+
{ name: chalk.bold('Comment') }
|
|
75
|
+
]
|
|
71
76
|
});
|
|
72
77
|
|
|
73
78
|
data.worklogs.forEach((w: any) => {
|
|
74
|
-
table.
|
|
79
|
+
table.addRow([
|
|
75
80
|
w.author?.displayName || 'Unknown',
|
|
76
81
|
w.timeSpent,
|
|
77
82
|
w.started.split('T')[0],
|
|
@@ -79,7 +84,7 @@ Examples:
|
|
|
79
84
|
]);
|
|
80
85
|
});
|
|
81
86
|
|
|
82
|
-
console.log(table.
|
|
87
|
+
console.log(table.render());
|
|
83
88
|
|
|
84
89
|
} catch (e: any) {
|
|
85
90
|
handleCommandError(spinner, e, 'Failed to list worklogs');
|
package/src/commands/issue.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
import chalk from 'chalk';
|
|
3
|
-
import Table from '
|
|
3
|
+
import { Table } from 'cmd-table';
|
|
4
4
|
import { api } from '../services/api-service.js';
|
|
5
5
|
import { aiService } from '../services/ai-service.js';
|
|
6
|
-
import ora from '
|
|
6
|
+
import ora from '../utils/spinner.js';
|
|
7
7
|
import enquirer from 'enquirer';
|
|
8
8
|
import { parseADF } from '../utils/adf-parser.js';
|
|
9
9
|
import { textToADF } from '../utils/text-to-adf.js';
|
|
@@ -134,11 +134,18 @@ Examples:
|
|
|
134
134
|
}
|
|
135
135
|
|
|
136
136
|
const table = new Table({
|
|
137
|
-
|
|
137
|
+
columns: [
|
|
138
|
+
{ name: chalk.bold('Key') },
|
|
139
|
+
{ name: chalk.bold('Summary') },
|
|
140
|
+
{ name: chalk.bold('Status') },
|
|
141
|
+
{ name: chalk.bold('Assignee') },
|
|
142
|
+
{ name: chalk.bold('Created') },
|
|
143
|
+
{ name: chalk.bold('Updated') }
|
|
144
|
+
]
|
|
138
145
|
});
|
|
139
146
|
|
|
140
147
|
data.issues.forEach((i: any) => {
|
|
141
|
-
table.
|
|
148
|
+
table.addRow([
|
|
142
149
|
chalk.cyan(i.key),
|
|
143
150
|
i.fields.summary ? (i.fields.summary.length > 50 ? i.fields.summary.substring(0, 47) + '...' : i.fields.summary) : '',
|
|
144
151
|
i.fields.status ? i.fields.status.name : '',
|
|
@@ -148,7 +155,7 @@ Examples:
|
|
|
148
155
|
]);
|
|
149
156
|
});
|
|
150
157
|
|
|
151
|
-
console.log(table.
|
|
158
|
+
console.log(table.render());
|
|
152
159
|
|
|
153
160
|
} catch (e: any) {
|
|
154
161
|
handleCommandError(spinner, e, 'Failed to list issues');
|
|
@@ -213,7 +220,7 @@ Examples:
|
|
|
213
220
|
if (issue.fields.comment && issue.fields.comment.comments.length > 0) {
|
|
214
221
|
console.log(chalk.bold('\nComments:'));
|
|
215
222
|
issue.fields.comment.comments.forEach((c: any) => {
|
|
216
|
-
console.log(chalk.cyan(c.author.displayName) + ': ' + c.body);
|
|
223
|
+
console.log(chalk.cyan(c.author.displayName) + ': ' + (parseADF(c.body) || ''));
|
|
217
224
|
});
|
|
218
225
|
}
|
|
219
226
|
console.log('');
|
|
@@ -232,6 +239,11 @@ Examples:
|
|
|
232
239
|
.option('-d, --description <text>', 'Issue description')
|
|
233
240
|
.option('--priority <name>', 'Priority name (e.g., High, Medium, Low)')
|
|
234
241
|
.option('-a, --assignee <id>', 'Assignee account ID (use "me" for self)')
|
|
242
|
+
.option('-l, --labels <list>', 'Labels (comma separated)')
|
|
243
|
+
.option('-c, --components <list>', 'Component IDs (comma separated)', (v: string, l: string[]) => l.concat([v]), [])
|
|
244
|
+
.option('--fix-versions <list>', 'Fix Version IDs (comma separated)', (v: string, l: string[]) => l.concat([v]), [])
|
|
245
|
+
.option('--due-date <date>', 'Due Date (YYYY-MM-DD)')
|
|
246
|
+
.option('--no-input', 'Disable interactive prompts for optional fields')
|
|
235
247
|
.option('--custom <key=value>', 'Custom fields (key=value, repeatable)', (v: string, l: string[]) => l.concat([v]), [])
|
|
236
248
|
.addHelpText('after', `
|
|
237
249
|
Examples:
|
|
@@ -339,7 +351,7 @@ Examples:
|
|
|
339
351
|
|
|
340
352
|
// ── Step 5: Priority ────────────────────────────────
|
|
341
353
|
let priorityName = options.priority;
|
|
342
|
-
if (!priorityName) {
|
|
354
|
+
if (!priorityName && !options.noInput) {
|
|
343
355
|
const spinner = ora('Fetching priorities...').start();
|
|
344
356
|
try {
|
|
345
357
|
const priorities = await api.get('/priority');
|
|
@@ -366,73 +378,87 @@ Examples:
|
|
|
366
378
|
}
|
|
367
379
|
|
|
368
380
|
// ── Step 5.5: Components ────────────────────────────
|
|
369
|
-
let componentIds: string[] = [];
|
|
370
|
-
// Interactive only
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
381
|
+
let componentIds: string[] = options.components || [];
|
|
382
|
+
// Interactive only if components not provided and input allowed
|
|
383
|
+
if (componentIds.length === 0 && !options.noInput) {
|
|
384
|
+
const compSpinner = ora('Fetching components...').start();
|
|
385
|
+
try {
|
|
386
|
+
const components = await api.get(`/project/${projectKey}/components`);
|
|
387
|
+
compSpinner.stop();
|
|
388
|
+
|
|
389
|
+
if (Array.isArray(components) && components.length > 0) {
|
|
390
|
+
const { selectedComponents } = await enquirer.prompt({
|
|
391
|
+
type: 'multiselect',
|
|
392
|
+
name: 'selectedComponents',
|
|
393
|
+
message: 'Select Components (Space to select, Enter to confirm):',
|
|
394
|
+
choices: components.map((c: any) => ({ name: c.id, message: c.name }))
|
|
395
|
+
}) as any;
|
|
396
|
+
componentIds = selectedComponents;
|
|
397
|
+
}
|
|
398
|
+
} catch {
|
|
399
|
+
compSpinner.stop();
|
|
384
400
|
}
|
|
385
|
-
} catch {
|
|
386
|
-
compSpinner.stop();
|
|
387
401
|
}
|
|
388
402
|
|
|
389
403
|
// ── Step 5.6: Labels ────────────────────────────────
|
|
390
404
|
let labels: string[] = [];
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
405
|
+
if (options.labels) {
|
|
406
|
+
labels = options.labels.split(',').map((l: string) => l.trim()).filter((l: string) => l.length > 0);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
if (labels.length === 0 && !options.noInput) {
|
|
410
|
+
const { inputLabels } = await enquirer.prompt({
|
|
411
|
+
type: 'input',
|
|
412
|
+
name: 'inputLabels',
|
|
413
|
+
message: 'Labels (comma-separated, optional):'
|
|
414
|
+
}) as any;
|
|
415
|
+
|
|
416
|
+
if (inputLabels && inputLabels.trim().length > 0) {
|
|
417
|
+
labels = inputLabels.split(',').map((l: string) => l.trim()).filter((l: string) => l.length > 0);
|
|
418
|
+
}
|
|
399
419
|
}
|
|
400
420
|
|
|
401
421
|
// ── Step 5.7: Fix Versions ──────────────────────────
|
|
402
|
-
let fixVersionIds: string[] = [];
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
const
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
422
|
+
let fixVersionIds: string[] = options.fixVersions || [];
|
|
423
|
+
|
|
424
|
+
if (fixVersionIds.length === 0 && !options.noInput) {
|
|
425
|
+
const verSpinner = ora('Fetching versions...').start();
|
|
426
|
+
try {
|
|
427
|
+
const versions = await api.get(`/project/${projectKey}/versions`);
|
|
428
|
+
verSpinner.stop();
|
|
429
|
+
|
|
430
|
+
// Filter unreleased versions usually
|
|
431
|
+
const unreleased = versions.filter((v: any) => !v.released);
|
|
432
|
+
|
|
433
|
+
if (Array.isArray(unreleased) && unreleased.length > 0) {
|
|
434
|
+
const { selectedVersions } = await enquirer.prompt({
|
|
435
|
+
type: 'multiselect',
|
|
436
|
+
name: 'selectedVersions',
|
|
437
|
+
message: 'Fix Versions:',
|
|
438
|
+
choices: unreleased.map((v: any) => ({ name: v.id, message: v.name }))
|
|
439
|
+
}) as any;
|
|
440
|
+
fixVersionIds = selectedVersions;
|
|
441
|
+
}
|
|
442
|
+
} catch {
|
|
443
|
+
verSpinner.stop();
|
|
419
444
|
}
|
|
420
|
-
} catch {
|
|
421
|
-
verSpinner.stop();
|
|
422
445
|
}
|
|
423
446
|
|
|
424
447
|
// ── Step 5.8: Due Date ──────────────────────────────
|
|
425
|
-
let duedate: string | null = null;
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
448
|
+
let duedate: string | null = options.dueDate || null;
|
|
449
|
+
|
|
450
|
+
if (!duedate && !options.noInput) {
|
|
451
|
+
const { inputDueDate } = await enquirer.prompt({
|
|
452
|
+
type: 'input',
|
|
453
|
+
name: 'inputDueDate',
|
|
454
|
+
message: 'Due Date (YYYY-MM-DD, optional):',
|
|
455
|
+
validate: (val: string) => {
|
|
456
|
+
if (!val) return true;
|
|
457
|
+
return /^\d{4}-\d{2}-\d{2}$/.test(val) || 'Format must be YYYY-MM-DD';
|
|
458
|
+
}
|
|
459
|
+
}) as any;
|
|
460
|
+
if (inputDueDate) duedate = inputDueDate;
|
|
461
|
+
}
|
|
436
462
|
|
|
437
463
|
// ── Step 6: Assignee ────────────────────────────────
|
|
438
464
|
let assigneeId = options.assignee;
|
|
@@ -510,25 +536,27 @@ Examples:
|
|
|
510
536
|
}
|
|
511
537
|
|
|
512
538
|
// ── Confirmation ────────────────────────────────────
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
539
|
+
if (!options.noInput) {
|
|
540
|
+
console.log(chalk.blue('\n── Issue Summary ──────────────────'));
|
|
541
|
+
console.log(` Project: ${chalk.cyan(projectKey)}`);
|
|
542
|
+
console.log(` Type: ${issueTypeName}`);
|
|
543
|
+
console.log(` Summary: ${summary}`);
|
|
544
|
+
console.log(` Description: ${description || chalk.grey('(none)')}`);
|
|
545
|
+
console.log(` Priority: ${priorityName || chalk.grey('(default)')}`);
|
|
546
|
+
console.log(` Assignee: ${assigneeId || chalk.grey('Unassigned')}`);
|
|
547
|
+
console.log(chalk.blue('──────────────────────────────────\n'));
|
|
548
|
+
|
|
549
|
+
const { confirmed } = await enquirer.prompt({
|
|
550
|
+
type: 'confirm',
|
|
551
|
+
name: 'confirmed',
|
|
552
|
+
message: 'Create this issue?',
|
|
553
|
+
initial: true
|
|
554
|
+
}) as any;
|
|
555
|
+
|
|
556
|
+
if (!confirmed) {
|
|
557
|
+
console.log(chalk.yellow('Issue creation cancelled.'));
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
532
560
|
}
|
|
533
561
|
|
|
534
562
|
// ── Build Request Body ──────────────────────────────
|
|
@@ -1012,17 +1040,22 @@ Examples:
|
|
|
1012
1040
|
}
|
|
1013
1041
|
|
|
1014
1042
|
const table = new Table({
|
|
1015
|
-
|
|
1043
|
+
columns: [
|
|
1044
|
+
{ name: chalk.bold('Key') },
|
|
1045
|
+
{ name: chalk.bold('Summary') },
|
|
1046
|
+
{ name: chalk.bold('Status') },
|
|
1047
|
+
{ name: chalk.bold('Assignee') }
|
|
1048
|
+
]
|
|
1016
1049
|
});
|
|
1017
1050
|
data.issues.forEach((i: any) => {
|
|
1018
|
-
table.
|
|
1051
|
+
table.addRow([
|
|
1019
1052
|
chalk.cyan(i.key),
|
|
1020
1053
|
i.fields.summary ? (i.fields.summary.length > 55 ? i.fields.summary.substring(0, 52) + '...' : i.fields.summary) : '',
|
|
1021
1054
|
i.fields.status?.name || '',
|
|
1022
1055
|
i.fields.assignee?.displayName || 'Unassigned'
|
|
1023
1056
|
]);
|
|
1024
1057
|
});
|
|
1025
|
-
console.log(table.
|
|
1058
|
+
console.log(table.render());
|
|
1026
1059
|
console.log(chalk.grey(`Found ${data.issues.length} result(s)`));
|
|
1027
1060
|
|
|
1028
1061
|
} catch (e) {
|
|
@@ -1136,14 +1169,39 @@ Examples:
|
|
|
1136
1169
|
|
|
1137
1170
|
const spinner = ora(`Fetching parent ${parentKey}...`).start();
|
|
1138
1171
|
try {
|
|
1139
|
-
const parent = await api.get(`/issue/${parentKey}?fields=project,summary`);
|
|
1172
|
+
const parent = await api.get(`/issue/${parentKey}?fields=project,summary,issuetype,id`);
|
|
1140
1173
|
const projectKey = parent.fields.project.key;
|
|
1174
|
+
|
|
1175
|
+
if (parent.fields.issuetype.subtask) {
|
|
1176
|
+
spinner.fail(chalk.red(`Issue ${parentKey} is already a subtask. Cannot create a subtask of a subtask.`));
|
|
1177
|
+
return;
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
if (parent.fields.issuetype.name === 'Epic') {
|
|
1181
|
+
spinner.fail(chalk.red(`Issue ${parentKey} is an Epic. Epics cannot have sub-tasks.`));
|
|
1182
|
+
console.log(chalk.yellow('Tip: To add work to an Epic, create a standard issue (Story, Task) and link it to the Epic.'));
|
|
1183
|
+
return;
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1141
1186
|
spinner.text = 'Fetching subtask types...';
|
|
1142
1187
|
|
|
1143
1188
|
// Get valid subtask types for project
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1189
|
+
let subtaskTypes: any[] = [];
|
|
1190
|
+
try {
|
|
1191
|
+
// Correct V3 endpoint for creation metadata
|
|
1192
|
+
const meta = await api.get(`/issue/createmeta?projectKeys=${projectKey}`);
|
|
1193
|
+
if (meta.projects && meta.projects.length > 0) {
|
|
1194
|
+
subtaskTypes = meta.projects[0].issuetypes.filter((t: any) => t.subtask);
|
|
1195
|
+
}
|
|
1196
|
+
} catch (err) {
|
|
1197
|
+
// Fallback to project fetch
|
|
1198
|
+
try {
|
|
1199
|
+
const proj = await api.get(`/project/${projectKey}`);
|
|
1200
|
+
subtaskTypes = (proj.issueTypes || []).filter((t: any) => t.subtask);
|
|
1201
|
+
} catch (e) {
|
|
1202
|
+
console.error(chalk.red('Failed to fetch project issue types.'));
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1147
1205
|
spinner.stop();
|
|
1148
1206
|
|
|
1149
1207
|
if (subtaskTypes.length === 0) {
|
|
@@ -1183,13 +1241,15 @@ Examples:
|
|
|
1183
1241
|
const issueBody: any = {
|
|
1184
1242
|
fields: {
|
|
1185
1243
|
project: { key: projectKey },
|
|
1186
|
-
parent: {
|
|
1244
|
+
parent: { id: parent.id }, // Use ID instead of Key
|
|
1187
1245
|
issuetype: { id: subtaskTypeId },
|
|
1188
1246
|
summary: summary
|
|
1189
1247
|
}
|
|
1190
1248
|
};
|
|
1191
1249
|
|
|
1192
1250
|
if (priorityName) issueBody.fields.priority = { name: priorityName };
|
|
1251
|
+
// ... rest of assignee logic ...
|
|
1252
|
+
|
|
1193
1253
|
if (assigneeId === 'me') {
|
|
1194
1254
|
const me = await api.get('/myself');
|
|
1195
1255
|
issueBody.fields.assignee = { accountId: me.accountId };
|
package/src/commands/project.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
import chalk from 'chalk';
|
|
3
|
-
import Table from '
|
|
3
|
+
import { Table } from 'cmd-table';
|
|
4
4
|
import { api } from '../services/api-service.js';
|
|
5
|
-
import ora from '
|
|
5
|
+
import ora from '../utils/spinner.js';
|
|
6
6
|
import { handleCommandError } from '../utils/error-handler.js';
|
|
7
7
|
|
|
8
8
|
export function registerProjectCommand(program: Command) {
|
|
@@ -37,11 +37,16 @@ Common Actions:
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
const table = new Table({
|
|
40
|
-
|
|
40
|
+
columns: [
|
|
41
|
+
{ name: chalk.bold('Key') },
|
|
42
|
+
{ name: chalk.bold('Name') },
|
|
43
|
+
{ name: chalk.bold('Leader') },
|
|
44
|
+
{ name: chalk.bold('Style') }
|
|
45
|
+
]
|
|
41
46
|
});
|
|
42
47
|
|
|
43
48
|
data.values.forEach((p: any) => {
|
|
44
|
-
table.
|
|
49
|
+
table.addRow([
|
|
45
50
|
chalk.cyan(p.key),
|
|
46
51
|
p.name,
|
|
47
52
|
p.lead ? p.lead.displayName : 'N/A',
|
|
@@ -49,7 +54,7 @@ Common Actions:
|
|
|
49
54
|
]);
|
|
50
55
|
});
|
|
51
56
|
|
|
52
|
-
console.log(table.
|
|
57
|
+
console.log(table.render());
|
|
53
58
|
} catch (e: any) {
|
|
54
59
|
handleCommandError(spinner, e, 'Failed to list projects');
|
|
55
60
|
}
|