jiradc-cli 1.0.10 → 1.0.12-g3332dfd.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 (3) hide show
  1. package/README.md +143 -0
  2. package/dist/index.js +423 -86
  3. package/package.json +2 -2
package/README.md ADDED
@@ -0,0 +1,143 @@
1
+ # jiradc-cli
2
+
3
+ Command-line interface for [Jira Data Center](https://developer.atlassian.com/server/jira/platform/rest-apis/). 35 commands across 6 domains — issues, projects, boards, sprints, fields, and users.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install -g jiradc-cli
9
+ ```
10
+
11
+ ## Setup
12
+
13
+ ```bash
14
+ export JIRA_URL="https://jira.example.com" # Base URL of your Jira instance
15
+ export JIRA_TOKEN="your-personal-access-token" # HTTP Access Token from Jira
16
+ ```
17
+
18
+ ## Commands
19
+
20
+ All commands output JSON. Add `--pretty` to pretty-print.
21
+
22
+ ### issue
23
+
24
+ | Command | Description |
25
+ |---------|-------------|
26
+ | `jiradc issue get <key>` | Get issue details (`--fields` to select, `--expand` for changelog/transitions) |
27
+ | `jiradc issue search <jql>` | Search issues with JQL |
28
+ | `jiradc issue create` | Create an issue (`--project`, `--type`, `--summary`, `--description`, `--custom-fields`) |
29
+ | `jiradc issue update <key>` | Update an issue |
30
+ | `jiradc issue delete <key>` | Delete an issue |
31
+ | `jiradc issue transition <key> <transitionId>` | Transition issue to a new status |
32
+ | `jiradc issue transitions <key>` | List available transitions |
33
+ | `jiradc issue comment <key>` | Add a comment |
34
+ | `jiradc issue comment-edit <key> <commentId>` | Edit a comment |
35
+ | `jiradc issue link <key> <targetKey>` | Link two issues (`--type` link type name) |
36
+ | `jiradc issue unlink <linkId>` | Remove a link |
37
+ | `jiradc issue link-types` | List available link types |
38
+ | `jiradc issue link-epic <epicKey> <issueKey>` | Link an issue to an epic |
39
+ | `jiradc issue worklog <key>` | Add a work log entry |
40
+ | `jiradc issue get-worklog <key>` | Get work log entries |
41
+ | `jiradc issue changelog <key>` | Get issue changelog |
42
+ | `jiradc issue batch-changelog` | Get changelog for multiple issues (`--keys`) |
43
+ | `jiradc issue clone <key>` | Clone an issue with subtasks |
44
+ | `jiradc issue batch-create` | Create multiple issues from JSON |
45
+ | `jiradc issue dev-status <key>` | Get development status (branches, PRs, commits) |
46
+
47
+ #### issue attachment
48
+
49
+ | Command | Description |
50
+ |---------|-------------|
51
+ | `jiradc issue attachment list <key>` | List attachments |
52
+ | `jiradc issue attachment download <key> <attachmentId>` | Download a specific attachment |
53
+ | `jiradc issue attachment download-all <key>` | Download all attachments |
54
+ | `jiradc issue attachment upload <key> <files...>` | Upload files |
55
+ | `jiradc issue attachment delete <attachmentId>` | Delete an attachment |
56
+
57
+ ### project
58
+
59
+ | Command | Description |
60
+ |---------|-------------|
61
+ | `jiradc project list` | List projects |
62
+ | `jiradc project versions <key>` | List versions for a project |
63
+ | `jiradc project components <key>` | List components for a project |
64
+
65
+ ### board
66
+
67
+ | Command | Description |
68
+ |---------|-------------|
69
+ | `jiradc board list` | List boards (`--project`, `--type`: scrum/kanban/simple, `--name`) |
70
+ | `jiradc board issues <boardId>` | Get issues on a board |
71
+
72
+ ### sprint
73
+
74
+ | Command | Description |
75
+ |---------|-------------|
76
+ | `jiradc sprint list <boardId>` | List sprints (`--state`: future/active/closed) |
77
+ | `jiradc sprint issues <boardId> <sprintId>` | Get issues in a sprint |
78
+ | `jiradc sprint create <boardId>` | Create a sprint |
79
+ | `jiradc sprint update <sprintId>` | Update a sprint |
80
+
81
+ ### field
82
+
83
+ | Command | Description |
84
+ |---------|-------------|
85
+ | `jiradc field search` | Search for fields (`--query`, `--type`: custom/system) |
86
+ | `jiradc field options <fieldKey>` | Get allowed values for a field (`--project`, `--issue-type`) |
87
+
88
+ ### user
89
+
90
+ | Command | Description |
91
+ |---------|-------------|
92
+ | `jiradc user me` | Get current user info |
93
+
94
+ ## Pagination
95
+
96
+ Search and list commands accept `--max` to control page size (Jira DC caps at 50 for agile endpoints). Responses include `nextPage` — pass it back as `--start-at` to fetch the next page. When `nextPage` is `null`, there are no more results.
97
+
98
+ ## Examples
99
+
100
+ ```bash
101
+ # Get an issue with default fields
102
+ jiradc issue get AI-123
103
+
104
+ # Get an issue with specific fields
105
+ jiradc issue get AI-123 --fields summary,status,assignee,customfield_10100
106
+
107
+ # Search with JQL
108
+ jiradc issue search 'project = AI AND status = "In Development" ORDER BY updated DESC'
109
+
110
+ # Create an issue
111
+ jiradc issue create --project AI --type Task --summary "Implement feature X" --description "Details here"
112
+
113
+ # Create with custom fields
114
+ jiradc issue create --project AI --type Story --summary "User login" --custom-fields '{"customfield_10100": "value"}'
115
+
116
+ # Transition an issue (find transition ID first)
117
+ jiradc issue transitions AI-123
118
+ jiradc issue transition AI-123 31
119
+
120
+ # Add a comment
121
+ jiradc issue comment AI-123 --body "Fixed in commit abc123"
122
+
123
+ # Link two issues
124
+ jiradc issue link AI-123 AI-456 --type "blocks"
125
+
126
+ # Log work
127
+ jiradc issue worklog AI-123 --time "2h 30m" --comment "Code review"
128
+
129
+ # List active sprints
130
+ jiradc sprint list 42 --state active
131
+
132
+ # Get sprint issues
133
+ jiradc sprint issues 42 100
134
+
135
+ # Find a custom field key
136
+ jiradc field search --query "story points"
137
+
138
+ # Clone an issue with subtasks
139
+ jiradc issue clone AI-123
140
+
141
+ # Get dev status (linked branches, PRs)
142
+ jiradc issue dev-status AI-123
143
+ ```
package/dist/index.js CHANGED
@@ -5,7 +5,7 @@ import { readFileSync } from "fs";
5
5
  import { dirname, join as join4 } from "path";
6
6
  import { fileURLToPath } from "url";
7
7
  import { styleText } from "util";
8
- import { Command as Command6 } from "commander";
8
+ import { Command as Command8 } from "commander";
9
9
 
10
10
  // src/commands/board/issues.ts
11
11
  import { Argument } from "commander";
@@ -80,35 +80,13 @@ function getClient() {
80
80
  return new JiraClient({ baseUrl, token });
81
81
  }
82
82
 
83
- // src/utils/strip-response.ts
84
- function stripResponse(obj) {
85
- if (Array.isArray(obj)) {
86
- return obj.map(stripResponse);
87
- }
88
- if (obj === null || typeof obj !== "object") {
89
- return obj;
90
- }
91
- const record = obj;
92
- const result = {};
93
- for (const [key, value] of Object.entries(record)) {
94
- if (key === "self" || key === "_links" || key === "_expandable" || key === "expand" || key === "avatarUrls" || key === "avatarId" || key === "iconUrl") {
95
- continue;
96
- }
97
- if (value === null || value === void 0) {
98
- continue;
99
- }
100
- result[key] = stripResponse(value);
101
- }
102
- return result;
103
- }
104
-
105
83
  // src/utils/output.ts
106
84
  var prettyPrint = false;
107
85
  function setPretty(value) {
108
86
  prettyPrint = value;
109
87
  }
110
88
  function output(data) {
111
- process.stdout.write(`${JSON.stringify(stripResponse(data), null, prettyPrint ? 2 : void 0)}
89
+ process.stdout.write(`${JSON.stringify(data, null, prettyPrint ? 2 : void 0)}
112
90
  `);
113
91
  }
