Install scripts beat always-on machines Anthropic moved Claude Code cloud sessions out of research preview this week, pitching them as Claude Code that keeps working when a laptop is closed, with a one-time credit of $100 on Pro and $250 on Max for existing subscribers. The author, who spent one to two days debugging a Claude Code setup script before a cloud session came up configured, notes that cloud agents start from a fresh clone of the repo so ~/.claude and ~/.cursor are never read, and that Claude Code on the web defines environments owned by the account while Cursor Cloud Agents commit .cursor/environment.json plus an image into the repo. The author counted 18 repos with a .cursor/environment.json and 12 using the harness image, with 10 repos on image v1.1.0 and two still not, after bumping from v1.0.0 on the 14th of September. Install scripts beat always-on machines Table of contents It took me a day or two of fighting a Claude Code setup script before a cloud session came up knowing who I was. Run it, read the log, edit, run it again. The snag I actually remember was a silly one. The machine running the script wasn’t allowed to reach my own domain, or GitHub releases. I added both to the allowlist, started a fresh session, typed /codePlanner , and it was there. Agents, commands, the whole kit I spent the last post https://prashamhtrivedi.in/two-git-repos-isnt-env-parity/ complaining about. I haven’t touched the script since. Then this week Anthropic took cloud sessions out of research preview https://x.com/ClaudeDevs/status/2102871550974427462 , pitched as Claude Code that keeps working when your laptop is closed, with a one-time credit for existing subscribers, $100 on Pro and $250 on Max. So a lot of people are about to open their first cloud session and find their own setup missing from it, the same way I did. The last post promised the working setup and ended on a footnote about what a setup script should be allowed to reach for. I use Claude Code on the web and Cursor Cloud Agents, and they solve this almost the opposite way round, so both are here. Where your home directory lives now A cloud agent starts from a fresh clone of your repo. Your laptop isn’t involved, so ~/.claude and ~/.cursor never get read. Git keeps two laptops in sync fine, but a machine that exists for one task and is thrown away after it has to be set up again every single time. So I stopped asking how to copy my home directory and started digging into what each platform does at boot besides cloning the repo, when my harness gets installed, and what happens between my message and the agent’s first reply. | | Claude Code on the web | Cursor Cloud Agents | |---|---|---| | Where “home” is defined | An environment, owned by your account | .cursor/environment.json plus an image, committed in the repo | | How it binds to repos | Any environment with any repo, picked before the first message | One file per repo, living inside it | | Runs before the agent, cached | Setup script | install , and the Dockerfile build | | Runs on every agent start | A SessionStart hook, committed per repo | start | | Secrets | Environment variables, or API credentials the proxy attaches | Secrets tab, exposed as environment variables | | Partial first-party fix | Skills you enable on claude.ai | Sync Skills, which covers ~/.cursor/skills only | Who owns the environment The beauty of Claude’s setup is that environments and repos aren’t tied to each other. An environment belongs to my account, and every time I start a session I pick a repo and an environment separately. One environment covers everything I work on. If some repo ever needs a special machine, it can get one without the others noticing. Cursor puts the environment inside the repo, as .cursor/environment.json . For most of what’s in that file that’s the right call. Mine have ports, database migrations, start commands, and the names of 31 secrets across 6 repos, and all of that is about the project. My harness isn’t about the project. Cursor has nowhere else to read it from, though, so it gets copied into every repo along with everything else. I counted while writing this. 18 of my repos have a .cursor/environment.json and 12 use the harness image. On the 14th of September I bumped the image from v1.0.0 to v1.1.0 . As of today ten repos are on v1.1.0 and two still aren’t, and every one of the twelve still has a comment saying v1.0.0 . Four other repos have a Dockerfile pointing at the harness that their environment.json never references, and an install that never runs it. In the last post I wrote that copying shared config into every repo ends with a skill “correct in four repos and wrong in sixteen.” My own Cursor setup got there in under two weeks. Model routing is the cleanest example of config that’s mine, not the project’s. On Claude, Opus runs the main session and my agents pin sonnet or haiku . On Cursor, Grok supervises and the mechanical skills run on Composer https://github.com/PrashamTrivedi/CursorPlugin/commit/16148f8 , and the repos themselves stay on default models. Which model does which job is how I work. It has nothing to do with which repo I happen to be in. The Claude Code setup script and the Cursor image For Claude, the laptop half is Agent Config Adapter https://github.com/PrashamTrivedi/agent-config-adapter , a Cloudflare Worker I built to store agent configs, and its CLI, aca . aca sync hashes my skills, agents and commands and uploads whatever changed. When nothing changed it doesn’t call the server at all. On the server I group things into a bundle, and bundles have IDs. The other half is the environment’s setup script, trimmed a bit: bash /bin/bash set -eo pipefail log { echo " aca-bootstrap $ "; } DEST="${ACA INSTALL DIR:-/usr/local/bin}/aca" REPO="PrashamTrivedi/agent-config-adapter" log "Downloading ACA latest ..." curl -fsSL -L "https://github.com/${REPO}/releases/latest/download/aca-linux-x64" -o "$DEST" chmod +x "$DEST" aca --version log "Downloading configs..." aca download --global \ --server "https://agent-config.prashamhtrivedi.app" \ --id KULvQCJENwLi97 SNyAtl --verbose ls -la ~/.claude/ ~/.claude/commands/ ~/.claude/agents/ 2 /dev/null || true log "Done." Install the binary, pull the bundle into ~/.claude , list what landed. The ls at the end puts what actually landed into the setup log. My API key lives in the environment’s variables as ACA API KEY and the CLI picks it up by itself. Bundle IDs are public by design, so that’s the real one. Nothing here is committed to any repo. The Cursor side took more work. The harness is CursorPlugin https://github.com/PrashamTrivedi/CursorPlugin , which builds an image called cursor-dev-setup and pushes it to GitHub’s container registry when I tag a release. Base is ubuntu:24.04 pinned by digest, plus Node 22, pnpm, Bun 1.2.21, and my skills, commands, hooks, rules and MCP config copied into /opt/prasham-cursor/ . Each repo that wants it gets a one-line Dockerfile: FROM ghcr.io/prashamtrivedi/cursor-dev-setup:v1.1.0 and an environment.json that builds it and runs the harness before the project’s own install: { "name": "agent-config-adapter", "build": { "dockerfile": "Dockerfile", "context": ".." }, "install": "/opt/prasham-cursor/bin/materialize-cursor-harness.sh && npm ci || npm install ", "start": "npm run dev" } materialize-cursor-harness.sh links ~/.cursor/skills and ~/.cursor/commands to the copies in the image. Cursor can run install again on a disk it already prepared, so the script checks what’s there before touching anything. What the setup script is allowed to reach for A Claude cloud environment starts on the Trusted network level. Package registries, GitHub, the common cloud SDKs, nothing else. My domain obviously wasn’t on it. Switching to Custom lets you add hosts, but Custom allows only what you list, so I listed my domain and GitHub’s release downloads, which redirect to release-assets.githubusercontent.com . Ticking “Also include default list of common package managers” brings GitHub and the registries back without listing them one by one. That means my script reaches exactly two places. GitHub, for the binary, and my domain, for the config. The binary comes from releases/latest . That’s an unpinned download running on a machine that’s about to get write access to my repo. It’s my repo on the other end, so I’ve let it be, but if you copy this, pin the release. Cursor’s image is pinned to a digest and each repo pins a tag, and the Claude side should be at least that strict. The API key is the one real secret, and environment variables aren’t a vault. Anyone who can use the environment can read them. On Pro and Max nobody else can use my environment, and those plans also have API credentials https://code.claude.com/docs/en/cloud-environments add-api-credentials , where a proxy attaches the key to requests for hosts you choose and the session never sees it. On Team and Enterprise an Owner can share an environment with the whole organisation, every member’s session reads its variables, and API credentials aren’t available there yet. A per-user key has no business in a shared environment. On Cursor, the extra reach is the container registry. GitHub’s registry wants the owner name in lowercase. My image reference had my username the way I normally type it, and pulls failed until this fix https://github.com/PrashamTrivedi/CursorPlugin/commit/edd74d4 , three days after the first image went out. A private image would also need a build secret with read:packages . Mine is public. From my message to the first reply Claude Code on the web: 1. I pick a repo and an environment, and send a message. 2. If the environment has no cached snapshot, or the cache is stale, the setup script runs, before Claude Code launches. Anthropic then snapshots the disk. If a snapshot exists, the session starts from it and the script doesn’t run. 3. Claude Code launches and runs any SessionStart hooks committed in the repo, on every session, resumed ones included. 4. Claude reads the message and replies. Cursor Cloud Agents: 1. Cursor creates a Build. It builds the Dockerfile and runs install . This happens in the background, not while an agent waits. 2. I start an agent and it boots from that Build. 3. start runs, then any terminals I configured. 4. The agent picks up the message. Step 2 on Claude is the one to read twice. The Claude Code setup script runs again only when you edit the script, change the allowed hosts, or the cache expires after roughly seven days https://code.claude.com/docs/en/cloud-environments environment-caching . Resuming a session never re-runs it. With aca sync that means a skill I fix on Tuesday isn’t in Wednesday’s cloud session. Wednesday gets whatever the snapshot caught, maybe six days ago. And starting a new session per task, which I do, doesn’t help. None of my sessions last past two days, but the snapshot belongs to the environment, not the session. It hasn’t bitten me, because my skills barely change. Slower than a week, anyway, and that’s the whole test. If you’re editing a skill daily, either touch the script, even just a comment, to force a rebuild, or fetch that one thing from a SessionStart hook, which runs every session. User-level hooks don’t reach the cloud, so that hook has to be committed into every repo’s .claude/settings.json . Which is the repo sprawl the environment was saving you from. Cursor caches step 1 and runs step 3 every boot, same shape. The difference in my setup is that the snapshot has a version number. It’s not a timer I can’t see, it’s a tag I have to bump in twelve repos. Ten done. What each platform still decides for you Cursor Cloud Agents read skills from $HOME/.cursor/skills , which the image fills. Slash commands they read only from the repo’s .cursor/commands . Not from $HOME . So the first time round every skill worked and not one command did. The fix https://github.com/PrashamTrivedi/CursorPlugin/commit/f4b3b77 has the materialize script symlink the repo’s .cursor/commands to the image’s copy and write that path into .git/info/exclude , so the symlink never shows up in a commit. Claude’s gaps are documented, and worth reading before you build anything: - Plugins a repo enables in .claude/settings.json don’t get installed in cloud sessions. Enable them on your claude.ai account and they come in as synced plugins. - A session across several repos loads no repo’s hooks. The environment’s setup script still runs. - MCP servers added at user or local scope stay on the laptop. Browser-based logins like AWS SSO don’t work. - aca carries skills, agents, commands and workflows. CLAUDE.md and settings.json stay in the repo, which is mostly where they should be. Why not just leave a machine on A correction to the last post first. I wrote that Claude Code on the web “will not read that box’s ~/.claude at any uptime.” Still true for Anthropic-hosted environments. There are now two ways around it. Remote Control https://code.claude.com/docs/en/remote-control , on Pro and Max, lets you drive Claude Code on your own machine from another device. Self-hosted environments https://code.claude.com/docs/en/self-hosted-environments , a public beta on Team and Enterprise, off by default, run cloud sessions on runners you operate, and those runners can seed hooks from the host’s ~/.claude/ . I’m not going to run one. A self-hosted runner means building and maintaining the runner image, patching the fleet, and sizing it for concurrency, since a runner locks to one person at a time. I use cloud sessions so four jobs can run while I’m not watching any of them. A fleet needs watching. A team with internal services the cloud can’t reach, or rules about where code may run, should look at self-hosting seriously. It’s built for them. I’m one person with a laptop and a phone. Install and keep My laptop was installed once, and I keep it. The cloud machine is installed on every run, and whatever’s on it is up to a week old on Claude, or as old as the image tag on Cursor. The second git repo from the last post still syncs my two laptops. It just never had anything to do with the cloud. May the force be with you…