jira-pilot 2.0.4 → 2.1.0

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 (35) hide show
  1. package/README.md +216 -173
  2. package/bin/{jira.js → jira.ts} +10 -1
  3. package/dist/bin/jira.js +64 -0
  4. package/package.json +21 -15
  5. package/src/commands/ai-actions/{plan.js → plan.ts} +9 -9
  6. package/src/commands/ai-actions/{review.js → review.ts} +2 -2
  7. package/src/commands/ai-actions/{standup.js → standup.ts} +4 -4
  8. package/src/commands/{ai.js → ai.ts} +11 -11
  9. package/src/commands/{board.js → board.ts} +11 -11
  10. package/src/commands/bulk.ts +230 -0
  11. package/src/commands/{config.js → config.ts} +57 -8
  12. package/src/commands/dashboard.ts +222 -0
  13. package/src/commands/filter.ts +84 -0
  14. package/src/commands/{git.js → git.ts} +4 -4
  15. package/src/commands/issue-attach.ts +44 -0
  16. package/src/commands/issue-pr.ts +87 -0
  17. package/src/commands/issue-worklog.ts +90 -0
  18. package/src/commands/{issue.js → issue.ts} +359 -68
  19. package/src/commands/{mcp.js → mcp.ts} +2 -2
  20. package/src/commands/{project.js → project.ts} +11 -11
  21. package/src/commands/sprint.ts +269 -0
  22. package/src/server/{mcp-server.js → mcp-server.ts} +235 -8
  23. package/src/services/{ai-service.js → ai-service.ts} +16 -16
  24. package/src/services/{api-service.js → api-service.ts} +33 -9
  25. package/src/services/config-service.ts +21 -0
  26. package/src/types.ts +68 -0
  27. package/src/utils/{adf-parser.js → adf-parser.ts} +12 -12
  28. package/src/utils/config-store.ts +109 -0
  29. package/src/utils/{config.js → config.ts} +14 -41
  30. package/src/utils/{error-handler.js → error-handler.ts} +2 -1
  31. package/src/utils/{text-to-adf.js → text-to-adf.ts} +1 -1
  32. package/src/utils/{validators.js → validators.ts} +4 -4
  33. package/src/commands/bulk.js +0 -108
  34. package/src/commands/dashboard.js +0 -89
  35. package/src/commands/sprint.js +0 -153