114
92
  function handleError(err) {
@@ -174,6 +152,234 @@ function handleError(err) {
174
152
  process.exit(1);
175
153
  }
176
154
 
155
+ // src/utils/transformers/base.ts
156
+ function jiraBaseUrl() {
157
+ return (process.env.JIRA_URL ?? "").replace(/\/+$/, "");
158
+ }
159
+ function transformPaged(response, fn) {
160
+ const { startAt, maxResults, total } = response;
161
+ if ("issues" in response) {
162
+ const mapped2 = response.issues.map(fn);
163
+ return { startAt, maxResults, total, isLast: startAt + mapped2.length >= total, issues: mapped2 };
164
+ }
165
+ if ("values" in response) {
166
+ const mapped2 = response.values.map(fn);
167
+ return { startAt, maxResults, total, isLast: startAt + mapped2.length >= total, values: mapped2 };
168
+ }
169
+ const mapped = response.worklogs.map(fn);
170
+ return { startAt, maxResults, total, isLast: startAt + mapped.length >= total, worklogs: mapped };
171
+ }
172
+
173
+ // src/utils/transformers/user.ts
174
+ function transformUser(user) {
175
+ const { self: _self, avatarUrls: _avatarUrls, expand: _expand, ...rest } = user;
176
+ return rest;
177
+ }
178
+
179
+ // src/utils/transformers/field.ts
180
+ function transformField(field) {
181
+ return field;
182
+ }
183
+ function transformCustomFieldOption(option) {
184
+ const { self: _self, ...rest } = option;
185
+ return rest;
186
+ }
187
+
188
+ // src/utils/transformers/project.ts
189
+ function projectBrowseUrl(key) {
190
+ return `${jiraBaseUrl()}/projects/${key}`;
191
+ }
192
+ function transformProject(p) {
193
+ const { self: _self, avatarUrls: _avatarUrls, expand: _expand, lead, ...rest } = p;
194
+ return {
195
+ ...rest,
196
+ url: projectBrowseUrl(p.key),
197
+ ...lead ? { lead: transformUser(lead) } : {}
198
+ };
199
+ }
200
+
201
+ // src/utils/transformers/component.ts
202
+ function componentBrowseUrl(projectKey) {
203
+ return `${jiraBaseUrl()}/projects/${projectKey}/components`;
204
+ }
205
+ function transformComponent(c) {
206
+ const { self: _self, lead, assignee, realAssignee, ...rest } = c;
207
+ return {
208
+ ...rest,
209
+ ...c.project ? { url: componentBrowseUrl(c.project) } : {},
210
+ ...lead ? { lead: transformUser(lead) } : {},
211
+ ...assignee ? { assignee: transformUser(assignee) } : {},
212
+ ...realAssignee ? { realAssignee: transformUser(realAssignee) } : {}
213
+ };
214
+ }
215
+ function transformComponentIssueCounts(c) {
216
+ const { self: _self, ...rest } = c;
217
+ return rest;
218
+ }
219
+
220
+ // src/utils/transformers/board.ts
221
+ function boardBrowseUrl(boardId) {
222
+ return `${jiraBaseUrl()}/secure/RapidBoard.jspa?rapidView=${boardId}`;
223
+ }
224
+ function transformBoard(b) {
225
+ const { self: _self, ...rest } = b;
226
+ return { ...rest, url: boardBrowseUrl(b.id) };
227
+ }
228
+
229
+ // src/utils/transformers/sprint.ts
230
+ function sprintBrowseUrl(boardId, sprintId) {
231
+ return `${jiraBaseUrl()}/secure/RapidBoard.jspa?rapidView=${boardId}&view=planning&sprint=${sprintId}`;
232
+ }
233
+ function transformSprint(s) {
234
+ const { self: _self, ...rest } = s;
235
+ return {
236
+ ...rest,
237
+ ...s.originBoardId !== void 0 ? { url: sprintBrowseUrl(s.originBoardId, s.id) } : {}
238
+ };
239
+ }
240
+
241
+ // src/utils/transformers/issue.ts
242
+ function issueBrowseUrl(key) {
243
+ return `${jiraBaseUrl()}/browse/${key}`;
244
+ }
245
+ function transformIssueRef(key) {
246
+ return { key, url: issueBrowseUrl(key) };
247
+ }
248
+ function transformCreatedIssue(r) {
249
+ return { id: r.id, key: r.key, url: issueBrowseUrl(r.key) };
250
+ }
251
+ function transformIssueType(t) {
252
+ const { self: _self, iconUrl: _iconUrl, avatarId: _avatarId, ...rest } = t;
253
+ return rest;
254
+ }
255
+ function transformStatus(s) {
256
+ const { self: _self, iconUrl: _iconUrl, ...rest } = s;
257
+ return rest;
258
+ }
259
+ function transformPriority(p) {
260
+ const { self: _self, iconUrl: _iconUrl, ...rest } = p;
261
+ return rest;
262
+ }
263
+ function transformResolution(r) {
264
+ const { self: _self, ...rest } = r;
265
+ return rest;
266
+ }
267
+ function transformVersion(v) {
268
+ const { self: _self, ...rest } = v;
269
+ return rest;
270
+ }
271
+ function transformIssueLinkType(t) {
272
+ const { self: _self, ...rest } = t;
273
+ return rest;
274
+ }
275
+ function transformComment(c) {
276
+ const { self: _self, author, updateAuthor, ...rest } = c;
277
+ return {
278
+ ...rest,
279
+ ...author ? { author: transformUser(author) } : {},
280
+ ...updateAuthor ? { updateAuthor: transformUser(updateAuthor) } : {}
281
+ };
282
+ }
283
+ function transformWorklog(w) {
284
+ const { self: _self, author, updateAuthor, ...rest } = w;
285
+ return {
286
+ ...rest,
287
+ ...author ? { author: transformUser(author) } : {},
288
+ ...updateAuthor ? { updateAuthor: transformUser(updateAuthor) } : {}
289
+ };
290
+ }
291
+ function transformAttachment(a) {
292
+ const { self: _self, author, ...rest } = a;
293
+ return {
294
+ ...rest,
295
+ ...author ? { author: transformUser(author) } : {}
296
+ };
297
+ }
298
+ function transformIssueBasic(b) {
299
+ const { self: _self, fields, ...rest } = b;
300
+ return {
301
+ ...rest,
302
+ url: issueBrowseUrl(b.key),
303
+ ...fields ? {
304
+ fields: {
305
+ ...fields.summary !== void 0 ? { summary: fields.summary } : {},
306
+ ...fields.status ? { status: transformStatus(fields.status) } : {},
307
+ ...fields.priority ? { priority: transformPriority(fields.priority) } : {},
308
+ ...fields.issuetype ? { issuetype: transformIssueType(fields.issuetype) } : {}
309
+ }
310
+ } : {}
311
+ };
312
+ }
313
+ function transformIssueLink(l) {
314
+ const { self: _self, type, inwardIssue, outwardIssue, ...rest } = l;
315
+ return {
316
+ ...rest,
317
+ type: transformIssueLinkType(type),
318
+ ...inwardIssue ? { inwardIssue: transformIssueBasic(inwardIssue) } : {},
319
+ ...outwardIssue ? { outwardIssue: transformIssueBasic(outwardIssue) } : {}
320
+ };
321
+ }
322
+ function transformIssue(issue) {
323
+ const { self: _self, expand: _expand, fields, ...rest } = issue;
324
+ return {
325
+ ...rest,
326
+ url: issueBrowseUrl(issue.key),
327
+ fields: transformIssueFields(fields)
328
+ };
329
+ }
330
+ function transformIssueFields(fields) {
331
+ const {
332
+ issuetype,
333
+ status,
334
+ priority,
335
+ resolution,
336
+ assignee,
337
+ reporter,
338
+ creator,
339
+ fixVersions,
340
+ versions: versions2,
341
+ issuelinks,
342
+ subtasks,
343
+ parent,
344
+ comment: comment2,
345
+ worklog: worklog2,
346
+ attachment,
347
+ ...rest
348
+ } = fields;
349
+ return {
350
+ ...rest,
351
+ issuetype: transformIssueType(issuetype),
352
+ status: transformStatus(status),
353
+ ...priority ? { priority: transformPriority(priority) } : {},
354
+ ...resolution ? { resolution: transformResolution(resolution) } : {},
355
+ ...assignee ? { assignee: transformUser(assignee) } : {},
356
+ ...reporter ? { reporter: transformUser(reporter) } : {},
357
+ ...creator ? { creator: transformUser(creator) } : {},
358
+ ...fixVersions ? { fixVersions: fixVersions.map(transformVersion) } : {},
359
+ ...versions2 ? { versions: versions2.map(transformVersion) } : {},
360
+ ...issuelinks ? { issuelinks: issuelinks.map(transformIssueLink) } : {},
361
+ ...subtasks ? { subtasks: subtasks.map(transformIssueBasic) } : {},
362
+ ...parent ? { parent: transformIssueBasic(parent) } : {},
363
+ ...comment2 ? {
364
+ comment: {
365
+ comments: comment2.comments.map(transformComment),
366
+ maxResults: comment2.maxResults,
367
+ total: comment2.total,
368
+ startAt: comment2.startAt
369
+ }
370
+ } : {},
371
+ ...worklog2 ? {
372
+ worklog: {
373
+ worklogs: worklog2.worklogs.map(transformWorklog),
374
+ maxResults: worklog2.maxResults,
375
+ total: worklog2.total,
376
+ startAt: worklog2.startAt
377
+ }
378
+ } : {},
379
+ ...attachment ? { attachment: attachment.map(transformAttachment) } : {}
380
+ };
381
+ }
382
+
177
383
  // src/commands/board/issues.ts
178
384
  function issues(parent) {
179
385
  parent.command("issues").description("Get issues for a board").addArgument(new Argument("<id>", "Board ID").argParser(positiveInt)).option("--max <number>", "Max results (1-50, Jira DC caps at 50)", intInRange(1, 50), 25).option("--start-at <number>", "Starting index for pagination", nonNegativeInt).option("--fields <fields>", "Comma-separated field names to return").option("--jql <jql>", "Additional JQL filter within the board").addHelpText(
@@ -188,7 +394,7 @@ function issues(parent) {
188
394
  fields: opts.fields?.split(",").map((f) => f.trim()),
189
395
  jql: opts.jql
190
396
  });
191
- output(result);
397
+ output(transformPaged(result, transformIssue));
192
398
  });
