jira-ai 0.3.6 → 0.3.8
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 +15 -1
- package/dist/commands/add-label.js +40 -0
- package/dist/commands/auth.js +76 -28
- package/dist/commands/delete-label.js +40 -0
- package/dist/lib/formatters.js +7 -0
- package/dist/lib/jira-client.js +36 -0
- package/package.json +1 -1
- package/settings.yaml +6 -8
package/dist/cli.js
CHANGED
|
@@ -16,6 +16,8 @@ const list_issue_types_1 = require("./commands/list-issue-types");
|
|
|
16
16
|
const run_jql_1 = require("./commands/run-jql");
|
|
17
17
|
const update_description_1 = require("./commands/update-description");
|
|
18
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");
|
|
19
21
|
const create_task_1 = require("./commands/create-task");
|
|
20
22
|
const about_1 = require("./commands/about");
|
|
21
23
|
const auth_1 = require("./commands/auth");
|
|
@@ -51,7 +53,9 @@ function withPermission(commandName, commandFn, skipValidation = false) {
|
|
|
51
53
|
program
|
|
52
54
|
.command('auth')
|
|
53
55
|
.description('Set up Jira authentication credentials')
|
|
54
|
-
.
|
|
56
|
+
.option('--from-json <json_string>', 'Accepts a raw JSON string with credentials')
|
|
57
|
+
.option('--from-file <path>', 'Accepts a path to a file (typically .env) with credentials')
|
|
58
|
+
.action((options) => (0, auth_1.authCommand)(options));
|
|
55
59
|
// Me command
|
|
56
60
|
program
|
|
57
61
|
.command('me')
|
|
@@ -96,6 +100,16 @@ program
|
|
|
96
100
|
.requiredOption('--file-path <path>', 'Path to Markdown file')
|
|
97
101
|
.requiredOption('--issue-key <key>', 'Jira issue key (e.g., PS-123)')
|
|
98
102
|
.action(withPermission('add-comment', add_comment_1.addCommentCommand));
|
|
103
|
+
// Add label command
|
|
104
|
+
program
|
|
105
|
+
.command('add-label-to-issue <task-id> <labels>')
|
|
106
|
+
.description('Add one or more labels to a Jira issue (comma-separated)')
|
|
107
|
+
.action(withPermission('add-label-to-issue', add_label_1.addLabelCommand));
|
|
108
|
+
// Delete label command
|
|
109
|
+
program
|
|
110
|
+
.command('delete-label-from-issue <task-id> <labels>')
|
|
111
|
+
.description('Remove one or more labels from a Jira issue (comma-separated)')
|
|
112
|
+
.action(withPermission('delete-label-from-issue', delete_label_1.deleteLabelCommand));
|
|
99
113
|
// Create task command
|
|
100
114
|
program
|
|
101
115
|
.command('create-task')
|
|
@@ -0,0 +1,40 @@
|
|
|
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) {
|
|
11
|
+
// Validate input
|
|
12
|
+
if (!taskId || taskId.trim() === '') {
|
|
13
|
+
console.error(chalk_1.default.red('\nError: Task ID is required'));
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
if (!labelsString || labelsString.trim() === '') {
|
|
17
|
+
console.error(chalk_1.default.red('\nError: Labels are required (comma-separated)'));
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
// Parse labels
|
|
21
|
+
const labels = labelsString.split(',').map(l => l.trim()).filter(l => l !== '');
|
|
22
|
+
if (labels.length === 0) {
|
|
23
|
+
console.error(chalk_1.default.red('\nError: No valid labels provided'));
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
const spinner = (0, ora_1.default)(`Adding labels to ${taskId}...`).start();
|
|
27
|
+
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(', ')}`));
|
|
31
|
+
}
|
|
32
|
+
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')));
|
|
35
|
+
if (error instanceof Error && error.message.includes('404')) {
|
|
36
|
+
console.log(chalk_1.default.yellow('\nHint: Check that the issue ID/key is correct'));
|
|
37
|
+
}
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
}
|
package/dist/commands/auth.js
CHANGED
|
@@ -7,6 +7,8 @@ exports.authCommand = authCommand;
|
|
|
7
7
|
const readline_1 = __importDefault(require("readline"));
|
|
8
8
|
const chalk_1 = __importDefault(require("chalk"));
|
|
9
9
|
const ora_1 = __importDefault(require("ora"));
|
|
10
|
+
const fs_1 = __importDefault(require("fs"));
|
|
11
|
+
const dotenv_1 = __importDefault(require("dotenv"));
|
|
10
12
|
const jira_client_1 = require("../lib/jira-client");
|
|
11
13
|
const auth_storage_1 = require("../lib/auth-storage");
|
|
12
14
|
const rl = readline_1.default.createInterface({
|
|
@@ -33,45 +35,91 @@ function askSecret(question) {
|
|
|
33
35
|
});
|
|
34
36
|
});
|
|
35
37
|
}
|
|
36
|
-
async function authCommand() {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
38
|
+
async function authCommand(options = {}) {
|
|
39
|
+
let host = '';
|
|
40
|
+
let email = '';
|
|
41
|
+
let apiToken = '';
|
|
42
|
+
if (options.fromJson) {
|
|
43
|
+
try {
|
|
44
|
+
const data = JSON.parse(options.fromJson);
|
|
45
|
+
host = data.url || data.host;
|
|
46
|
+
email = data.email;
|
|
47
|
+
apiToken = data.apikey || data.apiToken;
|
|
48
|
+
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);
|
|
51
|
+
}
|
|
43
52
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
console.error(chalk_1.default.red('Email is required.'));
|
|
53
|
+
catch (error) {
|
|
54
|
+
console.error(chalk_1.default.red(`Error: Invalid JSON string: ${error.message}`));
|
|
47
55
|
process.exit(1);
|
|
48
56
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
if (!
|
|
52
|
-
console.error(chalk_1.default.red(
|
|
57
|
+
}
|
|
58
|
+
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}`));
|
|
53
61
|
process.exit(1);
|
|
54
62
|
}
|
|
55
|
-
const spinner = (0, ora_1.default)('Verifying credentials...').start();
|
|
56
63
|
try {
|
|
57
|
-
const
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
+
const content = fs_1.default.readFileSync(options.fromFile, 'utf8');
|
|
65
|
+
const config = dotenv_1.default.parse(content);
|
|
66
|
+
host = config.JIRA_HOST;
|
|
67
|
+
email = config.JIRA_USER_EMAIL;
|
|
68
|
+
apiToken = config.JIRA_API_TOKEN;
|
|
69
|
+
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);
|
|
72
|
+
}
|
|
64
73
|
}
|
|
65
74
|
catch (error) {
|
|
66
|
-
|
|
67
|
-
console.error(chalk_1.default.red(`Error: ${error.message || 'Invalid credentials'}`));
|
|
68
|
-
if (error.response && error.response.status === 401) {
|
|
69
|
-
console.error(chalk_1.default.yellow('Hint: Check if your email and API token are correct.'));
|
|
70
|
-
}
|
|
75
|
+
console.error(chalk_1.default.red(`Error: Failed to parse file: ${error.message}`));
|
|
71
76
|
process.exit(1);
|
|
72
77
|
}
|
|
73
78
|
}
|
|
74
|
-
|
|
79
|
+
if (!host || !email || !apiToken) {
|
|
80
|
+
console.log(chalk_1.default.cyan('\n--- Jira Authentication Setup ---\n'));
|
|
81
|
+
try {
|
|
82
|
+
host = await ask('Jira URL (e.g., https://your-domain.atlassian.net): ');
|
|
83
|
+
if (!host) {
|
|
84
|
+
console.error(chalk_1.default.red('URL is required.'));
|
|
85
|
+
process.exit(1);
|
|
86
|
+
}
|
|
87
|
+
email = await ask('Email: ');
|
|
88
|
+
if (!email) {
|
|
89
|
+
console.error(chalk_1.default.red('Email is required.'));
|
|
90
|
+
process.exit(1);
|
|
91
|
+
}
|
|
92
|
+
console.log(chalk_1.default.gray('Get your API token from: https://id.atlassian.com/manage-profile/security/api-tokens'));
|
|
93
|
+
apiToken = await ask('API Token: ');
|
|
94
|
+
if (!apiToken) {
|
|
95
|
+
console.error(chalk_1.default.red('API Token is required.'));
|
|
96
|
+
process.exit(1);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
finally {
|
|
100
|
+
rl.close();
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
// Non-interactive mode, just close readline
|
|
75
105
|
rl.close();
|
|
76
106
|
}
|
|
107
|
+
const spinner = (0, ora_1.default)('Verifying credentials...').start();
|
|
108
|
+
try {
|
|
109
|
+
const tempClient = (0, jira_client_1.createTemporaryClient)(host, email, apiToken);
|
|
110
|
+
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.'));
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
spinner.fail(chalk_1.default.red('Authentication failed.'));
|
|
119
|
+
console.error(chalk_1.default.red(`Error: ${error.message || 'Invalid credentials'}`));
|
|
120
|
+
if (error.response && error.response.status === 401) {
|
|
121
|
+
console.error(chalk_1.default.yellow('Hint: Check if your email and API token are correct.'));
|
|
122
|
+
}
|
|
123
|
+
process.exit(1);
|
|
124
|
+
}
|
|
77
125
|
}
|
|
@@ -0,0 +1,40 @@
|
|
|
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.deleteLabelCommand = deleteLabelCommand;
|
|
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 deleteLabelCommand(taskId, labelsString) {
|
|
11
|
+
// Validate input
|
|
12
|
+
if (!taskId || taskId.trim() === '') {
|
|
13
|
+
console.error(chalk_1.default.red('\nError: Task ID is required'));
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
if (!labelsString || labelsString.trim() === '') {
|
|
17
|
+
console.error(chalk_1.default.red('\nError: Labels are required (comma-separated)'));
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
// Parse labels
|
|
21
|
+
const labels = labelsString.split(',').map(l => l.trim()).filter(l => l !== '');
|
|
22
|
+
if (labels.length === 0) {
|
|
23
|
+
console.error(chalk_1.default.red('\nError: No valid labels provided'));
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
const spinner = (0, ora_1.default)(`Removing labels from ${taskId}...`).start();
|
|
27
|
+
try {
|
|
28
|
+
await (0, jira_client_1.removeIssueLabels)(taskId, labels);
|
|
29
|
+
spinner.succeed(chalk_1.default.green(`Labels removed successfully from ${taskId}`));
|
|
30
|
+
console.log(chalk_1.default.gray(`\nLabels: ${labels.join(', ')}`));
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
spinner.fail(chalk_1.default.red('Failed to remove labels'));
|
|
34
|
+
console.error(chalk_1.default.red('\nError: ' + (error instanceof Error ? error.message : 'Unknown error')));
|
|
35
|
+
if (error instanceof Error && error.message.includes('404')) {
|
|
36
|
+
console.log(chalk_1.default.yellow('\nHint: Check that the issue ID/key is correct'));
|
|
37
|
+
}
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
}
|
package/dist/lib/formatters.js
CHANGED
|
@@ -62,6 +62,13 @@ function formatTaskDetails(task) {
|
|
|
62
62
|
// Basic info table
|
|
63
63
|
const infoTable = createTable(['Property', 'Value'], [15, 65]);
|
|
64
64
|
infoTable.push(['Status', chalk_1.default.green(task.status.name)], ['Assignee', task.assignee?.displayName || chalk_1.default.gray('Unassigned')], ['Reporter', task.reporter?.displayName || chalk_1.default.gray('N/A')], ['Created', (0, utils_1.formatTimestamp)(task.created)], ['Updated', (0, utils_1.formatTimestamp)(task.updated)]);
|
|
65
|
+
// Add labels to basic info table if present
|
|
66
|
+
if (task.labels && task.labels.length > 0) {
|
|
67
|
+
const labelsString = task.labels
|
|
68
|
+
.map((label) => chalk_1.default.bgBlue.white.bold(` ${label} `))
|
|
69
|
+
.join(' ');
|
|
70
|
+
infoTable.push(['Labels', labelsString]);
|
|
71
|
+
}
|
|
65
72
|
output += infoTable.toString() + '\n\n';
|
|
66
73
|
// Parent Task
|
|
67
74
|
if (task.parent) {
|
package/dist/lib/jira-client.js
CHANGED
|
@@ -11,6 +11,8 @@ exports.updateIssueDescription = updateIssueDescription;
|
|
|
11
11
|
exports.addIssueComment = addIssueComment;
|
|
12
12
|
exports.getProjectIssueTypes = getProjectIssueTypes;
|
|
13
13
|
exports.createIssue = createIssue;
|
|
14
|
+
exports.addIssueLabels = addIssueLabels;
|
|
15
|
+
exports.removeIssueLabels = removeIssueLabels;
|
|
14
16
|
const jira_js_1 = require("jira.js");
|
|
15
17
|
const utils_1 = require("./utils");
|
|
16
18
|
const auth_storage_1 = require("./auth-storage");
|
|
@@ -119,6 +121,7 @@ async function getTaskWithDetails(taskId) {
|
|
|
119
121
|
'comment',
|
|
120
122
|
'parent',
|
|
121
123
|
'subtasks',
|
|
124
|
+
'labels',
|
|
122
125
|
],
|
|
123
126
|
});
|
|
124
127
|
// Extract comments
|
|
@@ -169,6 +172,7 @@ async function getTaskWithDetails(taskId) {
|
|
|
169
172
|
} : undefined,
|
|
170
173
|
created: issue.fields.created || '',
|
|
171
174
|
updated: issue.fields.updated || '',
|
|
175
|
+
labels: issue.fields.labels || [],
|
|
172
176
|
comments,
|
|
173
177
|
parent,
|
|
174
178
|
subtasks,
|
|
@@ -303,3 +307,35 @@ async function createIssue(projectKey, summary, issueTypeName, parentKey) {
|
|
|
303
307
|
id: response.id || '',
|
|
304
308
|
};
|
|
305
309
|
}
|
|
310
|
+
/**
|
|
311
|
+
* Add labels to a Jira issue
|
|
312
|
+
* @param taskId - The issue key (e.g., "PROJ-123")
|
|
313
|
+
* @param labels - Array of labels to add
|
|
314
|
+
*/
|
|
315
|
+
async function addIssueLabels(taskId, labels) {
|
|
316
|
+
const client = getJiraClient();
|
|
317
|
+
await client.issues.editIssue({
|
|
318
|
+
issueIdOrKey: taskId,
|
|
319
|
+
update: {
|
|
320
|
+
labels: labels.map(label => ({
|
|
321
|
+
add: label,
|
|
322
|
+
})),
|
|
323
|
+
},
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Remove labels from a Jira issue
|
|
328
|
+
* @param taskId - The issue key (e.g., "PROJ-123")
|
|
329
|
+
* @param labels - Array of labels to remove
|
|
330
|
+
*/
|
|
331
|
+
async function removeIssueLabels(taskId, labels) {
|
|
332
|
+
const client = getJiraClient();
|
|
333
|
+
await client.issues.editIssue({
|
|
334
|
+
issueIdOrKey: taskId,
|
|
335
|
+
update: {
|
|
336
|
+
labels: labels.map(label => ({
|
|
337
|
+
remove: label,
|
|
338
|
+
})),
|
|
339
|
+
},
|
|
340
|
+
});
|
|
341
|
+
}
|
package/package.json
CHANGED
package/settings.yaml
CHANGED
|
@@ -13,14 +13,12 @@ projects:
|
|
|
13
13
|
# Commands: List of allowed commands (use "all" to allow all commands)
|
|
14
14
|
# Available commands: me, projects, task-with-details, project-statuses, list-issue-types, run-jql, update-description, add-comment, create-task, about
|
|
15
15
|
commands:
|
|
16
|
-
-
|
|
17
|
-
- projects
|
|
18
|
-
- run-jql
|
|
16
|
+
- all
|
|
19
17
|
# Uncomment below to allow all commands
|
|
20
18
|
# - all
|
|
21
|
-
- task-with-details
|
|
19
|
+
# - task-with-details
|
|
22
20
|
# - project-statuses
|
|
23
|
-
- list-issue-types
|
|
24
|
-
- update-description
|
|
25
|
-
- add-comment
|
|
26
|
-
- create-task
|
|
21
|
+
# - list-issue-types
|
|
22
|
+
# - update-description
|
|
23
|
+
# - add-comment
|
|
24
|
+
# - create-task
|