open source is social v-it.org
0
fork

Configure Feed

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

feat: seed network with skills, caps, and explore improvements

Publish 4 skills (using-vit, semantic-commits, bun-project, atproto-records)
and 8 caps to bootstrap explore.v-it.org with real content.

- Add dogfooding section to CLAUDE.md with shipping instructions
- Create 3 seed skills under skills/ with genuine agent-useful content
- Ship all 4 skills and 8 feature/fix caps to ATProto
- Add starter follow suggestion (jeremie.com) to getting started page
- Improve explore app: pulse indicator, AT URI record links, recent
activity in stats view, better empty state copy with getting-started links
- Fix putRecord validate flag: custom org.v-it.* collections need
validate: false since the ATProto SDK doesn't have these lexicons

+752 -23
+8
.vit/caps.jsonl
··· 2 2 {"ts":"2026-03-09T13:33:55.478Z","did":"did:plc:gnmc53xy3rhbyepipqv2b3rl","rkey":"3mgmxu7cptc2j","ref":"network-explorer-launch","collection":"org.v-it.cap","pds":"https://bsky.social","uri":"at://did:plc:gnmc53xy3rhbyepipqv2b3rl/org.v-it.cap/3mgmxu7cptc2j","cid":"bafyreicocgzx6ges4nzdybnyowmudqnaiqrxd7hnrzf44c5kn7i65kvfhu"} 3 3 {"ts":"2026-03-09T13:53:08.868Z","did":"did:plc:gnmc53xy3rhbyepipqv2b3rl","rkey":"3mgmywlbrx22k","ref":"clean-vouch-lexicon","collection":"org.v-it.cap","pds":"https://bsky.social","uri":"at://did:plc:gnmc53xy3rhbyepipqv2b3rl/org.v-it.cap/3mgmywlbrx22k","cid":"bafyreifnaurvivxrnuihv74si4yf4og4z25pchy6tfa3ckcn7abmdhdncy"} 4 4 {"ts":"2026-03-24T21:47:34.202Z","did":"did:plc:gnmc53xy3rhbyepipqv2b3rl","rkey":"3mhtkgpstnc2l","ref":"skim-handle-attribution","collection":"org.v-it.cap","pds":"https://bsky.social","uri":"at://did:plc:gnmc53xy3rhbyepipqv2b3rl/org.v-it.cap/3mhtkgpstnc2l","cid":"bafyreiglrfvrpgkzktlsiv3tlqlo5xneoikorjr47af4lb4hv2yyqhmnam"} 5 + {"ts":"2026-03-27T20:01:33.496Z","did":"did:plc:gnmc53xy3rhbyepipqv2b3rl","rkey":"3mi2vvw63gk24","ref":"network-stream-discovery","collection":"org.v-it.cap","pds":"https://bsky.social","uri":"at://did:plc:gnmc53xy3rhbyepipqv2b3rl/org.v-it.cap/3mi2vvw63gk24","cid":"bafyreibo25zuznoqnuw35cq3nqkmtvgrjpsx2ldc2jzsp34xhvtar73lwq"} 6 + {"ts":"2026-03-27T20:01:40.823Z","did":"did:plc:gnmc53xy3rhbyepipqv2b3rl","rkey":"3mi2vw55cyc2l","ref":"skill-trust-installation","collection":"org.v-it.cap","pds":"https://bsky.social","uri":"at://did:plc:gnmc53xy3rhbyepipqv2b3rl/org.v-it.cap/3mi2vw55cyc2l","cid":"bafyreid7io54egvz3w43uqxf5asbdvcoexofteglcz3bqr6epo63juryui"} 7 + {"ts":"2026-03-27T20:01:45.164Z","did":"did:plc:gnmc53xy3rhbyepipqv2b3rl","rkey":"3mi2vwbbu722f","ref":"fork-install-source","collection":"org.v-it.cap","pds":"https://bsky.social","uri":"at://did:plc:gnmc53xy3rhbyepipqv2b3rl/org.v-it.cap/3mi2vwbbu722f","cid":"bafyreifv26nqxd7tvo3dsluzrahrs2w5ifcxjcua4i2avcfpeayzzatl6m"} 8 + {"ts":"2026-03-27T20:01:49.610Z","did":"did:plc:gnmc53xy3rhbyepipqv2b3rl","rkey":"3mi2vwfjmwc25","ref":"cap-derivation-planning","collection":"org.v-it.cap","pds":"https://bsky.social","uri":"at://did:plc:gnmc53xy3rhbyepipqv2b3rl/org.v-it.cap/3mi2vwfjmwc25","cid":"bafyreihoekjx54ueiy4u5rniposfkjmzgzyrd7o2yvcnlkx2iqgrxtlsqq"} 9 + {"ts":"2026-03-27T20:01:55.243Z","did":"did:plc:gnmc53xy3rhbyepipqv2b3rl","rkey":"3mi2vwkvpr22u","ref":"skill-publishing-pipeline","collection":"org.v-it.cap","pds":"https://bsky.social","uri":"at://did:plc:gnmc53xy3rhbyepipqv2b3rl/org.v-it.cap/3mi2vwkvpr22u","cid":"bafyreictzwib4p5q5ilbbvb4fbaipcrywtath7a263bjqx23jteltxnirm"} 10 + {"ts":"2026-03-27T20:02:00.879Z","did":"did:plc:gnmc53xy3rhbyepipqv2b3rl","rkey":"3mi2vwqbqnc24","ref":"human-trust-evaluation","collection":"org.v-it.cap","pds":"https://bsky.social","uri":"at://did:plc:gnmc53xy3rhbyepipqv2b3rl/org.v-it.cap/3mi2vwqbqnc24","cid":"bafyreif36db6r5gmuwugwsu4we25kzujgvmgfabqqvg4pvtde5fncwu6gm"} 11 + {"ts":"2026-03-27T20:02:05.488Z","did":"did:plc:gnmc53xy3rhbyepipqv2b3rl","rkey":"3mi2vwunuzc2r","ref":"protocol-correctness-fixes","collection":"org.v-it.cap","pds":"https://bsky.social","uri":"at://did:plc:gnmc53xy3rhbyepipqv2b3rl/org.v-it.cap/3mi2vwunuzc2r","cid":"bafyreif6rqljykkotp2gizfpoah6l6tqafz3aw7hvj7x6c6zx7aaqkixdm"} 12 + {"ts":"2026-03-27T20:02:12.583Z","did":"did:plc:gnmc53xy3rhbyepipqv2b3rl","rkey":"3mi2vx3hey226","ref":"network-content-seeding","collection":"org.v-it.cap","pds":"https://bsky.social","uri":"at://did:plc:gnmc53xy3rhbyepipqv2b3rl/org.v-it.cap/3mi2vx3hey226","cid":"bafyreianibeog5nybuvmdlodmukv24vgtg6zbg6mx4pyg3iomwadtvviom"}
+4
.vit/skills.jsonl
··· 1 + {"ts":"2026-03-27T20:01:13.542Z","did":"did:plc:gnmc53xy3rhbyepipqv2b3rl","rkey":"3mi2vvcwbes2a","ref":"skill-using-vit","name":"using-vit","collection":"org.v-it.skill","pds":"https://bsky.social","uri":"at://did:plc:gnmc53xy3rhbyepipqv2b3rl/org.v-it.skill/3mi2vvcwbes2a","cid":"bafyreifd7i6lm4xphbd3n4dohx4xt2uqcrmvxnzritiaz6dxejcbden6ei"} 2 + {"ts":"2026-03-27T20:01:19.719Z","did":"did:plc:gnmc53xy3rhbyepipqv2b3rl","rkey":"3mi2vvisqms2l","ref":"skill-semantic-commits","name":"semantic-commits","collection":"org.v-it.skill","pds":"https://bsky.social","uri":"at://did:plc:gnmc53xy3rhbyepipqv2b3rl/org.v-it.skill/3mi2vvisqms2l","cid":"bafyreicd7a67iudpm7itjftllr5skzrbvlaplseyfm6jkzbx5hqcenkg5u"} 3 + {"ts":"2026-03-27T20:01:24.120Z","did":"did:plc:gnmc53xy3rhbyepipqv2b3rl","rkey":"3mi2vvmz2i223","ref":"skill-bun-project","name":"bun-project","collection":"org.v-it.skill","pds":"https://bsky.social","uri":"at://did:plc:gnmc53xy3rhbyepipqv2b3rl/org.v-it.skill/3mi2vvmz2i223","cid":"bafyreic62ataqa5iknanbq4v6r4qrdfbjbhnl5qniazt7sbtwkzimzsgre"} 4 + {"ts":"2026-03-27T20:01:27.396Z","did":"did:plc:gnmc53xy3rhbyepipqv2b3rl","rkey":"3mi2vvq4zp22z","ref":"skill-atproto-records","name":"atproto-records","collection":"org.v-it.skill","pds":"https://bsky.social","uri":"at://did:plc:gnmc53xy3rhbyepipqv2b3rl/org.v-it.skill/3mi2vvq4zp22z","cid":"bafyreieqzawlaakdzibizqtc2hjmdteplsgzvktlf774k7mc53ych653mi"}
+17
CLAUDE.md
··· 59 59 - Use gerund or action-oriented naming (lowercase-hyphens only) for skill names. 60 60 - Write descriptions in third person that specify both what the skill does and when to use it. 61 61 - Test with all target models (Haiku, Sonnet, Opus) as effectiveness varies. 62 + 63 + ## Dogfooding 64 + 65 + When completing significant work (features, fixes, or meaningful refactors), ship a cap to the vit network: 66 + 67 + ```bash 68 + echo "2-4 sentence description of the capability for developers" | ./bin/vit.js ship \ 69 + --title "Short Title" \ 70 + --description "One sentence of value" \ 71 + --ref three-word-ref \ 72 + --kind feat 73 + ``` 74 + 75 + - Use `--kind`: `feat` for features, `fix` for bug fixes, `docs` for documentation, `refactor`, `test`, `perf`, `chore` 76 + - Refs must be exactly three lowercase words separated by dashes 77 + - Do not ship for trivial changes (typos, formatting, config tweaks) 78 + - Ship skills with: `./bin/vit.js ship --skill ./skills/<name>/`
+5
docs/start/index.html
··· 289 289 <li><strong>ship</strong> — publish a capability or skill to the network</li> 290 290 <li><strong>learn</strong> — install a vetted skill for your agents</li> 291 291 </ul> 292 + <h3>who to follow</h3> 293 + <p>start by following the vit project itself:</p> 294 + <pre><code class="language-bash">npx vit follow jeremie.com 295 + </code></pre> 296 + <p>then discover more builders with <code>vit scan</code> — it replays recent network activity and shows who&#39;s shipping.</p> 292 297 <hr> 293 298 <h2>explore first</h2> 294 299 <p>before you install anything, see what&#39;s out there:</p>
+97 -19
explore/public/index.html
··· 330 330 font-weight: 600; 331 331 } 332 332 333 + .pulse { 334 + display: inline-block; 335 + width: 8px; 336 + height: 8px; 337 + background: var(--vit-green); 338 + border-radius: 50%; 339 + margin-right: 6px; 340 + animation: pulse-glow 2s ease-in-out infinite; 341 + } 342 + 343 + @keyframes pulse-glow { 344 + 0%, 100% { opacity: 1; } 345 + 50% { opacity: 0.4; } 346 + } 347 + 348 + .last-activity { 349 + font-size: 0.85rem; 350 + color: #6b7280; 351 + margin-top: 4px; 352 + } 353 + 354 + .recent-activity { 355 + margin-top: 24px; 356 + } 357 + 358 + .recent-activity h3 { 359 + font-size: 0.95rem; 360 + font-weight: 600; 361 + margin: 0 0 12px; 362 + } 363 + 333 364 @media (max-width: 600px) { 334 365 .nav-toggle { 335 366 display: block; ··· 392 423 </nav> 393 424 </div> 394 425 <p class="tagline">open source is social</p> 426 + <p class="last-activity" id="pulse"></p> 395 427 </header> 396 428 397 429 <main id="content"> ··· 477 509 }); 478 510 } 479 511 512 + function atUriToRecordUrl(uri) { 513 + if (!uri || !uri.startsWith('at://')) return null; 514 + var parts = uri.slice(5).split('/'); 515 + if (parts.length < 3) return null; 516 + return 'https://bsky.social/xrpc/com.atproto.repo.getRecord?repo=' + encodeURIComponent(parts[0]) + '&collection=' + encodeURIComponent(parts[1]) + '&rkey=' + encodeURIComponent(parts[2]); 517 + } 518 + 480 519 function renderCapItem(cap) { 481 - const title = esc(cap.title) || 'untitled'; 482 - const name = esc(displayName(cap)); 483 - const ref = cap.ref ? '<code>' + esc(cap.ref) + '</code>' : ''; 484 - const beacon = cap.beacon ? '<a href="#beacon/' + encodeURIComponent(cap.beacon) + '">' + esc(cap.beacon) + '</a>' : ''; 485 - const time = timeAgo(cap.created_at); 486 - const metaParts = [name, beacon, ref, time].filter(Boolean); 487 - return '<div class="cap-item"><div class="cap-title">' + title + '</div><div class="cap-meta">' + metaParts.join(' · ') + '</div></div>'; 520 + var title = esc(cap.title) || 'untitled'; 521 + var recordUrl = atUriToRecordUrl(cap.uri); 522 + var titleHtml = recordUrl ? '<a href="' + esc(recordUrl) + '" title="' + esc(cap.uri || '') + '">' + title + '</a>' : title; 523 + var name = esc(displayName(cap)); 524 + var ref = cap.ref ? '<code>' + esc(cap.ref) + '</code>' : ''; 525 + var beacon = cap.beacon ? '<a href="#beacon/' + encodeURIComponent(cap.beacon) + '">' + esc(cap.beacon) + '</a>' : ''; 526 + var time = timeAgo(cap.created_at); 527 + var metaParts = [name, beacon, ref, time].filter(Boolean); 528 + return '<div class="cap-item"><div class="cap-title">' + titleHtml + '</div><div class="cap-meta">' + metaParts.join(' · ') + '</div></div>'; 488 529 } 489 530 490 531 function renderSkillItem(skill) { ··· 548 589 capsCursor = data.cursor; 549 590 550 591 if (data.caps.length === 0) { 551 - el.innerHTML = '<div class="empty-state"><p>no caps yet — be the first to <code>vit ship</code></p></div>'; 592 + el.innerHTML = '<div class="empty-state"><p>no caps yet — capabilities appear here as builders ship them. <a href="https://v-it.org/start/">get started</a></p></div>'; 552 593 return; 553 594 } 554 595 ··· 564 605 skillsCursor = data.cursor; 565 606 566 607 if (data.skills.length === 0) { 567 - el.innerHTML = '<div class="empty-state"><p>no skills yet — publish one with <code>vit ship --skill</code></p></div>'; 608 + el.innerHTML = '<div class="empty-state"><p>no skills yet — reusable agent abilities appear here when published. <a href="https://v-it.org/start/">get started</a></p></div>'; 568 609 return; 569 610 } 570 611 ··· 583 624 const heading = '<h2 class="view-title">' + esc(beacon) + '</h2>'; 584 625 585 626 if (data.caps.length === 0) { 586 - el.innerHTML = heading + '<div class="empty-state"><p>no caps in this beacon yet</p></div>'; 627 + el.innerHTML = heading + '<div class="empty-state"><p>no caps in this beacon yet — capabilities will appear here as builders ship to this project. <a href="https://v-it.org/start/">get started</a></p></div>'; 587 628 return; 588 629 } 589 630 ··· 597 638 const data = await api('beacons'); 598 639 599 640 if (data.beacons.length === 0) { 600 - el.innerHTML = '<div class="empty-state"><p>no beacons yet — create one with <code>vit init</code></p></div>'; 641 + el.innerHTML = '<div class="empty-state"><p>no beacons yet — projects appear here when builders run <code>vit init</code>. <a href="https://v-it.org/start/">get started</a></p></div>'; 601 642 return; 602 643 } 603 644 ··· 609 650 } 610 651 611 652 async function renderStats(el) { 612 - const data = await api('stats'); 613 - el.innerHTML = '<div class="stat-grid">' + 614 - '<div class="stat-card"><div class="stat-number">' + data.total_caps + '</div><div class="stat-label">capabilities</div></div>' + 615 - '<div class="stat-card"><div class="stat-number">' + data.total_vouches + '</div><div class="stat-label">vouches</div></div>' + 616 - '<div class="stat-card"><div class="stat-number">' + data.total_beacons + '</div><div class="stat-label">beacons</div></div>' + 617 - '<div class="stat-card"><div class="stat-number">' + data.active_dids + '</div><div class="stat-label">active participants</div></div>' + 618 - '<div class="stat-card"><div class="stat-number">' + data.total_skills + '</div><div class="stat-label">skills</div></div>' + 619 - '<div class="stat-card"><div class="stat-number">' + data.skill_publishers + '</div><div class="stat-label">skill publishers</div></div>' + 653 + var [stats, recentCaps, recentSkills] = await Promise.all([ 654 + api('stats'), 655 + api('caps?limit=5'), 656 + api('skills?limit=5') 657 + ]); 658 + var grid = '<div class="stat-grid">' + 659 + '<div class="stat-card"><div class="stat-number">' + stats.total_caps + '</div><div class="stat-label">capabilities</div></div>' + 660 + '<div class="stat-card"><div class="stat-number">' + stats.total_vouches + '</div><div class="stat-label">vouches</div></div>' + 661 + '<div class="stat-card"><div class="stat-number">' + stats.total_beacons + '</div><div class="stat-label">beacons</div></div>' + 662 + '<div class="stat-card"><div class="stat-number">' + stats.active_dids + '</div><div class="stat-label">active participants</div></div>' + 663 + '<div class="stat-card"><div class="stat-number">' + stats.total_skills + '</div><div class="stat-label">skills</div></div>' + 664 + '<div class="stat-card"><div class="stat-number">' + stats.skill_publishers + '</div><div class="stat-label">skill publishers</div></div>' + 620 665 '</div>'; 666 + 667 + var events = []; 668 + (recentCaps.caps || []).forEach(function(c) { events.push({ type: 'cap', label: c.title || c.ref || 'untitled', author: displayName(c), time: c.created_at }); }); 669 + (recentSkills.skills || []).forEach(function(s) { events.push({ type: 'skill', label: s.name || 'unnamed', author: displayName(s), time: s.created_at }); }); 670 + events.sort(function(a, b) { return (b.time || '').localeCompare(a.time || ''); }); 671 + events = events.slice(0, 5); 672 + 673 + var activity = ''; 674 + if (events.length > 0) { 675 + activity = '<div class="recent-activity"><h3>recent activity</h3>' + 676 + events.map(function(e) { 677 + return '<div class="cap-item"><div class="cap-title">' + esc(e.label) + '</div><div class="cap-meta">' + esc(e.type) + ' · ' + esc(e.author) + ' · ' + timeAgo(e.time) + '</div></div>'; 678 + }).join('') + '</div>'; 679 + } 680 + 681 + el.innerHTML = grid + activity; 621 682 } 622 683 623 684 async function navigate() { ··· 633 694 } 634 695 635 696 window.addEventListener('hashchange', navigate); 697 + // Populate pulse indicator with most recent activity 698 + (async function() { 699 + try { 700 + var [capData, skillData] = await Promise.all([api('caps?limit=1'), api('skills?limit=1')]); 701 + var latest = null; 702 + if (capData.caps && capData.caps.length > 0) latest = capData.caps[0].created_at; 703 + if (skillData.skills && skillData.skills.length > 0) { 704 + var skillTime = skillData.skills[0].created_at; 705 + if (!latest || skillTime > latest) latest = skillTime; 706 + } 707 + var pulseEl = document.getElementById('pulse'); 708 + if (latest) { 709 + pulseEl.innerHTML = '<span class="pulse"></span>last activity: ' + timeAgo(latest); 710 + } 711 + } catch(e) {} 712 + })(); 713 + 636 714 navigate(); 637 715 </script> 638 716 </body>
+208
skills/atproto-records/SKILL.md
··· 1 + --- 2 + name: atproto-records 3 + description: >- 4 + Teaches agents to read and write ATProto records using XRPC endpoints. 5 + Activates when working with AT Protocol repositories, collections, DIDs, 6 + or Bluesky/ATmosphere applications. 7 + version: 0.1.0 8 + license: AGPL-3.0-only 9 + --- 10 + 11 + # ATProto Records 12 + 13 + Use this skill when working with AT Protocol repositories, DID resolution, 14 + record retrieval, or record writes. 15 + 16 + ## Data Model 17 + 18 + ATProto stores data in per-account repositories. 19 + 20 + Core concepts: 21 + 22 + - Repo: the account's record store, typically identified by a DID 23 + - Collection: a typed namespace such as `app.bsky.feed.post` 24 + - Record: a document stored in a collection 25 + - Rkey: the record key within the collection 26 + 27 + A single record is identified by repo DID + collection + rkey. 28 + 29 + ## AT URIs 30 + 31 + AT URIs use this format: 32 + 33 + ```text 34 + at://did/collection/rkey 35 + ``` 36 + 37 + Example: 38 + 39 + ```text 40 + at://did:plc:abc123/org.v-it.cap/3mhtkgpstnc2l 41 + ``` 42 + 43 + When code needs an HTTP URL for a record, it usually has to map the AT URI into 44 + an XRPC request using those three parts. 45 + 46 + ## Collection Naming 47 + 48 + Collections use reverse-DNS naming. 49 + 50 + Examples: 51 + 52 + - `org.v-it.cap` 53 + - `org.v-it.skill` 54 + - `com.atproto.repo.listRecords` is an XRPC method, not a collection 55 + 56 + Keep collection names stable because they become part of the protocol surface. 57 + 58 + ## Key XRPC Endpoints 59 + 60 + Common record endpoints: 61 + 62 + - `com.atproto.repo.getRecord` 63 + - `com.atproto.repo.listRecords` 64 + - `com.atproto.repo.createRecord` 65 + - `com.atproto.repo.putRecord` 66 + - `com.atproto.repo.deleteRecord` 67 + 68 + Typical read flow: 69 + 70 + 1. Resolve the repo DID to discover its PDS 71 + 2. Call the XRPC endpoint against that PDS 72 + 3. Pass repo DID, collection, and optional rkey or cursor parameters 73 + 74 + Example `getRecord` query shape: 75 + 76 + ```text 77 + /xrpc/com.atproto.repo.getRecord?repo=<did>&collection=<nsid>&rkey=<rkey> 78 + ``` 79 + 80 + Example `listRecords` query shape: 81 + 82 + ```text 83 + /xrpc/com.atproto.repo.listRecords?repo=<did>&collection=<nsid>&limit=100 84 + ``` 85 + 86 + ## DID Resolution 87 + 88 + Two common DID methods: 89 + 90 + ### `did:plc` 91 + 92 + Resolve through `https://plc.directory/<did>`. 93 + 94 + Example: 95 + 96 + ```text 97 + https://plc.directory/did:plc:abc123 98 + ``` 99 + 100 + ### `did:web` 101 + 102 + Resolve through the domain's DID document. 103 + 104 + Root form: 105 + 106 + ```text 107 + did:web:example.com 108 + -> https://example.com/.well-known/did.json 109 + ``` 110 + 111 + Path form: 112 + 113 + ```text 114 + did:web:example.com:team:app 115 + -> https://example.com/team/app/did.json 116 + ``` 117 + 118 + ## PDS Discovery 119 + 120 + After fetching the DID document, inspect its `service` array and find the 121 + ATProto PDS service endpoint. 122 + 123 + In practice, the service entry is the one used for personal data server access. 124 + Some codebases look for a fragment such as `#atproto_pds`; others match the 125 + service type used by the document. Follow the repo's existing convention. 126 + 127 + The result is the base URL for XRPC calls like `listRecords` and `getRecord`. 128 + 129 + ## Authentication 130 + 131 + Reads against public data can often be anonymous, but writes require 132 + authentication. 133 + 134 + Typical app flow: 135 + 136 + - User completes OAuth login 137 + - App stores session credentials or tokens 138 + - Client restores session before calling write endpoints 139 + - Write requests include DPoP or other required proof material for the session 140 + 141 + For agent-oriented tools, session restoration usually happens once near command 142 + startup, then the authenticated client is reused for record writes. 143 + 144 + ## Pagination 145 + 146 + `listRecords` supports cursor-based pagination. 147 + 148 + Common parameters: 149 + 150 + - `limit`: max records to return 151 + - `cursor`: token for the next page 152 + 153 + Typical loop: 154 + 155 + 1. Request the first page with `limit` 156 + 2. Read `records` 157 + 3. If `cursor` is present, request the next page with that cursor 158 + 4. Repeat until no cursor remains 159 + 160 + Do not assume a single response contains the full collection. 161 + 162 + ## CIDs and Content Addressing 163 + 164 + Records are content-addressed and often accompanied by a CID. 165 + 166 + - CID identifies the exact content 167 + - AT URI identifies the location in the repo 168 + - Updating a record can keep the same AT URI while changing its CID 169 + 170 + Use both when you need strong reference integrity, such as vouches or quoted 171 + references to a specific record state. 172 + 173 + ## Write Operations 174 + 175 + Choose the endpoint by intent: 176 + 177 + - `createRecord` when creating a new record and letting the server generate or 178 + accept an rkey 179 + - `putRecord` when writing to a known rkey 180 + - `deleteRecord` when removing a record 181 + 182 + Many codebases use generated TIDs for rkeys on append-only feeds. 183 + 184 + ## Example: Read Records From a PDS 185 + 186 + ```js 187 + const pds = "https://bsky.social"; 188 + const url = new URL("/xrpc/com.atproto.repo.listRecords", pds); 189 + url.searchParams.set("repo", "did:plc:abc123"); 190 + url.searchParams.set("collection", "org.v-it.cap"); 191 + url.searchParams.set("limit", "100"); 192 + 193 + const res = await fetch(url); 194 + if (!res.ok) throw new Error(`listRecords failed: ${res.status}`); 195 + const data = await res.json(); 196 + console.log(data.records); 197 + ``` 198 + 199 + If the response includes `cursor`, continue fetching until exhausted. 200 + 201 + ## Working Style 202 + 203 + When editing ATProto code: 204 + 205 + - Keep DID parsing and PDS discovery centralized 206 + - Reuse shared helpers for record listing and URI handling 207 + - Preserve collection names exactly 208 + - Treat pagination and CID handling as protocol concerns, not optional details
+191
skills/bun-project/SKILL.md
··· 1 + --- 2 + name: bun-project 3 + description: >- 4 + Guides agents through Bun project setup, testing, and conventions. Activates 5 + when working in JavaScript/TypeScript projects that use Bun as their runtime 6 + and package manager. 7 + version: 0.1.0 8 + license: AGPL-3.0-only 9 + --- 10 + 11 + # Bun Project 12 + 13 + Use this skill when a project runs on Bun or expects Bun-native tooling. 14 + 15 + ## What Bun Covers 16 + 17 + Bun can act as: 18 + 19 + - Runtime for JavaScript and TypeScript 20 + - Package manager 21 + - Test runner 22 + - Bundler 23 + 24 + That means many projects can standardize on Bun commands instead of mixing 25 + `node`, `npm`, Jest, and separate bundlers. 26 + 27 + ## Core Commands 28 + 29 + Install dependencies: 30 + 31 + ```bash 32 + bun install 33 + ``` 34 + 35 + Run tests: 36 + 37 + ```bash 38 + bun test 39 + ``` 40 + 41 + Run a package script or entrypoint: 42 + 43 + ```bash 44 + bun run dev 45 + bun run src/index.ts 46 + ``` 47 + 48 + Use `bun run <script>` for scripts defined in `package.json`, and `bun run 49 + <file>` for direct file execution. 50 + 51 + ## `bunfig.toml` 52 + 53 + `bunfig.toml` is Bun's project configuration file. Use it to define defaults 54 + that should not be repeated in every command. 55 + 56 + Common uses: 57 + 58 + - Test configuration 59 + - Install behavior 60 + - JSX or transpiler-related defaults 61 + - Runtime flags for local development 62 + 63 + Check whether the repo already has `bunfig.toml` before introducing one. Keep 64 + configuration minimal and aligned with existing `package.json` scripts. 65 + 66 + ## `bun:test` 67 + 68 + Bun ships a built-in test API via `bun:test`. 69 + 70 + Common imports: 71 + 72 + ```ts 73 + import { describe, test, expect, beforeEach, afterEach, mock } from "bun:test"; 74 + ``` 75 + 76 + Use: 77 + 78 + - `describe` to group related tests 79 + - `test` for individual cases 80 + - `expect` for assertions 81 + - `beforeEach` and `afterEach` for setup and cleanup 82 + - `mock` for spies, stubs, and controlled replacements 83 + 84 + Example: 85 + 86 + ```ts 87 + import { describe, test, expect, beforeEach, mock } from "bun:test"; 88 + 89 + describe("config loader", () => { 90 + beforeEach(() => { 91 + mock.restore(); 92 + }); 93 + 94 + test("reads defaults", () => { 95 + expect(true).toBe(true); 96 + }); 97 + }); 98 + ``` 99 + 100 + Follow the local test style. If the repo already uses plain functions and 101 + minimal fixtures, do not introduce heavier abstractions. 102 + 103 + ## Native Bun APIs 104 + 105 + Prefer Bun-native APIs when the project already depends on Bun behavior. 106 + 107 + ### `Bun.file()` 108 + 109 + Use for efficient file reads and metadata. 110 + 111 + ```ts 112 + const file = Bun.file("README.md"); 113 + const text = await file.text(); 114 + ``` 115 + 116 + ### `Bun.write()` 117 + 118 + Use for simple file writes. 119 + 120 + ```ts 121 + await Bun.write("out.txt", "hello"); 122 + ``` 123 + 124 + ### `Bun.serve()` 125 + 126 + Use for HTTP servers when the project is Bun-native. 127 + 128 + ```ts 129 + Bun.serve({ 130 + port: 3000, 131 + fetch() { 132 + return new Response("ok"); 133 + }, 134 + }); 135 + ``` 136 + 137 + If the repo already uses another server abstraction, preserve it unless the 138 + change explicitly calls for Bun-native serving. 139 + 140 + ## Module Resolution 141 + 142 + Bun is ESM-first. Prefer standard ESM imports and explicit file paths that match 143 + repo conventions. 144 + 145 + - Check `package.json` for `"type": "module"` 146 + - Respect `exports` when consuming a package 147 + - Avoid relying on legacy CommonJS patterns unless the repo already does 148 + - Keep relative imports explicit and consistent 149 + 150 + When publishing packages, ensure `package.json` `exports` reflects the supported 151 + entrypoints instead of depending on deep imports. 152 + 153 + ## Performance Guidance 154 + 155 + Bun is fast when you avoid unnecessary compatibility layers. 156 + 157 + - Prefer native Bun APIs over extra wrappers when they simplify the code 158 + - Avoid adding transpilation steps that Bun already handles 159 + - Reuse Bun's built-in test runner instead of introducing another test stack 160 + - Keep startup paths simple for CLIs and small services 161 + 162 + Do not add Bun-specific code if the repository is intentionally runtime-agnostic. 163 + 164 + ## Common Project Patterns 165 + 166 + Typical `package.json` scripts: 167 + 168 + ```json 169 + { 170 + "scripts": { 171 + "dev": "bun run src/index.ts", 172 + "test": "bun test", 173 + "build": "bun build src/index.ts --outdir dist" 174 + } 175 + } 176 + ``` 177 + 178 + For workspaces: 179 + 180 + - Define workspaces in the root `package.json` 181 + - Run `bun install` at the workspace root 182 + - Keep shared tooling in root scripts when that matches the repo's structure 183 + 184 + ## Working Style 185 + 186 + When editing a Bun project: 187 + 188 + - Use existing `make`, `package.json`, or repo scripts first 189 + - Run the smallest relevant Bun test target during iteration 190 + - Run the repo's required final verification before handing work back 191 + - Match established import style and file layout
+218
skills/semantic-commits/SKILL.md
··· 1 + --- 2 + name: semantic-commits 3 + description: >- 4 + Teaches agents to write structured commit messages using conventional commit 5 + types (feat, fix, docs, test, refactor, perf, chore, style). Activates when 6 + committing code changes or writing commit messages. 7 + version: 0.1.0 8 + license: AGPL-3.0-only 9 + --- 10 + 11 + # Semantic Commits 12 + 13 + Use conventional commits so history stays searchable, releasable, and easy to 14 + scan. 15 + 16 + ## Format 17 + 18 + Default form: 19 + 20 + ```text 21 + type: subject 22 + ``` 23 + 24 + Optional scoped form: 25 + 26 + ```text 27 + type(scope): subject 28 + ``` 29 + 30 + Use a scope only when it adds useful precision, such as a package, command, or 31 + subsystem. 32 + 33 + Examples: 34 + 35 + ```text 36 + feat: add skill publishing command 37 + fix(scan): handle empty cursor responses 38 + docs(api): clarify AT URI examples 39 + ``` 40 + 41 + ## Types 42 + 43 + ### `feat` 44 + 45 + Use for a user-visible capability or meaningful enhancement. 46 + 47 + Examples: 48 + 49 + ```text 50 + feat: add skill discovery filters 51 + feat(explore): show recent network activity 52 + ``` 53 + 54 + ### `fix` 55 + 56 + Use for a defect correction or behavior regression. 57 + 58 + Examples: 59 + 60 + ```text 61 + fix: preserve beacon when shipping recap 62 + fix(vet): reject malformed skill refs 63 + ``` 64 + 65 + ### `docs` 66 + 67 + Use for documentation-only changes. 68 + 69 + Examples: 70 + 71 + ```text 72 + docs: add dogfooding guidance 73 + docs(start): explain who to follow first 74 + ``` 75 + 76 + ### `test` 77 + 78 + Use for adding or improving tests without changing production behavior. 79 + 80 + Examples: 81 + 82 + ```text 83 + test: cover did:web path resolution 84 + test(ship): verify skill frontmatter validation 85 + ``` 86 + 87 + ### `refactor` 88 + 89 + Use for internal restructuring that keeps behavior the same. 90 + 91 + Examples: 92 + 93 + ```text 94 + refactor: extract record pagination helper 95 + refactor(remix): simplify trust gate flow 96 + ``` 97 + 98 + ### `perf` 99 + 100 + Use for measurable performance improvements. 101 + 102 + Examples: 103 + 104 + ```text 105 + perf: batch DID queries with bounded concurrency 106 + perf(explore): reduce duplicate API requests 107 + ``` 108 + 109 + ### `chore` 110 + 111 + Use for maintenance work that does not fit the other categories. 112 + 113 + Examples: 114 + 115 + ```text 116 + chore: update lockfile 117 + chore(ci): pin bun version 118 + ``` 119 + 120 + ### `style` 121 + 122 + Use for formatting or stylistic cleanup with no behavioral impact. 123 + 124 + Examples: 125 + 126 + ```text 127 + style: normalize markdown spacing 128 + style(explore): align inline script indentation 129 + ``` 130 + 131 + ## Subject Rules 132 + 133 + - Write the subject in imperative mood: "add", "fix", "remove", not "added" 134 + - Keep it lowercase unless a proper noun requires capitalization 135 + - Do not end with a period 136 + - Keep it under 72 characters 137 + - Focus on the main change, not every touched file 138 + 139 + Good: 140 + 141 + ```text 142 + fix: handle missing did document service 143 + ``` 144 + 145 + Bad: 146 + 147 + ```text 148 + Fix: Handled missing DID document service. 149 + ``` 150 + 151 + ## Body Guidelines 152 + 153 + Add a body when context helps reviewers or future readers. 154 + 155 + - Explain why the change was needed 156 + - Explain constraints, side effects, or tradeoffs 157 + - Avoid narrating obvious code mechanics line by line 158 + - Wrap body lines at 72 characters when practical 159 + 160 + Example: 161 + 162 + ```text 163 + refactor: isolate PDS record scanning 164 + 165 + The remix, vet, and vouch commands all repeated the same network scan 166 + pattern. Centralizing the loop keeps selection logic consistent and 167 + makes follow-up pagination changes safer. 168 + ``` 169 + 170 + ## Breaking Changes 171 + 172 + Mark breaking changes in one of two ways: 173 + 174 + ```text 175 + feat!: rename ship --tag to --tags 176 + ``` 177 + 178 + Or with a footer: 179 + 180 + ```text 181 + refactor(api): normalize record payloads 182 + 183 + BREAKING CHANGE: listRecords callers must now read data.records 184 + instead of records directly. 185 + ``` 186 + 187 + Use both when the break should be impossible to miss. 188 + 189 + ## Anti-Patterns 190 + 191 + Avoid: 192 + 193 + - Vague subjects like `update code`, `fix stuff`, `misc changes` 194 + - Mixing unrelated changes under one type when the work should be split 195 + - Using `chore` or `refactor` to hide a real feature or bug fix 196 + - Over-scoping every commit when the scope adds no value 197 + - Writing subjects as status reports, such as `work on login flow` 198 + 199 + Weak: 200 + 201 + ```text 202 + chore: update code 203 + ``` 204 + 205 + Better: 206 + 207 + ```text 208 + fix(login): refresh expired session before API calls 209 + ``` 210 + 211 + ## Quick Checklist 212 + 213 + Before finalizing a commit message, confirm: 214 + 215 + - The type matches the user-visible intent of the change 216 + - The subject is imperative, lowercase, and under 72 chars 217 + - The body explains why when needed 218 + - Breaking changes are marked clearly
+2 -2
src/cmd/ship.js
··· 227 227 collection: SKILL_COLLECTION, 228 228 rkey, 229 229 record, 230 - validate: true, 230 + validate: false, 231 231 }; 232 232 233 233 if (verbose) console.log(`[verbose] putRecord ${putArgs.collection} rkey=${rkey}`); ··· 396 396 collection: CAP_COLLECTION, 397 397 rkey, 398 398 record, 399 - validate: true, 399 + validate: false, 400 400 }; 401 401 if (verbose) console.log(`[verbose] putRecord ${putArgs.collection} rkey=${rkey}`); 402 402 const putRes = await agent.com.atproto.repo.putRecord(putArgs);
+2 -2
src/cmd/vouch.js
··· 107 107 collection: VOUCH_COLLECTION, 108 108 rkey, 109 109 record: vouchRecord, 110 - validate: true, 110 + validate: false, 111 111 }); 112 112 113 113 try { ··· 198 198 collection: VOUCH_COLLECTION, 199 199 rkey, 200 200 record: vouchRecord, 201 - validate: true, 201 + validate: false, 202 202 }); 203 203 204 204 try {