{"id":51577191,"url":"https://github.com/royashbrook/waterbear","last_synced_at":"2026-07-11T01:32:06.937Z","repository":{"id":369539923,"uuid":"1290276073","full_name":"royashbrook/waterbear","owner":"royashbrook","description":"keep a claude --remote-control session alive forever (tmux + launchd), optionally resuming the same conversation on respawn , named for the tardigrade","archived":false,"fork":false,"pushed_at":"2026-07-05T21:04:34.000Z","size":23,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-07-05T21:12:12.697Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/royashbrook.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-07-05T19:23:05.000Z","updated_at":"2026-07-05T21:04:37.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/royashbrook/waterbear","commit_stats":null,"previous_names":["royashbrook/waterbear"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/royashbrook/waterbear","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/royashbrook%2Fwaterbear","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/royashbrook%2Fwaterbear/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/royashbrook%2Fwaterbear/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/royashbrook%2Fwaterbear/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/royashbrook","download_url":"https://codeload.github.com/royashbrook/waterbear/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/royashbrook%2Fwaterbear/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35348376,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-10T02:00:06.465Z","response_time":60,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-07-11T01:32:06.695Z","updated_at":"2026-07-11T01:32:06.872Z","avatar_url":"https://github.com/royashbrook.png","language":"Shell","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cpicture\u003e\n    \u003csource media=\"(prefers-color-scheme: dark)\" srcset=\"assets/logo-dark.png\"\u003e\n    \u003cimg src=\"assets/logo.png\" width=\"200\" alt=\"waterbear\"\u003e\n  \u003c/picture\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\u003cem\u003ethe session that refuses to die.\u003c/em\u003e\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/license-MIT-057b8d?style=flat-square\" alt=\"MIT\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/platform-macOS%20(launchd)-057b8d?style=flat-square\" alt=\"macOS\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/claude%20code-skill-057b8d?style=flat-square\" alt=\"Claude Code skill\"\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\u003cstrong\u003ekeep a \u003ccode\u003eclaude --remote-control\u003c/code\u003e session alive through crashes, patches, quits, and reboots, and resume the same conversation when it comes back.\u003c/strong\u003e\u003c/p\u003e\n\n---\n\nNamed for the [tardigrade](https://en.wikipedia.org/wiki/Tardigrade) (the \"water bear\"): the animal\nthat suspends its life under stress and revives when conditions return. Same trick, for a Claude Code\nsession. A crash, a patch, a quit, or a reboot kills the process; waterbear brings it back on its own,\nand (optionally) resumes the exact prior conversation from disk so it returns as itself, mid-thought.\n\nwaterbear is local to one machine: it wires a launchd LaunchAgent + tmux + a `claude` CLI process on\none mac, tied to that machine and user. It does not run in the cloud or follow you to another box.\n\n## agents: start here\n\nThe whole thing is one sentence you paste to your agent:\n\n```\ninstall the skill at https://github.com/royashbrook/waterbear, then use it to waterbear yourself\n```\n\nThe agent clones the repo, reads [`SKILL.md`](SKILL.md), and wires the durable body around this exact\nconversation. The one thing you provide is the wake prompt that re-establishes it on respawn (a role\nor persona cue). It handles tmux, launchd, and the resume wiring itself.\n\nTo keep waterbear as a reusable skill in future sessions, clone it into the skills dir instead, then\nrestart your UI so it loads (after that, `/waterbear` or \"waterbear yourself\" works with no install\nstep):\n\n```bash\ngit clone https://github.com/royashbrook/waterbear ~/.claude/skills/waterbear\n```\n\n## install (humans)\n\nIf you'd rather run it directly, the installer is one script. Straight from the raw file:\n\n```bash\nCLAUDE_RC_NAME=myagent curl -fsSL \\\n  https://raw.githubusercontent.com/royashbrook/waterbear/main/scripts/waterbear-install | bash\n```\n\nOr clone and run it with the options you want:\n\n```bash\n# floor: respawn on death, come up with a fresh-start prompt\nCLAUDE_RC_NAME=myagent CLAUDE_RC_DIR=~/proj CLAUDE_RC_WAKE=\"you are my X agent, resume work\" \\\n  bash scripts/waterbear-install\n\n# always-on: respawn AND resume the same conversation from disk\nCLAUDE_RC_NAME=myagent CLAUDE_RC_DIR=~/proj CLAUDE_RC_RESUME=1 \\\n  CLAUDE_RC_RESUME_WAKE=\"re-init\" CLAUDE_RC_WAKE=\"you are my X agent\" \\\n  bash scripts/waterbear-install\n```\n\nResume-mode also needs a one-time SessionStart hook so the live session id is recorded for the guard\nto resume, see [`SKILL.md`](SKILL.md) for the exact `settings.json` block.\n\nAttach any time with `tmux attach -t myagent` (it also shows on your phone, the desktop app, and\nclaude.ai). Stop with `launchctl bootout gui/$(id -u)/com.\u003cuser\u003e.claude-rc.myagent`.\n\n**Prereqs:** the `claude` CLI (run `claude` once to log in: waterbear reuses your login, it does not\nhandle auth) and `tmux` (`brew install tmux`).\n\n**Auth expiry:** the CLI runs on your subscription login (OAuth), which expires now and then. When it\ndoes, the session stays alive and shows an auth error on each request, so it announces itself (you\nsee it via remote-control), it does not silently die. Run `claude` + `/login` at the machine to clear\nit. waterbear cannot drive a browser re-login, so this one stays a manual step. Expected with a\nsubscription backbone. (The guard also has a crashloop net that notifies and backs off if a session\ngenuinely dies in a loop, e.g. a bad resume id; an expired login does not trigger that.)\n\n## how it works\n\nFour pieces, each doing one job:\n\n1. **`claude --remote-control`** keeps one session reachable from phone / desktop / web at once. It\n   needs a real tty.\n2. **tmux** supplies that tty and a detached, attachable home for the session.\n3. **A launchd LaunchAgent** (`RunAtLoad` + `KeepAlive`) starts a small guard at login and respawns\n   it whenever the session dies. The guard recreates the tmux session, then blocks until it exits.\n4. **The wake.** launchd has no keyboard, and a positional prompt does not auto-run in interactive\n   mode (under `--remote-control` it is read as a session title), so the guard types the first prompt\n   with `tmux send-keys` once the UI settles. Two modes:\n   - **fresh**, types `CLAUDE_RC_WAKE`, whatever re-establishes your agent. Without it a respawn\n     comes up as a blank assistant.\n   - **resume** (`CLAUDE_RC_RESUME=1`), relaunches `claude --remote-control \u003cname\u003e --resume \u003cid\u003e`\n     from the on-disk transcript (which survives a crash), so it returns as itself with full context,\n     then types `CLAUDE_RC_RESUME_WAKE` to re-arm anything session-scoped (a monitor or watcher dies\n     with the process; context comes back, rails do not).\n\nResume-mode records the live session id on every start via a SessionStart hook\n(`scripts/rc-session-capture-hook`, gated on `CLAUDE_RC_NAME`) to `~/.claude/rc-session-\u003cname\u003e`. The\nguard consumes that file before resuming, so a stale id can't crashloop: it falls back to a fresh\nstart on the next respawn.\n\n## environment\n\n| env | meaning |\n|---|---|\n| `CLAUDE_RC_NAME` | internal id: tmux name + launchd label + id file (default `claude`; keep short, no spaces) |\n| `CLAUDE_RC_TITLE` | display title in the session list (default = NAME; may have spaces, e.g. `\"Roy - Theaetetus\"`) |\n| `CLAUDE_RC_DIR` | working directory (default `$HOME`) |\n| `CLAUDE_RC_WAKE` | prompt typed on a FRESH respawn (identity / bootstrap) |\n| `CLAUDE_RC_RESUME` | `1` = resume the prior conversation by id instead of a fresh one |\n| `CLAUDE_RC_RESUME_WAKE` | prompt typed AFTER a resume (re-arm session-scoped rails) |\n| `CLAUDE_RC_SESSION_FILE` | override the id file path (default `~/.claude/rc-session-\u003cname\u003e`) |\n\n## the resume caveat\n\nResume replays the full transcript into context every time, so context grows with each respawn. Use\nresume for continuity across crashes, and periodically start a clean session to shed weight. Resume is\nfor recovery, not infinite accumulation.\n\nRunning several bodies for one name over time? The id file names ONE session, and a wrong id resumes\nthe wrong body silently. Keep using the body you want and let its capture hook keep the id current;\ndon't hand-set the id from a session you're not sure is the right one. See\n[`SKILL.md`](SKILL.md) → Common mistakes.\n\n## other operating systems\n\nThis is a macOS/launchd reference implementation. The pattern ports directly: swap the LaunchAgent for\na systemd user service (`Restart=always`) or any process supervisor, and keep the guard logic: tmux +\n`--remote-control` + `send-keys` wake + resume-by-captured-id. Read\n[`scripts/waterbear-install`](scripts/waterbear-install) and adapt.\n\n## license\n\nMIT, see [LICENSE](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Froyashbrook%2Fwaterbear","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Froyashbrook%2Fwaterbear","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Froyashbrook%2Fwaterbear/lists"}