{"_id":"@anhnth24/harnessctl","_rev":"16-b7e7683f1b911f1800508dadaed5b4b4","name":"@anhnth24/harnessctl","dist-tags":{"latest":"0.5.2"},"versions":{"0.3.2":{"name":"@anhnth24/harnessctl","version":"0.3.2","keywords":["claude-code","claude","hooks","enforcement","audit","policy","lint","ai-coding","agent-guardrails"],"author":{"name":"anhnth","email":"anhnthntl@gmail.com"},"license":"MIT","_id":"@anhnth24/harnessctl@0.3.2","maintainers":[{"name":"anhnth24","email":"anhnthntl@gmail.com"}],"homepage":"https://github.com/anhnth24/harnessctl#readme","bugs":{"url":"https://github.com/anhnth24/harnessctl/issues"},"bin":{"harnessctl":"bin/harnessctl.cjs","harness-dispatcher":"bin/harness-dispatcher.cjs"},"dist":{"shasum":"e98fc857f2011fdde6cb18d82e32bd9d2d80bdf2","tarball":"https://registry.npmjs.org/@anhnth24/harnessctl/-/harnessctl-0.3.2.tgz","fileCount":58,"integrity":"sha512-OPgBzw0DXEgp83kvJmdBn82smxZkLWMFIrqavw8L+iDJHtIweRLxOfGEO0YvMlk81i9240A4PHsO2SObAxTUJw==","signatures":[{"sig":"MEQCICCOJO4aaJXZD/Lq2AdMO470eIHJhernv8g3hoo/u6+/AiBK3CZcJIEv78bIURKwMWXoNDSKl0b6R22QESM5sbb0EA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":205981},"engines":{"node":">=18"},"gitHead":"1a5c186b3659e28370f20bcb09eaad18b63edb2c","scripts":{"test":"node --test tests/unit/*.test.cjs tests/integration/*.test.cjs","test:perf":"node tests/perf/baseline-and-delta.cjs","test:unit":"node --test tests/unit/*.test.cjs","test:integration":"node --test tests/integration/*.test.cjs"},"_npmUser":{"name":"anhnth24","email":"anhnthntl@gmail.com"},"repository":{"url":"git+https://github.com/anhnth24/harnessctl.git","type":"git"},"_npmVersion":"10.9.2","description":"Standalone enforcement overlay for Claude Code hooks — audit-grade JSONL, deterministic gates, profile-based policy","directories":{},"_nodeVersion":"22.14.0","dependencies":{"ajv":"^8.0.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/harnessctl_0.3.2_1778441687937_0.33218081760738905","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"name":"@anhnth24/harnessctl","version":"0.4.0","keywords":["claude-code","claude","hooks","enforcement","audit","policy","lint","ai-coding","agent-guardrails"],"author":{"name":"anhnth","email":"anhnthntl@gmail.com"},"license":"MIT","_id":"@anhnth24/harnessctl@0.4.0","maintainers":[{"name":"anhnth24","email":"anhnthntl@gmail.com"}],"homepage":"https://github.com/anhnth24/harnessctl#readme","bugs":{"url":"https://github.com/anhnth24/harnessctl/issues"},"bin":{"harn":"bin/harnessctl.cjs","harnessctl":"bin/harnessctl.cjs","harness-dispatcher":"bin/harness-dispatcher.cjs"},"dist":{"shasum":"85f0f6ac5f19f6d3800a700bc41b9d1dcbc643de","tarball":"https://registry.npmjs.org/@anhnth24/harnessctl/-/harnessctl-0.4.0.tgz","fileCount":59,"integrity":"sha512-TQkHw6giOupJWfMh42YMsWwqjG+Slu0SJDNaSWVOJG8i1gz+VqG/kLEkESktXuqeCRaP+AJb4DMAyB9d0Lw6pw==","signatures":[{"sig":"MEUCIDl92XfaS02919veetGeLHsEsJAMOkmVmRzItqAPMNfVAiEA5HRIaayQ6TtOe5G/izYNImkaOqUTvnq+5NvWvNXTVA4=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":209062},"engines":{"node":">=18"},"gitHead":"d9d1f4a92a0ab33ae5e33f1c6a90fe5153866cd7","scripts":{"test":"node --test tests/unit/*.test.cjs tests/integration/*.test.cjs","test:perf":"node tests/perf/baseline-and-delta.cjs","test:unit":"node --test tests/unit/*.test.cjs","test:integration":"node --test tests/integration/*.test.cjs"},"_npmUser":{"name":"anhnth24","email":"anhnthntl@gmail.com"},"repository":{"url":"git+https://github.com/anhnth24/harnessctl.git","type":"git"},"_npmVersion":"10.9.2","description":"Standalone enforcement overlay for Claude Code hooks — audit-grade JSONL, deterministic gates, profile-based policy","directories":{},"_nodeVersion":"22.14.0","dependencies":{"ajv":"^8.0.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/harnessctl_0.4.0_1778466448606_0.35719373274378285","host":"s3://npm-registry-packages-npm-production"}},"0.4.1":{"name":"@anhnth24/harnessctl","version":"0.4.1","keywords":["claude-code","claude","hooks","enforcement","audit","policy","lint","ai-coding","agent-guardrails"],"author":{"name":"anhnth","email":"anhnthntl@gmail.com"},"license":"MIT","_id":"@anhnth24/harnessctl@0.4.1","maintainers":[{"name":"anhnth24","email":"anhnthntl@gmail.com"}],"homepage":"https://github.com/anhnth24/harnessctl#readme","bugs":{"url":"https://github.com/anhnth24/harnessctl/issues"},"bin":{"harn":"bin/harnessctl.cjs","harnessctl":"bin/harnessctl.cjs","harness-dispatcher":"bin/harness-dispatcher.cjs"},"dist":{"shasum":"5510715d30115c00f3be4bfa74c3e632d3c5b433","tarball":"https://registry.npmjs.org/@anhnth24/harnessctl/-/harnessctl-0.4.1.tgz","fileCount":65,"integrity":"sha512-V++vU5WWfhqb8Js9ZyW7mnTVoYljJgHd8RZmOcmjtsv14+b2+DFD/h5CJW54RxT9TRjLL+1JE+0pPn4sj9ngzQ==","signatures":[{"sig":"MEUCIQDktTfnOrv2bAiDugqLcnZbhnIbI7i+sUfzfGfP6kuLYgIgRYpoMMRLgfieKc1NHyHvKxaOplT9b7FWLhFOu6KEj9E=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":243494},"engines":{"node":">=18"},"gitHead":"d9d1f4a92a0ab33ae5e33f1c6a90fe5153866cd7","scripts":{"test":"node --test tests/unit/*.test.cjs tests/integration/*.test.cjs","test:perf":"node tests/perf/baseline-and-delta.cjs","test:unit":"node --test tests/unit/*.test.cjs","test:integration":"node --test tests/integration/*.test.cjs"},"_npmUser":{"name":"anhnth24","email":"anhnthntl@gmail.com"},"repository":{"url":"git+https://github.com/anhnth24/harnessctl.git","type":"git"},"_npmVersion":"10.9.2","description":"Standalone enforcement overlay for Claude Code hooks — audit-grade JSONL, deterministic gates, profile-based policy","directories":{},"_nodeVersion":"22.14.0","dependencies":{"ajv":"^8.0.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/harnessctl_0.4.1_1778470478728_0.509441750688312","host":"s3://npm-registry-packages-npm-production"}},"0.4.2":{"name":"@anhnth24/harnessctl","version":"0.4.2","keywords":["claude-code","claude","hooks","enforcement","audit","policy","lint","ai-coding","agent-guardrails"],"author":{"name":"anhnth","email":"anhnthntl@gmail.com"},"license":"MIT","_id":"@anhnth24/harnessctl@0.4.2","maintainers":[{"name":"anhnth24","email":"anhnthntl@gmail.com"}],"homepage":"https://github.com/anhnth24/harnessctl#readme","bugs":{"url":"https://github.com/anhnth24/harnessctl/issues"},"bin":{"harn":"bin/harnessctl.cjs","harnessctl":"bin/harnessctl.cjs","harness-dispatcher":"bin/harness-dispatcher.cjs"},"dist":{"shasum":"99218ab3d8269e8a20c6e8a40583bba9a12773b2","tarball":"https://registry.npmjs.org/@anhnth24/harnessctl/-/harnessctl-0.4.2.tgz","fileCount":65,"integrity":"sha512-m6C/nHS0Ly+U4mhbzgfxa6vOR1JjR9s9wuBa5j2sJkzoL3ZzV45JdofWaSDTncoTZMzyzZinmtBbWfItBB2YrA==","signatures":[{"sig":"MEYCIQCMhg24qgkeQ7cbA3QKL3oSR/D4CaJqz6mYbwbXwvikQgIhAOjhJWw1CMiou0GvBHuUikNrf9fy4OBgymSNUepam5z4","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":243779},"engines":{"node":">=18"},"gitHead":"d9d1f4a92a0ab33ae5e33f1c6a90fe5153866cd7","scripts":{"test":"node --test tests/unit/*.test.cjs tests/integration/*.test.cjs","test:perf":"node tests/perf/baseline-and-delta.cjs","test:unit":"node --test tests/unit/*.test.cjs","test:integration":"node --test tests/integration/*.test.cjs"},"_npmUser":{"name":"anhnth24","email":"anhnthntl@gmail.com"},"repository":{"url":"git+https://github.com/anhnth24/harnessctl.git","type":"git"},"_npmVersion":"10.9.2","description":"Standalone enforcement overlay for Claude Code hooks — audit-grade JSONL, deterministic gates, profile-based policy","directories":{},"_nodeVersion":"22.14.0","dependencies":{"ajv":"^8.0.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/harnessctl_0.4.2_1778470542765_0.9860355948453385","host":"s3://npm-registry-packages-npm-production"}},"0.4.3":{"name":"@anhnth24/harnessctl","version":"0.4.3","keywords":["claude-code","claude","hooks","enforcement","audit","policy","lint","ai-coding","agent-guardrails"],"author":{"name":"anhnth","email":"anhnthntl@gmail.com"},"license":"MIT","_id":"@anhnth24/harnessctl@0.4.3","maintainers":[{"name":"anhnth24","email":"anhnthntl@gmail.com"}],"homepage":"https://github.com/anhnth24/harnessctl#readme","bugs":{"url":"https://github.com/anhnth24/harnessctl/issues"},"bin":{"harn":"bin/harnessctl.cjs","harnessctl":"bin/harnessctl.cjs","harness-dispatcher":"bin/harness-dispatcher.cjs"},"dist":{"shasum":"ca095212876805131c936263d329b9bb539cafc1","tarball":"https://registry.npmjs.org/@anhnth24/harnessctl/-/harnessctl-0.4.3.tgz","fileCount":65,"integrity":"sha512-+AmZJzE+oMPIp2RPcWzRmONzfjlmHXRJwmJGkUfzLF3aPnX1vi/ZIQPkXThmpX8wdHCBW1qE92IBeX/ChXdWeg==","signatures":[{"sig":"MEUCIH5z6CCTFqwe1EGAhh2YAFqr5b3wrBvwo80aQsd79BvqAiEA5SAb1b8Dq2kKA9pVJd5/5zA0cz2SZfmmpD0rDGqedTQ=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":244093},"engines":{"node":">=18"},"gitHead":"d9d1f4a92a0ab33ae5e33f1c6a90fe5153866cd7","scripts":{"test":"node --test tests/unit/*.test.cjs tests/integration/*.test.cjs","test:perf":"node tests/perf/baseline-and-delta.cjs","test:unit":"node --test tests/unit/*.test.cjs","test:integration":"node --test tests/integration/*.test.cjs"},"_npmUser":{"name":"anhnth24","email":"anhnthntl@gmail.com"},"repository":{"url":"git+https://github.com/anhnth24/harnessctl.git","type":"git"},"_npmVersion":"10.9.2","description":"Standalone enforcement overlay for Claude Code hooks — audit-grade JSONL, deterministic gates, profile-based policy","directories":{},"_nodeVersion":"22.14.0","dependencies":{"ajv":"^8.0.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/harnessctl_0.4.3_1778470786878_0.9201875762962997","host":"s3://npm-registry-packages-npm-production"}},"0.4.4":{"name":"@anhnth24/harnessctl","version":"0.4.4","keywords":["claude-code","claude","hooks","enforcement","audit","policy","lint","ai-coding","agent-guardrails"],"author":{"name":"anhnth","email":"anhnthntl@gmail.com"},"license":"MIT","_id":"@anhnth24/harnessctl@0.4.4","maintainers":[{"name":"anhnth24","email":"anhnthntl@gmail.com"}],"homepage":"https://github.com/anhnth24/harnessctl#readme","bugs":{"url":"https://github.com/anhnth24/harnessctl/issues"},"bin":{"harn":"bin/harnessctl.cjs","harnessctl":"bin/harnessctl.cjs","harness-dispatcher":"bin/harness-dispatcher.cjs"},"dist":{"shasum":"ce9e9b7f18b0b6d0298071ee6f5d160eae0b02bf","tarball":"https://registry.npmjs.org/@anhnth24/harnessctl/-/harnessctl-0.4.4.tgz","fileCount":69,"integrity":"sha512-9B0yXwn8bPTgbMqupwCPug6RmOS4E1C8LvIduFd+WDfTxkUHkuojbk4mMqo5gPypdiZg59zA65Xy4Uiu+5rqGg==","signatures":[{"sig":"MEQCIHen0cEUubA4DQdCVe/TjI1yATPtUzDZmdvUXi5tD2CqAiB/enZRKFkdpns0VQBE+RzhIiBq8UvUV67DcaXcON9ixw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":265303},"engines":{"node":">=18"},"gitHead":"d9d1f4a92a0ab33ae5e33f1c6a90fe5153866cd7","scripts":{"test":"node --test tests/unit/*.test.cjs tests/integration/*.test.cjs","test:perf":"node tests/perf/baseline-and-delta.cjs","test:unit":"node --test tests/unit/*.test.cjs","test:integration":"node --test tests/integration/*.test.cjs"},"_npmUser":{"name":"anhnth24","email":"anhnthntl@gmail.com"},"repository":{"url":"git+https://github.com/anhnth24/harnessctl.git","type":"git"},"_npmVersion":"10.9.2","description":"Standalone enforcement overlay for Claude Code hooks — audit-grade JSONL, deterministic gates, profile-based policy","directories":{},"_nodeVersion":"22.14.0","dependencies":{"ajv":"^8.0.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/harnessctl_0.4.4_1778488519815_0.7827600393903216","host":"s3://npm-registry-packages-npm-production"}},"0.4.5":{"name":"@anhnth24/harnessctl","version":"0.4.5","keywords":["claude-code","claude","hooks","enforcement","audit","policy","lint","ai-coding","agent-guardrails"],"author":{"name":"anhnth","email":"anhnthntl@gmail.com"},"license":"MIT","_id":"@anhnth24/harnessctl@0.4.5","maintainers":[{"name":"anhnth24","email":"anhnthntl@gmail.com"}],"homepage":"https://github.com/anhnth24/harnessctl#readme","bugs":{"url":"https://github.com/anhnth24/harnessctl/issues"},"bin":{"harn":"bin/harnessctl.cjs","harnessctl":"bin/harnessctl.cjs","harness-dispatcher":"bin/harness-dispatcher.cjs"},"dist":{"shasum":"6569bb6aaf8022dbe0c98e35ca363d19ea5d4e44","tarball":"https://registry.npmjs.org/@anhnth24/harnessctl/-/harnessctl-0.4.5.tgz","fileCount":69,"integrity":"sha512-CTgsr9GqGQuBSju2t/fprz39lSr5cEXawgNkTBm2EEd/IIA4agbyEiZM2cAtcMJFb9SfvDW6CFE1nIPB0+pjQw==","signatures":[{"sig":"MEUCIQDjMmgWS8HYKIwrcBs0fgsmVif0g1YyzkJiQtgQpzqohQIgfX6dWXBOt9t+P2AQ0V3rUt0ZBDTn9OMQTJ3P/LcbpEk=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":265303},"engines":{"node":">=18"},"gitHead":"d9d1f4a92a0ab33ae5e33f1c6a90fe5153866cd7","scripts":{"test":"node --test tests/unit/*.test.cjs tests/integration/*.test.cjs","test:perf":"node tests/perf/baseline-and-delta.cjs","test:unit":"node --test tests/unit/*.test.cjs","test:integration":"node --test tests/integration/*.test.cjs"},"_npmUser":{"name":"anhnth24","email":"anhnthntl@gmail.com"},"repository":{"url":"git+https://github.com/anhnth24/harnessctl.git","type":"git"},"_npmVersion":"10.9.2","description":"Standalone enforcement overlay for Claude Code hooks — audit-grade JSONL, deterministic gates, profile-based policy","directories":{},"_nodeVersion":"22.14.0","dependencies":{"ajv":"^8.0.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/harnessctl_0.4.5_1778488561905_0.11682952536059577","host":"s3://npm-registry-packages-npm-production"}},"0.4.6":{"name":"@anhnth24/harnessctl","version":"0.4.6","keywords":["claude-code","claude","hooks","enforcement","audit","policy","lint","ai-coding","agent-guardrails"],"author":{"name":"anhnth","email":"anhnthntl@gmail.com"},"license":"MIT","_id":"@anhnth24/harnessctl@0.4.6","maintainers":[{"name":"anhnth24","email":"anhnthntl@gmail.com"}],"homepage":"https://github.com/anhnth24/harnessctl#readme","bugs":{"url":"https://github.com/anhnth24/harnessctl/issues"},"bin":{"harn":"bin/harnessctl.cjs","harnessctl":"bin/harnessctl.cjs","harness-dispatcher":"bin/harness-dispatcher.cjs"},"dist":{"shasum":"d382013a4b84664400d8555a183e1ee5346d1bc6","tarball":"https://registry.npmjs.org/@anhnth24/harnessctl/-/harnessctl-0.4.6.tgz","fileCount":70,"integrity":"sha512-rSPFfunQouaB4D3ljLK5hO6DdyPgaEPOZuf7lbr5LJPJP30Srkmdb+5cfu2vUk4BiVdhvvSPGxwsSjQulMyUYg==","signatures":[{"sig":"MEYCIQDv1F8Ot3yx6Emx6FpNpki3iIRdzJXsY8+V1ZBJT8MLQAIhAMINq5z/NUmPtJuXYQhzFLGoaei34BZQd6Ar3tARqWxO","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":271653},"engines":{"node":">=18"},"gitHead":"d9d1f4a92a0ab33ae5e33f1c6a90fe5153866cd7","scripts":{"test":"node --test tests/unit/*.test.cjs tests/integration/*.test.cjs","test:perf":"node tests/perf/baseline-and-delta.cjs","test:unit":"node --test tests/unit/*.test.cjs","test:integration":"node --test tests/integration/*.test.cjs"},"_npmUser":{"name":"anhnth24","email":"anhnthntl@gmail.com"},"repository":{"url":"git+https://github.com/anhnth24/harnessctl.git","type":"git"},"_npmVersion":"10.9.2","description":"Standalone enforcement overlay for Claude Code hooks — audit-grade JSONL, deterministic gates, profile-based policy","directories":{},"_nodeVersion":"22.14.0","dependencies":{"ajv":"^8.0.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/harnessctl_0.4.6_1778488681649_0.48816578996122195","host":"s3://npm-registry-packages-npm-production"}},"0.4.7":{"name":"@anhnth24/harnessctl","version":"0.4.7","keywords":["claude-code","claude","hooks","enforcement","audit","policy","lint","ai-coding","agent-guardrails"],"author":{"name":"anhnth","email":"anhnthntl@gmail.com"},"license":"MIT","_id":"@anhnth24/harnessctl@0.4.7","maintainers":[{"name":"anhnth24","email":"anhnthntl@gmail.com"}],"homepage":"https://github.com/anhnth24/harnessctl#readme","bugs":{"url":"https://github.com/anhnth24/harnessctl/issues"},"bin":{"harn":"bin/harnessctl.cjs","harnessctl":"bin/harnessctl.cjs","harness-dispatcher":"bin/harness-dispatcher.cjs"},"dist":{"shasum":"5ba0bf1700f60a02b1776e8d2ef5b6ea0d1f153d","tarball":"https://registry.npmjs.org/@anhnth24/harnessctl/-/harnessctl-0.4.7.tgz","fileCount":70,"integrity":"sha512-iM80jxLXd9b+iUc3DE43YE988jghzT0Seb4IFMQt93IuY7H7W+mWBmsBO5pAts4nz4/kNfujFtt4SlmyONDqMw==","signatures":[{"sig":"MEYCIQC6+gZZtiGYyeky7h+EW5/4sSYP1CnsU4BJSeeElRfGeQIhAIJhKMxZf3AYgV//FPTFciqdZcE6tCeAZKGYauOGC28T","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":272579},"engines":{"node":">=18"},"gitHead":"d9d1f4a92a0ab33ae5e33f1c6a90fe5153866cd7","scripts":{"test":"node --test tests/unit/*.test.cjs tests/integration/*.test.cjs","test:perf":"node tests/perf/baseline-and-delta.cjs","test:unit":"node --test tests/unit/*.test.cjs","test:integration":"node --test tests/integration/*.test.cjs"},"_npmUser":{"name":"anhnth24","email":"anhnthntl@gmail.com"},"repository":{"url":"git+https://github.com/anhnth24/harnessctl.git","type":"git"},"_npmVersion":"10.9.2","description":"Standalone enforcement overlay for Claude Code hooks — audit-grade JSONL, deterministic gates, profile-based policy","directories":{},"_nodeVersion":"22.14.0","dependencies":{"ajv":"^8.0.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/harnessctl_0.4.7_1778488949330_0.46378927336367215","host":"s3://npm-registry-packages-npm-production"}},"0.4.8":{"name":"@anhnth24/harnessctl","version":"0.4.8","keywords":["claude-code","claude","hooks","enforcement","audit","policy","lint","ai-coding","agent-guardrails"],"author":{"name":"anhnth","email":"anhnthntl@gmail.com"},"license":"MIT","_id":"@anhnth24/harnessctl@0.4.8","maintainers":[{"name":"anhnth24","email":"anhnthntl@gmail.com"}],"homepage":"https://github.com/anhnth24/harnessctl#readme","bugs":{"url":"https://github.com/anhnth24/harnessctl/issues"},"bin":{"harn":"bin/harnessctl.cjs","harnessctl":"bin/harnessctl.cjs","harness-dispatcher":"bin/harness-dispatcher.cjs"},"dist":{"shasum":"62b3902a6ebf637cb5d156d8f579635550529dbe","tarball":"https://registry.npmjs.org/@anhnth24/harnessctl/-/harnessctl-0.4.8.tgz","fileCount":70,"integrity":"sha512-/8nur7VAIMp0HUymGPiM1h85A/182spGxqbIrQyLPtqBAcRos3+vkt6VBVohbmJ5/31Wr/5FPVgUJ08lJ0gdYw==","signatures":[{"sig":"MEYCIQCijP27p1BoE651hpxiArR7T3mzPKiSGwxgaiRX2+MkIQIhAKB3cSS2C2GrsTtwhYa5tnKEWwMJhvBvNperTlvEQbD6","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":274893},"engines":{"node":">=18"},"gitHead":"d9d1f4a92a0ab33ae5e33f1c6a90fe5153866cd7","scripts":{"test":"node --test tests/unit/*.test.cjs tests/integration/*.test.cjs","test:perf":"node tests/perf/baseline-and-delta.cjs","test:unit":"node --test tests/unit/*.test.cjs","test:integration":"node --test tests/integration/*.test.cjs"},"_npmUser":{"name":"anhnth24","email":"anhnthntl@gmail.com"},"repository":{"url":"git+https://github.com/anhnth24/harnessctl.git","type":"git"},"_npmVersion":"10.9.2","description":"Standalone enforcement overlay for Claude Code hooks — audit-grade JSONL, deterministic gates, profile-based policy","directories":{},"_nodeVersion":"22.14.0","dependencies":{"ajv":"^8.0.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/harnessctl_0.4.8_1778489139180_0.7026052780766829","host":"s3://npm-registry-packages-npm-production"}},"0.4.9":{"name":"@anhnth24/harnessctl","version":"0.4.9","keywords":["claude-code","claude","hooks","enforcement","audit","policy","lint","ai-coding","agent-guardrails"],"author":{"name":"anhnth","email":"anhnthntl@gmail.com"},"license":"MIT","_id":"@anhnth24/harnessctl@0.4.9","maintainers":[{"name":"anhnth24","email":"anhnthntl@gmail.com"}],"homepage":"https://github.com/anhnth24/harnessctl#readme","bugs":{"url":"https://github.com/anhnth24/harnessctl/issues"},"bin":{"harn":"bin/harnessctl.cjs","harnessctl":"bin/harnessctl.cjs","harness-dispatcher":"bin/harness-dispatcher.cjs"},"dist":{"shasum":"8dd50979d71fb7dbdbd7eca0a09324e30e82ba8d","tarball":"https://registry.npmjs.org/@anhnth24/harnessctl/-/harnessctl-0.4.9.tgz","fileCount":70,"integrity":"sha512-fFgZ3T6w0SufAVCwnWE0CXMNrrIdYaOwtfar38U8FxWYm8GVHdwiNiA8Cr3RILgEEMJ3ZxQnZOC1zvcemBN2pg==","signatures":[{"sig":"MEUCIQCbBwAHHXrkirViyUFGNgMMwdhuDMCldB9/l9PDSq1CcwIgdOq5baiH87ineEmFIjN/N2Eo61aEraNvQ5QnEtGgU+Y=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":274893},"engines":{"node":">=18"},"gitHead":"d9d1f4a92a0ab33ae5e33f1c6a90fe5153866cd7","scripts":{"test":"node --test tests/unit/*.test.cjs tests/integration/*.test.cjs","test:perf":"node tests/perf/baseline-and-delta.cjs","test:unit":"node --test tests/unit/*.test.cjs","test:integration":"node --test tests/integration/*.test.cjs"},"_npmUser":{"name":"anhnth24","email":"anhnthntl@gmail.com"},"repository":{"url":"git+https://github.com/anhnth24/harnessctl.git","type":"git"},"_npmVersion":"10.9.2","description":"Standalone enforcement overlay for Claude Code hooks — audit-grade JSONL, deterministic gates, profile-based policy","directories":{},"_nodeVersion":"22.14.0","dependencies":{"ajv":"^8.0.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/harnessctl_0.4.9_1778491951549_0.1993878884110456","host":"s3://npm-registry-packages-npm-production"}},"0.4.10":{"name":"@anhnth24/harnessctl","version":"0.4.10","keywords":["claude-code","claude","hooks","enforcement","audit","policy","lint","ai-coding","agent-guardrails"],"author":{"name":"anhnth","email":"anhnthntl@gmail.com"},"license":"MIT","_id":"@anhnth24/harnessctl@0.4.10","maintainers":[{"name":"anhnth24","email":"anhnthntl@gmail.com"}],"homepage":"https://github.com/anhnth24/harnessctl#readme","bugs":{"url":"https://github.com/anhnth24/harnessctl/issues"},"bin":{"harn":"bin/harnessctl.cjs","harnessctl":"bin/harnessctl.cjs","harness-dispatcher":"bin/harness-dispatcher.cjs"},"dist":{"shasum":"6837f11090b318193ebab08ab86a7bb50b913f86","tarball":"https://registry.npmjs.org/@anhnth24/harnessctl/-/harnessctl-0.4.10.tgz","fileCount":70,"integrity":"sha512-Q2EoJy9DmYQMJml0QkB5mEFeG+T4tWmjorj7eecHV9lWCwf8QKQM84Uz12iBToNI1HggsjeHTFmmyxOOiQbMbg==","signatures":[{"sig":"MEUCIFyLYByawWNg2vrrJzPjZf9b0DMFQkBMHCQc3460gyOEAiEAy5GGNnpph2Q3pcGSXozR7xVz45eKeJWU3MdkJgDjZ7o=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":275938},"engines":{"node":">=18"},"gitHead":"d9d1f4a92a0ab33ae5e33f1c6a90fe5153866cd7","scripts":{"test":"node --test tests/unit/*.test.cjs tests/integration/*.test.cjs","test:perf":"node tests/perf/baseline-and-delta.cjs","test:unit":"node --test tests/unit/*.test.cjs","test:integration":"node --test tests/integration/*.test.cjs"},"_npmUser":{"name":"anhnth24","email":"anhnthntl@gmail.com"},"repository":{"url":"git+https://github.com/anhnth24/harnessctl.git","type":"git"},"_npmVersion":"10.9.2","description":"Standalone enforcement overlay for Claude Code hooks — audit-grade JSONL, deterministic gates, profile-based policy","directories":{},"_nodeVersion":"22.14.0","dependencies":{"ajv":"^8.0.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/harnessctl_0.4.10_1778492122566_0.5088472872818881","host":"s3://npm-registry-packages-npm-production"}},"0.4.11":{"name":"@anhnth24/harnessctl","version":"0.4.11","keywords":["claude-code","claude","hooks","enforcement","audit","policy","lint","ai-coding","agent-guardrails"],"author":{"name":"anhnth","email":"anhnthntl@gmail.com"},"license":"MIT","_id":"@anhnth24/harnessctl@0.4.11","maintainers":[{"name":"anhnth24","email":"anhnthntl@gmail.com"}],"homepage":"https://github.com/anhnth24/harnessctl#readme","bugs":{"url":"https://github.com/anhnth24/harnessctl/issues"},"bin":{"harn":"bin/harnessctl.cjs","harnessctl":"bin/harnessctl.cjs","harness-dispatcher":"bin/harness-dispatcher.cjs"},"dist":{"shasum":"e9ca4a2dc3058dc847243835d7a774e22b6f4369","tarball":"https://registry.npmjs.org/@anhnth24/harnessctl/-/harnessctl-0.4.11.tgz","fileCount":70,"integrity":"sha512-KmnSsvsC8SXYVz5ii9Klk7NTubxaOOlkS7zbhD8m7S6MPqNR0kD9CeSNObAYvxw7/1fNEjZe9mTDlVHXAW3+AQ==","signatures":[{"sig":"MEYCIQDPD/Ozq8E1k151MnyM7hvtJ2sywFznEFDe12Ib5f3QCQIhAJqpYDpGhdtw5WR72FjTJTOqhT4M5B9g1X9bBFp9MAKA","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":279212},"engines":{"node":">=18"},"gitHead":"d9d1f4a92a0ab33ae5e33f1c6a90fe5153866cd7","scripts":{"test":"node --test tests/unit/*.test.cjs tests/integration/*.test.cjs","test:perf":"node tests/perf/baseline-and-delta.cjs","test:unit":"node --test tests/unit/*.test.cjs","test:integration":"node --test tests/integration/*.test.cjs"},"_npmUser":{"name":"anhnth24","email":"anhnthntl@gmail.com"},"repository":{"url":"git+https://github.com/anhnth24/harnessctl.git","type":"git"},"_npmVersion":"10.9.2","description":"Standalone enforcement overlay for Claude Code hooks — audit-grade JSONL, deterministic gates, profile-based policy","directories":{},"_nodeVersion":"22.14.0","dependencies":{"ajv":"^8.0.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/harnessctl_0.4.11_1778493003270_0.8562997508440091","host":"s3://npm-registry-packages-npm-production"}},"0.5.0":{"name":"@anhnth24/harnessctl","version":"0.5.0","keywords":["claude-code","claude","hooks","enforcement","audit","policy","lint","ai-coding","agent-guardrails"],"author":{"name":"anhnth","email":"anhnthntl@gmail.com"},"license":"MIT","_id":"@anhnth24/harnessctl@0.5.0","maintainers":[{"name":"anhnth24","email":"anhnthntl@gmail.com"}],"homepage":"https://github.com/anhnth24/harnessctl#readme","bugs":{"url":"https://github.com/anhnth24/harnessctl/issues"},"bin":{"harn":"bin/harnessctl.cjs","harnessctl":"bin/harnessctl.cjs","harness-dispatcher":"bin/harness-dispatcher.cjs"},"dist":{"shasum":"a1ff02123c6fbd50eb3ff987762ed51f67098d9b","tarball":"https://registry.npmjs.org/@anhnth24/harnessctl/-/harnessctl-0.5.0.tgz","fileCount":87,"integrity":"sha512-7wDBVK1KdqBCJ81/CQe0UWcU/gsFwBUXktV0XEaYASmVoR1sATgp/VcosVTD0a++tQvJWfMVilVUYWLtujTyjA==","signatures":[{"sig":"MEQCICVhjrOLpC4lCoTMuTmKtWx25y7lF6WbxmSwOFRtLF21AiBSjyEK/V4FNntPVvW43xcHGYvEoS5yoICJXZEE+ZIJgg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":381937},"engines":{"node":">=18"},"gitHead":"cb3c216705819ce2691d2defc13b86b71acfe33c","scripts":{"test":"node --test tests/unit/*.test.cjs tests/integration/*.test.cjs","test:perf":"node tests/perf/baseline-and-delta.cjs","test:unit":"node --test tests/unit/*.test.cjs","test:integration":"node --test tests/integration/*.test.cjs"},"_npmUser":{"name":"anhnth24","email":"anhnthntl@gmail.com"},"repository":{"url":"git+https://github.com/anhnth24/harnessctl.git","type":"git"},"_npmVersion":"10.9.2","description":"Standalone enforcement overlay for Claude Code hooks — audit-grade JSONL, deterministic gates, profile-based policy","directories":{},"_nodeVersion":"22.14.0","dependencies":{"ajv":"^8.0.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/harnessctl_0.5.0_1778548218478_0.7892074218713241","host":"s3://npm-registry-packages-npm-production"}},"0.5.1":{"name":"@anhnth24/harnessctl","version":"0.5.1","keywords":["claude-code","claude","hooks","enforcement","audit","policy","lint","ai-coding","agent-guardrails"],"author":{"name":"anhnth","email":"anhnthntl@gmail.com"},"license":"MIT","_id":"@anhnth24/harnessctl@0.5.1","maintainers":[{"name":"anhnth24","email":"anhnthntl@gmail.com"}],"homepage":"https://github.com/anhnth24/harnessctl#readme","bugs":{"url":"https://github.com/anhnth24/harnessctl/issues"},"bin":{"harn":"bin/harnessctl.cjs","harnessctl":"bin/harnessctl.cjs","harness-dispatcher":"bin/harness-dispatcher.cjs"},"dist":{"shasum":"dea96495be79e9efb3c221a14107c111a2e0e5cc","tarball":"https://registry.npmjs.org/@anhnth24/harnessctl/-/harnessctl-0.5.1.tgz","fileCount":87,"integrity":"sha512-IUKavuNtpU062Z5BIwDqCQ5EqeKurECuIL0/vCetmqDJYHm40PG467U+HDY9h7ztyVCaoquiDe4bDrtpxi2b7g==","signatures":[{"sig":"MEYCIQDDCvoJg/CNoMiVARapcOZ5AoOyqEezJhE4WHtsCMUf3AIhAKbNv+V6yEEUYVB2Grch5w8ZXIh6M+tM1fSC5P/3+Cmy","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":382508},"engines":{"node":">=18"},"gitHead":"cb3c216705819ce2691d2defc13b86b71acfe33c","scripts":{"test":"node --test tests/unit/*.test.cjs tests/integration/*.test.cjs","test:perf":"node tests/perf/baseline-and-delta.cjs","test:unit":"node --test tests/unit/*.test.cjs","test:integration":"node --test tests/integration/*.test.cjs"},"_npmUser":{"name":"anhnth24","email":"anhnthntl@gmail.com"},"repository":{"url":"git+https://github.com/anhnth24/harnessctl.git","type":"git"},"_npmVersion":"10.9.2","description":"Standalone enforcement overlay for Claude Code hooks — audit-grade JSONL, deterministic gates, profile-based policy","directories":{},"_nodeVersion":"22.14.0","dependencies":{"ajv":"^8.0.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/harnessctl_0.5.1_1778580090467_0.8197875210257735","host":"s3://npm-registry-packages-npm-production"}},"0.5.2":{"name":"@anhnth24/harnessctl","version":"0.5.2","description":"Standalone enforcement overlay for Claude Code hooks — audit-grade JSONL, deterministic gates, profile-based policy","license":"MIT","author":{"name":"anhnth","email":"anhnthntl@gmail.com"},"keywords":["claude-code","claude","hooks","enforcement","audit","policy","lint","ai-coding","agent-guardrails"],"engines":{"node":">=18"},"publishConfig":{"access":"public"},"repository":{"type":"git","url":"git+https://github.com/anhnth24/harnessctl.git"},"homepage":"https://github.com/anhnth24/harnessctl#readme","bugs":{"url":"https://github.com/anhnth24/harnessctl/issues"},"bin":{"harnessctl":"bin/harnessctl.cjs","harn":"bin/harnessctl.cjs","harness-dispatcher":"bin/harness-dispatcher.cjs"},"scripts":{"test":"node --test tests/unit/*.test.cjs tests/integration/*.test.cjs","test:unit":"node --test tests/unit/*.test.cjs","test:integration":"node --test tests/integration/*.test.cjs","test:perf":"node tests/perf/baseline-and-delta.cjs"},"dependencies":{"ajv":"^8.0.0"},"_id":"@anhnth24/harnessctl@0.5.2","gitHead":"0d65bc36903430c328445f7ffbf1ed42bdaf2c4e","_nodeVersion":"22.14.0","_npmVersion":"10.9.2","dist":{"integrity":"sha512-yx6EdUB9A+7tpkwJWm+TmMdN0xJc5/+Rnsy279qVPcDOqfWg5Ps+Zme5l3UUi1vROgdcZfKJ03NQ3eyInPJtUA==","shasum":"2982f359b5edf32ac7d4fcb555a002ed75d1e5fe","tarball":"https://registry.npmjs.org/@anhnth24/harnessctl/-/harnessctl-0.5.2.tgz","fileCount":88,"unpackedSize":393134,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIEVlWfTOXmmm6VI8aO44VIEJxYXcffhDW9Pu4V2wDF0nAiAU2JKFoyjUtddL3UPTpVbD3F35plhrOXjpkXTwV76afw=="}]},"_npmUser":{"name":"anhnth24","email":"anhnthntl@gmail.com"},"directories":{},"maintainers":[{"name":"anhnth24","email":"anhnthntl@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/harnessctl_0.5.2_1778603364668_0.7965579007870165"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-10T19:34:47.828Z","modified":"2026-05-12T16:29:24.971Z","0.3.2":"2026-05-10T19:34:48.114Z","0.4.0":"2026-05-11T02:27:28.759Z","0.4.1":"2026-05-11T03:34:38.869Z","0.4.2":"2026-05-11T03:35:42.909Z","0.4.3":"2026-05-11T03:39:47.013Z","0.4.4":"2026-05-11T08:35:20.068Z","0.4.5":"2026-05-11T08:36:02.052Z","0.4.6":"2026-05-11T08:38:01.797Z","0.4.7":"2026-05-11T08:42:29.461Z","0.4.8":"2026-05-11T08:45:39.318Z","0.4.9":"2026-05-11T09:32:31.723Z","0.4.10":"2026-05-11T09:35:22.685Z","0.4.11":"2026-05-11T09:50:03.418Z","0.5.0":"2026-05-12T01:10:18.677Z","0.5.1":"2026-05-12T10:01:30.591Z","0.5.2":"2026-05-12T16:29:24.845Z"},"bugs":{"url":"https://github.com/anhnth24/harnessctl/issues"},"author":{"name":"anhnth","email":"anhnthntl@gmail.com"},"license":"MIT","homepage":"https://github.com/anhnth24/harnessctl#readme","keywords":["claude-code","claude","hooks","enforcement","audit","policy","lint","ai-coding","agent-guardrails"],"repository":{"type":"git","url":"git+https://github.com/anhnth24/harnessctl.git"},"description":"Standalone enforcement overlay for Claude Code hooks — audit-grade JSONL, deterministic gates, profile-based policy","maintainers":[{"name":"anhnth24","email":"anhnthntl@gmail.com"}],"readme":"# harnessctl v0.3.2\r\n\r\nStandalone enforcement overlay for Claude Code hooks. Nudges, warns, or blocks on context,\r\nspec, and test violations — without forking or modifying Claude Code itself.\r\n\r\n---\r\n\r\n## What's new in v0.3.2\r\n\r\n1. **Per-event audit JSONL with passthrough fields.** Every gate decision is captured in\r\n   append-only JSONL with `gate`, `pattern_id`, `language`, `framework`, `snippet`. Auditable,\r\n   greppable, queryable. Redaction is the gate's responsibility; the logger never auto-redacts.\r\n\r\n2. **Config-as-policy.** `.harness/config.json` lets teams cap gate severity, disable per-gate,\r\n   or skip paths — without forking the harness. Profiles (`strict`/`balanced`/`lenient`) ship\r\n   preset policies.\r\n\r\n3. **Doctor visibility.** `harnessctl doctor` shows effective gate config with source attribution\r\n   (`user-config` | `profile:<name>` | `gate-default`) so teams see exactly why a gate behaves\r\n   as it does.\r\n\r\n4. **Three opinionated gates** demonstrate the spine: `hardcoded-path-blocker`,\r\n   `test-meaningfulness-regex`, `comment-quality-heuristic`. The audit platform is the point.\r\n\r\n> **Migration note:** New gates default to `nudge` in the `balanced` profile. Users with no\r\n> `.harness/config.json` will see new annotations on their next session. Override any gate with\r\n> `.harness/config.json` or set profile to `lenient`.\r\n\r\n### Policy-as-config example\r\n\r\nThe same gate can run at different severities across two team configs in the same monorepo:\r\n\r\n```jsonc\r\n// team-backend/.harness/config.json — strict on test quality\r\n{ \"hooks\": { \"test-meaningfulness-regex\": { \"enabled\": true, \"mode\": \"warn\" } } }\r\n\r\n// team-frontend/.harness/config.json — quieter while bootstrapping\r\n{ \"hooks\": { \"test-meaningfulness-regex\": { \"enabled\": true, \"mode\": \"nudge\" } } }\r\n```\r\n\r\nSame gate, different cap, no fork. `harnessctl doctor` shows which team member's config is active.\r\n\r\n---\r\n\r\n## What it is\r\n\r\nharnessctl is a **standalone hook overlay** that plugs into Claude Code's\r\n[hook system](https://docs.anthropic.com/claude-code/hooks). It registers a single dispatcher\r\n(`bin/harness-dispatcher.cjs`) for all hook events, routes each event in-process to one or more\r\n_gates_, and returns a structured CC-protocol response (pass / nudge / warn / block).\r\n\r\nEight enforcement gates ship in v0.3.2:\r\n\r\n| Gate | Hook event | What it checks | Default (balanced) |\r\n|---|---|---|---|\r\n| `context-completeness` | `UserPromptSubmit` | Prompt has ≥2 context signals (file path, identifier, plan ref, git ref, quoted error) | `warn` |\r\n| `spec-discipline` | `PreToolUse` (Edit/Write) | Recent messages reference `docs/` or a `plans/*.md` before editing `src/` | `warn` |\r\n| `test-first` | `PreToolUse` (Edit/Write) | A test file exists for the source file being edited | `warn` |\r\n| `plan-adherence` | `PreToolUse` (Edit/Write) | File is declared in the active plan phase | `warn` |\r\n| `edit-loop` | `PreToolUse` (Edit/Write) | Detects repeated edits to same file within session window | `warn` |\r\n| `hardcoded-path-blocker` | `PreToolUse` (Edit/Write) | Absolute user-specific paths written into source | `warn` |\r\n| `test-meaningfulness-regex` | `PreToolUse` (Edit/Write) | Weak assertions in Jest/pytest test files | `nudge` |\r\n| `comment-quality-heuristic` | `PreToolUse` (Edit/Write) | TODO/FIXME/HACK/XXX inline comments | `nudge` |\r\n\r\nAll gates **fail open**: a crash or timeout logs an `error` audit line and passes through silently.\r\nError counts surface at session start and via `harnessctl stats --errors`.\r\n\r\n---\r\n\r\n## Why (vs CK alone)\r\n\r\nClaudeKit provides slash-command workflows and skill orchestration. harnessctl adds\r\n**per-event enforcement** at the hook layer — before CC acts on a prompt or edit. It reads\r\nCK metadata from the filesystem only; no CK import, no fork, no coupling.\r\n\r\nUse both together: CK for planning and orchestration, harnessctl for discipline.\r\n\r\n---\r\n\r\n## Requirements\r\n\r\n- **Node.js** >= 18 (uses `node:test`, `fs.copyFileSync`, `crypto`)\r\n- **Claude Code** >= 1.x (smoke-tested on Claude Code as of 2026-05-10; see §Limitations #4)\r\n- **Platforms**: Windows, macOS, Linux\r\n\r\n---\r\n\r\n## Install\r\n\r\n```bash\r\n# 1. Clone or copy this repo into any directory on your PATH or project tooling dir\r\ngit clone <repo-url> harnessctl\r\ncd harnessctl\r\n\r\n# 2. Run init from your project root (the directory with .claude/settings.json)\r\nnode /path/to/harnessctl/bin/harnessctl.cjs init --profile=balanced\r\n\r\n# 3. Restart Claude Code — hooks are now active\r\n```\r\n\r\n`init` is idempotent: re-running with the same profile skips existing config. Use `--force`\r\nto overwrite. Use `--dry-run` to preview without writing.\r\n\r\n**Profiles**: `strict` | `balanced` (default) | `lenient`\r\n\r\n---\r\n\r\n## Profiles\r\n\r\nThree preset profiles ship in `lib/profiles/`. They control the `mode` for each gate:\r\n\r\n| Profile | context-completeness | spec-discipline | test-first | plan-adherence | edit-loop | hardcoded-path-blocker | test-meaningfulness-regex | comment-quality-heuristic |\r\n|---|---|---|---|---|---|---|---|---|\r\n| `strict` | `block` | `block` | `block` | `block` | `block` | `block` | `warn` | `nudge` |\r\n| `balanced` | `warn` | `warn` | `warn` | `warn` | `warn` | `warn` | `nudge` | `nudge` |\r\n| `lenient` | `off` | `off` | `off` | `disabled` | `warn` | `off` | `off` | `off` |\r\n\r\nModes:\r\n- `block` — CC denies the tool use or adds a correction notice; the user must fix and retry\r\n- `warn` — writes a warning to stderr; CC continues normally\r\n- `off` — gate is skipped entirely (bypass logged to audit)\r\n\r\nTo customize per-gate, edit `.harness/config.json` directly after init:\r\n\r\n```json\r\n{\r\n  \"schema_version\": 1,\r\n  \"profile\": \"balanced\",\r\n  \"hooks\": {\r\n    \"context-completeness\":      { \"enabled\": true, \"mode\": \"warn\" },\r\n    \"spec-discipline\":           { \"enabled\": true, \"mode\": \"block\" },\r\n    \"test-first\":                { \"enabled\": true, \"mode\": \"warn\" },\r\n    \"hardcoded-path-blocker\":    { \"enabled\": true, \"mode\": \"warn\",  \"skip_paths\": [\"docs/\", \".github/\"] },\r\n    \"test-meaningfulness-regex\": { \"enabled\": true, \"mode\": \"nudge\" },\r\n    \"comment-quality-heuristic\": { \"enabled\": true, \"mode\": \"off\" }\r\n  },\r\n  \"overrides\": {}\r\n}\r\n```\r\n\r\n`skip_paths` uses **prefix match** only in v0.3.2 (e.g. `\"docs/\"` matches any path starting with\r\n`docs/`). Suffix (`.snap`), exact-filename (`package-lock.json`), and internal-slash prefix\r\n(`.github/workflows`) are also supported. Glob characters (`*`, `?`) are not supported and\r\nemit a warning at load time.\r\n\r\n---\r\n\r\n## Hooks reference\r\n\r\n### `context-completeness` (UserPromptSubmit)\r\n\r\nScores the submitted prompt for context signals:\r\n\r\n| Signal | Example |\r\n|---|---|\r\n| `file_path` | `src/parser.ts`, `README.md` |\r\n| `code_identifier` | `parseToken()`, `user_id` |\r\n| `plan_reference` | `plans/my-feature/plan.md` |\r\n| `git_ref` | `abc1234`, `feature/auth` |\r\n| `quoted_error` | `` `TypeError: cannot read properties` `` (≥20 chars) |\r\n\r\n**Decision matrix**:\r\n\r\n```\r\nsignals >= 2  → pass\r\nsignals == 1  → nudge (inject additionalContext hint)\r\nsignals == 0  → warn (balanced) | block (strict)\r\nsame prompt submitted >2 times → escalate to block regardless of mode\r\n```\r\n\r\n**Opt-out**: include `[no-context]` anywhere in the prompt.\r\n\r\n**Example nudge message**:\r\n> Vague prompt detected. Add: a file path, an identifier, or a plan reference.\r\n\r\n---\r\n\r\n### `spec-discipline` (PreToolUse: Edit, Write, MultiEdit)\r\n\r\nFires when editing `src/**` or top-level `.ts/.cjs/.js` files (excluding test files).\r\nScans the last 5 user messages for a reference to `docs/` or `plans/*.md`.\r\n\r\n**Example warn message**:\r\n> No spec reference found before editing src/parser.ts. Add a docs/ or plans/*.md reference,\r\n> or include [no-spec] to opt out.\r\n\r\n**Opt-out**: include `[no-spec]` in a recent message.\r\n\r\n---\r\n\r\n### `test-first` (PreToolUse: Edit, Write, MultiEdit)\r\n\r\nFires when editing `src/**` non-test files. Checks the filesystem for a sibling\r\n`*.test.*` or `*.spec.*` file matching the target basename.\r\n\r\n**Example warn message**:\r\n> Create test for `parser` first\r\n\r\n**Opt-out**: include `[no-test]` in a recent message (checked via `transcript_path`).\r\n\r\n---\r\n\r\n### `plan-adherence` (PreToolUse: Edit, Write, MultiEdit)\r\n\r\nFires when editing any file while an active plan (`status: in-progress`) exists in `plans/`.\r\nChecks whether the target file is declared in the active phase's `create | modify | delete`\r\nfile list. Files declared under `read` are NOT gated (read-only access is unrestricted).\r\n\r\n**Gate behavior**:\r\n\r\n| Condition | Decision |\r\n|-----------|----------|\r\n| No `plan.json` found / no in-progress plan | `pass` (fail-open) |\r\n| No in-progress phase in the active plan | `pass` |\r\n| File is in the active phase file set | `pass` |\r\n| File NOT in set, `balanced` profile | `nudge` (warn in user turn) |\r\n| File NOT in set, `strict` profile | `block` (deny tool use) |\r\n| Gate disabled (`lenient` profile) | `pass` |\r\n\r\n**Profile defaults**:\r\n\r\n| Profile | plan-adherence |\r\n|---------|----------------|\r\n| `strict` | `block` |\r\n| `balanced` | `warn` |\r\n| `lenient` | `disabled` |\r\n\r\n**Opt-out**: edit phase markdown to add the file, re-run `harnessctl plan-init`, and commit.\r\nAd-hoc bypass: not implemented in v0.3 — use `mode: \"warn\"` in `.harness/config.json` temporarily.\r\n\r\n**Path normalization**: handles Windows backslash vs forward slash. Compares case-sensitively\r\n(no lowercase conversion — would break WSL case-sensitive mounts).\r\n\r\nSee [docs/plan-json-schema.md](docs/plan-json-schema.md) for full schema and author workflow.\r\n\r\n---\r\n\r\n## `plan-init` command (v0.3)\r\n\r\n```bash\r\nnode bin/harnessctl.cjs plan-init <plan-dir> [--check] [--force]\r\n```\r\n\r\nDerives `plan.json` from all `phase-*.md` files in `<plan-dir>` by parsing the\r\n`## Related Code Files` section in each phase file.\r\n\r\n**Usage**:\r\n\r\n```bash\r\n# Generate (or regenerate) plan.json\r\nharnessctl plan-init plans/my-feature\r\n\r\n# Dry-run: check if plan.json is in sync (exit 1 if stale — use in CI)\r\nharnessctl plan-init plans/my-feature --check\r\n```\r\n\r\n**Idempotent**: re-running on unchanged markdown produces byte-identical `plan.json`.\r\n\r\n**Ambiguous paths**: bullet lines with no path-like characters (`/`, `\\`, or file extension)\r\nemit a warning to stderr and are skipped. Fix the markdown and re-run.\r\n\r\n**Phase markdown format** (in each `phase-XX-name.md`):\r\n\r\n```markdown\r\n## Related Code Files\r\n\r\n### Create\r\n- lib/new-file.cjs\r\n\r\n### Modify\r\n- lib/existing.cjs\r\n\r\n### Delete\r\n- lib/old-file.cjs\r\n\r\n### Read\r\n- docs/reference.md\r\n```\r\n\r\nSee [docs/plan-json-schema.md](docs/plan-json-schema.md) for full schema documentation.\r\n\r\n---\r\n\r\n## `replay` command (v0.3)\r\n\r\n```bash\r\nnode bin/harnessctl.cjs replay <session_id> [--format=json] [--out=<path>] [--project=<slug>]\r\n```\r\n\r\nReads a Claude Code session transcript and cross-references the `.harness/audit/` log to\r\nproduce a structured JSON file — useful for post-mortems, compliance reviews, and debugging.\r\n\r\n**Options**:\r\n\r\n| Flag | Default | Description |\r\n|---|---|---|\r\n| `--format=json` | `json` | Output format. Only `json` supported in v0.3. |\r\n| `--out=<path>` | `./replay-{session_id}.json` | Output file path. |\r\n| `--project=<slug>` | derived from CWD | CC project slug (folder name under `~/.claude/projects/`). |\r\n\r\n**Format deferral**: `--format=html` and `--format=md` are reserved for v0.3.5 (HTML 3-panel renderer and Markdown narrative). Using them in v0.3 prints a deferral message and exits 1.\r\n\r\n### JSON output schema (v0.3-stable)\r\n\r\n```json\r\n{\r\n  \"schema_version\": 1,\r\n  \"session_id\": \"abc123...\",\r\n  \"transcript_path\": \"/Users/you/.claude/projects/my-project/abc123.jsonl\",\r\n  \"generated_at\": \"2026-05-10T12:00:00.000Z\",\r\n  \"turns\": [\r\n    {\r\n      \"ts\": \"2026-05-10T12:00:00.000Z\",\r\n      \"user_prompt\": \"Please edit src/index.ts to add a logger.\",\r\n      \"tool_calls\": [\r\n        { \"tool\": \"Edit\", \"tool_input\": { \"file_path\": \"src/index.ts\", \"...\" : \"...\" } }\r\n      ],\r\n      \"decisions\": [\r\n        { \"gate\": \"spec-discipline\", \"decision\": \"nudge\", \"audit_ts\": \"2026-05-10T12:00:01.000Z\" },\r\n        { \"gate\": \"plan-adherence\", \"decision\": \"pass\",  \"audit_ts\": \"2026-05-10T12:00:01.100Z\" }\r\n      ]\r\n    }\r\n  ]\r\n}\r\n```\r\n\r\n**Decision matching**: each turn's `decisions` array contains audit lines whose `ts` falls\r\nwithin ±5 seconds of the turn's timestamp and share the same `session_id`. If no audit lines\r\nmatch a turn, `decisions` is `[]` and `audit_match: null` is set on the turn.\r\nPre-harness sessions (no audit log) will have `decisions: []` for all turns — v0.3 does not\r\nre-run gates on historical transcripts. In-process fallback is deferred to v0.3.5.\r\n\r\n> **Security note**: replay output may contain the full text of user prompts. Treat output\r\n> files as sensitive — equivalent to your audit log.\r\n\r\n### Post-mortem workflow\r\n\r\n```bash\r\n# 1. Find session_id from last session\r\nharnessctl stats --json | jq '.sessions[-1].session_id'\r\n\r\n# 2. Generate replay JSON\r\nharnessctl replay <session_id> --out ./postmortem.json\r\n\r\n# 3. Inspect with jq\r\njq '.turns[] | select(.decisions | length > 0)' postmortem.json\r\n\r\n# 4. HTML rendering (v0.3.5)\r\n# harnessctl replay <session_id> --format=html --out ./postmortem.html\r\n```\r\n\r\n### v0.3.5 HTML preview note\r\n\r\nv0.3.5 will add `--format=html` (3-panel single-file renderer: timeline | turn detail |\r\naudit diff) and `--format=md` (Markdown narrative). The v0.3 JSON output is the stable\r\ndata layer both renderers will consume. Pin tooling against `schema_version: 1`.\r\n\r\n---\r\n\r\n## `/harness` toggle\r\n\r\nInside a Claude Code session, use the slash command to toggle gates on/off for the current\r\nsession (does not persist across restarts):\r\n\r\n```\r\n/harness off context-completeness   # disable for this session only\r\n/harness on  spec-discipline        # re-enable\r\n/harness status                     # show current toggles\r\n```\r\n\r\nImplemented via `hooks/harness-toggle.cjs`. Toggle state is stored in\r\n`.harness/state/{session_id}.json` and isolated per session.\r\n\r\n---\r\n\r\n## `stats` command\r\n\r\n```bash\r\nnode bin/harnessctl.cjs stats [--since=7d] [--errors] [--perf] [--hot-files] [--acceptance]\r\n```\r\n\r\n| Flag | Description |\r\n|---|---|\r\n| `--since=7d` | Time window: `7d`, `30d`, `all` (default: `7d`) |\r\n| `--errors` | Show fail-open event count and details |\r\n| `--perf` | Show p50/p99 latency per gate |\r\n| `--hot-files` | Top 10 most-edited file paths (from audit) |\r\n| `--acceptance` | Block acceptance rate (blocks not bypassed / total) |\r\n\r\nAll metrics are derived from `.harness/audit/{date}.jsonl` — no separate counter storage.\r\n\r\n## `tune` command (v0.4 — BETA)\r\n\r\nAnalyzes recent audit JSONL and **suggests** `.harness/config.json` deltas based on\r\nacceptance/bypass signals. Print-only — never writes config.\r\n\r\n```bash\r\nnode bin/harnessctl.cjs tune --experimental-thresholds [--since=30d] [--min-fires=50] [--json]\r\n```\r\n\r\n> ⚠ **BETA — uncalibrated thresholds.** Default thresholds (`bypass>=80%`, `accept>=90%`,\r\n> `fires<5`) are unvalidated guesses. The command requires `--experimental-thresholds` to\r\n> run. Treat output as a starting point for review, not as automatic policy.\r\n\r\n| Flag | Description |\r\n|---|---|\r\n| `--experimental-thresholds` | **Required.** Opt-in to BETA thresholds |\r\n| `--since=30d` | Window: `7d`, `30d`, `all` (default `30d`) |\r\n| `--min-fires=50` | Suppress suggestions for gates with fewer fires (default `50`) |\r\n| `--json` | Machine-readable JSON output |\r\n\r\n### Suggestion rules (v1)\r\n\r\n| Condition | Suggestion |\r\n|---|---|\r\n| `error_rate >= 5%` AND `fires >= 10` | Flag for investigation (no mode change) |\r\n| `fires < 5` | Flag \"below noise floor — review value\" |\r\n| `bypass_rate >= 80%` AND blocks ≥ 10 AND `fires >= min-fires` | Downgrade `block→warn`, `warn→nudge`, `nudge→off` |\r\n| `accept_rate >= 90%` AND mode is `nudge`/`warn` AND `fires >= min-fires` | Upgrade `nudge→warn`, `warn→block` |\r\n\r\n**Bypass detection:** `block` decision followed by a `pass` on the *same* `target_path`,\r\n*same* `session_id`, within **5 minutes**. Disabled-gate bypass events (`reason: mode_off |\r\nuser_toggled_off`) are filtered out — they are NOT policy bypasses.\r\n\r\nRead-only and offline. The command never writes config, never shells out, never echoes\r\naudit `snippet` content.\r\n\r\n## Plugin SDK (v0.4.1 — experimental)\r\n\r\nCustom team gates without forking. Drop a `.cjs` file under `.harness/gates/`, opt it\r\nin via `.harness/config.json`, done. Full reference: [`docs/plugin-sdk.md`](docs/plugin-sdk.md).\r\n\r\n> **Experimental.** Contract `schemaVersion: 1` may change before v0.5 freezes it.\r\n> Plugins are full-trust code — the opt-in flag is the trust boundary, not a sandbox.\r\n\r\n### Quick start (reference plugin)\r\n\r\n```bash\r\ncp examples/plugins/no-console-in-api.cjs .harness/gates/\r\n```\r\n\r\nEdit `.harness/config.json`:\r\n\r\n```json\r\n{\r\n  \"plugins\": {\r\n    \"totalBudgetMs\": 100,\r\n    \"gates\": {\r\n      \"no-console-in-api\": { \"enabled\": true, \"mode\": \"warn\", \"schemaVersion\": 1 }\r\n    }\r\n  }\r\n}\r\n```\r\n\r\nRun `harn doctor` to verify it loaded. The plugin warns when Edit/Write/MultiEdit\r\nintroduces `console.log(` under `src/api/`.\r\n\r\n### Author your own\r\n\r\n```js\r\n// .harness/gates/<name>.cjs\r\nmodule.exports = {\r\n  name:          'no-console-in-api',\r\n  event:         'PreToolUse',\r\n  defaultMode:   'warn',\r\n  schemaVersion: 1,\r\n  async run(payload, ctx) {\r\n    // ctx = { sessionId, hookName, mode, payload, pluginConfig, pluginName }\r\n    // Intentionally narrowed: NO root, NO full config, NO state.\r\n    return { decision: 'pass' };\r\n  },\r\n};\r\n```\r\n\r\n### Runtime guarantees\r\n\r\n- Plugin name collisions with built-ins are **rejected** (no silent replace).\r\n- Async timeouts and throws are isolated (`Promise.race` 50ms + try/catch).\r\n- **Sync `while(true){}` will hang CC** — documented limitation; v0.5 adds worker_threads.\r\n- Per-event `plugins.totalBudgetMs` cap short-circuits late plugins on chain explosion.\r\n- `harn init --force` preserves the `plugins` section across re-runs.\r\n\r\n---\r\n\r\n## Uninstall\r\n\r\n```bash\r\nnode bin/harnessctl.cjs uninstall\r\n```\r\n\r\nRemoves harness hook entries from `.claude/settings.json`. The `.harness/` directory and\r\nall audit/state data are preserved. Re-run `init` to re-enable.\r\n\r\n---\r\n\r\n## FAQ\r\n\r\n**Q: Does harnessctl modify Claude Code itself?**\r\nNo. It registers hooks via `.claude/settings.json`. Claude Code invokes the dispatcher as a\r\nsubprocess on each hook event. No CC source is modified.\r\n\r\n**Q: Will it break my existing hooks?**\r\nNo. `installHooks` merges non-destructively — existing hook entries for other commands are\r\npreserved. Harness entries are identified by their command string and never duplicated.\r\n\r\n**Q: What happens if the dispatcher crashes?**\r\nIt exits 0 (fail-open). The crash is recorded in `.harness/audit/` with `decision:error`.\r\nAt the next `SessionStart`, harnessctl injects a 1-line warning with the 24h error count.\r\nRun `harnessctl stats --errors` for details.\r\n\r\n**Q: Can I use a custom profile?**\r\nNot via a CLI flag. Edit `.harness/config.json` directly after init. The `profile` field\r\nis informational only once you've customized gate modes.\r\n\r\n**Q: Does it work offline / without internet?**\r\nYes. All gates are heuristic-only in v0.2 — no LLM calls, no network I/O.\r\n\r\n**Q: How do I report bugs or false positives?**\r\nFile an issue or edit `.harness/config.json` to set the noisy gate to `\"mode\": \"warn\"`.\r\nRun `harnessctl stats --acceptance` after a week to measure false-positive impact.\r\n\r\n---\r\n\r\n## §Limitations\r\n\r\nAll known limitations of v0.2, enumerated explicitly:\r\n\r\n1. **Heuristic false positives** — `context-completeness` and `spec-discipline` are\r\n   regex-based heuristics; expect ~10-20% false-positive rate. Default `balanced` profile\r\n   uses `warn` mode (not block) to mitigate user friction.\r\n\r\n2. **Fail-open philosophy** — Gates that crash or time out pass through silently. The audit\r\n   records a `decision:error` line. Fail-open count is surfaced via `harnessctl stats --errors`\r\n   and injected as a 1-line warning at `SessionStart` if errors occurred in the last 24h.\r\n   Use this to detect gate regressions early.\r\n\r\n3. **No-LLM caveat** — All signals in v0.2 are heuristic (regex + filesystem). An\r\n   LLM-based classifier for vague-prompt detection is deferred to v0.3. Expect higher FP/FN\r\n   rates on ambiguous prompts compared to a semantic approach.\r\n\r\n4. **Claude Code version drift** — harnessctl was smoke-tested against Claude Code as of\r\n   2026-05-10. Newer CC versions may add hook payload fields not handled here (e.g., new\r\n   `source` enum values for `SessionStart`). Unknown `source` values default to startup\r\n   behavior; other new fields are ignored. Fail-open applies if the dispatcher crashes on\r\n   unexpected input.\r\n\r\n5. **Single-instance state isolation** — Each CC session has its own state file\r\n   (`.harness/state/{session_id}.json`). Two terminal windows in the same repo do **not**\r\n   share toggles. Running `/harness off context-completeness` in session A leaves session B\r\n   unaffected. This is intentional — per-session isolation prevents cross-session interference.\r\n\r\n6. **Windows path quirks** — Long paths, Unicode filenames, and locked files can cause\r\n   `fs` operations to fail. The dispatcher tolerates these via fail-open. The settings-writer\r\n   uses `copyFileSync + unlinkSync` (not `renameSync`) to avoid `EPERM` errors when\r\n   overwriting existing files on Windows. The mtime-cache invalidation in `test-discovery.cjs`\r\n   may behave differently on NTFS if two file writes occur within the same filesystem\r\n   timestamp granularity window.\r\n\r\n7. **Plan-adherence (v0.3)** — The plan-adherence gate is active as of v0.3. It requires\r\n   `plan.json` (generated by `harnessctl plan-init`) to function. Without a `plan.json` or\r\n   an in-progress plan, the gate passes through silently (fail-open). FP target is <5% over\r\n   1-week dogfood.\r\n\r\n8. **Edit-loop not enforced** — The edit-loop counter gate was replaced by passive audit\r\n   + `harnessctl stats --hot-files`. Files with high edit counts surface via `--hot-files`\r\n   for manual review. No active block is issued for repeated edits to the same file in v0.2.\r\n\r\n---\r\n\r\n## Standalone, no CK fork\r\n\r\nharnessctl is an independent tool. It does **not** fork, patch, or import ClaudeKit. It reads\r\nCK skill metadata from the filesystem only (e.g., `~/.claude/skills/*/SKILL.md`) for\r\ninformational purposes. Zero `claudekit` or `@ck/` imports across the entire codebase.\r\n\r\nA commercial preset pack (SOC2/ISO compliance profiles) is reserved for a future SKU. The\r\ncurrent v0.2 release is internal-only. The audit schema (`v:1`) and profile JSON format are\r\nkept stable and open to preserve that optionality.\r\n\r\n---\r\n\r\n## `doctor` command (v0.3)\r\n\r\n```bash\r\nnode bin/harnessctl.cjs doctor\r\n```\r\n\r\nRuns 6 health checks and prints one line per check (✓ green / ✗ red / ! yellow).\r\n**Print-only**: never modifies `settings.json`. Exit 0 if all green; exit 1 if any red.\r\n\r\n| # | Check | Detects |\r\n|---|---|---|\r\n| 1 | Dispatcher binary exists | Missing `bin/harness-dispatcher.cjs` |\r\n| 2 | `.harness/audit/` writable | Permissions preventing audit writes |\r\n| 3 | `.harness/config.json` schema valid | Invalid or missing config |\r\n| 4 | Profile JSONs parseable | Corrupt profile JSON files |\r\n| 5 | settings.json harness entries present | Entries wiped by CC init/update or other tool |\r\n| 6 | Harness regex matchers compile | Invalid regex in `_harness`-marked entries |\r\n\r\nOn red drift, prints: `Run 'harnessctl init' to re-merge harness entries.`\r\n\r\n`--fix` auto-heal is deferred to v0.3.5 (requires file-lock story). Passing `--fix` prints\r\na deferral message and exits 1.\r\n\r\n### Drift detection at SessionStart\r\n\r\nEvery `SessionStart` event runs a fast-path drift check (<50ms p99). If harness entries are\r\nmissing or corrupted, injects a one-line warning into CC's `additionalContext`:\r\n\r\n```\r\n⚠ harness drift detected: run 'harnessctl doctor' for details.\r\n```\r\n\r\n**Warn-once per session**: state flag at `.harness/session-state/{session_id}.json`\r\n`{ drift_warned: true }` suppresses repeat warnings within the same CC session.\r\n\r\n---\r\n\r\n## `_harness` marker convention (v0.3)\r\n\r\nEvery harness-injected hook entry in `.claude/settings.json` carries a `_harness` field:\r\n\r\n```json\r\n{\r\n  \"command\": \"node \\\"/path/to/bin/harness-dispatcher.cjs\\\"\",\r\n  \"_harness\": \"0.3.0\"\r\n}\r\n```\r\n\r\nThe `_harness` field stores the harness semver that wrote the entry. It serves as the\r\nidentity marker for drift detection — doctor identifies \"missing\" (no entry), \"corrupted\"\r\n(entry present but marker stripped), and \"foreign\" (marker on non-dispatcher command).\r\n\r\n**CC compatibility**: Empirically confirmed on CC v2.1.133 — CC passes through unknown JSON\r\nkeys verbatim. The marker survives `claude config list`, `claude -p`, and normal CC ops.\r\n\r\n---\r\n\r\n## `_order` convention (v0.3, documentation only)\r\n\r\nTeams may add `_order: N` to hook entries to communicate intended execution order across\r\nmultiple tools sharing the same event. Recommended ranges:\r\n\r\n| Range | Purpose |\r\n|---|---|\r\n| 0–29 | System / harness hooks (harness uses this range) |\r\n| 30–69 | User / team hooks |\r\n| 70–99 | Cleanup / post-processing hooks |\r\n\r\n`_order` is **not enforced by harnessctl code**. CC respects array insertion order natively.\r\nThis is a README convention for human readability and multi-tool coordination.\r\n\r\n---\r\n\r\n## Min CC version (v0.3)\r\n\r\n**Minimum tested: Claude Code v2.1.133** (smoke-tested 2026-05-10).\r\n\r\nEmpirical findings on v2.1.133:\r\n- `_harness` marker survives all normal CC ops (confirmed via researcher-260510-1755)\r\n- Hook entries accumulate within arrays (no replacement by CC update)\r\n- Cross-scope hooks (user + project) fire sequentially, in-order\r\n\r\nNewer CC versions may add hook payload fields not handled here. Unknown fields are ignored;\r\nunknown `source` values on `SessionStart` default to startup behavior. Fail-open applies on\r\ndispatcher crash.\r\n\r\nDoctor v0.3.5 will add CC version mismatch detection on SessionStart.\r\n\r\n---\r\n\r\n## Known limitations (v0.3 additions)\r\n\r\n9. **settings.json write race vs concurrent CC** — The atomic writer uses\r\n   `writeFileSync(tmp) → copyFileSync(tmp, target) → unlinkSync(tmp)` (not `renameSync`,\r\n   which throws EPERM on Windows when the target exists). This is best-effort, not a hard\r\n   lock. If CC is writing `settings.json` concurrently, a race window exists. Mitigation:\r\n   run `harnessctl init` only when Claude Code is not active. Auto-heal (`--fix`) is\r\n   deferred to v0.3.5 until a file-lock acquisition story exists.\r\n\r\n10. **Doctor print-only in v0.3** — `harnessctl doctor --fix` is deferred to v0.3.5.\r\n    On detected drift, run `harnessctl init` to re-merge. Doctor only reports; it never\r\n    writes to `settings.json` in v0.3.\r\n\r\n---\r\n\r\n## Edit-loop gate (v0.3 M2)\r\n\r\nThe `edit-loop` gate fires on `PreToolUse` for `Edit`, `Write`, and `MultiEdit` tools.\r\nIt counts how many times the same file has been edited within a rolling window — purely by\r\nreading the append-only audit JSONL. No state files are written; the gate is race-free.\r\n\r\n### Behavior\r\n\r\n1. On each edit-type tool invocation, the gate reads audit JSONL for the target file within\r\n   the configured `window_minutes` window (capped at 1000 events / scan for performance).\r\n2. If `edit_count > threshold`, it also scans the same window for any `Bash` tool event\r\n   whose `command` field matches a test-runner pattern\r\n   (`vitest`, `jest`, `pytest`, `cargo test`, `go test`, `mocha`, `playwright test`, `node --test`).\r\n3. Decision:\r\n   - `edit_count <= threshold` → **pass**\r\n   - `edit_count > threshold` AND test event found → **pass**\r\n   - `edit_count > threshold` AND no test event → **nudge** (warn mode) or **block** (block mode)\r\n\r\n### MultiEdit semantics\r\n\r\nOne `MultiEdit` invocation = one audit event, regardless of how many edits it contains.\r\nThreshold math applies to invocation count, not individual edit count within a MultiEdit batch.\r\n\r\n### Profile defaults\r\n\r\n| Profile  | mode  | threshold | window_minutes |\r\n|----------|-------|-----------|----------------|\r\n| strict   | block | 3         | 30             |\r\n| balanced | warn  | 5         | 60             |\r\n| lenient  | warn  | 10        | 120            |\r\n\r\n### Custom config\r\n\r\n```json\r\n{\r\n  \"hooks\": {\r\n    \"edit-loop\": {\r\n      \"enabled\": true,\r\n      \"mode\": \"warn\",\r\n      \"threshold\": 5,\r\n      \"window_minutes\": 60\r\n    }\r\n  }\r\n}\r\n```\r\n\r\n---\r\n\r\n## stats --hot-files and --perf\r\n\r\n```bash\r\nharnessctl stats --hot-files [--since=1h|30m|7d|all]\r\nharnessctl stats --perf [--since=7d]\r\n```\r\n\r\n`--hot-files` aggregates `target_path` from audit log; prints top 10 by edit count.\r\n\r\n`--perf` prints p50/p99 latency per gate from `duration_ms` field (recorded with\r\n`perf_hooks.performance.now()` precision).\r\n\r\nThe `--since` flag accepts:\r\n- `Nd` — N days (e.g. `7d`, `30d`)\r\n- `Nh` — N hours (e.g. `1h`, `6h`)\r\n- `Nm` — N minutes (e.g. `30m`, `90m`)\r\n- `all` — no cutoff\r\n","readmeFilename":"README.md"}