jira-ai 0.2.11 → 0.3.1
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 +98 -2
- package/dist/cli.js +10 -0
- package/dist/commands/create-task.js +61 -0
- package/dist/lib/jira-client.js +33 -0
- package/package.json +25 -3
- package/settings.yaml +2 -1
- package/jest.config.js +0 -21
- package/src/cli.ts +0 -117
- package/src/commands/about.ts +0 -103
- package/src/commands/add-comment.ts +0 -94
- package/src/commands/auth.ts +0 -81
- package/src/commands/list-issue-types.ts +0 -18
- package/src/commands/me.ts +0 -18
- package/src/commands/project-statuses.ts +0 -18
- package/src/commands/projects.ts +0 -32
- package/src/commands/run-jql.ts +0 -40
- package/src/commands/task-with-details.ts +0 -18
- package/src/commands/update-description.ts +0 -94
- package/src/lib/auth-storage.ts +0 -57
- package/src/lib/formatters.ts +0 -277
- package/src/lib/jira-client.ts +0 -385
- package/src/lib/settings.ts +0 -109
- package/src/lib/utils.ts +0 -74
- package/src/types/md-to-adf.d.ts +0 -14
- package/tests/README.md +0 -97
- package/tests/__mocks__/jira.js.ts +0 -4
- package/tests/__mocks__/md-to-adf.ts +0 -7
- package/tests/__mocks__/mdast-util-from-adf.ts +0 -4
- package/tests/__mocks__/mdast-util-to-markdown.ts +0 -1
- package/tests/add-comment.test.ts +0 -226
- package/tests/auth-storage.test.ts +0 -64
- package/tests/cli-permissions.test.ts +0 -156
- package/tests/jira-client.test.ts +0 -123
- package/tests/projects.test.ts +0 -205
- package/tests/settings.test.ts +0 -357
- package/tests/task-with-details.test.ts +0 -83
- package/tests/update-description.test.ts +0 -262
- package/to-do.md +0 -8
- package/tsconfig.json +0 -18
package/README.md
CHANGED
|
@@ -8,7 +8,9 @@ A TypeScript-based command-line interface for interacting with Atlassian Jira. B
|
|
|
8
8
|
- List all projects
|
|
9
9
|
- View task details with comments
|
|
10
10
|
- Show available statuses for a project
|
|
11
|
+
- Create new Jira issues (tasks, epics, subtasks, etc.)
|
|
11
12
|
- Update issue descriptions from Markdown files
|
|
13
|
+
- Add comments to issues from Markdown files
|
|
12
14
|
- Execute JQL queries with formatted results
|
|
13
15
|
- Beautiful table formatting with cli-table3
|
|
14
16
|
- Colored output for better readability
|
|
@@ -210,6 +212,45 @@ JQL Query Results (5 issues found)
|
|
|
210
212
|
└────────────┴──────────────────────────┴─────────────┴────────────┴──────────┘
|
|
211
213
|
```
|
|
212
214
|
|
|
215
|
+
### Create New Issue
|
|
216
|
+
|
|
217
|
+
Create a new Jira issue in any project.
|
|
218
|
+
|
|
219
|
+
```bash
|
|
220
|
+
jira-ai create-task --title <title> --project <project-key> --issue-type <type> [--parent <parent-key>]
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
**Example:**
|
|
224
|
+
```bash
|
|
225
|
+
jira-ai create-task --title "Configure production server" --project BP --issue-type Task
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
**For subtasks:**
|
|
229
|
+
```bash
|
|
230
|
+
jira-ai create-task --title "Update documentation" --project BP --issue-type Subtask --parent BP-1234
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
**Options:**
|
|
234
|
+
- `--title` - Issue title/summary (required)
|
|
235
|
+
- `--project` - Project key (required, e.g., "BP", "PROJ")
|
|
236
|
+
- `--issue-type` - Issue type name (required, e.g., "Task", "Epic", "Subtask", "Bug", "Story")
|
|
237
|
+
- `--parent` - Parent issue key (optional, required for subtasks)
|
|
238
|
+
|
|
239
|
+
**Output:**
|
|
240
|
+
```
|
|
241
|
+
✔ Issue created successfully: BP-1235
|
|
242
|
+
|
|
243
|
+
Title: Configure production server
|
|
244
|
+
Project: BP
|
|
245
|
+
Issue Type: Task
|
|
246
|
+
|
|
247
|
+
Issue Key: BP-1235
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
**Tips:**
|
|
251
|
+
- Use `jira-ai list-issue-types <project-key>` to see available issue types for a project
|
|
252
|
+
- Use `jira-ai projects` to see available projects
|
|
253
|
+
|
|
213
254
|
### Update Issue Description
|
|
214
255
|
|
|
215
256
|
Update a Jira issue's description from a Markdown file. The Markdown content is automatically converted to Atlassian Document Format (ADF).
|
|
@@ -246,6 +287,54 @@ File: /path/to/description.md
|
|
|
246
287
|
|
|
247
288
|
**Note:** This command replaces the entire issue description with the content from the file.
|
|
248
289
|
|
|
290
|
+
### Add Comment to Issue
|
|
291
|
+
|
|
292
|
+
Add a comment to a Jira issue from a Markdown file. The Markdown content is automatically converted to Atlassian Document Format (ADF).
|
|
293
|
+
|
|
294
|
+
```bash
|
|
295
|
+
jira-ai add-comment --issue-key <issue-key> --file-path <path-to-markdown-file>
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
**Example:**
|
|
299
|
+
```bash
|
|
300
|
+
jira-ai add-comment --issue-key BP-1234 --file-path ./comment.md
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
**Output:**
|
|
304
|
+
```
|
|
305
|
+
✔ Comment added successfully to BP-1234
|
|
306
|
+
|
|
307
|
+
File: /path/to/comment.md
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### List Issue Types
|
|
311
|
+
|
|
312
|
+
Display all available issue types for a project.
|
|
313
|
+
|
|
314
|
+
```bash
|
|
315
|
+
jira-ai list-issue-types <project-key>
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
**Example:**
|
|
319
|
+
```bash
|
|
320
|
+
jira-ai list-issue-types BP
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
**Output:**
|
|
324
|
+
```
|
|
325
|
+
Issue Types for Project BP (5 total)
|
|
326
|
+
|
|
327
|
+
┌─────────────┬──────────────────────────┬──────────┬────────────────┐
|
|
328
|
+
│ Name │ Description │ Subtask │ Hierarchy │
|
|
329
|
+
├─────────────┼──────────────────────────┼──────────┼────────────────┤
|
|
330
|
+
│ Epic │ A big user story │ No │ Level 1 │
|
|
331
|
+
├─────────────┼──────────────────────────┼──────────┼────────────────┤
|
|
332
|
+
│ Task │ A task │ No │ Level 0 │
|
|
333
|
+
├─────────────┼──────────────────────────┼──────────┼────────────────┤
|
|
334
|
+
│ Subtask │ A subtask │ Yes │ Level -1 │
|
|
335
|
+
└─────────────┴──────────────────────────┴──────────┴────────────────┘
|
|
336
|
+
```
|
|
337
|
+
|
|
249
338
|
## Settings
|
|
250
339
|
|
|
251
340
|
The CLI uses a `settings.yaml` file to control which commands and projects are allowed. This provides an additional security layer.
|
|
@@ -262,11 +351,14 @@ commands:
|
|
|
262
351
|
- projects
|
|
263
352
|
- run-jql
|
|
264
353
|
- task-with-details
|
|
265
|
-
|
|
354
|
+
- list-issue-types
|
|
355
|
+
# - create-task # Uncomment to enable creating issues
|
|
356
|
+
# - add-comment # Uncomment to enable adding comments
|
|
357
|
+
# - update-description # Uncomment to enable description updates
|
|
266
358
|
# - project-statuses
|
|
267
359
|
```
|
|
268
360
|
|
|
269
|
-
**Important:**
|
|
361
|
+
**Important:** Write operations (`create-task`, `add-comment`, `update-description`) are commented out by default for safety. Uncomment them in `settings.yaml` only if you need these features.
|
|
270
362
|
|
|
271
363
|
## Development
|
|
272
364
|
|
|
@@ -285,6 +377,10 @@ jira-service/
|
|
|
285
377
|
│ ├── cli.ts # Main entry point
|
|
286
378
|
│ ├── commands/ # Command implementations
|
|
287
379
|
│ │ ├── about.ts
|
|
380
|
+
│ │ ├── add-comment.ts # Add comments to issues
|
|
381
|
+
│ │ ├── auth.ts
|
|
382
|
+
│ │ ├── create-task.ts # Create new issues
|
|
383
|
+
│ │ ├── list-issue-types.ts
|
|
288
384
|
│ │ ├── me.ts
|
|
289
385
|
│ │ ├── projects.ts
|
|
290
386
|
│ │ ├── project-statuses.ts
|
package/dist/cli.js
CHANGED
|
@@ -16,6 +16,7 @@ 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 create_task_1 = require("./commands/create-task");
|
|
19
20
|
const about_1 = require("./commands/about");
|
|
20
21
|
const auth_1 = require("./commands/auth");
|
|
21
22
|
const settings_1 = require("./lib/settings");
|
|
@@ -95,6 +96,15 @@ program
|
|
|
95
96
|
.requiredOption('--file-path <path>', 'Path to Markdown file')
|
|
96
97
|
.requiredOption('--issue-key <key>', 'Jira issue key (e.g., PS-123)')
|
|
97
98
|
.action(withPermission('add-comment', add_comment_1.addCommentCommand));
|
|
99
|
+
// Create task command
|
|
100
|
+
program
|
|
101
|
+
.command('create-task')
|
|
102
|
+
.description('Create a new Jira issue')
|
|
103
|
+
.requiredOption('--title <title>', 'Issue title/summary')
|
|
104
|
+
.requiredOption('--project <project>', 'Project key (e.g., PROJ)')
|
|
105
|
+
.requiredOption('--issue-type <type>', 'Issue type (e.g., Task, Epic, Subtask)')
|
|
106
|
+
.option('--parent <key>', 'Parent issue key (required for subtasks)')
|
|
107
|
+
.action(withPermission('create-task', create_task_1.createTaskCommand));
|
|
98
108
|
// About command (always allowed)
|
|
99
109
|
program
|
|
100
110
|
.command('about')
|
|
@@ -0,0 +1,61 @@
|
|
|
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.createTaskCommand = createTaskCommand;
|
|
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 createTaskCommand(options) {
|
|
11
|
+
const { title, project, issueType, parent } = options;
|
|
12
|
+
// Validate required fields
|
|
13
|
+
if (!title || title.trim() === '') {
|
|
14
|
+
console.error(chalk_1.default.red('\nError: Title is required (use --title)'));
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
if (!project || project.trim() === '') {
|
|
18
|
+
console.error(chalk_1.default.red('\nError: Project is required (use --project)'));
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
if (!issueType || issueType.trim() === '') {
|
|
22
|
+
console.error(chalk_1.default.red('\nError: Issue type is required (use --issue-type)'));
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
// Create issue with spinner
|
|
26
|
+
const spinner = (0, ora_1.default)(`Creating ${issueType} in project ${project}...`).start();
|
|
27
|
+
try {
|
|
28
|
+
const result = await (0, jira_client_1.createIssue)(project, title, issueType, parent);
|
|
29
|
+
spinner.succeed(chalk_1.default.green(`Issue created successfully: ${result.key}`));
|
|
30
|
+
console.log(chalk_1.default.gray(`\nTitle: ${title}`));
|
|
31
|
+
console.log(chalk_1.default.gray(`Project: ${project}`));
|
|
32
|
+
console.log(chalk_1.default.gray(`Issue Type: ${issueType}`));
|
|
33
|
+
if (parent) {
|
|
34
|
+
console.log(chalk_1.default.gray(`Parent: ${parent}`));
|
|
35
|
+
}
|
|
36
|
+
console.log(chalk_1.default.cyan(`\nIssue Key: ${result.key}`));
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
spinner.fail(chalk_1.default.red('Failed to create issue'));
|
|
40
|
+
console.error(chalk_1.default.red('\nError: ' + (error instanceof Error ? error.message : 'Unknown error')));
|
|
41
|
+
// Provide helpful hints based on error
|
|
42
|
+
if (error instanceof Error) {
|
|
43
|
+
if (error.message.includes('project') || error.message.includes('Project')) {
|
|
44
|
+
console.log(chalk_1.default.yellow('\nHint: Check that the project key is correct'));
|
|
45
|
+
console.log(chalk_1.default.yellow('Use "jira-ai projects" to see available projects'));
|
|
46
|
+
}
|
|
47
|
+
else if (error.message.includes('issue type') || error.message.includes('issuetype')) {
|
|
48
|
+
console.log(chalk_1.default.yellow('\nHint: Check that the issue type is correct'));
|
|
49
|
+
console.log(chalk_1.default.yellow(`Use "jira-ai list-issue-types ${project}" to see available issue types`));
|
|
50
|
+
}
|
|
51
|
+
else if (error.message.includes('parent') || error.message.includes('Parent')) {
|
|
52
|
+
console.log(chalk_1.default.yellow('\nHint: Check that the parent issue key is correct'));
|
|
53
|
+
console.log(chalk_1.default.yellow('Parent issues are required for subtasks'));
|
|
54
|
+
}
|
|
55
|
+
else if (error.message.includes('403')) {
|
|
56
|
+
console.log(chalk_1.default.yellow('\nHint: You may not have permission to create issues in this project'));
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
}
|
package/dist/lib/jira-client.js
CHANGED
|
@@ -10,6 +10,7 @@ exports.searchIssuesByJql = searchIssuesByJql;
|
|
|
10
10
|
exports.updateIssueDescription = updateIssueDescription;
|
|
11
11
|
exports.addIssueComment = addIssueComment;
|
|
12
12
|
exports.getProjectIssueTypes = getProjectIssueTypes;
|
|
13
|
+
exports.createIssue = createIssue;
|
|
13
14
|
const jira_js_1 = require("jira.js");
|
|
14
15
|
const utils_1 = require("./utils");
|
|
15
16
|
const auth_storage_1 = require("./auth-storage");
|
|
@@ -270,3 +271,35 @@ async function getProjectIssueTypes(projectIdOrKey) {
|
|
|
270
271
|
hierarchyLevel: issueType.hierarchyLevel || 0,
|
|
271
272
|
})) || [];
|
|
272
273
|
}
|
|
274
|
+
/**
|
|
275
|
+
* Create a new issue
|
|
276
|
+
* @param projectKey - The project key (e.g., "PROJ")
|
|
277
|
+
* @param summary - The issue title/summary
|
|
278
|
+
* @param issueTypeName - The issue type name (e.g., "Task", "Epic", "Subtask")
|
|
279
|
+
* @param parentKey - Optional parent issue key for subtasks
|
|
280
|
+
*/
|
|
281
|
+
async function createIssue(projectKey, summary, issueTypeName, parentKey) {
|
|
282
|
+
const client = getJiraClient();
|
|
283
|
+
const fields = {
|
|
284
|
+
project: {
|
|
285
|
+
key: projectKey,
|
|
286
|
+
},
|
|
287
|
+
summary,
|
|
288
|
+
issuetype: {
|
|
289
|
+
name: issueTypeName,
|
|
290
|
+
},
|
|
291
|
+
};
|
|
292
|
+
// Add parent field if this is a subtask
|
|
293
|
+
if (parentKey) {
|
|
294
|
+
fields.parent = {
|
|
295
|
+
key: parentKey,
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
const response = await client.issues.createIssue({
|
|
299
|
+
fields,
|
|
300
|
+
});
|
|
301
|
+
return {
|
|
302
|
+
key: response.key || '',
|
|
303
|
+
id: response.id || '',
|
|
304
|
+
};
|
|
305
|
+
}
|
package/package.json
CHANGED
|
@@ -1,16 +1,30 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jira-ai",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "CLI tool for interacting with Atlassian Jira",
|
|
5
5
|
"main": "dist/cli.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"jira-ai": "./dist/cli.js"
|
|
8
8
|
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"settings.yaml",
|
|
12
|
+
"README.md"
|
|
13
|
+
],
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "https://github.com/yourusername/jira-ai.git"
|
|
17
|
+
},
|
|
18
|
+
"bugs": {
|
|
19
|
+
"url": "https://github.com/yourusername/jira-ai/issues"
|
|
20
|
+
},
|
|
21
|
+
"homepage": "https://github.com/yourusername/jira-ai#readme",
|
|
9
22
|
"scripts": {
|
|
10
23
|
"dev": "ts-node src/cli.ts",
|
|
11
24
|
"build": "tsc && chmod +x dist/cli.js",
|
|
12
25
|
"start": "node dist/cli.js",
|
|
13
26
|
"link": "npm run build && npm link",
|
|
27
|
+
"prepublishOnly": "npm run build && npm test",
|
|
14
28
|
"test": "jest",
|
|
15
29
|
"test:watch": "jest --watch",
|
|
16
30
|
"test:coverage": "jest --coverage",
|
|
@@ -19,10 +33,18 @@
|
|
|
19
33
|
"keywords": [
|
|
20
34
|
"jira",
|
|
21
35
|
"cli",
|
|
22
|
-
"atlassian"
|
|
36
|
+
"atlassian",
|
|
37
|
+
"jira-api",
|
|
38
|
+
"issue-tracker",
|
|
39
|
+
"task-management",
|
|
40
|
+
"command-line",
|
|
41
|
+
"typescript"
|
|
23
42
|
],
|
|
24
|
-
"author": "",
|
|
43
|
+
"author": "Your Name <your.email@example.com>",
|
|
25
44
|
"license": "ISC",
|
|
45
|
+
"engines": {
|
|
46
|
+
"node": ">=14.0.0"
|
|
47
|
+
},
|
|
26
48
|
"dependencies": {
|
|
27
49
|
"chalk": "^4.1.2",
|
|
28
50
|
"cli-table3": "^0.6.5",
|
package/settings.yaml
CHANGED
|
@@ -11,7 +11,7 @@ projects:
|
|
|
11
11
|
# - all
|
|
12
12
|
|
|
13
13
|
# Commands: List of allowed commands (use "all" to allow all commands)
|
|
14
|
-
# Available commands: me, projects, task-with-details, project-statuses, list-issue-types, run-jql, update-description, add-comment, about
|
|
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
16
|
- me
|
|
17
17
|
- projects
|
|
@@ -23,3 +23,4 @@ commands:
|
|
|
23
23
|
- list-issue-types
|
|
24
24
|
- update-description
|
|
25
25
|
- add-comment
|
|
26
|
+
- create-task
|
package/jest.config.js
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
module.exports = {
|
|
2
|
-
preset: 'ts-jest',
|
|
3
|
-
testEnvironment: 'node',
|
|
4
|
-
roots: ['<rootDir>/src', '<rootDir>/tests'],
|
|
5
|
-
testMatch: ['**/__tests__/**/*.ts', '**/*.test.ts', '**/*.spec.ts'],
|
|
6
|
-
collectCoverageFrom: [
|
|
7
|
-
'src/**/*.ts',
|
|
8
|
-
'!src/**/*.d.ts',
|
|
9
|
-
'!src/cli.ts'
|
|
10
|
-
],
|
|
11
|
-
coverageDirectory: 'coverage',
|
|
12
|
-
coverageReporters: ['text', 'lcov', 'html'],
|
|
13
|
-
moduleFileExtensions: ['ts', 'js', 'json'],
|
|
14
|
-
verbose: true,
|
|
15
|
-
moduleNameMapper: {
|
|
16
|
-
'^jira\\.js$': '<rootDir>/tests/__mocks__/jira.js.ts',
|
|
17
|
-
'^mdast-util-from-adf$': '<rootDir>/tests/__mocks__/mdast-util-from-adf.ts',
|
|
18
|
-
'^mdast-util-to-markdown$': '<rootDir>/tests/__mocks__/mdast-util-to-markdown.ts',
|
|
19
|
-
'^md-to-adf$': '<rootDir>/tests/__mocks__/md-to-adf.ts'
|
|
20
|
-
}
|
|
21
|
-
};
|
package/src/cli.ts
DELETED
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import { Command } from 'commander';
|
|
4
|
-
import dotenv from 'dotenv';
|
|
5
|
-
import chalk from 'chalk';
|
|
6
|
-
import { validateEnvVars } from './lib/utils';
|
|
7
|
-
import { meCommand } from './commands/me';
|
|
8
|
-
import { projectsCommand } from './commands/projects';
|
|
9
|
-
import { taskWithDetailsCommand } from './commands/task-with-details';
|
|
10
|
-
import { projectStatusesCommand } from './commands/project-statuses';
|
|
11
|
-
import { listIssueTypesCommand } from './commands/list-issue-types';
|
|
12
|
-
import { runJqlCommand } from './commands/run-jql';
|
|
13
|
-
import { updateDescriptionCommand } from './commands/update-description';
|
|
14
|
-
import { addCommentCommand } from './commands/add-comment';
|
|
15
|
-
import { aboutCommand } from './commands/about';
|
|
16
|
-
import { authCommand } from './commands/auth';
|
|
17
|
-
import { isCommandAllowed, getAllowedCommands } from './lib/settings';
|
|
18
|
-
|
|
19
|
-
// Load environment variables
|
|
20
|
-
dotenv.config();
|
|
21
|
-
|
|
22
|
-
// Create CLI program
|
|
23
|
-
const program = new Command();
|
|
24
|
-
|
|
25
|
-
program
|
|
26
|
-
.name('jira-ai')
|
|
27
|
-
.description('CLI tool for interacting with Atlassian Jira')
|
|
28
|
-
.version('1.0.0');
|
|
29
|
-
|
|
30
|
-
// Middleware to validate credentials for commands that need them
|
|
31
|
-
const validateCredentials = () => {
|
|
32
|
-
validateEnvVars();
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
// Helper function to wrap commands with permission check and credential validation
|
|
36
|
-
function withPermission(commandName: string, commandFn: (...args: any[]) => Promise<void>, skipValidation = false) {
|
|
37
|
-
return async (...args: any[]) => {
|
|
38
|
-
if (!skipValidation) {
|
|
39
|
-
validateCredentials();
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
if (!isCommandAllowed(commandName)) {
|
|
43
|
-
console.error(chalk.red(`\n❌ Command '${commandName}' is not allowed.`));
|
|
44
|
-
console.log(chalk.gray('Allowed commands: ' + getAllowedCommands().join(', ')));
|
|
45
|
-
console.log(chalk.gray('Update settings.yaml to enable this command.\n'));
|
|
46
|
-
process.exit(1);
|
|
47
|
-
}
|
|
48
|
-
return commandFn(...args);
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// Auth command (always allowed, skips validation)
|
|
53
|
-
program
|
|
54
|
-
.command('auth')
|
|
55
|
-
.description('Set up Jira authentication credentials')
|
|
56
|
-
.action(() => authCommand());
|
|
57
|
-
|
|
58
|
-
// Me command
|
|
59
|
-
program
|
|
60
|
-
.command('me')
|
|
61
|
-
.description('Show basic user information')
|
|
62
|
-
.action(withPermission('me', meCommand));
|
|
63
|
-
|
|
64
|
-
// Projects command
|
|
65
|
-
program
|
|
66
|
-
.command('projects')
|
|
67
|
-
.description('Show list of projects')
|
|
68
|
-
.action(withPermission('projects', projectsCommand));
|
|
69
|
-
|
|
70
|
-
// Task with details command
|
|
71
|
-
program
|
|
72
|
-
.command('task-with-details <task-id>')
|
|
73
|
-
.description('Show task title, body, and comments')
|
|
74
|
-
.action(withPermission('task-with-details', taskWithDetailsCommand));
|
|
75
|
-
|
|
76
|
-
// Project statuses command
|
|
77
|
-
program
|
|
78
|
-
.command('project-statuses <project-id>')
|
|
79
|
-
.description('Show all possible statuses for a project')
|
|
80
|
-
.action(withPermission('project-statuses', projectStatusesCommand));
|
|
81
|
-
|
|
82
|
-
// List issue types command
|
|
83
|
-
program
|
|
84
|
-
.command('list-issue-types <project-key>')
|
|
85
|
-
.description('Show all issue types for a project')
|
|
86
|
-
.action(withPermission('list-issue-types', listIssueTypesCommand));
|
|
87
|
-
|
|
88
|
-
// Run JQL command
|
|
89
|
-
program
|
|
90
|
-
.command('run-jql <jql-query>')
|
|
91
|
-
.description('Execute JQL query and display results')
|
|
92
|
-
.option('-l, --limit <number>', 'Maximum number of results (default: 50)', '50')
|
|
93
|
-
.action(withPermission('run-jql', runJqlCommand));
|
|
94
|
-
|
|
95
|
-
// Update description command
|
|
96
|
-
program
|
|
97
|
-
.command('update-description <task-id>')
|
|
98
|
-
.description('Update task description from a Markdown file')
|
|
99
|
-
.requiredOption('--from-file <path>', 'Path to Markdown file')
|
|
100
|
-
.action(withPermission('update-description', updateDescriptionCommand));
|
|
101
|
-
|
|
102
|
-
// Add comment command
|
|
103
|
-
program
|
|
104
|
-
.command('add-comment')
|
|
105
|
-
.description('Add a comment to a Jira issue from a Markdown file')
|
|
106
|
-
.requiredOption('--file-path <path>', 'Path to Markdown file')
|
|
107
|
-
.requiredOption('--issue-key <key>', 'Jira issue key (e.g., PS-123)')
|
|
108
|
-
.action(withPermission('add-comment', addCommentCommand));
|
|
109
|
-
|
|
110
|
-
// About command (always allowed)
|
|
111
|
-
program
|
|
112
|
-
.command('about')
|
|
113
|
-
.description('Show information about available commands')
|
|
114
|
-
.action(aboutCommand);
|
|
115
|
-
|
|
116
|
-
// Parse command line arguments
|
|
117
|
-
program.parse();
|
package/src/commands/about.ts
DELETED
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
import chalk from 'chalk';
|
|
2
|
-
import { getAllowedCommands, getAllowedProjects, isCommandAllowed, getSettingsPath } from '../lib/settings';
|
|
3
|
-
|
|
4
|
-
interface CommandInfo {
|
|
5
|
-
name: string;
|
|
6
|
-
description: string;
|
|
7
|
-
usage: string;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
const ALL_COMMANDS: CommandInfo[] = [
|
|
11
|
-
{
|
|
12
|
-
name: 'me',
|
|
13
|
-
description: 'Show details of current user',
|
|
14
|
-
usage: 'jira-ai me --help'
|
|
15
|
-
},
|
|
16
|
-
{
|
|
17
|
-
name: 'projects',
|
|
18
|
-
description: 'List available projects',
|
|
19
|
-
usage: 'jira-ai projects --help'
|
|
20
|
-
},
|
|
21
|
-
{
|
|
22
|
-
name: 'task-with-details',
|
|
23
|
-
description: 'Show task title, body, and comments',
|
|
24
|
-
usage: 'jira-ai task-with-details --help'
|
|
25
|
-
},
|
|
26
|
-
{
|
|
27
|
-
name: 'project-statuses',
|
|
28
|
-
description: 'Show all possible statuses for a project',
|
|
29
|
-
usage: 'jira-ai project-statuses --help'
|
|
30
|
-
},
|
|
31
|
-
{
|
|
32
|
-
name: 'list-issue-types',
|
|
33
|
-
description: 'Show all issue types for a project (e.g., Epic, Task, Subtask)',
|
|
34
|
-
usage: 'jira-ai list-issue-types <project-key>'
|
|
35
|
-
},
|
|
36
|
-
{
|
|
37
|
-
name: 'run-jql',
|
|
38
|
-
description: 'Execute JQL query and display results',
|
|
39
|
-
usage: 'jira-ai run-jql "<jql-query>" [-l <limit>]'
|
|
40
|
-
},
|
|
41
|
-
{
|
|
42
|
-
name: 'update-description',
|
|
43
|
-
description: 'Update task description from a Markdown file',
|
|
44
|
-
usage: 'jira-ai update-description <task-id> --from-file <path>'
|
|
45
|
-
},
|
|
46
|
-
{
|
|
47
|
-
name: 'add-comment',
|
|
48
|
-
description: 'Add a comment to a Jira issue from a Markdown file',
|
|
49
|
-
usage: 'jira-ai add-comment --file-path <path> --issue-key <key>'
|
|
50
|
-
},
|
|
51
|
-
{
|
|
52
|
-
name: 'about',
|
|
53
|
-
description: 'Show this help message',
|
|
54
|
-
usage: 'jira-ai about'
|
|
55
|
-
}
|
|
56
|
-
];
|
|
57
|
-
|
|
58
|
-
export async function aboutCommand() {
|
|
59
|
-
console.log(chalk.bold.cyan('\n📋 Jira AI - Available Commands\n'));
|
|
60
|
-
|
|
61
|
-
console.log(chalk.bold('Usage:'));
|
|
62
|
-
console.log(' jira-ai <command> [options]\n');
|
|
63
|
-
|
|
64
|
-
const allowedCommandsList = getAllowedCommands();
|
|
65
|
-
const isAllAllowed = allowedCommandsList.includes('all');
|
|
66
|
-
|
|
67
|
-
// Filter commands based on settings (about is always shown)
|
|
68
|
-
const commandsToShow = ALL_COMMANDS.filter(cmd =>
|
|
69
|
-
cmd.name === 'about' || isAllAllowed || isCommandAllowed(cmd.name)
|
|
70
|
-
);
|
|
71
|
-
|
|
72
|
-
console.log(chalk.bold('Available Commands:\n'));
|
|
73
|
-
|
|
74
|
-
for (const cmd of commandsToShow) {
|
|
75
|
-
console.log(chalk.yellow(` ${cmd.name}`));
|
|
76
|
-
console.log(` ${cmd.description}`);
|
|
77
|
-
console.log(` Usage: ${cmd.usage}\n`);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// Show disabled commands if not all are allowed
|
|
81
|
-
if (!isAllAllowed) {
|
|
82
|
-
const disabledCommands = ALL_COMMANDS.filter(cmd =>
|
|
83
|
-
cmd.name !== 'about' && !isCommandAllowed(cmd.name)
|
|
84
|
-
);
|
|
85
|
-
|
|
86
|
-
if (disabledCommands.length > 0) {
|
|
87
|
-
console.log(chalk.bold('Disabled Commands:\n'));
|
|
88
|
-
for (const cmd of disabledCommands) {
|
|
89
|
-
console.log(chalk.gray(` ${cmd.name} - ${cmd.description}`));
|
|
90
|
-
}
|
|
91
|
-
console.log();
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
console.log(chalk.bold('For detailed help on any command, run:'));
|
|
96
|
-
console.log(chalk.green(' jira-ai <command> --help\n'));
|
|
97
|
-
|
|
98
|
-
console.log(chalk.bold('Configuration:'));
|
|
99
|
-
console.log(` Settings file: ${chalk.cyan(getSettingsPath())}`);
|
|
100
|
-
const allowedProjects = getAllowedProjects();
|
|
101
|
-
console.log(` - Projects: ${allowedProjects.includes('all') ? 'All allowed' : allowedProjects.join(', ')}`);
|
|
102
|
-
console.log(` - Commands: ${isAllAllowed ? 'All allowed' : allowedCommandsList.join(', ')}\n`);
|
|
103
|
-
}
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
import chalk from 'chalk';
|
|
2
|
-
import ora from 'ora';
|
|
3
|
-
import * as fs from 'fs';
|
|
4
|
-
import * as path from 'path';
|
|
5
|
-
import mdToAdf from 'md-to-adf';
|
|
6
|
-
import { addIssueComment } from '../lib/jira-client';
|
|
7
|
-
|
|
8
|
-
export async function addCommentCommand(
|
|
9
|
-
options: { filePath: string; issueKey: string }
|
|
10
|
-
): Promise<void> {
|
|
11
|
-
const { filePath, issueKey } = options;
|
|
12
|
-
|
|
13
|
-
// Validate issueKey
|
|
14
|
-
if (!issueKey || issueKey.trim() === '') {
|
|
15
|
-
console.error(chalk.red('\nError: Issue key is required (use --issue-key)'));
|
|
16
|
-
process.exit(1);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
// Validate file path
|
|
20
|
-
if (!filePath || filePath.trim() === '') {
|
|
21
|
-
console.error(chalk.red('\nError: File path is required (use --file-path)'));
|
|
22
|
-
process.exit(1);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
// Resolve file path to absolute
|
|
26
|
-
const absolutePath = path.resolve(filePath);
|
|
27
|
-
|
|
28
|
-
// Check file exists
|
|
29
|
-
if (!fs.existsSync(absolutePath)) {
|
|
30
|
-
console.error(chalk.red(`\nError: File not found: ${absolutePath}`));
|
|
31
|
-
process.exit(1);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// Read file
|
|
35
|
-
let markdownContent: string;
|
|
36
|
-
try {
|
|
37
|
-
markdownContent = fs.readFileSync(absolutePath, 'utf-8');
|
|
38
|
-
} catch (error) {
|
|
39
|
-
console.error(
|
|
40
|
-
chalk.red(
|
|
41
|
-
'\nError reading file: ' +
|
|
42
|
-
(error instanceof Error ? error.message : 'Unknown error')
|
|
43
|
-
)
|
|
44
|
-
);
|
|
45
|
-
process.exit(1);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// Validate file is not empty
|
|
49
|
-
if (markdownContent.trim() === '') {
|
|
50
|
-
console.error(chalk.red('\nError: File is empty'));
|
|
51
|
-
process.exit(1);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// Convert Markdown to ADF
|
|
55
|
-
let adfContent: any;
|
|
56
|
-
try {
|
|
57
|
-
adfContent = mdToAdf(markdownContent);
|
|
58
|
-
} catch (error) {
|
|
59
|
-
console.error(
|
|
60
|
-
chalk.red(
|
|
61
|
-
'\nError converting Markdown to ADF: ' +
|
|
62
|
-
(error instanceof Error ? error.message : 'Unknown error')
|
|
63
|
-
)
|
|
64
|
-
);
|
|
65
|
-
process.exit(1);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// Add comment with spinner
|
|
69
|
-
const spinner = ora(`Adding comment to ${issueKey}...`).start();
|
|
70
|
-
|
|
71
|
-
try {
|
|
72
|
-
await addIssueComment(issueKey, adfContent);
|
|
73
|
-
spinner.succeed(chalk.green(`Comment added successfully to ${issueKey}`));
|
|
74
|
-
console.log(chalk.gray(`\nFile: ${absolutePath}`));
|
|
75
|
-
} catch (error) {
|
|
76
|
-
spinner.fail(chalk.red('Failed to add comment'));
|
|
77
|
-
console.error(
|
|
78
|
-
chalk.red(
|
|
79
|
-
'\nError: ' + (error instanceof Error ? error.message : 'Unknown error')
|
|
80
|
-
)
|
|
81
|
-
);
|
|
82
|
-
|
|
83
|
-
// Provide helpful hints based on error
|
|
84
|
-
if (error instanceof Error && error.message.includes('404')) {
|
|
85
|
-
console.log(chalk.yellow('\nHint: Check that the issue key is correct'));
|
|
86
|
-
} else if (error instanceof Error && error.message.includes('403')) {
|
|
87
|
-
console.log(
|
|
88
|
-
chalk.yellow('\nHint: You may not have permission to comment on this issue')
|
|
89
|
-
);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
process.exit(1);
|
|
93
|
-
}
|
|
94
|
-
}
|