Something went wrong. Try again.
open source is social v-it.org
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286// SPDX-License-Identifier: MIT// Copyright (c) 2026 sol pbc
import { existsSync, writeFileSync } from 'node:fs';import { execSync } from 'node:child_process';import { join } from 'node:path';import { normalizeBeacon, tryNormalizeBeacon, looksLikeIdentityBeacon } from '../lib/beacon.js';import { vitDir, readProjectConfig, readRawProjectConfig, writeProjectConfig } from '../lib/vit-dir.js';import { requireAgent } from '../lib/agent.js';import { mark, name, DOT_VIT_README } from '../lib/brand.js';import { jsonOk, jsonError } from '../lib/json-output.js';import { errorMessage, formatError } from '../lib/error-format.js';
const BEACON_REPAIR_HINT = 'run vit init --beacon <canonical-git-url> to repair it';const IDENTITY_BEACON_HINT = 'use the repo\'s own URL, not a bare host+identity URL (e.g. a Tangled knot URL with no repo path)';
function assertNotIdentityBeacon(beacon, source) { if (!looksLikeIdentityBeacon(beacon)) return; const err = new Error( `${source} looks like a person/identity URL, not a repo URL (derived beacon: ${beacon}). ` + 'Every repo hosted the same way would collapse onto this one beacon.' ); err.hint = IDENTITY_BEACON_HINT; throw err;}
export default function register(program) { program .command('init') .description('Initialize .vit directory and set project beacon. Use the most official upstream or well-known git URL so all contributors converge on the same beacon.') .option('--beacon <url>', 'Git URL (or "." to read from git remote upstream/origin) to derive the beacon URI') .option('--secondary <url>', 'Secondary beacon URL for upstream cap discovery') .option('--json', 'Output as JSON') .option('-v, --verbose', 'Show step-by-step details') .action(async (opts) => { try { const gate = requireAgent(); if (!gate.ok) { if (opts.json) { jsonError('agent required', 'run vit init from a coding agent'); return; } console.error(`${name} init should be run by a coding agent (e.g. claude code, codex, gemini cli, opencode, pi).`); console.error(`open your agent and ask it to run '${name} init' for you.`); process.exitCode = 1; return; }
const { verbose } = opts; const vlog = opts.json ? (...a) => console.error(...a) : console.log; const dir = vitDir(); if (verbose) vlog(`[verbose] .vit dir: ${dir}`);
if (!opts.beacon && !opts.secondary) { let config; try { config = readProjectConfig(); } catch (err) { err.hint = BEACON_REPAIR_HINT; throw err; } if (config.beacon) { if (opts.json) { const out = { beacon: config.beacon }; if (config.secondaryBeacon) out.secondaryBeacon = config.secondaryBeacon; jsonOk(out); return; } console.log(`${mark} beacon: ${config.beacon}`); if (config.secondaryBeacon) { console.log(`${mark} secondary beacon: ${config.secondaryBeacon}`); } console.log(`hint: to change the beacon, run: ${name} init --beacon <git-url>`); return; }
let isGitRepo = false; try { execSync('git rev-parse --is-inside-work-tree', { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'], }); isGitRepo = true; } catch {} if (verbose) vlog(`[verbose] in git repo: ${isGitRepo ? 'yes' : 'no'}`);
const hasVitDir = existsSync(dir); if (!isGitRepo) { let remotes = []; if (opts.json) { jsonOk({ status: hasVitDir ? 'no beacon' : 'not initialized', git: false, remotes, }); return; } console.log(hasVitDir ? 'status: no beacon' : 'status: not initialized'); console.log('git: false'); if (hasVitDir) { console.log(`hint: run: ${name} init --beacon <canonical-git-url>`); } else { console.log(`hint: navigate to a git repository and run '${name} init' there.`); console.log(` to start fresh: git init && git remote add origin <your-repo-url>`); } return; }
console.log(hasVitDir ? 'status: no beacon' : 'status: not initialized'); console.log('git: true');
let remoteNames = []; try { remoteNames = execSync('git remote', { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'], }) .trim() .split('\n') .filter(Boolean); } catch (err) { console.warn(`warning: failed to list git remotes: ${errorMessage(err)}`); remoteNames = []; } if (verbose) vlog(`[verbose] remotes detected: ${remoteNames.length > 0 ? remoteNames.join(', ') : 'none'}`);
const remotes = []; for (const name of remoteNames) { try { const url = execSync(`git config --get remote.${name}.url`, { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'], }).trim(); if (url) remotes.push({ name, url }); } catch (err) { console.warn(`warning: failed to read git remote ${name} url: ${errorMessage(err)}`); } } if (verbose && remotes.length > 0) { vlog(`[verbose] remote urls: ${remotes.map(r => `${r.name}=${r.url}`).join(' ')}`); }
if (opts.json) { jsonOk({ status: hasVitDir ? 'no beacon' : 'not initialized', git: true, remotes: remotes.map(r => ({ name: r.name, url: r.url })), }); return; }
const remotesDisplay = remotes.length > 0 ? remotes.map(remote => `${remote.name}=${remote.url}`).join(' ') : 'none'; console.log(`remotes: ${remotesDisplay}`);
const upstream = remotes.find(remote => remote.name === 'upstream'); const origin = remotes.find(remote => remote.name === 'origin'); if (upstream) { console.log('hint: detected upstream remote. upstream points to the canonical repo.'); console.log(`hint: run: ${name} init --beacon ${upstream.url}`); } else if (origin) { console.log(`hint: run: ${name} init --beacon ${origin.url}`); } else { console.log(`hint: no git remotes found. run: ${name} init --beacon <canonical-git-url>`); } return; }
if (opts.secondary && !opts.beacon) { const config = readRawProjectConfig(); if (!config.beacon) { if (opts.json) { jsonError("no primary beacon set — run 'vit init --beacon <url>' first"); return; } console.error("no primary beacon set — run 'vit init --beacon <url>' first"); process.exitCode = 1; return; }
let primary; try { primary = normalizeBeacon(config.beacon, '.vit/config.json "beacon"'); } catch (err) { err.hint = BEACON_REPAIR_HINT; throw err; } const secondary = normalizeBeacon(opts.secondary, '--secondary'); assertNotIdentityBeacon(secondary, '--secondary'); const merged = { ...config, beacon: primary, secondaryBeacon: secondary }; writeProjectConfig(merged); if (opts.json) { jsonOk({ beacon: merged.beacon, secondaryBeacon: merged.secondaryBeacon }); return; } console.log(`${mark} beacon: ${merged.beacon}`); console.log(`${mark} secondary beacon: ${merged.secondaryBeacon}`); return; }
let gitUrl = opts.beacon; if (gitUrl === '.') { if (verbose) vlog('[verbose] resolving --beacon . via remote.upstream.url then remote.origin.url'); let usedRemote = ''; try { gitUrl = execSync('git config --get remote.upstream.url', { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'], }).trim(); if (gitUrl) usedRemote = 'upstream'; } catch { gitUrl = ''; }
if (!gitUrl) { try { gitUrl = execSync('git config --get remote.origin.url', { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'], }).trim(); if (gitUrl) usedRemote = 'origin'; } catch { gitUrl = ''; } }
if (!gitUrl) { if (opts.json) { jsonError('no git remote found', 'set a remote or provide a git URL directly'); return; } console.error('No git remote found. Set a remote or provide a git URL directly.'); process.exitCode = 1; return; } if (verbose) vlog(`[verbose] Read git remote ${usedRemote}: ${gitUrl}`); }
const beacon = normalizeBeacon(gitUrl, '--beacon'); assertNotIdentityBeacon(beacon, '--beacon'); if (verbose) vlog(`[verbose] Computed beacon: ${beacon}`); const existing = readRawProjectConfig(); const merged = { ...existing, beacon }; if (opts.secondary) { merged.secondaryBeacon = normalizeBeacon(opts.secondary, '--secondary'); assertNotIdentityBeacon(merged.secondaryBeacon, '--secondary'); } else if (Object.hasOwn(existing, 'secondaryBeacon')) { // Tolerant normalization is deliberate on this repair path: an invalid secondary // must be dropped with a warning, not block repair of the primary beacon. const secondary = tryNormalizeBeacon(existing.secondaryBeacon); if (secondary) { merged.secondaryBeacon = secondary; } else { delete merged.secondaryBeacon; console.warn('warning: dropping invalid .vit/config.json "secondaryBeacon" while repairing beacon'); } } writeProjectConfig(merged); if (verbose) vlog(`[verbose] Wrote config.json`); const readmePath = join(vitDir(), 'README.md'); if (!existsSync(readmePath)) { writeFileSync(readmePath, DOT_VIT_README); if (verbose) vlog(`[verbose] Wrote .vit/README.md`); } if (opts.json) { const out = { beacon: merged.beacon }; if (merged.secondaryBeacon) out.secondaryBeacon = merged.secondaryBeacon; jsonOk(out); return; } console.log(`${mark} beacon: ${beacon}`); if (merged.secondaryBeacon) { console.log(`${mark} secondary beacon: ${merged.secondaryBeacon}`); } } catch (err) { if (opts.json) { jsonError(err); return; } console.error(formatError(err, { hint: err?.hint, verbose: opts.verbose })); process.exitCode = 1; } });}