jira-pilot 2.0.4 → 2.0.5
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 +10 -16
- package/package.json +8 -9
- package/src/commands/board.js +6 -6
- package/src/commands/dashboard.js +6 -5
- package/src/commands/issue.js +11 -11
- package/src/commands/project.js +6 -6
- package/src/commands/sprint.js +11 -11
- package/src/utils/config-store.js +96 -0
- package/src/utils/config.js +2 -33
package/README.md
CHANGED
|
@@ -41,6 +41,9 @@
|
|
|
41
41
|
|
|
42
42
|
## 🚀 Installation
|
|
43
43
|
|
|
44
|
+
### Prerequisites
|
|
45
|
+
- Node.js 20.0.0 or higher
|
|
46
|
+
|
|
44
47
|
### Global Install (Recommended)
|
|
45
48
|
```bash
|
|
46
49
|
npm install -g jira-pilot
|
|
@@ -48,14 +51,6 @@ npm install -g jira-pilot
|
|
|
48
51
|
|
|
49
52
|
After installing, the `jira` command is available globally.
|
|
50
53
|
|
|
51
|
-
### Local Development
|
|
52
|
-
```bash
|
|
53
|
-
git clone https://github.com/Aarul5/jira-pilot.git
|
|
54
|
-
cd jira-pilot
|
|
55
|
-
npm install
|
|
56
|
-
npm link # Makes 'jira' command available globally
|
|
57
|
-
```
|
|
58
|
-
|
|
59
54
|
---
|
|
60
55
|
|
|
61
56
|
## ⚙️ Configuration
|
|
@@ -91,7 +86,7 @@ jira config profiles # List all saved profiles
|
|
|
91
86
|
jira config delete-profile work
|
|
92
87
|
```
|
|
93
88
|
|
|
94
|
-
> **Note:** Credentials are stored securely using
|
|
89
|
+
> **Note:** Credentials are stored securely using a custom `ConfigStore` in your system's config directory.
|
|
95
90
|
|
|
96
91
|
---
|
|
97
92
|
|
|
@@ -451,14 +446,13 @@ Bulk Subcommands:
|
|
|
451
446
|
|
|
452
447
|
## 🤝 Contributing
|
|
453
448
|
|
|
454
|
-
|
|
449
|
+
We welcome contributions! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to submit a pull request and set up your development environment.
|
|
455
450
|
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
```
|
|
451
|
+
Please note that this project is released with a [Contributor Code of Conduct](CODE_OF_CONDUCT.md). By participating in this project you agree to abide by its terms.
|
|
452
|
+
|
|
453
|
+
## 🛡️ Security
|
|
454
|
+
|
|
455
|
+
If you discover a security vulnerability within this project, please check [SECURITY.md](SECURITY.md) for our reporting policy.
|
|
462
456
|
|
|
463
457
|
## 📄 License
|
|
464
458
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jira-pilot",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.5",
|
|
4
4
|
"description": "AI powered Jira CLI and MCP server for humans and agents — manage issues, sprints, boards with interactive wizards, multi-provider AI (OpenAI/Gemini/Anthropic), and an 8-tool MCP server for AI assistants",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"link": "npm link"
|
|
18
18
|
},
|
|
19
19
|
"engines": {
|
|
20
|
-
"node": ">=
|
|
20
|
+
"node": ">=20.0.0"
|
|
21
21
|
},
|
|
22
22
|
"files": [
|
|
23
23
|
"bin/",
|
|
@@ -74,15 +74,14 @@
|
|
|
74
74
|
"homepage": "https://github.com/Aarul5/jira-pilot#readme",
|
|
75
75
|
"license": "ISC",
|
|
76
76
|
"dependencies": {
|
|
77
|
-
"@modelcontextprotocol/sdk": "^
|
|
78
|
-
"axios": "^1.
|
|
77
|
+
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
78
|
+
"axios": "^1.13.5",
|
|
79
79
|
"chalk": "^5.3.0",
|
|
80
|
-
"
|
|
81
|
-
"
|
|
80
|
+
"cli-table3": "^0.6.5",
|
|
81
|
+
"commander": "^14.0.3",
|
|
82
82
|
"enquirer": "^2.4.1",
|
|
83
|
-
"open": "^
|
|
84
|
-
"ora": "^
|
|
85
|
-
"table": "^6.8.0"
|
|
83
|
+
"open": "^11.0.0",
|
|
84
|
+
"ora": "^9.3.0"
|
|
86
85
|
},
|
|
87
86
|
"devDependencies": {
|
|
88
87
|
"@vitest/coverage-v8": "^4.0.18",
|
package/src/commands/board.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
import chalk from 'chalk';
|
|
3
|
-
import
|
|
3
|
+
import Table from 'cli-table3';
|
|
4
4
|
import { api } from '../services/api-service.js';
|
|
5
5
|
import ora from 'ora';
|
|
6
6
|
import { handleCommandError } from '../utils/error-handler.js';
|
|
@@ -50,12 +50,12 @@ Common Actions:
|
|
|
50
50
|
return;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
const
|
|
54
|
-
[chalk.bold('ID'), chalk.bold('Name'), chalk.bold('Type'), chalk.bold('Project')]
|
|
55
|
-
|
|
53
|
+
const table = new Table({
|
|
54
|
+
head: [chalk.bold('ID'), chalk.bold('Name'), chalk.bold('Type'), chalk.bold('Project')]
|
|
55
|
+
});
|
|
56
56
|
|
|
57
57
|
data.values.forEach(b => {
|
|
58
|
-
|
|
58
|
+
table.push([
|
|
59
59
|
b.id,
|
|
60
60
|
b.name,
|
|
61
61
|
b.type,
|
|
@@ -63,7 +63,7 @@ Common Actions:
|
|
|
63
63
|
]);
|
|
64
64
|
});
|
|
65
65
|
|
|
66
|
-
console.log(table(
|
|
66
|
+
console.log(table.toString());
|
|
67
67
|
console.log(chalk.grey(`Showing ${data.values.length} board(s)`));
|
|
68
68
|
|
|
69
69
|
} catch (e) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
import chalk from 'chalk';
|
|
3
|
-
import
|
|
3
|
+
import Table from 'cli-table3';
|
|
4
4
|
import ora from 'ora';
|
|
5
5
|
import { api } from '../services/api-service.js';
|
|
6
6
|
import { handleCommandError } from '../utils/error-handler.js';
|
|
@@ -51,9 +51,10 @@ Examples:
|
|
|
51
51
|
console.log(chalk.bold('\n📋 Your Open Issues') + chalk.grey(` (${myIssues.total || 0} total)`));
|
|
52
52
|
|
|
53
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
|
-
|
|
54
|
+
const openTable = new Table({
|
|
55
|
+
head: [chalk.bold('Key'), chalk.bold('Summary'), chalk.bold('Status'), chalk.bold('Priority')]
|
|
56
|
+
});
|
|
57
|
+
|
|
57
58
|
myIssues.issues.forEach(i => {
|
|
58
59
|
const prio = i.fields.priority?.name || '';
|
|
59
60
|
const prioColor = prio === 'Highest' || prio === 'High' ? chalk.red(prio) : prio === 'Low' || prio === 'Lowest' ? chalk.blue(prio) : prio;
|
|
@@ -64,7 +65,7 @@ Examples:
|
|
|
64
65
|
prioColor
|
|
65
66
|
]);
|
|
66
67
|
});
|
|
67
|
-
console.log(
|
|
68
|
+
console.log(openTable.toString());
|
|
68
69
|
} else {
|
|
69
70
|
console.log(chalk.green(' 🎉 No open issues — nice work!\n'));
|
|
70
71
|
}
|
package/src/commands/issue.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
import chalk from 'chalk';
|
|
3
|
-
import
|
|
3
|
+
import Table from 'cli-table3';
|
|
4
4
|
import { api } from '../services/api-service.js';
|
|
5
5
|
import { aiService } from '../services/ai-service.js';
|
|
6
6
|
import ora from 'ora';
|
|
@@ -128,12 +128,12 @@ Examples:
|
|
|
128
128
|
return;
|
|
129
129
|
}
|
|
130
130
|
|
|
131
|
-
const
|
|
132
|
-
[chalk.bold('Key'), chalk.bold('Summary'), chalk.bold('Status'), chalk.bold('Assignee'), chalk.bold('Created'), chalk.bold('Updated')]
|
|
133
|
-
|
|
131
|
+
const table = new Table({
|
|
132
|
+
head: [chalk.bold('Key'), chalk.bold('Summary'), chalk.bold('Status'), chalk.bold('Assignee'), chalk.bold('Created'), chalk.bold('Updated')]
|
|
133
|
+
});
|
|
134
134
|
|
|
135
135
|
data.issues.forEach(i => {
|
|
136
|
-
|
|
136
|
+
table.push([
|
|
137
137
|
chalk.cyan(i.key),
|
|
138
138
|
i.fields.summary ? (i.fields.summary.length > 50 ? i.fields.summary.substring(0, 47) + '...' : i.fields.summary) : '',
|
|
139
139
|
i.fields.status ? i.fields.status.name : '',
|
|
@@ -143,7 +143,7 @@ Examples:
|
|
|
143
143
|
]);
|
|
144
144
|
});
|
|
145
145
|
|
|
146
|
-
console.log(table(
|
|
146
|
+
console.log(table.toString());
|
|
147
147
|
|
|
148
148
|
} catch (e) {
|
|
149
149
|
handleCommandError(spinner, e, 'Failed to list issues');
|
|
@@ -815,18 +815,18 @@ Examples:
|
|
|
815
815
|
return;
|
|
816
816
|
}
|
|
817
817
|
|
|
818
|
-
const
|
|
819
|
-
[chalk.bold('Key'), chalk.bold('Summary'), chalk.bold('Status'), chalk.bold('Assignee')]
|
|
820
|
-
|
|
818
|
+
const table = new Table({
|
|
819
|
+
head: [chalk.bold('Key'), chalk.bold('Summary'), chalk.bold('Status'), chalk.bold('Assignee')]
|
|
820
|
+
});
|
|
821
821
|
data.issues.forEach(i => {
|
|
822
|
-
|
|
822
|
+
table.push([
|
|
823
823
|
chalk.cyan(i.key),
|
|
824
824
|
i.fields.summary ? (i.fields.summary.length > 55 ? i.fields.summary.substring(0, 52) + '...' : i.fields.summary) : '',
|
|
825
825
|
i.fields.status?.name || '',
|
|
826
826
|
i.fields.assignee?.displayName || 'Unassigned'
|
|
827
827
|
]);
|
|
828
828
|
});
|
|
829
|
-
console.log(table(
|
|
829
|
+
console.log(table.toString());
|
|
830
830
|
console.log(chalk.grey(`Found ${data.issues.length} result(s)`));
|
|
831
831
|
|
|
832
832
|
} catch (e) {
|
package/src/commands/project.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
import chalk from 'chalk';
|
|
3
|
-
import
|
|
3
|
+
import Table from 'cli-table3';
|
|
4
4
|
import { api } from '../services/api-service.js';
|
|
5
5
|
import ora from 'ora';
|
|
6
6
|
import { handleCommandError } from '../utils/error-handler.js';
|
|
@@ -36,12 +36,12 @@ Common Actions:
|
|
|
36
36
|
return;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
const
|
|
40
|
-
[chalk.bold('Key'), chalk.bold('Name'), chalk.bold('Leader'), chalk.bold('Style')]
|
|
41
|
-
|
|
39
|
+
const table = new Table({
|
|
40
|
+
head: [chalk.bold('Key'), chalk.bold('Name'), chalk.bold('Leader'), chalk.bold('Style')]
|
|
41
|
+
});
|
|
42
42
|
|
|
43
43
|
data.values.forEach(p => {
|
|
44
|
-
|
|
44
|
+
table.push([
|
|
45
45
|
chalk.cyan(p.key),
|
|
46
46
|
p.name,
|
|
47
47
|
p.lead ? p.lead.displayName : 'N/A',
|
|
@@ -49,7 +49,7 @@ Common Actions:
|
|
|
49
49
|
]);
|
|
50
50
|
});
|
|
51
51
|
|
|
52
|
-
console.log(table(
|
|
52
|
+
console.log(table.toString());
|
|
53
53
|
} catch (e) {
|
|
54
54
|
handleCommandError(spinner, e, 'Failed to list projects');
|
|
55
55
|
}
|
package/src/commands/sprint.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
import chalk from 'chalk';
|
|
3
|
-
import
|
|
3
|
+
import Table from 'cli-table3';
|
|
4
4
|
import { api } from '../services/api-service.js';
|
|
5
5
|
import ora from 'ora';
|
|
6
6
|
import { handleCommandError } from '../utils/error-handler.js';
|
|
@@ -54,12 +54,12 @@ Common Actions:
|
|
|
54
54
|
return;
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
const
|
|
58
|
-
[chalk.bold('ID'), chalk.bold('Name'), chalk.bold('State'), chalk.bold('Dates')]
|
|
59
|
-
|
|
57
|
+
const table = new Table({
|
|
58
|
+
head: [chalk.bold('ID'), chalk.bold('Name'), chalk.bold('State'), chalk.bold('Dates')]
|
|
59
|
+
});
|
|
60
60
|
|
|
61
61
|
data.values.forEach(s => {
|
|
62
|
-
|
|
62
|
+
table.push([
|
|
63
63
|
s.id,
|
|
64
64
|
s.name,
|
|
65
65
|
s.state === 'active' ? chalk.green(s.state) : s.state,
|
|
@@ -67,7 +67,7 @@ Common Actions:
|
|
|
67
67
|
]);
|
|
68
68
|
});
|
|
69
69
|
|
|
70
|
-
console.log(table(
|
|
70
|
+
console.log(table.toString());
|
|
71
71
|
|
|
72
72
|
} catch (e) {
|
|
73
73
|
handleCommandError(spinner, e, 'Failed to list sprints');
|
|
@@ -129,11 +129,11 @@ Examples:
|
|
|
129
129
|
return;
|
|
130
130
|
}
|
|
131
131
|
|
|
132
|
-
const
|
|
133
|
-
[chalk.bold('Key'), chalk.bold('Summary'), chalk.bold('Status'), chalk.bold('Assignee'), chalk.bold('Priority')]
|
|
134
|
-
|
|
132
|
+
const table = new Table({
|
|
133
|
+
head: [chalk.bold('Key'), chalk.bold('Summary'), chalk.bold('Status'), chalk.bold('Assignee'), chalk.bold('Priority')]
|
|
134
|
+
});
|
|
135
135
|
issues.issues.forEach(i => {
|
|
136
|
-
|
|
136
|
+
table.push([
|
|
137
137
|
chalk.cyan(i.key),
|
|
138
138
|
i.fields.summary ? (i.fields.summary.length > 50 ? i.fields.summary.substring(0, 47) + '...' : i.fields.summary) : '',
|
|
139
139
|
i.fields.status?.name || '',
|
|
@@ -141,7 +141,7 @@ Examples:
|
|
|
141
141
|
i.fields.priority?.name || ''
|
|
142
142
|
]);
|
|
143
143
|
});
|
|
144
|
-
console.log(table(
|
|
144
|
+
console.log(table.toString());
|
|
145
145
|
console.log(chalk.grey(`${issues.issues.length} issue(s) in sprint`));
|
|
146
146
|
|
|
147
147
|
} catch (e) {
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import os from 'os';
|
|
4
|
+
|
|
5
|
+
function getAppDataPath() {
|
|
6
|
+
if (process.platform === 'win32') {
|
|
7
|
+
return process.env.APPDATA || path.join(os.homedir(), 'AppData', 'Roaming');
|
|
8
|
+
}
|
|
9
|
+
if (process.platform === 'darwin') {
|
|
10
|
+
return path.join(os.homedir(), 'Library', 'Preferences');
|
|
11
|
+
}
|
|
12
|
+
return process.env.XDG_CONFIG_HOME || path.join(os.homedir(), '.config');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
class ConfigStore {
|
|
16
|
+
constructor(projectName) {
|
|
17
|
+
this.dir = path.join(getAppDataPath(), projectName);
|
|
18
|
+
this.path = path.join(this.dir, 'config.json');
|
|
19
|
+
this.ensureDir();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
ensureDir() {
|
|
23
|
+
if (!fs.existsSync(this.dir)) {
|
|
24
|
+
fs.mkdirSync(this.dir, { recursive: true });
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
load() {
|
|
29
|
+
try {
|
|
30
|
+
if (!fs.existsSync(this.path)) return {};
|
|
31
|
+
return JSON.parse(fs.readFileSync(this.path, 'utf8'));
|
|
32
|
+
} catch (e) {
|
|
33
|
+
return {};
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
save(data) {
|
|
38
|
+
this.ensureDir();
|
|
39
|
+
fs.writeFileSync(this.path, JSON.stringify(data, null, 4), 'utf8');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
get(key) {
|
|
43
|
+
const data = this.load();
|
|
44
|
+
if (!key) return data;
|
|
45
|
+
|
|
46
|
+
// Support dot notation: profiles.work
|
|
47
|
+
const parts = key.split('.');
|
|
48
|
+
let current = data;
|
|
49
|
+
for (const part of parts) {
|
|
50
|
+
if (current === undefined || current === null) return undefined;
|
|
51
|
+
current = current[part];
|
|
52
|
+
}
|
|
53
|
+
return current;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
set(key, value) {
|
|
57
|
+
const data = this.load();
|
|
58
|
+
|
|
59
|
+
const parts = key.split('.');
|
|
60
|
+
const last = parts.pop();
|
|
61
|
+
|
|
62
|
+
let current = data;
|
|
63
|
+
for (const part of parts) {
|
|
64
|
+
if (typeof current[part] !== 'object' || current[part] === null) {
|
|
65
|
+
current[part] = {};
|
|
66
|
+
}
|
|
67
|
+
current = current[part];
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
current[last] = value;
|
|
71
|
+
this.save(data);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
delete(key) {
|
|
75
|
+
const data = this.load();
|
|
76
|
+
const parts = key.split('.');
|
|
77
|
+
const last = parts.pop();
|
|
78
|
+
|
|
79
|
+
let current = data;
|
|
80
|
+
for (const part of parts) {
|
|
81
|
+
if (current === undefined || current === null) return; // Key doesn't exist
|
|
82
|
+
current = current[part];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (current && typeof current === 'object') {
|
|
86
|
+
delete current[last];
|
|
87
|
+
this.save(data);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
clear() {
|
|
92
|
+
this.save({});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export default ConfigStore;
|
package/src/utils/config.js
CHANGED
|
@@ -1,37 +1,6 @@
|
|
|
1
|
-
import
|
|
1
|
+
import ConfigStore from './config-store.js';
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
jiraUrl: {
|
|
5
|
-
type: 'string',
|
|
6
|
-
format: 'url'
|
|
7
|
-
},
|
|
8
|
-
email: {
|
|
9
|
-
type: 'string',
|
|
10
|
-
format: 'email'
|
|
11
|
-
},
|
|
12
|
-
apiToken: {
|
|
13
|
-
type: 'string'
|
|
14
|
-
},
|
|
15
|
-
aiKey: {
|
|
16
|
-
type: 'string'
|
|
17
|
-
},
|
|
18
|
-
aiProvider: {
|
|
19
|
-
type: 'string',
|
|
20
|
-
default: 'openai'
|
|
21
|
-
},
|
|
22
|
-
aiEnabled: {
|
|
23
|
-
type: 'boolean',
|
|
24
|
-
default: false
|
|
25
|
-
},
|
|
26
|
-
githubToken: {
|
|
27
|
-
type: 'string'
|
|
28
|
-
}
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
const config = new Conf({
|
|
32
|
-
projectName: 'jira-pilot',
|
|
33
|
-
schema
|
|
34
|
-
});
|
|
3
|
+
const config = new ConfigStore('jira-pilot');
|
|
35
4
|
|
|
36
5
|
export const getCredentials = () => {
|
|
37
6
|
return {
|