Skip to content
Hoody.com

Close your laptop mid-answer. Open your phone. The same turn is still streaming.

Your work should not depend on the device in front of you. Hoody keeps the working environment — your files, your sessions, the turn currently streaming — in your container, and the Hoody Agent runs right there beside it. Closing one device never means cloning, configuring, or explaining everything again on the next. You reach the Agent from wherever you happen to be: a browser tab, ssh hoody.com, or hoody agent in your own terminal. Three doors into the same running session. Nothing to install, nothing to sync, no context to ship.


You should not have to be at a particular desk to keep working.

When everything lives on one machine — your files, your keys, your half-finished session — none of it travels. Close the lid and the work stops. Pick up a different device and you start over: clone the repo again, paste the keys again, re-explain everything you already explained.

Hoody keeps that whole world in your container instead, with the Agent already running beside your files. So the work is never somewhere you have to get back to. Nothing to install. Nothing to sync. No certificates, no ports, no VPN, no “works on my machine”. You open a URL, or you type one command, and you are exactly where you left off — same session, same history, same turn still streaming. Phone at a bus stop, a borrowed laptop, your own terminal at 3am: the same work, still in motion.


hoody-agent is a terminal-native AI coding agent written in Go. It ships as two binaries:

  • hoody-agent-d — the daemon. It owns LLM streaming, tool execution, sessions, workflows, and memory.
  • hoody-agent — the client: a fast, fully mouse-driven TUI built on Bubble Tea, talking to the daemon over a Unix socket.

It’s a terminal UI on purpose. A TUI is a byte stream — the same Bubble Tea interface carries through your terminal emulator, over SSH, and into a browser tab through xterm.js. The browser layers its own integrations on top — clipboard images, DOM-backed web panes — so the three transports share the core interface without being pixel-for-pixel identical. That’s not a compromise; it’s what makes the three doors below one codebase instead of three. And “terminal” doesn’t mean keyboard-only: tabs, rows, headers, and chips are clickable, tables sort from their headers, pane dividers drag to resize, file paths open an in-TUI syntax-highlighted viewer, and URLs are real hyperlinks.


https://PROJECT_ID-CONTAINER_ID-agent-1.SERVER.containers.hoody.icu

The container’s terminal kit runs the agent binary as the session’s process — no shell underneath — and streams it to xterm.js: the same TUI, in a browser tab, with full mouse support. Instance indices start at 1; agent-1 is your first agent.

Or skip the URL entirely: os.hoody.com signs you in with your Hoody account and opens your container’s agent full-bleed, under a slim topbar.


The interface is organized into three tab capsules:

CapsuleTabsWhat lives there
AGENTSessions, Chat, Agents, Skills, Workflows, Hooks, TODOsThe AI work surface — conversations, named agent profiles, the MCP servers they reach, reusable skills, multi-step pipelines, a triageable work inbox
FLEETComputers, Files, Scripts, Domains, ConsoleYour Hoody machines — their live terminals and filesystems, published exec scripts, custom domains, and your platform account: servers, rentals, wallet, API keys
SYSTEMModels, Settings, Vault, Memory, LogsProviders and credentials, encrypted secrets, persistent memory, observability
Screenshot Needed The Hoody Agent TUI running in a browser tab — the AGENT, FLEET, and SYSTEM tab capsules across the top, a multi-pane chat grid streaming two sessions side by side
The same TUI, whether you opened a terminal, an SSH session, or a browser tab