@@ -1,108 +0,0 @@
1
- import { Command } from 'commander';
2
- import chalk from 'chalk';
3
- import ora from 'ora';
4
- import enquirer from 'enquirer';
5
- import { api } from '../services/api-service.js';
6
- import { handleCommandError } from '../utils/error-handler.js';
7
-
8
- export function registerBulkCommand(program) {
9
- const bulkCmd = new Command('bulk')
10
- .description('Bulk operations on Jira issues')
11
- .addHelpText('after', `
12
- Common Actions:
13
- $ jira bulk transition -j "project = PROJ AND status = 'To Do'" -s "In Progress"
14
- `);
15
-
16
- // ── BULK TRANSITION ──────────────────────────────────────────────
17
- bulkCmd
18
- .command('transition')
19
- .description('Transition multiple issues matching a JQL filter')
20
- .requiredOption('-j, --jql <query>', 'JQL query to select issues')
21
- .option('-s, --status <name>', 'Target status name')
22
- .option('-y, --yes', 'Skip confirmation prompt')
23
- .option('-l, --limit <n>', 'Max issues to process', '50')
24
- .addHelpText('after', `
25
- Examples:
26
- $ jira bulk transition -j "project = PROJ AND status = 'To Do'" -s "In Progress"
27
- $ jira bulk transition -j "assignee = currentUser() AND status = Review" -s Done -y
28
- `)
29
- .action(async (options) => {
30
- const spinner = ora('Finding matching issues...').start();
31
- try {
32
- const data = await api.post('/search/jql', {
33
- jql: options.jql,
34
- maxResults: parseInt(options.limit),
35
- fields: ['summary', 'status']
36
- });
37
- spinner.stop();
38
-
39
- if (!data.issues || data.issues.length === 0) {
40
- console.log(chalk.yellow('No issues match the query.'));
41
- return;
42
- }
43
-
44
- console.log(chalk.bold(`\nFound ${data.issues.length} issue(s):\n`));
45
- data.issues.forEach(i => {
46
- console.log(` ${chalk.cyan(i.key)} ${i.fields.summary} [${i.fields.status.name}]`);
47
- });
48
-
49
- let targetStatus = options.status;
50
-
51
- if (!targetStatus) {
52
- // Get transitions from the first issue to show available statuses
53
- const transData = await api.get(`/issue/${data.issues[0].key}/transitions`);
54
- const { Select } = enquirer;
55
- const statusSelect = new Select({
56
- name: 'status',
57
- message: 'Target status',
58
- choices: transData.transitions.map(t => ({ name: t.name, message: t.name }))
59
- });
60
- targetStatus = await statusSelect.run();
61
- }
62
-
63
- if (!options.yes) {
64
- const { Confirm } = enquirer;
65
- const confirm = new Confirm({
66
- name: 'proceed',
67
- message: `Transition ${data.issues.length} issue(s) to "${targetStatus}"?`
68
- });
69
- if (!await confirm.run()) {
70
- console.log(chalk.yellow('Cancelled.'));
71
- return;
72
- }
73
- }
74
-
75
- const transSpinner = ora(`Transitioning ${data.issues.length} issue(s)...`).start();
76
- let success = 0;
77
- let failed = 0;
78
-
79
- for (const issue of data.issues) {
80
- try {
81
- const transData = await api.get(`/issue/${issue.key}/transitions`);
82
- const transition = transData.transitions.find(
83
- t => t.name.toLowerCase() === targetStatus.toLowerCase()
84
- );
85
-
86
- if (transition) {
87
- await api.post(`/issue/${issue.key}/transitions`, {
88
- transition: { id: transition.id }
89
- });
90
- success++;
91
- } else {
92
- failed++;
93
- }
94
- } catch {
95
- failed++;
96
- }
97
- transSpinner.text = `Transitioning... (${success + failed}/${data.issues.length})`;
98
- }
99
-
100
- transSpinner.succeed(`Done: ${chalk.green(`${success} succeeded`)}, ${failed > 0 ? chalk.red(`${failed} failed`) : '0 failed'}`);
101
-
102
- } catch (e) {
103
- handleCommandError(spinner, e, 'Bulk transition failed');
104
- }
105
- });
106
-
107
- program.addCommand(bulkCmd);
108
- }
@@ -1,89 +0,0 @@
1
- import { Command } from 'commander';
2
- import chalk from 'chalk';
3
- import { table } from 'table';
4
- import ora from 'ora';
5
- import { api } from '../services/api-service.js';
6
- import { handleCommandError } from '../utils/error-handler.js';
7
-
8
- export function registerDashboardCommand(program) {
9
- program
10
- .command('dashboard')
11
- .description('Show a quick overview of your Jira activity')
12
- .option('-o, --output <format>', 'Output format (json)')
13
- .addHelpText('after', `
14
- Examples:
15
- $ jira dashboard
16
- $ jira dashboard --output json
17
- `)
18
- .action(async (options) => {
19
- const spinner = ora('Loading dashboard...').start();
20
- try {
21
- // Fetch in parallel: my open issues + recently updated
22
- const [myIssues, recentIssues] = await Promise.all([
23
- api.post('/search/jql', {
24
- jql: 'assignee = currentUser() AND statusCategory != Done ORDER BY priority ASC, updated DESC',
25
- maxResults: 8,
26
- fields: ['summary', 'status', 'priority', 'updated']
27
- }),
28
- api.post('/search/jql', {
29
- jql: 'assignee = currentUser() ORDER BY updated DESC',
30
- maxResults: 5,
31
- fields: ['summary', 'status', 'updated']
32
- })
33
- ]);
34
- spinner.stop();
35
-
36
- if (options.output === 'json') {
37
- console.log(JSON.stringify({
38
- openIssues: (myIssues.issues || []).map(i => ({
39
- key: i.key, summary: i.fields.summary,
40
- status: i.fields.status?.name, priority: i.fields.priority?.name
41
- })),
42
- recentActivity: (recentIssues.issues || []).map(i => ({
43
- key: i.key, summary: i.fields.summary,
44
- status: i.fields.status?.name, updated: i.fields.updated
45
- }))
46
- }, null, 2));
47
- return;
48
- }
49
-
50
- // ── Open Issues ──────────────────────────────────────
51
- console.log(chalk.bold('\n📋 Your Open Issues') + chalk.grey(` (${myIssues.total || 0} total)`));
52
-
53
- if (myIssues.issues && myIssues.issues.length > 0) {
54
- const openTable = [
55
- [chalk.bold('Key'), chalk.bold('Summary'), chalk.bold('Status'), chalk.bold('Priority')]
56
- ];
57
- myIssues.issues.forEach(i => {
58
- const prio = i.fields.priority?.name || '';
59
- const prioColor = prio === 'Highest' || prio === 'High' ? chalk.red(prio) : prio === 'Low' || prio === 'Lowest' ? chalk.blue(prio) : prio;
60
- openTable.push([
61
- chalk.cyan(i.key),
62
- i.fields.summary ? (i.fields.summary.length > 50 ? i.fields.summary.substring(0, 47) + '...' : i.fields.summary) : '',
63
- i.fields.status?.name || '',
64
- prioColor
65
- ]);
66
- });
67
- console.log(table(openTable));
68
- } else {
69
- console.log(chalk.green(' 🎉 No open issues — nice work!\n'));
70
- }
71
-
72
- // ── Recent Activity ──────────────────────────────────
73
- console.log(chalk.bold('🕐 Recent Activity'));
74
-
75
- if (recentIssues.issues && recentIssues.issues.length > 0) {
76
- recentIssues.issues.forEach(i => {
77
- const updated = i.fields.updated ? new Date(i.fields.updated).toLocaleDateString() : '';
78
- console.log(` ${chalk.cyan(i.key)} ${i.fields.summary || ''} ${chalk.grey(`[${i.fields.status?.name}] ${updated}`)}`);
79
- });
80
- } else {
81
- console.log(chalk.grey(' No recent activity.'));
82
- }
83
- console.log('');
84
-
85
- } catch (e) {
86
- handleCommandError(spinner, e, 'Failed to load dashboard');
87
- }
88
- });
89
- }
@@ -1,153 +0,0 @@
1
- import { Command } from 'commander';
2
- import chalk from 'chalk';
3
- import { table } from 'table';
4
- import { api } from '../services/api-service.js';
5
- import ora from 'ora';
6
- import { handleCommandError } from '../utils/error-handler.js';
7
-
8
- export function registerSprintCommand(program) {
9
- const sprintCmd = new Command('sprint')
10
- .description('Manage Sprints')
11
- .addHelpText('after', `
12
- Common Actions:
13
- $ jira sprint list --board <ID|Name> # List sprints for a board
14
- `);
15
-
16
- sprintCmd
17
- .command('list')
18
- .description('List sprints for a board')
19
- .requiredOption('-b, --board <id>', 'Board ID or name')
20
- .option('-s, --state <state>', 'State (active, future, closed)', 'active,future')
21
- .action(async (options) => {
22
- const spinner = ora(`Fetching sprints for board ${options.board}...`).start();
23
- try {
24
- let boardId = options.board;
25
-
26
- // If board option is not a number, look it up by name
27
- if (isNaN(boardId)) {
28
- spinner.text = `Looking up board "${options.board}"...`;
29
- const boardData = await api.agileGet(`/board?name=${encodeURIComponent(options.board)}`);
30
-
31
- if (!boardData.values || boardData.values.length === 0) {
32
- throw new Error(`Board with name "${options.board}" not found. Please provide the numeric Board ID.`);
33
- }
34
-
35
- if (boardData.values.length > 1) {
36
- const exact = boardData.values.find(b => b.name.toLowerCase() === options.board.toLowerCase());
37
- if (exact) {
38
- boardId = exact.id;
39
- } else {
40
- console.log(chalk.yellow(`\nMultiple boards found for "${options.board}". Using "${boardData.values[0].name}" (ID: ${boardData.values[0].id}).`));
41
- boardId = boardData.values[0].id;
42
- }
43
- } else {
44
- boardId = boardData.values[0].id;
45
- }
46
- spinner.text = `Fetching sprints for board ${options.board} (ID: ${boardId})...`;
47
- }
48
-
49
- const data = await api.agileGet(`/board/${boardId}/sprint?state=${options.state}`);
50
- spinner.stop();
51
-
52
- if (!data.values || data.values.length === 0) {
53
- console.log(chalk.yellow('No sprints found.'));
54
- return;
55
- }
56
-
57
- const tableData = [
58
- [chalk.bold('ID'), chalk.bold('Name'), chalk.bold('State'), chalk.bold('Dates')]
59
- ];
60
-
61
- data.values.forEach(s => {
62
- tableData.push([
63
- s.id,
64
- s.name,
65
- s.state === 'active' ? chalk.green(s.state) : s.state,
66
- `${s.startDate ? s.startDate.split('T')[0] : ''} -> ${s.endDate ? s.endDate.split('T')[0] : ''}`
67
- ]);
68
- });
69
-
70
- console.log(table(tableData));
71
-
72
- } catch (e) {
73
- handleCommandError(spinner, e, 'Failed to list sprints');
74
- }
75
- });
76
-
77
- // ── SPRINT ISSUES ────────────────────────────────────────────────
78
- sprintCmd
79
- .command('issues')
80
- .description('List issues in the active sprint')
81
- .requiredOption('-b, --board <id>', 'Board ID or name')
82
- .option('-o, --output <format>', 'Output format (json)')
83
- .addHelpText('after', `
84
- Examples:
85
- $ jira sprint issues --board 5
86
- $ jira sprint issues --board "My Board" --output json
87
- `)
88
- .action(async (options) => {
89
- const spinner = ora('Fetching active sprint...').start();
90
- try {
91
- let boardId = options.board;
92
-
93
- if (isNaN(boardId)) {
94
- spinner.text = `Looking up board "${options.board}"...`;
95
- const boardData = await api.agileGet(`/board?name=${encodeURIComponent(options.board)}`);
96
- if (!boardData.values || boardData.values.length === 0) {
97
- throw new Error(`Board "${options.board}" not found.`);
98
- }
99
- boardId = boardData.values[0].id;
100
- }
101
-
102
- // Get active sprint
103
- const sprints = await api.agileGet(`/board/${boardId}/sprint?state=active`);
104
- if (!sprints.values || sprints.values.length === 0) {
105
- spinner.stop();
106
- console.log(chalk.yellow('No active sprint found.'));
107
- return;
108
- }
109
-
110
- const activeSprint = sprints.values[0];
111
- spinner.text = `Fetching issues for sprint "${activeSprint.name}"...`;
112
-
113
- const issues = await api.agileGet(`/sprint/${activeSprint.id}/issue?maxResults=50&fields=summary,status,assignee,priority`);
114
- spinner.stop();
115
-
116
- if (!issues.issues || issues.issues.length === 0) {
117
- console.log(chalk.yellow('No issues in active sprint.'));
118
- return;
119
- }
120
-
121
- console.log(chalk.bold(`\n🏃 Sprint: ${activeSprint.name}\n`));
122
-
123
- if (options.output === 'json') {
124
- console.log(JSON.stringify(issues.issues.map(i => ({
125
- key: i.key, summary: i.fields.summary,
126
- status: i.fields.status?.name, assignee: i.fields.assignee?.displayName || null,
127
- priority: i.fields.priority?.name
128
- })), null, 2));
129
- return;
130
- }
131
-
132
- const tableData = [
133
- [chalk.bold('Key'), chalk.bold('Summary'), chalk.bold('Status'), chalk.bold('Assignee'), chalk.bold('Priority')]
134
- ];
135
- issues.issues.forEach(i => {
136
- tableData.push([
137
- chalk.cyan(i.key),
138
- i.fields.summary ? (i.fields.summary.length > 50 ? i.fields.summary.substring(0, 47) + '...' : i.fields.summary) : '',
139
- i.fields.status?.name || '',
140
- i.fields.assignee?.displayName || 'Unassigned',
141
- i.fields.priority?.name || ''
142
- ]);
143
- });
144
- console.log(table(tableData));
145
- console.log(chalk.grey(`${issues.issues.length} issue(s) in sprint`));
146
-
147
- } catch (e) {
148
- handleCommandError(spinner, e, 'Failed to list sprint issues');
149
- }
150
- });
151
-
152
- program.addCommand(sprintCmd);
153
- }