jiradc-cli 1.0.39 → 1.0.41

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,688 +1,31 @@
1
1
  #!/usr/bin/env node
2
-
3
- // ../../cli-utils/dist/cache.js
4
- import { readFileSync, writeFileSync, mkdirSync, statSync } from "fs";
5
- import { homedir } from "os";
6
- import { join } from "path";
7
- var DEFAULT_TTL = 36e5;
8
- function getCacheDir(name) {
9
- return join(homedir(), ".cache", name);
10
- }
11
- function getCachePath(name, key) {
12
- return join(getCacheDir(name), `${key}.json`);
13
- }
14
- function cacheGet(options, key) {
15
- const ttl = options.ttl ?? DEFAULT_TTL;
16
- const path = getCachePath(options.name, key);
17
- try {
18
- const stat = statSync(path);
19
- if (Date.now() - stat.mtimeMs > ttl)
20
- return null;
21
- const raw = readFileSync(path, "utf-8");
22
- const entry = JSON.parse(raw);
23
- return entry.data;
24
- } catch {
25
- return null;
26
- }
27
- }
28
- function cacheSet(options, key, data) {
29
- const dir = getCacheDir(options.name);
30
- const path = getCachePath(options.name, key);
31
- const entry = { data, timestamp: Date.now() };
32
- try {
33
- mkdirSync(dir, { recursive: true });
34
- writeFileSync(path, JSON.stringify(entry));
35
- } catch {
36
- }
37
- }
38
- async function cacheGetOrFetch(options, key, fetcher) {
39
- const cached = cacheGet(options, key);
40
- if (cached !== null)
41
- return cached;
42
- const data = await fetcher();
43
- cacheSet(options, key, data);
44
- return data;
45
- }
46
-
47
- // ../../cli-utils/dist/bootstrap.js
48
- import { readFileSync as readFileSync2 } from "fs";
49
- import { dirname, join as join2 } from "path";
50
- import { fileURLToPath } from "url";
51
- function readPackageVersion(importMetaUrl) {
52
- try {
53
- const here = dirname(fileURLToPath(importMetaUrl));
54
- const pkg = JSON.parse(readFileSync2(join2(here, "..", "package.json"), "utf-8"));
55
- return pkg.version ?? "0.0.0";
56
- } catch {
57
- return "0.0.0";
58
- }
59
- }
60
-
61
- // ../../cli-utils/dist/validators.js
62
- import { InvalidArgumentError } from "commander";
63
- function intInRange(min, max) {
64
- return (raw) => {
65
- const n = parseInt(raw, 10);
66
- if (Number.isNaN(n) || !Number.isFinite(n)) {
67
- throw new InvalidArgumentError("Must be an integer.");
68
- }
69
- if (n < min || n > max) {
70
- throw new InvalidArgumentError(`Must be between ${min} and ${max}.`);
71
- }
72
- return n;
73
- };
74
- }
75
- function nonNegativeInt(raw) {
76
- const n = parseInt(raw, 10);
77
- if (Number.isNaN(n) || n < 0) {
78
- throw new InvalidArgumentError("Must be a non-negative integer.");
79
- }
80
- return n;
81
- }
82
- function positiveInt(raw) {
83
- const n = parseInt(raw, 10);
84
- if (Number.isNaN(n) || n < 1) {
85
- throw new InvalidArgumentError("Must be a positive integer.");
86
- }
87
- return n;
88
- }
89
- function listOf(item) {
90
- return (raw, previous) => [...Array.isArray(previous) ? previous : [], item(raw)];
91
- }
92
- function text(raw) {
93
- return raw;
94
- }
95
- function nonEmpty(raw) {
96
- if (raw.trim() === "") {
97
- throw new InvalidArgumentError("Must not be empty.");
98
- }
99
- return raw;
100
- }
101
- function integer(raw) {
102
- if (!/^[+-]?\d+$/.test(raw.trim())) {
103
- throw new InvalidArgumentError("Must be an integer.");
104
- }
105
- return Number(raw);
106
- }
107
- function date(raw) {
108
- const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(raw);
109
- if (!m) {
110
- throw new InvalidArgumentError("Must be a date in YYYY-MM-DD format.");
111
- }
112
- const [, y, mo, d] = m;
113
- const year = Number(y);
114
- const month = Number(mo);
115
- const day = Number(d);
116
- const dt = new Date(Date.UTC(year, month - 1, day));
117
- if (dt.getUTCFullYear() !== year || dt.getUTCMonth() !== month - 1 || dt.getUTCDate() !== day) {
118
- throw new InvalidArgumentError("Not a real calendar date.");
119
- }
120
- return raw;
121
- }
122
- function dateTime(raw) {
123
- if (!/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}/.test(raw) || Number.isNaN(Date.parse(raw))) {
124
- throw new InvalidArgumentError("Must be an ISO 8601 date-time, e.g. 2026-06-08T14:30:00Z.");
125
- }
126
- return raw;
127
- }
128
-
129
- // ../../cli-utils/dist/json.js
130
- import { InvalidArgumentError as InvalidArgumentError2 } from "commander";
131
- function jsonShape(schema) {
132
- return (raw) => {
133
- let parsed;
134
- try {
135
- parsed = JSON.parse(raw);
136
- } catch {
137
- throw new InvalidArgumentError2("Must be valid JSON.");
138
- }
139
- const result = schema.safeParse(parsed);
140
- if (!result.success) {
141
- const issues3 = result.error.issues.map((i) => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ");
142
- throw new InvalidArgumentError2(`Invalid JSON shape \u2014 ${issues3}`);
143
- }
144
- return result.data;
145
- };
146
- }
147
-
148
- // ../../cli-utils/dist/text-or-file.js
149
- import { readFileSync as readFileSync3 } from "fs";
150
- import { InvalidArgumentError as InvalidArgumentError3, Option } from "commander";
151
- var STDIN_REF = "-";
152
- function rejectStdinSentinel(value) {
153
- if (value === "@-" || value === STDIN_REF) {
154
- throw new InvalidArgumentError3(`"${value}" looks like a stdin redirect, which is not supported here. Pass the text directly, or read it from a file/stdin with the matching --\u2026-file <path|-> option.`);
155
- }
156
- return value;
157
- }
158
- function readFileOrStdin(ref) {
159
- if (ref === STDIN_REF) {
160
- if (process.stdin.isTTY) {
161
- throw new InvalidArgumentError3('"--\u2026-file -" reads stdin, but stdin is a terminal (nothing piped).');
162
- }
163
- try {
164
- return readFileSync3(0, "utf8");
165
- } catch (err) {
166
- throw new InvalidArgumentError3(`failed to read stdin for "--\u2026-file -": ${err.message}`);
167
- }
168
- }
169
- try {
170
- return readFileSync3(ref, "utf8");
171
- } catch (err) {
172
- const e = err;
173
- if (e.code === "ENOENT") {
174
- throw new InvalidArgumentError3(`file not found: "${ref}".`);
175
- }
176
- throw new InvalidArgumentError3(`failed to read file "${ref}": ${e.message}`);
177
- }
178
- }
179
- function textOrFileOption(cmd, name, opts = {}) {
180
- const label = `${name.charAt(0).toUpperCase()}${name.slice(1)} content`;
181
- cmd.option(`--${name} <text>`, opts.description ?? label, rejectStdinSentinel);
182
- cmd.addOption(new Option(`--${name}-file <path>`, `Read --${name} from a file, or "-" for stdin (mutually exclusive with --${name})`).conflicts(name).argParser(text));
183
- return cmd;
184
- }
185
- function resolveTextOrFile(opts, name, { required = true } = {}) {
186
- const record = opts;
187
- const literal = record[name];
188
- const ref = record[`${name}File`];
189
- if (literal !== void 0 && ref !== void 0) {
190
- throw new InvalidArgumentError3(`--${name} and --${name}-file are mutually exclusive; provide only one.`);
191
- }
192
- if (ref !== void 0) {
193
- return readFileOrStdin(ref);
194
- }
195
- if (literal === void 0 && required) {
196
- throw new InvalidArgumentError3(`a ${name} is required: provide --${name} <text> or --${name}-file <path>.`);
197
- }
198
- return literal;
199
- }
200
-
201
- // ../../cli-utils/dist/registry.js
202
- var SUB_ENTITY_ID_NUMERIC = {
203
- comment: true,
204
- worklog: true,
205
- attachment: true,
206
- request: true,
207
- type: true,
208
- employee: true,
209
- message: false,
210
- membership: false
211
- };
212
-
213
- // ../../cli-utils/dist/builders.js
214
- function defineOption(cmd, flags, description, parser, mandatory) {
215
- if (mandatory) {
216
- return parser ? cmd.requiredOption(flags, description, parser) : cmd.requiredOption(flags, description);
217
- }
218
- return parser ? cmd.option(flags, description, parser) : cmd.option(flags, description);
219
- }
220
- function subjectArg(cmd, name, opts = {}) {
221
- const inner = opts.variadic ? `${name}...` : name;
222
- const token = opts.optional ? `[${inner}]` : `<${inner}>`;
223
- return opts.parser ? cmd.argument(token, opts.description ?? "", opts.parser) : cmd.argument(token, opts.description ?? "");
224
- }
225
- function subEntityOption(cmd, entity, opts = {}) {
226
- const numeric = opts.numeric ?? SUB_ENTITY_ID_NUMERIC[entity] ?? true;
227
- const description = `${entity.charAt(0).toUpperCase()}${entity.slice(1)} id`;
228
- return defineOption(cmd, `--${entity}-id <id>`, description, numeric ? positiveInt : text, opts.mandatory);
229
- }
230
- function paginationOptions(cmd, opts = {}) {
231
- const max = opts.maxLimit ?? 1e3;
232
- const def = opts.defaultLimit ?? 25;
233
- cmd.option("--limit <n>", `Max results per page (1-${max})`, intInRange(1, max), def);
234
- if (opts.startIsToken) {
235
- cmd.option("--start <indexOrToken>", "Pagination cursor: 0-based offset or opaque next-page token", text);
236
- } else {
237
- cmd.option("--start <index>", "Pagination offset (0-based)", nonNegativeInt);
238
- }
239
- return cmd;
240
- }
241
- function bodyOption(cmd, opts = {}) {
242
- return textOrFileOption(cmd, "body", { description: "Prose body content", ...opts });
243
- }
244
- function commentOption(cmd, opts = {}) {
245
- return textOrFileOption(cmd, "comment", { description: "Optional note attached to the action", ...opts });
246
- }
247
- var EXAMPLES = /* @__PURE__ */ new WeakSet();
248
- function commandPath(cmd) {
249
- const parts = [];
250
- for (let c = cmd; c; c = c.parent)
251
- parts.unshift(c.name());
252
- return parts.join(" ");
253
- }
254
- function exampleLine(path, entry) {
255
- if (typeof entry === "string")
256
- return entry ? ` ${path} ${entry}` : ` ${path}`;
257
- if ("raw" in entry)
258
- return ` ${entry.raw}`;
259
- const [args, comment] = entry;
260
- const line = args ? `${path} ${args}` : path;
261
- return ` ${line} # ${comment}`;
262
- }
263
- function examples(cmd, entries) {
264
- EXAMPLES.add(cmd);
265
- return cmd.addHelpText("after", () => {
266
- const path = commandPath(cmd);
267
- return `
268
- Examples:
269
- ${entries.map((e) => exampleLine(path, e)).join("\n")}`;
270
- });
271
- }
272
-
273
- // ../../cli-utils/dist/errors.js
274
- import { CommanderError } from "commander";
275
- var EXIT = {
276
- SUCCESS: 0,
277
- GENERIC: 1,
278
- USAGE: 2,
279
- NOT_FOUND: 3,
280
- FORBIDDEN: 4,
281
- CONFLICT: 5,
282
- AUTH: 6
283
- };
284
- var CliAuthError = class extends Error {
285
- constructor(message) {
286
- super(message);
287
- this.name = "CliAuthError";
288
- }
289
- };
290
- var NOT_FOUND_RECOVERY = "Verify the id/key, then check your access.";
291
- var NOT_FOUND_MESSAGE = "Not found: it may not exist, or you may not have permission to see it";
292
- var USAGE_RECOVERY = "Check the command syntax and flags; run the command with --help.";
293
- var CliNotFoundError = class extends Error {
294
- recovery;
295
- constructor(message, recovery = NOT_FOUND_RECOVERY) {
296
- super(message);
297
- this.name = "CliNotFoundError";
298
- this.recovery = recovery;
299
- }
300
- };
301
- var CliUsageError = class extends Error {
302
- recovery;
303
- detail;
304
- constructor(message, recovery = USAGE_RECOVERY, detail) {
305
- super(message);
306
- this.name = "CliUsageError";
307
- this.recovery = recovery;
308
- this.detail = detail;
309
- }
310
- };
311
- var SubcommandRequiredError = class extends Error {
312
- subcommands;
313
- constructor(commandPath3, subcommands) {
314
- super(`'${commandPath3}' requires a subcommand`);
315
- this.name = "SubcommandRequiredError";
316
- this.subcommands = subcommands;
317
- }
318
- };
319
- var UnknownSubcommandError = class extends Error {
320
- subcommands;
321
- suggestion;
322
- constructor(commandPath3, token, subcommands, suggestion) {
323
- super(`'${token}' is not a subcommand of '${commandPath3}'`);
324
- this.name = "UnknownSubcommandError";
325
- this.subcommands = subcommands;
326
- this.suggestion = suggestion;
327
- }
328
- };
329
- var TYPE_EXIT = {
330
- usage: EXIT.USAGE,
331
- not_found: EXIT.NOT_FOUND,
332
- forbidden: EXIT.FORBIDDEN,
333
- conflict: EXIT.CONFLICT,
334
- auth: EXIT.AUTH,
335
- rate_limited: EXIT.GENERIC,
336
- server: EXIT.GENERIC,
337
- timeout: EXIT.GENERIC,
338
- network: EXIT.GENERIC,
339
- unknown: EXIT.GENERIC
340
- };
341
- var TRANSPORT_CODES = {
342
- /** Our own timeout fired: connected (or tried to), no response in time. */
343
- readTimeout: ["ECONNABORTED"],
344
- /** The OS gave up establishing the connection. */
345
- connectTimeout: ["ETIMEDOUT"],
346
- /** No such host, or DNS itself is unavailable. */
347
- unresolved: ["ENOTFOUND", "EAI_AGAIN"],
348
- /** Host is there, nothing is listening on that port. */
349
- refused: ["ECONNREFUSED"],
350
- /** Established, then died mid-flight. */
351
- dropped: ["ECONNRESET", "EPIPE"]
352
- };
353
- function humanMs(ms) {
354
- return ms % 1e3 === 0 ? `${ms / 1e3}s` : `${ms}ms`;
355
- }
356
- function targetHost(err) {
357
- const config = err?.config;
358
- const raw = config?.baseURL ?? config?.url;
359
- if (raw === void 0 || raw === "")
360
- return void 0;
361
- try {
362
- return new URL(raw).host;
363
- } catch {
364
- return void 0;
365
- }
366
- }
367
- function configuredTimeoutMs(err) {
368
- const t = err?.config?.timeout;
369
- return typeof t === "number" && t > 0 ? t : void 0;
370
- }
371
- function retryAfterSeconds(err) {
372
- const headers = err?.response?.headers;
373
- const raw = headers?.["retry-after"];
374
- if (raw === void 0 || raw === "")
375
- return void 0;
376
- const seconds = Number(raw);
377
- if (Number.isFinite(seconds))
378
- return Math.max(0, Math.round(seconds));
379
- if (typeof raw !== "string")
380
- return void 0;
381
- const at = Date.parse(raw);
382
- if (Number.isNaN(at))
383
- return void 0;
384
- return Math.max(0, Math.round((at - Date.now()) / 1e3));
385
- }
386
- function httpStatus(err) {
387
- const e = err;
388
- return e?.response?.status ?? e?.statusCode;
389
- }
390
- function responseDetail(err) {
391
- const e = err;
392
- const data = e?.response?.data;
393
- if (data && typeof data === "object")
394
- return data;
395
- const body = e?.body;
396
- if (typeof body === "string") {
397
- try {
398
- const parsed = JSON.parse(body);
399
- return parsed.error ?? parsed;
400
- } catch {
401
- return void 0;
402
- }
403
- }
404
- if (body && typeof body === "object") {
405
- return body.error ?? body;
406
- }
407
- return void 0;
408
- }
409
- function errorCode(err) {
410
- return err?.code;
411
- }
412
- function normalize(err, opts) {
413
- const message = err instanceof Error ? err.message : String(err);
414
- if (err instanceof CommanderError) {
415
- return {
416
- type: "usage",
417
- message: (message || "Invalid command usage").replace(/^error:\s+/, ""),
418
- recovery: "Check the command syntax and flags; run the command with --help.",
419
- retryable: false
420
- };
421
- }
422
- if (err instanceof SubcommandRequiredError) {
423
- return {
424
- type: "usage",
425
- message,
426
- recovery: `Re-run with one of these subcommands: ${err.subcommands.join(", ")}.`,
427
- retryable: false,
428
- detail: { subcommands: err.subcommands }
429
- };
430
- }
431
- if (err instanceof UnknownSubcommandError) {
432
- const didYouMean = err.suggestion === void 0 ? "" : `Did you mean '${err.suggestion}'? `;
433
- return {
434
- type: "usage",
435
- message,
436
- recovery: `${didYouMean}Valid subcommands: ${err.subcommands.join(", ")}.`,
437
- retryable: false,
438
- detail: { subcommands: err.subcommands }
439
- };
440
- }
441
- if (err instanceof CliAuthError) {
442
- return { type: "auth", message: message || "Missing credentials", recovery: opts.authRecovery, retryable: false };
443
- }
444
- if (err instanceof CliNotFoundError) {
445
- return { type: "not_found", message, recovery: err.recovery, retryable: false };
446
- }
447
- if (err instanceof CliUsageError) {
448
- return {
449
- type: "usage",
450
- message,
451
- recovery: err.recovery,
452
- retryable: false,
453
- ...err.detail !== void 0 ? { detail: err.detail } : {}
454
- };
455
- }
456
- const status = httpStatus(err);
457
- const detail = responseDetail(err);
458
- if (status !== void 0) {
459
- switch (status) {
460
- case 400:
461
- return {
462
- type: "usage",
463
- status,
464
- message: "Bad request",
465
- recovery: "Check parameter values (ids, keys, query syntax) against the API.",
466
- retryable: false,
467
- detail
468
- };
469
- case 401:
470
- return {
471
- type: "auth",
472
- status,
473
- message: "Authentication failed",
474
- recovery: opts.authRecovery,
475
- retryable: false
476
- };
477
- case 403:
478
- return {
479
- type: "forbidden",
480
- status,
481
- message: "Forbidden",
482
- recovery: `Your ${opts.service} account lacks permission for this operation; check token scope and resource permissions.`,
483
- retryable: false,
484
- detail
485
- };
486
- case 404:
487
- return {
488
- type: "not_found",
489
- status,
490
- message: NOT_FOUND_MESSAGE,
491
- recovery: NOT_FOUND_RECOVERY,
492
- retryable: false,
493
- detail
494
- };
495
- case 409:
496
- return {
497
- type: "conflict",
498
- status,
499
- message: "Conflict",
500
- recovery: "The resource changed or already exists; re-fetch current state and retry.",
501
- retryable: false,
502
- detail
503
- };
504
- case 429: {
505
- const wait = retryAfterSeconds(err);
506
- return {
507
- type: "rate_limited",
508
- status,
509
- message: `${opts.service} is rate limiting these requests`,
510
- recovery: wait === void 0 ? `${opts.service} did not say how long to wait. Pause a few seconds before retrying, and make fewer requests at once.` : `${opts.service} asked for a ${wait}s pause before the next request. Wait at least that long, then retry.`,
511
- retryable: true,
512
- ...wait === void 0 ? {} : { detail: { retryAfterSeconds: wait } }
513
- };
514
- }
515
- }
516
- if (status >= 500) {
517
- return {
518
- type: "server",
519
- status,
520
- message: status === 503 ? `${opts.service} returned 503 (service unavailable)` : `${opts.service} returned ${status}`,
521
- recovery: status === 503 ? `${opts.service} is temporarily refusing work. Retry shortly.` : `${opts.service} failed to complete the request. Usually temporary, so retry. If it persists, the request itself may be at fault.`,
522
- retryable: true,
523
- detail
524
- };
525
- }
526
- return {
527
- type: "unknown",
528
- status,
529
- message: `${opts.service} error (HTTP ${status}): ${message}`,
530
- recovery: "Inspect the detail field for the API response.",
531
- retryable: false,
532
- detail
533
- };
534
- }
535
- const code = errorCode(err);
536
- const transport = classifyTransport(err, code, message, opts);
537
- if (transport)
538
- return transport;
539
- return { type: "unknown", message, recovery: "Unexpected error; inspect the message.", retryable: false };
540
- }
541
- function classifyTransport(err, code, message, opts) {
542
- const matches = (codes) => code !== void 0 && codes.includes(code) || codes.some((c) => message.includes(c));
543
- const host = targetHost(err);
544
- const where = host ?? opts.service;
545
- const urlVar = opts.urlEnvVar ?? "the base-URL environment variable";
546
- if (matches(TRANSPORT_CODES.readTimeout)) {
547
- const limit = configuredTimeoutMs(err);
548
- return {
549
- type: "timeout",
550
- message: `${opts.service} did not respond${limit === void 0 ? " in time" : ` within ${humanMs(limit)}`}`,
551
- recovery: `The request reached ${opts.service} but no response came back in time. Usually the server is busy or the query is too large. Retry, or ask for less: fewer results per page, fewer fields, a narrower query.`,
552
- retryable: true
553
- };
554
- }
555
- if (matches(TRANSPORT_CODES.connectTimeout)) {
556
- const limit = configuredTimeoutMs(err);
557
- return {
558
- type: "timeout",
559
- message: `Could not open a connection to ${where}${limit === void 0 ? "" : ` within ${humanMs(limit)}`}`,
560
- recovery: `The host did not accept a connection in time. If it is reachable at all it is likely overloaded, so retry. If this repeats, confirm ${urlVar} points at a host this machine can reach.`,
561
- retryable: true
562
- };
563
- }
564
- if (matches(TRANSPORT_CODES.unresolved)) {
565
- return {
566
- type: "network",
567
- message: `The host ${where} does not resolve`,
568
- recovery: `${urlVar} points at a hostname that cannot be looked up from this machine. Check it for a typo. Retrying will not help until it changes.`,
569
- retryable: false
570
- };
571
- }
572
- if (matches(TRANSPORT_CODES.refused)) {
573
- return {
574
- type: "network",
575
- message: `Nothing accepted a connection at ${where}`,
576
- recovery: `The host resolved but refused the connection, usually a wrong port or a service that is not running. Retrying will not help until that changes.`,
577
- retryable: false
578
- };
579
- }
580
- if (matches(TRANSPORT_CODES.dropped)) {
581
- return {
582
- type: "network",
583
- message: `The connection to ${opts.service} closed before a response arrived`,
584
- recovery: `The connection dropped mid-request. This is usually a blip, so retry.`,
585
- retryable: true
586
- };
587
- }
588
- return void 0;
589
- }
590
- function classifyError(err, opts) {
591
- const base = normalize(err, opts);
592
- const n = opts.adapt ? opts.adapt(base, err) : base;
593
- if (n.type === "auth" && n.detail === void 0 && opts.credentialInfo) {
594
- n.detail = opts.credentialInfo();
595
- }
596
- return {
597
- envelope: {
598
- error: {
599
- type: n.type,
600
- message: n.message,
601
- recovery: n.recovery,
602
- retryable: n.retryable,
603
- ...n.detail !== void 0 ? { detail: n.detail } : {}
604
- }
605
- },
606
- exitCode: TYPE_EXIT[n.type]
607
- };
608
- }
609
- function createErrorHandler(opts) {
610
- return (err) => {
611
- const { envelope, exitCode } = classifyError(err, opts);
612
- process.stderr.write(`${JSON.stringify(envelope)}
613
- `);
614
- return process.exit(exitCode);
615
- };
616
- }
617
- function isCleanCommanderExit(err) {
618
- return err instanceof CommanderError && (err.exitCode === 0 || err.code === "commander.helpDisplayed" || err.code === "commander.help" || err.code === "commander.version");
619
- }
620
- function routeErrors(cmd) {
621
- cmd.exitOverride();
622
- cmd.configureOutput({ writeErr: () => void 0 });
623
- cmd.commands.forEach(routeErrors);
624
- }
625
- function commandPath2(cmd) {
626
- const parts = [];
627
- let cur = cmd;
628
- while (cur) {
629
- parts.unshift(cur.name());
630
- cur = cur.parent;
631
- }
632
- return parts.join(" ");
633
- }
634
- function attachSubcommandGuards(cmd) {
635
- cmd.commands.forEach(attachSubcommandGuards);
636
- if (cmd.commands.length === 0)
637
- return;
638
- const hasAction = Boolean(cmd._actionHandler);
639
- if (hasAction)
640
- return;
641
- cmd.allowExcessArguments(true);
642
- cmd.action((...params) => {
643
- const invoked = params[params.length - 1];
644
- const names = cmd.commands.map((c) => c.name()).filter((name) => name !== "help");
645
- const [token] = invoked.args;
646
- if (token === void 0)
647
- throw new SubcommandRequiredError(commandPath2(cmd), names);
648
- throw new UnknownSubcommandError(commandPath2(cmd), token, names, suggestSubcommand(token, names));
649
- });
650
- }
651
- function editDistance(a, b) {
652
- let prev = Array.from({ length: b.length + 1 }, (_, i) => i);
653
- for (let i = 1; i <= a.length; i++) {
654
- const row = [i];
655
- for (let j = 1; j <= b.length; j++) {
656
- const substitution = prev[j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1);
657
- row[j] = Math.min(row[j - 1] + 1, prev[j] + 1, substitution);
658
- }
659
- prev = row;
660
- }
661
- return prev[b.length];
662
- }
663
- function suggestSubcommand(token, names) {
664
- const MAX_EDITS = 2;
665
- let best;
666
- for (const name of names) {
667
- const distance = editDistance(token.toLowerCase(), name.toLowerCase());
668
- if (distance <= MAX_EDITS && distance < token.length && (!best || distance < best.distance)) {
669
- best = { name, distance };
670
- }
671
- }
672
- return best?.name;
673
- }
674
- async function runCli(program, opts) {
675
- routeErrors(program);
676
- attachSubcommandGuards(program);
677
- try {
678
- await program.parseAsync();
679
- } catch (err) {
680
- if (isCleanCommanderExit(err)) {
681
- process.exit(err.exitCode ?? 0);
682
- }
683
- createErrorHandler(opts)(err);
684
- }
685
- }
2
+ import {
3
+ CliAuthError,
4
+ CliNotFoundError,
5
+ CliUsageError,
6
+ bodyOption,
7
+ cacheGetOrFetch,
8
+ commentOption,
9
+ date,
10
+ dateTime,
11
+ examples,
12
+ intInRange,
13
+ integer,
14
+ jsonShape,
15
+ listOf,
16
+ nonEmpty,
17
+ nonNegativeInt,
18
+ paginationOptions,
19
+ positiveInt,
20
+ readPackageVersion,
21
+ resolveTextOrFile,
22
+ runCli,
23
+ scopeIdOption,
24
+ subEntityOption,
25
+ subjectArg,
26
+ text,
27
+ textOrFileOption
28
+ } from "./chunk-3GFKV36K.js";
686
29
 
687
30
  // src/program.ts
688
31
  import { styleText } from "util";
@@ -1294,10 +637,10 @@ function issues(parent) {
1294
637
  }
1295
638
 
1296
639
  // src/commands/board/list.ts
1297
- import { Option as Option2 } from "commander";
640
+ import { Option } from "commander";
1298
641
  var BOARD_TYPES = ["scrum", "kanban", "simple"];
1299
642
  function list(parent) {
1300
- 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", text).addOption(new Option2("--type <type>", "Board type filter").choices(BOARD_TYPES)).option("--name <name>", "Filter boards by name", text);
643
+ 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", text).addOption(new Option("--type <type>", "Board type filter").choices(BOARD_TYPES)).option("--name <name>", "Filter boards by name", text);
1301
644
  examples(cmd, ["", "--limit 10", "--project PROJ", '--type scrum --name "Team Board"']);
1302
645
  cmd.action(async (opts) => {
1303
646
  const client = getClient();
@@ -1321,7 +664,7 @@ function registerBoardCommands(program) {
1321
664
  }
1322
665
 
1323
666
  // src/commands/component/create.ts
1324
- import { Option as Option3 } from "commander";
667
+ import { Option as Option2 } from "commander";
1325
668
  var ASSIGNEE_TYPES = [
1326
669
  "PROJECT_DEFAULT",
1327
670
  "COMPONENT_LEAD",
@@ -1329,7 +672,7 @@ var ASSIGNEE_TYPES = [
1329
672
  "UNASSIGNED"
1330
673
  ];
1331
674
  function create(parent) {
1332
- const cmd = parent.command("create").description("Create a new component in a project").requiredOption("--project <key>", "Project key (e.g., PROJ)", text).requiredOption("--name <name>", "Component name", text).option("--lead <username>", "Username of the component lead", text).addOption(new Option3("--assignee-type <type>", "Assignee strategy").choices(ASSIGNEE_TYPES));
675
+ const cmd = parent.command("create").description("Create a new component in a project").requiredOption("--project <key>", "Project key (e.g., PROJ)", text).requiredOption("--name <name>", "Component name", text).option("--lead <username>", "Username of the component lead", text).addOption(new Option2("--assignee-type <type>", "Assignee strategy").choices(ASSIGNEE_TYPES));
1333
676
  textOrFileOption(cmd, "description", { description: "Component description" });
1334
677
  examples(cmd, [
1335
678
  "--project PROJ --name Backend",
@@ -1397,7 +740,7 @@ function list2(parent) {
1397
740
  }
1398
741
 
1399
742
  // src/commands/component/update.ts
1400
- import { Option as Option4 } from "commander";
743
+ import { Option as Option3 } from "commander";
1401
744
  var ASSIGNEE_TYPES2 = [
1402
745
  "PROJECT_DEFAULT",
1403
746
  "COMPONENT_LEAD",
@@ -1405,7 +748,7 @@ var ASSIGNEE_TYPES2 = [
1405
748
  "UNASSIGNED"
1406
749
  ];
1407
750
  function update(parent) {
1408
- const cmd = parent.command("update").description("Update an existing component (only provided fields are changed)").argument("<id>", "Component ID", positiveInt).option("--name <name>", "New component name", text).option("--lead <username>", "Username of the component lead (empty string clears it)", text).addOption(new Option4("--assignee-type <type>", "New assignee strategy").choices(ASSIGNEE_TYPES2));
751
+ const cmd = parent.command("update").description("Update an existing component (only provided fields are changed)").argument("<id>", "Component ID", positiveInt).option("--name <name>", "New component name", text).option("--lead <username>", "Username of the component lead (empty string clears it)", text).addOption(new Option3("--assignee-type <type>", "New assignee strategy").choices(ASSIGNEE_TYPES2));
1409
752
  textOrFileOption(cmd, "description", { description: "New component description" });
1410
753
  examples(cmd, [
1411
754
  "11289 --name Backend",
@@ -1502,21 +845,21 @@ async function resolveUserToken(token) {
1502
845
 
1503
846
  // src/utils/validators.ts
1504
847
  import { existsSync } from "fs";
1505
- import { InvalidArgumentError as InvalidArgumentError4 } from "commander";
848
+ import { InvalidArgumentError } from "commander";
1506
849
  function issueKey(raw) {
1507
850
  if (!/^(\d+|[A-Z][A-Z0-9]+-\d+)$/.test(raw)) {
1508
- throw new InvalidArgumentError4("Must be a Jira issue key (e.g. PROJ-123) or a numeric issue id.");
851
+ throw new InvalidArgumentError("Must be a Jira issue key (e.g. PROJ-123) or a numeric issue id.");
1509
852
  }
1510
853
  return raw;
1511
854
  }
1512
855
  function keyList(raw) {
1513
856
  const keys = raw.split(",").map((k) => k.trim()).filter(Boolean);
1514
- if (keys.length === 0) throw new InvalidArgumentError4("Provide at least one comma-separated issue key.");
857
+ if (keys.length === 0) throw new InvalidArgumentError("Provide at least one comma-separated issue key.");
1515
858
  for (const k of keys) issueKey(k);
1516
859
  return keys;
1517
860
  }
1518
861
  function filePath(raw) {
1519
- if (!existsSync(raw)) throw new InvalidArgumentError4(`File not found: ${raw}`);
862
+ if (!existsSync(raw)) throw new InvalidArgumentError(`File not found: ${raw}`);
1520
863
  return raw;
1521
864
  }
1522
865
 
@@ -1548,14 +891,14 @@ function deleteAttachment(parent) {
1548
891
  }
1549
892
 
1550
893
  // src/commands/issue/attachment/download-all.ts
1551
- import { mkdirSync as mkdirSync2 } from "fs";
1552
- import { join as join3 } from "path";
894
+ import { mkdirSync } from "fs";
895
+ import { join } from "path";
1553
896
  function downloadAll(parent) {
1554
897
  const cmd = parent.command("download-all").description("Download all attachments from an issue").argument("<key>", "Issue key", issueKey).requiredOption("--output <dir>", "Local directory to save attachments into", text);
1555
898
  examples(cmd, ["PROJ-123 --output ./downloads"]);
1556
899
  cmd.action(async (key, opts) => {
1557
900
  const client = getClient();
1558
- mkdirSync2(opts.output, { recursive: true });
901
+ mkdirSync(opts.output, { recursive: true });
1559
902
  const issue = await client.issues.get({
1560
903
  issueKeyOrId: key,
1561
904
  fields: ["attachment"]
@@ -1572,7 +915,7 @@ function downloadAll(parent) {
1572
915
  failed.push({ filename: att.filename, error: "No content URL" });
1573
916
  continue;
1574
917
  }
1575
- const destPath = join3(opts.output, att.filename);
918
+ const destPath = join(opts.output, att.filename);
1576
919
  try {
1577
920
  await client.issues.downloadAttachment({ url: att.content, destinationPath: destPath });
1578
921
  results.push({ filename: att.filename, size: att.size, path: destPath });
@@ -1743,7 +1086,7 @@ function changelog(parent) {
1743
1086
  // src/commands/issue/clone.ts
1744
1087
  import { unlink } from "fs/promises";
1745
1088
  import { tmpdir } from "os";
1746
- import { join as join4 } from "path";
1089
+ import { join as join2 } from "path";
1747
1090
  var CLONE_FIELDS = [
1748
1091
  "summary",
1749
1092
  "description",
@@ -1793,7 +1136,7 @@ function clone(parent) {
1793
1136
  const tmpFiles = [];
1794
1137
  const copied = await Promise.all(
1795
1138
  f.attachment.map(async (att) => {
1796
- const tmpPath = join4(tmpdir(), `jiradc-clone-${Date.now()}-${att.filename}`);
1139
+ const tmpPath = join2(tmpdir(), `jiradc-clone-${Date.now()}-${att.filename}`);
1797
1140
  tmpFiles.push(tmpPath);
1798
1141
  await client.issues.downloadAttachment({ url: att.content, destinationPath: tmpPath });
1799
1142
  await client.issues.addAttachment({ issueKeyOrId: newKey, filePath: tmpPath });
@@ -1926,7 +1269,7 @@ function registerCommentCommands(parent) {
1926
1269
  }
1927
1270
 
1928
1271
  // src/commands/issue/create.ts
1929
- import { Option as Option5 } from "commander";
1272
+ import { Option as Option4 } from "commander";
1930
1273
  import { z as z2 } from "zod";
1931
1274
  var fieldsSchema = z2.record(z2.unknown());
1932
1275
  function create3(parent) {
@@ -1935,7 +1278,7 @@ function create3(parent) {
1935
1278
  `Additional fields as JSON (e.g., '{"customfield_10100": "EPIC-1"}')`,
1936
1279
  jsonShape(fieldsSchema)
1937
1280
  ).addOption(
1938
- new Option5("--custom-fields <json>", "Deprecated alias for --fields").argParser(jsonShape(fieldsSchema)).hideHelp()
1281
+ new Option4("--custom-fields <json>", "Deprecated alias for --fields").argParser(jsonShape(fieldsSchema)).hideHelp()
1939
1282
  );
1940
1283
  textOrFileOption(cmd, "description", { description: "Issue description in wiki markup" });
1941
1284
  examples(cmd, [
@@ -2459,20 +1802,20 @@ function unlink2(parent) {
2459
1802
  import { z as z4 } from "zod";
2460
1803
 
2461
1804
  // src/utils/multi-value.ts
2462
- import { InvalidArgumentError as InvalidArgumentError5 } from "commander";
1805
+ import { InvalidArgumentError as InvalidArgumentError2 } from "commander";
2463
1806
  function parseMultiValue(flagName, raw) {
2464
1807
  const items = raw.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
2465
1808
  if (items.length === 0) {
2466
- throw new InvalidArgumentError5(`${flagName} cannot be empty`);
1809
+ throw new InvalidArgumentError2(`${flagName} cannot be empty`);
2467
1810
  }
2468
1811
  const lonePrefix = items.find((s) => s === "+" || s === "-");
2469
1812
  if (lonePrefix !== void 0) {
2470
- throw new InvalidArgumentError5(`${flagName} has an empty value after '${lonePrefix}' prefix`);
1813
+ throw new InvalidArgumentError2(`${flagName} has an empty value after '${lonePrefix}' prefix`);
2471
1814
  }
2472
1815
  const prefixed = items.filter((s) => s.startsWith("+") || s.startsWith("-"));
2473
1816
  const bare = items.filter((s) => !s.startsWith("+") && !s.startsWith("-"));
2474
1817
  if (prefixed.length > 0 && bare.length > 0) {
2475
- throw new InvalidArgumentError5(
1818
+ throw new InvalidArgumentError2(
2476
1819
  `${flagName} mixes set and mutate syntax. Either all values have +/- prefix, or none do.`
2477
1820
  );
2478
1821
  }
@@ -2601,12 +1944,12 @@ function create4(parent) {
2601
1944
  }
2602
1945
 
2603
1946
  // src/commands/issue/worklog/delete.ts
2604
- import { Option as Option6 } from "commander";
1947
+ import { Option as Option5 } from "commander";
2605
1948
  var ADJUST_ESTIMATE = ["new", "leave", "manual", "auto"];
2606
1949
  function deleteWorklog(parent) {
2607
1950
  const cmd = parent.command("delete").description("Delete a worklog entry").argument("<key>", "Issue key", issueKey);
2608
1951
  subEntityOption(cmd, "worklog", { mandatory: true });
2609
- cmd.addOption(new Option6("--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"', text).option(
1952
+ cmd.addOption(new Option5("--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"', text).option(
2610
1953
  "--increase-by <amount>",
2611
1954
  'Amount to increase the estimate by; required when --adjust-estimate is "manual"',
2612
1955
  text
@@ -2647,12 +1990,12 @@ function list5(parent) {
2647
1990
  }
2648
1991
 
2649
1992
  // src/commands/issue/worklog/update.ts
2650
- import { Option as Option7 } from "commander";
1993
+ import { Option as Option6 } from "commander";
2651
1994
  var ADJUST_ESTIMATE2 = ["new", "leave", "auto"];
2652
1995
  function update4(parent) {
2653
1996
  const cmd = parent.command("update").description("Update an existing worklog entry").argument("<key>", "Issue key", issueKey);
2654
1997
  subEntityOption(cmd, "worklog", { mandatory: true });
2655
- cmd.option("--time <timeSpent>", "Time spent (e.g., '2h', '30m', '1d 4h')", text).option("--started <datetime>", "Start time in ISO 8601 format", dateTime).addOption(new Option7("--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"', text);
1998
+ cmd.option("--time <timeSpent>", "Time spent (e.g., '2h', '30m', '1d 4h')", text).option("--started <datetime>", "Start time in ISO 8601 format", dateTime).addOption(new Option6("--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"', text);
2656
1999
  commentOption(cmd, { description: "Worklog comment" });
2657
2000
  examples(cmd, [
2658
2001
  'PROJ-123 --worklog-id 12345 --time "1h 30m"',
@@ -2836,10 +2179,10 @@ function issues2(parent) {
2836
2179
  }
2837
2180
 
2838
2181
  // src/commands/sprint/list.ts
2839
- import { Option as Option8 } from "commander";
2182
+ import { Option as Option7 } from "commander";
2840
2183
  var SPRINT_STATES = ["future", "active", "closed"];
2841
2184
  function list7(parent) {
2842
- const cmd = parent.command("list").description("List sprints for a board").requiredOption("--board <id>", "Board ID", positiveInt).addOption(new Option8("--state <state>", "Filter by sprint state").choices(SPRINT_STATES));
2185
+ const cmd = parent.command("list").description("List sprints for a board").requiredOption("--board <id>", "Board ID", positiveInt).addOption(new Option7("--state <state>", "Filter by sprint state").choices(SPRINT_STATES));
2843
2186
  examples(cmd, ["--board 42", "--board 42 --state active"]);
2844
2187
  cmd.action(async (opts) => {
2845
2188
  const client = getClient();
@@ -2852,10 +2195,10 @@ function list7(parent) {
2852
2195
  }
2853
2196
 
2854
2197
  // src/commands/sprint/update.ts
2855
- import { Argument as Argument4, Option as Option9 } from "commander";
2198
+ import { Argument as Argument4, Option as Option8 } from "commander";
2856
2199
  var SPRINT_STATES2 = ["future", "active", "closed"];
2857
2200
  function update5(parent) {
2858
- const cmd = parent.command("update").description("Update an existing sprint").addArgument(new Argument4("<id>", "Sprint ID").argParser(positiveInt)).option("--name <name>", "New sprint name", text).addOption(new Option9("--state <state>", "New sprint state").choices(SPRINT_STATES2)).option("--start-date <date>", "New start date in ISO 8601 format", text).option("--end-date <date>", "New end date in ISO 8601 format", text).option("--goal <goal>", "New sprint goal", text);
2201
+ const cmd = parent.command("update").description("Update an existing sprint").addArgument(new Argument4("<id>", "Sprint ID").argParser(positiveInt)).option("--name <name>", "New sprint name", text).addOption(new Option8("--state <state>", "New sprint state").choices(SPRINT_STATES2)).option("--start-date <date>", "New start date in ISO 8601 format", text).option("--end-date <date>", "New end date in ISO 8601 format", text).option("--goal <goal>", "New sprint goal", text);
2859
2202
  examples(cmd, [
2860
2203
  '100 --name "Sprint 10 - Extended"',
2861
2204
  "100 --state active",
@@ -2895,6 +2238,484 @@ function registerSprintCommands(program) {
2895
2238
  deleteSprint(sprint);
2896
2239
  }
2897
2240
 
2241
+ // src/commands/structure/create.ts
2242
+ function create6(parent) {
2243
+ const cmd = parent.command("create").description("Create a new, empty structure").requiredOption("--name <text>", "Structure name", text).option("--description <text>", "Structure description", text);
2244
+ examples(cmd, ['--name "Release 24.3"', '--name Scratch --description "Throwaway"']);
2245
+ cmd.action(async (opts) => {
2246
+ const client = getClient();
2247
+ output(await client.structure.create({ name: opts.name, description: opts.description }));
2248
+ });
2249
+ }
2250
+
2251
+ // src/commands/structure/delete.ts
2252
+ function deleteStructure(parent) {
2253
+ const cmd = parent.command("delete").description("Delete a structure and every row in it. The rows go; the underlying Jira issues do not.");
2254
+ subjectArg(cmd, "structureId", { description: "Structure id", parser: positiveInt });
2255
+ examples(cmd, ["119"]);
2256
+ cmd.action(async (structureId) => {
2257
+ const client = getClient();
2258
+ await client.structure.delete({ structureId });
2259
+ output({ deleted: true, structureId });
2260
+ });
2261
+ }
2262
+
2263
+ // src/utils/structure/forest.ts
2264
+ var FOLDER_TYPE_KEY = "com.almworks.jira.structure:type-folder";
2265
+ function parseForest(forest) {
2266
+ const formula = forest.formula ?? "";
2267
+ if (formula.trim() === "") return [];
2268
+ const rows = [];
2269
+ const lastRowAtDepth = /* @__PURE__ */ new Map();
2270
+ for (const component of formula.split(",")) {
2271
+ const parts = component.split(":");
2272
+ if (parts.length < 3) {
2273
+ throw new CliUsageError(`Malformed forest component "${component}": expected rowId:depth:itemId`);
2274
+ }
2275
+ const [rawRowId, rawDepth, rawItemId] = parts;
2276
+ const rowId = Number(rawRowId);
2277
+ const depth = Number(rawDepth);
2278
+ if (!Number.isInteger(rowId) || !Number.isInteger(depth) || depth < 0) {
2279
+ throw new CliUsageError(`Malformed forest component "${component}": rowId and depth must be integers`);
2280
+ }
2281
+ const slash = rawItemId.indexOf("/");
2282
+ const prefix = slash === -1 ? void 0 : rawItemId.slice(0, slash);
2283
+ const numeric = Number(slash === -1 ? rawItemId : rawItemId.slice(slash + 1));
2284
+ if (!Number.isInteger(numeric)) {
2285
+ throw new CliUsageError(`Malformed forest component "${component}": item id is not numeric`);
2286
+ }
2287
+ const itemType = prefix === void 0 ? "issue" : forest.itemTypes?.[prefix] ?? `unknown:${prefix}`;
2288
+ rows.push({
2289
+ rowId,
2290
+ depth,
2291
+ itemId: rawItemId,
2292
+ itemType,
2293
+ itemNumericId: numeric,
2294
+ parentRowId: depth === 0 ? 0 : lastRowAtDepth.get(depth - 1) ?? 0,
2295
+ isFolder: itemType === FOLDER_TYPE_KEY
2296
+ });
2297
+ lastRowAtDepth.set(depth, rowId);
2298
+ for (const seen of [...lastRowAtDepth.keys()]) {
2299
+ if (seen > depth) lastRowAtDepth.delete(seen);
2300
+ }
2301
+ }
2302
+ return rows;
2303
+ }
2304
+ function childrenOf(rows, parentRowId) {
2305
+ return rows.filter((r) => r.parentRowId === parentRowId);
2306
+ }
2307
+ function descendantsOf(rows, parentRowId) {
2308
+ const start = rows.findIndex((r) => r.rowId === parentRowId);
2309
+ if (start === -1) return [];
2310
+ const parentDepth = rows[start].depth;
2311
+ const out = [];
2312
+ for (let i = start + 1; i < rows.length && rows[i].depth > parentDepth; i += 1) out.push(rows[i]);
2313
+ return out;
2314
+ }
2315
+ function withinDepth(rows, parentRowId, maxDepth) {
2316
+ const base = parentRowId === 0 ? -1 : rows.find((r) => r.rowId === parentRowId)?.depth ?? -1;
2317
+ const scope = parentRowId === 0 ? rows : descendantsOf(rows, parentRowId);
2318
+ return scope.filter((r) => r.depth - base <= maxDepth);
2319
+ }
2320
+ function addSpec(itemId, placeholderRowId = -100) {
2321
+ if (placeholderRowId >= 0) {
2322
+ throw new CliUsageError(`Placeholder row id must be negative, got ${placeholderRowId}`);
2323
+ }
2324
+ return `${placeholderRowId}:0:${itemId}`;
2325
+ }
2326
+ function assertRowExists(rows, rowId, structureId, what = "Row") {
2327
+ if (!rows.some((r) => r.rowId === rowId)) {
2328
+ throw new CliNotFoundError(`${what} ${rowId} is not in structure ${structureId}`);
2329
+ }
2330
+ }
2331
+
2332
+ // src/utils/structure/resolve.ts
2333
+ var VALUE_BATCH_SIZE = 500;
2334
+ async function resolveStructureId(client, idOrName) {
2335
+ if (/^\d+$/.test(idOrName)) return Number(idOrName);
2336
+ const { structures } = await client.structure.list();
2337
+ const wanted = idOrName.trim().toLowerCase();
2338
+ const matches = structures.filter((s) => s.name?.trim().toLowerCase() === wanted);
2339
+ if (matches.length === 1) return matches[0].id;
2340
+ if (matches.length === 0) {
2341
+ throw new CliUsageError(
2342
+ `No structure named "${idOrName}"`,
2343
+ "Pass a structure id, or one of the names in detail.available.",
2344
+ { available: structures.map((s) => ({ id: s.id, name: s.name })).slice(0, 50) }
2345
+ );
2346
+ }
2347
+ throw new CliUsageError(
2348
+ `"${idOrName}" matches ${matches.length} structures`,
2349
+ "Names are not unique here; pass the numeric id of the one you meant.",
2350
+ { matches: matches.map((s) => ({ id: s.id, name: s.name })) }
2351
+ );
2352
+ }
2353
+ async function resolveRowNames(client, structureId, rowIds) {
2354
+ const out = /* @__PURE__ */ new Map();
2355
+ if (rowIds.length === 0) return out;
2356
+ for (let i = 0; i < rowIds.length; i += VALUE_BATCH_SIZE) {
2357
+ const batch = rowIds.slice(i, i + VALUE_BATCH_SIZE);
2358
+ const res = await client.structure.getValues({
2359
+ requests: [
2360
+ {
2361
+ forestSpec: { structureId },
2362
+ rows: batch,
2363
+ // `format` is required; without it the API returns an empty `data` array.
2364
+ attributes: [
2365
+ { id: "summary", format: "text" },
2366
+ { id: "key", format: "text" }
2367
+ ]
2368
+ }
2369
+ ]
2370
+ });
2371
+ const data = res.responses?.[0]?.data ?? [];
2372
+ const summaries = data.find((d) => d.attribute.id === "summary")?.values ?? [];
2373
+ const keys = data.find((d) => d.attribute.id === "key")?.values ?? [];
2374
+ batch.forEach((rowId, idx) => {
2375
+ out.set(rowId, { name: summaries[idx] ?? null, key: keys[idx] ?? null });
2376
+ });
2377
+ }
2378
+ return out;
2379
+ }
2380
+ async function withNames(client, structureId, rows) {
2381
+ const names = await resolveRowNames(
2382
+ client,
2383
+ structureId,
2384
+ rows.map((r) => r.rowId)
2385
+ );
2386
+ return rows.map((r) => ({
2387
+ rowId: r.rowId,
2388
+ depth: r.depth,
2389
+ parentRowId: r.parentRowId,
2390
+ type: r.isFolder ? "folder" : r.itemType === "issue" ? "issue" : r.itemType,
2391
+ name: names.get(r.rowId)?.name ?? null,
2392
+ key: names.get(r.rowId)?.key ?? null,
2393
+ itemId: r.itemId
2394
+ }));
2395
+ }
2396
+
2397
+ // src/commands/structure/folder/create.ts
2398
+ function create7(parent) {
2399
+ const cmd = parent.command("create").description("Create a folder in a structure");
2400
+ scopeIdOption(cmd, "structure", { mandatory: true });
2401
+ cmd.requiredOption("--name <text>", "Folder name", text);
2402
+ cmd.option("--under <rowId>", "Parent folder row id (0 = top level)", integer, 0);
2403
+ examples(cmd, ["--structure 42 --name Backlog", '--structure 42 --name "Phase Two" --under 1001']);
2404
+ cmd.action(async (opts) => {
2405
+ const client = getClient();
2406
+ const structureId = await resolveStructureId(client, opts.structure);
2407
+ const forest = await client.structure.getForest({ structureId });
2408
+ if (opts.under !== 0) {
2409
+ assertRowExists(parseForest(forest), opts.under, structureId, "Target parent row");
2410
+ }
2411
+ const result = await client.structure.createFolder({
2412
+ structureId,
2413
+ version: forest.version,
2414
+ name: opts.name,
2415
+ under: opts.under
2416
+ });
2417
+ output({
2418
+ structureId,
2419
+ itemId: result.itemId,
2420
+ rowId: result.newRowIds?.[0] ?? null,
2421
+ name: opts.name,
2422
+ under: opts.under
2423
+ });
2424
+ });
2425
+ }
2426
+
2427
+ // src/commands/structure/folder/get.ts
2428
+ function get7(parent) {
2429
+ const cmd = parent.command("get").description("Get one folder row, with its path and child counts");
2430
+ subjectArg(cmd, "rowId", { description: "Folder row id", parser: positiveInt });
2431
+ scopeIdOption(cmd, "structure", { mandatory: true });
2432
+ examples(cmd, ['1001 --structure "My Structure"', "1001 --structure 42"]);
2433
+ cmd.action(async (rowId, opts) => {
2434
+ const client = getClient();
2435
+ const structureId = await resolveStructureId(client, opts.structure);
2436
+ const rows = parseForest(await client.structure.getForest({ structureId }));
2437
+ const row = rows.find((r) => r.rowId === rowId);
2438
+ if (!row) throw new CliNotFoundError(`Row ${rowId} is not in structure ${structureId}`);
2439
+ const ancestry = [];
2440
+ for (let cur = row; cur.parentRowId !== 0; ) {
2441
+ const next = rows.find((r) => r.rowId === cur.parentRowId);
2442
+ if (!next) break;
2443
+ ancestry.unshift(next);
2444
+ cur = next;
2445
+ }
2446
+ const children = childrenOf(rows, rowId);
2447
+ const [self, ...path] = await withNames(client, structureId, [row, ...ancestry]);
2448
+ output({
2449
+ structureId,
2450
+ ...self,
2451
+ path: path.map((p) => p.name),
2452
+ childCount: children.length,
2453
+ childFolderCount: children.filter((c) => c.isFolder).length,
2454
+ descendantCount: descendantsOf(rows, rowId).length
2455
+ });
2456
+ });
2457
+ }
2458
+
2459
+ // src/commands/structure/folder/list.ts
2460
+ function list8(parent) {
2461
+ const cmd = parent.command("list").description("List folders in a structure");
2462
+ scopeIdOption(cmd, "structure", { mandatory: true });
2463
+ cmd.option("--parent <rowId>", "Only look under this folder row (default: top level)", integer, 0);
2464
+ cmd.option("--depth <n>", "How many levels below the parent to include", integer, 1);
2465
+ cmd.option("--query <text>", "Only folders whose name contains this text, at any depth", text);
2466
+ paginationOptions(cmd, { defaultLimit: 25 });
2467
+ cmd.option("--all", "Collect every match, not just one page");
2468
+ examples(cmd, [
2469
+ '--structure "My Structure"',
2470
+ '--structure "My Structure" --query billing',
2471
+ "--structure 42 --parent 1001 --depth 2"
2472
+ ]);
2473
+ cmd.action(
2474
+ async (opts) => {
2475
+ const client = getClient();
2476
+ const structureId = await resolveStructureId(client, opts.structure);
2477
+ const forest = await client.structure.getForest({ structureId });
2478
+ const rows = parseForest(forest);
2479
+ const scoped = opts.query ? opts.parent === 0 ? rows : descendantsOf(rows, opts.parent) : withinDepth(rows, opts.parent, opts.depth);
2480
+ const folders = scoped.filter((r) => r.isFolder);
2481
+ let named = await withNames(client, structureId, folders);
2482
+ if (opts.query) {
2483
+ const needle = opts.query.toLowerCase();
2484
+ named = named.filter((r) => (r.name ?? "").toLowerCase().includes(needle));
2485
+ }
2486
+ const start = opts.start ?? 0;
2487
+ const page = opts.all ? named.slice(start) : named.slice(start, start + opts.limit);
2488
+ output({ structureId, total: named.length, start, count: page.length, folders: page });
2489
+ }
2490
+ );
2491
+ }
2492
+
2493
+ // src/commands/structure/folder/index.ts
2494
+ function registerFolderCommands(structure) {
2495
+ const folder = structure.command("folder").description("Find and create folders inside a structure");
2496
+ examples(folder, ['list --structure "My Structure" --query billing']);
2497
+ list8(folder);
2498
+ get7(folder);
2499
+ create7(folder);
2500
+ }
2501
+
2502
+ // src/commands/structure/get.ts
2503
+ function get8(parent) {
2504
+ const cmd = parent.command("get").description("Get one structure by id");
2505
+ subjectArg(cmd, "structureId", { description: "Structure id", parser: positiveInt });
2506
+ cmd.option("--permissions", "Include the structure\u2019s permission rules");
2507
+ examples(cmd, ["1", "1 --permissions"]);
2508
+ cmd.action(async (structureId, opts) => {
2509
+ const client = getClient();
2510
+ output(await client.structure.get({ structureId, withPermissions: opts.permissions }));
2511
+ });
2512
+ }
2513
+
2514
+ // src/commands/structure/list.ts
2515
+ function list9(parent) {
2516
+ const cmd = parent.command("list").description("List every structure visible to you");
2517
+ cmd.option("--permissions", "Include each structure\u2019s permission rules");
2518
+ paginationOptions(cmd, { defaultLimit: 25 });
2519
+ cmd.option("--all", "Collect every match, not just one page");
2520
+ examples(cmd, ["", "--limit 50", "--all --permissions"]);
2521
+ cmd.action(async (opts) => {
2522
+ if (opts.all && process.argv.includes("--limit")) {
2523
+ const { CliUsageError: CliUsageError2 } = await import("./dist-JXPWCMQP.js");
2524
+ throw new CliUsageError2("--all and --limit ask for opposite things; pass one or the other");
2525
+ }
2526
+ const client = getClient();
2527
+ const { structures } = await client.structure.list({ withPermissions: opts.permissions });
2528
+ const start = opts.start ?? 0;
2529
+ const page = opts.all ? structures.slice(start) : structures.slice(start, start + opts.limit);
2530
+ output({ total: structures.length, start, count: page.length, structures: page });
2531
+ });
2532
+ }
2533
+
2534
+ // src/commands/structure/row/add.ts
2535
+ function add(parent) {
2536
+ const cmd = parent.command("add").description("Add an issue to a structure, under a folder");
2537
+ scopeIdOption(cmd, "structure", { mandatory: true });
2538
+ cmd.requiredOption("--issue <key>", "Issue key to place, e.g. PROJ-123", text);
2539
+ cmd.option("--under <rowId>", "Parent folder row id (0 = top level)", integer, 0);
2540
+ examples(cmd, ["--structure 42 --issue PROJ-123 --under 1001", '--structure "My Structure" --issue PROJ-123']);
2541
+ cmd.action(async (opts) => {
2542
+ const client = getClient();
2543
+ const structureId = await resolveStructureId(client, opts.structure);
2544
+ const issue = await client.issues.get({ issueKeyOrId: opts.issue, fields: ["summary"] });
2545
+ const issueId = Number(issue.id);
2546
+ const forest = await client.structure.getForest({ structureId });
2547
+ const rows = parseForest(forest);
2548
+ if (opts.under !== 0) assertRowExists(rows, opts.under, structureId, "Target parent row");
2549
+ const already = childrenOf(rows, opts.under).find((r) => r.itemType === "issue" && r.itemNumericId === issueId);
2550
+ const elsewhere = rows.filter((r) => r.itemType === "issue" && r.itemNumericId === issueId && r.parentRowId !== opts.under).map((r) => ({ rowId: r.rowId, parentRowId: r.parentRowId }));
2551
+ if (already) {
2552
+ output({
2553
+ structureId,
2554
+ issue: issue.key,
2555
+ rowId: already.rowId,
2556
+ under: opts.under,
2557
+ added: false,
2558
+ reason: "already under this parent",
2559
+ alsoAppearsAt: elsewhere
2560
+ });
2561
+ return;
2562
+ }
2563
+ const result = await client.structure.updateForest({
2564
+ structureId,
2565
+ version: forest.version,
2566
+ actions: [{ action: "add", under: opts.under, after: 0, before: 0, forest: addSpec(issueId) }]
2567
+ });
2568
+ output({
2569
+ structureId,
2570
+ issue: issue.key,
2571
+ rowId: result.newRowIds?.[0] ?? null,
2572
+ under: opts.under,
2573
+ added: true,
2574
+ alsoAppearsAt: elsewhere
2575
+ });
2576
+ });
2577
+ }
2578
+
2579
+ // src/commands/structure/row/list.ts
2580
+ function list10(parent) {
2581
+ const cmd = parent.command("list").description("List rows (issues and folders) under a parent row");
2582
+ scopeIdOption(cmd, "structure", { mandatory: true });
2583
+ cmd.option("--parent <rowId>", "Parent row id (0 = top level)", integer, 0);
2584
+ cmd.option("--depth <n>", "How many levels below the parent to include", integer, 1);
2585
+ paginationOptions(cmd, { defaultLimit: 25 });
2586
+ cmd.option("--all", "Collect every match, not just one page");
2587
+ examples(cmd, ["--structure 42", '--structure "My Structure" --parent 1001 --depth 2']);
2588
+ cmd.action(
2589
+ async (opts) => {
2590
+ const client = getClient();
2591
+ const structureId = await resolveStructureId(client, opts.structure);
2592
+ const rows = parseForest(await client.structure.getForest({ structureId }));
2593
+ const scoped = withinDepth(rows, opts.parent, opts.depth);
2594
+ const start = opts.start ?? 0;
2595
+ const page = opts.all ? scoped.slice(start) : scoped.slice(start, start + opts.limit);
2596
+ output({
2597
+ structureId,
2598
+ total: scoped.length,
2599
+ start,
2600
+ count: page.length,
2601
+ rows: await withNames(client, structureId, page)
2602
+ });
2603
+ }
2604
+ );
2605
+ }
2606
+
2607
+ // src/commands/structure/row/move.ts
2608
+ function move(parent) {
2609
+ const cmd = parent.command("move").description("Move a row to a different parent");
2610
+ subjectArg(cmd, "rowId", { description: "Row id to move (not the issue id)", parser: positiveInt });
2611
+ scopeIdOption(cmd, "structure", { mandatory: true });
2612
+ cmd.requiredOption("--under <rowId>", "New parent row id (0 = top level)", integer);
2613
+ examples(cmd, ["1002 --structure 42 --under 1001", '1002 --structure "My Structure" --under 0']);
2614
+ cmd.action(async (rowId, opts) => {
2615
+ const client = getClient();
2616
+ const structureId = await resolveStructureId(client, opts.structure);
2617
+ const forest = await client.structure.getForest({ structureId });
2618
+ const rows = parseForest(forest);
2619
+ assertRowExists(rows, rowId, structureId);
2620
+ if (opts.under !== 0) assertRowExists(rows, opts.under, structureId, "Target parent row");
2621
+ const row = rows.find((r) => r.rowId === rowId);
2622
+ const result = await client.structure.updateForest({
2623
+ structureId,
2624
+ version: forest.version,
2625
+ actions: [{ action: "move", rowId, under: opts.under, after: 0, before: 0 }]
2626
+ });
2627
+ output({ structureId, rowId, from: row.parentRowId, under: opts.under, moved: result.successfulActions > 0 });
2628
+ });
2629
+ }
2630
+
2631
+ // src/commands/structure/row/remove.ts
2632
+ function remove(parent) {
2633
+ const cmd = parent.command("remove").description("Remove a row from a structure. The row goes; the underlying Jira issue does not.");
2634
+ subjectArg(cmd, "rowId", { description: "Row id to remove (not the issue id)", parser: positiveInt });
2635
+ scopeIdOption(cmd, "structure", { mandatory: true });
2636
+ examples(cmd, ["1002 --structure 42", '1002 --structure "My Structure"']);
2637
+ cmd.action(async (rowId, opts) => {
2638
+ const client = getClient();
2639
+ const structureId = await resolveStructureId(client, opts.structure);
2640
+ const forest = await client.structure.getForest({ structureId });
2641
+ const rows = parseForest(forest);
2642
+ assertRowExists(rows, rowId, structureId);
2643
+ const removedDescendants = descendantsOf(rows, rowId).length;
2644
+ const result = await client.structure.updateForest({
2645
+ structureId,
2646
+ version: forest.version,
2647
+ actions: [{ action: "remove", rowId }]
2648
+ });
2649
+ output({ structureId, rowId, removed: result.successfulActions > 0, removedDescendants });
2650
+ });
2651
+ }
2652
+
2653
+ // src/commands/structure/row/index.ts
2654
+ function registerRowCommands(structure) {
2655
+ const row = structure.command("row").description("Place, move and remove rows in a structure. A row is one appearance of an issue, not the issue.");
2656
+ examples(row, ["add --structure 42 --issue PROJ-123 --under 1001"]);
2657
+ list10(row);
2658
+ add(row);
2659
+ move(row);
2660
+ remove(row);
2661
+ }
2662
+
2663
+ // src/commands/structure/search.ts
2664
+ function search3(parent) {
2665
+ const cmd = parent.command("search").description("Find issues by their place in a structure, using S-JQL");
2666
+ subjectArg(cmd, "sjql", {
2667
+ description: `S-JQL query, e.g. "child of folder('Billing')"`,
2668
+ parser: nonEmpty
2669
+ });
2670
+ scopeIdOption(cmd, "structure", { mandatory: true });
2671
+ cmd.option("--fields <fields>", "Comma-separated fields to return", text);
2672
+ paginationOptions(cmd, { defaultLimit: 25, maxLimit: 50 });
2673
+ examples(cmd, [
2674
+ `"child of folder('Billing')" --structure "My Structure"`,
2675
+ `"descendants of folder('Billing')" --structure 42 --limit 50`
2676
+ ]);
2677
+ cmd.action(async (sjql, opts) => {
2678
+ for (const [name, value] of [
2679
+ ["--structure", opts.structure],
2680
+ ["<sjql>", sjql]
2681
+ ]) {
2682
+ if (value.includes('"')) {
2683
+ throw new CliUsageError(
2684
+ `${name} must not contain a double quote \u2014 S-JQL uses single quotes inside, e.g. "child of folder('Billing')"`
2685
+ );
2686
+ }
2687
+ }
2688
+ const client = getClient();
2689
+ const jql = `issue in structure("${opts.structure}", "${sjql}")`;
2690
+ const res = await client.issues.search({
2691
+ jql,
2692
+ startAt: opts.start ?? 0,
2693
+ maxResults: opts.limit,
2694
+ fields: opts.fields
2695
+ });
2696
+ output({
2697
+ jql,
2698
+ total: res.total,
2699
+ startAt: res.startAt,
2700
+ count: res.issues?.length ?? 0,
2701
+ issues: (res.issues ?? []).map(transformIssue)
2702
+ });
2703
+ });
2704
+ }
2705
+
2706
+ // src/commands/structure/index.ts
2707
+ function registerStructureCommands(program) {
2708
+ const structure = program.command("structure").description("Structure plugin \u2014 browse and edit the hierarchies issues are filed into");
2709
+ examples(structure, ["list", `search "child of folder('Billing')" --structure "My Structure"`]);
2710
+ list9(structure);
2711
+ get8(structure);
2712
+ create6(structure);
2713
+ deleteStructure(structure);
2714
+ search3(structure);
2715
+ registerFolderCommands(structure);
2716
+ registerRowCommands(structure);
2717
+ }
2718
+
2898
2719
  // src/commands/token/client.ts
2899
2720
  import { JiraClient as JiraClient2 } from "jira-data-center-client";
2900
2721
  function getTokenClient(options = {}) {
@@ -2914,7 +2735,7 @@ function getTokenClient(options = {}) {
2914
2735
  }
2915
2736
 
2916
2737
  // src/commands/token/create.ts
2917
- function create6(parent) {
2738
+ function create8(parent) {
2918
2739
  const cmd = parent.command("create").description("Create a Personal Access Token. The secret is returned exactly once.").requiredOption("--name <name>", "Token name", text).option(
2919
2740
  "--expiration-duration <days>",
2920
2741
  "Token lifetime in days. Omit for a non-expiring token (admin policy permitting).",
@@ -2941,7 +2762,7 @@ function create6(parent) {
2941
2762
  }
2942
2763
 
2943
2764
  // src/commands/token/list.ts
2944
- function list8(parent) {
2765
+ function list11(parent) {
2945
2766
  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", text).option(
2946
2767
  "--basic-password <p>",
2947
2768
  "Override $JIRA_BASIC_PASSWORD (caution: visible in process table; prefer the env var)",
@@ -2984,13 +2805,13 @@ function registerTokenCommands(program) {
2984
2805
  "after",
2985
2806
  "\nAuth:\n Requires JIRA_BASIC_USERNAME + JIRA_BASIC_PASSWORD\n (or --basic-username / --basic-password on any subcommand)."
2986
2807
  );
2987
- create6(token);
2988
- list8(token);
2808
+ create8(token);
2809
+ list11(token);
2989
2810
  revoke(token);
2990
2811
  }
2991
2812
 
2992
2813
  // src/commands/user/get.ts
2993
- function get7(parent) {
2814
+ function get9(parent) {
2994
2815
  const cmd = parent.command("get").description("Get a user profile by exact username or key").argument("<username>", "Username or user key", nonEmpty).option("--by-key", "Treat the positional argument as a user key instead of a username");
2995
2816
  examples(cmd, ["jsmith", "JIRAUSER10100 --by-key"]);
2996
2817
  cmd.action(async (identifier, opts) => {
@@ -3012,7 +2833,7 @@ function me(parent) {
3012
2833
  }
3013
2834
 
3014
2835
  // src/commands/user/search.ts
3015
- function search3(parent) {
2836
+ function search4(parent) {
3016
2837
  const cmd = parent.command("search").description("Search users by partial username, display name or email").argument("<query>", "Search query", nonEmpty).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);
3017
2838
  examples(cmd, ["Smith", '"John Smith"', "jsmith@example.com --limit 5"]);
3018
2839
  cmd.action(async (query, opts) => {
@@ -3033,8 +2854,8 @@ function registerUserCommands(program) {
3033
2854
  const user = program.command("user").description("User operations");
3034
2855
  examples(user, ["me", "get jsmith", "search Smith"]);
3035
2856
  me(user);
3036
- get7(user);
3037
- search3(user);
2857
+ get9(user);
2858
+ search4(user);
3038
2859
  }
3039
2860
 
3040
2861
  // src/utils/xray/guards.ts
@@ -3050,7 +2871,7 @@ function assertAtLeastOne(opts, keys) {
3050
2871
  }
3051
2872
 
3052
2873
  // src/commands/xray/execution/add.ts
3053
- function add(parent) {
2874
+ function add2(parent) {
3054
2875
  const cmd = parent.command("add").description("Add tests to a Test Execution, directly (--test) and/or by expanding Test Sets (--set)").argument("<execKey>", "Test Execution issue key (e.g. PROJ-200)", issueKey).option("--test <keys>", "Comma-separated test issue keys to add (e.g. PROJ-1,PROJ-2)", keyList).option(
3055
2876
  "--set <keys>",
3056
2877
  "Comma-separated Test Set keys; each set's current tests are added to the execution (e.g. PROJ-300)",
@@ -3068,8 +2889,8 @@ function add(parent) {
3068
2889
  const keys = new Set(opts.test ?? []);
3069
2890
  if (opts.set) {
3070
2891
  const lists = await Promise.all(opts.set.map((setKey) => client.testSets.listTests({ setKey })));
3071
- for (const list21 of lists) {
3072
- const tests = Array.isArray(list21) ? list21 : list21.tests ?? [];
2892
+ for (const list24 of lists) {
2893
+ const tests = Array.isArray(list24) ? list24 : list24.tests ?? [];
3073
2894
  for (const t of tests) {
3074
2895
  if (t.key) keys.add(String(t.key));
3075
2896
  }
@@ -3115,7 +2936,7 @@ function deleteXrayIssue(client, key) {
3115
2936
  }
3116
2937
 
3117
2938
  // src/commands/xray/execution/create.ts
3118
- function create7(parent) {
2939
+ function create9(parent) {
3119
2940
  const cmd = parent.command("create").description("Create a new Xray Test Execution issue").requiredOption("--project <key>", "Project key (e.g. PROJ)", text).requiredOption("--summary <text>", "Test Execution summary (title)", text);
3120
2941
  textOrFileOption(cmd, "description", { description: "Test Execution description" });
3121
2942
  examples(cmd, [
@@ -3147,7 +2968,7 @@ function deleteExecution(parent) {
3147
2968
  }
3148
2969
 
3149
2970
  // src/commands/xray/execution/list.ts
3150
- function list9(parent) {
2971
+ function list12(parent) {
3151
2972
  const cmd = parent.command("list").description(
3152
2973
  "List Test Executions that contain a given test (inverse view). For the tests inside an execution, use: xray test list --execution <key>."
3153
2974
  ).requiredOption("--test <key>", "Test issue key whose executions to list (e.g. PROJ-584)", issueKey);
@@ -3160,7 +2981,7 @@ function list9(parent) {
3160
2981
  }
3161
2982
 
3162
2983
  // src/commands/xray/execution/remove.ts
3163
- function remove(parent) {
2984
+ function remove2(parent) {
3164
2985
  const cmd = parent.command("remove").description("Remove tests from a Test Execution").argument("<execKey>", "Test Execution issue key (e.g. PROJ-200)", issueKey).requiredOption("--test <keys>", "Comma-separated test issue keys to remove (e.g. PROJ-1,PROJ-2)", keyList);
3165
2986
  examples(cmd, ["PROJ-200 --test PROJ-1", "PROJ-200 --test PROJ-1,PROJ-2"]);
3166
2987
  cmd.action(async (execKey, opts) => {
@@ -3193,17 +3014,17 @@ function registerExecutionCommands(xray) {
3193
3014
  "add PROJ-200 --test PROJ-1,PROJ-2",
3194
3015
  "list --test PROJ-584"
3195
3016
  ]);
3196
- create7(execution2);
3017
+ create9(execution2);
3197
3018
  update6(execution2);
3198
3019
  deleteExecution(execution2);
3199
- add(execution2);
3200
- remove(execution2);
3201
- list9(execution2);
3020
+ add2(execution2);
3021
+ remove2(execution2);
3022
+ list12(execution2);
3202
3023
  }
3203
3024
 
3204
3025
  // src/commands/xray/export/feature.ts
3205
- import { writeFileSync as writeFileSync2 } from "fs";
3206
- import { basename, join as join5 } from "path";
3026
+ import { writeFileSync } from "fs";
3027
+ import { basename, join as join3 } from "path";
3207
3028
  function feature(parent) {
3208
3029
  const cmd = parent.command("feature").description("Export Gherkin .feature files for the given test keys").requiredOption("--test <keys>", "Comma-separated Test, Set, Plan, or Execution issue keys", keyList).option("--output <path>", "Write the exported file to this path (default: ./<filename> from response)", text).option("--zip", "Request a zip archive instead of a single .feature file");
3209
3030
  examples(cmd, ["--test PROJ-584 --output ai-584.feature"]);
@@ -3213,8 +3034,8 @@ function feature(parent) {
3213
3034
  keys: opts.test,
3214
3035
  zip: opts.zip
3215
3036
  });
3216
- const writePath = opts.output ?? join5(".", basename(filename));
3217
- writeFileSync2(writePath, data);
3037
+ const writePath = opts.output ?? join3(".", basename(filename));
3038
+ writeFileSync(writePath, data);
3218
3039
  output({ written: writePath, bytes: data.length });
3219
3040
  });
3220
3041
  }
@@ -3226,7 +3047,7 @@ function registerExportCommands(xray) {
3226
3047
  }
3227
3048
 
3228
3049
  // src/commands/xray/field/list.ts
3229
- function list10(parent) {
3050
+ function list13(parent) {
3230
3051
  const cmd = parent.command("list").description("List Xray custom fields available on this instance");
3231
3052
  examples(cmd, [""]);
3232
3053
  cmd.action(async () => {
@@ -3239,11 +3060,11 @@ function list10(parent) {
3239
3060
  function registerFieldCommands2(xray) {
3240
3061
  const field = xray.command("field").description("Xray field discovery");
3241
3062
  examples(field, ["list"]);
3242
- list10(field);
3063
+ list13(field);
3243
3064
  }
3244
3065
 
3245
3066
  // src/commands/xray/folder/add.ts
3246
- function add2(parent) {
3067
+ function add3(parent) {
3247
3068
  const cmd = parent.command("add").description("Add tests to a folder in the test repository").argument("<folderId>", "Folder id (from folder list)", positiveInt).requiredOption("--project <key>", "Project key (e.g. PROJ)", text).requiredOption("--test <keys>", "Comma-separated test issue keys to add (e.g. PROJ-1,PROJ-2)", keyList);
3248
3069
  examples(cmd, ["818 --project PROJ --test PROJ-1", "818 --project PROJ --test PROJ-1,PROJ-2,PROJ-3"]);
3249
3070
  cmd.action(async (folderId, opts) => {
@@ -3258,7 +3079,7 @@ function add2(parent) {
3258
3079
  }
3259
3080
 
3260
3081
  // src/commands/xray/folder/create.ts
3261
- function create8(parent) {
3082
+ function create10(parent) {
3262
3083
  const cmd = parent.command("create").description("Create a new folder in the test repository").requiredOption("--project <key>", "Project key (e.g. PROJ)", text).option("--parent-id <id>", "Parent folder id (-1 = root)", integer, -1).requiredOption("--name <text>", "Folder name", text);
3263
3084
  examples(cmd, [
3264
3085
  "--project PROJ --name Smoke",
@@ -3288,7 +3109,7 @@ function deleteFolder(parent) {
3288
3109
  }
3289
3110
 
3290
3111
  // src/commands/xray/folder/list.ts
3291
- function list11(parent) {
3112
+ function list14(parent) {
3292
3113
  const cmd = parent.command("list").description("List the test repository folder tree for a project").requiredOption("--project <key>", "Project key (e.g. PROJ)", text);
3293
3114
  examples(cmd, ["--project PROJ", "--project PROJ"]);
3294
3115
  cmd.action(async (opts) => {
@@ -3299,7 +3120,7 @@ function list11(parent) {
3299
3120
  }
3300
3121
 
3301
3122
  // src/commands/xray/folder/remove.ts
3302
- function remove2(parent) {
3123
+ function remove3(parent) {
3303
3124
  const cmd = parent.command("remove").description("Remove tests from a folder in the test repository").argument("<folderId>", "Folder id (from folder list)", positiveInt).requiredOption("--project <key>", "Project key (e.g. PROJ)", text).requiredOption("--test <keys>", "Comma-separated test issue keys to remove (e.g. PROJ-1,PROJ-2)", keyList);
3304
3125
  examples(cmd, ["818 --project PROJ --test PROJ-1", "818 --project PROJ --test PROJ-1,PROJ-2"]);
3305
3126
  cmd.action(async (folderId, opts) => {
@@ -3337,28 +3158,28 @@ function update7(parent) {
3337
3158
  }
3338
3159
 
3339
3160
  // src/commands/xray/folder/index.ts
3340
- function registerFolderCommands(xray) {
3161
+ function registerFolderCommands2(xray) {
3341
3162
  const folder = xray.command("folder").description(
3342
3163
  "Xray Test Repository folder management (rename/reorder within parent only \u2014 cross-parent moves are UI-only, not supported by the API). To list tests in a folder use: xray test list --folder <id> --project <key>"
3343
3164
  );
3344
3165
  examples(folder, ["list --project PROJ", "create --project PROJ --parent-id -1 --name Smoke"]);
3345
- list11(folder);
3346
- create8(folder);
3166
+ list14(folder);
3167
+ create10(folder);
3347
3168
  update7(folder);
3348
3169
  deleteFolder(folder);
3349
- add2(folder);
3350
- remove2(folder);
3170
+ add3(folder);
3171
+ remove3(folder);
3351
3172
  }
3352
3173
 
3353
3174
  // src/commands/xray/import/execution.ts
3354
- import { readFileSync as readFileSync4 } from "fs";
3175
+ import { readFileSync } from "fs";
3355
3176
  import { basename as basename2 } from "path";
3356
- import { Option as Option10 } from "commander";
3177
+ import { Option as Option9 } from "commander";
3357
3178
  var FORMATS = ["xray", "junit", "testng", "nunit", "xunit", "robot", "cucumber", "behave"];
3358
3179
  var NEEDS_SCOPE = /* @__PURE__ */ new Set(["junit", "testng", "nunit", "xunit", "robot"]);
3359
3180
  function execution(parent) {
3360
3181
  const cmd = parent.command("execution").description("Import test execution results into Xray").requiredOption("--file <path>", "Path to the result file", filePath).addOption(
3361
- new Option10(
3182
+ new Option9(
3362
3183
  "--format <format>",
3363
3184
  "Result format. xray/cucumber/behave: JSON body, no --project/--execution required. junit/testng/nunit/xunit/robot: XML body, requires either --project or --execution."
3364
3185
  ).choices(FORMATS).makeOptionMandatory()
@@ -3383,7 +3204,7 @@ function execution(parent) {
3383
3204
  `--project or --execution is required for ${opts.format} format. Provide --project <key> to create a new execution, or --execution <key> to import into an existing one.`
3384
3205
  );
3385
3206
  }
3386
- const body = readFileSync4(opts.file);
3207
+ const body = readFileSync(opts.file);
3387
3208
  const query = {};
3388
3209
  if (opts.project) query["projectKey"] = opts.project;
3389
3210
  if (opts.execution) query["testExecKey"] = opts.execution;
@@ -3404,13 +3225,13 @@ function execution(parent) {
3404
3225
  }
3405
3226
 
3406
3227
  // src/commands/xray/import/feature.ts
3407
- import { readFileSync as readFileSync5 } from "fs";
3228
+ import { readFileSync as readFileSync2 } from "fs";
3408
3229
  import { basename as basename3 } from "path";
3409
3230
  function feature2(parent) {
3410
3231
  const cmd = parent.command("feature").description("Import a Gherkin .feature file into the Xray test repository (multipart)").requiredOption("--file <path>", "Path to the .feature file", filePath).requiredOption("--project <text>", "Project key to import the feature into", text);
3411
3232
  examples(cmd, ["--file login.feature --project PROJ"]);
3412
3233
  cmd.action(async (opts) => {
3413
- const fileBuffer = readFileSync5(opts.file);
3234
+ const fileBuffer = readFileSync2(opts.file);
3414
3235
  const filename = basename3(opts.file);
3415
3236
  const client = getClient();
3416
3237
  const result = await client.xrayImport.importFeature({
@@ -3430,7 +3251,7 @@ function registerImportCommands(xray) {
3430
3251
  }
3431
3252
 
3432
3253
  // src/commands/xray/plan/add.ts
3433
- function add3(parent) {
3254
+ function add4(parent) {
3434
3255
  const cmd = parent.command("add").description("Add tests and/or executions to a Test Plan").argument("<planKey>", "Test Plan issue key (e.g. PROJ-573)", issueKey).option("--test <keys>", "Comma-separated test issue keys to add (e.g. PROJ-1,PROJ-2)", keyList).option("--execution <keys>", "Comma-separated test execution keys to add (e.g. PROJ-200,PROJ-201)", keyList);
3435
3256
  examples(cmd, [
3436
3257
  "PROJ-573 --test PROJ-1,PROJ-2",
@@ -3449,7 +3270,7 @@ function add3(parent) {
3449
3270
  }
3450
3271
 
3451
3272
  // src/commands/xray/plan/create.ts
3452
- function create9(parent) {
3273
+ function create11(parent) {
3453
3274
  const cmd = parent.command("create").description("Create a new Xray Test Plan issue").requiredOption("--project <key>", "Project key (e.g. PROJ)", text).requiredOption("--summary <text>", "Test Plan summary (title)", text);
3454
3275
  textOrFileOption(cmd, "description", { description: "Test Plan description" });
3455
3276
  examples(cmd, [
@@ -3481,7 +3302,7 @@ function deletePlan(parent) {
3481
3302
  }
3482
3303
 
3483
3304
  // src/commands/xray/plan/list.ts
3484
- function list12(parent) {
3305
+ function list15(parent) {
3485
3306
  const cmd = parent.command("list").description(
3486
3307
  "List Test Plans that contain a given test (inverse view). For the tests inside a plan, use: xray test list --plan <key>. For executions in a plan, use: xray execution list --plan <key>."
3487
3308
  ).requiredOption("--test <key>", "Test issue key whose plans to list (e.g. PROJ-574)", issueKey);
@@ -3494,7 +3315,7 @@ function list12(parent) {
3494
3315
  }
3495
3316
 
3496
3317
  // src/commands/xray/plan/remove.ts
3497
- function remove3(parent) {
3318
+ function remove4(parent) {
3498
3319
  const cmd = parent.command("remove").description("Remove tests and/or executions from a Test Plan").argument("<planKey>", "Test Plan issue key (e.g. PROJ-573)", issueKey).option("--test <keys>", "Comma-separated test issue keys to remove (e.g. PROJ-1,PROJ-2)", keyList).option("--execution <keys>", "Comma-separated test execution keys to remove (e.g. PROJ-200)", keyList);
3499
3320
  examples(cmd, [
3500
3321
  "PROJ-573 --test PROJ-1",
@@ -3536,16 +3357,16 @@ function registerPlanCommands(xray) {
3536
3357
  "add PROJ-573 --test PROJ-1 --execution PROJ-200",
3537
3358
  "list --test PROJ-574"
3538
3359
  ]);
3539
- create9(plan);
3360
+ create11(plan);
3540
3361
  update8(plan);
3541
3362
  deletePlan(plan);
3542
- add3(plan);
3543
- remove3(plan);
3544
- list12(plan);
3363
+ add4(plan);
3364
+ remove4(plan);
3365
+ list15(plan);
3545
3366
  }
3546
3367
 
3547
3368
  // src/commands/xray/precondition/add.ts
3548
- function add4(parent) {
3369
+ function add5(parent) {
3549
3370
  const cmd = parent.command("add").description("Add tests to a Pre-Condition").argument("<preKey>", "Pre-Condition issue key (e.g. PROJ-50)", issueKey).requiredOption("--test <keys>", "Comma-separated test issue keys to add (e.g. PROJ-1,PROJ-2)", keyList);
3550
3371
  examples(cmd, ["PROJ-50 --test PROJ-1", "PROJ-50 --test PROJ-1,PROJ-2,PROJ-3"]);
3551
3372
  cmd.action(async (preKey, opts) => {
@@ -3556,7 +3377,7 @@ function add4(parent) {
3556
3377
  }
3557
3378
 
3558
3379
  // src/commands/xray/precondition/create.ts
3559
- import { Option as Option11 } from "commander";
3380
+ import { Option as Option10 } from "commander";
3560
3381
 
3561
3382
  // src/utils/xray/fields.ts
3562
3383
  var CONCEPT_SUFFIX = {
@@ -3657,11 +3478,11 @@ function buildPreconditionCustomFields(map, opts) {
3657
3478
  }
3658
3479
 
3659
3480
  // src/commands/xray/precondition/create.ts
3660
- function create10(parent) {
3481
+ function create12(parent) {
3661
3482
  const cmd = parent.command("create").description(
3662
3483
  "Create a new Xray Pre-Condition issue. Note: the Pre-Condition issue type is only available in projects configured for it; it may not be on your project scheme."
3663
3484
  ).requiredOption("--project <key>", "Project key (e.g. PROJ)", text).requiredOption("--summary <text>", "Pre-Condition summary (title)", text).addOption(
3664
- new Option11("--type <type>", "Pre-Condition type").choices(["manual", "generic", "cucumber"]).default("manual")
3485
+ new Option10("--type <type>", "Pre-Condition type").choices(["manual", "generic", "cucumber"]).default("manual")
3665
3486
  );
3666
3487
  textOrFileOption(cmd, "condition", { description: "Pre-condition body / definition text" });
3667
3488
  textOrFileOption(cmd, "description", { description: "Pre-Condition issue description" });
@@ -3708,7 +3529,7 @@ function deletePrecondition(parent) {
3708
3529
  }
3709
3530
 
3710
3531
  // src/commands/xray/precondition/list.ts
3711
- function list13(parent) {
3532
+ function list16(parent) {
3712
3533
  const cmd = parent.command("list").description(
3713
3534
  "List Pre-Conditions that apply to a given test (inverse view). For the forward view (tests covered by a precondition), use: xray precondition add/remove."
3714
3535
  ).requiredOption("--test <key>", "Test issue key whose pre-conditions to list (e.g. PROJ-584)", issueKey);
@@ -3721,7 +3542,7 @@ function list13(parent) {
3721
3542
  }
3722
3543
 
3723
3544
  // src/commands/xray/precondition/remove.ts
3724
- function remove4(parent) {
3545
+ function remove5(parent) {
3725
3546
  const cmd = parent.command("remove").description("Remove tests from a Pre-Condition").argument("<preKey>", "Pre-Condition issue key (e.g. PROJ-50)", issueKey).requiredOption("--test <keys>", "Comma-separated test issue keys to remove (e.g. PROJ-1,PROJ-2)", keyList);
3726
3547
  examples(cmd, ["PROJ-50 --test PROJ-1", "PROJ-50 --test PROJ-1,PROJ-2"]);
3727
3548
  cmd.action(async (preKey, opts) => {
@@ -3732,9 +3553,9 @@ function remove4(parent) {
3732
3553
  }
3733
3554
 
3734
3555
  // src/commands/xray/precondition/update.ts
3735
- import { Option as Option12 } from "commander";
3556
+ import { Option as Option11 } from "commander";
3736
3557
  function update9(parent) {
3737
- const cmd = parent.command("update").description("Update an Xray Pre-Condition issue").argument("<preKey>", "Pre-Condition issue key (e.g. PROJ-50)", issueKey).option("--summary <text>", "New summary (title)", text).addOption(new Option12("--type <type>", "New Pre-Condition type").choices(["manual", "generic", "cucumber"]));
3558
+ const cmd = parent.command("update").description("Update an Xray Pre-Condition issue").argument("<preKey>", "Pre-Condition issue key (e.g. PROJ-50)", issueKey).option("--summary <text>", "New summary (title)", text).addOption(new Option11("--type <type>", "New Pre-Condition type").choices(["manual", "generic", "cucumber"]));
3738
3559
  textOrFileOption(cmd, "condition", { description: "New pre-condition body / definition text" });
3739
3560
  textOrFileOption(cmd, "description", { description: "New Pre-Condition issue description" });
3740
3561
  examples(cmd, ['PROJ-50 --summary "Updated precondition"', 'PROJ-50 --type generic --condition "apiKey != null"']);
@@ -3782,12 +3603,12 @@ function registerPreconditionCommands(xray) {
3782
3603
  "add PROJ-50 --test PROJ-1,PROJ-2",
3783
3604
  "list --test PROJ-584"
3784
3605
  ]);
3785
- create10(precondition);
3606
+ create12(precondition);
3786
3607
  update9(precondition);
3787
3608
  deletePrecondition(precondition);
3788
- add4(precondition);
3789
- remove4(precondition);
3790
- list13(precondition);
3609
+ add5(precondition);
3610
+ remove5(precondition);
3611
+ list16(precondition);
3791
3612
  }
3792
3613
 
3793
3614
  // src/utils/xray/run-id.ts
@@ -3801,7 +3622,7 @@ async function resolveRunId(client, o) {
3801
3622
  }
3802
3623
 
3803
3624
  // src/commands/xray/run/defect/add.ts
3804
- function add5(parent) {
3625
+ function add6(parent) {
3805
3626
  const cmd = parent.command("add").description("Link defect issues to a test run").argument("[runId]", "Test run id (from run list)", positiveInt).requiredOption("--defect <keys>", "Comma-separated defect issue keys to link", keyList).option("--execution <key>", "Test execution issue key (used with --test to resolve run id)", issueKey).option("--test <key>", "Test issue key (used with --execution to resolve run id)", issueKey);
3806
3627
  examples(cmd, ["42 --defect BUG-1", "--execution PROJ-1 --test PROJ-2 --defect BUG-1,BUG-2"]);
3807
3628
  cmd.action(async (runId, opts) => {
@@ -3813,7 +3634,7 @@ function add5(parent) {
3813
3634
  }
3814
3635
 
3815
3636
  // src/commands/xray/run/defect/remove.ts
3816
- function remove5(parent) {
3637
+ function remove6(parent) {
3817
3638
  const cmd = parent.command("remove").description("Unlink defect issues from a test run (per-key, reports failures without aborting)").argument("[runId]", "Test run id (from run list)", positiveInt).requiredOption("--defect <keys>", "Comma-separated defect issue keys to unlink", keyList).option("--execution <key>", "Test execution issue key (used with --test to resolve run id)", issueKey).option("--test <key>", "Test issue key (used with --execution to resolve run id)", issueKey);
3818
3639
  examples(cmd, ["42 --defect BUG-1", "--execution PROJ-1 --test PROJ-2 --defect BUG-1,BUG-2"]);
3819
3640
  cmd.action(async (runId, opts) => {
@@ -3850,12 +3671,12 @@ function registerDefectCommands(run) {
3850
3671
  "Manage defects linked to a test run. See also: run update --defect for the one-command fail+link loop."
3851
3672
  );
3852
3673
  examples(defect, ["add --execution PROJ-1 --test PROJ-2 --defect BUG-1", "remove 42 --defect BUG-1"]);
3853
- add5(defect);
3854
- remove5(defect);
3674
+ add6(defect);
3675
+ remove6(defect);
3855
3676
  }
3856
3677
 
3857
3678
  // src/commands/xray/run/evidence/add.ts
3858
- import { readFileSync as readFileSync6 } from "fs";
3679
+ import { readFileSync as readFileSync3 } from "fs";
3859
3680
  import { basename as basename4, extname } from "path";
3860
3681
  var CONTENT_TYPES = {
3861
3682
  ".png": "image/png",
@@ -3873,13 +3694,13 @@ var CONTENT_TYPES = {
3873
3694
  function inferContentType(fp) {
3874
3695
  return CONTENT_TYPES[extname(fp).toLowerCase()] ?? "application/octet-stream";
3875
3696
  }
3876
- function add6(parent) {
3697
+ function add7(parent) {
3877
3698
  const cmd = parent.command("add").description("Attach a file as evidence to a test run").argument("[runId]", "Test run id (from run list)", positiveInt).requiredOption("--file <path>", "Path to the file to attach", filePath).option("--execution <key>", "Test execution issue key (used with --test to resolve run id)", issueKey).option("--test <key>", "Test issue key (used with --execution to resolve run id)", issueKey);
3878
3699
  examples(cmd, ["42 --file screenshot.png", "--execution PROJ-1 --test PROJ-2 --file report.pdf"]);
3879
3700
  cmd.action(async (runId, opts) => {
3880
3701
  const client = getClient();
3881
3702
  const id = await resolveRunId(client, { runId, execution: opts.execution, test: opts.test });
3882
- const fileBuf = readFileSync6(opts.file);
3703
+ const fileBuf = readFileSync3(opts.file);
3883
3704
  const filename = basename4(opts.file);
3884
3705
  const contentType = inferContentType(opts.file);
3885
3706
  const data = fileBuf.toString("base64");
@@ -3901,7 +3722,7 @@ function deleteEvidence(parent) {
3901
3722
  }
3902
3723
 
3903
3724
  // src/commands/xray/run/evidence/list.ts
3904
- function list14(parent) {
3725
+ function list17(parent) {
3905
3726
  const cmd = parent.command("list").description("List evidence (attachments) on a test run").argument("[runId]", "Test run id (from run list)", positiveInt).option("--execution <key>", "Test execution issue key (used with --test to resolve run id)", issueKey).option("--test <key>", "Test issue key (used with --execution to resolve run id)", issueKey);
3906
3727
  examples(cmd, ["42", "--execution PROJ-1 --test PROJ-2"]);
3907
3728
  cmd.action(async (runId, opts) => {
@@ -3920,13 +3741,13 @@ function registerEvidenceCommands(run) {
3920
3741
  "list 42",
3921
3742
  "delete 42 --evidence-id 7"
3922
3743
  ]);
3923
- add6(evidence);
3924
- list14(evidence);
3744
+ add7(evidence);
3745
+ list17(evidence);
3925
3746
  deleteEvidence(evidence);
3926
3747
  }
3927
3748
 
3928
3749
  // src/commands/xray/run/field/get.ts
3929
- function get8(parent) {
3750
+ function get10(parent) {
3930
3751
  const cmd = parent.command("get").description("Get a custom field value on a test run").argument("[runId]", "Test run id (from run list)", positiveInt).requiredOption("--field-id <id>", "Custom field id (from xray field list)", text).option("--execution <key>", "Test execution issue key (used with --test to resolve run id)", issueKey).option("--test <key>", "Test issue key (used with --execution to resolve run id)", issueKey);
3931
3752
  examples(cmd, ["42 --field-id customfield_10100", "--execution PROJ-1 --test PROJ-2 --field-id customfield_10100"]);
3932
3753
  cmd.action(async (runId, opts) => {
@@ -3958,12 +3779,12 @@ function set(parent) {
3958
3779
  function registerRunFieldCommands(run) {
3959
3780
  const field = run.command("field").description("Get or set Xray custom field values on a test run");
3960
3781
  examples(field, ["get 42 --field-id customfield_10100", 'set 42 --field-id customfield_10100 --value "approved"']);
3961
- get8(field);
3782
+ get10(field);
3962
3783
  set(field);
3963
3784
  }
3964
3785
 
3965
3786
  // src/commands/xray/run/get.ts
3966
- function get9(parent) {
3787
+ function get11(parent) {
3967
3788
  const cmd = parent.command("get").description("Get a test run by id or by execution + test key").argument("[runId]", "Test run id (from run list)", positiveInt).option("--execution <key>", "Test execution issue key (used with --test to resolve run id)", issueKey).option("--test <key>", "Test issue key (used with --execution to resolve run id)", issueKey);
3968
3789
  examples(cmd, ["42", "--execution PROJ-1 --test PROJ-2"]);
3969
3790
  cmd.action(async (runId, opts) => {
@@ -3975,7 +3796,7 @@ function get9(parent) {
3975
3796
  }
3976
3797
 
3977
3798
  // src/commands/xray/run/list.ts
3978
- function list15(parent) {
3799
+ function list18(parent) {
3979
3800
  const cmd = parent.command("list").description("List test runs for a test execution (CLI-side slice with --limit/--start)").requiredOption("--execution <key>", "Test execution issue key", issueKey).option("--limit <number>", "Max results to return (1-200)", intInRange(1, 200), 50).option("--start <number>", "Starting index for pagination", nonNegativeInt);
3980
3801
  examples(cmd, ["--execution PROJ-1", "--execution PROJ-1 --limit 10", "--execution PROJ-1 --limit 10 --start 10"]);
3981
3802
  cmd.action(async (opts) => {
@@ -3988,7 +3809,7 @@ function list15(parent) {
3988
3809
  }
3989
3810
 
3990
3811
  // src/commands/xray/run/step/list.ts
3991
- function list16(parent) {
3812
+ function list19(parent) {
3992
3813
  const cmd = parent.command("list").description("List step results for a test run").argument("[runId]", "Test run id (from run list)", positiveInt).option("--execution <key>", "Test execution issue key (used with --test to resolve run id)", issueKey).option("--test <key>", "Test issue key (used with --execution to resolve run id)", issueKey);
3993
3814
  examples(cmd, ["42", "--execution PROJ-1 --test PROJ-2"]);
3994
3815
  cmd.action(async (runId, opts) => {
@@ -4035,7 +3856,7 @@ function update10(parent) {
4035
3856
  function registerRunStepCommands(run) {
4036
3857
  const step = run.command("step").description("Manage step results within a test run. See also: xray step for test step definitions.");
4037
3858
  examples(step, ["list --execution PROJ-1 --test PROJ-2", "update 42 --step-id 1 --status PASS"]);
4038
- list16(step);
3859
+ list19(step);
4039
3860
  update10(step);
4040
3861
  }
4041
3862
 
@@ -4081,8 +3902,8 @@ function registerRunCommands(xray) {
4081
3902
  "step list --execution PROJ-1 --test PROJ-2",
4082
3903
  "field get 42 --field-id customfield_10100"
4083
3904
  ]);
4084
- get9(run);
4085
- list15(run);
3905
+ get11(run);
3906
+ list18(run);
4086
3907
  update11(run);
4087
3908
  registerDefectCommands(run);
4088
3909
  registerEvidenceCommands(run);
@@ -4091,7 +3912,7 @@ function registerRunCommands(xray) {
4091
3912
  }
4092
3913
 
4093
3914
  // src/commands/xray/status/list.ts
4094
- function list17(parent) {
3915
+ function list20(parent) {
4095
3916
  const cmd = parent.command("list").description("List configured Xray test (run) statuses").option("--step", "List test-step statuses instead of run statuses");
4096
3917
  examples(cmd, ["", ["--step", "step statuses"]]);
4097
3918
  cmd.action(async (opts) => {
@@ -4105,11 +3926,11 @@ function list17(parent) {
4105
3926
  function registerStatusCommands(xray) {
4106
3927
  const status = xray.command("status").description("Xray status discovery");
4107
3928
  examples(status, ["list", ["list --step", "step statuses"]]);
4108
- list17(status);
3929
+ list20(status);
4109
3930
  }
4110
3931
 
4111
3932
  // src/commands/xray/step/add.ts
4112
- function add7(parent) {
3933
+ function add8(parent) {
4113
3934
  const cmd = parent.command("add").description("Add a manual test step to a Test issue").requiredOption("--test <key>", "Test issue key (e.g. PROJ-584)", issueKey);
4114
3935
  textOrFileOption(cmd, "action", { description: "Step action text (required: provide --action or --action-file)" });
4115
3936
  textOrFileOption(cmd, "data", { description: "Step test data text" });
@@ -4142,7 +3963,7 @@ function deleteStep(parent) {
4142
3963
  }
4143
3964
 
4144
3965
  // src/commands/xray/step/list.ts
4145
- function list18(parent) {
3966
+ function list21(parent) {
4146
3967
  const cmd = parent.command("list").description("List manual test steps for a Test issue").requiredOption("--test <key>", "Test issue key (e.g. PROJ-584)", issueKey);
4147
3968
  examples(cmd, ["--test PROJ-584"]);
4148
3969
  cmd.action(async (opts) => {
@@ -4185,23 +4006,23 @@ function registerStepCommands(xray) {
4185
4006
  "Manage manual test steps for a Test issue (scope: --test). Steps are ordered by insertion; use step list to see current ids."
4186
4007
  );
4187
4008
  examples(step, ['add --test PROJ-584 --action "Open login" --result "Form shows"', "list --test PROJ-584"]);
4188
- list18(step);
4189
- add7(step);
4009
+ list21(step);
4010
+ add8(step);
4190
4011
  update12(step);
4191
4012
  deleteStep(step);
4192
4013
  }
4193
4014
 
4194
4015
  // src/commands/xray/test/create.ts
4195
- import { Option as Option13 } from "commander";
4016
+ import { Option as Option12 } from "commander";
4196
4017
  import { z as z5 } from "zod";
4197
4018
  var stepsSchema = z5.array(
4198
4019
  z5.object({ action: z5.string(), data: z5.string().optional(), result: z5.string().optional() })
4199
4020
  );
4200
- function create11(parent) {
4021
+ function create13(parent) {
4201
4022
  const cmd = parent.command("create").description("Create a new Xray Test issue").requiredOption("--project <key>", "Project key (e.g. PROJ)", text).requiredOption("--summary <text>", "Test summary (title)", text).addOption(
4202
- new Option13("--type <type>", "Test type (when cucumber: also pass --gherkin; when generic: --definition)").choices(["manual", "cucumber", "generic"]).default("manual")
4023
+ new Option12("--type <type>", "Test type (when cucumber: also pass --gherkin; when generic: --definition)").choices(["manual", "cucumber", "generic"]).default("manual")
4203
4024
  ).addOption(
4204
- new Option13("--cucumber-type <cucumberType>", "Cucumber scenario type (required when --type cucumber)").choices([
4025
+ new Option12("--cucumber-type <cucumberType>", "Cucumber scenario type (required when --type cucumber)").choices([
4205
4026
  "scenario",
4206
4027
  "scenario-outline"
4207
4028
  ])
@@ -4286,7 +4107,7 @@ function deleteTest(parent) {
4286
4107
  }
4287
4108
 
4288
4109
  // src/commands/xray/test/get.ts
4289
- function get10(parent) {
4110
+ function get12(parent) {
4290
4111
  const cmd = parent.command("get").description("Get one or more Xray test issues by key").argument("<testKey...>", "One or more test issue keys (e.g. PROJ-584)", listOf(issueKey));
4291
4112
  examples(cmd, ["PROJ-584", "PROJ-584 PROJ-585"]);
4292
4113
  cmd.action(async (testKeys) => {
@@ -4297,7 +4118,7 @@ function get10(parent) {
4297
4118
  }
4298
4119
 
4299
4120
  // src/commands/xray/test/list.ts
4300
- function list19(parent) {
4121
+ function list22(parent) {
4301
4122
  const cmd = parent.command("list").description(
4302
4123
  "List Xray Test issues by scope: all tests in a project, or tests belonging to a set, plan, execution, precondition, or folder. Note: use `jiradc issue search` for richer JQL when scoping by project."
4303
4124
  ).option("--project <text>", "Project key \u2014 list all tests in the project (or companion to --folder)", text).option("--set <key>", "Test Set issue key \u2014 list tests in this set", issueKey).option("--plan <key>", "Test Plan issue key \u2014 list tests in this plan", issueKey).option("--execution <key>", "Test Execution issue key \u2014 list tests in this execution", issueKey).option("--precondition <key>", "Precondition issue key \u2014 list tests with this precondition", issueKey).option("--folder <id>", "Folder id \u2014 list tests in this folder (requires --project)", positiveInt).option("--limit <number>", "Max results (1-50, applies to --project scope)", intInRange(1, 50), 25).option("--start <number>", "Starting index for pagination (applies to --project scope)", nonNegativeInt);
@@ -4371,10 +4192,10 @@ function list19(parent) {
4371
4192
  }
4372
4193
 
4373
4194
  // src/commands/xray/test/update.ts
4374
- import { Option as Option14 } from "commander";
4195
+ import { Option as Option13 } from "commander";
4375
4196
  function update13(parent) {
4376
- const cmd = parent.command("update").description("Update an Xray Test issue").argument("<testKey>", "Test issue key (e.g. PROJ-584)", issueKey).option("--summary <text>", "New summary (title)", text).addOption(new Option14("--type <type>", "Test type").choices(["manual", "cucumber", "generic"])).addOption(
4377
- new Option14("--cucumber-type <cucumberType>", "Cucumber scenario type (required when --type cucumber)").choices([
4197
+ const cmd = parent.command("update").description("Update an Xray Test issue").argument("<testKey>", "Test issue key (e.g. PROJ-584)", issueKey).option("--summary <text>", "New summary (title)", text).addOption(new Option13("--type <type>", "Test type").choices(["manual", "cucumber", "generic"])).addOption(
4198
+ new Option13("--cucumber-type <cucumberType>", "Cucumber scenario type (required when --type cucumber)").choices([
4378
4199
  "scenario",
4379
4200
  "scenario-outline"
4380
4201
  ])
@@ -4439,15 +4260,15 @@ function update13(parent) {
4439
4260
  function registerTestCommands(xray) {
4440
4261
  const test = xray.command("test").description("Xray Test issue management (CRUD)");
4441
4262
  examples(test, ["get PROJ-584", 'create --project PROJ --summary "Login" --type manual']);
4442
- get10(test);
4443
- create11(test);
4263
+ get12(test);
4264
+ create13(test);
4444
4265
  update13(test);
4445
4266
  deleteTest(test);
4446
- list19(test);
4267
+ list22(test);
4447
4268
  }
4448
4269
 
4449
4270
  // src/commands/xray/testset/add.ts
4450
- function add8(parent) {
4271
+ function add9(parent) {
4451
4272
  const cmd = parent.command("add").description("Add tests to a Test Set").argument("<setKey>", "Test Set issue key (e.g. PROJ-100)", issueKey).requiredOption("--test <keys>", "Comma-separated test issue keys to add (e.g. PROJ-1,PROJ-2)", keyList);
4452
4273
  examples(cmd, ["PROJ-100 --test PROJ-1", "PROJ-100 --test PROJ-1,PROJ-2,PROJ-3"]);
4453
4274
  cmd.action(async (setKey, opts) => {
@@ -4458,7 +4279,7 @@ function add8(parent) {
4458
4279
  }
4459
4280
 
4460
4281
  // src/commands/xray/testset/create.ts
4461
- function create12(parent) {
4282
+ function create14(parent) {
4462
4283
  const cmd = parent.command("create").description("Create a new Xray Test Set issue").requiredOption("--project <key>", "Project key (e.g. PROJ)", text).requiredOption("--summary <text>", "Test Set summary (title)", text);
4463
4284
  textOrFileOption(cmd, "description", { description: "Test Set description" });
4464
4285
  examples(cmd, [
@@ -4490,7 +4311,7 @@ function deleteTestset(parent) {
4490
4311
  }
4491
4312
 
4492
4313
  // src/commands/xray/testset/list.ts
4493
- function list20(parent) {
4314
+ function list23(parent) {
4494
4315
  const cmd = parent.command("list").description(
4495
4316
  "List Test Sets that contain a given test (inverse view). For the forward view (tests inside a set), use: xray test list --set <key>"
4496
4317
  ).requiredOption("--test <key>", "Test issue key whose sets to list (e.g. PROJ-584)", issueKey);
@@ -4503,7 +4324,7 @@ function list20(parent) {
4503
4324
  }
4504
4325
 
4505
4326
  // src/commands/xray/testset/remove.ts
4506
- function remove6(parent) {
4327
+ function remove7(parent) {
4507
4328
  const cmd = parent.command("remove").description("Remove tests from a Test Set").argument("<setKey>", "Test Set issue key (e.g. PROJ-100)", issueKey).requiredOption("--test <keys>", "Comma-separated test issue keys to remove (e.g. PROJ-1,PROJ-2)", keyList);
4508
4329
  examples(cmd, ["PROJ-100 --test PROJ-1", "PROJ-100 --test PROJ-1,PROJ-2"]);
4509
4330
  cmd.action(async (setKey, opts) => {
@@ -4532,12 +4353,12 @@ function registerTestsetCommands(xray) {
4532
4353
  "Xray Test Set issue management (CRUD, membership). To list tests inside a set, use: xray test list --set <key>"
4533
4354
  );
4534
4355
  examples(testset, ['create --project PROJ --summary "Smoke tests"', "add PROJ-100 --test PROJ-1,PROJ-2"]);
4535
- create12(testset);
4356
+ create14(testset);
4536
4357
  update14(testset);
4537
4358
  deleteTestset(testset);
4538
- add8(testset);
4539
- remove6(testset);
4540
- list20(testset);
4359
+ add9(testset);
4360
+ remove7(testset);
4361
+ list23(testset);
4541
4362
  }
4542
4363
 
4543
4364
  // src/commands/xray/index.ts
@@ -4553,7 +4374,7 @@ function registerXrayCommands(program) {
4553
4374
  registerPlanCommands(xray);
4554
4375
  registerStepCommands(xray);
4555
4376
  registerRunCommands(xray);
4556
- registerFolderCommands(xray);
4377
+ registerFolderCommands2(xray);
4557
4378
  registerImportCommands(xray);
4558
4379
  registerExportCommands(xray);
4559
4380
  }
@@ -4600,6 +4421,7 @@ ${styleText("bold", "Examples:")}
4600
4421
  registerFieldCommands(program);
4601
4422
  registerUserCommands(program);
4602
4423
  registerTokenCommands(program);
4424
+ registerStructureCommands(program);
4603
4425
  registerXrayCommands(program);
4604
4426
  return program;
4605
4427
  }