open source is social v-it.org
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'hopper-6xw4z2fp-skill-install-modernization'

+107 -223
+1 -1
CLAUDE.md
··· 5 5 ## Project Overview 6 6 7 7 vit is a Bun CLI for discovering, vetting, remixing, and shipping software capabilities: 8 - - `vit setup`, `vit login`, `vit init`, `vit doctor` — authentication and environment setup 8 + - `vit login`, `vit init`, `vit doctor` — authentication and environment setup 9 9 - `vit beacon`, `vit config` — project beacon inspection and user configuration 10 10 - `vit firehose` — listen to Jetstream for cap events 11 11 - `vit ship`, `vit skim` — publish and read caps
+1 -13
COMMANDS.md
··· 9 9 10 10 --- 11 11 12 - ## setup commands 13 - 14 - ### setup 15 - 16 - **you run this** (terminal) 17 - 18 - check prerequisites and install the vit skill for your coding agent. 19 - 20 - ```bash 21 - vit setup 22 - ``` 23 - 24 - 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. 12 + ## getting started 25 13 26 14 ### login 27 15
+2 -2
CONTRIBUTING.md
··· 17 17 **you run this** (terminal): 18 18 19 19 ```bash 20 - vit setup 20 + npm install -g vit 21 21 vit login <your-handle>.bsky.social 22 22 vit adopt vit:github.com/solpbc/vit 23 23 ``` 24 24 25 - `setup` checks prerequisites and installs the vit skill for your agent. 25 + installing vit auto-installs the agent skill. 26 26 `login` authenticates with Bluesky via browser OAuth. 27 27 `adopt` forks or clones the vit repo and initializes it. 28 28
+2 -3
README.md
··· 25 25 26 26 ```bash 27 27 npm install -g vit 28 - vit setup 29 28 vit login your-handle.bsky.social 30 29 ``` 31 30 32 - 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. 31 + 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. 33 32 34 33 **[full getting started guide →](https://v-it.org/start/)** 35 34 ··· 66 65 67 66 ## works with 68 67 69 - 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). 68 + 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). 70 69 71 70 ## reference 72 71
+2 -7
VOCAB.md
··· 100 100 check system readiness and configure vit for first use. 101 101 102 102 ```bash 103 - vit setup 104 103 vit init 105 104 vit doctor 106 105 ``` 107 - 108 - `setup` 109 - - log in to Bluesky (invokes login flow) 110 - - install skills (agent capabilities) 111 106 112 107 `init` 113 108 - initialize `.vit/` in the current git repo ··· 257 252 setup (one-time): 258 253 259 254 ```bash 260 - vit setup 255 + npm install -g vit 261 256 vit adopt <beacon> 262 257 ``` 263 258 ··· 279 274 ``` 280 275 281 276 conceptual lifecycle: 282 - - setup prepares the system 277 + - install prepares the system 283 278 - init prepares the project environment 284 279 - adopt joins a project via its beacon 285 280 - beacon anchors the project
-1
docs/decks/atmosphere-2026/index.html
··· 412 412 <div style="text-align:left;"> 413 413 <p class="green" style="font-size:38px; margin:0 0 24px 0; font-weight:600;">you</p> 414 414 <div class="mono" style="font-size:30px; line-height:2;"> 415 - <p style="margin:0;">setup</p> 416 415 <p style="margin:0;">login</p> 417 416 <p style="margin:0;">vet</p> 418 417 <p style="margin:0;">vouch</p>
+5 -6
docs/start/index.html
··· 297 297 298 298 <hr> 299 299 300 - <h2>1. install &amp; set up</h2> 300 + <h2>1. install</h2> 301 301 <span class="cmd-label">you run this (terminal)</span> 302 - <pre><code>npm install -g vit 303 - vit setup</code></pre> 304 - <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 &mdash; just use <code>npx vit</code> instead.</p> 302 + <pre><code>npm install -g vit</code></pre> 303 + <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 &mdash; just use <code>npx vit</code> instead.</p> 305 304 306 305 <h2>2. log in</h2> 307 306 <span class="cmd-label">you run this (terminal)</span> ··· 320 319 <hr> 321 320 322 321 <h2>4. open your agent</h2> 323 - <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> 322 + <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> 324 323 325 324 <h2>5. initialize your project</h2> 326 325 <span class="cmd-label">your agent runs this (inside Claude Code / Codex / Gemini CLI)</span> ··· 351 350 <p>vit is a human+agent collaboration tool. some commands are for you, some are for your agent:</p> 352 351 353 352 <p><strong>you run these</strong> (terminal):<br> 354 - <code>setup</code>, <code>login</code>, <code>vet</code> &mdash; setup, authentication, and trust decisions stay with you.</p> 353 + <code>login</code>, <code>vet</code> &mdash; authentication and trust decisions stay with you.</p> 355 354 356 355 <p><strong>your agent runs these</strong> (inside your coding agent):<br> 357 356 <code>init</code>, <code>skim</code>, <code>remix</code>, <code>ship</code>, <code>follow</code>, <code>learn</code> &mdash; discovery, integration, and publishing are agent tasks.</p>
+5 -2
package.json
··· 8 8 }, 9 9 "files": [ 10 10 "bin/", 11 - "src/" 11 + "src/", 12 + "skills/vit/" 12 13 ], 13 14 "type": "module", 14 - "scripts": {}, 15 + "scripts": { 16 + "postinstall": "node src/postinstall.js" 17 + }, 15 18 "engines": { 16 19 "node": ">=20.0.0" 17 20 },
+1 -17
skills/vit/COMMANDS.md
··· 57 57 - None. 58 58 59 59 Output format: 60 - - Text diagnostics for setup and beacon status. 60 + - Text diagnostics for install and beacon status. 61 61 62 62 Error conditions: 63 63 - Config read/parse failures. ··· 194 194 - `vit beacon vit:github.com/org/repo` 195 195 196 196 ## Human-Only Commands 197 - 198 - ### `vit setup` 199 - Usage: `vit setup` 200 - 201 - Options: 202 - - None. 203 - 204 - Gate: 205 - - `requireNotAgent()` 206 - 207 - Output format: 208 - - Text status for tool prerequisites and login state. 209 - 210 - Error conditions: 211 - - Running in an agent context. 212 - - Missing required tools (`git` or `bun`). 213 197 214 198 ### `vit login <handle>` 215 199 Usage: `vit login <handle>`
+5 -6
skills/vit/SKILL.md
··· 8 8 9 9 ## 1. Overview 10 10 11 - 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. 11 + 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. 12 12 13 13 ## 2. Prerequisites 14 14 15 - Dependency chain: `setup → login → init → follow → skim/ship`. 15 + Dependency chain: `login → init → follow → skim/ship`. 16 16 17 - `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. 17 + `login` is human-only. The agent starts at `init`. Use `vit doctor` to check beacon status before running discovery or shipping commands. 18 18 19 19 ## 3. Agent Workflow 20 20 ··· 64 64 ### Agent-usable commands 65 65 66 66 ### `vit doctor` 67 - - Description: Read-only diagnostic for setup and beacon status. 67 + - Description: Read-only diagnostic for install and beacon status. 68 68 - Usage: `vit doctor` 69 69 - Key flags: none. 70 - - Output: text status lines for setup and beacon. 70 + - Output: text status lines for install and beacon. 71 71 - Common errors: generic runtime or config read failures. 72 72 73 73 ### `vit config [action] [key] [value]` ··· 163 163 ## 5. Commands the Agent Must NOT Run 164 164 165 165 These commands require human interaction. Tell the user exactly what to run: 166 - - `vit setup` - Tell user: "Run `vit setup` in your terminal to check prerequisites (git, bun)." 167 166 - `vit login <handle>` - Tell user: "Run `vit login <handle>` in your terminal to authenticate via browser OAuth." 168 167 - `vit adopt <beacon>` - Tell user: "Run `vit adopt <beacon>` in your terminal to fork and clone a project." 169 168 - `vit vet <ref>` - Human review command. Tell the user to run it in their terminal.
-2
src/cli.js
··· 19 19 import registerVet from './cmd/vet.js'; 20 20 import registerVouch from './cmd/vouch.js'; 21 21 import registerFollow from './cmd/follow.js'; 22 - import registerSetup from './cmd/setup.js'; 23 22 import registerHack from './cmd/hack.js'; 24 23 import registerLink from './cmd/link.js'; 25 24 import registerInbox from './cmd/inbox.js'; ··· 46 45 registerVet(program); 47 46 registerVouch(program); 48 47 registerFollow(program); 49 - registerSetup(program); 50 48 registerHack(program); 51 49 registerLink(program); 52 50 registerInbox(program);
+5 -16
src/cmd/doctor.js
··· 41 41 async function checkHealth(opts) { 42 42 try { 43 43 const config = loadConfig(); 44 - const setup = { 45 - done: !!config.setup_at, 46 - at: config.setup_at ? new Date(config.setup_at * 1000).toISOString() : null, 47 - }; 48 44 let installType = 'not on PATH'; 49 45 let vitPath = which(name); 50 46 let installPath = vitPath || null; ··· 54 50 let userSkills = []; 55 51 let blueskyOk = false; 56 52 let pds = null; 57 - 58 - if (config.setup_at) { 59 - const when = new Date(config.setup_at * 1000).toISOString(); 60 - if (!opts.json) console.log(`${mark} setup: ok (${when})`); 61 - } else { 62 - if (!opts.json) console.log(`${mark} setup: not done (run ${name} setup)`); 63 - } 64 53 65 54 if (!vitPath) { 66 55 installType = 'not on PATH'; ··· 91 80 if (!opts.json) console.log(`${mark} beacon: not set (run vit init)`); 92 81 } 93 82 94 - const skillPath = join(process.cwd(), '.claude', 'skills', 'using-vit', 'SKILL.md'); 95 - skillInstalled = existsSync(skillPath); 96 - if (existsSync(skillPath)) { 83 + const projectSkillPath = join(process.cwd(), '.claude', 'skills', 'using-vit', 'SKILL.md'); 84 + const userSkillPath = join(homedir(), '.claude', 'skills', 'using-vit', 'SKILL.md'); 85 + skillInstalled = existsSync(projectSkillPath) || existsSync(userSkillPath); 86 + if (skillInstalled) { 97 87 if (!opts.json) console.log(`${mark} skill: ok (using-vit)`); 98 88 } else { 99 - if (!opts.json) console.log(`${mark} skill: not installed (run ${name} setup)`); 89 + if (!opts.json) console.log(`${mark} skill: not installed (reinstall vit)`); 100 90 } 101 91 102 92 // Report installed skills ··· 162 152 163 153 if (opts.json) { 164 154 jsonOk({ 165 - setup, 166 155 install: { type: installType, path: installPath }, 167 156 beacon, 168 157 skill: skillInstalled,
+50 -36
src/cmd/learn.js
··· 1 1 // SPDX-License-Identifier: MIT 2 2 // Copyright (c) 2026 sol pbc 3 3 4 - import { existsSync, mkdirSync, writeFileSync } from 'node:fs'; 4 + import { spawnSync } from 'node:child_process'; 5 + import { mkdirSync, writeFileSync, mkdtempSync, rmSync } from 'node:fs'; 5 6 import { join, dirname } from 'node:path'; 6 - import { homedir } from 'node:os'; 7 + import { homedir, tmpdir } from 'node:os'; 7 8 import { requireDid } from '../lib/config.js'; 8 9 import { SKILL_COLLECTION } from '../lib/constants.js'; 9 10 import { restoreAgent } from '../lib/oauth.js'; ··· 169 170 const record = match.value; 170 171 if (verbose) vlog(`[verbose] found skill: ${record.name} from ${match.uri}`); 171 172 172 - // Determine install path 173 - let installDir; 174 - if (isUserInstall) { 175 - installDir = join(homedir(), '.claude', 'skills', skillName); 176 - } else { 177 - installDir = join(process.cwd(), '.claude', 'skills', skillName); 178 - } 173 + // Install via skills CLI 174 + const tempDir = mkdtempSync(join(tmpdir(), 'vit-learn-')); 175 + try { 176 + writeFileSync(join(tempDir, 'SKILL.md'), record.text); 177 + if (verbose) vlog('[verbose] wrote SKILL.md to temp dir'); 179 178 180 - mkdirSync(installDir, { recursive: true }); 179 + // Download resource blobs to temp dir 180 + if (record.resources && record.resources.length > 0) { 181 + const authorDid = match.uri.split('/')[2]; 182 + const pds = await resolvePds(authorDid); 181 183 182 - // Write SKILL.md from text field — verbatim, no reconstruction 183 - writeFileSync(join(installDir, 'SKILL.md'), record.text); 184 - if (verbose) vlog(`[verbose] wrote SKILL.md to ${installDir}`); 184 + for (const resource of record.resources) { 185 + const resourcePath = join(tempDir, resource.path); 186 + mkdirSync(dirname(resourcePath), { recursive: true }); 185 187 186 - // Download and write resource blobs 187 - if (record.resources && record.resources.length > 0) { 188 - const authorDid = match.uri.split('/')[2]; 189 - const pds = await resolvePds(authorDid); 190 - 191 - for (const resource of record.resources) { 192 - const resourcePath = join(installDir, resource.path); 193 - mkdirSync(dirname(resourcePath), { recursive: true }); 194 - 195 - try { 196 - // Download blob from PDS 197 - const blobCid = resource.blob?.ref?.$link || resource.blob?.cid; 198 - if (blobCid) { 199 - const blobUrl = new URL('/xrpc/com.atproto.sync.getBlob', pds); 200 - blobUrl.searchParams.set('did', authorDid); 201 - blobUrl.searchParams.set('cid', blobCid); 202 - const blobRes = await fetch(blobUrl); 203 - if (!blobRes.ok) throw new Error(`blob fetch failed: ${blobRes.status}`); 204 - const blobData = Buffer.from(await blobRes.arrayBuffer()); 205 - writeFileSync(resourcePath, blobData); 206 - if (verbose) vlog(`[verbose] wrote resource: ${resource.path}`); 188 + try { 189 + // Download blob from PDS 190 + const blobCid = resource.blob?.ref?.$link || resource.blob?.cid; 191 + if (blobCid) { 192 + const blobUrl = new URL('/xrpc/com.atproto.sync.getBlob', pds); 193 + blobUrl.searchParams.set('did', authorDid); 194 + blobUrl.searchParams.set('cid', blobCid); 195 + const blobRes = await fetch(blobUrl); 196 + if (!blobRes.ok) throw new Error(`blob fetch failed: ${blobRes.status}`); 197 + const blobData = Buffer.from(await blobRes.arrayBuffer()); 198 + writeFileSync(resourcePath, blobData); 199 + if (verbose) vlog(`[verbose] wrote resource: ${resource.path}`); 200 + } 201 + } catch (err) { 202 + console.error(`warning: failed to download resource ${resource.path}: ${err.message}`); 207 203 } 208 - } catch (err) { 209 - console.error(`warning: failed to download resource ${resource.path}: ${err.message}`); 210 204 } 211 205 } 206 + 207 + // Delegate to skills CLI 208 + const addArgs = ['skills', 'add', tempDir, '-a', 'claude-code', '-y']; 209 + if (isUserInstall) addArgs.push('-g'); 210 + const addResult = spawnSync('npx', addArgs, { 211 + encoding: 'utf-8', 212 + stdio: ['pipe', 'pipe', 'pipe'], 213 + }); 214 + if (addResult.status !== 0) { 215 + const errText = (addResult.stderr || addResult.stdout || '').trim(); 216 + throw new Error(`skill install failed: ${errText || 'unknown error'}`); 217 + } 218 + if (verbose) vlog('[verbose] installed via npx skills add'); 219 + } finally { 220 + try { rmSync(tempDir, { recursive: true, force: true }); } catch {} 212 221 } 222 + 223 + // Determine install path for logging 224 + const installDir = isUserInstall 225 + ? join(homedir(), '.claude', 'skills', skillName) 226 + : join(process.cwd(), '.claude', 'skills', skillName); 213 227 214 228 // Log to learned.jsonl 215 229 try {
-76
src/cmd/setup.js
··· 1 - // SPDX-License-Identifier: MIT 2 - // Copyright (c) 2026 sol pbc 3 - 4 - import { spawnSync } from 'node:child_process'; 5 - import { loadConfig, saveConfig } from '../lib/config.js'; 6 - import { requireNotAgent } from '../lib/agent.js'; 7 - import { which } from '../lib/compat.js'; 8 - import { mark, brand, name } from '../lib/brand.js'; 9 - 10 - export default function register(program) { 11 - program 12 - .command('setup') 13 - .description('Initialize user-level vit setup') 14 - .action(async () => { 15 - try { 16 - const gate = requireNotAgent(); 17 - if (!gate.ok) { 18 - console.error(`${name} setup must be run by a human. run it in your own terminal.`); 19 - process.exitCode = 1; 20 - return; 21 - } 22 - 23 - const gitPath = which('git'); 24 - console.log(`${mark} git: ${gitPath ? 'found' : 'not found'}`); 25 - if (!gitPath) { 26 - console.error('missing required tool: git'); 27 - process.exitCode = 1; 28 - return; 29 - } 30 - 31 - // skill installation 32 - const npxPath = which('npx'); 33 - if (npxPath) { 34 - try { 35 - const result = spawnSync( 36 - 'npx', ['skills', 'add', 'https://github.com/solpbc/vit/tree/main/skills/vit', '-a', 'claude-code', '-y'], 37 - { 38 - encoding: 'utf-8', 39 - stdio: ['pipe', 'pipe', 'pipe'], 40 - } 41 - ); 42 - if (result.status === 0) { 43 - console.log(`${mark} skill: installed (using-vit)`); 44 - } else { 45 - const errText = (result.stderr || '').trim(); 46 - console.log(`${mark} skill: failed (${errText || 'unknown error'})`); 47 - } 48 - } catch { 49 - console.log(`${mark} skill: failed (could not run npx skills)`); 50 - } 51 - } else { 52 - console.log(`${mark} skill: skipped (npx not found)`); 53 - } 54 - 55 - const config = loadConfig(); 56 - if (config.did) { 57 - console.log(`${mark} login: ${config.did}`); 58 - } else { 59 - console.log(`${mark} login: not logged in`); 60 - console.log(`next: run '${name} login <handle>' to authenticate with Bluesky`); 61 - } 62 - 63 - if (!config.setup_at) { 64 - config.setup_at = Math.floor(Date.now() / 1000); 65 - saveConfig(config); 66 - } 67 - 68 - if (config.did) { 69 - console.log(`${brand} setup complete`); 70 - } 71 - } catch (err) { 72 - console.error(err instanceof Error ? err.message : String(err)); 73 - process.exitCode = 1; 74 - } 75 - }); 76 - }
+26
src/postinstall.js
··· 1 + // SPDX-License-Identifier: MIT 2 + // Copyright (c) 2026 sol pbc 3 + 4 + import { spawnSync } from 'node:child_process'; 5 + import { dirname, join } from 'node:path'; 6 + import { fileURLToPath } from 'node:url'; 7 + 8 + try { 9 + const currentFile = fileURLToPath(import.meta.url); 10 + const currentDir = dirname(currentFile); 11 + const skillDir = join(currentDir, '..', 'skills', 'vit'); 12 + const result = spawnSync( 13 + 'npx', 14 + ['skills', 'add', skillDir, '-g', '-a', 'claude-code', '-y'], 15 + { 16 + encoding: 'utf-8', 17 + stdio: ['pipe', 'pipe', 'pipe'], 18 + } 19 + ); 20 + 21 + if (result.status === 0) { 22 + console.log('vit: skill installed (using-vit)'); 23 + } 24 + } catch { 25 + process.exit(0); 26 + }
+1 -6
test/doctor.test.js
··· 5 5 import { run } from './helpers.js'; 6 6 7 7 describe('vit doctor', () => { 8 - test('reports setup status', () => { 9 - const result = run('doctor'); 10 - expect(result.stdout).toMatch(/setup:/); 11 - }); 12 - 13 8 test('reports beacon status', () => { 14 9 const result = run('doctor'); 15 10 expect(result.stdout).toMatch(/beacon:/); ··· 32 27 33 28 test('vit status is an alias for doctor', () => { 34 29 const result = run('status'); 35 - expect(result.stdout).toMatch(/setup:/); 30 + expect(result.stdout).toMatch(/install:/); 36 31 }); 37 32 });
+1 -1
test/json-output.test.js
··· 62 62 const r = run('doctor --json'); 63 63 const j = parseJson(r.stdout); 64 64 expect(j.ok).toBe(true); 65 - expect(j).toHaveProperty('setup'); 65 + expect(j).toHaveProperty('install'); 66 66 expect(j).toHaveProperty('beacon'); 67 67 expect(j).toHaveProperty('bluesky'); 68 68 });
-28
test/setup.test.js
··· 1 - // SPDX-License-Identifier: MIT 2 - // Copyright (c) 2026 sol pbc 3 - 4 - import { describe, test, expect } from 'bun:test'; 5 - import { run } from './helpers.js'; 6 - 7 - describe('vit setup', () => { 8 - test('rejects when run inside a coding agent', () => { 9 - const result = run('setup', undefined, { CLAUDECODE: '1' }); 10 - expect(result.exitCode).toBe(1); 11 - expect(result.stderr).toContain('must be run by a human'); 12 - }); 13 - 14 - test('checks for git', () => { 15 - const result = run('setup', undefined, { CLAUDECODE: '', GEMINI_CLI: '', CODEX_CI: '' }); 16 - expect(result.stdout).toContain('git: found'); 17 - }); 18 - 19 - test('reports login status', () => { 20 - const result = run('setup', undefined, { CLAUDECODE: '', GEMINI_CLI: '', CODEX_CI: '' }); 21 - expect(result.stdout).toMatch(/login:/); 22 - }); 23 - 24 - test('reports skill installation status', () => { 25 - const result = run('setup', undefined, { CLAUDECODE: '', GEMINI_CLI: '', CODEX_CI: '' }); 26 - expect(result.stdout).toMatch(/skill:/); 27 - }); 28 - });