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.
Files changed (39) hide show
  1. package/README.md +98 -2
  2. package/dist/cli.js +10 -0
  3. package/dist/commands/create-task.js +61 -0
  4. package/dist/lib/jira-client.js +33 -0
  5. package/package.json +25 -3
  6. package/settings.yaml +2 -1
  7. package/jest.config.js +0 -21
  8. package/src/cli.ts +0 -117
  9. package/src/commands/about.ts +0 -103
  10. package/src/commands/add-comment.ts +0 -94
  11. package/src/commands/auth.ts +0 -81
  12. package/src/commands/list-issue-types.ts +0 -18
  13. package/src/commands/me.ts +0 -18
  14. package/src/commands/project-statuses.ts +0 -18
  15. package/src/commands/projects.ts +0 -32
  16. package/src/commands/run-jql.ts +0 -40
  17. package/src/commands/task-with-details.ts +0 -18
  18. package/src/commands/update-description.ts +0 -94
  19. package/src/lib/auth-storage.ts +0 -57
  20. package/src/lib/formatters.ts +0 -277
  21. package/src/lib/jira-client.ts +0 -385
  22. package/src/lib/settings.ts +0 -109
  23. package/src/lib/utils.ts +0 -74
  24. package/src/types/md-to-adf.d.ts +0 -14
  25. package/tests/README.md +0 -97
  26. package/tests/__mocks__/jira.js.ts +0 -4
  27. package/tests/__mocks__/md-to-adf.ts +0 -7
  28. package/tests/__mocks__/mdast-util-from-adf.ts +0 -4
  29. package/tests/__mocks__/mdast-util-to-markdown.ts +0 -1
  30. package/tests/add-comment.test.ts +0 -226
  31. package/tests/auth-storage.test.ts +0 -64
  32. package/tests/cli-permissions.test.ts +0 -156
  33. package/tests/jira-client.test.ts +0 -123
  34. package/tests/projects.test.ts +0 -205
  35. package/tests/settings.test.ts +0 -357
  36. package/tests/task-with-details.test.ts +0 -83
  37. package/tests/update-description.test.ts +0 -262
  38. package/to-do.md +0 -8
  39. 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
- # - update-description # Uncomment to enable description updates
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:** The `update-description` command is commented out by default since it's a WRITE operation. Uncomment it only if you need to update issue descriptions.
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
+ }
@@ -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.2.11",
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();
@@ -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
- }