open source is social v-it.org
at main 92 lines 3.5 kB view raw
1// SPDX-License-Identifier: MIT 2// Copyright (c) 2026 sol pbc 3 4import { existsSync } from 'node:fs'; 5import { resolve } from 'node:path'; 6import { execFileSync } from 'node:child_process'; 7import { parseGitUrl, toBeacon, beaconToHttps } from '../lib/beacon.js'; 8import { requireNotAgent } from '../lib/agent.js'; 9import { which } from '../lib/compat.js'; 10import { mark, name } from '../lib/brand.js'; 11 12export default function register(program) { 13 program 14 .command('adopt') 15 .argument('<beacon>', 'Beacon URI, git URL, or slug to adopt (e.g. vit:github.com/org/repo)') 16 .argument('[name]', 'Local directory name (defaults to repo name)') 17 .description('Fork or clone a project') 18 .option('-v, --verbose', 'Show step-by-step details') 19 .action(async (beacon, targetName, opts) => { 20 try { 21 const gate = requireNotAgent(); 22 if (!gate.ok) { 23 console.error(`${name} adopt must be run by a human. run it in your own terminal.`); 24 process.exitCode = 1; 25 return; 26 } 27 28 const { verbose } = opts; 29 30 // resolve beacon 31 if (verbose) console.log(`[verbose] resolving beacon: ${beacon}`); 32 const httpsUrl = beaconToHttps(beacon); 33 const parsed = parseGitUrl(httpsUrl); 34 const beaconUri = 'vit:' + toBeacon(httpsUrl); 35 if (verbose) console.log(`[verbose] beacon: ${beaconUri}`); 36 if (verbose) console.log(`[verbose] https: ${httpsUrl}`); 37 38 // determine directory name 39 const dirName = targetName || parsed.repo; 40 const dirPath = resolve(dirName); 41 if (verbose) console.log(`[verbose] target directory: ${dirPath}`); 42 43 // fail fast if directory exists 44 if (existsSync(dirPath)) { 45 console.error(`Directory already exists: ${dirName}`); 46 process.exitCode = 1; 47 return; 48 } 49 50 // detect gh + github host 51 const ghPath = which('gh'); 52 const isGitHub = parsed.host === 'github.com'; 53 if (verbose) console.log(`[verbose] gh available: ${ghPath ? 'yes' : 'no'}, github host: ${isGitHub ? 'yes' : 'no'}`); 54 55 if (ghPath && isGitHub) { 56 if (verbose) console.log(`[verbose] gh found at ${ghPath}, forking via gh`); 57 try { 58 execFileSync('gh', ['repo', 'fork', httpsUrl, '--clone', '--', dirName], { 59 encoding: 'utf-8', 60 stdio: ['pipe', 'pipe', 'pipe'], 61 }); 62 } catch (err) { 63 console.error(`Fork failed: ${(err.stderr || err.message || '').trim()}`); 64 process.exitCode = 1; 65 return; 66 } 67 } else { 68 if (verbose) console.log(`[verbose] cloning via git`); 69 try { 70 execFileSync('git', ['clone', httpsUrl, dirName], { 71 encoding: 'utf-8', 72 stdio: ['pipe', 'pipe', 'pipe'], 73 }); 74 } catch (err) { 75 console.error(`Clone failed: ${(err.stderr || err.message || '').trim()}`); 76 process.exitCode = 1; 77 return; 78 } 79 } 80 81 // success output 82 console.log(`${mark} beacon: ${beaconUri}`); 83 console.log(`${mark} directory: ${dirName}`); 84 console.log(`run: cd ${dirName}`); 85 console.log(''); 86 console.log(`next: start your agent and ask it to run '${name} init'`); 87 } catch (err) { 88 console.error(err instanceof Error ? err.message : String(err)); 89 process.exitCode = 1; 90 } 91 }); 92}