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