open source is social v-it.org
1// SPDX-License-Identifier: MIT
2// Copyright (c) 2026 sol pbc
3
4import { requireDid } from '../lib/config.js';
5import { CAP_COLLECTION } from '../lib/constants.js';
6import { restoreAgent } from '../lib/oauth.js';
7import { readBeaconSet, readFollowing, readLog } from '../lib/vit-dir.js';
8import { requireAgent, detectCodingAgent } from '../lib/agent.js';
9import { shouldBypassVet } from '../lib/trust-gate.js';
10import { resolveRef, REF_PATTERN } from '../lib/cap-ref.js';
11import { brand, name } from '../lib/brand.js';
12import { resolvePds, listRecordsFromPds, batchQuery } from '../lib/pds.js';
13import { loadConfig } from '../lib/config.js';
14import { jsonOk, jsonError } from '../lib/json-output.js';
15
16export default function register(program) {
17 program
18 .command('remix')
19 .argument('<ref>', 'Three-word cap reference (e.g. fast-cache-invalidation)')
20 .description('Derive a vetted cap into the local codebase')
21 .option('--did <did>', 'DID to use')
22 .option('--json', 'Output as JSON')
23 .option('-v, --verbose', 'Show step-by-step details')
24 .action(async (ref, opts) => {
25 try {
26 const gate = requireAgent();
27 if (!gate.ok) {
28 if (opts.json) {
29 jsonError('agent required', 'run vit remix from a coding agent');
30 return;
31 }
32 console.error(`${name} remix should be run by a coding agent (e.g. claude code, gemini cli).`);
33 console.error(`open your agent and ask it to run '${name} remix' for you.`);
34 process.exitCode = 1;
35 return;
36 }
37
38 const { verbose } = opts;
39 const vlog = opts.json ? (...a) => console.error(...a) : console.log;
40
41 if (!REF_PATTERN.test(ref)) {
42 if (opts.json) {
43 jsonError('invalid ref', 'expected three lowercase words with dashes');
44 return;
45 }
46 console.error('invalid ref. expected three lowercase words with dashes (e.g. fast-cache-invalidation)');
47 process.exitCode = 1;
48 return;
49 }
50
51 if (opts.json && !(opts.did || loadConfig().did)) {
52 jsonError('no DID configured', "run 'vit login <handle>' first");
53 return;
54 }
55 const did = requireDid(opts);
56 if (!did) return;
57 if (verbose) vlog(`[verbose] DID: ${did}`);
58
59 const beaconSet = readBeaconSet();
60 if (beaconSet.size === 0) {
61 if (opts.json) {
62 jsonError('no beacon set', "run 'vit init' first");
63 return;
64 }
65 console.error(`no beacon set. run '${name} init' in a project directory first.`);
66 process.exitCode = 1;
67 return;
68 }
69 if (verbose) vlog(`[verbose] beacons: ${[...beaconSet].join(', ')}`);
70
71 const trusted = readLog('trusted.jsonl');
72 const trustedEntry = trusted.find(e => e.ref === ref);
73 if (!trustedEntry) {
74 const trustGate = shouldBypassVet();
75 if (!trustGate.bypass) {
76 if (opts.json) {
77 jsonError(`cap '${ref}' is not trusted`, `tell your operator to run 'vit vet ${ref} --trust'`);
78 return;
79 }
80 console.error(`cap '${ref}' is not trusted. tell your operator to vet it first:`);
81 console.error('');
82 console.error(` vit vet ${ref}`);
83 console.error('');
84 console.error('after reviewing, they can trust it with:');
85 console.error('');
86 console.error(` vit vet ${ref} --trust`);
87 if (detectCodingAgent()) {
88 console.error('');
89 console.error('or, to trust all items without review:');
90 console.error('');
91 console.error(' vit vet --dangerous-accept --confirm');
92 }
93 process.exitCode = 1;
94 return;
95 }
96 if (verbose) vlog(`[verbose] vet gate bypassed: ${trustGate.reason}`);
97 }
98 if (verbose && trustedEntry) vlog(`[verbose] trusted entry found, uri: ${trustedEntry.uri}`);
99
100 const { agent } = await restoreAgent(did);
101 if (verbose) vlog('[verbose] session restored');
102
103 const following = readFollowing();
104 const dids = following.map(e => e.did);
105 dids.push(did);
106
107 const allRecords = await batchQuery(dids, async (repoDid) => {
108 const pds = await resolvePds(repoDid);
109 if (verbose) vlog(`[verbose] ${repoDid}: resolved PDS ${pds}`);
110 return (await listRecordsFromPds(pds, repoDid, CAP_COLLECTION, 50)).records;
111 }, { verbose });
112
113 let match = null;
114 for (const records of allRecords) {
115 for (const rec of records) {
116 if (!beaconSet.has(rec.value.beacon)) continue;
117 const recRef = resolveRef(rec.value, rec.cid);
118 if (recRef === ref) {
119 if (!match || (rec.value.createdAt || '') > (match.value.createdAt || '')) {
120 match = rec;
121 }
122 }
123 }
124 }
125
126 if (!match) {
127 if (opts.json) {
128 jsonError(`no cap found with ref '${ref}' for this beacon`);
129 return;
130 }
131 console.error(`no cap found with ref '${ref}' for this beacon.`);
132 console.error('');
133 console.error('hint: caps only appear from accounts you follow and your own.');
134 console.error(` vit following check who you're following`);
135 console.error(` vit explore cap ${ref} search the network-wide index`);
136 process.exitCode = 1;
137 return;
138 }
139
140 const record = match.value;
141 const author = match.uri.split('/')[2];
142 const title = record.title || ref;
143 const description = record.description || '';
144 const text = record.text || '';
145
146 if (opts.json) {
147 jsonOk({ ref, author, title, description, text });
148 return;
149 }
150
151 console.log(`# ${brand} remix: ${title}`);
152 console.log('');
153 console.log(`ref: ${ref}`);
154 console.log(`author: ${author}`);
155 if (description) console.log(`description: ${description}`);
156 console.log('');
157 console.log('---');
158 console.log('');
159 console.log('you are remixing a vetted cap into the current codebase.');
160 console.log('create a thorough implementation plan that:');
161 console.log('');
162 console.log('1. adapts the cap to this repo\'s architecture, conventions, and existing code');
163 console.log('2. follows local guidelines (CLAUDE.md, project conventions, coding standards)');
164 console.log('3. identifies all files to create or modify');
165 console.log('4. specifies tests to add or update');
166 console.log('5. notes any dependencies or migrations needed');
167 console.log('');
168 console.log('do not apply the cap blindly. produce a well-researched plan first.');
169 console.log('');
170 console.log('---');
171 console.log('');
172 console.log('## cap content');
173 console.log('');
174 console.log(text);
175 } catch (err) {
176 const msg = err instanceof Error ? err.message : String(err);
177 if (opts.json) {
178 jsonError(msg);
179 return;
180 }
181 console.error(msg);
182 process.exitCode = 1;
183 }
184 });
185}