jiradc-cli 1.0.8 → 1.0.10
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/dist/index.js +115 -52
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -1,8 +1,43 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
+
import { readFileSync } from "fs";
|
|
5
|
+
import { dirname, join as join4 } from "path";
|
|
6
|
+
import { fileURLToPath } from "url";
|
|
4
7
|
import { styleText } from "util";
|
|
5
|
-
import { Command } from "commander";
|
|
8
|
+
import { Command as Command6 } from "commander";
|
|
9
|
+
|
|
10
|
+
// src/commands/board/issues.ts
|
|
11
|
+
import { Argument } from "commander";
|
|
12
|
+
|
|
13
|
+
// src/utils/cli.ts
|
|
14
|
+
import { InvalidArgumentError } from "commander";
|
|
15
|
+
function intInRange(min, max) {
|
|
16
|
+
return (raw) => {
|
|
17
|
+
const n = parseInt(raw, 10);
|
|
18
|
+
if (Number.isNaN(n) || !Number.isFinite(n)) {
|
|
19
|
+
throw new InvalidArgumentError(`Not a number: "${raw}"`);
|
|
20
|
+
}
|
|
21
|
+
if (n < min || n > max) {
|
|
22
|
+
throw new InvalidArgumentError(`Must be between ${min} and ${max} (got ${n})`);
|
|
23
|
+
}
|
|
24
|
+
return n;
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function nonNegativeInt(raw) {
|
|
28
|
+
const n = parseInt(raw, 10);
|
|
29
|
+
if (Number.isNaN(n) || n < 0) {
|
|
30
|
+
throw new InvalidArgumentError(`Must be a non-negative integer (got "${raw}")`);
|
|
31
|
+
}
|
|
32
|
+
return n;
|
|
33
|
+
}
|
|
34
|
+
function positiveInt(raw) {
|
|
35
|
+
const n = parseInt(raw, 10);
|
|
36
|
+
if (Number.isNaN(n) || n < 1) {
|
|
37
|
+
throw new InvalidArgumentError(`Must be a positive integer (got "${raw}")`);
|
|
38
|
+
}
|
|
39
|
+
return n;
|
|
40
|
+
}
|
|
6
41
|
|
|
7
42
|
// src/utils/client.ts
|
|
8
43
|
import { JiraClient } from "jira-data-center-client";
|
|
@@ -125,21 +160,29 @@ function handleError(err) {
|
|
|
125
160
|
`
|
|
126
161
|
);
|
|
127
162
|
} else {
|
|
128
|
-
|
|
129
|
-
|
|
163
|
+
const responseData = err?.response?.data;
|
|
164
|
+
const detail = responseData && typeof responseData === "object" ? responseData : void 0;
|
|
165
|
+
process.stderr.write(
|
|
166
|
+
`${JSON.stringify({
|
|
167
|
+
error: message,
|
|
168
|
+
...axiosStatus !== void 0 && { statusCode: axiosStatus },
|
|
169
|
+
...detail && { detail }
|
|
170
|
+
})}
|
|
171
|
+
`
|
|
172
|
+
);
|
|
130
173
|
}
|
|
131
174
|
process.exit(1);
|
|
132
175
|
}
|
|
133
176
|
|
|
134
177
|
// src/commands/board/issues.ts
|
|
135
178
|
function issues(parent) {
|
|
136
|
-
parent.command("issues
|
|
179
|
+
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(
|
|
137
180
|
"after",
|
|
138
181
|
'\nExamples:\n jiradc board issues 42\n jiradc board issues 42 --max 10\n jiradc board issues 42 --jql "status = Open" --fields summary,status\n jiradc board issues 42 --start-at 50 --max 25'
|
|
139
182
|
).action(async (id, opts) => {
|
|
140
183
|
const client = getClient();
|
|
141
184
|
const result = await client.agile.getBoardIssues({
|
|
142
|
-
boardId:
|
|
185
|
+
boardId: id,
|
|
143
186
|
startAt: opts.startAt,
|
|
144
187
|
maxResults: opts.max,
|
|
145
188
|
fields: opts.fields?.split(",").map((f) => f.trim()),
|
|
@@ -150,8 +193,10 @@ function issues(parent) {
|
|
|
150
193
|
}
|
|
151
194
|
|
|
152
195
|
// src/commands/board/list.ts
|
|
196
|
+
import { Option } from "commander";
|
|
197
|
+
var BOARD_TYPES = ["scrum", "kanban", "simple"];
|
|
153
198
|
function list(parent) {
|
|
154
|
-
parent.command("list").description("List agile boards").option("--max <number>", "Max results",
|
|
199
|
+
parent.command("list").description("List agile boards").option("--max <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(
|
|
155
200
|
"after",
|
|
156
201
|
'\nExamples:\n jiradc board list\n jiradc board list --max 10\n jiradc board list --project PROJ\n jiradc board list --type scrum --name "Team Board"'
|
|
157
202
|
).action(async (opts) => {
|
|
@@ -183,7 +228,7 @@ Examples:
|
|
|
183
228
|
|
|
184
229
|
// src/commands/field/options.ts
|
|
185
230
|
function options(parent) {
|
|
186
|
-
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",
|
|
231
|
+
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(
|
|
187
232
|
"after",
|
|
188
233
|
'\nExamples:\n jiradc field options 10001\n jiradc field options 10001 --query "High"\n jiradc field options 10001 --max 20 --page 2'
|
|
189
234
|
).action(async (id, opts) => {
|
|
@@ -200,7 +245,7 @@ function options(parent) {
|
|
|
200
245
|
|
|
201
246
|
// src/commands/field/search.ts
|
|
202
247
|
function search(parent) {
|
|
203
|
-
parent.command("search <keyword>").description("Search for fields by name or ID").option("--limit <number>", "Maximum number of results (
|
|
248
|
+
parent.command("search <keyword>").description("Search for fields by name or ID").option("--limit <number>", "Maximum number of results (1-1000)", intInRange(1, 1e3), 25).addHelpText(
|
|
204
249
|
"after",
|
|
205
250
|
"\nExamples:\n jiradc field search epic\n jiradc field search customfield_10100\n jiradc field search priority --limit 5"
|
|
206
251
|
).action(async (keyword, opts) => {
|
|
@@ -410,21 +455,23 @@ function attachments(parent) {
|
|
|
410
455
|
|
|
411
456
|
// src/commands/issue/batch-changelog.ts
|
|
412
457
|
function batchChangelog(parent) {
|
|
413
|
-
parent.command("batch-changelog <keys>").description("Get changelogs for multiple issues at once").option("--max <number>", "Max changelog entries per issue (
|
|
458
|
+
parent.command("batch-changelog <keys>").description("Get changelogs for multiple issues at once").option("--max <number>", "Max changelog entries per issue (1-50, Jira DC caps at 50)", intInRange(1, 50), 25).addHelpText(
|
|
414
459
|
"after",
|
|
415
460
|
"\nExamples:\n jiradc issue batch-changelog PROJ-1,PROJ-2,PROJ-3\n jiradc issue batch-changelog PROJ-123,PROJ-124 --max 10"
|
|
416
461
|
).action(async (keys, opts) => {
|
|
417
462
|
const client = getClient();
|
|
418
463
|
const keyList = keys.split(",").map((k) => k.trim());
|
|
419
|
-
const
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
464
|
+
const entries = await Promise.all(
|
|
465
|
+
keyList.map(async (key) => {
|
|
466
|
+
try {
|
|
467
|
+
const data = await client.issues.getChangelog({ issueKeyOrId: key, maxResults: opts.max });
|
|
468
|
+
return [key, data];
|
|
469
|
+
} catch (error) {
|
|
470
|
+
return [key, { error: error instanceof Error ? error.message : "Unknown error" }];
|
|
471
|
+
}
|
|
472
|
+
})
|
|
473
|
+
);
|
|
474
|
+
output(Object.fromEntries(entries));
|
|
428
475
|
});
|
|
429
476
|
}
|
|
430
477
|
|
|
@@ -453,7 +500,7 @@ Examples:
|
|
|
453
500
|
|
|
454
501
|
// src/commands/issue/changelog.ts
|
|
455
502
|
function changelog(parent) {
|
|
456
|
-
parent.command("changelog <key>").description("Get changelog for an issue").option("--max <number>", "Max changelog entries (
|
|
503
|
+
parent.command("changelog <key>").description("Get changelog for an issue").option("--max <number>", "Max changelog entries (1-50, Jira DC caps at 50)", intInRange(1, 50), 25).addHelpText("after", "\nExamples:\n jiradc issue changelog PROJ-123\n jiradc issue changelog PROJ-123 --max 10").action(async (key, opts) => {
|
|
457
504
|
const client = getClient();
|
|
458
505
|
const result = await client.issues.getChangelog({ issueKeyOrId: key, maxResults: opts.max });
|
|
459
506
|
output(result);
|
|
@@ -511,43 +558,44 @@ Examples:
|
|
|
511
558
|
newIssue: result
|
|
512
559
|
};
|
|
513
560
|
if (opts.includeAttachments && f.attachment?.length) {
|
|
514
|
-
const copied = [];
|
|
515
561
|
const tmpFiles = [];
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
562
|
+
const copied = await Promise.all(
|
|
563
|
+
f.attachment.map(async (att) => {
|
|
564
|
+
const tmpPath = join3(tmpdir(), `jiradc-clone-${Date.now()}-${att.filename}`);
|
|
565
|
+
tmpFiles.push(tmpPath);
|
|
566
|
+
await client.issues.downloadAttachment({ url: att.content, destinationPath: tmpPath });
|
|
567
|
+
await client.issues.addAttachment({ issueKeyOrId: newKey, filePath: tmpPath });
|
|
568
|
+
return att.filename;
|
|
569
|
+
})
|
|
570
|
+
);
|
|
523
571
|
await Promise.all(tmpFiles.map((p) => unlink(p).catch(() => void 0)));
|
|
524
572
|
cloneResult.attachmentsCopied = copied.length;
|
|
525
573
|
cloneResult.attachments = copied;
|
|
526
574
|
}
|
|
527
575
|
if (opts.includeLinks && f.issuelinks?.length) {
|
|
528
|
-
const
|
|
529
|
-
|
|
530
|
-
try {
|
|
576
|
+
const outcomes = await Promise.allSettled(
|
|
577
|
+
f.issuelinks.map((link2) => {
|
|
531
578
|
if (link2.outwardIssue) {
|
|
532
|
-
|
|
579
|
+
return client.links.create({
|
|
533
580
|
typeName: link2.type.name,
|
|
534
581
|
inwardIssueKey: newKey,
|
|
535
582
|
outwardIssueKey: link2.outwardIssue.key
|
|
536
583
|
});
|
|
537
|
-
}
|
|
538
|
-
|
|
584
|
+
}
|
|
585
|
+
if (link2.inwardIssue) {
|
|
586
|
+
return client.links.create({
|
|
539
587
|
typeName: link2.type.name,
|
|
540
588
|
inwardIssueKey: link2.inwardIssue.key,
|
|
541
589
|
outwardIssueKey: newKey
|
|
542
590
|
});
|
|
543
591
|
}
|
|
544
|
-
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
cloneResult.linksCopied =
|
|
550
|
-
if (
|
|
592
|
+
return Promise.resolve();
|
|
593
|
+
})
|
|
594
|
+
);
|
|
595
|
+
const created = outcomes.filter((o) => o.status === "fulfilled").length;
|
|
596
|
+
const skipped = outcomes.filter((o) => o.status === "rejected").length;
|
|
597
|
+
cloneResult.linksCopied = created;
|
|
598
|
+
if (skipped > 0) cloneResult.linksSkipped = skipped;
|
|
551
599
|
}
|
|
552
600
|
output(cloneResult);
|
|
553
601
|
}
|
|
@@ -698,7 +746,7 @@ function devStatus(parent) {
|
|
|
698
746
|
|
|
699
747
|
// src/commands/issue/get-worklog.ts
|
|
700
748
|
function getWorklog(parent) {
|
|
701
|
-
parent.command("get-worklog <key>").description("Get worklogs for an issue").option("--start-at <number>", "Starting index for pagination
|
|
749
|
+
parent.command("get-worklog <key>").description("Get worklogs for an issue").option("--start-at <number>", "Starting index for pagination", nonNegativeInt).option("--max <number>", "Max results per page (1-1000)", intInRange(1, 1e3), 25).addHelpText(
|
|
702
750
|
"after",
|
|
703
751
|
"\nExamples:\n jiradc issue get-worklog PROJ-123\n jiradc issue get-worklog PROJ-123 --max 10\n jiradc issue get-worklog PROJ-123 --start-at 10 --max 5"
|
|
704
752
|
).action(async (key, opts) => {
|
|
@@ -785,7 +833,7 @@ function link(parent) {
|
|
|
785
833
|
|
|
786
834
|
// src/commands/issue/search.ts
|
|
787
835
|
function search2(parent) {
|
|
788
|
-
parent.command("search <jql>").description("Search issues using JQL").option("--max <number>", "Max results (
|
|
836
|
+
parent.command("search <jql>").description("Search issues using JQL").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 fields to return (defaults to essential fields)").option("--all-fields", "Return all fields instead of defaults").addHelpText(
|
|
789
837
|
"after",
|
|
790
838
|
'\nExamples:\n jiradc issue search "project = PROJ AND status = Open"\n jiradc issue search "assignee = currentUser()" --max 10 --fields summary,status\n jiradc issue search "project = PROJ" --start-at 50 --max 50'
|
|
791
839
|
).action(async (jql, opts) => {
|
|
@@ -971,14 +1019,14 @@ Examples:
|
|
|
971
1019
|
|
|
972
1020
|
// src/commands/sprint/create.ts
|
|
973
1021
|
function create2(parent) {
|
|
974
|
-
parent.command("create").description("Create a new sprint").requiredOption("--board <id>", "Board ID to create sprint in").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(
|
|
1022
|
+
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(
|
|
975
1023
|
"after",
|
|
976
1024
|
'\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"'
|
|
977
1025
|
).action(async (opts) => {
|
|
978
1026
|
const client = getClient();
|
|
979
1027
|
const result = await client.agile.createSprint({
|
|
980
1028
|
name: opts.name,
|
|
981
|
-
originBoardId:
|
|
1029
|
+
originBoardId: opts.board,
|
|
982
1030
|
startDate: opts.startDate,
|
|
983
1031
|
endDate: opts.endDate,
|
|
984
1032
|
goal: opts.goal
|
|
@@ -988,14 +1036,15 @@ function create2(parent) {
|
|
|
988
1036
|
}
|
|
989
1037
|
|
|
990
1038
|
// src/commands/sprint/issues.ts
|
|
1039
|
+
import { Argument as Argument2 } from "commander";
|
|
991
1040
|
function issues2(parent) {
|
|
992
|
-
parent.command("issues
|
|
1041
|
+
parent.command("issues").description("Get issues in a sprint").addArgument(new Argument2("<id>", "Sprint 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 sprint").addHelpText(
|
|
993
1042
|
"after",
|
|
994
1043
|
'\nExamples:\n jiradc sprint issues 100\n jiradc sprint issues 100 --max 20\n jiradc sprint issues 100 --jql "status = Done" --fields summary,status\n jiradc sprint issues 100 --start-at 50 --max 25'
|
|
995
1044
|
).action(async (id, opts) => {
|
|
996
1045
|
const client = getClient();
|
|
997
1046
|
const result = await client.agile.getSprintIssues({
|
|
998
|
-
sprintId:
|
|
1047
|
+
sprintId: id,
|
|
999
1048
|
startAt: opts.startAt,
|
|
1000
1049
|
maxResults: opts.max,
|
|
1001
1050
|
fields: opts.fields?.split(",").map((f) => f.trim()),
|
|
@@ -1006,14 +1055,16 @@ function issues2(parent) {
|
|
|
1006
1055
|
}
|
|
1007
1056
|
|
|
1008
1057
|
// src/commands/sprint/list.ts
|
|
1058
|
+
import { Option as Option2 } from "commander";
|
|
1059
|
+
var SPRINT_STATES = ["future", "active", "closed"];
|
|
1009
1060
|
function list4(parent) {
|
|
1010
|
-
parent.command("list").description("List sprints for a board").requiredOption("--board <id>", "Board ID").
|
|
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(
|
|
1011
1062
|
"after",
|
|
1012
1063
|
"\nExamples:\n jiradc sprint list --board 42\n jiradc sprint list --board 42 --state active"
|
|
1013
1064
|
).action(async (opts) => {
|
|
1014
1065
|
const client = getClient();
|
|
1015
1066
|
const result = await client.agile.getSprints({
|
|
1016
|
-
boardId:
|
|
1067
|
+
boardId: opts.board,
|
|
1017
1068
|
state: opts.state
|
|
1018
1069
|
});
|
|
1019
1070
|
output(result);
|
|
@@ -1021,15 +1072,17 @@ function list4(parent) {
|
|
|
1021
1072
|
}
|
|
1022
1073
|
|
|
1023
1074
|
// src/commands/sprint/update.ts
|
|
1075
|
+
import { Argument as Argument3, Option as Option3 } from "commander";
|
|
1076
|
+
var SPRINT_STATES2 = ["future", "active", "closed"];
|
|
1024
1077
|
function update2(parent) {
|
|
1025
|
-
parent.command("update
|
|
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(
|
|
1026
1079
|
"after",
|
|
1027
1080
|
'\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"'
|
|
1028
1081
|
).action(
|
|
1029
1082
|
async (id, opts) => {
|
|
1030
1083
|
const client = getClient();
|
|
1031
1084
|
const result = await client.agile.updateSprint({
|
|
1032
|
-
sprintId:
|
|
1085
|
+
sprintId: id,
|
|
1033
1086
|
name: opts.name,
|
|
1034
1087
|
state: opts.state,
|
|
1035
1088
|
startDate: opts.startDate,
|
|
@@ -1082,10 +1135,20 @@ Examples:
|
|
|
1082
1135
|
}
|
|
1083
1136
|
|
|
1084
1137
|
// src/index.ts
|
|
1138
|
+
function readPackageVersion() {
|
|
1139
|
+
try {
|
|
1140
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
1141
|
+
const pkgPath = join4(here, "..", "package.json");
|
|
1142
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
1143
|
+
return pkg.version ?? "0.0.0";
|
|
1144
|
+
} catch {
|
|
1145
|
+
return "0.0.0";
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1085
1148
|
var DIM = "\x1B[2m";
|
|
1086
1149
|
var RESET = "\x1B[0m";
|
|
1087
|
-
var program = new
|
|
1088
|
-
program.name("jiradc").description("Jira Data Center CLI").version(
|
|
1150
|
+
var program = new Command6();
|
|
1151
|
+
program.name("jiradc").description("Jira Data Center CLI").version(readPackageVersion()).configureHelp({
|
|
1089
1152
|
styleTitle: (str) => styleText("bold", str),
|
|
1090
1153
|
styleUsage: (str) => styleText("dim", str),
|
|
1091
1154
|
styleCommandDescription: (str) => styleText("dim", str),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jiradc-cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.10",
|
|
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.
|
|
15
|
+
"jira-data-center-client": "1.0.29"
|
|
16
16
|
},
|
|
17
17
|
"devDependencies": {
|
|
18
18
|
"@types/node": "24.10.4",
|
|
@@ -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-typescript": "0.0.0",
|
|
26
|
+
"config-eslint": "0.0.0"
|
|
27
27
|
},
|
|
28
28
|
"engines": {
|
|
29
29
|
"node": ">=22.0.0"
|