65 lines
4.1 KiB
Markdown
65 lines
4.1 KiB
Markdown
# 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 is `bun.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 committing
|
|
- `bun run build` — `bun build --compile` to `dist/shiro` (single native binary)
|
|
- `bun run release` — cross-compile all five targets into `dist/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.ts` per `src/<name>.ts`; `*.test.tsx` for UI tests via
|
|
`ink-testing-library`. `test/helpers.ts` has `testHooks()`, the standard App fixture.
|
|
- `docs/` — user-facing docs, one per feature area.
|
|
- `scripts/` — `release.ts`, `install.ts` (+ `.sh`/`.ps1` installers).
|
|
- `src/version.ts` — hardcoded VERSION; the release workflow fails if it disagrees with the git tag.
|
|
|
|
## Conventions
|
|
|
|
- ES modules, `verbatimModuleSyntax` on: import types with `import type`. Strict TS with
|
|
`noUncheckedIndexedAccess` — indexing gives `T | 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 through `Bun.*`, not `fs`, where practical.
|
|
- Path safety: every user/model-supplied path goes through `jail()` (in `src/ignore.ts`), which
|
|
rejects escapes outside `process.cwd()`. Tools resolve paths against `process.cwd()`.
|
|
- Error handling: tool `execute` functions return error text to the model or throw `Error` with 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 zod `inputSchema`. Any tool that mutates the workspace
|
|
must be added to `MUTATING_TOOLS` in `src/tools.ts` — a test in `permission.test.ts` fails
|
|
otherwise. The permission system (`src/permission.ts`) matches rules against the tool's subject
|
|
(command for `bash`, 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 with `MockLanguageModelV4` +
|
|
`simulateReadableStream` from `ai/test`, and each test that touches the filesystem does
|
|
`process.chdir()` into a fresh `mkdtemp` dir in `beforeEach` and restores in `afterEach`.
|
|
|
|
## Surprising / easy to break
|
|
|
|
- `bun test` runs the whole suite including `fallback-live.test.ts`, which spins up real local
|
|
HTTP servers via `Bun.serve`, and `commit.test.ts`, which runs real `git` in temp repos — both
|
|
need a working network stack and git on PATH.
|
|
- Tool output is capped (`MAX_OUTPUT` in `src/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 with `SHIRO_HOME`;
|
|
tests depend on that env var to isolate state. Don't resolve the path eagerly at module load —
|
|
`store.ts` resolves it per call for this reason.
|
|
- Release tags must match `src/version.ts` exactly or `release.ts` stops the build.
|