Skip to content
Hoody.com

The Hoody Agent runs inside your container, next to your files and sessions. Because the working environment lives there rather than on any one device, a turn that is streaming when you close your laptop is still streaming when you open your phone. You reach the agent from a browser tab, from ssh hoody.com, or with hoody agent in your own terminal; all three open the same running session, with nothing to install and no context to move.


When everything lives on one machine, none of it travels: close the lid and the work stops, and picking up a different device means cloning the repository again, pasting the keys again, and re-explaining what you already explained.

Hoody keeps that environment in your container instead, with the agent already running beside your files. There is nothing to install or sync on the device you are holding, no certificate, port, or VPN to set up, and no environment drift between devices. Opening a URL or typing one command returns you to the same session, the same history, and the same turn still streaming, whether from a phone, a borrowed laptop, or your own terminal.


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 fully mouse-driven TUI built on Bubble Tea that talks to the daemon over a Unix socket.

The terminal interface is deliberate. A TUI is a byte stream, so 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 shared interface is what lets the three access paths below run on one codebase instead of three. “Terminal” does not 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.com

The container’s terminal kit runs the agent binary as the session’s process, with 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.

You can also skip the URL: 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 Coming Soon 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

Most of the work happens in the Chat surface:

  • 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. It is 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 covers your first prompt; you can switch providers at any time. 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 in the Models tab.

For a Claude Pro or Max plan, Hoody implements no Anthropic OAuth flow of its own. 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 keeps the same tools, the same files, and the same confirmations. Claude Code is currently the only agent a whole session can be delegated to; the Codex, Gemini, and OpenCode CLIs are 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 completes within the turn you are watching rather than continuing after it. Hoody’s own background subagents and orchestrator mode are unaffected and remain available.

You can switch models mid-session, pin a model per agent profile, and pool multiple OAuth accounts per provider, rotating through them as each hits its limit. A client-side fusion engine can also vote, synthesize, or debate across several models.

Token frugality is implemented in the client code rather than left to the model: Hoody runs one generic agent and delivers phase instructions as user messages, so moving from exploring to planning never invalidates the prompt cache, and exploration reads go through a minified virtual file system that strips whitespace the model does not need.


Everything that shapes the agent’s behavior is a file under .hoody/, so it can be versioned, diffed, and copied 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 job that surveys an unfamiliar repository, and its 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 does not 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 or track by hand.

Adding a server does not require editing a file. 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 to trace where a tool came from. M again, or esc, goes back to Definitions (tab does not: the tab switcher claims it before either surface sees it). The mode 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 accepts 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 the config 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, where 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"]
}
]
}

Each entry demonstrates one decision:

EntryWhat it shows
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. -e NAME with no value makes Docker forward 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.
  • type: stdio (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 fallback 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, and only in these two fields. 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 that you expect nothing there yet is a precondition like any other, not a reason to leave the field off.

POST /mcp/probe, which connects to a candidate config, reports what it offers, and saves 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 becomes active:

  • 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, 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 fleet tools: discover containers, author and run exec scripts, supervise long-running programs, schedule cron jobs, send notifications. They exist only 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, and 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 the same on your own machine; the fleet surfaces stay inactive.


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 and browsable in the Agent API reference; the daemon can also write it offline with hoody-agent-d --generate-openapi. The URL you open in a browser tab is the same one your scripts, webhooks, and other agents call; there is no 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 installed: the binary and daemon are present and already started, your sessions and files stay with the container, and the TUI is reachable the moment you are, from a browser, SSH, or the CLI. There is no setup step and no policy file to write.

Sandboxing and confirmation defaults are on without configuration. 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 with nothing for you to configure. The agent sends no analytics, crash reports, device identifiers, or other telemetry network calls; it still contacts the model providers, web services, OAuth endpoints, and optional Hoody platform services that you choose to use.


The agent also runs standalone on your own machine, without a Hoody account or the platform. You bring a model credential, and the FLEET capsule stays inactive.

It is pre-release and source-built: there are no published binaries, installers, or packages yet. Building it takes 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 answering at its URL.