open source is social v-it.org
0
fork

Configure Feed

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

fix skim/vouch beacon ordering, add tests for skills feature

Fix skim to require beacon in default mode (matching original behavior),
only skip beacon check in --skills-only mode. Fix vouch to check beacon
before trusted for cap refs (matching original behavior).

Tests added:
- skill-ref: name validation, ref format, round-trip conversion
- ship --skill: SKILL.md parsing, name validation, no-beacon requirement
- learn: trust gate (vet required, skip-perms bypass, --user always requires vet)
- skim --skills: beacon requirements per mode
- vet skill: ref format, human-only gate, no-beacon for skills
- vouch skill: ref format, no-beacon for skills, beacon for caps

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

+479 -34
+8 -12
src/cmd/skim.js
··· 40 40 const projectConfig = readProjectConfig(); 41 41 const beacon = projectConfig.beacon; 42 42 43 - // Caps require a beacon; skills-only mode does not 44 43 const wantCaps = !opts.skills; 45 44 const wantSkills = !opts.caps; 45 + const skillsOnly = opts.skills && !opts.caps; 46 46 47 - if (wantCaps && !beacon) { 48 - if (!wantSkills) { 49 - // Caps-only mode and no beacon 50 - console.error(`no beacon set. run '${name} init' in a project directory first.`); 51 - process.exitCode = 1; 52 - return; 53 - } 54 - // Mixed mode with no beacon: just show skills 55 - if (verbose) console.log('[verbose] no beacon set, showing skills only'); 47 + // Beacon required unless --skills only mode 48 + if (!beacon && !skillsOnly) { 49 + console.error(`no beacon set. run '${name} init' in a project directory first.`); 50 + process.exitCode = 1; 51 + return; 56 52 } 57 53 58 54 if (verbose && beacon) console.log(`[verbose] beacon: ${beacon}`); ··· 141 137 console.log(JSON.stringify(capped, null, 2)); 142 138 } else { 143 139 if (capped.length === 0) { 144 - if (wantSkills && !wantCaps) { 140 + if (skillsOnly) { 145 141 console.log('no skills found.'); 146 - } else if (wantCaps && !wantSkills) { 142 + } else if (opts.caps) { 147 143 console.log('no caps found for this beacon.'); 148 144 } else { 149 145 console.log('no caps or skills found.');
+38 -22
src/cmd/vouch.js
··· 42 42 if (!did) return; 43 43 if (verbose) console.log(`[verbose] DID: ${did}`); 44 44 45 - // Check trusted 46 - const trusted = readLog('trusted.jsonl'); 47 - const trustedEntry = trusted.find(e => e.ref === ref); 48 - if (!trustedEntry) { 49 - const itemType = isSkill ? 'skill' : 'cap'; 50 - console.error(`${itemType} '${ref}' is not yet vetted. vet it first:`); 51 - console.error(''); 52 - console.error(` vit vet ${ref}`); 53 - console.error(''); 54 - console.error('after reviewing, trust it with:'); 55 - console.error(''); 56 - console.error(` vit vet ${ref} --trust`); 57 - process.exitCode = 1; 58 - return; 59 - } 60 - if (verbose) console.log(`[verbose] trusted entry found, uri: ${trustedEntry.uri}`); 61 - 62 - const { agent } = await restoreAgent(did); 63 - if (verbose) console.log('[verbose] session restored'); 64 - 65 45 if (isSkill) { 66 - // Skill vouch — no beacon required 46 + // Skill vouch — no beacon required, check trusted first 47 + const trusted = readLog('trusted.jsonl'); 48 + const trustedEntry = trusted.find(e => e.ref === ref); 49 + if (!trustedEntry) { 50 + console.error(`skill '${ref}' is not yet vetted. vet it first:`); 51 + console.error(''); 52 + console.error(` vit vet ${ref}`); 53 + console.error(''); 54 + console.error('after reviewing, trust it with:'); 55 + console.error(''); 56 + console.error(` vit vet ${ref} --trust`); 57 + process.exitCode = 1; 58 + return; 59 + } 60 + if (verbose) console.log(`[verbose] trusted entry found, uri: ${trustedEntry.uri}`); 61 + 67 62 const skillName = nameFromSkillRef(ref); 63 + 64 + const { agent } = await restoreAgent(did); 65 + if (verbose) console.log('[verbose] session restored'); 68 66 69 67 const following = readFollowing(); 70 68 const dids = following.map(e => e.did); ··· 131 129 132 130 console.log(`${mark} vouched: ${ref} (${match.uri})`); 133 131 } else { 134 - // Cap vouch — requires beacon 132 + // Cap vouch — requires beacon (check beacon before trusted, matching original behavior) 135 133 const projectConfig = readProjectConfig(); 136 134 const beacon = projectConfig.beacon; 137 135 if (!beacon) { ··· 140 138 return; 141 139 } 142 140 if (verbose) console.log(`[verbose] beacon: ${beacon}`); 141 + 142 + const trusted = readLog('trusted.jsonl'); 143 + const trustedEntry = trusted.find(e => e.ref === ref); 144 + if (!trustedEntry) { 145 + console.error(`cap '${ref}' is not yet vetted. vet it first:`); 146 + console.error(''); 147 + console.error(` vit vet ${ref}`); 148 + console.error(''); 149 + console.error('after reviewing, trust it with:'); 150 + console.error(''); 151 + console.error(` vit vet ${ref} --trust`); 152 + process.exitCode = 1; 153 + return; 154 + } 155 + if (verbose) console.log(`[verbose] trusted entry found, uri: ${trustedEntry.uri}`); 156 + 157 + const { agent } = await restoreAgent(did); 158 + if (verbose) console.log('[verbose] session restored'); 143 159 144 160 const following = readFollowing(); 145 161 const dids = following.map(e => e.did);
+103
test/learn.test.js
··· 1 + // SPDX-License-Identifier: AGPL-3.0-only 2 + // Copyright (c) 2026 sol pbc 3 + 4 + import { describe, test, expect } from 'bun:test'; 5 + import { run } from './helpers.js'; 6 + import { mkdirSync, writeFileSync, rmSync } from 'node:fs'; 7 + import { tmpdir } from 'node:os'; 8 + import { join } from 'node:path'; 9 + 10 + const agentEnv = { CLAUDECODE: '1' }; 11 + 12 + describe('vit learn', () => { 13 + test('rejects when run outside a coding agent', () => { 14 + const r = run('learn skill-test', '/tmp', { CLAUDECODE: '', GEMINI_CLI: '', CODEX_CI: '' }); 15 + expect(r.exitCode).not.toBe(0); 16 + expect(r.stderr).toContain('should be run by a coding agent'); 17 + }); 18 + 19 + test('rejects non-skill ref format', () => { 20 + const r = run('learn fast-cache-invalidation', '/tmp', agentEnv); 21 + expect(r.exitCode).not.toBe(0); 22 + expect(r.stderr).toContain('invalid skill ref'); 23 + }); 24 + 25 + test('rejects invalid skill name in ref', () => { 26 + const r = run('learn skill-Bad-Name', '/tmp', agentEnv); 27 + expect(r.exitCode).not.toBe(0); 28 + expect(r.stderr).toContain('invalid skill ref'); 29 + }); 30 + 31 + test('rejects skill ref with consecutive hyphens', () => { 32 + const r = run('learn skill-bad--name', '/tmp', agentEnv); 33 + expect(r.exitCode).not.toBe(0); 34 + expect(r.stderr).toContain('invalid skill ref'); 35 + }); 36 + 37 + test('fails when no arguments provided', () => { 38 + const r = run('learn', '/tmp', agentEnv); 39 + expect(r.exitCode).not.toBe(0); 40 + }); 41 + 42 + test('requires vet for --user install', () => { 43 + const tmp = join(tmpdir(), '.test-learn-user-' + Math.random().toString(36).slice(2)); 44 + mkdirSync(join(tmp, '.vit'), { recursive: true }); 45 + const r = run('learn skill-test --user --did did:plc:test123', tmp, agentEnv); 46 + expect(r.exitCode).not.toBe(0); 47 + expect(r.stderr).toContain('not yet vetted'); 48 + expect(r.stderr).toContain('user-wide install requires vetting'); 49 + rmSync(tmp, { recursive: true, force: true }); 50 + }); 51 + 52 + test('requires vet for project-level install without skip-perms', () => { 53 + const tmp = join(tmpdir(), '.test-learn-proj-' + Math.random().toString(36).slice(2)); 54 + mkdirSync(join(tmp, '.vit'), { recursive: true }); 55 + const r = run('learn skill-test --did did:plc:test123', tmp, agentEnv); 56 + expect(r.exitCode).not.toBe(0); 57 + expect(r.stderr).toContain('not yet vetted'); 58 + rmSync(tmp, { recursive: true, force: true }); 59 + }); 60 + 61 + test('errors when no DID configured (with skip-perms for project-level)', () => { 62 + // Use skip-perms to bypass vet, then hit DID check 63 + const configHome = join(tmpdir(), '.test-learn-config-' + Math.random().toString(36).slice(2)); 64 + mkdirSync(configHome, { recursive: true }); 65 + const r = run('learn skill-test', '/tmp', { ...agentEnv, XDG_CONFIG_HOME: configHome, CLAUDE_SKIP_PERMISSIONS: '1' }); 66 + expect(r.exitCode).not.toBe(0); 67 + expect(r.stderr).toContain('no DID configured'); 68 + rmSync(configHome, { recursive: true, force: true }); 69 + }); 70 + 71 + test('trust gate: vet check happens before network call', () => { 72 + // Even with a valid DID, should fail at vet check 73 + const tmp = join(tmpdir(), '.test-learn-trust-' + Math.random().toString(36).slice(2)); 74 + mkdirSync(join(tmp, '.vit'), { recursive: true }); 75 + const r = run('learn skill-test --did did:plc:test123', tmp, agentEnv); 76 + expect(r.exitCode).not.toBe(0); 77 + expect(r.stderr).toContain('not yet vetted'); 78 + expect(r.stderr).toContain('vit vet skill-test'); 79 + rmSync(tmp, { recursive: true, force: true }); 80 + }); 81 + 82 + test('trust gate: skip-perms bypasses vet for project-level', () => { 83 + // With skip-perms, should pass vet and fail at auth 84 + const tmp = join(tmpdir(), '.test-learn-skip-' + Math.random().toString(36).slice(2)); 85 + mkdirSync(join(tmp, '.vit'), { recursive: true }); 86 + const r = run('learn skill-test --did did:plc:test123', tmp, { ...agentEnv, CLAUDE_SKIP_PERMISSIONS: '1' }); 87 + expect(r.exitCode).not.toBe(0); 88 + // Should NOT fail at vet check 89 + expect(r.stderr).not.toContain('not yet vetted'); 90 + rmSync(tmp, { recursive: true, force: true }); 91 + }); 92 + 93 + test('trust gate: skip-perms does NOT bypass vet for --user', () => { 94 + const tmp = join(tmpdir(), '.test-learn-skip-user-' + Math.random().toString(36).slice(2)); 95 + mkdirSync(join(tmp, '.vit'), { recursive: true }); 96 + const r = run('learn skill-test --user --did did:plc:test123', tmp, { ...agentEnv, CLAUDE_SKIP_PERMISSIONS: '1' }); 97 + expect(r.exitCode).not.toBe(0); 98 + // Should STILL fail at vet check for --user 99 + expect(r.stderr).toContain('not yet vetted'); 100 + expect(r.stderr).toContain('user-wide install requires vetting'); 101 + rmSync(tmp, { recursive: true, force: true }); 102 + }); 103 + });
+106
test/ship-skill.test.js
··· 1 + // SPDX-License-Identifier: AGPL-3.0-only 2 + // Copyright (c) 2026 sol pbc 3 + 4 + import { describe, test, expect } from 'bun:test'; 5 + import { run } from './helpers.js'; 6 + import { mkdirSync, writeFileSync, rmSync } from 'node:fs'; 7 + import { tmpdir } from 'node:os'; 8 + import { join } from 'node:path'; 9 + 10 + const agentEnv = { CLAUDECODE: '1' }; 11 + 12 + describe('vit ship --skill', () => { 13 + test('rejects when run outside a coding agent', () => { 14 + const tmp = join(tmpdir(), '.test-ship-skill-gate-' + Math.random().toString(36).slice(2)); 15 + mkdirSync(tmp, { recursive: true }); 16 + writeFileSync(join(tmp, 'SKILL.md'), '---\nname: test\ndescription: test skill\n---\n# Test'); 17 + const r = run(`ship --skill ${tmp}`, '/tmp', { CLAUDECODE: '', GEMINI_CLI: '', CODEX_CI: '' }); 18 + expect(r.exitCode).not.toBe(0); 19 + expect(r.stderr).toContain('should be run by a coding agent'); 20 + rmSync(tmp, { recursive: true, force: true }); 21 + }); 22 + 23 + test('errors when SKILL.md is missing', () => { 24 + const tmp = join(tmpdir(), '.test-ship-skill-no-md-' + Math.random().toString(36).slice(2)); 25 + mkdirSync(tmp, { recursive: true }); 26 + const r = run(`ship --skill ${tmp}`, '/tmp', agentEnv); 27 + expect(r.exitCode).not.toBe(0); 28 + expect(r.stderr).toContain('no SKILL.md found'); 29 + rmSync(tmp, { recursive: true, force: true }); 30 + }); 31 + 32 + test('errors when SKILL.md has no name', () => { 33 + const tmp = join(tmpdir(), '.test-ship-skill-no-name-' + Math.random().toString(36).slice(2)); 34 + mkdirSync(tmp, { recursive: true }); 35 + writeFileSync(join(tmp, 'SKILL.md'), '---\ndescription: test\n---\n# Test'); 36 + const r = run(`ship --skill ${tmp} --did did:plc:abc`, '/tmp', agentEnv); 37 + expect(r.exitCode).not.toBe(0); 38 + expect(r.stderr).toContain('name'); 39 + rmSync(tmp, { recursive: true, force: true }); 40 + }); 41 + 42 + test('errors when SKILL.md has no description', () => { 43 + const tmp = join(tmpdir(), '.test-ship-skill-no-desc-' + Math.random().toString(36).slice(2)); 44 + mkdirSync(tmp, { recursive: true }); 45 + writeFileSync(join(tmp, 'SKILL.md'), '---\nname: test\n---\n# Test'); 46 + const r = run(`ship --skill ${tmp} --did did:plc:abc`, '/tmp', agentEnv); 47 + expect(r.exitCode).not.toBe(0); 48 + expect(r.stderr).toContain('description'); 49 + rmSync(tmp, { recursive: true, force: true }); 50 + }); 51 + 52 + test('rejects invalid skill name (uppercase)', () => { 53 + const tmp = join(tmpdir(), '.test-ship-skill-bad-name-' + Math.random().toString(36).slice(2)); 54 + mkdirSync(tmp, { recursive: true }); 55 + writeFileSync(join(tmp, 'SKILL.md'), '---\nname: BadName\ndescription: test\n---\n# Test'); 56 + const r = run(`ship --skill ${tmp} --did did:plc:abc`, '/tmp', agentEnv); 57 + expect(r.exitCode).not.toBe(0); 58 + expect(r.stderr).toContain('lowercase'); 59 + rmSync(tmp, { recursive: true, force: true }); 60 + }); 61 + 62 + test('rejects skill name starting with hyphen', () => { 63 + const tmp = join(tmpdir(), '.test-ship-skill-hyphen-' + Math.random().toString(36).slice(2)); 64 + mkdirSync(tmp, { recursive: true }); 65 + writeFileSync(join(tmp, 'SKILL.md'), '---\nname: -bad\ndescription: test\n---\n# Test'); 66 + const r = run(`ship --skill ${tmp} --did did:plc:abc`, '/tmp', agentEnv); 67 + expect(r.exitCode).not.toBe(0); 68 + expect(r.stderr).toContain('lowercase'); 69 + rmSync(tmp, { recursive: true, force: true }); 70 + }); 71 + 72 + test('passes validation with valid skill (fails at auth, not validation)', () => { 73 + const tmp = join(tmpdir(), '.test-ship-skill-valid-' + Math.random().toString(36).slice(2)); 74 + mkdirSync(tmp, { recursive: true }); 75 + writeFileSync(join(tmp, 'SKILL.md'), '---\nname: test-skill\ndescription: a test skill\n---\n# Test'); 76 + const r = run(`ship --skill ${tmp} --did did:plc:abc`, '/tmp', agentEnv); 77 + expect(r.exitCode).not.toBe(0); 78 + // Should fail at auth, not at validation 79 + expect(r.stderr).not.toContain('no SKILL.md'); 80 + expect(r.stderr).not.toContain('name'); 81 + expect(r.stderr).not.toContain('description'); 82 + rmSync(tmp, { recursive: true, force: true }); 83 + }); 84 + 85 + test('can parse the existing vit skill SKILL.md', () => { 86 + // Use the real skill directory in the vit repo 87 + const skillDir = join(import.meta.dir, '..', 'skills', 'vit'); 88 + const r = run(`ship --skill ${skillDir} --did did:plc:abc`, '/tmp', agentEnv); 89 + expect(r.exitCode).not.toBe(0); 90 + // Should fail at auth, not at SKILL.md parsing 91 + expect(r.stderr).not.toContain('no SKILL.md'); 92 + expect(r.stderr).not.toContain('frontmatter must include'); 93 + expect(r.stderr).not.toContain('skill name must be'); 94 + }); 95 + 96 + test('does not require beacon for skill shipping', () => { 97 + const tmp = join(tmpdir(), '.test-ship-skill-no-beacon-' + Math.random().toString(36).slice(2)); 98 + mkdirSync(tmp, { recursive: true }); 99 + writeFileSync(join(tmp, 'SKILL.md'), '---\nname: test-skill\ndescription: a test skill\n---\n# Test'); 100 + const r = run(`ship --skill ${tmp} --did did:plc:abc`, tmp, agentEnv); 101 + expect(r.exitCode).not.toBe(0); 102 + // Should NOT fail due to beacon 103 + expect(r.stderr).not.toContain('no beacon set'); 104 + rmSync(tmp, { recursive: true, force: true }); 105 + }); 106 + });
+112
test/skill-ref.test.js
··· 1 + // SPDX-License-Identifier: AGPL-3.0-only 2 + // Copyright (c) 2026 sol pbc 3 + 4 + import { describe, test, expect } from 'bun:test'; 5 + import { 6 + isValidSkillName, 7 + isSkillRef, 8 + isValidSkillRef, 9 + skillRefFromName, 10 + nameFromSkillRef, 11 + SKILL_NAME_PATTERN, 12 + SKILL_REF_PATTERN, 13 + SKILL_NAME_MAX, 14 + } from '../src/lib/skill-ref.js'; 15 + 16 + describe('isValidSkillName', () => { 17 + test('accepts simple lowercase names', () => { 18 + expect(isValidSkillName('vit')).toBe(true); 19 + expect(isValidSkillName('agent-test-patterns')).toBe(true); 20 + expect(isValidSkillName('pdf-form-filler')).toBe(true); 21 + }); 22 + 23 + test('accepts names with numbers', () => { 24 + expect(isValidSkillName('tool2')).toBe(true); 25 + expect(isValidSkillName('agent-v2-helper')).toBe(true); 26 + }); 27 + 28 + test('rejects names starting with hyphen', () => { 29 + expect(isValidSkillName('-bad')).toBe(false); 30 + }); 31 + 32 + test('rejects names with consecutive hyphens', () => { 33 + expect(isValidSkillName('bad--name')).toBe(false); 34 + }); 35 + 36 + test('rejects names starting with number', () => { 37 + expect(isValidSkillName('3bad')).toBe(false); 38 + }); 39 + 40 + test('rejects uppercase', () => { 41 + expect(isValidSkillName('BadName')).toBe(false); 42 + expect(isValidSkillName('ALLCAPS')).toBe(false); 43 + }); 44 + 45 + test('rejects empty and null', () => { 46 + expect(isValidSkillName('')).toBe(false); 47 + expect(isValidSkillName(null)).toBe(false); 48 + expect(isValidSkillName(undefined)).toBe(false); 49 + }); 50 + 51 + test('rejects names over 64 chars', () => { 52 + const long = 'a'.repeat(65); 53 + expect(isValidSkillName(long)).toBe(false); 54 + const justRight = 'a'.repeat(64); 55 + expect(isValidSkillName(justRight)).toBe(true); 56 + }); 57 + 58 + test('rejects names with special chars', () => { 59 + expect(isValidSkillName('has_underscore')).toBe(false); 60 + expect(isValidSkillName('has.dot')).toBe(false); 61 + expect(isValidSkillName('has space')).toBe(false); 62 + expect(isValidSkillName('has@sign')).toBe(false); 63 + }); 64 + 65 + test('rejects trailing hyphen', () => { 66 + expect(isValidSkillName('bad-')).toBe(false); 67 + }); 68 + }); 69 + 70 + describe('isSkillRef', () => { 71 + test('detects skill- prefix', () => { 72 + expect(isSkillRef('skill-vit')).toBe(true); 73 + expect(isSkillRef('skill-agent-test')).toBe(true); 74 + }); 75 + 76 + test('rejects non-skill refs', () => { 77 + expect(isSkillRef('fast-cache-invalidation')).toBe(false); 78 + expect(isSkillRef('vit')).toBe(false); 79 + expect(isSkillRef('')).toBe(false); 80 + expect(isSkillRef(null)).toBe(false); 81 + }); 82 + }); 83 + 84 + describe('isValidSkillRef', () => { 85 + test('validates well-formed skill refs', () => { 86 + expect(isValidSkillRef('skill-vit')).toBe(true); 87 + expect(isValidSkillRef('skill-agent-test-patterns')).toBe(true); 88 + expect(isValidSkillRef('skill-pdf2')).toBe(true); 89 + }); 90 + 91 + test('rejects invalid skill refs', () => { 92 + expect(isValidSkillRef('skill-')).toBe(false); 93 + expect(isValidSkillRef('skill--bad')).toBe(false); 94 + expect(isValidSkillRef('skill-Bad')).toBe(false); 95 + expect(isValidSkillRef('not-a-skill-ref')).toBe(false); 96 + expect(isValidSkillRef('')).toBe(false); 97 + }); 98 + }); 99 + 100 + describe('skillRefFromName / nameFromSkillRef', () => { 101 + test('round-trips correctly', () => { 102 + expect(skillRefFromName('vit')).toBe('skill-vit'); 103 + expect(nameFromSkillRef('skill-vit')).toBe('vit'); 104 + expect(nameFromSkillRef(skillRefFromName('agent-test'))).toBe('agent-test'); 105 + }); 106 + 107 + test('nameFromSkillRef returns null for non-skill refs', () => { 108 + expect(nameFromSkillRef('fast-cache-invalidation')).toBe(null); 109 + expect(nameFromSkillRef('')).toBe(null); 110 + expect(nameFromSkillRef(null)).toBe(null); 111 + }); 112 + });
+40
test/skim-skills.test.js
··· 1 + // SPDX-License-Identifier: AGPL-3.0-only 2 + // Copyright (c) 2026 sol pbc 3 + 4 + import { describe, test, expect } from 'bun:test'; 5 + import { run } from './helpers.js'; 6 + import { mkdirSync, rmSync } from 'node:fs'; 7 + import { tmpdir } from 'node:os'; 8 + import { join } from 'node:path'; 9 + 10 + const agentEnv = { CLAUDECODE: '1' }; 11 + 12 + describe('vit skim --skills', () => { 13 + test('still requires beacon in default mode', () => { 14 + const result = run('skim --did did:plc:test123', '/tmp', agentEnv); 15 + expect(result.exitCode).not.toBe(0); 16 + expect(result.stderr).toContain('no beacon set'); 17 + }); 18 + 19 + test('still requires beacon with --caps flag', () => { 20 + const result = run('skim --caps --did did:plc:test123', '/tmp', agentEnv); 21 + expect(result.exitCode).not.toBe(0); 22 + expect(result.stderr).toContain('no beacon set'); 23 + }); 24 + 25 + test('does NOT require beacon with --skills flag', () => { 26 + const configHome = join(tmpdir(), '.test-skim-skills-' + Math.random().toString(36).slice(2)); 27 + mkdirSync(configHome, { recursive: true }); 28 + const result = run('skim --skills --did did:plc:test123', '/tmp', { ...agentEnv, XDG_CONFIG_HOME: configHome }); 29 + expect(result.exitCode).not.toBe(0); 30 + // Should fail at auth, not at beacon check 31 + expect(result.stderr).not.toContain('no beacon set'); 32 + rmSync(configHome, { recursive: true, force: true }); 33 + }); 34 + 35 + test('shows help mentioning --skills and --caps', () => { 36 + const result = run('skim --help'); 37 + expect(result.stdout).toContain('--skills'); 38 + expect(result.stdout).toContain('--caps'); 39 + }); 40 + });
+36
test/vet-skill.test.js
··· 1 + // SPDX-License-Identifier: AGPL-3.0-only 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 vet (skill refs)', () => { 8 + test('accepts skill ref format', () => { 9 + // Should not fail on ref format validation 10 + const result = run('vet skill-agent-test', undefined, { CLAUDECODE: '', GEMINI_CLI: '', CODEX_CI: '' }); 11 + expect(result.exitCode).not.toBe(0); 12 + // Should fail at DID, not ref validation 13 + expect(result.stderr).not.toContain('invalid'); 14 + }); 15 + 16 + test('rejects when run inside a coding agent (skill ref)', () => { 17 + const result = run('vet skill-agent-test', undefined, { CLAUDECODE: '1' }); 18 + expect(result.exitCode).toBe(1); 19 + expect(result.stderr).toContain('must be run by a human'); 20 + expect(result.stderr).toContain('vit vet skill-agent-test'); 21 + expect(result.stderr).toContain('--trust'); 22 + }); 23 + 24 + test('rejects invalid skill ref', () => { 25 + const result = run('vet skill-Bad-Name', undefined, { CLAUDECODE: '', GEMINI_CLI: '', CODEX_CI: '' }); 26 + expect(result.exitCode).not.toBe(0); 27 + expect(result.stderr).toContain('invalid skill ref'); 28 + }); 29 + 30 + test('does NOT require beacon for skill vet', () => { 31 + // Skill vet should not need a beacon 32 + const result = run('vet skill-test --did did:plc:test123', undefined, { CLAUDECODE: '', GEMINI_CLI: '', CODEX_CI: '' }); 33 + expect(result.exitCode).not.toBe(0); 34 + expect(result.stderr).not.toContain('no beacon set'); 35 + }); 36 + });
+36
test/vouch-skill.test.js
··· 1 + // SPDX-License-Identifier: AGPL-3.0-only 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 vouch (skill refs)', () => { 8 + test('accepts skill ref format', () => { 9 + // Should not fail on ref format validation 10 + const result = run('vouch skill-agent-test --did did:plc:test123', '/tmp'); 11 + expect(result.exitCode).not.toBe(0); 12 + // Should fail at trusted check, not ref validation 13 + expect(result.stderr).not.toContain('invalid'); 14 + expect(result.stderr).toContain('not yet vetted'); 15 + }); 16 + 17 + test('rejects invalid skill ref', () => { 18 + const result = run('vouch skill-Bad-Name --did did:plc:test123', '/tmp'); 19 + expect(result.exitCode).not.toBe(0); 20 + expect(result.stderr).toContain('invalid skill ref'); 21 + }); 22 + 23 + test('does NOT require beacon for skill vouches', () => { 24 + // Skill vouch should check trusted, not beacon 25 + const result = run('vouch skill-test --did did:plc:test123', '/tmp'); 26 + expect(result.exitCode).not.toBe(0); 27 + expect(result.stderr).not.toContain('no beacon set'); 28 + expect(result.stderr).toContain('not yet vetted'); 29 + }); 30 + 31 + test('still requires beacon for cap vouches', () => { 32 + const result = run('vouch fast-cache-invalidation --did did:plc:test123', '/tmp'); 33 + expect(result.exitCode).not.toBe(0); 34 + expect(result.stderr).toContain('no beacon set'); 35 + }); 36 + });