open source is social v-it.org
0
fork

Configure Feed

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

Consistency and maintenance cleanup

- Standardize error handling: all 9 command files use instanceof guard
- Extract shared CAP_COLLECTION constant to src/lib/constants.js
- Add DID validation to ship and skim (fail fast with clear message)
- Normalize ship command declaration to .command().argument() pattern
- Use single timestamp per ship operation
- Add @atproto/common-web to package.json dependencies
- Extract shared test run() helper to test/helpers.js
- Add .vit/.gitignore to ignore local JSONL logs
- Fix docs: correct doctor/init alias claims, login syntax, add beacon

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

+82 -58
+1
.vit/.gitignore
··· 1 + *.jsonl
+15 -1
README.md
··· 19 19 ## Terminology 20 20 21 21 - **beacon** — a git repo that uniquely represents a project for all vit users; all project groupings are based on a single beacon (one unified "upstream"); git urls are canonicalized into a uniform beacon; stored in `.vit/config.json` 22 - - **init** — check environment readiness and configure vit for first use; alias: `doctor` 22 + - **init** — check environment readiness and configure vit for first use 23 + - **doctor** — check vit setup status and beacon configuration 23 24 - **adopt** — adopt an existing project by its beacon; forks or clones the repo 24 25 - **follow** — atproto handles; stored in local project `.vit/` 25 26 - **skim** — check follows + beacon for posts; read the feed ··· 27 28 - **vouch** — publicly endorse a vetted cap, optionally attaching evidence 28 29 - **remix** — mix a post with local codebase and create a plan to implement; auto-likes 29 30 - **ship** — take any locally implemented feature and write up a post (or quote post if was remixed) 31 + 32 + ## beacon 33 + 34 + Probe a remote repo for its beacon. 35 + 36 + ```bash 37 + vit beacon https://github.com/solpbc/vit.git 38 + vit beacon vit:github.com/solpbc/vit 39 + ``` 40 + 41 + | Option | Description | 42 + |---|---| 43 + | `-v, --verbose` | Show step-by-step details | 30 44 31 45 ## login 32 46
+1 -1
VOCAB.md
··· 86 86 vit doctor 87 87 ``` 88 88 89 - `doctor` is an alias for `init`. 89 + `doctor` checks setup status and beacon configuration without modifying anything. 90 90 91 91 Capabilities: 92 92 - Log in to Bluesky (invokes login flow)
+1
bun.lock
··· 5 5 "name": "vit", 6 6 "dependencies": { 7 7 "@atproto/api": "^0.18.20", 8 + "@atproto/common-web": "^0.4.16", 8 9 "@atproto/oauth-client-node": "^0.3.16", 9 10 "@ipld/dag-cbor": "^9.2.0", 10 11 "@noble/curves": "^1.8.0",
+1
package.json
··· 13 13 "type": "module", 14 14 "dependencies": { 15 15 "@atproto/api": "^0.18.20", 16 + "@atproto/common-web": "^0.4.16", 16 17 "@atproto/oauth-client-node": "^0.3.16", 17 18 "@ipld/dag-cbor": "^9.2.0", 18 19 "@noble/curves": "^1.8.0",
+3 -2
skills/vit/SKILL.md
··· 12 12 ```bash 13 13 bun install # install dependencies 14 14 vit init # check environment and configure vit 15 - vit login --handle alice.bsky.social # authenticate with Bluesky 15 + vit login alice.bsky.social # authenticate with Bluesky 16 16 ``` 17 17 18 18 ## Subcommands ··· 20 20 | Command | Purpose | 21 21 |---------|---------| 22 22 | `vit init` | Check environment readiness, configure vit for first use | 23 + | `vit beacon <target>` | Probe a remote repo for its beacon | 23 24 | `vit setup` | Initialize user-level config | 24 - | `vit doctor` | Check vit setup status (alias for init) | 25 + | `vit doctor` | Check vit setup status and beacon configuration | 25 26 | `vit login <handle>` | Browser-based ATProto OAuth, saves DID to vit.json | 26 27 | `vit config [action]` | Read/write vit.json config (list, set, delete) | 27 28 | `vit firehose` | Listen to Bluesky Jetstream for custom record events |
+1 -1
src/cmd/beacon.js
··· 54 54 console.log('beacon: unlit'); 55 55 } 56 56 } catch (err) { 57 - console.error(err.message); 57 + console.error(err instanceof Error ? err.message : String(err)); 58 58 process.exitCode = 1; 59 59 } 60 60 });
+1 -1
src/cmd/doctor.js
··· 25 25 console.log('beacon: not set'); 26 26 } 27 27 } catch (err) { 28 - console.error(err.message); 28 + console.error(err instanceof Error ? err.message : String(err)); 29 29 process.exitCode = 1; 30 30 } 31 31 });
+2 -2
src/cmd/firehose.js
··· 2 2 // Copyright (c) 2026 sol pbc 3 3 4 4 import { loadConfig } from '../lib/config.js'; 5 + import { CAP_COLLECTION } from '../lib/constants.js'; 5 6 6 7 const JETSTREAM_URL = 'wss://jetstream2.us-east.bsky.network/subscribe'; 7 - const DEFAULT_COLLECTION = 'org.v-it.cap'; 8 8 9 9 let ws = null; 10 10 let shuttingDown = false; ··· 109 109 .description('Listen to Bluesky Jetstream firehose for custom record events') 110 110 .option('-v, --verbose', 'Show full JSON for each event') 111 111 .option('--did <did>', 'Filter by DID (reads saved DID from config if not provided)') 112 - .option('--collection <nsid>', 'Collection NSID to filter', DEFAULT_COLLECTION) 112 + .option('--collection <nsid>', 'Collection NSID to filter', CAP_COLLECTION) 113 113 .action(async (opts) => { 114 114 try { 115 115 if (!opts.did) {
+1 -1
src/cmd/init.js
··· 56 56 if (verbose) console.log(`[verbose] Wrote config.json`); 57 57 console.log(`beacon: ${beacon}`); 58 58 } catch (err) { 59 - console.error(err.message); 59 + console.error(err instanceof Error ? err.message : String(err)); 60 60 process.exitCode = 1; 61 61 } 62 62 });
+1 -1
src/cmd/setup.js
··· 19 19 saveConfig(config); 20 20 console.log('vit setup complete'); 21 21 } catch (err) { 22 - console.error(err.message); 22 + console.error(err instanceof Error ? err.message : String(err)); 23 23 process.exitCode = 1; 24 24 } 25 25 });
+17 -9
src/cmd/ship.js
··· 2 2 // Copyright (c) 2026 sol pbc 3 3 4 4 import { TID } from '@atproto/common-web'; 5 + import { CAP_COLLECTION } from '../lib/constants.js'; 5 6 import { loadConfig } from '../lib/config.js'; 6 7 import { restoreAgent } from '../lib/oauth.js'; 7 8 import { appendLog } from '../lib/vit-dir.js'; 8 9 9 10 export default function register(program) { 10 11 program 11 - .command('ship <text>') 12 + .command('ship') 13 + .argument('<text>', 'Cap text to publish') 12 14 .description('Write a cap to the authenticated PDS') 13 15 .option('-v, --verbose', 'Show step-by-step details') 14 16 .option('--did <did>', 'DID to use (reads saved DID from config if not provided)') ··· 16 18 try { 17 19 const { verbose } = opts; 18 20 const did = opts.did || loadConfig().did; 21 + if (!did) { 22 + console.error("No DID configured. Run 'vit login <handle>' first or pass --did."); 23 + process.exitCode = 1; 24 + return; 25 + } 19 26 if (verbose) console.log(`[verbose] DID: ${did}`); 27 + const now = new Date().toISOString(); 20 28 21 29 const { agent, session } = await restoreAgent(did); 22 30 if (verbose) console.log(`[verbose] Session restored, PDS: ${session.serverMetadata?.issuer}`); 23 31 24 32 const record = { 25 - $type: 'org.v-it.cap', 33 + $type: CAP_COLLECTION, 26 34 text, 27 - createdAt: new Date().toISOString(), 35 + createdAt: now, 28 36 }; 29 37 const rkey = TID.nextStr(); 30 38 if (verbose) console.log(`[verbose] Record built, rkey: ${rkey}`); 31 39 const putArgs = { 32 40 repo: did, 33 - collection: 'org.v-it.cap', 41 + collection: CAP_COLLECTION, 34 42 rkey, 35 43 record, 36 44 validate: false, ··· 39 47 const putRes = await agent.com.atproto.repo.putRecord(putArgs); 40 48 try { 41 49 appendLog('caps.jsonl', { 42 - ts: new Date().toISOString(), 50 + ts: now, 43 51 did, 44 52 rkey, 45 - collection: 'org.v-it.cap', 53 + collection: CAP_COLLECTION, 46 54 pds: session.serverMetadata?.issuer, 47 55 uri: putRes.data.uri, 48 56 cid: putRes.data.cid, ··· 53 61 if (verbose) console.log(`[verbose] Log written to caps.jsonl`); 54 62 console.log( 55 63 JSON.stringify({ 56 - ts: new Date().toISOString(), 64 + ts: now, 57 65 pds: session.serverMetadata?.issuer, 58 66 xrpc: 'com.atproto.repo.putRecord', 59 67 request: putArgs, 60 68 response: putRes.data, 61 69 }), 62 70 ); 63 - } catch (e) { 64 - console.error(e.message); 71 + } catch (err) { 72 + console.error(err instanceof Error ? err.message : String(err)); 65 73 process.exitCode = 1; 66 74 } 67 75 });
+9 -3
src/cmd/skim.js
··· 2 2 // Copyright (c) 2026 sol pbc 3 3 4 4 import { loadConfig } from '../lib/config.js'; 5 + import { CAP_COLLECTION } from '../lib/constants.js'; 5 6 import { restoreAgent } from '../lib/oauth.js'; 6 7 7 8 export default function register(program) { ··· 15 16 try { 16 17 const { verbose } = opts; 17 18 const did = opts.did || loadConfig().did; 19 + if (!did) { 20 + console.error("No DID configured. Run 'vit login <handle>' first or pass --did."); 21 + process.exitCode = 1; 22 + return; 23 + } 18 24 if (verbose) console.log(`[verbose] DID: ${did}`); 19 25 20 26 const { agent, session } = await restoreAgent(did); ··· 22 28 23 29 const listArgs = { 24 30 repo: did, 25 - collection: 'org.v-it.cap', 31 + collection: CAP_COLLECTION, 26 32 limit: parseInt(opts.limit, 10), 27 33 }; 28 34 if (verbose) console.log(`[verbose] listRecords ${listArgs.collection} limit=${listArgs.limit}`); ··· 38 44 }), 39 45 ); 40 46 } 41 - } catch (e) { 42 - console.error(e.message); 47 + } catch (err) { 48 + console.error(err instanceof Error ? err.message : String(err)); 43 49 process.exitCode = 1; 44 50 } 45 51 });
+4
src/lib/constants.js
··· 1 + // SPDX-License-Identifier: AGPL-3.0-only 2 + // Copyright (c) 2026 sol pbc 3 + 4 + export const CAP_COLLECTION = 'org.v-it.cap';
+1 -19
test/beacon-cmd.test.js
··· 2 2 // Copyright (c) 2026 sol pbc 3 3 4 4 import { describe, test, expect } from 'bun:test'; 5 - import { join } from 'node:path'; 6 - import { execSync } from 'node:child_process'; 7 - 8 - const vitBin = join(import.meta.dir, '..', 'bin', 'vit.js'); 9 - 10 - function run(args) { 11 - try { 12 - return { 13 - stdout: execSync(`bun ${vitBin} ${args}`, { encoding: 'utf-8', timeout: 30000, stdio: ['pipe', 'pipe', 'pipe'] }).trim(), 14 - exitCode: 0, 15 - }; 16 - } catch (err) { 17 - return { 18 - stdout: (err.stdout || '').trim(), 19 - stderr: (err.stderr || '').trim(), 20 - exitCode: err.status, 21 - }; 22 - } 23 - } 5 + import { run } from './helpers.js'; 24 6 25 7 describe('vit beacon', () => { 26 8 test('probes a public repo without .vit/config.json beacon (unlit)', () => {
+22
test/helpers.js
··· 1 + // SPDX-License-Identifier: AGPL-3.0-only 2 + // Copyright (c) 2026 sol pbc 3 + 4 + import { join } from 'node:path'; 5 + import { execSync } from 'node:child_process'; 6 + 7 + const vitBin = join(import.meta.dir, '..', 'bin', 'vit.js'); 8 + 9 + export function run(args, cwd) { 10 + try { 11 + return { 12 + stdout: execSync(`bun ${vitBin} ${args}`, { cwd, encoding: 'utf-8', timeout: 30000, stdio: ['pipe', 'pipe', 'pipe'] }).trim(), 13 + exitCode: 0, 14 + }; 15 + } catch (err) { 16 + return { 17 + stdout: (err.stdout || '').trim(), 18 + stderr: (err.stderr || '').trim(), 19 + exitCode: err.status, 20 + }; 21 + } 22 + }
+1 -17
test/init.test.js
··· 2 2 // Copyright (c) 2026 sol pbc 3 3 4 4 import { describe, test, expect, beforeEach, afterEach } from 'bun:test'; 5 + import { run } from './helpers.js'; 5 6 import { mkdirSync, rmSync, readFileSync, existsSync } from 'node:fs'; 6 7 import { tmpdir } from 'node:os'; 7 8 import { join } from 'node:path'; 8 9 import { execSync } from 'node:child_process'; 9 - 10 - const vitBin = join(import.meta.dir, '..', 'bin', 'vit.js'); 11 - 12 - function run(args, cwd) { 13 - try { 14 - return { 15 - stdout: execSync(`bun ${vitBin} ${args}`, { cwd, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }).trim(), 16 - exitCode: 0, 17 - }; 18 - } catch (err) { 19 - return { 20 - stdout: (err.stdout || '').trim(), 21 - stderr: (err.stderr || '').trim(), 22 - exitCode: err.status, 23 - }; 24 - } 25 - } 26 10 27 11 describe('vit init --beacon', () => { 28 12 let tmpDir;