···55## Project Overview
6677vit is a Bun CLI for discovering, vetting, remixing, and shipping software capabilities:
88-- `vit setup`, `vit login`, `vit init`, `vit doctor` — authentication and environment setup
88+- `vit login`, `vit init`, `vit doctor` — authentication and environment setup
99- `vit beacon`, `vit config` — project beacon inspection and user configuration
1010- `vit firehose` — listen to Jetstream for cap events
1111- `vit ship`, `vit skim` — publish and read caps
+1-13
COMMANDS.md
···991010---
11111212-## setup commands
1313-1414-### setup
1515-1616-**you run this** (terminal)
1717-1818-check prerequisites and install the vit skill for your coding agent.
1919-2020-```bash
2121-vit setup
2222-```
2323-2424-this is the first command you run. it verifies your environment (Node.js, git) and installs the agent skill so your coding agent knows how to use vit.
1212+## getting started
25132614### login
2715
+2-2
CONTRIBUTING.md
···1717**you run this** (terminal):
18181919```bash
2020-vit setup
2020+npm install -g vit
2121vit login <your-handle>.bsky.social
2222vit adopt vit:github.com/solpbc/vit
2323```
24242525-`setup` checks prerequisites and installs the vit skill for your agent.
2525+installing vit auto-installs the agent skill.
2626`login` authenticates with Bluesky via browser OAuth.
2727`adopt` forks or clones the vit repo and initializes it.
2828
+2-3
README.md
···25252626```bash
2727npm install -g vit
2828-vit setup
2928vit login your-handle.bsky.social
3029```
31303232-then open your coding agent (Claude Code, Codex CLI, or Gemini CLI) — it already knows how to use vit because `setup` installed the skill. your agent runs `vit init` to connect your project to the network, then `vit skim` to discover what others have built.
3131+then open your coding agent (Claude Code, Codex CLI, or Gemini CLI) — it already knows how to use vit because installing vit auto-installs the agent skill. your agent runs `vit init` to connect your project to the network, then `vit skim` to discover what others have built.
33323433**[full getting started guide →](https://v-it.org/start/)**
3534···66656766## works with
68676969-vit is a human+agent collaboration tool. it works with [Claude Code](https://claude.ai/code), [Codex CLI](https://github.com/openai/codex), and [Gemini CLI](https://github.com/google-gemini/gemini-cli). some commands are for you (setup, login, vet), others are for your agent (skim, remix, ship).
6868+vit is a human+agent collaboration tool. it works with [Claude Code](https://claude.ai/code), [Codex CLI](https://github.com/openai/codex), and [Gemini CLI](https://github.com/google-gemini/gemini-cli). some commands are for you (login, vet), others are for your agent (skim, remix, ship).
70697170## reference
7271
+2-7
VOCAB.md
···100100check system readiness and configure vit for first use.
101101102102```bash
103103-vit setup
104103vit init
105104vit doctor
106105```
107107-108108-`setup`
109109-- log in to Bluesky (invokes login flow)
110110-- install skills (agent capabilities)
111106112107`init`
113108- initialize `.vit/` in the current git repo
···257252setup (one-time):
258253259254```bash
260260-vit setup
255255+npm install -g vit
261256vit adopt <beacon>
262257```
263258···279274```
280275281276conceptual lifecycle:
282282-- setup prepares the system
277277+- install prepares the system
283278- init prepares the project environment
284279- adopt joins a project via its beacon
285280- beacon anchors the project
···297297298298<hr>
299299300300-<h2>1. install & set up</h2>
300300+<h2>1. install</h2>
301301<span class="cmd-label">you run this (terminal)</span>
302302-<pre><code>npm install -g vit
303303-vit setup</code></pre>
304304-<p><code>setup</code> checks your prerequisites and installs the vit skill so your coding agent knows how to use vit. you can also try vit without installing globally — just use <code>npx vit</code> instead.</p>
302302+<pre><code>npm install -g vit</code></pre>
303303+<p>installing vit auto-installs the agent skill so your coding agent knows how to use vit. you can also try vit without installing globally — just use <code>npx vit</code> instead.</p>
305304306305<h2>2. log in</h2>
307306<span class="cmd-label">you run this (terminal)</span>
···320319<hr>
321320322321<h2>4. open your agent</h2>
323323-<p>open your coding agent (Claude Code, Codex, Gemini CLI). because you ran <code>vit setup</code>, your agent already has the vit skill installed and knows how to use every vit command.</p>
322322+<p>open your coding agent (Claude Code, Codex, Gemini CLI). the vit skill was installed automatically, so your agent already knows how to use every vit command.</p>
324323325324<h2>5. initialize your project</h2>
326325<span class="cmd-label">your agent runs this (inside Claude Code / Codex / Gemini CLI)</span>
···351350<p>vit is a human+agent collaboration tool. some commands are for you, some are for your agent:</p>
352351353352<p><strong>you run these</strong> (terminal):<br>
354354-<code>setup</code>, <code>login</code>, <code>vet</code> — setup, authentication, and trust decisions stay with you.</p>
353353+<code>login</code>, <code>vet</code> — authentication and trust decisions stay with you.</p>
355354356355<p><strong>your agent runs these</strong> (inside your coding agent):<br>
357356<code>init</code>, <code>skim</code>, <code>remix</code>, <code>ship</code>, <code>follow</code>, <code>learn</code> — discovery, integration, and publishing are agent tasks.</p>
···5757- None.
58585959Output format:
6060-- Text diagnostics for setup and beacon status.
6060+- Text diagnostics for install and beacon status.
61616262Error conditions:
6363- Config read/parse failures.
···194194- `vit beacon vit:github.com/org/repo`
195195196196## Human-Only Commands
197197-198198-### `vit setup`
199199-Usage: `vit setup`
200200-201201-Options:
202202-- None.
203203-204204-Gate:
205205-- `requireNotAgent()`
206206-207207-Output format:
208208-- Text status for tool prerequisites and login state.
209209-210210-Error conditions:
211211-- Running in an agent context.
212212-- Missing required tools (`git` or `bun`).
213197214198### `vit login <handle>`
215199Usage: `vit login <handle>`
+5-6
skills/vit/SKILL.md
···8899## 1. Overview
10101111-vit is a Bun CLI for social software capabilities. Agents use it to initialize projects, follow accounts, skim caps from followed accounts, and ship new caps. Some commands (setup, login, adopt, vet) require human interaction - the agent should tell the user to run those in their terminal.
1111+vit is a Bun CLI for social software capabilities. Agents use it to initialize projects, follow accounts, skim caps from followed accounts, and ship new caps. Some commands (login, adopt, vet) require human interaction - the agent should tell the user to run those in their terminal.
12121313## 2. Prerequisites
14141515-Dependency chain: `setup → login → init → follow → skim/ship`.
1515+Dependency chain: `login → init → follow → skim/ship`.
16161717-`setup` and `login` are human-only. The agent starts at `init`. Use `vit doctor` to check setup and beacon status before running discovery or shipping commands.
1717+`login` is human-only. The agent starts at `init`. Use `vit doctor` to check beacon status before running discovery or shipping commands.
18181919## 3. Agent Workflow
2020···6464### Agent-usable commands
65656666### `vit doctor`
6767-- Description: Read-only diagnostic for setup and beacon status.
6767+- Description: Read-only diagnostic for install and beacon status.
6868- Usage: `vit doctor`
6969- Key flags: none.
7070-- Output: text status lines for setup and beacon.
7070+- Output: text status lines for install and beacon.
7171- Common errors: generic runtime or config read failures.
72727373### `vit config [action] [key] [value]`
···163163## 5. Commands the Agent Must NOT Run
164164165165These commands require human interaction. Tell the user exactly what to run:
166166-- `vit setup` - Tell user: "Run `vit setup` in your terminal to check prerequisites (git, bun)."
167166- `vit login <handle>` - Tell user: "Run `vit login <handle>` in your terminal to authenticate via browser OAuth."
168167- `vit adopt <beacon>` - Tell user: "Run `vit adopt <beacon>` in your terminal to fork and clone a project."
169168- `vit vet <ref>` - Human review command. Tell the user to run it in their terminal.
-2
src/cli.js
···1919import registerVet from './cmd/vet.js';
2020import registerVouch from './cmd/vouch.js';
2121import registerFollow from './cmd/follow.js';
2222-import registerSetup from './cmd/setup.js';
2322import registerHack from './cmd/hack.js';
2423import registerLink from './cmd/link.js';
2524import registerInbox from './cmd/inbox.js';
···4645registerVet(program);
4746registerVouch(program);
4847registerFollow(program);
4949-registerSetup(program);
5048registerHack(program);
5149registerLink(program);
5250registerInbox(program);
+5-16
src/cmd/doctor.js
···4141 async function checkHealth(opts) {
4242 try {
4343 const config = loadConfig();
4444- const setup = {
4545- done: !!config.setup_at,
4646- at: config.setup_at ? new Date(config.setup_at * 1000).toISOString() : null,
4747- };
4844 let installType = 'not on PATH';
4945 let vitPath = which(name);
5046 let installPath = vitPath || null;
···5450 let userSkills = [];
5551 let blueskyOk = false;
5652 let pds = null;
5757-5858- if (config.setup_at) {
5959- const when = new Date(config.setup_at * 1000).toISOString();
6060- if (!opts.json) console.log(`${mark} setup: ok (${when})`);
6161- } else {
6262- if (!opts.json) console.log(`${mark} setup: not done (run ${name} setup)`);
6363- }
64536554 if (!vitPath) {
6655 installType = 'not on PATH';
···9180 if (!opts.json) console.log(`${mark} beacon: not set (run vit init)`);
9281 }
93829494- const skillPath = join(process.cwd(), '.claude', 'skills', 'using-vit', 'SKILL.md');
9595- skillInstalled = existsSync(skillPath);
9696- if (existsSync(skillPath)) {
8383+ const projectSkillPath = join(process.cwd(), '.claude', 'skills', 'using-vit', 'SKILL.md');
8484+ const userSkillPath = join(homedir(), '.claude', 'skills', 'using-vit', 'SKILL.md');
8585+ skillInstalled = existsSync(projectSkillPath) || existsSync(userSkillPath);
8686+ if (skillInstalled) {
9787 if (!opts.json) console.log(`${mark} skill: ok (using-vit)`);
9888 } else {
9999- if (!opts.json) console.log(`${mark} skill: not installed (run ${name} setup)`);
8989+ if (!opts.json) console.log(`${mark} skill: not installed (reinstall vit)`);
10090 }
1019110292 // Report installed skills
···162152163153 if (opts.json) {
164154 jsonOk({
165165- setup,
166155 install: { type: installType, path: installPath },
167156 beacon,
168157 skill: skillInstalled,
+50-36
src/cmd/learn.js
···11// SPDX-License-Identifier: MIT
22// Copyright (c) 2026 sol pbc
3344-import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
44+import { spawnSync } from 'node:child_process';
55+import { mkdirSync, writeFileSync, mkdtempSync, rmSync } from 'node:fs';
56import { join, dirname } from 'node:path';
66-import { homedir } from 'node:os';
77+import { homedir, tmpdir } from 'node:os';
78import { requireDid } from '../lib/config.js';
89import { SKILL_COLLECTION } from '../lib/constants.js';
910import { restoreAgent } from '../lib/oauth.js';
···169170 const record = match.value;
170171 if (verbose) vlog(`[verbose] found skill: ${record.name} from ${match.uri}`);
171172172172- // Determine install path
173173- let installDir;
174174- if (isUserInstall) {
175175- installDir = join(homedir(), '.claude', 'skills', skillName);
176176- } else {
177177- installDir = join(process.cwd(), '.claude', 'skills', skillName);
178178- }
173173+ // Install via skills CLI
174174+ const tempDir = mkdtempSync(join(tmpdir(), 'vit-learn-'));
175175+ try {
176176+ writeFileSync(join(tempDir, 'SKILL.md'), record.text);
177177+ if (verbose) vlog('[verbose] wrote SKILL.md to temp dir');
179178180180- mkdirSync(installDir, { recursive: true });
179179+ // Download resource blobs to temp dir
180180+ if (record.resources && record.resources.length > 0) {
181181+ const authorDid = match.uri.split('/')[2];
182182+ const pds = await resolvePds(authorDid);
181183182182- // Write SKILL.md from text field — verbatim, no reconstruction
183183- writeFileSync(join(installDir, 'SKILL.md'), record.text);
184184- if (verbose) vlog(`[verbose] wrote SKILL.md to ${installDir}`);
184184+ for (const resource of record.resources) {
185185+ const resourcePath = join(tempDir, resource.path);
186186+ mkdirSync(dirname(resourcePath), { recursive: true });
185187186186- // Download and write resource blobs
187187- if (record.resources && record.resources.length > 0) {
188188- const authorDid = match.uri.split('/')[2];
189189- const pds = await resolvePds(authorDid);
190190-191191- for (const resource of record.resources) {
192192- const resourcePath = join(installDir, resource.path);
193193- mkdirSync(dirname(resourcePath), { recursive: true });
194194-195195- try {
196196- // Download blob from PDS
197197- const blobCid = resource.blob?.ref?.$link || resource.blob?.cid;
198198- if (blobCid) {
199199- const blobUrl = new URL('/xrpc/com.atproto.sync.getBlob', pds);
200200- blobUrl.searchParams.set('did', authorDid);
201201- blobUrl.searchParams.set('cid', blobCid);
202202- const blobRes = await fetch(blobUrl);
203203- if (!blobRes.ok) throw new Error(`blob fetch failed: ${blobRes.status}`);
204204- const blobData = Buffer.from(await blobRes.arrayBuffer());
205205- writeFileSync(resourcePath, blobData);
206206- if (verbose) vlog(`[verbose] wrote resource: ${resource.path}`);
188188+ try {
189189+ // Download blob from PDS
190190+ const blobCid = resource.blob?.ref?.$link || resource.blob?.cid;
191191+ if (blobCid) {
192192+ const blobUrl = new URL('/xrpc/com.atproto.sync.getBlob', pds);
193193+ blobUrl.searchParams.set('did', authorDid);
194194+ blobUrl.searchParams.set('cid', blobCid);
195195+ const blobRes = await fetch(blobUrl);
196196+ if (!blobRes.ok) throw new Error(`blob fetch failed: ${blobRes.status}`);
197197+ const blobData = Buffer.from(await blobRes.arrayBuffer());
198198+ writeFileSync(resourcePath, blobData);
199199+ if (verbose) vlog(`[verbose] wrote resource: ${resource.path}`);
200200+ }
201201+ } catch (err) {
202202+ console.error(`warning: failed to download resource ${resource.path}: ${err.message}`);
207203 }
208208- } catch (err) {
209209- console.error(`warning: failed to download resource ${resource.path}: ${err.message}`);
210204 }
211205 }
206206+207207+ // Delegate to skills CLI
208208+ const addArgs = ['skills', 'add', tempDir, '-a', 'claude-code', '-y'];
209209+ if (isUserInstall) addArgs.push('-g');
210210+ const addResult = spawnSync('npx', addArgs, {
211211+ encoding: 'utf-8',
212212+ stdio: ['pipe', 'pipe', 'pipe'],
213213+ });
214214+ if (addResult.status !== 0) {
215215+ const errText = (addResult.stderr || addResult.stdout || '').trim();
216216+ throw new Error(`skill install failed: ${errText || 'unknown error'}`);
217217+ }
218218+ if (verbose) vlog('[verbose] installed via npx skills add');
219219+ } finally {
220220+ try { rmSync(tempDir, { recursive: true, force: true }); } catch {}
212221 }
222222+223223+ // Determine install path for logging
224224+ const installDir = isUserInstall
225225+ ? join(homedir(), '.claude', 'skills', skillName)
226226+ : join(process.cwd(), '.claude', 'skills', skillName);
213227214228 // Log to learned.jsonl
215229 try {
-76
src/cmd/setup.js
···11-// SPDX-License-Identifier: MIT
22-// Copyright (c) 2026 sol pbc
33-44-import { spawnSync } from 'node:child_process';
55-import { loadConfig, saveConfig } from '../lib/config.js';
66-import { requireNotAgent } from '../lib/agent.js';
77-import { which } from '../lib/compat.js';
88-import { mark, brand, name } from '../lib/brand.js';
99-1010-export default function register(program) {
1111- program
1212- .command('setup')
1313- .description('Initialize user-level vit setup')
1414- .action(async () => {
1515- try {
1616- const gate = requireNotAgent();
1717- if (!gate.ok) {
1818- console.error(`${name} setup must be run by a human. run it in your own terminal.`);
1919- process.exitCode = 1;
2020- return;
2121- }
2222-2323- const gitPath = which('git');
2424- console.log(`${mark} git: ${gitPath ? 'found' : 'not found'}`);
2525- if (!gitPath) {
2626- console.error('missing required tool: git');
2727- process.exitCode = 1;
2828- return;
2929- }
3030-3131- // skill installation
3232- const npxPath = which('npx');
3333- if (npxPath) {
3434- try {
3535- const result = spawnSync(
3636- 'npx', ['skills', 'add', 'https://github.com/solpbc/vit/tree/main/skills/vit', '-a', 'claude-code', '-y'],
3737- {
3838- encoding: 'utf-8',
3939- stdio: ['pipe', 'pipe', 'pipe'],
4040- }
4141- );
4242- if (result.status === 0) {
4343- console.log(`${mark} skill: installed (using-vit)`);
4444- } else {
4545- const errText = (result.stderr || '').trim();
4646- console.log(`${mark} skill: failed (${errText || 'unknown error'})`);
4747- }
4848- } catch {
4949- console.log(`${mark} skill: failed (could not run npx skills)`);
5050- }
5151- } else {
5252- console.log(`${mark} skill: skipped (npx not found)`);
5353- }
5454-5555- const config = loadConfig();
5656- if (config.did) {
5757- console.log(`${mark} login: ${config.did}`);
5858- } else {
5959- console.log(`${mark} login: not logged in`);
6060- console.log(`next: run '${name} login <handle>' to authenticate with Bluesky`);
6161- }
6262-6363- if (!config.setup_at) {
6464- config.setup_at = Math.floor(Date.now() / 1000);
6565- saveConfig(config);
6666- }
6767-6868- if (config.did) {
6969- console.log(`${brand} setup complete`);
7070- }
7171- } catch (err) {
7272- console.error(err instanceof Error ? err.message : String(err));
7373- process.exitCode = 1;
7474- }
7575- });
7676-}