@avantmedia/af 0.0.1
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/LICENSE +21 -0
- package/README.md +539 -0
- package/af +2 -0
- package/bun-upgrade.ts +130 -0
- package/commands/bun.ts +55 -0
- package/commands/changes.ts +35 -0
- package/commands/e2e.ts +12 -0
- package/commands/help.ts +236 -0
- package/commands/install-extension.ts +133 -0
- package/commands/jira.ts +577 -0
- package/commands/licenses.ts +32 -0
- package/commands/npm.ts +55 -0
- package/commands/scaffold.ts +105 -0
- package/commands/setup.tsx +156 -0
- package/commands/spec.ts +405 -0
- package/commands/stop-hook.ts +90 -0
- package/commands/todo.ts +208 -0
- package/commands/versions.ts +150 -0
- package/commands/watch.ts +344 -0
- package/commands/worktree.ts +424 -0
- package/components/change-select.tsx +71 -0
- package/components/confirm.tsx +41 -0
- package/components/file-conflict.tsx +52 -0
- package/components/input.tsx +53 -0
- package/components/layout.tsx +70 -0
- package/components/messages.tsx +48 -0
- package/components/progress.tsx +71 -0
- package/components/select.tsx +90 -0
- package/components/status-display.tsx +74 -0
- package/components/table.tsx +79 -0
- package/generated/setup-manifest.ts +67 -0
- package/git-worktree.ts +184 -0
- package/main.ts +12 -0
- package/npm-upgrade.ts +117 -0
- package/package.json +83 -0
- package/resources/copy-prompt-reporter.ts +443 -0
- package/router.ts +220 -0
- package/setup/.claude/commands/commit-work.md +47 -0
- package/setup/.claude/commands/complete-work.md +34 -0
- package/setup/.claude/commands/e2e.md +29 -0
- package/setup/.claude/commands/start-work.md +51 -0
- package/setup/.claude/skills/pm/SKILL.md +294 -0
- package/setup/.claude/skills/pm/templates/api-endpoint.md +69 -0
- package/setup/.claude/skills/pm/templates/bug-fix.md +77 -0
- package/setup/.claude/skills/pm/templates/feature.md +87 -0
- package/setup/.claude/skills/pm/templates/ui-component.md +78 -0
- package/utils/change-select-render.tsx +44 -0
- package/utils/claude.ts +9 -0
- package/utils/config.ts +58 -0
- package/utils/env.ts +53 -0
- package/utils/git.ts +120 -0
- package/utils/ink-render.tsx +50 -0
- package/utils/openspec.ts +54 -0
- package/utils/output.ts +104 -0
- package/utils/proposal.ts +160 -0
- package/utils/resources.ts +64 -0
- package/utils/setup-files.ts +230 -0
package/commands/jira.ts
ADDED
|
@@ -0,0 +1,577 @@
|
|
|
1
|
+
import { error } from '../utils/output.ts';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Command options for Jira CLI
|
|
5
|
+
*/
|
|
6
|
+
interface JiraOptions {
|
|
7
|
+
json?: boolean;
|
|
8
|
+
project?: string;
|
|
9
|
+
type?: string;
|
|
10
|
+
summary?: string;
|
|
11
|
+
description?: string;
|
|
12
|
+
priority?: string;
|
|
13
|
+
labels?: string;
|
|
14
|
+
to?: string;
|
|
15
|
+
add?: string;
|
|
16
|
+
limit?: number;
|
|
17
|
+
parent?: string;
|
|
18
|
+
estimate?: string;
|
|
19
|
+
remaining?: string;
|
|
20
|
+
name?: string;
|
|
21
|
+
'start-date'?: string;
|
|
22
|
+
'release-date'?: string;
|
|
23
|
+
released?: boolean;
|
|
24
|
+
unreleased?: boolean;
|
|
25
|
+
'fix-version'?: string;
|
|
26
|
+
'affected-version'?: string;
|
|
27
|
+
'move-fix-issues-to'?: string;
|
|
28
|
+
'move-affected-issues-to'?: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Parse command-line arguments into subcommand, args, and options.
|
|
33
|
+
*/
|
|
34
|
+
function parseArgs(argv: string[]): {
|
|
35
|
+
subcommand: string;
|
|
36
|
+
args: string[];
|
|
37
|
+
options: JiraOptions;
|
|
38
|
+
} {
|
|
39
|
+
const args: string[] = [];
|
|
40
|
+
const options: JiraOptions = {};
|
|
41
|
+
|
|
42
|
+
let i = 0;
|
|
43
|
+
while (i < argv.length) {
|
|
44
|
+
const arg = argv[i];
|
|
45
|
+
|
|
46
|
+
if (arg === '--json') {
|
|
47
|
+
options.json = true;
|
|
48
|
+
} else if (arg === '--released') {
|
|
49
|
+
options.released = true;
|
|
50
|
+
} else if (arg === '--unreleased') {
|
|
51
|
+
options.unreleased = true;
|
|
52
|
+
} else if (arg.startsWith('--')) {
|
|
53
|
+
const key = arg.slice(2) as keyof JiraOptions;
|
|
54
|
+
const value = argv[++i];
|
|
55
|
+
if (value === undefined) {
|
|
56
|
+
throw new Error(`Option ${arg} requires a value`);
|
|
57
|
+
}
|
|
58
|
+
if (key === 'limit') {
|
|
59
|
+
options.limit = parseInt(value, 10);
|
|
60
|
+
} else {
|
|
61
|
+
(options as Record<string, string>)[key] = value;
|
|
62
|
+
}
|
|
63
|
+
} else {
|
|
64
|
+
args.push(arg);
|
|
65
|
+
}
|
|
66
|
+
i++;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const subcommand = args[0] ?? '';
|
|
70
|
+
return { subcommand, args: args.slice(1), options };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Display Jira-specific help.
|
|
75
|
+
*/
|
|
76
|
+
function showJiraHelp(): void {
|
|
77
|
+
console.log(`
|
|
78
|
+
Jira CLI - Manage Jira issues from the command line
|
|
79
|
+
|
|
80
|
+
USAGE:
|
|
81
|
+
af jira <command> [arguments] [options]
|
|
82
|
+
|
|
83
|
+
COMMANDS:
|
|
84
|
+
get <issue-key> Get issue details
|
|
85
|
+
list <project> List issues in a project
|
|
86
|
+
search "<jql>" Search issues with JQL
|
|
87
|
+
create Create a new issue
|
|
88
|
+
update <issue-key> Update an issue
|
|
89
|
+
delete <issue-key> Delete an issue
|
|
90
|
+
comment <issue-key> List or add comments
|
|
91
|
+
attach <issue-key> <file> Attach a file to an issue
|
|
92
|
+
transition <issue-key> Change issue status
|
|
93
|
+
transitions <issue-key> List available transitions
|
|
94
|
+
assign <issue-key> Assign issue to a user
|
|
95
|
+
projects List all projects
|
|
96
|
+
types <project> List issue types for a project
|
|
97
|
+
|
|
98
|
+
VERSION COMMANDS:
|
|
99
|
+
versions <project> List all versions in a project
|
|
100
|
+
version <version-id> Get version details
|
|
101
|
+
version-create Create a new version
|
|
102
|
+
version-update <id> Update a version
|
|
103
|
+
version-delete <id> Delete a version
|
|
104
|
+
|
|
105
|
+
OPTIONS:
|
|
106
|
+
--json Output as JSON instead of markdown
|
|
107
|
+
--limit <n> Limit results (default: 50)
|
|
108
|
+
|
|
109
|
+
CREATE OPTIONS:
|
|
110
|
+
--project <key> Project key (required)
|
|
111
|
+
--type <name> Issue type (required)
|
|
112
|
+
--summary "<text>" Summary (required)
|
|
113
|
+
--description "<text>" Description
|
|
114
|
+
--priority <name> Priority (e.g., High, Medium, Low)
|
|
115
|
+
--labels <a,b,c> Comma-separated labels
|
|
116
|
+
--parent <issue-key> Parent issue (for subtasks)
|
|
117
|
+
--estimate <time> Original estimate (e.g., "2h", "1d", "30m")
|
|
118
|
+
--fix-version <v1,v2> Fix version(s), comma-separated
|
|
119
|
+
--affected-version <v> Affected version(s), comma-separated
|
|
120
|
+
|
|
121
|
+
UPDATE OPTIONS:
|
|
122
|
+
--summary "<text>" New summary
|
|
123
|
+
--description "<text>" New description
|
|
124
|
+
--priority <name> New priority
|
|
125
|
+
--labels <a,b,c> New labels (replaces existing)
|
|
126
|
+
--estimate <time> Original estimate (e.g., "2h", "1d", "30m")
|
|
127
|
+
--remaining <time> Remaining estimate (e.g., "1h", "4h")
|
|
128
|
+
--fix-version <v1,v2> Fix version(s), comma-separated (empty to clear)
|
|
129
|
+
--affected-version <v> Affected version(s), comma-separated (empty to clear)
|
|
130
|
+
|
|
131
|
+
VERSION-CREATE OPTIONS:
|
|
132
|
+
--project <key> Project key (required)
|
|
133
|
+
--name "<text>" Version name (required)
|
|
134
|
+
--description "<text>" Description
|
|
135
|
+
--start-date <YYYY-MM-DD> Start date
|
|
136
|
+
--release-date <YYYY-MM-DD> Release date
|
|
137
|
+
--released Mark as released
|
|
138
|
+
|
|
139
|
+
VERSION-UPDATE OPTIONS:
|
|
140
|
+
--name "<text>" New version name
|
|
141
|
+
--description "<text>" New description
|
|
142
|
+
--start-date <YYYY-MM-DD> New start date
|
|
143
|
+
--release-date <YYYY-MM-DD> New release date
|
|
144
|
+
--released Mark as released
|
|
145
|
+
--unreleased Mark as unreleased
|
|
146
|
+
|
|
147
|
+
VERSION-DELETE OPTIONS:
|
|
148
|
+
--move-fix-issues-to <id> Move fix version issues to this version
|
|
149
|
+
--move-affected-issues-to <id> Move affected version issues to this version
|
|
150
|
+
|
|
151
|
+
COMMENT OPTIONS:
|
|
152
|
+
--add "<text>" Add a comment (omit to list comments)
|
|
153
|
+
|
|
154
|
+
TRANSITION OPTIONS:
|
|
155
|
+
--to "<status>" Target status name (required)
|
|
156
|
+
|
|
157
|
+
ASSIGN OPTIONS:
|
|
158
|
+
--to "<email>" User email (use "none" to unassign)
|
|
159
|
+
|
|
160
|
+
EXAMPLES:
|
|
161
|
+
af jira get PROJ-123
|
|
162
|
+
af jira list PROJ --limit 20
|
|
163
|
+
af jira search "assignee = currentUser() AND status != Done"
|
|
164
|
+
af jira create --project PROJ --type Bug --summary "Login broken"
|
|
165
|
+
af jira create --project PROJ --type Task --summary "Feature" --estimate "4h"
|
|
166
|
+
af jira create --project PROJ --type Bug --summary "Bug" --fix-version "v1.0.0"
|
|
167
|
+
af jira update PROJ-123 --summary "Updated title" --priority High
|
|
168
|
+
af jira update PROJ-123 --estimate "8h" --remaining "2h"
|
|
169
|
+
af jira update PROJ-123 --fix-version "v2.0.0" --affected-version "v1.0.0"
|
|
170
|
+
af jira comment PROJ-123 --add "Working on this"
|
|
171
|
+
af jira transition PROJ-123 --to "In Progress"
|
|
172
|
+
af jira assign PROJ-123 --to user@example.com
|
|
173
|
+
af jira versions PROJ
|
|
174
|
+
af jira version-create --project PROJ --name "v1.0.0" --release-date 2024-06-01
|
|
175
|
+
af jira version-update 12345 --released
|
|
176
|
+
`);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Handle the 'jira' command.
|
|
181
|
+
* Routes to appropriate Jira subcommand handlers.
|
|
182
|
+
*
|
|
183
|
+
* @param args - Command arguments (excluding 'jira')
|
|
184
|
+
* @returns Exit code (0 for success, 1 for error)
|
|
185
|
+
*/
|
|
186
|
+
export async function handleJira(args: string[]): Promise<number> {
|
|
187
|
+
// Handle --help flag
|
|
188
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
189
|
+
showJiraHelp();
|
|
190
|
+
return 0;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Parse arguments
|
|
194
|
+
let parsed: ReturnType<typeof parseArgs>;
|
|
195
|
+
try {
|
|
196
|
+
parsed = parseArgs(args);
|
|
197
|
+
} catch (err) {
|
|
198
|
+
error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
199
|
+
return 1;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const { subcommand, args: subArgs, options } = parsed;
|
|
203
|
+
const json = options.json ?? false;
|
|
204
|
+
|
|
205
|
+
// Show help if no subcommand
|
|
206
|
+
if (!subcommand || subcommand === 'help') {
|
|
207
|
+
showJiraHelp();
|
|
208
|
+
return 0;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Lazy load client and formatters only when needed
|
|
212
|
+
const client = await import('../jira/lib/client.ts');
|
|
213
|
+
const fmt = await import('../jira/lib/formatters.ts');
|
|
214
|
+
|
|
215
|
+
try {
|
|
216
|
+
switch (subcommand) {
|
|
217
|
+
case 'get': {
|
|
218
|
+
const issueKey = subArgs[0];
|
|
219
|
+
if (!issueKey) {
|
|
220
|
+
error('Error: Issue key required. Usage: af jira get <issue-key>');
|
|
221
|
+
return 1;
|
|
222
|
+
}
|
|
223
|
+
const issue = await client.getIssue(issueKey);
|
|
224
|
+
fmt.output(json ? issue : fmt.formatIssue(issue), json);
|
|
225
|
+
break;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
case 'list': {
|
|
229
|
+
const projectKey = subArgs[0];
|
|
230
|
+
if (!projectKey) {
|
|
231
|
+
error('Error: Project key required. Usage: af jira list <project>');
|
|
232
|
+
return 1;
|
|
233
|
+
}
|
|
234
|
+
const result = await client.listProjectIssues(projectKey, options.limit ?? 50);
|
|
235
|
+
fmt.output(json ? result : fmt.formatIssueList(result), json);
|
|
236
|
+
break;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
case 'search': {
|
|
240
|
+
const jql = subArgs[0];
|
|
241
|
+
if (!jql) {
|
|
242
|
+
error('Error: JQL query required. Usage: af jira search "<jql>"');
|
|
243
|
+
return 1;
|
|
244
|
+
}
|
|
245
|
+
const result = await client.searchIssues(jql, options.limit ?? 50);
|
|
246
|
+
fmt.output(json ? result : fmt.formatIssueList(result), json);
|
|
247
|
+
break;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
case 'create': {
|
|
251
|
+
const { project, type, summary, description, priority, labels, parent, estimate } =
|
|
252
|
+
options;
|
|
253
|
+
if (!project || !type || !summary) {
|
|
254
|
+
error('Error: --project, --type, and --summary are required');
|
|
255
|
+
console.error(
|
|
256
|
+
'Usage: af jira create --project PROJ --type Task --summary "Title"',
|
|
257
|
+
);
|
|
258
|
+
return 1;
|
|
259
|
+
}
|
|
260
|
+
const labelList = labels?.split(',').map(l => l.trim());
|
|
261
|
+
const fixVersionList = options['fix-version']
|
|
262
|
+
?.split(',')
|
|
263
|
+
.map(v => v.trim())
|
|
264
|
+
.filter(v => v);
|
|
265
|
+
const affectedVersionList = options['affected-version']
|
|
266
|
+
?.split(',')
|
|
267
|
+
.map(v => v.trim())
|
|
268
|
+
.filter(v => v);
|
|
269
|
+
const issue = await client.createIssue(
|
|
270
|
+
project,
|
|
271
|
+
type,
|
|
272
|
+
summary,
|
|
273
|
+
description,
|
|
274
|
+
priority,
|
|
275
|
+
labelList,
|
|
276
|
+
parent,
|
|
277
|
+
estimate,
|
|
278
|
+
fixVersionList,
|
|
279
|
+
affectedVersionList,
|
|
280
|
+
);
|
|
281
|
+
fmt.output(
|
|
282
|
+
json ? issue : fmt.formatSuccess(`Created issue ${fmt.issueLink(issue.key)}`),
|
|
283
|
+
json,
|
|
284
|
+
);
|
|
285
|
+
break;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
case 'update': {
|
|
289
|
+
const issueKey = subArgs[0];
|
|
290
|
+
if (!issueKey) {
|
|
291
|
+
error('Error: Issue key required. Usage: af jira update <issue-key> [options]');
|
|
292
|
+
return 1;
|
|
293
|
+
}
|
|
294
|
+
const updates: Parameters<typeof client.updateIssue>[1] = {};
|
|
295
|
+
if (options.summary !== undefined) updates.summary = options.summary;
|
|
296
|
+
if (options.description !== undefined) updates.description = options.description;
|
|
297
|
+
if (options.priority !== undefined) updates.priority = options.priority;
|
|
298
|
+
if (options.labels !== undefined) {
|
|
299
|
+
updates.labels = options.labels.split(',').map(l => l.trim());
|
|
300
|
+
}
|
|
301
|
+
if (options.estimate !== undefined) updates.originalEstimate = options.estimate;
|
|
302
|
+
if (options.remaining !== undefined) updates.remainingEstimate = options.remaining;
|
|
303
|
+
if (options['fix-version'] !== undefined) {
|
|
304
|
+
updates.fixVersions = options['fix-version']
|
|
305
|
+
? options['fix-version'].split(',').map(v => v.trim())
|
|
306
|
+
: [];
|
|
307
|
+
}
|
|
308
|
+
if (options['affected-version'] !== undefined) {
|
|
309
|
+
updates.affectedVersions = options['affected-version']
|
|
310
|
+
? options['affected-version'].split(',').map(v => v.trim())
|
|
311
|
+
: [];
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
if (Object.keys(updates).length === 0) {
|
|
315
|
+
error('Error: No update options provided');
|
|
316
|
+
console.error(
|
|
317
|
+
'Use --summary, --description, --priority, --labels, --estimate, --remaining, --fix-version, or --affected-version',
|
|
318
|
+
);
|
|
319
|
+
return 1;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
await client.updateIssue(issueKey, updates);
|
|
323
|
+
fmt.output(
|
|
324
|
+
json
|
|
325
|
+
? { success: true, key: issueKey }
|
|
326
|
+
: fmt.formatSuccess(`Updated issue ${fmt.issueLink(issueKey)}`),
|
|
327
|
+
json,
|
|
328
|
+
);
|
|
329
|
+
break;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
case 'delete': {
|
|
333
|
+
const issueKey = subArgs[0];
|
|
334
|
+
if (!issueKey) {
|
|
335
|
+
error('Error: Issue key required. Usage: af jira delete <issue-key>');
|
|
336
|
+
return 1;
|
|
337
|
+
}
|
|
338
|
+
await client.deleteIssue(issueKey);
|
|
339
|
+
fmt.output(
|
|
340
|
+
json
|
|
341
|
+
? { success: true, key: issueKey }
|
|
342
|
+
: fmt.formatSuccess(`Deleted issue ${fmt.issueLink(issueKey)}`),
|
|
343
|
+
json,
|
|
344
|
+
);
|
|
345
|
+
break;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
case 'comment': {
|
|
349
|
+
const issueKey = subArgs[0];
|
|
350
|
+
if (!issueKey) {
|
|
351
|
+
error(
|
|
352
|
+
'Error: Issue key required. Usage: af jira comment <issue-key> [--add "text"]',
|
|
353
|
+
);
|
|
354
|
+
return 1;
|
|
355
|
+
}
|
|
356
|
+
if (options.add) {
|
|
357
|
+
const comment = await client.addComment(issueKey, options.add);
|
|
358
|
+
fmt.output(
|
|
359
|
+
json
|
|
360
|
+
? comment
|
|
361
|
+
: fmt.formatSuccess(`Added comment to ${fmt.issueLink(issueKey)}`),
|
|
362
|
+
json,
|
|
363
|
+
);
|
|
364
|
+
} else {
|
|
365
|
+
const comments = await client.getComments(issueKey);
|
|
366
|
+
fmt.output(json ? comments : fmt.formatComments(issueKey, comments), json);
|
|
367
|
+
}
|
|
368
|
+
break;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
case 'transition': {
|
|
372
|
+
const issueKey = subArgs[0];
|
|
373
|
+
if (!issueKey || !options.to) {
|
|
374
|
+
error('Error: Issue key and --to required');
|
|
375
|
+
console.error('Usage: af jira transition <issue-key> --to "Status Name"');
|
|
376
|
+
return 1;
|
|
377
|
+
}
|
|
378
|
+
await client.transitionIssue(issueKey, options.to);
|
|
379
|
+
fmt.output(
|
|
380
|
+
json
|
|
381
|
+
? { success: true, key: issueKey, status: options.to }
|
|
382
|
+
: fmt.formatSuccess(
|
|
383
|
+
`Transitioned ${fmt.issueLink(issueKey)} to "${options.to}"`,
|
|
384
|
+
),
|
|
385
|
+
json,
|
|
386
|
+
);
|
|
387
|
+
break;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
case 'transitions': {
|
|
391
|
+
const issueKey = subArgs[0];
|
|
392
|
+
if (!issueKey) {
|
|
393
|
+
error('Error: Issue key required. Usage: af jira transitions <issue-key>');
|
|
394
|
+
return 1;
|
|
395
|
+
}
|
|
396
|
+
const { transitions } = await client.getTransitions(issueKey);
|
|
397
|
+
fmt.output(json ? transitions : fmt.formatTransitions(issueKey, transitions), json);
|
|
398
|
+
break;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
case 'assign': {
|
|
402
|
+
const issueKey = subArgs[0];
|
|
403
|
+
if (!issueKey || !options.to) {
|
|
404
|
+
error('Error: Issue key and --to required');
|
|
405
|
+
console.error('Usage: af jira assign <issue-key> --to user@email.com');
|
|
406
|
+
return 1;
|
|
407
|
+
}
|
|
408
|
+
if (options.to.toLowerCase() === 'none') {
|
|
409
|
+
await client.unassignIssue(issueKey);
|
|
410
|
+
fmt.output(
|
|
411
|
+
json
|
|
412
|
+
? { success: true, key: issueKey, assignee: null }
|
|
413
|
+
: fmt.formatSuccess(`Unassigned ${fmt.issueLink(issueKey)}`),
|
|
414
|
+
json,
|
|
415
|
+
);
|
|
416
|
+
} else {
|
|
417
|
+
await client.assignIssue(issueKey, options.to);
|
|
418
|
+
fmt.output(
|
|
419
|
+
json
|
|
420
|
+
? { success: true, key: issueKey, assignee: options.to }
|
|
421
|
+
: fmt.formatSuccess(
|
|
422
|
+
`Assigned ${fmt.issueLink(issueKey)} to ${options.to}`,
|
|
423
|
+
),
|
|
424
|
+
json,
|
|
425
|
+
);
|
|
426
|
+
}
|
|
427
|
+
break;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
case 'attach': {
|
|
431
|
+
const issueKey = subArgs[0];
|
|
432
|
+
const filePath = subArgs[1];
|
|
433
|
+
if (!issueKey || !filePath) {
|
|
434
|
+
error(
|
|
435
|
+
'Error: Issue key and file path required. Usage: af jira attach <issue-key> <file>',
|
|
436
|
+
);
|
|
437
|
+
return 1;
|
|
438
|
+
}
|
|
439
|
+
const attachments = await client.addAttachment(issueKey, filePath);
|
|
440
|
+
fmt.output(json ? attachments : fmt.formatAttachments(issueKey, attachments), json);
|
|
441
|
+
break;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
case 'projects': {
|
|
445
|
+
const projects = await client.getProjects();
|
|
446
|
+
fmt.output(json ? projects : fmt.formatProjects(projects), json);
|
|
447
|
+
break;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
case 'types': {
|
|
451
|
+
const projectKey = subArgs[0];
|
|
452
|
+
if (!projectKey) {
|
|
453
|
+
error('Error: Project key required. Usage: af jira types <project>');
|
|
454
|
+
return 1;
|
|
455
|
+
}
|
|
456
|
+
const types = await client.getIssueTypes(projectKey);
|
|
457
|
+
fmt.output(json ? types : fmt.formatIssueTypes(projectKey, types), json);
|
|
458
|
+
break;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
case 'versions': {
|
|
462
|
+
const projectKey = subArgs[0];
|
|
463
|
+
if (!projectKey) {
|
|
464
|
+
error('Error: Project key required. Usage: af jira versions <project>');
|
|
465
|
+
return 1;
|
|
466
|
+
}
|
|
467
|
+
const versions = await client.getProjectVersions(projectKey);
|
|
468
|
+
fmt.output(json ? versions : fmt.formatVersions(projectKey, versions), json);
|
|
469
|
+
break;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
case 'version': {
|
|
473
|
+
const versionId = subArgs[0];
|
|
474
|
+
if (!versionId) {
|
|
475
|
+
error('Error: Version ID required. Usage: af jira version <version-id>');
|
|
476
|
+
return 1;
|
|
477
|
+
}
|
|
478
|
+
const version = await client.getVersion(versionId);
|
|
479
|
+
fmt.output(json ? version : fmt.formatVersion(version), json);
|
|
480
|
+
break;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
case 'version-create': {
|
|
484
|
+
const { project, name, description } = options;
|
|
485
|
+
if (!project || !name) {
|
|
486
|
+
error('Error: --project and --name are required');
|
|
487
|
+
console.error('Usage: af jira version-create --project PROJ --name "v1.0.0"');
|
|
488
|
+
return 1;
|
|
489
|
+
}
|
|
490
|
+
const version = await client.createVersion(project, name, {
|
|
491
|
+
description,
|
|
492
|
+
startDate: options['start-date'],
|
|
493
|
+
releaseDate: options['release-date'],
|
|
494
|
+
released: options.released,
|
|
495
|
+
});
|
|
496
|
+
fmt.output(
|
|
497
|
+
json
|
|
498
|
+
? version
|
|
499
|
+
: fmt.formatSuccess(`Created version ${version.name} (ID: ${version.id})`),
|
|
500
|
+
json,
|
|
501
|
+
);
|
|
502
|
+
break;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
case 'version-update': {
|
|
506
|
+
const versionId = subArgs[0];
|
|
507
|
+
if (!versionId) {
|
|
508
|
+
error(
|
|
509
|
+
'Error: Version ID required. Usage: af jira version-update <version-id> [options]',
|
|
510
|
+
);
|
|
511
|
+
return 1;
|
|
512
|
+
}
|
|
513
|
+
const versionUpdates: Parameters<typeof client.updateVersion>[1] = {};
|
|
514
|
+
if (options.name !== undefined) versionUpdates.name = options.name;
|
|
515
|
+
if (options.description !== undefined)
|
|
516
|
+
versionUpdates.description = options.description;
|
|
517
|
+
if (options['start-date'] !== undefined)
|
|
518
|
+
versionUpdates.startDate = options['start-date'];
|
|
519
|
+
if (options['release-date'] !== undefined)
|
|
520
|
+
versionUpdates.releaseDate = options['release-date'];
|
|
521
|
+
if (options.released) versionUpdates.released = true;
|
|
522
|
+
if (options.unreleased) versionUpdates.released = false;
|
|
523
|
+
|
|
524
|
+
if (Object.keys(versionUpdates).length === 0) {
|
|
525
|
+
error('Error: No update options provided');
|
|
526
|
+
console.error(
|
|
527
|
+
'Use --name, --description, --start-date, --release-date, --released, or --unreleased',
|
|
528
|
+
);
|
|
529
|
+
return 1;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
const updatedVersion = await client.updateVersion(versionId, versionUpdates);
|
|
533
|
+
fmt.output(
|
|
534
|
+
json
|
|
535
|
+
? updatedVersion
|
|
536
|
+
: fmt.formatSuccess(`Updated version ${updatedVersion.name}`),
|
|
537
|
+
json,
|
|
538
|
+
);
|
|
539
|
+
break;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
case 'version-delete': {
|
|
543
|
+
const versionId = subArgs[0];
|
|
544
|
+
if (!versionId) {
|
|
545
|
+
error('Error: Version ID required. Usage: af jira version-delete <version-id>');
|
|
546
|
+
return 1;
|
|
547
|
+
}
|
|
548
|
+
await client.deleteVersion(versionId, {
|
|
549
|
+
moveFixIssuesTo: options['move-fix-issues-to'],
|
|
550
|
+
moveAffectedIssuesTo: options['move-affected-issues-to'],
|
|
551
|
+
});
|
|
552
|
+
fmt.output(
|
|
553
|
+
json
|
|
554
|
+
? { success: true, id: versionId }
|
|
555
|
+
: fmt.formatSuccess(`Deleted version ${versionId}`),
|
|
556
|
+
json,
|
|
557
|
+
);
|
|
558
|
+
break;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
default:
|
|
562
|
+
error(`Unknown jira command: ${subcommand}`);
|
|
563
|
+
console.error("Run 'af jira --help' for usage information");
|
|
564
|
+
return 1;
|
|
565
|
+
}
|
|
566
|
+
} catch (err) {
|
|
567
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
568
|
+
if (json) {
|
|
569
|
+
console.error(JSON.stringify({ error: message }));
|
|
570
|
+
} else {
|
|
571
|
+
error(`Error: ${message}`);
|
|
572
|
+
}
|
|
573
|
+
return 1;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
return 0;
|
|
577
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { header, listItem, section } from '../utils/output.ts';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Third-party dependencies bundled in the compiled binary.
|
|
5
|
+
* These are runtime dependencies from package.json.
|
|
6
|
+
*/
|
|
7
|
+
const THIRD_PARTY_LICENSES: Array<{ name: string; license: string; url: string }> = [
|
|
8
|
+
{ name: 'ink', license: 'MIT', url: 'https://github.com/vadimdemedes/ink' },
|
|
9
|
+
{ name: 'react', license: 'MIT', url: 'https://github.com/facebook/react' },
|
|
10
|
+
{ name: 'react-devtools-core', license: 'MIT', url: 'https://github.com/facebook/react' },
|
|
11
|
+
];
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Handle the 'licenses' command.
|
|
15
|
+
* Displays copyright and license information.
|
|
16
|
+
*
|
|
17
|
+
* @returns Exit code (always 0)
|
|
18
|
+
*/
|
|
19
|
+
export async function handleLicenses(): Promise<number> {
|
|
20
|
+
header('Artifex');
|
|
21
|
+
console.log('(c) Avant Media LTD. Proprietary and confidential.\n');
|
|
22
|
+
console.log('This software is the exclusive property of Avant Media LTD.');
|
|
23
|
+
console.log('Unauthorized copying, modification, or distribution is prohibited.');
|
|
24
|
+
|
|
25
|
+
section('Third-Party Licenses');
|
|
26
|
+
console.log('This software includes the following open source components:\n');
|
|
27
|
+
for (const dep of THIRD_PARTY_LICENSES) {
|
|
28
|
+
listItem(`${dep.name} (${dep.license}) - ${dep.url}`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return 0;
|
|
32
|
+
}
|
package/commands/npm.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { getOutdatedPackages, upgradeAllPackages } from '../npm-upgrade.ts';
|
|
2
|
+
import { info, success, error, header, listItem } from '../utils/output.ts';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Handle the 'npm upgrade' command.
|
|
6
|
+
* Checks for outdated packages and upgrades them all.
|
|
7
|
+
*
|
|
8
|
+
* @returns Exit code (0 for success, 1 for error)
|
|
9
|
+
*/
|
|
10
|
+
export async function handleNpmUpgrade(): Promise<number> {
|
|
11
|
+
try {
|
|
12
|
+
info('Checking for outdated packages...');
|
|
13
|
+
|
|
14
|
+
const outdatedPackages = await getOutdatedPackages();
|
|
15
|
+
|
|
16
|
+
if (outdatedPackages.length === 0) {
|
|
17
|
+
success('All packages are up to date!');
|
|
18
|
+
return 0;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
console.log(`\nFound ${outdatedPackages.length} package(s) to upgrade:`);
|
|
22
|
+
outdatedPackages.forEach(pkg => listItem(pkg));
|
|
23
|
+
console.log('');
|
|
24
|
+
|
|
25
|
+
const results = await upgradeAllPackages(outdatedPackages);
|
|
26
|
+
|
|
27
|
+
// Display summary
|
|
28
|
+
const successful = results.filter(r => r.success);
|
|
29
|
+
const failed = results.filter(r => !r.success);
|
|
30
|
+
|
|
31
|
+
header('Upgrade Summary');
|
|
32
|
+
console.log('='.repeat(50));
|
|
33
|
+
console.log(`Successfully upgraded: ${successful.length} package(s)`);
|
|
34
|
+
|
|
35
|
+
if (successful.length > 0) {
|
|
36
|
+
successful.forEach(r => listItem(r.package, '✓'));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (failed.length > 0) {
|
|
40
|
+
console.log(`\nFailed to upgrade: ${failed.length} package(s)`);
|
|
41
|
+
failed.forEach(r => listItem(`${r.package}: ${r.error}`, '✗'));
|
|
42
|
+
return 1;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
success('\nAll packages upgraded successfully!');
|
|
46
|
+
return 0;
|
|
47
|
+
} catch (err) {
|
|
48
|
+
if (err instanceof Error) {
|
|
49
|
+
error(`Error: ${err.message}`);
|
|
50
|
+
} else {
|
|
51
|
+
error('An unexpected error occurred');
|
|
52
|
+
}
|
|
53
|
+
return 1;
|
|
54
|
+
}
|
|
55
|
+
}
|