{"_id":"@aivolution/isitdone","_rev":"7-ee0af36dc43678718ba12a35a13646d9","name":"@aivolution/isitdone","dist-tags":{"latest":"0.4.2"},"versions":{"0.1.0":{"name":"@aivolution/isitdone","version":"0.1.0","keywords":["claude-code","codex","cursor","gemini-cli","ai-agent","coding-agent","stop-hook","hooks","verification","tests","ci","agent-skills","mcp"],"author":{"name":"raimondasl","email":"raimondas@hotmail.com"},"license":"MIT","_id":"@aivolution/isitdone@0.1.0","maintainers":[{"name":"raimondasl","email":"raimondas@hotmail.com"}],"homepage":"https://github.com/raimondasl/isitdone#readme","bugs":{"url":"https://github.com/raimondasl/isitdone/issues"},"bin":{"isitdone":"dist/isitdone.js"},"dist":{"shasum":"1d57a9133aba88a57a06e0dcddf4c2bbd87913d7","tarball":"https://registry.npmjs.org/@aivolution/isitdone/-/isitdone-0.1.0.tgz","fileCount":5,"integrity":"sha512-gNYY2JAMqZggmLrpudE81IfdVb6eseZ0a6w31oMk24JZpgIWoM2YqX7y3BI7VoH9QXhHRE47LBkZlF6lWuw+/w==","signatures":[{"sig":"MEYCIQDfqtZz3S0J7NOpzB2YGYGM1JapLuL4dpCMfLH+0bOBiQIhAO4MxulX1Hib8QQ6nD8REenJJaJ7yZTjKQ+YVEbPRzGT","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":109423},"type":"module","engines":{"node":">=20"},"gitHead":"8aba0f00754cd752b75f2895554dade78e70fffa","scripts":{"test":"vitest run","build":"node scripts/build.mjs","typecheck":"tsc --noEmit","prepublishOnly":"npm run typecheck && npm test && npm run build"},"_npmUser":{"name":"raimondasl","email":"raimondas@hotmail.com"},"repository":{"url":"git+https://github.com/raimondasl/isitdone.git","type":"git"},"_npmVersion":"12.0.2","description":"Don't let your coding agent say \"done\" until the tests actually pass. Zero-LLM Stop hook + CLI for Claude Code, Codex, Cursor and Gemini CLI.","directories":{},"_nodeVersion":"24.16.0","allowScripts":{"esbuild@0.28.2":true},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^5.0.0","esbuild":"^0.28.2","typescript":"^7.0.2","@types/node":"^26.4.1"},"_npmOperationalInternal":{"tmp":"tmp/isitdone_0.1.0_1788794407857_0.3362997934467462","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@aivolution/isitdone","version":"0.2.0","keywords":["claude-code","codex","cursor","gemini-cli","ai-agent","coding-agent","stop-hook","hooks","verification","tests","ci","agent-skills","mcp"],"author":{"name":"raimondasl","email":"raimondas@hotmail.com"},"license":"MIT","_id":"@aivolution/isitdone@0.2.0","maintainers":[{"name":"raimondasl","email":"raimondas@hotmail.com"}],"homepage":"https://github.com/raimondasl/isitdone#readme","bugs":{"url":"https://github.com/raimondasl/isitdone/issues"},"bin":{"isitdone":"dist/isitdone.js"},"dist":{"shasum":"98cf13bf1e3bf5163be815ed8a136016c11b57e7","tarball":"https://registry.npmjs.org/@aivolution/isitdone/-/isitdone-0.2.0.tgz","fileCount":5,"integrity":"sha512-cwKCyekbTazt7FVDKgaLW/9kB0BqWPhm4lLfxZ7zr88rBWjQJ0ogVMjDVuzvv0LN6JjHcHsYAgtCRSZACEp5xw==","signatures":[{"sig":"MEYCIQD3E1dw4TR/CYp13woWZWdyZP5jw6+fcm0HA1ZdVOV9vwIhAIIuwW5n14s9MaUZlbLrVZO+oA/rWiuVb5EoWO2I13Qn","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@aivolution%2fisitdone@0.2.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":170656},"type":"module","engines":{"node":">=20"},"gitHead":"c29bb2359490f3d56d8cda5e11b0818c991fd231","scripts":{"test":"vitest run","build":"node scripts/build.mjs","typecheck":"tsc --noEmit","prepublishOnly":"npm run typecheck && npm test && npm run build"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:7ad50b76-9eb1-4c1a-835b-351ae9dfb2a5"}},"repository":{"url":"git+https://github.com/raimondasl/isitdone.git","type":"git"},"_npmVersion":"12.0.2","description":"Don't let your coding agent say \"done\" until the tests actually pass. Zero-LLM Stop hook + CLI for Claude Code, Codex, Cursor and Gemini CLI.","directories":{},"_nodeVersion":"24.20.0","allowScripts":{"esbuild@0.28.2":true},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^5.0.0","esbuild":"^0.28.2","typescript":"^7.0.2","@types/node":"^26.4.1"},"_npmOperationalInternal":{"tmp":"tmp/isitdone_0.2.0_1788800979577_0.4126383179438433","host":"s3://npm-registry-packages-npm-production"}},"0.2.1":{"name":"@aivolution/isitdone","version":"0.2.1","keywords":["claude-code","codex","cursor","gemini-cli","ai-agent","coding-agent","stop-hook","hooks","verification","tests","ci","agent-skills","mcp"],"author":{"name":"raimondasl","email":"raimondas@hotmail.com"},"license":"MIT","_id":"@aivolution/isitdone@0.2.1","maintainers":[{"name":"raimondasl","email":"raimondas@hotmail.com"}],"homepage":"https://github.com/raimondasl/isitdone#readme","bugs":{"url":"https://github.com/raimondasl/isitdone/issues"},"bin":{"isitdone":"dist/isitdone.js"},"dist":{"shasum":"c7821393651cacfd6bf054c6ea5da0a9c76ddb3d","tarball":"https://registry.npmjs.org/@aivolution/isitdone/-/isitdone-0.2.1.tgz","fileCount":5,"integrity":"sha512-UjXty7v1apE+xbs1Ra7OWcIdmHfGzTV3SMXtCMcTFFNQ1gSbFY67IUML9EdecX9Jfr1B00me6rsyrP8hkwyVvw==","signatures":[{"sig":"MEUCIQDM7j8/4afppXBMfdwbLRbVw58GhKD+to9jvX3/ST6MRgIgZvZGX15Pmi18ZErFdIm3YicBgNCC4L7vmKkulC8ovus=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@aivolution%2fisitdone@0.2.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":176341},"type":"module","engines":{"node":">=20"},"gitHead":"94c92811b165a0f70fd85cda8200becd7cebd619","scripts":{"test":"vitest run","build":"node scripts/build.mjs","typecheck":"tsc --noEmit","prepublishOnly":"npm run typecheck && npm test && npm run build"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:7ad50b76-9eb1-4c1a-835b-351ae9dfb2a5"}},"repository":{"url":"git+https://github.com/raimondasl/isitdone.git","type":"git"},"_npmVersion":"12.0.2","description":"Don't let your coding agent say \"done\" until the tests actually pass. Zero-LLM Stop hook + CLI for Claude Code, Codex, Cursor and Gemini CLI.","directories":{},"_nodeVersion":"24.20.0","allowScripts":{"esbuild@0.28.2":true},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^5.0.0","esbuild":"^0.28.2","typescript":"^7.0.2","@types/node":"^26.4.1"},"_npmOperationalInternal":{"tmp":"tmp/isitdone_0.2.1_1788803144164_0.8765167329284713","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"@aivolution/isitdone","version":"0.3.0","keywords":["claude-code","codex","cursor","gemini-cli","ai-agent","coding-agent","stop-hook","hooks","verification","tests","ci","agent-skills","mcp"],"author":{"name":"raimondasl","email":"raimondas@hotmail.com"},"license":"MIT","_id":"@aivolution/isitdone@0.3.0","maintainers":[{"name":"raimondasl","email":"raimondas@hotmail.com"}],"homepage":"https://github.com/raimondasl/isitdone#readme","bugs":{"url":"https://github.com/raimondasl/isitdone/issues"},"bin":{"isitdone":"dist/isitdone.js"},"dist":{"shasum":"65316838f4fdcfa0759f61ad9c7f3c93250b48e9","tarball":"https://registry.npmjs.org/@aivolution/isitdone/-/isitdone-0.3.0.tgz","fileCount":6,"integrity":"sha512-XTWkCGSd6NVONoJ6gycP6r/DBIdd02cEhzTPRhs1uExXGsR2N49iJv6Z9UqfMc4qYhUUT77aaQevn9NsCvtD/w==","signatures":[{"sig":"MEUCIHVxf0VRicYHEeMyfONvN/1+h7fLIFvE+qWv0lBDQ1vSAiEAupCwy4hn9NmcMo/ypDRIYLmWemuRBaz3pebY6ugrelU=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@aivolution%2fisitdone@0.3.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":367187},"main":"dist/index.js","type":"module","engines":{"node":">=20"},"gitHead":"34b32ff4227359f4e083a11b5910844104a636a6","scripts":{"test":"vitest run","bench":"node scripts/bench.mjs","build":"node scripts/build.mjs","typecheck":"tsc --noEmit","prepublishOnly":"npm run typecheck && npm test && npm run build"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:7ad50b76-9eb1-4c1a-835b-351ae9dfb2a5"}},"repository":{"url":"git+https://github.com/raimondasl/isitdone.git","type":"git"},"_npmVersion":"12.0.2","description":"Don't let your coding agent say \"done\" until the tests actually pass. Zero-LLM Stop hook + CLI for Claude Code, Codex, Cursor and Gemini CLI.","directories":{},"_nodeVersion":"24.20.0","allowScripts":{"esbuild@0.28.2":true},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^5.0.0","esbuild":"^0.28.2","typescript":"^7.0.2","@types/node":"^26.4.1"},"_npmOperationalInternal":{"tmp":"tmp/isitdone_0.3.0_1788811882659_0.35875013101668696","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"name":"@aivolution/isitdone","version":"0.4.0","keywords":["claude-code","codex","cursor","gemini-cli","copilot-cli","qwen-code","goose","opencode","devin","ai-agent","coding-agent","stop-hook","hooks","verification","tests","ci","agent-skills","mcp"],"author":{"name":"raimondasl","email":"raimondas@hotmail.com"},"license":"MIT","_id":"@aivolution/isitdone@0.4.0","maintainers":[{"name":"raimondasl","email":"raimondas@hotmail.com"}],"homepage":"https://github.com/raimondasl/isitdone#readme","bugs":{"url":"https://github.com/raimondasl/isitdone/issues"},"bin":{"isitdone":"dist/isitdone.js"},"dist":{"shasum":"9ba2bd059a70bdbc62e0053e2c5b28d143c4c3f1","tarball":"https://registry.npmjs.org/@aivolution/isitdone/-/isitdone-0.4.0.tgz","fileCount":6,"integrity":"sha512-+lX15uaQ5g1DL5tjRXJ/8NdSIGqMuuyIUiNGeEXoQZZtY44Kp7xSutkWJykJ+qd72E/Zut5gwLXHcjCyMwqMWQ==","signatures":[{"sig":"MEUCIEf57EoaA3azrda34yfN+mwrnKOR052jn7G3t0o/bu8vAiEAjSl8W/dBFTiOY4go5k36W+rzz0XIl5dU+6aQ5+e/95c=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@aivolution%2fisitdone@0.4.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":626746},"main":"dist/index.js","type":"module","engines":{"node":">=20"},"gitHead":"849914d881ab8e2654eebbd4fb96007e67d5c566","scripts":{"demo":"node scripts/demo.mjs","test":"vitest run","bench":"node scripts/bench.mjs","build":"node scripts/build.mjs","typecheck":"tsc --noEmit","prepublishOnly":"npm run typecheck && npm test && npm run build"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:7ad50b76-9eb1-4c1a-835b-351ae9dfb2a5"}},"repository":{"url":"git+https://github.com/raimondasl/isitdone.git","type":"git"},"_npmVersion":"12.0.2","description":"Don't let your coding agent say \"done\" until the tests actually pass. Zero-LLM Stop hook + CLI for Claude Code, Codex, Cursor, Gemini CLI, Copilot CLI, Qwen Code, Goose, Droid, Devin, Augment, OpenCode and Junie.","directories":{},"_nodeVersion":"24.20.0","allowScripts":{"esbuild@0.28.2":true},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^5.0.0","esbuild":"^0.28.2","typescript":"^7.0.2","@types/node":"^26.4.1"},"_npmOperationalInternal":{"tmp":"tmp/isitdone_0.4.0_1788925436296_0.907489468365497","host":"s3://npm-registry-packages-npm-production"}},"0.4.1":{"name":"@aivolution/isitdone","version":"0.4.1","keywords":["claude-code","codex","cursor","gemini-cli","copilot-cli","qwen-code","goose","opencode","devin","ai-agent","coding-agent","stop-hook","hooks","verification","tests","ci","agent-skills","mcp"],"author":{"name":"raimondasl","email":"raimondas@hotmail.com"},"license":"MIT","_id":"@aivolution/isitdone@0.4.1","maintainers":[{"name":"raimondasl","email":"raimondas@hotmail.com"}],"homepage":"https://github.com/raimondasl/isitdone#readme","bugs":{"url":"https://github.com/raimondasl/isitdone/issues"},"bin":{"isitdone":"dist/isitdone.js"},"dist":{"shasum":"84ca54c4aa00652890bae2e52e08141cf8d8f4a3","tarball":"https://registry.npmjs.org/@aivolution/isitdone/-/isitdone-0.4.1.tgz","fileCount":6,"integrity":"sha512-fKPuWhrpeJFv7eYi8+Zv16UmSiwHXzBbM+60X2nSju6xEzj9LijWZ8a6DW2i9IIIf0Poyj67+fYjBwb9picQUQ==","signatures":[{"sig":"MEYCIQCXnyDyMA2UdkOHRl7Ij/tQp+f54ig240ioiPq4PC619AIhAPZULhynvDVyKCdCRRMxl90Pq0hObhs+td8evVjGjBKt","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@aivolution%2fisitdone@0.4.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":626746},"main":"dist/index.js","type":"module","engines":{"node":">=20"},"gitHead":"a0cec3ca559850efaad21930285d2b69a9349743","scripts":{"demo":"node scripts/demo.mjs","test":"vitest run","bench":"node scripts/bench.mjs","build":"node scripts/build.mjs","typecheck":"tsc --noEmit","prepublishOnly":"npm run typecheck && npm test && npm run build"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:7ad50b76-9eb1-4c1a-835b-351ae9dfb2a5"}},"repository":{"url":"git+https://github.com/raimondasl/isitdone.git","type":"git"},"_npmVersion":"12.0.2","description":"Don't let your coding agent say \"done\" until the tests actually pass. Zero-LLM Stop hook + CLI for Claude Code, Codex, Cursor, Gemini CLI, Copilot CLI, Qwen Code, Goose, Droid, Devin, Augment, OpenCode and Junie.","directories":{},"_nodeVersion":"24.20.0","allowScripts":{"esbuild@0.28.2":true},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^5.0.0","esbuild":"^0.28.2","typescript":"^7.0.2","@types/node":"^26.4.1"},"_npmOperationalInternal":{"tmp":"tmp/isitdone_0.4.1_1788926736173_0.031190741797841426","host":"s3://npm-registry-packages-npm-production"}},"0.4.2":{"name":"@aivolution/isitdone","version":"0.4.2","description":"Don't let your coding agent say \"done\" until the tests actually pass. Zero-LLM Stop hook + CLI for Claude Code, Codex, Cursor, Gemini CLI, Copilot CLI, Qwen Code, Goose, Droid, Devin, Augment, OpenCode and Junie.","type":"module","main":"dist/index.js","bin":{"isitdone":"dist/isitdone.js"},"engines":{"node":">=20"},"scripts":{"build":"node scripts/build.mjs","test":"vitest run","typecheck":"tsc --noEmit","prepublishOnly":"npm run typecheck && npm test && npm run build","bench":"node scripts/bench.mjs","demo":"node scripts/demo.mjs"},"keywords":["claude-code","codex","cursor","gemini-cli","copilot-cli","qwen-code","goose","opencode","devin","ai-agent","coding-agent","stop-hook","hooks","verification","tests","ci","agent-skills","mcp"],"author":{"name":"raimondasl","email":"raimondas@hotmail.com"},"license":"MIT","publishConfig":{"access":"public"},"repository":{"type":"git","url":"git+https://github.com/raimondasl/isitdone.git"},"homepage":"https://github.com/raimondasl/isitdone#readme","bugs":{"url":"https://github.com/raimondasl/isitdone/issues"},"devDependencies":{"@types/node":"^26.4.1","esbuild":"^0.28.2","typescript":"^7.0.2","vitest":"^5.0.0"},"allowScripts":{"esbuild@0.28.2":true},"gitHead":"8ac29049a1bf69111af05ff6188c6c5acc6aa397","_id":"@aivolution/isitdone@0.4.2","_nodeVersion":"24.20.0","_npmVersion":"12.0.2","dist":{"integrity":"sha512-kJ93stx6ia8KGhHtpFZCGXcnO3zd2t9n8dpukLfiZq1epiZK19ZmaBYDk8yJ5/oNq8pBv/FR1mU2Hl6oc4dalg==","shasum":"663695ced0eff491f0cb27e6143a596f75476a45","tarball":"https://registry.npmjs.org/@aivolution/isitdone/-/isitdone-0.4.2.tgz","fileCount":6,"unpackedSize":629324,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@aivolution%2fisitdone@0.4.2","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQCZ3kpMYiwDbvWCVoM9h/8j/Ry3eHNJHWgvdKRZc2HYaAIhALHdzZUHiNBnXI1SK8MeH/ZRKHIuZSJjT2mcNnzt0/hP"}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:7ad50b76-9eb1-4c1a-835b-351ae9dfb2a5"}},"directories":{},"maintainers":[{"name":"raimondasl","email":"raimondas@hotmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/isitdone_0.4.2_1789174618539_0.06680786894449842"},"_hasShrinkwrap":false}},"time":{"created":"2026-09-07T15:20:07.373Z","modified":"2026-09-12T00:56:59.010Z","0.1.0":"2026-09-07T15:20:08.005Z","0.2.0":"2026-09-07T17:09:39.714Z","0.2.1":"2026-09-07T17:45:44.412Z","0.3.0":"2026-09-07T20:11:22.804Z","0.4.0":"2026-09-09T03:43:56.445Z","0.4.1":"2026-09-09T04:05:36.339Z","0.4.2":"2026-09-12T00:56:58.676Z"},"bugs":{"url":"https://github.com/raimondasl/isitdone/issues"},"author":{"name":"raimondasl","email":"raimondas@hotmail.com"},"license":"MIT","homepage":"https://github.com/raimondasl/isitdone#readme","keywords":["claude-code","codex","cursor","gemini-cli","copilot-cli","qwen-code","goose","opencode","devin","ai-agent","coding-agent","stop-hook","hooks","verification","tests","ci","agent-skills","mcp"],"repository":{"type":"git","url":"git+https://github.com/raimondasl/isitdone.git"},"description":"Don't let your coding agent say \"done\" until the tests actually pass. Zero-LLM Stop hook + CLI for Claude Code, Codex, Cursor, Gemini CLI, Copilot CLI, Qwen Code, Goose, Droid, Devin, Augment, OpenCode and Junie.","maintainers":[{"name":"raimondasl","email":"raimondas@hotmail.com"}],"readme":"# isitdone\n\n**Don't let your coding agent say \"done\" until the tests actually pass.**\n\n<p align=\"center\"><img src=\"docs/demo.svg\" alt=\"isitdone blocks a false 'done': the Stop hook runs the real tests, they fail, the agent fixes them, the receipt says DONE\" width=\"880\"></p>\n\n*Rendered from a real run (`npm run demo`): the agent lines are narration; the hook and CLI output are captured as-is, with only the temporary path shortened.*\n\n`isitdone` is a zero-LLM, zero-dependency Stop hook and CLI for Claude Code, Codex CLI, Cursor, Gemini CLI, GitHub Copilot CLI, Qwen Code, Goose, Factory Droid, Devin, Augment, OpenCode and Junie CLI. When the agent tries to end its turn claiming the work is complete, `isitdone` runs the repository's *real* test, typecheck and lint commands on the *exact* working tree, scans the diff for weakened tests, and refuses the stop until they pass. It also tells the agent mid-turn when an edit just weakened a test, runs the same verification on pull requests as a GitHub Action, and leaves a git-bound receipt you can paste into a PR.\n\n```\nnpx isitdone init\n```\n\nNo API keys. No network. No telemetry. Just exit codes.\n\n## The moment it exists for\n\nClaude Code ends its turn with:\n\n> Done. All 4 tests pass and the auth refactor is complete.\n\nThe Stop hook runs `isitdone` before that turn is allowed to end. The agent receives this instead:\n\n```\nisitdone: NOT DONE. 1 check failed on the current working tree (attempt 1/3).\nYou claimed: \"All 4 tests pass and the auth refactor is complete.\"\n\n  npm run typecheck  PASS\n  npm run lint       PASS\n  npm test           FAIL  1 failed, 3 passed\n\n--- npm test (last 24 lines) ---\n✖ failing tests:\n\ntest at test/auth.test.js:14:1\n✖ is case-insensitive about the scheme (1.498ms)\n  AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:\n\n  null !== 'abc'\n  ...\n\nFix the failures, then run `npx isitdone` and paste its output before claiming completion.\nDo not skip, delete or weaken tests to make this pass; if a check is wrong for this repo,\nsay so explicitly to the user.\n```\n\nThe agent keeps working. When it genuinely finishes:\n\n```\n$ npx isitdone\nisitdone  main@2429c82  dirty (1 file)\n\n  npm run typecheck  PASS  428ms\n  npm run lint       PASS  411ms\n  npm test           PASS  532ms\n\n  DONE   receipt -> PASS (tree e5e47d9, 3 checks, 1.8s)\n```\n\n```\n$ npx isitdone receipt --md        # paste into the PR\n| check | result | time |\n|---|---|---|\n| `npm run typecheck` | PASS | 428ms |\n| `npm run lint` | PASS | 411ms |\n| `npm test` | PASS | 532ms |\n\nTree e5e47d9 on main@2429c82 (+1 uncommitted). Receipt: **PASS** · isitdone 0.1.0\n```\n\nEdit one more file and the receipt goes **STALE** until the checks run again. A hand-edited receipt reads **NONE**. (`isitdone` on npm is a short alias of `@aivolution/isitdone`; both commands are the same program.)\n\n## How often does this actually happen?\n\nMeasure it on your own machine. `isitdone history` reads the transcripts already on disk: Claude Code (`~/.claude/projects`), Codex CLI (`~/.codex/sessions`: legacy, paginated and pre-0.40 rollouts, honouring `/undo` rollbacks), Gemini CLI (`~/.gemini/tmp/<project>/chats`, both the `.json` and the 0.39+ `.jsonl` layouts), Qwen Code (`~/.qwen/projects/<cwd>/chats`) and Cursor (the IDE's `state.vscdb` bubble store, opened read-only in place, which needs Node 22.13+ or 24 for `node:sqlite`; on Node 20 the `agent-transcripts` JSONL is read instead, which carries no exit codes, and the report says so). It finds every turn where the agent edited files and then claimed completion, and checks whether a test command actually passed after the last edit. Nothing leaves your machine; only counts are printed.\n\n```\n$ npx isitdone history\nisitdone history  ~/.claude/projects\n  scanned 591 sessions in 9 projects; 1400 turns edited files; 516 of those ended with a completion claim\n\n  VERIFIED   31%   a test command passed after the last edit\n  STALE      37%   tests passed, then more edits, no re-run\n  FAILED     0%    the last test run failed, \"done\" claimed anyway\n  NEVER RAN  31%   no test command in the turn at all\n\n  69% of \"done\" claims had no passing test run behind them.\n```\n\nThat is the author's real result over seven months of sessions. Post yours.\n\n## Install\n\nOne command per host. Run it inside the repository.\n\n| Host | Command | Where it writes |\n|---|---|---|\n| Claude Code | `npx isitdone init` | `.claude/settings.json` (Stop) |\n| Codex CLI | `npx isitdone init --agent codex` | `.codex/hooks.json` (Stop), then run `/hooks` in Codex and trust it |\n| Cursor | `npx isitdone init --agent cursor` | `.cursor/hooks.json` (stop) |\n| Gemini CLI | `npx isitdone init --agent gemini` | `.gemini/settings.json` (AfterAgent); or `gemini extensions install https://github.com/raimondasl/isitdone-gemini` |\n| GitHub Copilot CLI | `npx isitdone init --agent copilot` | `.github/hooks/isitdone.json` (agentStop); restart Copilot |\n| Qwen Code | `npx isitdone init --agent qwen` | `.qwen/settings.json` (Stop) |\n| Goose | `npx isitdone init --agent goose` | `.agents/plugins/isitdone/hooks/hooks.json` (Stop) |\n| Factory Droid | `npx isitdone init --agent droid` | `.factory/hooks.json` (Stop) |\n| Devin | `npx isitdone init --agent devin` | `.devin/hooks.v1.json` (Stop); skipped when the Claude Code hook is present, since Devin loads that too |\n| Augment (Auggie) | `npx isitdone init --agent augment` | `.augment/settings.json` (Stop) pointing at `.augment/hooks/isitdone-hook.sh`/`.cmd`, since Auggie runs script files |\n| OpenCode | `npx isitdone init --agent opencode` | `.opencode/plugins/isitdone.js` (a plugin: OpenCode has no blocking hook, so failed checks come back as a visible `[isitdone]` follow-up message in the same session) |\n| Junie CLI (early access) | `npx isitdone init --agent junie --user` | `~/.junie/config.json` (Stop) |\n| Everything | `npx isitdone init --agent all` | all of the above that apply to the repo |\n\n`init` detects the checks, writes the Stop hook and (for Claude Code, Codex, Gemini CLI, Qwen Code, Devin and OpenCode) a warn-only post-edit hook, adds `.isitdone/` to `.gitignore`, and runs `doctor`, which pipes a synthetic \"all tests pass\" stop event through the hook and proves it blocks:\n\n```\n$ npx isitdone init\nisitdone init  ~/work/demo-app\n  detected   npm run typecheck, npm run lint, npm test\n  profile    claim-gated  (lite checks on every stop, full checks when the agent claims done)\n  gitignore  added .isitdone/\n  Claude Code added           .claude/settings.json\n\nisitdone doctor  ~/work/demo-app\n  ok  git            main@2429c82, 0 dirty file(s), tree 8d11d2f\n  ok  config         no .isitdone.json (auto-detect only)\n  ok  checks         typecheck: npm run typecheck [lite, 60s]; lint: npm run lint [lite, 60s]; test: npm test [full, 120s]\n  ok  hook:claude    Claude Code (project) .claude/settings.json -> npx -y @aivolution/isitdone hook --host claude\n  ok  probe:claude   synthetic \"tests pass\" stop was blocked in 1.9s (decision block with reason)\n\n  OK   the agent cannot claim done with failing checks in this repo\n```\n\nClaude Code users can also install it as a plugin: `/plugin marketplace add raimondasl/isitdone` then `/plugin install isitdone@isitdone`. Gemini CLI users can install it as an extension: `gemini extensions install https://github.com/raimondasl/isitdone-gemini` ([isitdone-gemini](https://github.com/raimondasl/isitdone-gemini)). A paste-to-agent version of these instructions is in [docs/install.md](docs/install.md).\n\nAdd `--user` to install into your user-level settings instead of the project. `npx isitdone uninstall` removes it. Teach the agent to run it itself with `npx skills add raimondasl/isitdone` (the [`SKILL.md`](SKILL.md) is at the repo root).\n\nThe code lives in the [`@aivolution/isitdone`](https://www.npmjs.com/package/@aivolution/isitdone) package; `isitdone` on npm is a short alias with the same command, and installed hooks always call the canonical package. `npm i -D @aivolution/isitdone` makes the hook resolve locally, with no registry lookup and offline.\n\n## Updating\n\nThe hook runs through `npx`, which keeps its own install cache and does not refresh an unpinned package by itself. To move the hook to the latest release:\n\n```\nnpx isitdone update          # clears the cached copies of both packages and re-warms them\nnpx isitdone update --check  # only reports whether a newer release exists\n```\n\n`doctor` reports the version the hook actually runs and mentions when a newer release is available. Projects that installed `@aivolution/isitdone` as a dev dependency update it with `npm update @aivolution/isitdone` instead.\n\n## Mid-turn warnings\n\nThe Stop hook is the gate; the post-edit hook is the nudge. On Claude Code, Codex, Gemini CLI, Qwen Code, Devin and OpenCode, `init` also registers a hook that runs after every `Edit`/`Write` (Codex and Devin: `apply_patch`, Gemini and Qwen: `write_file`/`replace`, OpenCode: `edit`/`write`/`apply_patch`). It scans just that file against `HEAD` (so it sees every uncommitted change to the file, not only the lines this edit touched) and, when the tests got weaker, adds a short factual note next to the tool result:\n\n```\nisitdone: after your edit, src/auth.test.ts has weaker tests than HEAD (Tests 4 -> 4   Assertions 6 -> 4   Skipped 0 -> 1):\n  - line 12: test skipped or marked expected-failure [high]\n  - line 30: assertion weakened: toEqual -> existence check [medium]\nIf this is intentional, or the change was already there before your edit, tell the user why; otherwise restore the tests. The Stop hook will run the full checks before you can finish.\n```\n\nIt never blocks and it is fast (one file, no test run). Cursor has no channel an agent can see after an edit, so there the warning arrives with the Stop hook instead. Skip it with `init --no-edit-hook`; `\"integrity\": \"off\"` in the config turns it off as well.\n\n## GitHub Action\n\nThe same verification on every pull request, from a clean checkout that never trusts a local receipt:\n\n```yaml\non: { pull_request: {} }\npermissions:\n  contents: read\n  pull-requests: write      # for the sticky PR comment\njobs:\n  isitdone:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n        with: { fetch-depth: 0 }   # exact merge-base for the test-integrity diff\n      - uses: raimondasl/isitdone@v0\n```\n\nListed on the [GitHub Marketplace as isitdone verify](https://github.com/marketplace/actions/isitdone-verify). It runs the repo's checks, scans the diff against the PR base for weakened tests (strict by default: high/critical findings fail the job even when the checks pass), writes the receipt to the job summary, keeps one updated comment on the PR, and can upload the findings as SARIF (`with: { sarif: 'true' }`, needs `security-events: write` on push events). On pull requests from forks the default token is read-only, so the comment is skipped and the job summary carries the receipt; pass a `token` with write access (a PAT or a GitHub App token) or run on `pull_request_target` to comment there too. `base` accepts a branch (default: the PR base), a commit sha, a tag or a qualified ref. Inputs: `version`, `base`, `strict`, `comment`, `sarif`, `token`, `args`, `command`; outputs: `status`, `report`, `json` (the path of the `--json-file` result).\n\n## How it decides\n\n1. **Detect.** Reads `package.json` scripts (`test`, `typecheck`, `lint`, `build`; npm, pnpm, yarn, bun, deno), `pyproject.toml`/`pytest.ini`/`requirements.txt` (pytest, ruff, flake8, mypy, pyright; uv/poetry/pipenv runners), `go.mod` (`go vet`, `go test ./...`), `Cargo.toml` (`cargo check`, `cargo test`), .NET solutions, Gradle/Maven, and `Makefile` targets. Anything can be overridden in `.isitdone.json`.\n2. **Gate on the claim.** The default `claim-gated` profile runs the fast *lite* checks (typecheck, lint) on every stop, and the *full* checks (tests, build) only when the agent's final message contains a completion claim: \"tests pass\", \"done\", \"implemented\", \"verified\", \"ready for review\", and so on. A question or a progress update does not trigger a two-minute test run. Hosts that do not pass the final message (Cursor, Copilot CLI, Factory Droid, Devin, Junie) get the full profile, cached per tree.\n3. **Cache per tree.** The working tree (tracked changes *and* untracked files, including the contents of submodules and embedded repositories) is hashed with a temporary git index, without writing any blob into `.git/objects`. A PASS receipt for the same tree hash and the same check configuration is reused; nothing runs twice for nothing.\n4. **Run and decide.** Checks run in a fresh subprocess with `CI=true`, per-check timeouts, and the last 30 lines captured. If anything fails, the hook returns the host's block shape with a bounded, plain-text reason quoting the claim and the failing output. If everything passes, the receipt is written and the agent may stop.\n5. **Scan the diff for weakened tests.** Deleted test files, new `.skip`/`.only`/`xfail`, dropped assertions, matchers downgraded (`toStrictEqual` to `toEqual`, `toThrow(\"msg\")` to `toThrow()`, `assertEqual` to `assertTrue`), widened tolerances, empty `catch`/`except: pass`, and neutered configuration (`|| true`, `--passWithNoTests`, `continue-on-error`, `testPathIgnorePatterns`, `-DskipTests`, `ignoreFailures`, `cargo test -- --skip`, removed CI test steps). JS/TS, Python, Go, Rust (`#[ignore]`, `#[should_panic]` loosened, `assert_eq!` to `is_ok()`, inline `#[cfg(test)]` modules found by content), Java/Kotlin (JUnit 4/5, TestNG, AssertJ, Hamcrest; `@Disabled`, `assumeTrue(false)`, `assertEquals` to `assertNotNull`; Kotlin is best effort for JUnit and kotlin.test) and C# (xUnit, NUnit, MSTest; `Skip =`, `[Ignore]`, `Assert.Equal` to `Assert.NotNull`); the build configuration scanned includes Cargo/nextest, Maven/Gradle, csproj/runsettings/xunit.runner.json and the common CI files. Findings are reported with a before/after line (`Tests 47 -> 44   Assertions 112 -> 104   Skipped 0 -> 1`) and recorded in the receipt; with `\"integrity\": \"strict\"` (or `--strict`) high/critical findings block the stop even when the checks pass. Suppress a line with `// isitdone: allow <reason>`; suppressions are reported, never hidden, and `--ci` treats new ones as findings.\n6. **Never loop forever.** The hook honours each host's `stop_hook_active` / `loop_count` (and counts attempts itself for hosts that send neither), counts its own attempts per session (default cap 3), and after the cap lets the agent stop with a visible warning. Malformed stdin, a broken config, or an internal error always allow the stop: `isitdone` must never brick the agent.\n\n## CLI\n\n```\nnpx isitdone                      run all checks, write the receipt, exit 0 (DONE) or 1 (NOT DONE)\nnpx isitdone --profile lite       typecheck + lint only\nnpx isitdone --json               {\"ok\", \"done\", \"checks\": [...]} for scripts and orchestrators (--json-file <path> writes it)\nnpx isitdone --no-cache           re-run even if a PASS receipt exists for this tree\nnpx isitdone --all                keep running tests even if typecheck failed\nnpx isitdone --strict             block when the change weakened tests (high/critical findings)\nnpx isitdone --ci                 strict, plus new \"isitdone: allow\" suppressions count as findings\nnpx isitdone --base main          scan the diff against a branch instead of HEAD (pull requests)\nnpx isitdone --report out.md      write a markdown report; --sarif out.sarif writes SARIF 2.1.0 for code scanning\nnpx isitdone history [--since 30d] [--exclude <substr>] [--verbose] [--min <pct>]\n                                  share of past \"done\" claims backed by a passing test run\n                                  (Claude Code, Codex, Gemini CLI, Qwen Code and Cursor transcripts, local only)\nnpx isitdone receipt [--md|--json] state of the current tree: PASS | FAIL | STALE | NONE (exit 0 only on full PASS)\nnpx isitdone detect [--json]      which checks would run and where they came from\nnpx isitdone init [--agent ...]   install the Stop hook (claude | codex | cursor | gemini | copilot | qwen | goose | droid |\n                                  devin | augment | opencode | junie | all | auto)\nnpx isitdone doctor               prove the installed hook blocks; show detected checks and host notes\nnpx isitdone uninstall            remove the hook(s)\n```\n\nFor orchestrators and agent loops, `npx isitdone --json` is a done-predicate: `done` is `true` only when every full check passed on the current tree. Exit codes: `0` done, `1` not done, `3` usage or internal error.\n\n## Configuration\n\nOptional. `.isitdone.json` at the repo root, or an `\"isitdone\"` key in `package.json`:\n\n```jsonc\n{\n  \"checks\": {\n    \"test\": \"npm run test:unit\",                    // replace a detected command\n    \"lint\": false,                                  // disable one\n    \"e2e\": { \"cmd\": \"playwright test\", \"kind\": \"full\", \"timeout\": 600 },  // add one\n    \"smoke\": { \"cmd\": \"./smoke.sh\", \"kind\": \"lite\" }\n  },\n  \"profile\": \"claim-gated\",   // claim-gated (default) | lite | full\n  \"timeout\": 120,             // seconds per full check\n  \"liteTimeout\": 60,          // seconds per lite check\n  \"maxAttempts\": 3,           // consecutive blocks before the agent may stop anyway\n  \"build\": false,             // include the build script even when tests exist\n  \"claimPatterns\": [\"ship it\"], // extra completion-claim regexes\n  \"integrity\": \"warn\",        // warn (default) | strict | off\n  \"history\": { \"exclude\": [\"client-x\"] }  // project paths to skip in `isitdone history`\n}\n```\n\n`kind: \"lite\"` checks run on every stop; `kind: \"full\"` checks run when the agent claims completion. `test` and `build` default to full, `typecheck`, `lint`, `check` and `vet` default to lite.\n\n## What it is not\n\n- **Not a lie detector.** It does not grade the agent's sentences; it runs commands and reads exit codes. The claim only decides *how much* to run.\n- **Not adversarial security.** The receipt is HMAC-signed so a hand-edited file reads NONE, and the tree hash makes a stale receipt visible, but an agent with permission to edit settings can remove the hook. `isitdone` guards honest mistakes, which is where nearly all \"tests pass\" fiction comes from.\n- **Not an AST.** The test-integrity scan is line-and-regex over the diff with a small, published detector list. It catches the common ways an agent makes red go green without fixing anything; it will miss clever ones (a test's expected value bent to match a regression, for one) and occasionally flag a legitimate refactor, which is why it warns by default and every finding shows its evidence. Its precision and recall are measured on a labelled corpus in [`bench/`](bench/) (`npm run bench`; 118 hand-labelled cases at the time of writing, 100% precision, 98% recall) that grows with every reported mistake.\n- **Not an LLM.** Nothing here calls a model, phones home, or needs a key.\n\n## Related tools\n\n| Tool | What it does | Relation |\n|---|---|---|\n| [oh-my-agent](https://github.com/first-fluke/oh-my-agent) | Multi-agent framework that includes a stop-hook gate running typecheck/test/lint | `isitdone` is that gate as a standalone primitive for any host, with receipts and claim-gating |\n| [taskmaster](https://github.com/blader/taskmaster) | Blocks the stop until a completion token appears | Token-based; does not run the checks |\n| [gutcheck](https://www.npmjs.com/package/gutcheck) | Diff-scoped mutation probe with a Claude Code stop hook | Complementary second gate; `isitdone` verifies the suite passes, gutcheck verifies the suite means something |\n| [checkwash](https://github.com/taipei49314/checkwash), [testseal](https://github.com/satwiksps/testseal) | Diff scanners for weakened tests | Diff-only; `isitdone` runs the checks (diff scanning is on the roadmap) |\n| [ProofRun](https://github.com/yebiguo/ProofRun) | Tree-bound PASS/FAIL/STALE receipts with hand-written config | Similar receipt idea; `isitdone` auto-detects and hooks into the agents |\n\n## Roadmap\n\n- **v0.5** A `--related` mode that runs only the tests touching the changed files for slow suites; Cline (a PreToolUse gate on `attempt_completion`) and Amp (`agent.end` plugin) adapters; a native OpenCode hook once `session.stopping` ships, and Windsurf/Cascade once its hooks can block; `history` for OpenCode's database; Kotest/Spek DSLs; a detector for expected values bent to match a regression.\n- Not planned: Roo Code and Kilo Code (no hooks), Kiro (its Stop trigger cannot block), Crush (PreToolUse only). Aider has no hooks but `aider --auto-test --test-cmd \"npx isitdone\"` feeds the same verdict back after every edit.\n\n## Development\n\n```\nnpm ci\nnpm test            # vitest, runs in a throwaway HOME so it never touches your real agent settings\nnpm run typecheck\nnpm run build       # esbuild -> dist/isitdone.js (CLI) and dist/index.js (library), no dependencies\nnpm run bench       # precision/recall of the test-integrity scanner over bench/cases/\nnode dist/isitdone.js\n```\n\nThe repository dogfoods itself: CI runs `node dist/isitdone.js --json` on every push, and pull requests run the GitHub Action. The library is importable too: `import { verify, scanIntegrity } from '@aivolution/isitdone'`.\n\n## License\n\nMIT\n","readmeFilename":"README.md"}