193
399
  }
194
400
 
@@ -207,7 +413,7 @@ function list(parent) {
207
413
  type: opts.type,
208
414
  name: opts.name
209
415
  });
210
- output(result);
416
+ output(result.map(transformBoard));
211
417
  });
212
418
  }
213
419
 
@@ -226,6 +432,135 @@ Examples:
226
432
  issues(board);
227
433
  }
228
434
 
435
+ // src/commands/component/create.ts
436
+ import { Option as Option2 } from "commander";
437
+ var ASSIGNEE_TYPES = [
438
+ "PROJECT_DEFAULT",
439
+ "COMPONENT_LEAD",
440
+ "PROJECT_LEAD",
441
+ "UNASSIGNED"
442
+ ];
443
+ function create(parent) {
444
+ parent.command("create").description("Create a new component in a project").requiredOption("--project <key>", "Project key (e.g., AI)").requiredOption("--name <name>", "Component name").option("--description <text>", "Component description").option("--lead <username>", "Username of the component lead").addOption(new Option2("--assignee-type <type>", "Assignee strategy").choices(ASSIGNEE_TYPES)).addHelpText(
445
+ "after",
446
+ `
447
+ Examples:
448
+ jiradc component create --project AI --name Backend
449
+ jiradc component create --project AI --name Frontend --description "UI work" --lead jsmith
450
+ jiradc component create --project AI --name Infra --assignee-type COMPONENT_LEAD --lead jsmith`
451
+ ).action(
452
+ async (opts) => {
453
+ const client = getClient();
454
+ const result = await client.components.create({
455
+ project: opts.project,
456
+ name: opts.name,
457
+ description: opts.description,
458
+ leadUserName: opts.lead,
459
+ assigneeType: opts.assigneeType
460
+ });
461
+ output(transformComponent(result));
462
+ }
463
+ );
464
+ }
465
+
466
+ // src/commands/component/delete.ts
467
+ function deleteComponent(parent) {
468
+ parent.command("delete <id>").description("Delete a component, optionally reassigning its issues to another component").option("--move-issues-to <id>", "Reassign existing issues to this component ID before deletion").addHelpText(
469
+ "after",
470
+ `
471
+ Examples:
472
+ jiradc component delete 11289
473
+ jiradc component delete 11289 --move-issues-to 11290`
474
+ ).action(async (id, opts) => {
475
+ const client = getClient();
476
+ await client.components.delete({ id, moveIssuesTo: opts.moveIssuesTo });
477
+ output({ deleted: true, componentId: id, ...opts.moveIssuesTo && { movedIssuesTo: opts.moveIssuesTo } });
478
+ });
479
+ }
480
+
481
+ // src/commands/component/get.ts
482
+ function get(parent) {
483
+ parent.command("get <id>").description("Get a component by ID").addHelpText("after", "\nExamples:\n jiradc component get 11289").action(async (id) => {
484
+ const client = getClient();
485
+ const result = await client.components.get({ id });
486
+ output(transformComponent(result));
487
+ });
488
+ }
489
+
490
+ // src/commands/component/issue-count.ts
491
+ function issueCount(parent) {
492
+ parent.command("issue-count <id>").description("Get the number of issues currently using this component").addHelpText("after", "\nExamples:\n jiradc component issue-count 11289").action(async (id) => {
493
+ const client = getClient();
494
+ const result = await client.components.getRelatedIssueCounts({ id });
495
+ output(transformComponentIssueCounts(result));
496
+ });
497
+ }
498
+
499
+ // src/commands/component/list.ts
500
+ function list2(parent) {
501
+ parent.command("list <projectKey>").description("List all components for a project").addHelpText("after", "\nExamples:\n jiradc component list AI").action(async (projectKey) => {
502
+ const client = getClient();
503
+ const result = await client.components.list({ projectKeyOrId: projectKey });
504
+ output(result.map(transformComponent));
505
+ });
506
+ }
507
+
508
+ // src/commands/component/update.ts
509
+ import { Option as Option3 } from "commander";
510
+ var ASSIGNEE_TYPES2 = [
511
+ "PROJECT_DEFAULT",
512
+ "COMPONENT_LEAD",
513
+ "PROJECT_LEAD",
514
+ "UNASSIGNED"
515
+ ];
516
+ function update(parent) {
517
+ parent.command("update <id>").description("Update an existing component (only provided fields are changed)").option("--name <name>", "New component name").option("--description <text>", "New component description").option("--lead <username>", "Username of the component lead (empty string clears it)").addOption(new Option3("--assignee-type <type>", "New assignee strategy").choices(ASSIGNEE_TYPES2)).addHelpText(
518
+ "after",
519
+ `
520
+ Examples:
521
+ jiradc component update 11289 --name Backend
522
+ jiradc component update 11289 --description "Server-side code"
523
+ jiradc component update 11289 --lead jsmith --assignee-type COMPONENT_LEAD`
524
+ ).action(
525
+ async (id, opts) => {
526
+ if (opts.name === void 0 && opts.description === void 0 && opts.lead === void 0 && opts.assigneeType === void 0) {
527
+ throw new Error("Provide at least one of --name, --description, --lead, --assignee-type");
528
+ }
529
+ const client = getClient();
530
+ const result = await client.components.update({
531
+ id,
532
+ name: opts.name,
533
+ description: opts.description,
534
+ leadUserName: opts.lead,
535
+ assigneeType: opts.assigneeType
536
+ });
537
+ output(transformComponent(result));
538
+ }
539
+ );
540
+ }
541
+
542
+ // src/commands/component/index.ts
543
+ function registerComponentCommands(program2) {
544
+ const component = program2.command("component").description("Project component operations").addHelpText(
545
+ "after",
546
+ `
547
+ Examples:
548
+ $ jiradc component list AI
549
+ $ jiradc component get 11289
550
+ $ jiradc component create --project AI --name Backend
551
+ $ jiradc component update 11289 --name Backend
552
+ $ jiradc component delete 11289 --move-issues-to 11290
553
+ $ jiradc component issue-count 11289
554
+ `
555
+ );
556
+ list2(component);
557
+ get(component);
558
+ create(component);
559
+ update(component);
560
+ deleteComponent(component);
561
+ issueCount(component);
562
+ }
563
+
229
564
  // src/commands/field/options.ts
