jira-pilot 2.0.3 → 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 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 the `conf` library in your system's config directory.
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
- Contributions are welcome! Please open an issue or submit a pull request.
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
- ```bash
457
- git clone https://github.com/Aarul5/jira-pilot.git
458
- cd jira-pilot
459
- npm install
460
- npm link
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,7 +1,7 @@
1
1
  {
2
2
  "name": "jira-pilot",
3
- "version": "2.0.3",
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",
3
+ "version": "2.0.5",
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",
7
7
  "bin": {
@@ -17,7 +17,7 @@
17
17
  "link": "npm link"
18
18
  },
19
19
  "engines": {
20
- "node": ">=18.0.0"
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": "^0.6.0",
78
- "axios": "^1.6.0",
77
+ "@modelcontextprotocol/sdk": "^1.26.0",
78
+ "axios": "^1.13.5",
79
79
  "chalk": "^5.3.0",
80
- "commander": "^11.1.0",
81
- "conf": "^12.0.0",
80
+ "cli-table3": "^0.6.5",
81
+ "commander": "^14.0.3",
82
82
  "enquirer": "^2.4.1",
83
- "open": "^10.0.0",
84
- "ora": "^8.0.0",
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",
@@ -1,6 +1,6 @@
1
1
  import { Command } from 'commander';
2
2
  import chalk from 'chalk';
3
- import { table } from 'table';
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 tableData = [
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
- tableData.push([
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(tableData));
66
+ console.log(table.toString());
67
67
  console.log(chalk.grey(`Showing ${data.values.length} board(s)`));
68
68
 
69
69
  } catch (e) {
@@ -49,21 +49,21 @@ export function registerConfigCommand(program) {
49
49
  message: 'Select AI Provider:',
50
50
  choices: ['openai', 'gemini', 'anthropic'],
51
51
  initial: current.aiProvider || 'openai',
52
- skip: (state) => !state.answers.aiEnabled
52
+ skip: function () { return !this.state.answers.aiEnabled; }
53
53
  },
54
54
  {
55
55
  type: 'password',
56
56
  name: 'aiKey',
57
57
  message: 'AI API Key:',
58
58
  initial: current.aiKey ? '*****' : undefined,
59
- skip: (state) => !state.answers.aiEnabled
59
+ skip: function () { return !this.state.answers.aiEnabled; }
60
60
  },
61
61
  {
62
62
  type: 'password',
63
63
  name: 'githubToken',
64
- message: 'GitHub Personal Access Token (for AI Code Review):',
64
+ message: 'GitHub Personal Access Token (for AI Code Review) [Optional, Press Enter to skip]:',
65
65
  initial: current.githubToken ? '*****' : undefined,
66
- skip: (state) => !state.answers.aiEnabled
66
+ skip: function () { return !this.state.answers.aiEnabled; }
67
67
  }
68
68
  ]);
69
69
 
@@ -1,6 +1,6 @@
1
1
  import { Command } from 'commander';
2
2
  import chalk from 'chalk';
3
- import { table } from 'table';
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(table(openTable));
68
+ console.log(openTable.toString());
68
69
  } else {
69
70
  console.log(chalk.green(' 🎉 No open issues — nice work!\n'));
70
71
  }
@@ -1,6 +1,6 @@
1
1
  import { Command } from 'commander';
2
2
  import chalk from 'chalk';
3
- import { table } from 'table';
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 tableData = [
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
- tableData.push([
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(tableData));
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 tableData = [
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
- tableData.push([
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(tableData));
829
+ console.log(table.toString());
830
830
  console.log(chalk.grey(`Found ${data.issues.length} result(s)`));
831
831
 
832
832
  } catch (e) {
@@ -1,6 +1,6 @@
1
1
  import { Command } from 'commander';
2
2
  import chalk from 'chalk';
3
- import { table } from 'table';
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 tableData = [
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
- tableData.push([
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(tableData));
52
+ console.log(table.toString());
53
53
  } catch (e) {
54
54
  handleCommandError(spinner, e, 'Failed to list projects');
55
55
  }
@@ -1,6 +1,6 @@
1
1
  import { Command } from 'commander';
2
2
  import chalk from 'chalk';
3
- import { table } from 'table';
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 tableData = [
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
- tableData.push([
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(tableData));
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 tableData = [
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
- tableData.push([
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(tableData));
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;
@@ -1,37 +1,6 @@
1
- import Conf from 'conf';
1
+ import ConfigStore from './config-store.js';
2
2
 
3
- const schema = {
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 {