{"_id":"@ba0918-dev/agentic-skill-vendor","_rev":"6-7af28ab657025baa3b1eddeecb6e75ce","name":"@ba0918-dev/agentic-skill-vendor","dist-tags":{"latest":"0.6.0"},"versions":{"0.1.0":{"name":"@ba0918-dev/agentic-skill-vendor","version":"0.1.0","license":"MIT","_id":"@ba0918-dev/agentic-skill-vendor@0.1.0","maintainers":[{"name":"ba0918-dev","email":"ba0918.dev@gmail.com"}],"homepage":"https://github.com/ba0918/agentic-skill-vendor#readme","bugs":{"url":"https://github.com/ba0918/agentic-skill-vendor/issues"},"bin":{"agentic-skill-vendor":"dist/cli.js"},"dist":{"shasum":"53b7e42decef72e3835e0cdced4afb79fd8e5d65","tarball":"https://registry.npmjs.org/@ba0918-dev/agentic-skill-vendor/-/agentic-skill-vendor-0.1.0.tgz","fileCount":4,"integrity":"sha512-5UdBLnmVSneAt+CRZcPALQCc8NE7LKV5CCUppuaI8h74LOF/HuPf2zgN6AkDSpKDX7WeYgKQGEzdH7p2nCjhnA==","signatures":[{"sig":"MEYCIQCyvsiTVT08Vrdx2YLA5GppxKDIiC9pjHDq+fP/PSMF6QIhAPmzr7+1NATqFXdH4NmRBLq7F2fv8embuYIVYeKnKftv","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":50961},"type":"module","engines":{"node":">=20.10"},"gitHead":"8baad6c8d9a8c8b6c706fca199824c5c831dd17e","scripts":{"fmt":"biome format --write .","lint":"biome lint .","test":"bun test","build":"bun build src/cli.ts --target node --packages external --outdir dist","prepack":"bun run build","prepare":"lefthook install","fmt:check":"biome format .","typecheck":"tsc --noEmit"},"_npmUser":{"name":"ba0918-dev","email":"ba0918.dev@gmail.com"},"repository":{"url":"git+https://github.com/ba0918/agentic-skill-vendor.git","type":"git"},"_npmVersion":"11.19.0","description":"Vendors shared reference documents into agent skill directories and locks what each skill depends on.","directories":{},"_nodeVersion":"25.9.0","dependencies":{"ignore":"7.0.6","js-yaml":"5.2.3"},"_hasShrinkwrap":false,"devDependencies":{"lefthook":"2.1.10","@types/bun":"1.3.14","typescript":"7.0.2","@biomejs/biome":"2.5.7"},"_npmOperationalInternal":{"tmp":"tmp/agentic-skill-vendor_0.1.0_1787013366220_0.883294142449734","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@ba0918-dev/agentic-skill-vendor","version":"0.2.0","license":"MIT","_id":"@ba0918-dev/agentic-skill-vendor@0.2.0","maintainers":[{"name":"ba0918-dev","email":"ba0918.dev@gmail.com"}],"homepage":"https://github.com/ba0918/agentic-skill-vendor#readme","bugs":{"url":"https://github.com/ba0918/agentic-skill-vendor/issues"},"bin":{"agentic-skill-vendor":"dist/cli.js"},"dist":{"shasum":"51d2748516b4dda6b1d7c57810f973bcb7e40801","tarball":"https://registry.npmjs.org/@ba0918-dev/agentic-skill-vendor/-/agentic-skill-vendor-0.2.0.tgz","fileCount":5,"integrity":"sha512-859/fjEdZuaD1X1Jtb+UpDqF5Pb5SpYF3qHBJepAgEMTQkfj5StmgSWxSBZIVAb9xB29GTWdnS8Rcgv7NCL2Tw==","signatures":[{"sig":"MEYCIQCY6MUBWFbzWaYAsZKAWu0xPXLsEVVVpGo3JnJycqGmEwIhAPmga+EBSo9Q210YHzOQevlZ5gMrNChpHJO5wrciV9jS","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@ba0918-dev%2fagentic-skill-vendor@0.2.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":101431},"type":"module","engines":{"node":">=20.10"},"gitHead":"ac3605c0411d1ae9875442825b6ef2158449660f","scripts":{"fmt":"biome format --write .","lint":"biome lint .","test":"bun test","build":"bun build src/cli.ts --target node --packages external --outdir dist","prepack":"bun run build","fmt:check":"biome format .","typecheck":"tsc --noEmit"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:763c9ba8-6660-45d2-8604-e0bc716cf10d"}},"repository":{"url":"git+https://github.com/ba0918/agentic-skill-vendor.git","type":"git"},"_npmVersion":"12.0.2","description":"Vendors shared reference documents into agent skill directories and locks what each skill depends on.","directories":{},"_nodeVersion":"24.19.0","dependencies":{"ignore":"7.0.6","js-yaml":"5.2.3"},"_hasShrinkwrap":false,"devDependencies":{"lefthook":"2.1.10","@types/bun":"1.3.14","typescript":"7.0.2","@biomejs/biome":"2.5.7"},"_npmOperationalInternal":{"tmp":"tmp/agentic-skill-vendor_0.2.0_1787051239030_0.09913017295054782","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"@ba0918-dev/agentic-skill-vendor","version":"0.3.0","license":"MIT","_id":"@ba0918-dev/agentic-skill-vendor@0.3.0","maintainers":[{"name":"ba0918-dev","email":"ba0918.dev@gmail.com"}],"homepage":"https://github.com/ba0918/agentic-skill-vendor#readme","bugs":{"url":"https://github.com/ba0918/agentic-skill-vendor/issues"},"bin":{"agentic-skill-vendor":"dist/cli.js"},"dist":{"shasum":"be9bfa6ab08917f12d8674f11ce2942e036250e4","tarball":"https://registry.npmjs.org/@ba0918-dev/agentic-skill-vendor/-/agentic-skill-vendor-0.3.0.tgz","fileCount":5,"integrity":"sha512-VMwsft4+a35Fl4X/K5kcGSChN66qTkrnsoidOyphvB1Le3qDvCEqAPC59WbzZ35xZFai/SuZLrkvIQ+uh+J6jw==","signatures":[{"sig":"MEUCIQCTZCwjcsj6ERelQhpDysFFC3r1/XIalaAVfw9BVIYfXwIgbAkCfJ1NSs/21ij6duzcpLDXhmttQ+8EZKkNsXkzlhM=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@ba0918-dev%2fagentic-skill-vendor@0.3.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":145873},"type":"module","engines":{"node":">=20.10"},"gitHead":"ba46902f948b32860bf099025e8eb672aaecda99","scripts":{"fmt":"biome format --write .","lint":"biome lint .","test":"bun test","build":"bun build src/cli.ts --target node --packages external --outdir dist","prepack":"bun run build","fmt:check":"biome format .","typecheck":"tsc --noEmit"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:763c9ba8-6660-45d2-8604-e0bc716cf10d"}},"repository":{"url":"git+https://github.com/ba0918/agentic-skill-vendor.git","type":"git"},"_npmVersion":"12.0.2","description":"Vendors shared reference documents into agent skill directories and locks what each skill depends on.","directories":{},"_nodeVersion":"24.19.0","dependencies":{"ignore":"7.0.6","js-yaml":"5.2.3"},"_hasShrinkwrap":false,"devDependencies":{"lefthook":"2.1.10","@types/bun":"1.3.14","typescript":"7.0.2","@biomejs/biome":"2.5.7"},"_npmOperationalInternal":{"tmp":"tmp/agentic-skill-vendor_0.3.0_1787545358141_0.3545257930150938","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"name":"@ba0918-dev/agentic-skill-vendor","version":"0.4.0","license":"MIT","_id":"@ba0918-dev/agentic-skill-vendor@0.4.0","maintainers":[{"name":"ba0918-dev","email":"ba0918.dev@gmail.com"}],"homepage":"https://github.com/ba0918/agentic-skill-vendor#readme","bugs":{"url":"https://github.com/ba0918/agentic-skill-vendor/issues"},"bin":{"agentic-skill-vendor":"dist/cli.js"},"dist":{"shasum":"588259325a8996d701df2575876fb5b911ba3460","tarball":"https://registry.npmjs.org/@ba0918-dev/agentic-skill-vendor/-/agentic-skill-vendor-0.4.0.tgz","fileCount":5,"integrity":"sha512-Y/lMc4cXh3Fi0RcYa/sz4Bf/tqVvFMlGlDxua4AVIb22KXaFhmqg2L0ZM6Z9Fd9dcKBWJSrvRchyc4aoZJrDAg==","signatures":[{"sig":"MEUCIQCg1d/CGg4zzLABSgQTqcSVT4he1WeHbJXoj6vM0lZVKgIgYuULG8uHR3Gf56LELhQPunBeRPqK9mkWs0wsPg63pkA=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@ba0918-dev%2fagentic-skill-vendor@0.4.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":166021},"type":"module","engines":{"node":">=20.10"},"gitHead":"11bf21f9f3cae7fc6250c8976e637c5d5341953a","scripts":{"fmt":"biome format --write .","lint":"biome lint .","test":"bun test","build":"bun build src/cli.ts --target node --packages external --outdir dist","prepack":"bun run build","fmt:check":"biome format .","typecheck":"tsc --noEmit"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:763c9ba8-6660-45d2-8604-e0bc716cf10d"}},"repository":{"url":"git+https://github.com/ba0918/agentic-skill-vendor.git","type":"git"},"_npmVersion":"12.0.2","description":"Vendors shared reference documents into agent skill directories and locks what each skill depends on.","directories":{},"_nodeVersion":"24.19.0","dependencies":{"ignore":"7.0.6","js-yaml":"5.2.3"},"_hasShrinkwrap":false,"devDependencies":{"lefthook":"2.1.10","@types/bun":"1.3.14","typescript":"7.0.2","@biomejs/biome":"2.5.7"},"_npmOperationalInternal":{"tmp":"tmp/agentic-skill-vendor_0.4.0_1787579752500_0.08645181369688348","host":"s3://npm-registry-packages-npm-production"}},"0.5.0":{"name":"@ba0918-dev/agentic-skill-vendor","version":"0.5.0","keywords":["agent-skills","claude-code","skill","skill-md","vendoring","vendor","lockfile","integrity","digest","monorepo","ai-agents","cli"],"license":"MIT","_id":"@ba0918-dev/agentic-skill-vendor@0.5.0","maintainers":[{"name":"ba0918-dev","email":"ba0918.dev@gmail.com"}],"homepage":"https://github.com/ba0918/agentic-skill-vendor#readme","bugs":{"url":"https://github.com/ba0918/agentic-skill-vendor/issues"},"bin":{"agentic-skill-vendor":"dist/cli.js"},"dist":{"shasum":"ca62bcd49b810fb13e31d6a76f55cbb7a92b2289","tarball":"https://registry.npmjs.org/@ba0918-dev/agentic-skill-vendor/-/agentic-skill-vendor-0.5.0.tgz","fileCount":5,"integrity":"sha512-h+C4pYTnMvdnc9LEwE/1oSMG/5N+/HLRlNVxIEyGtqWweB/yXgpDY4aAAGNoyfTZuML74CfP+EQEr27lGFthmA==","signatures":[{"sig":"MEUCIGiSqJwZuNjS8x8YJccz8pmaYgOpfix7WcQ6LDwZAfzdAiEA3Rcg6boYg2P5TfEXqQ8qAog970TPgPJigETbsfdNNxE=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@ba0918-dev%2fagentic-skill-vendor@0.5.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":177653},"type":"module","engines":{"node":">=20.10"},"gitHead":"c2700dfd2b9d5535c31876d431d037ca33aa5d91","scripts":{"fmt":"biome format --write .","lint":"biome lint .","test":"bun test","build":"bun build src/cli.ts --target node --packages external --outdir dist","prepack":"bun run build","fmt:check":"biome format .","typecheck":"tsc --noEmit"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:763c9ba8-6660-45d2-8604-e0bc716cf10d"}},"repository":{"url":"git+https://github.com/ba0918/agentic-skill-vendor.git","type":"git"},"_npmVersion":"12.0.2","description":"Vendors shared reference documents and raw files into agent skill directories, and locks what each skill depends on so CI can verify every copy.","directories":{},"_nodeVersion":"24.19.0","dependencies":{"ignore":"7.0.6","js-yaml":"5.2.3"},"_hasShrinkwrap":false,"devDependencies":{"lefthook":"2.1.10","@types/bun":"1.3.14","typescript":"7.0.2","@biomejs/biome":"2.5.7"},"_npmOperationalInternal":{"tmp":"tmp/agentic-skill-vendor_0.5.0_1787624929264_0.5655017546946219","host":"s3://npm-registry-packages-npm-production"}},"0.6.0":{"name":"@ba0918-dev/agentic-skill-vendor","version":"0.6.0","description":"Vendors shared reference documents and raw files into agent skill directories, and locks what each skill depends on so CI can verify every copy.","keywords":["agent-skills","claude-code","skill","skill-md","vendoring","vendor","lockfile","integrity","digest","monorepo","ai-agents","cli"],"license":"MIT","type":"module","bin":{"agentic-skill-vendor":"dist/cli.js"},"engines":{"node":">=20.10"},"repository":{"type":"git","url":"git+https://github.com/ba0918/agentic-skill-vendor.git"},"scripts":{"test":"bun test","lint":"biome lint .","typecheck":"tsc --noEmit","fmt":"biome format --write .","fmt:check":"biome format .","build":"bun build src/cli.ts --target node --packages external --outdir dist","prepack":"bun run build"},"dependencies":{"ignore":"7.0.6","js-yaml":"5.2.3"},"devDependencies":{"@biomejs/biome":"2.5.7","@types/bun":"1.3.14","lefthook":"2.1.10","typescript":"7.0.2"},"gitHead":"3cac202100dc9bfe160b5442a51287979c9f29a0","_id":"@ba0918-dev/agentic-skill-vendor@0.6.0","bugs":{"url":"https://github.com/ba0918/agentic-skill-vendor/issues"},"homepage":"https://github.com/ba0918/agentic-skill-vendor#readme","_nodeVersion":"24.19.0","_npmVersion":"12.0.2","dist":{"integrity":"sha512-Wn7tMCkWG1f3t7xvwShb5XgYiV2orX5ASLYuYJo7V3wK78fVgKjBjOgYdK7ZObjQe9DHEEXVkrt2WxjVjed2mA==","shasum":"e779d0290f7bc0228b43efd9e028f65942a93bc5","tarball":"https://registry.npmjs.org/@ba0918-dev/agentic-skill-vendor/-/agentic-skill-vendor-0.6.0.tgz","fileCount":5,"unpackedSize":214105,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@ba0918-dev%2fagentic-skill-vendor@0.6.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIEbqDk4KkTX8gWZiesCAEB69pLcQPWYRfRXaY5riraP+AiEA7q2W8dgRa5jutYLKnSV2/aAUkT/8xo3zEcZrm3T5OG4="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:763c9ba8-6660-45d2-8604-e0bc716cf10d"}},"directories":{},"maintainers":[{"name":"ba0918-dev","email":"ba0918.dev@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/agentic-skill-vendor_0.6.0_1787678625349_0.0029029437542209546"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-18T00:36:05.989Z","modified":"2026-08-25T17:23:45.791Z","0.1.0":"2026-08-18T00:36:06.399Z","0.2.0":"2026-08-18T11:07:19.186Z","0.3.0":"2026-08-24T04:22:38.276Z","0.4.0":"2026-08-24T13:55:52.638Z","0.5.0":"2026-08-25T02:28:49.436Z","0.6.0":"2026-08-25T17:23:45.478Z"},"bugs":{"url":"https://github.com/ba0918/agentic-skill-vendor/issues"},"license":"MIT","homepage":"https://github.com/ba0918/agentic-skill-vendor#readme","keywords":["agent-skills","claude-code","skill","skill-md","vendoring","vendor","lockfile","integrity","digest","monorepo","ai-agents","cli"],"repository":{"type":"git","url":"git+https://github.com/ba0918/agentic-skill-vendor.git"},"description":"Vendors shared reference documents and raw files into agent skill directories, and locks what each skill depends on so CI can verify every copy.","maintainers":[{"name":"ba0918-dev","email":"ba0918.dev@gmail.com"}],"readme":"# agentic-skill-vendor\n\nKeeps a shared document in one place and gives every skill that needs it its own copy.\n\nIn this README, a contract is either a shared document or a set of raw files and directories\nthat skills need to carry identically.\n\nA document contract's generated copy carries a fixed header followed by its canonical body. A\nraw-byte contract copies each payload file byte for byte; a generated directory marker is a\nseparate file. Every change to what a skill carries lands in one reviewable diff — nothing\nreaches a skill silently. The source may live in this repository, on GitHub, or on another\nGit host reachable over allowlisted SSH or HTTPS repository syntax.\n\nCompatibility judgment is out of scope. A digest proves a copy matches its source, not that a\nnew version still suits the skills depending on it — that judgment belongs to the consuming\nrepository's own regression machinery.\n\n## Choose a starting point\n\n- Keep local documents in sync: [Quickstart](#quickstart)\n- Distribute raw files or directories: [Distributing files and directories as they are](#distributing-files-and-directories-as-they-are)\n- Take a contract from another repository: [Taking a contract from another repository](#taking-a-contract-from-another-repository)\n- Take one from a private GitHub repository: [Taking a contract from a private GitHub repository](#taking-a-contract-from-a-private-github-repository)\n- Check committed output in CI: [Running it in CI](#running-it-in-ci)\n\nWhichever path you take, [review the generated changes](#review-generated-changes) after `gen`.\n\n## Review generated changes\n\nAfter `gen`, review and commit the generated copies and `vendor-lock.json` with the canonical\nsource change. Also review and commit `vendor-manifest.yaml` when you add or change a raw\nmapping or a remote-source row.\n\n## Quickstart\n\nAdd it as a dev dependency of the repository holding your skills:\n\n```\nbun add --dev @ba0918-dev/agentic-skill-vendor\n```\n\nWrite the document once, under `contracts/`:\n\n```\ncontracts/changelog-entry.md\n```\n\nHave each skill that needs it name it — by id, and only by id — in its `SKILL.md`\nfrontmatter:\n\n```yaml\nmetadata:\n  contracts:\n    - changelog-entry\n```\n\nThen distribute it, and check the result:\n\n```\nbunx agentic-skill-vendor gen\nbunx agentic-skill-vendor verify\n```\n\n`gen` writes `skills/<name>/references/vendor/changelog-entry.md` into every skill that\ndeclared it, and records the digest it distributed in `vendor-lock.json`. `verify` exits `1`\nif anything in the tree no longer agrees with that lock — put it in CI. Editing the document\nis the same two commands again.\n\nThat is the whole cycle for documents this repository owns. Taking one from another\nrepository adds three commands, [below](#taking-a-contract-from-another-repository).\n\n## Install and runtimes\n\nAny npm-compatible package manager works (`npm install --save-dev` and `npx`, pnpm, yarn).\nIt also runs with nothing installed, through a one-shot runner — pin the version there, since\na bare name resolves the newest release each time:\n\n```\nbunx @ba0918-dev/agentic-skill-vendor@<version> <command> [--root <path>]\n# A read-only command\ndeno run --allow-read=. npm:@ba0918-dev/agentic-skill-vendor@<version> verify\n\n# A local write, with no network access\ndeno run --allow-read=. --allow-write=. npm:@ba0918-dev/agentic-skill-vendor@<version> gen\n\n# Commands that fetch from GitHub\ndeno run --allow-read=. --allow-write=. --allow-net=api.github.com,raw.githubusercontent.com npm:@ba0918-dev/agentic-skill-vendor@<version> add <owner/repo>\ndeno run --allow-read=. --allow-write=. --allow-net=api.github.com,raw.githubusercontent.com npm:@ba0918-dev/agentic-skill-vendor@<version> update\ndeno run --allow-read=. --allow-write=. --allow-net=api.github.com,raw.githubusercontent.com npm:@ba0918-dev/agentic-skill-vendor@<version> fetch\n\n# A command that fetches through the installed Git and OpenSSH\ndeno run --allow-read --allow-write --allow-env --allow-run=git npm:@ba0918-dev/agentic-skill-vendor@<version> add ssh://git@git.example.com/team/contracts.git\n```\n\nThe offline and GitHub examples limit file access to the current root. When using\n`--root <path>`, replace `.` in those read and write permissions with that path. The generic\nGit example deliberately grants broader file access so Git can use its temporary bare\nrepository and the user's normal Git/OpenSSH configuration.\n\nThe same source runs on Node (>= 20.10), Bun and Deno. Four commands — `gen`, `verify`,\n`lint-selfcontain` and `self-test` — never read the environment, start a subprocess or reach a\nnetwork. The other three commands, `add`, `update` and `fetch`, choose their capability from\neach source: `owner/repo` uses HTTPS only to `api.github.com` and\n`raw.githubusercontent.com`, while SSH and HTTPS repository URLs invoke the installed `git`,\nwhich may in turn invoke OpenSSH or a configured credential helper. Under Deno, a generic Git\nsource therefore also needs environment access and `--allow-run=git`; the broad read/write\npermissions in the example let Git use a temporary bare repository outside the project root.\n\n## The commands\n\n| Command | What it does | Network |\n|---|---|---|\n| `gen` | Writes each contract's current text into every skill that declares it, and rewrites the lock to match | no |\n| `verify` | Checks the whole tree against the lock; exit `1` on any violation | no |\n| `lint-selfcontain` | Checks that no skill points outside its own directory | no |\n| `self-test` | Smoke-checks the tool against vectors embedded in it | no |\n| `add <repository> [name]` | Registers another repository as a source and takes up every declared contract it holds | yes |\n| `update` | Moves every pin to what its ref names now, and fetches what the new pin holds | yes |\n| `fetch` | Fills the cache with exactly what the lock already pins — what a clean checkout runs | yes |\n\n`--root` names the tree to work on and defaults to the current directory. `--token-stdin`\nreads a GitHub token from standard input and is taken by the three commands that reach a\nnetwork, but applies only to `owner/repo` sources — see\n[below](#taking-a-contract-from-a-private-github-repository). Exit codes: `0` nothing\nto report, `1` violations (one per line on standard output), `2` a refusal or an internal\nerror (standard error).\n\n## The tree\n\n```\ncontracts/<id>.md                         the canonical text of a contract\ncontracts/<id>/conformance/**             its conformance tests, if any\nskills/<name>/SKILL.md                    a skill, declaring what it depends on\nskills/<name>/references/vendor/<id>.md   the copy this tool writes into that skill\nskills/<name>/<dest>                      a raw-byte contract's copy, where the table says\nvendor-manifest.yaml                      origins and raw-byte source-to-destination mappings\nvendor-lock.json                          the lock: the digest recorded for each contract\n.agentic-skill-vendor/                    fetched cache and raw-byte staging — never committed\n```\n\nThe last three are the tool's own files. `vendor-manifest.yaml` is needed when a contract comes\nfrom another repository or when raw files or directories are distributed, because it records\ntheir origins and source-to-destination mappings. It is not needed when every contract is a\nlocal document at its standard `contracts/<id>.md` path. `.agentic-skill-vendor/` appears after\na repository fetches a contract from another repository or stages a raw-byte distribution; a\nrepository using only local documents has the lock and generated copies, as it always did.\n\n## Changing a contract\n\nThe canonical text is the authority and the lock is the snapshot of it — the relation\n`package.json` has to a lockfile. There is no separate approval command, because the text, the\nlock and the copies are reviewed together in the pull request they land in.\n\n`gen` reports every digest it recorded a new value for:\n\n```\nadopted: <id> <old digest> -> <new digest>\nadopted: <id> conformance <old> -> <new>\nretired: <id> conformance <old>\n```\n\nA first recording names one digest only, annotated `(initial adoption)`. A contract's text and\nits conformance tests move independently, so they get a line each; losing the tests is a\nretirement, since a value left the lock and nothing was taken up in its place. These lines are\nwhat to read in a review, and what a consuming repository's regression machinery matches its\nown evidence against.\n\nUntil `gen` runs, `verify` reports the edit as `stale-lock`. An edited vendored copy, a missing\nor extra file, and a lock that no longer matches what the tree renders to fail the same run.\n\nWithdrawing a contract — removing it from the skills' declarations and deleting its canonical\ntext — is the same act at the other end: the next `gen` retires its resolution and reports\n`retired: <id>`, so the removal never happens silently.\n\nWhen an id leaves every declaration but its canonical text remains locatable, its existing\nresolution is a different state, and not a retirement. Every `gen` from then on says so, and\ngoes on saying so, because it is a standing state rather than an event:\n\n```\nunused: <id> (no skill declares it; its resolution stays in the lock)\n```\n\nThe resolution is reported, not removed — deleting the digest would make a contract briefly\nout of use one to re-adopt from scratch when it comes back — and the exit code is untouched.\nThis does not override maintenance of the origins table: an undeclared document mapping is\nstill pruned. If that mapping was the only way to locate a remote document, its resolution is\nretired normally. A canonical text that no skill has ever declared says nothing at all:\nnothing resolves it, so a repository holding contracts purely for other repositories to fetch\nreports none of them.\n\n## Distributing files and directories as they are\n\nA contract need not be a document. Scripts several skills share — a runtime every workflow\nskill drives, a helper a few of them call — are distributed as raw bytes, from one canonical\nplace to a position of your choosing inside each skill, by a `files` line in the table of\norigins in `vendor-manifest.yaml`:\n\n```yaml\nignore:\n  - \"**/*.test.ts\"                         # every raw directory source\n\ncontracts:\n  workflow-runtime:\n    source: local\n    ignore:\n      - \"fixtures/\"                        # this contract only\n    files:\n      tools/workflow-runtime/: scripts/_runtime/    # a directory, whole\n  check-script:\n    source: local\n    files:\n      tools/scripts/check.sh: scripts/check.sh      # one file\n```\n\nThe left side is where the canonical files are (in this repository, or in a registered source);\nthe right side is where each copy lands, relative to the skill. A trailing slash on both sides\nnames a directory, on neither a file. The bytes are copied exactly — no header, no line-ending\nnormalization — and a directory copy carries one extra file, `.vendored`, saying where it came\nfrom. Skills declare the contract by id, as they declare every contract:\n\n```yaml\nmetadata:\n  contracts:\n    - workflow-runtime\n```\n\nBoth `ignore` fields are optional arrays of strings. They use `.gitignore` pattern syntax,\nincluding `*`, `**`, anchored `/`, comments, and escapes. An unescaped leading `!` is refused:\ncontract-specific rules may add exclusions but cannot undo a shared one. Each directory mapping\nis matched independently against POSIX paths relative to its own source directory, so `/build.ts`\nmeans the `build.ts` immediately inside every mapped directory. These rules do not affect an\nexplicit file source or a document contract.\n\nExclusion changes what is distributed, digested, and recorded, not what is fetched or safety\nchecked. A remote directory source is fetched and verified in full and then filtered while its\ncache is read. If that cache is absent, `verify` still compares the lock with the existing copies\nbut cannot evaluate an `ignore` change; run `fetch` and then `verify` for the full comparison.\nLinks, reserved files, and other unsafe source entries are inspected before filtering.\n\nWhen a new rule excludes a file that was distributed earlier, `verify` reports the old copy as\n`drift` when the canonical source is available, and the next `gen` removes it by replacing the\ndirectory destination. If filtering leaves a mapped directory with no distributable files,\n`gen` and `verify` stop with a configuration error before changing the lock or existing copies.\n\n`files` lines are yours to write, always: there is no conventional position for a set of files,\nso nothing derives them, and `gen` never takes one out. `add` and `update` report a declared id\nthey find at no conventional position as `unlocated: <id>`, which is the cue to write one.\n\nA dest conflicts only with the other final dests in the same skill. Different skills may use\nthe same dest text independently. Within one skill, identical dests and ancestor/descendant\ndests are refused when `gen` or `verify` combines the table with the skill declarations. The\ntable itself remains readable, so `add`, `update`, and `fetch` are not stopped by overlapping\ndests that no skill places together.\n\nThe copies land wherever you pointed them, so the lock records what was written where —\n`placements`, skill by skill and dest by dest — and `gen` reads that record before it touches a\npath:\n\n- A dest that holds nothing is written. A dest the lock remembers, still holding what was\n  written there, is replaced. A dest that already holds exactly what `gen` would write — a hand\n  copy from before, or a tree whose lock was lost — is taken over and reported as\n  `claimed: skills/<skill>/<dest> (<id>)`. Anything else standing at a dest stops the run: the\n  tool never replaces what it cannot show it wrote.\n- A dest the lock remembers that no skill declares any more — the skill withdrew, the table\n  moved the dest, the skill directory went — is cleared and reported as\n  `cleared: skills/<skill>/<dest> (<id>)`, or `(<id>; already absent)` where nothing was left\n  to clear. Only a dest still holding what the lock recorded is cleared; one you have edited\n  since is refused.\n- Inside a directory dest, files the repository's `.gitignore` rules exclude — `__pycache__/`\n  after a run, an editor's leavings — are neither checked nor protected: they go with the next\n  replacement. A directory dest is the tool's; keep local files out of it. A dest, or a file\n  being placed in one, that those rules would exclude outright is refused instead, since a copy\n  `verify` cannot see is not one `gen` may write.\n\nWhen an old recorded dest overlaps its replacement in the same skill — for example, a directory\nsplit into child files, or child files gathered into a directory — one `gen` can migrate the\nownership. From the intact old state, every old placement must still match its recorded digest,\nand the newly owned range must contain no content that was neither owned before nor written by\nthis run. The final files are built as one artifact under `.agentic-skill-vendor/staging/` and\nthe outermost owned dest is replaced once. If a run stops at that replacement boundary, the next\n`gen` continues only from the intact old state, an absent outermost dest, or the exact completed\nartifact with the old lock; every other partial state is refused before any copy or lock change.\nThe staging and destination file systems are checked before the old dest is removed.\n\nThis migration adds no lock field or report kind. It also adds no network access: `gen` and\n`verify` retain their file-system-only boundary, while only `add`, `update`, and `fetch` may use\na configured remote transport.\n\n`verify` compares each recorded dest with the digest recorded for it, and the record itself\nwith what the declarations and the table say it should be (`placement`), and needs neither the\ncanonical files nor a network to do so. Moving a dest in the table changes no contract digest\nand produces no `adopted` line; moving the canonical files themselves does, since where the\nfiles sit is part of what a raw-byte contract is.\n\nA contract cannot change kind in place: a row rewritten from `files` to a document, or back, is\nrefused while the lock remembers the other kind. Withdraw it from every skill, run `gen`, take\nthe row out, run `gen` again, then write the new row. Executable bits are not copied and not\nchecked; invoke a distributed script through its interpreter.\n\n## Taking a contract from another repository\n\nA shared document belongs in the repository most responsible for it, and every other\nrepository fetches it rather than keeping a copy of its own. Register the source once, then\ndistribute as usual:\n\n```\nbunx agentic-skill-vendor add ba0918/agentic-workflow workflow\nbunx agentic-skill-vendor gen\n```\n\nThe short `owner/repo` form keeps using the fixed-host GitHub API. An arbitrary Git host can\ninstead be registered with any of these allowlisted forms:\n\n```\nbunx agentic-skill-vendor add ssh://git@git.example.com/team/contracts.git\nbunx agentic-skill-vendor add git@git.example.com:team/contracts.git\nbunx agentic-skill-vendor add https://git.example.com/team/contracts.git\n```\n\nThe repository text is preserved exactly. Without the optional source name, the final path\ncomponent becomes the name after a trailing `.git` is removed; give a name explicitly if that\ncomponent is not a usable source name. Plain `http://`, `file://`, local paths, unsupported\nremote helpers, option-like inputs, HTTP(S) URLs containing a username, password or token, and\nSSH URLs containing a password are rejected before Git starts.\n\nGeneric sources require Git at runtime and OpenSSH for SSH URLs. They reuse the user's normal\nsystem/global Git and SSH setup, including an SSH agent, private keys, `known_hosts` and stored\ncredential-helper results. Every run is nevertheless non-interactive: standard input and raw\nchild diagnostics are closed, Git terminal prompts are disabled, and OpenSSH uses batch mode.\nIf the URL needs a username, password, key passphrase or host-key confirmation, the command\nfails instead of waiting. Run ordinary Git or OpenSSH directly with the same URL to complete\nthat one-time authentication and connection setup, confirm that it then succeeds without a\nprompt, and retry this tool. `--token-stdin` is not passed to generic Git; it remains a\nGitHub-API credential only.\n\nOne generic source has cumulative limits of 120 seconds, 256 MiB for its temporary bare\nrepository, 1 MiB for one extracted file and 256 MiB for all extracted files. A timeout,\ncapacity failure or acquisition error normally terminates the detached Git process group and\ndeletes its temporary bare repository. If the OS cannot confirm that the process group has\nstopped, the tool fails safely and retains that exact temporary bare repository under the OS\ntemporary directory with the `agentic-skill-git-` prefix instead of deleting it. The refusal names\nthat exact outer directory and the detached process group identifier. In either case,\nthe existing cache, manifest and lock remain unchanged, and raw child stderr is suppressed. To\nrecover a retained repository, first confirm that the named process group has stopped, then\nmanually delete only that exact retained directory; recursive removal is allowed for that exact\ndirectory after confirmation. Never recursively clean the OS temporary root or a parent directory,\nchoose a target with a glob, or rely on unresolved variables. Both SHA-1\nand SHA-256 Git object formats are verified; a SHA-256 source records `objectFormat: sha256`\nbeside its 64-digit revision in the lock.\n\n`add` discovers the branch that repository hands out, resolves and fetches it, then publishes\nthe source registration in `vendor-manifest.yaml` and its commit pin in `vendor-lock.json`\nonly after acquisition succeeds. It fetches every contract your skills already declare and\nthat repository holds at `contracts/<id>.md`. The optional second argument names the source;\nwithout it the repository's own name is used.\n\nKeep the cache out of git — anchored to the repository root, or the fetching commands warn on\nevery run:\n\n```\n/.agentic-skill-vendor/\n```\n\nDeleting the whole directory costs one `fetch`.\n\nFrom then on, `update` moves every pin to what its ref names now, and `fetch` restores the\ncache from what the lock already pins. `gen` and `verify` never fetch. `gen` stops and asks\nfor a `fetch` when the cache is missing rather than resolving a ref of its own, since that\nwould take up whatever the source holds today with nothing in any diff saying a new version\nwas adopted; where the lock pins no commit at all it asks for an `update` instead, since a\n`fetch` reproduces a pin rather than deciding one.\n\n### The table of origins\n\n`vendor-manifest.yaml` is written by the tool. Two lines are yours to write:\n\n```yaml\ncontracts:\n  writing-style:\n    source: local\n    path: docs/style/writing-style.md   # a canonical text outside contracts/\n  tdd-contract:\n    source: workflow                    # which source, when two of them hold it\n```\n\nEverything else is derived and reported: a `source: local` line for each contract of your own,\na line for each contract exactly one source holds, and the removal of a line no skill declares\nany more — `mapped: <id> <- <source>` when a line is written, `unmapped: <id>` when one is\ntaken out, `resolved: <source> <old commit> -> <new commit>` when a pin moves (a first\nresolution names one commit, annotated `(initial resolution)`).\n\nThe repository each source is pinned to is the one this table registers. Edit that line and the\ntree disagrees with itself until `update` runs: `verify` reports `source-mismatch`, and `gen`\nand `fetch` stop for that source rather than act on a pin the table contradicts. `update` is\nthe way back — it reads the repository and the ref from the table alone.\n\n## Taking a contract from a private GitHub repository\n\nA GitHub API source in a private repository, or a public one being fetched often enough to\nmeet the hourly allowance, needs a token. It is piped in; nothing else is accepted:\n\n```\ngh auth token | bunx agentic-skill-vendor update --token-stdin\n```\n\nAnything that writes a token to standard output composes the same way — `op read`,\n`vault kv get -field=token`, a `secrets` value in a workflow step:\n\n```yaml\n- run: echo \"${{ secrets.CONTRACTS_TOKEN }}\" | bunx agentic-skill-vendor fetch --token-stdin\n```\n\n**A pipe, and not a file or an environment variable, on purpose.** A file is a second copy of\nthe secret at rest — one more thing to be committed, backed up, synced, or left readable by\neverything running as the same person — and making one safe would take a permission check\nthat means nothing on a file system without POSIX modes. An environment variable is inherited\nby child processes, while the GitHub API credential path needs none. A pipe leaves nothing\nbehind, appears in no process listing and in no shell history, and needs no permission of its\nown — so the GitHub Deno flags above do not change, and `--token-stdin` needs no `--allow-env`.\nGeneric Git may read the environment for normal Git/OpenSSH configuration, but never receives\nthis token.\n\nThe token is held in memory for the length of one run, reaches only the `Authorization`\nheader of each request, is written nowhere, and appears in no message this tool prints. It is\njudged before it is sent: printable ASCII with no spaces, at most 1024 characters, with exactly\none trailing LF or CRLF removed. A value carrying any other line break is refused by position —\na header field is terminated by CRLF, so such a value would put headers of its own into the\nrequest — and the refusal names the position rather than the value.\n\n`--token-stdin` is refused by `gen`, `verify`, `lint-selfcontain` and `self-test`. Those four\nreach no network, and a flag they accepted would quietly contradict the one thing this\ndocument says about them.\n\nTwo things are worth knowing before the first run:\n\n- **A wrong token is worse than no token on a public source.** Handed an `Authorization`\n  header it cannot validate, `raw.githubusercontent.com` answers `404` for a file it would\n  serve anonymously with `200`. The run refuses rather than reading that as \"the source holds\n  no such contract\", and the refusal says to look at the token — but a token that is merely\n  expired makes a public source that worked yesterday look empty.\n- **The token is needed only where a fetch is.** `gen` and `verify` read the cache and the\n  tree, and `verify` alone needs no credential. The cache is disposable and must not be\n  committed; a clean CI run that needs the complete canonical-text checks runs authenticated\n  `fetch` first, while offline `verify` still checks the committed copies and lock.\n\n## Running it in CI\n\nCI runs `verify` and fails the build on a non-zero exit. CI never runs `gen` — its job is\ndetecting a tree that disagrees with its lock, not resolving the disagreement.\n\nFor a repository that installs this package with Bun, the smallest GitHub Actions workflow is:\n\n```yaml\nname: Verify vendored contracts\n\non: [pull_request]\n\npermissions:\n  contents: read\n\njobs:\n  verify:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n      - uses: oven-sh/setup-bun@v2\n        with:\n          bun-version: \"1.3.x\"\n      - run: bun install --frozen-lockfile\n      - run: bunx agentic-skill-vendor verify\n```\n\nAfter dependencies are installed, the `verify` step itself needs neither network access nor the\n`.agentic-skill-vendor/` cache. The install step downloads packages from the registry and does\nneed network access. For a contract fetched from another repository, `verify` compares the\ncopies against the lock and the lock against what the tree renders to, and silently leaves out\nthe two comparisons that need the canonical text (the text against the lock, and the conformance\ntests against the lock) when the cache is not there. Run `fetch` before `verify` where the full\ncomparison is wanted.\n\nWhen that preceding `fetch` includes a generic Git source, the CI image needs Git and, for an\nSSH URL, OpenSSH. Provision the host key and an SSH key/agent, or a non-prompting HTTPS\ncredential helper, before the command. Confirm the same repository URL with ordinary Git in\nthe job setup: `fetch` never opens an authentication or host-key prompt. GitHub-hosted Ubuntu\nrunners already include Git and OpenSSH, but credentials and `known_hosts` remain the\nrepository owner's responsibility.\n\nWhat is never left out is the lock recording nothing at all for a declared contract: that is\nreported as `unresolved` with a cache or without one, so the tree an `add` wrote the mapping\nfor and no `gen` ever finished fails the build instead of shipping a skill without the document\nit declares.\n\nA pre-commit hook running `verify` is an optional tightening: it reads the tree and digests the\ncopies, nothing more.\n\nThe vendored copies also carry a `DO NOT EDIT` header naming the generating tool, so an editor\nwho finds a copy learns the canonical text lives elsewhere before an edit lands in the wrong\nfile; `verify` catches whatever lands anyway.\n\n## Reference\n\nEverything in this section is external compatibility: none of it changes without a version\nchange.\n\n**A vendored copy's bytes** — a fixed four-line header, then the canonical body:\n\n```\n<!-- DO NOT EDIT. Generated by agentic-skill-vendor. -->\n<!-- contract: <id> -->\n<!-- source-digest: sha256:<64 lowercase hex digits> -->\n\n```\n\nNo source path and no time of generation appear anywhere in the file, so two runs over\nunchanged input produce the same bytes.\n\n**A raw-byte contract's digests** — the same framing as a conformance digest (below), twice.\nThe contract's own digest names each file by its canonical path (the `files` key, expanded for\na directory), so it says what the canonical side is and nothing about where copies land. Each\nplacement's digest names the files relative to the dest — a file dest by its own name — and\nleaves out the `.vendored` marker, so a copy can be judged from the copy alone. The marker\nitself is the four-line header above as a file of its own, carrying the contract's digest.\n\n**The lock** — `dependencies` (skill → ids), `resolutions` (id → digest, with `conformance`\nwhere tests exist and `\"kind\": \"raw\"` for a raw-byte contract), `sources` (pins, only where a\nsource is registered) and `placements` (skill → dest → `contract`, `src`, `digest`, only where\nraw bytes are distributed). `placements` is written by `gen` alone and carried unchanged by\nevery other command; directory dests and srcs keep their trailing slash.\n\n**A conformance digest** — the contract's conformance tree hashed as one sequence, each file\nframed as `<relative posix path> NUL <byte length in decimal> NUL <bytes>`, in path order, raw\nbytes, never canonicalized. Files excluded by the tree's own `.gitignore` rules are left out —\nthe rules are read, never git's index — so editing a `.gitignore` can change a conformance\ndigest, and `verify` reports that until `gen` records the new value.\n\n**Declarations** — frontmatter is read as YAML and judged against a schema. Any YAML spelling\nof \"a list of ids under `metadata.contracts`\" is accepted. A declaration the tool cannot make\nsense of stops the run with exit `2` rather than being read as \"this skill declares nothing\":\nunparseable YAML, a malformed opening `---`, a `contracts` value that is not a non-empty list,\nan entry that is not text, an id unusable as a path component, or a digest written beside an id\n— pins live in the lock, never in a skill.\n\n**Guarded tree access** — a symlink anywhere in the tree is refused; a path holding a different\nkind of file system entity than expected stops the run instead of reading as absent; writes are\natomic; identity is verified byte for byte. A run that fails part-way never leaves a tree that\nlooks finished: whatever it leaves behind is a state `verify` reports as a violation.\n\n**Violation kinds** — every reported line opens with a stable kind prefix:\n\n| Kind | From | What it means |\n|---|---|---|\n| `closure` | `gen`, `verify` | a skill declares a contract whose canonical text is not there — the one state `gen` refuses to write over |\n| `unresolved` | `verify` | the lock records nothing for a declared contract |\n| `stale-lock` | `verify` | the lock records a digest the canonical text no longer has |\n| `drift` | `verify` | a vendored copy, a raw-byte dest or its `.vendored` marker is missing, or is not what the lock pins |\n| `extra` | `verify` | a file under a skill's vendor directory answers to no declaration |\n| `lock` | `verify` | the lock file differs from what the tree renders to |\n| `placement` | `verify` | the lock's record of what was placed where disagrees with what the declarations and the table say |\n| `source-mismatch` | `verify` | the lock pins a source to a repository the table of origins does not register it at |\n| `conformance-mismatch` | `verify` | a conformance tree differs from the digest the lock records |\n| `parent-escape` | `lint-selfcontain` | something inside a skill points above its own directory |\n| `absolute-path` | `lint-selfcontain` | something inside a skill names an absolute path |\n| `symlink-escape` | `lint-selfcontain` | a symlink inside a skill resolves outside it |\n| `self-test` | `self-test` | the tool disagrees with a vector embedded in it |\n\nA successful run reports in the same shape, and on the same stability footing: `adopted`,\n`retired`, `claimed`, `cleared` and `unused` from `gen`, `mapped` and `unmapped` for the table\nof origins, and `resolved` and `unlocated` from `add` and `update`.\n\n## Design notes\n\nWhy the fetching half is shaped the way it is. None of this is needed to use the tool.\n\n**A download is judged against its commit, never against the lock.** The lock records the\ncommit each source is pinned at, and `fetch` judges every downloaded file against the object id\nthat commit's own listing gives it. A commit is immutable and says what each of its files\nhashes to, so \"the cache holds what this commit holds\" is established without the lock — which\nis what lets the cache be rebuilt from whatever state the tree is in.\n\n**A revision arrives whole or not at all.** Its directory is placed in a single move once every\nfile has arrived, so a directory standing at its place means that revision was fetched whole,\nand a run stopped part way leaves no revision behind for a later command to read as a fetch\nthat finished.\n\n**Three GitHub API answers stop a fetching run with nothing written.** A file the run was about to take —\nthe canonical text at its mapped path, or a conformance test beside it — listed as anything but\nan ordinary file (a symlink, a submodule) or under a path that does not stay inside the\nrepository listing it (an empty segment, a `.` or `..` step, a backslash); a redirect, since\nthe fixed pair of hosts would otherwise hold for the first request of a run only; and a value\nthat would not read back as itself, the default branch `add` records included.\n\n**The conformance directory is judged although nothing is taken from it.** A link or a submodule\nstanding there is listed with nothing beneath it, so tests the source does keep would be pinned\nas absent. An ordinary file there is left alone: nothing can sit under a path a blob occupies,\nso a contract carrying no tests is then a fact rather than something the fetch dropped.\n\n**Nothing else in a source is judged.** Everything else is ignored whatever its mode and\nwhatever its name, and never fetched — a file no run opens cannot be dropped from a fetch and\nread back as one upstream does not hold. Judged over the whole listing instead, one file a\nrepository on POSIX legitimately tracks (`tests/fixtures/windows\\path.txt` among them) put\nevery contract that source holds out of reach, over a name no contract had anything to do with.\n\n## Development\n\nThe development toolchain is Bun, with Biome for lint and format; `PROJECT.md` records the\ncommands and layout, and `docs/spec/` (Japanese) records the design decisions. The source is\nwritten against Node-compatible builtins and web standard APIs only — no runtime's own API —\nwhich is what keeps Node, Bun and Deno equally supported.\n\n## License\n\nMIT\n","readmeFilename":"README.md"}