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 CHANGED
@@ -1,38 +1,34 @@
1
1
  #!/usr/bin/env node
2
- "use strict";
3
- var __importDefault = (this && this.__importDefault) || function (mod) {
4
- return (mod && mod.__esModule) ? mod : { "default": mod };
5
- };
6
- Object.defineProperty(exports, "__esModule", { value: true });
7
- const commander_1 = require("commander");
8
- const dotenv_1 = __importDefault(require("dotenv"));
9
- const chalk_1 = __importDefault(require("chalk"));
10
- const utils_1 = require("./lib/utils");
11
- const me_1 = require("./commands/me");
12
- const projects_1 = require("./commands/projects");
13
- const task_with_details_1 = require("./commands/task-with-details");
14
- const project_statuses_1 = require("./commands/project-statuses");
15
- const list_issue_types_1 = require("./commands/list-issue-types");
16
- const run_jql_1 = require("./commands/run-jql");
17
- const update_description_1 = require("./commands/update-description");
18
- const add_comment_1 = require("./commands/add-comment");
19
- const add_label_1 = require("./commands/add-label");
20
- const delete_label_1 = require("./commands/delete-label");
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
- dotenv_1.default.config();
22
+ dotenv.config();
27
23
  // Create CLI program
28
- const program = new commander_1.Command();
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('1.0.0');
28
+ .version('0.3.10');
33
29
  // Middleware to validate credentials for commands that need them
34
30
  const validateCredentials = () => {
35
- (0, utils_1.validateEnvVars)();
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 (!(0, settings_1.isCommandAllowed)(commandName)) {
44
- console.error(chalk_1.default.red(`\n❌ Command '${commandName}' is not allowed.`));
45
- console.log(chalk_1.default.gray('Allowed commands: ' + (0, settings_1.getAllowedCommands)().join(', ')));
46
- console.log(chalk_1.default.gray('Update settings.yaml to enable this command.\n'));
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) => (0, auth_1.authCommand)(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', me_1.meCommand));
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', projects_1.projectsCommand));
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', task_with_details_1.taskWithDetailsCommand));
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', project_statuses_1.projectStatusesCommand));
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', list_issue_types_1.listIssueTypesCommand));
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', run_jql_1.runJqlCommand));
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', update_description_1.updateDescriptionCommand));
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', add_comment_1.addCommentCommand));
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', add_label_1.addLabelCommand));
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', delete_label_1.deleteLabelCommand));
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', create_task_1.createTaskCommand));
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(about_1.aboutCommand);
121
+ .action(aboutCommand);
127
122
  // Parse command line arguments
128
- program.parse();
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();
@@ -1,11 +1,5 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
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(chalk_1.default.bold.cyan('\n📋 Jira AI - Available Commands\n'));
68
- console.log(chalk_1.default.bold('Usage:'));
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 = (0, settings_1.getAllowedCommands)();
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 || (0, settings_1.isCommandAllowed)(cmd.name));
74
- console.log(chalk_1.default.bold('Available Commands:\n'));
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(chalk_1.default.yellow(` ${cmd.name}`));
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' && !(0, settings_1.isCommandAllowed)(cmd.name));
76
+ const disabledCommands = ALL_COMMANDS.filter(cmd => cmd.name !== 'about' && !isCommandAllowed(cmd.name));
83
77
  if (disabledCommands.length > 0) {
84
- console.log(chalk_1.default.bold('Disabled Commands:\n'));
78
+ console.log(chalk.bold('Disabled Commands:\n'));
85
79
  for (const cmd of disabledCommands) {
86
- console.log(chalk_1.default.gray(` ${cmd.name} - ${cmd.description}`));
80
+ console.log(chalk.gray(` ${cmd.name} - ${cmd.description}`));
87
81
  }
88
82
  console.log();
89
83
  }
90
84
  }
