diff --git a/README.md b/README.md index 5a5df3e..8e5b1bf 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,10 @@ Most agents need no plugin: add the MCP server `https://mcp.box.simular.ai/mcp` | Any of Claude Code, Codex, Cursor, OpenCode, Gemini CLI, Hermes, OpenClaw, VS Code (and steps for Claude Desktop) | [`cli/`](cli/) | OAuth; personal key with `--auth key` for Hermes and OpenClaw | `npx @simular-ai/box connect` (check with `npx @simular-ai/box doctor`) | | Hermes Agent | [`hermes/`](hermes/) | Personal key (`cb_...`) in `MCP_BOX_API_KEY`; OAuth optional | [hermes/README.md](hermes/README.md) | +## Offload: move a coding task into your box and back + +Going offline in the middle of a task? In Claude Code with the plugin, `/box:offload` hands the task to a fresh Claude Code (or Codex, Cursor) session in your box, which keeps working while you are away; `/box:back` brings its commits and its written hand-over home and merges them. Any other agent or a plain terminal uses `npx @simular-ai/box offload` and `npx @simular-ai/box back`. Setup and details: [cli/README.md](cli/README.md#offload). + ## Adding an agent Add a folder named after the agent with its own manifest, MCP config and, where the agent supports skills, a copy of `claude/skills/box/SKILL.md`. Point it at `https://mcp.box.simular.ai/mcp`. Use OAuth sign-in where the agent supports it; otherwise read a personal key from an environment variable or the agent's own credentials store, never from a file in the package. Add a row above. diff --git a/claude/.claude-plugin/plugin.json b/claude/.claude-plugin/plugin.json index b512aab..af3d7de 100644 --- a/claude/.claude-plugin/plugin.json +++ b/claude/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "box", "displayName": "box by Simular", - "version": "0.1.1", + "version": "0.2.0", "description": "Give Claude a computer of its own: a cloud Linux desktop with Chrome and a shell that it drives over MCP, while your own computer stays free. You sign in once in the browser; no key to paste.", "author": { "name": "Simular", diff --git a/claude/commands/back.md b/claude/commands/back.md new file mode 100644 index 0000000..9d67e1e --- /dev/null +++ b/claude/commands/back.md @@ -0,0 +1,5 @@ +--- +description: Bring the task back from my box and continue it here +--- + +Use the box-offload skill: BACK. $ARGUMENTS diff --git a/claude/commands/offload.md b/claude/commands/offload.md new file mode 100644 index 0000000..c7efe9a --- /dev/null +++ b/claude/commands/offload.md @@ -0,0 +1,5 @@ +--- +description: Move this coding task into my box, where an agent keeps working on it +--- + +Use the box-offload skill: OFFLOAD this task to my box. $ARGUMENTS diff --git a/claude/skills/box-offload/SKILL.md b/claude/skills/box-offload/SKILL.md new file mode 100644 index 0000000..1a99c19 --- /dev/null +++ b/claude/skills/box-offload/SKILL.md @@ -0,0 +1,95 @@ +--- +name: box-offload +description: Hand this coding task to an agent in the user's box (their cloud computer) and take it back. Use when the user says offload, "move this to my box", "keep working while I'm away", "I'm back" or "bring it back", or when a message is exactly OFFLOAD or BACK. +--- + +The agent in the box starts fresh. It knows only what your hand-over says, plus the repo. +Write the hand-over as if for a capable engineer who has never seen this conversation. +`box` below is `npx -y @simular-ai/box@^0.2.0`. Run it in the repo the user works in. It does every +mechanical step: never copy files or secrets yourself, never print a secret, never edit the +files it writes. + +## OFFLOAD + +1. Finish the step you are in, or stop at a safe point. Do not start new work. +2. Run `box offload handover-path` in the repo and write the hand-over to the path it + prints, with the template below. Fill every section; write "none" for an empty one. Be + concrete: exact commands, versions you saw, file paths, error messages, decisions with + their reasons. Under "Secrets needed" give names only, never values. +3. Run `box offload --kind claude` (or the CLI the user asked for in the box: + `codex`, `cursor`). Add `--box NAME` when the user names a box. + - "not signed in": ask the user to run `box login` in their terminal (it opens the + browser), then run offload again. + - Exit 3 is a block: stop and tell the user why, one line per finding. If the box CLI is + missing or signed out, tell the user to run, in their own terminal, + `box offload install-cli --kind ` and then `box offload sign-in --kind ` + (the sign-in asks them to approve in the browser and paste a code, so you cannot do it). + - Exit 4 is warnings: tell the user each one, and run again with `--yes` only when they + agree, or said beforehand to go ahead. + - For a secret it warns about, ask the user; only they opt one in + (`box offload secrets add file:.env`). Never opt in on your own. +4. When it prints `offloaded`, tell the user the box, that its agent runs with permission + prompts bypassed, and that `/box:back` returns the work. End your turn and make no more + changes to this repo. +5. On exit 1, report its last line. Nothing changed on this computer; do not retry by hand. + +While the task is in the box, `box offload inbox` shows what the box agent posted +(progress, questions), `box offload send ""` answers it, and +`box offload status` shows whether it is working. The user can watch its terminal with +the `rsh -t ... tmux attach` line offload printed. + +## BACK + +Run `box back` in the repo. It waits for the box agent to go idle, then up to 10 minutes in +all for its hand-over, then lands the box's work against the snapshot taken at the offload. +Report how the code landed, as its first line says: fast-forward (the branch now points at +the box's commit), merged (the branch moved to the box's commit, or the box's changes applied +uncommitted, with any uncommitted paths listed), unchanged, or a branch with conflicts, where +your work tree is untouched. If it says nothing of this repo is in a box and names a +`back.md`, the work has landed already: read that file as the hand-over. Then check the +hand-over's claims against `git log` and `git diff`, run its verify commands, and continue +from its next steps. + +## Hand-over template + +```markdown +# Hand-over: + +From: Agent: Repo: Branch: @ + +## Goal and definition of done + + +## User preferences and constraints + + +## Current state + + +## Decisions made +- — ; rejected: + +## Files touched +- `` — + +## Next steps +1. +2. <…> + +## Commands +- Build: `` Test: `` Lint: `` Run: `` +- Verify the current state: `` → + +## Environment facts +- Tools and versions: +- Services: +- Hosts: +- Secrets needed: +- Installed during this session: + +## Gotchas learned +- + +## Open questions +- +``` diff --git a/cli/README.md b/cli/README.md index 7604d7e..664c5ae 100644 --- a/cli/README.md +++ b/cli/README.md @@ -56,9 +56,36 @@ Without `BOX_TOKEN`, an interactive run asks for the key with hidden input. The It prints a fix for each problem. `--json` prints a machine-readable report; the exit code is 1 when something needs fixing. OAuth sign-ins are stored by each app, so doctor can't see whether you've signed in. +## Offload + +Move the coding task you are working on into your box, let an agent there keep going while you are away, and bring the work back. This is upstream cocoonbox's Offload (design §27), with the same box side. + +```bash +npx @simular-ai/box login # once: sign this CLI in to box in the browser +npx @simular-ai/box offload install-cli --kind claude # once per box: the vendor's official installer, in the box +npx @simular-ai/box offload sign-in --kind claude # once per box: Claude Code's own login, run in the box +``` + +Then, in Claude Code with the `box` plugin, `/box:offload` and later `/box:back`. Elsewhere, write the hand-over to the path `offload handover-path` prints (or paste the request `offload --handover-prompt --kind codex` prints into your agent), then: + +```bash +npx @simular-ai/box offload --kind claude # preflight, copy the repo, start the box agent from the hand-over +npx @simular-ai/box offload inbox # what the box agent posted; offload send "..." answers it +npx @simular-ai/box back # wait for its hand-over, fetch its commits, merge them here +``` + +How it works: + +- **Sign-in.** `login` signs the CLI in as the relay's first-party `cocoonbox-cli` client: the consent page opens in your browser, the answer comes back to a one-time listener on `127.0.0.1` (PKCE, `state` and issuer checked), and the CLI gets a device token of its own, listed under Keys -> Devices and revoked there or with `logout`. It is kept in `~/.simular-box/credentials.json` (`0600`, directory `0700`; `BOX_HOME` moves it). `BOX_TOKEN=cb_...` (a personal key) wins over it, for CI or a server. `login --token -` reads a personal key from stdin or a hidden prompt. A token is never accepted as an argument or printed. +- **The box CLI has its own login.** `offload sign-in` runs the vendor's login (`claude auth login`, `codex login --device-auth`, `cursor-agent login`) on a terminal in the box, relayed to yours, with the box's browser kept shut. The first https link it prints on that vendor's own sign-in hosts opens in your browser, once; no other link from the box is opened. Approve, then paste the code into your terminal. Nothing is copied from this computer. +- **Transport.** Everything goes over `Session.Exec` on the relay's gRPC: `npx @simular-ai/box rsh` is an ssh-like remote shell (`rsync -e`, `GIT_SSH_COMMAND`). An offload is three Execs: `begin` (box checks), rsync of the work tree and `.git` without objects, and `send`, whose stream carries the hand-over, `env.json`, the history as a git bundle and any opted-in secrets. +- **Back.** It waits for the box agent to go idle (a hand-over request pasted while it is ending a turn can be lost; `back --interrupt` stops its current step instead), then up to 10 minutes in all for its hand-over, fetches the box's commits over `rsh`, and lands them against the snapshot taken at offload: a fast-forward when your repo has not changed, else a clean merge applied uncommitted (never touching a file you changed meanwhile), else the branch `cocoonbox/back-