{"_id":"@aman_asmuei/niato","_rev":"7-10c3a833f9397984244138005491430f","name":"@aman_asmuei/niato","dist-tags":{"latest":"1.3.0"},"versions":{"1.0.2":{"name":"@aman_asmuei/niato","version":"1.0.2","keywords":["claude","agent-sdk","intent-routing","ai-agent","anthropic","niat"],"license":"MIT","_id":"@aman_asmuei/niato@1.0.2","maintainers":[{"name":"aman_asmuei","email":"amanasmuei@gmail.com"}],"homepage":"https://github.com/amanasmuei/niato#readme","bugs":{"url":"https://github.com/amanasmuei/niato/issues"},"bin":{"niato":"bin/niato"},"dist":{"shasum":"ec77ecf91a8dd8321e5359c1f2aa1d69b7a525de","tarball":"https://registry.npmjs.org/@aman_asmuei/niato/-/niato-1.0.2.tgz","fileCount":363,"integrity":"sha512-bex+8ckyzav/ycLT8td9ytnUq3ufR7zeItSHGScXNcwsiLJclKTbPBj2ij/MtcHe4J0j8x9homtdlX92dLpWjg==","signatures":[{"sig":"MEYCIQCuZZw46d3mLOeJZROG9tk57Qjaygq0e/zXxdNX0FE8oAIhAJUSbDTy/PA91ftcVU+DATDRJDKQ5j26KjZLY77THaFm","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@aman_asmuei%2fniato@1.0.2","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":426137},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"d7e9d25e23baedf95f4a3f995de9b9c90f33b79f","private":false,"scripts":{"dev":"tsx --env-file=.env src/cli.ts","tui":"tsx --env-file=.env src/cli/tui/index.tsx","chat":"tsx --env-file=.env src/cli-chat.ts","eval":"tsx --env-file=.env src/evals/runner.ts","lint":"eslint .","test":"vitest run","build":"tsc -p tsconfig.build.json && node scripts/copy-prompts.mjs","login":"tsx src/cli-login.ts","dev:tui":"tsx --env-file=.env src/cli-tui.tsx","dev:multi":"tsx --env-file=.env src/cli-multi.ts","typecheck":"tsc --noEmit","test:watch":"vitest"},"_npmUser":{"name":"aman_asmuei","email":"amanasmuei@gmail.com"},"repository":{"url":"git+https://github.com/amanasmuei/niato.git","type":"git"},"_npmVersion":"10.8.2","description":"Intent-routing agent on the Claude Agent SDK. Niato — \"intention\", from the Malay/Indonesian niat — declares before it acts.","directories":{},"_nodeVersion":"20.20.2","dependencies":{"ink":"^7.0.1","zod":"^4.3.6","react":"^19.2.5","ink-spinner":"^5.0.0","@anthropic-ai/claude-agent-sdk":"^0.2.121"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.21.0","eslint":"^10.2.1","vitest":"^4.1.5","typescript":"^6.0.3","@types/node":"^25.6.0","@types/react":"^19.2.14","typescript-eslint":"^8.59.1","ink-testing-library":"^4.0.0"},"_npmOperationalInternal":{"tmp":"tmp/niato_1.0.2_1777602554173_0.5339702706740705","host":"s3://npm-registry-packages-npm-production"}},"1.0.3":{"name":"@aman_asmuei/niato","version":"1.0.3","keywords":["claude","agent-sdk","intent-routing","ai-agent","anthropic","niat"],"license":"MIT","_id":"@aman_asmuei/niato@1.0.3","maintainers":[{"name":"aman_asmuei","email":"amanasmuei@gmail.com"}],"homepage":"https://github.com/amanasmuei/niato#readme","bugs":{"url":"https://github.com/amanasmuei/niato/issues"},"bin":{"niato":"bin/niato"},"dist":{"shasum":"61b8b3fcb5c11d3c3d0fe790b2d889cbcd7d8ade","tarball":"https://registry.npmjs.org/@aman_asmuei/niato/-/niato-1.0.3.tgz","fileCount":363,"integrity":"sha512-JziqSLA+g8/FQZ1/L2mDB3pbquuvsJnAGkrkT3RVviiAC/B0vjVdJeVCiDAopQHLMFvN1HoLGj9vHf0pdDv8gA==","signatures":[{"sig":"MEUCIQDBe6+p8w2GaIiN6GDh4Ja2JfaUjJfCliIUqZ02UK9gDAIgQTGMYhoJh5a6p6SKRcWP70JC0PygZEzYOzwbSAbZhkc=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@aman_asmuei%2fniato@1.0.3","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":427044},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"6b8227f021f6e38c5a98af61c62feeeccce652c9","private":false,"scripts":{"dev":"tsx --env-file=.env src/cli.ts","tui":"tsx --env-file=.env src/cli/tui/index.tsx","chat":"tsx --env-file=.env src/cli-chat.ts","eval":"tsx --env-file=.env src/evals/runner.ts","lint":"eslint .","test":"vitest run","build":"tsc -p tsconfig.build.json && node scripts/copy-prompts.mjs","login":"tsx src/cli-login.ts","dev:tui":"tsx --env-file=.env src/cli-tui.tsx","dev:multi":"tsx --env-file=.env src/cli-multi.ts","typecheck":"tsc --noEmit","test:watch":"vitest"},"_npmUser":{"name":"aman_asmuei","email":"amanasmuei@gmail.com"},"repository":{"url":"git+https://github.com/amanasmuei/niato.git","type":"git"},"_npmVersion":"10.8.2","description":"Intent-routing agent on the Claude Agent SDK. Niato — \"intention\", from the Malay/Indonesian niat — declares before it acts.","directories":{},"_nodeVersion":"20.20.2","dependencies":{"ink":"^7.0.1","zod":"^4.3.6","react":"^19.2.5","ink-spinner":"^5.0.0","@anthropic-ai/claude-agent-sdk":"^0.2.121"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.21.0","eslint":"^10.2.1","vitest":"^4.1.5","typescript":"^6.0.3","@types/node":"^25.6.0","@types/react":"^19.2.14","typescript-eslint":"^8.59.1","ink-testing-library":"^4.0.0"},"_npmOperationalInternal":{"tmp":"tmp/niato_1.0.3_1777604717770_0.8693163748424217","host":"s3://npm-registry-packages-npm-production"}},"1.0.4":{"name":"@aman_asmuei/niato","version":"1.0.4","keywords":["claude","agent-sdk","intent-routing","ai-agent","anthropic","niat"],"license":"MIT","_id":"@aman_asmuei/niato@1.0.4","maintainers":[{"name":"aman_asmuei","email":"amanasmuei@gmail.com"}],"homepage":"https://github.com/amanasmuei/niato#readme","bugs":{"url":"https://github.com/amanasmuei/niato/issues"},"bin":{"niato":"bin/niato"},"dist":{"shasum":"3a07a45c521bc4cd6c33fe0f5ccb36ae2998502a","tarball":"https://registry.npmjs.org/@aman_asmuei/niato/-/niato-1.0.4.tgz","fileCount":363,"integrity":"sha512-CHEpvKHjSmZRsayfY/luJee+RUzYNXSf74CWRyXKNGCsrdeOH2OrB62k5pSf03QG0BE0+VOXLOzsPOOvxv5XeQ==","signatures":[{"sig":"MEQCICsSnmxZU/tOMM4yLJJ344whUJMr5h+tXaUnLsFSdzAlAiBXZO/UG7PhzLKpXoAoK+Fd+xf4vQJsVpYoZwIjb4DaVw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@aman_asmuei%2fniato@1.0.4","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":428454},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"2291039f48e853dcec51c86cb3719673fc36ae28","private":false,"scripts":{"dev":"tsx --env-file=.env src/cli.ts","tui":"tsx --env-file=.env src/cli/tui/index.tsx","chat":"tsx --env-file=.env src/cli-chat.ts","eval":"tsx --env-file=.env src/evals/runner.ts","lint":"eslint .","test":"vitest run","build":"tsc -p tsconfig.build.json && node scripts/copy-prompts.mjs","login":"tsx src/cli-login.ts","dev:tui":"tsx --env-file=.env src/cli-tui.tsx","dev:multi":"tsx --env-file=.env src/cli-multi.ts","typecheck":"tsc --noEmit","test:watch":"vitest"},"_npmUser":{"name":"aman_asmuei","email":"amanasmuei@gmail.com"},"repository":{"url":"git+https://github.com/amanasmuei/niato.git","type":"git"},"_npmVersion":"10.8.2","description":"Intent-routing agent on the Claude Agent SDK. Niato — \"intention\", from the Malay/Indonesian niat — declares before it acts.","directories":{},"_nodeVersion":"20.20.2","dependencies":{"ink":"^7.0.1","zod":"^4.3.6","react":"^19.2.5","ink-spinner":"^5.0.0","@anthropic-ai/claude-agent-sdk":"^0.2.121"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.21.0","eslint":"^10.2.1","vitest":"^4.1.5","typescript":"^6.0.3","@types/node":"^25.6.0","@types/react":"^19.2.14","typescript-eslint":"^8.59.1","ink-testing-library":"^4.0.0"},"_npmOperationalInternal":{"tmp":"tmp/niato_1.0.4_1777680806013_0.9274982764825344","host":"s3://npm-registry-packages-npm-production"}},"1.1.0":{"name":"@aman_asmuei/niato","version":"1.1.0","keywords":["claude","agent-sdk","intent-routing","ai-agent","anthropic","niat"],"license":"MIT","_id":"@aman_asmuei/niato@1.1.0","maintainers":[{"name":"aman_asmuei","email":"amanasmuei@gmail.com"}],"homepage":"https://github.com/amanasmuei/niato#readme","bugs":{"url":"https://github.com/amanasmuei/niato/issues"},"bin":{"niato":"bin/niato"},"dist":{"shasum":"bdad9b0ffab449d7ee83f56fe68586d1d082f8de","tarball":"https://registry.npmjs.org/@aman_asmuei/niato/-/niato-1.1.0.tgz","fileCount":384,"integrity":"sha512-vV/9ntGit8rDhJoy71WH4WaQRt1OKuumIl0YF0q7MUq0dEP3xsI8b3J+VABQEpEAx6I20rKEOvxC2HrAVQ0oQQ==","signatures":[{"sig":"MEYCIQCysbzIVWqJmpiFk7kDMqi6Mr3Uf056ZhvyJKsO8mOhlwIhAJf8KaK9wBG/DUr1ZYvKpedSFTnXwEoPqBl0YhSyU5c2","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@aman_asmuei%2fniato@1.1.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":494931},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"b47d55b0d30c76f8d4bd3a3acbf58c13649bd70a","private":false,"scripts":{"dev":"tsx --env-file=.env src/cli.ts","tui":"tsx --env-file=.env src/cli/tui/index.tsx","chat":"tsx --env-file=.env src/cli-chat.ts","eval":"tsx --env-file=.env src/evals/runner.ts","lint":"eslint .","test":"vitest run","build":"tsc -p tsconfig.build.json && node scripts/copy-prompts.mjs","login":"tsx src/cli-login.ts","dev:tui":"tsx --env-file=.env src/cli-tui.tsx","dev:multi":"tsx --env-file=.env src/cli-multi.ts","typecheck":"tsc --noEmit","test:watch":"vitest"},"_npmUser":{"name":"aman_asmuei","email":"amanasmuei@gmail.com"},"repository":{"url":"git+https://github.com/amanasmuei/niato.git","type":"git"},"_npmVersion":"10.8.2","description":"Intent-routing agent on the Claude Agent SDK. Niato — \"intention\", from the Malay/Indonesian niat — declares before it acts.","directories":{},"_nodeVersion":"20.20.2","dependencies":{"ink":"^7.0.1","zod":"^4.3.6","react":"^19.2.5","ink-spinner":"^5.0.0","@anthropic-ai/claude-agent-sdk":"^0.2.121"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.21.0","eslint":"^10.2.1","vitest":"^4.1.5","typescript":"^6.0.3","@types/node":"^25.6.0","@types/react":"^19.2.14","typescript-eslint":"^8.59.1","ink-testing-library":"^4.0.0"},"_npmOperationalInternal":{"tmp":"tmp/niato_1.1.0_1777683375187_0.44771878900825346","host":"s3://npm-registry-packages-npm-production"}},"1.2.0":{"name":"@aman_asmuei/niato","version":"1.2.0","keywords":["claude","agent-sdk","intent-routing","ai-agent","anthropic","niat"],"license":"MIT","_id":"@aman_asmuei/niato@1.2.0","maintainers":[{"name":"aman_asmuei","email":"amanasmuei@gmail.com"}],"homepage":"https://github.com/amanasmuei/niato#readme","bugs":{"url":"https://github.com/amanasmuei/niato/issues"},"bin":{"niato":"bin/niato"},"dist":{"shasum":"d60d11439022aa3e303f827d41fe69f49ee29781","tarball":"https://registry.npmjs.org/@aman_asmuei/niato/-/niato-1.2.0.tgz","fileCount":396,"integrity":"sha512-1axSHEoaZkBAYG+kzBJT/Nff6Vzhl1Ad8XpG/wEGf/N6RPS8D5YtibaE9T8wkKePv6EnOE5BT7hpsnmFjCw6tw==","signatures":[{"sig":"MEUCIQDESaRp24qS9cLeYHQ7Vz60abqvl6olcYYkjHVSu09xuQIgE1PgGi2nbmnQITgePgrVqtffnVJXppRUQI4XpMnkHpA=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@aman_asmuei%2fniato@1.2.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":515492},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"efd7297dc0d006cdef4c9f904a898c890c09bc13","private":false,"scripts":{"dev":"tsx --env-file=.env src/cli.ts","tui":"tsx --env-file=.env src/cli/tui/index.tsx","chat":"tsx --env-file=.env src/cli-chat.ts","eval":"tsx --env-file=.env src/evals/runner.ts","lint":"eslint .","test":"vitest run","build":"tsc -p tsconfig.build.json && node scripts/copy-prompts.mjs","login":"tsx src/cli-login.ts","dev:tui":"tsx --env-file=.env src/cli-tui.tsx","dev:multi":"tsx --env-file=.env src/cli-multi.ts","typecheck":"tsc --noEmit","test:watch":"vitest","setup-token":"tsx src/cli-setup-token.ts"},"_npmUser":{"name":"aman_asmuei","email":"amanasmuei@gmail.com"},"repository":{"url":"git+https://github.com/amanasmuei/niato.git","type":"git"},"_npmVersion":"10.8.2","description":"Intent-routing agent on the Claude Agent SDK. Niato — \"intention\", from the Malay/Indonesian niat — declares before it acts.","directories":{},"_nodeVersion":"20.20.2","dependencies":{"ink":"^7.0.1","zod":"^4.3.6","react":"^19.2.5","ink-spinner":"^5.0.0","@anthropic-ai/claude-agent-sdk":"^0.2.121"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.21.0","eslint":"^10.2.1","vitest":"^4.1.5","typescript":"^6.0.3","@types/node":"^25.6.0","@types/react":"^19.2.14","typescript-eslint":"^8.59.1","ink-testing-library":"^4.0.0"},"_npmOperationalInternal":{"tmp":"tmp/niato_1.2.0_1777697909479_0.46089908111143174","host":"s3://npm-registry-packages-npm-production"}},"1.2.1":{"name":"@aman_asmuei/niato","version":"1.2.1","keywords":["claude","agent-sdk","intent-routing","ai-agent","anthropic","niat"],"license":"MIT","_id":"@aman_asmuei/niato@1.2.1","maintainers":[{"name":"aman_asmuei","email":"amanasmuei@gmail.com"}],"homepage":"https://github.com/amanasmuei/niato#readme","bugs":{"url":"https://github.com/amanasmuei/niato/issues"},"bin":{"niato":"bin/niato"},"dist":{"shasum":"a42a23ae84d3ed7edf9222e7dbb6f8af10ed2b29","tarball":"https://registry.npmjs.org/@aman_asmuei/niato/-/niato-1.2.1.tgz","fileCount":396,"integrity":"sha512-5+63xeeK4ZOuQ24OmiHo9NNyog1WVsODCCMZ00NzsB1DBvFrwK7GTrZ2o3/mp2KZYBfczcFd3x/JSjFv4gj35w==","signatures":[{"sig":"MEUCIQCC7gNuUqMWDg6ltcHetZkIAb6nb2+1Dfdg1nF6DlGDZwIgV+Kz254HaqlKU9VWlEr+ZAL4+leek8ou9OTYEkT7ipg=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@aman_asmuei%2fniato@1.2.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":516438},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"0c4bf4abbfd95c71cc335d4c8def7bc6b1ebf42d","private":false,"scripts":{"dev":"tsx --env-file=.env src/cli.ts","tui":"tsx --env-file=.env src/cli/tui/index.tsx","chat":"tsx --env-file=.env src/cli-chat.ts","eval":"tsx --env-file=.env src/evals/runner.ts","lint":"eslint .","test":"vitest run","build":"tsc -p tsconfig.build.json && node scripts/copy-prompts.mjs","login":"tsx src/cli-login.ts","dev:tui":"tsx --env-file=.env src/cli-tui.tsx","dev:multi":"tsx --env-file=.env src/cli-multi.ts","typecheck":"tsc --noEmit","test:watch":"vitest","setup-token":"tsx src/cli-setup-token.ts"},"_npmUser":{"name":"aman_asmuei","email":"amanasmuei@gmail.com"},"repository":{"url":"git+https://github.com/amanasmuei/niato.git","type":"git"},"_npmVersion":"10.8.2","description":"Intent-routing agent on the Claude Agent SDK. Niato — \"intention\", from the Malay/Indonesian niat — declares before it acts.","directories":{},"_nodeVersion":"20.20.2","dependencies":{"ink":"^7.0.1","zod":"^4.3.6","react":"^19.2.5","ink-spinner":"^5.0.0","@anthropic-ai/claude-agent-sdk":"^0.2.121"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.21.0","eslint":"^10.2.1","vitest":"^4.1.5","typescript":"^6.0.3","@types/node":"^25.6.0","@types/react":"^19.2.14","typescript-eslint":"^8.59.1","ink-testing-library":"^4.0.0"},"_npmOperationalInternal":{"tmp":"tmp/niato_1.2.1_1777698925117_0.7317563004327754","host":"s3://npm-registry-packages-npm-production"}},"1.3.0":{"name":"@aman_asmuei/niato","version":"1.3.0","description":"Intent-routing agent on the Claude Agent SDK. Niato — \"intention\", from the Malay/Indonesian niat — declares before it acts.","type":"module","main":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"bin":{"niato":"bin/niato"},"scripts":{"dev":"tsx --env-file=.env src/cli.ts","dev:multi":"tsx --env-file=.env src/cli-multi.ts","dev:tui":"tsx --env-file=.env src/cli-tui.tsx","tui":"tsx --env-file=.env src/cli/tui/index.tsx","chat":"tsx --env-file=.env src/cli-chat.ts","login":"tsx src/cli-login.ts","setup-token":"tsx src/cli-setup-token.ts","build":"tsc -p tsconfig.build.json && node scripts/copy-prompts.mjs","typecheck":"tsc --noEmit","lint":"eslint .","test":"vitest run","test:watch":"vitest","eval":"tsx --env-file=.env src/evals/runner.ts"},"engines":{"node":">=20"},"license":"MIT","private":false,"publishConfig":{"access":"public"},"repository":{"type":"git","url":"git+https://github.com/amanasmuei/niato.git"},"homepage":"https://github.com/amanasmuei/niato#readme","bugs":{"url":"https://github.com/amanasmuei/niato/issues"},"keywords":["claude","agent-sdk","intent-routing","ai-agent","anthropic","niat"],"dependencies":{"@anthropic-ai/claude-agent-sdk":"^0.2.121","ink":"^7.0.1","ink-spinner":"^5.0.0","react":"^19.2.5","zod":"^4.3.6"},"devDependencies":{"@types/node":"^25.6.0","@types/react":"^19.2.14","eslint":"^10.2.1","ink-testing-library":"^4.0.0","tsx":"^4.21.0","typescript":"^6.0.3","typescript-eslint":"^8.59.1","vitest":"^4.1.5"},"_id":"@aman_asmuei/niato@1.3.0","gitHead":"d20f8c5d0c1d818e2c6b847d4382df2d4f59b878","_nodeVersion":"20.20.2","_npmVersion":"10.8.2","dist":{"integrity":"sha512-x3nH4qYGFNdisjkZ5WVRZbqGJkp94r3akaWo73KnPYr9J4DW5DdbjN08QY13EtesGFXl3iZPAfFOfg1GP6NmXQ==","shasum":"946555a14ea0faab0b432f2784d429b1947cbebe","tarball":"https://registry.npmjs.org/@aman_asmuei/niato/-/niato-1.3.0.tgz","fileCount":416,"unpackedSize":573771,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@aman_asmuei%2fniato@1.3.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIEPFEthaN7eOhW3TXb9vY0RKfjjXWTUkfqJDtPT3rCY5AiEAufQ1X8syuDigwWfeydatseeOrANjVYZXVRJaKfF5QBI="}]},"_npmUser":{"name":"aman_asmuei","email":"amanasmuei@gmail.com"},"directories":{},"maintainers":[{"name":"aman_asmuei","email":"amanasmuei@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/niato_1.3.0_1777717163984_0.13168122653787284"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-01T02:29:14.102Z","modified":"2026-05-02T10:19:24.475Z","1.0.2":"2026-05-01T02:29:14.353Z","1.0.3":"2026-05-01T03:05:17.959Z","1.0.4":"2026-05-02T00:13:26.167Z","1.1.0":"2026-05-02T00:56:15.340Z","1.2.0":"2026-05-02T04:58:29.678Z","1.2.1":"2026-05-02T05:15:25.299Z","1.3.0":"2026-05-02T10:19:24.196Z"},"bugs":{"url":"https://github.com/amanasmuei/niato/issues"},"license":"MIT","homepage":"https://github.com/amanasmuei/niato#readme","keywords":["claude","agent-sdk","intent-routing","ai-agent","anthropic","niat"],"repository":{"type":"git","url":"git+https://github.com/amanasmuei/niato.git"},"description":"Intent-routing agent on the Claude Agent SDK. Niato — \"intention\", from the Malay/Indonesian niat — declares before it acts.","maintainers":[{"name":"aman_asmuei","email":"amanasmuei@gmail.com"}],"readme":"<div align=\"center\">\n\n# Niato\n\n**An intent-routing agent built on the [Claude Agent SDK](https://code.claude.com/docs/en/agent-sdk/overview).**\n\n[![npm version](https://img.shields.io/npm/v/niato.svg?color=blue)](https://www.npmjs.com/package/niato)\n[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE)\n[![TypeScript](https://img.shields.io/badge/TypeScript-strict-blue?logo=typescript)](./tsconfig.json)\n[![Node 20+](https://img.shields.io/badge/Node-20.6%2B-brightgreen)](#)\n\n> *Niato* — derived from *niat* (Malay/Indonesian for *\"intention\"*, from the Arabic root نِيَّة). The formal declaration of intent before an act.\n\n</div>\n\n---\n\nA small, fast classifier states the user's intent, an Opus orchestrator declares a plan, the right specialist subagent — drawn from a pluggable *Domain Pack* — carries it out, and guardrails declare what's about to happen before any tool runs. **Classify, plan, gate, act** — every meaningful action is preceded by a stated intent.\n\n```\n   user input\n       │\n       ▼\n   ┌─────────────────┐\n   │  classifier     │  Sonnet 4.6 — { intent, domain, confidence }\n   └────────┬────────┘\n            ▼\n   ┌─────────────────┐\n   │  orchestrator   │  Opus 4.7 — declares plan, dispatches via Agent tool\n   └────────┬────────┘\n            ▼\n   ┌─────────────────┐\n   │  specialist(s)  │  Sonnet 4.6 — minimal tool allowlist per role\n   └────────┬────────┘\n            ▼\n        response  (+ TurnRecord: classification, tokens, cost, guardrails)\n```\n\n---\n\n## Table of contents\n\n- [Quick start](#quick-start) — install + first turn in 3 commands\n- [Use the TUI](#use-the-tui) — terminal companion app\n- [Embed in your code](#embed-in-your-code) — `createNiato({ ... })`\n- [How it works](#how-it-works) — the four declarations\n- [Authentication](#authentication) — API key vs. Claude subscription\n- [Domain packs](#domain-packs) — what ships, how to add your own\n- [Reference](#reference) — tracing, guardrails, persona, eval baselines\n- [Development](#development) — clone, test, contribute\n- [Roadmap & status](#roadmap--status)\n\n---\n\n## Quick start\n\nRequires **Node 20.6+**. Three commands to your first turn:\n\n```bash\n# 1. Install\nnpm i -g @aman_asmuei/niato\n\n# 2. Authenticate (pick one)\nniato login                                # Claude subscription (browser OAuth, recommended for laptops)\n# — or —\nniato setup-token                          # long-lived token, then `export CLAUDE_CODE_OAUTH_TOKEN=...` (CI / headless)\n# — or —\nexport ANTHROPIC_API_KEY=sk-ant-...        # developer API path (per-token billing)\n\n# 3. Run\nniato\n```\n\nThat's it. The TUI walks you through companion setup on first run — no shell env vars, no config files to edit by hand.\n\n> **Prefer not to install globally?** `npx @aman_asmuei/niato` works the same way.\n>\n> **Which auth path?** Laptop / personal: `niato login`. CI / headless / containers: `niato setup-token`. Production / multi-user: `ANTHROPIC_API_KEY`. See [Authentication](#authentication) for the full table and ToS notes.\n\n---\n\n## Use the TUI\n\nAfter install, `niato` (or `niato tui`) launches a polished Ink terminal app:\n\n- **Launcher** — New session · Resume last · Settings · About\n- **Mode pick** — *Casual* (warm, minimal observability) · *Dev* (full per-turn trace)\n- **Always-visible footer** — classify/dispatch ticks, latency, cost\n- **Sessions persisted** as JSONL at `~/.niato/sessions/{id}.jsonl` (last 50 retained)\n\n```\nniato                # launcher\nniato login          # OAuth subscription auth (wraps `claude /login`)\nniato chat           # legacy multi-turn REPL (kept for back-compat)\nniato --help         # subcommands\n```\n\n**First-run flow** (all in-app — no shell setup needed):\n\n1. Pick auth — Claude subscription or API key.\n2. If API key: paste it in the in-app prompt. Saved to `~/.niato/auth.json` (chmod 600).\n3. Companion setup — name, voice archetype, optional preferences. Saved to `~/.niato/companion.json`.\n\n**Default packs.** The TUI ships with the **Generic** pack only (Support and Dev Tools are demo packs — Support uses a stub MCP). To enable them:\n\n```bash\nexport NIATO_PACKS=support,dev_tools\n```\n\n---\n\n## Embed in your code\n\nThe package exports `createNiato(...)` as the entry-point factory. Minimal example:\n\n```ts\nimport { createNiato, genericPack } from \"@aman_asmuei/niato\";\n\nconst niato = createNiato({ packs: [genericPack] });\n\nconst turn = await niato.run(\"Explain how DNS works in three sentences.\");\nconsole.log(turn.result);                    // model's answer\nconsole.log(turn.trace);                     // TurnRecord — see Reference below\nconsole.log(niato.metrics(turn.session.id)); // rolling SessionMetrics\n```\n\nA more configured example with validators, hooks, persona, and telemetry:\n\n```ts\nimport {\n  createNiato,\n  genericPack,\n  supportPack,\n  promptInjectionValidator,\n  maxLengthValidator,\n} from \"@aman_asmuei/niato\";\n\nconst niato = createNiato({\n  packs: [genericPack, supportPack],\n  inputValidators: [maxLengthValidator(8_000), promptInjectionValidator()],\n  costLimitUsd: 1.0,                  // reject further turns once session spends $1\n  globalHooks: {\n    // your custom org-wide PreToolUse / PostToolUse / Stop hooks\n  },\n  persona: {\n    name: \"Layla\",\n    description: \"Warm, faith-aware. Address the user by name.\",\n  },\n  onTurnComplete: async (trace) => {\n    // pipe to OTel / Datadog / your time-series store\n  },\n});\n```\n\n`run()` is async, isolated per session, and propagates two typed errors callers can render directly:\n\n| Error | Thrown when | Carries |\n| ----- | ----------- | ------- |\n| `NiatoInputRejectedError` | An input validator rejects the message | `reason: string` |\n| `NiatoBudgetExceededError` | Session cumulative cost ≥ `costLimitUsd` | `cumulativeUsd`, `limitUsd` |\n| `NiatoAuthError` | Neither auth path configured at startup | actionable message |\n\nFor non-Niato errors (network, 401, 429, malformed model output), `classifyError(err)` from `@aman_asmuei/niato` returns a `ClassifiedError` with a friendly message — used by the TUI, available to library consumers.\n\n---\n\n## How it works\n\nEvery turn is four declarations, in order:\n\n1. **Input validators** run synchronously over the raw message (`maxLength` on by default, `promptInjection` opt-in). First failure throws `NiatoInputRejectedError` before any tokens are spent.\n2. **Cost-limit gate** checks `session.cumulativeCostUsd ≥ costLimitUsd`. Pre-turn — never mid-turn.\n3. **Classifier** (Sonnet 4.6) returns `{ intent, domain, confidence }` against the loaded packs' vocabulary. Same SDK as the orchestrator, so OAuth subscription auth Just Works.\n4. **Orchestrator** (Opus 4.7) reads the classification, picks the pack from `domain`, calls `pack.route(intent)` to pick the specialist, and dispatches via the SDK's `Agent` tool. The **specialist** (Sonnet 4.6) does the work using its declared tool allowlist; pack hooks gate every tool call before it executes.\n\nAfter each turn, a `TurnRecord` is rebuilt from the SDK's message stream and the per-session ledger updates.\n\n**Three architectural invariants** worth knowing:\n\n- **The orchestrator may only dispatch via the `Agent` tool** — enforced at the SDK permission layer by the always-on `agentOnlyOrchestratorHook`. Direct `Read` / `Write` / `Bash` / MCP calls from the orchestrator are denied.\n- **The classifier is out-of-band** — not a tool the orchestrator can call. It runs once before the orchestrator does.\n- **Subagents do not inherit parent context** — anything a specialist needs (file paths, entities, prior decisions) is passed in the dispatch prompt.\n\nFor the full design, see [`ARCHITECTURE.md`](./ARCHITECTURE.md).\n\n---\n\n## Authentication\n\nThree paths into the Anthropic API; pick whichever matches your setup. All flow through the Agent SDK's auto-resolution — Niato just classifies which one is in use for logging and gates startup until at least one is configured.\n\n| Path | Trigger | Cost | Use case |\n| ---- | ------- | ---- | -------- |\n| **Subscription (interactive)** | `niato login` (sets `NIATO_AUTH=subscription`, uses `~/.claude/`) | $0 — Claude Max quota | Laptop, single user, hands-on. Default for `npx @aman_asmuei/niato`. *ToS note below.* |\n| **Subscription (token)** | `niato setup-token` → `export CLAUDE_CODE_OAUTH_TOKEN=ct-...` | $0 — Claude Max quota | CI, scripts, containers, anywhere a browser login isn't available. Long-lived (1 year). *Same ToS note.* |\n| **API key** | `ANTHROPIC_API_KEY=sk-ant-...` | per-token against your API budget | Production, multi-user, or anywhere a developer API key is the right call |\n| **None** | none set | n/a | `NiatoAuthError` at startup with actionable message |\n\n`createNiato()` logs the chosen path at startup. Priority when more than one is configured: **`CLAUDE_CODE_OAUTH_TOKEN` > `NIATO_AUTH=subscription` > `ANTHROPIC_API_KEY`** — the most specific credential wins, and explicit subscription intent overrides a leftover API key.\n\n<details>\n<summary><strong>Note on subscription auth (read before non-personal use)</strong></summary>\n\nThe Agent SDK supports OAuth (subscription) authentication via two transport mechanisms — `~/.claude/` session storage (driven by `NIATO_AUTH=subscription`) and the `CLAUDE_CODE_OAUTH_TOKEN` env var (long-lived token from `claude setup-token`). Niato exposes both. They are **opt-in only**: without one of those signals, Niato uses the developer API path or fails clearly at startup. We made this change because:\n\n- The Agent SDK explicitly supports OAuth (`ApiKeySource = 'oauth'` in its types).\n- What's *not* explicit is whether Anthropic's Consumer Terms / Acceptable Use Policy permit using your Claude Max subscription to power applications other than Claude Code itself.\n- Silently defaulting strangers onto that path would push them onto a ToS-uncertain path without their knowledge.\n\nShape of the question:\n\n- **Probably fine**: personal use, low volume, on your own machine. You're already paying for Max; running a personal companion through it is close to the spirit of subscription auth.\n- **Verify before doing**: anything you'd put in front of customers, deploy to production, distribute to other users, or run at sustained volume. The right path for those cases is a developer API key with explicit billing.\n\nThe authoritative sources are Anthropic's [Consumer Terms](https://www.anthropic.com/legal/consumer-terms), [Acceptable Use Policy](https://www.anthropic.com/legal/aup), the Claude Max subscription agreement, and — for anything ambiguous — Anthropic support directly. Verify before scaling beyond personal use.\n\n</details>\n\n---\n\n## Domain packs\n\nA `DomainPack` is a self-contained bundle: intents, specialist `AgentDefinition`s, MCP servers, hooks, and a `route(intent) ⇒ specialist` function. Three packs ship today; the orchestrator can compose dispatches across them in a single turn.\n\n| Pack | Intents | Specialists | MCP | Hooks |\n| ---- | ------- | ----------- | --- | ----- |\n| **Generic** | `question`, `task`, `escalate` | `retrieval`, `action`, `escalate` | none | none |\n| **Support** | `order_status`, `refund_request`, `billing_question`, `complaint`, `account_help` | `ticket_lookup`, `refund_processor`, `kb_search`, `escalate` | in-process `support_stub` (canned responses; production swaps in real Zendesk / Stripe URLs) | `piiRedactionHook`, `dollarLimit({ tool, autoApproveBelow })` |\n| **Dev Tools** | `find_code`, `explain_code`, `fix_bug`, `debug_ci` | `codebase_search`, `code_explainer`, `bug_fixer`, `ci_debugger` | none — built-in tools (`Read`, `Grep`, `Glob`, `Edit`, `Bash`, `WebFetch`) | `secretsScanHook`, `sandboxBashHook({ allowedCommands })` |\n\n**Adding your own pack:** create `src/packs/<name>/{pack.ts, agents/, prompts/, evals/}` and export a single `DomainPack`. The Core never imports from inside a pack — only the public interface. Per-pack hooks merge into the orchestrator's `Options.hooks` after the built-in invariants and any global hooks.\n\n<details>\n<summary><strong>Cross-pack composition</strong> — when one user message spans multiple packs</summary>\n\nWhen a single user message genuinely spans multiple packs (*\"the refund webhook is broken — find the bug and open a ticket\"*), the classifier extends its output with an optional `secondary: SecondaryIntent[]` array of additional `(intent, domain, confidence)` triples. The orchestrator surfaces these as `Additional recommendations:` in its planning prompt and decides:\n\n- **Sequential** dispatch when one specialist's output feeds the next (e.g. `dev_tools.bug_fixer` → `support.escalate`). The orchestrator pastes the upstream output into the downstream `Agent` prompt — subagents don't share context.\n- **Parallel** dispatch when the asks are independent (e.g. an order-status check + an explanation). Both `Agent` calls go out in the same assistant message; the SDK runs them concurrently.\n- **Clarify** when a secondary's confidence is below the 0.85 dispatch bar — the orchestrator asks one question rather than guessing.\n\nAfter every specialist returns, the orchestrator synthesizes a single answer that cites each contributing specialist.\n\n</details>\n\n<details>\n<summary><strong>Persona (Level 1)</strong> — configurable user-facing identity layer</summary>\n\n`persona?: { name?, description }` on `NiatoOptions` adds a configurable user-facing identity layer. The text is prepended to the orchestrator's system prompt — the orchestrator becomes the persona; specialists stay role-focused tools the persona uses. Pack brand voice (already in each specialist's `prompt.md`) keeps working unchanged.\n\n```ts\npersona: {\n  name: \"Layla\",\n  description: \"Warm, faith-aware. Address the user by name. Acknowledge difficulty without minimizing.\",\n}\n```\n\nWhat Level 1 covers: a consistent voice across every turn. What it doesn't: persistent memory, time-of-day modulation, evolving rapport, per-user persona — those are Level 2 / Level 3 work.\n\n</details>\n\n<details>\n<summary><strong>Long-term memory (Tier 2)</strong> — durable per-user facts the orchestrator remembers</summary>\n\n`memory?: { store?, userId? }` on `NiatoOptions` opts a Niato instance into cross-session memory. Facts are loaded once at `createNiato()` startup, formatted into a preamble between the persona and the operational prompt, and persisted to the configured `MemoryStore`. The default `FileMemoryStore` writes JSON to `~/.niato/memory/<userId>.json`; swap in Redis / Postgres / etc. by implementing the two-method interface. Append facts with `await niato.remember([...])` — the next turn sees them. Soft-capped at 100 facts / ~4 KB; older facts are dropped with a `warn` log on overflow. Memory injects into the orchestrator's system prompt only; specialists never see it (architectural invariant #4).\n\n```ts\nconst niato = createNiato({\n  packs: [genericPack],\n  memory: { userId: \"alice\" },             // FileMemoryStore by default\n  persona: { name: \"Layla\", description: \"Warm, faith-aware companion.\" },\n});\n\nawait niato.run(\"Hi, I'm new here.\");\nawait niato.remember([\"Prefers concise answers.\", \"Lives in Kuala Lumpur.\"]);\nawait niato.run(\"Where can I buy good kopi?\"); // sees the remembered facts\n```\n\n`userId` resolves from `NiatoOptions.memory.userId` → `Config.NIATO_USER_ID` → `\"default\"`. One Niato instance is one user — `userId` is *not* accepted per-turn on `niato.run()`. Auto-extraction of facts from conversation lands in v1.1; for now `remember()` is the only writer.\n\n</details>\n\n---\n\n## Reference\n\n<details>\n<summary><strong>TurnRecord shape</strong> — what every <code>niato.run()</code> returns</summary>\n\n```ts\ninterface TurnRecord {\n  sessionId: string;\n  turnId: string;                                 // uuid generated per turn\n  classification: IntentResult;                   // { intent, domain, confidence, secondary? }\n  plan: string[];                                 // specialist names dispatched, in order\n  specialists: { name: string; toolCalls: number }[];\n  tokensByModel: Record<string, {\n    inputTokens: number;\n    outputTokens: number;\n    cacheReadInputTokens: number;\n    cacheCreationInputTokens: number;\n  }>;\n  costUsd: number;                                // SDK-reported total\n  latencyMs: number;                              // wall clock, includes classifier\n  outcome: \"success\" | \"error\";\n  guardrailsTriggered: string[];                  // tool_names denied by any hook this turn\n}\n```\n\nMatches the per-turn record shape from `ARCHITECTURE.md` §11 minus cross-turn aggregation and `user_id`.\n\n</details>\n\n<details>\n<summary><strong>SessionMetrics</strong> — rolling per-session aggregates</summary>\n\nRead with `niato.metrics(sessionId)`:\n\n```ts\ninterface SessionMetrics {\n  turnCount: number;\n  cumulativeCostUsd: number;\n  cumulativeLatencyMs: number;\n  guardrailsTriggered: Record<string, number>;       // tool_name → count\n  dispatchesByPackSpecialist: Record<string, number>; // \"support.refund_processor\" → count\n  errorCount: number;\n}\n```\n\nReturns `undefined` for unknown sessions. The result is a defensive clone — mutating it does not corrupt the live ledger.\n\n</details>\n\n<details>\n<summary><strong>Guardrails</strong> — three layers, fail-fast</summary>\n\n| Layer | Where it runs | Failure mode |\n| ----- | ------------- | ------------ |\n| Input validators | Before classification | `NiatoInputRejectedError` |\n| Cost-limit gate | Before classification | `NiatoBudgetExceededError` |\n| SDK hooks | Around tool calls (built-in invariants → `globalHooks` → pack hooks) | SDK permission deny with reason text |\n\nHooks are *enforcement*, not logging. The first hook to deny halts the SDK's permission flow before the tool runs. `agentOnlyOrchestratorHook` and `mergeHooks(...layers)` are exported so you can reuse them in custom configurations.\n\n</details>\n\n<details>\n<summary><strong>Observability</strong> — TurnRecord, SessionMetrics, onTurnComplete</summary>\n\nThree layers, none of which require an external dependency:\n\n**Per-turn `TurnRecord`** — the single-turn shape above. Every `niato.run()` returns it; the `info` log line includes it as a flat JSON object.\n\n**Per-session `SessionMetrics`** — rolling aggregates updated after each turn settles. Read with `niato.metrics(sessionId)`.\n\n**`onTurnComplete` callback** — `(trace: TurnRecord) => void | Promise<void>`. Fires after each turn's trace is built. Wire OTel / Datadog / Honeycomb / your own time-series store from here. Errors thrown by the callback are caught and logged at `warn` level; telemetry never breaks user flows.\n\nOpenTelemetry-style distributed tracing isn't bundled — that's a per-deployment decision. A copy-paste OTel adapter recipe (Datadog included via OTLP receiver) lives at [`docs/otel-adapter.ts`](./docs/otel-adapter.ts) with the explainer at [`docs/otel-adapter.md`](./docs/otel-adapter.md).\n\n</details>\n\n<details>\n<summary><strong>Conversation memory</strong> — how prior turns persist</summary>\n\nNiato uses the Agent SDK's built-in session management:\n\n- First turn of a session passes `Options.sessionId = <uuid>` to the SDK.\n- Subsequent turns pass `Options.resume = <uuid>` so the model sees the prior transcript.\n- The SDK handles compaction automatically when context fills.\n- Transcripts persist at `~/.niato/sdk-sessions/` regardless of where you launched `niato` from.\n\nPass the same `sessionId` to two consecutive `run()` calls and the second one resumes coherently. The TUI's \"Resume Last\" path uses this transparently.\n\n</details>\n\n<details>\n<summary><strong>Eval regression baselines</strong> — CI gate against silent quality drift</summary>\n\nEach pack ships a `baseline.json` next to its `cases.jsonl`. CI uses it to catch silent classifier-quality regressions:\n\n```bash\npnpm eval support --baseline           # asserts current ≥ baseline; non-zero exit on regression\npnpm eval support --baseline=path.json # custom baseline path (CI artifact stores)\npnpm eval support --write-baseline     # records the current score as the new baseline\n```\n\nThe check is strict: any drop in `passed` count fails. Case-count changes (i.e. someone edited `cases.jsonl`) require an explicit `--write-baseline` rather than silently passing.\n\n</details>\n\n---\n\n## Development\n\nClone-based workflow:\n\n```bash\ngit clone https://github.com/<your-fork>/niato.git\ncd niato\npnpm install\ncp .env.example .env       # fill in ANTHROPIC_API_KEY for E2E tests\npnpm typecheck && pnpm lint && pnpm test\npnpm dev \"explain how DNS works in three sentences\"\n```\n\nYou'll see a structured `turn` log line (classification, dispatched specialist, tokens, cost, latency) followed by the model's answer.\n\n**Useful scripts:**\n\n| Command | What it does |\n| ------- | ------------ |\n| `pnpm dev \"<prompt>\"` | Single-turn, Generic pack only |\n| `pnpm dev:multi \"<prompt>\"` | Single-turn, all packs loaded (cross-pack examples) |\n| `pnpm dev:tui \"<prompt>\"` | Single-turn through Ink dashboard |\n| `pnpm chat` | Persistent multi-turn REPL with companion persona |\n| `pnpm typecheck` | `tsc --noEmit` |\n| `pnpm lint` | ESLint with `@typescript-eslint/recommended-strict` |\n| `pnpm test` | Vitest (offline; E2E suites unskip when `ANTHROPIC_API_KEY` is set) |\n| `pnpm eval <pack>` | Run a pack's golden suite |\n| `pnpm build` | Production build (`dist/` + `.md` prompt copy) |\n\n<details>\n<summary><strong>Test suite layout</strong></summary>\n\n| Suite | Path | Runs offline? |\n| ----- | ---- | ------------- |\n| Wiring | `tests/wiring.test.ts` | Yes |\n| Classifier unit | `tests/classifier.test.ts` | Yes (Anthropic SDK mocked) |\n| Orchestrator enforcement | `tests/orchestrator-enforcement.test.ts` | Yes |\n| Validators | `tests/validators.test.ts` | Yes |\n| Support stub MCP | `tests/support-stub.test.ts` | Yes |\n| Support hooks | `tests/support-hooks.test.ts` | Yes |\n| Dev Tools hooks | `tests/dev-tools-hooks.test.ts` | Yes |\n| Cross-pack orchestrator | `tests/cross-pack-orchestrator.test.ts` | Yes |\n| Trace guardrails extractor | `tests/trace-guardrails.test.ts` | Yes |\n| Session metrics | `tests/metrics.test.ts` | Yes |\n| Conversation memory | `tests/conversation-memory.test.ts` | Yes |\n| Persona | `tests/persona.test.ts` | Yes |\n| TUI screens (ApiKeyEntry, CompanionWizard, etc.) | `tests/cli/tui/screens/*.test.tsx` | Yes |\n| Smoke (E2E) | `tests/smoke.test.ts` | No — needs `ANTHROPIC_API_KEY` |\n| Support smoke (E2E) | `tests/support-smoke.test.ts` | No — three real turns, ~$0.15 |\n| Dev Tools smoke (E2E) | `tests/dev-tools-smoke.test.ts` | No — three real turns, ~$0.25 |\n| Cross-pack smoke (E2E) | `tests/cross-pack-smoke.test.ts` | No — one real turn |\n| Cost-limit (E2E) | `tests/cost-limit-e2e.test.ts` | No — two real turns |\n| Cross-pack classifier (E2E) | `tests/cross-pack-classifier.test.ts` | No — eight live cases |\n| Pack evals | `tests/evals.test.ts` | No — Generic / Support / Dev Tools golden suites |\n\n`pnpm test` picks up `ANTHROPIC_API_KEY` from `.env` automatically; the E2E suites un-skip themselves when the key is present.\n\n</details>\n\n<details>\n<summary><strong>Repository layout</strong></summary>\n\n```\nsrc/\n├── core/           ingress, session, classifier, orchestrator, compose\n├── packs/          DomainPack interface + generic/, support/, dev-tools/\n├── tools/          built-in tool name constants\n├── memory/         session store\n├── guardrails/     hooks, validators, errors, orchestrator-enforcement\n├── observability/  log, trace\n├── evals/          shared runPackEvals helper + CLI runner\n├── cli/            error-classify, dispatch, TUI screens\n└── index.ts        public exports\n```\n\nConventions: TypeScript strict, one `AgentDefinition` per file, prompts longer than 30 lines in adjacent `.md` files, single typed config module that fails fast, MCP credentials referenced via env vars never literal strings. See [`CLAUDE.md`](./CLAUDE.md).\n\n</details>\n\n---\n\n## Releasing\n\nCI/CD lives in `.github/workflows/`:\n\n- **`ci.yml`** — runs typecheck + lint + test + build on every push and PR.\n- **`release.yml`** — publishes to npm with Sigstore provenance when a `vX.Y.Z` tag is pushed. Requires an `NPM_TOKEN` repo secret (granular access token, publish-only, bypass-2FA enabled).\n\nFull setup + per-release flow in [`docs/RELEASING.md`](./docs/RELEASING.md). TL;DR for an existing setup:\n\n```bash\n# Bump version in package.json + CHANGELOG.md, commit, then:\ngit tag -a vX.Y.Z -m \"vX.Y.Z\"\ngit push origin master vX.Y.Z\n# CI takes over: builds, validates, publishes to npm.\n```\n\n## Roadmap & status\n\n**v1.0.0 — General Availability.** Plans 1–4 of the v1 release roadmap shipped. See [`docs/CHANGELOG.md`](./docs/CHANGELOG.md) for the full history.\n\n| Phase | Version | Theme |\n| ----- | ------- | ----- |\n| 1–9 | pre-1.0 | Skeleton → classifier → hooks → packs → cross-pack → observability → persona → OAuth → memory groundwork |\n| 10 | v0.2.0 | Release prep — license, npm-publishable, NIATO_AUTH opt-in, Node bin dispatcher |\n| 11 | v0.3.0 | In-app onboarding — Ink-native ApiKeyEntry + CompanionWizard |\n| 12 | v0.4.0 | Conversation memory — SDK sessionId/resume threading |\n| 13 | v1.0.0 | Polish — friendly errors, Generic-default packs, CHANGELOG |\n\n**Backlog (post-1.0):**\n\n- Real GitHub MCP wiring for `pr_creator` (currently stubbed via `dev_tools_github_stub`).\n\n**Shipped (post-1.0):**\n\n- Auth UX hardening (v1.2.0) — `niato login` now persists the choice (no more \"logged in but logged out\"), `niato setup-token` for CI / headless via `CLAUDE_CODE_OAUTH_TOKEN`, three auth paths documented in priority order.\n- Eval baselines committed for all three packs (`generic` 20/20, `support` 23/25, `dev_tools` 25/25). CI gate active via `pnpm eval <pack> --baseline`.\n- Long-term cross-session memory — file-based default at `~/.niato/memory/<userId>.json` with a thin `MemoryStore` interface for plugging in Redis/Postgres later.\n- TUI multi-turn history dashboard — scrollable session view in the launcher.\n- OpenTelemetry adapter — copy-paste recipe at [`docs/otel-adapter.ts`](./docs/otel-adapter.ts) + explainer at [`docs/otel-adapter.md`](./docs/otel-adapter.md). Datadog covered via the Agent's OTLP receiver — same code.\n- `pr_creator` specialist + `protectedBranchGate` hook (with stub MCP — see backlog above for real wiring).\n\n---\n\n## License\n\n[MIT](./LICENSE) · © 2026 Abdul Rahman\n\n---\n\n<div align=\"center\">\n\n*Niato declares before it acts.*\n\n</div>\n","readmeFilename":"README.md"}