230
565
  function options(parent) {
231
566
  parent.command("options <id>").description("Get available options for a custom field").option("--query <text>", "Filter options by text").option("--max <number>", "Max results to return (1-1000)", intInRange(1, 1e3), 25).option("--page <number>", "Page number (1-indexed)", positiveInt).addHelpText(
@@ -239,7 +574,7 @@ function options(parent) {
239
574
  maxResults: opts.max,
240
575
  page: opts.page
241
576
  });
242
- output(result);
577
+ output(transformPaged({ ...result, startAt: result.startAt ?? 0 }, transformCustomFieldOption));
243
578
  });
244
579
  }
245
580
 
@@ -251,7 +586,7 @@ function search(parent) {
251
586
  ).action(async (keyword, opts) => {
252
587
  const client = getClient();
253
588
  const result = await client.fields.search(keyword, opts.limit);
254
- output(result);
589
+ output(result.map(transformField));
255
590
  });
256
591
  }
257
592
 
@@ -343,7 +678,7 @@ function download(parent) {
343
678
  }
344
679
 
345
680
  // src/commands/issue/attachment/list.ts
346
- function list2(parent) {
681
+ function list3(parent) {
347
682
  parent.command("list <key>").description("List attachments on an issue").addHelpText("after", "\nExamples:\n jiradc issue attachment list PROJ-123").action(async (key) => {
348
683
  const client = getClient();
349
684
  const issue = await client.issues.get({
@@ -406,7 +741,7 @@ Examples:
406
741
  `
407
742
  );
408
743
  upload(attachment);
409
- list2(attachment);
744
+ list3(attachment);
410
745
  download(attachment);
411
746
  downloadAll(attachment);
412
747
  deleteAttachment(attachment);
@@ -489,7 +824,7 @@ Examples:
489
824
  for (const issue of parsed) {
490
825
  try {
491
826
  const result = await client.issues.create(issue);
492
- results.push({ key: result.key });
827
+ results.push(transformCreatedIssue(result));
493
828
  } catch (error) {
494
829
  results.push({ error: error instanceof Error ? error.message : "Unknown error" });
495
830
  }
@@ -550,12 +885,12 @@ Examples:
550
885
  dueDate: f.duedate ?? void 0,
551
886
  assignee: opts.assignee ?? f.assignee?.name
552
887
  };
553
- const result = await client.issues.create(createParams);
554
- const newKey = result.key;
888
+ const created = transformCreatedIssue(await client.issues.create(createParams));
889
+ const newKey = created.key;
555
890
  const cloneResult = {
556
891
  cloned: true,
557
892
  source: key,
558
- newIssue: result
893
+ newIssue: created
559
894
  };
560
895
  if (opts.includeAttachments && f.attachment?.length) {
561
896
  const tmpFiles = [];
@@ -592,9 +927,9 @@ Examples:
592
927
  return Promise.resolve();
593
928
  })
594
929
  );
595
- const created = outcomes.filter((o) => o.status === "fulfilled").length;
930
+ const created2 = outcomes.filter((o) => o.status === "fulfilled").length;
596
931
  const skipped = outcomes.filter((o) => o.status === "rejected").length;
597
- cloneResult.linksCopied = created;
932
+ cloneResult.linksCopied = created2;
598
933
  if (skipped > 0) cloneResult.linksSkipped = skipped;
599
934
  }
600
935
  output(cloneResult);
@@ -607,7 +942,7 @@ function commentEdit(parent) {
607
942
  parent.command("comment-edit <key>").description("Edit an existing comment").requiredOption("--id <commentId>", "Comment ID to edit").requiredOption("--body <text>", "Updated comment body in wiki markup").addHelpText("after", '\nExamples:\n jiradc issue comment-edit PROJ-123 --id 12345 --body "Updated comment text"').action(async (key, opts) => {
608
943
  const client = getClient();
609
944
  const result = await client.issues.editComment({ issueKeyOrId: key, commentId: opts.id, body: opts.body });
610
- output(result);
945
+ output(transformComment(result));
611
946
  });
612
947
  }
613
948
 
@@ -616,12 +951,12 @@ function comment(parent) {
616
951
  parent.command("comment <key>").description("Add a comment to an issue").requiredOption("--body <text>", "Comment body in wiki markup").addHelpText("after", '\nExamples:\n jiradc issue comment PROJ-123 --body "Fixed in latest build"').action(async (key, opts) => {
617
952
  const client = getClient();
618
953
  const result = await client.issues.addComment({ issueKeyOrId: key, body: opts.body });
619
- output(result);
954
+ output(transformComment(result));
620
955
  });
621
956
  }
622
957
 
623
958
  // src/commands/issue/create.ts
624
- function create(parent) {
959
+ function create2(parent) {
625
960
  parent.command("create").description("Create a new issue").requiredOption("--project <key>", "Project key or ID").requiredOption("--type <name>", "Issue type name (e.g., Task, Bug, Story)").requiredOption("--summary <text>", "Issue summary/title").option("--description <text>", "Issue description in wiki markup").option("--assignee <username>", "Assignee username").option("--reporter <username>", "Reporter username").option("--priority <name>", "Priority name (e.g., High, Medium, Low)").option("--labels <labels>", "Comma-separated labels").option("--components <names>", "Comma-separated component names").option("--fix-versions <versions>", "Comma-separated fix version names").option("--due-date <date>", "Due date in YYYY-MM-DD format").option("--parent <key>", "Parent issue key (for subtasks)").option("--custom-fields <json>", `Additional custom fields as JSON (e.g., '{"customfield_10100": "EPIC-1"}')`).addHelpText(
626
961
  "after",
627
962
  `
@@ -648,7 +983,7 @@ Examples:
648
983
  parent: opts.parent,
649
984
  customFields: opts.customFields ? JSON.parse(opts.customFields) : void 0
650
985
  });
651
- output(result);
986
+ output(transformCreatedIssue(result));
652
987
  }
653
988
  );
654
989
  }
@@ -756,7 +1091,7 @@ function getWorklog(parent) {
756
1091
  startAt: opts.startAt,
757
1092
  maxResults: opts.max
758
1093
  });
