- Bun workspace monorepo (
bun@1.3.13) withapps/*andpackages/*; onlyapps/serverandapps/clicurrently exist. - Use root scripts from the repo root:
bun run dev:server,bun run dev:cli,bun run check:server,bun run check:cli,bun run build:server,bun run build:cli. - Root scripts intentionally use
bun run --cwd ./apps/<app>rather than Bun workspace filters. This is important for OpenTUI apps because runtime asset/module resolution can depend on the current working directory being the app folder; running from the repo root can make the TUI look for generated/native/runtime files in the wrong place. apps/cli/README.mdis scaffold boilerplate; trustpackage.jsonscripts over that README.
apps/server/src/index.tsis the server entrypoint: it serves the Hono app fromapps/server/src/app.tswithBun.serve(), defaulting toPORT=3000.apps/cli/src/index.tsxis the CLI entrypoint: keep it focused on renderer/root setup and route to screen components from there.- There are no shared workspace packages yet, so keep changes app-local unless you are intentionally introducing shared code.
- When the CLI calls the server, prefer the typed Hono RPC client in
apps/cli/src/lib/client.tsover rawfetchwhenever possible. - When an API consumer needs a URL string instead of making the RPC request directly, use the Hono RPC
.$url()helper from the typed client, e.g.client["ai-test"].$url().toString(), rather than manually concatenating server URLs and paths. - Keep server routes chained in
apps/server/src/app.tsand exportAppTypefrom the chained route value so the CLI RPC client can infer request and response types. - For Hono request body validation, prefer
zValidatorfrom@hono/zod-validatorwith a Zod schema over manualc.req.json()parsing,hono/validatorcasts, or ad hocunknownobject checks. - For client-side object parsing, prefer Zod schemas over manual
typeof/inchecks. This includes route state, search params, local storage payloads, and external data before rendering or branching on it. - Use raw
fetchonly for external services or endpoints that cannot reasonably use the Hono RPC client, and keep that exception local and explicit.
- Use kebab-case for source filenames (
home-screen.tsx,prompt-text-area.tsx), not PascalCase filenames. - Use extensionless relative imports for internal TypeScript/TSX modules.
- Keep CLI screens and UI components separated: screens belong in
apps/cli/src/screens, reusable components belong inapps/cli/src/components. - Do not bury reusable components inside screen folders; only keep code screen-local when it is truly private to that screen.
- Do not use IIFEs (e.g.
(async () => { ... })()). Define a named function and call it normally instead — it reads better in stack traces, is easier to debug, and keeps intent obvious. This applies especially to async work insideuseEffect.
- This stack is Hono RPC (server) + OpenTUI/React (CLI) + AI SDK (chat/agents). Whenever you add features, refactor, or scale any part of the project, consult the relevant skills first and prefer their idiomatic patterns over ad-hoc solutions:
honoskill — for routes, middleware, validation (zValidator), streaming, and the RPC client. We rely on Hono RPC for end-to-end types between server and CLI.opentuiskill — for any CLI/TUI work: components, layout, keyboard handling, the React reconciler, animations.ai-sdkskill — forstreamText,useChat, tool calling, structured output, providers, and chat transports.
- End-to-end type safety is non-negotiable. The chain is: Zod schemas validate inputs → chained Hono routes export
AppType→hc<AppType>inapps/cli/src/lib/client.tsinfers request/response types → CLI consumers use those inferred types directly. Do not break that chain.- Do not weaken types with
any,as unknown as T, non-null!, or?? ""/?? 0fallbacks just to silence errors. If TypeScript complains, fix the underlying shape (narrow with a guard, capture into aconst, add a Zod schema, or fix the route). - Never bypass the typed RPC client with raw
fetchto a hardcoded path — that severs inference. The only exception is genuinely external services, kept local and explicit (see API Requests). - Run
bun run check:cliandbun run check:serverbefore declaring a feature done; both must be clean.
- Do not weaken types with
- No CI workflows, pre-commit hooks, linters, formatters, or test files are present in this repo.
- The practical verification path is typecheck then build for the touched app:
bun run check:server && bun run build:serverorbun run check:cli && bun run build:cli. - Build output goes to ignored
dist/directories.
- CLI TSX depends on
apps/cli/tsconfig.json: JSX uses"@opentui/react"asjsxImportSource. - App TypeScript uses Bun globals (
types: ["bun"]) and bundler module resolution.
- Use Conventional Commits:
<type>(<scope>): <message> - Example:
feat(cli): implement React Router navigation with OpenTUI integration - Types:
feat,fix,docs,style,refactor,test,chore - Scope:
cli,server,shared, orrootfor repo-wide changes