jiradc-cli 1.0.21 → 2.0.0-g0cec505.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +705 -559
  2. package/package.json +5 -4
package/dist/index.js CHANGED
@@ -1,16 +1,25 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- // src/index.ts
4
- import { readFileSync } from "fs";
5
- import { dirname, join as join4 } from "path";
6
- import { fileURLToPath } from "url";
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
- // src/commands/board/issues.ts
11
- import { Argument } from "commander";
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
- // src/utils/cli.ts
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
- // src/utils/client.ts
43
- import { JiraClient } from "jira-data-center-client";
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
- // src/utils/credentials.ts
46
- function getCredentialInfo() {
47
- const baseUrl = process.env.JIRA_URL;
48
- const token = process.env.JIRA_TOKEN;
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
- environment: {
51
- JIRA_URL: {
52
- value: baseUrl ?? null,
53
- description: "Your Jira Server base URL (e.g., https://jira.example.com)"
54
- },
55
- JIRA_TOKEN: {
56
- value: token ? "<set>" : null,
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
- tokenUrl: `${baseUrl ?? "https://jira.example.com"}/secure/ViewProfile.jspa`,
61
- hint: "Export environment variables in your shell profile (e.g., ~/.zshrc)."
280
+ exitCode: TYPE_EXIT[n.type]
281
+ };
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);
62
289
  };