759
- output(result);
1094
+ output(transformPaged(result, transformWorklog));
760
1095
  });
761
1096
  }
762
1097
 
@@ -777,7 +1112,7 @@ var DEFAULT_FIELDS = [
777
1112
  ];
778
1113
 
779
1114
  // src/commands/issue/get.ts
780
- function get(parent) {
1115
+ function get2(parent) {
781
1116
  parent.command("get <key>").description("Get issue details").option("--fields <fields>", "Comma-separated fields to return (defaults to essential fields)").option("--all-fields", "Return all fields instead of defaults").option("--expand <expand>", 'Expand options (e.g., "transitions", "changelog")').addHelpText(
782
1117
  "after",
783
1118
  "\nExamples:\n jiradc issue get PROJ-123\n jiradc issue get PROJ-123 --fields summary,status,assignee\n jiradc issue get PROJ-123 --all-fields\n jiradc issue get PROJ-123 --expand changelog,transitions"
@@ -789,7 +1124,7 @@ function get(parent) {
789
1124
  fields,
790
1125
  expand: opts.expand
791
1126
  });
792
- output(result);
1127
+ output(transformIssue(result));
793
1128
  });
794
1129
  }
795
1130
 
@@ -801,7 +1136,11 @@ function linkEpic(parent) {
801
1136
  issueKeyOrId: key,
802
1137
  fields: { customfield_10100: opts.epic }
803
1138
  });
