jira-ai 0.3.8 → 0.3.10
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/dist/cli.js +62 -46
- package/dist/commands/about.js +17 -23
- package/dist/commands/add-comment.js +23 -70
- package/dist/commands/add-label.js +15 -24
- package/dist/commands/auth.js +37 -47
- package/dist/commands/create-task.js +25 -34
- package/dist/commands/delete-label.js +15 -24
- package/dist/commands/list-issue-types.js +11 -18
- package/dist/commands/me.js +11 -18
- package/dist/commands/project-statuses.js +11 -18
- package/dist/commands/projects.js +16 -23
- package/dist/commands/run-jql.js +15 -23
- package/dist/commands/task-with-details.js +11 -18
- package/dist/commands/update-description.js +23 -70
- package/dist/lib/auth-storage.js +17 -26
- package/dist/lib/formatters.js +71 -82
- package/dist/lib/jira-client.js +22 -37
- package/dist/lib/settings.js +28 -40
- package/dist/lib/utils.js +16 -25
- package/dist/types/errors.js +6 -0
- package/package.json +17 -16
package/dist/cli.js
CHANGED
|
@@ -1,38 +1,34 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
};
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const create_task_1 = require("./commands/create-task");
|
|
22
|
-
const about_1 = require("./commands/about");
|
|
23
|
-
const auth_1 = require("./commands/auth");
|
|
24
|
-
const settings_1 = require("./lib/settings");
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import dotenv from 'dotenv';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
import { validateEnvVars } from './lib/utils.js';
|
|
6
|
+
import { meCommand } from './commands/me.js';
|
|
7
|
+
import { projectsCommand } from './commands/projects.js';
|
|
8
|
+
import { taskWithDetailsCommand } from './commands/task-with-details.js';
|
|
9
|
+
import { projectStatusesCommand } from './commands/project-statuses.js';
|
|
10
|
+
import { listIssueTypesCommand } from './commands/list-issue-types.js';
|
|
11
|
+
import { runJqlCommand } from './commands/run-jql.js';
|
|
12
|
+
import { updateDescriptionCommand } from './commands/update-description.js';
|
|
13
|
+
import { addCommentCommand } from './commands/add-comment.js';
|
|
14
|
+
import { addLabelCommand } from './commands/add-label.js';
|
|
15
|
+
import { deleteLabelCommand } from './commands/delete-label.js';
|
|
16
|
+
import { createTaskCommand } from './commands/create-task.js';
|
|
17
|
+
import { aboutCommand } from './commands/about.js';
|
|
18
|
+
import { authCommand } from './commands/auth.js';
|
|
19
|
+
import { isCommandAllowed, getAllowedCommands } from './lib/settings.js';
|
|
20
|
+
import { CliError } from './types/errors.js';
|
|
25
21
|
// Load environment variables
|
|
26
|
-
|
|
22
|
+
dotenv.config();
|
|
27
23
|
// Create CLI program
|
|
28
|
-
const program = new
|
|
24
|
+
const program = new Command();
|
|
29
25
|
program
|
|
30
26
|
.name('jira-ai')
|
|
31
27
|
.description('CLI tool for interacting with Atlassian Jira')
|
|
32
|
-
.version('
|
|
28
|
+
.version('0.3.10');
|
|
33
29
|
// Middleware to validate credentials for commands that need them
|
|
34
30
|
const validateCredentials = () => {
|
|
35
|
-
|
|
31
|
+
validateEnvVars();
|
|
36
32
|
};
|
|
37
33
|
// Helper function to wrap commands with permission check and credential validation
|
|
38
34
|
function withPermission(commandName, commandFn, skipValidation = false) {
|
|
@@ -40,11 +36,10 @@ function withPermission(commandName, commandFn, skipValidation = false) {
|
|
|
40
36
|
if (!skipValidation) {
|
|
41
37
|
validateCredentials();
|
|
42
38
|
}
|
|
43
|
-
if (!
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
process.exit(1);
|
|
39
|
+
if (!isCommandAllowed(commandName)) {
|
|
40
|
+
throw new CliError(`Command '${commandName}' is not allowed.\n` +
|
|
41
|
+
`Allowed commands: ${getAllowedCommands().join(', ')}\n` +
|
|
42
|
+
`Update settings.yaml to enable this command.`);
|
|
48
43
|
}
|
|
49
44
|
return commandFn(...args);
|
|
50
45
|
};
|
|
@@ -55,61 +50,61 @@ program
|
|
|
55
50
|
.description('Set up Jira authentication credentials')
|
|
56
51
|
.option('--from-json <json_string>', 'Accepts a raw JSON string with credentials')
|
|
57
52
|
.option('--from-file <path>', 'Accepts a path to a file (typically .env) with credentials')
|
|
58
|
-
.action((options) =>
|
|
53
|
+
.action((options) => authCommand(options));
|
|
59
54
|
// Me command
|
|
60
55
|
program
|
|
61
56
|
.command('me')
|
|
62
57
|
.description('Show basic user information')
|
|
63
|
-
.action(withPermission('me',
|
|
58
|
+
.action(withPermission('me', meCommand));
|
|
64
59
|
// Projects command
|
|
65
60
|
program
|
|
66
61
|
.command('projects')
|
|
67
62
|
.description('Show list of projects')
|
|
68
|
-
.action(withPermission('projects',
|
|
63
|
+
.action(withPermission('projects', projectsCommand));
|
|
69
64
|
// Task with details command
|
|
70
65
|
program
|
|
71
66
|
.command('task-with-details <task-id>')
|
|
72
67
|
.description('Show task title, body, and comments')
|
|
73
|
-
.action(withPermission('task-with-details',
|
|
68
|
+
.action(withPermission('task-with-details', taskWithDetailsCommand));
|
|
74
69
|
// Project statuses command
|
|
75
70
|
program
|
|
76
71
|
.command('project-statuses <project-id>')
|
|
77
72
|
.description('Show all possible statuses for a project')
|
|
78
|
-
.action(withPermission('project-statuses',
|
|
73
|
+
.action(withPermission('project-statuses', projectStatusesCommand));
|
|
79
74
|
// List issue types command
|
|
80
75
|
program
|
|
81
76
|
.command('list-issue-types <project-key>')
|
|
82
77
|
.description('Show all issue types for a project')
|
|
83
|
-
.action(withPermission('list-issue-types',
|
|
78
|
+
.action(withPermission('list-issue-types', listIssueTypesCommand));
|
|
84
79
|
// Run JQL command
|
|
85
80
|
program
|
|
86
81
|
.command('run-jql <jql-query>')
|
|
87
82
|
.description('Execute JQL query and display results')
|
|
88
83
|
.option('-l, --limit <number>', 'Maximum number of results (default: 50)', '50')
|
|
89
|
-
.action(withPermission('run-jql',
|
|
84
|
+
.action(withPermission('run-jql', runJqlCommand));
|
|
90
85
|
// Update description command
|
|
91
86
|
program
|
|
92
87
|
.command('update-description <task-id>')
|
|
93
88
|
.description('Update task description from a Markdown file')
|
|
94
89
|
.requiredOption('--from-file <path>', 'Path to Markdown file')
|
|
95
|
-
.action(withPermission('update-description',
|
|
90
|
+
.action(withPermission('update-description', updateDescriptionCommand));
|
|
96
91
|
// Add comment command
|
|
97
92
|
program
|
|
98
93
|
.command('add-comment')
|
|
99
94
|
.description('Add a comment to a Jira issue from a Markdown file')
|
|
100
95
|
.requiredOption('--file-path <path>', 'Path to Markdown file')
|
|
101
96
|
.requiredOption('--issue-key <key>', 'Jira issue key (e.g., PS-123)')
|
|
102
|
-
.action(withPermission('add-comment',
|
|
97
|
+
.action(withPermission('add-comment', addCommentCommand));
|
|
103
98
|
// Add label command
|
|
104
99
|
program
|
|
105
100
|
.command('add-label-to-issue <task-id> <labels>')
|
|
106
101
|
.description('Add one or more labels to a Jira issue (comma-separated)')
|
|
107
|
-
.action(withPermission('add-label-to-issue',
|
|
102
|
+
.action(withPermission('add-label-to-issue', addLabelCommand));
|
|
108
103
|
// Delete label command
|
|
109
104
|
program
|
|
110
105
|
.command('delete-label-from-issue <task-id> <labels>')
|
|
111
106
|
.description('Remove one or more labels from a Jira issue (comma-separated)')
|
|
112
|
-
.action(withPermission('delete-label-from-issue',
|
|
107
|
+
.action(withPermission('delete-label-from-issue', deleteLabelCommand));
|
|
113
108
|
// Create task command
|
|
114
109
|
program
|
|
115
110
|
.command('create-task')
|
|
@@ -118,11 +113,32 @@ program
|
|
|
118
113
|
.requiredOption('--project <project>', 'Project key (e.g., PROJ)')
|
|
119
114
|
.requiredOption('--issue-type <type>', 'Issue type (e.g., Task, Epic, Subtask)')
|
|
120
115
|
.option('--parent <key>', 'Parent issue key (required for subtasks)')
|
|
121
|
-
.action(withPermission('create-task',
|
|
116
|
+
.action(withPermission('create-task', createTaskCommand));
|
|
122
117
|
// About command (always allowed)
|
|
123
118
|
program
|
|
124
119
|
.command('about')
|
|
125
120
|
.description('Show information about available commands')
|
|
126
|
-
.action(
|
|
121
|
+
.action(aboutCommand);
|
|
127
122
|
// Parse command line arguments
|
|
128
|
-
|
|
123
|
+
async function main() {
|
|
124
|
+
try {
|
|
125
|
+
await program.parseAsync(process.argv);
|
|
126
|
+
}
|
|
127
|
+
catch (error) {
|
|
128
|
+
if (error instanceof CliError) {
|
|
129
|
+
console.error(chalk.red(`\n❌ ${error.message}\n`));
|
|
130
|
+
}
|
|
131
|
+
else if (error instanceof Error) {
|
|
132
|
+
console.error(chalk.red(`\n💥 Unexpected Error: ${error.message}`));
|
|
133
|
+
if (process.env.DEBUG) {
|
|
134
|
+
console.error(chalk.gray(error.stack));
|
|
135
|
+
}
|
|
136
|
+
console.error(chalk.gray('\nPlease report this issue if it persists.\n'));
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
console.error(chalk.red(`\n💥 An unknown error occurred: ${String(error)}\n`));
|
|
140
|
+
}
|
|
141
|
+
process.exit(1);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
main();
|
package/dist/commands/about.js
CHANGED
|
@@ -1,11 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.aboutCommand = aboutCommand;
|
|
7
|
-
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
-
const settings_1 = require("../lib/settings");
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { getAllowedCommands, getAllowedProjects, isCommandAllowed, getSettingsPath } from '../lib/settings.js';
|
|
9
3
|
const ALL_COMMANDS = [
|
|
10
4
|
{
|
|
11
5
|
name: 'auth',
|
|
@@ -63,36 +57,36 @@ const ALL_COMMANDS = [
|
|
|
63
57
|
usage: 'jira-ai about'
|
|
64
58
|
}
|
|
65
59
|
];
|
|
66
|
-
async function aboutCommand() {
|
|
67
|
-
console.log(
|
|
68
|
-
console.log(
|
|
60
|
+
export async function aboutCommand() {
|
|
61
|
+
console.log(chalk.bold.cyan('\n📋 Jira AI - Available Commands\n'));
|
|
62
|
+
console.log(chalk.bold('Usage:'));
|
|
69
63
|
console.log(' jira-ai <command> [options]\n');
|
|
70
|
-
const allowedCommandsList =
|
|
64
|
+
const allowedCommandsList = getAllowedCommands();
|
|
71
65
|
const isAllAllowed = allowedCommandsList.includes('all');
|
|
72
66
|
// Filter commands based on settings (about is always shown)
|
|
73
|
-
const commandsToShow = ALL_COMMANDS.filter(cmd => cmd.name === 'about' || isAllAllowed ||
|
|
74
|
-
console.log(
|
|
67
|
+
const commandsToShow = ALL_COMMANDS.filter(cmd => cmd.name === 'about' || isAllAllowed || isCommandAllowed(cmd.name));
|
|
68
|
+
console.log(chalk.bold('Available Commands:\n'));
|
|
75
69
|
for (const cmd of commandsToShow) {
|
|
76
|
-
console.log(
|
|
70
|
+
console.log(chalk.yellow(` ${cmd.name}`));
|
|
77
71
|
console.log(` ${cmd.description}`);
|
|
78
72
|
console.log(` Usage: ${cmd.usage}\n`);
|
|
79
73
|
}
|
|
80
74
|
// Show disabled commands if not all are allowed
|
|
81
75
|
if (!isAllAllowed) {
|
|
82
|
-
const disabledCommands = ALL_COMMANDS.filter(cmd => cmd.name !== 'about' && !
|
|
76
|
+
const disabledCommands = ALL_COMMANDS.filter(cmd => cmd.name !== 'about' && !isCommandAllowed(cmd.name));
|
|
83
77
|
if (disabledCommands.length > 0) {
|
|
84
|
-
console.log(
|
|
78
|
+
console.log(chalk.bold('Disabled Commands:\n'));
|
|
85
79
|
for (const cmd of disabledCommands) {
|
|
86
|
-
console.log(
|
|
80
|
+
console.log(chalk.gray(` ${cmd.name} - ${cmd.description}`));
|
|
87
81
|
}
|
|
88
82
|
console.log();
|
|
89
83
|
}
|
|
90
84
|
}
|
|
91
|
-
console.log(
|
|
92
|
-
console.log(
|
|
93
|
-
console.log(
|
|
94
|
-
console.log(` Settings file: ${
|
|
95
|
-
const allowedProjects =
|
|
85
|
+
console.log(chalk.bold('For detailed help on any command, run:'));
|
|
86
|
+
console.log(chalk.green(' jira-ai <command> --help\n'));
|
|
87
|
+
console.log(chalk.bold('Configuration:'));
|
|
88
|
+
console.log(` Settings file: ${chalk.cyan(getSettingsPath())}`);
|
|
89
|
+
const allowedProjects = getAllowedProjects();
|
|
96
90
|
console.log(` - Projects: ${allowedProjects.includes('all') ? 'All allowed' : allowedProjects.join(', ')}`);
|
|
97
91
|
console.log(` - Commands: ${isAllAllowed ? 'All allowed' : allowedCommandsList.join(', ')}\n`);
|
|
98
92
|
}
|
|
@@ -1,66 +1,25 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
-
};
|
|
38
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
exports.addCommentCommand = addCommentCommand;
|
|
40
|
-
const chalk_1 = __importDefault(require("chalk"));
|
|
41
|
-
const ora_1 = __importDefault(require("ora"));
|
|
42
|
-
const fs = __importStar(require("fs"));
|
|
43
|
-
const path = __importStar(require("path"));
|
|
44
|
-
const marklassian_1 = require("marklassian");
|
|
45
|
-
const jira_client_1 = require("../lib/jira-client");
|
|
46
|
-
async function addCommentCommand(options) {
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import ora from 'ora';
|
|
3
|
+
import * as fs from 'fs';
|
|
4
|
+
import * as path from 'path';
|
|
5
|
+
import { markdownToAdf } from 'marklassian';
|
|
6
|
+
import { addIssueComment } from '../lib/jira-client.js';
|
|
7
|
+
import { CliError } from '../types/errors.js';
|
|
8
|
+
export async function addCommentCommand(options) {
|
|
47
9
|
const { filePath, issueKey } = options;
|
|
48
10
|
// Validate issueKey
|
|
49
11
|
if (!issueKey || issueKey.trim() === '') {
|
|
50
|
-
|
|
51
|
-
process.exit(1);
|
|
12
|
+
throw new CliError('Issue key is required (use --issue-key)');
|
|
52
13
|
}
|
|
53
14
|
// Validate file path
|
|
54
15
|
if (!filePath || filePath.trim() === '') {
|
|
55
|
-
|
|
56
|
-
process.exit(1);
|
|
16
|
+
throw new CliError('File path is required (use --file-path)');
|
|
57
17
|
}
|
|
58
18
|
// Resolve file path to absolute
|
|
59
19
|
const absolutePath = path.resolve(filePath);
|
|
60
20
|
// Check file exists
|
|
61
21
|
if (!fs.existsSync(absolutePath)) {
|
|
62
|
-
|
|
63
|
-
process.exit(1);
|
|
22
|
+
throw new CliError(`File not found: ${absolutePath}`);
|
|
64
23
|
}
|
|
65
24
|
// Read file
|
|
66
25
|
let markdownContent;
|
|
@@ -68,42 +27,36 @@ async function addCommentCommand(options) {
|
|
|
68
27
|
markdownContent = fs.readFileSync(absolutePath, 'utf-8');
|
|
69
28
|
}
|
|
70
29
|
catch (error) {
|
|
71
|
-
|
|
72
|
-
(error instanceof Error ? error.message : 'Unknown error')));
|
|
73
|
-
process.exit(1);
|
|
30
|
+
throw new CliError(`Error reading file: ${error instanceof Error ? error.message : String(error)}`);
|
|
74
31
|
}
|
|
75
32
|
// Validate file is not empty
|
|
76
33
|
if (markdownContent.trim() === '') {
|
|
77
|
-
|
|
78
|
-
process.exit(1);
|
|
34
|
+
throw new CliError('File is empty');
|
|
79
35
|
}
|
|
80
36
|
// Convert Markdown to ADF
|
|
81
37
|
let adfContent;
|
|
82
38
|
try {
|
|
83
|
-
adfContent =
|
|
39
|
+
adfContent = markdownToAdf(markdownContent);
|
|
84
40
|
}
|
|
85
41
|
catch (error) {
|
|
86
|
-
|
|
87
|
-
(error instanceof Error ? error.message : 'Unknown error')));
|
|
88
|
-
process.exit(1);
|
|
42
|
+
throw new CliError(`Error converting Markdown to ADF: ${error instanceof Error ? error.message : String(error)}`);
|
|
89
43
|
}
|
|
90
44
|
// Add comment with spinner
|
|
91
|
-
const spinner = (
|
|
45
|
+
const spinner = ora(`Adding comment to ${issueKey}...`).start();
|
|
92
46
|
try {
|
|
93
|
-
await
|
|
94
|
-
spinner.succeed(
|
|
95
|
-
console.log(
|
|
47
|
+
await addIssueComment(issueKey, adfContent);
|
|
48
|
+
spinner.succeed(chalk.green(`Comment added successfully to ${issueKey}`));
|
|
49
|
+
console.log(chalk.gray(`\nFile: ${absolutePath}`));
|
|
96
50
|
}
|
|
97
51
|
catch (error) {
|
|
98
|
-
spinner.fail(
|
|
99
|
-
console.error(chalk_1.default.red('\nError: ' + (error instanceof Error ? error.message : 'Unknown error')));
|
|
52
|
+
spinner.fail(chalk.red('Failed to add comment'));
|
|
100
53
|
// Provide helpful hints based on error
|
|
101
54
|
if (error instanceof Error && error.message.includes('404')) {
|
|
102
|
-
console.log(
|
|
55
|
+
console.log(chalk.yellow('\nHint: Check that the issue key is correct'));
|
|
103
56
|
}
|
|
104
57
|
else if (error instanceof Error && error.message.includes('403')) {
|
|
105
|
-
console.log(
|
|
58
|
+
console.log(chalk.yellow('\nHint: You may not have permission to comment on this issue'));
|
|
106
59
|
}
|
|
107
|
-
|
|
60
|
+
throw error;
|
|
108
61
|
}
|
|
109
62
|
}
|
|
@@ -1,40 +1,31 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
};
|
|
5
|
-
|
|
6
|
-
exports.addLabelCommand = addLabelCommand;
|
|
7
|
-
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
-
const ora_1 = __importDefault(require("ora"));
|
|
9
|
-
const jira_client_1 = require("../lib/jira-client");
|
|
10
|
-
async function addLabelCommand(taskId, labelsString) {
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import ora from 'ora';
|
|
3
|
+
import { addIssueLabels } from '../lib/jira-client.js';
|
|
4
|
+
import { CliError } from '../types/errors.js';
|
|
5
|
+
export async function addLabelCommand(taskId, labelsString) {
|
|
11
6
|
// Validate input
|
|
12
7
|
if (!taskId || taskId.trim() === '') {
|
|
13
|
-
|
|
14
|
-
process.exit(1);
|
|
8
|
+
throw new CliError('Task ID is required');
|
|
15
9
|
}
|
|
16
10
|
if (!labelsString || labelsString.trim() === '') {
|
|
17
|
-
|
|
18
|
-
process.exit(1);
|
|
11
|
+
throw new CliError('Labels are required (comma-separated)');
|
|
19
12
|
}
|
|
20
13
|
// Parse labels
|
|
21
14
|
const labels = labelsString.split(',').map(l => l.trim()).filter(l => l !== '');
|
|
22
15
|
if (labels.length === 0) {
|
|
23
|
-
|
|
24
|
-
process.exit(1);
|
|
16
|
+
throw new CliError('No valid labels provided');
|
|
25
17
|
}
|
|
26
|
-
const spinner = (
|
|
18
|
+
const spinner = ora(`Adding labels to ${taskId}...`).start();
|
|
27
19
|
try {
|
|
28
|
-
await
|
|
29
|
-
spinner.succeed(
|
|
30
|
-
console.log(
|
|
20
|
+
await addIssueLabels(taskId, labels);
|
|
21
|
+
spinner.succeed(chalk.green(`Labels added successfully to ${taskId}`));
|
|
22
|
+
console.log(chalk.gray(`\nLabels: ${labels.join(', ')}`));
|
|
31
23
|
}
|
|
32
24
|
catch (error) {
|
|
33
|
-
spinner.fail(
|
|
34
|
-
console.error(chalk_1.default.red('\nError: ' + (error instanceof Error ? error.message : 'Unknown error')));
|
|
25
|
+
spinner.fail(chalk.red('Failed to add labels'));
|
|
35
26
|
if (error instanceof Error && error.message.includes('404')) {
|
|
36
|
-
console.log(
|
|
27
|
+
console.log(chalk.yellow('\nHint: Check that the issue ID/key is correct'));
|
|
37
28
|
}
|
|
38
|
-
|
|
29
|
+
throw error;
|
|
39
30
|
}
|
|
40
31
|
}
|
package/dist/commands/auth.js
CHANGED
|
@@ -1,17 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const
|
|
10
|
-
const fs_1 = __importDefault(require("fs"));
|
|
11
|
-
const dotenv_1 = __importDefault(require("dotenv"));
|
|
12
|
-
const jira_client_1 = require("../lib/jira-client");
|
|
13
|
-
const auth_storage_1 = require("../lib/auth-storage");
|
|
14
|
-
const rl = readline_1.default.createInterface({
|
|
1
|
+
import readline from 'readline';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import ora from 'ora';
|
|
4
|
+
import fs from 'fs';
|
|
5
|
+
import dotenv from 'dotenv';
|
|
6
|
+
import { createTemporaryClient } from '../lib/jira-client.js';
|
|
7
|
+
import { saveCredentials } from '../lib/auth-storage.js';
|
|
8
|
+
import { CliError } from '../types/errors.js';
|
|
9
|
+
const rl = readline.createInterface({
|
|
15
10
|
input: process.stdin,
|
|
16
11
|
output: process.stdout,
|
|
17
12
|
});
|
|
@@ -35,7 +30,7 @@ function askSecret(question) {
|
|
|
35
30
|
});
|
|
36
31
|
});
|
|
37
32
|
}
|
|
38
|
-
async function authCommand(options = {}) {
|
|
33
|
+
export async function authCommand(options = {}) {
|
|
39
34
|
let host = '';
|
|
40
35
|
let email = '';
|
|
41
36
|
let apiToken = '';
|
|
@@ -46,54 +41,50 @@ async function authCommand(options = {}) {
|
|
|
46
41
|
email = data.email;
|
|
47
42
|
apiToken = data.apikey || data.apiToken;
|
|
48
43
|
if (!host || !email || !apiToken) {
|
|
49
|
-
|
|
50
|
-
process.exit(1);
|
|
44
|
+
throw new CliError('Missing required fields in JSON. Required: url/host, email, apikey/apiToken.');
|
|
51
45
|
}
|
|
52
46
|
}
|
|
53
47
|
catch (error) {
|
|
54
|
-
|
|
55
|
-
|
|
48
|
+
if (error instanceof CliError)
|
|
49
|
+
throw error;
|
|
50
|
+
throw new CliError(`Invalid JSON string: ${error.message}`);
|
|
56
51
|
}
|
|
57
52
|
}
|
|
58
53
|
else if (options.fromFile) {
|
|
59
|
-
if (!
|
|
60
|
-
|
|
61
|
-
process.exit(1);
|
|
54
|
+
if (!fs.existsSync(options.fromFile)) {
|
|
55
|
+
throw new CliError(`File not found: ${options.fromFile}`);
|
|
62
56
|
}
|
|
63
57
|
try {
|
|
64
|
-
const content =
|
|
65
|
-
const config =
|
|
58
|
+
const content = fs.readFileSync(options.fromFile, 'utf8');
|
|
59
|
+
const config = dotenv.parse(content);
|
|
66
60
|
host = config.JIRA_HOST;
|
|
67
61
|
email = config.JIRA_USER_EMAIL;
|
|
68
62
|
apiToken = config.JIRA_API_TOKEN;
|
|
69
63
|
if (!host || !email || !apiToken) {
|
|
70
|
-
|
|
71
|
-
process.exit(1);
|
|
64
|
+
throw new CliError('Missing required environment variables in file. Required: JIRA_HOST, JIRA_USER_EMAIL, JIRA_API_TOKEN.');
|
|
72
65
|
}
|
|
73
66
|
}
|
|
74
67
|
catch (error) {
|
|
75
|
-
|
|
76
|
-
|
|
68
|
+
if (error instanceof CliError)
|
|
69
|
+
throw error;
|
|
70
|
+
throw new CliError(`Failed to parse file: ${error.message}`);
|
|
77
71
|
}
|
|
78
72
|
}
|
|
79
73
|
if (!host || !email || !apiToken) {
|
|
80
|
-
console.log(
|
|
74
|
+
console.log(chalk.cyan('\n--- Jira Authentication Setup ---\n'));
|
|
81
75
|
try {
|
|
82
76
|
host = await ask('Jira URL (e.g., https://your-domain.atlassian.net): ');
|
|
83
77
|
if (!host) {
|
|
84
|
-
|
|
85
|
-
process.exit(1);
|
|
78
|
+
throw new CliError('URL is required.');
|
|
86
79
|
}
|
|
87
80
|
email = await ask('Email: ');
|
|
88
81
|
if (!email) {
|
|
89
|
-
|
|
90
|
-
process.exit(1);
|
|
82
|
+
throw new CliError('Email is required.');
|
|
91
83
|
}
|
|
92
|
-
console.log(
|
|
84
|
+
console.log(chalk.gray('Get your API token from: https://id.atlassian.com/manage-profile/security/api-tokens'));
|
|
93
85
|
apiToken = await ask('API Token: ');
|
|
94
86
|
if (!apiToken) {
|
|
95
|
-
|
|
96
|
-
process.exit(1);
|
|
87
|
+
throw new CliError('API Token is required.');
|
|
97
88
|
}
|
|
98
89
|
}
|
|
99
90
|
finally {
|
|
@@ -104,22 +95,21 @@ async function authCommand(options = {}) {
|
|
|
104
95
|
// Non-interactive mode, just close readline
|
|
105
96
|
rl.close();
|
|
106
97
|
}
|
|
107
|
-
const spinner = (
|
|
98
|
+
const spinner = ora('Verifying credentials...').start();
|
|
108
99
|
try {
|
|
109
|
-
const tempClient =
|
|
100
|
+
const tempClient = createTemporaryClient(host, email, apiToken);
|
|
110
101
|
const user = await tempClient.myself.getCurrentUser();
|
|
111
|
-
spinner.succeed(
|
|
112
|
-
console.log(
|
|
113
|
-
|
|
114
|
-
console.log(
|
|
115
|
-
console.log(
|
|
102
|
+
spinner.succeed(chalk.green('Authentication successful!'));
|
|
103
|
+
console.log(chalk.blue(`\nWelcome, ${user.displayName} (${user.emailAddress})`));
|
|
104
|
+
saveCredentials({ host, email, apiToken });
|
|
105
|
+
console.log(chalk.green('\nCredentials saved successfully to ~/.jira-ai/config.json'));
|
|
106
|
+
console.log(chalk.gray('These credentials will be used for future commands on this machine.'));
|
|
116
107
|
}
|
|
117
108
|
catch (error) {
|
|
118
|
-
spinner.fail(
|
|
119
|
-
console.error(chalk_1.default.red(`Error: ${error.message || 'Invalid credentials'}`));
|
|
109
|
+
spinner.fail(chalk.red('Authentication failed.'));
|
|
120
110
|
if (error.response && error.response.status === 401) {
|
|
121
|
-
console.error(
|
|
111
|
+
console.error(chalk.yellow('Hint: Check if your email and API token are correct.'));
|
|
122
112
|
}
|
|
123
|
-
|
|
113
|
+
throw error;
|
|
124
114
|
}
|
|
125
115
|
}
|