@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/bun.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { getBunOutdatedPackages, bunUpgradeAllPackages } from '../bun-upgrade.ts';
|
|
2
|
+
import { info, success, error, header, listItem } from '../utils/output.ts';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Handle the 'bun upgrade' command.
|
|
6
|
+
* Checks for outdated packages and upgrades them all using Bun.
|
|
7
|
+
*
|
|
8
|
+
* @returns Exit code (0 for success, 1 for error)
|
|
9
|
+
*/
|
|
10
|
+
export async function handleBunUpgrade(): Promise<number> {
|
|
11
|
+
try {
|
|
12
|
+
info('Checking for outdated packages...');
|
|
13
|
+
|
|
14
|
+
const outdatedPackages = await getBunOutdatedPackages();
|
|
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 bunUpgradeAllPackages(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
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { error, info } from '../utils/output.ts';
|
|
2
|
+
import { getActiveChanges } from '../utils/proposal.ts';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Handle the 'changes' command.
|
|
6
|
+
* Lists all OpenSpec changes with their titles and task progress.
|
|
7
|
+
*
|
|
8
|
+
* @param hasArgs - Whether any arguments were provided to the command
|
|
9
|
+
* @returns Exit code (0 for success, 1 for error)
|
|
10
|
+
*/
|
|
11
|
+
export async function handleChanges(hasArgs: boolean): Promise<number> {
|
|
12
|
+
// Reject if arguments were provided
|
|
13
|
+
if (hasArgs) {
|
|
14
|
+
error('Error: changes command does not accept arguments');
|
|
15
|
+
console.error('Usage: af changes');
|
|
16
|
+
return 1;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const changes = getActiveChanges();
|
|
20
|
+
|
|
21
|
+
if (changes.length === 0) {
|
|
22
|
+
info('No active changes');
|
|
23
|
+
return 0;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
console.log('Changes:');
|
|
27
|
+
|
|
28
|
+
for (const change of changes) {
|
|
29
|
+
const displayName = change.title ? `${change.title} (${change.id})` : change.id;
|
|
30
|
+
const taskProgress = `${change.completedTasks}/${change.totalTasks} tasks`;
|
|
31
|
+
console.log(` ${displayName} ${taskProgress}`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return 0;
|
|
35
|
+
}
|
package/commands/e2e.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { runE2eTests } from '../scripts/e2e_tests.ts';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Handle the 'e2e' command.
|
|
5
|
+
* Runs e2e tests directly with any provided arguments.
|
|
6
|
+
*
|
|
7
|
+
* @param args - Arguments to pass through to the test runner
|
|
8
|
+
* @returns Exit code from the test runner
|
|
9
|
+
*/
|
|
10
|
+
export async function handleE2e(args: string[]): Promise<number> {
|
|
11
|
+
return await runE2eTests(args);
|
|
12
|
+
}
|
package/commands/help.ts
ADDED
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import { header, section, listItem, error } from '../utils/output.ts';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Help content for all commands
|
|
5
|
+
*/
|
|
6
|
+
const HELP_CONTENT: Record<string, { description: string; usage: string; examples?: string[] }> = {
|
|
7
|
+
npm: {
|
|
8
|
+
description: 'Manage npm packages',
|
|
9
|
+
usage: 'af npm upgrade',
|
|
10
|
+
examples: ['af npm upgrade # Upgrade all outdated npm packages'],
|
|
11
|
+
},
|
|
12
|
+
bun: {
|
|
13
|
+
description: 'Manage Bun packages',
|
|
14
|
+
usage: 'af bun upgrade',
|
|
15
|
+
examples: ['af bun upgrade # Upgrade all outdated Bun packages'],
|
|
16
|
+
},
|
|
17
|
+
spec: {
|
|
18
|
+
description: 'Manage OpenSpec changes',
|
|
19
|
+
usage: 'af spec <subcommand> [args]',
|
|
20
|
+
examples: [
|
|
21
|
+
'af spec propose "Add new feature" # Create a new proposal',
|
|
22
|
+
'af spec apply my-change-id # Apply a change',
|
|
23
|
+
'af spec apply # Interactively select a change',
|
|
24
|
+
'af spec archive my-change-id # Archive a change',
|
|
25
|
+
'af spec archive # Interactively select a spec',
|
|
26
|
+
],
|
|
27
|
+
},
|
|
28
|
+
propose: {
|
|
29
|
+
description: 'Create a new OpenSpec proposal (shorthand for "spec propose")',
|
|
30
|
+
usage: 'af propose <proposal-text>',
|
|
31
|
+
examples: ['af propose "Add dark mode support"'],
|
|
32
|
+
},
|
|
33
|
+
apply: {
|
|
34
|
+
description: 'Apply an approved OpenSpec change (shorthand for "spec apply")',
|
|
35
|
+
usage: 'af apply [change-id]',
|
|
36
|
+
examples: [
|
|
37
|
+
'af apply my-change-id # Apply a specific change',
|
|
38
|
+
'af apply # Interactively select a change',
|
|
39
|
+
],
|
|
40
|
+
},
|
|
41
|
+
archive: {
|
|
42
|
+
description: 'Archive an OpenSpec change (shorthand for "spec archive")',
|
|
43
|
+
usage: 'af archive [spec-id]',
|
|
44
|
+
examples: [
|
|
45
|
+
'af archive my-change-id # Archive a specific spec',
|
|
46
|
+
'af archive # Interactively select a spec',
|
|
47
|
+
],
|
|
48
|
+
},
|
|
49
|
+
commit: {
|
|
50
|
+
description: 'Commit changes with message and optional trailers',
|
|
51
|
+
usage: 'af commit <subcommand> [args]',
|
|
52
|
+
examples: [
|
|
53
|
+
'af commit save "Fix bug" # Commit with message',
|
|
54
|
+
'af commit save "Fix bug" Issue=PROJ-123 # Commit with Issue trailer',
|
|
55
|
+
'af commit save "Add feature" Issue=PROJ-456 Reviewed-by=alice',
|
|
56
|
+
'af commit my-change-id # Commit with "Apply: <title>"',
|
|
57
|
+
'af commit apply my-change-id # Same as above',
|
|
58
|
+
],
|
|
59
|
+
},
|
|
60
|
+
changes: {
|
|
61
|
+
description: 'List all OpenSpec changes',
|
|
62
|
+
usage: 'af changes',
|
|
63
|
+
examples: ['af changes # Show all active OpenSpec changes'],
|
|
64
|
+
},
|
|
65
|
+
e2e: {
|
|
66
|
+
description: 'Run E2E tests in a fresh Docker environment',
|
|
67
|
+
usage: 'af e2e [args...]',
|
|
68
|
+
examples: [
|
|
69
|
+
'af e2e # Run all tests with defaults',
|
|
70
|
+
'af e2e npm run e2e -- --grep "booking" # Custom command',
|
|
71
|
+
],
|
|
72
|
+
},
|
|
73
|
+
'stop-hook': {
|
|
74
|
+
description: 'Conditionally run e2e tests if relevant files changed',
|
|
75
|
+
usage: 'af stop-hook',
|
|
76
|
+
examples: ['af stop-hook # Run e2e only if source files changed (not just openspec/)'],
|
|
77
|
+
},
|
|
78
|
+
todo: {
|
|
79
|
+
description: 'Show all TODO items from active OpenSpec changes',
|
|
80
|
+
usage: 'af todo',
|
|
81
|
+
examples: ['af todo # Display all tasks from active changes'],
|
|
82
|
+
},
|
|
83
|
+
watch: {
|
|
84
|
+
description:
|
|
85
|
+
'Continuously monitor and display TODO items from active changes with idle indicator',
|
|
86
|
+
usage: 'af watch',
|
|
87
|
+
examples: [
|
|
88
|
+
'af watch # Start watch mode for real-time TODO updates',
|
|
89
|
+
' # Shows idle warning after 60 seconds of inactivity',
|
|
90
|
+
],
|
|
91
|
+
},
|
|
92
|
+
versions: {
|
|
93
|
+
description: 'Manage version worktrees',
|
|
94
|
+
usage: 'af versions <subcommand>',
|
|
95
|
+
examples: [
|
|
96
|
+
'af versions reset # Reset all version worktrees to HEAD',
|
|
97
|
+
'af versions push # Force push all version worktrees',
|
|
98
|
+
],
|
|
99
|
+
},
|
|
100
|
+
jira: {
|
|
101
|
+
description: 'Manage Jira issues from the command line',
|
|
102
|
+
usage: 'af jira <subcommand> [args] [options]',
|
|
103
|
+
examples: [
|
|
104
|
+
'af jira get PROJ-123 # Get issue details',
|
|
105
|
+
'af jira list PROJ --limit 20 # List project issues',
|
|
106
|
+
'af jira search "status = Open" # Search with JQL',
|
|
107
|
+
'af jira create --project PROJ --type Bug --summary "Title"',
|
|
108
|
+
'af jira projects # List all projects',
|
|
109
|
+
],
|
|
110
|
+
},
|
|
111
|
+
licenses: {
|
|
112
|
+
description: 'Show license and copyright information',
|
|
113
|
+
usage: 'af licenses',
|
|
114
|
+
examples: ['af licenses # Display copyright and license details'],
|
|
115
|
+
},
|
|
116
|
+
scaffold: {
|
|
117
|
+
description: 'Generate project files from templates',
|
|
118
|
+
usage: 'af scaffold <subcommand>',
|
|
119
|
+
examples: ['af scaffold test-compose # Generate docker-compose.test.yml for E2E testing'],
|
|
120
|
+
},
|
|
121
|
+
setup: {
|
|
122
|
+
description: 'Copy configuration files to Claude and OpenCode directories',
|
|
123
|
+
usage: 'af setup [--list] [--force]',
|
|
124
|
+
examples: [
|
|
125
|
+
'af setup # Copy to ~/.claude/ and ~/.config/opencode/',
|
|
126
|
+
'af setup --list # Preview files without copying',
|
|
127
|
+
'af setup --force # Overwrite existing files',
|
|
128
|
+
],
|
|
129
|
+
},
|
|
130
|
+
'install-extension': {
|
|
131
|
+
description: 'Install the OpenSpec Tasks VSCode extension',
|
|
132
|
+
usage: 'af install-extension',
|
|
133
|
+
examples: ['af install-extension # Install the bundled VSCode extension'],
|
|
134
|
+
},
|
|
135
|
+
worktree: {
|
|
136
|
+
description: 'Manage git worktrees',
|
|
137
|
+
usage: 'af worktree <subcommand> [args]',
|
|
138
|
+
examples: [
|
|
139
|
+
'af worktree new feature-x # Create worktree with new branch',
|
|
140
|
+
'af worktree new hotfix --detach # Create worktree with detached HEAD',
|
|
141
|
+
'af worktree reset # Reset current worktree to HEAD',
|
|
142
|
+
'af worktree reset feature-x # Reset named worktree to HEAD',
|
|
143
|
+
],
|
|
144
|
+
},
|
|
145
|
+
help: {
|
|
146
|
+
description: 'Display help information',
|
|
147
|
+
usage: 'af help [command]',
|
|
148
|
+
examples: ['af help # Show all commands', 'af help npm # Show npm help'],
|
|
149
|
+
},
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Display general help showing all available commands.
|
|
154
|
+
*/
|
|
155
|
+
function showGeneralHelp(): void {
|
|
156
|
+
header('af - Development utility CLI');
|
|
157
|
+
|
|
158
|
+
section('USAGE');
|
|
159
|
+
console.log(' af <command> [options]');
|
|
160
|
+
|
|
161
|
+
section('COMMANDS');
|
|
162
|
+
listItem('npm upgrade Upgrade all outdated npm packages');
|
|
163
|
+
listItem('bun upgrade Upgrade all outdated Bun packages');
|
|
164
|
+
listItem('spec propose <text> Create a new OpenSpec proposal');
|
|
165
|
+
listItem('spec apply [id] Apply an approved OpenSpec change');
|
|
166
|
+
listItem('spec archive [id] Archive an OpenSpec change');
|
|
167
|
+
listItem('propose <text> Shorthand for "spec propose"');
|
|
168
|
+
listItem('apply [id] Shorthand for "spec apply"');
|
|
169
|
+
listItem('archive [id] Shorthand for "spec archive"');
|
|
170
|
+
listItem('commit save <msg> Commit all changes with message and trailers');
|
|
171
|
+
listItem('commit [apply] [id] Commit all changes with "Apply: <title>"');
|
|
172
|
+
listItem('changes List all OpenSpec changes');
|
|
173
|
+
listItem('e2e [args...] Run E2E tests in Docker');
|
|
174
|
+
listItem('stop-hook Run e2e only if relevant files changed');
|
|
175
|
+
listItem('todo Show all TODO items from active changes');
|
|
176
|
+
listItem('watch Continuously show updated TODO items (with idle indicator)');
|
|
177
|
+
listItem('versions reset Reset version worktrees to HEAD');
|
|
178
|
+
listItem('versions push Force push version worktrees');
|
|
179
|
+
listItem('jira <subcommand> Manage Jira issues (get, list, create, etc.)');
|
|
180
|
+
listItem('licenses Show license and copyright information');
|
|
181
|
+
listItem('scaffold <subcommand> Generate project files from templates');
|
|
182
|
+
listItem('setup Copy Claude + OpenCode config files');
|
|
183
|
+
listItem('install-extension Install VSCode extension');
|
|
184
|
+
listItem('worktree new <name> Create new worktree with env files');
|
|
185
|
+
listItem('worktree reset [name] Reset worktree to HEAD');
|
|
186
|
+
listItem('help [command] Show help for a command');
|
|
187
|
+
|
|
188
|
+
section('OPTIONS');
|
|
189
|
+
listItem('--help, -h Show help information');
|
|
190
|
+
|
|
191
|
+
console.log("\nRun 'af help <command>' for more information on a command.");
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Display help for a specific command.
|
|
196
|
+
*
|
|
197
|
+
* @param command - The command to show help for
|
|
198
|
+
*/
|
|
199
|
+
function showCommandHelp(command: string): void {
|
|
200
|
+
const helpInfo = HELP_CONTENT[command];
|
|
201
|
+
|
|
202
|
+
if (!helpInfo) {
|
|
203
|
+
error(`Unknown command: ${command}`);
|
|
204
|
+
console.log("\nRun 'af help' to see all available commands.");
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
header(`af ${command}`);
|
|
209
|
+
|
|
210
|
+
section('DESCRIPTION');
|
|
211
|
+
console.log(` ${helpInfo.description}`);
|
|
212
|
+
|
|
213
|
+
section('USAGE');
|
|
214
|
+
console.log(` ${helpInfo.usage}`);
|
|
215
|
+
|
|
216
|
+
if (helpInfo.examples && helpInfo.examples.length > 0) {
|
|
217
|
+
section('EXAMPLES');
|
|
218
|
+
helpInfo.examples.forEach(example => console.log(` ${example}`));
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Handle the 'help' command.
|
|
224
|
+
* Displays general help or command-specific help.
|
|
225
|
+
*
|
|
226
|
+
* @param command - Optional command to show help for
|
|
227
|
+
* @returns Exit code (always 0)
|
|
228
|
+
*/
|
|
229
|
+
export async function handleHelp(command?: string): Promise<number> {
|
|
230
|
+
if (command) {
|
|
231
|
+
showCommandHelp(command);
|
|
232
|
+
} else {
|
|
233
|
+
showGeneralHelp();
|
|
234
|
+
}
|
|
235
|
+
return 0;
|
|
236
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Install extension command handler.
|
|
3
|
+
* Installs the bundled VSCode extension from the embedded VSIX file.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { spawn } from 'node:child_process';
|
|
7
|
+
import { copyFileSync, unlinkSync, mkdtempSync, writeFileSync } from 'node:fs';
|
|
8
|
+
import { tmpdir } from 'node:os';
|
|
9
|
+
import { join, dirname } from 'node:path';
|
|
10
|
+
import { EXTENSION_FILE, isCompiled } from '../generated/setup-manifest.ts';
|
|
11
|
+
import { success, error, info } from '../utils/output.ts';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Check if VSCode CLI is available.
|
|
15
|
+
*/
|
|
16
|
+
async function isCodeCliAvailable(): Promise<boolean> {
|
|
17
|
+
return new Promise(resolve => {
|
|
18
|
+
const child = spawn('code', ['--version'], { stdio: 'pipe' });
|
|
19
|
+
child.on('error', () => resolve(false));
|
|
20
|
+
child.on('close', code => resolve(code === 0));
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Install extension using VSCode CLI.
|
|
26
|
+
*/
|
|
27
|
+
async function installExtension(vsixPath: string): Promise<{ success: boolean; output: string }> {
|
|
28
|
+
return new Promise(resolve => {
|
|
29
|
+
const child = spawn('code', ['--install-extension', vsixPath], {
|
|
30
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
let stdout = '';
|
|
34
|
+
let stderr = '';
|
|
35
|
+
|
|
36
|
+
child.stdout?.on('data', data => {
|
|
37
|
+
stdout += data.toString();
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
child.stderr?.on('data', data => {
|
|
41
|
+
stderr += data.toString();
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
child.on('error', err => {
|
|
45
|
+
resolve({ success: false, output: err.message });
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
child.on('close', code => {
|
|
49
|
+
if (code === 0) {
|
|
50
|
+
resolve({ success: true, output: stdout });
|
|
51
|
+
} else {
|
|
52
|
+
resolve({ success: false, output: stderr || stdout });
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Handle the 'install-extension' command.
|
|
60
|
+
* Installs the bundled VSCode extension.
|
|
61
|
+
*
|
|
62
|
+
* @returns Exit code (0 for success, 1 for error)
|
|
63
|
+
*/
|
|
64
|
+
export async function handleInstallExtension(): Promise<number> {
|
|
65
|
+
// Check if extension file is available
|
|
66
|
+
if (!EXTENSION_FILE) {
|
|
67
|
+
error('Error: No extension file bundled');
|
|
68
|
+
console.error('The VSCode extension file is not available.');
|
|
69
|
+
return 1;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Check if VSCode CLI is available
|
|
73
|
+
info('Checking for VSCode CLI...');
|
|
74
|
+
const codeAvailable = await isCodeCliAvailable();
|
|
75
|
+
|
|
76
|
+
if (!codeAvailable) {
|
|
77
|
+
error('Error: VSCode CLI not found');
|
|
78
|
+
console.error('The "code" command is not available. Please ensure VSCode is installed');
|
|
79
|
+
console.error('and the "code" command is in your PATH.');
|
|
80
|
+
console.error('');
|
|
81
|
+
console.error('To add the "code" command to PATH:');
|
|
82
|
+
console.error(' 1. Open VSCode');
|
|
83
|
+
console.error(' 2. Press Cmd+Shift+P (macOS) or Ctrl+Shift+P (Windows/Linux)');
|
|
84
|
+
console.error(' 3. Type "Shell Command: Install \'code\' command in PATH"');
|
|
85
|
+
return 1;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Create temp directory and copy VSIX file
|
|
89
|
+
info(`Installing ${EXTENSION_FILE.name}...`);
|
|
90
|
+
const tempDir = mkdtempSync(join(tmpdir(), 'artifex-extension-'));
|
|
91
|
+
const tempVsixPath = join(tempDir, EXTENSION_FILE.name);
|
|
92
|
+
|
|
93
|
+
try {
|
|
94
|
+
// Copy embedded file to temp location
|
|
95
|
+
if (isCompiled()) {
|
|
96
|
+
// In compiled mode, use Bun.file() to read from $bunfs virtual filesystem
|
|
97
|
+
const content = await Bun.file(EXTENSION_FILE.embeddedPath).bytes();
|
|
98
|
+
writeFileSync(tempVsixPath, content);
|
|
99
|
+
} else {
|
|
100
|
+
// In development mode, copy from source directory
|
|
101
|
+
const sourcePath = join(
|
|
102
|
+
dirname(import.meta.dirname),
|
|
103
|
+
'vscode-extension',
|
|
104
|
+
EXTENSION_FILE.name,
|
|
105
|
+
);
|
|
106
|
+
copyFileSync(sourcePath, tempVsixPath);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Install the extension
|
|
110
|
+
const result = await installExtension(tempVsixPath);
|
|
111
|
+
|
|
112
|
+
if (result.success) {
|
|
113
|
+
success('Extension installed successfully!');
|
|
114
|
+
if (result.output.trim()) {
|
|
115
|
+
console.log(result.output.trim());
|
|
116
|
+
}
|
|
117
|
+
return 0;
|
|
118
|
+
} else {
|
|
119
|
+
error('Error: Extension installation failed');
|
|
120
|
+
if (result.output.trim()) {
|
|
121
|
+
console.error(result.output.trim());
|
|
122
|
+
}
|
|
123
|
+
return 1;
|
|
124
|
+
}
|
|
125
|
+
} finally {
|
|
126
|
+
// Clean up temp file
|
|
127
|
+
try {
|
|
128
|
+
unlinkSync(tempVsixPath);
|
|
129
|
+
} catch {
|
|
130
|
+
// Ignore cleanup errors
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|