804
- output({ linked: true, issue: key, epic: opts.epic });
1139
+ output({
1140
+ linked: true,
1141
+ issue: transformIssueRef(key),
1142
+ epic: transformIssueRef(opts.epic)
1143
+ });
805
1144
  });
806
1145
  }
807
1146
 
@@ -810,7 +1149,7 @@ function linkTypes(parent) {
810
1149
  parent.command("link-types").description("List all issue link types").addHelpText("after", "\nExamples:\n jiradc issue link-types").action(async () => {
811
1150
  const client = getClient();
812
1151
  const result = await client.links.getTypes();
813
- output(result);
1152
+ output(result.map(transformIssueLinkType));
814
1153
  });
815
1154
  }
816
1155
 
@@ -827,7 +1166,12 @@ function link(parent) {
827
1166
  outwardIssueKey: opts.to,
828
1167
  comment: opts.comment
829
1168
  });
830
- output({ created: true, type: opts.type, inward: opts.from, outward: opts.to });
1169
+ output({
1170
+ created: true,
1171
+ type: opts.type,
1172
+ inward: transformIssueRef(opts.from),
1173
+ outward: transformIssueRef(opts.to)
1174
+ });
831
1175
  });
832
1176
  }
833
1177
 
@@ -845,7 +1189,7 @@ function search2(parent) {
845
1189
  maxResults: opts.max,
846
1190
  fields
847
1191
  });
848
- output(result);
1192
+ output(transformPaged(result, transformIssue));
849
1193
  });
