diff --git a/package-lock.json b/package-lock.json
deleted file mode 100644
index df9193a..0000000
--- a/package-lock.json
+++ /dev/null
@@ -1,853 +0,0 @@
-{
- "name": "ai-development",
- "lockfileVersion": 3,
- "requires": true,
- "packages": {
- "": {
- "devDependencies": {
- "markdownlint": "^0.41.0"
- }
- },
- "node_modules/@types/debug": {
- "version": "4.1.13",
- "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz",
- "integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@types/ms": "*"
- }
- },
- "node_modules/@types/katex": {
- "version": "0.16.8",
- "resolved": "https://registry.npmjs.org/@types/katex/-/katex-0.16.8.tgz",
- "integrity": "sha512-trgaNyfU+Xh2Tc+ABIb44a5AYUpicB3uwirOioeOkNPPbmgRNtcWyDeeFRzjPZENO9Vq8gvVqfhaaXWLlevVwg==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@types/ms": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz",
- "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@types/unist": {
- "version": "2.0.11",
- "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz",
- "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/ansi-regex": {
- "version": "6.2.2",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
- "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/chalk/ansi-regex?sponsor=1"
- }
- },
- "node_modules/character-entities": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz",
- "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==",
- "dev": true,
- "license": "MIT",
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/wooorm"
- }
- },
- "node_modules/character-entities-legacy": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz",
- "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==",
- "dev": true,
- "license": "MIT",
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/wooorm"
- }
- },
- "node_modules/character-reference-invalid": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz",
- "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==",
- "dev": true,
- "license": "MIT",
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/wooorm"
- }
- },
- "node_modules/commander": {
- "version": "8.3.0",
- "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz",
- "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 12"
- }
- },
- "node_modules/debug": {
- "version": "4.4.3",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
- "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ms": "^2.1.3"
- },
- "engines": {
- "node": ">=6.0"
- },
- "peerDependenciesMeta": {
- "supports-color": {
- "optional": true
- }
- }
- },
- "node_modules/decode-named-character-reference": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.3.0.tgz",
- "integrity": "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "character-entities": "^2.0.0"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/wooorm"
- }
- },
- "node_modules/dequal": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
- "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/devlop": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz",
- "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "dequal": "^2.0.0"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/wooorm"
- }
- },
- "node_modules/get-east-asian-width": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz",
- "integrity": "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/is-alphabetical": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz",
- "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==",
- "dev": true,
- "license": "MIT",
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/wooorm"
- }
- },
- "node_modules/is-alphanumerical": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz",
- "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-alphabetical": "^2.0.0",
- "is-decimal": "^2.0.0"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/wooorm"
- }
- },
- "node_modules/is-decimal": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz",
- "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==",
- "dev": true,
- "license": "MIT",
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/wooorm"
- }
- },
- "node_modules/is-hexadecimal": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz",
- "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==",
- "dev": true,
- "license": "MIT",
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/wooorm"
- }
- },
- "node_modules/katex": {
- "version": "0.16.47",
- "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.47.tgz",
- "integrity": "sha512-Eeo8Ys1doU1z+x8AZsPpQu+p/QcZBI5PeOo7QGQdy2x2m0MU/hYagBbGOmXwr5KVbEfVuWv9LpnQWeehogurjg==",
- "dev": true,
- "funding": [
- "https://opencollective.com/katex",
- "https://github.com/sponsors/katex"
- ],
- "license": "MIT",
- "dependencies": {
- "commander": "^8.3.0"
- },
- "bin": {
- "katex": "cli.js"
- }
- },
- "node_modules/markdownlint": {
- "version": "0.41.0",
- "resolved": "https://registry.npmjs.org/markdownlint/-/markdownlint-0.41.0.tgz",
- "integrity": "sha512-xMUI3ChBuRuxuLF4ENvCZyS8z/+Jly1coUcZwErKLIB3sDj7ojpaTBa1e9YVPhSN4jGEIjYGQCldbTJS/hqS+A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "micromark": "4.0.2",
- "micromark-core-commonmark": "2.0.3",
- "micromark-extension-directive": "4.0.0",
- "micromark-extension-gfm-autolink-literal": "2.1.0",
- "micromark-extension-gfm-footnote": "2.1.0",
- "micromark-extension-gfm-table": "2.1.1",
- "micromark-extension-math": "3.1.0",
- "micromark-util-types": "2.0.2",
- "string-width": "8.2.1"
- },
- "engines": {
- "node": ">=22"
- },
- "funding": {
- "url": "https://github.com/sponsors/DavidAnson"
- }
- },
- "node_modules/micromark": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz",
- "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==",
- "dev": true,
- "funding": [
- {
- "type": "GitHub Sponsors",
- "url": "https://github.com/sponsors/unifiedjs"
- },
- {
- "type": "OpenCollective",
- "url": "https://opencollective.com/unified"
- }
- ],
- "license": "MIT",
- "dependencies": {
- "@types/debug": "^4.0.0",
- "debug": "^4.0.0",
- "decode-named-character-reference": "^1.0.0",
- "devlop": "^1.0.0",
- "micromark-core-commonmark": "^2.0.0",
- "micromark-factory-space": "^2.0.0",
- "micromark-util-character": "^2.0.0",
- "micromark-util-chunked": "^2.0.0",
- "micromark-util-combine-extensions": "^2.0.0",
- "micromark-util-decode-numeric-character-reference": "^2.0.0",
- "micromark-util-encode": "^2.0.0",
- "micromark-util-normalize-identifier": "^2.0.0",
- "micromark-util-resolve-all": "^2.0.0",
- "micromark-util-sanitize-uri": "^2.0.0",
- "micromark-util-subtokenize": "^2.0.0",
- "micromark-util-symbol": "^2.0.0",
- "micromark-util-types": "^2.0.0"
- }
- },
- "node_modules/micromark-core-commonmark": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz",
- "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==",
- "dev": true,
- "funding": [
- {
- "type": "GitHub Sponsors",
- "url": "https://github.com/sponsors/unifiedjs"
- },
- {
- "type": "OpenCollective",
- "url": "https://opencollective.com/unified"
- }
- ],
- "license": "MIT",
- "dependencies": {
- "decode-named-character-reference": "^1.0.0",
- "devlop": "^1.0.0",
- "micromark-factory-destination": "^2.0.0",
- "micromark-factory-label": "^2.0.0",
- "micromark-factory-space": "^2.0.0",
- "micromark-factory-title": "^2.0.0",
- "micromark-factory-whitespace": "^2.0.0",
- "micromark-util-character": "^2.0.0",
- "micromark-util-chunked": "^2.0.0",
- "micromark-util-classify-character": "^2.0.0",
- "micromark-util-html-tag-name": "^2.0.0",
- "micromark-util-normalize-identifier": "^2.0.0",
- "micromark-util-resolve-all": "^2.0.0",
- "micromark-util-subtokenize": "^2.0.0",
- "micromark-util-symbol": "^2.0.0",
- "micromark-util-types": "^2.0.0"
- }
- },
- "node_modules/micromark-extension-directive": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/micromark-extension-directive/-/micromark-extension-directive-4.0.0.tgz",
- "integrity": "sha512-/C2nqVmXXmiseSSuCdItCMho7ybwwop6RrrRPk0KbOHW21JKoCldC+8rFOaundDoRBUWBnJJcxeA/Kvi34WQXg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "devlop": "^1.0.0",
- "micromark-factory-space": "^2.0.0",
- "micromark-factory-whitespace": "^2.0.0",
- "micromark-util-character": "^2.0.0",
- "micromark-util-symbol": "^2.0.0",
- "micromark-util-types": "^2.0.0",
- "parse-entities": "^4.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/unified"
- }
- },
- "node_modules/micromark-extension-gfm-autolink-literal": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz",
- "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "micromark-util-character": "^2.0.0",
- "micromark-util-sanitize-uri": "^2.0.0",
- "micromark-util-symbol": "^2.0.0",
- "micromark-util-types": "^2.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/unified"
- }
- },
- "node_modules/micromark-extension-gfm-footnote": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz",
- "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "devlop": "^1.0.0",
- "micromark-core-commonmark": "^2.0.0",
- "micromark-factory-space": "^2.0.0",
- "micromark-util-character": "^2.0.0",
- "micromark-util-normalize-identifier": "^2.0.0",
- "micromark-util-sanitize-uri": "^2.0.0",
- "micromark-util-symbol": "^2.0.0",
- "micromark-util-types": "^2.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/unified"
- }
- },
- "node_modules/micromark-extension-gfm-table": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz",
- "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "devlop": "^1.0.0",
- "micromark-factory-space": "^2.0.0",
- "micromark-util-character": "^2.0.0",
- "micromark-util-symbol": "^2.0.0",
- "micromark-util-types": "^2.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/unified"
- }
- },
- "node_modules/micromark-extension-math": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/micromark-extension-math/-/micromark-extension-math-3.1.0.tgz",
- "integrity": "sha512-lvEqd+fHjATVs+2v/8kg9i5Q0AP2k85H0WUOwpIVvUML8BapsMvh1XAogmQjOCsLpoKRCVQqEkQBB3NhVBcsOg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@types/katex": "^0.16.0",
- "devlop": "^1.0.0",
- "katex": "^0.16.0",
- "micromark-factory-space": "^2.0.0",
- "micromark-util-character": "^2.0.0",
- "micromark-util-symbol": "^2.0.0",
- "micromark-util-types": "^2.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/unified"
- }
- },
- "node_modules/micromark-factory-destination": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz",
- "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==",
- "dev": true,
- "funding": [
- {
- "type": "GitHub Sponsors",
- "url": "https://github.com/sponsors/unifiedjs"
- },
- {
- "type": "OpenCollective",
- "url": "https://opencollective.com/unified"
- }
- ],
- "license": "MIT",
- "dependencies": {
- "micromark-util-character": "^2.0.0",
- "micromark-util-symbol": "^2.0.0",
- "micromark-util-types": "^2.0.0"
- }
- },
- "node_modules/micromark-factory-label": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz",
- "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==",
- "dev": true,
- "funding": [
- {
- "type": "GitHub Sponsors",
- "url": "https://github.com/sponsors/unifiedjs"
- },
- {
- "type": "OpenCollective",
- "url": "https://opencollective.com/unified"
- }
- ],
- "license": "MIT",
- "dependencies": {
- "devlop": "^1.0.0",
- "micromark-util-character": "^2.0.0",
- "micromark-util-symbol": "^2.0.0",
- "micromark-util-types": "^2.0.0"
- }
- },
- "node_modules/micromark-factory-space": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz",
- "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==",
- "dev": true,
- "funding": [
- {
- "type": "GitHub Sponsors",
- "url": "https://github.com/sponsors/unifiedjs"
- },
- {
- "type": "OpenCollective",
- "url": "https://opencollective.com/unified"
- }
- ],
- "license": "MIT",
- "dependencies": {
- "micromark-util-character": "^2.0.0",
- "micromark-util-types": "^2.0.0"
- }
- },
- "node_modules/micromark-factory-title": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz",
- "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==",
- "dev": true,
- "funding": [
- {
- "type": "GitHub Sponsors",
- "url": "https://github.com/sponsors/unifiedjs"
- },
- {
- "type": "OpenCollective",
- "url": "https://opencollective.com/unified"
- }
- ],
- "license": "MIT",
- "dependencies": {
- "micromark-factory-space": "^2.0.0",
- "micromark-util-character": "^2.0.0",
- "micromark-util-symbol": "^2.0.0",
- "micromark-util-types": "^2.0.0"
- }
- },
- "node_modules/micromark-factory-whitespace": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz",
- "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==",
- "dev": true,
- "funding": [
- {
- "type": "GitHub Sponsors",
- "url": "https://github.com/sponsors/unifiedjs"
- },
- {
- "type": "OpenCollective",
- "url": "https://opencollective.com/unified"
- }
- ],
- "license": "MIT",
- "dependencies": {
- "micromark-factory-space": "^2.0.0",
- "micromark-util-character": "^2.0.0",
- "micromark-util-symbol": "^2.0.0",
- "micromark-util-types": "^2.0.0"
- }
- },
- "node_modules/micromark-util-character": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz",
- "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==",
- "dev": true,
- "funding": [
- {
- "type": "GitHub Sponsors",
- "url": "https://github.com/sponsors/unifiedjs"
- },
- {
- "type": "OpenCollective",
- "url": "https://opencollective.com/unified"
- }
- ],
- "license": "MIT",
- "dependencies": {
- "micromark-util-symbol": "^2.0.0",
- "micromark-util-types": "^2.0.0"
- }
- },
- "node_modules/micromark-util-chunked": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz",
- "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==",
- "dev": true,
- "funding": [
- {
- "type": "GitHub Sponsors",
- "url": "https://github.com/sponsors/unifiedjs"
- },
- {
- "type": "OpenCollective",
- "url": "https://opencollective.com/unified"
- }
- ],
- "license": "MIT",
- "dependencies": {
- "micromark-util-symbol": "^2.0.0"
- }
- },
- "node_modules/micromark-util-classify-character": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz",
- "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==",
- "dev": true,
- "funding": [
- {
- "type": "GitHub Sponsors",
- "url": "https://github.com/sponsors/unifiedjs"
- },
- {
- "type": "OpenCollective",
- "url": "https://opencollective.com/unified"
- }
- ],
- "license": "MIT",
- "dependencies": {
- "micromark-util-character": "^2.0.0",
- "micromark-util-symbol": "^2.0.0",
- "micromark-util-types": "^2.0.0"
- }
- },
- "node_modules/micromark-util-combine-extensions": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz",
- "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==",
- "dev": true,
- "funding": [
- {
- "type": "GitHub Sponsors",
- "url": "https://github.com/sponsors/unifiedjs"
- },
- {
- "type": "OpenCollective",
- "url": "https://opencollective.com/unified"
- }
- ],
- "license": "MIT",
- "dependencies": {
- "micromark-util-chunked": "^2.0.0",
- "micromark-util-types": "^2.0.0"
- }
- },
- "node_modules/micromark-util-decode-numeric-character-reference": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz",
- "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==",
- "dev": true,
- "funding": [
- {
- "type": "GitHub Sponsors",
- "url": "https://github.com/sponsors/unifiedjs"
- },
- {
- "type": "OpenCollective",
- "url": "https://opencollective.com/unified"
- }
- ],
- "license": "MIT",
- "dependencies": {
- "micromark-util-symbol": "^2.0.0"
- }
- },
- "node_modules/micromark-util-encode": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz",
- "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==",
- "dev": true,
- "funding": [
- {
- "type": "GitHub Sponsors",
- "url": "https://github.com/sponsors/unifiedjs"
- },
- {
- "type": "OpenCollective",
- "url": "https://opencollective.com/unified"
- }
- ],
- "license": "MIT"
- },
- "node_modules/micromark-util-html-tag-name": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz",
- "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==",
- "dev": true,
- "funding": [
- {
- "type": "GitHub Sponsors",
- "url": "https://github.com/sponsors/unifiedjs"
- },
- {
- "type": "OpenCollective",
- "url": "https://opencollective.com/unified"
- }
- ],
- "license": "MIT"
- },
- "node_modules/micromark-util-normalize-identifier": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz",
- "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==",
- "dev": true,
- "funding": [
- {
- "type": "GitHub Sponsors",
- "url": "https://github.com/sponsors/unifiedjs"
- },
- {
- "type": "OpenCollective",
- "url": "https://opencollective.com/unified"
- }
- ],
- "license": "MIT",
- "dependencies": {
- "micromark-util-symbol": "^2.0.0"
- }
- },
- "node_modules/micromark-util-resolve-all": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz",
- "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==",
- "dev": true,
- "funding": [
- {
- "type": "GitHub Sponsors",
- "url": "https://github.com/sponsors/unifiedjs"
- },
- {
- "type": "OpenCollective",
- "url": "https://opencollective.com/unified"
- }
- ],
- "license": "MIT",
- "dependencies": {
- "micromark-util-types": "^2.0.0"
- }
- },
- "node_modules/micromark-util-sanitize-uri": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz",
- "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==",
- "dev": true,
- "funding": [
- {
- "type": "GitHub Sponsors",
- "url": "https://github.com/sponsors/unifiedjs"
- },
- {
- "type": "OpenCollective",
- "url": "https://opencollective.com/unified"
- }
- ],
- "license": "MIT",
- "dependencies": {
- "micromark-util-character": "^2.0.0",
- "micromark-util-encode": "^2.0.0",
- "micromark-util-symbol": "^2.0.0"
- }
- },
- "node_modules/micromark-util-subtokenize": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz",
- "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==",
- "dev": true,
- "funding": [
- {
- "type": "GitHub Sponsors",
- "url": "https://github.com/sponsors/unifiedjs"
- },
- {
- "type": "OpenCollective",
- "url": "https://opencollective.com/unified"
- }
- ],
- "license": "MIT",
- "dependencies": {
- "devlop": "^1.0.0",
- "micromark-util-chunked": "^2.0.0",
- "micromark-util-symbol": "^2.0.0",
- "micromark-util-types": "^2.0.0"
- }
- },
- "node_modules/micromark-util-symbol": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz",
- "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==",
- "dev": true,
- "funding": [
- {
- "type": "GitHub Sponsors",
- "url": "https://github.com/sponsors/unifiedjs"
- },
- {
- "type": "OpenCollective",
- "url": "https://opencollective.com/unified"
- }
- ],
- "license": "MIT"
- },
- "node_modules/micromark-util-types": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz",
- "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==",
- "dev": true,
- "funding": [
- {
- "type": "GitHub Sponsors",
- "url": "https://github.com/sponsors/unifiedjs"
- },
- {
- "type": "OpenCollective",
- "url": "https://opencollective.com/unified"
- }
- ],
- "license": "MIT"
- },
- "node_modules/ms": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
- "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/parse-entities": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz",
- "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@types/unist": "^2.0.0",
- "character-entities-legacy": "^3.0.0",
- "character-reference-invalid": "^2.0.0",
- "decode-named-character-reference": "^1.0.0",
- "is-alphanumerical": "^2.0.0",
- "is-decimal": "^2.0.0",
- "is-hexadecimal": "^2.0.0"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/wooorm"
- }
- },
- "node_modules/string-width": {
- "version": "8.2.1",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.1.tgz",
- "integrity": "sha512-IIaP0g3iy9Cyy18w3M9YcaDudujEAVHKt3a3QJg1+sr/oX96TbaGUubG0hJyCjCBThFH+tFpcIyoUHUn1ogaLA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "get-east-asian-width": "^1.5.0",
- "strip-ansi": "^7.1.2"
- },
- "engines": {
- "node": ">=20"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/strip-ansi": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz",
- "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ansi-regex": "^6.2.2"
- },
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/chalk/strip-ansi?sponsor=1"
- }
- }
- }
-}
diff --git a/package.json b/package.json
deleted file mode 100644
index 294d3b1..0000000
--- a/package.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "devDependencies": {
- "markdownlint": "^0.41.0"
- }
-}
diff --git a/plugins/bin/evals/cross-cutting/gitleaks/eval.yaml b/plugins/bin/evals/cross-cutting/gitleaks/eval.yaml
deleted file mode 100644
index 7f7f05e..0000000
--- a/plugins/bin/evals/cross-cutting/gitleaks/eval.yaml
+++ /dev/null
@@ -1,84 +0,0 @@
-skill_name: gitleaks
-
-trigger_tests:
- - id: explicit-trigger-install
- name: Explicit trigger — install and configure
- query: "set up gitleaks in this repo"
- should_trigger: true
-
- - id: explicit-trigger-update-hook
- name: Explicit trigger — update hook
- query: "update the gitleaks pre-commit hook"
- should_trigger: true
-
- - id: implicit-trigger-false-positive
- name: Implicit trigger — suppress false positive in pre-commit hook
- query: "my pre-commit hook keeps blocking commits because it thinks my test fixture has an API key, how do I suppress it?"
- should_trigger: true
-
- - id: implicit-trigger-scan-history
- name: Implicit trigger — audit repo history for secrets
- query: "I want to scan my entire git history to make sure no credentials were ever committed"
- should_trigger: true
-
- - id: negative-trigger-security-review
- name: Negative trigger — general code security review
- query: "do a security review of this pull request"
- should_trigger: false
-
- - id: negative-trigger-ci-scan
- name: Negative trigger — CI pipeline secret scanning
- query: "add secret scanning to my GitHub Actions CI pipeline"
- should_trigger: false
-
- - id: negative-trigger-other-tool
- name: Negative trigger — different secret scanning tool
- query: "trufflehog is flagging false positives in my repo, how do I configure it?"
- should_trigger: false
-
-output_tests:
- - id: output-states-intent-before-running
- name: Deterministic — states intent and waits for confirmation before running setup script
- type: deterministic
- prompt: "set up gitleaks in /home/user/myproject"
- expected_output: >
- The skill states what it is about to do (run setup-gitleaks.sh, which will modify
- .git/hooks/pre-commit and seed .gitleaks.toml) and asks for confirmation before
- executing the script. The confirmation request appears before any Bash command
- that runs the script.
- assertions:
- - "Output contains 'setup-gitleaks.sh' before any Bash tool invocation that runs it"
- - "Output contains a confirmation prompt (e.g. 'Proceed?') before running the setup script"
- - "Output does not run the setup script without an explicit confirmation step"
-
- - id: output-redacts-secret-values
- name: Deterministic — redacts secret value and prescribes rotation before history rewrite
- type: deterministic
- prompt: "gitleaks found an AWS secret key on line 42 of config/settings.py — it says the value is AKIAIOSFODNN7EXAMPLE. What do I do?"
- expected_output: >
- The skill does not echo or repeat the secret value. It references the finding by
- rule ID, file path, and line number only. It states that the secret must be rotated
- immediately. It mentions git filter-repo (not git filter-branch) for history rewrite
- and asks for user confirmation before running the rewrite.
- assertions:
- - "Output does not contain the literal string 'AKIAIOSFODNN7EXAMPLE'"
- - "Output mentions rotating or revoking the credential as the first action"
- - "Output references 'git filter-repo' for history rewrite, not 'git filter-branch'"
- - "Output states that user confirmation is required before running the history rewrite"
-
- - id: output-quality-allowlist-guidance
- name: LLM-rubric — allowlist guidance is correct, version-aware, and minimal
- type: llm-rubric
- prompt: "gitleaks keeps flagging my docs/research/ directory as containing secrets, how do I suppress it?"
- expected_output: >
- High-quality output checks the installed gitleaks version before prescribing any
- TOML syntax, recommends a path-based allowlist entry in .gitleaks.toml (not a
- .gitleaksignore fingerprint), uses the correct TOML syntax for the detected version,
- adds only the minimal allowlist entry needed for the identified false positive, and
- includes a verification step (re-run gitleaks dir -v or gitleaks dir --log-level debug)
- after making the change.
- assertions:
- - "Output checks or asks about the gitleaks version before writing TOML syntax"
- - "Output recommends a path-based allowlist entry in .gitleaks.toml rather than .gitleaksignore"
- - "Output includes a command to verify the suppression works after the change"
- - "Output explains why .gitleaksignore fingerprints are fragile (line numbers shift)"
diff --git a/plugins/bin/evals/cross-cutting/neuledge-context/eval.yaml b/plugins/bin/evals/cross-cutting/neuledge-context/eval.yaml
deleted file mode 100644
index 3f2f62c..0000000
--- a/plugins/bin/evals/cross-cutting/neuledge-context/eval.yaml
+++ /dev/null
@@ -1,97 +0,0 @@
-skill_name: neuledge-context
-
-trigger_tests:
- - id: explicit-install-register
- name: "Explicit trigger — install and register"
- query: "install @neuledge/context and register it as an MCP server in Claude Code"
- should_trigger: true
-
- - id: explicit-package-management
- name: "Explicit trigger — package management"
- query: "install the react documentation package using neuledge context"
- should_trigger: true
-
- - id: implicit-offline-docs
- name: "Implicit trigger — offline docs for AI agent"
- query: "I need React and Next.js docs available to my AI agent without web searches"
- should_trigger: true
-
- - id: negative-different-mcp
- name: "Negative — different MCP server"
- query: "Add the Gitea MCP server to Claude Code"
- should_trigger: false
-
- - id: negative-query-existing
- name: "Negative — querying an already-running server"
- query: "How do I query React docs using the context server that's already running?"
- should_trigger: false
-
- - id: negative-cursor-setup
- name: "Negative — different provider"
- query: "Set up context serve for Cursor"
- should_trigger: false
-
-output_tests:
- - id: install-script-announced
- name: "Install — script announced before running, version verified after"
- type: deterministic
- prompt: "install @neuledge/context"
- expected_output: >
- The skill announces that it will run scripts/setup-neuledge-context.sh before executing it,
- then verifies the installation by running `context --version`.
- assertions:
- - "Output mentions 'scripts/setup-neuledge-context.sh' before any install command is run"
- - "Output includes a `context --version` call after the install step"
- - "Output does not contain `npm install -g @neuledge/context@latest` (no floating @latest)"
-
- - id: mcp-list-before-add
- name: "MCP registration — list checked before add, skipped if present"
- type: deterministic
- prompt: "register @neuledge/context as a Claude Code MCP server"
- expected_output: >
- The skill runs `claude mcp list` and checks for an existing 'context' entry before
- running `claude mcp add`. If already registered, the add step is skipped.
- assertions:
- - "Output includes `claude mcp list` before `claude mcp add context`"
- - "Output states that registration is skipped when the server is already present"
- - "The `claude mcp add` command uses stdio form: `claude mcp add context -- context serve`"
-
- - id: auth-chmod-paired
- name: "Auth — secure-context-config.sh run immediately after auth add"
- type: deterministic
- prompt: "add auth credentials for docs.example.com to neuledge context"
- expected_output: >
- The skill runs `context auth add docs.example.com` with an environment variable reference
- for the credential, then immediately runs scripts/secure-context-config.sh.
- No credential value appears in the output.
- assertions:
- - "Output references an environment variable (e.g. $TOKEN) rather than a literal credential value"
- - "Output runs `scripts/secure-context-config.sh` in the same step as or immediately after `context auth add`"
- - "No bearer token, cookie value, or other credential string appears in the output"
-
- - id: git-check-url-source
- name: "context add — git prerequisite checked for URL sources only"
- type: deterministic
- prompt: "add documentation from https://github.com/prisma/prisma using context add"
- expected_output: >
- Before running `context add`, the skill checks `git --version` because the source is a
- GitHub URL. The check is present for URL/repo sources and absent for local .db file paths.
- assertions:
- - "Output includes `git --version` before the `context add https://github.com/...` command"
- - "If given a local .db file path instead, the git check is absent"
-
- - id: install-flow-quality
- name: "Full install + register flow quality"
- type: llm-rubric
- prompt: "install @neuledge/context and set it up as my Claude Code MCP server"
- expected_output: >
- A complete, ordered install-then-register flow: (1) announce the install script,
- (2) run setup-neuledge-context.sh, (3) verify with context --version,
- (4) check claude mcp list, (5) run claude mcp add if not already registered,
- (6) confirm with claude mcp list. Steps are in the correct order with verification
- between install and registration.
- assertions:
- - "Install step comes before MCP registration step"
- - "A verification command (context --version) appears between install and registration"
- - "The output would leave a user with a working @neuledge/context MCP server in Claude Code"
- - "No step is skipped without an explanation of why it was skipped"
diff --git a/plugins/bin/evals/implement/write-docs/eval.yaml b/plugins/bin/evals/implement/write-docs/eval.yaml
deleted file mode 100644
index b044078..0000000
--- a/plugins/bin/evals/implement/write-docs/eval.yaml
+++ /dev/null
@@ -1,61 +0,0 @@
-skill_name: write-docs
-
-trigger_tests:
- - id: explicit-trigger-document-module
- name: "Explicit trigger — document a script"
- query: "Write documentation for the install.sh script"
- should_trigger: true
-
- - id: explicit-trigger-create-docs
- name: "Explicit trigger — create docs for a feature"
- query: "Create docs for this feature"
- should_trigger: true
-
- - id: implicit-trigger-readme-update
- name: "Implicit trigger — outdated README section, no trigger phrase"
- query: "We need to update the README section for the auth module, the current one is outdated"
- should_trigger: true
-
- - id: negative-trigger-prd
- name: "Negative — PRD request should route to to-prd"
- query: "Write a PRD for the new logging feature"
- should_trigger: false
-
- - id: negative-trigger-write-skill
- name: "Negative — skill authoring request should route to write-skill"
- query: "Write a skill for generating documentation automatically"
- should_trigger: false
-
- - id: negative-trigger-skill-file
- name: "Negative — SKILL.md update (skill files are self-describing)"
- query: "Document how the write-docs skill works by updating its SKILL.md"
- should_trigger: false
-
-output_tests:
- - id: output-proposes-files-before-reading
- name: "Deterministic — candidates proposed or approval sought before reading files"
- type: deterministic
- prompt: "Write documentation for the config module"
- expected_output: "Skill proposes candidate files or asks the user to name specific files before reading any file content"
- assertions:
- - "Response proposes candidate file paths or asks the user to confirm which files to read before showing any extracted content"
- - "Response does not display extracted code content or API surface without first receiving file approval"
-
- - id: output-gap-check-present
- name: "Deterministic — gap check step present before drafting"
- type: deterministic
- prompt: "Write documentation for the install.sh script, audience: developer"
- expected_output: "Skill presents extracted behaviour to the user and asks them to fill gaps before drafting any section"
- assertions:
- - "Response includes a gap check step that presents extracted behaviour and asks what the code does not explain"
- - "Response does not skip directly to a drafted documentation section without presenting extracted content first"
-
- - id: output-never-invents-behaviour
- name: "LLM rubric — no invented behaviour, all claims sourced"
- type: llm-rubric
- prompt: "Document the src/config.py file for internal developers"
- expected_output: "Documentation where every claim is attributed to code content or explicit user input, with no invented explanations, assumptions about intent, or unverifiable behaviour claims."
- assertions:
- - "The skill explicitly derives each documented claim from a named source — a code line, spec section, or user statement — and does not add claims without attribution"
- - "The skill does not include descriptions of caller intent, design rationale, or future behaviour that are not present in the source material"
- - "If a behaviour is undocumentable (internal detail with no public spec), the skill notes it as out-of-scope rather than inventing an explanation"
diff --git a/plugins/bin/skills/gitleaks/META.md b/plugins/bin/skills/gitleaks/META.md
deleted file mode 100644
index ee41cf2..0000000
--- a/plugins/bin/skills/gitleaks/META.md
+++ /dev/null
@@ -1,15 +0,0 @@
-```yaml
-version: "1.0"
-updated: 2026-06-20
-
-when: >
- Invoked when the user wants to install gitleaks and wire it as a git pre-commit secret
- scanner, update the hook in an existing repo, tune allowlist rules to suppress false
- positives, debug a scan finding, or rotate a real secret that was found. Covers the full
- lifecycle: install → configure → maintain → remediate. Not invoked for general code
- security review (security-review skill) or CI pipeline secret scanning (write-ci-pipeline skill).
-
-references:
- - https://github.com/gitleaks/gitleaks/releases/tag/v8.24.2
- - https://github.com/gitleaks/gitleaks/blob/main/README.md
-```
diff --git a/plugins/bin/skills/gitleaks/SKILL.md b/plugins/bin/skills/gitleaks/SKILL.md
deleted file mode 100644
index 4e5d5b9..0000000
--- a/plugins/bin/skills/gitleaks/SKILL.md
+++ /dev/null
@@ -1,111 +0,0 @@
----
-name: gitleaks
-description: Use when the user wants to install gitleaks, wire it as a git pre-commit secret scanner, update the hook in an existing repo, tune allowlist rules, resolve false positives, or debug a gitleaks scan finding. Do NOT use when the user wants a general security review of code (use security-review), wants to add secret scanning to a CI pipeline (use write-ci-pipeline), or is asking about a different secret scanning tool such as trufflehog or git-secrets.
-metadata:
- category: cross-cutting
-allowed-tools:
- - Bash
- - Read
- - Edit
----
-
-
-
-## Required inputs
-
-- **Target repo path** — absolute path to the git repository to configure; inferred from current working directory if not stated, ask if ambiguous
-- **Task type** — install/configure, update hook, tune allowlist, debug finding; inferred from the user's request
-
-## Constraints
-
-- Always state what you are about to do before running `setup-gitleaks.sh` — the script modifies `.git/hooks/pre-commit` and seeds `.gitleaks.toml`
-- Never modify `.gitleaks.toml` if the user has not asked for allowlist changes — it is project-owned once seeded; treat it as user-controlled config
-- Never run `gitleaks git` or `gitleaks dir` across the full history without warning the user it may be slow on large repos
-- Redact any secret values that appear in gitleaks output before showing them to the user — show the rule ID, file, and line number only
-- When the installed gitleaks version is unknown, check it with `gitleaks version` before suggesting config syntax — v8.24.2 uses `[allowlist]`; v8.25.0+ uses `[[allowlists]]`
-- False positive suppression: prefer path-based allowlists in `.gitleaks.toml` over fingerprint-based entries in `.gitleaksignore` — fingerprints are line-number-sensitive and break on file edits
-
-
-
-
-
-## Process
-
-### Install and configure
-
-1. **Confirm target.** State: "I will run `scripts/setup-gitleaks.sh ` which will install gitleaks (if absent), seed `.gitleaks.toml` (first run only), and write the pre-commit hook. Proceed?" Wait for confirmation — this modifies the repo's git hook.
-
-2. **Run setup script.** Execute from the ai-development repo root:
- ```
- bash scripts/setup-gitleaks.sh
- ```
- The script is idempotent — it replaces the gitleaks block in the hook on every run without disturbing other hook content.
-
-3. **Verify installation.** Run `gitleaks version` to confirm the binary is available. Run `gitleaks git --staged --redact -v` in the target repo to confirm the hook would work on a staged commit (add a dummy change if needed to test).
-
-4. **Commit `.gitleaks.toml`.** Remind the user that `.gitleaks.toml` belongs in version control so all contributors share the same allowlist rules.
-
-### Update hook
-
-Re-run `bash scripts/setup-gitleaks.sh ` from the ai-development repo root. The managed block (delimited by `# managed by setup-gitleaks.sh` / `# end gitleaks` markers) is always replaced with the current version. Non-gitleaks hook content is preserved.
-
-### Tune allowlist / resolve false positives
-
-1. **Identify the false positive.** Run `gitleaks dir --log-level debug ` to see which rule fired and which allowlist entries (if any) are already active.
-
-2. **Check the gitleaks version.** Run `gitleaks version`. Use `[allowlist]` syntax for v8.24.2; use `[[allowlists]]` syntax for v8.25.0+. Using the wrong syntax silently produces no errors but the allowlist does nothing — this is the most common configuration trap.
-
-3. **Choose suppression strategy.** Read `.gitleaks.toml` first. See `references/allowlist-patterns.md` for syntax examples and when to use each approach:
- - Path regex in `[allowlist]` — for files that can never contain real secrets (research notes, terminal captures, test fixtures). Preferred.
- - Stopwords in `[allowlist]` — for placeholder patterns like "example", "changeme".
- - `disabledRules` in `[extend]` — to disable a noisy default rule entirely. Use only when the rule has no value for this repo.
- - `.gitleaksignore` fingerprint — last resort; breaks when the file is edited because line numbers shift.
-
-4. **Edit `.gitleaks.toml`.** Add the minimal allowlist entry needed. Do not suppress more than the identified false positive.
-
-5. **Verify.** Re-run `gitleaks dir -v ` or `gitleaks git -v` to confirm the false positive is suppressed and no real findings are hidden.
-
-### Scan modes
-
-| Mode | Command | When to use |
-|---|---|---|
-| Staged changes (pre-commit) | `gitleaks git --staged --redact -v` | What the hook runs |
-| Full commit history | `gitleaks git -v` | Audit existing repo history |
-| Working directory files | `gitleaks dir -v ` | Scan uncommitted files |
-| Debug allowlists | `gitleaks dir --log-level debug ` | See which files are skipped and which allowlists fire |
-
-### Resolve a real finding
-
-1. Do not redact or show the secret value. Reference the rule ID, file, and line number only.
-2. The secret is compromised the moment it was committed — rotate it immediately, regardless of whether the commit is reachable from the public remote.
-3. Remove the secret from history using `git filter-repo` (not `git filter-branch`). This is a history-rewrite — confirm with the user before running. Force-push to all remotes after rewriting.
-4. Add the file path to the `.gitleaks.toml` allowlist only if the file is known to be a false-positive source going forward (e.g. a test fixture). Do not add an allowlist entry to suppress a real finding that has been removed.
-
-## Output format
-
-No structured output file. The skill produces:
-- Modified `.git/hooks/pre-commit` in the target repo (via the setup script)
-- Modified `.gitleaks.toml` in the target repo (allowlist changes only, when requested)
-- Terminal confirmation of what was changed and what to do next
-
-
-
-
-
-## Failure handling
-
-- `setup-gitleaks.sh` not found — stop; instruct the user to run from the ai-development repo root at `/root/ai-development/`
-- Target path is not a git repository — report the error from the script and ask the user to confirm the correct path
-- `gitleaks` binary not installed and download fails — report the curl/network error; direct the user to manual install at `https://github.com/gitleaks/gitleaks/releases`
-- Wrong TOML syntax for installed version — detect via `gitleaks version`, show the correct syntax for that version, do not guess
-
-## Self-check
-
-- [ ] Target repo confirmed before running the setup script
-- [ ] `gitleaks version` checked before writing any `.gitleaks.toml` allowlist syntax
-- [ ] Secret values in scan output redacted before displaying to the user
-- [ ] `.gitleaks.toml` edits are minimal — only the identified false positive suppressed
-- [ ] After any allowlist change: re-ran scan to verify suppression works and no real findings are hidden
-- [ ] For real findings: rotation step stated before history rewrite, user confirmed history rewrite before running `git filter-repo`
-
-
diff --git a/plugins/bin/skills/gitleaks/references/allowlist-patterns.md b/plugins/bin/skills/gitleaks/references/allowlist-patterns.md
deleted file mode 100644
index c5e17e6..0000000
--- a/plugins/bin/skills/gitleaks/references/allowlist-patterns.md
+++ /dev/null
@@ -1,83 +0,0 @@
-# Gitleaks allowlist patterns
-
-## Version syntax
-
-| Version | Allowlist syntax |
-|---|---|
-| v8.24.2 and earlier | `[allowlist]` (singular table) |
-| v8.25.0 and later | `[[allowlists]]` (array of tables) |
-
-**Critical**: using the wrong syntax produces no error but the allowlist silently does nothing. Always check `gitleaks version` first.
-
-## v8.24.2 syntax (this repo uses 8.24.2)
-
-### Suppress by path regex
-
-Use for files that can never contain real secrets (research notes, terminal captures, test fixtures, generated docs).
-
-```toml
-[allowlist]
-description = "research notes and terminal captures"
-paths = [
- '''docs/research/.*''',
- '''tests/fixtures/.*''',
-]
-```
-
-### Suppress by stopword
-
-Use for placeholder values that match secret patterns but are clearly not real.
-
-```toml
-[allowlist]
-description = "placeholder values"
-stopwords = ["example", "placeholder", "changeme", "your-api-key-here"]
-```
-
-### Disable a default rule entirely
-
-Use only when a rule has no value for this repo and produces pervasive false positives.
-
-```toml
-[extend]
-useDefault = true
-disabledRules = ["generic-api-key"]
-```
-
-## v8.25.0+ syntax (for reference)
-
-```toml
-[[allowlists]]
-description = "research notes"
-paths = ['''docs/research/.*''']
-
-[[allowlists]]
-description = "placeholder values"
-stopwords = ["example", "placeholder"]
-```
-
-## .gitleaksignore (fingerprint-based — last resort)
-
-```
-# Format: :
-# Generated by: gitleaks git -v --report-format json | jq -r '.[] | "\(.Fingerprint):\(.StartLine)"'
-abc123def456:42
-```
-
-Avoid this approach: fingerprints embed line numbers. Any edit to the file shifts line numbers and invalidates the entry, re-surfacing the false positive.
-
-## Verification after any change
-
-```bash
-# Scan current files
-gitleaks dir -v .
-
-# Scan with debug output to see which allowlists fired
-gitleaks dir --log-level debug .
-
-# Scan commit history
-gitleaks git -v
-
-# Scan only staged changes (what the pre-commit hook runs)
-gitleaks git --staged --redact -v
-```
diff --git a/scripts/gitleaks.toml b/scripts/gitleaks.toml
deleted file mode 100644
index ab7c8d8..0000000
--- a/scripts/gitleaks.toml
+++ /dev/null
@@ -1,24 +0,0 @@
-title = "gitleaks config"
-
-[extend]
-# Extends the default ruleset built into gitleaks.
-# Remove useDefault and define [[rules]] from scratch if you want full control.
-useDefault = true
-
-# Rules to disable from the default set — uncomment and add IDs for known false positives.
-# Run `gitleaks git -v` on your repo first to discover which rules fire.
-# disabledRules = ["generic-api-key"]
-
-# Global allowlist — applies to all rules.
-# Note: uses [allowlist] (v8 syntax). v8.25.0+ uses [[allowlists]] (array of tables).
-# Add path regexes or stopwords to suppress known false positives.
-
-[allowlist]
-description = "Known false positives — prose patterns and research session notes"
-# docs/research/: high-entropy text from terminal captures in session notes
-# docs/ROADMAP.md: documents known false positives, triggering the same rules
-# ai-coding-factory-session.md:90 specifically: 'Token routing: Haiku/Sonnet/Opus'
-paths = [
- '''docs/research/.*''',
- '''docs/ROADMAP\.md''',
-]
diff --git a/scripts/setup-gitleaks.sh b/scripts/setup-gitleaks.sh
deleted file mode 100755
index ea322c5..0000000
--- a/scripts/setup-gitleaks.sh
+++ /dev/null
@@ -1,124 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-
-# Sets up gitleaks as a git pre-commit hook in a target repository.
-# Usage: setup-gitleaks.sh [TARGET_REPO]
-# TARGET_REPO — path to the git repo to configure (default: current directory)
-# Idempotent: safe to re-run; always replaces the hook block with the current version.
-
-GITLEAKS_VERSION="8.24.2"
-GITLEAKS_INSTALL_DIR="${GITLEAKS_INSTALL_DIR:-/usr/local/bin}"
-
-SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
-TARGET="${1:-$(pwd)}"
-HOOK_FILE="$TARGET/.git/hooks/pre-commit"
-CONFIG_SRC="$SCRIPT_DIR/gitleaks.toml"
-CONFIG_DEST="$TARGET/.gitleaks.toml"
-MARKER="# managed by setup-gitleaks.sh"
-END_MARKER="# end gitleaks"
-
-# --- Install gitleaks if not present ---
-
-install_gitleaks() {
- local os arch tarball url tmp_dir
-
- case "$(uname -s)" in
- Linux) os="linux" ;;
- Darwin) os="darwin" ;;
- *)
- echo "Error: unsupported OS '$(uname -s)' — install gitleaks manually from https://github.com/gitleaks/gitleaks/releases" >&2
- exit 1
- ;;
- esac
-
- case "$(uname -m)" in
- x86_64) arch="x64" ;;
- aarch64 | arm64) arch="arm64" ;;
- *)
- echo "Error: unsupported architecture '$(uname -m)' — install gitleaks manually from https://github.com/gitleaks/gitleaks/releases" >&2
- exit 1
- ;;
- esac
-
- tarball="gitleaks_${GITLEAKS_VERSION}_${os}_${arch}.tar.gz"
- url="https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}/${tarball}"
- tmp_dir="$(mktemp -d)"
- trap 'rm -rf "$tmp_dir"' RETURN
-
- echo "Installing gitleaks v${GITLEAKS_VERSION}..."
- curl -fsSL "$url" -o "$tmp_dir/$tarball"
- tar -xzf "$tmp_dir/$tarball" -C "$tmp_dir" gitleaks
- install -m 755 "$tmp_dir/gitleaks" "$GITLEAKS_INSTALL_DIR/gitleaks"
- echo "Installed: $GITLEAKS_INSTALL_DIR/gitleaks"
-}
-
-if ! command -v gitleaks &>/dev/null; then
- install_gitleaks
-fi
-
-# --- Validate ---
-
-if [ ! -d "$TARGET/.git" ]; then
- echo "Error: $TARGET is not a git repository" >&2
- exit 1
-fi
-
-if [ ! -f "$CONFIG_SRC" ]; then
- echo "Error: config template not found at $CONFIG_SRC" >&2
- exit 1
-fi
-
-# --- Deploy config ---
-
-if [ -f "$CONFIG_DEST" ]; then
- echo "Skipped: $CONFIG_DEST already exists — edit it directly to customise rules."
-else
- cp "$CONFIG_SRC" "$CONFIG_DEST"
- echo "Wrote: $CONFIG_DEST"
- echo " Commit this file — it belongs in version control."
-fi
-
-# --- Deploy hook ---
-
-hook_block() {
- cat </dev/null; then
- gitleaks git --staged --redact -v
-else
- echo "Warning: gitleaks not installed — secret scan skipped (https://github.com/gitleaks/gitleaks/releases)" >&2
-fi
-$END_MARKER
-BLOCK
-}
-
-write_hook() {
- local hook_file="$1"
-
- if grep -qF "$MARKER" "$hook_file"; then
- # Remove old block (start marker through end marker inclusive) then append current version
- awk -v start="$MARKER" -v end="$END_MARKER" '
- $0 == start { skip=1; next }
- skip && $0 == end { skip=0; next }
- !skip { print }
- ' "$hook_file" > "${hook_file}.tmp" && mv "${hook_file}.tmp" "$hook_file"
- hook_block >> "$hook_file"
- echo "Updated: $hook_file (gitleaks block replaced)"
- else
- hook_block >> "$hook_file"
- echo "Updated: $hook_file (gitleaks block appended to existing hook)"
- fi
-}
-
-if [ -f "$HOOK_FILE" ]; then
- write_hook "$HOOK_FILE"
-else
- { echo '#!/usr/bin/env bash'; echo 'set -euo pipefail'; hook_block; } > "$HOOK_FILE"
- chmod +x "$HOOK_FILE"
- echo "Created: $HOOK_FILE"
-fi
-
-echo ""
-echo "Done. Staged secrets will be scanned on every commit in $TARGET."
-echo "To skip on a single commit: SKIP=gitleaks git commit ..."
diff --git a/tests/test-setup-gitleaks.sh b/tests/test-setup-gitleaks.sh
deleted file mode 100755
index 57c81b2..0000000
--- a/tests/test-setup-gitleaks.sh
+++ /dev/null
@@ -1,151 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-
-REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
-SCRIPT="$REPO_ROOT/scripts/setup-gitleaks.sh"
-PASS=0
-FAIL=0
-
-pass() { echo " PASS: $1"; PASS=$((PASS + 1)); }
-fail() { echo " FAIL: $1"; FAIL=$((FAIL + 1)); }
-
-# Fake gitleaks binary — prevents the install step from running during tests
-FAKE_BIN="$(mktemp -d)"
-trap 'rm -rf "$FAKE_BIN"' EXIT
-printf '#!/bin/sh\necho "gitleaks fake"\n' > "$FAKE_BIN/gitleaks"
-chmod +x "$FAKE_BIN/gitleaks"
-export PATH="$FAKE_BIN:$PATH"
-
-# Helper: create an isolated git repo in a temp dir
-make_repo() {
- local dir
- dir="$(mktemp -d)"
- git -C "$dir" init -q
- echo "$dir"
-}
-
-# Helper: run setup script against a target repo; capture output; always return exit code
-run_setup() {
- local target="$1"
- bash "$SCRIPT" "$target" 2>&1
-}
-
-# --- 1. Rejects non-git directory ---
-echo ""
-echo "--- rejects non-git directory ---"
-NON_GIT="$(mktemp -d)"
-trap 'rm -rf "$NON_GIT"' EXIT
-if bash "$SCRIPT" "$NON_GIT" >/dev/null 2>&1; then
- fail "exited 0 for non-git directory — expected exit 1"
-else
- pass "exits non-zero for non-git directory"
-fi
-
-# --- 2. Config deployed ---
-echo ""
-echo "--- config deployed to target repo ---"
-REPO="$(make_repo)"
-trap 'rm -rf "$REPO"' EXIT
-run_setup "$REPO" > /dev/null
-if [ -f "$REPO/.gitleaks.toml" ]; then
- pass ".gitleaks.toml created in target repo"
-else
- fail ".gitleaks.toml missing from target repo"
-fi
-if diff -q "$REPO_ROOT/scripts/gitleaks.toml" "$REPO/.gitleaks.toml" > /dev/null 2>&1; then
- pass ".gitleaks.toml matches the template"
-else
- fail ".gitleaks.toml content differs from template"
-fi
-
-# --- 3. Hook created from scratch ---
-echo ""
-echo "--- hook created when none exists ---"
-REPO2="$(make_repo)"
-trap 'rm -rf "$REPO2"' EXIT
-run_setup "$REPO2" > /dev/null
-HOOK="$REPO2/.git/hooks/pre-commit"
-if [ -f "$HOOK" ]; then
- pass "pre-commit hook created"
-else
- fail "pre-commit hook not created"
-fi
-if [ -x "$HOOK" ]; then
- pass "pre-commit hook is executable"
-else
- fail "pre-commit hook is not executable"
-fi
-if head -1 "$HOOK" | grep -q "^#!"; then
- pass "pre-commit hook has a shebang"
-else
- fail "pre-commit hook missing shebang"
-fi
-if grep -q "gitleaks git --staged" "$HOOK"; then
- pass "pre-commit hook contains gitleaks command"
-else
- fail "pre-commit hook missing gitleaks command"
-fi
-if grep -q "# managed by setup-gitleaks.sh" "$HOOK"; then
- pass "pre-commit hook contains idempotency marker"
-else
- fail "pre-commit hook missing idempotency marker"
-fi
-
-# --- 4. Appends to existing hook; existing content retained ---
-echo ""
-echo "--- appends to existing hook; prior content retained ---"
-REPO3="$(make_repo)"
-trap 'rm -rf "$REPO3"' EXIT
-HOOK3="$REPO3/.git/hooks/pre-commit"
-printf '#!/usr/bin/env bash\nnpm test\n' > "$HOOK3"
-chmod +x "$HOOK3"
-run_setup "$REPO3" > /dev/null
-if grep -q "npm test" "$HOOK3"; then
- pass "existing hook content retained after append"
-else
- fail "existing hook content lost after append"
-fi
-if grep -q "gitleaks git --staged" "$HOOK3"; then
- pass "gitleaks block appended to existing hook"
-else
- fail "gitleaks block missing after append"
-fi
-
-# --- 5. Second run replaces stale block; existing content still retained ---
-echo ""
-echo "--- second run replaces stale block; existing content still retained ---"
-# Corrupt the gitleaks block to simulate stale content from an older version
-sed -i 's/gitleaks git --staged/gitleaks protect --staged/' "$HOOK3"
-run_setup "$REPO3" > /dev/null
-if grep -q "npm test" "$HOOK3"; then
- pass "existing content retained after block replacement"
-else
- fail "existing content lost after block replacement"
-fi
-marker_count="$(grep -c "# managed by setup-gitleaks.sh" "$HOOK3")"
-if [ "$marker_count" -eq 1 ]; then
- pass "gitleaks block appears exactly once after second run"
-else
- fail "gitleaks block duplicated — found $marker_count occurrences of marker"
-fi
-if grep -q "gitleaks git --staged" "$HOOK3"; then
- pass "stale gitleaks command replaced with current command"
-else
- fail "stale gitleaks command not replaced — block was skipped, not updated"
-fi
-
-# --- 6. Third run still idempotent ---
-echo ""
-echo "--- repeated runs stay idempotent ---"
-run_setup "$REPO3" > /dev/null
-run_setup "$REPO3" > /dev/null
-marker_count="$(grep -c "# managed by setup-gitleaks.sh" "$HOOK3")"
-if [ "$marker_count" -eq 1 ]; then
- pass "gitleaks block still appears exactly once after four total runs"
-else
- fail "gitleaks block duplicated — found $marker_count occurrences after four runs"
-fi
-
-echo ""
-echo "Results: $PASS passed, $FAIL failed"
-[[ $FAIL -eq 0 ]]
diff --git a/tests/test-setup-hooks.sh b/tests/test-setup-hooks.sh
deleted file mode 100644
index 372047f..0000000
--- a/tests/test-setup-hooks.sh
+++ /dev/null
@@ -1,210 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-
-REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
-SCRIPT="$REPO_ROOT/scripts/setup-hooks.sh"
-PASS=0
-FAIL=0
-
-pass() { echo " PASS: $1"; PASS=$((PASS + 1)); }
-fail() { echo " FAIL: $1"; FAIL=$((FAIL + 1)); }
-
-# Helper: make a bare git repo with no hooks yet
-make_repo() {
- local dir
- dir="$(mktemp -d)"
- git -C "$dir" init -q
- echo "$dir"
-}
-
-# Fake binaries for tools we don't want to install-check during tests
-FAKE_BIN="$(mktemp -d)"
-trap 'rm -rf "$FAKE_BIN"' EXIT
-for tool in shellcheck jq yq; do
- printf '#!/bin/sh\necho "fake %s"\n' "$tool" > "$FAKE_BIN/$tool"
- chmod +x "$FAKE_BIN/$tool"
-done
-export PATH="$FAKE_BIN:$PATH"
-
-# --- 1. Rejects non-git directory ---
-echo ""
-echo "--- rejects non-git directory ---"
-NON_GIT="$(mktemp -d)"
-trap 'rm -rf "$NON_GIT"' EXIT
-if bash "$SCRIPT" "$NON_GIT" > /dev/null 2>&1; then
- fail "exited 0 for non-git directory — expected exit 1"
-else
- pass "exits non-zero for non-git directory"
-fi
-
-# --- 2. Creates commit-msg hook ---
-echo ""
-echo "--- creates commit-msg hook ---"
-REPO="$(make_repo)"
-trap 'rm -rf "$REPO"' EXIT
-bash "$SCRIPT" "$REPO" > /dev/null 2>&1
-HOOK="$REPO/.git/hooks/commit-msg"
-if [[ -f "$HOOK" ]]; then
- pass "commit-msg hook file created"
-else
- fail "commit-msg hook not created"
-fi
-if [[ -x "$HOOK" ]]; then
- pass "commit-msg hook is executable"
-else
- fail "commit-msg hook is not executable"
-fi
-if grep -q "# managed by setup-hooks.sh" "$HOOK"; then
- pass "commit-msg hook contains idempotency marker"
-else
- fail "commit-msg hook missing idempotency marker"
-fi
-
-# --- 3. commit-msg hook validates conventional commits ---
-echo ""
-echo "--- commit-msg hook: valid message passes ---"
-REPO2="$(make_repo)"
-trap 'rm -rf "$REPO2"' EXIT
-bash "$SCRIPT" "$REPO2" > /dev/null 2>&1
-HOOK2="$REPO2/.git/hooks/commit-msg"
-TMPFILE="$(mktemp)"
-trap 'rm -f "$TMPFILE"' EXIT
-
-for valid_msg in "feat: add validation" "fix(core): correct path resolution" "chore!: drop python dep" "docs: update readme" "refactor(hooks): extract marker logic"; do
- echo "$valid_msg" > "$TMPFILE"
- if bash "$HOOK2" "$TMPFILE" > /dev/null 2>&1; then
- pass "commit-msg hook accepts: $valid_msg"
- else
- fail "commit-msg hook wrongly rejected: $valid_msg"
- fi
-done
-
-echo ""
-echo "--- commit-msg hook: invalid message is rejected ---"
-for invalid_msg in "added some stuff" "WIP" "Fix the thing" "FEAT: bad case" "feat bad colon"; do
- echo "$invalid_msg" > "$TMPFILE"
- if bash "$HOOK2" "$TMPFILE" > /dev/null 2>&1; then
- fail "commit-msg hook wrongly accepted: $invalid_msg"
- else
- pass "commit-msg hook rejects: $invalid_msg"
- fi
-done
-
-# --- 4. Appends pre-commit validation block ---
-echo ""
-echo "--- appends validation block to pre-commit hook ---"
-REPO3="$(make_repo)"
-trap 'rm -rf "$REPO3"' EXIT
-bash "$SCRIPT" "$REPO3" > /dev/null 2>&1
-PRE_COMMIT="$REPO3/.git/hooks/pre-commit"
-if [[ -f "$PRE_COMMIT" ]]; then
- pass "pre-commit hook created"
-else
- fail "pre-commit hook not created"
-fi
-if grep -q "shellcheck" "$PRE_COMMIT"; then
- pass "pre-commit hook contains shellcheck"
-else
- fail "pre-commit hook missing shellcheck"
-fi
-if grep -q "jq" "$PRE_COMMIT"; then
- pass "pre-commit hook contains jq"
-else
- fail "pre-commit hook missing jq"
-fi
-if grep -q "SKILL.md" "$PRE_COMMIT"; then
- pass "pre-commit hook contains SKILL.md frontmatter check"
-else
- fail "pre-commit hook missing SKILL.md frontmatter check"
-fi
-
-# --- 5. Creates pre-push hook ---
-echo ""
-echo "--- creates pre-push hook ---"
-REPO4="$(make_repo)"
-trap 'rm -rf "$REPO4"' EXIT
-bash "$SCRIPT" "$REPO4" > /dev/null 2>&1
-PUSH_HOOK="$REPO4/.git/hooks/pre-push"
-if [[ -f "$PUSH_HOOK" ]]; then
- pass "pre-push hook created"
-else
- fail "pre-push hook not created"
-fi
-if [[ -x "$PUSH_HOOK" ]]; then
- pass "pre-push hook is executable"
-else
- fail "pre-push hook is not executable"
-fi
-if grep -q "check-manifests" "$PUSH_HOOK"; then
- pass "pre-push hook calls check-manifests.sh"
-else
- fail "pre-push hook missing check-manifests.sh call"
-fi
-if grep -q "run-tests.sh" "$PUSH_HOOK"; then
- pass "pre-push hook calls run-tests.sh"
-else
- fail "pre-push hook missing run-tests.sh call"
-fi
-
-# --- 6. Idempotent: second run replaces each block exactly once ---
-echo ""
-echo "--- idempotent: second run does not duplicate blocks ---"
-REPO5="$(make_repo)"
-trap 'rm -rf "$REPO5"' EXIT
-bash "$SCRIPT" "$REPO5" > /dev/null 2>&1
-bash "$SCRIPT" "$REPO5" > /dev/null 2>&1
-bash "$SCRIPT" "$REPO5" > /dev/null 2>&1
-
-for hook_file in "$REPO5/.git/hooks/commit-msg" "$REPO5/.git/hooks/pre-commit" "$REPO5/.git/hooks/pre-push"; do
- count=$(grep -c "# managed by setup-hooks.sh" "$hook_file" || true)
- hook_name="$(basename "$hook_file")"
- if [[ "$count" -eq 1 ]]; then
- pass "idempotent: $hook_name marker appears exactly once after 3 runs"
- else
- fail "idempotent: $hook_name marker appears $count times — block duplicated"
- fi
- if [[ -x "$hook_file" ]]; then
- pass "idempotent: $hook_name remains executable after 3 runs"
- else
- fail "idempotent: $hook_name lost executable bit after repeated runs"
- fi
-done
-
-# --- 7. Setup installs tools; no "not installed" warnings when tools present ---
-echo ""
-echo "--- setup does not warn when tools are available ---"
-REPO6="$(make_repo)"
-trap 'rm -rf "$REPO6"' EXIT
-output6="$(bash "$SCRIPT" "$REPO6" 2>&1)"
-for tool in shellcheck jq yq; do
- if echo "$output6" | grep -qi "$tool not installed\|$tool.*not found"; then
- fail "setup warned about missing $tool — should install or already be present"
- else
- pass "setup emits no 'not installed' warning for: $tool (present or installed)"
- fi
-done
-
-echo ""
-echo "--- pre-commit hook retains runtime fallback for missing tools ---"
-PRE_COMMIT6="$REPO6/.git/hooks/pre-commit"
-for tool in shellcheck jq yq; do
- if grep -q "Warning:.*$tool\|$tool.*not installed\|$tool.*skipped" "$PRE_COMMIT6"; then
- pass "pre-commit hook has runtime fallback for missing: $tool"
- else
- fail "pre-commit hook missing runtime fallback for: $tool"
- fi
-done
-
-echo ""
-echo "--- setup exits 0 when tools are present ---"
-REPO7="$(make_repo)"
-trap 'rm -rf "$REPO7"' EXIT
-if bash "$SCRIPT" "$REPO7" > /dev/null 2>&1; then
- pass "setup exits 0 when tools are present"
-else
- fail "setup exited non-zero unexpectedly"
-fi
-
-echo ""
-echo "Results: $PASS passed, $FAIL failed"
-[[ $FAIL -eq 0 ]]