@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.
Files changed (57) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +539 -0
  3. package/af +2 -0
  4. package/bun-upgrade.ts +130 -0
  5. package/commands/bun.ts +55 -0
  6. package/commands/changes.ts +35 -0
  7. package/commands/e2e.ts +12 -0
  8. package/commands/help.ts +236 -0
  9. package/commands/install-extension.ts +133 -0
  10. package/commands/jira.ts +577 -0
  11. package/commands/licenses.ts +32 -0
  12. package/commands/npm.ts +55 -0
  13. package/commands/scaffold.ts +105 -0
  14. package/commands/setup.tsx +156 -0
  15. package/commands/spec.ts +405 -0
  16. package/commands/stop-hook.ts +90 -0
  17. package/commands/todo.ts +208 -0
  18. package/commands/versions.ts +150 -0
  19. package/commands/watch.ts +344 -0
  20. package/commands/worktree.ts +424 -0
  21. package/components/change-select.tsx +71 -0
  22. package/components/confirm.tsx +41 -0
  23. package/components/file-conflict.tsx +52 -0
  24. package/components/input.tsx +53 -0
  25. package/components/layout.tsx +70 -0
  26. package/components/messages.tsx +48 -0
  27. package/components/progress.tsx +71 -0
  28. package/components/select.tsx +90 -0
  29. package/components/status-display.tsx +74 -0
  30. package/components/table.tsx +79 -0
  31. package/generated/setup-manifest.ts +67 -0
  32. package/git-worktree.ts +184 -0
  33. package/main.ts +12 -0
  34. package/npm-upgrade.ts +117 -0
  35. package/package.json +83 -0
  36. package/resources/copy-prompt-reporter.ts +443 -0
  37. package/router.ts +220 -0
  38. package/setup/.claude/commands/commit-work.md +47 -0
  39. package/setup/.claude/commands/complete-work.md +34 -0
  40. package/setup/.claude/commands/e2e.md +29 -0
  41. package/setup/.claude/commands/start-work.md +51 -0
  42. package/setup/.claude/skills/pm/SKILL.md +294 -0
  43. package/setup/.claude/skills/pm/templates/api-endpoint.md +69 -0
  44. package/setup/.claude/skills/pm/templates/bug-fix.md +77 -0
  45. package/setup/.claude/skills/pm/templates/feature.md +87 -0
  46. package/setup/.claude/skills/pm/templates/ui-component.md +78 -0
  47. package/utils/change-select-render.tsx +44 -0
  48. package/utils/claude.ts +9 -0
  49. package/utils/config.ts +58 -0
  50. package/utils/env.ts +53 -0
  51. package/utils/git.ts +120 -0
  52. package/utils/ink-render.tsx +50 -0
  53. package/utils/openspec.ts +54 -0
  54. package/utils/output.ts +104 -0
  55. package/utils/proposal.ts +160 -0
  56. package/utils/resources.ts +64 -0
  57. package/utils/setup-files.ts +230 -0
@@ -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
+ }
@@ -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
+ }