91
- console.log(chalk_1.default.bold('For detailed help on any command, run:'));
92
- console.log(chalk_1.default.green(' jira-ai <command> --help\n'));
93
- console.log(chalk_1.default.bold('Configuration:'));
94
- console.log(` Settings file: ${chalk_1.default.cyan((0, settings_1.getSettingsPath)())}`);
95
- const allowedProjects = (0, settings_1.getAllowedProjects)();
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
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
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
- console.error(chalk_1.default.red('\nError: Issue key is required (use --issue-key)'));
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
- console.error(chalk_1.default.red('\nError: File path is required (use --file-path)'));
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
- console.error(chalk_1.default.red(`\nError: File not found: ${absolutePath}`));
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
- console.error(chalk_1.default.red('\nError reading file: ' +
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
- console.error(chalk_1.default.red('\nError: File is empty'));
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 = (0, marklassian_1.markdownToAdf)(markdownContent);
39
+ adfContent = markdownToAdf(markdownContent);
84
40
  }
85
41
  catch (error) {
86
- console.error(chalk_1.default.red('\nError converting Markdown to ADF: ' +
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 = (0, ora_1.default)(`Adding comment to ${issueKey}...`).start();
45
+ const spinner = ora(`Adding comment to ${issueKey}...`).start();
92
46
  try {
93
- await (0, jira_client_1.addIssueComment)(issueKey, adfContent);
94
- spinner.succeed(chalk_1.default.green(`Comment added successfully to ${issueKey}`));
95
- console.log(chalk_1.default.gray(`\nFile: ${absolutePath}`));
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(chalk_1.default.red('Failed to add comment'));
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(chalk_1.default.yellow('\nHint: Check that the issue key is correct'));
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(chalk_1.default.yellow('\nHint: You may not have permission to comment on this issue'));
58
+ console.log(chalk.yellow('\nHint: You may not have permission to comment on this issue'));
106
59
  }
107
- process.exit(1);
60
+ throw error;
108
61
  }
109
62
  }
@@ -1,40 +1,31 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
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
- console.error(chalk_1.default.red('\nError: Task ID is required'));
14
- process.exit(1);
8
+ throw new CliError('Task ID is required');
15
9
  }
16
10
  if (!labelsString || labelsString.trim() === '') {
17
- console.error(chalk_1.default.red('\nError: Labels are required (comma-separated)'));
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
- console.error(chalk_1.default.red('\nError: No valid labels provided'));
24
- process.exit(1);
16
+ throw new CliError('No valid labels provided');
25
17
  }
26
- const spinner = (0, ora_1.default)(`Adding labels to ${taskId}...`).start();
18
+ const spinner = ora(`Adding labels to ${taskId}...`).start();
27
19
  try {
28
- await (0, jira_client_1.addIssueLabels)(taskId, labels);
29
- spinner.succeed(chalk_1.default.green(`Labels added successfully to ${taskId}`));
30
- console.log(chalk_1.default.gray(`\nLabels: ${labels.join(', ')}`));
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(chalk_1.default.red('Failed to add labels'));
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(chalk_1.default.yellow('\nHint: Check that the issue ID/key is correct'));
27
+ console.log(chalk.yellow('\nHint: Check that the issue ID/key is correct'));
37
28
  }
38
- process.exit(1);
29
+ throw error;
39
30
  }
40
31
  }
@@ -1,17 +1,12 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.authCommand = authCommand;
7
- const readline_1 = __importDefault(require("readline"));
8
- const chalk_1 = __importDefault(require("chalk"));
9
- const ora_1 = __importDefault(require("ora"));
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
- console.error(chalk_1.default.red('Error: Missing required fields in JSON. Required: url/host, email, apikey/apiToken.'));
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
- console.error(chalk_1.default.red(`Error: Invalid JSON string: ${error.message}`));
55
- process.exit(1);
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 (!fs_1.default.existsSync(options.fromFile)) {
60
- console.error(chalk_1.default.red(`Error: File not found: ${options.fromFile}`));
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 = fs_1.default.readFileSync(options.fromFile, 'utf8');
65
- const config = dotenv_1.default.parse(content);
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
- console.error(chalk_1.default.red('Error: Missing required environment variables in file. Required: JIRA_HOST, JIRA_USER_EMAIL, JIRA_API_TOKEN.'));
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
- console.error(chalk_1.default.red(`Error: Failed to parse file: ${error.message}`));
76
- process.exit(1);
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(chalk_1.default.cyan('\n--- Jira Authentication Setup ---\n'));
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
- console.error(chalk_1.default.red('URL is required.'));
85
- process.exit(1);
78
+ throw new CliError('URL is required.');
86
79
  }
87
80
  email = await ask('Email: ');
88
81
  if (!email) {
89
- console.error(chalk_1.default.red('Email is required.'));
90
- process.exit(1);
82
+ throw new CliError('Email is required.');
91
83
  }
92
- console.log(chalk_1.default.gray('Get your API token from: https://id.atlassian.com/manage-profile/security/api-tokens'));
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
- console.error(chalk_1.default.red('API Token is required.'));
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 = (0, ora_1.default)('Verifying credentials...').start();
98
+ const spinner = ora('Verifying credentials...').start();
108
99
  try {
109
- const tempClient = (0, jira_client_1.createTemporaryClient)(host, email, apiToken);
100
+ const tempClient = createTemporaryClient(host, email, apiToken);
110
101
  const user = await tempClient.myself.getCurrentUser();
111
- spinner.succeed(chalk_1.default.green('Authentication successful!'));
112
- console.log(chalk_1.default.blue(`\nWelcome, ${user.displayName} (${user.emailAddress})`));
113
- (0, auth_storage_1.saveCredentials)({ host, email, apiToken });
114
- console.log(chalk_1.default.green('\nCredentials saved successfully to ~/.jira-ai/config.json'));
115
- console.log(chalk_1.default.gray('These credentials will be used for future commands on this machine.'));
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(chalk_1.default.red('Authentication failed.'));
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(chalk_1.default.yellow('Hint: Check if your email and API token are correct.'));
111
+ console.error(chalk.yellow('Hint: Check if your email and API token are correct.'));
122
112
  }
123
- process.exit(1);
113
+ throw error;
124
114
  }
125
115
  }