open source is social v-it.org
1// SPDX-License-Identifier: MIT
2// Copyright (c) 2026 sol pbc
3
4import { execFileSync } from 'node:child_process';
5import { existsSync, readFileSync, writeFileSync } from 'node:fs';
6import { join, resolve } from 'node:path';
7import { which } from '../lib/compat.js';
8import { mark, name } from '../lib/brand.js';
9import { errorMessage, formatError } from '../lib/error-format.js';
10
11export default function register(program) {
12 program
13 .command('hack')
14 .description('Fork and install vit from source')
15 .option('--from <repo>', 'Clone an existing fork instead of creating a new one (e.g. user/vit)')
16 .action(async (opts) => {
17 try {
18 const ghPath = which('gh');
19 if (!opts.from && !ghPath) {
20 console.error(`${name} hack requires gh (GitHub CLI). Install it from https://cli.github.com`);
21 process.exitCode = 1;
22 return;
23 }
24
25 const gitPath = which('git');
26 if (opts.from && !gitPath) {
27 console.error('missing required tool: git');
28 process.exitCode = 1;
29 return;
30 }
31
32 const repo = opts.from || 'solpbc/vit';
33 const dirName = repo.split('/').pop();
34 const dirPath = resolve(process.cwd(), dirName);
35 const cloned = !existsSync(dirPath);
36
37 if (!cloned) {
38 console.log(`${mark} ${dirName}/ already exists, skipping clone`);
39 } else if (opts.from) {
40 execFileSync('git', ['clone', 'https://github.com/' + opts.from + '.git', dirName], { stdio: 'inherit' });
41 } else {
42 execFileSync('gh', ['repo', 'fork', 'solpbc/vit', '--clone'], { stdio: 'inherit' });
43 }
44
45 process.chdir(dirName);
46
47 const bunPath = which('bun');
48 if (!bunPath) {
49 console.error('missing required tool: bun');
50 process.exitCode = 1;
51 return;
52 }
53
54 execFileSync('bun', ['install'], { stdio: 'inherit' });
55 execFileSync('node', [join(process.cwd(), 'bin', 'vit.js'), 'link'], { stdio: 'inherit' });
56
57 if (!opts.from) {
58 const remote = execFileSync(
59 'gh',
60 ['repo', 'view', '--json', 'nameWithOwner', '-q', '.nameWithOwner'],
61 { encoding: 'utf-8' }
62 ).trim();
63 const hackPath = join(process.cwd(), 'hack');
64 if (existsSync(hackPath)) {
65 const current = readFileSync(hackPath, 'utf-8');
66 writeFileSync(hackPath, current.replace('SELF="solpbc/vit"', `SELF="${remote}"`));
67 }
68 }
69
70 if (opts.from && cloned) {
71 try {
72 execFileSync('git', ['remote', 'add', 'upstream', 'https://github.com/solpbc/vit.git'], { stdio: 'inherit' });
73 } catch (err) {
74 console.warn(`warning: failed to add upstream remote: ${errorMessage(err)}`);
75 }
76 }
77
78 console.log('');
79 console.log(`${mark} ${name} installed from source`);
80 console.log(`run: cd ${dirName}`);
81 } catch (err) {
82 console.error(formatError(err, { verbose: false }));
83 process.exitCode = 1;
84 }
85 });
86}