jiradc-cli 1.0.21 → 2.0.0-g40cd2b1.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 +704 -551
- package/package.json +5 -4
package/dist/index.js
CHANGED
|
@@ -1,16 +1,25 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
//
|
|
4
|
-
import { readFileSync } from "fs";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { styleText } from "util";
|
|
8
|
-
import { Command as Command11 } from "commander";
|
|
3
|
+
// ../../cli-utils/dist/cache.js
|
|
4
|
+
import { readFileSync, writeFileSync, mkdirSync, statSync } from "fs";
|
|
5
|
+
import { homedir } from "os";
|
|
6
|
+
import { join } from "path";
|
|
9
7
|
|
|
10
|
-
//
|
|
11
|
-
import {
|
|
8
|
+
// ../../cli-utils/dist/bootstrap.js
|
|
9
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
10
|
+
import { dirname, join as join2 } from "path";
|
|
11
|
+
import { fileURLToPath } from "url";
|
|
12
|
+
function readPackageVersion(importMetaUrl) {
|
|
13
|
+
try {
|
|
14
|
+
const here = dirname(fileURLToPath(importMetaUrl));
|
|
15
|
+
const pkg = JSON.parse(readFileSync2(join2(here, "..", "package.json"), "utf-8"));
|
|
16
|
+
return pkg.version ?? "0.0.0";
|
|
17
|
+
} catch {
|
|
18
|
+
return "0.0.0";
|
|
19
|
+
}
|
|
20
|
+
}
|
|
12
21
|
|
|
13
|
-
//
|
|
22
|
+
// ../../cli-utils/dist/validators.js
|
|
14
23
|
import { InvalidArgumentError } from "commander";
|
|
15
24
|
function intInRange(min, max) {
|
|
16
25
|
return (raw) => {
|
|
@@ -39,43 +48,281 @@ function positiveInt(raw) {
|
|
|
39
48
|
return n;
|
|
40
49
|
}
|
|
41
50
|
|
|
42
|
-
//
|
|
43
|
-
|
|
51
|
+
// ../../cli-utils/dist/registry.js
|
|
52
|
+
var SUB_ENTITY_ID_NUMERIC = {
|
|
53
|
+
comment: true,
|
|
54
|
+
worklog: true,
|
|
55
|
+
attachment: true,
|
|
56
|
+
request: true,
|
|
57
|
+
type: true,
|
|
58
|
+
employee: true,
|
|
59
|
+
message: false,
|
|
60
|
+
membership: false
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// ../../cli-utils/dist/builders.js
|
|
64
|
+
function defineOption(cmd, flags, description, parser, mandatory) {
|
|
65
|
+
if (mandatory) {
|
|
66
|
+
return parser ? cmd.requiredOption(flags, description, parser) : cmd.requiredOption(flags, description);
|
|
67
|
+
}
|
|
68
|
+
return parser ? cmd.option(flags, description, parser) : cmd.option(flags, description);
|
|
69
|
+
}
|
|
70
|
+
function subjectArg(cmd, name, opts = {}) {
|
|
71
|
+
const inner = opts.variadic ? `${name}...` : name;
|
|
72
|
+
const token = opts.optional ? `[${inner}]` : `<${inner}>`;
|
|
73
|
+
return opts.parser ? cmd.argument(token, opts.description ?? "", opts.parser) : cmd.argument(token, opts.description ?? "");
|
|
74
|
+
}
|
|
75
|
+
function subEntityOption(cmd, entity, opts = {}) {
|
|
76
|
+
const numeric = opts.numeric ?? SUB_ENTITY_ID_NUMERIC[entity] ?? true;
|
|
77
|
+
const description = `${entity.charAt(0).toUpperCase()}${entity.slice(1)} id`;
|
|
78
|
+
return defineOption(cmd, `--${entity}-id <id>`, description, numeric ? positiveInt : void 0, opts.mandatory);
|
|
79
|
+
}
|
|
80
|
+
function bodyOption(cmd, opts = {}) {
|
|
81
|
+
return defineOption(cmd, "--body <text>", "Prose body content", void 0, opts.mandatory);
|
|
82
|
+
}
|
|
83
|
+
var EXAMPLES = /* @__PURE__ */ new WeakSet();
|
|
84
|
+
function commandPath(cmd) {
|
|
85
|
+
const parts = [];
|
|
86
|
+
for (let c = cmd; c; c = c.parent)
|
|
87
|
+
parts.unshift(c.name());
|
|
88
|
+
return parts.join(" ");
|
|
89
|
+
}
|
|
90
|
+
function exampleLine(path, entry) {
|
|
91
|
+
if (typeof entry === "string")
|
|
92
|
+
return entry ? ` ${path} ${entry}` : ` ${path}`;
|
|
93
|
+
if ("raw" in entry)
|
|
94
|
+
return ` ${entry.raw}`;
|
|
95
|
+
const [args, comment] = entry;
|
|
96
|
+
const line = args ? `${path} ${args}` : path;
|
|
97
|
+
return ` ${line} # ${comment}`;
|
|
98
|
+
}
|
|
99
|
+
function examples(cmd, entries) {
|
|
100
|
+
EXAMPLES.add(cmd);
|
|
101
|
+
return cmd.addHelpText("after", () => {
|
|
102
|
+
const path = commandPath(cmd);
|
|
103
|
+
return `
|
|
104
|
+
Examples:
|
|
105
|
+
${entries.map((e) => exampleLine(path, e)).join("\n")}`;
|
|
106
|
+
});
|
|
107
|
+
}
|
|
44
108
|
|
|
45
|
-
//
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
109
|
+
// ../../cli-utils/dist/errors.js
|
|
110
|
+
import { CommanderError } from "commander";
|
|
111
|
+
var EXIT = {
|
|
112
|
+
SUCCESS: 0,
|
|
113
|
+
GENERIC: 1,
|
|
114
|
+
USAGE: 2,
|
|
115
|
+
NOT_FOUND: 3,
|
|
116
|
+
FORBIDDEN: 4,
|
|
117
|
+
CONFLICT: 5,
|
|
118
|
+
AUTH: 6
|
|
119
|
+
};
|
|
120
|
+
var CliAuthError = class extends Error {
|
|
121
|
+
constructor(message) {
|
|
122
|
+
super(message);
|
|
123
|
+
this.name = "CliAuthError";
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
var TYPE_EXIT = {
|
|
127
|
+
usage: EXIT.USAGE,
|
|
128
|
+
not_found: EXIT.NOT_FOUND,
|
|
129
|
+
forbidden: EXIT.FORBIDDEN,
|
|
130
|
+
conflict: EXIT.CONFLICT,
|
|
131
|
+
auth: EXIT.AUTH,
|
|
132
|
+
rate_limited: EXIT.GENERIC,
|
|
133
|
+
server: EXIT.GENERIC,
|
|
134
|
+
network: EXIT.GENERIC,
|
|
135
|
+
unknown: EXIT.GENERIC
|
|
136
|
+
};
|
|
137
|
+
var NETWORK_CODES = ["ENOTFOUND", "ECONNREFUSED", "ECONNRESET", "ETIMEDOUT"];
|
|
138
|
+
function httpStatus(err) {
|
|
139
|
+
const e = err;
|
|
140
|
+
return e?.response?.status ?? e?.statusCode;
|
|
141
|
+
}
|
|
142
|
+
function responseDetail(err) {
|
|
143
|
+
const e = err;
|
|
144
|
+
const data = e?.response?.data;
|
|
145
|
+
if (data && typeof data === "object")
|
|
146
|
+
return data;
|
|
147
|
+
const body = e?.body;
|
|
148
|
+
if (typeof body === "string") {
|
|
149
|
+
try {
|
|
150
|
+
const parsed = JSON.parse(body);
|
|
151
|
+
return parsed.error ?? parsed;
|
|
152
|
+
} catch {
|
|
153
|
+
return void 0;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
if (body && typeof body === "object") {
|
|
157
|
+
return body.error ?? body;
|
|
158
|
+
}
|
|
159
|
+
return void 0;
|
|
160
|
+
}
|
|
161
|
+
function errorCode(err) {
|
|
162
|
+
return err?.code;
|
|
163
|
+
}
|
|
164
|
+
function normalize(err, opts) {
|
|
165
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
166
|
+
if (err instanceof CommanderError) {
|
|
167
|
+
return {
|
|
168
|
+
type: "usage",
|
|
169
|
+
message: message || "Invalid command usage",
|
|
170
|
+
recovery: "Check the command syntax and flags; run the command with --help.",
|
|
171
|
+
retryable: false
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
if (err instanceof CliAuthError) {
|
|
175
|
+
return { type: "auth", message: message || "Missing credentials", recovery: opts.authRecovery, retryable: false };
|
|
176
|
+
}
|
|
177
|
+
const status = httpStatus(err);
|
|
178
|
+
const detail = responseDetail(err);
|
|
179
|
+
if (status !== void 0) {
|
|
180
|
+
switch (status) {
|
|
181
|
+
case 400:
|
|
182
|
+
return {
|
|
183
|
+
type: "usage",
|
|
184
|
+
status,
|
|
185
|
+
message: "Bad request (HTTP 400)",
|
|
186
|
+
recovery: "Check parameter values (ids, keys, query syntax) against the API.",
|
|
187
|
+
retryable: false,
|
|
188
|
+
detail
|
|
189
|
+
};
|
|
190
|
+
case 401:
|
|
191
|
+
return {
|
|
192
|
+
type: "auth",
|
|
193
|
+
status,
|
|
194
|
+
message: "Authentication failed (HTTP 401)",
|
|
195
|
+
recovery: opts.authRecovery,
|
|
196
|
+
retryable: false
|
|
197
|
+
};
|
|
198
|
+
case 403:
|
|
199
|
+
return {
|
|
200
|
+
type: "forbidden",
|
|
201
|
+
status,
|
|
202
|
+
message: "Forbidden (HTTP 403)",
|
|
203
|
+
recovery: `Your ${opts.service} account lacks permission for this operation; check token scope and resource permissions.`,
|
|
204
|
+
retryable: false,
|
|
205
|
+
detail
|
|
206
|
+
};
|
|
207
|
+
case 404:
|
|
208
|
+
return {
|
|
209
|
+
type: "not_found",
|
|
210
|
+
status,
|
|
211
|
+
message: "Not found (HTTP 404)",
|
|
212
|
+
recovery: "Verify the id/key exists and that you have access to it.",
|
|
213
|
+
retryable: false,
|
|
214
|
+
detail
|
|
215
|
+
};
|
|
216
|
+
case 409:
|
|
217
|
+
return {
|
|
218
|
+
type: "conflict",
|
|
219
|
+
status,
|
|
220
|
+
message: "Conflict (HTTP 409)",
|
|
221
|
+
recovery: "The resource changed or already exists; re-fetch current state and retry.",
|
|
222
|
+
retryable: false,
|
|
223
|
+
detail
|
|
224
|
+
};
|
|
225
|
+
case 429:
|
|
226
|
+
return {
|
|
227
|
+
type: "rate_limited",
|
|
228
|
+
status,
|
|
229
|
+
message: "Rate limited (HTTP 429)",
|
|
230
|
+
recovery: "Wait and retry the request.",
|
|
231
|
+
retryable: true
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
if (status >= 500) {
|
|
235
|
+
return {
|
|
236
|
+
type: "server",
|
|
237
|
+
status,
|
|
238
|
+
message: `Server error (HTTP ${status})`,
|
|
239
|
+
recovery: `${opts.service} returned an internal error; retry shortly.`,
|
|
240
|
+
retryable: true,
|
|
241
|
+
detail
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
return {
|
|
245
|
+
type: "unknown",
|
|
246
|
+
status,
|
|
247
|
+
message: `${opts.service} error (HTTP ${status}): ${message}`,
|
|
248
|
+
recovery: "Inspect the detail field for the API response.",
|
|
249
|
+
retryable: false,
|
|
250
|
+
detail
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
const code = errorCode(err);
|
|
254
|
+
if (code && NETWORK_CODES.includes(code) || NETWORK_CODES.some((c) => message.includes(c))) {
|
|
255
|
+
return {
|
|
256
|
+
type: "network",
|
|
257
|
+
message: `Cannot connect to ${opts.service}: ${message}`,
|
|
258
|
+
recovery: opts.networkRecovery ?? "Verify the *_URL is correct, the server is reachable, and you are on the VPN if required.",
|
|
259
|
+
retryable: true
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
return { type: "unknown", message, recovery: "Unexpected error; inspect the message.", retryable: false };
|
|
263
|
+
}
|
|
264
|
+
function classifyError(err, opts) {
|
|
265
|
+
const base = normalize(err, opts);
|
|
266
|
+
const n = opts.adapt ? opts.adapt(base, err) : base;
|
|
267
|
+
if (n.type === "auth" && n.detail === void 0 && opts.credentialInfo) {
|
|
268
|
+
n.detail = opts.credentialInfo();
|
|
269
|
+
}
|
|
49
270
|
return {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
description: "Personal Access Token"
|
|
271
|
+
envelope: {
|
|
272
|
+
error: {
|
|
273
|
+
type: n.type,
|
|
274
|
+
message: n.message,
|
|
275
|
+
recovery: n.recovery,
|
|
276
|
+
retryable: n.retryable,
|
|
277
|
+
...n.detail !== void 0 ? { detail: n.detail } : {}
|
|
58
278
|
}
|
|
59
279
|
},
|
|
60
|
-
|
|
61
|
-
hint: "Export environment variables in your shell profile (e.g., ~/.zshrc)."
|
|
280
|
+
exitCode: TYPE_EXIT[n.type]
|
|
62
281
|
};
|
|
63
282
|
}
|
|
283
|
+
function createErrorHandler(opts) {
|
|
284
|
+
return (err) => {
|
|
285
|
+
const { envelope, exitCode } = classifyError(err, opts);
|
|
286
|
+
process.stderr.write(`${JSON.stringify(envelope)}
|
|
287
|
+
`);
|
|
288
|
+
return process.exit(exitCode);
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
function isCleanCommanderExit(err) {
|
|
292
|
+
return err instanceof CommanderError && (err.exitCode === 0 || err.code === "commander.helpDisplayed" || err.code === "commander.help" || err.code === "commander.version");
|
|
293
|
+
}
|
|
294
|
+
function routeErrors(cmd) {
|
|
295
|
+
cmd.exitOverride();
|
|
296
|
+
cmd.configureOutput({ writeErr: () => void 0 });
|
|
297
|
+
cmd.commands.forEach(routeErrors);
|
|
298
|
+
}
|
|
299
|
+
async function runCli(program, opts) {
|
|
300
|
+
routeErrors(program);
|
|
301
|
+
try {
|
|
302
|
+
await program.parseAsync();
|
|
303
|
+
} catch (err) {
|
|
304
|
+
if (isCleanCommanderExit(err)) {
|
|
305
|
+
process.exit(err.exitCode ?? 0);
|
|
306
|
+
}
|
|
307
|
+
createErrorHandler(opts)(err);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// src/program.ts
|
|
312
|
+
import { styleText } from "util";
|
|
313
|
+
import { Command as Command11 } from "commander";
|
|
314
|
+
|
|
315
|
+
// src/commands/board/issues.ts
|
|
316
|
+
import { Argument } from "commander";
|
|
64
317
|
|
|
65
318
|
// src/utils/client.ts
|
|
319
|
+
import { JiraClient } from "jira-data-center-client";
|
|
66
320
|
function getClient() {
|
|
67
321
|
const baseUrl = process.env.JIRA_URL;
|
|
68
322
|
const token = process.env.JIRA_TOKEN;
|
|
69
323
|
if (!baseUrl || !token) {
|
|
70
324
|
const missing = [...!baseUrl ? ["JIRA_URL"] : [], ...!token ? ["JIRA_TOKEN"] : []];
|
|
71
|
-
|
|
72
|
-
`${JSON.stringify({
|
|
73
|
-
error: `Missing required environment variables: ${missing.join(", ")}`,
|
|
74
|
-
...getCredentialInfo()
|
|
75
|
-
})}
|
|
76
|
-
`
|
|
77
|
-
);
|
|
78
|
-
process.exit(1);
|
|
325
|
+
throw new CliAuthError(`Missing required environment variables: ${missing.join(", ")}`);
|
|
79
326
|
}
|
|
80
327
|
return new JiraClient({ baseUrl, token });
|
|
81
328
|
}
|
|
@@ -89,68 +336,6 @@ function output(data) {
|
|
|
89
336
|
process.stdout.write(`${JSON.stringify(data, null, prettyPrint ? 2 : void 0)}
|
|
90
337
|
`);
|
|
91
338
|
}
|
|
92
|
-
function handleError(err) {
|
|
93
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
94
|
-
const axiosStatus = err?.response?.status;
|
|
95
|
-
if (axiosStatus === 400) {
|
|
96
|
-
const responseData = err?.response?.data;
|
|
97
|
-
const jiraErrors = responseData && typeof responseData === "object" ? responseData : void 0;
|
|
98
|
-
process.stderr.write(
|
|
99
|
-
`${JSON.stringify({
|
|
100
|
-
error: `Bad request (HTTP 400)`,
|
|
101
|
-
detail: jiraErrors ?? message,
|
|
102
|
-
hint: "Check that all parameters are valid (JQL syntax, field names, project keys, etc.)."
|
|
103
|
-
})}
|
|
104
|
-
`
|
|
105
|
-
);
|
|
106
|
-
} else if (axiosStatus === 401) {
|
|
107
|
-
process.stderr.write(
|
|
108
|
-
`${JSON.stringify({
|
|
109
|
-
error: "Authentication failed (HTTP 401)",
|
|
110
|
-
...getCredentialInfo()
|
|
111
|
-
})}
|
|
112
|
-
`
|
|
113
|
-
);
|
|
114
|
-
} else if (axiosStatus === 403) {
|
|
115
|
-
const responseData = err?.response?.data;
|
|
116
|
-
process.stderr.write(
|
|
117
|
-
`${JSON.stringify({
|
|
118
|
-
error: "Forbidden (HTTP 403)",
|
|
119
|
-
detail: responseData && typeof responseData === "object" ? responseData : message,
|
|
120
|
-
hint: "Your account does not have permission for this operation. Check project permissions and token scope."
|
|
121
|
-
})}
|
|
122
|
-
`
|
|
123
|
-
);
|
|
124
|
-
} else if (message.includes("ENOTFOUND") || message.includes("ECONNREFUSED") || message.includes("ECONNRESET")) {
|
|
125
|
-
process.stderr.write(
|
|
126
|
-
`${JSON.stringify({
|
|
127
|
-
error: `Cannot connect to Jira server: ${message}`,
|
|
128
|
-
hint: "Verify that JIRA_URL is correct and the server is reachable."
|
|
129
|
-
})}
|
|
130
|
-
`
|
|
131
|
-
);
|
|
132
|
-
} else if (axiosStatus === 500) {
|
|
133
|
-
process.stderr.write(
|
|
134
|
-
`${JSON.stringify({
|
|
135
|
-
error: `Server error (HTTP 500): ${message}`,
|
|
136
|
-
hint: "The server returned an internal error. Check that all parameters are valid (project keys, issue keys, JQL query syntax)."
|
|
137
|
-
})}
|
|
138
|
-
`
|
|
139
|
-
);
|
|
140
|
-
} else {
|
|
141
|
-
const responseData = err?.response?.data;
|
|
142
|
-
const detail = responseData && typeof responseData === "object" ? responseData : void 0;
|
|
143
|
-
process.stderr.write(
|
|
144
|
-
`${JSON.stringify({
|
|
145
|
-
error: message,
|
|
146
|
-
...axiosStatus !== void 0 && { statusCode: axiosStatus },
|
|
147
|
-
...detail && { detail }
|
|
148
|
-
})}
|
|
149
|
-
`
|
|
150
|
-
);
|
|
151
|
-
}
|
|
152
|
-
process.exit(1);
|
|
153
|
-
}
|
|
154
339
|
|
|
155
340
|
// src/utils/transformers/base.ts
|
|
156
341
|
function jiraBaseUrl() {
|
|
@@ -477,10 +662,14 @@ function transformTransitions(response) {
|
|
|
477
662
|
|
|
478
663
|
// src/commands/board/issues.ts
|
|
479
664
|
function issues(parent) {
|
|
480
|
-
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")
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
665
|
+
const cmd = 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");
|
|
666
|
+
examples(cmd, [
|
|
667
|
+
"42",
|
|
668
|
+
"42 --limit 10",
|
|
669
|
+
'42 --jql "status = Open" --fields summary,status',
|
|
670
|
+
"42 --start 50 --limit 25"
|
|
671
|
+
]);
|
|
672
|
+
cmd.action(async (id, opts) => {
|
|
484
673
|
const client = getClient();
|
|
485
674
|
const result = await client.agile.getBoardIssues({
|
|
486
675
|
boardId: id,
|
|
@@ -497,10 +686,9 @@ function issues(parent) {
|
|
|
497
686
|
import { Option } from "commander";
|
|
498
687
|
var BOARD_TYPES = ["scrum", "kanban", "simple"];
|
|
499
688
|
function list(parent) {
|
|
500
|
-
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")
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
).action(async (opts) => {
|
|
689
|
+
const cmd = 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");
|
|
690
|
+
examples(cmd, ["", "--limit 10", "--project PROJ", '--type scrum --name "Team Board"']);
|
|
691
|
+
cmd.action(async (opts) => {
|
|
504
692
|
const client = getClient();
|
|
505
693
|
const result = await client.agile.getBoards({
|
|
506
694
|
maxResults: opts.limit,
|
|
@@ -513,16 +701,9 @@ function list(parent) {
|
|
|
513
701
|
}
|
|
514
702
|
|
|
515
703
|
// src/commands/board/index.ts
|
|
516
|
-
function registerBoardCommands(
|
|
517
|
-
const board =
|
|
518
|
-
|
|
519
|
-
`
|
|
520
|
-
Examples:
|
|
521
|
-
$ jiradc board list
|
|
522
|
-
$ jiradc board list --type scrum --project PROJ
|
|
523
|
-
$ jiradc board issues 42 --limit 20
|
|
524
|
-
`
|
|
525
|
-
);
|
|
704
|
+
function registerBoardCommands(program) {
|
|
705
|
+
const board = program.command("board").description("Board operations");
|
|
706
|
+
examples(board, ["list", "list --type scrum --project PROJ", "issues 42 --limit 20"]);
|
|
526
707
|
list(board);
|
|
527
708
|
issues(board);
|
|
528
709
|
}
|
|
@@ -536,14 +717,13 @@ var ASSIGNEE_TYPES = [
|
|
|
536
717
|
"UNASSIGNED"
|
|
537
718
|
];
|
|
538
719
|
function create(parent) {
|
|
539
|
-
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))
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
).action(
|
|
720
|
+
const cmd = 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));
|
|
721
|
+
examples(cmd, [
|
|
722
|
+
"--project AI --name Backend",
|
|
723
|
+
'--project AI --name Frontend --description "UI work" --lead jsmith',
|
|
724
|
+
"--project AI --name Infra --assignee-type COMPONENT_LEAD --lead jsmith"
|
|
725
|
+
]);
|
|
726
|
+
cmd.action(
|
|
547
727
|
async (opts) => {
|
|
548
728
|
const client = getClient();
|
|
549
729
|
const result = await client.components.create({
|
|
@@ -560,13 +740,9 @@ Examples:
|
|
|
560
740
|
|
|
561
741
|
// src/commands/component/delete.ts
|
|
562
742
|
function deleteComponent(parent) {
|
|
563
|
-
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")
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
Examples:
|
|
567
|
-
jiradc component delete 11289
|
|
568
|
-
jiradc component delete 11289 --move-issues-to 11290`
|
|
569
|
-
).action(async (id, opts) => {
|
|
743
|
+
const cmd = 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");
|
|
744
|
+
examples(cmd, ["11289", "11289 --move-issues-to 11290"]);
|
|
745
|
+
cmd.action(async (id, opts) => {
|
|
570
746
|
const client = getClient();
|
|
571
747
|
await client.components.delete({ id, moveIssuesTo: opts.moveIssuesTo });
|
|
572
748
|
output({ deleted: true, componentId: id, ...opts.moveIssuesTo && { movedIssuesTo: opts.moveIssuesTo } });
|
|
@@ -575,7 +751,9 @@ Examples:
|
|
|
575
751
|
|
|
576
752
|
// src/commands/component/get.ts
|
|
577
753
|
function get(parent) {
|
|
578
|
-
parent.command("get <id>").description("Get a component by ID")
|
|
754
|
+
const cmd = parent.command("get <id>").description("Get a component by ID");
|
|
755
|
+
examples(cmd, ["11289"]);
|
|
756
|
+
cmd.action(async (id) => {
|
|
579
757
|
const client = getClient();
|
|
580
758
|
const result = await client.components.get({ id });
|
|
581
759
|
output(transformComponent(result));
|
|
@@ -584,7 +762,9 @@ function get(parent) {
|
|
|
584
762
|
|
|
585
763
|
// src/commands/component/issue-count.ts
|
|
586
764
|
function issueCount(parent) {
|
|
587
|
-
parent.command("issue-count <id>").description("Get the number of issues currently using this component")
|
|
765
|
+
const cmd = parent.command("issue-count <id>").description("Get the number of issues currently using this component");
|
|
766
|
+
examples(cmd, ["11289"]);
|
|
767
|
+
cmd.action(async (id) => {
|
|
588
768
|
const client = getClient();
|
|
589
769
|
const result = await client.components.getRelatedIssueCounts({ id });
|
|
590
770
|
output(transformComponentIssueCounts(result));
|
|
@@ -593,7 +773,9 @@ function issueCount(parent) {
|
|
|
593
773
|
|
|
594
774
|
// src/commands/component/list.ts
|
|
595
775
|
function list2(parent) {
|
|
596
|
-
parent.command("list").description("List all components for a project").requiredOption("--project <key>", "Project key (e.g., AI)")
|
|
776
|
+
const cmd = parent.command("list").description("List all components for a project").requiredOption("--project <key>", "Project key (e.g., AI)");
|
|
777
|
+
examples(cmd, ["--project AI"]);
|
|
778
|
+
cmd.action(async (opts) => {
|
|
597
779
|
const client = getClient();
|
|
598
780
|
const result = await client.components.list({ projectKeyOrId: opts.project });
|
|
599
781
|
output(result.map(transformComponent));
|
|
@@ -609,14 +791,13 @@ var ASSIGNEE_TYPES2 = [
|
|
|
609
791
|
"UNASSIGNED"
|
|
610
792
|
];
|
|
611
793
|
function update(parent) {
|
|
612
|
-
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))
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
).action(
|
|
794
|
+
const cmd = 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));
|
|
795
|
+
examples(cmd, [
|
|
796
|
+
"11289 --name Backend",
|
|
797
|
+
'11289 --description "Server-side code"',
|
|
798
|
+
"11289 --lead jsmith --assignee-type COMPONENT_LEAD"
|
|
799
|
+
]);
|
|
800
|
+
cmd.action(
|
|
620
801
|
async (id, opts) => {
|
|
621
802
|
if (opts.name === void 0 && opts.description === void 0 && opts.lead === void 0 && opts.assigneeType === void 0) {
|
|
622
803
|
throw new Error("Provide at least one of --name, --description, --lead, --assignee-type");
|
|
@@ -635,19 +816,16 @@ Examples:
|
|
|
635
816
|
}
|
|
636
817
|
|
|
637
818
|
// src/commands/component/index.ts
|
|
638
|
-
function registerComponentCommands(
|
|
639
|
-
const component =
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
$ jiradc component issue-count 11289
|
|
649
|
-
`
|
|
650
|
-
);
|
|
819
|
+
function registerComponentCommands(program) {
|
|
820
|
+
const component = program.command("component").description("Project component operations");
|
|
821
|
+
examples(component, [
|
|
822
|
+
"list --project AI",
|
|
823
|
+
"get 11289",
|
|
824
|
+
"create --project AI --name Backend",
|
|
825
|
+
"update 11289 --name Backend",
|
|
826
|
+
"delete 11289 --move-issues-to 11290",
|
|
827
|
+
"issue-count 11289"
|
|
828
|
+
]);
|
|
651
829
|
list2(component);
|
|
652
830
|
get(component);
|
|
653
831
|
create(component);
|
|
@@ -658,16 +836,15 @@ Examples:
|
|
|
658
836
|
|
|
659
837
|
// src/commands/field/options.ts
|
|
660
838
|
function options(parent) {
|
|
661
|
-
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("--
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
).action(async (id, opts) => {
|
|
839
|
+
const cmd = 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("--start <number>", "Page number (1-indexed)", positiveInt);
|
|
840
|
+
examples(cmd, ["10001", '10001 --query "High"', "10001 --limit 20 --start 2"]);
|
|
841
|
+
cmd.action(async (id, opts) => {
|
|
665
842
|
const client = getClient();
|
|
666
843
|
const result = await client.fields.getFieldOptions({
|
|
667
844
|
fieldId: id,
|
|
668
845
|
query: opts.query,
|
|
669
846
|
maxResults: opts.limit,
|
|
670
|
-
page: opts.
|
|
847
|
+
page: opts.start
|
|
671
848
|
});
|
|
672
849
|
output(transformPaged({ ...result, startAt: result.startAt ?? 0 }, transformCustomFieldOption));
|
|
673
850
|
});
|
|
@@ -675,10 +852,9 @@ function options(parent) {
|
|
|
675
852
|
|
|
676
853
|
// src/commands/field/search.ts
|
|
677
854
|
function search(parent) {
|
|
678
|
-
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)
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
).action(async (keyword, opts) => {
|
|
855
|
+
const cmd = 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);
|
|
856
|
+
examples(cmd, ["epic", "customfield_10100", "priority --limit 5"]);
|
|
857
|
+
cmd.action(async (keyword, opts) => {
|
|
682
858
|
const client = getClient();
|
|
683
859
|
const result = await client.fields.search(keyword, opts.limit);
|
|
684
860
|
output(result.map(transformField));
|
|
@@ -686,16 +862,9 @@ function search(parent) {
|
|
|
686
862
|
}
|
|
687
863
|
|
|
688
864
|
// src/commands/field/index.ts
|
|
689
|
-
function registerFieldCommands(
|
|
690
|
-
const field =
|
|
691
|
-
|
|
692
|
-
`
|
|
693
|
-
Examples:
|
|
694
|
-
$ jiradc field search "epic"
|
|
695
|
-
$ jiradc field search "priority"
|
|
696
|
-
$ jiradc field options 10120
|
|
697
|
-
`
|
|
698
|
-
);
|
|
865
|
+
function registerFieldCommands(program) {
|
|
866
|
+
const field = program.command("field").description("Field operations");
|
|
867
|
+
examples(field, ['search "epic"', 'search "priority"', "options 10120"]);
|
|
699
868
|
search(field);
|
|
700
869
|
options(field);
|
|
701
870
|
}
|
|
@@ -718,15 +887,10 @@ async function resolveUserToken(token) {
|
|
|
718
887
|
|
|
719
888
|
// src/commands/issue/assign.ts
|
|
720
889
|
function assign(parent) {
|
|
721
|
-
parent.command("assign <key>
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
jiradc issue assign PROJ-123 jsmith
|
|
726
|
-
jiradc issue assign PROJ-123 me
|
|
727
|
-
jiradc issue assign PROJ-123 none`
|
|
728
|
-
).action(async (key, user) => {
|
|
729
|
-
const resolved = await resolveUserToken(user);
|
|
890
|
+
const cmd = parent.command("assign <key>").description('Assign an issue. --assignee is a username, "me", or "none" to unassign.').requiredOption("--assignee <user>", 'Username to assign, "me" for the current user, or "none" to unassign');
|
|
891
|
+
examples(cmd, ["PROJ-123 --assignee jsmith", "PROJ-123 --assignee me", "PROJ-123 --assignee none"]);
|
|
892
|
+
cmd.action(async (key, opts) => {
|
|
893
|
+
const resolved = await resolveUserToken(opts.assignee);
|
|
730
894
|
const client = getClient();
|
|
731
895
|
await client.issues.update({
|
|
732
896
|
issueKeyOrId: key,
|
|
@@ -738,20 +902,25 @@ Examples:
|
|
|
738
902
|
|
|
739
903
|
// src/commands/issue/attachment/delete.ts
|
|
740
904
|
function deleteAttachment(parent) {
|
|
741
|
-
parent.command("delete").description("Delete an attachment by ID")
|
|
905
|
+
const cmd = parent.command("delete <key>").description("Delete an attachment by ID");
|
|
906
|
+
subEntityOption(cmd, "attachment", { mandatory: true });
|
|
907
|
+
examples(cmd, ["PROJ-123 --attachment-id 12345"]);
|
|
908
|
+
cmd.action(async (key, opts) => {
|
|
742
909
|
const client = getClient();
|
|
743
|
-
await client.issues.deleteAttachment({ attachmentId: opts.
|
|
744
|
-
output({ deleted: true, attachmentId: opts.
|
|
910
|
+
await client.issues.deleteAttachment({ attachmentId: String(opts.attachmentId) });
|
|
911
|
+
output({ deleted: true, issueKey: key, attachmentId: opts.attachmentId });
|
|
745
912
|
});
|
|
746
913
|
}
|
|
747
914
|
|
|
748
915
|
// src/commands/issue/attachment/download-all.ts
|
|
749
|
-
import { mkdirSync } from "fs";
|
|
750
|
-
import { join } from "path";
|
|
916
|
+
import { mkdirSync as mkdirSync2 } from "fs";
|
|
917
|
+
import { join as join3 } from "path";
|
|
751
918
|
function downloadAll(parent) {
|
|
752
|
-
parent.command("download-all <key>").description("Download all attachments from an issue").requiredOption("--output <dir>", "Local directory to save attachments into")
|
|
919
|
+
const cmd = parent.command("download-all <key>").description("Download all attachments from an issue").requiredOption("--output <dir>", "Local directory to save attachments into");
|
|
920
|
+
examples(cmd, ["PROJ-123 --output ./downloads"]);
|
|
921
|
+
cmd.action(async (key, opts) => {
|
|
753
922
|
const client = getClient();
|
|
754
|
-
|
|
923
|
+
mkdirSync2(opts.output, { recursive: true });
|
|
755
924
|
const issue = await client.issues.get({
|
|
756
925
|
issueKeyOrId: key,
|
|
757
926
|
fields: ["attachment"]
|
|
@@ -768,7 +937,7 @@ function downloadAll(parent) {
|
|
|
768
937
|
failed.push({ filename: att.filename, error: "No content URL" });
|
|
769
938
|
continue;
|
|
770
939
|
}
|
|
771
|
-
const destPath =
|
|
940
|
+
const destPath = join3(opts.output, att.filename);
|
|
772
941
|
try {
|
|
773
942
|
await client.issues.downloadAttachment({ url: att.content, destinationPath: destPath });
|
|
774
943
|
results.push({ filename: att.filename, size: att.size, path: destPath });
|
|
@@ -788,11 +957,16 @@ function downloadAll(parent) {
|
|
|
788
957
|
|
|
789
958
|
// src/commands/issue/attachment/download.ts
|
|
790
959
|
function download(parent) {
|
|
791
|
-
parent.command("download <key>").description("Download a single attachment by ID")
|
|
960
|
+
const cmd = parent.command("download <key>").description("Download a single attachment by ID");
|
|
961
|
+
subEntityOption(cmd, "attachment", { mandatory: true });
|
|
962
|
+
cmd.requiredOption("--output <path>", "Local file path to save the attachment");
|
|
963
|
+
examples(cmd, ["PROJ-123 --attachment-id 12345 --output ./report.pdf"]);
|
|
964
|
+
cmd.action(async (key, opts) => {
|
|
792
965
|
const client = getClient();
|
|
793
|
-
const
|
|
966
|
+
const attachmentId = String(opts.attachmentId);
|
|
967
|
+
const attachment = await client.issues.getAttachment({ attachmentId });
|
|
794
968
|
if (!attachment.content) {
|
|
795
|
-
throw new Error(`Attachment ${
|
|
969
|
+
throw new Error(`Attachment ${attachmentId} has no content URL`);
|
|
796
970
|
}
|
|
797
971
|
await client.issues.downloadAttachment({
|
|
798
972
|
url: attachment.content,
|
|
@@ -810,7 +984,9 @@ function download(parent) {
|
|
|
810
984
|
|
|
811
985
|
// src/commands/issue/attachment/list.ts
|
|
812
986
|
function list3(parent) {
|
|
813
|
-
parent.command("list <key>").description("List attachments on an issue")
|
|
987
|
+
const cmd = parent.command("list <key>").description("List attachments on an issue");
|
|
988
|
+
examples(cmd, ["PROJ-123"]);
|
|
989
|
+
cmd.action(async (key) => {
|
|
814
990
|
const client = getClient();
|
|
815
991
|
const issue = await client.issues.get({
|
|
816
992
|
issueKeyOrId: key,
|
|
@@ -833,16 +1009,15 @@ function list3(parent) {
|
|
|
833
1009
|
|
|
834
1010
|
// src/commands/issue/attachment/upload.ts
|
|
835
1011
|
function upload(parent) {
|
|
836
|
-
parent.command("upload <key>").description("Upload attachments to an issue").requiredOption("--files <paths>", "Comma-separated file paths to upload")
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
).action(async (key, opts) => {
|
|
1012
|
+
const cmd = parent.command("upload <key>").description("Upload attachments to an issue").requiredOption("--files <paths>", "Comma-separated file paths to upload");
|
|
1013
|
+
examples(cmd, ["PROJ-123 --files ./report.pdf", "PROJ-123 --files ./a.txt,./b.png"]);
|
|
1014
|
+
cmd.action(async (key, opts) => {
|
|
840
1015
|
const client = getClient();
|
|
841
1016
|
const filePaths = opts.files.split(",").map((f) => f.trim());
|
|
842
1017
|
const results = [];
|
|
843
1018
|
for (const filePath of filePaths) {
|
|
844
|
-
const
|
|
845
|
-
results.push(...
|
|
1019
|
+
const attachments = await client.issues.addAttachment({ issueKeyOrId: key, filePath });
|
|
1020
|
+
results.push(...attachments);
|
|
846
1021
|
}
|
|
847
1022
|
output({
|
|
848
1023
|
issueKey: key,
|
|
@@ -860,17 +1035,14 @@ function upload(parent) {
|
|
|
860
1035
|
|
|
861
1036
|
// src/commands/issue/attachment/index.ts
|
|
862
1037
|
function registerAttachmentCommands(parent) {
|
|
863
|
-
const attachment = parent.command("attachment").description("Attachment operations")
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
$ jiradc issue attachment delete --id 12345
|
|
872
|
-
`
|
|
873
|
-
);
|
|
1038
|
+
const attachment = parent.command("attachment").description("Attachment operations");
|
|
1039
|
+
examples(attachment, [
|
|
1040
|
+
"list PROJ-123",
|
|
1041
|
+
"upload PROJ-123 --files ./report.pdf",
|
|
1042
|
+
"download PROJ-123 --attachment-id 12345 --output ./report.pdf",
|
|
1043
|
+
"download-all PROJ-123 --output ./downloads",
|
|
1044
|
+
"delete PROJ-123 --attachment-id 12345"
|
|
1045
|
+
]);
|
|
874
1046
|
upload(attachment);
|
|
875
1047
|
list3(attachment);
|
|
876
1048
|
download(attachment);
|
|
@@ -878,53 +1050,11 @@ Examples:
|
|
|
878
1050
|
deleteAttachment(attachment);
|
|
879
1051
|
}
|
|
880
1052
|
|
|
881
|
-
// src/commands/issue/attachments.ts
|
|
882
|
-
import { mkdirSync as mkdirSync2 } from "fs";
|
|
883
|
-
import { join as join2 } from "path";
|
|
884
|
-
function attachments(parent) {
|
|
885
|
-
parent.command("attachments <key>").description("Download all attachments from an issue").requiredOption("--output <dir>", "Local directory to save attachments into").addHelpText("after", "\nExamples:\n jiradc issue attachments PROJ-123 --output ./downloads").action(async (key, opts) => {
|
|
886
|
-
const client = getClient();
|
|
887
|
-
mkdirSync2(opts.output, { recursive: true });
|
|
888
|
-
const issue = await client.issues.get({
|
|
889
|
-
issueKeyOrId: key,
|
|
890
|
-
fields: ["attachment"]
|
|
891
|
-
});
|
|
892
|
-
const atts = issue.fields.attachment ?? [];
|
|
893
|
-
if (atts.length === 0) {
|
|
894
|
-
output({ issueKey: key, downloaded: 0, files: [] });
|
|
895
|
-
return;
|
|
896
|
-
}
|
|
897
|
-
const results = [];
|
|
898
|
-
const failed = [];
|
|
899
|
-
for (const att of atts) {
|
|
900
|
-
if (!att.content) {
|
|
901
|
-
failed.push({ filename: att.filename, error: "No content URL" });
|
|
902
|
-
continue;
|
|
903
|
-
}
|
|
904
|
-
const destPath = join2(opts.output, att.filename);
|
|
905
|
-
try {
|
|
906
|
-
await client.issues.downloadAttachment({ url: att.content, destinationPath: destPath });
|
|
907
|
-
results.push({ filename: att.filename, size: att.size, path: destPath });
|
|
908
|
-
} catch (err) {
|
|
909
|
-
failed.push({ filename: att.filename, error: String(err) });
|
|
910
|
-
}
|
|
911
|
-
}
|
|
912
|
-
output({
|
|
913
|
-
issueKey: key,
|
|
914
|
-
downloaded: results.length,
|
|
915
|
-
total: atts.length,
|
|
916
|
-
files: results,
|
|
917
|
-
...failed.length > 0 && { failed }
|
|
918
|
-
});
|
|
919
|
-
});
|
|
920
|
-
}
|
|
921
|
-
|
|
922
1053
|
// src/commands/issue/batch-changelog.ts
|
|
923
1054
|
function batchChangelog(parent) {
|
|
924
|
-
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)
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
).action(async (keys, opts) => {
|
|
1055
|
+
const cmd = 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);
|
|
1056
|
+
examples(cmd, ["PROJ-1,PROJ-2,PROJ-3", "PROJ-123,PROJ-124 --limit 10"]);
|
|
1057
|
+
cmd.action(async (keys, opts) => {
|
|
928
1058
|
const client = getClient();
|
|
929
1059
|
const keyList = keys.split(",").map((k) => k.trim());
|
|
930
1060
|
const entries = await Promise.all(
|
|
@@ -943,12 +1073,11 @@ function batchChangelog(parent) {
|
|
|
943
1073
|
|
|
944
1074
|
// src/commands/issue/batch-create.ts
|
|
945
1075
|
function batchCreate(parent) {
|
|
946
|
-
parent.command("batch-create").description("Create multiple issues from a JSON array").requiredOption("--issues <json>", "JSON array of issue objects")
|
|
947
|
-
|
|
948
|
-
`
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
).action(async (opts) => {
|
|
1076
|
+
const cmd = parent.command("batch-create").description("Create multiple issues from a JSON array").requiredOption("--issues <json>", "JSON array of issue objects");
|
|
1077
|
+
examples(cmd, [
|
|
1078
|
+
`--issues '[{"projectKeyOrId":"PROJ","issueTypeName":"Task","summary":"Task 1"},{"projectKeyOrId":"PROJ","issueTypeName":"Task","summary":"Task 2"}]'`
|
|
1079
|
+
]);
|
|
1080
|
+
cmd.action(async (opts) => {
|
|
952
1081
|
const client = getClient();
|
|
953
1082
|
const parsed = JSON.parse(opts.issues);
|
|
954
1083
|
const results = [];
|
|
@@ -966,10 +1095,9 @@ Examples:
|
|
|
966
1095
|
|
|
967
1096
|
// src/commands/issue/changelog.ts
|
|
968
1097
|
function changelog(parent) {
|
|
969
|
-
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)
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
).action(async (key, opts) => {
|
|
1098
|
+
const cmd = 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);
|
|
1099
|
+
examples(cmd, ["PROJ-123", "PROJ-123 --limit 10"]);
|
|
1100
|
+
cmd.action(async (key, opts) => {
|
|
973
1101
|
const client = getClient();
|
|
974
1102
|
const result = await client.issues.getChangelog({ issueKeyOrId: key, maxResults: opts.limit });
|
|
975
1103
|
output(result);
|
|
@@ -979,7 +1107,7 @@ function changelog(parent) {
|
|
|
979
1107
|
// src/commands/issue/clone.ts
|
|
980
1108
|
import { unlink } from "fs/promises";
|
|
981
1109
|
import { tmpdir } from "os";
|
|
982
|
-
import { join as
|
|
1110
|
+
import { join as join4 } from "path";
|
|
983
1111
|
var CLONE_FIELDS = [
|
|
984
1112
|
"summary",
|
|
985
1113
|
"description",
|
|
@@ -995,14 +1123,13 @@ var CLONE_FIELDS = [
|
|
|
995
1123
|
"issuelinks"
|
|
996
1124
|
];
|
|
997
1125
|
function clone(parent) {
|
|
998
|
-
parent.command("clone <key>").description("Clone an issue (create a duplicate with the same fields)").option("--summary <text>", 'Override the summary (default: "CLONE - <original>")').option("--project <key>", "Create in a different project").option("--assignee <username>", "Override assignee").option("--include-attachments", "Copy attachments to the cloned issue").option("--include-links", "Copy issue links to the cloned issue")
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
).action(
|
|
1126
|
+
const cmd = parent.command("clone <key>").description("Clone an issue (create a duplicate with the same fields)").option("--summary <text>", 'Override the summary (default: "CLONE - <original>")').option("--project <key>", "Create in a different project").option("--assignee <username>", "Override assignee").option("--include-attachments", "Copy attachments to the cloned issue").option("--include-links", "Copy issue links to the cloned issue");
|
|
1127
|
+
examples(cmd, [
|
|
1128
|
+
"PROJ-123",
|
|
1129
|
+
'PROJ-123 --summary "Cloned: new title"',
|
|
1130
|
+
"PROJ-123 --include-attachments --include-links"
|
|
1131
|
+
]);
|
|
1132
|
+
cmd.action(
|
|
1006
1133
|
async (key, opts) => {
|
|
1007
1134
|
const client = getClient();
|
|
1008
1135
|
const source = await client.issues.get({ issueKeyOrId: key, fields: CLONE_FIELDS });
|
|
@@ -1030,7 +1157,7 @@ Examples:
|
|
|
1030
1157
|
const tmpFiles = [];
|
|
1031
1158
|
const copied = await Promise.all(
|
|
1032
1159
|
f.attachment.map(async (att) => {
|
|
1033
|
-
const tmpPath =
|
|
1160
|
+
const tmpPath = join4(tmpdir(), `jiradc-clone-${Date.now()}-${att.filename}`);
|
|
1034
1161
|
tmpFiles.push(tmpPath);
|
|
1035
1162
|
await client.issues.downloadAttachment({ url: att.content, destinationPath: tmpPath });
|
|
1036
1163
|
await client.issues.addAttachment({ issueKeyOrId: newKey, filePath: tmpPath });
|
|
@@ -1071,9 +1198,12 @@ Examples:
|
|
|
1071
1198
|
);
|
|
1072
1199
|
}
|
|
1073
1200
|
|
|
1074
|
-
// src/commands/issue/comment/
|
|
1075
|
-
function
|
|
1076
|
-
parent.command("
|
|
1201
|
+
// src/commands/issue/comment/create.ts
|
|
1202
|
+
function create2(parent) {
|
|
1203
|
+
const cmd = parent.command("create <key>").description("Add a comment to an issue");
|
|
1204
|
+
bodyOption(cmd, { mandatory: true });
|
|
1205
|
+
examples(cmd, ['PROJ-123 --body "Fixed in latest build"']);
|
|
1206
|
+
cmd.action(async (key, opts) => {
|
|
1077
1207
|
const client = getClient();
|
|
1078
1208
|
const result = await client.issues.addComment({ issueKeyOrId: key, body: opts.body });
|
|
1079
1209
|
output(transformComment(result));
|
|
@@ -1082,49 +1212,55 @@ function add(parent) {
|
|
|
1082
1212
|
|
|
1083
1213
|
// src/commands/issue/comment/delete.ts
|
|
1084
1214
|
function deleteComment(parent) {
|
|
1085
|
-
parent.command("delete <key>").description("Delete a comment from an issue")
|
|
1215
|
+
const cmd = parent.command("delete <key>").description("Delete a comment from an issue");
|
|
1216
|
+
subEntityOption(cmd, "comment", { mandatory: true });
|
|
1217
|
+
examples(cmd, ["PROJ-123 --comment-id 12345"]);
|
|
1218
|
+
cmd.action(async (key, opts) => {
|
|
1086
1219
|
const client = getClient();
|
|
1087
|
-
await client.issues.deleteComment({ issueKeyOrId: key, commentId: opts.
|
|
1088
|
-
output({ deleted: true, issueKey: key, commentId: opts.
|
|
1220
|
+
await client.issues.deleteComment({ issueKeyOrId: key, commentId: String(opts.commentId) });
|
|
1221
|
+
output({ deleted: true, issueKey: key, commentId: opts.commentId });
|
|
1089
1222
|
});
|
|
1090
1223
|
}
|
|
1091
1224
|
|
|
1092
|
-
// src/commands/issue/comment/
|
|
1093
|
-
function
|
|
1094
|
-
parent.command("
|
|
1225
|
+
// src/commands/issue/comment/update.ts
|
|
1226
|
+
function update2(parent) {
|
|
1227
|
+
const cmd = parent.command("update <key>").description("Update an existing comment");
|
|
1228
|
+
subEntityOption(cmd, "comment", { mandatory: true });
|
|
1229
|
+
bodyOption(cmd, { mandatory: true });
|
|
1230
|
+
examples(cmd, ['PROJ-123 --comment-id 12345 --body "Updated comment text"']);
|
|
1231
|
+
cmd.action(async (key, opts) => {
|
|
1095
1232
|
const client = getClient();
|
|
1096
|
-
const result = await client.issues.editComment({
|
|
1233
|
+
const result = await client.issues.editComment({
|
|
1234
|
+
issueKeyOrId: key,
|
|
1235
|
+
commentId: String(opts.commentId),
|
|
1236
|
+
body: opts.body
|
|
1237
|
+
});
|
|
1097
1238
|
output(transformComment(result));
|
|
1098
1239
|
});
|
|
1099
1240
|
}
|
|
1100
1241
|
|
|
1101
1242
|
// src/commands/issue/comment/index.ts
|
|
1102
1243
|
function registerCommentCommands(parent) {
|
|
1103
|
-
const comment = parent.command("comment").description("Comment operations")
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
);
|
|
1112
|
-
add(comment);
|
|
1113
|
-
edit(comment);
|
|
1244
|
+
const comment = parent.command("comment").description("Comment operations");
|
|
1245
|
+
examples(comment, [
|
|
1246
|
+
'create PROJ-123 --body "Fixed in latest build"',
|
|
1247
|
+
'update PROJ-123 --comment-id 12345 --body "Updated comment text"',
|
|
1248
|
+
"delete PROJ-123 --comment-id 12345"
|
|
1249
|
+
]);
|
|
1250
|
+
create2(comment);
|
|
1251
|
+
update2(comment);
|
|
1114
1252
|
deleteComment(comment);
|
|
1115
1253
|
}
|
|
1116
1254
|
|
|
1117
1255
|
// src/commands/issue/create.ts
|
|
1118
|
-
function
|
|
1119
|
-
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"}')`)
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
jiradc issue create --project PROJ --type Bug --summary "Urgent" --labels urgent,production --components Backend`
|
|
1127
|
-
).action(
|
|
1256
|
+
function create3(parent) {
|
|
1257
|
+
const cmd = 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"}')`);
|
|
1258
|
+
examples(cmd, [
|
|
1259
|
+
'--project PROJ --type Task --summary "Fix login bug"',
|
|
1260
|
+
'--project PROJ --type Story --summary "New feature" --assignee jsmith --priority High',
|
|
1261
|
+
'--project PROJ --type Bug --summary "Urgent" --labels urgent,production --components Backend'
|
|
1262
|
+
]);
|
|
1263
|
+
cmd.action(
|
|
1128
1264
|
async (opts) => {
|
|
1129
1265
|
const client = getClient();
|
|
1130
1266
|
const result = await client.issues.create({
|
|
@@ -1149,10 +1285,9 @@ Examples:
|
|
|
1149
1285
|
|
|
1150
1286
|
// src/commands/issue/delete.ts
|
|
1151
1287
|
function deleteIssue(parent) {
|
|
1152
|
-
parent.command("delete <key>").description("Delete an issue").option("--delete-subtasks", "Also delete subtasks (default: false)")
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
).action(async (key, opts) => {
|
|
1288
|
+
const cmd = parent.command("delete <key>").description("Delete an issue").option("--delete-subtasks", "Also delete subtasks (default: false)");
|
|
1289
|
+
examples(cmd, ["PROJ-123", "PROJ-123 --delete-subtasks"]);
|
|
1290
|
+
cmd.action(async (key, opts) => {
|
|
1156
1291
|
const client = getClient();
|
|
1157
1292
|
await client.issues.delete({ issueKeyOrId: key, deleteSubtasks: opts.deleteSubtasks });
|
|
1158
1293
|
output({ deleted: true, issueKey: key });
|
|
@@ -1161,10 +1296,9 @@ function deleteIssue(parent) {
|
|
|
1161
1296
|
|
|
1162
1297
|
// src/commands/issue/dev-status.ts
|
|
1163
1298
|
function devStatus(parent) {
|
|
1164
|
-
parent.command("dev-status <key>").description("Get development status (PRs, commits, branches, builds) for an issue").option("--detail", "Include PR URLs, commit IDs, and other details")
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
).action(async (key, opts) => {
|
|
1299
|
+
const cmd = parent.command("dev-status <key>").description("Get development status (PRs, commits, branches, builds) for an issue").option("--detail", "Include PR URLs, commit IDs, and other details");
|
|
1300
|
+
examples(cmd, ["PROJ-123", "PROJ-123 --detail"]);
|
|
1301
|
+
cmd.action(async (key, opts) => {
|
|
1168
1302
|
const client = getClient();
|
|
1169
1303
|
const issue = await client.issues.get({ issueKeyOrId: key, fields: ["summary"] });
|
|
1170
1304
|
const issueId = issue.id;
|
|
@@ -1256,10 +1390,14 @@ var DEFAULT_FIELDS = [
|
|
|
1256
1390
|
|
|
1257
1391
|
// src/commands/issue/get.ts
|
|
1258
1392
|
function get2(parent) {
|
|
1259
|
-
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")')
|
|
1260
|
-
|
|
1261
|
-
"
|
|
1262
|
-
|
|
1393
|
+
const cmd = 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")');
|
|
1394
|
+
examples(cmd, [
|
|
1395
|
+
"PROJ-123",
|
|
1396
|
+
"PROJ-123 --fields summary,status,assignee",
|
|
1397
|
+
"PROJ-123 --all-fields",
|
|
1398
|
+
"PROJ-123 --expand changelog,transitions"
|
|
1399
|
+
]);
|
|
1400
|
+
cmd.action(async (key, opts) => {
|
|
1263
1401
|
const client = getClient();
|
|
1264
1402
|
const fields = opts.allFields ? void 0 : opts.fields?.split(",").map((f) => f.trim()) ?? DEFAULT_FIELDS;
|
|
1265
1403
|
const result = await client.issues.get({
|
|
@@ -1273,13 +1411,9 @@ function get2(parent) {
|
|
|
1273
1411
|
|
|
1274
1412
|
// src/commands/issue/link-epic.ts
|
|
1275
1413
|
function linkEpic(parent) {
|
|
1276
|
-
parent.command("link-epic <keys...>").description("Link one or more issues to an epic").requiredOption("--epic <epicKey>", "Epic issue key")
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
Examples:
|
|
1280
|
-
jiradc issue link-epic PROJ-456 --epic PROJ-123
|
|
1281
|
-
jiradc issue link-epic PROJ-456 PROJ-457 PROJ-458 --epic PROJ-123`
|
|
1282
|
-
).action(async (keys, opts) => {
|
|
1414
|
+
const cmd = parent.command("link-epic <keys...>").description("Link one or more issues to an epic").requiredOption("--epic <epicKey>", "Epic issue key");
|
|
1415
|
+
examples(cmd, ["PROJ-456 --epic PROJ-123", "PROJ-456 PROJ-457 PROJ-458 --epic PROJ-123"]);
|
|
1416
|
+
cmd.action(async (keys, opts) => {
|
|
1283
1417
|
const client = getClient();
|
|
1284
1418
|
const results = await Promise.allSettled(
|
|
1285
1419
|
keys.map(
|
|
@@ -1309,7 +1443,9 @@ Examples:
|
|
|
1309
1443
|
|
|
1310
1444
|
// src/commands/issue/link-types.ts
|
|
1311
1445
|
function linkTypes(parent) {
|
|
1312
|
-
parent.command("link-types").description("List all issue link types")
|
|
1446
|
+
const cmd = parent.command("link-types").description("List all issue link types");
|
|
1447
|
+
examples(cmd, [""]);
|
|
1448
|
+
cmd.action(async () => {
|
|
1313
1449
|
const client = getClient();
|
|
1314
1450
|
const result = await client.links.getTypes();
|
|
1315
1451
|
output(result.map(transformIssueLinkType));
|
|
@@ -1318,10 +1454,12 @@ function linkTypes(parent) {
|
|
|
1318
1454
|
|
|
1319
1455
|
// src/commands/issue/link.ts
|
|
1320
1456
|
function link(parent) {
|
|
1321
|
-
parent.command("link").description("Link two issues together").requiredOption("--type <name>", "Link type name (e.g., 'Blocks', 'Duplicate', 'Relates')").requiredOption("--from <key>", 'Source issue key (e.g., the issue that "blocks")').requiredOption("--to <key>", 'Target issue key (e.g., the issue that "is blocked by")').option("--comment <text>", "Optional comment")
|
|
1322
|
-
|
|
1323
|
-
"
|
|
1324
|
-
|
|
1457
|
+
const cmd = parent.command("link").description("Link two issues together").requiredOption("--type <name>", "Link type name (e.g., 'Blocks', 'Duplicate', 'Relates')").requiredOption("--from <key>", 'Source issue key (e.g., the issue that "blocks")').requiredOption("--to <key>", 'Target issue key (e.g., the issue that "is blocked by")').option("--comment <text>", "Optional comment");
|
|
1458
|
+
examples(cmd, [
|
|
1459
|
+
"--type Relates --from AI-154 --to AI-149",
|
|
1460
|
+
["--type Blocks --from PROJ-456 --to PROJ-123", "PROJ-456 blocks PROJ-123"]
|
|
1461
|
+
]);
|
|
1462
|
+
cmd.action(async (opts) => {
|
|
1325
1463
|
const client = getClient();
|
|
1326
1464
|
await client.links.create({
|
|
1327
1465
|
typeName: opts.type,
|
|
@@ -1340,10 +1478,13 @@ function link(parent) {
|
|
|
1340
1478
|
|
|
1341
1479
|
// src/commands/issue/search.ts
|
|
1342
1480
|
function search2(parent) {
|
|
1343
|
-
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")
|
|
1344
|
-
|
|
1345
|
-
'
|
|
1346
|
-
|
|
1481
|
+
const cmd = 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");
|
|
1482
|
+
examples(cmd, [
|
|
1483
|
+
'"project = PROJ AND status = Open"',
|
|
1484
|
+
'"assignee = currentUser()" --limit 10 --fields summary,status',
|
|
1485
|
+
'"project = PROJ" --start 50 --limit 50'
|
|
1486
|
+
]);
|
|
1487
|
+
cmd.action(async (jql, opts) => {
|
|
1347
1488
|
const client = getClient();
|
|
1348
1489
|
const fields = opts.allFields ? void 0 : opts.fields?.split(",").map((f) => f.trim()) ?? DEFAULT_FIELDS;
|
|
1349
1490
|
const result = await client.issues.search({
|
|
@@ -1358,14 +1499,13 @@ function search2(parent) {
|
|
|
1358
1499
|
|
|
1359
1500
|
// src/commands/issue/transition.ts
|
|
1360
1501
|
function transition(parent) {
|
|
1361
|
-
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")
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
).action(async (key, opts) => {
|
|
1502
|
+
const cmd = 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");
|
|
1503
|
+
examples(cmd, [
|
|
1504
|
+
"PROJ-123 --to 31",
|
|
1505
|
+
'PROJ-123 --to "In Review"',
|
|
1506
|
+
'PROJ-123 --to Done --comment "Verified in staging"'
|
|
1507
|
+
]);
|
|
1508
|
+
cmd.action(async (key, opts) => {
|
|
1369
1509
|
const client = getClient();
|
|
1370
1510
|
let transitionId;
|
|
1371
1511
|
if (/^\d+$/.test(opts.to)) {
|
|
@@ -1395,7 +1535,9 @@ Examples:
|
|
|
1395
1535
|
|
|
1396
1536
|
// src/commands/issue/transitions.ts
|
|
1397
1537
|
function transitions(parent) {
|
|
1398
|
-
parent.command("transitions <key>").description("Get available transitions for an issue")
|
|
1538
|
+
const cmd = parent.command("transitions <key>").description("Get available transitions for an issue");
|
|
1539
|
+
examples(cmd, ["PROJ-123"]);
|
|
1540
|
+
cmd.action(async (key) => {
|
|
1399
1541
|
const client = getClient();
|
|
1400
1542
|
const result = await client.issues.getTransitions({
|
|
1401
1543
|
issueKeyOrId: key,
|
|
@@ -1407,7 +1549,9 @@ function transitions(parent) {
|
|
|
1407
1549
|
|
|
1408
1550
|
// src/commands/issue/unlink.ts
|
|
1409
1551
|
function unlink2(parent) {
|
|
1410
|
-
parent.command("unlink <id>").description("Remove a link between two issues")
|
|
1552
|
+
const cmd = parent.command("unlink <id>").description("Remove a link between two issues");
|
|
1553
|
+
examples(cmd, ["12345"]);
|
|
1554
|
+
cmd.action(async (id) => {
|
|
1411
1555
|
const client = getClient();
|
|
1412
1556
|
await client.links.remove({ linkId: id });
|
|
1413
1557
|
output({ removed: true, linkId: id });
|
|
@@ -1449,19 +1593,17 @@ function buildSetValue(parsed, wrap) {
|
|
|
1449
1593
|
if (parsed.mode !== "set") return void 0;
|
|
1450
1594
|
return parsed.values.map(wrap);
|
|
1451
1595
|
}
|
|
1452
|
-
function
|
|
1453
|
-
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")')
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
jiradc issue update PROJ-123 --attachments /path/to/file.pdf,/path/to/image.png`
|
|
1464
|
-
).action(async (key, opts) => {
|
|
1596
|
+
function update3(parent) {
|
|
1597
|
+
const cmd = 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")');
|
|
1598
|
+
examples(cmd, [
|
|
1599
|
+
'PROJ-123 --summary "New title"',
|
|
1600
|
+
"PROJ-123 --priority High --assignee me",
|
|
1601
|
+
"PROJ-123 --labels backend,urgent",
|
|
1602
|
+
"PROJ-123 --labels +urgent,-backend",
|
|
1603
|
+
"PROJ-123 --components +Frontend",
|
|
1604
|
+
"PROJ-123 --attachments /path/to/file.pdf,/path/to/image.png"
|
|
1605
|
+
]);
|
|
1606
|
+
cmd.action(async (key, opts) => {
|
|
1465
1607
|
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;
|
|
1466
1608
|
if (!opts.fields && !opts.attachments && !hasShortcut) {
|
|
1467
1609
|
throw new Error(
|
|
@@ -1530,12 +1672,15 @@ Examples:
|
|
|
1530
1672
|
});
|
|
1531
1673
|
}
|
|
1532
1674
|
|
|
1533
|
-
// src/commands/issue/worklog/
|
|
1534
|
-
function
|
|
1535
|
-
parent.command("
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1675
|
+
// src/commands/issue/worklog/create.ts
|
|
1676
|
+
function create4(parent) {
|
|
1677
|
+
const cmd = parent.command("create <key>").description("Log time spent on an issue").requiredOption("--time <timeSpent>", "Time spent (e.g., '2h', '30m', '1d 4h')").option("--comment <text>", "Worklog comment").option("--started <datetime>", "Start time in ISO 8601 format");
|
|
1678
|
+
examples(cmd, [
|
|
1679
|
+
"PROJ-123 --time 2h",
|
|
1680
|
+
'PROJ-123 --time "1d 4h" --comment "Backend implementation"',
|
|
1681
|
+
'PROJ-123 --time 3h --started "2026-03-19T09:00:00.000+0000"'
|
|
1682
|
+
]);
|
|
1683
|
+
cmd.action(async (key, opts) => {
|
|
1539
1684
|
const client = getClient();
|
|
1540
1685
|
const result = await client.issues.addWorklog({
|
|
1541
1686
|
issueKeyOrId: key,
|
|
@@ -1551,37 +1696,65 @@ function add2(parent) {
|
|
|
1551
1696
|
import { Option as Option4 } from "commander";
|
|
1552
1697
|
var ADJUST_ESTIMATE = ["new", "leave", "manual", "auto"];
|
|
1553
1698
|
function deleteWorklog(parent) {
|
|
1554
|
-
parent.command("delete <key>").description("Delete a worklog entry")
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1699
|
+
const cmd = parent.command("delete <key>").description("Delete a worklog entry");
|
|
1700
|
+
subEntityOption(cmd, "worklog", { mandatory: true });
|
|
1701
|
+
cmd.addOption(new Option4("--adjust-estimate <mode>", "How to adjust the remaining estimate").choices(ADJUST_ESTIMATE)).option("--new-estimate <estimate>", 'New remaining estimate; required when --adjust-estimate is "new"').option(
|
|
1702
|
+
"--increase-by <amount>",
|
|
1703
|
+
'Amount to increase the estimate by; required when --adjust-estimate is "manual"'
|
|
1704
|
+
);
|
|
1705
|
+
examples(cmd, [
|
|
1706
|
+
"PROJ-123 --worklog-id 12345",
|
|
1707
|
+
"PROJ-123 --worklog-id 12345 --adjust-estimate leave",
|
|
1708
|
+
"PROJ-123 --worklog-id 12345 --adjust-estimate new --new-estimate 2h"
|
|
1709
|
+
]);
|
|
1710
|
+
cmd.action(
|
|
1558
1711
|
async (key, opts) => {
|
|
1559
1712
|
const client = getClient();
|
|
1560
1713
|
await client.issues.deleteWorklog({
|
|
1561
1714
|
issueKeyOrId: key,
|
|
1562
|
-
worklogId: opts.
|
|
1715
|
+
worklogId: String(opts.worklogId),
|
|
1563
1716
|
adjustEstimate: opts.adjustEstimate,
|
|
1564
1717
|
newEstimate: opts.newEstimate,
|
|
1565
1718
|
increaseBy: opts.increaseBy
|
|
1566
1719
|
});
|
|
1567
|
-
output({ deleted: true, issueKey: key, worklogId: opts.
|
|
1720
|
+
output({ deleted: true, issueKey: key, worklogId: opts.worklogId });
|
|
1568
1721
|
}
|
|
1569
1722
|
);
|
|
1570
1723
|
}
|
|
1571
1724
|
|
|
1572
|
-
// src/commands/issue/worklog/
|
|
1725
|
+
// src/commands/issue/worklog/list.ts
|
|
1726
|
+
function list4(parent) {
|
|
1727
|
+
const cmd = parent.command("list <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);
|
|
1728
|
+
examples(cmd, ["PROJ-123", "PROJ-123 --limit 10", "PROJ-123 --start 10 --limit 5"]);
|
|
1729
|
+
cmd.action(async (key, opts) => {
|
|
1730
|
+
const client = getClient();
|
|
1731
|
+
const result = await client.issues.getWorklogs({
|
|
1732
|
+
issueKeyOrId: key,
|
|
1733
|
+
startAt: opts.start,
|
|
1734
|
+
maxResults: opts.limit
|
|
1735
|
+
});
|
|
1736
|
+
output(transformPaged(result, transformWorklog));
|
|
1737
|
+
});
|
|
1738
|
+
}
|
|
1739
|
+
|
|
1740
|
+
// src/commands/issue/worklog/update.ts
|
|
1573
1741
|
import { Option as Option5 } from "commander";
|
|
1574
1742
|
var ADJUST_ESTIMATE2 = ["new", "leave", "auto"];
|
|
1575
|
-
function
|
|
1576
|
-
parent.command("
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1743
|
+
function update4(parent) {
|
|
1744
|
+
const cmd = parent.command("update <key>").description("Update an existing worklog entry");
|
|
1745
|
+
subEntityOption(cmd, "worklog", { mandatory: true });
|
|
1746
|
+
cmd.option("--time <timeSpent>", "Time spent (e.g., '2h', '30m', '1d 4h')").option("--comment <text>", "Worklog comment").option("--started <datetime>", "Start time in ISO 8601 format").addOption(new Option5("--adjust-estimate <mode>", "How to adjust the remaining estimate").choices(ADJUST_ESTIMATE2)).option("--new-estimate <estimate>", 'New remaining estimate; required when --adjust-estimate is "new"');
|
|
1747
|
+
examples(cmd, [
|
|
1748
|
+
'PROJ-123 --worklog-id 12345 --time "1h 30m"',
|
|
1749
|
+
'PROJ-123 --worklog-id 12345 --comment "Revised note"',
|
|
1750
|
+
"PROJ-123 --worklog-id 12345 --time 2h --adjust-estimate new --new-estimate 4h"
|
|
1751
|
+
]);
|
|
1752
|
+
cmd.action(
|
|
1580
1753
|
async (key, opts) => {
|
|
1581
1754
|
const client = getClient();
|
|
1582
1755
|
const result = await client.issues.updateWorklog({
|
|
1583
1756
|
issueKeyOrId: key,
|
|
1584
|
-
worklogId: opts.
|
|
1757
|
+
worklogId: String(opts.worklogId),
|
|
1585
1758
|
timeSpent: opts.time,
|
|
1586
1759
|
comment: opts.comment,
|
|
1587
1760
|
started: opts.started,
|
|
@@ -1593,58 +1766,36 @@ function edit2(parent) {
|
|
|
1593
1766
|
);
|
|
1594
1767
|
}
|
|
1595
1768
|
|
|
1596
|
-
// src/commands/issue/worklog/list.ts
|
|
1597
|
-
function list4(parent) {
|
|
1598
|
-
parent.command("list <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(
|
|
1599
|
-
"after",
|
|
1600
|
-
"\nExamples:\n jiradc issue worklog list PROJ-123\n jiradc issue worklog list PROJ-123 --limit 10\n jiradc issue worklog list PROJ-123 --start 10 --limit 5"
|
|
1601
|
-
).action(async (key, opts) => {
|
|
1602
|
-
const client = getClient();
|
|
1603
|
-
const result = await client.issues.getWorklogs({
|
|
1604
|
-
issueKeyOrId: key,
|
|
1605
|
-
startAt: opts.start,
|
|
1606
|
-
maxResults: opts.limit
|
|
1607
|
-
});
|
|
1608
|
-
output(transformPaged(result, transformWorklog));
|
|
1609
|
-
});
|
|
1610
|
-
}
|
|
1611
|
-
|
|
1612
1769
|
// src/commands/issue/worklog/index.ts
|
|
1613
1770
|
function registerWorklogCommands(parent) {
|
|
1614
|
-
const worklog = parent.command("worklog").description("Worklog operations")
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
`
|
|
1623
|
-
);
|
|
1624
|
-
add2(worklog);
|
|
1771
|
+
const worklog = parent.command("worklog").description("Worklog operations");
|
|
1772
|
+
examples(worklog, [
|
|
1773
|
+
'create PROJ-123 --time 2h --comment "Backend work"',
|
|
1774
|
+
"list PROJ-123 --limit 10",
|
|
1775
|
+
'update PROJ-123 --worklog-id 12345 --time "1h 30m"',
|
|
1776
|
+
"delete PROJ-123 --worklog-id 12345"
|
|
1777
|
+
]);
|
|
1778
|
+
create4(worklog);
|
|
1625
1779
|
list4(worklog);
|
|
1626
|
-
|
|
1780
|
+
update4(worklog);
|
|
1627
1781
|
deleteWorklog(worklog);
|
|
1628
1782
|
}
|
|
1629
1783
|
|
|
1630
1784
|
// src/commands/issue/index.ts
|
|
1631
|
-
function registerIssueCommands(
|
|
1632
|
-
const issue =
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
$ jiradc issue attachment list PROJ-123
|
|
1642
|
-
`
|
|
1643
|
-
);
|
|
1785
|
+
function registerIssueCommands(program) {
|
|
1786
|
+
const issue = program.command("issue").description("Issue operations");
|
|
1787
|
+
examples(issue, [
|
|
1788
|
+
"get PROJ-123",
|
|
1789
|
+
'search "project = PROJ AND status = Open" --limit 10',
|
|
1790
|
+
'create --project PROJ --type Task --summary "Fix the bug" --priority High',
|
|
1791
|
+
`update PROJ-123 --fields '{"summary": "New title"}'`,
|
|
1792
|
+
"transition PROJ-123 --to 11",
|
|
1793
|
+
"attachment list PROJ-123"
|
|
1794
|
+
]);
|
|
1644
1795
|
get2(issue);
|
|
1645
1796
|
search2(issue);
|
|
1646
|
-
|
|
1647
|
-
|
|
1797
|
+
create3(issue);
|
|
1798
|
+
update3(issue);
|
|
1648
1799
|
deleteIssue(issue);
|
|
1649
1800
|
transition(issue);
|
|
1650
1801
|
transitions(issue);
|
|
@@ -1658,7 +1809,6 @@ Examples:
|
|
|
1658
1809
|
linkTypes(issue);
|
|
1659
1810
|
linkEpic(issue);
|
|
1660
1811
|
registerAttachmentCommands(issue);
|
|
1661
|
-
attachments(issue);
|
|
1662
1812
|
batchCreate(issue);
|
|
1663
1813
|
clone(issue);
|
|
1664
1814
|
devStatus(issue);
|
|
@@ -1666,10 +1816,9 @@ Examples:
|
|
|
1666
1816
|
|
|
1667
1817
|
// src/commands/project/list.ts
|
|
1668
1818
|
function list5(parent) {
|
|
1669
|
-
parent.command("list").description("List all projects").option("--expand <expand>", 'Expand options (e.g., "description,lead")').option("--include-archived", "Include archived projects (default: false)")
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
).action(async (opts) => {
|
|
1819
|
+
const cmd = parent.command("list").description("List all projects").option("--expand <expand>", 'Expand options (e.g., "description,lead")').option("--include-archived", "Include archived projects (default: false)");
|
|
1820
|
+
examples(cmd, ["", "--expand description,lead", "--include-archived"]);
|
|
1821
|
+
cmd.action(async (opts) => {
|
|
1673
1822
|
const client = getClient();
|
|
1674
1823
|
const result = await client.projects.getAll({ expand: opts.expand, includeArchived: opts.includeArchived });
|
|
1675
1824
|
output(result.map(transformProject));
|
|
@@ -1678,10 +1827,9 @@ function list5(parent) {
|
|
|
1678
1827
|
|
|
1679
1828
|
// src/commands/project/versions.ts
|
|
1680
1829
|
function versions(parent) {
|
|
1681
|
-
parent.command("versions <key>").description("Get all versions for a project").option("--expand <expand>", "Expand options")
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
).action(async (key, opts) => {
|
|
1830
|
+
const cmd = parent.command("versions <key>").description("Get all versions for a project").option("--expand <expand>", "Expand options");
|
|
1831
|
+
examples(cmd, ["PROJ", "PROJ --expand operations"]);
|
|
1832
|
+
cmd.action(async (key, opts) => {
|
|
1685
1833
|
const client = getClient();
|
|
1686
1834
|
const result = await client.projects.getVersions({ projectKeyOrId: key, expand: opts.expand });
|
|
1687
1835
|
output(result.map(transformVersion));
|
|
@@ -1689,25 +1837,21 @@ function versions(parent) {
|
|
|
1689
1837
|
}
|
|
1690
1838
|
|
|
1691
1839
|
// src/commands/project/index.ts
|
|
1692
|
-
function registerProjectCommands(
|
|
1693
|
-
const project =
|
|
1694
|
-
|
|
1695
|
-
`
|
|
1696
|
-
Examples:
|
|
1697
|
-
$ jiradc project list
|
|
1698
|
-
$ jiradc project versions PROJ
|
|
1699
|
-
`
|
|
1700
|
-
);
|
|
1840
|
+
function registerProjectCommands(program) {
|
|
1841
|
+
const project = program.command("project").description("Project operations");
|
|
1842
|
+
examples(project, ["list", "versions PROJ"]);
|
|
1701
1843
|
list5(project);
|
|
1702
1844
|
versions(project);
|
|
1703
1845
|
}
|
|
1704
1846
|
|
|
1705
1847
|
// src/commands/sprint/create.ts
|
|
1706
|
-
function
|
|
1707
|
-
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")
|
|
1708
|
-
|
|
1709
|
-
'
|
|
1710
|
-
|
|
1848
|
+
function create5(parent) {
|
|
1849
|
+
const cmd = 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");
|
|
1850
|
+
examples(cmd, [
|
|
1851
|
+
'--board 42 --name "Sprint 10"',
|
|
1852
|
+
'--board 42 --name "Sprint 10" --start-date 2026-03-20 --end-date 2026-04-03 --goal "Complete auth module"'
|
|
1853
|
+
]);
|
|
1854
|
+
cmd.action(async (opts) => {
|
|
1711
1855
|
const client = getClient();
|
|
1712
1856
|
const result = await client.agile.createSprint({
|
|
1713
1857
|
name: opts.name,
|
|
@@ -1723,7 +1867,9 @@ function create3(parent) {
|
|
|
1723
1867
|
// src/commands/sprint/delete.ts
|
|
1724
1868
|
import { Argument as Argument2 } from "commander";
|
|
1725
1869
|
function deleteSprint(parent) {
|
|
1726
|
-
parent.command("delete").description("Delete a sprint (returns its issues to the backlog)").addArgument(new Argument2("<id>", "Sprint ID").argParser(positiveInt))
|
|
1870
|
+
const cmd = parent.command("delete").description("Delete a sprint (returns its issues to the backlog)").addArgument(new Argument2("<id>", "Sprint ID").argParser(positiveInt));
|
|
1871
|
+
examples(cmd, ["100"]);
|
|
1872
|
+
cmd.action(async (id) => {
|
|
1727
1873
|
const client = getClient();
|
|
1728
1874
|
await client.agile.deleteSprint({ sprintId: id });
|
|
1729
1875
|
output({ deleted: true, sprintId: id });
|
|
@@ -1733,10 +1879,14 @@ function deleteSprint(parent) {
|
|
|
1733
1879
|
// src/commands/sprint/issues.ts
|
|
1734
1880
|
import { Argument as Argument3 } from "commander";
|
|
1735
1881
|
function issues2(parent) {
|
|
1736
|
-
parent.command("issues").description("Get issues in a sprint").addArgument(new Argument3("<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")
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1882
|
+
const cmd = parent.command("issues").description("Get issues in a sprint").addArgument(new Argument3("<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");
|
|
1883
|
+
examples(cmd, [
|
|
1884
|
+
"100",
|
|
1885
|
+
"100 --limit 20",
|
|
1886
|
+
'100 --jql "status = Done" --fields summary,status',
|
|
1887
|
+
"100 --start 50 --limit 25"
|
|
1888
|
+
]);
|
|
1889
|
+
cmd.action(async (id, opts) => {
|
|
1740
1890
|
const client = getClient();
|
|
1741
1891
|
const result = await client.agile.getSprintIssues({
|
|
1742
1892
|
sprintId: id,
|
|
@@ -1753,10 +1903,9 @@ function issues2(parent) {
|
|
|
1753
1903
|
import { Option as Option6 } from "commander";
|
|
1754
1904
|
var SPRINT_STATES = ["future", "active", "closed"];
|
|
1755
1905
|
function list6(parent) {
|
|
1756
|
-
parent.command("list").description("List sprints for a board").requiredOption("--board <id>", "Board ID", positiveInt).addOption(new Option6("--state <state>", "Filter by sprint state").choices(SPRINT_STATES))
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
).action(async (opts) => {
|
|
1906
|
+
const cmd = parent.command("list").description("List sprints for a board").requiredOption("--board <id>", "Board ID", positiveInt).addOption(new Option6("--state <state>", "Filter by sprint state").choices(SPRINT_STATES));
|
|
1907
|
+
examples(cmd, ["--board 42", "--board 42 --state active"]);
|
|
1908
|
+
cmd.action(async (opts) => {
|
|
1760
1909
|
const client = getClient();
|
|
1761
1910
|
const result = await client.agile.getSprints({
|
|
1762
1911
|
boardId: opts.board,
|
|
@@ -1769,11 +1918,14 @@ function list6(parent) {
|
|
|
1769
1918
|
// src/commands/sprint/update.ts
|
|
1770
1919
|
import { Argument as Argument4, Option as Option7 } from "commander";
|
|
1771
1920
|
var SPRINT_STATES2 = ["future", "active", "closed"];
|
|
1772
|
-
function
|
|
1773
|
-
parent.command("update").description("Update an existing sprint").addArgument(new Argument4("<id>", "Sprint ID").argParser(positiveInt)).option("--name <name>", "New sprint name").addOption(new Option7("--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")
|
|
1774
|
-
|
|
1775
|
-
'
|
|
1776
|
-
|
|
1921
|
+
function update5(parent) {
|
|
1922
|
+
const cmd = parent.command("update").description("Update an existing sprint").addArgument(new Argument4("<id>", "Sprint ID").argParser(positiveInt)).option("--name <name>", "New sprint name").addOption(new Option7("--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");
|
|
1923
|
+
examples(cmd, [
|
|
1924
|
+
'100 --name "Sprint 10 - Extended"',
|
|
1925
|
+
"100 --state active",
|
|
1926
|
+
'100 --end-date 2026-04-10 --goal "Updated goal"'
|
|
1927
|
+
]);
|
|
1928
|
+
cmd.action(
|
|
1777
1929
|
async (id, opts) => {
|
|
1778
1930
|
const client = getClient();
|
|
1779
1931
|
const result = await client.agile.updateSprint({
|
|
@@ -1790,21 +1942,18 @@ function update3(parent) {
|
|
|
1790
1942
|
}
|
|
1791
1943
|
|
|
1792
1944
|
// src/commands/sprint/index.ts
|
|
1793
|
-
function registerSprintCommands(
|
|
1794
|
-
const sprint =
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
$ jiradc sprint create --board 42 --name "Sprint 10" --start-date 2026-04-01 --end-date 2026-04-14
|
|
1802
|
-
`
|
|
1803
|
-
);
|
|
1945
|
+
function registerSprintCommands(program) {
|
|
1946
|
+
const sprint = program.command("sprint").description("Sprint operations");
|
|
1947
|
+
examples(sprint, [
|
|
1948
|
+
"list --board 42",
|
|
1949
|
+
"list --board 42 --state active",
|
|
1950
|
+
"issues 123 --limit 20",
|
|
1951
|
+
'create --board 42 --name "Sprint 10" --start-date 2026-04-01 --end-date 2026-04-14'
|
|
1952
|
+
]);
|
|
1804
1953
|
list6(sprint);
|
|
1805
1954
|
issues2(sprint);
|
|
1806
|
-
|
|
1807
|
-
|
|
1955
|
+
create5(sprint);
|
|
1956
|
+
update5(sprint);
|
|
1808
1957
|
deleteSprint(sprint);
|
|
1809
1958
|
}
|
|
1810
1959
|
|
|
@@ -1834,22 +1983,17 @@ function getTokenClient(options2 = {}) {
|
|
|
1834
1983
|
}
|
|
1835
1984
|
|
|
1836
1985
|
// src/commands/token/create.ts
|
|
1837
|
-
function
|
|
1838
|
-
parent.command("create").description("Create a Personal Access Token. The secret is returned exactly once.").requiredOption("--name <name>", "Token name").option(
|
|
1986
|
+
function create6(parent) {
|
|
1987
|
+
const cmd = parent.command("create").description("Create a Personal Access Token. The secret is returned exactly once.").requiredOption("--name <name>", "Token name").option(
|
|
1839
1988
|
"--expiration-duration <days>",
|
|
1840
1989
|
"Token lifetime in days. Omit for a non-expiring token (admin policy permitting).",
|
|
1841
1990
|
positiveInt
|
|
1842
1991
|
).option("--basic-username <u>", "Override $JIRA_BASIC_USERNAME").option(
|
|
1843
1992
|
"--basic-password <p>",
|
|
1844
1993
|
"Override $JIRA_BASIC_PASSWORD (caution: visible in process table; prefer the env var)"
|
|
1845
|
-
)
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
Examples:
|
|
1849
|
-
$ jiradc token create --name my-pat # non-expiring
|
|
1850
|
-
$ jiradc token create --name svc-token --expiration-duration 365
|
|
1851
|
-
`
|
|
1852
|
-
).action(async (opts) => {
|
|
1994
|
+
);
|
|
1995
|
+
examples(cmd, [["--name my-pat", "non-expiring"], "--name svc-token --expiration-duration 365"]);
|
|
1996
|
+
cmd.action(async (opts) => {
|
|
1853
1997
|
const { client, username, password } = getTokenClient({
|
|
1854
1998
|
basicUsername: opts.basicUsername,
|
|
1855
1999
|
basicPassword: opts.basicPassword
|
|
@@ -1866,10 +2010,12 @@ Examples:
|
|
|
1866
2010
|
|
|
1867
2011
|
// src/commands/token/list.ts
|
|
1868
2012
|
function list7(parent) {
|
|
1869
|
-
parent.command("list").description("List Personal Access Tokens owned by the authenticated user (secrets not included)").option("--basic-username <u>", "Override $JIRA_BASIC_USERNAME").option(
|
|
2013
|
+
const cmd = parent.command("list").description("List Personal Access Tokens owned by the authenticated user (secrets not included)").option("--basic-username <u>", "Override $JIRA_BASIC_USERNAME").option(
|
|
1870
2014
|
"--basic-password <p>",
|
|
1871
2015
|
"Override $JIRA_BASIC_PASSWORD (caution: visible in process table; prefer the env var)"
|
|
1872
|
-
)
|
|
2016
|
+
);
|
|
2017
|
+
examples(cmd, [""]);
|
|
2018
|
+
cmd.action(async (opts) => {
|
|
1873
2019
|
const { client, username, password } = getTokenClient({
|
|
1874
2020
|
basicUsername: opts.basicUsername,
|
|
1875
2021
|
basicPassword: opts.basicPassword
|
|
@@ -1880,42 +2026,40 @@ function list7(parent) {
|
|
|
1880
2026
|
|
|
1881
2027
|
// src/commands/token/revoke.ts
|
|
1882
2028
|
function revoke(parent) {
|
|
1883
|
-
parent.command("revoke").description("Revoke a Personal Access Token by id").
|
|
2029
|
+
const cmd = parent.command("revoke").description("Revoke a Personal Access Token by id").option("--basic-username <u>", "Override $JIRA_BASIC_USERNAME").option(
|
|
1884
2030
|
"--basic-password <p>",
|
|
1885
2031
|
"Override $JIRA_BASIC_PASSWORD (caution: visible in process table; prefer the env var)"
|
|
1886
|
-
)
|
|
2032
|
+
);
|
|
2033
|
+
subjectArg(cmd, "tokenId", { parser: positiveInt, description: "Token id to revoke" });
|
|
2034
|
+
examples(cmd, ["173"]);
|
|
2035
|
+
cmd.action(async (tokenId, opts) => {
|
|
1887
2036
|
const { client, username, password } = getTokenClient({
|
|
1888
2037
|
basicUsername: opts.basicUsername,
|
|
1889
2038
|
basicPassword: opts.basicPassword
|
|
1890
2039
|
});
|
|
1891
|
-
await client.accessTokens.revoke({ username, password, tokenId
|
|
1892
|
-
output({ revoked:
|
|
2040
|
+
await client.accessTokens.revoke({ username, password, tokenId });
|
|
2041
|
+
output({ revoked: tokenId });
|
|
1893
2042
|
});
|
|
1894
2043
|
}
|
|
1895
2044
|
|
|
1896
2045
|
// src/commands/token/index.ts
|
|
1897
|
-
function registerTokenCommands(
|
|
1898
|
-
const token =
|
|
2046
|
+
function registerTokenCommands(program) {
|
|
2047
|
+
const token = program.command("token").description("Personal Access Token management");
|
|
2048
|
+
examples(token, ["list", "create --name my-pat", "revoke 173"]);
|
|
2049
|
+
token.addHelpText(
|
|
1899
2050
|
"after",
|
|
1900
|
-
|
|
1901
|
-
Examples:
|
|
1902
|
-
$ jiradc token list
|
|
1903
|
-
$ jiradc token create --name my-pat
|
|
1904
|
-
$ jiradc token revoke --id 173
|
|
1905
|
-
|
|
1906
|
-
Auth:
|
|
1907
|
-
Requires JIRA_BASIC_USERNAME + JIRA_BASIC_PASSWORD
|
|
1908
|
-
(or --basic-username / --basic-password on any subcommand).
|
|
1909
|
-
`
|
|
2051
|
+
"\nAuth:\n Requires JIRA_BASIC_USERNAME + JIRA_BASIC_PASSWORD\n (or --basic-username / --basic-password on any subcommand)."
|
|
1910
2052
|
);
|
|
1911
|
-
|
|
2053
|
+
create6(token);
|
|
1912
2054
|
list7(token);
|
|
1913
2055
|
revoke(token);
|
|
1914
2056
|
}
|
|
1915
2057
|
|
|
1916
2058
|
// src/commands/user/get.ts
|
|
1917
2059
|
function get3(parent) {
|
|
1918
|
-
parent.command("get <username>").description("Get a user profile by exact username or key").option("--by-key", "Treat the positional argument as a user key instead of a username")
|
|
2060
|
+
const cmd = parent.command("get <username>").description("Get a user profile by exact username or key").option("--by-key", "Treat the positional argument as a user key instead of a username");
|
|
2061
|
+
examples(cmd, ["jsmith", "JIRAUSER10100 --by-key"]);
|
|
2062
|
+
cmd.action(async (identifier, opts) => {
|
|
1919
2063
|
const client = getClient();
|
|
1920
2064
|
const result = await client.users.getUser(opts.byKey ? { key: identifier } : { username: identifier });
|
|
1921
2065
|
output(transformUser(result));
|
|
@@ -1924,7 +2068,9 @@ function get3(parent) {
|
|
|
1924
2068
|
|
|
1925
2069
|
// src/commands/user/me.ts
|
|
1926
2070
|
function me(parent) {
|
|
1927
|
-
parent.command("me").description("Get the authenticated user profile")
|
|
2071
|
+
const cmd = parent.command("me").description("Get the authenticated user profile");
|
|
2072
|
+
examples(cmd, [""]);
|
|
2073
|
+
cmd.action(async () => {
|
|
1928
2074
|
const client = getClient();
|
|
1929
2075
|
const result = await client.users.getMyself();
|
|
1930
2076
|
output(transformUser(result));
|
|
@@ -1933,10 +2079,9 @@ function me(parent) {
|
|
|
1933
2079
|
|
|
1934
2080
|
// src/commands/user/search.ts
|
|
1935
2081
|
function search3(parent) {
|
|
1936
|
-
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)
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
).action(async (query, opts) => {
|
|
2082
|
+
const cmd = 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);
|
|
2083
|
+
examples(cmd, ["Drago", '"John Smith"', "dragomir@first.bet --limit 5"]);
|
|
2084
|
+
cmd.action(async (query, opts) => {
|
|
1940
2085
|
const client = getClient();
|
|
1941
2086
|
const result = await client.users.searchUsers({
|
|
1942
2087
|
username: query,
|
|
@@ -1950,46 +2095,30 @@ function search3(parent) {
|
|
|
1950
2095
|
}
|
|
1951
2096
|
|
|
1952
2097
|
// src/commands/user/index.ts
|
|
1953
|
-
function registerUserCommands(
|
|
1954
|
-
const user =
|
|
1955
|
-
|
|
1956
|
-
`
|
|
1957
|
-
Examples:
|
|
1958
|
-
$ jiradc user me
|
|
1959
|
-
$ jiradc user get jsmith
|
|
1960
|
-
$ jiradc user search Drago
|
|
1961
|
-
`
|
|
1962
|
-
);
|
|
2098
|
+
function registerUserCommands(program) {
|
|
2099
|
+
const user = program.command("user").description("User operations");
|
|
2100
|
+
examples(user, ["me", "get jsmith", "search Drago"]);
|
|
1963
2101
|
me(user);
|
|
1964
2102
|
get3(user);
|
|
1965
2103
|
search3(user);
|
|
1966
2104
|
}
|
|
1967
2105
|
|
|
1968
|
-
// src/
|
|
1969
|
-
function readPackageVersion() {
|
|
1970
|
-
try {
|
|
1971
|
-
const here = dirname(fileURLToPath(import.meta.url));
|
|
1972
|
-
const pkgPath = join4(here, "..", "package.json");
|
|
1973
|
-
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
1974
|
-
return pkg.version ?? "0.0.0";
|
|
1975
|
-
} catch {
|
|
1976
|
-
return "0.0.0";
|
|
1977
|
-
}
|
|
1978
|
-
}
|
|
2106
|
+
// src/program.ts
|
|
1979
2107
|
var DIM = "\x1B[2m";
|
|
1980
2108
|
var RESET = "\x1B[0m";
|
|
1981
|
-
|
|
1982
|
-
program
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
2109
|
+
function buildProgram() {
|
|
2110
|
+
const program = new Command11();
|
|
2111
|
+
program.name("jiradc").description("Jira Data Center CLI").version(readPackageVersion(import.meta.url)).configureHelp({
|
|
2112
|
+
styleTitle: (str) => styleText("bold", str),
|
|
2113
|
+
styleUsage: (str) => styleText("dim", str),
|
|
2114
|
+
styleCommandDescription: (str) => styleText("dim", str),
|
|
2115
|
+
styleOptionDescription: (str) => styleText("dim", str),
|
|
2116
|
+
styleSubcommandDescription: (str) => styleText("dim", str)
|
|
2117
|
+
}).addHelpText("beforeAll", `
|
|
1989
2118
|
${styleText("bold", "jiradc")} ${DIM}\u2014 Jira Data Center CLI${RESET}
|
|
1990
2119
|
`).addHelpText(
|
|
1991
|
-
|
|
1992
|
-
|
|
2120
|
+
"after",
|
|
2121
|
+
`
|
|
1993
2122
|
${styleText("bold", "Environment:")}
|
|
1994
2123
|
JIRA_URL Jira Server base URL ${DIM}(e.g., https://jira.example.com)${RESET}
|
|
1995
2124
|
JIRA_TOKEN Personal Access Token ${DIM}(generate in Jira > Profile > Personal Access Tokens)${RESET}
|
|
@@ -2003,21 +2132,45 @@ ${styleText("bold", "Examples:")}
|
|
|
2003
2132
|
${DIM}$${RESET} jiradc board list --type scrum
|
|
2004
2133
|
${DIM}$${RESET} jiradc sprint list --board 42 --state active
|
|
2005
2134
|
`
|
|
2006
|
-
);
|
|
2007
|
-
program.option("--pretty", "Pretty-print JSON output");
|
|
2008
|
-
program.hook("preAction", (thisCommand) => {
|
|
2009
|
-
|
|
2010
|
-
});
|
|
2011
|
-
registerIssueCommands(program);
|
|
2012
|
-
registerProjectCommands(program);
|
|
2013
|
-
registerComponentCommands(program);
|
|
2014
|
-
registerBoardCommands(program);
|
|
2015
|
-
registerSprintCommands(program);
|
|
2016
|
-
registerFieldCommands(program);
|
|
2017
|
-
registerUserCommands(program);
|
|
2018
|
-
registerTokenCommands(program);
|
|
2019
|
-
|
|
2020
|
-
await program.parseAsync();
|
|
2021
|
-
} catch (err) {
|
|
2022
|
-
handleError(err);
|
|
2135
|
+
);
|
|
2136
|
+
program.option("--pretty", "Pretty-print JSON output");
|
|
2137
|
+
program.hook("preAction", (thisCommand) => {
|
|
2138
|
+
if (thisCommand.optsWithGlobals().pretty) setPretty(true);
|
|
2139
|
+
});
|
|
2140
|
+
registerIssueCommands(program);
|
|
2141
|
+
registerProjectCommands(program);
|
|
2142
|
+
registerComponentCommands(program);
|
|
2143
|
+
registerBoardCommands(program);
|
|
2144
|
+
registerSprintCommands(program);
|
|
2145
|
+
registerFieldCommands(program);
|
|
2146
|
+
registerUserCommands(program);
|
|
2147
|
+
registerTokenCommands(program);
|
|
2148
|
+
return program;
|
|
2023
2149
|
}
|
|
2150
|
+
|
|
2151
|
+
// src/utils/credentials.ts
|
|
2152
|
+
function getCredentialInfo() {
|
|
2153
|
+
const baseUrl = process.env.JIRA_URL;
|
|
2154
|
+
const token = process.env.JIRA_TOKEN;
|
|
2155
|
+
return {
|
|
2156
|
+
environment: {
|
|
2157
|
+
JIRA_URL: {
|
|
2158
|
+
value: baseUrl ?? null,
|
|
2159
|
+
description: "Your Jira Server base URL (e.g., https://jira.example.com)"
|
|
2160
|
+
},
|
|
2161
|
+
JIRA_TOKEN: {
|
|
2162
|
+
value: token ? "<set>" : null,
|
|
2163
|
+
description: "Personal Access Token"
|
|
2164
|
+
}
|
|
2165
|
+
},
|
|
2166
|
+
tokenUrl: `${baseUrl ?? "https://jira.example.com"}/secure/ViewProfile.jspa`
|
|
2167
|
+
};
|
|
2168
|
+
}
|
|
2169
|
+
|
|
2170
|
+
// src/index.ts
|
|
2171
|
+
await runCli(buildProgram(), {
|
|
2172
|
+
credentialInfo: getCredentialInfo,
|
|
2173
|
+
service: "Jira",
|
|
2174
|
+
authRecovery: "Set the JIRA_URL and JIRA_TOKEN environment variables.",
|
|
2175
|
+
networkRecovery: "Verify JIRA_URL is correct, the server is reachable, and you are on the VPN if required."
|
|
2176
|
+
});
|