jirallm 0.1.1 → 0.3.0
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 +75 -0
- package/dist/cli/commands/assign.d.ts +9 -0
- package/dist/cli/commands/assign.d.ts.map +1 -0
- package/dist/cli/commands/assign.js +37 -0
- package/dist/cli/commands/assign.js.map +1 -0
- package/dist/cli/commands/assign.test.d.ts +2 -0
- package/dist/cli/commands/assign.test.d.ts.map +1 -0
- package/dist/cli/commands/assign.test.js +69 -0
- package/dist/cli/commands/assign.test.js.map +1 -0
- package/dist/cli/commands/attach.d.ts +16 -0
- package/dist/cli/commands/attach.d.ts.map +1 -0
- package/dist/cli/commands/attach.js +52 -0
- package/dist/cli/commands/attach.js.map +1 -0
- package/dist/cli/commands/attach.test.d.ts +2 -0
- package/dist/cli/commands/attach.test.d.ts.map +1 -0
- package/dist/cli/commands/attach.test.js +92 -0
- package/dist/cli/commands/attach.test.js.map +1 -0
- package/dist/cli/commands/board.d.ts +10 -0
- package/dist/cli/commands/board.d.ts.map +1 -0
- package/dist/cli/commands/board.js +54 -0
- package/dist/cli/commands/board.js.map +1 -0
- package/dist/cli/commands/boards.d.ts +11 -0
- package/dist/cli/commands/boards.d.ts.map +1 -0
- package/dist/cli/commands/boards.js +27 -0
- package/dist/cli/commands/boards.js.map +1 -0
- package/dist/cli/commands/boards.test.d.ts +2 -0
- package/dist/cli/commands/boards.test.d.ts.map +1 -0
- package/dist/cli/commands/boards.test.js +80 -0
- package/dist/cli/commands/boards.test.js.map +1 -0
- package/dist/cli/commands/comment.d.ts +15 -0
- package/dist/cli/commands/comment.d.ts.map +1 -0
- package/dist/cli/commands/comment.js +61 -0
- package/dist/cli/commands/comment.js.map +1 -0
- package/dist/cli/commands/components.d.ts +7 -0
- package/dist/cli/commands/components.d.ts.map +1 -0
- package/dist/cli/commands/components.js +23 -0
- package/dist/cli/commands/components.js.map +1 -0
- package/dist/cli/commands/components.test.d.ts +2 -0
- package/dist/cli/commands/components.test.d.ts.map +1 -0
- package/dist/cli/commands/components.test.js +62 -0
- package/dist/cli/commands/components.test.js.map +1 -0
- package/dist/cli/commands/create.d.ts +18 -0
- package/dist/cli/commands/create.d.ts.map +1 -0
- package/dist/cli/commands/create.js +49 -0
- package/dist/cli/commands/create.js.map +1 -0
- package/dist/cli/commands/create.test.d.ts +2 -0
- package/dist/cli/commands/create.test.d.ts.map +1 -0
- package/dist/cli/commands/create.test.js +133 -0
- package/dist/cli/commands/create.test.js.map +1 -0
- package/dist/cli/commands/edit.d.ts +17 -0
- package/dist/cli/commands/edit.d.ts.map +1 -0
- package/dist/cli/commands/edit.js +52 -0
- package/dist/cli/commands/edit.js.map +1 -0
- package/dist/cli/commands/edit.test.d.ts +2 -0
- package/dist/cli/commands/edit.test.d.ts.map +1 -0
- package/dist/cli/commands/edit.test.js +96 -0
- package/dist/cli/commands/edit.test.js.map +1 -0
- package/dist/cli/commands/fetch.d.ts +7 -0
- package/dist/cli/commands/fetch.d.ts.map +1 -0
- package/dist/cli/commands/fetch.js +32 -0
- package/dist/cli/commands/fetch.js.map +1 -0
- package/dist/cli/commands/fetch.test.d.ts +2 -0
- package/dist/cli/commands/fetch.test.d.ts.map +1 -0
- package/dist/cli/commands/fetch.test.js +93 -0
- package/dist/cli/commands/fetch.test.js.map +1 -0
- package/dist/cli/commands/fields.d.ts +8 -0
- package/dist/cli/commands/fields.d.ts.map +1 -0
- package/dist/cli/commands/fields.js +47 -0
- package/dist/cli/commands/fields.js.map +1 -0
- package/dist/cli/commands/fields.test.d.ts +2 -0
- package/dist/cli/commands/fields.test.d.ts.map +1 -0
- package/dist/cli/commands/fields.test.js +78 -0
- package/dist/cli/commands/fields.test.js.map +1 -0
- package/dist/cli/commands/issuetypes.d.ts +7 -0
- package/dist/cli/commands/issuetypes.d.ts.map +1 -0
- package/dist/cli/commands/issuetypes.js +23 -0
- package/dist/cli/commands/issuetypes.js.map +1 -0
- package/dist/cli/commands/issuetypes.test.d.ts +2 -0
- package/dist/cli/commands/issuetypes.test.d.ts.map +1 -0
- package/dist/cli/commands/issuetypes.test.js +62 -0
- package/dist/cli/commands/issuetypes.test.js.map +1 -0
- package/dist/cli/commands/link.d.ts +18 -0
- package/dist/cli/commands/link.d.ts.map +1 -0
- package/dist/cli/commands/link.js +51 -0
- package/dist/cli/commands/link.js.map +1 -0
- package/dist/cli/commands/link.test.d.ts +2 -0
- package/dist/cli/commands/link.test.d.ts.map +1 -0
- package/dist/cli/commands/link.test.js +96 -0
- package/dist/cli/commands/link.test.js.map +1 -0
- package/dist/cli/commands/linktypes.d.ts +6 -0
- package/dist/cli/commands/linktypes.d.ts.map +1 -0
- package/dist/cli/commands/linktypes.js +21 -0
- package/dist/cli/commands/linktypes.js.map +1 -0
- package/dist/cli/commands/linktypes.test.d.ts +2 -0
- package/dist/cli/commands/linktypes.test.d.ts.map +1 -0
- package/dist/cli/commands/linktypes.test.js +55 -0
- package/dist/cli/commands/linktypes.test.js.map +1 -0
- package/dist/cli/commands/me.d.ts +6 -0
- package/dist/cli/commands/me.d.ts.map +1 -0
- package/dist/cli/commands/me.js +17 -0
- package/dist/cli/commands/me.js.map +1 -0
- package/dist/cli/commands/me.test.d.ts +2 -0
- package/dist/cli/commands/me.test.d.ts.map +1 -0
- package/dist/cli/commands/me.test.js +58 -0
- package/dist/cli/commands/me.test.js.map +1 -0
- package/dist/cli/commands/projects.d.ts +9 -0
- package/dist/cli/commands/projects.d.ts.map +1 -0
- package/dist/cli/commands/projects.js +25 -0
- package/dist/cli/commands/projects.js.map +1 -0
- package/dist/cli/commands/projects.test.d.ts +2 -0
- package/dist/cli/commands/projects.test.d.ts.map +1 -0
- package/dist/cli/commands/projects.test.js +73 -0
- package/dist/cli/commands/projects.test.js.map +1 -0
- package/dist/cli/commands/search.d.ts +11 -0
- package/dist/cli/commands/search.d.ts.map +1 -0
- package/dist/cli/commands/search.js +41 -0
- package/dist/cli/commands/search.js.map +1 -0
- package/dist/cli/commands/search.test.d.ts +2 -0
- package/dist/cli/commands/search.test.d.ts.map +1 -0
- package/dist/cli/commands/search.test.js +106 -0
- package/dist/cli/commands/search.test.js.map +1 -0
- package/dist/cli/commands/setup.d.ts.map +1 -1
- package/dist/cli/commands/setup.js +5 -13
- package/dist/cli/commands/setup.js.map +1 -1
- package/dist/cli/commands/sprints.d.ts +10 -0
- package/dist/cli/commands/sprints.d.ts.map +1 -0
- package/dist/cli/commands/sprints.js +28 -0
- package/dist/cli/commands/sprints.js.map +1 -0
- package/dist/cli/commands/sprints.test.d.ts +2 -0
- package/dist/cli/commands/sprints.test.d.ts.map +1 -0
- package/dist/cli/commands/sprints.test.js +68 -0
- package/dist/cli/commands/sprints.test.js.map +1 -0
- package/dist/cli/commands/transition.d.ts +8 -0
- package/dist/cli/commands/transition.d.ts.map +1 -0
- package/dist/cli/commands/transition.js +46 -0
- package/dist/cli/commands/transition.js.map +1 -0
- package/dist/cli/commands/upgrade.d.ts +16 -0
- package/dist/cli/commands/upgrade.d.ts.map +1 -0
- package/dist/cli/commands/upgrade.js +83 -0
- package/dist/cli/commands/upgrade.js.map +1 -0
- package/dist/cli/commands/upgrade.test.d.ts +2 -0
- package/dist/cli/commands/upgrade.test.d.ts.map +1 -0
- package/dist/cli/commands/upgrade.test.js +146 -0
- package/dist/cli/commands/upgrade.test.js.map +1 -0
- package/dist/cli/commands/watchers.d.ts +10 -0
- package/dist/cli/commands/watchers.d.ts.map +1 -0
- package/dist/cli/commands/watchers.js +43 -0
- package/dist/cli/commands/watchers.js.map +1 -0
- package/dist/cli/commands/watchers.test.d.ts +2 -0
- package/dist/cli/commands/watchers.test.d.ts.map +1 -0
- package/dist/cli/commands/watchers.test.js +90 -0
- package/dist/cli/commands/watchers.test.js.map +1 -0
- package/dist/cli/commands/worklog.d.ts +8 -0
- package/dist/cli/commands/worklog.d.ts.map +1 -0
- package/dist/cli/commands/worklog.js +100 -0
- package/dist/cli/commands/worklog.js.map +1 -0
- package/dist/cli/commands/worklog.test.d.ts +2 -0
- package/dist/cli/commands/worklog.test.d.ts.map +1 -0
- package/dist/cli/commands/worklog.test.js +195 -0
- package/dist/cli/commands/worklog.test.js.map +1 -0
- package/dist/cli/index.js +712 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/issueKey.d.ts.map +1 -1
- package/dist/cli/issueKey.js +6 -2
- package/dist/cli/issueKey.js.map +1 -1
- package/dist/cli/issueKey.test.js +35 -0
- package/dist/cli/issueKey.test.js.map +1 -1
- package/dist/cli/jsonOutput.d.ts +6 -0
- package/dist/cli/jsonOutput.d.ts.map +1 -0
- package/dist/cli/jsonOutput.js +7 -0
- package/dist/cli/jsonOutput.js.map +1 -0
- package/dist/cli/jsonOutput.test.d.ts +2 -0
- package/dist/cli/jsonOutput.test.d.ts.map +1 -0
- package/dist/cli/jsonOutput.test.js +44 -0
- package/dist/cli/jsonOutput.test.js.map +1 -0
- package/dist/cli/resolveOrg.d.ts +2 -0
- package/dist/cli/resolveOrg.d.ts.map +1 -0
- package/dist/cli/resolveOrg.js +15 -0
- package/dist/cli/resolveOrg.js.map +1 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/chunkMarkdown.d.ts +6 -0
- package/dist/lib/chunkMarkdown.d.ts.map +1 -0
- package/dist/lib/chunkMarkdown.js +73 -0
- package/dist/lib/chunkMarkdown.js.map +1 -0
- package/dist/lib/chunkMarkdown.test.d.ts +2 -0
- package/dist/lib/chunkMarkdown.test.d.ts.map +1 -0
- package/dist/lib/chunkMarkdown.test.js +171 -0
- package/dist/lib/chunkMarkdown.test.js.map +1 -0
- package/dist/lib/config.d.ts +6 -0
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +25 -0
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/config.test.js +67 -0
- package/dist/lib/config.test.js.map +1 -1
- package/dist/lib/configSchema.d.ts +44 -0
- package/dist/lib/configSchema.d.ts.map +1 -1
- package/dist/lib/configSchema.js +20 -0
- package/dist/lib/configSchema.js.map +1 -1
- package/dist/lib/customFieldWrite.d.ts +21 -0
- package/dist/lib/customFieldWrite.d.ts.map +1 -0
- package/dist/lib/customFieldWrite.js +83 -0
- package/dist/lib/customFieldWrite.js.map +1 -0
- package/dist/lib/customFieldWrite.test.d.ts +2 -0
- package/dist/lib/customFieldWrite.test.d.ts.map +1 -0
- package/dist/lib/customFieldWrite.test.js +87 -0
- package/dist/lib/customFieldWrite.test.js.map +1 -0
- package/dist/lib/exportFields.d.ts +30 -0
- package/dist/lib/exportFields.d.ts.map +1 -0
- package/dist/lib/exportFields.js +169 -0
- package/dist/lib/exportFields.js.map +1 -0
- package/dist/lib/exportFields.test.d.ts +2 -0
- package/dist/lib/exportFields.test.d.ts.map +1 -0
- package/dist/lib/exportFields.test.js +145 -0
- package/dist/lib/exportFields.test.js.map +1 -0
- package/dist/lib/exporter.d.ts +25 -3
- package/dist/lib/exporter.d.ts.map +1 -1
- package/dist/lib/exporter.js +195 -18
- package/dist/lib/exporter.js.map +1 -1
- package/dist/lib/exporter.test.d.ts +2 -0
- package/dist/lib/exporter.test.d.ts.map +1 -0
- package/dist/lib/exporter.test.js +227 -0
- package/dist/lib/exporter.test.js.map +1 -0
- package/dist/lib/jiraClient.d.ts +272 -1
- package/dist/lib/jiraClient.d.ts.map +1 -1
- package/dist/lib/jiraClient.discovery.test.d.ts +2 -0
- package/dist/lib/jiraClient.discovery.test.d.ts.map +1 -0
- package/dist/lib/jiraClient.discovery.test.js +209 -0
- package/dist/lib/jiraClient.discovery.test.js.map +1 -0
- package/dist/lib/jiraClient.js +656 -16
- package/dist/lib/jiraClient.js.map +1 -1
- package/dist/lib/jiraClient.test.d.ts +2 -0
- package/dist/lib/jiraClient.test.d.ts.map +1 -0
- package/dist/lib/jiraClient.test.js +383 -0
- package/dist/lib/jiraClient.test.js.map +1 -0
- package/dist/lib/jiraClient.write.test.d.ts +2 -0
- package/dist/lib/jiraClient.write.test.d.ts.map +1 -0
- package/dist/lib/jiraClient.write.test.js +358 -0
- package/dist/lib/jiraClient.write.test.js.map +1 -0
- package/dist/lib/markdownToWiki.d.ts +2 -0
- package/dist/lib/markdownToWiki.d.ts.map +1 -0
- package/dist/lib/markdownToWiki.js +110 -0
- package/dist/lib/markdownToWiki.js.map +1 -0
- package/dist/lib/markdownToWiki.test.d.ts +2 -0
- package/dist/lib/markdownToWiki.test.d.ts.map +1 -0
- package/dist/lib/markdownToWiki.test.js +122 -0
- package/dist/lib/markdownToWiki.test.js.map +1 -0
- package/dist/lib/platform.d.ts +2 -0
- package/dist/lib/platform.d.ts.map +1 -1
- package/dist/lib/platform.js +8 -0
- package/dist/lib/platform.js.map +1 -1
- package/dist/lib/runCommand.d.ts +1 -0
- package/dist/lib/runCommand.d.ts.map +1 -1
- package/dist/lib/runCommand.js +8 -1
- package/dist/lib/runCommand.js.map +1 -1
- package/dist/lib/worklog.d.ts +36 -0
- package/dist/lib/worklog.d.ts.map +1 -0
- package/dist/lib/worklog.js +188 -0
- package/dist/lib/worklog.js.map +1 -0
- package/dist/lib/worklog.test.d.ts +2 -0
- package/dist/lib/worklog.test.d.ts.map +1 -0
- package/dist/lib/worklog.test.js +118 -0
- package/dist/lib/worklog.test.js.map +1 -0
- package/package.json +3 -1
package/dist/cli/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
3
|
import { Command } from 'commander';
|
|
4
|
+
import updateNotifier from 'update-notifier';
|
|
4
5
|
import { select, isCancel, cancel } from '@clack/prompts';
|
|
5
6
|
import { JiraExporter } from '../lib/exporter.js';
|
|
6
7
|
import { findOrgsByProjectKey, loadProfile, readConfig } from '../lib/config.js';
|
|
@@ -10,6 +11,28 @@ import { runAuthSet, runAuthRm, runAuthList, runAuthStatus } from './commands/au
|
|
|
10
11
|
import { runOrgsList, runOrgsRemove, runProjectRemove } from './commands/orgs.js';
|
|
11
12
|
import { runDoctor } from './commands/doctor.js';
|
|
12
13
|
import { runSetup } from './commands/setup.js';
|
|
14
|
+
import { runComment, runDeleteComment } from './commands/comment.js';
|
|
15
|
+
import { runBoardIssues } from './commands/board.js';
|
|
16
|
+
import { runTransition } from './commands/transition.js';
|
|
17
|
+
import { runWorklog } from './commands/worklog.js';
|
|
18
|
+
import { runSearch } from './commands/search.js';
|
|
19
|
+
import { runProjects } from './commands/projects.js';
|
|
20
|
+
import { runBoards } from './commands/boards.js';
|
|
21
|
+
import { runSprints } from './commands/sprints.js';
|
|
22
|
+
import { runIssueTypes } from './commands/issuetypes.js';
|
|
23
|
+
import { runComponents } from './commands/components.js';
|
|
24
|
+
import { runFields } from './commands/fields.js';
|
|
25
|
+
import { runLinkTypes } from './commands/linktypes.js';
|
|
26
|
+
import { runMe } from './commands/me.js';
|
|
27
|
+
import { runFetch } from './commands/fetch.js';
|
|
28
|
+
import { runCreate } from './commands/create.js';
|
|
29
|
+
import { runEdit } from './commands/edit.js';
|
|
30
|
+
import { runAssign } from './commands/assign.js';
|
|
31
|
+
import { runLink, runLinkRemove } from './commands/link.js';
|
|
32
|
+
import { runAttach, runAttachRemove } from './commands/attach.js';
|
|
33
|
+
import { runWatchers } from './commands/watchers.js';
|
|
34
|
+
import { runUpgrade } from './commands/upgrade.js';
|
|
35
|
+
import { parseFieldsFlag, resolveFieldSet } from '../lib/exportFields.js';
|
|
13
36
|
async function pickOrgInteractively(candidates, projectKey) {
|
|
14
37
|
const raw = readConfig();
|
|
15
38
|
const choice = await select({
|
|
@@ -64,6 +87,8 @@ async function runExport(rawArgs, flags) {
|
|
|
64
87
|
let videoEnabled = flags.videoFrames;
|
|
65
88
|
let fps = flags.fps ? parseInt(flags.fps, 10) : 5;
|
|
66
89
|
let maxFrames = flags.maxFrames ? parseInt(flags.maxFrames, 10) : 10;
|
|
90
|
+
let fieldSelector = flags.fields ? parseFieldsFlag(flags.fields) : undefined;
|
|
91
|
+
let customFieldDefs;
|
|
67
92
|
let keys;
|
|
68
93
|
let org = flags.org;
|
|
69
94
|
try {
|
|
@@ -89,6 +114,14 @@ async function runExport(rawArgs, flags) {
|
|
|
89
114
|
if (flags.includeSubtasks === undefined && resolved.org.includeSubtasks !== undefined) {
|
|
90
115
|
includeSubtasks = resolved.org.includeSubtasks;
|
|
91
116
|
}
|
|
117
|
+
if (resolved.org.export) {
|
|
118
|
+
if (!fieldSelector && resolved.org.export.fieldSelector) {
|
|
119
|
+
fieldSelector = resolved.org.export.fieldSelector;
|
|
120
|
+
}
|
|
121
|
+
if (resolved.org.export.customFieldDefs) {
|
|
122
|
+
customFieldDefs = resolved.org.export.customFieldDefs;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
92
125
|
if (resolved.org.videoFrames) {
|
|
93
126
|
if (resolved.org.videoFrames.enabled === false && flags.videoFrames !== false) {
|
|
94
127
|
videoEnabled = false;
|
|
@@ -106,19 +139,51 @@ async function runExport(rawArgs, flags) {
|
|
|
106
139
|
process.exit(1);
|
|
107
140
|
}
|
|
108
141
|
}
|
|
142
|
+
if (flags.dryRun) {
|
|
143
|
+
const resolved = resolveFieldSet(fieldSelector, customFieldDefs ?? {});
|
|
144
|
+
console.log('Dry run — no Jira calls or file writes will be performed.');
|
|
145
|
+
console.log(`Org: ${org}`);
|
|
146
|
+
console.log(`Project: ${projectKey}`);
|
|
147
|
+
console.log(`Base URL: ${baseUrl}`);
|
|
148
|
+
console.log(`Output dir: ${outputDir}`);
|
|
149
|
+
console.log(`Issues (${keys.length}): ${keys.join(', ')}`);
|
|
150
|
+
console.log(`Field preset: ${fieldSelector?.preset ?? '(default)'}`);
|
|
151
|
+
if (fieldSelector?.include?.length)
|
|
152
|
+
console.log(` include: ${fieldSelector.include.join(', ')}`);
|
|
153
|
+
if (fieldSelector?.exclude?.length)
|
|
154
|
+
console.log(` exclude: ${fieldSelector.exclude.join(', ')}`);
|
|
155
|
+
console.log(`Frontmatter fields: ${resolved.friendlyKeys.join(', ')}`);
|
|
156
|
+
console.log(`Jira fields requested: ${resolved.jiraFieldIds.join(', ')}`);
|
|
157
|
+
if (customFieldDefs && Object.keys(customFieldDefs).length > 0) {
|
|
158
|
+
console.log(`Custom fields: ${Object.keys(customFieldDefs).join(', ')}`);
|
|
159
|
+
}
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
109
162
|
const exporter = new JiraExporter({ baseUrl: baseUrl, projectKey: projectKey, userEmail: userEmail }, apiToken);
|
|
110
163
|
console.log(`Exporting ${keys.length} issue(s) to ${outputDir}...`);
|
|
111
164
|
const result = await exporter.exportIssues(keys, {
|
|
112
165
|
outputDir,
|
|
113
166
|
includeSubtasks,
|
|
167
|
+
fieldSelector,
|
|
168
|
+
customFieldDefs,
|
|
114
169
|
videoFrames: { enabled: videoEnabled, fps, maxFrames },
|
|
115
170
|
});
|
|
171
|
+
const printItem = (item) => {
|
|
172
|
+
console.log(` - ${item.key}: ${item.path}`);
|
|
173
|
+
if (item.attachmentCount > 0) {
|
|
174
|
+
console.log(` attachments: ${item.attachmentCount}`);
|
|
175
|
+
}
|
|
176
|
+
};
|
|
116
177
|
console.log('\nExport summary:');
|
|
117
178
|
if (result.imported.length > 0) {
|
|
118
|
-
console.log(` Imported (${result.imported.length})
|
|
179
|
+
console.log(` Imported (${result.imported.length}):`);
|
|
180
|
+
for (const item of result.imported)
|
|
181
|
+
printItem(item);
|
|
119
182
|
}
|
|
120
183
|
if (result.updated.length > 0) {
|
|
121
|
-
console.log(` Updated (${result.updated.length})
|
|
184
|
+
console.log(` Updated (${result.updated.length}):`);
|
|
185
|
+
for (const item of result.updated)
|
|
186
|
+
printItem(item);
|
|
122
187
|
}
|
|
123
188
|
if (result.failed.length > 0) {
|
|
124
189
|
console.log(` Failed (${result.failed.length}):`);
|
|
@@ -131,6 +196,14 @@ const program = new Command();
|
|
|
131
196
|
program.enablePositionalOptions();
|
|
132
197
|
const pkg = createRequire(import.meta.url)('../../package.json');
|
|
133
198
|
program.name(pkg.name).description(pkg.description).version(pkg.version);
|
|
199
|
+
const notifier = updateNotifier({ pkg, updateCheckInterval: 1000 * 60 * 60 * 24 });
|
|
200
|
+
if (!process.argv.includes('--json')) {
|
|
201
|
+
notifier.notify({
|
|
202
|
+
defer: true,
|
|
203
|
+
isGlobal: true,
|
|
204
|
+
message: `Update available {currentVersion} → {latestVersion}\nRun \`${pkg.name} upgrade\` to update.`,
|
|
205
|
+
});
|
|
206
|
+
}
|
|
134
207
|
program
|
|
135
208
|
.argument('[issue-keys...]', 'Jira issue keys, e.g. PROJ-123 or acme/PROJ-123')
|
|
136
209
|
.option('-o, --org <name>', 'Organization name from config (auto-resolved from issue prefix if unique)')
|
|
@@ -143,6 +216,8 @@ program
|
|
|
143
216
|
.option('--fps <n>', 'Frame extraction FPS')
|
|
144
217
|
.option('--max-frames <n>', 'Max frames kept per video')
|
|
145
218
|
.option('--include-subtasks', 'Fetch and include subtask metadata')
|
|
219
|
+
.option('--fields <list>', 'Comma-separated friendly field names to include in frontmatter. Use +name/-name to add/remove, or "all" | "default" | "minimal".')
|
|
220
|
+
.option('--dry-run', 'Resolve config & print plan without calling Jira or writing files')
|
|
146
221
|
.action(async (keys, flags) => {
|
|
147
222
|
await runExport(keys, flags);
|
|
148
223
|
})
|
|
@@ -259,6 +334,641 @@ Examples:
|
|
|
259
334
|
$ jirallm setup --yes
|
|
260
335
|
$ jirallm setup --bundled
|
|
261
336
|
`);
|
|
337
|
+
program
|
|
338
|
+
.command('upgrade')
|
|
339
|
+
.description('Upgrade jirallm to the latest version (auto-detects npm/pnpm/yarn/Homebrew)')
|
|
340
|
+
.option('-y, --yes', 'Skip confirmation prompt')
|
|
341
|
+
.option('--check', 'Print whether an update is available without installing (exits non-zero if outdated)')
|
|
342
|
+
.action(async (opts) => {
|
|
343
|
+
try {
|
|
344
|
+
await runUpgrade(opts);
|
|
345
|
+
}
|
|
346
|
+
catch (err) {
|
|
347
|
+
console.error(err.message);
|
|
348
|
+
process.exit(1);
|
|
349
|
+
}
|
|
350
|
+
})
|
|
351
|
+
.addHelpText('after', `
|
|
352
|
+
Examples:
|
|
353
|
+
$ jirallm upgrade
|
|
354
|
+
$ jirallm upgrade --yes
|
|
355
|
+
$ jirallm upgrade --check
|
|
356
|
+
`);
|
|
357
|
+
program
|
|
358
|
+
.command('comment <issue-key>')
|
|
359
|
+
.description('Post a (possibly multi-part) comment on a Jira issue. Markdown converted to Jira wiki by default.')
|
|
360
|
+
.option('-f, --file <path>', 'Read comment body from a file')
|
|
361
|
+
.option('-t, --text <text>', 'Inline comment text (alternative to --file / stdin)')
|
|
362
|
+
.option('-o, --org <name>', 'Organization name override')
|
|
363
|
+
.option('--max-chars <n>', 'Max chars per comment chunk (default 25000)')
|
|
364
|
+
.option('--no-wiki', 'Skip markdown→wiki conversion (post body as-is)')
|
|
365
|
+
.option('--reply-to <commentId>', 'Post as reply to an existing comment (threaded)')
|
|
366
|
+
.option('--no-thread', 'When posting multiple chunks, do not chain them as replies')
|
|
367
|
+
.option('--dry-run', 'Show what would be posted without calling Jira')
|
|
368
|
+
.action(async (issueKey, opts) => {
|
|
369
|
+
try {
|
|
370
|
+
await runComment(issueKey, {
|
|
371
|
+
file: opts.file,
|
|
372
|
+
text: opts.text,
|
|
373
|
+
org: opts.org,
|
|
374
|
+
maxChars: opts.maxChars,
|
|
375
|
+
noWiki: opts.wiki === false,
|
|
376
|
+
noThread: opts.thread === false,
|
|
377
|
+
replyTo: opts.replyTo,
|
|
378
|
+
dryRun: opts.dryRun,
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
catch (err) {
|
|
382
|
+
console.error(err.message);
|
|
383
|
+
process.exit(1);
|
|
384
|
+
}
|
|
385
|
+
})
|
|
386
|
+
.addHelpText('after', `
|
|
387
|
+
Examples:
|
|
388
|
+
$ jirallm comment CN-2505 --file ./summary.md
|
|
389
|
+
$ jirallm comment CN-2505 -t "Quick note"
|
|
390
|
+
$ cat summary.md | jirallm comment CN-2505
|
|
391
|
+
$ jirallm comment CN-2505 --file ./summary.md --dry-run
|
|
392
|
+
$ jirallm comment CN-2505 --reply-to 26215 -t "follow-up"
|
|
393
|
+
`);
|
|
394
|
+
program
|
|
395
|
+
.command('board:issues')
|
|
396
|
+
.description('List issues in a board column. Output JSON with --json for piping into other tools.')
|
|
397
|
+
.requiredOption('-b, --board <name>', 'Board name (exact match)')
|
|
398
|
+
.requiredOption('-c, --column <name>', 'Column name (exact match)')
|
|
399
|
+
.requiredOption('-o, --org <name>', 'Organization name from config')
|
|
400
|
+
.option('-P, --project <key>', 'Project key override (defaults to the org\'s configured project)')
|
|
401
|
+
.option('-a, --assignee <accountIdOrMe>', 'Filter by assignee. Use "me" for the current user.')
|
|
402
|
+
.option('--json', 'Output JSON instead of human-readable')
|
|
403
|
+
.action(async (opts) => {
|
|
404
|
+
try {
|
|
405
|
+
await runBoardIssues(opts);
|
|
406
|
+
}
|
|
407
|
+
catch (err) {
|
|
408
|
+
console.error(err.message);
|
|
409
|
+
process.exit(1);
|
|
410
|
+
}
|
|
411
|
+
})
|
|
412
|
+
.addHelpText('after', `
|
|
413
|
+
Examples:
|
|
414
|
+
$ jirallm board:issues -o MyOrg -b "My Board" -c "In Review" -a me --json
|
|
415
|
+
$ jirallm board:issues -o MyOrg -b "My Board" -c "Done"
|
|
416
|
+
`);
|
|
417
|
+
program
|
|
418
|
+
.command('transition <issue-key>')
|
|
419
|
+
.description('Transition a Jira issue to a target status (matches transition.to.name).')
|
|
420
|
+
.option('-t, --to <status>', 'Target status name (e.g. "In Review")')
|
|
421
|
+
.option('-o, --org <name>', 'Organization name override')
|
|
422
|
+
.option('-l, --list', 'List available transitions for the issue instead of performing one')
|
|
423
|
+
.option('--json', 'Output JSON instead of human-readable')
|
|
424
|
+
.action(async (issueKey, opts) => {
|
|
425
|
+
try {
|
|
426
|
+
if (!opts.list && !opts.to) {
|
|
427
|
+
throw new Error('Either --to <status> or --list is required.');
|
|
428
|
+
}
|
|
429
|
+
await runTransition(issueKey, { to: opts.to ?? '', org: opts.org, list: opts.list, json: opts.json });
|
|
430
|
+
}
|
|
431
|
+
catch (err) {
|
|
432
|
+
console.error(err.message);
|
|
433
|
+
process.exit(1);
|
|
434
|
+
}
|
|
435
|
+
})
|
|
436
|
+
.addHelpText('after', `
|
|
437
|
+
Examples:
|
|
438
|
+
$ jirallm transition PROJ-123 --to "In Review"
|
|
439
|
+
$ jirallm transition PROJ-123 --list
|
|
440
|
+
`);
|
|
441
|
+
program
|
|
442
|
+
.command('worklog')
|
|
443
|
+
.description('Batch log work to Jira from a JSON array (stdin or --file).')
|
|
444
|
+
.option('-f, --file <path>', 'Read JSON array from a file (default: stdin)')
|
|
445
|
+
.option('-o, --org <name>', 'Default org for entries without an org/ prefix or "org" field')
|
|
446
|
+
.option('--no-wiki', 'Skip markdown→wiki conversion of description')
|
|
447
|
+
.option('--dry-run', 'Validate and print what would be posted, without calling Jira')
|
|
448
|
+
.action(async (opts) => {
|
|
449
|
+
try {
|
|
450
|
+
await runWorklog({
|
|
451
|
+
file: opts.file,
|
|
452
|
+
org: opts.org,
|
|
453
|
+
noWiki: opts.wiki === false,
|
|
454
|
+
dryRun: opts.dryRun,
|
|
455
|
+
});
|
|
456
|
+
}
|
|
457
|
+
catch (err) {
|
|
458
|
+
console.error(err.message);
|
|
459
|
+
process.exit(1);
|
|
460
|
+
}
|
|
461
|
+
})
|
|
462
|
+
.addHelpText('after', `
|
|
463
|
+
JSON entry shape (any 2 of startTime/endTime/duration required):
|
|
464
|
+
{
|
|
465
|
+
"issueKey": "PROJ-123", // required; supports "org/PROJ-123"
|
|
466
|
+
"startTime": "2026-05-23T09:00:00+02:00",
|
|
467
|
+
"endTime": "2026-05-23T10:30:00+02:00",
|
|
468
|
+
"duration": "1h 30m", // seconds | "1h 30m" | "PT1H30M"
|
|
469
|
+
"description": "**markdown** ok",
|
|
470
|
+
"org": "acme", // optional per-entry override
|
|
471
|
+
"visibility": { "type": "role", "value": "Developers" }
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
Examples:
|
|
475
|
+
$ jirallm worklog -f ./worklogs.json
|
|
476
|
+
$ cat worklogs.json | jirallm worklog --dry-run
|
|
477
|
+
$ echo '[{"issueKey":"PROJ-1","startTime":"2026-05-23T09:00:00+02:00","duration":"1h"}]' | jirallm worklog
|
|
478
|
+
`);
|
|
479
|
+
program
|
|
480
|
+
.command('comment:rm <issue-key> <comment-id>')
|
|
481
|
+
.description('Delete a comment from a Jira issue')
|
|
482
|
+
.option('-o, --org <name>', 'Organization name override')
|
|
483
|
+
.action(async (issueKey, commentId, opts) => {
|
|
484
|
+
try {
|
|
485
|
+
await runDeleteComment(issueKey, commentId, opts);
|
|
486
|
+
}
|
|
487
|
+
catch (err) {
|
|
488
|
+
console.error(err.message);
|
|
489
|
+
process.exit(1);
|
|
490
|
+
}
|
|
491
|
+
})
|
|
492
|
+
.addHelpText('after', `
|
|
493
|
+
Examples:
|
|
494
|
+
$ jirallm comment:rm PROJ-123 26215
|
|
495
|
+
$ jirallm comment:rm acme/PROJ-123 26215
|
|
496
|
+
`);
|
|
497
|
+
function exitOnError(err) {
|
|
498
|
+
console.error(err.message);
|
|
499
|
+
process.exit(1);
|
|
500
|
+
}
|
|
501
|
+
/** Commander collector for repeatable options (e.g. --field a=1 --field b=2). */
|
|
502
|
+
function collect(value, previous = []) {
|
|
503
|
+
return [...previous, value];
|
|
504
|
+
}
|
|
505
|
+
program
|
|
506
|
+
.command('search <jql>')
|
|
507
|
+
.description('Search issues by JQL (single page; pass --cursor for next page).')
|
|
508
|
+
.option('-o, --org <name>', 'Organization name')
|
|
509
|
+
.option('-P, --project <key>', 'Project key override')
|
|
510
|
+
.option('--limit <n>', 'Page size (default 50)')
|
|
511
|
+
.option('--cursor <token>', 'Next page token from prior search')
|
|
512
|
+
.option('--fields <list>', 'Comma-separated Jira field IDs to include')
|
|
513
|
+
.option('--json', 'Output JSON instead of human-readable')
|
|
514
|
+
.action(async (jql, opts) => {
|
|
515
|
+
try {
|
|
516
|
+
await runSearch({ jql, ...opts });
|
|
517
|
+
}
|
|
518
|
+
catch (err) {
|
|
519
|
+
exitOnError(err);
|
|
520
|
+
}
|
|
521
|
+
})
|
|
522
|
+
.addHelpText('after', `
|
|
523
|
+
JQL must be quoted in your shell (it usually contains spaces and shell metacharacters).
|
|
524
|
+
Output JSON includes "nextPageToken" — pass it back via --cursor for the next page.
|
|
525
|
+
|
|
526
|
+
Examples:
|
|
527
|
+
$ jirallm search 'assignee = currentUser() AND statusCategory != Done' -o acme --json
|
|
528
|
+
$ jirallm search 'project = PROJ AND sprint in openSprints()' -o acme --limit 25
|
|
529
|
+
$ jirallm search 'project = PROJ' -o acme --cursor eyJsYXN0SXNzdWVLZXkiOi4uLn0= --json
|
|
530
|
+
$ jirallm search 'project = PROJ' -o acme --fields summary,status,assignee --json
|
|
531
|
+
`);
|
|
532
|
+
program
|
|
533
|
+
.command('projects')
|
|
534
|
+
.description('List projects accessible in an org.')
|
|
535
|
+
.requiredOption('-o, --org <name>', 'Organization name')
|
|
536
|
+
.option('--query <text>', 'Filter by name/key substring')
|
|
537
|
+
.option('--limit <n>', 'Page size')
|
|
538
|
+
.option('--start-at <n>', 'Pagination offset')
|
|
539
|
+
.option('--json', 'Output JSON instead of human-readable')
|
|
540
|
+
.action(async (opts) => {
|
|
541
|
+
try {
|
|
542
|
+
await runProjects(opts);
|
|
543
|
+
}
|
|
544
|
+
catch (err) {
|
|
545
|
+
exitOnError(err);
|
|
546
|
+
}
|
|
547
|
+
})
|
|
548
|
+
.addHelpText('after', `
|
|
549
|
+
Examples:
|
|
550
|
+
$ jirallm projects -o acme
|
|
551
|
+
$ jirallm projects -o acme --query docs --json
|
|
552
|
+
$ jirallm projects -o acme --limit 50 --start-at 100 --json
|
|
553
|
+
`);
|
|
554
|
+
program
|
|
555
|
+
.command('boards')
|
|
556
|
+
.description('List agile boards in an org.')
|
|
557
|
+
.requiredOption('-o, --org <name>', 'Organization name')
|
|
558
|
+
.option('-P, --project <key>', 'Filter by project key')
|
|
559
|
+
.option('-t, --type <type>', 'scrum | kanban | simple')
|
|
560
|
+
.option('-n, --name <name>', 'Name substring filter')
|
|
561
|
+
.option('--limit <n>', 'Page size')
|
|
562
|
+
.option('--start-at <n>', 'Pagination offset')
|
|
563
|
+
.option('--json', 'Output JSON instead of human-readable')
|
|
564
|
+
.action(async (opts) => {
|
|
565
|
+
try {
|
|
566
|
+
await runBoards(opts);
|
|
567
|
+
}
|
|
568
|
+
catch (err) {
|
|
569
|
+
exitOnError(err);
|
|
570
|
+
}
|
|
571
|
+
})
|
|
572
|
+
.addHelpText('after', `
|
|
573
|
+
When --project is omitted, falls back to the org's default project.
|
|
574
|
+
The board id printed here is what you pass to \`jirallm sprints <id>\`.
|
|
575
|
+
|
|
576
|
+
Examples:
|
|
577
|
+
$ jirallm boards -o acme
|
|
578
|
+
$ jirallm boards -o acme -P PROJ -t scrum --json
|
|
579
|
+
$ jirallm boards -o acme -n "Team Alpha" --json
|
|
580
|
+
`);
|
|
581
|
+
program
|
|
582
|
+
.command('sprints <board-id>')
|
|
583
|
+
.description('List sprints on a board.')
|
|
584
|
+
.requiredOption('-o, --org <name>', 'Organization name')
|
|
585
|
+
.option('-s, --state <state>', 'active | future | closed')
|
|
586
|
+
.option('--limit <n>', 'Page size')
|
|
587
|
+
.option('--start-at <n>', 'Pagination offset')
|
|
588
|
+
.option('--json', 'Output JSON instead of human-readable')
|
|
589
|
+
.action(async (boardId, opts) => {
|
|
590
|
+
try {
|
|
591
|
+
await runSprints({ boardId, ...opts });
|
|
592
|
+
}
|
|
593
|
+
catch (err) {
|
|
594
|
+
exitOnError(err);
|
|
595
|
+
}
|
|
596
|
+
})
|
|
597
|
+
.addHelpText('after', `
|
|
598
|
+
Get board ids from \`jirallm boards --org <name>\`.
|
|
599
|
+
|
|
600
|
+
Examples:
|
|
601
|
+
$ jirallm sprints 42 -o acme --state active --json
|
|
602
|
+
$ jirallm sprints 42 -o acme --state closed --limit 20
|
|
603
|
+
`);
|
|
604
|
+
program
|
|
605
|
+
.command('issuetypes')
|
|
606
|
+
.description('List issue types (project-scoped if --project provided).')
|
|
607
|
+
.requiredOption('-o, --org <name>', 'Organization name')
|
|
608
|
+
.option('-P, --project <key>', 'Project key (defaults to org default)')
|
|
609
|
+
.option('--json', 'Output JSON instead of human-readable')
|
|
610
|
+
.action(async (opts) => {
|
|
611
|
+
try {
|
|
612
|
+
await runIssueTypes(opts);
|
|
613
|
+
}
|
|
614
|
+
catch (err) {
|
|
615
|
+
exitOnError(err);
|
|
616
|
+
}
|
|
617
|
+
})
|
|
618
|
+
.addHelpText('after', `
|
|
619
|
+
Use this before \`jirallm create\` to discover the valid --type values for a project.
|
|
620
|
+
|
|
621
|
+
Examples:
|
|
622
|
+
$ jirallm issuetypes -o acme --json
|
|
623
|
+
$ jirallm issuetypes -o acme -P PROJ --json
|
|
624
|
+
`);
|
|
625
|
+
program
|
|
626
|
+
.command('components')
|
|
627
|
+
.description('List components defined on a project.')
|
|
628
|
+
.requiredOption('-o, --org <name>', 'Organization name')
|
|
629
|
+
.option('-P, --project <key>', 'Project key (defaults to org default)')
|
|
630
|
+
.option('--json', 'Output JSON instead of human-readable')
|
|
631
|
+
.action(async (opts) => {
|
|
632
|
+
try {
|
|
633
|
+
await runComponents(opts);
|
|
634
|
+
}
|
|
635
|
+
catch (err) {
|
|
636
|
+
exitOnError(err);
|
|
637
|
+
}
|
|
638
|
+
})
|
|
639
|
+
.addHelpText('after', `
|
|
640
|
+
The "name" values are what \`jirallm create/edit --components\` expects.
|
|
641
|
+
|
|
642
|
+
Examples:
|
|
643
|
+
$ jirallm components -o acme -P PROJ
|
|
644
|
+
$ jirallm components -o acme -P PROJ --json
|
|
645
|
+
`);
|
|
646
|
+
program
|
|
647
|
+
.command('fields')
|
|
648
|
+
.description('List custom fields (and select options with --type) for use with --field.')
|
|
649
|
+
.requiredOption('-o, --org <name>', 'Organization name')
|
|
650
|
+
.option('-P, --project <key>', 'Project key (defaults to org default)')
|
|
651
|
+
.option('-t, --type <issueType>', 'Show create-screen fields + allowed values for this issue type')
|
|
652
|
+
.option('--json', 'Output JSON instead of human-readable')
|
|
653
|
+
.action(async (opts) => {
|
|
654
|
+
try {
|
|
655
|
+
await runFields(opts);
|
|
656
|
+
}
|
|
657
|
+
catch (err) {
|
|
658
|
+
exitOnError(err);
|
|
659
|
+
}
|
|
660
|
+
})
|
|
661
|
+
.addHelpText('after', `
|
|
662
|
+
The [customfield_NNNNN] ids are what \`jirallm create/edit --field\` expects.
|
|
663
|
+
Add --type Bug to see valid select options (e.g. for Severity, Environment).
|
|
664
|
+
|
|
665
|
+
Examples:
|
|
666
|
+
$ jirallm fields -o acme -P PROJ
|
|
667
|
+
$ jirallm fields -o acme -P PROJ --type Bug
|
|
668
|
+
$ jirallm fields -o acme -P PROJ --type Bug --json
|
|
669
|
+
`);
|
|
670
|
+
program
|
|
671
|
+
.command('linktypes')
|
|
672
|
+
.description('List issue link types available in an org.')
|
|
673
|
+
.requiredOption('-o, --org <name>', 'Organization name')
|
|
674
|
+
.option('--json', 'Output JSON instead of human-readable')
|
|
675
|
+
.action(async (opts) => {
|
|
676
|
+
try {
|
|
677
|
+
await runLinkTypes(opts);
|
|
678
|
+
}
|
|
679
|
+
catch (err) {
|
|
680
|
+
exitOnError(err);
|
|
681
|
+
}
|
|
682
|
+
})
|
|
683
|
+
.addHelpText('after', `
|
|
684
|
+
The "name" column is what \`jirallm link\` expects as <type> (e.g. "Blocks", "Relates").
|
|
685
|
+
|
|
686
|
+
Examples:
|
|
687
|
+
$ jirallm linktypes -o acme
|
|
688
|
+
$ jirallm linktypes -o acme --json
|
|
689
|
+
`);
|
|
690
|
+
program
|
|
691
|
+
.command('me')
|
|
692
|
+
.description('Show the currently authenticated Jira user.')
|
|
693
|
+
.requiredOption('-o, --org <name>', 'Organization name')
|
|
694
|
+
.option('--json', 'Output JSON instead of human-readable')
|
|
695
|
+
.action(async (opts) => {
|
|
696
|
+
try {
|
|
697
|
+
await runMe(opts);
|
|
698
|
+
}
|
|
699
|
+
catch (err) {
|
|
700
|
+
exitOnError(err);
|
|
701
|
+
}
|
|
702
|
+
})
|
|
703
|
+
.addHelpText('after', `
|
|
704
|
+
Returns the accountId — use it as --assignee for \`create\` / \`edit\` / \`assign\`,
|
|
705
|
+
or pass "me" as a shorthand in commands that resolve it (\`assign\`, \`watchers\`).
|
|
706
|
+
|
|
707
|
+
Examples:
|
|
708
|
+
$ jirallm me -o acme
|
|
709
|
+
$ jirallm me -o acme --json | jq -r .accountId
|
|
710
|
+
`);
|
|
711
|
+
program
|
|
712
|
+
.command('fetch <issue-key>')
|
|
713
|
+
.description('Fetch a single issue as JSON or pretty text (no file output).')
|
|
714
|
+
.option('-o, --org <name>', 'Organization name override')
|
|
715
|
+
.option('--json', 'Output JSON instead of human-readable')
|
|
716
|
+
.action(async (issueKey, opts) => {
|
|
717
|
+
try {
|
|
718
|
+
await runFetch({ issueKey, ...opts });
|
|
719
|
+
}
|
|
720
|
+
catch (err) {
|
|
721
|
+
exitOnError(err);
|
|
722
|
+
}
|
|
723
|
+
})
|
|
724
|
+
.addHelpText('after', `
|
|
725
|
+
For the full export bundle (attachments, video frames, on-disk folder),
|
|
726
|
+
use the default \`jirallm <key>\` command instead.
|
|
727
|
+
|
|
728
|
+
Examples:
|
|
729
|
+
$ jirallm fetch PROJ-123 --json
|
|
730
|
+
$ jirallm fetch acme/PROJ-123
|
|
731
|
+
$ jirallm fetch PROJ-123 --json | jq .status
|
|
732
|
+
`);
|
|
733
|
+
program
|
|
734
|
+
.command('create')
|
|
735
|
+
.description('Create a new Jira issue.')
|
|
736
|
+
.requiredOption('-o, --org <name>', 'Organization name')
|
|
737
|
+
.option('-P, --project <key>', 'Project key (defaults to org default)')
|
|
738
|
+
.requiredOption('-t, --type <type>', 'Issue type name (e.g. Task, Bug, Story)')
|
|
739
|
+
.requiredOption('-s, --summary <text>', 'Issue summary')
|
|
740
|
+
.option('-d, --description <text>', 'Description (markdown)')
|
|
741
|
+
.option('--description-file <path>', 'Read description (markdown) from a file')
|
|
742
|
+
.option('-a, --assignee <accountId>', 'Assignee accountId')
|
|
743
|
+
.option('-l, --labels <list>', 'Comma-separated labels')
|
|
744
|
+
.option('--priority <name>', 'Priority name (e.g. High)')
|
|
745
|
+
.option('--parent <key>', 'Parent issue key (for subtasks / epic children)')
|
|
746
|
+
.option('--components <names>', 'Comma-separated component names')
|
|
747
|
+
.option('-F, --field <pair>', 'Set a custom field: friendlyName=value or customfield_NNNNN[:type]=value (repeatable)', collect)
|
|
748
|
+
.option('--dry-run', 'Show what would be created without calling Jira')
|
|
749
|
+
.option('--json', 'Output JSON instead of human-readable')
|
|
750
|
+
.action(async (opts) => {
|
|
751
|
+
try {
|
|
752
|
+
await runCreate({
|
|
753
|
+
org: opts.org,
|
|
754
|
+
projectKey: opts.project,
|
|
755
|
+
type: opts.type,
|
|
756
|
+
summary: opts.summary,
|
|
757
|
+
description: opts.description,
|
|
758
|
+
descriptionFile: opts.descriptionFile,
|
|
759
|
+
assignee: opts.assignee,
|
|
760
|
+
labels: opts.labels,
|
|
761
|
+
priority: opts.priority,
|
|
762
|
+
parent: opts.parent,
|
|
763
|
+
components: opts.components,
|
|
764
|
+
field: opts.field,
|
|
765
|
+
dryRun: opts.dryRun,
|
|
766
|
+
json: opts.json,
|
|
767
|
+
});
|
|
768
|
+
}
|
|
769
|
+
catch (err) {
|
|
770
|
+
exitOnError(err);
|
|
771
|
+
}
|
|
772
|
+
})
|
|
773
|
+
.addHelpText('after', `
|
|
774
|
+
Run \`jirallm issuetypes -o <org>\` to discover valid --type values.
|
|
775
|
+
Run \`jirallm components -o <org> -P <project>\` to discover valid --components names.
|
|
776
|
+
Run \`jirallm fields -o <org> -P <project> --type Bug\` to discover custom field ids and select options.
|
|
777
|
+
Get a user's accountId via \`jirallm me -o <org>\` (for the current user).
|
|
778
|
+
--description is markdown; it gets converted to Jira wiki on the way in.
|
|
779
|
+
|
|
780
|
+
Custom fields:
|
|
781
|
+
Friendly names defined under [orgs.<org>.export.custom_fields] resolve automatically
|
|
782
|
+
(e.g. --field severity=High shapes to the right payload from the configured type).
|
|
783
|
+
For ad-hoc fields use the raw id with an optional type: --field customfield_10050:select=High.
|
|
784
|
+
|
|
785
|
+
Examples:
|
|
786
|
+
$ jirallm create -o acme -t Task -s "Investigate flaky test"
|
|
787
|
+
$ jirallm create -o acme -P PROJ -t Bug -s "Crash on save" --description-file ./repro.md
|
|
788
|
+
$ jirallm create -o acme -t Story -s "Spike X" -l backend,p1 --priority High
|
|
789
|
+
$ jirallm create -o acme -t Sub-task -s "Subtask of PROJ-1" --parent PROJ-1
|
|
790
|
+
$ jirallm create -o acme -t Bug -s "Crash" --components Web,API --field severity=High --field environment=PROD
|
|
791
|
+
$ jirallm create -o acme -t Bug -s "test" --dry-run --json
|
|
792
|
+
`);
|
|
793
|
+
program
|
|
794
|
+
.command('edit <issue-key>')
|
|
795
|
+
.description('Edit fields on an existing Jira issue.')
|
|
796
|
+
.option('-o, --org <name>', 'Organization name override')
|
|
797
|
+
.option('-s, --summary <text>', 'New summary')
|
|
798
|
+
.option('-d, --description <text>', 'New description (markdown)')
|
|
799
|
+
.option('--description-file <path>', 'Read description (markdown) from a file')
|
|
800
|
+
.option('-a, --assignee <accountId>', 'Assignee accountId')
|
|
801
|
+
.option('--unassign', 'Unassign the issue (clears assignee)')
|
|
802
|
+
.option('-l, --labels <list>', 'Comma-separated labels (replaces existing)')
|
|
803
|
+
.option('--priority <name>', 'Priority name')
|
|
804
|
+
.option('--components <names>', 'Comma-separated component names (replaces existing)')
|
|
805
|
+
.option('-F, --field <pair>', 'Set a custom field: friendlyName=value or customfield_NNNNN[:type]=value (repeatable)', collect)
|
|
806
|
+
.option('--dry-run', 'Show what would change without calling Jira')
|
|
807
|
+
.option('--json', 'Output JSON instead of human-readable')
|
|
808
|
+
.action(async (issueKey, opts) => {
|
|
809
|
+
try {
|
|
810
|
+
await runEdit({ issueKey, ...opts });
|
|
811
|
+
}
|
|
812
|
+
catch (err) {
|
|
813
|
+
exitOnError(err);
|
|
814
|
+
}
|
|
815
|
+
})
|
|
816
|
+
.addHelpText('after', `
|
|
817
|
+
--labels and --components REPLACE the existing sets; there is no add/remove syntax here.
|
|
818
|
+
--unassign and --assignee are mutually exclusive (use one).
|
|
819
|
+
Custom fields work like \`create\`: --field friendlyName=value or --field customfield_NNNNN[:type]=value.
|
|
820
|
+
|
|
821
|
+
Examples:
|
|
822
|
+
$ jirallm edit PROJ-123 --summary "New title"
|
|
823
|
+
$ jirallm edit PROJ-123 --description-file ./updated.md
|
|
824
|
+
$ jirallm edit PROJ-123 --labels backend,p1 --priority High
|
|
825
|
+
$ jirallm edit PROJ-123 --components Web,API --field reproductionRate=Always
|
|
826
|
+
$ jirallm edit PROJ-123 --assignee 5ac1234567890abcdef
|
|
827
|
+
$ jirallm edit PROJ-123 --unassign --dry-run --json
|
|
828
|
+
`);
|
|
829
|
+
program
|
|
830
|
+
.command('assign <issue-key> <assignee>')
|
|
831
|
+
.description('Assign an issue. Use "me" for the current user, "none" to unassign.')
|
|
832
|
+
.option('-o, --org <name>', 'Organization name override')
|
|
833
|
+
.option('--dry-run', 'Show what would change without calling Jira')
|
|
834
|
+
.option('--json', 'Output JSON instead of human-readable')
|
|
835
|
+
.action(async (issueKey, assignee, opts) => {
|
|
836
|
+
try {
|
|
837
|
+
await runAssign({ issueKey, assignee, ...opts });
|
|
838
|
+
}
|
|
839
|
+
catch (err) {
|
|
840
|
+
exitOnError(err);
|
|
841
|
+
}
|
|
842
|
+
})
|
|
843
|
+
.addHelpText('after', `
|
|
844
|
+
<assignee> shortcuts: "me" (current user) or "none"/"-" (unassign).
|
|
845
|
+
Any other value is treated as a Jira accountId.
|
|
846
|
+
|
|
847
|
+
Examples:
|
|
848
|
+
$ jirallm assign PROJ-123 me
|
|
849
|
+
$ jirallm assign PROJ-123 none
|
|
850
|
+
$ jirallm assign PROJ-123 5ac1234567890abcdef
|
|
851
|
+
$ jirallm assign PROJ-123 me --dry-run --json
|
|
852
|
+
`);
|
|
853
|
+
program
|
|
854
|
+
.command('link <inward-key> <type> <outward-key>')
|
|
855
|
+
.description('Create an issue link (e.g. `link FOO-1 "blocks" FOO-2`).')
|
|
856
|
+
.option('-c, --comment <text>', 'Add a comment when linking')
|
|
857
|
+
.option('-o, --org <name>', 'Organization name override')
|
|
858
|
+
.option('--dry-run', 'Show what would be created without calling Jira')
|
|
859
|
+
.option('--json', 'Output JSON instead of human-readable')
|
|
860
|
+
.action(async (inwardKey, type, outwardKey, opts) => {
|
|
861
|
+
try {
|
|
862
|
+
await runLink({ inwardKey, type, outwardKey, ...opts });
|
|
863
|
+
}
|
|
864
|
+
catch (err) {
|
|
865
|
+
exitOnError(err);
|
|
866
|
+
}
|
|
867
|
+
})
|
|
868
|
+
.addHelpText('after', `
|
|
869
|
+
<type> is the link-type NAME (e.g. "Blocks", "Relates", "Duplicate"), not the inward/outward label.
|
|
870
|
+
Discover the available types with: \`jirallm linktypes -o <org>\`.
|
|
871
|
+
--comment is markdown; it gets converted to Jira wiki.
|
|
872
|
+
|
|
873
|
+
Examples:
|
|
874
|
+
$ jirallm link PROJ-1 "Blocks" PROJ-2
|
|
875
|
+
$ jirallm link PROJ-1 "Relates" PROJ-2 --comment "see also **infra rewrite**"
|
|
876
|
+
$ jirallm link PROJ-1 "Blocks" PROJ-2 --dry-run --json
|
|
877
|
+
`);
|
|
878
|
+
program
|
|
879
|
+
.command('link:rm <link-id>')
|
|
880
|
+
.description('Remove an issue link by ID.')
|
|
881
|
+
.requiredOption('-o, --org <name>', 'Organization name')
|
|
882
|
+
.option('--dry-run', 'Show what would be deleted without calling Jira')
|
|
883
|
+
.option('--json', 'Output JSON instead of human-readable')
|
|
884
|
+
.action(async (linkId, opts) => {
|
|
885
|
+
try {
|
|
886
|
+
await runLinkRemove({ linkId, ...opts });
|
|
887
|
+
}
|
|
888
|
+
catch (err) {
|
|
889
|
+
exitOnError(err);
|
|
890
|
+
}
|
|
891
|
+
})
|
|
892
|
+
.addHelpText('after', `
|
|
893
|
+
Get link IDs from the issueLinks array in \`jirallm fetch <key> --json\`.
|
|
894
|
+
|
|
895
|
+
Examples:
|
|
896
|
+
$ jirallm link:rm 10042 -o acme
|
|
897
|
+
$ jirallm link:rm 10042 -o acme --dry-run --json
|
|
898
|
+
`);
|
|
899
|
+
program
|
|
900
|
+
.command('attach <issue-key> [files...]')
|
|
901
|
+
.description('Upload one or more file attachments to an issue. At least one file is required.')
|
|
902
|
+
.option('-o, --org <name>', 'Organization name override')
|
|
903
|
+
.option('--dry-run', 'Show what would be uploaded without calling Jira')
|
|
904
|
+
.option('--json', 'Output JSON instead of human-readable')
|
|
905
|
+
.action(async (issueKey, files, opts) => {
|
|
906
|
+
try {
|
|
907
|
+
if (!files || files.length === 0)
|
|
908
|
+
throw new Error('At least one file is required.');
|
|
909
|
+
await runAttach({ issueKey, files, ...opts });
|
|
910
|
+
}
|
|
911
|
+
catch (err) {
|
|
912
|
+
exitOnError(err);
|
|
913
|
+
}
|
|
914
|
+
})
|
|
915
|
+
.addHelpText('after', `
|
|
916
|
+
Files are uploaded sequentially; --json returns the aggregated attachments array.
|
|
917
|
+
The id from the response is what \`jirallm attach:rm\` takes.
|
|
918
|
+
|
|
919
|
+
Examples:
|
|
920
|
+
$ jirallm attach PROJ-123 ./screenshot.png
|
|
921
|
+
$ jirallm attach PROJ-123 ./a.png ./b.png ./recording.mp4 --json
|
|
922
|
+
$ jirallm attach PROJ-123 ./screenshot.png --dry-run
|
|
923
|
+
`);
|
|
924
|
+
program
|
|
925
|
+
.command('attach:rm <attachment-id>')
|
|
926
|
+
.description('Delete an attachment by ID.')
|
|
927
|
+
.requiredOption('-o, --org <name>', 'Organization name')
|
|
928
|
+
.option('--dry-run', 'Show what would be deleted without calling Jira')
|
|
929
|
+
.option('--json', 'Output JSON instead of human-readable')
|
|
930
|
+
.action(async (attachmentId, opts) => {
|
|
931
|
+
try {
|
|
932
|
+
await runAttachRemove({ attachmentId, ...opts });
|
|
933
|
+
}
|
|
934
|
+
catch (err) {
|
|
935
|
+
exitOnError(err);
|
|
936
|
+
}
|
|
937
|
+
})
|
|
938
|
+
.addHelpText('after', `
|
|
939
|
+
Get attachment IDs from the attachments array in \`jirallm fetch <key> --json\`.
|
|
940
|
+
|
|
941
|
+
Examples:
|
|
942
|
+
$ jirallm attach:rm 99021 -o acme
|
|
943
|
+
$ jirallm attach:rm 99021 -o acme --dry-run --json
|
|
944
|
+
`);
|
|
945
|
+
program
|
|
946
|
+
.command('watchers <issue-key>')
|
|
947
|
+
.description('List, add, or remove watchers on an issue.')
|
|
948
|
+
.option('-o, --org <name>', 'Organization name override')
|
|
949
|
+
.option('--add <accountId>', 'Add watcher by accountId (or "me")')
|
|
950
|
+
.option('--rm <accountId>', 'Remove watcher by accountId (or "me")')
|
|
951
|
+
.option('--dry-run', 'Show what would change without calling Jira')
|
|
952
|
+
.option('--json', 'Output JSON instead of human-readable')
|
|
953
|
+
.action(async (issueKey, opts) => {
|
|
954
|
+
try {
|
|
955
|
+
await runWatchers({ issueKey, ...opts });
|
|
956
|
+
}
|
|
957
|
+
catch (err) {
|
|
958
|
+
exitOnError(err);
|
|
959
|
+
}
|
|
960
|
+
})
|
|
961
|
+
.addHelpText('after', `
|
|
962
|
+
With no --add/--rm, just lists current watchers.
|
|
963
|
+
You can pass both --add and --rm in one call; both run before the final listing.
|
|
964
|
+
Pass "me" to add/remove yourself (resolved via \`/myself\`).
|
|
965
|
+
|
|
966
|
+
Examples:
|
|
967
|
+
$ jirallm watchers PROJ-123 --json
|
|
968
|
+
$ jirallm watchers PROJ-123 --add me
|
|
969
|
+
$ jirallm watchers PROJ-123 --rm 5ac1234567890abcdef
|
|
970
|
+
$ jirallm watchers PROJ-123 --add me --rm 5acOldUser --json
|
|
971
|
+
`);
|
|
262
972
|
program.parseAsync(process.argv).catch((error) => {
|
|
263
973
|
console.error('Unexpected error:', error);
|
|
264
974
|
process.exit(1);
|