{"_id":"@air-bizapps/crucible-cli","_rev":"8-b16086d4221931979cce04def6bd48c4","name":"@air-bizapps/crucible-cli","dist-tags":{"latest":"1.5.0"},"versions":{"1.0.0":{"name":"@air-bizapps/crucible-cli","version":"1.0.0","license":"MIT","_id":"@air-bizapps/crucible-cli@1.0.0","maintainers":[{"name":"mralisson","email":"mralisson@gmail.com"},{"name":"alissonaguiar-air","email":"alisson@aircompany.ai"},{"name":"bruno-araujo-air","email":"bruno.rodrigues@aircompany.ai"},{"name":"deocleciano.junior","email":"deocleciano.junior@aircompany.ai"}],"homepage":"https://github.com/air-bizapps/crucible-cli#readme","bugs":{"url":"https://github.com/air-bizapps/crucible-cli/issues"},"bin":{"crucible":"dist/cli.js"},"dist":{"shasum":"110255772626d5bd9e24789d7380cbb99bdabc5b","tarball":"https://registry.npmjs.org/@air-bizapps/crucible-cli/-/crucible-cli-1.0.0.tgz","fileCount":3,"integrity":"sha512-tL10rMkuNS75/0+axUWfk4ArCkphudrKTRTyfA+k4ojcrLcBnUgW+3/sHDnbN/GqcQSVOJ5C+48wWVULgijotg==","signatures":[{"sig":"MEUCIQCqA1V3dZ/cNikMO0QmGV6QS7r11teVuj3Xxg0RkHNrawIgZnykX1/2qgAxQIWxC9YYktHn4IwpOHpAcQ3yA5TKByY=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":174834},"type":"module","engines":{"node":">=20.19.0"},"gitHead":"395b07f5e01a41544715149b5254f6f09d6c1f1f","scripts":{"dev":"tsx src/cli.tsx","test":"vitest run","build":"tsup","test:tty":"python3 scripts/e2e-tty.py","typecheck":"tsc --noEmit","smoke:real":"python3 scripts/smoke-real.py","test:watch":"vitest"},"_npmUser":{"name":"deocleciano.junior","email":"deocleciano.junior@aircompany.ai"},"repository":{"url":"git+https://github.com/air-bizapps/crucible-cli.git","type":"git"},"_npmVersion":"11.6.1","description":"Vibe coding CLI conectado ao seu LiteLLM proxy","directories":{},"_nodeVersion":"24.10.0","dependencies":{"ai":"^7.0.42","ink":"^7.1.1","zod":"^4.4.3","diff":"^9.0.0","react":"^19.2.8","picomatch":"^4.0.5","tinyglobby":"^0.2.17","html-to-text":"^10.0.0","string-width":"^8.2.2","ink-text-input":"^6.0.0","@ai-sdk/openai-compatible":"^3.0.17","@modelcontextprotocol/sdk":"^1.30.0"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.23.1","tsup":"^8.5.1","vitest":"^4.1.10","typescript":"^5.9.3","@types/node":"^26.1.2","@types/react":"^19.2.17","@types/picomatch":"^4.0.3","@types/html-to-text":"^9.0.4","ink-testing-library":"^4.0.0"},"_npmOperationalInternal":{"tmp":"tmp/crucible-cli_1.0.0_1785942011205_0.7079542132884684","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"name":"@air-bizapps/crucible-cli","version":"1.0.1","license":"MIT","_id":"@air-bizapps/crucible-cli@1.0.1","maintainers":[{"name":"mralisson","email":"mralisson@gmail.com"},{"name":"alissonaguiar-air","email":"alisson@aircompany.ai"},{"name":"bruno-araujo-air","email":"bruno.rodrigues@aircompany.ai"},{"name":"deocleciano.junior","email":"deocleciano.junior@aircompany.ai"}],"homepage":"https://github.com/air-bizapps/crucible-cli#readme","bugs":{"url":"https://github.com/air-bizapps/crucible-cli/issues"},"bin":{"crucible":"dist/cli.js"},"dist":{"shasum":"cc1762c19edcb7f1ae8ae513c49ffe8ad1139a53","tarball":"https://registry.npmjs.org/@air-bizapps/crucible-cli/-/crucible-cli-1.0.1.tgz","fileCount":3,"integrity":"sha512-rGFq2sFNjehV4CWqNH40I+MzHryF2sZaqvtjhZLo3b+opYZ+rxQDtnMsKxSVszwXtR0ihpZPv+GIYnKsHWkpLA==","signatures":[{"sig":"MEYCIQDh979SLeqpVxYthX5lUYy9Z7X1+/PvXquiAwnp/90BegIhAOsMLzHO5ucbrlDRgFy75U4dHfm/TwcL9XnMoahSuypt","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":174834},"type":"module","engines":{"node":">=20.19.0"},"gitHead":"3d20ac0c6657356e8298203061e3a482b171de4e","scripts":{"dev":"tsx src/cli.tsx","test":"vitest run","build":"tsup","test:tty":"python3 scripts/e2e-tty.py","typecheck":"tsc --noEmit","smoke:real":"python3 scripts/smoke-real.py","test:watch":"vitest"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:dfaa05a3-a1b4-4a9d-aefe-32a84a4a9c76"}},"repository":{"url":"git+https://github.com/air-bizapps/crucible-cli.git","type":"git"},"_npmVersion":"11.19.0","description":"Vibe coding CLI conectado ao seu LiteLLM proxy","directories":{},"_nodeVersion":"22.23.1","dependencies":{"ai":"^7.0.42","ink":"^7.1.1","zod":"^4.4.3","diff":"^9.0.0","react":"^19.2.8","picomatch":"^4.0.5","tinyglobby":"^0.2.17","html-to-text":"^10.0.0","string-width":"^8.2.2","ink-text-input":"^6.0.0","@ai-sdk/openai-compatible":"^3.0.17","@modelcontextprotocol/sdk":"^1.30.0"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.23.1","tsup":"^8.5.1","vitest":"^4.1.10","typescript":"^5.9.3","@types/node":"^26.1.2","@types/react":"^19.2.17","@types/picomatch":"^4.0.3","@types/html-to-text":"^9.0.4","ink-testing-library":"^4.0.0"},"_npmOperationalInternal":{"tmp":"tmp/crucible-cli_1.0.1_1785943765016_0.5724191990355245","host":"s3://npm-registry-packages-npm-production"}},"1.1.0":{"name":"@air-bizapps/crucible-cli","version":"1.1.0","license":"MIT","_id":"@air-bizapps/crucible-cli@1.1.0","maintainers":[{"name":"mralisson","email":"mralisson@gmail.com"},{"name":"alissonaguiar-air","email":"alisson@aircompany.ai"},{"name":"bruno-araujo-air","email":"bruno.rodrigues@aircompany.ai"},{"name":"deocleciano.junior","email":"deocleciano.junior@aircompany.ai"}],"homepage":"https://github.com/air-bizapps/crucible-cli#readme","bugs":{"url":"https://github.com/air-bizapps/crucible-cli/issues"},"bin":{"crucible":"dist/cli.js"},"dist":{"shasum":"1194467f351683cfc5ea8db153569de3ffaa482d","tarball":"https://registry.npmjs.org/@air-bizapps/crucible-cli/-/crucible-cli-1.1.0.tgz","fileCount":3,"integrity":"sha512-c8qKGlr9vMTRvbWFbFxevLRVF313towNIvxf2hkkdiTvY21CRKIxhKQv+VfkqLzqgu5Qr0aHK51TVprq7nbFVA==","signatures":[{"sig":"MEYCIQDmGw9p5ohabR85WQ6hRSpykD+GRPIZWQipe2xmZ8HQowIhAJ3jUwxvFyfovn+nBmslxfsytLAtZm0ygz0IRJVA0Z+w","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":177672},"type":"module","engines":{"node":">=20.19.0"},"gitHead":"0ba6cbf079b01b299f08edc25842954311092ec8","scripts":{"dev":"tsx src/cli.tsx","test":"vitest run","build":"tsup","test:tty":"python3 scripts/e2e-tty.py","typecheck":"tsc --noEmit","smoke:real":"python3 scripts/smoke-real.py","test:watch":"vitest"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:dfaa05a3-a1b4-4a9d-aefe-32a84a4a9c76"}},"repository":{"url":"git+https://github.com/air-bizapps/crucible-cli.git","type":"git"},"_npmVersion":"11.19.0","description":"Vibe coding CLI conectado ao seu LiteLLM proxy","directories":{},"_nodeVersion":"22.23.1","dependencies":{"ai":"^7.0.42","ink":"^7.1.1","zod":"^4.4.3","diff":"^9.0.0","react":"^19.2.8","picomatch":"^4.0.5","tinyglobby":"^0.2.17","html-to-text":"^10.0.0","string-width":"^8.2.2","ink-text-input":"^6.0.0","@ai-sdk/openai-compatible":"^3.0.17","@modelcontextprotocol/sdk":"^1.30.0"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.23.1","tsup":"^8.5.1","vitest":"^4.1.10","typescript":"^5.9.3","@types/node":"^26.1.2","@types/react":"^19.2.17","@types/picomatch":"^4.0.3","@types/html-to-text":"^9.0.4","ink-testing-library":"^4.0.0"},"_npmOperationalInternal":{"tmp":"tmp/crucible-cli_1.1.0_1785953436717_0.27206516209315534","host":"s3://npm-registry-packages-npm-production"}},"1.2.0":{"name":"@air-bizapps/crucible-cli","version":"1.2.0","license":"MIT","_id":"@air-bizapps/crucible-cli@1.2.0","maintainers":[{"name":"mralisson","email":"mralisson@gmail.com"},{"name":"alissonaguiar-air","email":"alisson@aircompany.ai"},{"name":"bruno-araujo-air","email":"bruno.rodrigues@aircompany.ai"},{"name":"deocleciano.junior","email":"deocleciano.junior@aircompany.ai"}],"homepage":"https://github.com/air-bizapps/crucible-cli#readme","bugs":{"url":"https://github.com/air-bizapps/crucible-cli/issues"},"bin":{"crucible":"dist/cli.js"},"dist":{"shasum":"4af5af1ded51c042e09792c6603d2c412fa467dd","tarball":"https://registry.npmjs.org/@air-bizapps/crucible-cli/-/crucible-cli-1.2.0.tgz","fileCount":11,"integrity":"sha512-yF8THEP+G6Xim9EuBpaB6EvEiZwLu4BIK+/59y78Yw/59OPIOOW7iXpbxl0sDsulcqv8AdDQdeJUGJRoB7Xbqw==","signatures":[{"sig":"MEUCIEzovg/U0qYiCFRIHA68wlvsbq8l0sQIbAKOisEfVhBQAiEAwmVelJhBbOYcJCjYXaj6J/E0ffi8snQtO73uV5ob+Zc=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":195934},"type":"module","engines":{"node":">=20.19.0"},"gitHead":"bf992942847e59b8180c3689d678ea0ff3e84e42","scripts":{"dev":"tsx src/cli.tsx","test":"vitest run","build":"tsup","test:tty":"python3 scripts/e2e-tty.py","typecheck":"tsc --noEmit","smoke:real":"python3 scripts/smoke-real.py","test:watch":"vitest"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:dfaa05a3-a1b4-4a9d-aefe-32a84a4a9c76"}},"repository":{"url":"git+https://github.com/air-bizapps/crucible-cli.git","type":"git"},"_npmVersion":"11.19.0","description":"Vibe coding CLI conectado ao seu LiteLLM proxy","directories":{},"_nodeVersion":"22.23.1","dependencies":{"ai":"^7.0.42","ink":"^7.1.1","zod":"^4.4.3","diff":"^9.0.0","react":"^19.2.8","picomatch":"^4.0.5","tinyglobby":"^0.2.17","html-to-text":"^10.0.0","string-width":"^8.2.2","ink-text-input":"^6.0.0","@ai-sdk/openai-compatible":"^3.0.17","@modelcontextprotocol/sdk":"^1.30.0"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.23.1","tsup":"^8.5.1","vitest":"^4.1.10","typescript":"^5.9.3","@types/node":"^26.1.2","@types/react":"^19.2.17","@types/picomatch":"^4.0.3","@types/html-to-text":"^9.0.4","ink-testing-library":"^4.0.0"},"_npmOperationalInternal":{"tmp":"tmp/crucible-cli_1.2.0_1785958042661_0.19771596108967016","host":"s3://npm-registry-packages-npm-production"}},"1.3.0":{"name":"@air-bizapps/crucible-cli","version":"1.3.0","license":"MIT","_id":"@air-bizapps/crucible-cli@1.3.0","maintainers":[{"name":"mralisson","email":"mralisson@gmail.com"},{"name":"alissonaguiar-air","email":"alisson@aircompany.ai"},{"name":"bruno-araujo-air","email":"bruno.rodrigues@aircompany.ai"},{"name":"deocleciano.junior","email":"deocleciano.junior@aircompany.ai"}],"homepage":"https://github.com/air-bizapps/crucible-cli#readme","bugs":{"url":"https://github.com/air-bizapps/crucible-cli/issues"},"bin":{"crucible":"dist/cli.js"},"dist":{"shasum":"0eaca8dfcd479ed22f1762ff1b141c0912e6da34","tarball":"https://registry.npmjs.org/@air-bizapps/crucible-cli/-/crucible-cli-1.3.0.tgz","fileCount":11,"integrity":"sha512-wPiQU070k9dNUZphjHbURlLWl7ipqemT6N/Z5jM0GtldtU6N6wEZowfWISnmHVb/4JibfqJzA2mDdgRrQhbpdQ==","signatures":[{"sig":"MEUCIAwWRm5mdFPesjc4iElqh25tAo/4ApAF7pvUygbwT24GAiEA9AbuG/uy/+h+WkwrrIUhThpL2YTdDQ2qLF4tYp6gA5k=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":199652},"type":"module","engines":{"node":">=20.19.0"},"gitHead":"ec570ad2db8404b6f33c07658af58f21b558924f","scripts":{"dev":"tsx src/cli.tsx","test":"vitest run","build":"tsup","test:tty":"python3 scripts/e2e-tty.py","typecheck":"tsc --noEmit","smoke:real":"python3 scripts/smoke-real.py","test:watch":"vitest"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:dfaa05a3-a1b4-4a9d-aefe-32a84a4a9c76"}},"repository":{"url":"git+https://github.com/air-bizapps/crucible-cli.git","type":"git"},"_npmVersion":"11.19.0","description":"Vibe coding CLI conectado ao seu LiteLLM proxy","directories":{},"_nodeVersion":"22.23.1","dependencies":{"ai":"^7.0.42","ink":"^7.1.1","zod":"^4.4.3","diff":"^9.0.0","react":"^19.2.8","picomatch":"^4.0.5","tinyglobby":"^0.2.17","html-to-text":"^10.0.0","string-width":"^8.2.2","ink-text-input":"^6.0.0","@ai-sdk/openai-compatible":"^3.0.17","@modelcontextprotocol/sdk":"^1.30.0"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.23.1","tsup":"^8.5.1","vitest":"^4.1.10","typescript":"^5.9.3","@types/node":"^26.1.2","@types/react":"^19.2.17","@types/picomatch":"^4.0.3","@types/html-to-text":"^9.0.4","ink-testing-library":"^4.0.0"},"_npmOperationalInternal":{"tmp":"tmp/crucible-cli_1.3.0_1786148219775_0.39562978170637497","host":"s3://npm-registry-packages-npm-production"}},"1.3.1":{"name":"@air-bizapps/crucible-cli","version":"1.3.1","license":"MIT","_id":"@air-bizapps/crucible-cli@1.3.1","maintainers":[{"name":"mralisson","email":"mralisson@gmail.com"},{"name":"alissonaguiar-air","email":"alisson@aircompany.ai"},{"name":"bruno-araujo-air","email":"bruno.rodrigues@aircompany.ai"},{"name":"deocleciano.junior","email":"deocleciano.junior@aircompany.ai"}],"homepage":"https://github.com/air-bizapps/crucible-cli#readme","bugs":{"url":"https://github.com/air-bizapps/crucible-cli/issues"},"bin":{"crucible":"dist/cli.js"},"dist":{"shasum":"f8bf7e4869ce9296599e45cdedb1ac1792fa2158","tarball":"https://registry.npmjs.org/@air-bizapps/crucible-cli/-/crucible-cli-1.3.1.tgz","fileCount":11,"integrity":"sha512-szx1wWUy56aiUQpBvk9hZCCB8/h2/lPwGhsrc8LUhyz157qf0bhK0l7jUcfvDe4+D9T0EoFRC5IoZwrP/2dFYg==","signatures":[{"sig":"MEUCIQCDbgXOzrmiQA9Wd8lR1NHqGnPHG21uQy5nsAvYwsrFuAIgEiSc/Gym5Gsh6tNWe3zkcKj1PSiXNGwz2WYh/dnc/No=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":206083},"type":"module","engines":{"node":">=20.19.0"},"gitHead":"cd17850e06f29c4834063e4911bfd33245cd50f7","scripts":{"dev":"tsx src/cli.tsx","test":"vitest run","build":"tsup","test:tty":"python3 scripts/e2e-tty.py","typecheck":"tsc --noEmit","smoke:real":"python3 scripts/smoke-real.py","test:watch":"vitest"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:dfaa05a3-a1b4-4a9d-aefe-32a84a4a9c76"}},"repository":{"url":"git+https://github.com/air-bizapps/crucible-cli.git","type":"git"},"_npmVersion":"11.19.0","description":"Vibe coding CLI conectado ao seu LiteLLM proxy","directories":{},"_nodeVersion":"22.23.1","dependencies":{"ai":"^7.0.42","ink":"^7.1.1","zod":"^4.4.3","diff":"^9.0.0","react":"^19.2.8","picomatch":"^4.0.5","tinyglobby":"^0.2.17","html-to-text":"^10.0.0","string-width":"^8.2.2","ink-text-input":"^6.0.0","@ai-sdk/openai-compatible":"^3.0.17","@modelcontextprotocol/sdk":"^1.30.0"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.23.1","tsup":"^8.5.1","vitest":"^4.1.10","typescript":"^5.9.3","@types/node":"^26.1.2","@types/react":"^19.2.17","@types/picomatch":"^4.0.3","@types/html-to-text":"^9.0.4","ink-testing-library":"^4.0.0"},"_npmOperationalInternal":{"tmp":"tmp/crucible-cli_1.3.1_1786478020921_0.9658280642307833","host":"s3://npm-registry-packages-npm-production"}},"1.4.0":{"name":"@air-bizapps/crucible-cli","version":"1.4.0","license":"MIT","_id":"@air-bizapps/crucible-cli@1.4.0","maintainers":[{"name":"mralisson","email":"mralisson@gmail.com"},{"name":"alissonaguiar-air","email":"alisson@aircompany.ai"},{"name":"bruno-araujo-air","email":"bruno.rodrigues@aircompany.ai"},{"name":"deocleciano.junior","email":"deocleciano.junior@aircompany.ai"}],"homepage":"https://github.com/air-bizapps/crucible-cli#readme","bugs":{"url":"https://github.com/air-bizapps/crucible-cli/issues"},"bin":{"crucible":"dist/cli.js"},"dist":{"shasum":"e65caf3aac071a8f42ec75eb3aa4c3c14dec8a8e","tarball":"https://registry.npmjs.org/@air-bizapps/crucible-cli/-/crucible-cli-1.4.0.tgz","fileCount":17,"integrity":"sha512-T2nSqvPzLn2Ds2fcXwIsTSMhoBD01x+HYa90dfdrk8jGNGVPIURFE8eVDFaZiPT3PpBFkqOLlv/3D5T3Ymgr4Q==","signatures":[{"sig":"MEUCIGkgvhcj+KiLbRH2281Ogmq4BBWoIh+6QKgmqMdo85c8AiEAyT8/9IfAZyhAoSszl4HHtt+Pe1SM4JaWdy0+7EChXDw=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":300347},"type":"module","engines":{"node":">=20.19.0"},"gitHead":"32e9a82976b344f236972e8a04e38686331bdfad","scripts":{"dev":"tsx src/cli.tsx","test":"vitest run","build":"tsup","test:tty":"python3 scripts/e2e-tty.py","typecheck":"tsc --noEmit","smoke:real":"python3 scripts/smoke-real.py","test:watch":"vitest"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:dfaa05a3-a1b4-4a9d-aefe-32a84a4a9c76"}},"repository":{"url":"git+https://github.com/air-bizapps/crucible-cli.git","type":"git"},"_npmVersion":"11.19.0","description":"Vibe coding CLI conectado ao seu LiteLLM proxy","directories":{},"_nodeVersion":"22.23.2","dependencies":{"ai":"^7.0.42","ink":"^7.1.1","zod":"^4.4.3","diff":"^9.0.0","react":"^19.2.8","picomatch":"^4.0.5","tinyglobby":"^0.2.17","html-to-text":"^10.0.0","string-width":"^8.2.2","ink-text-input":"^6.0.0","@ai-sdk/openai-compatible":"^3.0.17","@modelcontextprotocol/sdk":"^1.30.0"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.23.1","tsup":"^8.5.1","vitest":"^4.1.10","typescript":"^5.9.3","@types/node":"^26.1.2","@types/react":"^19.2.17","@types/picomatch":"^4.0.3","@types/html-to-text":"^9.0.4","ink-testing-library":"^4.0.0"},"_npmOperationalInternal":{"tmp":"tmp/crucible-cli_1.4.0_1786752943729_0.8588603595616005","host":"s3://npm-registry-packages-npm-production"}},"1.5.0":{"name":"@air-bizapps/crucible-cli","version":"1.5.0","description":"Vibe coding CLI conectado ao seu LiteLLM proxy","repository":{"type":"git","url":"git+https://github.com/air-bizapps/crucible-cli.git"},"type":"module","bin":{"crucible":"dist/cli.js"},"engines":{"node":">=20.19.0"},"scripts":{"build":"tsup","postbuild":"tsx scripts/gen-whats-new.ts","dev":"tsx src/cli.tsx","test":"vitest run","test:watch":"vitest","test:tty":"python3 scripts/e2e-tty.py","smoke:real":"python3 scripts/smoke-real.py","typecheck":"tsc --noEmit"},"dependencies":{"@ai-sdk/openai-compatible":"^3.0.17","@modelcontextprotocol/sdk":"^1.30.0","ai":"^7.0.42","diff":"^9.0.0","html-to-text":"^10.0.0","ink":"^7.1.1","ink-text-input":"^6.0.0","picomatch":"^4.0.5","react":"^19.2.8","string-width":"^8.2.2","tinyglobby":"^0.2.17","zod":"^4.4.3"},"license":"MIT","devDependencies":{"@types/html-to-text":"^9.0.4","@types/node":"^26.1.2","@types/picomatch":"^4.0.3","@types/react":"^19.2.17","ink-testing-library":"^4.0.0","tsup":"^8.5.1","tsx":"^4.23.1","typescript":"^5.9.3","vitest":"^4.1.10"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"gitHead":"23ed5071f3bf963b0307f8840f1f4d7c0d5c3bcd","_id":"@air-bizapps/crucible-cli@1.5.0","bugs":{"url":"https://github.com/air-bizapps/crucible-cli/issues"},"homepage":"https://github.com/air-bizapps/crucible-cli#readme","_nodeVersion":"22.23.2","_npmVersion":"11.19.0","dist":{"integrity":"sha512-SmXnzXTAEzUlGIpcx7sQzhlFtEWlLlCIi/izYX3x6FIFz2q5rcSXrkqIWXr+hoSnICk7ZasacSoQWZosIiSLgw==","shasum":"d0dd2d7c170b04d746d57bf7d80036f7165b0542","tarball":"https://registry.npmjs.org/@air-bizapps/crucible-cli/-/crucible-cli-1.5.0.tgz","fileCount":18,"unpackedSize":307921,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCGniV7Uj6OIE+d5s62tb+WkuiDUuqNDjSCXRtWNUVCrAIgATyqiGUj59VdOB+F1mbFfTsrG6wUtq5fGY6ph8zqzQ8="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:dfaa05a3-a1b4-4a9d-aefe-32a84a4a9c76"}},"directories":{},"maintainers":[{"name":"mralisson","email":"mralisson@gmail.com"},{"name":"alissonaguiar-air","email":"alisson@aircompany.ai"},{"name":"bruno-araujo-air","email":"bruno.rodrigues@aircompany.ai"},{"name":"deocleciano.junior","email":"deocleciano.junior@aircompany.ai"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/crucible-cli_1.5.0_1786926424060_0.8799244396403696"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-05T15:00:10.999Z","modified":"2026-08-17T00:27:04.369Z","1.0.0":"2026-08-05T15:00:11.429Z","1.0.1":"2026-08-05T15:29:25.161Z","1.1.0":"2026-08-05T18:10:36.866Z","1.2.0":"2026-08-05T19:27:22.834Z","1.3.0":"2026-08-08T00:16:59.936Z","1.3.1":"2026-08-11T19:53:41.062Z","1.4.0":"2026-08-15T00:15:43.887Z","1.5.0":"2026-08-17T00:27:04.195Z"},"bugs":{"url":"https://github.com/air-bizapps/crucible-cli/issues"},"license":"MIT","homepage":"https://github.com/air-bizapps/crucible-cli#readme","repository":{"type":"git","url":"git+https://github.com/air-bizapps/crucible-cli.git"},"description":"Vibe coding CLI conectado ao seu LiteLLM proxy","maintainers":[{"name":"mralisson","email":"mralisson@gmail.com"},{"name":"alissonaguiar-air","email":"alisson@aircompany.ai"},{"name":"bruno-araujo-air","email":"bruno.rodrigues@aircompany.ai"},{"name":"deocleciano.junior","email":"deocleciano.junior@aircompany.ai"}],"readme":"# Crucible CLI\n\nCLI de vibe coding conectado ao seu proxy [LiteLLM](https://docs.litellm.ai/). Um harness de coding completo no terminal: o agente lê e edita arquivos, roda comandos, baixa URLs, fala com servidores MCP, planeja antes de agir e delega tarefas a sub-agentes paralelos — tudo atrás de um modelo de permissões y/n/always com confiança ligada ao conteúdo do projeto.\n\nRecursos, em uma olhada:\n\n- **Permissões e confiança** — leituras livres; escritas, bash, fetch e tools MCP pedem confirmação; regras persistidas em `.crucible/settings.json`; a confiança liga ao conteúdo que você viu, não ao diretório (um `git pull` que muda o que o arquivo concede repergunta).\n- **Escrever fora do projeto** — `--add-dir`/`/add-dir` autorizam outros diretórios, com veto para home/raiz/ancestrais.\n- **Ler a web** — `fetch_url` (HTML → texto) e busca via servidor MCP, com a chave no servidor, não no CLI.\n- **MCP** — servidores com tools, resources e prompts, globais ou por projeto.\n- **Plan mode** — `/plan` explora só-leitura; `/go` executa o plano aprovado.\n- **Skills e hooks** — instruções reusáveis em markdown; comandos de shell em eventos (`pos-escrita`, `pos-turno`).\n- **Sub-agentes** — `spawn_subagents` roda até 4 filhos em paralelo, com tipo `explore`/`plan` (só leitura) ou `general` (herda permissões). Ctrl+C aborta o pai e a fila.\n- **Deny** — `deny` no settings e `--deny` na CLI vencem allow e vencem `allow-all`.\n- **Bash em background** — `bash` com `background: true` não sequestra o turno; `await_task` espera o resultado.\n- **Sessões e rewind** — conversa gravada por turno, `/resume`, e `/rewind` que desfaz arquivos e/ou conversa.\n\n## Instalação\n\nPré-requisitos, antes de clonar:\n\n- **2FA na conta GitHub.** A org `air-bizapps` exige autenticação de dois fatores para acessar o repositório — sem isso, o `git clone` falha com \"You must set up two-factor authentication\" mesmo com uma chave SSH válida cadastrada. Configure em [github.com/settings/security](https://github.com/settings/security).\n- **Chave SSH cadastrada.** Clonar por SSH sem chave pública associada à conta falha com `Permission denied (publickey)`. Adicione a sua em [github.com/settings/keys](https://github.com/settings/keys) (ou clone por HTTPS com um token).\n\n    git clone git@github.com:air-bizapps/crucible-cli.git\n    cd crucible-cli\n    npm install && npm run build && npm link\n\nEm instalações de Node \"de sistema\" (installer oficial, sem nvm/volta), o diretório global do npm pertence ao `root` e o `npm link` acima falha com `EACCES`; rode `sudo npm link`. **Não** contorne com `npm link --prefix <caminho>` — a flag muda o prefixo global e cria um `bin/`/`lib/` falsos dentro do próprio clone (não afeta o `/usr/local` de verdade): o comando roda sem erro, ainda imprime \"added 1 package\", e parece ter funcionado sem ter funcionado.\n\n## Uso\n\n    crucible\n\nNo primeiro uso, o login já vem com a URL do proxy padrão (`https://llm.twinforge.webjump.ai`) preenchida — basta dar Enter e informar sua API key (`sk-...`). Para apontar a outro proxy, edite o campo antes de confirmar; o default fica em `src/defaults.ts`. A config fica em `~/.config/crucible/config.json`.\n\n### Atualização\n\n`npm i -g` não atualiza sozinho. Na abertura, o CLI consulta o registro npm (uma vez a cada 24h, com cache em `~/.config/crucible/atualizacao.json`) e avisa quando há versão nova:\n\n    ⚠ 1.5.0 disponível (você está na 1.4.0) · npm i -g @air-bizapps/crucible-cli@latest\n\nA consulta não atrasa a abertura e é silenciosa em qualquer falha (offline, timeout, registro fora do ar). `CRUCIBLE_NO_UPDATE_CHECK=1` desliga a checagem.\n\n## Modo headless\n\nPara orquestradores e CI (ex.: o adapter `crucible_local` da plataforma TwinForge), o CLI roda sem TTY:\n\n    crucible -p \"corrija os testes\"              # texto final em stdout\n    crucible -p \"...\" --output-format stream-json  # NDJSON: 1 evento por linha\n    crucible -p \"...\" --model gpt-5.2 --resume-session <id>\n    crucible -p \"...\" --model auto                  # harness escolhe o modelo pelo tier do pedido\n    crucible -p \"...\" --permission-mode allow-all   # default: settings\n    crucible --prompt-file pedido.md                # prompt longo sem lutar com a shell\n    crucible -p \"...\" --cwd /outro/repo\n    crucible -p \"...\" --tools read_file,grep,ls     # allowlist de tools builtin\n    crucible -p \"...\" --disallowed-tools bash\n    crucible -p \"...\" --allow 'bash(npm test:*)' --deny 'bash(rm:*)'\n    crucible -p \"...\" --max-turns 20                # teto de passos deste run (vence config.maxSteps)\n    crucible models --json                          # ids de modelos do proxy\n\nEventos do `stream-json` (primeiro `system/init`, último sempre `result` — exceção: falha antes do run começar, como credencial ausente ou sessão desconhecida, emite **só** o `result` de erro; stdout contém **só** JSON — avisos vão para stderr):\n\n    {\"type\":\"system\",\"subtype\":\"init\",\"sessionId\":\"...\",\"model\":\"...\",\"cwd\":\"...\"}\n    {\"type\":\"text\",\"text\":\"...\"}\n    {\"type\":\"tool_use\",\"name\":\"write_file\",\"input\":{\"path\":\"...\"}}\n    {\"type\":\"tool_result\",\"name\":\"write_file\",\"isError\":false,\"output\":\"<truncado>\"}\n    {\"type\":\"result\",\"subtype\":\"success\",\"sessionId\":\"...\",\"model\":\"...\",\"usage\":{\"inputTokens\":0,\"outputTokens\":0},\"costUsd\":0.0123,\"turns\":1}\n\n- **Credenciais:** `CRUCIBLE_BASE_URL`/`CRUCIBLE_API_KEY` no ambiente vencem o `config.json`, campo a campo. Sem credencial: `result` com `errorCode:\"auth\"` e exit 1 — nunca abre login.\n- **Permissões:** `settings` aplica as regras do `.crucible/settings.json` confiado e **nega** o que pediria confirmação (vira `tool_result` com `isError:true`); `allow-all` aprova tudo. Headless nunca pergunta.\n- **Sessões:** mesma store da TUI — um run headless pode ser retomado na TUI e vice-versa. `--resume-session` com id inexistente devolve `errorCode:\"unknown_session\"` e exit 1.\n- **Progresso:** durante o turno, um evento `{\"type\":\"progress\",\"outputTokens\":N}` sai a cada 30 s — sinal de vida para o orquestrador (parsers que não o conhecem podem ignorar o tipo com segurança).\n- **Custo:** `costUsd` vem do pricing do `/model/info` do LiteLLM; `null` quando o proxy não informa preço. `turns` conta os turnos **desta invocação** (um `-p` = 1), não o acumulado da sessão.\n- **Modelo AUTO:** `--model auto` (ver \"Modo AUTO\") vale também no headless; o `system/init` e o `result` sempre carregam o modelo **resolvido** (o real que atendeu o pedido, nunca `crucible:auto`) — um adapter que consome o NDJSON não precisa saber que o AUTO existe.\n- Exit codes: `0` sucesso, `1` erro. `crucible --version` imprime a versão real do pacote (o adapter valida versão mínima por ela).\n- Limitação: prompt começando com `-` é interpretado como flag (proteção contra flag engolir flag) — comece com espaço ou reformule.\n\n## Comandos\n\n| Comando | Descrição |\n|---|---|\n| `/model` | Selecionar o modelo (lista do LiteLLM) ou AUTO, em que o harness escolhe por turno |\n| `/login` | Reconfigurar proxy/key |\n| `/clear` | Limpar a conversa |\n| `/compact` | Resumir a conversa para liberar contexto |\n| `/init` | Gerar o CRUCIBLE.md do projeto |\n| `/resume` | Retomar uma sessão anterior deste diretório |\n| `/rewind` | Voltar arquivos e/ou conversa a um turno anterior |\n| `/add-dir` | Autorizar escrita em outro diretório (sem argumento, lista as raízes ativas) |\n| `/permissions` | Ver e gerenciar as regras de permissão do projeto (`add`/`remove`, `auto on\\|off` para a sessão) |\n| `/mcp` | Listar servidores MCP, tools e prompts |\n| `/cost` | Tokens e custo estimado da sessão (preço vem do proxy, quando informado); em AUTO, detalha por modelo usado |\n| `/precos` | Ver a tabela de preços em uso, com a origem de cada um e a data da última sondagem (`/precos sondar` força uma nova sondagem e regrava o cache) |\n| `/context` | Uso da janela de contexto por categoria (system, histórico, tools, skills) |\n| `/plan` | Entrar no modo de planejamento (só leitura) |\n| `/go` | Aprovar o plano e executar (aceita ajuste: `/go só a parte 1`) |\n| `/skills` | Listar as skills disponíveis |\n| `/hooks` | Listar os hooks configurados |\n| `/novidades` | Ver as novidades desta versão (a abertura mostra as três primeiras) |\n| `/help` | Ajuda |\n| `/exit` | Sair |\n\n## Modo AUTO\n\n`/model` tem uma entrada extra, `AUTO`: em vez de fixar um modelo, o harness escolhe um a cada turno.\n\n**Como o tier é escolhido.** Uma heurística local (sem chamada extra ao modelo) classifica o pedido em `rapido`, `equilibrado` ou `profundo`, nessa ordem de prioridade: `/plan` ligado sempre vira `profundo` (é declaração explícita do usuário, não palpite sobre texto); palavras-chave em PT/EN denunciam a tarefa (`refatora`, `investiga`, `arquitetura`, `debug`... → profundo; `lista`, `mostra`, `roda`... → rápido, só quando o pedido também é curto e de uma linha); sem keyword, o tamanho decide (acima de ~600 caracteres ou 8 linhas vira profundo). Sem nenhuma regra, o pedido cai em `equilibrado`.\n\n**De onde vêm os modelos de cada tier.** Os três tiers saem do preço de cada modelo (de onde esse preço vem, ver \"Se o proxy não expõe preço\" abaixo): o mais barato vira `rapido`, o mais caro vira `profundo`, e `equilibrado` é o modelo mais próximo da média **geométrica** dos dois extremos (a aritmética colaria o meio perto do topo, porque preço de LLM varia em ordens de magnitude). Dá para sobrepor qualquer tier no `config.json`:\n\n    {\n      \"auto\": {\n        \"rapido\": \"gpt-5-mini\",\n        \"equilibrado\": \"gpt-5\",\n        \"profundo\": \"gpt-5.2\"\n      }\n    }\n\nUm tier ausente no override continua derivado do preço; um id que não bate com nenhum modelo já visto em `/model/info` vira aviso (`auto.<tier>: modelo \"...\" não está no catálogo do proxy`) e cai no derivado — mas essa validação só roda quando `/model/info` devolveu algum modelo. Se o catálogo veio **vazio**, não há nada contra o que validar, e o override é aceito como digitado, sem aviso: um id que você escreveu é evidência melhor que um catálogo que o CLI não conseguiu buscar, e um typo aparece no proxy no primeiro uso — sinal imediato.\n\n**Preço zero não conta.** No LiteLLM, custo zero por token quase sempre significa \"não configurado\", não \"grátis\" — por isso a derivação de tiers ignora qualquer modelo com `inputCostPerToken` igual a zero (ou ausente): um modelo de 550B nessas condições não deveria virar o tier `rapido` por acidente. Isso só tira o modelo da derivação automática do AUTO; ele continua listado e selecionável à mão no `/model`, e o `/cost` continua contando esse mesmo zero como custo real — a mesma constante lida de duas formas opostas por dois consumidores diferentes, de propósito.\n\n**O tier não é a última palavra.** O modelo do tier ainda passa por um filtro de contexto antes de o turno sair: se a janela dele não comporta o histórico atual mais uma folga de 8k tokens, o AUTO sobe para o modelo mais barato que couber (e, se nenhum couber, para o de maior janela). O tier anunciado não muda junto — isso é restrição, não classificação —, então numa conversa já longa o transcript pode dizer `AUTO: rapido → <modelo caro>` e o `/cost` pode aparecer dominado por um modelo caro numa sessão de pedidos triviais. Subagente não sofre esse efeito: o contexto dele é a própria task, não o histórico do turno que o disparou.\n\n**Se o proxy não expõe preço, o CLI sonda sozinho.** O preço de cada modelo vem de uma cadeia em camadas, cada uma preenchendo só o que a anterior deixou vazio:\n\n1. **Override completo** — os três tiers declarados em `auto` no `config.json` (o exemplo acima). Palavra final do usuário, sem nenhuma chamada de rede.\n2. **`/model/info`** — sempre tentado, mesmo com override completo (é grátis quando a chave tem acesso).\n3. **Cache em disco** — preços de uma sondagem anterior, lidos de `~/.config/crucible/precos.json`.\n4. **Sondagem (probe)** — só para o que sobrou sem preço depois das três camadas acima.\n\nA sondagem faz duas chamadas **não-streaming** e curtas por modelo, com a saída limitada a 1 token (o header de custo, `x-litellm-response-cost`, não existe em respostas em stream): os dois pontos, com contagens de tokens diferentes entre si, bastam para resolver um sistema de duas equações e chegar ao preço de entrada e ao de saída. Ela acontece **uma vez por modelo por proxy** — o resultado fica em `~/.config/crucible/precos.json`, indexado pela URL do proxy (o mesmo id de modelo em outro proxy pode custar outra coisa), e nas próximas vezes o AUTO lê o cache em vez de sondar de novo. Apagar esse arquivo é seguro: ele só reconstrói os preços na próxima sondagem, ao custo de pagar de novo as chamadas que já tinha economizado. `/precos` mostra a tabela em uso e de onde veio cada preço; `/precos sondar` força uma nova sondagem e regrava o cache, ignorando o que já estava lá.\n\nNeste projeto, contra o proxy próprio, a chave configurada recebe HTTP 403 (`Virtual key is not allowed to call this route`) em `/model/info` — mas isso deixou de ser um beco sem saída: a cadeia cai para o cache e, na primeira vez, para a sondagem, e o AUTO deriva os três tiers sozinho, sem exigir override nenhum. A sondagem custa uma fração de centavo (duas chamadas curtas por modelo sem preço) e avisa antes de rodar — entrada de sistema na TUI, stderr no headless.\n\nSó numa situação mais extrema o AUTO continua de fato indisponível: se nem a sondagem conseguir preço para nenhum modelo (proxy fora do ar, ou a chave também sem acesso a `/v1/chat/completions`). Nesse caso o picker mostra a entrada `AUTO` desabilitada com o motivo, e o transcript avisa que o AUTO não está rodando e qual modelo está sendo usado no lugar: `gpt-5`, uma constante do próprio CLI (`DEFAULT_MODEL`, em `src/defaults.ts`) e **não** o default do seu proxy. Se esse modelo não existir no proxy, quem não trava é o CLI: o turno em si morre na chamada de chat, com o erro do proxy no transcript. As duas saídas abaixo continuam válidas para esse caso extremo:\n\n1. Dar à chave acesso a `/model/info` ou a `/v1/chat/completions` sem streaming (o suficiente para a sondagem funcionar).\n2. Declarar os três tiers explicitamente em `auto` no `config.json` (o exemplo acima) — sempre aceito, sem I/O nenhum, mesmo com o catálogo vazio.\n\nSó um detalhe: o override precisa cobrir os **três** tiers para dispensar preço por completo. Cobertura parcial não fecha o mapa — um tier ausente do override é completado pela cadeia acima (cache ou sondagem) e, só se ela também falhar, some, deixando o AUTO indisponível.\n\n**Modelo que não responde.** Se um modelo não responde — watchdog de inatividade (`idleTimeoutSec`) ou falha de chamada ao proxy — ele é marcado como suspeito e pulado no turno seguinte, sem retry automático dentro do turno que falhou. Se todos os modelos dos tiers estiverem suspeitos, a lista é ignorada — tentar um modelo que talvez tenha voltado é melhor que travar a sessão.\n\n**Onde mais o AUTO entra.** A compactação da conversa (`/compact` e a automática a 90%) sempre roda no tier `rapido` — resumir é mecânico, não precisa do modelo mais caro. Cada subagente de `spawn_subagents` é classificado pela própria task, independente do tier do turno que o disparou. E no headless, `--model auto` ativa o mesmo mecanismo (veja \"Modo headless\").\n\n**O `[auto]` da StatusBar é outra coisa.** A StatusBar pode mostrar duas marcas parecidas ao mesmo tempo, e não têm relação uma com a outra: `[auto]` em amarelo é **auto-aprovação** (tecla `s`, `/permissions auto on|off`) — nada a ver com escolha de modelo. Já `AUTO→gpt-5` é o modo AUTO deste modelo escolhendo por turno. As duas podem aparecer juntas na mesma linha.\n\n## Contexto\n\nO CLI mostra o uso da janela de contexto na barra de status. Aos 80% ele avisa; acima de 90% compacta a conversa automaticamente antes do próximo turno (preservando os últimos 2 turnos literais). `/compact` força a compactação a qualquer momento. `/context` quebra a janela por categoria (system prompt, histórico, tools, skills) — estimado a ~4 chars/token, mais o usage medido do último turno. O raciocínio (thinking) do modelo não entra no histórico: não é re-enviado nem gravado na sessão.\n\n`Shift+Tab` cicla o modo da sessão: Normal → Plan → Auto-aprovação → Normal. Não age no meio de um turno nem de um prompt de permissão.\n\nInstruções de projeto: o system prompt carrega `CLAUDE.md`, `AGENTS.md` e `CRUCIBLE.md` da raiz (nessa ordem — o último vence em conflito). `/init` continua gerando só o `CRUCIBLE.md`.\n\n## Convergência\n\nO agente é pressionado a concluir cedo: releitura idêntica dentro do turno é negada (após uma mutação, reler volta a valer), e quando o orçamento de passos esgota (`maxSteps` no `config.json`, default 50) o turno não morre calado — o CLI força uma última chamada **sem tools** em que o modelo entrega sua melhor conclusão, rotulando o que está verificado e o que é hipótese.\n\n## Anti-stall\n\nProteções contra geração monolítica e streams mudos, todas impostas pelo harness:\n\n- **Escrita em partes**: `write_file`/`edit_file` com argumento acima de 48 KB retornam erro imediato instruindo a dividir (`write_file` aceita `append: true` para as continuações). Feedback de segundos em vez de minutos de stall.\n- **Teto de saída por chamada** (`maxOutputTokens` no `config.json`, default 32768): geração gigante numa chamada só fica impossível; quando o corte acontece, o modelo recebe a instrução de continuar em partes (no máximo 2 realimentações por turno).\n- **Watchdog de inatividade** (`idleTimeoutSec`, default 120): stream sem nenhum sinal é abortado com erro claro — pausado durante a execução de tools (um build longo não é travamento).\n- **Retries visíveis**: quando o SDK repete uma chamada ao proxy (429/5xx), o CLI avisa — entrada de sistema na TUI, stderr no headless.\n\nNota de operação: sessões longas re-enviam o histórico a cada turno — habilite **prefix caching** no proxy (LiteLLM/vLLM) para não pagar o prefixo repetido; a auto-compactação a 90% complementa.\n\n### Configurações do `config.json`\n\n| Campo | Default | Efeito |\n|---|---|---|\n| `maxSteps` | 50 | orçamento de passos por turno (esgotar dispara a última palavra) |\n| `maxOutputTokens` | 32768 | teto de tokens de saída por chamada |\n| `idleTimeoutSec` | 120 | watchdog de inatividade do stream |\n| `auto` | — | override por tier do modo AUTO (`rapido`/`equilibrado`/`profundo`); tier ausente é derivado do preço do `/model/info` (ver \"Modo AUTO\") |\n\n## Permissões\n\nLeituras são livres. Escritas e bash pedem confirmação com 5 teclas: `y` (uma vez), `a` (sempre — salva regra em `.crucible/settings.json`), `c` (sempre para o comando base, só em bash — ex. `bash(npm:*)` em vez de `bash(npm test:*)`), `s` (auto-aprova o resto da sessão, sem gravar nada em disco), `n` (nega). `a` e `c` somem quando não há o que persistir (ver \"Escrever fora do projeto\" para o caso do diretório vetado). Regras: `bash(npm test:*)`, `write(src/**)`, `fetch(example.com)`, `mcp(busca:web_search)`.\n\n`deny` é hard limit: vence `allow` e vence `--permission-mode allow-all`. Vale mesmo sem o projeto confiado (não é concessão). `--deny`/`--allow` na CLI somam ao settings, sem gravar em disco.\n\n    { \"allow\": [\"bash(npm test:*)\"], \"deny\": [\"bash(rm *)\", \"write(**/.env)\"] }\n\nRegras `write(...)` são sempre confinadas ao diretório do projeto: caminhos absolutos ou com `../` que saem do projeto nunca são aprovados automaticamente, nem por `write(**)`. Deny `write` alcança também as raízes adicionais.\n\n### ⚠️ Aviso de segurança: `.crucible/settings.json` é local ao projeto\n\nO arquivo `.crucible/settings.json` fica **dentro do repositório**. Um repositório que você clonou pode trazer o próprio arquivo com regras como `{\"allow\": [\"bash(sh:*)\", \"write(**)\"]}` e pode declarar servidores MCP (que podem executar comandos stdio) — por isso, na primeira execução num diretório cujo settings concede alguma coisa, o CLI mostra o conteúdo do arquivo e pergunta se você confia nele.\n\nA confiança não é \"uma vez por diretório\": ela liga ao **conteúdo específico que você viu** — as regras, os diretórios autorizados e os servidores MCP declarados. Se um `git pull` (ou qualquer edição do arquivo) muda o que o settings concede, o prompt reaparece antes de qualquer concessão valer, pedindo que você confirme o novo conteúdo. **Primeira abertura após esta versão**: em projetos já confiados, o CLI repergunta uma vez (migração interna do formato do registro).\n\nO prompt também exibe qualquer servidor MCP declarado no arquivo. Enquanto você não confiar, o arquivo não concede nada; recusar não fecha o CLI, só ignora o arquivo. A resposta fica gravada fora do repositório, então um clone não consegue se pré-aprovar.\n\nAntes de confiar num repositório de terceiros, **inspecione o arquivo**:\n\n    cat .crucible/settings.json\n\nSe não reconhecer alguma regra ou servidor MCP, apague o arquivo ou responda `n` no prompt.\n\n## Escrever fora do projeto\n\nO projeto é o diretório em que você chamou o `crucible`. Escrever fora dele pede confirmação; apertar `a` autoriza aquele diretório para sempre, gravando em `additionalDirectories` no `.crucible/settings.json`.\n\nTambém dá para autorizar na abertura, com `crucible --add-dir /outro/repo` (a flag pode repetir), ou de dentro da sessão com `/add-dir /outro/repo`. `/add-dir` sem argumento lista as raízes ativas.\n\nTrês coisas que o CLI recusa autorizar, porque a autorização valeria para tudo abaixo delas: a sua home, a raiz do sistema, e qualquer diretório acima do projeto. Nesses casos só o `a` some do prompt (não há o que persistir); `y`, `s` e `n` continuam disponíveis.\n\nRegras `write(...)` continuam valendo **só dentro do projeto**: um `write(**)` no seu settings não alcança os diretórios adicionados.\n\nArquivos escritos fora do projeto entram no snapshot do turno como os de dentro, então o `/rewind` também os desfaz.\n\n## Ler URLs\n\nA tool `fetch_url` baixa uma URL http(s) e entrega o conteúdo como texto (HTML é convertido). Cada fetch pede confirmação mostrando a URL completa; `a` grava a regra `fetch(host)` — só aquele host, com a porta quando houver (`fetch(localhost:3000)` não cobre `localhost:8080`), sem subdomínios.\n\nRedirecionamento para outro host não é seguido automaticamente: a resposta indica o destino e um novo `fetch_url` passa pela permissão de novo. Limites: 15s de timeout, 2 MB de download, só conteúdo de texto.\n\n## Plan mode\n\n`/plan` coloca o agente em modo de planejamento: bash e spawn somem; a única escrita permitida é `.crucible/plan.md`. Quando o plano estiver bom, `/go` abre o review: `j`/`k` navega, `c` comenta a linha, `a` aprova e executa, `s` manda revisar (com os comentários), Esc cancela. `/go sem a parte de docs` leva o ajuste junto na aprovação. Sem o arquivo, `/go` executa o que ficou na conversa. O status mostra `[plan]` enquanto o modo está ativo.\n\nTools MCP marcadas pelo servidor como só-leitura (`readOnlyHint`) continuam disponíveis no modo — e continuam pedindo confirmação, como sempre.\n\n## Skills\n\nSkills são instruções reusáveis em markdown: um `.md` solto ou um diretório com `SKILL.md` (o diretório vence o arquivo de mesmo nome). Crie em `~/.config/crucible/skills/` (globais) ou `.crucible/skills/` (do projeto, que vence colisões de nome). Para puxar de um repo de terceiros (formato `SKILL.md`):\n\n    crucible skill install anthropics/skills --path frontend-design\n    crucible skill install ./minha-skill          # caminho local\n    crucible skill install owner/repo@v1.2 --path skills/review --project\n    crucible skill list\n    crucible skill update                 # reinstala todas com origem gravada\n    crucible skill update review\n    crucible skill remove review\n\nA instalação só copia o diretório da skill (`SKILL.md`, `references/`, `scripts/`). Não roda scripts, não ativa hooks nem MCP. Se a fonte tiver várias skills, `--path` escolhe uma. `--force` substitui. `--project` grava em `.crucible/skills/` do cwd. O SHA do clone (quando houver git) fica em `.install.json`; `skill update` reinstala a partir da origem gravada.\n\n## Workflows\n\nRoteiros nomeados em `.crucible/workflows/<nome>.yaml` (ou `.json`; o projeto vence o global em `~/.config/crucible/workflows/`):\n\n    name: review\n    steps:\n      - type: explore\n        prompt: Diff contra main; liste riscos.\n      - type: plan\n        prompt: Plano mínimo para os riscos reais.\n\n    crucible workflow list\n    crucible workflow run review\n\n`explore`/`plan` rodam só com tools de leitura; `general` é o turno normal. Os passos compartilham a mesma sessão.\n\n## ACP\n\n    crucible agent stdio\n\nJSON-RPC 2.0 no stdio (initialize, session/new, session/load, session/prompt, session/cancel). Para editores que falam [ACP](https://agentclientprotocol.com). Sem servidor WebSocket.\n\nO frontmatter leva uma `description`:\n\n    ---\n    description: Revisa o diff atual com o checklist do time\n    ---\n    Instruções da skill...\n\nO modelo vê as descrições e carrega a skill sozinho quando a tarefa combina (`load_skill`); você também pode disparar direto: `/revisar` (o nome do arquivo vira comando) — com argumentos, `/revisar só o gate`. `/skills` lista tudo.\n\nSkills do projeto carregam sem o prompt de confiança — são texto de prompt, como o CRUCIBLE.md; não executam nada.\n\n## Hooks\n\nHooks são comandos de shell que rodam em eventos, definidos em `hooks` no config global (`~/.config/crucible/config.json`) ou no settings do projeto (`.crucible/settings.json`, atrás do prompt de confiança):\n\n    {\n      \"hooks\": {\n        \"pos-escrita\": [{ \"command\": \"npx prettier --write \\\"$CRUCIBLE_FILE\\\"\" }],\n        \"pos-turno\": [{ \"command\": \"afplay /System/Library/Sounds/Glass.aiff\" }],\n        \"pre-bash\": [{ \"command\": \"echo \\\"$CRUCIBLE_COMMAND\\\" | grep -qv rm || exit 1\" }]\n      }\n    }\n\n`pos-escrita` roda após cada arquivo escrito (com `$CRUCIBLE_FILE` no ambiente); `pos-turno`, ao fim de cada turno. São observadores: rodam em segundo plano, e só aparecem se falharem. `pre-bash` é diferente: roda **antes** do comando, com `$CRUCIBLE_COMMAND`; exit ≠ 0 veta a execução e devolve o stderr ao modelo. `/hooks` lista os ativos.\n\nComo hooks executam comandos, os do projeto entram no prompt de confiança — e mudá-los (por um `git pull`, por exemplo) faz o prompt reaparecer antes de qualquer hook rodar.\n\nO fingerprint de confiança passou a incluir `hooks`: todo projeto já confiado antes desta versão reabre o prompt uma única vez na primeira abertura seguinte, mesmo sem nenhum hook configurado — é esperado, e confirmar de novo resolve.\n\n## Subagentes\n\nO agente pode delegar tarefas independentes a sub-agentes que rodam em paralelo (até 4 por vez) via `spawn_subagents` — úteis para explorar partes diferentes do projeto ao mesmo tempo. Cada tarefa é uma string (tipo `general`) ou `{prompt, type}`:\n\n- `explore` / `plan` — só leitura (não escrevem mesmo com `write(**)` no settings).\n- `general` — herda tools e permissões, mas **nunca pede confirmação**: o que exigiria aprovação ele pula.\n\nO agente principal consolida os resultados. Ctrl+C no turno aborta os filhos em voo e **não inicia** a fila restante.\n\nIsso significa que, sob uma regra já concedida (por exemplo `write(src/**)`), filhos `general` podem escrever nesses caminhos em paralelo sem perguntar por ação — é a mesma autorização que você já deu, aplicada em paralelo. `explore`/`plan` não escrevem, regra ou não. Fora do que o settings libera, `general` nega a ação em vez de perguntar — mas também roda `bash`, `fetch_url` e tools MCP já autorizados pelo `allow`. As escritas via `write_file`/`edit_file` entram no `/rewind` como as suas e disparam os hooks `pos-escrita`; escritas feitas via `bash` não entram no `/rewind`, exatamente como no agente principal.\n\nCtrl+C aborta o turno principal **e** os sub-agentes (os que ainda não começaram saem como `interrompido`). Um `bash` em background (`background: true`) segue até você chamar `await_task` — não é morto pelo abort do turno. `Ctrl+B` no meio de um bash em primeiro plano faz o mesmo: o comando continua, o turno recebe um `task_id` e você volta a falar.\n\n## Servidores MCP\n\nDeclare servidores no config global (`~/.config/crucible/config.json`) ou no settings do projeto (`.crucible/settings.json`, atrás do prompt de confiança):\n\n    {\n      \"mcpServers\": {\n        \"busca\": { \"url\": \"https://seu-servidor-mcp.dev\", \"headers\": { \"authorization\": \"Bearer ...\" } },\n        \"fs\": { \"command\": \"npx\", \"args\": [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/dados\"] }\n      }\n    }\n\nAs tools dos servidores aparecem para o agente como `mcp__servidor__tool` e pedem confirmação; `a` grava `mcp(servidor:tool)` — só aquela tool daquele servidor. Resources são lidos via `list_mcp_resources`/`read_mcp_resource` (o `a` da leitura grava `mcp(servidor:resources)`). Prompts viram comandos: `/mcp__servidor__prompt argumentos`. `/mcp` mostra o estado de tudo.\n\nNome de servidor do projeto que colide com um global é ignorado — o global vence. Servidor que falha na conexão vira aviso; o CLI abre normalmente. A busca na web entra por aqui: aponte um servidor MCP de busca e a chave fica nele, nunca no CLI.\n\n## Sessões\n\nA conversa é gravada a cada turno em `~/.config/crucible/projects/<projeto>/`, fora do repositório. Retome com `crucible --continue` (a última deste diretório), `crucible --resume` (escolhe da lista) ou `/resume` de dentro da sessão.\n\nAo retomar, a tela mostra um resumo compacto (`sessão retomada — N turnos anteriores`) em vez de reconstruir a conversa mensagem a mensagem — mas o histórico inteiro volta para o modelo, só a exibição é resumida. `crucible --continue` num diretório sem sessão anterior não é erro nem no-op: começa uma sessão nova, avisando (`Nenhuma sessão anterior neste diretório; começando uma nova.`).\n\n`/rewind` volta ao estado de um turno anterior e deixa você escolher: só os arquivos, só a conversa, ou os dois. **Rewind não desfaz o que comandos bash executaram** — migration, install e push permanecem.\n\n## Desenvolvimento\n\n    npm run dev        # roda via tsx\n    npm test           # vitest\n    npm run typecheck\n    npm run test:tty   # teste ponta a ponta num pty real (exige build antes)\n\n## Smoke test manual\n\nO build automatizado confirma que `dist/cli.js` é gerado com o shebang correto e que `--version` funciona, mas o fluxo interativo completo depende do seu proxy LiteLLM real e não pode ser validado sem credenciais.\n\nA maior parte do checklist abaixo tem versão automatizada: `npm run smoke:real` (exige build antes e config real em `~/.config/crucible`) roda 13 cenários num pty contra o proxy de verdade — **gasta tokens e tem latência real**, exceto `permissions` (só mexe em `/permissions` e no `.crucible/settings.json` do disco, sem chamar o modelo). `SMOKE_ONLY=write,cost` roda um subconjunto (ids: `write`, `persist`, `cost`, `ctrlc`, `trust`, `skill`, `fetch`, `subagentes`, `permissions`, `auto`, `automodel`, `plan`, `hooks`). O que segue manual, e por quê:\n\n- **login** — precisa de credenciais interativas (URL/key reais); não dá para automatizar sem expor segredo.\n- **`/model`** — selecionar um modelo qualquer da lista mexe na config real do usuário (`~/.config/crucible/config.json`); a seleção livre fica manual, para não trocar o modelo configurado por engano. O fluxo do AUTO tem cobertura automatizada à parte no cenário `automodel` (id diferente de `auto`, que já é o de auto-aprovação) porque ele restaura o modelo original da config no final, sucesso ou falha.\n- **MCP** — exige um servidor MCP externo já configurado (`command`/`url` de verdade); não há um genérico para subir no CI/smoke.\n\nDepois de `npm link`, rode manualmente:\n\n    cd /tmp && mkdir -p crucible-smoke && cd crucible-smoke\n    crucible\n\nE confira cada item:\n\n**Básico**\n\n1. Splash aparece com logo colorido, versão e dicas.\n2. Sem config → fluxo de login pede URL e key; key errada mostra erro; key certa entra no chat.\n3. `/model` lista os modelos reais do proxy; seleção persiste (sair e reabrir mantém).\n4. `/model` → `AUTO`; um pedido curto (`lista os testes`) e um profundo (`refatora o loop`) usam modelos diferentes, visíveis na StatusBar; `/cost` mostra os dois.\n5. Pedir \"crie um arquivo hello.txt com 'oi'\" → PermissionPrompt aparece; `y` cria o arquivo.\n6. Pedir de novo com `a` → regra aparece em `.crucible/settings.json`; próxima escrita no mesmo dir não pergunta.\n7. `/help`, `/clear`, `/compact`, `/cost`, `/exit` funcionam; `/cost` mostra tokens (e custo, se o proxy informar preço).\n8. Ctrl+C durante um turno interrompe; Ctrl+C 2x sai.\n\n**Confiança por conteúdo**\n\n9. Adicione uma regra ao `.crucible/settings.json` à mão e reabra → o prompt de confiança reaparece mostrando o conteúdo; `n` ignora o arquivo, `s` confia. Edite de novo → repergunta.\n\n**Web e MCP**\n\n10. Pedir \"leia https://example.com\" → `fetch_url` pede confirmação com a URL completa; o conteúdo volta como texto.\n11. Declare um servidor MCP em `config.json` (ou no settings) → `/mcp` lista tools/resources/prompts; uma tool MCP pede confirmação; um prompt vira `/mcp__servidor__nome`.\n\n**Plan mode, skills, hooks**\n\n12. `/plan` → o status mostra `[plan]` e o agente não escreve; `/go` executa o plano.\n13. Crie `.crucible/skills/revisar.md` com um frontmatter `description` → `/skills` lista, `/revisar` dispara, e o modelo pode carregá-la sozinho.\n14. Configure um hook `pos-escrita` (ex.: `touch /tmp/hook-rodou`) → após uma escrita, o hook roda; um hook que falha vira aviso no transcript.\n\n**Sub-agentes e rewind**\n\n15. Pedir \"use subagentes para explorar src/ e docs/ em paralelo\" → `spawn_subagents` pede `y`, mostra o andamento e consolida os resultados.\n16. Após algumas escritas, `/rewind` volta arquivos e/ou conversa a um turno anterior; `--continue`/`--resume` retomam a sessão.\n","readmeFilename":"README.md"}