open source is social v-it.org
0
fork

Configure Feed

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

at main 195 lines 7.3 kB view raw
1// SPDX-License-Identifier: MIT 2// Copyright (c) 2026 sol pbc 3 4import { loadConfig } from '../lib/config.js'; 5import { restoreAgent } from '../lib/oauth.js'; 6import { readProjectConfig } from '../lib/vit-dir.js'; 7import { existsSync, lstatSync, readdirSync, readFileSync } from 'node:fs'; 8import { join } from 'node:path'; 9import { homedir } from 'node:os'; 10import { mark, name } from '../lib/brand.js'; 11import { which } from '../lib/compat.js'; 12import { jsonOk, jsonError } from '../lib/json-output.js'; 13import { configPath } from '../lib/paths.js'; 14import { errorMessage, formatError } from '../lib/error-format.js'; 15 16function scanSkillDir(dir) { 17 const skills = []; 18 if (!existsSync(dir)) return skills; 19 try { 20 const entries = readdirSync(dir, { withFileTypes: true }); 21 for (const entry of entries) { 22 if (!entry.isDirectory()) continue; 23 const skillMd = join(dir, entry.name, 'SKILL.md'); 24 if (existsSync(skillMd)) { 25 let version = null; 26 try { 27 const content = readFileSync(skillMd, 'utf-8'); 28 const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/); 29 if (match) { 30 const versionMatch = match[1].match(/^version:\s*(.+)$/m); 31 if (versionMatch) version = versionMatch[1].trim(); 32 } 33 } catch (err) { 34 console.warn(`warning: failed to read ${skillMd}: ${errorMessage(err)}`); 35 } 36 skills.push({ name: entry.name, version }); 37 } 38 } 39 } catch (err) { 40 console.warn(`warning: failed to read skill directory ${dir}: ${errorMessage(err)}`); 41 } 42 return skills; 43} 44 45export default function register(program) { 46 async function checkHealth(opts) { 47 try { 48 const config = loadConfig(); 49 let installType = 'not on PATH'; 50 let vitPath = which(name); 51 let installPath = vitPath || null; 52 let beacon = null; 53 let skillInstalled = false; 54 let projectSkills = []; 55 let userSkills = []; 56 let blueskyOk = false; 57 let pds = null; 58 59 if (!vitPath) { 60 installType = 'not on PATH'; 61 if (!opts.json) console.log(`${mark} install: not on PATH`); 62 } else { 63 try { 64 if (lstatSync(vitPath).isSymbolicLink()) { 65 installType = 'linked'; 66 if (!opts.json) console.log(`${mark} install: linked (${vitPath})`); 67 } else if (vitPath.includes('node_modules')) { 68 installType = 'global'; 69 if (!opts.json) console.log(`${mark} install: global`); 70 } else { 71 installType = 'source'; 72 if (!opts.json) console.log(`${mark} install: source (${vitPath})`); 73 } 74 } catch (err) { 75 console.warn(`warning: failed to inspect install path ${vitPath}: ${errorMessage(err)}`); 76 installType = 'source'; 77 if (!opts.json) console.log(`${mark} install: source (${vitPath})`); 78 } 79 } 80 81 const projConfig = readProjectConfig(); 82 beacon = projConfig.beacon || null; 83 if (projConfig.beacon) { 84 if (!opts.json) console.log(`${mark} beacon: ${projConfig.beacon}`); 85 } else { 86 if (!opts.json) console.log(`${mark} beacon: not set (run vit init)`); 87 } 88 89 const projectSkillPath = join(process.cwd(), '.claude', 'skills', 'using-vit', 'SKILL.md'); 90 const userSkillPath = join(homedir(), '.claude', 'skills', 'using-vit', 'SKILL.md'); 91 skillInstalled = existsSync(projectSkillPath) || existsSync(userSkillPath); 92 if (skillInstalled) { 93 if (!opts.json) console.log(`${mark} skill: ok (using-vit)`); 94 } else { 95 if (!opts.json) console.log(`${mark} skill: not installed (reinstall vit)`); 96 } 97 98 // Report installed skills 99 const projectSkillDir = join(process.cwd(), '.claude', 'skills'); 100 projectSkills = scanSkillDir(projectSkillDir); 101 const userSkillDir = join(homedir(), '.claude', 'skills'); 102 userSkills = scanSkillDir(userSkillDir); 103 104 if (!opts.json && projectSkills.length > 0) { 105 console.log(`${mark} project skills: ${projectSkills.length} installed`); 106 for (const s of projectSkills) { 107 const ver = s.version ? ` v${s.version}` : ''; 108 console.log(` ${s.name}${ver}`); 109 } 110 } 111 if (!opts.json && userSkills.length > 0) { 112 console.log(`${mark} user skills: ${userSkills.length} installed`); 113 for (const s of userSkills) { 114 const ver = s.version ? ` v${s.version}` : ''; 115 console.log(` ${s.name}${ver}`); 116 } 117 } 118 119 let effectiveDid = config.did; 120 let identitySource = effectiveDid ? 'global' : null; 121 let authType = 'oauth'; 122 123 const localLoginPath = join(process.cwd(), '.vit', 'login.json'); 124 try { 125 if (existsSync(localLoginPath)) { 126 const local = JSON.parse(readFileSync(localLoginPath, 'utf-8')); 127 if (local.did) { 128 effectiveDid = local.did; 129 identitySource = 'local'; 130 authType = local.type || 'oauth'; 131 } 132 } 133 } catch (err) { 134 console.warn(`warning: failed to read ${localLoginPath}: ${errorMessage(err)}`); 135 } 136 137 if (!identitySource && effectiveDid) identitySource = 'global'; 138 139 if (identitySource === 'global' && effectiveDid) { 140 const sessionFile = configPath('session.json'); 141 try { 142 if (existsSync(sessionFile)) { 143 const raw = readFileSync(sessionFile, 'utf-8'); 144 const sessionData = JSON.parse(raw); 145 if (sessionData[effectiveDid]?.type === 'app-password') authType = 'app-password'; 146 } 147 } catch (err) { 148 console.warn(`warning: failed to read ${sessionFile}: ${errorMessage(err)}`); 149 } 150 } 151 152 if (!effectiveDid) { 153 if (!opts.json) console.log(`${mark} bluesky: not logged in (run ${name} login <handle>)`); 154 } else { 155 try { 156 const { session } = await restoreAgent(effectiveDid); 157 blueskyOk = true; 158 pds = session.serverMetadata?.issuer || null; 159 if (!opts.json) console.log(`${mark} bluesky: ok (${session.did || effectiveDid}${pds ? ', ' + pds : ''})`); 160 } catch (err) { 161 console.warn(`warning: failed to validate Bluesky session: ${errorMessage(err)}`); 162 if (!opts.json) console.log(`${mark} bluesky: token expired or invalid (run ${name} login <handle>)`); 163 } 164 if (!opts.json) console.log(`${mark} identity: ${identitySource} (${authType})`); 165 } 166 167 if (opts.json) { 168 jsonOk({ 169 install: { type: installType, path: installPath }, 170 beacon, 171 skill: skillInstalled, 172 projectSkills, 173 userSkills, 174 bluesky: { ok: blueskyOk, did: effectiveDid || null, pds, source: identitySource, authType }, 175 }); 176 } 177 } catch (err) { 178 if (opts.json) { 179 jsonError(err); 180 return; 181 } 182 console.error(formatError(err, { verbose: false })); 183 process.exitCode = 1; 184 } 185 } 186 187 program.command('doctor') 188 .description('Verify vit environment and project configuration') 189 .option('--json', 'Output as JSON') 190 .action(checkHealth); 191 program.command('status') 192 .description('Alias for doctor') 193 .option('--json', 'Output as JSON') 194 .action(checkHealth); 195}