850
1194
  }
851
1195
 
@@ -857,7 +1201,7 @@ function transition(parent) {
857
1201
  ).action(async (key, opts) => {
858
1202
  const client = getClient();
859
1203
  await client.issues.transition({ issueKeyOrId: key, transitionId: opts.to, comment: opts.comment });
860
- output({ transitioned: true, issueKey: key });
1204
+ output({ transitioned: true, issue: transformIssueRef(key) });
861
1205
  });
862
1206
  }
863
1207
 
@@ -880,7 +1224,7 @@ function unlink2(parent) {
880
1224
  }
881
1225
 
882
1226
  // src/commands/issue/update.ts
883
- function update(parent) {
1227
+ function update2(parent) {
884
1228
  parent.command("update <key>").description("Update issue fields").option("--fields <json>", "JSON string of fields to update").option("--notify-users", "Notify users about the update (default: true)").option("--attachments <paths>", "Comma-separated local file paths to attach").addHelpText(
885
1229
  "after",
886
1230
  `
@@ -891,8 +1235,7 @@ Examples:
891
1235
  jiradc issue update PROJ-123 --fields '{"summary": "With attachment"}' --attachments ./report.pdf`
892
1236
  ).action(async (key, opts) => {
893
1237
  if (!opts.fields && !opts.attachments) {
894
- console.error(JSON.stringify({ error: "Provide --fields and/or --attachments" }));
895
- process.exit(1);
1238
+ throw new Error("Provide --fields and/or --attachments");
896
1239
  }
897
1240
  const client = getClient();
898
1241
  if (opts.fields) {
@@ -909,7 +1252,11 @@ Examples:
909
1252
  }
910
1253
  }
911
1254
  }
912
- output({ updated: true, issueKey: key, ...uploaded.length > 0 && { attachments: uploaded } });
1255
+ output({
1256
+ updated: true,
1257
+ issue: transformIssueRef(key),
1258
+ ...uploaded.length > 0 && { attachments: uploaded }
1259
+ });
913
1260
  });
914
1261
  }
915
1262
 
@@ -926,7 +1273,7 @@ function worklog(parent) {
926
1273
  comment: opts.comment,
927
1274
  started: opts.started
928
1275
  });
929
- output(result);
1276
+ output(transformWorklog(result));
930
1277
  });
931
1278
  }
932
1279
 
@@ -944,10 +1291,10 @@ Examples:
944
1291
  $ jiradc issue attachment list PROJ-123
945
1292
  `
946
1293
  );
947
- get(issue);
1294
+ get2(issue);
948
1295
  search2(issue);
949
- create(issue);
950
- update(issue);
1296
+ create2(issue);
1297
+ update2(issue);
951
1298
  deleteIssue(issue);
952
1299
  transition(issue);
953
1300
  transitions(issue);
@@ -968,24 +1315,15 @@ Examples:
968
1315
  devStatus(issue);
969
1316
  }
970
1317
 
971
- // src/commands/project/components.ts
972
- function components(parent) {
973
- parent.command("components <key>").description("Get all components for a project").addHelpText("after", "\nExamples:\n jiradc project components PROJ").action(async (key) => {
974
- const client = getClient();
975
- const result = await client.projects.getComponents({ projectKeyOrId: key });
976
- output(result);
977
- });
978
- }
979
-
980
1318
  // src/commands/project/list.ts
981
- function list3(parent) {
1319
+ function list4(parent) {
982
1320
  parent.command("list").description("List all projects").option("--expand <expand>", 'Expand options (e.g., "description,lead")').option("--include-archived", "Include archived projects (default: false)").addHelpText(
983
1321
  "after",
984
1322
  "\nExamples:\n jiradc project list\n jiradc project list --expand description,lead\n jiradc project list --include-archived"
985
1323
  ).action(async (opts) => {
986
1324
  const client = getClient();
987
1325
  const result = await client.projects.getAll({ expand: opts.expand, includeArchived: opts.includeArchived });
988
- output(result);
1326
+ output(result.map(transformProject));
989
1327
  });
990
1328
  }
991
1329
 
@@ -997,7 +1335,7 @@ function versions(parent) {
997
1335
  ).action(async (key, opts) => {
998
1336
  const client = getClient();
999
1337
  const result = await client.projects.getVersions({ projectKeyOrId: key, expand: opts.expand });
1000
- output(result);
1338
+ output(result.map(transformVersion));
1001
1339
  });
1002
1340
  }
1003
1341
 
@@ -1009,16 +1347,14 @@ function registerProjectCommands(program2) {
1009
1347
  Examples:
1010
1348
  $ jiradc project list
1011
1349
  $ jiradc project versions PROJ
1012
- $ jiradc project components PROJ
1013
1350
  `
1014
1351
  );
1015
- list3(project);
1352
+ list4(project);
1016
1353
  versions(project);
1017
- components(project);
1018
1354
  }
1019
1355
 
1020
1356
  // src/commands/sprint/create.ts
1021
- function create2(parent) {
1357
+ function create3(parent) {
1022
1358
  parent.command("create").description("Create a new sprint").requiredOption("--board <id>", "Board ID to create sprint in", positiveInt).requiredOption("--name <name>", "Sprint name").option("--start-date <date>", "Start date in ISO 8601 format").option("--end-date <date>", "End date in ISO 8601 format").option("--goal <goal>", "Sprint goal").addHelpText(
1023
1359
  "after",
1024
1360
  '\nExamples:\n jiradc sprint create --board 42 --name "Sprint 10"\n jiradc sprint create --board 42 --name "Sprint 10" --start-date 2026-03-20 --end-date 2026-04-03 --goal "Complete auth module"'
@@ -1031,7 +1367,7 @@ function create2(parent) {
1031
1367
  endDate: opts.endDate,
1032
1368
  goal: opts.goal
1033
1369
  });
1034
- output(result);
1370
+ output(transformSprint(result));
1035
1371
  });