The Chat surface is where you’ll live:

  • Multi-pane chat — split the surface into a live grid and watch several sessions stream at once; each pane scrolls independently and flags when it needs you.
  • Multi-attach sessions — the same session can be open in several windows and HTTP clients simultaneously. Every window streams the turn live, any of them can type or answer a permission gate, and a mid-turn joiner gets the full viewport plus the in-flight turn replayed. Closing one window just leaves; the session lives on for the others.
  • Clipboard images — copy an image anywhere and press Ctrl+V in the composer (right-click, then Paste, in the browser terminal). It attaches as a real image the model sees.
  • Paste collapse — drop a wall of text into the composer and it folds into a [Pasted text #N] chip you can click to edit. The model still receives the full content.
  • Web pages inside chat panes — when the TUI renders in the browser terminal, an “open a URL” pane overlays a genuine DOM iframe, clipped exactly to the pane’s rectangle. In a plain terminal the same pane degrades to a placeholder. A feature inside a terminal — not a window manager.
  • Attention notifications — when a background session needs a decision, a snackbar slides in that clicks straight to the waiting prompt.

A fresh install defaults to Hoody AI (hoody-ai/hoody-free), a passwordless relay that needs no separate model key wherever the relay is reachable and your network position is authorized; outside that path, you bring a supported model credential. The free default is there so your first prompt lands, not so it is the last model you ever use. Bring your own whenever you want — a catalogue of 100+ providers ships embedded in the binary, with credentials resolved from the environment or ~/.hoody/.env, and OAuth sign-in flows for ChatGPT Codex and OpenRouter right in the Models tab.

Using a Claude Pro/Max plan? Hoody implements no Anthropic OAuth flow of its own. Instead it delegates the turn to Claude Code: the separately installed claude CLI drives the reasoning on its own login, while Hoody’s tools stay mapped in and executed by Hoody — so the session still behaves like a Hoody session, with the same tools, the same files, the same confirmations you already know. Claude Code is currently the only agent a whole session can be delegated to; the Codex, Gemini, and OpenCode CLIs are instead available as one-shot session tools (run_codex, run_gemini, run_opencode) whenever the CLI is installed and enabled.

A delegated Claude Code session can delegate further, to its own subagents, and they stay inside the same confinement: a subagent gets no native file or shell tools of its own, so every action it takes is a Hoody tool executed by Hoody, under the same deny-list, confirmations and sandbox as the parent’s. Their tokens are counted toward the session’s token usage rather than going unbilled. Two limits are deliberate — a subagent cannot spawn a further subagent, and subagents run in the foreground, so the work belongs to the turn you are watching rather than continuing after it. Hoody’s own background subagents and orchestrator mode are unaffected and remain available.

Switch models mid-session, pin a model per agent profile, pool multiple OAuth accounts per provider and rotate through them as each hits its limit — and when one model isn’t enough, a client-side fusion engine can vote, synthesize, or debate across several.

It’s also engineered to be frugal with tokens, in the code rather than the model: Hoody runs one generic agent and delivers phase instructions as user messages — so moving from exploring to planning never blows away the prompt cache — and exploration reads go through a minified virtual file system that strips whitespace the model doesn’t need.


Everything that shapes the agent’s behavior is a file under .hoody/ — versionable, diffable, copyable between containers:

  • Agents (.hoody/agents/*.md) — named profiles that drive chat sessions, subagents, and workflow steps. Swap the active agent mid-session.
  • Skills (.hoody/skills/<name>/SKILL.md) — reusable instruction sets with /<skill> slash commands. They hot-reload — drop in a SKILL.md and live sessions pick it up — and the Skills tab searches and installs from the skill hub.
  • Workflows — multi-step agent pipelines defined in JSON. Steps marked read_only hard-gate the step and every subagent it spawns to a read-only tool set. Plan, Dev Pipeline, and Codebase Map ship built in — Codebase Map is a one-click “land on a new repo and understand it” job whose second pass fact-checks the draft against the real code.
  • Hooks — your own shell commands on agent lifecycle events, Claude Code-compatible.
  • TODOs — a work inbox the agent shares with you: file items, let an LLM triage pass sort them, and dispatch any todo as a background orchestrator run.
  • Background subagents — spawn parallel work; the parent is notified on completion, and a live, scrollable transcript opens for each one. Orchestrator mode goes further: a delegation-first persona with no file or shell tools of its own, which won’t report a change as done until an independent reviewer subagent verifies it.

Beyond file and shell tools, the agent ships web fetch, model-routed web search, image generation, grep/glob, tree-sitter code scanning with optional per-language LSP, persistent memory with hybrid recall, and MCP servers.


Model Context Protocol servers bring third-party tools — GitHub, Jira, a database, your own internal service — into a session beside the agent’s built-in ones. They arrive namespaced as mcp__<server>__<tool>, and the model can call them the moment they connect. The client is built on the official Go SDK and speaks every spec revision from 2024-11-05 through the current 2026-07-28, negotiating with each server automatically — there is no revision to pin and nothing to keep in step by hand.

You no longer edit a file to add one. The Agents tab has a second mode, MCP servers — press M on the Agents tab, click it on the mode strip above the list, or press M from the Skills tab when the question is “where did this tool come from”. M again — or esc — goes back to Definitions. (tab does not: the tab switcher claims it before either surface sees it.) It lists every configured server with live state: connected or not, the revision it negotiated, how many tools it advertises, the process id of a stdio child, and the tail of its stderr when one dies.

KeyDoes
space / enterEnable or disable the selected server
d / deleteArm removal — press it again to confirm; any other key disarms it
iImport servers from a pasted config
RRe-read the config from disk and reconnect (rate-limited to once every 5s per session)
rRefresh the list
M / escBack to Definitions

Removal asks twice on purpose: deleting an entry destroys its env and headers values, and the read surface never returns those, so there is nothing on screen to retype them from. The arm is cancelled by anything that changes what the next press would act on — moving the selection, leaving the surface or the tab, or a refresh that reloads the list.

Import understands the config you already have. Paste a block straight out of Claude, Cursor or VS Code — all three dialects are recognized, and a document that mixes them is refused rather than guessed at. ctrl+p previews exactly what would be written, with credential values stripped; ctrl+s writes it. Imported servers land disabled, so nothing runs before you have read it and switched it on.

All of it applies to sessions that are already open. Disabling or removing a server cuts it off immediately — mid-turn, before the next call is admitted. A call that has already been sent to the server is allowed to return its result, since the server has already acted on it; nothing new is admitted after that point. A healthy server you did not touch is never restarted by an unrelated edit.

Servers live under mcp_servers in any settings layer — ~/.hoody/settings.json, then the project’s, then local — and the layers merge field by field, so a project file can add one restriction without restating the whole entry.

A realistic starting set — each entry shows a shape that behaves differently:

{
"mcp_servers": [
{
"name": "filesystem",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/workspace"],
"require_confirmation": true
},
{
"name": "github",
"command": "docker",
"args": ["run", "-i", "--rm", "-e", "GITHUB_PERSONAL_ACCESS_TOKEN",
"ghcr.io/github/github-mcp-server"],
"env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_TOKEN}" }
},
{
"name": "git",
"command": "uvx",
"args": ["mcp-server-git", "--repository", "/workspace"]
},
{
"name": "sentry",
"type": "http",
"url": "https://mcp.sentry.dev/mcp",
"headers": { "Authorization": "Sentry-Bearer ${SENTRY_TOKEN}" },
"allowed_tools": ["search_issues", "search_events"]
}
]
}

Read that as four decisions, not four servers:

EntryThe thing worth copying
filesystemPass the narrowest path that still works. That argument is the server’s own directory-access control, not Hoody’s — the process still runs with your privileges, so treat it as tidiness, not confinement. require_confirmation is the part Hoody enforces, and it gates every call from that server, reads included.
githubThe variable the server expects (GITHUB_PERSONAL_ACCESS_TOKEN) and the one holding your secret (GITHUB_TOKEN) need not match: the left side is the server’s name for it, the right side is ${VAR} read from the daemon’s environment. Note -e NAME with no value — Docker forwards it from the environment Hoody built, so the token never appears in args.
gitNot everything is npx. uvx (Python) works identically — Hoody runs a command and does not care which ecosystem it came from.
sentryA remote server: type: "http", no subprocess, nothing installed. Also a warning against pattern-matching — Sentry requires Sentry-Bearer, not Bearer, because plain Bearer is reserved for MCP OAuth tokens there. Read the server’s own auth docs rather than assuming. allowed_tools is an exact-name filter, so a near-miss name silently exposes nothing.
  • typestdio (the default), http/url for Streamable HTTP, or sse for the deprecated 2024-11-05 HTTP+SSE transport. All three are implemented; an unrecognized value is a config error, not a silent fall back to stdio.
  • name — letters, digits, _ and -; no __ (that separates the tool name), and neither hoody nor mcp.
  • enabled — absent means enabled. A disabled server keeps its config and its credentials; it is simply not connected and not advertised.
  • allowed_tools — absent means no restriction. Present and empty means expose nothing.
  • require_confirmation — route every call from this server through the confirm gate. A tool the server itself marks destructive forces one anyway.
  • env (stdio) and headers (URL) — ${VAR} is expanded against the daemon’s environment. Only these two. command, args and url are used verbatim: a literal secret written there is stored in clear text in settings.json, and a ${VAR} written there is not substituted. What that breaks depends on the field: in args the server receives the six literal characters ${VAR} and usually fails to authenticate with no obvious cause; in command the daemon tries to execute a program literally named ${VAR} and the server never starts at all; in url the literal reaches URL parsing.

A stdio server starts with a scrubbed environment: no provider API key and no Hoody platform token reaches it by inheritance. Naming one is how you share it on purpose — "env": {"GITHUB_TOKEN": "${GITHUB_TOKEN}"} reads the daemon’s own value and hands it to that one server.

One rule is worth knowing before splitting config across layers: if a later layer changes what a server is — its type, command, args or url — without also supplying env/headers, those are cleared rather than inherited. Credentials are never carried onto new code or a new endpoint.

The same surface is scriptable under /api/v1/agent/mcp/… on the container’s agent URL — GET/PUT/DELETE /mcp/servers, plus POST /mcp/servers/enable, /mcp/import, /mcp/parse and /mcp/reconnect. Writes are two-step: POST /mcp/write-intents mints a single-use nonce bound to one operation and one settings layer, and the write presents it together with expect_hash, the content hash you last read.

Both are required on every write — upsert, delete, enable and import alike — so a concurrent edit comes back as a conflict instead of a silent overwrite. A first write into a settings file that does not exist yet is no exception: listMCPServers and beginMCPWrite return the empty-array hash for a missing file, and you pass that. Stating “I expect nothing there yet” is a precondition like any other, not a case for leaving the field off.

POST /mcp/probe — connect to a candidate config, see what it offers, save nothing — is human-only and refuses a machine caller, because probing starts a process or reaches out to a URL of the caller’s choosing.


Connect the agent to the Hoody platform and the FLEET capsule lights up:

  • Manage remote containers and their filesystems (Computers, Files), and run tools on the remote container instead of locally.
  • Drive multiple live container terminals in a grid, right beside each machine’s details (Computers).
  • Handle the account side of the platform without leaving the TUI — servers, rentals, wallet, account, API keys (Console).
  • Publish exec Scripts as instant HTTP endpoints and connect custom Domains.
  • The agent gains first-class fleet tools — discover containers, author and run exec scripts, supervise long-running programs, schedule cron jobs, send notifications. They only exist when the platform is reachable, so a standalone session carries none of their prompt cost.
  • Browse a realm-scoped Vault with client-side AES-256-GCM encryption — the TUI encrypts; the daemon only ever sees envelopes, never plaintext.

None of it is required. Build the agent yourself and everything outside this section — the TUI, the models, the files under .hoody/ — works exactly the same on your own machine; the fleet surfaces simply stay dark.


The entire agent — sessions, workflows, skills, models, memory, logs — is also an HTTP surface under /api/v1/agent/... on the container’s agent URL. It is described by a generated OpenAPI 3.1 spec, served live at /api/v1/agent/openapi.json (and .yaml) on that same agent URL — the daemon can also write it offline with hoody-agent-d --generate-openapi — and browsable in the Agent API reference. The same URL you open in a browser tab is the one your scripts, webhooks, and other agents call — there is no second, separate “API mode” to set up.

Terminal window
# One-shot: stream a full agent turn from your shell
hoody agent prompt "Run the tests and fix the first failure" -c $CONTAINER_ID
# Unattended: auto-approve confirm gates for the life of the turn
hoody agent prompt "Upgrade the deps and rerun the suite" -c $CONTAINER_ID -y
# The generated subcommands cover the whole surface
hoody agent sessions list -c $CONTAINER_ID

Inside a Hoody container, the Agent arrives finished: the binary and daemon are installed and already started, your sessions and files stay with the container, and the TUI is reachable the moment you are — browser, SSH, or CLI. There is no setup step, no policy file to write, no first-run checklist.

The quieter mechanics are implementation details, already handled. Command execution uses what your OS provides — Landlock on Linux, with a bubblewrap fallback; Seatbelt on macOS — and where no such layer is available, the Agent says so explicitly. Confirmation rules and terminal-output sanitization stay active without a single thing for you to configure. And the Agent sends no analytics, no crash reports, no device identifier, no telemetry network calls at all. It still contacts the model providers, web services, OAuth endpoints, and optional Hoody platform services that you choose to use.


The agent works completely standalone on your own machine — no Hoody account, no platform. You bring a model credential, and the FLEET capsule simply stays dark.

It is pre-release and source-built: there are no published binaries, no installer, and no package yet. Building it is one command — you need Go 1.26+, Git, and make:

Terminal window
git clone https://github.com/hoody-network/hoody.git
cd hoody
make build # produces ./bin/hoody-agent and ./bin/hoody-agent-d
./bin/hoody-agent # TUI — auto-starts a private daemon
./bin/hoody-agent -p "Explain this repository" # headless one-shot

Inside a Hoody container, none of this applies — the agent is already installed and already answering at its URL.



Your work, available through three doors. Open a browser, connect through SSH, or use your terminal — and continue in the same container. Nothing to move. Nothing to re-create. Open it and keep going.