63
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
- process.stderr.write(
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").addHelpText(
481
- "after",
482
- '\nExamples:\n jiradc board issues 42\n jiradc board issues 42 --limit 10\n jiradc board issues 42 --jql "status = Open" --fields summary,status\n jiradc board issues 42 --start 50 --limit 25'
483
- ).action(async (id, opts) => {
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").addHelpText(
501
- "after",
502
- '\nExamples:\n jiradc board list\n jiradc board list --limit 10\n jiradc board list --project PROJ\n jiradc board list --type scrum --name "Team Board"'
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(program2) {
517
- const board = program2.command("board").description("Board operations").addHelpText(
518
- "after",
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)).addHelpText(
540
- "after",
541
- `
542
- Examples:
543
- jiradc component create --project AI --name Backend
544
- jiradc component create --project AI --name Frontend --description "UI work" --lead jsmith
545
- jiradc component create --project AI --name Infra --assignee-type COMPONENT_LEAD --lead jsmith`
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").addHelpText(
564
- "after",
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").addHelpText("after", "\nExamples:\n jiradc component get 11289").action(async (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").addHelpText("after", "\nExamples:\n jiradc component issue-count 11289").action(async (id) => {
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)").addHelpText("after", "\nExamples:\n jiradc component list --project AI").action(async (opts) => {
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)).addHelpText(
613
- "after",
614
- `
615
- Examples:
616
- jiradc component update 11289 --name Backend
617
- jiradc component update 11289 --description "Server-side code"
618
- jiradc component update 11289 --lead jsmith --assignee-type COMPONENT_LEAD`
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(program2) {
639
- const component = program2.command("component").description("Project component operations").addHelpText(
640
- "after",
641
- `
642
- Examples:
643
- $ jiradc component list --project AI
644
- $ jiradc component get 11289
645
- $ jiradc component create --project AI --name Backend
646
- $ jiradc component update 11289 --name Backend
647
- $ jiradc component delete 11289 --move-issues-to 11290
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("--page <number>", "Page number (1-indexed)", positiveInt).addHelpText(
662
- "after",
663
- '\nExamples:\n jiradc field options 10001\n jiradc field options 10001 --query "High"\n jiradc field options 10001 --limit 20 --page 2'
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.page
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).addHelpText(
679
- "after",
680
- "\nExamples:\n jiradc field search epic\n jiradc field search customfield_10100\n jiradc field search priority --limit 5"
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(program2) {
690
- const field = program2.command("field").description("Field operations").addHelpText(
691
- "after",
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> <user>").description('Assign an issue. <user> is a username, "me", or "none" to unassign.').addHelpText(
722
- "after",
723
- `
724
- Examples:
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").requiredOption("--id <attachmentId>", "Attachment ID to delete").addHelpText("after", "\nExamples:\n jiradc issue attachment delete --id 12345").action(async (opts) => {
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.id });
744
- output({ deleted: true, attachmentId: opts.id });
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").addHelpText("after", "\nExamples:\n jiradc issue attachment download-all PROJ-123 --output ./downloads").action(async (key, opts) => {
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
- mkdirSync(opts.output, { recursive: true });
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 = join(opts.output, att.filename);
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").requiredOption("--id <attachmentId>", "Attachment ID").requiredOption("--output <path>", "Local file path to save the attachment").addHelpText("after", "\nExamples:\n jiradc issue attachment download PROJ-123 --id 12345 --output ./report.pdf").action(async (key, opts) => {
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 attachment = await client.issues.getAttachment({ attachmentId: opts.id });
966
+ const attachmentId = String(opts.attachmentId);
967
+ const attachment = await client.issues.getAttachment({ attachmentId });
794
968
  if (!attachment.content) {
795
- throw new Error(`Attachment ${opts.id} has no content URL`);
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").addHelpText("after", "\nExamples:\n jiradc issue attachment list PROJ-123").action(async (key) => {
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").addHelpText(
837
- "after",
838
- "\nExamples:\n jiradc issue attachment upload PROJ-123 --files ./report.pdf\n jiradc issue attachment upload PROJ-123 --files ./a.txt,./b.png"
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 attachments2 = await client.issues.addAttachment({ issueKeyOrId: key, filePath });
845
- results.push(...attachments2);
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").addHelpText(
864
- "after",
865
- `
866
- Examples:
867
- $ jiradc issue attachment list PROJ-123
868
- $ jiradc issue attachment upload PROJ-123 --files ./report.pdf
869
- $ jiradc issue attachment download PROJ-123 --id 12345 --output ./report.pdf
870
- $ jiradc issue attachment download-all PROJ-123 --output ./downloads
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).addHelpText(
925
- "after",
926
- "\nExamples:\n jiradc issue batch-changelog PROJ-1,PROJ-2,PROJ-3\n jiradc issue batch-changelog PROJ-123,PROJ-124 --limit 10"
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").addHelpText(
947
- "after",
948
- `
949
- Examples:
950
- jiradc issue batch-create --issues '[{"projectKeyOrId":"PROJ","issueTypeName":"Task","summary":"Task 1"},{"projectKeyOrId":"PROJ","issueTypeName":"Task","summary":"Task 2"}]'`
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).addHelpText(
970
- "after",
971
- "\nExamples:\n jiradc issue changelog PROJ-123\n jiradc issue changelog PROJ-123 --limit 10"
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 join3 } from "path";
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").addHelpText(
999
- "after",
1000
- `
1001
- Examples:
1002
- jiradc issue clone PROJ-123
1003
- jiradc issue clone PROJ-123 --summary "Cloned: new title"
1004
- jiradc issue clone PROJ-123 --include-attachments --include-links`
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 = join3(tmpdir(), `jiradc-clone-${Date.now()}-${att.filename}`);
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/add.ts
1075
- function add(parent) {
1076
- parent.command("add <key>").description("Add a comment to an issue").requiredOption("--body <text>", "Comment body in wiki markup").addHelpText("after", '\nExamples:\n jiradc issue comment add PROJ-123 --body "Fixed in latest build"').action(async (key, opts) => {
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").requiredOption("--id <commentId>", "Comment ID to delete").addHelpText("after", "\nExamples:\n jiradc issue comment delete PROJ-123 --id 12345").action(async (key, opts) => {
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.id });
1088
- output({ deleted: true, issueKey: key, commentId: opts.id });
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/edit.ts
1093
- function edit(parent) {
1094
- parent.command("edit <key>").description("Edit an existing comment").requiredOption("--id <commentId>", "Comment ID to edit").requiredOption("--body <text>", "Updated comment body in wiki markup").addHelpText("after", '\nExamples:\n jiradc issue comment edit PROJ-123 --id 12345 --body "Updated comment text"').action(async (key, opts) => {
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({ issueKeyOrId: key, commentId: opts.id, body: opts.body });
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").addHelpText(
1104
- "after",
1105
- `
1106
- Examples:
1107
- $ jiradc issue comment add PROJ-123 --body "Fixed in latest build"
1108
- $ jiradc issue comment edit PROJ-123 --id 12345 --body "Updated comment text"
1109
- $ jiradc issue comment delete PROJ-123 --id 12345
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 create2(parent) {
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"}')`).addHelpText(
1120
- "after",
1121
- `
1122
- Examples:
1123
- jiradc issue create --project PROJ --type Task --summary "Fix login bug"
1124
- jiradc issue create --project PROJ --type Story --summary "New feature" --assignee jsmith --priority High
1125
- jiradc issue create --project PROJ --type Task --summary "Epic child" --custom-fields '{"customfield_10100": "PROJ-123"}'
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)").addHelpText(
1153
- "after",
1154
- "\nExamples:\n jiradc issue delete PROJ-123\n jiradc issue delete PROJ-123 --delete-subtasks"
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").addHelpText(
1165
- "after",
1166
- "\nExamples:\n jiradc issue dev-status PROJ-123\n jiradc issue dev-status PROJ-123 --detail"
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")').addHelpText(
1260
- "after",
1261
- "\nExamples:\n jiradc issue get PROJ-123\n jiradc issue get PROJ-123 --fields summary,status,assignee\n jiradc issue get PROJ-123 --all-fields\n jiradc issue get PROJ-123 --expand changelog,transitions"
1262
- ).action(async (key, opts) => {
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").addHelpText(
1277
- "after",
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").addHelpText("after", "\nExamples:\n jiradc issue link-types").action(async () => {
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").addHelpText(
1322
- "after",
1323
- "\nExamples:\n jiradc issue link --type Relates --from AI-154 --to AI-149\n jiradc issue link --type Blocks --from PROJ-456 --to PROJ-123 # PROJ-456 blocks PROJ-123"
1324
- ).action(async (opts) => {
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").addHelpText(
1344
- "after",
1345
- '\nExamples:\n jiradc issue search "project = PROJ AND status = Open"\n jiradc issue search "assignee = currentUser()" --limit 10 --fields summary,status\n jiradc issue search "project = PROJ" --start 50 --limit 50'
1346
- ).action(async (jql, opts) => {
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").addHelpText(
1362
- "after",
1363
- `
1364
- Examples:
1365
- jiradc issue transition PROJ-123 --to 31
1366
- jiradc issue transition PROJ-123 --to "In Review"
1367
- jiradc issue transition PROJ-123 --to Done --comment "Verified in staging"`
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").addHelpText("after", "\nExamples:\n jiradc issue transitions PROJ-123").action(async (key) => {
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").addHelpText("after", "\nExamples:\n jiradc issue unlink 12345").action(async (id) => {
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 update2(parent) {
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")').addHelpText(
1454
- "after",
1455
- `
1456
- Examples:
1457
- jiradc issue update PROJ-123 --summary "New title"
1458
- jiradc issue update PROJ-123 --priority High --assignee me
1459
- jiradc issue update PROJ-123 --labels backend,urgent
1460
- jiradc issue update PROJ-123 --labels +urgent,-backend
1461
- jiradc issue update PROJ-123 --components +Frontend
1462
- jiradc issue update PROJ-123 --fields '{"customfield_10100": "EPIC-1"}' --priority High
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/add.ts
1534
- function add2(parent) {
1535
- parent.command("add <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").addHelpText(
1536
- "after",
1537
- '\nExamples:\n jiradc issue worklog add PROJ-123 --time 2h\n jiradc issue worklog add PROJ-123 --time "1d 4h" --comment "Backend implementation"\n jiradc issue worklog add PROJ-123 --time 3h --started "2026-03-19T09:00:00.000+0000"'
1538
- ).action(async (key, opts) => {
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").requiredOption("--id <worklogId>", "Worklog ID to delete").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("--increase-by <amount>", 'Amount to increase the estimate by; required when --adjust-estimate is "manual"').addHelpText(
1555
- "after",
1556
- "\nExamples:\n jiradc issue worklog delete PROJ-123 --id 12345\n jiradc issue worklog delete PROJ-123 --id 12345 --adjust-estimate leave\n jiradc issue worklog delete PROJ-123 --id 12345 --adjust-estimate new --new-estimate 2h"
1557
- ).action(
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.id,
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.id });
1720
+ output({ deleted: true, issueKey: key, worklogId: opts.worklogId });
1568
1721
  }
1569
1722
  );
1570
1723
  }
1571
1724
 
1572
- // src/commands/issue/worklog/edit.ts
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 edit2(parent) {
1576
- parent.command("edit <key>").description("Update an existing worklog entry").requiredOption("--id <worklogId>", "Worklog ID to edit").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"').addHelpText(
1577
- "after",
1578
- '\nExamples:\n jiradc issue worklog edit PROJ-123 --id 12345 --time "1h 30m"\n jiradc issue worklog edit PROJ-123 --id 12345 --comment "Revised note"\n jiradc issue worklog edit PROJ-123 --id 12345 --time 2h --adjust-estimate new --new-estimate 4h'
1579
- ).action(
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.id,
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").addHelpText(
1615
- "after",
1616
- `
1617
- Examples:
1618
- $ jiradc issue worklog add PROJ-123 --time 2h --comment "Backend work"
1619
- $ jiradc issue worklog list PROJ-123 --limit 10
1620
- $ jiradc issue worklog edit PROJ-123 --id 12345 --time "1h 30m"
1621
- $ jiradc issue worklog delete PROJ-123 --id 12345
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
- edit2(worklog);
1780
+ update4(worklog);
1627
1781
  deleteWorklog(worklog);
1628
1782
  }
1629
1783
 
1630
1784
  // src/commands/issue/index.ts
1631
- function registerIssueCommands(program2) {
1632
- const issue = program2.command("issue").description("Issue operations").addHelpText(
1633
- "after",
1634
- `
1635
- Examples:
1636
- $ jiradc issue get PROJ-123
1637
- $ jiradc issue search "project = PROJ AND status = Open" --limit 10
1638
- $ jiradc issue create --project PROJ --type Task --summary "Fix the bug" --priority High
1639
- $ jiradc issue update PROJ-123 --fields '{"summary": "New title"}'
1640
- $ jiradc issue transition PROJ-123 --to 11
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
- create2(issue);
1647
- update2(issue);
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)").addHelpText(
1670
- "after",
1671
- "\nExamples:\n jiradc project list\n jiradc project list --expand description,lead\n jiradc project list --include-archived"
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").addHelpText(
1682
- "after",
1683
- "\nExamples:\n jiradc project versions PROJ\n jiradc project versions PROJ --expand operations"
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(program2) {
1693
- const project = program2.command("project").description("Project operations").addHelpText(
1694
- "after",
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 create3(parent) {
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").addHelpText(
1708
- "after",
1709
- '\nExamples:\n jiradc sprint create --board 42 --name "Sprint 10"\n jiradc sprint create --board 42 --name "Sprint 10" --start-date 2026-03-20 --end-date 2026-04-03 --goal "Complete auth module"'
1710
- ).action(async (opts) => {
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)).addHelpText("after", "\nExamples:\n jiradc sprint delete 100").action(async (id) => {
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").addHelpText(
1737
- "after",
1738
- '\nExamples:\n jiradc sprint issues 100\n jiradc sprint issues 100 --limit 20\n jiradc sprint issues 100 --jql "status = Done" --fields summary,status\n jiradc sprint issues 100 --start 50 --limit 25'
1739
- ).action(async (id, opts) => {
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)).addHelpText(
1757
- "after",
1758
- "\nExamples:\n jiradc sprint list --board 42\n jiradc sprint list --board 42 --state active"
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 update3(parent) {
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").addHelpText(
1774
- "after",
1775
- '\nExamples:\n jiradc sprint update 100 --name "Sprint 10 - Extended"\n jiradc sprint update 100 --state active\n jiradc sprint update 100 --end-date 2026-04-10 --goal "Updated goal"'
1776
- ).action(
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(program2) {
1794
- const sprint = program2.command("sprint").description("Sprint operations").addHelpText(
1795
- "after",
1796
- `
1797
- Examples:
1798
- $ jiradc sprint list --board 42
1799
- $ jiradc sprint list --board 42 --state active
1800
- $ jiradc sprint issues 123 --limit 20
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
- create3(sprint);
1807
- update3(sprint);
1955
+ create5(sprint);
1956
+ update5(sprint);
1808
1957
  deleteSprint(sprint);
1809
1958
  }
1810
1959
 
@@ -1820,36 +1969,24 @@ function getTokenClient(options2 = {}) {
1820
1969
  ...!username ? ["JIRA_BASIC_USERNAME (or --basic-username)"] : [],
1821
1970
  ...!password ? ["JIRA_BASIC_PASSWORD (or --basic-password)"] : []
1822
1971
  ];
1823
- process.stderr.write(
1824
- `${JSON.stringify({
1825
- error: `Missing required credentials: ${missing.join(", ")}`,
1826
- hint: "Set JIRA_BASIC_USERNAME and JIRA_BASIC_PASSWORD in your shell profile, or pass --basic-username / --basic-password."
1827
- })}
1828
- `
1829
- );
1830
- process.exit(1);
1972
+ throw new CliAuthError(`Missing required credentials: ${missing.join(", ")}`);
1831
1973
  }
1832
1974
  const client = new JiraClient2({ baseUrl });
1833
1975
  return { client, username, password };
1834
1976
  }
1835
1977
 
1836
1978
  // src/commands/token/create.ts
1837
- function create4(parent) {
1838
- parent.command("create").description("Create a Personal Access Token. The secret is returned exactly once.").requiredOption("--name <name>", "Token name").option(
1979
+ function create6(parent) {
1980
+ const cmd = parent.command("create").description("Create a Personal Access Token. The secret is returned exactly once.").requiredOption("--name <name>", "Token name").option(
1839
1981
  "--expiration-duration <days>",
1840
1982
  "Token lifetime in days. Omit for a non-expiring token (admin policy permitting).",
1841
1983
  positiveInt
1842
1984
  ).option("--basic-username <u>", "Override $JIRA_BASIC_USERNAME").option(
1843
1985
  "--basic-password <p>",
1844
1986
  "Override $JIRA_BASIC_PASSWORD (caution: visible in process table; prefer the env var)"
1845
- ).addHelpText(
1846
- "after",
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) => {
1987
+ );
1988
+ examples(cmd, [["--name my-pat", "non-expiring"], "--name svc-token --expiration-duration 365"]);
1989
+ cmd.action(async (opts) => {
1853
1990
  const { client, username, password } = getTokenClient({
1854
1991
  basicUsername: opts.basicUsername,
1855
1992
  basicPassword: opts.basicPassword
@@ -1866,10 +2003,12 @@ Examples:
1866
2003
 
1867
2004
  // src/commands/token/list.ts
1868
2005
  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(
2006
+ 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
2007
  "--basic-password <p>",
1871
2008
  "Override $JIRA_BASIC_PASSWORD (caution: visible in process table; prefer the env var)"
1872
- ).action(async (opts) => {
2009
+ );
2010
+ examples(cmd, [""]);
2011
+ cmd.action(async (opts) => {
1873
2012
  const { client, username, password } = getTokenClient({
1874
2013
  basicUsername: opts.basicUsername,
1875
2014
  basicPassword: opts.basicPassword
@@ -1880,42 +2019,40 @@ function list7(parent) {
1880
2019
 
1881
2020
  // src/commands/token/revoke.ts
1882
2021
  function revoke(parent) {
1883
- parent.command("revoke").description("Revoke a Personal Access Token by id").requiredOption("--id <tokenId>", "Token id to revoke", positiveInt).option("--basic-username <u>", "Override $JIRA_BASIC_USERNAME").option(
2022
+ const cmd = parent.command("revoke").description("Revoke a Personal Access Token by id").option("--basic-username <u>", "Override $JIRA_BASIC_USERNAME").option(
1884
2023
  "--basic-password <p>",
1885
2024
  "Override $JIRA_BASIC_PASSWORD (caution: visible in process table; prefer the env var)"
1886
- ).action(async (opts) => {
2025
+ );
2026
+ subjectArg(cmd, "tokenId", { parser: positiveInt, description: "Token id to revoke" });
2027
+ examples(cmd, ["173"]);
2028
+ cmd.action(async (tokenId, opts) => {
1887
2029
  const { client, username, password } = getTokenClient({
1888
2030
  basicUsername: opts.basicUsername,
1889
2031
  basicPassword: opts.basicPassword
1890
2032
  });
1891
- await client.accessTokens.revoke({ username, password, tokenId: opts.id });
1892
- output({ revoked: opts.id });
2033
+ await client.accessTokens.revoke({ username, password, tokenId });
2034
+ output({ revoked: tokenId });
1893
2035
  });
1894
2036
  }
1895
2037
 
1896
2038
  // src/commands/token/index.ts
1897
- function registerTokenCommands(program2) {
1898
- const token = program2.command("token").description("Personal Access Token management").addHelpText(
2039
+ function registerTokenCommands(program) {
2040
+ const token = program.command("token").description("Personal Access Token management");
2041
+ examples(token, ["list", "create --name my-pat", "revoke 173"]);
2042
+ token.addHelpText(
1899
2043
  "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
- `
2044
+ "\nAuth:\n Requires JIRA_BASIC_USERNAME + JIRA_BASIC_PASSWORD\n (or --basic-username / --basic-password on any subcommand)."
1910
2045
  );
1911
- create4(token);
2046
+ create6(token);
1912
2047
  list7(token);
1913
2048
  revoke(token);
1914
2049
  }
1915
2050
 
1916
2051
  // src/commands/user/get.ts
1917
2052
  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").addHelpText("after", "\nExamples:\n jiradc user get jsmith\n jiradc user get JIRAUSER10100 --by-key").action(async (identifier, opts) => {
2053
+ 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");
2054
+ examples(cmd, ["jsmith", "JIRAUSER10100 --by-key"]);
2055
+ cmd.action(async (identifier, opts) => {
1919
2056
  const client = getClient();
1920
2057
  const result = await client.users.getUser(opts.byKey ? { key: identifier } : { username: identifier });
1921
2058
  output(transformUser(result));
@@ -1924,7 +2061,9 @@ function get3(parent) {
1924
2061
 
1925
2062
  // src/commands/user/me.ts
1926
2063
  function me(parent) {
1927
- parent.command("me").description("Get the authenticated user profile").addHelpText("after", "\nExamples:\n jiradc user me").action(async () => {
2064
+ const cmd = parent.command("me").description("Get the authenticated user profile");
2065
+ examples(cmd, [""]);
2066
+ cmd.action(async () => {
1928
2067
  const client = getClient();
1929
2068
  const result = await client.users.getMyself();
1930
2069
  output(transformUser(result));
@@ -1933,10 +2072,9 @@ function me(parent) {
1933
2072
 
1934
2073
  // src/commands/user/search.ts
1935
2074
  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).addHelpText(
1937
- "after",
1938
- '\nExamples:\n jiradc user search Drago\n jiradc user search "John Smith"\n jiradc user search dragomir@first.bet --limit 5'
1939
- ).action(async (query, opts) => {
2075
+ 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);
2076
+ examples(cmd, ["Drago", '"John Smith"', "dragomir@first.bet --limit 5"]);
2077
+ cmd.action(async (query, opts) => {
1940
2078
  const client = getClient();
1941
2079
  const result = await client.users.searchUsers({
1942
2080
  username: query,
@@ -1950,46 +2088,30 @@ function search3(parent) {
1950
2088
  }
1951
2089
 
1952
2090
  // src/commands/user/index.ts
1953
- function registerUserCommands(program2) {
1954
- const user = program2.command("user").description("User operations").addHelpText(
1955
- "after",
1956
- `
1957
- Examples:
1958
- $ jiradc user me
1959
- $ jiradc user get jsmith
1960
- $ jiradc user search Drago
1961
- `
1962
- );
2091
+ function registerUserCommands(program) {
2092
+ const user = program.command("user").description("User operations");
2093
+ examples(user, ["me", "get jsmith", "search Drago"]);
1963
2094
  me(user);
1964
2095
  get3(user);
1965
2096
  search3(user);
1966
2097
  }
1967
2098
 
1968
- // src/index.ts
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
- }
2099
+ // src/program.ts
1979
2100
  var DIM = "\x1B[2m";
1980
2101
  var RESET = "\x1B[0m";
1981
- var program = new Command11();
1982
- program.name("jiradc").description("Jira Data Center CLI").version(readPackageVersion()).configureHelp({
1983
- styleTitle: (str) => styleText("bold", str),
1984
- styleUsage: (str) => styleText("dim", str),
1985
- styleCommandDescription: (str) => styleText("dim", str),
1986
- styleOptionDescription: (str) => styleText("dim", str),
1987
- styleSubcommandDescription: (str) => styleText("dim", str)
1988
- }).addHelpText("beforeAll", `
2102
+ function buildProgram() {
2103
+ const program = new Command11();
2104
+ program.name("jiradc").description("Jira Data Center CLI").version(readPackageVersion(import.meta.url)).configureHelp({
2105
+ styleTitle: (str) => styleText("bold", str),
2106
+ styleUsage: (str) => styleText("dim", str),
2107
+ styleCommandDescription: (str) => styleText("dim", str),
2108
+ styleOptionDescription: (str) => styleText("dim", str),
2109
+ styleSubcommandDescription: (str) => styleText("dim", str)
2110
+ }).addHelpText("beforeAll", `
1989
2111
  ${styleText("bold", "jiradc")} ${DIM}\u2014 Jira Data Center CLI${RESET}
1990
2112
  `).addHelpText(
1991
- "after",
1992
- `
2113
+ "after",
2114
+ `
1993
2115
  ${styleText("bold", "Environment:")}
1994
2116
  JIRA_URL Jira Server base URL ${DIM}(e.g., https://jira.example.com)${RESET}
1995
2117
  JIRA_TOKEN Personal Access Token ${DIM}(generate in Jira > Profile > Personal Access Tokens)${RESET}
@@ -2003,21 +2125,45 @@ ${styleText("bold", "Examples:")}
2003
2125
  ${DIM}$${RESET} jiradc board list --type scrum
2004
2126
  ${DIM}$${RESET} jiradc sprint list --board 42 --state active
2005
2127
  `
2006
- );
2007
- program.option("--pretty", "Pretty-print JSON output");
2008
- program.hook("preAction", (thisCommand) => {
2009
- if (thisCommand.optsWithGlobals().pretty) setPretty(true);
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
- try {
2020
- await program.parseAsync();
2021
- } catch (err) {
2022
- handleError(err);
2128
+ );
2129
+ program.option("--pretty", "Pretty-print JSON output");
2130
+ program.hook("preAction", (thisCommand) => {
2131
+ if (thisCommand.optsWithGlobals().pretty) setPretty(true);
2132
+ });
2133
+ registerIssueCommands(program);
2134
+ registerProjectCommands(program);
2135
+ registerComponentCommands(program);
2136
+ registerBoardCommands(program);
2137
+ registerSprintCommands(program);
2138
+ registerFieldCommands(program);
2139
+ registerUserCommands(program);
2140
+ registerTokenCommands(program);
2141
+ return program;
2142
+ }
2143
+
2144
+ // src/utils/credentials.ts
2145
+ function getCredentialInfo() {
2146
+ const baseUrl = process.env.JIRA_URL;
2147
+ const token = process.env.JIRA_TOKEN;
2148
+ return {
2149
+ environment: {
2150
+ JIRA_URL: {
2151
+ value: baseUrl ?? null,
2152
+ description: "Your Jira Server base URL (e.g., https://jira.example.com)"
2153
+ },
2154
+ JIRA_TOKEN: {
2155
+ value: token ? "<set>" : null,
2156
+ description: "Personal Access Token"
2157
+ }
2158
+ },
2159
+ tokenUrl: `${baseUrl ?? "https://jira.example.com"}/secure/ViewProfile.jspa`
2160
+ };
2023
2161
  }
2162
+
2163
+ // src/index.ts
2164
+ await runCli(buildProgram(), {
2165
+ credentialInfo: getCredentialInfo,
2166
+ service: "Jira",
2167
+ authRecovery: "Set the JIRA_URL and JIRA_TOKEN environment variables.",
2168
+ networkRecovery: "Verify JIRA_URL is correct, the server is reachable, and you are on the VPN if required."
2169
+ });