Agent chat UI powered by Claude Agent SDK, Bun, React, and Tailwind.
server/β Bun server, agent SDK, WebSocket, state persistenceclient/β React SPA, components, hooks, styleslib/β Shared types between server and client
AGENTS.md files (root, client/, server/) are the canonical instructions; every CLAUDE.md is a symlink to its sibling AGENTS.md. Edit the AGENTS.md files, never the symlinks. Rules and skills likewise live in agent-neutral directories, with symlinks for Claude Code:
.agents/rules/β topic rules (canonical)..claude/rulesis a symlink to it, so Claude Code auto-loads them. Other agents: read the relevant rule before editing matching files:typescript.md(*.ts,*.tsx) β props types, noany,typeoverinterfacetailwind.md(*.tsx,*.css) β Tailwind-only styling, no inline styles,cn()icons.md(*.tsx) β@tabler/icons-reactonly, sizing and stroke conventionsanimations.md(*.tsx,*.css) βtw-animate-cssutilities, no custom keyframesbun.mdβ Bun instead of Node/npm/vite, Bun-native APIsREADME.mdβ format for writing new rules
.agents/skills/β skills in theSKILL.mdAgent Skills format (canonical). Codex and other tools that follow the standard discover this directory natively; Claude Code loads them via per-skill symlinks in.claude/skills/. When adding a skill, put it in.agents/skills/<name>/and add a matching symlink in.claude/skills/.
Claude-only config (.claude/settings.json hooks, .claude/launch.json) stays in .claude/.
Client connects via WebSocket. Agent responses stream back and are broadcast to all clients.
bun run devβ Start the dev server on port 13337 (alias forbun server/cli.ts start --dev).
bun run dev starts a small supervisor that runs the server without bun --hot:
- Frontend (
client/) β hot-reloaded in place byBun.serve's dev bundler (HMR). Edit a React component and the browser updates; no restart. - Server (
server/,lib/) β the supervisor watches these and does a full process restart on change (gracefulSIGTERMβ close servers, kill function workers, respawn). Module state is rebuilt cleanly each time.
Do not start the server with bun --hot server/web.ts or the old moi start --dev path: bun --hot soft-reloads server modules in place, which churns the dev bundler's chunk hashes and serves stale frontend bundles (the symptom: edits don't show up, browser keeps old code). The supervisor exists specifically to avoid this.
Only one server runs at a time β it binds port 13337 (HTTP) and 13059 (control). To restart, kill the existing bun run dev process and start it again; a second instance fails on the control port.
- Dev link:
bun linkin the repo βmoiruns current source. Keepdist/deleted β ifdist/index.htmlexists the server silently serves that stale prebuilt client (server/static.ts); onlybun run devignores it (MOI_DEV). - Prod test before publish:
bun pm pack(prepack builds the client) βbun install -g ./moi-computer-<version>.tgz. Tests the real artifact:fileswhitelist, prebuiltdist/, production React, global install tree. - Published:
bun install -g moi-computer@latest.
All three overwrite ~/.bun/bin/moi β last action wins; readlink ~/.bun/bin/moi shows which. Kill the running server before switching (single port 13337).
To drive the app in a browser inside Claude Code on the web, use the vendored agent-browser skill (.agents/skills/agent-browser/). Cloud-specific setup β server startup, AGENT_BROWSER_EXECUTABLE_PATH, Playwright alternative, egress-relay caveats β is in docs/browser-testing-cloud.md.
Claude Code stores sessions as .jsonl files under:
- macOS/Linux:
~/.claude/projects/<encoded-path>/ - Windows:
%USERPROFILE%\.claude\projects\<encoded-path>\
Path encoding: each / (or \ on Windows) in the working directory path is replaced with -.
Example: /Users/foo/my-project β -Users-foo-my-project
Potential use: auto-discover workspaces by listing ~/.claude/projects/ and decoding folder names back to paths.