4.1 KiB
4.1 KiB
shiro-neko
Agentic coding CLI, built with Bun + TypeScript. The interactive UI is React rendered to the
terminal with Ink; LLM access goes through the Vercel AI SDK (ai) with Anthropic, OpenAI,
OpenAI-compatible, and MCP providers. Entry point and only executable is src/cli.tsx (bin shiro).
Commands
bun install --frozen-lockfile— install deps (CI uses this; lockfile isbun.lock)bun run shiro— run the CLI from source (i.e.bun run src/cli.tsx)bun test— full test suite (bun:test, no other runner)bun run typecheck—tsc --noEmit; must pass before committingbun run build—bun build --compiletodist/shiro(single native binary)bun run release— cross-compile all five targets intodist/release/- No linter or formatter is configured; don't invent one.
CI (.github/workflows/ci.yml) runs install → typecheck → test → build on Ubuntu, macOS, and
Windows, pinned to Bun 1.3.14. Everything must be cross-platform: the tools shell out to the
platform shell, and paths in code and tests go through node:path, never hardcoded /.
Layout
src/— flat modules, one concern per file, lowercase names (session.ts,prune.ts).src/ui/holds the Ink components, PascalCase (App.tsx,Panels.tsx).test/— one<name>.test.tspersrc/<name>.ts;*.test.tsxfor UI tests viaink-testing-library.test/helpers.tshastestHooks(), the standard App fixture.docs/— user-facing docs, one per feature area.scripts/—release.ts,install.ts(+.sh/.ps1installers).src/version.ts— hardcoded VERSION; the release workflow fails if it disagrees with the git tag.
Conventions
- ES modules,
verbatimModuleSyntaxon: import types withimport type. Strict TS withnoUncheckedIndexedAccess— indexing givesT | undefined, so handle it (arr[i]!appears where provably safe). - Uses Bun APIs directly (
Bun.file,Bun.write,Bun.spawn,Bun.Glob) — no fs-extra, no node shims. File tools read/write throughBun.*, notfs, where practical. - Path safety: every user/model-supplied path goes through
jail()(insrc/ignore.ts), which rejects escapes outsideprocess.cwd(). Tools resolve paths againstprocess.cwd(). - Error handling: tool
executefunctions return error text to the model or throwErrorwith a plain message — no error classes, no codes. Storage reads (store.ts,memory.ts) catch and degrade to empty rather than throw on corrupt JSON. - Tools are AI-SDK
tool()objects with zodinputSchema. Any tool that mutates the workspace must be added toMUTATING_TOOLSinsrc/tools.ts— a test inpermission.test.tsfails otherwise. The permission system (src/permission.ts) matches rules against the tool's subject (command forbash, path for file tools) with glob matching, and is pure/no-IO on purpose. - Comments explain why, often naming the failure being guarded against. Match that style.
- Tests import from
bun:test, build mock models withMockLanguageModelV4+simulateReadableStreamfromai/test, and each test that touches the filesystem doesprocess.chdir()into a freshmkdtempdir inbeforeEachand restores inafterEach.
Surprising / easy to break
bun testruns the whole suite includingfallback-live.test.ts, which spins up real local HTTP servers viaBun.serve, andcommit.test.ts, which runs realgitin temp repos — both need a working network stack and git on PATH.- Tool output is capped (
MAX_OUTPUTinsrc/tools.ts); read_file returns NUL-sniffed binary files as an error. Don't remove these — they stop a model from burning its context. - Ink renders to the terminal, so library warnings are suppressed at the top of
cli.tsx(AI_SDK_LOG_WARNINGS = false); anything written to stderr tears the UI. - Sessions, memory, and history live under
~/.shiro-neko/, relocatable withSHIRO_HOME; tests depend on that env var to isolate state. Don't resolve the path eagerly at module load —store.tsresolves it per call for this reason. - Release tags must match
src/version.tsexactly orrelease.tsstops the build.