Tiny Engineer is a Wi-Fi desk robot. Drive it from any tool that can make HTTP requests, or use one of the dedicated helpers (Cursor, Antigravity, Claude Code) that map agent hook events to poses.
Robot must be on the same network. Base URL: http://tiny-engineer.local (or the IP shown on the OLED). Full HTTP reference: api.md.
Four integration paths:
| Path | Best for | How |
|---|---|---|
| REST API | Any AI IDE, script, CI, custom agent | POST /anim?name=… |
| Cursor CLI | Cursor project hooks | npx → tiny-engineer-cursor |
| Antigravity CLI | Antigravity CLI lifecycle hooks | tiny-engineer-antigravity |
| Claude Code CLI | Claude Code project hooks | tiny-engineer-claude-code |
flowchart TB
subgraph anyIde [Any AI IDE or script]
HookOrScript[Agent event or your code]
HookOrScript --> Post["POST /anim?name=typing|reading|…"]
end
subgraph cursorPath [Cursor]
CursorHook[Cursor hooks.json]
CursorHook --> Npx["npx tiny-engineer-cursor"]
Npx --> Post
end
subgraph antigravityPath [Antigravity]
AgHook[Antigravity hooks.json]
AgHook --> AgCli["tiny-engineer-antigravity"]
AgCli --> Post
end
subgraph claudeCodePath [Claude Code]
CcHook[Claude Code settings.json]
CcHook --> CcCli["tiny-engineer-claude-code"]
CcCli --> Post
end
Post --> Robot[Tiny Engineer on Wi-Fi]
Call the board directly. Works with Claude Code, Windsurf, Continue, custom plugins, shell hooks, or anything that can POST over HTTP.
curl -X POST "http://tiny-engineer.local/anim?name=typing"name |
Typical use |
|---|---|
typing |
Agent writing / editing / running tools |
reading |
Agent reading files / context |
thinking |
Agent reasoning / waiting on a long step |
ring |
Turn finished (attention ping) |
welcome |
Session start / greeting |
wakeup |
Sleep-inertia wake (eyes/head) |
sleep |
Force sleep (eye close + OLED off) |
attention |
Needs user input |
error / abort |
Failure / cancel |
dead |
Out of power (dead.wav, then X X hold) |
none |
Idle / clear pose |
Firmware holds each pose ≥1s and keeps only the latest pending switch — spam-safe. Auth is optional: if you set an access token on the device, send Authorization: Bearer <token> (check GET /auth for required). Prefer short timeouts (e.g. 2s) and ignore network errors so the agent never stalls if the robot is offline.
Shell
curl -4 -sS -m 2 -X POST "http://tiny-engineer.local/anim?name=reading" >/dev/null || trueJavaScript (Node 18+)
await fetch("http://tiny-engineer.local/anim?name=thinking", {
method: "POST",
signal: AbortSignal.timeout(2000),
}).catch(() => {});Python
import urllib.request
urllib.request.urlopen(
urllib.request.Request(
"http://tiny-engineer.local/anim?name=typing",
method="POST",
),
timeout=2,
)Wire these into your IDE’s hook / plugin / lifecycle events (prompt submitted → reading, tool use → typing, turn end → ring, etc.). Mapping is yours; the robot only cares about name.
Health check (no motion):
curl http://tiny-engineer.local/healthMore routes (tests, servo, web UI): api.md.
For Cursor only: a small Node CLI reads Cursor hook JSON on stdin, picks an animation, and POSTs /anim. No need to implement the event map yourself.
npx -y --package=https://github.com/jamro/tiny-engineer/archive/refs/heads/main.tar.gz tiny-engineer-cursor- HTTPS tarball (not
github:…SSH shorthand — that often fails in Cursor hooks with no SSH agent). - Bin name
tiny-engineer-cursorrequired after--package=…. - Optional:
--url http://192.168.x.x(defaulthttp://tiny-engineer.local). - Auth: if the device has an
access_token, setTINY_ENGINEER_TOKENin the process env or a project-root.envfile (same value). The CLI sendsAuthorization: Bearer …. No token → no header (auth disabled on device). --helpfor usage and event map.
npx -y --package=https://github.com/jamro/tiny-engineer/archive/refs/heads/main.tar.gz tiny-engineer-cursor --help
echo '{"hook_event_name":"stop"}' | npx -y --package=https://github.com/jamro/tiny-engineer/archive/refs/heads/main.tar.gz tiny-engineer-cursorIn that project’s .cursor/hooks.json, use the same command for each anim hook. Set timeout ≥ 30 (cold npx can exceed 2s). Full sample + event table: hooks.md.
Inside this firmware repo you can instead run the local bin while developing the CLI:
node packages/tiny-engineer-cursor/bin/tiny-engineer-cursor.jsFor Google Antigravity (antigravity-cli): a lightweight Node CLI receives Antigravity lifecycle hook payloads on stdin, maps the agent lifecycle / tool calls to animations, and responds with the expected hook decision JSON while posting /anim.
PreInvocation(Model thinking) →thinkingPreToolUse(Reading tools:view_file,grep_search,find_by_name,list_dir, etc.) →readingPreToolUse(Writing tools:write_to_file,replace_file_content,run_command) →typingPostToolUse(Tool error) →attentionStop(model_stop/ completion) →ring(rings the physical desk bell!)Stop(error/aborted) →error/abort
Inside this firmware repository, .agents/hooks.json is pre-configured. Run the local bin: node packages/tiny-engineer-antigravity/bin/tiny-engineer-antigravity.js. Other machines can npx -y --package=https://github.com/jamro/tiny-engineer/archive/refs/heads/main.tar.gz tiny-engineer-antigravity (same tarball pattern as Cursor).
To run globally across all projects on your machine, configure ~/.gemini/config/hooks.json:
{
"tiny-engineer": {
"PreInvocation": [
{
"command": "tiny-engineer-antigravity PreInvocation",
"timeout": 3
}
],
"PreToolUse": [
{
"matcher": "*",
"hooks": [
{
"command": "tiny-engineer-antigravity PreToolUse",
"timeout": 3
}
]
}
],
"PostToolUse": [
{
"matcher": "*",
"hooks": [
{
"command": "tiny-engineer-antigravity PostToolUse",
"timeout": 3
}
]
}
],
"Stop": [
{
"command": "tiny-engineer-antigravity Stop",
"timeout": 3
}
]
}
}For Claude Code: a small Node CLI reads Claude Code hook JSON on stdin, picks an animation, and POSTs /anim. Same shape as the Cursor CLI — one command for every hook, no animation args.
SessionStart(startup, a fresh launch) →welcomeSessionStart(resume,clear,compact,fork, or nosource) →wakeupSessionEnd→sleep(skipped forclearandresume, where the nextSessionStartwakes the robot)UserPromptSubmit→readingPreToolUse(Reading tools:Read,Grep,Glob,WebFetch,WebSearch) →readingPreToolUse(Writing tools:Bash,PowerShell,Edit,Write,NotebookEdit) →typingSubagentStart→thinkingPostToolBatch(tool results go back to the model) →thinkingPostToolUseFailure(a tool call failed) →errorPreCompact→thinkingPermissionRequest→attentionPermissionDenied(auto mode denied a tool call) →abortNotification(permission prompt, or waiting for input) →attentionStop(turn finished) →ring(rings the physical desk bell!)StopFailure→error
To change the map, edit packages/tiny-engineer-claude-code/src/map.js.
Inside this firmware repository, .claude/settings.json is pre-configured: open the repo in Claude Code (Node.js 18+) and the hooks run node packages/tiny-engineer-claude-code/bin/tiny-engineer-claude-code.js.
- Every hook is
"async": true, so Claude Code runs it in the background — an offline robot never stalls a tool call on the 2s HTTP timeout. - Robot address:
TINY_ENGINEER_URLor--url http://192.168.x.x(defaulthttp://tiny-engineer.local). - Auth: if the device has an
access_token, setTINY_ENGINEER_TOKENin the process env or a project-root.envfile. The CLI sendsAuthorization: Bearer …. No token → no header (auth disabled on device). When the token comes from the process env, aTINY_ENGINEER_URLin the project.envis ignored, so a project can't redirect your token; set the URL in the env or with--url. - The CLI never writes to stdout in hook mode — Claude Code adds
SessionStart/UserPromptSubmithook stdout to the model's context. - If a hook never fires, run
/hooksinside Claude Code to confirm the project settings loaded.
To use it in every project, copy the hooks block into ~/.claude/settings.json and replace $CLAUDE_PROJECT_DIR/packages/… with the absolute path to this repo's bin.
node packages/tiny-engineer-claude-code/bin/tiny-engineer-claude-code.js --help
echo '{"hook_event_name":"Stop"}' | node packages/tiny-engineer-claude-code/bin/tiny-engineer-claude-code.js- Building for one IDE / custom agent → REST. One
POST, zero Node dependency. - Using Cursor and want zero mapping code → Cursor CLI + hooks.
- Using Antigravity CLI → Antigravity CLI + hooks.
- Using Claude Code → Claude Code CLI + hooks.
- All four are fine together: the CLIs are thin clients of the same
/animAPI.
Prerequisites for any path: flash firmware, join 2.4 GHz Wi-Fi, confirm http://tiny-engineer.local/health (or the OLED IP) responds.
Same POST /anim API; not part of the four paths above. On macOS you can run a small host helper that POSTs sleep / wakeup when the screen locks or unlocks: macos-lock-unlock.md. The robot does not need it.