{"_id":"@agentvibes/guardrails","_rev":"6-78c447a80bf2ef1f2cda8b3231ebdfda","name":"@agentvibes/guardrails","dist-tags":{"latest":"0.2.4"},"versions":{"0.1.0":{"name":"@agentvibes/guardrails","version":"0.1.0","license":"MIT","_id":"@agentvibes/guardrails@0.1.0","maintainers":[{"name":"yatsyk","email":"yatsyk@gmail.com"}],"homepage":"https://github.com/AgentVibes/guardrails#readme","bugs":{"url":"https://github.com/AgentVibes/guardrails/issues"},"bin":{"guardrails":"dist/cli.js"},"dist":{"shasum":"67b9d2776317ad215fbcc28f90b9c0b23bcd3450","tarball":"https://registry.npmjs.org/@agentvibes/guardrails/-/guardrails-0.1.0.tgz","fileCount":260,"integrity":"sha512-dWQt0FBZEwwxBhg8Rswyh45mtQATop6lTva9Sl17A3npUHhqSI0uB3kn7B9+Nr2MEmNTIcrZhuuvLE/ghB2heg==","signatures":[{"sig":"MEUCIB/ssxetRzyqD+uUjJJ6qkNO/PZxASQcARXegcgtvD78AiEA7eqjhAEhZO9W3Uiaz5jOziV+wxNtFMLf/r0vN7JqjC4=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":503816},"type":"module","engines":{"node":">=22"},"exports":{"./biome":"./configs/biome.json","./plugin":"./dist/pluginApi.js","./sgconfig":"./sgconfig.yml","./tsconfig":"./configs/tsconfig.base.json","./package.json":"./package.json"},"gitHead":"c41a3cc881435766ded3143aa22e494f4463cc51","scripts":{"lint":"biome check .","build":"tsc -p tsconfig.json","check":"pnpm build && pnpm lint && pnpm test:rules && pnpm test:gate && pnpm test:metrics && pnpm test:init && pnpm test:severity && pnpm test:screens && pnpm test:hooks && pnpm test:iterate && pnpm test:leaks && pnpm leaks","leaks":"node dist/cli.js leaks .","format":"biome check --write .","prepare":"tsc -p tsconfig.json","test:gate":"node dist/gateRed.js","test:init":"node dist/initTest.js","typecheck":"tsc -p tsconfig.json --noEmit","test:hooks":"node dist/hookStopTest.js","test:leaks":"node dist/leaksRed.js","test:rules":"node dist/testRules.js","test:iterate":"node dist/iterateTest.js","test:metrics":"node dist/metricsCycle.js","test:screens":"node dist/screenScopeTest.js","test:severity":"node dist/severityTest.js"},"_npmUser":{"name":"yatsyk","email":"yatsyk@gmail.com"},"repository":{"url":"git+https://github.com/AgentVibes/guardrails.git","type":"git"},"_npmVersion":"11.12.1","description":"Verification toolkit that makes agents write simple code: ast-grep rule canon, diff ratchet, biome/tsconfig presets, one CLI.","directories":{},"_nodeVersion":"25.9.0","dependencies":{"ts-pattern":"^5.6.2","typescript":"^5.7.3"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"packageManager":"pnpm@10.12.1","devDependencies":{"@types/node":"^22.13.0"},"_npmOperationalInternal":{"tmp":"tmp/guardrails_0.1.0_1788161618155_0.1865614539908358","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@agentvibes/guardrails","version":"0.2.0","license":"MIT","_id":"@agentvibes/guardrails@0.2.0","maintainers":[{"name":"yatsyk","email":"yatsyk@gmail.com"}],"homepage":"https://github.com/AgentVibes/guardrails#readme","bugs":{"url":"https://github.com/AgentVibes/guardrails/issues"},"bin":{"guardrails":"dist/cli.js"},"dist":{"shasum":"0ecfe74ab9528ba1f9516debf11466f867d04c83","tarball":"https://registry.npmjs.org/@agentvibes/guardrails/-/guardrails-0.2.0.tgz","fileCount":303,"integrity":"sha512-eKouBSX0p1WvPyCKONQWxKNC27kUBJ82mKg+1bRTcyzzRb06oiyLtpFXodiqCbT1z9S5Vj9A80GwGwxC4pP4iw==","signatures":[{"sig":"MEUCIHVXpPl7mnljhjKGoYv5XoL1uZjXz6/605QCNcbkQQIbAiEAhBKe9IKxLoXHsyUHTE13PZJSwaVjLXYv2AFG1PPa/Yc=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":644182},"type":"module","engines":{"node":">=22"},"exports":{"./biome":"./configs/biome.json","./plugin":"./dist/pluginApi.js","./sgconfig":"./sgconfig.yml","./tsconfig":"./configs/tsconfig.base.json","./package.json":"./package.json"},"gitHead":"8965cd472a2b8986175dd689f68d8a8d1bd2bd7c","scripts":{"lint":"biome check .","build":"tsc -p tsconfig.json","check":"pnpm build && pnpm lint && pnpm test:rules && pnpm test:twins && pnpm test:repo-rules && pnpm test:preset && pnpm test:gate && pnpm test:sibling-clone && pnpm test:metrics && pnpm test:init && pnpm test:severity && pnpm test:screens && pnpm test:hooks && pnpm test:iterate && pnpm test:leaks && pnpm leaks","leaks":"node dist/cli.js leaks .","format":"biome check --write .","prepare":"tsc -p tsconfig.json","test:gate":"node dist/gateRed.js","test:init":"node dist/initTest.js","typecheck":"tsc -p tsconfig.json --noEmit","test:hooks":"node dist/hookStopTest.js","test:leaks":"node dist/leaksRed.js","test:rules":"node dist/testRules.js","test:twins":"node dist/twinCoverageTest.js","test:preset":"node dist/presetDriftTest.js","test:iterate":"node dist/iterateTest.js","test:metrics":"node dist/metricsCycle.js","test:screens":"node dist/screenScopeTest.js","test:severity":"node dist/severityTest.js","test:repo-rules":"node dist/repoRulesTest.js","test:sibling-clone":"node dist/siblingCloneTest.js"},"_npmUser":{"name":"yatsyk","email":"yatsyk@gmail.com"},"repository":{"url":"git+https://github.com/AgentVibes/guardrails.git","type":"git"},"_npmVersion":"11.12.1","description":"Verification toolkit that makes agents write simple code: ast-grep rule canon, diff ratchet, biome/tsconfig presets, one CLI.","directories":{},"_nodeVersion":"25.9.0","dependencies":{"ts-pattern":"^5.6.2","typescript":"^5.7.3"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"packageManager":"pnpm@10.12.1","devDependencies":{"@types/node":"^22.13.0"},"_npmOperationalInternal":{"tmp":"tmp/guardrails_0.2.0_1788759691943_0.8787826926503746","host":"s3://npm-registry-packages-npm-production"}},"0.2.1":{"name":"@agentvibes/guardrails","version":"0.2.1","license":"MIT","_id":"@agentvibes/guardrails@0.2.1","maintainers":[{"name":"yatsyk","email":"yatsyk@gmail.com"}],"homepage":"https://github.com/AgentVibes/guardrails#readme","bugs":{"url":"https://github.com/AgentVibes/guardrails/issues"},"bin":{"guardrails":"dist/cli.js"},"dist":{"shasum":"d5eaf017d08b042cd0beb0fca8f978a4d2bd6c87","tarball":"https://registry.npmjs.org/@agentvibes/guardrails/-/guardrails-0.2.1.tgz","fileCount":303,"integrity":"sha512-rQ1h3nWUOSuUC6dFmXcUrV/2mPcCmQdXZISlD8tRBNTbZ19OcNvbsSRUo0Vbg6A/OFMhxIxYBkXyrKsAl/x8BA==","signatures":[{"sig":"MEUCIDAwunHvw7BMtF1/f9If/Yt/9D2RYOfanp7GM9XPSl54AiEAjeISi5Cg0Gxc+rSDj52AogSy+WMZo+Gjy7eYztZuBtE=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":644196},"type":"module","engines":{"node":">=22"},"exports":{"./biome":"./configs/biome.json","./plugin":"./dist/pluginApi.js","./sgconfig":"./sgconfig.yml","./tsconfig":"./configs/tsconfig.base.json","./package.json":"./package.json"},"gitHead":"617504ac671bffc12e7de29129126bb96da40d71","scripts":{"lint":"biome check .","build":"tsc -p tsconfig.json","check":"pnpm build && pnpm lint && pnpm test:rules && pnpm test:twins && pnpm test:repo-rules && pnpm test:preset && pnpm test:gate && pnpm test:sibling-clone && pnpm test:metrics && pnpm test:init && pnpm test:severity && pnpm test:screens && pnpm test:hooks && pnpm test:iterate && pnpm test:leaks && pnpm leaks","leaks":"node dist/cli.js leaks .","format":"biome check --write .","prepare":"tsc -p tsconfig.json","test:gate":"node dist/gateRed.js","test:init":"node dist/initTest.js","typecheck":"tsc -p tsconfig.json --noEmit","test:hooks":"node dist/hookStopTest.js","test:leaks":"node dist/leaksRed.js","test:rules":"node dist/testRules.js","test:twins":"node dist/twinCoverageTest.js","test:preset":"node dist/presetDriftTest.js","test:iterate":"node dist/iterateTest.js","test:metrics":"node dist/metricsCycle.js","test:screens":"node dist/screenScopeTest.js","test:severity":"node dist/severityTest.js","test:repo-rules":"node dist/repoRulesTest.js","test:sibling-clone":"node dist/siblingCloneTest.js"},"_npmUser":{"name":"yatsyk","email":"yatsyk@gmail.com"},"repository":{"url":"git+https://github.com/AgentVibes/guardrails.git","type":"git"},"_npmVersion":"11.12.1","description":"Verification toolkit that makes agents write simple code: ast-grep rule canon, diff ratchet, biome/tsconfig presets, one CLI.","directories":{},"_nodeVersion":"25.9.0","dependencies":{"ts-pattern":"^5.6.2","typescript":"^5.7.3"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"packageManager":"pnpm@10.12.1","devDependencies":{"@types/node":"^22.13.0"},"_npmOperationalInternal":{"tmp":"tmp/guardrails_0.2.1_1788763544662_0.6093143312354186","host":"s3://npm-registry-packages-npm-production"}},"0.2.2":{"name":"@agentvibes/guardrails","version":"0.2.2","license":"MIT","_id":"@agentvibes/guardrails@0.2.2","maintainers":[{"name":"yatsyk","email":"yatsyk@gmail.com"}],"homepage":"https://github.com/AgentVibes/guardrails#readme","bugs":{"url":"https://github.com/AgentVibes/guardrails/issues"},"bin":{"guardrails":"dist/cli.js"},"dist":{"shasum":"85db540a06a074256be44e1e8b99f1ea9d6bde51","tarball":"https://registry.npmjs.org/@agentvibes/guardrails/-/guardrails-0.2.2.tgz","fileCount":309,"integrity":"sha512-mOr8HqYuVhfOK/5AKnDG/C000SO453zj8r7PyMcHfA/Ar1Dw+9+BTydGtClHMTVSLlqeaOa+XvpW18TONE4xZw==","signatures":[{"sig":"MEUCIEMCgniXUNnLh9TmVF22JfHiDM9/VrjUjVjSdAOUxgSYAiEA5NwxHhoVO+6wnt0wf449qkbRIBwnuNN2pQTO5myYxfU=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":655740},"type":"module","engines":{"node":">=22"},"exports":{"./biome":"./configs/biome.json","./plugin":"./dist/pluginApi.js","./sgconfig":"./sgconfig.yml","./tsconfig":"./configs/tsconfig.base.json","./package.json":"./package.json"},"gitHead":"aea13faea6ffb601ccc06694d912dacb3e2f7599","scripts":{"lint":"biome check .","build":"tsc -p tsconfig.json","check":"pnpm build && pnpm lint && pnpm test:rules && pnpm test:twins && pnpm test:repo-rules && pnpm test:preset && pnpm test:gate && pnpm test:sibling-clone && pnpm test:metrics && pnpm test:init && pnpm test:severity && pnpm test:screens && pnpm test:jsx-suppress && pnpm test:hooks && pnpm test:iterate && pnpm test:leaks && pnpm leaks","leaks":"node dist/cli.js leaks .","format":"biome check --write .","prepare":"tsc -p tsconfig.json","test:gate":"node dist/gateRed.js","test:init":"node dist/initTest.js","typecheck":"tsc -p tsconfig.json --noEmit","test:hooks":"node dist/hookStopTest.js","test:leaks":"node dist/leaksRed.js","test:rules":"node dist/testRules.js","test:twins":"node dist/twinCoverageTest.js","test:preset":"node dist/presetDriftTest.js","test:iterate":"node dist/iterateTest.js","test:metrics":"node dist/metricsCycle.js","test:screens":"node dist/screenScopeTest.js","test:severity":"node dist/severityTest.js","test:repo-rules":"node dist/repoRulesTest.js","test:jsx-suppress":"node dist/jsxSuppressTest.js","test:sibling-clone":"node dist/siblingCloneTest.js"},"_npmUser":{"name":"yatsyk","email":"yatsyk@gmail.com"},"repository":{"url":"git+https://github.com/AgentVibes/guardrails.git","type":"git"},"_npmVersion":"11.12.1","description":"Verification toolkit that makes agents write simple code: ast-grep rule canon, diff ratchet, biome/tsconfig presets, one CLI.","directories":{},"_nodeVersion":"25.9.0","dependencies":{"ts-pattern":"^5.6.2","typescript":"^5.7.3"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"packageManager":"pnpm@10.12.1","devDependencies":{"@types/node":"^22.13.0"},"_npmOperationalInternal":{"tmp":"tmp/guardrails_0.2.2_1788764969411_0.605184514969455","host":"s3://npm-registry-packages-npm-production"}},"0.2.3":{"name":"@agentvibes/guardrails","version":"0.2.3","license":"MIT","_id":"@agentvibes/guardrails@0.2.3","maintainers":[{"name":"yatsyk","email":"yatsyk@gmail.com"}],"homepage":"https://github.com/AgentVibes/guardrails#readme","bugs":{"url":"https://github.com/AgentVibes/guardrails/issues"},"bin":{"guardrails":"dist/cli.js"},"dist":{"shasum":"cf81e62eec4e4065577c8ddab55f8a9d32d98228","tarball":"https://registry.npmjs.org/@agentvibes/guardrails/-/guardrails-0.2.3.tgz","fileCount":309,"integrity":"sha512-rTaLgE4pNQjInHs0hCJL8b+2bGI+XM9lShHvjRXiPotdKFI34lRgT6eGSdRoOOhGxJsS29lbHK3A1yrpHXQ+Cw==","signatures":[{"sig":"MEYCIQDr9XaJNJ+4MZNF8KcqylIJDLuOxitBm3XJlpBk56+wlAIhALdx46uv6gNCzOqtAm1jR6wTMdN8q91FNW+F8HWcDzM+","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":662750},"type":"module","engines":{"node":">=22"},"exports":{"./biome":"./configs/biome.json","./plugin":"./dist/pluginApi.js","./sgconfig":"./sgconfig.yml","./tsconfig":"./configs/tsconfig.base.json","./package.json":"./package.json"},"gitHead":"ba41fbd9dd816c5760f996f426887fad581f0198","scripts":{"lint":"biome check .","build":"tsc -p tsconfig.json","check":"pnpm build && pnpm lint && pnpm test:rules && pnpm test:twins && pnpm test:repo-rules && pnpm test:preset && pnpm test:gate && pnpm test:sibling-clone && pnpm test:metrics && pnpm test:init && pnpm test:severity && pnpm test:screens && pnpm test:jsx-suppress && pnpm test:hooks && pnpm test:iterate && pnpm test:leaks && pnpm leaks","leaks":"node dist/cli.js leaks .","format":"biome check --write .","prepare":"tsc -p tsconfig.json","test:gate":"node dist/gateRed.js","test:init":"node dist/initTest.js","typecheck":"tsc -p tsconfig.json --noEmit","test:hooks":"node dist/hookStopTest.js","test:leaks":"node dist/leaksRed.js","test:rules":"node dist/testRules.js","test:twins":"node dist/twinCoverageTest.js","test:preset":"node dist/presetDriftTest.js","test:iterate":"node dist/iterateTest.js","test:metrics":"node dist/metricsCycle.js","test:screens":"node dist/screenScopeTest.js","test:severity":"node dist/severityTest.js","test:repo-rules":"node dist/repoRulesTest.js","test:jsx-suppress":"node dist/jsxSuppressTest.js","test:sibling-clone":"node dist/siblingCloneTest.js"},"_npmUser":{"name":"yatsyk","email":"yatsyk@gmail.com"},"repository":{"url":"git+https://github.com/AgentVibes/guardrails.git","type":"git"},"_npmVersion":"11.12.1","description":"Verification toolkit that makes agents write simple code: ast-grep rule canon, diff ratchet, biome/tsconfig presets, one CLI.","directories":{},"_nodeVersion":"25.9.0","dependencies":{"ts-pattern":"^5.6.2","typescript":"^5.7.3"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"packageManager":"pnpm@10.12.1","devDependencies":{"@types/node":"^22.13.0"},"_npmOperationalInternal":{"tmp":"tmp/guardrails_0.2.3_1788773303516_0.10956969955618123","host":"s3://npm-registry-packages-npm-production"}},"0.2.4":{"_id":"@agentvibes/guardrails@0.2.4","bin":{"guardrails":"dist/cli.js"},"bugs":{"url":"https://github.com/AgentVibes/guardrails/issues"},"dist":{"shasum":"f94aa4bbdf1a1d49147f934c2ffe475858575d88","tarball":"https://registry.npmjs.org/@agentvibes/guardrails/-/guardrails-0.2.4.tgz","fileCount":312,"integrity":"sha512-Lsal+JqwEirwBt2EWqXON5FTikt2yaT0LFZmYQzbgIqg1yxM1YSSkWL9QNVe1x0l5GPW8btN/J8CkJQkWTLKJw==","signatures":[{"sig":"MEUCIBTMqTnLDTpnAqZiQhD3GhaLGSypn8JpYw4h0vHj+PIiAiEAme0gUByRbkrk5UFOZSHnpNzDRS2SQXiHiYrjcuCwLpY=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIDCWu1xCBn9pCvFM6YxMy7EKaXs/N4Yaxep6UFkN/lazAiAevarwHs54p1viscKlCm30mCnoXLRljz+p16hw2ICetg=="}],"unpackedSize":680998},"name":"@agentvibes/guardrails","type":"module","engines":{"node":">=22"},"exports":{"./biome":"./configs/biome.json","./plugin":"./dist/pluginApi.js","./sgconfig":"./sgconfig.yml","./tsconfig":"./configs/tsconfig.base.json","./package.json":"./package.json"},"license":"MIT","scripts":{"lint":"biome check .","build":"tsc -p tsconfig.json","check":"pnpm build && pnpm lint && pnpm test:rules && pnpm test:twins && pnpm test:repo-rules && pnpm test:preset && pnpm test:gate && pnpm test:sibling-clone && pnpm test:metrics && pnpm test:init && pnpm test:severity && pnpm test:screens && pnpm test:jsx-suppress && pnpm test:hooks && pnpm test:iterate && pnpm test:rename-diff && pnpm test:leaks && pnpm leaks","leaks":"node dist/cli.js leaks .","format":"biome check --write .","prepare":"tsc -p tsconfig.json","test:gate":"node dist/gateRed.js","test:init":"node dist/initTest.js","typecheck":"tsc -p tsconfig.json --noEmit","test:hooks":"node dist/hookStopTest.js","test:leaks":"node dist/leaksRed.js","test:rules":"node dist/testRules.js","test:twins":"node dist/twinCoverageTest.js","test:preset":"node dist/presetDriftTest.js","test:iterate":"node dist/iterateTest.js","test:metrics":"node dist/metricsCycle.js","test:screens":"node dist/screenScopeTest.js","test:severity":"node dist/severityTest.js","test:repo-rules":"node dist/repoRulesTest.js","test:rename-diff":"node dist/renameDiffTest.js","test:jsx-suppress":"node dist/jsxSuppressTest.js","test:sibling-clone":"node dist/siblingCloneTest.js"},"version":"0.2.4","_npmUser":{"name":"yatsyk","email":"yatsyk@gmail.com"},"homepage":"https://github.com/AgentVibes/guardrails#readme","repository":{"url":"git+https://github.com/AgentVibes/guardrails.git","type":"git"},"_npmVersion":"11.12.1","description":"Verification toolkit that makes agents write simple code: ast-grep rule canon, diff ratchet, biome/tsconfig presets, one CLI.","directories":{},"maintainers":[{"name":"yatsyk","email":"yatsyk@gmail.com"}],"_nodeVersion":"25.9.0","dependencies":{"ts-pattern":"^5.6.2","typescript":"^5.7.3"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"packageManager":"pnpm@10.12.1","devDependencies":{"@types/node":"^22.13.0"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/guardrails_0.2.4_1790769620428_0.44235306464943536"}}},"time":{"created":"2026-08-31T07:33:37.962Z","modified":"2026-09-30T12:00:20.734Z","0.1.0":"2026-08-31T07:33:38.304Z","0.2.0":"2026-09-07T05:41:32.092Z","0.2.1":"2026-09-07T06:45:44.834Z","0.2.2":"2026-09-07T07:09:29.553Z","0.2.3":"2026-09-07T09:28:23.701Z","0.2.4":"2026-09-30T12:00:20.526Z"},"bugs":{"url":"https://github.com/AgentVibes/guardrails/issues"},"license":"MIT","homepage":"https://github.com/AgentVibes/guardrails#readme","repository":{"url":"git+https://github.com/AgentVibes/guardrails.git","type":"git"},"description":"Verification toolkit that makes agents write simple code: ast-grep rule canon, diff ratchet, biome/tsconfig presets, one CLI.","maintainers":[{"name":"yatsyk","email":"yatsyk@gmail.com"}],"readme":"# @agentvibes/guardrails\n\nVerification toolkit that makes agents write simple code. One package holds the\ncanonical ast-grep rule set (moved here from the agent-skills `defensive-errors`\nskill — this repo is now the source of truth; the skill and hooks consume\nbuilds of this package), the react structure check, an added-lines diff\nratchet, and the shared biome/tsconfig presets.\n\n## Install\n\n```sh\npnpm add -D @agentvibes/guardrails\n```\n\nTools (ast-grep, biome) are not npm dependencies: the CLI resolves them from\nPATH, falls back to `mise x <tool>@<pin>` using the pins in this package's\n`mise.toml`, and prints a one-line install hint when neither works.\n\n**Biome must be 2.5 or newer.** The preset writes\n`linter.rules.preset: \"recommended\"`, a key biome 2.4 does not know, so an older\nbiome refuses the whole config rather than degrading — it lints nothing and says\n`Found an unknown key 'preset'`. Biome v1 refuses it harder still, on\n`includes`, `root` and `assist` as well. Measured across the fleet on\n2026-09-07: of 18 repos with a readable biome, twelve were on 1.9.4.\n\nAdopting the preset, and what a repo may keep in its own `biome.json`, is\n[Presets](#presets) below — including `[biome] preset = \"enforced\"`, which is\nwhat makes drift fail a build rather than only be reported.\n\nInstalling straight from git (`github:AgentVibes/guardrails#<sha>`) works —\nthe `prepare` script builds `dist/` at install time — but pnpm blocks\nlifecycle scripts of git-hosted deps by default, and the unblock knob moved\nbetween pnpm versions:\n\n| pnpm | where to allow the build |\n|---|---|\n| 10.0–10.4 | `package.json` → `pnpm.onlyBuiltDependencies: [\"@agentvibes/guardrails\"]` |\n| 10.5+ | `pnpm-workspace.yaml` → `onlyBuiltDependencies: [\"@agentvibes/guardrails\"]` |\n| 11+ | `pnpm-workspace.yaml` → `allowBuilds` keyed by the exact resolved spec |\n\n(or install from the registry once the package is published).\n\n## Commands\n\nAll subcommands accept `--json`.\n\n| command | what | exit |\n|---|---|---|\n| `guardrails verify [paths]` | full scan: rule canon + text-greps + structure (one component per file, 120-line error / 90-line warning) | 1 on any error-tier finding |\n| `guardrails verify-diff [--base R]` | ratchet: error-tier findings on lines your diff ADDED vs merge-base; falls back to whole-file, then whole-tree — never to silence | 1 on new findings only |\n| `guardrails doctor` | tool versions + resolution route, ruleset SHA, config discovery | 1 if a tool is missing |\n| `guardrails init` | writes `sgconfig.yml`, `biome.json` (extends the preset), and a detected-and-materialized `[stack]` in `.agentvibes/project.toml` | 0 |\n| `guardrails metrics [paths]` | per-component (loc, hooks, props, observer, JSX depth, branching), per-file (sloc, context-cost) and per-project metrics; `--check` compares the GATED set against the committed `.guardrails/metrics.json` baseline (recomputes facts, never trusts the file); `--update-baseline` tightens it (2% hysteresis, never loosens without `--force`); `--snapshot` appends a JSONL trend row | 1 on ratchet regression, 2 when `--check` finds no baseline |\n\nGated (ratchet, lower = better): p90 component_loc, useState density,\ninline-map-row count, p90 context-cost, runInAction count, async-in-store,\nnew-Map-in-store, reactions total, loading-boolean shapes. Everything else is\nobserve-only.\n\nCounter provenance: runInAction / async-in-store / new-Map-in-store /\ninline-map-row are finding counts of the canon rules (`store-no-runinaction`,\n`store-async-method`, `store-new-map`, `inline-map-row` + tsx twins) — the\nmetric counts exactly what `verify` gates. Note the rules' scoping:\n\"store\" means a class calling `make(Auto)Observable` (not a name/path\nheuristic), and `async_in_store` counts only async methods that mutate `this`\nWITHOUT a `runInAction` patch — the patched ones are already in\n`runInAction_count`, so the two counters partition the should-be-`flow()`\npopulation without double counting. `reactions_total` is a direct AST\ncount (no rule yet). `loading_boolean_shapes` is deliberately NOT the\n`state-loading-boolean-shape` rule: the rule fires only on type-level shapes\nwhere the outcome is recorded twice (loading flag + second outcome/payload\nfield), while the metric also counts lone boolean progress-flag declarations\n(`loading = false` class fields) — the wider Resource<T> migration target.\n\n## CI gate (reusable workflow)\n\nAdd the whole gate as one job:\n\n```yaml\njobs:\n  guardrails:\n    uses: AgentVibes/guardrails/.github/workflows/guardrails-gate.yml@v0\n    # with:\n    #   leaks: true        # recommended for public repos\n    #   base: origin/main  # override the merge-base ladder\n```\n\nIt checks out with full history, installs the pinned toolchain via mise, runs\n`pnpm install --frozen-lockfile`, then BLOCKING `guardrails verify-diff` and\n`guardrails metrics --check` (skipped with a loud `::notice::` when the repo\nhas no `.guardrails/metrics.json` baseline — never silently).\n\nWorkspaces that reach outside their repo (`link:../<repo>/...` overrides,\n`../../<org>/<repo>` workspace globs) pass\n`sibling_repos: \"owner/repo owner2/repo2\"`: the gate then checks the main\nrepo out at `repos/<org>/<repo>` depth and shallow-clones each sibling at its\nown `repos/<owner>/<name>`, reproducing a `gits/<org>/<repo>` disk layout so\nthe relative escapes resolve. **Each sibling path is removed and re-cloned on\nevery run.** A self-hosted runner keeps its `_work` directory between jobs and\n`actions/checkout` cleans only its own path, so the leftovers used to make the\nstep exit 128 on every run after the first — before install, so the ratchet\nshowed as `skipped` rather than as not-run (is-8774fe0e). Re-cloning also keeps\nthe sibling current: gating against last week's sibling is a wrong answer, not\na cheap one. Listing the repo under test in `sibling_repos` is refused — that\npath holds the run's own checkout. Private siblings additionally need\n`secrets: { sibling_token: <PAT with read on them> }` — the default job token\ncannot reach other repos. `@v0` is a\nmoving tag that follows validated releases, actions-style; pin `@<commit-sha>`\nif your repo wants immutable supply-chain refs.\n\n**Mandatory adoption step — prove the gate can fail.** After wiring the job,\nopen a throwaway PR containing an error-tier violation and watch it go red:\n\n```sh\ngit checkout -b gate-red-team\nprintf 'export const boom = (x: unknown) => x as any;\\n' > gateRedTeam.ts\ngit add gateRedTeam.ts && git commit -m \"red-team the guardrails gate\" && git push -u origin gate-red-team\n# open the PR → the guardrails job MUST fail on as-any-escape.\n# Then close the PR and delete the branch.\n```\n\nA gate that has never been seen red proves nothing — do not skip this.\n\n## Stop hook\n\n`guardrails hook-stop` is the \"cannot say done over red findings\" echelon: on\nClaude Code's Stop event it runs the verify-diff ratchet over the session's\nchanged files and blocks the stop while NEW error-tier findings remain. Loop\nbreaker built in: after 3 blocks in one session (env\n`GUARDRAILS_STOP_MAX_BLOCKS`) it stops blocking and prints a loud warning\ninstead — a wedged agent is worse than undercleaned code, and CI holds the\nsame line anyway. Like hook-postedit, it never fails the session on its own\ndefects.\n\n```json\n{ \"hooks\": { \"Stop\": [ { \"hooks\": [{ \"type\": \"command\",\n  \"command\": \"pnpm exec guardrails hook-stop\" }] } ] } }\n```\n\nFor a USER-level install (synced settings.json, per-machine tools), route\nboth hooks through an opt-hook.sh-style wrapper that no-ops when the tool is\nabsent on a host — a bare command errors on every event on machines without\nthe package.\n\n## Iterate harness\n\n`guardrails iterate --task <file|text> --cmd '<runner>'` is the\niterate-until-pass loop for cheap-model campaigns: run the agent command, run\nthe gate ITSELF (never trusting the agent's report), feed the findings back as\nthe next prompt's compact feedback, repeat to green or `--max` (default 6;\n`--gate` overrides the default `verify-diff`). The runner gets the prompt on\nstdin and via `$GUARDRAILS_PROMPT_FILE`, plus `$GUARDRAILS_ATTEMPT`:\n\n```sh\nguardrails iterate --task task.md \\\n  --cmd 'claude -p \"$(cat \"$GUARDRAILS_PROMPT_FILE\")\" --model sonnet' \\\n  --gate 'pnpm exec guardrails verify-diff && pnpm exec guardrails metrics --check'\n```\n\nEvery attempt's tuple (agent exit, gate exit, gate tail, duration) is recorded\nand printed (`--json` for harness pipelines).\n\n## Post-edit hook\n\n`guardrails hook-postedit` is the Claude Code PostToolUse hook: it reads the\nhook JSON on stdin, and for an Edit/Write of a .ts/.tsx file scans just that\nfile, gating the lines the edit actually changed (merge-base ladder; Write,\nuntracked files, and unresolvable bases degrade to whole-file — never to\nsilence). Error-tier findings emit a blocking decision, warnings attach as\ncontext. Wire it in settings.json:\n\n```json\n{ \"hooks\": { \"PostToolUse\": [ { \"matcher\": \"Edit|Write\",\n  \"hooks\": [{ \"type\": \"command\", \"command\": \"pnpm exec guardrails hook-postedit\" }] } ] } }\n```\n\n## Leak gate\n\n`guardrails leaks [paths]` is the public/private boundary gate: it scans every\ntext file for credential patterns, runs gitleaks when available, and exits 1\non any hit. The package ships only generic credential shapes; house marker\nlists ship via private plugins/config — `.guardrails/leaks.txt`, a\n`[leaks] patterns_file =` manifest entry, or the plugin contract's\n`leakPatterns()` hook (see `@agentvibes/guardrails/plugin`). Pattern files are\none regex per line (`<id> <regex>`, `#` comments) and are themselves exempt\nfrom the scan. This repo runs the gate against itself in `pnpm check` and CI.\n\n## Deploy plugins\n\n`guardrails deploy [args…]` is an extension point, not a deployer: the public\nCLI resolves a plugin — `plugin = \"<npm name>\"` under `[deploy]` in\n`.agentvibes/project.toml`, or a single `guardrails-plugin-*` dependency — and\nhands it the args plus the `[deploy]` table. A plugin exports (default or\nnamed `plugin`) an object `{ name, deploy(args, context) }`; the contract type\nships as `@agentvibes/guardrails/plugin`. All topology facts (hosts, orgs,\nregistries, SSO) live in private plugin packages; `guardrails leaks` enforces\nthat this package contains none.\n\n## Presets\n\n```jsonc\n// biome.json\n{ \"extends\": [\"@agentvibes/guardrails/biome\"] }\n// tsconfig.json\n{ \"extends\": \"@agentvibes/guardrails/tsconfig\" }\n```\n\n### One biome config per repo, and it is the preset\n\nThe biome preset is a preset, not a starting point. `guardrails verify` and\n`guardrails doctor` check the repo for drift and name the offending keys in one\nline. Three things count as drift:\n\n| | what | why |\n|---|---|---|\n| a | `biome.json` does not list `@agentvibes/guardrails/biome` in `extends` | it is not using the preset at all |\n| b | it extends the preset and then carries its own `formatter`, `javascript.formatter`, `json.formatter`, `linter` or `assist` | those are the preset's decisions, restated locally so they can drift |\n| c | a second `biome.json` / `biome.jsonc` exists in the tree (outside `node_modules`, `dist`, `.claude` and the other skipped dirs) | two configs means two answers |\n\nTwo things stay legal, because they scope rather than restyle: `files` (a repo\ndecides which of ITS paths are linted) and an `overrides` entry that only turns\n`suspicious.noConsole` off for some paths (a CLI has to print).\n\n**Enforcement is opt-in per repo, for now.** Add\n\n```toml\n# .agentvibes/project.toml\n[biome]\npreset = \"enforced\"\n```\n\nand `verify` exits 2 on drift, `doctor` exits 1. Without it both still SAY the\ndrift — `verify` prints it as a note, `doctor` as a `biome preset` line — and\nneither changes its exit code. The reason is sequencing, not softness: epic\nis-a70a5963 is migrating 37 configs, and 5 of the 7 repos running this gate do\nnot conform yet. Each migration adds the line as its last step; when the last\none lands, the default flips to enforced.\n\nThe reusable gate workflow runs `verify-diff`, not `verify`, so a repo that opts\nin should add a `guardrails verify` (or `doctor`) step to its own CI.\n\n## Rules and fixtures\n\n- `rules/` — the canon. Stable ids, each message is a mini-manual (why + fix).\n  Suppress a genuine false positive with\n  `// ast-grep-ignore: <rule-id> -- <why>` on the line above.\n- `rules/__fixtures__/` — bad/good fixture pairs; `pnpm test:rules` asserts\n  exact hit counts in both directions (a rule that fails to load fails the\n  test — silence is not a pass).\n- `structure/` — the `component-decl` marker rule the CLI turns into\n  `react-multi-component` / `react-component-too-long` /\n  `react-component-needs-folder` findings.\n- `candidates/` — triaged in wave 2. Three PPA R-rules were promoted into\n  `rules/`; the eight that remain are repo-local by decision, with the reason\n  for each recorded in `candidates/README.md`. Still loaded nowhere.\n\n### Scopes — the repo states a fact, the canon says which rules stand down\n\nSome rules cannot be scoped by a glob inside the rule file, because the layout\nthey need to know about is the repo's, not the canon's. Those facts go in\n`.agentvibes/project.toml` under `[verify]`:\n\n```toml\n[verify]\nscreens    = \"^src/screens/\"   # these paths ARE screens\nkit_source = \"^src/\"           # this tree IS @agentvibes/kit\n```\n\nEach key states a fact about the repo — not a suppression. Which rules stop\napplying is the canon's business, declared in the rule itself:\n\n```yaml\nmetadata:\n  appliesTo: not-screens      # direct-store-import\n  appliesTo: not-kit-source   # no-local-kit-clone, + its tsx twin\n```\n\n`screens` exists because a screen owns its page store and must import the class\n`direct-store-import` bans in a component; `kit_source` because\n`no-local-kit-clone` is error-tier and fires on the six canonical declarations\ninside `@agentvibes/kit` itself — the definitions it tells everyone else to\nimport.\n\nAbsent key = absent behaviour: a repo that says nothing is scanned exactly as it\nwas. A pattern that is not a valid regular expression exits 2 with the reason\nrather than being dropped — a scope the gate cannot honour must fail loudly. The\nscope registry is closed (`SCOPES` in `src/screenScope.ts`); a free-form\nper-rule path map in user config would be the growing allowlist this refuses.\n`pnpm test:screens` asserts both directions of each scope AND that every\nregistered scope has at least one rule carrying its marker — a scope nothing\ndeclares filters nothing while looking like it works.\n\nPer-repo severity RAISE: a repo that held a rule stricter than the canon keeps\nits gate via `[severity]` in `.agentvibes/project.toml`\n(`zod-optional-nullable = \"error\"`) — applied through ast-grep's native\n`--error=<rule-id>` in verify, verify-diff and the post-edit hook, so no\nsame-id rule fork is ever needed. Raise-only: downgrades and unknown rule ids\nare refused (exit 2); weakening has its own sanctioned homes (warn-tier biome\ndeviation, `[verify] exclude` for vendored trees, justified per-line\n`ast-grep-ignore`).\n\n## Rule twins — one rule, two ids\n\nast-grep's `typescript` and `tsx` languages are **disjoint, not nested**: a rule\ndeclared `language: typescript` reads `.ts` and never `.tsx`, and vice versa.\nThere is no way to say \"both\" — `language: [typescript, tsx]` fails to parse,\n`languageGlobs` reassigns `.ts` to the tsx language and blinds every remaining\n`typescript` rule (measured corpus-wide: 5 rules dark, 6,094 findings lost\nagainst 334 gained), and two files sharing an id are refused outright.\n\nSo a rule that must run on both is **two files with two ids**: `catch-empty.yml`\nand `catch-empty-tsx.yml`. The trailing `-tsx` is what you suppress with in a\n`.tsx` file, and the finding prints the id, so copying from the output is always\nright. A handful of pairs are spelled the other way round — the tsx arm came\nfirst and the `.ts` half is the `-ts` suffix (`demo-mode-by-default` +\n`demo-mode-by-default-ts`). Both spellings are one family.\n\nThe two arms are one rule wearing two ids, so their `severity`, `files`,\n`ignores`, `utils` and `rule` blocks must stay byte-identical; only `id`,\n`language` and the message differ (the twin names its own suppression id).\n\n**This is enforced, not documented-and-hoped:** `pnpm test:twins`\n(`src/twinCoverageTest.ts`, part of `pnpm check`) fails when a rule family has\nonly one arm and no entry in its `EXCEPTIONS` table, and when two arms that do\nexist have drifted apart. Each exception says *why* — either the missing arm is\nimpossible (the rule matches JSX nodes, or its `files:` globs name only one\nextension) or it is a real gap, and then the entry carries the measurement and\nthe issue that owns it. The failure this prevents is specific and has happened:\na rule pointed at a language it cannot match reports zero, which is exactly what\na correct rule with nothing to find reports. `test:rules` catches that for every\nrule it asserts; `test:twins` catches the arm that was never written.\n\n## Repo-local extra rules\n\nA rule that encodes ONE repo's convention does not belong in the canon — but it\nshould still run. `guardrails init` already writes the second `ruleDir`\ncommented out; uncomment it and drop the rule in:\n\n```yaml\n# sgconfig.yml\nruleDirs:\n  - node_modules/@agentvibes/guardrails/rules\n  - .ast-grep/rules          # repo-local extras\n```\n\n`guardrails verify` and `verify-diff` scan with **both** configs — the bundled\ncanon and, when the working directory has one, the repo's own `sgconfig.yml` —\nand merge the findings, deduped by rule + file + line (the repo config almost\nalways lists the canon `ruleDir` too, so every canon finding otherwise arrives\ntwice). Two directions matter and both are pinned by `pnpm test:repo-rules`:\n\n- Repo-local rules run under `verify`, gate on error tier, and print their own\n  rule id, so a local rule suppresses under its own name. In 0.1.0 they did\n  not run at all — `verify` read only the package's config, and the intranet and\n  DataSpool each added a bare `ast-grep scan -c sgconfig.yml` as a second gate\n  stage to work around it.\n- A repo whose `sgconfig.yml` forgets the canon `ruleDir` does **not** lose the\n  canon. The canon is always scanned; the repo's config is additive.\n\nA repo `sgconfig.yml` that ast-grep cannot load (unreadable `ruleDir`, unparsable\nYAML) exits 2 with ast-grep's own reason. It never reads as \"no findings\" — a\nrule set that failed to load reports exactly what a rule set with nothing to find\nreports, and that silence is the failure this package exists to remove.\n`guardrails doctor` lists the `ruleDirs` the repo config declares, so a repo that\nbelieves it has local rules can see whether the config actually names them.\n\nKeep local ids distinct from canon ids — ast-grep refuses to load two rules with\nthe same id, and a repo-local override of a canon rule is a silent fork rather\nthan a fix. If a local rule turns out to be generally useful, promote it here\nwith fixtures instead of copying it into a second repo.\n\n## Development\n\n`pnpm check` = build (tsc) + biome + fixture harness + twin coverage +\ngate-can-go-red test + metrics/init/severity/screens/hooks/iterate/leaks.\nCI runs exactly that, blocking.\n","readmeFilename":"README.md"}