1036
1372
  }
1037
1373
 
@@ -1050,15 +1386,15 @@ function issues2(parent) {
1050
1386
  fields: opts.fields?.split(",").map((f) => f.trim()),
1051
1387
  jql: opts.jql
1052
1388
  });
1053
- output(result);
1389
+ output(transformPaged(result, transformIssue));
1054
1390
  });
1055
1391
  }
1056
1392
 
1057
1393
  // src/commands/sprint/list.ts
1058
- import { Option as Option2 } from "commander";
1394
+ import { Option as Option4 } from "commander";
1059
1395
  var SPRINT_STATES = ["future", "active", "closed"];
1060
- function list4(parent) {
1061
- parent.command("list").description("List sprints for a board").requiredOption("--board <id>", "Board ID", positiveInt).addOption(new Option2("--state <state>", "Filter by sprint state").choices(SPRINT_STATES)).addHelpText(
1396
+ function list5(parent) {
1397
+ parent.command("list").description("List sprints for a board").requiredOption("--board <id>", "Board ID", positiveInt).addOption(new Option4("--state <state>", "Filter by sprint state").choices(SPRINT_STATES)).addHelpText(
1062
1398
  "after",
1063
1399
  "\nExamples:\n jiradc sprint list --board 42\n jiradc sprint list --board 42 --state active"
1064
1400
  ).action(async (opts) => {
@@ -1067,15 +1403,15 @@ function list4(parent) {
1067
1403
  boardId: opts.board,
1068
1404
  state: opts.state
1069
1405
  });
1070
- output(result);
1406
+ output(result.map(transformSprint));
1071
1407
  });
1072
1408
  }
1073
1409
 
1074
1410
  // src/commands/sprint/update.ts
1075
- import { Argument as Argument3, Option as Option3 } from "commander";
1411
+ import { Argument as Argument3, Option as Option5 } from "commander";
1076
1412
  var SPRINT_STATES2 = ["future", "active", "closed"];
1077
- function update2(parent) {
1078
- parent.command("update").description("Update an existing sprint").addArgument(new Argument3("<id>", "Sprint ID").argParser(positiveInt)).option("--name <name>", "New sprint name").addOption(new Option3("--state <state>", "New sprint state").choices(SPRINT_STATES2)).option("--start-date <date>", "New start date in ISO 8601 format").option("--end-date <date>", "New end date in ISO 8601 format").option("--goal <goal>", "New sprint goal").addHelpText(
1413
+ function update3(parent) {
1414
+ parent.command("update").description("Update an existing sprint").addArgument(new Argument3("<id>", "Sprint ID").argParser(positiveInt)).option("--name <name>", "New sprint name").addOption(new Option5("--state <state>", "New sprint state").choices(SPRINT_STATES2)).option("--start-date <date>", "New start date in ISO 8601 format").option("--end-date <date>", "New end date in ISO 8601 format").option("--goal <goal>", "New sprint goal").addHelpText(
1079
1415
  "after",
1080
1416
  '\nExamples:\n jiradc sprint update 100 --name "Sprint 10 - Extended"\n jiradc sprint update 100 --state active\n jiradc sprint update 100 --end-date 2026-04-10 --goal "Updated goal"'
1081
1417
  ).action(
@@ -1089,7 +1425,7 @@ function update2(parent) {
1089
1425
  endDate: opts.endDate,
1090
1426
  goal: opts.goal
1091
1427
  });
1092
- output(result);
1428
+ output(transformSprint(result));
1093
1429
  }
1094
1430
  );
1095
1431
  }
@@ -1106,10 +1442,10 @@ Examples:
1106
1442
  $ jiradc sprint create --board 42 --name "Sprint 10" --start-date 2026-04-01 --end-date 2026-04-14
1107
1443
  `
1108
1444
  );
1109
- list4(sprint);
1445
+ list5(sprint);
1110
1446
  issues2(sprint);
1111
- create2(sprint);
1112
- update2(sprint);
1447
+ create3(sprint);
1448
+ update3(sprint);
1113
1449
  }
1114
1450
 
1115
1451
  // src/commands/user/me.ts
@@ -1147,7 +1483,7 @@ function readPackageVersion() {
1147
1483
  }
1148
1484
  var DIM = "\x1B[2m";
1149
1485
  var RESET = "\x1B[0m";
1150
- var program = new Command6();
1486
+ var program = new Command8();
1151
1487
  program.name("jiradc").description("Jira Data Center CLI").version(readPackageVersion()).configureHelp({
1152
1488
  styleTitle: (str) => styleText("bold", str),
1153
1489
  styleUsage: (str) => styleText("dim", str),
@@ -1179,6 +1515,7 @@ program.hook("preAction", (thisCommand) => {
1179
1515
  });
1180
1516
  registerIssueCommands(program);
1181
1517
  registerProjectCommands(program);
1518
+ registerComponentCommands(program);
1182
1519
  registerBoardCommands(program);
1183
1520
  registerSprintCommands(program);
1184
1521
  registerFieldCommands(program);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jiradc-cli",
3
- "version": "1.0.10",
3
+ "version": "1.0.12-g3332dfd.1",
4
4
  "publish": true,
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -12,7 +12,7 @@
12
12
  ],
13
13
  "dependencies": {
14
14
  "commander": "^13.1.0",
15
- "jira-data-center-client": "1.0.29"
15
+ "jira-data-center-client": "1.0.31-g3332dfd.1"
16
16
  },
17
17
  "devDependencies": {
18
18
  "@types/node": "24.10.4",