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