jiradc-cli 1.0.31 → 1.0.33
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +65 -59
- package/dist/index.js +349 -122
- package/package.json +6 -4
package/README.md
CHANGED
|
@@ -17,6 +17,11 @@ export JIRA_TOKEN="your-personal-access-token" # HTTP Access Token from Jira
|
|
|
17
17
|
|
|
18
18
|
## Breaking changes in this release
|
|
19
19
|
|
|
20
|
+
- **`field options` is removed.** It called a Jira 9 endpoint and returned 404 for every field on Jira 8.x. Jira scopes a custom field's options to a context, so there is no context-free replacement — use `issue createmeta --project <key> --type <name>` (create screen), `issue editmeta <key>` (edit screen), or `issue transitions <key>` (transition screen).
|
|
21
|
+
- **`issue transitions` no longer reports `required`** on screen fields. Jira reports field-configuration requiredness there, which cannot see the workflow validator that actually gates the transition, so it returned `false` for fields that then rejected the transition. Supply everything in a transition's `fields` block. (`required` from `createmeta`/`editmeta` reflects field configuration and is reliable.)
|
|
22
|
+
- **`issue get` / `issue search` return issue-picker custom fields as a bare key string** (or an array of keys) instead of the whole nested issue. `fields.customfield_NNNNN.key` becomes `fields.customfield_NNNNN`.
|
|
23
|
+
- **`issue create --custom-fields` is now `--fields`**, matching `issue update`. The old name still works as a deprecated alias.
|
|
24
|
+
- **Argument mistakes now exit 2/3/6** (`usage`/`not_found`/`auth`) instead of 1.
|
|
20
25
|
- `issue transitions` now returns a bare JSON array of transitions (previously wrapped in `{ transitions: [...] }`). Each transition is reduced to `{ id, name, to, fields? }` — verbose fields like `to.iconUrl`, `to.statusCategory`, `isGlobal`, `isInitial`, `isConditional`, `hasScreen` are stripped.
|
|
21
26
|
- `component list` now requires `--project <key>` instead of a positional `<projectKey>` argument.
|
|
22
27
|
- `issue link-epic` is now variadic on the issue argument: `link-epic <issueKey...> --epic <epicKey>`. Single-issue calls keep working.
|
|
@@ -27,88 +32,89 @@ All commands output JSON. Add `--pretty` to pretty-print.
|
|
|
27
32
|
|
|
28
33
|
### issue
|
|
29
34
|
|
|
30
|
-
| Command
|
|
31
|
-
|
|
32
|
-
| `jiradc issue get <key>`
|
|
33
|
-
| `jiradc issue search <jql>`
|
|
34
|
-
| `jiradc issue create`
|
|
35
|
-
| `jiradc issue update <key>`
|
|
36
|
-
| `jiradc issue delete <key>`
|
|
37
|
-
| `jiradc issue assign <key> <user>`
|
|
38
|
-
| `jiradc issue transition <key>`
|
|
39
|
-
| `jiradc issue transitions <key>`
|
|
40
|
-
| `jiradc issue
|
|
41
|
-
| `jiradc issue
|
|
42
|
-
| `jiradc issue comment
|
|
43
|
-
| `jiradc issue
|
|
44
|
-
| `jiradc issue
|
|
45
|
-
| `jiradc issue link
|
|
46
|
-
| `jiradc issue
|
|
47
|
-
| `jiradc issue
|
|
48
|
-
| `jiradc issue
|
|
49
|
-
| `jiradc issue worklog
|
|
50
|
-
| `jiradc issue worklog
|
|
51
|
-
| `jiradc issue
|
|
52
|
-
| `jiradc issue
|
|
53
|
-
| `jiradc issue
|
|
54
|
-
| `jiradc issue batch-
|
|
55
|
-
| `jiradc issue
|
|
35
|
+
| Command | Description |
|
|
36
|
+
| ------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
37
|
+
| `jiradc issue get <key>` | Get issue details (`--fields` to select, `--expand` for changelog/transitions) |
|
|
38
|
+
| `jiradc issue search <jql>` | Search issues with JQL |
|
|
39
|
+
| `jiradc issue create` | Create an issue (`--project`, `--type`, `--summary`, `--description`, `--custom-fields`) |
|
|
40
|
+
| `jiradc issue update <key>` | Update an issue (`--summary`, `--description`, `--priority`, `--assignee`, `--labels`, `--components`, `--fix-versions`, or `--fields` JSON) |
|
|
41
|
+
| `jiradc issue delete <key>` | Delete an issue |
|
|
42
|
+
| `jiradc issue assign <key> <user>` | Assign issue (user can be a username, `me`, or `none` to unassign) |
|
|
43
|
+
| `jiradc issue transition <key>` | Transition issue to a new status (`--to` accepts ID or status name, `--comment` to add a note) |
|
|
44
|
+
| `jiradc issue transitions <key>` | List available transitions (with any fields their screens require) |
|
|
45
|
+
| `jiradc issue createmeta` | Fields a new issue accepts, with allowed values (`--project`, `--type`) |
|
|
46
|
+
| `jiradc issue editmeta <key>` | Fields an existing issue's edit screen accepts, with allowed values |
|
|
47
|
+
| `jiradc issue comment add <key>` | Add a comment (`--body`) |
|
|
48
|
+
| `jiradc issue comment edit <key>` | Edit a comment (`--id`, `--body`) |
|
|
49
|
+
| `jiradc issue comment delete <key>` | Delete a comment (`--id`) |
|
|
50
|
+
| `jiradc issue link <key> <targetKey>` | Link two issues (`--type` link type name) |
|
|
51
|
+
| `jiradc issue unlink <linkId>` | Remove a link |
|
|
52
|
+
| `jiradc issue link-types` | List available link types |
|
|
53
|
+
| `jiradc issue link-epic <keys...>` | Link one or more issues to an epic (`--epic <epicKey>`) |
|
|
54
|
+
| `jiradc issue worklog add <key>` | Add a work log entry (`--time`, `--comment`, `--started`) |
|
|
55
|
+
| `jiradc issue worklog list <key>` | Get work log entries |
|
|
56
|
+
| `jiradc issue worklog edit <key>` | Update a work log entry (`--id`, `--time`, `--comment`, `--started`, `--adjust-estimate`, `--new-estimate`) |
|
|
57
|
+
| `jiradc issue worklog delete <key>` | Delete a work log entry (`--id`, `--adjust-estimate`, `--new-estimate`, `--increase-by`) |
|
|
58
|
+
| `jiradc issue changelog <key>` | Get issue changelog |
|
|
59
|
+
| `jiradc issue batch-changelog` | Get changelog for multiple issues (`--keys`) |
|
|
60
|
+
| `jiradc issue clone <key>` | Clone an issue with subtasks |
|
|
61
|
+
| `jiradc issue batch-create` | Create multiple issues from JSON |
|
|
62
|
+
| `jiradc issue dev-status <key>` | Get development status (branches, PRs, commits) |
|
|
56
63
|
|
|
57
64
|
#### issue attachment
|
|
58
65
|
|
|
59
|
-
| Command
|
|
60
|
-
|
|
61
|
-
| `jiradc issue attachment list <key>`
|
|
66
|
+
| Command | Description |
|
|
67
|
+
| ------------------------------------------------------- | ------------------------------ |
|
|
68
|
+
| `jiradc issue attachment list <key>` | List attachments |
|
|
62
69
|
| `jiradc issue attachment download <key> <attachmentId>` | Download a specific attachment |
|
|
63
|
-
| `jiradc issue attachment download-all <key>`
|
|
64
|
-
| `jiradc issue attachment upload <key> <files...>`
|
|
65
|
-
| `jiradc issue attachment delete <attachmentId>`
|
|
70
|
+
| `jiradc issue attachment download-all <key>` | Download all attachments |
|
|
71
|
+
| `jiradc issue attachment upload <key> <files...>` | Upload files |
|
|
72
|
+
| `jiradc issue attachment delete <attachmentId>` | Delete an attachment |
|
|
66
73
|
|
|
67
74
|
### project
|
|
68
75
|
|
|
69
|
-
| Command
|
|
70
|
-
|
|
71
|
-
| `jiradc project list`
|
|
76
|
+
| Command | Description |
|
|
77
|
+
| ------------------------------- | --------------------------- |
|
|
78
|
+
| `jiradc project list` | List projects |
|
|
72
79
|
| `jiradc project versions <key>` | List versions for a project |
|
|
73
80
|
|
|
74
81
|
### component
|
|
75
82
|
|
|
76
|
-
| Command
|
|
77
|
-
|
|
78
|
-
| `jiradc component list`
|
|
79
|
-
| `jiradc component get <id>`
|
|
80
|
-
| `jiradc component create`
|
|
81
|
-
| `jiradc component update <id>`
|
|
82
|
-
| `jiradc component issue-count <id>` | Get the number of issues using a component
|
|
83
|
+
| Command | Description |
|
|
84
|
+
| ----------------------------------- | --------------------------------------------------------------------- |
|
|
85
|
+
| `jiradc component list` | List components for a project (`--project <key>`) |
|
|
86
|
+
| `jiradc component get <id>` | Get a component by ID |
|
|
87
|
+
| `jiradc component create` | Create a component (`--project`, `--name`, `--description`, `--lead`) |
|
|
88
|
+
| `jiradc component update <id>` | Update a component |
|
|
89
|
+
| `jiradc component issue-count <id>` | Get the number of issues using a component |
|
|
83
90
|
|
|
84
91
|
### board
|
|
85
92
|
|
|
86
|
-
| Command
|
|
87
|
-
|
|
88
|
-
| `jiradc board list`
|
|
89
|
-
| `jiradc board issues <boardId>` | Get issues on a board
|
|
93
|
+
| Command | Description |
|
|
94
|
+
| ------------------------------- | ------------------------------------------------------------------ |
|
|
95
|
+
| `jiradc board list` | List boards (`--project`, `--type`: scrum/kanban/simple, `--name`) |
|
|
96
|
+
| `jiradc board issues <boardId>` | Get issues on a board |
|
|
90
97
|
|
|
91
98
|
### sprint
|
|
92
99
|
|
|
93
|
-
| Command
|
|
94
|
-
|
|
95
|
-
| `jiradc sprint list <boardId>`
|
|
96
|
-
| `jiradc sprint issues <boardId> <sprintId>` | Get issues in a sprint
|
|
97
|
-
| `jiradc sprint create <boardId>`
|
|
98
|
-
| `jiradc sprint update <sprintId>`
|
|
99
|
-
| `jiradc sprint delete <sprintId>`
|
|
100
|
+
| Command | Description |
|
|
101
|
+
| ------------------------------------------- | --------------------------------------------------- |
|
|
102
|
+
| `jiradc sprint list <boardId>` | List sprints (`--state`: future/active/closed) |
|
|
103
|
+
| `jiradc sprint issues <boardId> <sprintId>` | Get issues in a sprint |
|
|
104
|
+
| `jiradc sprint create <boardId>` | Create a sprint |
|
|
105
|
+
| `jiradc sprint update <sprintId>` | Update a sprint |
|
|
106
|
+
| `jiradc sprint delete <sprintId>` | Delete a sprint (returns its issues to the backlog) |
|
|
100
107
|
|
|
101
108
|
### field
|
|
102
109
|
|
|
103
|
-
| Command
|
|
104
|
-
|
|
105
|
-
| `jiradc field search
|
|
106
|
-
| `jiradc field options <fieldKey>` | Get allowed values for a field (`--project`, `--issue-type`) |
|
|
110
|
+
| Command | Description |
|
|
111
|
+
| ------------------------------- | ----------------------------------------------------------------------------- |
|
|
112
|
+
| `jiradc field search <keyword>` | Search for fields by name or ID (returns name + type, **not** allowed values) |
|
|
107
113
|
|
|
108
114
|
### user
|
|
109
115
|
|
|
110
|
-
| Command
|
|
111
|
-
|
|
116
|
+
| Command | Description |
|
|
117
|
+
| ---------------- | --------------------- |
|
|
112
118
|
| `jiradc user me` | Get current user info |
|
|
113
119
|
|
|
114
120
|
## Pagination
|
package/dist/index.js
CHANGED
|
@@ -239,6 +239,7 @@ var CliAuthError = class extends Error {
|
|
|
239
239
|
};
|
|
240
240
|
var NOT_FOUND_RECOVERY = "Verify the id/key, then check your access.";
|
|
241
241
|
var NOT_FOUND_MESSAGE = "Not found: it may not exist, or you may not have permission to see it";
|
|
242
|
+
var USAGE_RECOVERY = "Check the command syntax and flags; run the command with --help.";
|
|
242
243
|
var CliNotFoundError = class extends Error {
|
|
243
244
|
recovery;
|
|
244
245
|
constructor(message, recovery = NOT_FOUND_RECOVERY) {
|
|
@@ -247,6 +248,16 @@ var CliNotFoundError = class extends Error {
|
|
|
247
248
|
this.recovery = recovery;
|
|
248
249
|
}
|
|
249
250
|
};
|
|
251
|
+
var CliUsageError = class extends Error {
|
|
252
|
+
recovery;
|
|
253
|
+
detail;
|
|
254
|
+
constructor(message, recovery = USAGE_RECOVERY, detail) {
|
|
255
|
+
super(message);
|
|
256
|
+
this.name = "CliUsageError";
|
|
257
|
+
this.recovery = recovery;
|
|
258
|
+
this.detail = detail;
|
|
259
|
+
}
|
|
260
|
+
};
|
|
250
261
|
var SubcommandRequiredError = class extends Error {
|
|
251
262
|
subcommands;
|
|
252
263
|
constructor(commandPath3, subcommands) {
|
|
@@ -263,10 +274,55 @@ var TYPE_EXIT = {
|
|
|
263
274
|
auth: EXIT.AUTH,
|
|
264
275
|
rate_limited: EXIT.GENERIC,
|
|
265
276
|
server: EXIT.GENERIC,
|
|
277
|
+
timeout: EXIT.GENERIC,
|
|
266
278
|
network: EXIT.GENERIC,
|
|
267
279
|
unknown: EXIT.GENERIC
|
|
268
280
|
};
|
|
269
|
-
var
|
|
281
|
+
var TRANSPORT_CODES = {
|
|
282
|
+
/** Our own timeout fired: connected (or tried to), no response in time. */
|
|
283
|
+
readTimeout: ["ECONNABORTED"],
|
|
284
|
+
/** The OS gave up establishing the connection. */
|
|
285
|
+
connectTimeout: ["ETIMEDOUT"],
|
|
286
|
+
/** No such host, or DNS itself is unavailable. */
|
|
287
|
+
unresolved: ["ENOTFOUND", "EAI_AGAIN"],
|
|
288
|
+
/** Host is there, nothing is listening on that port. */
|
|
289
|
+
refused: ["ECONNREFUSED"],
|
|
290
|
+
/** Established, then died mid-flight. */
|
|
291
|
+
dropped: ["ECONNRESET", "EPIPE"]
|
|
292
|
+
};
|
|
293
|
+
function humanMs(ms) {
|
|
294
|
+
return ms % 1e3 === 0 ? `${ms / 1e3}s` : `${ms}ms`;
|
|
295
|
+
}
|
|
296
|
+
function targetHost(err) {
|
|
297
|
+
const config = err?.config;
|
|
298
|
+
const raw = config?.baseURL ?? config?.url;
|
|
299
|
+
if (raw === void 0 || raw === "")
|
|
300
|
+
return void 0;
|
|
301
|
+
try {
|
|
302
|
+
return new URL(raw).host;
|
|
303
|
+
} catch {
|
|
304
|
+
return void 0;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
function configuredTimeoutMs(err) {
|
|
308
|
+
const t = err?.config?.timeout;
|
|
309
|
+
return typeof t === "number" && t > 0 ? t : void 0;
|
|
310
|
+
}
|
|
311
|
+
function retryAfterSeconds(err) {
|
|
312
|
+
const headers = err?.response?.headers;
|
|
313
|
+
const raw = headers?.["retry-after"];
|
|
314
|
+
if (raw === void 0 || raw === "")
|
|
315
|
+
return void 0;
|
|
316
|
+
const seconds = Number(raw);
|
|
317
|
+
if (Number.isFinite(seconds))
|
|
318
|
+
return Math.max(0, Math.round(seconds));
|
|
319
|
+
if (typeof raw !== "string")
|
|
320
|
+
return void 0;
|
|
321
|
+
const at = Date.parse(raw);
|
|
322
|
+
if (Number.isNaN(at))
|
|
323
|
+
return void 0;
|
|
324
|
+
return Math.max(0, Math.round((at - Date.now()) / 1e3));
|
|
325
|
+
}
|
|
270
326
|
function httpStatus(err) {
|
|
271
327
|
const e = err;
|
|
272
328
|
return e?.response?.status ?? e?.statusCode;
|
|
@@ -318,6 +374,15 @@ function normalize(err, opts) {
|
|
|
318
374
|
if (err instanceof CliNotFoundError) {
|
|
319
375
|
return { type: "not_found", message, recovery: err.recovery, retryable: false };
|
|
320
376
|
}
|
|
377
|
+
if (err instanceof CliUsageError) {
|
|
378
|
+
return {
|
|
379
|
+
type: "usage",
|
|
380
|
+
message,
|
|
381
|
+
recovery: err.recovery,
|
|
382
|
+
retryable: false,
|
|
383
|
+
...err.detail !== void 0 ? { detail: err.detail } : {}
|
|
384
|
+
};
|
|
385
|
+
}
|
|
321
386
|
const status = httpStatus(err);
|
|
322
387
|
const detail = responseDetail(err);
|
|
323
388
|
if (status !== void 0) {
|
|
@@ -366,21 +431,24 @@ function normalize(err, opts) {
|
|
|
366
431
|
retryable: false,
|
|
367
432
|
detail
|
|
368
433
|
};
|
|
369
|
-
case 429:
|
|
434
|
+
case 429: {
|
|
435
|
+
const wait = retryAfterSeconds(err);
|
|
370
436
|
return {
|
|
371
437
|
type: "rate_limited",
|
|
372
438
|
status,
|
|
373
|
-
message:
|
|
374
|
-
recovery:
|
|
375
|
-
retryable: true
|
|
439
|
+
message: `${opts.service} is rate limiting these requests`,
|
|
440
|
+
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.`,
|
|
441
|
+
retryable: true,
|
|
442
|
+
...wait === void 0 ? {} : { detail: { retryAfterSeconds: wait } }
|
|
376
443
|
};
|
|
444
|
+
}
|
|
377
445
|
}
|
|
378
446
|
if (status >= 500) {
|
|
379
447
|
return {
|
|
380
448
|
type: "server",
|
|
381
449
|
status,
|
|
382
|
-
message:
|
|
383
|
-
recovery: `${opts.service}
|
|
450
|
+
message: status === 503 ? `${opts.service} returned 503 (service unavailable)` : `${opts.service} returned ${status}`,
|
|
451
|
+
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.`,
|
|
384
452
|
retryable: true,
|
|
385
453
|
detail
|
|
386
454
|
};
|
|
@@ -395,15 +463,59 @@ function normalize(err, opts) {
|
|
|
395
463
|
};
|
|
396
464
|
}
|
|
397
465
|
const code = errorCode(err);
|
|
398
|
-
|
|
466
|
+
const transport = classifyTransport(err, code, message, opts);
|
|
467
|
+
if (transport)
|
|
468
|
+
return transport;
|
|
469
|
+
return { type: "unknown", message, recovery: "Unexpected error; inspect the message.", retryable: false };
|
|
470
|
+
}
|
|
471
|
+
function classifyTransport(err, code, message, opts) {
|
|
472
|
+
const matches = (codes) => code !== void 0 && codes.includes(code) || codes.some((c) => message.includes(c));
|
|
473
|
+
const host = targetHost(err);
|
|
474
|
+
const where = host ?? opts.service;
|
|
475
|
+
const urlVar = opts.urlEnvVar ?? "the base-URL environment variable";
|
|
476
|
+
if (matches(TRANSPORT_CODES.readTimeout)) {
|
|
477
|
+
const limit = configuredTimeoutMs(err);
|
|
478
|
+
return {
|
|
479
|
+
type: "timeout",
|
|
480
|
+
message: `${opts.service} did not respond${limit === void 0 ? " in time" : ` within ${humanMs(limit)}`}`,
|
|
481
|
+
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.`,
|
|
482
|
+
retryable: true
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
if (matches(TRANSPORT_CODES.connectTimeout)) {
|
|
486
|
+
const limit = configuredTimeoutMs(err);
|
|
487
|
+
return {
|
|
488
|
+
type: "timeout",
|
|
489
|
+
message: `Could not open a connection to ${where}${limit === void 0 ? "" : ` within ${humanMs(limit)}`}`,
|
|
490
|
+
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.`,
|
|
491
|
+
retryable: true
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
if (matches(TRANSPORT_CODES.unresolved)) {
|
|
495
|
+
return {
|
|
496
|
+
type: "network",
|
|
497
|
+
message: `The host ${where} does not resolve`,
|
|
498
|
+
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.`,
|
|
499
|
+
retryable: false
|
|
500
|
+
};
|
|
501
|
+
}
|
|
502
|
+
if (matches(TRANSPORT_CODES.refused)) {
|
|
399
503
|
return {
|
|
400
504
|
type: "network",
|
|
401
|
-
message: `
|
|
402
|
-
recovery:
|
|
505
|
+
message: `Nothing accepted a connection at ${where}`,
|
|
506
|
+
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.`,
|
|
507
|
+
retryable: false
|
|
508
|
+
};
|
|
509
|
+
}
|
|
510
|
+
if (matches(TRANSPORT_CODES.dropped)) {
|
|
511
|
+
return {
|
|
512
|
+
type: "network",
|
|
513
|
+
message: `The connection to ${opts.service} closed before a response arrived`,
|
|
514
|
+
recovery: `The connection dropped mid-request. This is usually a blip, so retry.`,
|
|
403
515
|
retryable: true
|
|
404
516
|
};
|
|
405
517
|
}
|
|
406
|
-
return
|
|
518
|
+
return void 0;
|
|
407
519
|
}
|
|
408
520
|
function classifyError(err, opts) {
|
|
409
521
|
const base = normalize(err, opts);
|
|
@@ -476,7 +588,7 @@ async function runCli(program, opts) {
|
|
|
476
588
|
|
|
477
589
|
// src/program.ts
|
|
478
590
|
import { styleText } from "util";
|
|
479
|
-
import { Command as
|
|
591
|
+
import { Command as Command17 } from "commander";
|
|
480
592
|
|
|
481
593
|
// src/commands/board/issues.ts
|
|
482
594
|
import { Argument } from "commander";
|
|
@@ -531,9 +643,28 @@ function transformUser(user) {
|
|
|
531
643
|
function transformField(field) {
|
|
532
644
|
return field;
|
|
533
645
|
}
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
646
|
+
|
|
647
|
+
// src/utils/transformers/field-meta.ts
|
|
648
|
+
function slimAllowedValue(raw) {
|
|
649
|
+
const out = {};
|
|
650
|
+
if (typeof raw.id === "string") out.id = raw.id;
|
|
651
|
+
if (typeof raw.name === "string") out.name = raw.name;
|
|
652
|
+
if (typeof raw.value === "string") out.value = raw.value;
|
|
653
|
+
if (Array.isArray(raw.children) && raw.children.length > 0) out.children = raw.children.map(slimAllowedValue);
|
|
654
|
+
return out;
|
|
655
|
+
}
|
|
656
|
+
function transformFieldMetaEntry(id, meta) {
|
|
657
|
+
const out = { id, required: meta.required === true };
|
|
658
|
+
if (typeof meta.name === "string") out.name = meta.name;
|
|
659
|
+
if (typeof meta.schema?.type === "string") out.type = meta.schema.type;
|
|
660
|
+
if (Array.isArray(meta.allowedValues) && meta.allowedValues.length > 0) {
|
|
661
|
+
out.allowedValues = meta.allowedValues.map(slimAllowedValue);
|
|
662
|
+
}
|
|
663
|
+
return out;
|
|
664
|
+
}
|
|
665
|
+
function transformFieldMeta(fields) {
|
|
666
|
+
const entries = Object.entries(fields).map(([id, meta]) => transformFieldMetaEntry(id, meta));
|
|
667
|
+
return [...entries.filter((f) => f.required), ...entries.filter((f) => !f.required)];
|
|
537
668
|
}
|
|
538
669
|
|
|
539
670
|
// src/utils/transformers/project.ts
|
|
@@ -715,6 +846,29 @@ function pruneSentinels(fields) {
|
|
|
715
846
|
if (watches && watches.watchCount === 0) delete out.watches;
|
|
716
847
|
return out;
|
|
717
848
|
}
|
|
849
|
+
var ISSUE_KEY_PATTERN = /^[A-Z][A-Z0-9_]*-\d+$/;
|
|
850
|
+
function asIssueRefKey(value) {
|
|
851
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
852
|
+
const o = value;
|
|
853
|
+
if (typeof o.key !== "string" || !ISSUE_KEY_PATTERN.test(o.key)) return void 0;
|
|
854
|
+
if (typeof o.self !== "string" || !o.self.includes("/issue/")) return void 0;
|
|
855
|
+
return o.key;
|
|
856
|
+
}
|
|
857
|
+
function collapseIssueRefs(fields) {
|
|
858
|
+
const out = { ...fields };
|
|
859
|
+
for (const [key, value] of Object.entries(out)) {
|
|
860
|
+
const single = asIssueRefKey(value);
|
|
861
|
+
if (single !== void 0) {
|
|
862
|
+
out[key] = single;
|
|
863
|
+
continue;
|
|
864
|
+
}
|
|
865
|
+
if (Array.isArray(value) && value.length > 0) {
|
|
866
|
+
const keys = value.map(asIssueRefKey);
|
|
867
|
+
if (keys.every((k) => k !== void 0)) out[key] = keys;
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
return out;
|
|
871
|
+
}
|
|
718
872
|
function transformIssueFields(fields) {
|
|
719
873
|
const {
|
|
720
874
|
// shaped sub-entities (recursed individually)
|
|
@@ -741,7 +895,7 @@ function transformIssueFields(fields) {
|
|
|
741
895
|
// everything else: optional scalars, customfield_*, etc. — gets compacted
|
|
742
896
|
...rest
|
|
743
897
|
} = fields;
|
|
744
|
-
const compacted = pruneSentinels(compactRecord(rest));
|
|
898
|
+
const compacted = collapseIssueRefs(pruneSentinels(compactRecord(rest)));
|
|
745
899
|
return {
|
|
746
900
|
...compacted,
|
|
747
901
|
summary,
|
|
@@ -784,23 +938,23 @@ function transformIssueFields(fields) {
|
|
|
784
938
|
}
|
|
785
939
|
|
|
786
940
|
// src/utils/transformers/transition.ts
|
|
787
|
-
function
|
|
941
|
+
function slimAllowedValue2(raw) {
|
|
788
942
|
if (!raw || typeof raw !== "object") return {};
|
|
789
943
|
const av = raw;
|
|
790
944
|
const slim = {};
|
|
791
945
|
if (typeof av.id === "string") slim.id = av.id;
|
|
792
946
|
if (typeof av.name === "string") slim.name = av.name;
|
|
793
947
|
if (typeof av.value === "string") slim.value = av.value;
|
|
948
|
+
if (Array.isArray(av.children) && av.children.length > 0) slim.children = av.children.map(slimAllowedValue2);
|
|
794
949
|
return slim;
|
|
795
950
|
}
|
|
796
951
|
function slimField(raw) {
|
|
797
952
|
if (!raw || typeof raw !== "object") return {};
|
|
798
953
|
const field = raw;
|
|
799
954
|
const out = {};
|
|
800
|
-
if (typeof field.required === "boolean") out.required = field.required;
|
|
801
955
|
if (typeof field.name === "string") out.name = field.name;
|
|
802
956
|
if (Array.isArray(field.allowedValues)) {
|
|
803
|
-
out.allowedValues = field.allowedValues.map(
|
|
957
|
+
out.allowedValues = field.allowedValues.map(slimAllowedValue2);
|
|
804
958
|
}
|
|
805
959
|
return out;
|
|
806
960
|
}
|
|
@@ -1098,7 +1252,7 @@ function update(parent) {
|
|
|
1098
1252
|
async (id, opts) => {
|
|
1099
1253
|
const description = resolveTextOrFile(opts, "description", { required: false });
|
|
1100
1254
|
if (opts.name === void 0 && description === void 0 && opts.lead === void 0 && opts.assigneeType === void 0) {
|
|
1101
|
-
throw new
|
|
1255
|
+
throw new CliUsageError("Provide at least one of --name, --description, --lead, --assignee-type");
|
|
1102
1256
|
}
|
|
1103
1257
|
const client = getClient();
|
|
1104
1258
|
const result = await client.components.update({
|
|
@@ -1132,22 +1286,6 @@ function registerComponentCommands(program) {
|
|
|
1132
1286
|
issueCount(component);
|
|
1133
1287
|
}
|
|
1134
1288
|
|
|
1135
|
-
// src/commands/field/options.ts
|
|
1136
|
-
function options(parent) {
|
|
1137
|
-
const cmd = parent.command("options").description("Get available options for a custom field").argument("<id>", "Field ID", text).option("--query <text>", "Filter options by text", text).option("--limit <number>", "Max results to return (1-1000)", intInRange(1, 1e3), 25).option("--start <number>", "Page number (1-indexed)", positiveInt);
|
|
1138
|
-
examples(cmd, ["10001", '10001 --query "High"', "10001 --limit 20 --start 2"]);
|
|
1139
|
-
cmd.action(async (id, opts) => {
|
|
1140
|
-
const client = getClient();
|
|
1141
|
-
const result = await client.fields.getFieldOptions({
|
|
1142
|
-
fieldId: id,
|
|
1143
|
-
query: opts.query,
|
|
1144
|
-
maxResults: opts.limit,
|
|
1145
|
-
page: opts.start
|
|
1146
|
-
});
|
|
1147
|
-
output(transformPaged({ ...result, startAt: result.startAt ?? 0 }, transformCustomFieldOption));
|
|
1148
|
-
});
|
|
1149
|
-
}
|
|
1150
|
-
|
|
1151
1289
|
// src/commands/field/search.ts
|
|
1152
1290
|
function search(parent) {
|
|
1153
1291
|
const cmd = parent.command("search").description("Search for fields by name or ID").argument("<keyword>", "Search keyword", text).option("--limit <number>", "Maximum number of results (1-1000)", intInRange(1, 1e3), 25);
|
|
@@ -1162,9 +1300,8 @@ function search(parent) {
|
|
|
1162
1300
|
// src/commands/field/index.ts
|
|
1163
1301
|
function registerFieldCommands(program) {
|
|
1164
1302
|
const field = program.command("field").description("Field operations");
|
|
1165
|
-
examples(field, ['search "epic"', 'search "priority"'
|
|
1303
|
+
examples(field, ['search "epic"', 'search "priority"']);
|
|
1166
1304
|
search(field);
|
|
1167
|
-
options(field);
|
|
1168
1305
|
}
|
|
1169
1306
|
|
|
1170
1307
|
// src/utils/resolve-user.ts
|
|
@@ -1575,13 +1712,16 @@ function registerCommentCommands(parent) {
|
|
|
1575
1712
|
}
|
|
1576
1713
|
|
|
1577
1714
|
// src/commands/issue/create.ts
|
|
1715
|
+
import { Option as Option5 } from "commander";
|
|
1578
1716
|
import { z as z2 } from "zod";
|
|
1579
|
-
var
|
|
1717
|
+
var fieldsSchema = z2.record(z2.unknown());
|
|
1580
1718
|
function create3(parent) {
|
|
1581
1719
|
const cmd = parent.command("create").description("Create a new issue").requiredOption("--project <key>", "Project key or ID", text).requiredOption("--type <name>", "Issue type name (e.g., Task, Bug, Story)", text).requiredOption("--summary <text>", "Issue summary/title", text).option("--assignee <user>", 'Assignee. Username, "me", or "none" to leave unassigned.', text).option("--reporter <user>", 'Reporter. Username or "me".', text).option("--priority <name>", "Priority name (e.g., High, Medium, Low)", text).option("--labels <labels>", "Comma-separated labels", text).option("--components <names>", "Comma-separated component names", text).option("--fix-versions <versions>", "Comma-separated fix version names", text).option("--due-date <date>", "Due date in YYYY-MM-DD format", date).option("--parent <key>", "Parent issue key (for subtasks)", issueKey).option(
|
|
1582
|
-
"--
|
|
1583
|
-
`Additional
|
|
1584
|
-
jsonShape(
|
|
1720
|
+
"--fields <json>",
|
|
1721
|
+
`Additional fields as JSON (e.g., '{"customfield_10100": "EPIC-1"}')`,
|
|
1722
|
+
jsonShape(fieldsSchema)
|
|
1723
|
+
).addOption(
|
|
1724
|
+
new Option5("--custom-fields <json>", "Deprecated alias for --fields").argParser(jsonShape(fieldsSchema)).hideHelp()
|
|
1585
1725
|
);
|
|
1586
1726
|
textOrFileOption(cmd, "description", { description: "Issue description in wiki markup" });
|
|
1587
1727
|
examples(cmd, [
|
|
@@ -1592,6 +1732,13 @@ function create3(parent) {
|
|
|
1592
1732
|
cmd.action(
|
|
1593
1733
|
async (opts) => {
|
|
1594
1734
|
const description = resolveTextOrFile(opts, "description", { required: false });
|
|
1735
|
+
if (opts.fields && opts.customFields) {
|
|
1736
|
+
throw new CliUsageError(
|
|
1737
|
+
"Pass either --fields or --custom-fields, not both",
|
|
1738
|
+
"--custom-fields is a deprecated alias for --fields. Merge the two JSON objects into a single --fields value."
|
|
1739
|
+
);
|
|
1740
|
+
}
|
|
1741
|
+
const fields = opts.fields ?? opts.customFields;
|
|
1595
1742
|
const assignee = opts.assignee !== void 0 ? await resolveUserToken(opts.assignee) : void 0;
|
|
1596
1743
|
const reporter = opts.reporter !== void 0 ? await resolveUserToken(opts.reporter) : void 0;
|
|
1597
1744
|
const client = getClient();
|
|
@@ -1608,13 +1755,31 @@ function create3(parent) {
|
|
|
1608
1755
|
fixVersions: opts.fixVersions?.split(",").map((v) => v.trim()),
|
|
1609
1756
|
dueDate: opts.dueDate,
|
|
1610
1757
|
parent: opts.parent,
|
|
1611
|
-
customFields:
|
|
1758
|
+
customFields: fields
|
|
1612
1759
|
});
|
|
1613
1760
|
output(transformCreatedIssue(result));
|
|
1614
1761
|
}
|
|
1615
1762
|
);
|
|
1616
1763
|
}
|
|
1617
1764
|
|
|
1765
|
+
// src/commands/issue/createmeta.ts
|
|
1766
|
+
function createmeta(parent) {
|
|
1767
|
+
const cmd = parent.command("createmeta").description("Show which fields a new issue of a given type accepts, and the values they allow").requiredOption("--project <key>", "Project key", text).requiredOption("--type <name>", "Issue type name (e.g., Task, Bug, Story)", text);
|
|
1768
|
+
examples(cmd, ["--project PROJ --type Task", "--project PROJ --type Bug"]);
|
|
1769
|
+
cmd.action(async (opts) => {
|
|
1770
|
+
const client = getClient();
|
|
1771
|
+
const meta = await client.issues.getCreateMeta({ projectKey: opts.project, issueTypeName: opts.type });
|
|
1772
|
+
const issueType = meta.projects?.[0]?.issuetypes?.[0];
|
|
1773
|
+
if (!issueType?.fields) {
|
|
1774
|
+
throw new CliNotFoundError(
|
|
1775
|
+
`No create metadata for issue type "${opts.type}" in project "${opts.project}"`,
|
|
1776
|
+
"Verify the project key and issue type name, then check you may create that type there."
|
|
1777
|
+
);
|
|
1778
|
+
}
|
|
1779
|
+
output(transformFieldMeta(issueType.fields));
|
|
1780
|
+
});
|
|
1781
|
+
}
|
|
1782
|
+
|
|
1618
1783
|
// src/commands/issue/delete.ts
|
|
1619
1784
|
function deleteIssue(parent) {
|
|
1620
1785
|
const cmd = parent.command("delete").description("Delete an issue").argument("<key>", "Issue key", issueKey).option("--delete-subtasks", "Also delete subtasks (default: false)");
|
|
@@ -1704,6 +1869,17 @@ function devStatus(parent) {
|
|
|
1704
1869
|
});
|
|
1705
1870
|
}
|
|
1706
1871
|
|
|
1872
|
+
// src/commands/issue/editmeta.ts
|
|
1873
|
+
function editmeta(parent) {
|
|
1874
|
+
const cmd = parent.command("editmeta").description("Show which fields this issue's edit screen accepts, and the values they allow").argument("<key>", "Issue key", issueKey);
|
|
1875
|
+
examples(cmd, ["PROJ-123"]);
|
|
1876
|
+
cmd.action(async (key) => {
|
|
1877
|
+
const client = getClient();
|
|
1878
|
+
const meta = await client.issues.getEditMeta({ issueKeyOrId: key });
|
|
1879
|
+
output(transformFieldMeta(meta.fields ?? {}));
|
|
1880
|
+
});
|
|
1881
|
+
}
|
|
1882
|
+
|
|
1707
1883
|
// src/utils/constants.ts
|
|
1708
1884
|
var DEFAULT_FIELDS = [
|
|
1709
1885
|
"summary",
|
|
@@ -1832,41 +2008,60 @@ function search2(parent) {
|
|
|
1832
2008
|
}
|
|
1833
2009
|
|
|
1834
2010
|
// src/commands/issue/transition.ts
|
|
2011
|
+
import { z as z3 } from "zod";
|
|
2012
|
+
var fieldsSchema2 = z3.record(z3.unknown());
|
|
2013
|
+
function resolveTransitionId(to, transitions2) {
|
|
2014
|
+
const target = to.toLowerCase();
|
|
2015
|
+
const byName = transitions2.filter((t) => t.name.toLowerCase() === target);
|
|
2016
|
+
const matches = byName.length > 0 ? byName : transitions2.filter((t) => t.to.toLowerCase() === target);
|
|
2017
|
+
if (matches.length === 1) return matches[0].id;
|
|
2018
|
+
const available = transitions2.map((t) => ({ id: t.id, name: t.name, to: t.to }));
|
|
2019
|
+
if (matches.length === 0) {
|
|
2020
|
+
throw new CliUsageError(
|
|
2021
|
+
`Transition "${to}" not available from current status`,
|
|
2022
|
+
"Re-run --to with one of the listed transitions (by name, id, or target status).",
|
|
2023
|
+
{ available }
|
|
2024
|
+
);
|
|
2025
|
+
}
|
|
2026
|
+
const ids = matches.map((t) => t.id).join(", ");
|
|
2027
|
+
throw new CliUsageError(
|
|
2028
|
+
`"${to}" matches more than one available transition`,
|
|
2029
|
+
`Re-run --to with one of these ids: ${ids}.`,
|
|
2030
|
+
{ available }
|
|
2031
|
+
);
|
|
2032
|
+
}
|
|
1835
2033
|
function transition(parent) {
|
|
1836
|
-
const cmd = parent.command("transition").description("Transition issue to a new status").argument("<key>", "Issue key", issueKey).requiredOption("--to <idOrName>", "Transition ID, or status name (case-insensitive)", text)
|
|
2034
|
+
const cmd = parent.command("transition").description("Transition issue to a new status").argument("<key>", "Issue key", issueKey).requiredOption("--to <idOrName>", "Transition ID, transition name, or target status name (case-insensitive)", text).option(
|
|
2035
|
+
"--fields <json>",
|
|
2036
|
+
`Fields required by the transition screen, as JSON (e.g. '{"customfield_10100": {"value": "Approved"}}')`,
|
|
2037
|
+
jsonShape(fieldsSchema2)
|
|
2038
|
+
);
|
|
1837
2039
|
commentOption(cmd, { description: "Comment to add during transition" });
|
|
1838
2040
|
examples(cmd, [
|
|
1839
2041
|
"PROJ-123 --to 31",
|
|
2042
|
+
'PROJ-123 --to "Start Review"',
|
|
1840
2043
|
'PROJ-123 --to "In Review"',
|
|
1841
|
-
'PROJ-123 --to Done --comment "Verified in staging"'
|
|
2044
|
+
'PROJ-123 --to Done --comment "Verified in staging"',
|
|
2045
|
+
`PROJ-123 --to Approve --fields '{"customfield_10100": {"value": "Approved"}}'`
|
|
1842
2046
|
]);
|
|
1843
|
-
cmd.action(
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
const matches = transitions2.filter((t) => t.name.toLowerCase() === target);
|
|
1857
|
-
if (matches.length === 0) {
|
|
1858
|
-
const names = transitions2.map((t) => `"${t.name}"`).join(", ");
|
|
1859
|
-
throw new Error(`Transition "${opts.to}" not available from current status. Available: ${names || "(none)"}`);
|
|
1860
|
-
}
|
|
1861
|
-
if (matches.length > 1) {
|
|
1862
|
-
const ids = matches.map((t) => t.id).join(", ");
|
|
1863
|
-
throw new Error(`Multiple transitions named "${opts.to}". Use --to <id> with one of: ${ids}`);
|
|
2047
|
+
cmd.action(
|
|
2048
|
+
async (key, opts) => {
|
|
2049
|
+
const comment = resolveTextOrFile(opts, "comment", { required: false });
|
|
2050
|
+
const client = getClient();
|
|
2051
|
+
let transitionId;
|
|
2052
|
+
if (/^\d+$/.test(opts.to)) {
|
|
2053
|
+
transitionId = opts.to;
|
|
2054
|
+
} else {
|
|
2055
|
+
const raw = await client.issues.getTransitions({
|
|
2056
|
+
issueKeyOrId: key,
|
|
2057
|
+
expand: "transitions.fields"
|
|
2058
|
+
});
|
|
2059
|
+
transitionId = resolveTransitionId(opts.to, transformTransitions(raw));
|
|
1864
2060
|
}
|
|
1865
|
-
transitionId
|
|
2061
|
+
await client.issues.transition({ issueKeyOrId: key, transitionId, fields: opts.fields, comment });
|
|
2062
|
+
output({ transitioned: true, issue: transformIssueRef(key) });
|
|
1866
2063
|
}
|
|
1867
|
-
|
|
1868
|
-
output({ transitioned: true, issue: transformIssueRef(key) });
|
|
1869
|
-
});
|
|
2064
|
+
);
|
|
1870
2065
|
}
|
|
1871
2066
|
|
|
1872
2067
|
// src/commands/issue/transitions.ts
|
|
@@ -1895,7 +2090,7 @@ function unlink2(parent) {
|
|
|
1895
2090
|
}
|
|
1896
2091
|
|
|
1897
2092
|
// src/commands/issue/update.ts
|
|
1898
|
-
import { z as
|
|
2093
|
+
import { z as z4 } from "zod";
|
|
1899
2094
|
|
|
1900
2095
|
// src/utils/multi-value.ts
|
|
1901
2096
|
import { InvalidArgumentError as InvalidArgumentError5 } from "commander";
|
|
@@ -1924,7 +2119,7 @@ function parseMultiValue(flagName, raw) {
|
|
|
1924
2119
|
}
|
|
1925
2120
|
|
|
1926
2121
|
// src/commands/issue/update.ts
|
|
1927
|
-
var
|
|
2122
|
+
var fieldsSchema3 = z4.record(z4.unknown());
|
|
1928
2123
|
function buildUpdateOps(parsed, wrap) {
|
|
1929
2124
|
if (parsed.mode !== "mutate") return void 0;
|
|
1930
2125
|
return [...parsed.adds.map((v) => ({ add: wrap(v) })), ...parsed.removes.map((v) => ({ remove: wrap(v) }))];
|
|
@@ -1937,7 +2132,7 @@ function update3(parent) {
|
|
|
1937
2132
|
const cmd = parent.command("update").description("Update issue fields").argument("<key>", "Issue key", issueKey).option(
|
|
1938
2133
|
"--fields <json>",
|
|
1939
2134
|
"JSON string of fields to update (advanced; merges with shortcuts, wins on conflict)",
|
|
1940
|
-
jsonShape(
|
|
2135
|
+
jsonShape(fieldsSchema3)
|
|
1941
2136
|
).option("--no-notify-users", "Suppress notification emails (default: notify)").option("--attachments <paths>", "Comma-separated local file paths to attach", text).option("--summary <text>", "Set the issue summary", text).option("--priority <name>", "Set the priority by name (e.g. High)", text).option("--assignee <user>", 'Set the assignee. Username, "me", or "none" to unassign.', text).option("--labels <list>", 'Set labels ("a,b,c") or mutate ("+add,-remove")', text).option("--components <list>", 'Set components ("a,b") or mutate ("+add,-remove")', text).option("--fix-versions <list>", 'Set fix versions ("1.0,2.0") or mutate ("+1.0,-0.9")', text);
|
|
1942
2137
|
textOrFileOption(cmd, "description", { description: "Set the issue description (wiki markup)" });
|
|
1943
2138
|
examples(cmd, [
|
|
@@ -1952,7 +2147,7 @@ function update3(parent) {
|
|
|
1952
2147
|
const description = resolveTextOrFile(opts, "description", { required: false });
|
|
1953
2148
|
const hasShortcut = opts.summary !== void 0 || description !== void 0 || opts.priority !== void 0 || opts.assignee !== void 0 || opts.labels !== void 0 || opts.components !== void 0 || opts.fixVersions !== void 0;
|
|
1954
2149
|
if (!opts.fields && !opts.attachments && !hasShortcut) {
|
|
1955
|
-
throw new
|
|
2150
|
+
throw new CliUsageError(
|
|
1956
2151
|
"Provide at least one of --fields, --attachments, or a shortcut flag (--summary, --priority, --labels, ...)"
|
|
1957
2152
|
);
|
|
1958
2153
|
}
|
|
@@ -2040,12 +2235,12 @@ function create4(parent) {
|
|
|
2040
2235
|
}
|
|
2041
2236
|
|
|
2042
2237
|
// src/commands/issue/worklog/delete.ts
|
|
2043
|
-
import { Option as
|
|
2238
|
+
import { Option as Option6 } from "commander";
|
|
2044
2239
|
var ADJUST_ESTIMATE = ["new", "leave", "manual", "auto"];
|
|
2045
2240
|
function deleteWorklog(parent) {
|
|
2046
2241
|
const cmd = parent.command("delete").description("Delete a worklog entry").argument("<key>", "Issue key", issueKey);
|
|
2047
2242
|
subEntityOption(cmd, "worklog", { mandatory: true });
|
|
2048
|
-
cmd.addOption(new
|
|
2243
|
+
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(
|
|
2049
2244
|
"--increase-by <amount>",
|
|
2050
2245
|
'Amount to increase the estimate by; required when --adjust-estimate is "manual"',
|
|
2051
2246
|
text
|
|
@@ -2086,12 +2281,12 @@ function list4(parent) {
|
|
|
2086
2281
|
}
|
|
2087
2282
|
|
|
2088
2283
|
// src/commands/issue/worklog/update.ts
|
|
2089
|
-
import { Option as
|
|
2284
|
+
import { Option as Option7 } from "commander";
|
|
2090
2285
|
var ADJUST_ESTIMATE2 = ["new", "leave", "auto"];
|
|
2091
2286
|
function update4(parent) {
|
|
2092
2287
|
const cmd = parent.command("update").description("Update an existing worklog entry").argument("<key>", "Issue key", issueKey);
|
|
2093
2288
|
subEntityOption(cmd, "worklog", { mandatory: true });
|
|
2094
|
-
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
|
|
2289
|
+
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);
|
|
2095
2290
|
commentOption(cmd, { description: "Worklog comment" });
|
|
2096
2291
|
examples(cmd, [
|
|
2097
2292
|
'PROJ-123 --worklog-id 12345 --time "1h 30m"',
|
|
@@ -2149,6 +2344,8 @@ function registerIssueCommands(program) {
|
|
|
2149
2344
|
deleteIssue(issue);
|
|
2150
2345
|
transition(issue);
|
|
2151
2346
|
transitions(issue);
|
|
2347
|
+
createmeta(issue);
|
|
2348
|
+
editmeta(issue);
|
|
2152
2349
|
assign(issue);
|
|
2153
2350
|
registerCommentCommands(issue);
|
|
2154
2351
|
registerWorklogCommands(issue);
|
|
@@ -2250,10 +2447,10 @@ function issues2(parent) {
|
|
|
2250
2447
|
}
|
|
2251
2448
|
|
|
2252
2449
|
// src/commands/sprint/list.ts
|
|
2253
|
-
import { Option as
|
|
2450
|
+
import { Option as Option8 } from "commander";
|
|
2254
2451
|
var SPRINT_STATES = ["future", "active", "closed"];
|
|
2255
2452
|
function list6(parent) {
|
|
2256
|
-
const cmd = parent.command("list").description("List sprints for a board").requiredOption("--board <id>", "Board ID", positiveInt).addOption(new
|
|
2453
|
+
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));
|
|
2257
2454
|
examples(cmd, ["--board 42", "--board 42 --state active"]);
|
|
2258
2455
|
cmd.action(async (opts) => {
|
|
2259
2456
|
const client = getClient();
|
|
@@ -2266,10 +2463,10 @@ function list6(parent) {
|
|
|
2266
2463
|
}
|
|
2267
2464
|
|
|
2268
2465
|
// src/commands/sprint/update.ts
|
|
2269
|
-
import { Argument as Argument4, Option as
|
|
2466
|
+
import { Argument as Argument4, Option as Option9 } from "commander";
|
|
2270
2467
|
var SPRINT_STATES2 = ["future", "active", "closed"];
|
|
2271
2468
|
function update5(parent) {
|
|
2272
|
-
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
|
|
2469
|
+
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);
|
|
2273
2470
|
examples(cmd, [
|
|
2274
2471
|
'100 --name "Sprint 10 - Extended"',
|
|
2275
2472
|
"100 --state active",
|
|
@@ -2309,10 +2506,10 @@ function registerSprintCommands(program) {
|
|
|
2309
2506
|
|
|
2310
2507
|
// src/commands/token/client.ts
|
|
2311
2508
|
import { JiraClient as JiraClient2 } from "jira-data-center-client";
|
|
2312
|
-
function getTokenClient(
|
|
2509
|
+
function getTokenClient(options = {}) {
|
|
2313
2510
|
const baseUrl = process.env.JIRA_URL;
|
|
2314
|
-
const username =
|
|
2315
|
-
const password =
|
|
2511
|
+
const username = options.basicUsername ?? process.env.JIRA_BASIC_USERNAME;
|
|
2512
|
+
const password = options.basicPassword ?? process.env.JIRA_BASIC_PASSWORD;
|
|
2316
2513
|
if (!baseUrl || !username || !password) {
|
|
2317
2514
|
const missing = [
|
|
2318
2515
|
...!baseUrl ? ["JIRA_URL"] : [],
|
|
@@ -2453,12 +2650,12 @@ function registerUserCommands(program) {
|
|
|
2453
2650
|
function assertExactlyOne(opts, keys, label) {
|
|
2454
2651
|
const present = keys.filter((k) => opts[k] !== void 0);
|
|
2455
2652
|
if (present.length !== 1)
|
|
2456
|
-
throw new
|
|
2653
|
+
throw new CliUsageError(`Provide exactly one of ${keys.map((k) => `--${k}`).join(", ")} (${label}).`);
|
|
2457
2654
|
return present[0];
|
|
2458
2655
|
}
|
|
2459
2656
|
function assertAtLeastOne(opts, keys) {
|
|
2460
2657
|
if (!keys.some((k) => opts[k] !== void 0))
|
|
2461
|
-
throw new
|
|
2658
|
+
throw new CliUsageError(`Provide at least one of ${keys.map((k) => `--${k}`).join(", ")}.`);
|
|
2462
2659
|
}
|
|
2463
2660
|
|
|
2464
2661
|
// src/commands/xray/execution/add.ts
|
|
@@ -2489,7 +2686,9 @@ function add(parent) {
|
|
|
2489
2686
|
}
|
|
2490
2687
|
const allKeys = [...keys];
|
|
2491
2688
|
if (allKeys.length === 0) {
|
|
2492
|
-
throw new
|
|
2689
|
+
throw new CliUsageError(
|
|
2690
|
+
"No tests to add \u2014 the given --set(s) contain no tests and no --test keys were provided."
|
|
2691
|
+
);
|
|
2493
2692
|
}
|
|
2494
2693
|
const result = await client.testExecutions.addTests({ execKey, keys: allKeys });
|
|
2495
2694
|
output(result ?? { added: allKeys });
|
|
@@ -2733,7 +2932,7 @@ function update7(parent) {
|
|
|
2733
2932
|
]);
|
|
2734
2933
|
cmd.action(async (folderId, opts) => {
|
|
2735
2934
|
if (opts.name === void 0 && opts.rank === void 0) {
|
|
2736
|
-
throw new
|
|
2935
|
+
throw new CliUsageError("Provide at least one of --name or --rank.");
|
|
2737
2936
|
}
|
|
2738
2937
|
const client = getClient();
|
|
2739
2938
|
await client.testRepository.updateFolder({
|
|
@@ -2763,12 +2962,12 @@ function registerFolderCommands(xray) {
|
|
|
2763
2962
|
// src/commands/xray/import/execution.ts
|
|
2764
2963
|
import { readFileSync as readFileSync4 } from "fs";
|
|
2765
2964
|
import { basename as basename2 } from "path";
|
|
2766
|
-
import { Option as
|
|
2965
|
+
import { Option as Option10 } from "commander";
|
|
2767
2966
|
var FORMATS = ["xray", "junit", "testng", "nunit", "xunit", "robot", "cucumber", "behave"];
|
|
2768
2967
|
var NEEDS_SCOPE = /* @__PURE__ */ new Set(["junit", "testng", "nunit", "xunit", "robot"]);
|
|
2769
2968
|
function execution(parent) {
|
|
2770
2969
|
const cmd = parent.command("execution").description("Import test execution results into Xray").requiredOption("--file <path>", "Path to the result file", filePath).addOption(
|
|
2771
|
-
new
|
|
2970
|
+
new Option10(
|
|
2772
2971
|
"--format <format>",
|
|
2773
2972
|
"Result format. xray/cucumber/behave: JSON body, no --project/--execution required. junit/testng/nunit/xunit/robot: XML body, requires --project OR --execution."
|
|
2774
2973
|
).choices(FORMATS).makeOptionMandatory()
|
|
@@ -2789,7 +2988,7 @@ function execution(parent) {
|
|
|
2789
2988
|
cmd.action(
|
|
2790
2989
|
async (opts) => {
|
|
2791
2990
|
if (NEEDS_SCOPE.has(opts.format) && !opts.project && !opts.execution) {
|
|
2792
|
-
throw new
|
|
2991
|
+
throw new CliUsageError(
|
|
2793
2992
|
`--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.`
|
|
2794
2993
|
);
|
|
2795
2994
|
}
|
|
@@ -2966,7 +3165,7 @@ function add4(parent) {
|
|
|
2966
3165
|
}
|
|
2967
3166
|
|
|
2968
3167
|
// src/commands/xray/precondition/create.ts
|
|
2969
|
-
import { Option as
|
|
3168
|
+
import { Option as Option11 } from "commander";
|
|
2970
3169
|
|
|
2971
3170
|
// src/utils/xray/fields.ts
|
|
2972
3171
|
var CONCEPT_SUFFIX = {
|
|
@@ -2997,18 +3196,22 @@ function resolveTestTypeValue(allowedValues, type) {
|
|
|
2997
3196
|
if (type === "manual") {
|
|
2998
3197
|
const match2 = allowedValues.find((v) => /manual/i.test(v));
|
|
2999
3198
|
if (!match2)
|
|
3000
|
-
throw new
|
|
3199
|
+
throw new CliNotFoundError(
|
|
3200
|
+
`No 'manual' Test Type value found on this instance. Available: ${allowedValues.join(", ")}`
|
|
3201
|
+
);
|
|
3001
3202
|
return match2;
|
|
3002
3203
|
}
|
|
3003
3204
|
if (type === "generic") {
|
|
3004
3205
|
const match2 = allowedValues.find((v) => /generic/i.test(v));
|
|
3005
3206
|
if (!match2)
|
|
3006
|
-
throw new
|
|
3207
|
+
throw new CliNotFoundError(
|
|
3208
|
+
`No 'generic' Test Type value found on this instance. Available: ${allowedValues.join(", ")}`
|
|
3209
|
+
);
|
|
3007
3210
|
return match2;
|
|
3008
3211
|
}
|
|
3009
3212
|
const match = allowedValues.find((v) => !/manual/i.test(v) && !/generic/i.test(v));
|
|
3010
3213
|
if (!match)
|
|
3011
|
-
throw new
|
|
3214
|
+
throw new CliNotFoundError(
|
|
3012
3215
|
`No 'cucumber' (gherkin/automation) Test Type value found on this instance. Available: ${allowedValues.join(", ")}`
|
|
3013
3216
|
);
|
|
3014
3217
|
return match;
|
|
@@ -3029,7 +3232,7 @@ async function resolveTypeValue(client, params) {
|
|
|
3029
3232
|
}
|
|
3030
3233
|
function requireField(map, concept, label) {
|
|
3031
3234
|
const id = map[concept];
|
|
3032
|
-
if (!id) throw new
|
|
3235
|
+
if (!id) throw new CliNotFoundError(`Xray field for '${label}' not found on this instance.`);
|
|
3033
3236
|
return id;
|
|
3034
3237
|
}
|
|
3035
3238
|
function buildTestCustomFields(map, opts) {
|
|
@@ -3067,7 +3270,7 @@ function create10(parent) {
|
|
|
3067
3270
|
const cmd = parent.command("create").description(
|
|
3068
3271
|
"Create a new Xray Pre-Condition issue. Note: the Pre-Condition issue type is only available in projects that have it configured (e.g. BD); it is not on your project scheme."
|
|
3069
3272
|
).requiredOption("--project <key>", "Project key (e.g. BD)", text).requiredOption("--summary <text>", "Pre-Condition summary (title)", text).addOption(
|
|
3070
|
-
new
|
|
3273
|
+
new Option11("--type <type>", "Pre-Condition type").choices(["manual", "generic", "cucumber"]).default("manual")
|
|
3071
3274
|
);
|
|
3072
3275
|
textOrFileOption(cmd, "condition", { description: "Pre-condition body / definition text" });
|
|
3073
3276
|
textOrFileOption(cmd, "description", { description: "Pre-Condition issue description" });
|
|
@@ -3138,9 +3341,9 @@ function remove4(parent) {
|
|
|
3138
3341
|
}
|
|
3139
3342
|
|
|
3140
3343
|
// src/commands/xray/precondition/update.ts
|
|
3141
|
-
import { Option as
|
|
3344
|
+
import { Option as Option12 } from "commander";
|
|
3142
3345
|
function update9(parent) {
|
|
3143
|
-
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
|
|
3346
|
+
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"]));
|
|
3144
3347
|
textOrFileOption(cmd, "condition", { description: "New pre-condition body / definition text" });
|
|
3145
3348
|
textOrFileOption(cmd, "description", { description: "New Pre-Condition issue description" });
|
|
3146
3349
|
examples(cmd, ['PROJ-50 --summary "Updated precondition"', 'PROJ-50 --type generic --condition "apiKey != null"']);
|
|
@@ -3163,12 +3366,12 @@ function update9(parent) {
|
|
|
3163
3366
|
fieldMap: map
|
|
3164
3367
|
});
|
|
3165
3368
|
const typeId = map["precondition-type"];
|
|
3166
|
-
if (!typeId) throw new
|
|
3369
|
+
if (!typeId) throw new CliNotFoundError("Xray field for 'Pre-Condition Type' not found on this instance.");
|
|
3167
3370
|
customFields[typeId] = { value: typeValue };
|
|
3168
3371
|
}
|
|
3169
3372
|
if (condition !== void 0) {
|
|
3170
3373
|
const bodyId = map["precondition-body"];
|
|
3171
|
-
if (!bodyId) throw new
|
|
3374
|
+
if (!bodyId) throw new CliNotFoundError("Xray field for 'Conditions' not found on this instance.");
|
|
3172
3375
|
customFields[bodyId] = condition;
|
|
3173
3376
|
}
|
|
3174
3377
|
}
|
|
@@ -3199,9 +3402,10 @@ function registerPreconditionCommands(xray) {
|
|
|
3199
3402
|
// src/utils/xray/run-id.ts
|
|
3200
3403
|
async function resolveRunId(client, o) {
|
|
3201
3404
|
if (o.runId !== void 0) return o.runId;
|
|
3202
|
-
if (!o.execution || !o.test)
|
|
3405
|
+
if (!o.execution || !o.test)
|
|
3406
|
+
throw new CliUsageError("Provide a run id, or both --execution and --test to resolve the run.");
|
|
3203
3407
|
const run = await client.testRuns.resolve({ execKey: o.execution, testKey: o.test });
|
|
3204
|
-
if (!run?.id) throw new
|
|
3408
|
+
if (!run?.id) throw new CliNotFoundError(`No test run found for ${o.test} in ${o.execution}.`);
|
|
3205
3409
|
return Number(run.id);
|
|
3206
3410
|
}
|
|
3207
3411
|
|
|
@@ -3573,7 +3777,7 @@ function update12(parent) {
|
|
|
3573
3777
|
const data = resolveTextOrFile(opts, "data", { required: false });
|
|
3574
3778
|
const result = resolveTextOrFile(opts, "result", { required: false });
|
|
3575
3779
|
if (action === void 0 && data === void 0 && result === void 0) {
|
|
3576
|
-
throw new
|
|
3780
|
+
throw new CliUsageError(
|
|
3577
3781
|
"Provide at least one of --action (or --action-file), --data (or --data-file), --result (or --result-file)."
|
|
3578
3782
|
);
|
|
3579
3783
|
}
|
|
@@ -3597,16 +3801,16 @@ function registerStepCommands(xray) {
|
|
|
3597
3801
|
}
|
|
3598
3802
|
|
|
3599
3803
|
// src/commands/xray/test/create.ts
|
|
3600
|
-
import { Option as
|
|
3601
|
-
import { z as
|
|
3602
|
-
var stepsSchema =
|
|
3603
|
-
|
|
3804
|
+
import { Option as Option13 } from "commander";
|
|
3805
|
+
import { z as z5 } from "zod";
|
|
3806
|
+
var stepsSchema = z5.array(
|
|
3807
|
+
z5.object({ action: z5.string(), data: z5.string().optional(), result: z5.string().optional() })
|
|
3604
3808
|
);
|
|
3605
3809
|
function create11(parent) {
|
|
3606
3810
|
const cmd = parent.command("create").description("Create a new Xray Test issue").requiredOption("--project <key>", "Project key (e.g. AI)", text).requiredOption("--summary <text>", "Test summary (title)", text).addOption(
|
|
3607
|
-
new
|
|
3811
|
+
new Option13("--type <type>", "Test type (when cucumber: also pass --gherkin; when generic: --definition)").choices(["manual", "cucumber", "generic"]).default("manual")
|
|
3608
3812
|
).addOption(
|
|
3609
|
-
new
|
|
3813
|
+
new Option13("--cucumber-type <cucumberType>", "Cucumber scenario type (required when --type cucumber)").choices([
|
|
3610
3814
|
"scenario",
|
|
3611
3815
|
"scenario-outline"
|
|
3612
3816
|
])
|
|
@@ -3634,10 +3838,10 @@ function create11(parent) {
|
|
|
3634
3838
|
const definition = resolveTextOrFile(opts, "definition", { required: false });
|
|
3635
3839
|
const description = resolveTextOrFile(opts, "description", { required: false });
|
|
3636
3840
|
if (opts.type === "cucumber" && !gherkin) {
|
|
3637
|
-
throw new
|
|
3841
|
+
throw new CliUsageError("--gherkin (or --gherkin-file) is required when --type is cucumber.");
|
|
3638
3842
|
}
|
|
3639
3843
|
if (opts.type === "generic" && !definition) {
|
|
3640
|
-
throw new
|
|
3844
|
+
throw new CliUsageError("--definition (or --definition-file) is required when --type is generic.");
|
|
3641
3845
|
}
|
|
3642
3846
|
const cucumberType = opts.cucumberType === "scenario" ? "Scenario" : opts.cucumberType === "scenario-outline" ? "Scenario Outline" : void 0;
|
|
3643
3847
|
const client = getClient();
|
|
@@ -3712,13 +3916,13 @@ function list18(parent) {
|
|
|
3712
3916
|
const client = getClient();
|
|
3713
3917
|
if (opts.folder !== void 0) {
|
|
3714
3918
|
if (!opts.project) {
|
|
3715
|
-
throw new
|
|
3919
|
+
throw new CliUsageError("--project is required when --folder is specified.");
|
|
3716
3920
|
}
|
|
3717
3921
|
const otherScopes = ["set", "plan", "execution", "precondition"].filter(
|
|
3718
3922
|
(k) => opts[k] !== void 0
|
|
3719
3923
|
);
|
|
3720
3924
|
if (otherScopes.length > 0) {
|
|
3721
|
-
throw new
|
|
3925
|
+
throw new CliUsageError(
|
|
3722
3926
|
`--folder cannot be combined with ${otherScopes.map((k) => `--${k}`).join(", ")}. Use --folder with --project only.`
|
|
3723
3927
|
);
|
|
3724
3928
|
}
|
|
@@ -3776,10 +3980,10 @@ function list18(parent) {
|
|
|
3776
3980
|
}
|
|
3777
3981
|
|
|
3778
3982
|
// src/commands/xray/test/update.ts
|
|
3779
|
-
import { Option as
|
|
3983
|
+
import { Option as Option14 } from "commander";
|
|
3780
3984
|
function update13(parent) {
|
|
3781
|
-
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
|
|
3782
|
-
new
|
|
3985
|
+
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(
|
|
3986
|
+
new Option14("--cucumber-type <cucumberType>", "Cucumber scenario type (required when --type cucumber)").choices([
|
|
3783
3987
|
"scenario",
|
|
3784
3988
|
"scenario-outline"
|
|
3785
3989
|
])
|
|
@@ -3810,10 +4014,10 @@ function update13(parent) {
|
|
|
3810
4014
|
if (opts.reporter !== void 0) fields.reporter = { name: opts.reporter };
|
|
3811
4015
|
if (opts.type !== void 0) {
|
|
3812
4016
|
if (opts.type === "cucumber" && !gherkin) {
|
|
3813
|
-
throw new
|
|
4017
|
+
throw new CliUsageError("--gherkin (or --gherkin-file) is required when --type is cucumber.");
|
|
3814
4018
|
}
|
|
3815
4019
|
if (opts.type === "generic" && !definition) {
|
|
3816
|
-
throw new
|
|
4020
|
+
throw new CliUsageError("--definition (or --definition-file) is required when --type is generic.");
|
|
3817
4021
|
}
|
|
3818
4022
|
const cucumberType = opts.cucumberType === "scenario" ? "Scenario" : opts.cucumberType === "scenario-outline" ? "Scenario Outline" : void 0;
|
|
3819
4023
|
const projectKey = testKey.split("-")[0];
|
|
@@ -3967,7 +4171,7 @@ function registerXrayCommands(program) {
|
|
|
3967
4171
|
var DIM = "\x1B[2m";
|
|
3968
4172
|
var RESET = "\x1B[0m";
|
|
3969
4173
|
function buildProgram() {
|
|
3970
|
-
const program = new
|
|
4174
|
+
const program = new Command17();
|
|
3971
4175
|
program.name("jiradc").description("Jira Data Center CLI").version(readPackageVersion(import.meta.url)).configureHelp({
|
|
3972
4176
|
styleTitle: (str) => styleText("bold", str),
|
|
3973
4177
|
styleUsage: (str) => styleText("dim", str),
|
|
@@ -4028,10 +4232,33 @@ function getCredentialInfo() {
|
|
|
4028
4232
|
};
|
|
4029
4233
|
}
|
|
4030
4234
|
|
|
4235
|
+
// src/utils/field-error-recovery.ts
|
|
4236
|
+
var CUSTOM_FIELD_KEY = /^customfield_(\d+)$/;
|
|
4237
|
+
function customFieldIds(detail) {
|
|
4238
|
+
if (!detail || typeof detail !== "object") return [];
|
|
4239
|
+
const errors = detail.errors;
|
|
4240
|
+
if (!errors || typeof errors !== "object") return [];
|
|
4241
|
+
return Object.keys(errors).filter((k) => CUSTOM_FIELD_KEY.test(k));
|
|
4242
|
+
}
|
|
4243
|
+
function adaptFieldErrors(normalized) {
|
|
4244
|
+
const ids = customFieldIds(normalized.detail);
|
|
4245
|
+
if (ids.length === 0) return normalized;
|
|
4246
|
+
const numbers = ids.map((id) => CUSTOM_FIELD_KEY.exec(id)?.[1] ?? id);
|
|
4247
|
+
const lookup = numbers.map((n) => `jiradc field search ${n}`).join(", ");
|
|
4248
|
+
const plural = ids.length > 1 ? "s" : "";
|
|
4249
|
+
return {
|
|
4250
|
+
...normalized,
|
|
4251
|
+
recovery: `Jira rejected field${plural} ${ids.join(", ")}. Run ${lookup} to see the field's name and type before retrying. For the values a field accepts, use issue transitions (transition screens), issue createmeta (create) or issue editmeta (edit).`
|
|
4252
|
+
};
|
|
4253
|
+
}
|
|
4254
|
+
|
|
4031
4255
|
// src/index.ts
|
|
4032
4256
|
await runCli(buildProgram(), {
|
|
4033
4257
|
credentialInfo: getCredentialInfo,
|
|
4034
4258
|
service: "Jira",
|
|
4035
4259
|
authRecovery: "Set the JIRA_URL and JIRA_TOKEN environment variables.",
|
|
4036
|
-
|
|
4260
|
+
urlEnvVar: "JIRA_URL",
|
|
4261
|
+
// Jira names rejected fields by custom-field id. The generic recovery leaves
|
|
4262
|
+
// an agent holding an opaque id, so point it at the command that explains one.
|
|
4263
|
+
adapt: adaptFieldErrors
|
|
4037
4264
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jiradc-cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.33",
|
|
4
4
|
"publish": true,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"dependencies": {
|
|
14
14
|
"commander": "^13.1.0",
|
|
15
15
|
"zod": "^3.25.67",
|
|
16
|
-
"jira-data-center-client": "1.0.
|
|
16
|
+
"jira-data-center-client": "1.0.44"
|
|
17
17
|
},
|
|
18
18
|
"devDependencies": {
|
|
19
19
|
"@types/node": "24.10.4",
|
|
@@ -23,15 +23,17 @@
|
|
|
23
23
|
"tsx": "^4.19.2",
|
|
24
24
|
"typescript": "^5.7.2",
|
|
25
25
|
"vitest": "^4.0.16",
|
|
26
|
-
"
|
|
26
|
+
"config-typescript": "0.0.0",
|
|
27
27
|
"config-eslint": "0.0.0",
|
|
28
|
-
"
|
|
28
|
+
"cli-utils": "1.0.0"
|
|
29
29
|
},
|
|
30
30
|
"engines": {
|
|
31
31
|
"node": ">=22.0.0"
|
|
32
32
|
},
|
|
33
33
|
"scripts": {
|
|
34
34
|
"build": "tsup",
|
|
35
|
+
"check-types": "tsc --noEmit -p tsconfig.json",
|
|
36
|
+
"clean": "rm -rf dist",
|
|
35
37
|
"dev": "tsx src/index.ts",
|
|
36
38
|
"lint": "eslint src tests",
|
|
37
39
|
"lint:fix": "eslint src tests --fix",
|