{"_id":"@blundergoat/gruff-ts","_rev":"8-f9b338c8e5501bdc4fb670ddeb1319d1","name":"@blundergoat/gruff-ts","dist-tags":{"latest":"0.5.0"},"versions":{"0.1.0":{"name":"@blundergoat/gruff-ts","version":"0.1.0","keywords":["typescript","javascript","static-analysis","code-quality","linter","analyzer","cli","sarif","baseline","fingerprint","complexity","dead-code","security","naming","dashboard","ci","github-actions","nodejs"],"author":{"url":"https://www.blundergoat.com/about","name":"Matthew Hansen"},"license":"MIT","_id":"@blundergoat/gruff-ts@0.1.0","maintainers":[{"name":"blundergoat","email":"thatmatthansen@gmail.com"}],"homepage":"https://github.com/blundergoat/gruff-ts#readme","bugs":{"url":"https://github.com/blundergoat/gruff-ts/issues"},"bin":{"gruff-ts":"bin/gruff-ts"},"dist":{"shasum":"085aacaf9d5292d9226d519226d6a8183979386e","tarball":"https://registry.npmjs.org/@blundergoat/gruff-ts/-/gruff-ts-0.1.0.tgz","fileCount":54,"integrity":"sha512-VLStvvUMyiZgF9kjfGaBt5c3Nf2UOQUXCedSx2WV28DXvppGsgCYw9pnQAl/FsVbshx0K1qqbecUB9s1ebAlpw==","signatures":[{"sig":"MEUCIDL4ELWtAPCvayHqeNjFn6ZFZwTINP1Y02a5YFAVcfZFAiEAzgjydPyMFPQvwDQrKqJJ20t6dhDLoNgHLBk2HVAMrPM=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":633738},"type":"module","engines":{"node":">=22"},"gitHead":"64b20b42c78b0b9c9dffc38e55eb2051c1d0ae8c","scripts":{"test":"node --import tsx --test src/**/*.test.ts","check":"tsc --noEmit && npm test","start-dev":"tsx src/cli.ts dashboard"},"_npmUser":{"name":"blundergoat","email":"thatmatthansen@gmail.com"},"repository":{"url":"git+https://github.com/blundergoat/gruff-ts.git","type":"git"},"_npmVersion":"10.9.4","description":"Static analyzer for TypeScript and JavaScript projects - 121 rules across 11 quality pillars, SARIF output, baselines, and a local dashboard.","directories":{},"_nodeVersion":"22.22.1","dependencies":{"tsx":"^4.21.0","commander":"^14.0.2"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.9.3","@types/node":"^25.0.0","@blundergoat/goat-flow":"^1.6.4"},"_npmOperationalInternal":{"tmp":"tmp/gruff-ts_0.1.0_1779512970600_0.24164915026899503","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@blundergoat/gruff-ts","version":"0.1.1","keywords":["typescript","javascript","static-analysis","code-quality","linter","analyzer","cli","sarif","baseline","fingerprint","complexity","dead-code","security","naming","dashboard","ci","github-actions","nodejs"],"author":{"url":"https://www.blundergoat.com/about","name":"Matthew Hansen"},"license":"MIT","_id":"@blundergoat/gruff-ts@0.1.1","maintainers":[{"name":"blundergoat","email":"thatmatthansen@gmail.com"}],"homepage":"https://github.com/blundergoat/gruff-ts#readme","bugs":{"url":"https://github.com/blundergoat/gruff-ts/issues"},"bin":{"gruff-ts":"bin/gruff-ts"},"dist":{"shasum":"14f4d3bad1eb9dad7a49274a596b72e06685f8e3","tarball":"https://registry.npmjs.org/@blundergoat/gruff-ts/-/gruff-ts-0.1.1.tgz","fileCount":62,"integrity":"sha512-JlzoPM8FR/BCPzUD9t9D0QnRap5R3qtHArNYa7ZQN4LmCRKcSmSoIWhEKM/lquDR22ay0Xdh4FuUV3Nh/uxefA==","signatures":[{"sig":"MEQCIHcbK6+wmLWnJ2NIwmaxlWZNnXTzO8KtcSeWwBzhyHaEAiBRUjAHMc4wei3BHL9yyz+Xf62Kcc0kRYqNcKp74KIPZQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":678731},"type":"module","engines":{"node":">=22"},"gitHead":"4d4cafa52ee4e5bb3013cb491df4864938be7d60","scripts":{"test":"node --import tsx --test src/**/*.test.ts","check":"tsc --noEmit && npm test","start-dev":"tsx src/cli.ts dashboard"},"_npmUser":{"name":"blundergoat","email":"thatmatthansen@gmail.com"},"repository":{"url":"git+https://github.com/blundergoat/gruff-ts.git","type":"git"},"_npmVersion":"10.9.4","description":"Static analyzer for TypeScript and JavaScript projects - 119 rules across 11 quality pillars, SARIF output, baselines, and a local dashboard.","directories":{},"_nodeVersion":"22.22.1","dependencies":{"tsx":"^4.21.0","commander":"^14.0.2"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.9.3","@types/node":"^25.0.0","@blundergoat/goat-flow":"^1.6.4"},"_npmOperationalInternal":{"tmp":"tmp/gruff-ts_0.1.1_1779613398798_0.08606982331368118","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@blundergoat/gruff-ts","version":"0.2.0","keywords":["typescript","javascript","static-analysis","code-quality","linter","analyzer","cli","sarif","baseline","fingerprint","complexity","dead-code","security","naming","dashboard","ci","github-actions","nodejs"],"author":{"url":"https://www.blundergoat.com/about","name":"Matthew Hansen"},"license":"MIT","_id":"@blundergoat/gruff-ts@0.2.0","maintainers":[{"name":"blundergoat","email":"thatmatthansen@gmail.com"}],"homepage":"https://github.com/blundergoat/gruff-ts#readme","bugs":{"url":"https://github.com/blundergoat/gruff-ts/issues"},"bin":{"gruff-ts":"bin/gruff-ts"},"dist":{"shasum":"8de58edab23a91ca420a13b2f5d11a7f7c246962","tarball":"https://registry.npmjs.org/@blundergoat/gruff-ts/-/gruff-ts-0.2.0.tgz","fileCount":66,"integrity":"sha512-9al5TP0wEegLgDHajzQmrGcYfdqMOmnoMyFzJESIcx0HTBvi0NZVXR8r+fddHTgDSXTXrmbBPm1OZi+/Jai9ZQ==","signatures":[{"sig":"MEQCICKJQwEx8ywSPm8SGUJS7WHFv02ISxooXI11aZSuam+aAiBlRrDQG9/DDOnbZxIH472VvSUCiy9rUZLpAv1qFvThqQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":746263},"type":"module","engines":{"node":">=22"},"gitHead":"a96b9db8a589be8e3b4f0b7aa9985682775bf1e5","scripts":{"test":"node --import tsx --test src/**/*.test.ts","check":"tsc --noEmit && npm test","start-dev":"tsx src/cli.ts dashboard"},"_npmUser":{"name":"blundergoat","email":"thatmatthansen@gmail.com"},"repository":{"url":"git+https://github.com/blundergoat/gruff-ts.git","type":"git"},"_npmVersion":"10.9.4","description":"Static analyzer for TypeScript and JavaScript projects - 119 rules across 11 quality pillars, SARIF output, baselines, and a local dashboard.","directories":{},"_nodeVersion":"22.22.1","dependencies":{"tsx":"^4.21.0","commander":"^14.0.2"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.9.3","@types/node":"^25.0.0","@blundergoat/goat-flow":"^1.6.4"},"_npmOperationalInternal":{"tmp":"tmp/gruff-ts_0.2.0_1779909465602_0.5880934517752341","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"@blundergoat/gruff-ts","version":"0.3.0","keywords":["typescript","javascript","static-analysis","code-quality","linter","analyzer","cli","sarif","baseline","fingerprint","complexity","dead-code","security","naming","dashboard","ci","github-actions","nodejs"],"author":{"url":"https://www.blundergoat.com/about","name":"Matthew Hansen"},"license":"MIT","_id":"@blundergoat/gruff-ts@0.3.0","maintainers":[{"name":"blundergoat","email":"thatmatthansen@gmail.com"}],"homepage":"https://github.com/blundergoat/gruff-ts#readme","bugs":{"url":"https://github.com/blundergoat/gruff-ts/issues"},"bin":{"gruff-ts":"bin/gruff-ts"},"dist":{"shasum":"3ae3794367bbb14db8825e3cf0b99dc5c5fc8e3e","tarball":"https://registry.npmjs.org/@blundergoat/gruff-ts/-/gruff-ts-0.3.0.tgz","fileCount":72,"integrity":"sha512-PFKKRnSZ5EpG4xk8pOo9cxqg+NF8XtaW8kAc9utTyUeR0Rjf72D4LkPKA6NSk6cP5uy5qBTZaUthIP4qPMd0BA==","signatures":[{"sig":"MEYCIQCHlEzjUtcp7o2i6zwyrjpiH06p73CHUOPIEjIiiQDKZAIhAPEoO2db658PaWCGArRW6ETXNPeltdBnohbD+fUJ47c4","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":877238},"type":"module","engines":{"node":">=22"},"gitHead":"bf2bcc871d2cacedc187daa60441954342efaae8","scripts":{"test":"node --import tsx --test src/**/*.test.ts","check":"tsc --noEmit && npm test","start-dev":"tsx src/cli.ts dashboard"},"_npmUser":{"name":"blundergoat","email":"thatmatthansen@gmail.com"},"repository":{"url":"git+https://github.com/blundergoat/gruff-ts.git","type":"git"},"_npmVersion":"10.9.4","description":"Governs AI-generated code so a human can verify it: a TypeScript/JavaScript static analyzer - 119 rules across 11 quality pillars, SARIF output, baselines, and a local dashboard.","directories":{},"_nodeVersion":"22.22.1","dependencies":{"tsx":"^4.21.0","commander":"^14.0.2","typescript":"^5.9.3"},"_hasShrinkwrap":false,"devDependencies":{"@types/node":"^25.0.0","@blundergoat/goat-flow":"^1.6.4"},"_npmOperationalInternal":{"tmp":"tmp/gruff-ts_0.3.0_1780281848598_0.4437999483027517","host":"s3://npm-registry-packages-npm-production"}},"0.3.1":{"name":"@blundergoat/gruff-ts","version":"0.3.1","keywords":["typescript","javascript","static-analysis","code-quality","linter","analyzer","cli","sarif","baseline","fingerprint","complexity","dead-code","security","naming","dashboard","ci","github-actions","nodejs"],"author":{"url":"https://www.blundergoat.com/about","name":"Matthew Hansen"},"license":"MIT","_id":"@blundergoat/gruff-ts@0.3.1","maintainers":[{"name":"blundergoat","email":"thatmatthansen@gmail.com"}],"homepage":"https://github.com/blundergoat/gruff-ts#readme","bugs":{"url":"https://github.com/blundergoat/gruff-ts/issues"},"bin":{"gruff-ts":"bin/gruff-ts"},"dist":{"shasum":"1c0c029ff83a81dd39f9af040283acf0c185fb14","tarball":"https://registry.npmjs.org/@blundergoat/gruff-ts/-/gruff-ts-0.3.1.tgz","fileCount":73,"integrity":"sha512-OBH/1v+JLH04ga5RBTa8JUoBNiZwPBBMwQZbb+1FkDERGkVizsD+JxbVTgV+Xh3KvrX0poaWe/lygE5M6Kz6Gw==","signatures":[{"sig":"MEQCICSMKVolUpmU0IuHJ4PVgtr7FQszBhPyowps5Kks8/lPAiBfnODsjbPXY53tyXMwtqAqaWKKDaGoi8UnrkDaaO7HoQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":894430},"type":"module","engines":{"node":">=22"},"gitHead":"d21dc82b6af9b20f6e791843b2a8f744189425b8","scripts":{"test":"node --import tsx --test src/**/*.test.ts","check":"tsc --noEmit && npm test","start-dev":"tsx src/cli.ts dashboard"},"_npmUser":{"name":"blundergoat","email":"thatmatthansen@gmail.com"},"repository":{"url":"git+https://github.com/blundergoat/gruff-ts.git","type":"git"},"_npmVersion":"10.9.4","description":"Governs AI-generated code so a human can verify it: a TypeScript/JavaScript static analyzer - 119 rules across 11 quality pillars, SARIF output, baselines, and a local dashboard.","directories":{},"_nodeVersion":"22.22.1","dependencies":{"tsx":"^4.21.0","commander":"^14.0.2","typescript":"^5.9.3"},"_hasShrinkwrap":false,"devDependencies":{"@types/node":"^25.0.0","@blundergoat/goat-flow":"^1.6.4"},"_npmOperationalInternal":{"tmp":"tmp/gruff-ts_0.3.1_1780653627798_0.12426675337897564","host":"s3://npm-registry-packages-npm-production"}},"0.3.2":{"name":"@blundergoat/gruff-ts","version":"0.3.2","keywords":["typescript","javascript","static-analysis","code-quality","linter","analyzer","cli","sarif","baseline","fingerprint","complexity","dead-code","security","naming","dashboard","ci","github-actions","nodejs"],"author":{"url":"https://www.blundergoat.com/about","name":"Matthew Hansen"},"license":"MIT","_id":"@blundergoat/gruff-ts@0.3.2","maintainers":[{"name":"blundergoat","email":"thatmatthansen@gmail.com"}],"homepage":"https://github.com/blundergoat/gruff-ts#readme","bugs":{"url":"https://github.com/blundergoat/gruff-ts/issues"},"bin":{"gruff-ts":"bin/gruff-ts"},"dist":{"shasum":"c80c4f7a4e61e546960dd79b21dfd17ccf62c0a3","tarball":"https://registry.npmjs.org/@blundergoat/gruff-ts/-/gruff-ts-0.3.2.tgz","fileCount":63,"integrity":"sha512-W0vwMx4zFciwjDvVXFJF6RRCY8245UPXpZ0NatvPJCuCXpv0wFNK8bYPXUptuy3DVrMPY3RkAA682eQVmzvMlw==","signatures":[{"sig":"MEYCIQCpN8FtSMsLehfMsoaiRzih9ua7v796ssNnricyDe2mugIhAObzIeptoF1Z9A0fiEOSdtIqSA9vNIYWxas0BBk+DZfh","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":861647},"type":"module","engines":{"node":">=22"},"gitHead":"e0a43e4e1d9ee1c6fbce42d37b00e73b522e17be","scripts":{"test":"node --import tsx --test src/**/*.test.ts","check":"tsc --noEmit && npm test","start-dev":"tsx src/cli.ts dashboard"},"_npmUser":{"name":"blundergoat","email":"thatmatthansen@gmail.com"},"repository":{"url":"git+https://github.com/blundergoat/gruff-ts.git","type":"git"},"_npmVersion":"10.9.4","description":"Governs AI-generated code so a human can verify it: a TypeScript/JavaScript static analyzer - 120 rules across 11 quality pillars, SARIF output, baselines, and a local dashboard.","directories":{},"_nodeVersion":"22.22.1","dependencies":{"tsx":"^4.21.0","commander":"^14.0.2","typescript":"^5.9.3"},"_hasShrinkwrap":false,"devDependencies":{"@types/node":"^25.0.0","@blundergoat/goat-flow":"^1.6.4"},"_npmOperationalInternal":{"tmp":"tmp/gruff-ts_0.3.2_1780951859025_0.49783563667308406","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"name":"@blundergoat/gruff-ts","version":"0.4.0","keywords":["typescript","javascript","static-analysis","code-quality","linter","analyzer","cli","sarif","baseline","fingerprint","complexity","dead-code","security","naming","dashboard","ci","github-actions","nodejs"],"author":{"url":"https://www.blundergoat.com/about","name":"Matthew Hansen"},"license":"MIT","_id":"@blundergoat/gruff-ts@0.4.0","maintainers":[{"name":"blundergoat","email":"thatmatthansen@gmail.com"}],"homepage":"https://github.com/blundergoat/gruff-ts#readme","bugs":{"url":"https://github.com/blundergoat/gruff-ts/issues"},"bin":{"gruff-ts":"bin/gruff-ts"},"dist":{"shasum":"f58b4e0ba9cb6e5f8244087c763b29967a399e41","tarball":"https://registry.npmjs.org/@blundergoat/gruff-ts/-/gruff-ts-0.4.0.tgz","fileCount":65,"integrity":"sha512-XHvr7pg/8OJSTlcdnRX+X3tLxR/0ZZ8p756nkZtFscoVJgssl8c8rXwAisbUgZP+KS4iPEW3PBX+c9HBELvbAQ==","signatures":[{"sig":"MEUCIQDkk5jtD1gYYAF+XiPL1I9tGi/QneN7DrjLa0l8zwPWOwIgISyC4rbCxtnQhj01eStd59pmi2ZpntgeggNugRZ4mmM=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":916203},"type":"module","engines":{"node":">=22"},"gitHead":"8ef2a81ffced9a7e0a89f8a25aace25624b046d0","scripts":{"test":"node --import tsx --test src/**/*.test.ts","check":"tsc --noEmit && npm test","start-dev":"tsx src/cli.ts dashboard"},"_npmUser":{"name":"blundergoat","email":"thatmatthansen@gmail.com"},"repository":{"url":"git+https://github.com/blundergoat/gruff-ts.git","type":"git"},"_npmVersion":"10.9.4","description":"Governs AI-generated code so a human can verify it: a TypeScript/JavaScript static analyzer - 120 rules across 11 quality pillars, SARIF output, baselines, and a local dashboard.","directories":{},"_nodeVersion":"22.22.1","dependencies":{"tsx":"^4.21.0","commander":"^14.0.2","typescript":"^5.9.3"},"_hasShrinkwrap":false,"devDependencies":{"@types/node":"^25.0.0","@blundergoat/goat-flow":"^1.6.4"},"_npmOperationalInternal":{"tmp":"tmp/gruff-ts_0.4.0_1781122372794_0.2882119218955961","host":"s3://npm-registry-packages-npm-production"}},"0.5.0":{"name":"@blundergoat/gruff-ts","version":"0.5.0","description":"Governs AI-generated code so a human can verify it: a TypeScript/JavaScript static analyzer - 120 rules across 11 quality pillars, SARIF output, baselines, and a local dashboard.","license":"MIT","author":{"name":"Matthew Hansen","url":"https://www.blundergoat.com/about"},"homepage":"https://github.com/blundergoat/gruff-ts#readme","repository":{"type":"git","url":"git+https://github.com/blundergoat/gruff-ts.git"},"bugs":{"url":"https://github.com/blundergoat/gruff-ts/issues"},"keywords":["typescript","javascript","static-analysis","code-quality","linter","analyzer","cli","sarif","baseline","fingerprint","complexity","dead-code","security","naming","dashboard","ci","github-actions","nodejs"],"engines":{"node":">=22"},"type":"module","bin":{"gruff-ts":"bin/gruff-ts"},"scripts":{"check":"tsc --noEmit && npm test","test":"node --import tsx --test src/**/*.test.ts","start-dev":"tsx src/cli.ts dashboard"},"dependencies":{"commander":"^14.0.2","tsx":"^4.21.0","typescript":"^5.9.3"},"devDependencies":{"@blundergoat/goat-flow":"^1.6.4","@types/node":"^25.0.0"},"_id":"@blundergoat/gruff-ts@0.5.0","gitHead":"7d98d6125dd886636c993488c967582758781155","_nodeVersion":"22.22.1","_npmVersion":"10.9.4","dist":{"integrity":"sha512-eTTZ6cai36ujM/XThetveAWBR2ly6dSSBw3Mopa8A0KtKs7gGrXhBhjCq2YpAlrdQAv4LwdJdK0lvpJoHFEbbQ==","shasum":"492ab9e0e929b5265f940998760351ddcdf29414","tarball":"https://registry.npmjs.org/@blundergoat/gruff-ts/-/gruff-ts-0.5.0.tgz","fileCount":68,"unpackedSize":1057311,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQCw05epHDvLNJ26sjR8FlgppLJzObNX/NwoXZki1jKHBwIhAOp5G7WT9tgMTQR7d+N8MtgZGWUBbakJT3QFcuFeM6o3"}]},"_npmUser":{"name":"blundergoat","email":"thatmatthansen@gmail.com"},"directories":{},"maintainers":[{"name":"blundergoat","email":"thatmatthansen@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/gruff-ts_0.5.0_1786858330942_0.8573449858623146"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-23T05:09:30.494Z","modified":"2026-08-16T05:32:11.276Z","0.1.0":"2026-05-23T05:09:30.748Z","0.1.1":"2026-05-24T09:03:18.981Z","0.2.0":"2026-05-27T19:17:45.768Z","0.3.0":"2026-06-01T02:44:08.781Z","0.3.1":"2026-06-05T10:00:27.941Z","0.3.2":"2026-06-08T20:50:59.243Z","0.4.0":"2026-06-10T20:12:52.962Z","0.5.0":"2026-08-16T05:32:11.100Z"},"bugs":{"url":"https://github.com/blundergoat/gruff-ts/issues"},"author":{"name":"Matthew Hansen","url":"https://www.blundergoat.com/about"},"license":"MIT","homepage":"https://github.com/blundergoat/gruff-ts#readme","keywords":["typescript","javascript","static-analysis","code-quality","linter","analyzer","cli","sarif","baseline","fingerprint","complexity","dead-code","security","naming","dashboard","ci","github-actions","nodejs"],"repository":{"type":"git","url":"git+https://github.com/blundergoat/gruff-ts.git"},"description":"Governs AI-generated code so a human can verify it: a TypeScript/JavaScript static analyzer - 120 rules across 11 quality pillars, SARIF output, baselines, and a local dashboard.","maintainers":[{"name":"blundergoat","email":"thatmatthansen@gmail.com"}],"readme":"# gruff-ts\n\n`gruff-ts` governs AI-generated code. Wired in as a coding-agent hook, it forces the agent to produce changes a human who did not write them can actually sign off on: legible enough to verify by reading, secure where the human eye slips, and tested for real behaviour instead of padded with low-signal ceremony. Mechanically it is a dependency-light, opinionated static analyser for TypeScript and JavaScript - it scans source, tests, package metadata, and common config files, then emits reports for terminals, CI annotations, SARIF consumers, static HTML, and a local dashboard. It is heuristic static analysis; run it beside `tsc`, ESLint, tests, dependency scanners, and code review, not instead of them.\n\n## Why gruff-ts Exists\n\nThe reviewer of AI-generated code is not its author. A coding agent holds the full context while it writes; the human who has to read, review, and trust the result does not. Conventional linters optimise for the author who already understands the code and just wants it tidy. gruff-ts optimises for that reviewer instead, which is the position every human signing off on an agent's output is in.\n\nThat goal breaks into three:\n\n- **Verifiable.** A reviewer can read the change and confirm it does what was asked, rather than re-deriving what the agent was thinking. The complexity, size, naming, and documentation pillars push toward code whose intent is visible on its face.\n- **Secure where the eye slips.** Human review reliably scans past a known set of unsafe patterns - disabled TLS verification, `eval` and dynamic `Function` construction, injection-shaped string building, committed secrets. The security and sensitive-data pillars catch those mechanically so the reviewer does not have to.\n- **Honestly tested.** A suite should raise confidence, not just coverage. The test-quality pillar flags low-signal ceremony - mock-only, snapshot-only, assertion-free, and tautological tests - so an agent cannot satisfy a \"write tests\" instruction with padding.\n\nDocumentation rules carry extra weight here, which is why a doc comment is expected even on a private one-liner. Coding agents routinely produce code that superficially works while misunderstanding the requirement. Forcing the agent to state intent, usage, contract, and failure behaviour in prose gives a reviewer something to check the implementation against - a mismatch between the doc comment and the code is itself a signal that the change needs a deeper look.\n\nUsed as a hook on an agent's output, gruff-ts is a forcing function rather than advice: a finding is friction the agent must resolve before the change reaches a human, so what finally lands is already shaped for sign-off. See [Philosophy](docs/philosophy.md) for the longer form.\n\n## Status At A Glance\n\n| Field | Value |\n| --- | --- |\n| Release line | `0.5.0` |\n| Runtime | Node.js `22+`; release CI covers 22, 24, and 26 |\n| Runtime dependencies | `commander`, `tsx`, and `typescript` |\n| Package | `@blundergoat/gruff-ts` |\n| Binary | `gruff-ts` |\n| Rule catalogue | 120 rules across 11 pillars |\n| Primary config | `.gruff-ts.yaml`; `.gruff.json`, `.gruff.yaml`, and `.gruff.yml` are fallback files |\n| Analysis schema | `gruff.analysis.v2` |\n| Summary schema | `gruff.summary.v2` |\n| Baseline schema | `gruff.baseline.v1` |\n| Hotspot schema | `gruff.hotspot.v1` |\n| Agent-hook schema | `gruff.hook.v1` |\n| Config schema | `gruff-ts.config.v0.1` |\n| Severity gate | `--fail-on` with `none`, `advisory`, `warning`, `error` |\n| Dashboard | `127.0.0.1:8767` by default |\n\nScanned file types include TypeScript, JavaScript, JSON, YAML, TOML, INI, XML, and `.env*`.\n\n## Requirements\n\n- Node.js `22+`, matching [`package.json`](package.json).\n- npm for source-checkout development.\n- Git only for diff modes.\n\n## Install\n\nInstall as a project dev dependency:\n\n```bash\nnpm install --save-dev @blundergoat/gruff-ts\nnpx gruff-ts init\nnpx gruff-ts summary\n```\n\nFrom this checkout:\n\n```bash\nnpm install\n./bin/gruff-ts analyse . --fail-on=none\n```\n\n## Quick Start\n\n```bash\n# Create the project config.\nnpx gruff-ts init\n\n# Review the current finding mix.\nnpx gruff-ts summary\n\n# Explore without failing because of findings.\nnpx gruff-ts analyse . --fail-on=none\n\n# Gate on warning and error findings.\nnpx gruff-ts analyse . --fail-on=warning\n\n# Emit SARIF for code scanning.\nnpx gruff-ts analyse . --format=sarif --fail-on=none > gruff-ts.sarif\n\n# Generate a fresh-start baseline.\nnpx gruff-ts analyse . --generate-baseline gruff-baseline.json --fail-on=none\n\n# Start the local dashboard.\nnpx gruff-ts dashboard\n```\n\nOpen `http://127.0.0.1:8767/` for the dashboard.\n\n## Commands\n\n| Command | Purpose |\n| --- | --- |\n| `analyse [paths...]` | Run the analyser and print findings. |\n| `summary [paths...]` | Print compact score, pillar, rule, and file summaries. |\n| `report [paths...]` | Render an HTML or JSON report to stdout or `--output`. |\n| `init` | Write the default `.gruff-ts.yaml` to the current directory (`--force` to overwrite). |\n| `list-rules` | Print rule metadata as text or JSON. |\n| `list-profiles` | Print the built-in profiles (`gruff.minimal`, `gruff.recommended`, `gruff.strict`) with their rule-count summary, as text or JSON. |\n| `check-ignore <paths...>` | Report whether each path is ignored (config, gitignore, or default) with the matching source and pattern; runs no analysis. |\n| `hook [paths...]` | Emit the `gruff.hook.v1` coding-agent contract and capability metadata. |\n| `dashboard` | Serve the local browser dashboard. |\n| `completion [shell]` | Print a shell completion script for `bash`, `zsh`, or `fish`. |\n| `list` | Show the registered command catalogue. Use `--help` on the root or a command for detailed help. |\n\nGlobal console options match the broader gruff CLI surface: `--silent`, `--quiet`, `--ansi` / `--no-ansi`, `--no-interaction`, and `-v` / `-vv` / `-vvv`.\n\n## Output Formats\n\n`analyse --format <fmt>` accepts:\n\n| Format | Use it for |\n| --- | --- |\n| `text` | Human terminal output. |\n| `json` | Full `gruff.analysis.v2` report. |\n| `html` | Self-contained inspection report. |\n| `markdown` | Pull-request or issue comment summary. |\n| `github` | GitHub Actions workflow annotations. |\n| `hotspot` | `gruff.hotspot.v1` file-offender JSON. |\n| `sarif` | SARIF 2.1.0 for code scanning. |\n\n`report --format <fmt>` accepts `html` and `json`.\n\n## Exit Codes\n\n| Code | Meaning |\n| --- | --- |\n| `0` | Run completed and no finding met `--fail-on`. |\n| `1` | At least one finding met `--fail-on`. |\n| `2` | Fatal diagnostic such as missing input, parse error, config error, diff failure, baseline failure, or invalid input. |\n\n`analyse` and `summary` default to `--fail-on advisory`; `report` defaults to `--fail-on none`. The defaults can be overridden per-project by a `minimumSeverity:` block in `.gruff-ts.yaml`. CLI flag wins over config; config wins over the binary default. See ADR-004 and the Configuration section.\n\n## CI Usage\n\nGeneric CI command:\n\n```bash\nnpx gruff-ts analyse . --format=github --fail-on=warning\n```\n\nSARIF jobs can write an artifact for code scanning:\n\n```bash\nnpx gruff-ts analyse . --format=sarif --fail-on=none > gruff-ts.sarif\n```\n\nSecurity-focused gates can bypass adoption baselines:\n\n```bash\nnpx gruff-ts analyse . --no-baseline --fail-on=error\n```\n\n## Configuration\n\n`analyse` auto-loads the first supported config file it finds in the project root:\n\n1. `.gruff-ts.yaml`\n2. `.gruff.json`\n3. `.gruff.yaml`\n4. `.gruff.yml`\n\nUse `--config <path>` for an explicit file or `--no-config` to skip config loading. Recursive scans respect root and nested `.gitignore` files; `--include-ignored` includes default and Git-ignored paths for one run, but `paths.ignore` entries still apply as project policy.\n\n```yaml\nschemaVersion: gruff-ts.config.v0.1\n\npaths:\n  ignore:\n    - \"generated/**\"\n\nallowlists:\n  acceptedAbbreviations:\n    - api\n    - cli\n  acceptedBooleanNames:\n    - verbose\n    - enabled\n  secretPreviews: []\n\nrules:\n  complexity.cyclomatic:\n    threshold: 10\n    severity: warning\n  size.file-length:\n    threshold: 400\n    severity: warning\n```\n\nSee [Configuration](docs/configuration.md) for the full config shape.\n\n## Profiles\n\nA `profile:` selects a named bundle of rules instead of enumerating every rule by hand. Three profiles ship with the binary:\n\n| Profile | Intent |\n| --- | --- |\n| `gruff.minimal` | Security and sensitive-data rules only - the smallest sanity gate for incremental adoption. |\n| `gruff.recommended` | Every pillar at its default threshold and severity - identical to gruff's zero-config behaviour. |\n| `gruff.strict` | Every pillar enabled with tightened size, complexity, and secret thresholds for high-bar repositories. |\n\n`gruff-ts list-profiles` prints them with their enabled-rule counts. Select one in config or on the CLI:\n\n```yaml\n# Shorthand: the whole profile in one line.\nprofile: recommended\n```\n\n```yaml\n# Compose: extend a built-in, then override a few rules or add ignored paths.\nprofile:\n  extends: gruff.recommended      # a built-in name OR a relative path like ./team-profile.yaml\n  rules:\n    complexity.cyclomatic:\n      threshold: 12\n    docs.missing-public-doc:\n      enabled: false\n  ignoredPaths:\n    - \"examples/**\"\n```\n\n`--profile <name-or-path>` applies a profile for one run (on `analyse`, `report`, `summary`, and `dashboard`) and overrides a config-file `profile:`. The value is a built-in name (the bare `minimal` / `recommended` / `strict` short forms are accepted too) or a path to a `.yaml`/`.yml`/`.json` profile file.\n\nSemantics:\n\n- **Precedence (highest first):** `--profile` flag, config `profile:` block, the `extends:` base chain, the built-in default `gruff.recommended`. A top-level `rules:` entry still overrides the profile for that rule.\n- **`extends:`** accepts a built-in name or a relative file path - never a remote URL, never shell. A shared profile file's top level is itself a profile spec (`extends` / `rules` / `ignoredPaths`).\n- **Last-wins, deterministic:** a child profile's per-rule fields override the parent's same-rule fields, and a child `ignoredPaths` array replaces (does not concatenate) the parent's.\n- **Validated at load time:** an unknown built-in name resolved as a missing file, a missing `extends:` file, an `extends:` cycle, and a rule id outside the catalogue all fail with a clear error before any scan runs.\n\n## Rules And Pillars\n\nThe current catalogue contains 120 rules:\n\n| Pillar | Rules |\n| --- | ---: |\n| `complexity` | 2 |\n| `dead-code` | 1 |\n| `design` | 5 |\n| `documentation` | 18 |\n| `maintainability` | 14 |\n| `modernisation` | 14 |\n| `naming` | 10 |\n| `security` | 29 |\n| `sensitive-data` | 10 |\n| `size` | 3 |\n| `test-quality` | 14 |\n\nUse `npx gruff-ts list-rules --format=json` for exact rule IDs, severities, confidence levels, remediation text, thresholds, and options.\n\n## Baselines And Changed-Code Scans\n\nFor editor and coding-agent feedback, prefer the analyser-owned hook contract:\n\n```bash\nnpx gruff-ts hook --format=json --changed-ranges \"3-3,8-10\" src/foo.ts\nnpx gruff-ts hook --capabilities --format=json\n```\n\n`hook` emits `gruff.hook.v1` JSON with normalized `file`, `scope`, `suppressed.count`,\n`ignored.paths`, non-null `remediation`, stable identities, and threshold metadata. Hook mode is\nadvisory: findings exit `0`; operational failures such as invalid config exit `2` and are reported\nin `config.error`.\n\nBaselines suppress reviewed findings by stable fingerprint:\n\n```bash\nnpx gruff-ts analyse . --generate-baseline gruff-baseline.json --fail-on=none\nnpx gruff-ts analyse . --baseline gruff-baseline.json --fail-on=warning\nnpx gruff-ts analyse . --no-baseline --fail-on=none\n```\n\nChanged-file scans use Git only when requested:\n\n```bash\nnpx gruff-ts analyse . --diff=working-tree --format=github --fail-on=warning\nnpx gruff-ts analyse . --diff=staged --format=json --fail-on=none\n```\n\n`--diff` accepts `working-tree`, `staged`, `unstaged`, or a base ref. `report` renders raw inspection output and does not accept `--baseline`; use `analyse` when baseline suppression matters.\n\nChanged-region scans keep only findings attributable to the changed hunk or its enclosing symbol:\n\n```bash\nnpx gruff-ts analyse --format=json --changed-ranges \"3-3,8-10\" src/foo.ts\nnpx gruff-ts analyse --format=json --since HEAD src/foo.ts\ngit diff | npx gruff-ts analyse --format=json --diff -\n```\n\nUse `--changed-scope file` when a CI workflow intentionally wants every finding from touched\nfiles, including file-wide metrics such as `size.file-length`. The default `symbol` scope keeps\nthe coding-agent feedback focused on the changed line or enclosing declaration.\n\nJSON output keeps the normal `findings` array and adds `suppressedCount` when changed-region filtering is active.\n\n## Dashboard\n\n```bash\nnpx gruff-ts dashboard --host 127.0.0.1 --port 8767 --project-root .\n```\n\nThe dashboard serves a local iframe report and compact controls panel. It has no authentication; keep the default loopback bind unless the network is trusted. The `/scan` endpoint analyses filesystem paths from request parameters, so the bind address is the main safety boundary.\n\nIn polyglot repositories, `gruff-ts` defaults to port `8767`, `gruff-rs` defaults to `8766`, and `gruff-go`, `gruff-php`, and `gruff-py` default to `8765`; use `--port` when running multiple dashboards at the same time.\n\n## Trust Boundary\n\nDefault scans are local source inspections. `gruff-ts` parses supported source, config, and package metadata files; it does not execute target application code, run tests, type-check or emit through the TypeScript compiler, query package registries, or read vulnerability feeds. The runtime `typescript` dependency is used only for syntax parsing, while `tsx` launches the shipped TypeScript source and `commander` owns the CLI. Git is used only for explicit diff modes. Secret-like findings use redacted previews; raw secret values should not appear in terminal, JSON, SARIF, GitHub, Markdown, hotspot, or HTML output.\n\n## Stability Contract\n\nThe `0.5.x` line treats rule IDs, finding fingerprints, baseline identity, `gruff.analysis.v2`, `gruff.summary.v2`, `gruff.baseline.v1`, `gruff.hotspot.v1`, `gruff.hook.v1`, `gruff-ts.config.v0.1`, SARIF rendering, and CLI exit semantics as compatibility-sensitive. Breaking changes belong in a coordinated future release and must be recorded in [`CHANGELOG.md`](CHANGELOG.md).\n\nAnalysis JSON continues to emit canonical `file` alongside legacy `filePath` for findings and top offenders. The v0.3.1 plan to remove `filePath` in the next release was superseded when v0.4.0 retained the alias. Consumers should read `file` now; removing `filePath` waits for the coordinated family JSON unification instead of happening in this port alone.\n\n## How It Compares\n\n| Tool | Relationship |\n| --- | --- |\n| `tsc` | Type checking. `gruff-ts` does not prove type correctness or replace compiler diagnostics. |\n| ESLint | Rule-driven linting. `gruff-ts` adds scoring, baselines, reports, dashboard, and cross-file/project-quality signals. |\n| Prettier / formatters | Formatting only. `gruff-ts` does not format code. |\n| Knip / ts-prune | Focused unused export/dead-code tools. `gruff-ts` includes broader quality and security-oriented heuristics. |\n| `npm audit` / dependency scanners | Advisory-backed dependency checks. `gruff-ts` reports local static signals and does not replace advisory feeds. |\n\n## Development\n\n```bash\nnpm install\nnpm run check\nnpm test\nnpm run start-dev\n./bin/gruff-ts analyse . --fail-on=none\n```\n\nSource lives under `src/`: `src/cli.ts` is the bootstrap, `src/cli-program.ts` owns Commander wiring, `src/analyser.ts` orchestrates scans, and focused sibling modules own rules and renderers.\n\n## Documentation\n\n- [Changelog](CHANGELOG.md)\n- [Configuration](docs/configuration.md)\n- [Rules catalogue](docs/rules.md)\n- [Reports and CI](docs/reports-and-ci.md)\n- [Contributing](CONTRIBUTING.md)\n- [Security](SECURITY.md)\n\n## Author\n\nBuilt by [Matthew Hansen](https://www.blundergoat.com/about).\n\n## License\n\n[MIT](LICENSE)\n","readmeFilename":"README.md"}