jiradc-cli 1.0.4 → 1.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (201) hide show
  1. package/dist/index.js +1082 -33
  2. package/package.json +4 -3
  3. package/dist/commands/board/index.d.ts +0 -3
  4. package/dist/commands/board/index.d.ts.map +0 -1
  5. package/dist/commands/board/index.js +0 -16
  6. package/dist/commands/board/index.js.map +0 -1
  7. package/dist/commands/board/issues.d.ts +0 -3
  8. package/dist/commands/board/issues.d.ts.map +0 -1
  9. package/dist/commands/board/issues.js +0 -24
  10. package/dist/commands/board/issues.js.map +0 -1
  11. package/dist/commands/board/list.d.ts +0 -3
  12. package/dist/commands/board/list.d.ts.map +0 -1
  13. package/dist/commands/board/list.js +0 -23
  14. package/dist/commands/board/list.js.map +0 -1
  15. package/dist/commands/field/index.d.ts +0 -3
  16. package/dist/commands/field/index.d.ts.map +0 -1
  17. package/dist/commands/field/index.js +0 -16
  18. package/dist/commands/field/index.js.map +0 -1
  19. package/dist/commands/field/options.d.ts +0 -3
  20. package/dist/commands/field/options.d.ts.map +0 -1
  21. package/dist/commands/field/options.js +0 -22
  22. package/dist/commands/field/options.js.map +0 -1
  23. package/dist/commands/field/search.d.ts +0 -3
  24. package/dist/commands/field/search.d.ts.map +0 -1
  25. package/dist/commands/field/search.js +0 -15
  26. package/dist/commands/field/search.js.map +0 -1
  27. package/dist/commands/issue/attachment/delete.d.ts +0 -3
  28. package/dist/commands/issue/attachment/delete.d.ts.map +0 -1
  29. package/dist/commands/issue/attachment/delete.js +0 -15
  30. package/dist/commands/issue/attachment/delete.js.map +0 -1
  31. package/dist/commands/issue/attachment/download-all.d.ts +0 -3
  32. package/dist/commands/issue/attachment/download-all.d.ts.map +0 -1
  33. package/dist/commands/issue/attachment/download-all.js +0 -48
  34. package/dist/commands/issue/attachment/download-all.js.map +0 -1
  35. package/dist/commands/issue/attachment/download.d.ts +0 -3
  36. package/dist/commands/issue/attachment/download.d.ts.map +0 -1
  37. package/dist/commands/issue/attachment/download.js +0 -30
  38. package/dist/commands/issue/attachment/download.js.map +0 -1
  39. package/dist/commands/issue/attachment/index.d.ts +0 -3
  40. package/dist/commands/issue/attachment/index.d.ts.map +0 -1
  41. package/dist/commands/issue/attachment/index.js +0 -24
  42. package/dist/commands/issue/attachment/index.js.map +0 -1
  43. package/dist/commands/issue/attachment/list.d.ts +0 -3
  44. package/dist/commands/issue/attachment/list.d.ts.map +0 -1
  45. package/dist/commands/issue/attachment/list.js +0 -28
  46. package/dist/commands/issue/attachment/list.js.map +0 -1
  47. package/dist/commands/issue/attachment/upload.d.ts +0 -3
  48. package/dist/commands/issue/attachment/upload.d.ts.map +0 -1
  49. package/dist/commands/issue/attachment/upload.js +0 -30
  50. package/dist/commands/issue/attachment/upload.js.map +0 -1
  51. package/dist/commands/issue/attachments.d.ts +0 -3
  52. package/dist/commands/issue/attachments.d.ts.map +0 -1
  53. package/dist/commands/issue/attachments.js +0 -48
  54. package/dist/commands/issue/attachments.js.map +0 -1
  55. package/dist/commands/issue/batch-changelog.d.ts +0 -3
  56. package/dist/commands/issue/batch-changelog.d.ts.map +0 -1
  57. package/dist/commands/issue/batch-changelog.js +0 -24
  58. package/dist/commands/issue/batch-changelog.js.map +0 -1
  59. package/dist/commands/issue/batch-create.d.ts +0 -3
  60. package/dist/commands/issue/batch-create.d.ts.map +0 -1
  61. package/dist/commands/issue/batch-create.js +0 -25
  62. package/dist/commands/issue/batch-create.js.map +0 -1
  63. package/dist/commands/issue/changelog.d.ts +0 -3
  64. package/dist/commands/issue/changelog.d.ts.map +0 -1
  65. package/dist/commands/issue/changelog.js +0 -15
  66. package/dist/commands/issue/changelog.js.map +0 -1
  67. package/dist/commands/issue/clone.d.ts +0 -3
  68. package/dist/commands/issue/clone.d.ts.map +0 -1
  69. package/dist/commands/issue/clone.js +0 -103
  70. package/dist/commands/issue/clone.js.map +0 -1
  71. package/dist/commands/issue/comment-edit.d.ts +0 -3
  72. package/dist/commands/issue/comment-edit.d.ts.map +0 -1
  73. package/dist/commands/issue/comment-edit.js +0 -16
  74. package/dist/commands/issue/comment-edit.js.map +0 -1
  75. package/dist/commands/issue/comment.d.ts +0 -3
  76. package/dist/commands/issue/comment.d.ts.map +0 -1
  77. package/dist/commands/issue/comment.js +0 -15
  78. package/dist/commands/issue/comment.js.map +0 -1
  79. package/dist/commands/issue/create.d.ts +0 -3
  80. package/dist/commands/issue/create.d.ts.map +0 -1
  81. package/dist/commands/issue/create.js +0 -45
  82. package/dist/commands/issue/create.js.map +0 -1
  83. package/dist/commands/issue/delete.d.ts +0 -3
  84. package/dist/commands/issue/delete.d.ts.map +0 -1
  85. package/dist/commands/issue/delete.js +0 -15
  86. package/dist/commands/issue/delete.js.map +0 -1
  87. package/dist/commands/issue/dev-status.d.ts +0 -3
  88. package/dist/commands/issue/dev-status.d.ts.map +0 -1
  89. package/dist/commands/issue/dev-status.js +0 -81
  90. package/dist/commands/issue/dev-status.js.map +0 -1
  91. package/dist/commands/issue/get-worklog.d.ts +0 -3
  92. package/dist/commands/issue/get-worklog.d.ts.map +0 -1
  93. package/dist/commands/issue/get-worklog.js +0 -20
  94. package/dist/commands/issue/get-worklog.js.map +0 -1
  95. package/dist/commands/issue/get.d.ts +0 -3
  96. package/dist/commands/issue/get.d.ts.map +0 -1
  97. package/dist/commands/issue/get.js +0 -23
  98. package/dist/commands/issue/get.js.map +0 -1
  99. package/dist/commands/issue/index.d.ts +0 -3
  100. package/dist/commands/issue/index.d.ts.map +0 -1
  101. package/dist/commands/issue/index.js +0 -59
  102. package/dist/commands/issue/index.js.map +0 -1
  103. package/dist/commands/issue/link-epic.d.ts +0 -3
  104. package/dist/commands/issue/link-epic.d.ts.map +0 -1
  105. package/dist/commands/issue/link-epic.js +0 -18
  106. package/dist/commands/issue/link-epic.js.map +0 -1
  107. package/dist/commands/issue/link-types.d.ts +0 -3
  108. package/dist/commands/issue/link-types.d.ts.map +0 -1
  109. package/dist/commands/issue/link-types.js +0 -14
  110. package/dist/commands/issue/link-types.js.map +0 -1
  111. package/dist/commands/issue/link.d.ts +0 -3
  112. package/dist/commands/issue/link.d.ts.map +0 -1
  113. package/dist/commands/issue/link.js +0 -23
  114. package/dist/commands/issue/link.js.map +0 -1
  115. package/dist/commands/issue/search.d.ts +0 -3
  116. package/dist/commands/issue/search.d.ts.map +0 -1
  117. package/dist/commands/issue/search.js +0 -25
  118. package/dist/commands/issue/search.js.map +0 -1
  119. package/dist/commands/issue/transition.d.ts +0 -3
  120. package/dist/commands/issue/transition.d.ts.map +0 -1
  121. package/dist/commands/issue/transition.js +0 -16
  122. package/dist/commands/issue/transition.js.map +0 -1
  123. package/dist/commands/issue/transitions.d.ts +0 -3
  124. package/dist/commands/issue/transitions.d.ts.map +0 -1
  125. package/dist/commands/issue/transitions.js +0 -14
  126. package/dist/commands/issue/transitions.js.map +0 -1
  127. package/dist/commands/issue/unlink.d.ts +0 -3
  128. package/dist/commands/issue/unlink.d.ts.map +0 -1
  129. package/dist/commands/issue/unlink.js +0 -14
  130. package/dist/commands/issue/unlink.js.map +0 -1
  131. package/dist/commands/issue/update.d.ts +0 -3
  132. package/dist/commands/issue/update.d.ts.map +0 -1
  133. package/dist/commands/issue/update.js +0 -41
  134. package/dist/commands/issue/update.js.map +0 -1
  135. package/dist/commands/issue/worklog.d.ts +0 -3
  136. package/dist/commands/issue/worklog.d.ts.map +0 -1
  137. package/dist/commands/issue/worklog.js +0 -22
  138. package/dist/commands/issue/worklog.js.map +0 -1
  139. package/dist/commands/project/components.d.ts +0 -3
  140. package/dist/commands/project/components.d.ts.map +0 -1
  141. package/dist/commands/project/components.js +0 -14
  142. package/dist/commands/project/components.js.map +0 -1
  143. package/dist/commands/project/index.d.ts +0 -3
  144. package/dist/commands/project/index.d.ts.map +0 -1
  145. package/dist/commands/project/index.js +0 -18
  146. package/dist/commands/project/index.js.map +0 -1
  147. package/dist/commands/project/list.d.ts +0 -3
  148. package/dist/commands/project/list.d.ts.map +0 -1
  149. package/dist/commands/project/list.js +0 -16
  150. package/dist/commands/project/list.js.map +0 -1
  151. package/dist/commands/project/versions.d.ts +0 -3
  152. package/dist/commands/project/versions.d.ts.map +0 -1
  153. package/dist/commands/project/versions.js +0 -15
  154. package/dist/commands/project/versions.js.map +0 -1
  155. package/dist/commands/sprint/create.d.ts +0 -3
  156. package/dist/commands/sprint/create.d.ts.map +0 -1
  157. package/dist/commands/sprint/create.js +0 -25
  158. package/dist/commands/sprint/create.js.map +0 -1
  159. package/dist/commands/sprint/index.d.ts +0 -3
  160. package/dist/commands/sprint/index.d.ts.map +0 -1
  161. package/dist/commands/sprint/index.js +0 -21
  162. package/dist/commands/sprint/index.js.map +0 -1
  163. package/dist/commands/sprint/issues.d.ts +0 -3
  164. package/dist/commands/sprint/issues.d.ts.map +0 -1
  165. package/dist/commands/sprint/issues.js +0 -24
  166. package/dist/commands/sprint/issues.js.map +0 -1
  167. package/dist/commands/sprint/list.d.ts +0 -3
  168. package/dist/commands/sprint/list.d.ts.map +0 -1
  169. package/dist/commands/sprint/list.js +0 -19
  170. package/dist/commands/sprint/list.js.map +0 -1
  171. package/dist/commands/sprint/update.d.ts +0 -3
  172. package/dist/commands/sprint/update.d.ts.map +0 -1
  173. package/dist/commands/sprint/update.js +0 -26
  174. package/dist/commands/sprint/update.js.map +0 -1
  175. package/dist/commands/user/index.d.ts +0 -3
  176. package/dist/commands/user/index.d.ts.map +0 -1
  177. package/dist/commands/user/index.js +0 -13
  178. package/dist/commands/user/index.js.map +0 -1
  179. package/dist/commands/user/me.d.ts +0 -3
  180. package/dist/commands/user/me.d.ts.map +0 -1
  181. package/dist/commands/user/me.js +0 -14
  182. package/dist/commands/user/me.js.map +0 -1
  183. package/dist/index.d.ts +0 -3
  184. package/dist/index.d.ts.map +0 -1
  185. package/dist/index.js.map +0 -1
  186. package/dist/utils/client.d.ts +0 -3
  187. package/dist/utils/client.d.ts.map +0 -1
  188. package/dist/utils/client.js +0 -19
  189. package/dist/utils/client.js.map +0 -1
  190. package/dist/utils/constants.d.ts +0 -7
  191. package/dist/utils/constants.d.ts.map +0 -1
  192. package/dist/utils/constants.js +0 -20
  193. package/dist/utils/constants.js.map +0 -1
  194. package/dist/utils/output.d.ts +0 -3
  195. package/dist/utils/output.d.ts.map +0 -1
  196. package/dist/utils/output.js +0 -48
  197. package/dist/utils/output.js.map +0 -1
  198. package/dist/utils/strip-response.d.ts +0 -6
  199. package/dist/utils/strip-response.d.ts.map +0 -1
  200. package/dist/utils/strip-response.js +0 -33
  201. package/dist/utils/strip-response.js.map +0 -1
