using-vit

仓库: solpbc/vit
安装量: 42
排名: #17435

安装

npx skills add https://github.com/solpbc/vit --skill using-vit
  1. Overview 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.
  2. Prerequisites Dependency chain: setup → login → init → follow → skim/ship . 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.
  3. Agent Workflow Run vit init to initialize .vit/ directory (derives beacon from git remotes). Run vit follow to follow accounts whose caps you want to see. Run vit skim --json to read caps from followed accounts filtered by beacon. Run vit ship --title --description --ref <<'EOF' ... EOF to publish a cap (body on stdin). Handoffs: If no DID is configured, tell the user to run vit login . If the user wants to review a cap, tell them to run vit vet in their terminal.
  4. Commands the Agent Runs Agent-only commands vit init Description: Initialize .vit/ and set beacon data for the current repo. Usage: vit init Key flags: --beacon , --verbose Output: text, including beacon: vit:... on success. Common errors: no git remote. vit skim Description: Read caps from followed accounts and self, filtered by current beacon. Usage: vit skim Key flags: --handle , --did , --limit (default 25), --json , --verbose Output: prefer --json (JSON array of ATProto records); text mode prints ref , title , and description per cap. Common errors: no DID, no beacon, no following, session expired. vit remix Description: Derive a vetted cap into the current codebase and output an implementation plan. Usage: vit remix Key flags: --did , --verbose Output: text pretext block with cap content to stdout (consumed by the calling agent). Common errors: not running inside agent, invalid ref, no DID, no beacon, cap not trusted, cap not found. Agent-usable commands vit doctor Description: Read-only diagnostic for setup and beacon status. Usage: vit doctor Key flags: none. Output: text status lines for setup and beacon. Common errors: generic runtime or config read failures. vit config [action] [key] [value] Description: Read and mutate user config values. Usage: vit config [action] [key] [value] Key flags: none. Output: key=value lines for list ; silent success for set and delete . Common errors: invalid action; missing arguments for set or delete . vit follow Description: Add an account to .vit/following.json . Usage: vit follow Key flags: --did , -v, --verbose Output: following () . Common errors: no DID, duplicate handle, handle resolution failure. vit unfollow Description: Remove an account from .vit/following.json . Usage: vit unfollow Key flags: -v, --verbose Output: unfollowed . Common errors: not following that handle. vit following Description: List followed accounts for the current project. Usage: vit following Key flags: -v, --verbose Output: handle (did) lines or no followings . Common errors: malformed following file content. vit ship Description: Publish a cap to ATProto from stdin body input. Usage: vit ship --title --description <description> --ref <ref> [--recap <ref>] <<'EOF' ... EOF Key flags: required --title <title> , --description <description> , --ref <ref> ; optional --recap <ref> , --did <did> , -v, --verbose Input: cap body is required via stdin (pipe or heredoc). Gate: agent-only ( requireAgent() ). Output: JSON object on success. Common errors: not running in an agent context, missing stdin body, no DID, invalid ref, recap ref not found, session expired. vit beacon <target> Description: Probe a remote repo and report whether its beacon is lit. Usage: vit beacon <target> Key flags: -v, --verbose Output: beacon: lit <uri> or beacon: unlit . Common errors: invalid target URL or clone/probe failure.</li> <li>Commands the Agent Must NOT Run These commands require human interaction. Tell the user exactly what to run: vit setup</li> <li>Tell user: "Run vit setup in your terminal to check prerequisites (git, bun)." vit login <handle></li> <li>Tell user: "Run vit login <handle> in your terminal to authenticate via browser OAuth." vit adopt <beacon></li> <li>Tell user: "Run vit adopt <beacon> in your terminal to fork and clone a project." vit vet <ref></li> <li>Tell user: "Run vit vet <ref> in your terminal to review a cap." Mention --trust flag for approving. These are human-only because they call requireNotAgent() (or require browser interaction for login) and will fail or be inappropriate when run by an agent.</li> <li>Error Handling Error Cause Resolution no DID configured User hasn't logged in Tell user to run vit login <handle> no beacon set .vit/ not initialized or no beacon Run vit init no followings / empty skim results No accounts followed Run vit follow <handle> Session errors (deleted/expired) OAuth session invalid Tell user to run vit login <handle> Invalid ref format Ref doesn't match ^[a-z]+-[a-z]+-[a-z]+$ Use three lowercase words joined by hyphens</li> <li>Data Files .vit/config.json - { "beacon": "vit:host/org/repo" } .vit/following.json - [{ "handle": "...", "did": "...", "followedAt": "..." }] .vit/caps.jsonl</li> <li>Append-only shipped cap log .vit/trusted.jsonl</li> <li>Append-only vetted cap log ~/.config/vit/vit.json</li> <li>User config with did , timestamps</li> <li>Reference See COMMANDS.md for full option details and examples.</li> </ol> </article> <a href="/" class="back-link">← <span data-i18n="detail.backToLeaderboard">返回排行榜</span></a> </div> <aside class="sidebar"> <section class="related-skills" id="relatedSkillsSection"> <h2 class="related-title" data-i18n="detail.relatedSkills">相关 Skills</h2> <div class="related-list" id="relatedSkillsList"> <div class="skeleton-card"></div> <div class="skeleton-card"></div> <div class="skeleton-card"></div> </div> </section> </aside> </div> </div> <script src="https://unpkg.com/i18next@23.11.5/i18next.min.js" defer></script> <script src="https://unpkg.com/i18next-browser-languagedetector@7.2.1/i18nextBrowserLanguageDetector.min.js" defer></script> <script defer> // Language resources - same pattern as index page const resources = { 'zh-CN': null, 'en': null, 'ja': null, 'ko': null, 'zh-TW': null, 'es': null, 'fr': null }; // Load language files (only current + fallback for performance) async function loadLanguageResources() { const savedLang = localStorage.getItem('i18nextLng') || 'en'; const langsToLoad = new Set([savedLang, 'en']); // current + fallback await Promise.all([...langsToLoad].map(async (lang) => { try { const response = await fetch(`/locales/${lang}.json`); if (response.ok) { resources[lang] = { translation: await response.json() }; } } catch (error) { console.warn(`Failed to load ${lang} language file:`, error); } })); } // Load a single language on demand (for language switching) async function loadLanguage(lang) { if (resources[lang]) return; try { const response = await fetch(`/locales/${lang}.json`); if (response.ok) { resources[lang] = { translation: await response.json() }; i18next.addResourceBundle(lang, 'translation', resources[lang].translation); } } catch (error) { console.warn(`Failed to load ${lang} language file:`, error); } } // Initialize i18next async function initI18n() { try { await loadLanguageResources(); // Filter out null values from resources const validResources = {}; for (const [lang, data] of Object.entries(resources)) { if (data !== null) { validResources[lang] = data; } } console.log('Loaded languages:', Object.keys(validResources)); console.log('zh-CN resource:', validResources['zh-CN']); console.log('detail.home in resource:', validResources['zh-CN']?.translation?.detail?.home); // 检查是否有保存的语言偏好 const savedLang = localStorage.getItem('i18nextLng'); // 如果没有保存的语言偏好,默认使用英文 const defaultLang = savedLang && ['zh-CN', 'en', 'ja', 'ko', 'zh-TW', 'es', 'fr'].includes(savedLang) ? savedLang : 'en'; await i18next .use(i18nextBrowserLanguageDetector) .init({ lng: defaultLang, // 强制设置初始语言 fallbackLng: 'en', supportedLngs: ['zh-CN', 'en', 'ja', 'ko', 'zh-TW', 'es', 'fr'], resources: validResources, detection: { order: ['localStorage'], // 只使用 localStorage,不检测浏览器语言 caches: ['localStorage'], lookupLocalStorage: 'i18nextLng' }, interpolation: { escapeValue: false } }); console.log('i18next initialized, language:', i18next.language); console.log('Test translation:', i18next.t('detail.home')); // Set initial language in selector const langSwitcher = document.getElementById('langSwitcher'); langSwitcher.value = i18next.language; // Update page language updatePageLanguage(); // Language switch event langSwitcher.addEventListener('change', async (e) => { await loadLanguage(e.target.value); // load on demand i18next.changeLanguage(e.target.value).then(() => { updatePageLanguage(); localStorage.setItem('i18nextLng', e.target.value); }); }); } catch (error) { console.error('i18next init failed:', error); } } // Translation helper function t(key, options = {}) { return i18next.t(key, options); } // Update all translatable elements function updatePageLanguage() { // Update HTML lang attribute document.documentElement.lang = i18next.language; // Update elements with data-i18n attribute document.querySelectorAll('[data-i18n]').forEach(el => { const key = el.getAttribute('data-i18n'); el.textContent = t(key); }); } // Copy command function function copyCommand() { const command = document.getElementById('installCommand').textContent; const btn = document.getElementById('copyBtn'); navigator.clipboard.writeText(command).then(() => { btn.textContent = t('copied'); btn.classList.add('copied'); setTimeout(() => { btn.textContent = t('copy'); btn.classList.remove('copied'); }, 2000); }).catch(() => { // Fallback for non-HTTPS const textArea = document.createElement('textarea'); textArea.value = command; textArea.style.position = 'fixed'; textArea.style.left = '-9999px'; document.body.appendChild(textArea); textArea.select(); document.execCommand('copy'); document.body.removeChild(textArea); btn.textContent = t('copied'); btn.classList.add('copied'); setTimeout(() => { btn.textContent = t('copy'); btn.classList.remove('copied'); }, 2000); }); } // Initialize document.getElementById('copyBtn').addEventListener('click', copyCommand); initI18n(); // 异步加载相关 Skills async function loadRelatedSkills() { const owner = 'solpbc'; const skillName = 'using-vit'; const currentLang = 'es'; const listContainer = document.getElementById('relatedSkillsList'); const section = document.getElementById('relatedSkillsSection'); try { const response = await fetch(`/api/related-skills/${encodeURIComponent(owner)}/${encodeURIComponent(skillName)}?limit=6`); if (!response.ok) { throw new Error('Failed to load'); } const data = await response.json(); const relatedSkills = data.related_skills || []; if (relatedSkills.length === 0) { // 没有相关推荐时隐藏整个区域 section.style.display = 'none'; return; } // 渲染相关 Skills listContainer.innerHTML = relatedSkills.map(skill => { const desc = skill.description || ''; const truncatedDesc = desc.length > 60 ? desc.substring(0, 60) + '...' : desc; return ` <a href="${currentLang === 'en' ? '' : '/' + currentLang}/skill/${skill.owner}/${skill.repo}/${skill.skill_name}" class="related-card"> <div class="related-name">${escapeHtml(skill.skill_name)}</div> <div class="related-meta"> <span class="related-owner">${escapeHtml(skill.owner)}</span> <span class="related-installs">${skill.installs}</span> </div> <div class="related-desc">${escapeHtml(truncatedDesc)}</div> </a> `; }).join(''); } catch (error) { console.error('Failed to load related skills:', error); // 加载失败时显示提示或隐藏 listContainer.innerHTML = '<div class="related-empty">暂无相关推荐</div>'; } } // HTML 转义 function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } // 页面加载完成后异步加载相关 Skills if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', loadRelatedSkills); } else { loadRelatedSkills(); } </script> </body> </html>