jiradc-cli 1.0.17 → 1.0.18-g33b0a4d.3
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 +43 -7
- package/dist/index.js +245 -62
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -15,6 +15,12 @@ export JIRA_URL="https://jira.example.com" # Base URL of your Jira instance
|
|
|
15
15
|
export JIRA_TOKEN="your-personal-access-token" # HTTP Access Token from Jira
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
+
## Breaking changes in this release
|
|
19
|
+
|
|
20
|
+
- `issue transitions` now returns a bare JSON array of transitions (previously wrapped in `{ transitions: [...] }`). Each transition is reduced to `{ id, name, to, fields? }` — verbose fields like `to.iconUrl`, `to.statusCategory`, `isGlobal`, `isInitial`, `isConditional`, `hasScreen` are stripped.
|
|
21
|
+
- `component list` now requires `--project <key>` instead of a positional `<projectKey>` argument.
|
|
22
|
+
- `issue link-epic` is now variadic on the issue argument: `link-epic <issueKey...> --epic <epicKey>`. Single-issue calls keep working.
|
|
23
|
+
|
|
18
24
|
## Commands
|
|
19
25
|
|
|
20
26
|
All commands output JSON. Add `--pretty` to pretty-print.
|
|
@@ -26,16 +32,17 @@ All commands output JSON. Add `--pretty` to pretty-print.
|
|
|
26
32
|
| `jiradc issue get <key>` | Get issue details (`--fields` to select, `--expand` for changelog/transitions) |
|
|
27
33
|
| `jiradc issue search <jql>` | Search issues with JQL |
|
|
28
34
|
| `jiradc issue create` | Create an issue (`--project`, `--type`, `--summary`, `--description`, `--custom-fields`) |
|
|
29
|
-
| `jiradc issue update <key>` | Update an issue |
|
|
35
|
+
| `jiradc issue update <key>` | Update an issue (`--summary`, `--description`, `--priority`, `--assignee`, `--labels`, `--components`, `--fix-versions`, or `--fields` JSON) |
|
|
30
36
|
| `jiradc issue delete <key>` | Delete an issue |
|
|
31
|
-
| `jiradc issue
|
|
37
|
+
| `jiradc issue assign <key> <user>` | Assign issue (user can be a username, `me`, or `none` to unassign) |
|
|
38
|
+
| `jiradc issue transition <key>` | Transition issue to a new status (`--to` accepts ID or status name, `--comment` to add a note) |
|
|
32
39
|
| `jiradc issue transitions <key>` | List available transitions |
|
|
33
40
|
| `jiradc issue comment <key>` | Add a comment |
|
|
34
41
|
| `jiradc issue comment-edit <key> <commentId>` | Edit a comment |
|
|
35
42
|
| `jiradc issue link <key> <targetKey>` | Link two issues (`--type` link type name) |
|
|
36
43
|
| `jiradc issue unlink <linkId>` | Remove a link |
|
|
37
44
|
| `jiradc issue link-types` | List available link types |
|
|
38
|
-
| `jiradc issue link-epic <
|
|
45
|
+
| `jiradc issue link-epic <keys...>` | Link one or more issues to an epic (`--epic <epicKey>`) |
|
|
39
46
|
| `jiradc issue worklog <key>` | Add a work log entry |
|
|
40
47
|
| `jiradc issue get-worklog <key>` | Get work log entries |
|
|
41
48
|
| `jiradc issue changelog <key>` | Get issue changelog |
|
|
@@ -60,7 +67,16 @@ All commands output JSON. Add `--pretty` to pretty-print.
|
|
|
60
67
|
|---------|-------------|
|
|
61
68
|
| `jiradc project list` | List projects |
|
|
62
69
|
| `jiradc project versions <key>` | List versions for a project |
|
|
63
|
-
|
|
70
|
+
|
|
71
|
+
### component
|
|
72
|
+
|
|
73
|
+
| Command | Description |
|
|
74
|
+
|---------|-------------|
|
|
75
|
+
| `jiradc component list` | List components for a project (`--project <key>`) |
|
|
76
|
+
| `jiradc component get <id>` | Get a component by ID |
|
|
77
|
+
| `jiradc component create` | Create a component (`--project`, `--name`, `--description`, `--lead`) |
|
|
78
|
+
| `jiradc component update <id>` | Update a component |
|
|
79
|
+
| `jiradc component issue-count <id>` | Get the number of issues using a component |
|
|
64
80
|
|
|
65
81
|
### board
|
|
66
82
|
|
|
@@ -93,7 +109,7 @@ All commands output JSON. Add `--pretty` to pretty-print.
|
|
|
93
109
|
|
|
94
110
|
## Pagination
|
|
95
111
|
|
|
96
|
-
Search and list commands accept `--
|
|
112
|
+
Search and list commands accept `--limit` to control page size (Jira DC caps at 50 for agile endpoints). Responses include `nextPage` — pass it back as `--start` to fetch the next page. When `nextPage` is `null`, there are no more results.
|
|
97
113
|
|
|
98
114
|
## Examples
|
|
99
115
|
|
|
@@ -113,9 +129,26 @@ jiradc issue create --project AI --type Task --summary "Implement feature X" --d
|
|
|
113
129
|
# Create with custom fields
|
|
114
130
|
jiradc issue create --project AI --type Story --summary "User login" --custom-fields '{"customfield_10100": "value"}'
|
|
115
131
|
|
|
116
|
-
# Transition
|
|
132
|
+
# Transition by ID or by status name
|
|
117
133
|
jiradc issue transitions AI-123
|
|
118
|
-
jiradc issue transition AI-123 31
|
|
134
|
+
jiradc issue transition AI-123 --to 31
|
|
135
|
+
jiradc issue transition AI-123 --to "In Review" --comment "Ready for review"
|
|
136
|
+
|
|
137
|
+
# Assign
|
|
138
|
+
jiradc issue assign AI-123 me
|
|
139
|
+
jiradc issue assign AI-123 jsmith
|
|
140
|
+
jiradc issue assign AI-123 none
|
|
141
|
+
|
|
142
|
+
# Update with shortcuts (instead of --fields JSON)
|
|
143
|
+
jiradc issue update AI-123 --summary "New title" --priority High
|
|
144
|
+
jiradc issue update AI-123 --assignee me
|
|
145
|
+
jiradc issue update AI-123 --labels backend,urgent # set mode
|
|
146
|
+
jiradc issue update AI-123 --labels +urgent,-backend # mutate mode
|
|
147
|
+
jiradc issue update AI-123 --components +Frontend
|
|
148
|
+
jiradc issue update AI-123 --fix-versions 1.0,2.0
|
|
149
|
+
|
|
150
|
+
# Link multiple issues to an epic in one call
|
|
151
|
+
jiradc issue link-epic AI-456 AI-457 AI-458 --epic AI-100
|
|
119
152
|
|
|
120
153
|
# Add a comment
|
|
121
154
|
jiradc issue comment AI-123 --body "Fixed in commit abc123"
|
|
@@ -140,4 +173,7 @@ jiradc issue clone AI-123
|
|
|
140
173
|
|
|
141
174
|
# Get dev status (linked branches, PRs)
|
|
142
175
|
jiradc issue dev-status AI-123
|
|
176
|
+
|
|
177
|
+
# List components for a project
|
|
178
|
+
jiradc component list --project AI
|
|
143
179
|
```
|
package/dist/index.js
CHANGED
|
@@ -432,17 +432,33 @@ function transformIssueFields(fields) {
|
|
|
432
432
|
};
|
|
433
433
|
}
|
|
434
434
|
|
|
435
|
+
// src/utils/transformers/transition.ts
|
|
436
|
+
function transformTransition(t) {
|
|
437
|
+
const out = {
|
|
438
|
+
id: t.id,
|
|
439
|
+
name: t.name,
|
|
440
|
+
to: t.to.name
|
|
441
|
+
};
|
|
442
|
+
if (t.fields && Object.keys(t.fields).length > 0) {
|
|
443
|
+
out.fields = t.fields;
|
|
444
|
+
}
|
|
445
|
+
return out;
|
|
446
|
+
}
|
|
447
|
+
function transformTransitions(response) {
|
|
448
|
+
return response.transitions.map(transformTransition);
|
|
449
|
+
}
|
|
450
|
+
|
|
435
451
|
// src/commands/board/issues.ts
|
|
436
452
|
function issues(parent) {
|
|
437
|
-
parent.command("issues").description("Get issues for a board").addArgument(new Argument("<id>", "Board ID").argParser(positiveInt)).option("--
|
|
453
|
+
parent.command("issues").description("Get issues for a board").addArgument(new Argument("<id>", "Board ID").argParser(positiveInt)).option("--limit <number>", "Max results (1-50, Jira DC caps at 50)", intInRange(1, 50), 25).option("--start <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(
|
|
438
454
|
"after",
|
|
439
|
-
'\nExamples:\n jiradc board issues 42\n jiradc board issues 42 --
|
|
455
|
+
'\nExamples:\n jiradc board issues 42\n jiradc board issues 42 --limit 10\n jiradc board issues 42 --jql "status = Open" --fields summary,status\n jiradc board issues 42 --start 50 --limit 25'
|
|
440
456
|
).action(async (id, opts) => {
|
|
441
457
|
const client = getClient();
|
|
442
458
|
const result = await client.agile.getBoardIssues({
|
|
443
459
|
boardId: id,
|
|
444
|
-
startAt: opts.
|
|
445
|
-
maxResults: opts.
|
|
460
|
+
startAt: opts.start,
|
|
461
|
+
maxResults: opts.limit,
|
|
446
462
|
fields: opts.fields?.split(",").map((f) => f.trim()),
|
|
447
463
|
jql: opts.jql
|
|
448
464
|
});
|
|
@@ -454,13 +470,13 @@ function issues(parent) {
|
|
|
454
470
|
import { Option } from "commander";
|
|
455
471
|
var BOARD_TYPES = ["scrum", "kanban", "simple"];
|
|
456
472
|
function list(parent) {
|
|
457
|
-
parent.command("list").description("List agile boards").option("--
|
|
473
|
+
parent.command("list").description("List agile boards").option("--limit <number>", "Max results (1-1000)", intInRange(1, 1e3), 25).option("--project <key>", "Filter boards by project key or ID").addOption(new Option("--type <type>", "Board type filter").choices(BOARD_TYPES)).option("--name <name>", "Filter boards by name").addHelpText(
|
|
458
474
|
"after",
|
|
459
|
-
'\nExamples:\n jiradc board list\n jiradc board list --
|
|
475
|
+
'\nExamples:\n jiradc board list\n jiradc board list --limit 10\n jiradc board list --project PROJ\n jiradc board list --type scrum --name "Team Board"'
|
|
460
476
|
).action(async (opts) => {
|
|
461
477
|
const client = getClient();
|
|
462
478
|
const result = await client.agile.getBoards({
|
|
463
|
-
maxResults: opts.
|
|
479
|
+
maxResults: opts.limit,
|
|
464
480
|
projectKeyOrId: opts.project,
|
|
465
481
|
type: opts.type,
|
|
466
482
|
name: opts.name
|
|
@@ -477,7 +493,7 @@ function registerBoardCommands(program2) {
|
|
|
477
493
|
Examples:
|
|
478
494
|
$ jiradc board list
|
|
479
495
|
$ jiradc board list --type scrum --project PROJ
|
|
480
|
-
$ jiradc board issues 42 --
|
|
496
|
+
$ jiradc board issues 42 --limit 20
|
|
481
497
|
`
|
|
482
498
|
);
|
|
483
499
|
list(board);
|
|
@@ -550,9 +566,9 @@ function issueCount(parent) {
|
|
|
550
566
|
|
|
551
567
|
// src/commands/component/list.ts
|
|
552
568
|
function list2(parent) {
|
|
553
|
-
parent.command("list
|
|
569
|
+
parent.command("list").description("List all components for a project").requiredOption("--project <key>", "Project key (e.g., AI)").addHelpText("after", "\nExamples:\n jiradc component list --project AI").action(async (opts) => {
|
|
554
570
|
const client = getClient();
|
|
555
|
-
const result = await client.components.list({ projectKeyOrId:
|
|
571
|
+
const result = await client.components.list({ projectKeyOrId: opts.project });
|
|
556
572
|
output(result.map(transformComponent));
|
|
557
573
|
});
|
|
558
574
|
}
|
|
@@ -597,7 +613,7 @@ function registerComponentCommands(program2) {
|
|
|
597
613
|
"after",
|
|
598
614
|
`
|
|
599
615
|
Examples:
|
|
600
|
-
$ jiradc component list AI
|
|
616
|
+
$ jiradc component list --project AI
|
|
601
617
|
$ jiradc component get 11289
|
|
602
618
|
$ jiradc component create --project AI --name Backend
|
|
603
619
|
$ jiradc component update 11289 --name Backend
|
|
@@ -615,15 +631,15 @@ Examples:
|
|
|
615
631
|
|
|
616
632
|
// src/commands/field/options.ts
|
|
617
633
|
function options(parent) {
|
|
618
|
-
parent.command("options <id>").description("Get available options for a custom field").option("--query <text>", "Filter options by text").option("--
|
|
634
|
+
parent.command("options <id>").description("Get available options for a custom field").option("--query <text>", "Filter options by text").option("--limit <number>", "Max results to return (1-1000)", intInRange(1, 1e3), 25).option("--page <number>", "Page number (1-indexed)", positiveInt).addHelpText(
|
|
619
635
|
"after",
|
|
620
|
-
'\nExamples:\n jiradc field options 10001\n jiradc field options 10001 --query "High"\n jiradc field options 10001 --
|
|
636
|
+
'\nExamples:\n jiradc field options 10001\n jiradc field options 10001 --query "High"\n jiradc field options 10001 --limit 20 --page 2'
|
|
621
637
|
).action(async (id, opts) => {
|
|
622
638
|
const client = getClient();
|
|
623
639
|
const result = await client.fields.getFieldOptions({
|
|
624
640
|
fieldId: id,
|
|
625
641
|
query: opts.query,
|
|
626
|
-
maxResults: opts.
|
|
642
|
+
maxResults: opts.limit,
|
|
627
643
|
page: opts.page
|
|
628
644
|
});
|
|
629
645
|
output(transformPaged({ ...result, startAt: result.startAt ?? 0 }, transformCustomFieldOption));
|
|
@@ -657,6 +673,42 @@ Examples:
|
|
|
657
673
|
options(field);
|
|
658
674
|
}
|
|
659
675
|
|
|
676
|
+
// src/utils/resolve-user.ts
|
|
677
|
+
var cachedMe;
|
|
678
|
+
async function resolveUserToken(token) {
|
|
679
|
+
if (token === "none") return null;
|
|
680
|
+
if (token === "me") {
|
|
681
|
+
if (cachedMe !== void 0) return cachedMe;
|
|
682
|
+
const me2 = await getClient().users.getMyself();
|
|
683
|
+
if (!me2.name) {
|
|
684
|
+
throw new Error('Could not resolve "me": authenticated user has no name field');
|
|
685
|
+
}
|
|
686
|
+
cachedMe = me2.name;
|
|
687
|
+
return cachedMe;
|
|
688
|
+
}
|
|
689
|
+
return token;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
// src/commands/issue/assign.ts
|
|
693
|
+
function assign(parent) {
|
|
694
|
+
parent.command("assign <key> <user>").description('Assign an issue. <user> is a username, "me", or "none" to unassign.').addHelpText(
|
|
695
|
+
"after",
|
|
696
|
+
`
|
|
697
|
+
Examples:
|
|
698
|
+
jiradc issue assign PROJ-123 jsmith
|
|
699
|
+
jiradc issue assign PROJ-123 me
|
|
700
|
+
jiradc issue assign PROJ-123 none`
|
|
701
|
+
).action(async (key, user) => {
|
|
702
|
+
const resolved = await resolveUserToken(user);
|
|
703
|
+
const client = getClient();
|
|
704
|
+
await client.issues.update({
|
|
705
|
+
issueKeyOrId: key,
|
|
706
|
+
fields: { assignee: resolved === null ? null : { name: resolved } }
|
|
707
|
+
});
|
|
708
|
+
output({ assigned: true, issue: transformIssueRef(key), assignee: resolved });
|
|
709
|
+
});
|
|
710
|
+
}
|
|
711
|
+
|
|
660
712
|
// src/commands/issue/attachment/delete.ts
|
|
661
713
|
function deleteAttachment(parent) {
|
|
662
714
|
parent.command("delete").description("Delete an attachment by ID").requiredOption("--id <attachmentId>", "Attachment ID to delete").addHelpText("after", "\nExamples:\n jiradc issue attachment delete --id 12345").action(async (opts) => {
|
|
@@ -842,16 +894,16 @@ function attachments(parent) {
|
|
|
842
894
|
|
|
843
895
|
// src/commands/issue/batch-changelog.ts
|
|
844
896
|
function batchChangelog(parent) {
|
|
845
|
-
parent.command("batch-changelog <keys>").description("Get changelogs for multiple issues at once").option("--
|
|
897
|
+
parent.command("batch-changelog <keys>").description("Get changelogs for multiple issues at once").option("--limit <number>", "Max changelog entries per issue (1-50, Jira DC caps at 50)", intInRange(1, 50), 25).addHelpText(
|
|
846
898
|
"after",
|
|
847
|
-
"\nExamples:\n jiradc issue batch-changelog PROJ-1,PROJ-2,PROJ-3\n jiradc issue batch-changelog PROJ-123,PROJ-124 --
|
|
899
|
+
"\nExamples:\n jiradc issue batch-changelog PROJ-1,PROJ-2,PROJ-3\n jiradc issue batch-changelog PROJ-123,PROJ-124 --limit 10"
|
|
848
900
|
).action(async (keys, opts) => {
|
|
849
901
|
const client = getClient();
|
|
850
902
|
const keyList = keys.split(",").map((k) => k.trim());
|
|
851
903
|
const entries = await Promise.all(
|
|
852
904
|
keyList.map(async (key) => {
|
|
853
905
|
try {
|
|
854
|
-
const data = await client.issues.getChangelog({ issueKeyOrId: key, maxResults: opts.
|
|
906
|
+
const data = await client.issues.getChangelog({ issueKeyOrId: key, maxResults: opts.limit });
|
|
855
907
|
return [key, data];
|
|
856
908
|
} catch (error) {
|
|
857
909
|
return [key, { error: error instanceof Error ? error.message : "Unknown error" }];
|
|
@@ -887,9 +939,12 @@ Examples:
|
|
|
887
939
|
|
|
888
940
|
// src/commands/issue/changelog.ts
|
|
889
941
|
function changelog(parent) {
|
|
890
|
-
parent.command("changelog <key>").description("Get changelog for an issue").option("--
|
|
942
|
+
parent.command("changelog <key>").description("Get changelog for an issue").option("--limit <number>", "Max changelog entries (1-50, Jira DC caps at 50)", intInRange(1, 50), 25).addHelpText(
|
|
943
|
+
"after",
|
|
944
|
+
"\nExamples:\n jiradc issue changelog PROJ-123\n jiradc issue changelog PROJ-123 --limit 10"
|
|
945
|
+
).action(async (key, opts) => {
|
|
891
946
|
const client = getClient();
|
|
892
|
-
const result = await client.issues.getChangelog({ issueKeyOrId: key, maxResults: opts.
|
|
947
|
+
const result = await client.issues.getChangelog({ issueKeyOrId: key, maxResults: opts.limit });
|
|
893
948
|
output(result);
|
|
894
949
|
});
|
|
895
950
|
}
|
|
@@ -1133,15 +1188,15 @@ function devStatus(parent) {
|
|
|
1133
1188
|
|
|
1134
1189
|
// src/commands/issue/get-worklog.ts
|
|
1135
1190
|
function getWorklog(parent) {
|
|
1136
|
-
parent.command("get-worklog <key>").description("Get worklogs for an issue").option("--start
|
|
1191
|
+
parent.command("get-worklog <key>").description("Get worklogs for an issue").option("--start <number>", "Starting index for pagination", nonNegativeInt).option("--limit <number>", "Max results per page (1-1000)", intInRange(1, 1e3), 25).addHelpText(
|
|
1137
1192
|
"after",
|
|
1138
|
-
"\nExamples:\n jiradc issue get-worklog PROJ-123\n jiradc issue get-worklog PROJ-123 --
|
|
1193
|
+
"\nExamples:\n jiradc issue get-worklog PROJ-123\n jiradc issue get-worklog PROJ-123 --limit 10\n jiradc issue get-worklog PROJ-123 --start 10 --limit 5"
|
|
1139
1194
|
).action(async (key, opts) => {
|
|
1140
1195
|
const client = getClient();
|
|
1141
1196
|
const result = await client.issues.getWorklogs({
|
|
1142
1197
|
issueKeyOrId: key,
|
|
1143
|
-
startAt: opts.
|
|
1144
|
-
maxResults: opts.
|
|
1198
|
+
startAt: opts.start,
|
|
1199
|
+
maxResults: opts.limit
|
|
1145
1200
|
});
|
|
1146
1201
|
output(transformPaged(result, transformWorklog));
|
|
1147
1202
|
});
|
|
@@ -1182,17 +1237,37 @@ function get2(parent) {
|
|
|
1182
1237
|
|
|
1183
1238
|
// src/commands/issue/link-epic.ts
|
|
1184
1239
|
function linkEpic(parent) {
|
|
1185
|
-
parent.command("link-epic <
|
|
1240
|
+
parent.command("link-epic <keys...>").description("Link one or more issues to an epic").requiredOption("--epic <epicKey>", "Epic issue key").addHelpText(
|
|
1241
|
+
"after",
|
|
1242
|
+
`
|
|
1243
|
+
Examples:
|
|
1244
|
+
jiradc issue link-epic PROJ-456 --epic PROJ-123
|
|
1245
|
+
jiradc issue link-epic PROJ-456 PROJ-457 PROJ-458 --epic PROJ-123`
|
|
1246
|
+
).action(async (keys, opts) => {
|
|
1186
1247
|
const client = getClient();
|
|
1187
|
-
await
|
|
1188
|
-
|
|
1189
|
-
|
|
1248
|
+
const results = await Promise.allSettled(
|
|
1249
|
+
keys.map(
|
|
1250
|
+
(key) => client.issues.update({ issueKeyOrId: key, fields: { customfield_10100: opts.epic } }).then(() => key)
|
|
1251
|
+
)
|
|
1252
|
+
);
|
|
1253
|
+
const linked = [];
|
|
1254
|
+
const failed = [];
|
|
1255
|
+
results.forEach((r, i) => {
|
|
1256
|
+
if (r.status === "fulfilled") {
|
|
1257
|
+
linked.push({ key: r.value });
|
|
1258
|
+
} else {
|
|
1259
|
+
const message = r.reason instanceof Error ? r.reason.message : String(r.reason);
|
|
1260
|
+
failed.push({ key: keys[i], error: message });
|
|
1261
|
+
}
|
|
1190
1262
|
});
|
|
1191
1263
|
output({
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1264
|
+
epic: transformIssueRef(opts.epic),
|
|
1265
|
+
linked,
|
|
1266
|
+
...failed.length > 0 && { failed }
|
|
1195
1267
|
});
|
|
1268
|
+
if (failed.length > 0) {
|
|
1269
|
+
process.exitCode = 1;
|
|
1270
|
+
}
|
|
1196
1271
|
});
|
|
1197
1272
|
}
|
|
1198
1273
|
|
|
@@ -1229,16 +1304,16 @@ function link(parent) {
|
|
|
1229
1304
|
|
|
1230
1305
|
// src/commands/issue/search.ts
|
|
1231
1306
|
function search2(parent) {
|
|
1232
|
-
parent.command("search <jql>").description("Search issues using JQL").option("--
|
|
1307
|
+
parent.command("search <jql>").description("Search issues using JQL").option("--limit <number>", "Max results (1-50, Jira DC caps at 50)", intInRange(1, 50), 25).option("--start <number>", "Starting index for pagination", nonNegativeInt).option("--fields <fields>", "Comma-separated fields to return (defaults to essential fields)").option("--all-fields", "Return all fields instead of defaults").addHelpText(
|
|
1233
1308
|
"after",
|
|
1234
|
-
'\nExamples:\n jiradc issue search "project = PROJ AND status = Open"\n jiradc issue search "assignee = currentUser()" --
|
|
1309
|
+
'\nExamples:\n jiradc issue search "project = PROJ AND status = Open"\n jiradc issue search "assignee = currentUser()" --limit 10 --fields summary,status\n jiradc issue search "project = PROJ" --start 50 --limit 50'
|
|
1235
1310
|
).action(async (jql, opts) => {
|
|
1236
1311
|
const client = getClient();
|
|
1237
1312
|
const fields = opts.allFields ? void 0 : opts.fields?.split(",").map((f) => f.trim()) ?? DEFAULT_FIELDS;
|
|
1238
1313
|
const result = await client.issues.search({
|
|
1239
1314
|
jql,
|
|
1240
|
-
startAt: opts.
|
|
1241
|
-
maxResults: opts.
|
|
1315
|
+
startAt: opts.start,
|
|
1316
|
+
maxResults: opts.limit,
|
|
1242
1317
|
fields
|
|
1243
1318
|
});
|
|
1244
1319
|
output(transformPaged(result, transformIssue));
|
|
@@ -1247,12 +1322,37 @@ function search2(parent) {
|
|
|
1247
1322
|
|
|
1248
1323
|
// src/commands/issue/transition.ts
|
|
1249
1324
|
function transition(parent) {
|
|
1250
|
-
parent.command("transition <key>").description("Transition issue to a new status").requiredOption("--to <
|
|
1325
|
+
parent.command("transition <key>").description("Transition issue to a new status").requiredOption("--to <idOrName>", "Transition ID, or status name (case-insensitive)").option("--comment <text>", "Comment to add during transition").addHelpText(
|
|
1251
1326
|
"after",
|
|
1252
|
-
|
|
1327
|
+
`
|
|
1328
|
+
Examples:
|
|
1329
|
+
jiradc issue transition PROJ-123 --to 31
|
|
1330
|
+
jiradc issue transition PROJ-123 --to "In Review"
|
|
1331
|
+
jiradc issue transition PROJ-123 --to Done --comment "Verified in staging"`
|
|
1253
1332
|
).action(async (key, opts) => {
|
|
1254
1333
|
const client = getClient();
|
|
1255
|
-
|
|
1334
|
+
let transitionId;
|
|
1335
|
+
if (/^\d+$/.test(opts.to)) {
|
|
1336
|
+
transitionId = opts.to;
|
|
1337
|
+
} else {
|
|
1338
|
+
const raw = await client.issues.getTransitions({
|
|
1339
|
+
issueKeyOrId: key,
|
|
1340
|
+
expand: "transitions.fields"
|
|
1341
|
+
});
|
|
1342
|
+
const transitions2 = transformTransitions(raw);
|
|
1343
|
+
const target = opts.to.toLowerCase();
|
|
1344
|
+
const matches = transitions2.filter((t) => t.name.toLowerCase() === target);
|
|
1345
|
+
if (matches.length === 0) {
|
|
1346
|
+
const names = transitions2.map((t) => `"${t.name}"`).join(", ");
|
|
1347
|
+
throw new Error(`Transition "${opts.to}" not available from current status. Available: ${names || "(none)"}`);
|
|
1348
|
+
}
|
|
1349
|
+
if (matches.length > 1) {
|
|
1350
|
+
const ids = matches.map((t) => t.id).join(", ");
|
|
1351
|
+
throw new Error(`Multiple transitions named "${opts.to}". Use --to <id> with one of: ${ids}`);
|
|
1352
|
+
}
|
|
1353
|
+
transitionId = matches[0].id;
|
|
1354
|
+
}
|
|
1355
|
+
await client.issues.transition({ issueKeyOrId: key, transitionId, comment: opts.comment });
|
|
1256
1356
|
output({ transitioned: true, issue: transformIssueRef(key) });
|
|
1257
1357
|
});
|
|
1258
1358
|
}
|
|
@@ -1261,8 +1361,11 @@ function transition(parent) {
|
|
|
1261
1361
|
function transitions(parent) {
|
|
1262
1362
|
parent.command("transitions <key>").description("Get available transitions for an issue").addHelpText("after", "\nExamples:\n jiradc issue transitions PROJ-123").action(async (key) => {
|
|
1263
1363
|
const client = getClient();
|
|
1264
|
-
const result = await client.issues.getTransitions({
|
|
1265
|
-
|
|
1364
|
+
const result = await client.issues.getTransitions({
|
|
1365
|
+
issueKeyOrId: key,
|
|
1366
|
+
expand: "transitions.fields"
|
|
1367
|
+
});
|
|
1368
|
+
output(transformTransitions(result));
|
|
1266
1369
|
});
|
|
1267
1370
|
}
|
|
1268
1371
|
|
|
@@ -1275,24 +1378,103 @@ function unlink2(parent) {
|
|
|
1275
1378
|
});
|
|
1276
1379
|
}
|
|
1277
1380
|
|
|
1381
|
+
// src/utils/multi-value.ts
|
|
1382
|
+
import { InvalidArgumentError as InvalidArgumentError2 } from "commander";
|
|
1383
|
+
function parseMultiValue(flagName, raw) {
|
|
1384
|
+
const items = raw.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
1385
|
+
if (items.length === 0) {
|
|
1386
|
+
throw new InvalidArgumentError2(`${flagName} cannot be empty`);
|
|
1387
|
+
}
|
|
1388
|
+
const lonePrefix = items.find((s) => s === "+" || s === "-");
|
|
1389
|
+
if (lonePrefix !== void 0) {
|
|
1390
|
+
throw new InvalidArgumentError2(`${flagName} has an empty value after '${lonePrefix}' prefix`);
|
|
1391
|
+
}
|
|
1392
|
+
const prefixed = items.filter((s) => s.startsWith("+") || s.startsWith("-"));
|
|
1393
|
+
const bare = items.filter((s) => !s.startsWith("+") && !s.startsWith("-"));
|
|
1394
|
+
if (prefixed.length > 0 && bare.length > 0) {
|
|
1395
|
+
throw new InvalidArgumentError2(
|
|
1396
|
+
`${flagName} mixes set and mutate syntax. Either all values have +/- prefix, or none do.`
|
|
1397
|
+
);
|
|
1398
|
+
}
|
|
1399
|
+
if (bare.length > 0) {
|
|
1400
|
+
return { mode: "set", values: bare };
|
|
1401
|
+
}
|
|
1402
|
+
const adds = prefixed.filter((s) => s.startsWith("+")).map((s) => s.slice(1));
|
|
1403
|
+
const removes = prefixed.filter((s) => s.startsWith("-")).map((s) => s.slice(1));
|
|
1404
|
+
return { mode: "mutate", adds, removes };
|
|
1405
|
+
}
|
|
1406
|
+
|
|
1278
1407
|
// src/commands/issue/update.ts
|
|
1408
|
+
function buildUpdateOps(parsed, wrap) {
|
|
1409
|
+
if (parsed.mode !== "mutate") return void 0;
|
|
1410
|
+
return [...parsed.adds.map((v) => ({ add: wrap(v) })), ...parsed.removes.map((v) => ({ remove: wrap(v) }))];
|
|
1411
|
+
}
|
|
1412
|
+
function buildSetValue(parsed, wrap) {
|
|
1413
|
+
if (parsed.mode !== "set") return void 0;
|
|
1414
|
+
return parsed.values.map(wrap);
|
|
1415
|
+
}
|
|
1279
1416
|
function update2(parent) {
|
|
1280
|
-
parent.command("update <key>").description("Update issue fields").option("--fields <json>", "JSON string of fields to update").option("--notify-users", "
|
|
1417
|
+
parent.command("update <key>").description("Update issue fields").option("--fields <json>", "JSON string of fields to update (advanced; merges with shortcuts, wins on conflict)").option("--no-notify-users", "Suppress notification emails (default: notify)").option("--attachments <paths>", "Comma-separated local file paths to attach").option("--summary <text>", "Set the issue summary").option("--description <text>", "Set the issue description (wiki markup)").option("--priority <name>", "Set the priority by name (e.g. High)").option("--assignee <user>", 'Set the assignee. Username, "me", or "none" to unassign.').option("--labels <list>", 'Set labels ("a,b,c") or mutate ("+add,-remove")').option("--components <list>", 'Set components ("a,b") or mutate ("+add,-remove")').option("--fix-versions <list>", 'Set fix versions ("1.0,2.0") or mutate ("+1.0,-0.9")').addHelpText(
|
|
1281
1418
|
"after",
|
|
1282
1419
|
`
|
|
1283
1420
|
Examples:
|
|
1284
|
-
jiradc issue update PROJ-123 --
|
|
1285
|
-
jiradc issue update PROJ-123 --
|
|
1286
|
-
jiradc issue update PROJ-123 --
|
|
1287
|
-
jiradc issue update PROJ-123 --
|
|
1421
|
+
jiradc issue update PROJ-123 --summary "New title"
|
|
1422
|
+
jiradc issue update PROJ-123 --priority High --assignee me
|
|
1423
|
+
jiradc issue update PROJ-123 --labels backend,urgent
|
|
1424
|
+
jiradc issue update PROJ-123 --labels +urgent,-backend
|
|
1425
|
+
jiradc issue update PROJ-123 --components +Frontend
|
|
1426
|
+
jiradc issue update PROJ-123 --fields '{"customfield_10100": "EPIC-1"}' --priority High
|
|
1427
|
+
jiradc issue update PROJ-123 --attachments /path/to/file.pdf,/path/to/image.png`
|
|
1288
1428
|
).action(async (key, opts) => {
|
|
1289
|
-
|
|
1290
|
-
|
|
1429
|
+
const hasShortcut = opts.summary !== void 0 || opts.description !== void 0 || opts.priority !== void 0 || opts.assignee !== void 0 || opts.labels !== void 0 || opts.components !== void 0 || opts.fixVersions !== void 0;
|
|
1430
|
+
if (!opts.fields && !opts.attachments && !hasShortcut) {
|
|
1431
|
+
throw new Error(
|
|
1432
|
+
"Provide at least one of --fields, --attachments, or a shortcut flag (--summary, --priority, --labels, ...)"
|
|
1433
|
+
);
|
|
1434
|
+
}
|
|
1435
|
+
const fields = {};
|
|
1436
|
+
const updateOps = {};
|
|
1437
|
+
if (opts.summary !== void 0) fields.summary = opts.summary;
|
|
1438
|
+
if (opts.description !== void 0) fields.description = opts.description;
|
|
1439
|
+
if (opts.priority !== void 0) fields.priority = { name: opts.priority };
|
|
1440
|
+
if (opts.assignee !== void 0) {
|
|
1441
|
+
const resolved = await resolveUserToken(opts.assignee);
|
|
1442
|
+
fields.assignee = resolved === null ? null : { name: resolved };
|
|
1443
|
+
}
|
|
1444
|
+
if (opts.labels !== void 0) {
|
|
1445
|
+
const parsed = parseMultiValue("--labels", opts.labels);
|
|
1446
|
+
const setVal = buildSetValue(parsed, (v) => v);
|
|
1447
|
+
const ops = buildUpdateOps(parsed, (v) => v);
|
|
1448
|
+
if (setVal) fields.labels = setVal;
|
|
1449
|
+
if (ops) updateOps.labels = ops;
|
|
1450
|
+
}
|
|
1451
|
+
if (opts.components !== void 0) {
|
|
1452
|
+
const parsed = parseMultiValue("--components", opts.components);
|
|
1453
|
+
const setVal = buildSetValue(parsed, (v) => ({ name: v }));
|
|
1454
|
+
const ops = buildUpdateOps(parsed, (v) => ({ name: v }));
|
|
1455
|
+
if (setVal) fields.components = setVal;
|
|
1456
|
+
if (ops) updateOps.components = ops;
|
|
1457
|
+
}
|
|
1458
|
+
if (opts.fixVersions !== void 0) {
|
|
1459
|
+
const parsed = parseMultiValue("--fix-versions", opts.fixVersions);
|
|
1460
|
+
const setVal = buildSetValue(parsed, (v) => ({ name: v }));
|
|
1461
|
+
const ops = buildUpdateOps(parsed, (v) => ({ name: v }));
|
|
1462
|
+
if (setVal) fields.fixVersions = setVal;
|
|
1463
|
+
if (ops) updateOps.fixVersions = ops;
|
|
1291
1464
|
}
|
|
1292
|
-
const client = getClient();
|
|
1293
1465
|
if (opts.fields) {
|
|
1294
|
-
const
|
|
1295
|
-
|
|
1466
|
+
const parsedFields = JSON.parse(opts.fields);
|
|
1467
|
+
Object.assign(fields, parsedFields);
|
|
1468
|
+
}
|
|
1469
|
+
const client = getClient();
|
|
1470
|
+
const didFieldUpdate = Object.keys(fields).length > 0 || Object.keys(updateOps).length > 0;
|
|
1471
|
+
if (didFieldUpdate) {
|
|
1472
|
+
await client.issues.update({
|
|
1473
|
+
issueKeyOrId: key,
|
|
1474
|
+
fields: Object.keys(fields).length > 0 ? fields : void 0,
|
|
1475
|
+
update: Object.keys(updateOps).length > 0 ? updateOps : void 0,
|
|
1476
|
+
notifyUsers: opts.notifyUsers
|
|
1477
|
+
});
|
|
1296
1478
|
}
|
|
1297
1479
|
const uploaded = [];
|
|
1298
1480
|
if (opts.attachments) {
|
|
@@ -1305,9 +1487,9 @@ Examples:
|
|
|
1305
1487
|
}
|
|
1306
1488
|
}
|
|
1307
1489
|
output({
|
|
1308
|
-
updated: true,
|
|
1309
|
-
|
|
1310
|
-
|
|
1490
|
+
...didFieldUpdate && { updated: true },
|
|
1491
|
+
...uploaded.length > 0 && { attached: true, attachments: uploaded },
|
|
1492
|
+
issue: transformIssueRef(key)
|
|
1311
1493
|
});
|
|
1312
1494
|
});
|
|
1313
1495
|
}
|
|
@@ -1336,7 +1518,7 @@ function registerIssueCommands(program2) {
|
|
|
1336
1518
|
`
|
|
1337
1519
|
Examples:
|
|
1338
1520
|
$ jiradc issue get PROJ-123
|
|
1339
|
-
$ jiradc issue search "project = PROJ AND status = Open" --
|
|
1521
|
+
$ jiradc issue search "project = PROJ AND status = Open" --limit 10
|
|
1340
1522
|
$ jiradc issue create --project PROJ --type Task --summary "Fix the bug" --priority High
|
|
1341
1523
|
$ jiradc issue update PROJ-123 --fields '{"summary": "New title"}'
|
|
1342
1524
|
$ jiradc issue transition PROJ-123 --to 11
|
|
@@ -1350,6 +1532,7 @@ Examples:
|
|
|
1350
1532
|
deleteIssue(issue);
|
|
1351
1533
|
transition(issue);
|
|
1352
1534
|
transitions(issue);
|
|
1535
|
+
assign(issue);
|
|
1353
1536
|
comment(issue);
|
|
1354
1537
|
commentEdit(issue);
|
|
1355
1538
|
worklog(issue);
|
|
@@ -1426,15 +1609,15 @@ function create3(parent) {
|
|
|
1426
1609
|
// src/commands/sprint/issues.ts
|
|
1427
1610
|
import { Argument as Argument2 } from "commander";
|
|
1428
1611
|
function issues2(parent) {
|
|
1429
|
-
parent.command("issues").description("Get issues in a sprint").addArgument(new Argument2("<id>", "Sprint ID").argParser(positiveInt)).option("--
|
|
1612
|
+
parent.command("issues").description("Get issues in a sprint").addArgument(new Argument2("<id>", "Sprint ID").argParser(positiveInt)).option("--limit <number>", "Max results (1-50, Jira DC caps at 50)", intInRange(1, 50), 25).option("--start <number>", "Starting index for pagination", nonNegativeInt).option("--fields <fields>", "Comma-separated field names to return").option("--jql <jql>", "Additional JQL filter within the sprint").addHelpText(
|
|
1430
1613
|
"after",
|
|
1431
|
-
'\nExamples:\n jiradc sprint issues 100\n jiradc sprint issues 100 --
|
|
1614
|
+
'\nExamples:\n jiradc sprint issues 100\n jiradc sprint issues 100 --limit 20\n jiradc sprint issues 100 --jql "status = Done" --fields summary,status\n jiradc sprint issues 100 --start 50 --limit 25'
|
|
1432
1615
|
).action(async (id, opts) => {
|
|
1433
1616
|
const client = getClient();
|
|
1434
1617
|
const result = await client.agile.getSprintIssues({
|
|
1435
1618
|
sprintId: id,
|
|
1436
|
-
startAt: opts.
|
|
1437
|
-
maxResults: opts.
|
|
1619
|
+
startAt: opts.start,
|
|
1620
|
+
maxResults: opts.limit,
|
|
1438
1621
|
fields: opts.fields?.split(",").map((f) => f.trim()),
|
|
1439
1622
|
jql: opts.jql
|
|
1440
1623
|
});
|
|
@@ -1490,7 +1673,7 @@ function registerSprintCommands(program2) {
|
|
|
1490
1673
|
Examples:
|
|
1491
1674
|
$ jiradc sprint list --board 42
|
|
1492
1675
|
$ jiradc sprint list --board 42 --state active
|
|
1493
|
-
$ jiradc sprint issues 123 --
|
|
1676
|
+
$ jiradc sprint issues 123 --limit 20
|
|
1494
1677
|
$ jiradc sprint create --board 42 --name "Sprint 10" --start-date 2026-04-01 --end-date 2026-04-14
|
|
1495
1678
|
`
|
|
1496
1679
|
);
|
|
@@ -1625,15 +1808,15 @@ function me(parent) {
|
|
|
1625
1808
|
|
|
1626
1809
|
// src/commands/user/search.ts
|
|
1627
1810
|
function search3(parent) {
|
|
1628
|
-
parent.command("search <query>").description("Search users by partial username, display name or email").option("--
|
|
1811
|
+
parent.command("search <query>").description("Search users by partial username, display name or email").option("--limit <n>", "Maximum results (1-50)", intInRange(1, 50), 25).option("--start <n>", "Starting index (pagination offset)", nonNegativeInt, 0).option("--include-inactive", "Include inactive users in results", false).addHelpText(
|
|
1629
1812
|
"after",
|
|
1630
|
-
'\nExamples:\n jiradc user search Drago\n jiradc user search "John Smith"\n jiradc user search dragomir@first.bet --
|
|
1813
|
+
'\nExamples:\n jiradc user search Drago\n jiradc user search "John Smith"\n jiradc user search dragomir@first.bet --limit 5'
|
|
1631
1814
|
).action(async (query, opts) => {
|
|
1632
1815
|
const client = getClient();
|
|
1633
1816
|
const result = await client.users.searchUsers({
|
|
1634
1817
|
username: query,
|
|
1635
1818
|
startAt: opts.start,
|
|
1636
|
-
maxResults: opts.
|
|
1819
|
+
maxResults: opts.limit,
|
|
1637
1820
|
includeActive: true,
|
|
1638
1821
|
includeInactive: opts.includeInactive
|
|
1639
1822
|
});
|
|
@@ -1688,7 +1871,7 @@ ${styleText("bold", "Environment:")}
|
|
|
1688
1871
|
|
|
1689
1872
|
${styleText("bold", "Examples:")}
|
|
1690
1873
|
${DIM}$${RESET} jiradc user me
|
|
1691
|
-
${DIM}$${RESET} jiradc issue search "project = PROJ AND status = Open" --
|
|
1874
|
+
${DIM}$${RESET} jiradc issue search "project = PROJ AND status = Open" --limit 10
|
|
1692
1875
|
${DIM}$${RESET} jiradc issue get PROJ-123 --fields summary,status
|
|
1693
1876
|
${DIM}$${RESET} jiradc issue create --project PROJ --type Task --summary "Fix the bug"
|
|
1694
1877
|
${DIM}$${RESET} jiradc issue transition PROJ-123 --to 11
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jiradc-cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.18-g33b0a4d.3",
|
|
4
4
|
"publish": true,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
"tsx": "^4.19.2",
|
|
23
23
|
"typescript": "^5.7.2",
|
|
24
24
|
"vitest": "^4.0.16",
|
|
25
|
-
"config-
|
|
26
|
-
"config-
|
|
25
|
+
"config-eslint": "0.0.0",
|
|
26
|
+
"config-typescript": "0.0.0"
|
|
27
27
|
},
|
|
28
28
|
"engines": {
|
|
29
29
|
"node": ">=22.0.0"
|