package/dist/index.js CHANGED
@@ -1,34 +1,1084 @@
1
1
  #!/usr/bin/env node
2
- import { styleText } from 'node:util';
3
- import { Command } from 'commander';
4
- import { registerBoardCommands } from './commands/board/index.js';
5
- import { registerFieldCommands } from './commands/field/index.js';
6
- import { registerIssueCommands } from './commands/issue/index.js';
7
- import { registerProjectCommands } from './commands/project/index.js';
8
- import { registerSprintCommands } from './commands/sprint/index.js';
9
- import { registerUserCommands } from './commands/user/index.js';
10
- import { handleError } from './utils/output.js';
11
- const DIM = '\x1b[2m';
12
- const RESET = '\x1b[0m';
13
- const program = new Command();
14
- program
15
- .name('jiradc')
16
- .description('Jira Data Center CLI')
17
- .version('1.0.0')
18
- .configureHelp({
19
- styleTitle: (str) => styleText('bold', str),
20
- styleUsage: (str) => styleText('dim', str),
21
- styleCommandDescription: (str) => styleText('dim', str),
22
- styleOptionDescription: (str) => styleText('dim', str),
23
- styleSubcommandDescription: (str) => styleText('dim', str),
24
- })
25
- .addHelpText('beforeAll', `\n${styleText('bold', 'jiradc')} ${DIM}— Jira Data Center CLI${RESET}\n`)
26
- .addHelpText('after', `
27
- ${styleText('bold', 'Environment:')}
2
+
3
+ // src/index.ts
4
+ import { styleText } from "util";
5
+ import { Command } from "commander";
6
+
7
+ // src/utils/client.ts
8
+ import { JiraClient } from "jira-data-center-client";
9
+ function getClient() {
10
+ const baseUrl = process.env.JIRA_URL;
11
+ const token = process.env.JIRA_TOKEN;
12
+ if (!baseUrl || !token) {
13
+ const missing = [...!baseUrl ? ["JIRA_URL"] : [], ...!token ? ["JIRA_TOKEN"] : []];
14
+ process.stderr.write(
15
+ `${JSON.stringify({
16
+ error: `Missing required environment variables: ${missing.join(", ")}`,
17
+ setup: {
18
+ JIRA_URL: "Your Jira Server base URL (e.g., https://jira.example.com)",
19
+ JIRA_TOKEN: "Personal Access Token \u2014 generate in Jira > Profile > Personal Access Tokens"
20
+ },
21
+ hint: "Export these in your shell profile (e.g., ~/.zshrc)."
22
+ })}
23
+ `
24
+ );
25
+ process.exit(1);
26
+ }
27
+ return new JiraClient({ baseUrl, token });
28
+ }
29
+
30
+ // src/utils/strip-response.ts
31
+ function stripResponse(obj) {
32
+ if (Array.isArray(obj)) {
33
+ return obj.map(stripResponse);
34
+ }
35
+ if (obj === null || typeof obj !== "object") {
36
+ return obj;
37
+ }
38
+ const record = obj;
39
+ const result = {};
40
+ for (const [key, value] of Object.entries(record)) {
41
+ if (key === "self" || key === "_links" || key === "_expandable" || key === "expand" || key === "avatarUrls" || key === "avatarId" || key === "iconUrl") {
42
+ continue;
43
+ }
44
+ if (value === null || value === void 0) {
45
+ continue;
46
+ }
47
+ result[key] = stripResponse(value);
48
+ }
49
+ return result;
50
+ }
51
+
52
+ // src/utils/output.ts
53
+ function output(data) {
54
+ process.stdout.write(`${JSON.stringify(stripResponse(data), null, 2)}
55
+ `);
56
+ }
57
+ function handleError(err) {
58
+ const message = err instanceof Error ? err.message : String(err);
59
+ const axiosStatus = err?.response?.status;
60
+ if (axiosStatus === 400) {
61
+ const responseData = err?.response?.data;
62
+ const jiraErrors = responseData && typeof responseData === "object" ? responseData : void 0;
63
+ process.stderr.write(
64
+ `${JSON.stringify({
65
+ error: `Bad request (HTTP 400)`,
66
+ detail: jiraErrors ?? message,
67
+ hint: "Check that all parameters are valid (JQL syntax, field names, project keys, etc.)."
68
+ })}
69
+ `
70
+ );
71
+ } else if (axiosStatus === 401) {
72
+ process.stderr.write(
73
+ `${JSON.stringify({
74
+ error: "Authentication failed (HTTP 401)",
75
+ hint: "Verify that JIRA_URL and JIRA_TOKEN are set correctly. The token may be expired or invalid."
76
+ })}
77
+ `
78
+ );
79
+ } else if (axiosStatus === 403) {
80
+ const responseData = err?.response?.data;
81
+ process.stderr.write(
82
+ `${JSON.stringify({
83
+ error: "Forbidden (HTTP 403)",
84
+ detail: responseData && typeof responseData === "object" ? responseData : message,
85
+ hint: "Your account does not have permission for this operation. Check project permissions and token scope."
86
+ })}
87
+ `
88
+ );
89
+ } else if (message.includes("ENOTFOUND") || message.includes("ECONNREFUSED") || message.includes("ECONNRESET")) {
90
+ process.stderr.write(
91
+ `${JSON.stringify({
92
+ error: `Cannot connect to Jira server: ${message}`,
93
+ hint: "Verify that JIRA_URL is correct and the server is reachable."
94
+ })}
95
+ `
96
+ );
97
+ } else if (axiosStatus === 500) {
98
+ process.stderr.write(
99
+ `${JSON.stringify({
100
+ error: `Server error (HTTP 500): ${message}`,
101
+ hint: "The server returned an internal error. Check that all parameters are valid (project keys, issue keys, JQL query syntax)."
102
+ })}
103
+ `
104
+ );
105
+ } else {
106
+ process.stderr.write(`${JSON.stringify({ error: message })}
107
+ `);
108
+ }
109
+ process.exit(1);
110
+ }
111
+
112
+ // src/commands/board/issues.ts
113
+ function issues(parent) {
114
+ parent.command("issues <id>").description("Get issues for a board").option("--max <number>", "Max results", parseInt).option("--start-at <number>", "Starting index for pagination (default: 0)", parseInt).option("--fields <fields>", "Comma-separated field names to return").option("--jql <jql>", "Additional JQL filter within the board").addHelpText(
115
+ "after",
116
+ '\nExamples:\n jiradc board issues 42\n jiradc board issues 42 --max 10\n jiradc board issues 42 --jql "status = Open" --fields summary,status\n jiradc board issues 42 --start-at 50 --max 25'
117
+ ).action(async (id, opts) => {
118
+ const client = getClient();
119
+ const result = await client.agile.getBoardIssues({
120
+ boardId: parseInt(id, 10),
121
+ startAt: opts.startAt,
122
+ maxResults: opts.max,
123
+ fields: opts.fields?.split(",").map((f) => f.trim()),
124
+ jql: opts.jql
125
+ });
126
+ output(result);
127
+ });
128
+ }
129
+
130
+ // src/commands/board/list.ts
131
+ function list(parent) {
132
+ parent.command("list").description("List agile boards").option("--max <number>", "Max results", parseInt).option("--project <key>", "Filter boards by project key or ID").option("--type <type>", "Board type filter (scrum, kanban, simple)").option("--name <name>", "Filter boards by name").addHelpText(
133
+ "after",
134
+ '\nExamples:\n jiradc board list\n jiradc board list --max 10\n jiradc board list --project PROJ\n jiradc board list --type scrum --name "Team Board"'
135
+ ).action(async (opts) => {
136
+ const client = getClient();
137
+ const result = await client.agile.getBoards({
138
+ maxResults: opts.max,
139
+ projectKeyOrId: opts.project,
140
+ type: opts.type,
141
+ name: opts.name
142
+ });
143
+ output(result);
144
+ });
145
+ }
146
+
147
+ // src/commands/board/index.ts
148
+ function registerBoardCommands(program2) {
149
+ const board = program2.command("board").description("Board operations").addHelpText(
150
+ "after",
151
+ `
152
+ Examples:
153
+ $ jiradc board list
154
+ $ jiradc board list --type scrum --project PROJ
155
+ $ jiradc board issues 42 --max 20
156
+ `
157
+ );
158
+ list(board);
159
+ issues(board);
160
+ }
161
+
162
+ // src/commands/field/options.ts
163
+ function options(parent) {
164
+ parent.command("options <id>").description("Get available options for a custom field").option("--query <text>", "Filter options by text").option("--max <number>", "Max results to return", parseInt).option("--page <number>", "Page number", parseInt).addHelpText(
165
+ "after",
166
+ '\nExamples:\n jiradc field options 10001\n jiradc field options 10001 --query "High"\n jiradc field options 10001 --max 20 --page 2'
167
+ ).action(async (id, opts) => {
168
+ const client = getClient();
169
+ const result = await client.fields.getFieldOptions({
170
+ fieldId: id,
171
+ query: opts.query,
172
+ maxResults: opts.max,
173
+ page: opts.page
174
+ });
175
+ output(result);
176
+ });
177
+ }
178
+
179
+ // src/commands/field/search.ts
180
+ function search(parent) {
181
+ parent.command("search <keyword>").description("Search for fields by name or ID").option("--limit <number>", "Maximum number of results (default: 10)", parseInt).addHelpText(
182
+ "after",
183
+ "\nExamples:\n jiradc field search epic\n jiradc field search customfield_10100\n jiradc field search priority --limit 5"
184
+ ).action(async (keyword, opts) => {
185
+ const client = getClient();
186
+ const result = await client.fields.search(keyword, opts.limit);
187
+ output(result);
188
+ });
189
+ }
190
+
191
+ // src/commands/field/index.ts
192
+ function registerFieldCommands(program2) {
193
+ const field = program2.command("field").description("Field operations").addHelpText(
194
+ "after",
195
+ `
196
+ Examples:
197
+ $ jiradc field search "epic"
198
+ $ jiradc field search "priority"
199
+ $ jiradc field options 10120
200
+ `
201
+ );
202
+ search(field);
203
+ options(field);
204
+ }
205
+
206
+ // src/commands/issue/attachment/delete.ts
207
+ function deleteAttachment(parent) {
208
+ parent.command("delete").description("Delete an attachment by ID").requiredOption("--id <attachmentId>", "Attachment ID to delete").addHelpText("after", "\nExamples:\n jiradc issue attachment delete --id 12345").action(async (opts) => {
209
+ const client = getClient();
210
+ await client.issues.deleteAttachment({ attachmentId: opts.id });
211
+ output({ deleted: true, attachmentId: opts.id });
212
+ });
213
+ }
214
+
215
+ // src/commands/issue/attachment/download-all.ts
216
+ import { mkdirSync } from "fs";
217
+ import { join } from "path";
218
+ function downloadAll(parent) {
219
+ parent.command("download-all <key>").description("Download all attachments from an issue").requiredOption("--output <dir>", "Local directory to save attachments into").addHelpText("after", "\nExamples:\n jiradc issue attachment download-all PROJ-123 --output ./downloads").action(async (key, opts) => {
220
+ const client = getClient();
221
+ mkdirSync(opts.output, { recursive: true });
222
+ const issue = await client.issues.get({
223
+ issueKeyOrId: key,
224
+ fields: ["attachment"]
225
+ });
226
+ const atts = issue.fields.attachment ?? [];
227
+ if (atts.length === 0) {
228
+ output({ issueKey: key, downloaded: 0, files: [] });
229
+ return;
230
+ }
231
+ const results = [];
232
+ const failed = [];
233
+ for (const att of atts) {
234
+ if (!att.content) {
235
+ failed.push({ filename: att.filename, error: "No content URL" });
236
+ continue;
237
+ }
238
+ const destPath = join(opts.output, att.filename);
239
+ try {
240
+ await client.issues.downloadAttachment({ url: att.content, destinationPath: destPath });
241
+ results.push({ filename: att.filename, size: att.size, path: destPath });
242
+ } catch (err) {
243
+ failed.push({ filename: att.filename, error: String(err) });
244
+ }
245
+ }
246
+ output({
247
+ issueKey: key,
248
+ downloaded: results.length,
249
+ total: atts.length,
250
+ files: results,
251
+ ...failed.length > 0 && { failed }
252
+ });
253
+ });
254
+ }
255
+
256
+ // src/commands/issue/attachment/download.ts
257
+ function download(parent) {
258
+ parent.command("download <key>").description("Download a single attachment by ID").requiredOption("--id <attachmentId>", "Attachment ID").requiredOption("--output <path>", "Local file path to save the attachment").addHelpText("after", "\nExamples:\n jiradc issue attachment download PROJ-123 --id 12345 --output ./report.pdf").action(async (key, opts) => {
259
+ const client = getClient();
260
+ const attachment = await client.issues.getAttachment({ attachmentId: opts.id });
261
+ if (!attachment.content) {
262
+ throw new Error(`Attachment ${opts.id} has no content URL`);
263
+ }
264
+ await client.issues.downloadAttachment({
265
+ url: attachment.content,
266
+ destinationPath: opts.output
267
+ });
268
+ output({
269
+ downloaded: true,
270
+ issueKey: key,
271
+ path: opts.output,
272
+ filename: attachment.filename,
273
+ size: attachment.size
274
+ });
275
+ });
276
+ }
277
+
278
+ // src/commands/issue/attachment/list.ts
279
+ function list2(parent) {
280
+ parent.command("list <key>").description("List attachments on an issue").addHelpText("after", "\nExamples:\n jiradc issue attachment list PROJ-123").action(async (key) => {
281
+ const client = getClient();
282
+ const issue = await client.issues.get({
283
+ issueKeyOrId: key,
284
+ fields: ["attachment"]
285
+ });
286
+ const atts = issue.fields.attachment ?? [];
287
+ output({
288
+ issueKey: key,
289
+ total: atts.length,
290
+ attachments: atts.map((a) => ({
291
+ id: a.id,
292
+ filename: a.filename,
293
+ size: a.size,
294
+ mimeType: a.mimeType,
295
+ created: a.created
296
+ }))
297
+ });
298
+ });
299
+ }
300
+
301
+ // src/commands/issue/attachment/upload.ts
302
+ function upload(parent) {
303
+ parent.command("upload <key>").description("Upload attachments to an issue").requiredOption("--files <paths>", "Comma-separated file paths to upload").addHelpText(
304
+ "after",
305
+ "\nExamples:\n jiradc issue attachment upload PROJ-123 --files ./report.pdf\n jiradc issue attachment upload PROJ-123 --files ./a.txt,./b.png"
306
+ ).action(async (key, opts) => {
307
+ const client = getClient();
308
+ const filePaths = opts.files.split(",").map((f) => f.trim());
309
+ const results = [];
310
+ for (const filePath of filePaths) {
311
+ const attachments2 = await client.issues.addAttachment({ issueKeyOrId: key, filePath });
312
+ results.push(...attachments2);
313
+ }
314
+ output({
315
+ issueKey: key,
316
+ uploaded: results.length,
317
+ attachments: results.map((a) => ({
318
+ id: a.id,
319
+ filename: a.filename,
320
+ size: a.size,
321
+ mimeType: a.mimeType,
322
+ created: a.created
323
+ }))
324
+ });
325
+ });
326
+ }
327
+
328
+ // src/commands/issue/attachment/index.ts
329
+ function registerAttachmentCommands(parent) {
330
+ const attachment = parent.command("attachment").description("Attachment operations").addHelpText(
331
+ "after",
332
+ `
333
+ Examples:
334
+ $ jiradc issue attachment list PROJ-123
335
+ $ jiradc issue attachment upload PROJ-123 --files ./report.pdf
336
+ $ jiradc issue attachment download PROJ-123 --id 12345 --output ./report.pdf
337
+ $ jiradc issue attachment download-all PROJ-123 --output ./downloads
338
+ $ jiradc issue attachment delete --id 12345
339
+ `
340
+ );
341
+ upload(attachment);
342
+ list2(attachment);
343
+ download(attachment);
344
+ downloadAll(attachment);
345
+ deleteAttachment(attachment);
346
+ }
347
+
348
+ // src/commands/issue/attachments.ts
349
+ import { mkdirSync as mkdirSync2 } from "fs";
350
+ import { join as join2 } from "path";
351
+ function attachments(parent) {
352
+ parent.command("attachments <key>").description("Download all attachments from an issue").requiredOption("--output <dir>", "Local directory to save attachments into").addHelpText("after", "\nExamples:\n jiradc issue attachments PROJ-123 --output ./downloads").action(async (key, opts) => {
353
+ const client = getClient();
354
+ mkdirSync2(opts.output, { recursive: true });
355
+ const issue = await client.issues.get({
356
+ issueKeyOrId: key,
357
+ fields: ["attachment"]
358
+ });
359
+ const atts = issue.fields.attachment ?? [];
360
+ if (atts.length === 0) {
361
+ output({ issueKey: key, downloaded: 0, files: [] });
362
+ return;
363
+ }
364
+ const results = [];
365
+ const failed = [];
366
+ for (const att of atts) {
367
+ if (!att.content) {
368
+ failed.push({ filename: att.filename, error: "No content URL" });
369
+ continue;
370
+ }
371
+ const destPath = join2(opts.output, att.filename);
372
+ try {
373
+ await client.issues.downloadAttachment({ url: att.content, destinationPath: destPath });
374
+ results.push({ filename: att.filename, size: att.size, path: destPath });
375
+ } catch (err) {
376
+ failed.push({ filename: att.filename, error: String(err) });
377
+ }
378
+ }
379
+ output({
380
+ issueKey: key,
381
+ downloaded: results.length,
382
+ total: atts.length,
383
+ files: results,
384
+ ...failed.length > 0 && { failed }
385
+ });
386
+ });
387
+ }
388
+
389
+ // src/commands/issue/batch-changelog.ts
390
+ function batchChangelog(parent) {
391
+ parent.command("batch-changelog <keys>").description("Get changelogs for multiple issues at once").option("--max <number>", "Max changelog entries per issue (default: 50)", parseInt).addHelpText(
392
+ "after",
393
+ "\nExamples:\n jiradc issue batch-changelog PROJ-1,PROJ-2,PROJ-3\n jiradc issue batch-changelog PROJ-123,PROJ-124 --max 10"
394
+ ).action(async (keys, opts) => {
395
+ const client = getClient();
396
+ const keyList = keys.split(",").map((k) => k.trim());
397
+ const results = {};
398
+ for (const key of keyList) {
399
+ try {
400
+ results[key] = await client.issues.getChangelog({ issueKeyOrId: key, maxResults: opts.max });
401
+ } catch (error) {
402
+ results[key] = { error: error instanceof Error ? error.message : "Unknown error" };
403
+ }
404
+ }
405
+ output(results);
406
+ });
407
+ }
408
+
409
+ // src/commands/issue/batch-create.ts
410
+ function batchCreate(parent) {
411
+ parent.command("batch-create").description("Create multiple issues from a JSON array").requiredOption("--issues <json>", "JSON array of issue objects").addHelpText(
412
+ "after",
413
+ `
414
+ Examples:
415
+ jiradc issue batch-create --issues '[{"projectKeyOrId":"PROJ","issueTypeName":"Task","summary":"Task 1"},{"projectKeyOrId":"PROJ","issueTypeName":"Task","summary":"Task 2"}]'`
416
+ ).action(async (opts) => {
417
+ const client = getClient();
418
+ const parsed = JSON.parse(opts.issues);
419
+ const results = [];
420
+ for (const issue of parsed) {
421
+ try {
422
+ const result = await client.issues.create(issue);
423
+ results.push({ key: result.key });
424
+ } catch (error) {
425
+ results.push({ error: error instanceof Error ? error.message : "Unknown error" });
426
+ }
427
+ }
428
+ output(results);
429
+ });
430
+ }
431
+
432
+ // src/commands/issue/changelog.ts
433
+ function changelog(parent) {
434
+ parent.command("changelog <key>").description("Get changelog for an issue").option("--max <number>", "Max changelog entries (default: 50)", parseInt).addHelpText("after", "\nExamples:\n jiradc issue changelog PROJ-123\n jiradc issue changelog PROJ-123 --max 10").action(async (key, opts) => {
435
+ const client = getClient();
436
+ const result = await client.issues.getChangelog({ issueKeyOrId: key, maxResults: opts.max });
437
+ output(result);
438
+ });
439
+ }
440
+
441
+ // src/commands/issue/clone.ts
442
+ import { unlink } from "fs/promises";
443
+ import { tmpdir } from "os";
444
+ import { join as join3 } from "path";
445
+ var CLONE_FIELDS = [
446
+ "summary",
447
+ "description",
448
+ "issuetype",
449
+ "project",
450
+ "priority",
451
+ "labels",
452
+ "components",
453
+ "fixVersions",
454
+ "assignee",
455
+ "duedate",
456
+ "attachment",
457
+ "issuelinks"
458
+ ];
459
+ function clone(parent) {
460
+ parent.command("clone <key>").description("Clone an issue (create a duplicate with the same fields)").option("--summary <text>", 'Override the summary (default: "CLONE - <original>")').option("--project <key>", "Create in a different project").option("--assignee <username>", "Override assignee").option("--include-attachments", "Copy attachments to the cloned issue").option("--include-links", "Copy issue links to the cloned issue").addHelpText(
461
+ "after",
462
+ `
463
+ Examples:
464
+ jiradc issue clone PROJ-123
465
+ jiradc issue clone PROJ-123 --summary "Cloned: new title"
466
+ jiradc issue clone PROJ-123 --include-attachments --include-links`
467
+ ).action(
468
+ async (key, opts) => {
469
+ const client = getClient();
470
+ const source = await client.issues.get({ issueKeyOrId: key, fields: CLONE_FIELDS });
471
+ const f = source.fields;
472
+ const createParams = {
473
+ projectKeyOrId: opts.project ?? f.project?.key ?? "",
474
+ issueTypeName: f.issuetype?.name ?? "",
475
+ summary: opts.summary ?? `CLONE - ${f.summary ?? ""}`,
476
+ description: f.description ?? void 0,
477
+ priority: f.priority?.name,
478
+ labels: f.labels,
479
+ components: f.components?.map((c) => c.name),
480
+ fixVersions: f.fixVersions?.length ? f.fixVersions.map((v) => v.name) : void 0,
481
+ dueDate: f.duedate ?? void 0,
482
+ assignee: opts.assignee ?? f.assignee?.name
483
+ };
484
+ const result = await client.issues.create(createParams);
485
+ const newKey = result.key;
486
+ const cloneResult = {
487
+ cloned: true,
488
+ source: key,
489
+ newIssue: result
490
+ };
491
+ if (opts.includeAttachments && f.attachment?.length) {
492
+ const copied = [];
493
+ const tmpFiles = [];
494
+ for (const att of f.attachment) {
495
+ const tmpPath = join3(tmpdir(), `jiradc-clone-${Date.now()}-${att.filename}`);
496
+ tmpFiles.push(tmpPath);
497
+ await client.issues.downloadAttachment({ url: att.content, destinationPath: tmpPath });
498
+ await client.issues.addAttachment({ issueKeyOrId: newKey, filePath: tmpPath });
499
+ copied.push(att.filename);
500
+ }
501
+ await Promise.all(tmpFiles.map((p) => unlink(p).catch(() => void 0)));
502
+ cloneResult.attachmentsCopied = copied.length;
503
+ cloneResult.attachments = copied;
504
+ }
505
+ if (opts.includeLinks && f.issuelinks?.length) {
506
+ const linkedCount = { created: 0, skipped: 0 };
507
+ for (const link2 of f.issuelinks) {
508
+ try {
509
+ if (link2.outwardIssue) {
510
+ await client.links.create({
511
+ typeName: link2.type.name,
512
+ inwardIssueKey: newKey,
513
+ outwardIssueKey: link2.outwardIssue.key
514
+ });
515
+ } else if (link2.inwardIssue) {
516
+ await client.links.create({
517
+ typeName: link2.type.name,
518
+ inwardIssueKey: link2.inwardIssue.key,
519
+ outwardIssueKey: newKey
520
+ });
521
+ }
522
+ linkedCount.created++;
523
+ } catch {
524
+ linkedCount.skipped++;
525
+ }
526
+ }
527
+ cloneResult.linksCopied = linkedCount.created;
528
+ if (linkedCount.skipped > 0) cloneResult.linksSkipped = linkedCount.skipped;
529
+ }
530
+ output(cloneResult);
531
+ }
532
+ );
533
+ }
534
+
535
+ // src/commands/issue/comment-edit.ts
536
+ function commentEdit(parent) {
537
+ parent.command("comment-edit <key>").description("Edit an existing comment").requiredOption("--id <commentId>", "Comment ID to edit").requiredOption("--body <text>", "Updated comment body in wiki markup").addHelpText("after", '\nExamples:\n jiradc issue comment-edit PROJ-123 --id 12345 --body "Updated comment text"').action(async (key, opts) => {
538
+ const client = getClient();
539
+ const result = await client.issues.editComment({ issueKeyOrId: key, commentId: opts.id, body: opts.body });
540
+ output(result);
541
+ });
542
+ }
543
+
544
+ // src/commands/issue/comment.ts
545
+ function comment(parent) {
546
+ parent.command("comment <key>").description("Add a comment to an issue").requiredOption("--body <text>", "Comment body in wiki markup").addHelpText("after", '\nExamples:\n jiradc issue comment PROJ-123 --body "Fixed in latest build"').action(async (key, opts) => {
547
+ const client = getClient();
548
+ const result = await client.issues.addComment({ issueKeyOrId: key, body: opts.body });
549
+ output(result);
550
+ });
551
+ }
552
+
553
+ // src/commands/issue/create.ts
554
+ function create(parent) {
555
+ parent.command("create").description("Create a new issue").requiredOption("--project <key>", "Project key or ID").requiredOption("--type <name>", "Issue type name (e.g., Task, Bug, Story)").requiredOption("--summary <text>", "Issue summary/title").option("--description <text>", "Issue description in wiki markup").option("--assignee <username>", "Assignee username").option("--reporter <username>", "Reporter username").option("--priority <name>", "Priority name (e.g., High, Medium, Low)").option("--labels <labels>", "Comma-separated labels").option("--components <names>", "Comma-separated component names").option("--fix-versions <versions>", "Comma-separated fix version names").option("--due-date <date>", "Due date in YYYY-MM-DD format").option("--parent <key>", "Parent issue key (for subtasks)").option("--custom-fields <json>", `Additional custom fields as JSON (e.g., '{"customfield_10100": "EPIC-1"}')`).addHelpText(
556
+ "after",
557
+ `
558
+ Examples:
559
+ jiradc issue create --project PROJ --type Task --summary "Fix login bug"
560
+ jiradc issue create --project PROJ --type Story --summary "New feature" --assignee jsmith --priority High
561
+ jiradc issue create --project PROJ --type Task --summary "Epic child" --custom-fields '{"customfield_10100": "PROJ-123"}'
562
+ jiradc issue create --project PROJ --type Bug --summary "Urgent" --labels urgent,production --components Backend`
563
+ ).action(
564
+ async (opts) => {
565
+ const client = getClient();
566
+ const result = await client.issues.create({
567
+ projectKeyOrId: opts.project,
568
+ issueTypeName: opts.type,
569
+ summary: opts.summary,
570
+ description: opts.description,
571
+ assignee: opts.assignee,
572
+ reporter: opts.reporter,
573
+ priority: opts.priority,
574
+ labels: opts.labels?.split(",").map((l) => l.trim()),
575
+ components: opts.components?.split(",").map((c) => c.trim()),
576
+ fixVersions: opts.fixVersions?.split(",").map((v) => v.trim()),
577
+ dueDate: opts.dueDate,
578
+ parent: opts.parent,
579
+ customFields: opts.customFields ? JSON.parse(opts.customFields) : void 0
580
+ });
581
+ output(result);
582
+ }
583
+ );
584
+ }
585
+
586
+ // src/commands/issue/delete.ts
587
+ function deleteIssue(parent) {
588
+ parent.command("delete <key>").description("Delete an issue").option("--delete-subtasks", "Also delete subtasks (default: false)").addHelpText(
589
+ "after",
590
+ "\nExamples:\n jiradc issue delete PROJ-123\n jiradc issue delete PROJ-123 --delete-subtasks"
591
+ ).action(async (key, opts) => {
592
+ const client = getClient();
593
+ await client.issues.delete({ issueKeyOrId: key, deleteSubtasks: opts.deleteSubtasks });
594
+ output({ deleted: true, issueKey: key });
595
+ });
596
+ }
597
+
598
+ // src/commands/issue/dev-status.ts
599
+ function devStatus(parent) {
600
+ parent.command("dev-status <key>").description("Get development status (PRs, commits, branches, builds) for an issue").option("--detail", "Include PR URLs, commit IDs, and other details").addHelpText(
601
+ "after",
602
+ "\nExamples:\n jiradc issue dev-status PROJ-123\n jiradc issue dev-status PROJ-123 --detail"
603
+ ).action(async (key, opts) => {
604
+ const client = getClient();
605
+ const issue = await client.issues.get({ issueKeyOrId: key, fields: ["summary"] });
606
+ const issueId = issue.id;
607
+ const result = await client.devStatus.getSummary(issueId);
608
+ const s = result.summary;
609
+ const source = (byInstance) => {
610
+ const names = Object.values(byInstance).map((v) => v.name);
611
+ return names.length > 0 ? names.join(", ") : void 0;
612
+ };
613
+ const devInfo = {
614
+ pullRequests: {
615
+ total: s.pullrequest.overall.count,
616
+ open: s.pullrequest.overall.details.openCount,
617
+ merged: s.pullrequest.overall.details.mergedCount,
618
+ declined: s.pullrequest.overall.details.declinedCount,
619
+ ...source(s.pullrequest.byInstanceType) && { source: source(s.pullrequest.byInstanceType) }
620
+ },
621
+ commits: {
622
+ total: s.repository.overall.count,
623
+ ...s.repository.overall.lastUpdated && { lastUpdated: s.repository.overall.lastUpdated },
624
+ ...source(s.repository.byInstanceType) && { source: source(s.repository.byInstanceType) }
625
+ },
626
+ branches: { total: s.branch.overall.count },
627
+ builds: {
628
+ total: s.build.overall.count,
629
+ ...s.build.overall.count > 0 && {
630
+ successful: s.build.overall.successfulBuildCount,
631
+ failed: s.build.overall.failedBuildCount,
632
+ unknown: s.build.overall.unknownBuildCount
633
+ }
634
+ },
635
+ reviews: { total: s.review.overall.count },
636
+ deployments: { total: s["deployment-environment"].overall.count }
637
+ };
638
+ if (opts.detail) {
639
+ const [prDetail, repoDetail] = await Promise.all([
640
+ s.pullrequest.overall.count > 0 ? client.devStatus.getDetail(issueId, "pullrequest") : null,
641
+ s.repository.overall.count > 0 ? client.devStatus.getDetail(issueId, "repository") : null
642
+ ]);
643
+ if (prDetail) {
644
+ const prs = prDetail.detail.flatMap((d) => d.pullRequests);
645
+ devInfo.pullRequests.items = prs.map((pr) => ({
646
+ id: pr.id,
647
+ title: pr.name,
648
+ status: pr.status,
649
+ url: pr.url,
650
+ author: pr.author.name,
651
+ source: pr.source.branch,
652
+ destination: pr.destination.branch,
653
+ lastUpdate: pr.lastUpdate
654
+ }));
655
+ }
656
+ if (repoDetail) {
657
+ const commits = repoDetail.detail.flatMap(
658
+ (d) => d.repositories.flatMap(
659
+ (r) => r.commits.map((c) => ({
660
+ id: c.displayId,
661
+ message: c.message.split("\n")[0],
662
+ author: c.author.name,
663
+ date: c.authorTimestamp,
664
+ url: c.url,
665
+ fileCount: c.fileCount,
666
+ merge: c.merge
667
+ }))
668
+ )
669
+ );
670
+ devInfo.commits.items = commits;
671
+ }
672
+ }
673
+ output(devInfo);
674
+ });
675
+ }
676
+
677
+ // src/commands/issue/get-worklog.ts
678
+ function getWorklog(parent) {
679
+ parent.command("get-worklog <key>").description("Get worklogs for an issue").option("--start-at <number>", "Starting index for pagination (default: 0)", parseInt).option("--max <number>", "Max results per page", parseInt).addHelpText(
680
+ "after",
681
+ "\nExamples:\n jiradc issue get-worklog PROJ-123\n jiradc issue get-worklog PROJ-123 --max 10\n jiradc issue get-worklog PROJ-123 --start-at 10 --max 5"
682
+ ).action(async (key, opts) => {
683
+ const client = getClient();
684
+ const result = await client.issues.getWorklogs({
685
+ issueKeyOrId: key,
686
+ startAt: opts.startAt,
687
+ maxResults: opts.max
688
+ });
689
+ output(result);
690
+ });
691
+ }
692
+
693
+ // src/utils/constants.ts
694
+ var DEFAULT_FIELDS = [
695
+ "summary",
696
+ "description",
697
+ "status",
698
+ "assignee",
699
+ "reporter",
700
+ "labels",
701
+ "priority",
702
+ "created",
703
+ "updated",
704
+ "issuetype",
705
+ "components",
706
+ "comment"
707
+ ];
708
+
709
+ // src/commands/issue/get.ts
710
+ function get(parent) {
711
+ parent.command("get <key>").description("Get issue details").option("--fields <fields>", "Comma-separated fields to return (defaults to essential fields)").option("--all-fields", "Return all fields instead of defaults").option("--expand <expand>", 'Expand options (e.g., "transitions", "changelog")').addHelpText(
712
+ "after",
713
+ "\nExamples:\n jiradc issue get PROJ-123\n jiradc issue get PROJ-123 --fields summary,status,assignee\n jiradc issue get PROJ-123 --all-fields\n jiradc issue get PROJ-123 --expand changelog,transitions"
714
+ ).action(async (key, opts) => {
715
+ const client = getClient();
716
+ const fields = opts.allFields ? void 0 : opts.fields?.split(",").map((f) => f.trim()) ?? DEFAULT_FIELDS;
717
+ const result = await client.issues.get({
718
+ issueKeyOrId: key,
719
+ fields,
720
+ expand: opts.expand
721
+ });
722
+ output(result);
723
+ });
724
+ }
725
+
726
+ // src/commands/issue/link-epic.ts
727
+ function linkEpic(parent) {
728
+ parent.command("link-epic <key>").description("Link an issue to an epic").requiredOption("--epic <epicKey>", "Epic issue key").addHelpText("after", "\nExamples:\n jiradc issue link-epic PROJ-456 --epic PROJ-123").action(async (key, opts) => {
729
+ const client = getClient();
730
+ await client.issues.update({
731
+ issueKeyOrId: key,
732
+ fields: { customfield_10100: opts.epic }
733
+ });
734
+ output({ linked: true, issue: key, epic: opts.epic });
735
+ });
736
+ }
737
+
738
+ // src/commands/issue/link-types.ts
739
+ function linkTypes(parent) {
740
+ parent.command("link-types").description("List all issue link types").addHelpText("after", "\nExamples:\n jiradc issue link-types").action(async () => {
741
+ const client = getClient();
742
+ const result = await client.links.getTypes();
743
+ output(result);
744
+ });
745
+ }
746
+
747
+ // src/commands/issue/link.ts
748
+ function link(parent) {
749
+ parent.command("link").description("Link two issues together").requiredOption("--type <name>", "Link type name (e.g., 'Blocks', 'Duplicate', 'Relates')").requiredOption("--from <key>", 'Source issue key (e.g., the issue that "blocks")').requiredOption("--to <key>", 'Target issue key (e.g., the issue that "is blocked by")').option("--comment <text>", "Optional comment").addHelpText(
750
+ "after",
751
+ "\nExamples:\n jiradc issue link --type Relates --from AI-154 --to AI-149\n jiradc issue link --type Blocks --from PROJ-456 --to PROJ-123 # PROJ-456 blocks PROJ-123"
752
+ ).action(async (opts) => {
753
+ const client = getClient();
754
+ await client.links.create({
755
+ typeName: opts.type,
756
+ inwardIssueKey: opts.from,
757
+ outwardIssueKey: opts.to,
758
+ comment: opts.comment
759
+ });
760
+ output({ created: true, type: opts.type, inward: opts.from, outward: opts.to });
761
+ });
762
+ }
763
+
764
+ // src/commands/issue/search.ts
765
+ function search2(parent) {
766
+ parent.command("search <jql>").description("Search issues using JQL").option("--max <number>", "Max results (default: 50)", parseInt).option("--start-at <number>", "Starting index for pagination (default: 0)", parseInt).option("--fields <fields>", "Comma-separated fields to return (defaults to essential fields)").option("--all-fields", "Return all fields instead of defaults").addHelpText(
767
+ "after",
768
+ '\nExamples:\n jiradc issue search "project = PROJ AND status = Open"\n jiradc issue search "assignee = currentUser()" --max 10 --fields summary,status\n jiradc issue search "project = PROJ" --start-at 50 --max 50'
769
+ ).action(async (jql, opts) => {
770
+ const client = getClient();
771
+ const fields = opts.allFields ? void 0 : opts.fields?.split(",").map((f) => f.trim()) ?? DEFAULT_FIELDS;
772
+ const result = await client.issues.search({
773
+ jql,
774
+ startAt: opts.startAt,
775
+ maxResults: opts.max,
776
+ fields
777
+ });
778
+ output(result);
779
+ });
780
+ }
781
+
782
+ // src/commands/issue/transition.ts
783
+ function transition(parent) {
784
+ parent.command("transition <key>").description("Transition issue to a new status").requiredOption("--to <id>", 'Transition ID (get from "jiradc issue transitions")').option("--comment <text>", "Comment to add during transition").addHelpText(
785
+ "after",
786
+ '\nExamples:\n jiradc issue transition PROJ-123 --to 31\n jiradc issue transition PROJ-123 --to 31 --comment "Moving to review"'
787
+ ).action(async (key, opts) => {
788
+ const client = getClient();
789
+ await client.issues.transition({ issueKeyOrId: key, transitionId: opts.to, comment: opts.comment });
790
+ output({ transitioned: true, issueKey: key });
791
+ });
792
+ }
793
+
794
+ // src/commands/issue/transitions.ts
795
+ function transitions(parent) {
796
+ parent.command("transitions <key>").description("Get available transitions for an issue").addHelpText("after", "\nExamples:\n jiradc issue transitions PROJ-123").action(async (key) => {
797
+ const client = getClient();
798
+ const result = await client.issues.getTransitions({ issueKeyOrId: key });
799
+ output(result);
800
+ });
801
+ }
802
+
803
+ // src/commands/issue/unlink.ts
804
+ function unlink2(parent) {
805
+ parent.command("unlink <id>").description("Remove a link between two issues").addHelpText("after", "\nExamples:\n jiradc issue unlink 12345").action(async (id) => {
806
+ const client = getClient();
807
+ await client.links.remove({ linkId: id });
808
+ output({ removed: true, linkId: id });
809
+ });
810
+ }
811
+
812
+ // src/commands/issue/update.ts
813
+ function update(parent) {
814
+ parent.command("update <key>").description("Update issue fields").option("--fields <json>", "JSON string of fields to update").option("--notify-users", "Notify users about the update (default: true)").option("--attachments <paths>", "Comma-separated local file paths to attach").addHelpText(
815
+ "after",
816
+ `
817
+ Examples:
818
+ jiradc issue update PROJ-123 --fields '{"summary": "New title"}'
819
+ jiradc issue update PROJ-123 --fields '{"assignee": {"name": "jsmith"}, "priority": {"name": "High"}}'
820
+ jiradc issue update PROJ-123 --attachments /path/to/file.pdf,/path/to/image.png
821
+ jiradc issue update PROJ-123 --fields '{"summary": "With attachment"}' --attachments ./report.pdf`
822
+ ).action(async (key, opts) => {
823
+ if (!opts.fields && !opts.attachments) {
824
+ console.error(JSON.stringify({ error: "Provide --fields and/or --attachments" }));
825
+ process.exit(1);
826
+ }
827
+ const client = getClient();
828
+ if (opts.fields) {
829
+ const parsed = JSON.parse(opts.fields);
830
+ await client.issues.update({ issueKeyOrId: key, fields: parsed, notifyUsers: opts.notifyUsers });
831
+ }
832
+ const uploaded = [];
833
+ if (opts.attachments) {
834
+ const filePaths = opts.attachments.split(",").map((p) => p.trim()).filter(Boolean);
835
+ for (const filePath of filePaths) {
836
+ const result = await client.issues.addAttachment({ issueKeyOrId: key, filePath });
837
+ for (const att of result) {
838
+ uploaded.push({ filename: att.filename, id: att.id });
839
+ }
840
+ }
841
+ }
842
+ output({ updated: true, issueKey: key, ...uploaded.length > 0 && { attachments: uploaded } });
843
+ });
844
+ }
845
+
846
+ // src/commands/issue/worklog.ts
847
+ function worklog(parent) {
848
+ parent.command("worklog <key>").description("Log time spent on an issue").requiredOption("--time <timeSpent>", "Time spent (e.g., '2h', '30m', '1d 4h')").option("--comment <text>", "Worklog comment").option("--started <datetime>", "Start time in ISO 8601 format").addHelpText(
849
+ "after",
850
+ '\nExamples:\n jiradc issue worklog PROJ-123 --time 2h\n jiradc issue worklog PROJ-123 --time "1d 4h" --comment "Backend implementation"\n jiradc issue worklog PROJ-123 --time 3h --started "2026-03-19T09:00:00.000+0000"'
851
+ ).action(async (key, opts) => {
852
+ const client = getClient();
853
+ const result = await client.issues.addWorklog({
854
+ issueKeyOrId: key,
855
+ timeSpent: opts.time,
856
+ comment: opts.comment,
857
+ started: opts.started
858
+ });
859
+ output(result);
860
+ });
861
+ }
862
+
863
+ // src/commands/issue/index.ts
864
+ function registerIssueCommands(program2) {
865
+ const issue = program2.command("issue").description("Issue operations").addHelpText(
866
+ "after",
867
+ `
868
+ Examples:
869
+ $ jiradc issue get PROJ-123
870
+ $ jiradc issue search "project = PROJ AND status = Open" --max 10
871
+ $ jiradc issue create --project PROJ --type Task --summary "Fix the bug" --priority High
872
+ $ jiradc issue update PROJ-123 --fields '{"summary": "New title"}'
873
+ $ jiradc issue transition PROJ-123 --to 11
874
+ $ jiradc issue attachment list PROJ-123
875
+ `
876
+ );
877
+ get(issue);
878
+ search2(issue);
879
+ create(issue);
880
+ update(issue);
881
+ deleteIssue(issue);
882
+ transition(issue);
883
+ transitions(issue);
884
+ comment(issue);
885
+ commentEdit(issue);
886
+ worklog(issue);
887
+ getWorklog(issue);
888
+ changelog(issue);
889
+ batchChangelog(issue);
890
+ link(issue);
891
+ unlink2(issue);
892
+ linkTypes(issue);
893
+ linkEpic(issue);
894
+ registerAttachmentCommands(issue);
895
+ attachments(issue);
896
+ batchCreate(issue);
897
+ clone(issue);
898
+ devStatus(issue);
899
+ }
900
+
901
+ // src/commands/project/components.ts
902
+ function components(parent) {
903
+ parent.command("components <key>").description("Get all components for a project").addHelpText("after", "\nExamples:\n jiradc project components PROJ").action(async (key) => {
904
+ const client = getClient();
905
+ const result = await client.projects.getComponents({ projectKeyOrId: key });
906
+ output(result);
907
+ });
908
+ }
909
+
910
+ // src/commands/project/list.ts
911
+ function list3(parent) {
912
+ parent.command("list").description("List all projects").option("--expand <expand>", 'Expand options (e.g., "description,lead")').option("--include-archived", "Include archived projects (default: false)").addHelpText(
913
+ "after",
914
+ "\nExamples:\n jiradc project list\n jiradc project list --expand description,lead\n jiradc project list --include-archived"
915
+ ).action(async (opts) => {
916
+ const client = getClient();
917
+ const result = await client.projects.getAll({ expand: opts.expand, includeArchived: opts.includeArchived });
918
+ output(result);
919
+ });
920
+ }
921
+
922
+ // src/commands/project/versions.ts
923
+ function versions(parent) {
924
+ parent.command("versions <key>").description("Get all versions for a project").option("--expand <expand>", "Expand options").addHelpText(
925
+ "after",
926
+ "\nExamples:\n jiradc project versions PROJ\n jiradc project versions PROJ --expand operations"
927
+ ).action(async (key, opts) => {
928
+ const client = getClient();
929
+ const result = await client.projects.getVersions({ projectKeyOrId: key, expand: opts.expand });
930
+ output(result);
931
+ });
932
+ }
933
+
934
+ // src/commands/project/index.ts
935
+ function registerProjectCommands(program2) {
936
+ const project = program2.command("project").description("Project operations").addHelpText(
937
+ "after",
938
+ `
939
+ Examples:
940
+ $ jiradc project list
941
+ $ jiradc project versions PROJ
942
+ $ jiradc project components PROJ
943
+ `
944
+ );
945
+ list3(project);
946
+ versions(project);
947
+ components(project);
948
+ }
949
+
950
+ // src/commands/sprint/create.ts
951
+ function create2(parent) {
952
+ parent.command("create").description("Create a new sprint").requiredOption("--board <id>", "Board ID to create sprint in").requiredOption("--name <name>", "Sprint name").option("--start-date <date>", "Start date in ISO 8601 format").option("--end-date <date>", "End date in ISO 8601 format").option("--goal <goal>", "Sprint goal").addHelpText(
953
+ "after",
954
+ '\nExamples:\n jiradc sprint create --board 42 --name "Sprint 10"\n jiradc sprint create --board 42 --name "Sprint 10" --start-date 2026-03-20 --end-date 2026-04-03 --goal "Complete auth module"'
955
+ ).action(async (opts) => {
956
+ const client = getClient();
957
+ const result = await client.agile.createSprint({
958
+ name: opts.name,
959
+ originBoardId: parseInt(opts.board, 10),
960
+ startDate: opts.startDate,
961
+ endDate: opts.endDate,
962
+ goal: opts.goal
963
+ });
964
+ output(result);
965
+ });
966
+ }
967
+
968
+ // src/commands/sprint/issues.ts
969
+ function issues2(parent) {
970
+ parent.command("issues <id>").description("Get issues in a sprint").option("--max <number>", "Max results", parseInt).option("--start-at <number>", "Starting index for pagination (default: 0)", parseInt).option("--fields <fields>", "Comma-separated field names to return").option("--jql <jql>", "Additional JQL filter within the sprint").addHelpText(
971
+ "after",
972
+ '\nExamples:\n jiradc sprint issues 100\n jiradc sprint issues 100 --max 20\n jiradc sprint issues 100 --jql "status = Done" --fields summary,status\n jiradc sprint issues 100 --start-at 50 --max 25'
973
+ ).action(async (id, opts) => {
974
+ const client = getClient();
975
+ const result = await client.agile.getSprintIssues({
976
+ sprintId: parseInt(id, 10),
977
+ startAt: opts.startAt,
978
+ maxResults: opts.max,
979
+ fields: opts.fields?.split(",").map((f) => f.trim()),
980
+ jql: opts.jql
981
+ });
982
+ output(result);
983
+ });
984
+ }
985
+
986
+ // src/commands/sprint/list.ts
987
+ function list4(parent) {
988
+ parent.command("list").description("List sprints for a board").requiredOption("--board <id>", "Board ID").option("--state <state>", "Filter by sprint state (future, active, closed)").addHelpText(
989
+ "after",
990
+ "\nExamples:\n jiradc sprint list --board 42\n jiradc sprint list --board 42 --state active"
991
+ ).action(async (opts) => {
992
+ const client = getClient();
993
+ const result = await client.agile.getSprints({
994
+ boardId: parseInt(opts.board, 10),
995
+ state: opts.state
996
+ });
997
+ output(result);
998
+ });
999
+ }
1000
+
1001
+ // src/commands/sprint/update.ts
1002
+ function update2(parent) {
1003
+ parent.command("update <id>").description("Update an existing sprint").option("--name <name>", "New sprint name").option("--state <state>", "New sprint state (future, active, closed)").option("--start-date <date>", "New start date in ISO 8601 format").option("--end-date <date>", "New end date in ISO 8601 format").option("--goal <goal>", "New sprint goal").addHelpText(
1004
+ "after",
1005
+ '\nExamples:\n jiradc sprint update 100 --name "Sprint 10 - Extended"\n jiradc sprint update 100 --state active\n jiradc sprint update 100 --end-date 2026-04-10 --goal "Updated goal"'
1006
+ ).action(
1007
+ async (id, opts) => {
1008
+ const client = getClient();
1009
+ const result = await client.agile.updateSprint({
1010
+ sprintId: parseInt(id, 10),
1011
+ name: opts.name,
1012
+ state: opts.state,
1013
+ startDate: opts.startDate,
1014
+ endDate: opts.endDate,
1015
+ goal: opts.goal
1016
+ });
1017
+ output(result);
1018
+ }
1019
+ );
1020
+ }
1021
+
1022
+ // src/commands/sprint/index.ts
1023
+ function registerSprintCommands(program2) {
1024
+ const sprint = program2.command("sprint").description("Sprint operations").addHelpText(
1025
+ "after",
1026
+ `
1027
+ Examples:
1028
+ $ jiradc sprint list --board 42
1029
+ $ jiradc sprint list --board 42 --state active
1030
+ $ jiradc sprint issues 123 --max 20
1031
+ $ jiradc sprint create --board 42 --name "Sprint 10" --start-date 2026-04-01 --end-date 2026-04-14
1032
+ `
1033
+ );
1034
+ list4(sprint);
1035
+ issues2(sprint);
1036
+ create2(sprint);
1037
+ update2(sprint);
1038
+ }
1039
+
1040
+ // src/commands/user/me.ts
1041
+ function me(parent) {
1042
+ parent.command("me [username]").description("Get a user profile. Omit username to get the authenticated user.").addHelpText("after", "\nExamples:\n jiradc user me\n jiradc user me jsmith").action(async (username) => {
1043
+ const client = getClient();
1044
+ const result = username ? await client.users.getUser({ username }) : await client.users.getMyself();
1045
+ output(result);
1046
+ });
1047
+ }
1048
+
1049
+ // src/commands/user/index.ts
1050
+ function registerUserCommands(program2) {
1051
+ const user = program2.command("user").description("User operations").addHelpText(
1052
+ "after",
1053
+ `
1054
+ Examples:
1055
+ $ jiradc user me
1056
+ $ jiradc user me jsmith
1057
+ `
1058
+ );
1059
+ me(user);
1060
+ }
1061
+
1062
+ // src/index.ts
1063
+ var DIM = "\x1B[2m";
1064
+ var RESET = "\x1B[0m";
1065
+ var program = new Command();
1066
+ program.name("jiradc").description("Jira Data Center CLI").version("1.0.0").configureHelp({
1067
+ styleTitle: (str) => styleText("bold", str),
1068
+ styleUsage: (str) => styleText("dim", str),
1069
+ styleCommandDescription: (str) => styleText("dim", str),
1070
+ styleOptionDescription: (str) => styleText("dim", str),
1071
+ styleSubcommandDescription: (str) => styleText("dim", str)
1072
+ }).addHelpText("beforeAll", `
1073
+ ${styleText("bold", "jiradc")} ${DIM}\u2014 Jira Data Center CLI${RESET}
1074
+ `).addHelpText(
1075
+ "after",
1076
+ `
1077
+ ${styleText("bold", "Environment:")}
28
1078
  JIRA_URL Jira Server base URL ${DIM}(e.g., https://jira.example.com)${RESET}
29
1079
  JIRA_TOKEN Personal Access Token ${DIM}(generate in Jira > Profile > Personal Access Tokens)${RESET}
30
1080
 
31
- ${styleText('bold', 'Examples:')}
1081
+ ${styleText("bold", "Examples:")}
32
1082
  ${DIM}$${RESET} jiradc user me
33
1083
  ${DIM}$${RESET} jiradc issue search "project = PROJ AND status = Open" --max 10
34
1084
  ${DIM}$${RESET} jiradc issue get PROJ-123 --fields summary,status
@@ -36,7 +1086,8 @@ ${styleText('bold', 'Examples:')}
36
1086
  ${DIM}$${RESET} jiradc issue transition PROJ-123 --to 11
37
1087
  ${DIM}$${RESET} jiradc board list --type scrum
38
1088
  ${DIM}$${RESET} jiradc sprint list --board 42 --state active
39
- `);
1089
+ `
1090
+ );
40
1091
  registerIssueCommands(program);
41
1092
  registerProjectCommands(program);
42
1093
  registerBoardCommands(program);
@@ -44,9 +1095,7 @@ registerSprintCommands(program);
44
1095
  registerFieldCommands(program);
45
1096
  registerUserCommands(program);
46
1097
  try {
47
- await program.parseAsync();
48
- }
49
- catch (err) {
50
- handleError(err);
1098
+ await program.parseAsync();
1099
+ } catch (err) {
1100
+ handleError(err);
51
1101
  }
52
- //# sourceMappingURL=index.js.map