jira-pilot 2.0.0 → 2.0.2
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/LICENSE +5 -5
- package/README.md +465 -404
- package/bin/jira.js +62 -55
- package/package.json +90 -84
- package/src/commands/ai-actions/plan.js +119 -0
- package/src/commands/ai-actions/review.js +109 -0
- package/src/commands/ai-actions/standup.js +42 -0
- package/src/commands/ai.js +232 -204
- package/src/commands/board.js +75 -66
- package/src/commands/bulk.js +108 -0
- package/src/commands/config.js +224 -154
- package/src/commands/dashboard.js +89 -0
- package/src/commands/git.js +63 -60
- package/src/commands/issue.js +985 -698
- package/src/commands/mcp.js +20 -20
- package/src/commands/project.js +59 -50
- package/src/commands/sprint.js +153 -78
- package/src/server/mcp-server.js +332 -332
- package/src/services/ai-service.js +165 -107
- package/src/services/api-service.js +115 -115
- package/src/utils/adf-parser.js +49 -49
- package/src/utils/config.js +97 -60
- package/src/utils/error-handler.js +41 -41
- package/src/utils/text-to-adf.js +34 -34
- package/src/utils/validators.js +88 -0
package/src/commands/mcp.js
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
import chalk from 'chalk';
|
|
3
|
-
import { startServer } from '../server/mcp-server.js';
|
|
4
|
-
|
|
5
|
-
export function registerMcpCommand(program) {
|
|
6
|
-
const mcpCmd = new Command('mcp')
|
|
7
|
-
.description('Start MCP Agent Server (Stdio)')
|
|
8
|
-
.action(async () => {
|
|
9
|
-
// MCP server uses stdio, so we shouldn't log anything else to stdout.
|
|
10
|
-
// We can log to stderr if needed.
|
|
11
|
-
try {
|
|
12
|
-
await startServer();
|
|
13
|
-
} catch (e) {
|
|
14
|
-
console.error('MCP Server Error:', e);
|
|
15
|
-
process.exit(1);
|
|
16
|
-
}
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
program.addCommand(mcpCmd);
|
|
20
|
-
}
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { startServer } from '../server/mcp-server.js';
|
|
4
|
+
|
|
5
|
+
export function registerMcpCommand(program) {
|
|
6
|
+
const mcpCmd = new Command('mcp')
|
|
7
|
+
.description('Start MCP Agent Server (Stdio)')
|
|
8
|
+
.action(async () => {
|
|
9
|
+
// MCP server uses stdio, so we shouldn't log anything else to stdout.
|
|
10
|
+
// We can log to stderr if needed.
|
|
11
|
+
try {
|
|
12
|
+
await startServer();
|
|
13
|
+
} catch (e) {
|
|
14
|
+
console.error('MCP Server Error:', e);
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
program.addCommand(mcpCmd);
|
|
20
|
+
}
|
package/src/commands/project.js
CHANGED
|
@@ -1,50 +1,59 @@
|
|
|
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
|
-
|
|
7
|
-
export function registerProjectCommand(program) {
|
|
8
|
-
const projectCmd = new Command('project')
|
|
9
|
-
.description('Manage Jira projects')
|
|
10
|
-
.addHelpText('after', `
|
|
11
|
-
Common Actions:
|
|
12
|
-
$ jira project list # List all projects
|
|
13
|
-
`);
|
|
14
|
-
|
|
15
|
-
projectCmd
|
|
16
|
-
.command('list')
|
|
17
|
-
.description('List accessible projects')
|
|
18
|
-
.
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
]
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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
|
+
|
|
7
|
+
export function registerProjectCommand(program) {
|
|
8
|
+
const projectCmd = new Command('project')
|
|
9
|
+
.description('Manage Jira projects')
|
|
10
|
+
.addHelpText('after', `
|
|
11
|
+
Common Actions:
|
|
12
|
+
$ jira project list # List all projects
|
|
13
|
+
`);
|
|
14
|
+
|
|
15
|
+
projectCmd
|
|
16
|
+
.command('list')
|
|
17
|
+
.description('List accessible projects')
|
|
18
|
+
.option('-o, --output <format>', 'Output format (json)')
|
|
19
|
+
.action(async (options) => {
|
|
20
|
+
const spinner = ora('Fetching projects...').start();
|
|
21
|
+
try {
|
|
22
|
+
const data = await api.get('/project/search');
|
|
23
|
+
spinner.stop();
|
|
24
|
+
|
|
25
|
+
if (!data.values || data.values.length === 0) {
|
|
26
|
+
console.log(chalk.yellow('No projects found.'));
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (options.output === 'json') {
|
|
31
|
+
console.log(JSON.stringify(data.values.map(p => ({
|
|
32
|
+
key: p.key, name: p.name,
|
|
33
|
+
lead: p.lead?.displayName || null, style: p.style
|
|
34
|
+
})), null, 2));
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const tableData = [
|
|
39
|
+
[chalk.bold('Key'), chalk.bold('Name'), chalk.bold('Leader'), chalk.bold('Style')]
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
data.values.forEach(p => {
|
|
43
|
+
tableData.push([
|
|
44
|
+
chalk.cyan(p.key),
|
|
45
|
+
p.name,
|
|
46
|
+
p.lead ? p.lead.displayName : 'N/A',
|
|
47
|
+
p.style
|
|
48
|
+
]);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
console.log(table(tableData));
|
|
52
|
+
} catch (e) {
|
|
53
|
+
spinner.fail('Failed to list projects');
|
|
54
|
+
console.error(e.message);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
program.addCommand(projectCmd);
|
|
59
|
+
}
|
package/src/commands/sprint.js
CHANGED
|
@@ -1,78 +1,153 @@
|
|
|
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
|
-
|
|
78
|
-
|
|
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
|
+
}
|