jira-ai 0.1.0
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 +364 -0
- package/dist/cli.js +87 -0
- package/dist/commands/about.js +83 -0
- package/dist/commands/add-comment.js +109 -0
- package/dist/commands/me.js +23 -0
- package/dist/commands/project-statuses.js +23 -0
- package/dist/commands/projects.js +35 -0
- package/dist/commands/run-jql.js +44 -0
- package/dist/commands/task-with-details.js +23 -0
- package/dist/commands/update-description.js +109 -0
- package/dist/lib/formatters.js +193 -0
- package/dist/lib/jira-client.js +216 -0
- package/dist/lib/settings.js +68 -0
- package/dist/lib/utils.js +79 -0
- package/jest.config.js +21 -0
- package/package.json +47 -0
- package/settings.yaml +24 -0
- package/src/cli.ts +97 -0
- package/src/commands/about.ts +98 -0
- package/src/commands/add-comment.ts +94 -0
- package/src/commands/me.ts +18 -0
- package/src/commands/project-statuses.ts +18 -0
- package/src/commands/projects.ts +32 -0
- package/src/commands/run-jql.ts +40 -0
- package/src/commands/task-with-details.ts +18 -0
- package/src/commands/update-description.ts +94 -0
- package/src/lib/formatters.ts +224 -0
- package/src/lib/jira-client.ts +319 -0
- package/src/lib/settings.ts +77 -0
- package/src/lib/utils.ts +76 -0
- package/src/types/md-to-adf.d.ts +14 -0
- package/tests/README.md +97 -0
- package/tests/__mocks__/jira.js.ts +4 -0
- package/tests/__mocks__/md-to-adf.ts +7 -0
- package/tests/__mocks__/mdast-util-from-adf.ts +4 -0
- package/tests/__mocks__/mdast-util-to-markdown.ts +1 -0
- package/tests/add-comment.test.ts +226 -0
- package/tests/cli-permissions.test.ts +156 -0
- package/tests/jira-client.test.ts +123 -0
- package/tests/projects.test.ts +205 -0
- package/tests/settings.test.ts +288 -0
- package/tests/task-with-details.test.ts +83 -0
- package/tests/update-description.test.ts +262 -0
- package/to-do.md +9 -0
- package/tsconfig.json +18 -0
package/README.md
ADDED
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
# Jira AI
|
|
2
|
+
|
|
3
|
+
A TypeScript-based command-line interface for interacting with Atlassian Jira. Built with jira.js library and featuring beautiful terminal output with tables and colors.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- View your user information
|
|
8
|
+
- List all projects
|
|
9
|
+
- View task details with comments
|
|
10
|
+
- Show available statuses for a project
|
|
11
|
+
- Update issue descriptions from Markdown files
|
|
12
|
+
- Execute JQL queries with formatted results
|
|
13
|
+
- Beautiful table formatting with cli-table3
|
|
14
|
+
- Colored output for better readability
|
|
15
|
+
- Loading spinners for async operations
|
|
16
|
+
|
|
17
|
+
## Prerequisites
|
|
18
|
+
|
|
19
|
+
- Node.js (v14 or higher)
|
|
20
|
+
- npm or yarn
|
|
21
|
+
- Jira account with API access
|
|
22
|
+
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
1. Clone or navigate to the project directory:
|
|
26
|
+
```bash
|
|
27
|
+
cd /home/manager/jira-service
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
2. Install dependencies:
|
|
31
|
+
```bash
|
|
32
|
+
npm install
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
3. Build the project:
|
|
36
|
+
```bash
|
|
37
|
+
npm run build
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
4. Install globally:
|
|
41
|
+
```bash
|
|
42
|
+
sudo npm link
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
After installation, you can use the `jira-ai` command from anywhere in your terminal.
|
|
46
|
+
|
|
47
|
+
## Configuration
|
|
48
|
+
|
|
49
|
+
Create a `.env` file in the project root with your Jira credentials:
|
|
50
|
+
|
|
51
|
+
```env
|
|
52
|
+
JIRA_HOST=https://your-domain.atlassian.net
|
|
53
|
+
JIRA_USER_EMAIL=your-email@example.com
|
|
54
|
+
JIRA_API_TOKEN=your-api-token
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Getting Your API Token
|
|
58
|
+
|
|
59
|
+
1. Go to [Atlassian API Tokens](https://id.atlassian.com/manage-profile/security/api-tokens)
|
|
60
|
+
2. Click "Create API token"
|
|
61
|
+
3. Give it a label and copy the generated token
|
|
62
|
+
4. Add the token to your `.env` file
|
|
63
|
+
|
|
64
|
+
## Available Commands
|
|
65
|
+
|
|
66
|
+
### Show User Information
|
|
67
|
+
|
|
68
|
+
Display your basic Jira user information including host, display name, email, account ID, status, and timezone.
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
jira-ai me
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
**Output:**
|
|
75
|
+
```
|
|
76
|
+
User Information:
|
|
77
|
+
┌────────────────────┬──────────────────────────────────────────────────┐
|
|
78
|
+
│ Property │ Value │
|
|
79
|
+
├────────────────────┼──────────────────────────────────────────────────┤
|
|
80
|
+
│ Host │ https://your-domain.atlassian.net │
|
|
81
|
+
├────────────────────┼──────────────────────────────────────────────────┤
|
|
82
|
+
│ Display Name │ Your Name │
|
|
83
|
+
├────────────────────┼──────────────────────────────────────────────────┤
|
|
84
|
+
│ Email │ your-email@example.com │
|
|
85
|
+
├────────────────────┼──────────────────────────────────────────────────┤
|
|
86
|
+
│ Account ID │ 557058:xxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx │
|
|
87
|
+
├────────────────────┼──────────────────────────────────────────────────┤
|
|
88
|
+
│ Status │ Active │
|
|
89
|
+
├────────────────────┼──────────────────────────────────────────────────┤
|
|
90
|
+
│ Time Zone │ Europe/Kiev │
|
|
91
|
+
└────────────────────┴──────────────────────────────────────────────────┘
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### List Projects
|
|
95
|
+
|
|
96
|
+
Show all projects you have access to in a formatted table.
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
jira-ai projects
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
**Output:**
|
|
103
|
+
```
|
|
104
|
+
Projects (25 total)
|
|
105
|
+
|
|
106
|
+
┌────────────┬───────────────────────────────────┬───────────────┬─────────────────────────┐
|
|
107
|
+
│ Key │ Name │ Type │ Lead │
|
|
108
|
+
├────────────┼───────────────────────────────────┼───────────────┼─────────────────────────┤
|
|
109
|
+
│ BP │ BookingPal │ software │ Pavel Boiko │
|
|
110
|
+
├────────────┼───────────────────────────────────┼───────────────┼─────────────────────────┤
|
|
111
|
+
│ IT │ IT Support │ software │ Anatolii Fesiuk │
|
|
112
|
+
└────────────┴───────────────────────────────────┴───────────────┴─────────────────────────┘
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### View Task Details
|
|
116
|
+
|
|
117
|
+
Display detailed information about a specific task including title, status, assignee, reporter, description, and all comments.
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
jira-ai task-with-details <task-id>
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
**Example:**
|
|
124
|
+
```bash
|
|
125
|
+
jira-ai task-with-details BP-1234
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
**Output:**
|
|
129
|
+
```
|
|
130
|
+
BP-1234: Configure new production server
|
|
131
|
+
|
|
132
|
+
┌───────────────┬─────────────────────────────────────────────────────────────────┐
|
|
133
|
+
│ Property │ Value │
|
|
134
|
+
├───────────────┼─────────────────────────────────────────────────────────────────┤
|
|
135
|
+
│ Status │ In Progress │
|
|
136
|
+
├───────────────┼─────────────────────────────────────────────────────────────────┤
|
|
137
|
+
│ Assignee │ John Doe │
|
|
138
|
+
├───────────────┼─────────────────────────────────────────────────────────────────┤
|
|
139
|
+
│ Reporter │ Jane Smith │
|
|
140
|
+
├───────────────┼─────────────────────────────────────────────────────────────────┤
|
|
141
|
+
│ Created │ Jan 10, 2026, 02:30 PM │
|
|
142
|
+
├───────────────┼─────────────────────────────────────────────────────────────────┤
|
|
143
|
+
│ Updated │ Jan 10, 2026, 04:15 PM │
|
|
144
|
+
└───────────────┴─────────────────────────────────────────────────────────────────┘
|
|
145
|
+
|
|
146
|
+
Description:
|
|
147
|
+
────────────────────────────────────────────────────────────────────────────────
|
|
148
|
+
We need to configure the production server...
|
|
149
|
+
────────────────────────────────────────────────────────────────────────────────
|
|
150
|
+
|
|
151
|
+
Comments (2):
|
|
152
|
+
|
|
153
|
+
1. John Doe - Jan 10, 2026, 03:00 PM
|
|
154
|
+
────────────────────────────────────────────────────────────────────────────────
|
|
155
|
+
Started working on this task...
|
|
156
|
+
────────────────────────────────────────────────────────────────────────────────
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Show Project Statuses
|
|
160
|
+
|
|
161
|
+
Display all possible issue statuses available in a specific project.
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
jira-ai project-statuses <project-id>
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
**Example:**
|
|
168
|
+
```bash
|
|
169
|
+
jira-ai project-statuses BP
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
**Output:**
|
|
173
|
+
```
|
|
174
|
+
Project BP - Available Statuses (21 total)
|
|
175
|
+
|
|
176
|
+
┌─────────────────────────┬────────────────────┬─────────────────────────────────────────────┐
|
|
177
|
+
│ Status Name │ Category │ Description │
|
|
178
|
+
├─────────────────────────┼────────────────────┼─────────────────────────────────────────────┤
|
|
179
|
+
│ Open │ To Do │ The work item is open and ready for work │
|
|
180
|
+
├─────────────────────────┼────────────────────┼─────────────────────────────────────────────┤
|
|
181
|
+
│ In Progress │ In Progress │ This work item is being actively worked on │
|
|
182
|
+
├─────────────────────────┼────────────────────┼─────────────────────────────────────────────┤
|
|
183
|
+
│ Closed │ Done │ The work item is considered finished │
|
|
184
|
+
└─────────────────────────┴────────────────────┴─────────────────────────────────────────────┘
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### Execute JQL Query
|
|
188
|
+
|
|
189
|
+
Run a JQL (Jira Query Language) query and display results in a formatted table.
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
jira-ai run-jql <jql-query> [--limit <number>]
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
**Example:**
|
|
196
|
+
```bash
|
|
197
|
+
jira-ai run-jql "project = BP AND status = 'In Progress'" --limit 10
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
**Output:**
|
|
201
|
+
```
|
|
202
|
+
JQL Query Results (5 issues found)
|
|
203
|
+
|
|
204
|
+
┌────────────┬──────────────────────────┬─────────────┬────────────┬──────────┐
|
|
205
|
+
│ Key │ Summary │ Status │ Assignee │ Priority │
|
|
206
|
+
├────────────┼──────────────────────────┼─────────────┼────────────┼──────────┤
|
|
207
|
+
│ BP-1234 │ Configure production... │ In Progress │ John Doe │ High │
|
|
208
|
+
├────────────┼──────────────────────────┼─────────────┼────────────┼──────────┤
|
|
209
|
+
│ BP-5678 │ Update documentation │ In Progress │ Jane Smith │ Medium │
|
|
210
|
+
└────────────┴──────────────────────────┴─────────────┴────────────┴──────────┘
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### Update Issue Description
|
|
214
|
+
|
|
215
|
+
Update a Jira issue's description from a Markdown file. The Markdown content is automatically converted to Atlassian Document Format (ADF).
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
jira-ai update-description <task-id> --from-file <path-to-markdown-file>
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
**Example:**
|
|
222
|
+
```bash
|
|
223
|
+
jira-ai update-description BP-1234 --from-file ./description.md
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
**Requirements:**
|
|
227
|
+
- The command must be enabled in `settings.yaml` (commented out by default for safety)
|
|
228
|
+
- You must have edit permissions for the issue in Jira
|
|
229
|
+
- The Markdown file must exist and be non-empty
|
|
230
|
+
|
|
231
|
+
**Supported Markdown:**
|
|
232
|
+
- Headings (# H1, ## H2, etc.)
|
|
233
|
+
- Bold, italic, strikethrough
|
|
234
|
+
- Lists (ordered and unordered)
|
|
235
|
+
- Code blocks with syntax highlighting
|
|
236
|
+
- Links and images
|
|
237
|
+
- Tables
|
|
238
|
+
- Blockquotes
|
|
239
|
+
|
|
240
|
+
**Output:**
|
|
241
|
+
```
|
|
242
|
+
✔ Description updated successfully for BP-1234
|
|
243
|
+
|
|
244
|
+
File: /path/to/description.md
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
**Note:** This command replaces the entire issue description with the content from the file.
|
|
248
|
+
|
|
249
|
+
## Settings
|
|
250
|
+
|
|
251
|
+
The CLI uses a `settings.yaml` file to control which commands and projects are allowed. This provides an additional security layer.
|
|
252
|
+
|
|
253
|
+
**Example settings.yaml:**
|
|
254
|
+
```yaml
|
|
255
|
+
# Projects: List of allowed projects (use "all" to allow all projects)
|
|
256
|
+
projects:
|
|
257
|
+
- all
|
|
258
|
+
|
|
259
|
+
# Commands: List of allowed commands (use "all" to allow all commands)
|
|
260
|
+
commands:
|
|
261
|
+
- me
|
|
262
|
+
- projects
|
|
263
|
+
- run-jql
|
|
264
|
+
- task-with-details
|
|
265
|
+
# - update-description # Uncomment to enable description updates
|
|
266
|
+
# - project-statuses
|
|
267
|
+
```
|
|
268
|
+
|
|
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.
|
|
270
|
+
|
|
271
|
+
## Development
|
|
272
|
+
|
|
273
|
+
### Scripts
|
|
274
|
+
|
|
275
|
+
- `npm run dev` - Run in development mode with ts-node
|
|
276
|
+
- `npm run build` - Compile TypeScript to JavaScript
|
|
277
|
+
- `npm start` - Run the compiled version
|
|
278
|
+
- `npm run link` - Build and install globally
|
|
279
|
+
|
|
280
|
+
### Project Structure
|
|
281
|
+
|
|
282
|
+
```
|
|
283
|
+
jira-service/
|
|
284
|
+
├── src/
|
|
285
|
+
│ ├── cli.ts # Main entry point
|
|
286
|
+
│ ├── commands/ # Command implementations
|
|
287
|
+
│ │ ├── about.ts
|
|
288
|
+
│ │ ├── me.ts
|
|
289
|
+
│ │ ├── projects.ts
|
|
290
|
+
│ │ ├── project-statuses.ts
|
|
291
|
+
│ │ ├── run-jql.ts
|
|
292
|
+
│ │ ├── task-with-details.ts
|
|
293
|
+
│ │ └── update-description.ts # Update issue descriptions
|
|
294
|
+
│ ├── lib/
|
|
295
|
+
│ │ ├── jira-client.ts # Jira API wrapper
|
|
296
|
+
│ │ ├── formatters.ts # Output formatting
|
|
297
|
+
│ │ ├── settings.ts # Settings management
|
|
298
|
+
│ │ └── utils.ts # Utility functions
|
|
299
|
+
│ └── types/
|
|
300
|
+
│ └── md-to-adf.d.ts # Type definitions for md-to-adf
|
|
301
|
+
├── tests/ # Test files
|
|
302
|
+
│ ├── __mocks__/ # Jest mocks
|
|
303
|
+
│ ├── cli-permissions.test.ts
|
|
304
|
+
│ ├── projects.test.ts
|
|
305
|
+
│ ├── settings.test.ts
|
|
306
|
+
│ └── update-description.test.ts
|
|
307
|
+
├── dist/ # Compiled JavaScript
|
|
308
|
+
├── package.json
|
|
309
|
+
├── tsconfig.json
|
|
310
|
+
├── jest.config.js
|
|
311
|
+
├── settings.yaml # Command/project permissions
|
|
312
|
+
└── .env # Environment variables
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
## Technologies Used
|
|
316
|
+
|
|
317
|
+
- **TypeScript** - Type-safe JavaScript
|
|
318
|
+
- **jira.js** - Jira API client library
|
|
319
|
+
- **Commander.js** - CLI framework
|
|
320
|
+
- **cli-table3** - Beautiful terminal tables
|
|
321
|
+
- **chalk** - Terminal colors and styling
|
|
322
|
+
- **ora** - Loading spinners
|
|
323
|
+
- **dotenv** - Environment variable management
|
|
324
|
+
- **md-to-adf** - Markdown to Atlassian Document Format conversion
|
|
325
|
+
- **Jest** - Testing framework
|
|
326
|
+
|
|
327
|
+
## Error Handling
|
|
328
|
+
|
|
329
|
+
The CLI provides helpful error messages:
|
|
330
|
+
|
|
331
|
+
- Missing environment variables with instructions
|
|
332
|
+
- API errors with detailed messages
|
|
333
|
+
- Invalid task IDs or project keys
|
|
334
|
+
- Network connectivity issues
|
|
335
|
+
|
|
336
|
+
## Troubleshooting
|
|
337
|
+
|
|
338
|
+
### Command not found after installation
|
|
339
|
+
|
|
340
|
+
Make sure you ran `sudo npm link` and that your npm global bin directory is in your PATH.
|
|
341
|
+
|
|
342
|
+
### Authentication errors
|
|
343
|
+
|
|
344
|
+
Verify your `.env` file has the correct:
|
|
345
|
+
- JIRA_HOST (with https://)
|
|
346
|
+
- JIRA_USER_EMAIL
|
|
347
|
+
- JIRA_API_TOKEN
|
|
348
|
+
|
|
349
|
+
Regenerate your API token if needed.
|
|
350
|
+
|
|
351
|
+
### Permission denied
|
|
352
|
+
|
|
353
|
+
If you get permission errors, make sure the CLI is executable:
|
|
354
|
+
```bash
|
|
355
|
+
chmod +x dist/cli.js
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
## License
|
|
359
|
+
|
|
360
|
+
ISC
|
|
361
|
+
|
|
362
|
+
## Contributing
|
|
363
|
+
|
|
364
|
+
Feel free to submit issues and enhancement requests.
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
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 run_jql_1 = require("./commands/run-jql");
|
|
16
|
+
const update_description_1 = require("./commands/update-description");
|
|
17
|
+
const add_comment_1 = require("./commands/add-comment");
|
|
18
|
+
const about_1 = require("./commands/about");
|
|
19
|
+
const settings_1 = require("./lib/settings");
|
|
20
|
+
// Load environment variables
|
|
21
|
+
dotenv_1.default.config();
|
|
22
|
+
// Validate environment variables
|
|
23
|
+
(0, utils_1.validateEnvVars)();
|
|
24
|
+
// Helper function to wrap commands with permission check
|
|
25
|
+
function withPermission(commandName, commandFn) {
|
|
26
|
+
return async (...args) => {
|
|
27
|
+
if (!(0, settings_1.isCommandAllowed)(commandName)) {
|
|
28
|
+
console.error(chalk_1.default.red(`\n❌ Command '${commandName}' is not allowed.`));
|
|
29
|
+
console.log(chalk_1.default.gray('Allowed commands: ' + (0, settings_1.getAllowedCommands)().join(', ')));
|
|
30
|
+
console.log(chalk_1.default.gray('Update settings.yaml to enable this command.\n'));
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
return commandFn(...args);
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
// Create CLI program
|
|
37
|
+
const program = new commander_1.Command();
|
|
38
|
+
program
|
|
39
|
+
.name('jira-ai')
|
|
40
|
+
.description('CLI tool for interacting with Atlassian Jira')
|
|
41
|
+
.version('1.0.0');
|
|
42
|
+
// Me command
|
|
43
|
+
program
|
|
44
|
+
.command('me')
|
|
45
|
+
.description('Show basic user information')
|
|
46
|
+
.action(withPermission('me', me_1.meCommand));
|
|
47
|
+
// Projects command
|
|
48
|
+
program
|
|
49
|
+
.command('projects')
|
|
50
|
+
.description('Show list of projects')
|
|
51
|
+
.action(withPermission('projects', projects_1.projectsCommand));
|
|
52
|
+
// Task with details command
|
|
53
|
+
program
|
|
54
|
+
.command('task-with-details <task-id>')
|
|
55
|
+
.description('Show task title, body, and comments')
|
|
56
|
+
.action(withPermission('task-with-details', task_with_details_1.taskWithDetailsCommand));
|
|
57
|
+
// Project statuses command
|
|
58
|
+
program
|
|
59
|
+
.command('project-statuses <project-id>')
|
|
60
|
+
.description('Show all possible statuses for a project')
|
|
61
|
+
.action(withPermission('project-statuses', project_statuses_1.projectStatusesCommand));
|
|
62
|
+
// Run JQL command
|
|
63
|
+
program
|
|
64
|
+
.command('run-jql <jql-query>')
|
|
65
|
+
.description('Execute JQL query and display results')
|
|
66
|
+
.option('-l, --limit <number>', 'Maximum number of results (default: 50)', '50')
|
|
67
|
+
.action(withPermission('run-jql', run_jql_1.runJqlCommand));
|
|
68
|
+
// Update description command
|
|
69
|
+
program
|
|
70
|
+
.command('update-description <task-id>')
|
|
71
|
+
.description('Update task description from a Markdown file')
|
|
72
|
+
.requiredOption('--from-file <path>', 'Path to Markdown file')
|
|
73
|
+
.action(withPermission('update-description', update_description_1.updateDescriptionCommand));
|
|
74
|
+
// Add comment command
|
|
75
|
+
program
|
|
76
|
+
.command('add-comment')
|
|
77
|
+
.description('Add a comment to a Jira issue from a Markdown file')
|
|
78
|
+
.requiredOption('--file-path <path>', 'Path to Markdown file')
|
|
79
|
+
.requiredOption('--issue-key <key>', 'Jira issue key (e.g., PS-123)')
|
|
80
|
+
.action(withPermission('add-comment', add_comment_1.addCommentCommand));
|
|
81
|
+
// About command (always allowed)
|
|
82
|
+
program
|
|
83
|
+
.command('about')
|
|
84
|
+
.description('Show information about available commands')
|
|
85
|
+
.action(about_1.aboutCommand);
|
|
86
|
+
// Parse command line arguments
|
|
87
|
+
program.parse();
|
|
@@ -0,0 +1,83 @@
|
|
|
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");
|
|
9
|
+
const ALL_COMMANDS = [
|
|
10
|
+
{
|
|
11
|
+
name: 'me',
|
|
12
|
+
description: 'Show details of current user',
|
|
13
|
+
usage: 'jira-ai me --help'
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
name: 'projects',
|
|
17
|
+
description: 'List available projects',
|
|
18
|
+
usage: 'jira-ai projects --help'
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
name: 'task-with-details',
|
|
22
|
+
description: 'Show task title, body, and comments',
|
|
23
|
+
usage: 'jira-ai task-with-details --help'
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
name: 'project-statuses',
|
|
27
|
+
description: 'Show all possible statuses for a project',
|
|
28
|
+
usage: 'jira-ai project-statuses --help'
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
name: 'run-jql',
|
|
32
|
+
description: 'Execute JQL query and display results',
|
|
33
|
+
usage: 'jira-ai run-jql "<jql-query>" [-l <limit>]'
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
name: 'update-description',
|
|
37
|
+
description: 'Update task description from a Markdown file',
|
|
38
|
+
usage: 'jira-ai update-description <task-id> --from-file <path>'
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
name: 'add-comment',
|
|
42
|
+
description: 'Add a comment to a Jira issue from a Markdown file',
|
|
43
|
+
usage: 'jira-ai add-comment --file-path <path> --issue-key <key>'
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
name: 'about',
|
|
47
|
+
description: 'Show this help message',
|
|
48
|
+
usage: 'jira-ai about'
|
|
49
|
+
}
|
|
50
|
+
];
|
|
51
|
+
async function aboutCommand() {
|
|
52
|
+
console.log(chalk_1.default.bold.cyan('\n📋 Jira AI - Available Commands\n'));
|
|
53
|
+
console.log(chalk_1.default.bold('Usage:'));
|
|
54
|
+
console.log(' jira-ai <command> [options]\n');
|
|
55
|
+
const allowedCommandsList = (0, settings_1.getAllowedCommands)();
|
|
56
|
+
const isAllAllowed = allowedCommandsList.includes('all');
|
|
57
|
+
// Filter commands based on settings (about is always shown)
|
|
58
|
+
const commandsToShow = ALL_COMMANDS.filter(cmd => cmd.name === 'about' || isAllAllowed || (0, settings_1.isCommandAllowed)(cmd.name));
|
|
59
|
+
console.log(chalk_1.default.bold('Available Commands:\n'));
|
|
60
|
+
for (const cmd of commandsToShow) {
|
|
61
|
+
console.log(chalk_1.default.yellow(` ${cmd.name}`));
|
|
62
|
+
console.log(` ${cmd.description}`);
|
|
63
|
+
console.log(` Usage: ${cmd.usage}\n`);
|
|
64
|
+
}
|
|
65
|
+
// Show disabled commands if not all are allowed
|
|
66
|
+
if (!isAllAllowed) {
|
|
67
|
+
const disabledCommands = ALL_COMMANDS.filter(cmd => cmd.name !== 'about' && !(0, settings_1.isCommandAllowed)(cmd.name));
|
|
68
|
+
if (disabledCommands.length > 0) {
|
|
69
|
+
console.log(chalk_1.default.bold('Disabled Commands:\n'));
|
|
70
|
+
for (const cmd of disabledCommands) {
|
|
71
|
+
console.log(chalk_1.default.gray(` ${cmd.name} - ${cmd.description}`));
|
|
72
|
+
}
|
|
73
|
+
console.log();
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
console.log(chalk_1.default.bold('For detailed help on any command, run:'));
|
|
77
|
+
console.log(chalk_1.default.green(' jira-ai <command> --help\n'));
|
|
78
|
+
console.log(chalk_1.default.bold('Configuration:'));
|
|
79
|
+
console.log(' Settings are managed in settings.yaml');
|
|
80
|
+
const allowedProjects = (0, settings_1.getAllowedProjects)();
|
|
81
|
+
console.log(` - Projects: ${allowedProjects.includes('all') ? 'All allowed' : allowedProjects.join(', ')}`);
|
|
82
|
+
console.log(` - Commands: ${isAllAllowed ? 'All allowed' : allowedCommandsList.join(', ')}\n`);
|
|
83
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
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 md_to_adf_1 = __importDefault(require("md-to-adf"));
|
|
45
|
+
const jira_client_1 = require("../lib/jira-client");
|
|
46
|
+
async function addCommentCommand(options) {
|
|
47
|
+
const { filePath, issueKey } = options;
|
|
48
|
+
// Validate issueKey
|
|
49
|
+
if (!issueKey || issueKey.trim() === '') {
|
|
50
|
+
console.error(chalk_1.default.red('\nError: Issue key is required (use --issue-key)'));
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
// Validate file path
|
|
54
|
+
if (!filePath || filePath.trim() === '') {
|
|
55
|
+
console.error(chalk_1.default.red('\nError: File path is required (use --file-path)'));
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
// Resolve file path to absolute
|
|
59
|
+
const absolutePath = path.resolve(filePath);
|
|
60
|
+
// Check file exists
|
|
61
|
+
if (!fs.existsSync(absolutePath)) {
|
|
62
|
+
console.error(chalk_1.default.red(`\nError: File not found: ${absolutePath}`));
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
// Read file
|
|
66
|
+
let markdownContent;
|
|
67
|
+
try {
|
|
68
|
+
markdownContent = fs.readFileSync(absolutePath, 'utf-8');
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
console.error(chalk_1.default.red('\nError reading file: ' +
|
|
72
|
+
(error instanceof Error ? error.message : 'Unknown error')));
|
|
73
|
+
process.exit(1);
|
|
74
|
+
}
|
|
75
|
+
// Validate file is not empty
|
|
76
|
+
if (markdownContent.trim() === '') {
|
|
77
|
+
console.error(chalk_1.default.red('\nError: File is empty'));
|
|
78
|
+
process.exit(1);
|
|
79
|
+
}
|
|
80
|
+
// Convert Markdown to ADF
|
|
81
|
+
let adfContent;
|
|
82
|
+
try {
|
|
83
|
+
adfContent = (0, md_to_adf_1.default)(markdownContent);
|
|
84
|
+
}
|
|
85
|
+
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);
|
|
89
|
+
}
|
|
90
|
+
// Add comment with spinner
|
|
91
|
+
const spinner = (0, ora_1.default)(`Adding comment to ${issueKey}...`).start();
|
|
92
|
+
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}`));
|
|
96
|
+
}
|
|
97
|
+
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')));
|
|
100
|
+
// Provide helpful hints based on error
|
|
101
|
+
if (error instanceof Error && error.message.includes('404')) {
|
|
102
|
+
console.log(chalk_1.default.yellow('\nHint: Check that the issue key is correct'));
|
|
103
|
+
}
|
|
104
|
+
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'));
|
|
106
|
+
}
|
|
107
|
+
process.exit(1);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
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.meCommand = meCommand;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const ora_1 = __importDefault(require("ora"));
|
|
9
|
+
const jira_client_1 = require("../lib/jira-client");
|
|
10
|
+
const formatters_1 = require("../lib/formatters");
|
|
11
|
+
async function meCommand() {
|
|
12
|
+
const spinner = (0, ora_1.default)('Fetching user information...').start();
|
|
13
|
+
try {
|
|
14
|
+
const user = await (0, jira_client_1.getCurrentUser)();
|
|
15
|
+
spinner.succeed(chalk_1.default.green('User information retrieved'));
|
|
16
|
+
console.log((0, formatters_1.formatUserInfo)(user));
|
|
17
|
+
}
|
|
18
|
+
catch (error) {
|
|
19
|
+
spinner.fail(chalk_1.default.red('Failed to fetch user information'));
|
|
20
|
+
console.error(chalk_1.default.red('\nError: ' + (error instanceof Error ? error.message : 'Unknown error')));
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
}
|