Install Entire CLI in Cursor Cloud Agent environments on enable - #2122
Install Entire CLI in Cursor Cloud Agent environments on enable#2122Soph wants to merge 7 commits into
Conversation
When Cursor is enabled, patch an existing .cursor/environment.json install command to run .entire/install-cli.sh so remote agents have `entire` on PATH. Do not create environment.json: a committed copy overrides dashboard-managed environments. Skip the patch when install already puts entire on PATH. Co-authored-by: Stefan Haubold <Soph@users.noreply.github.com>
Co-authored-by: Stefan Haubold <Soph@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR ensures committed git hooks that call entire also work in Cursor Cloud Agent environments (which don’t inherit a developer’s local PATH) by generating a repo-managed bootstrap script and wiring it into Cursor’s existing Cloud Agent environment configuration when present.
Changes:
- Add a shared, idempotent
.entire/install-cli.shbootstrap (written byentire enable) for remote/cloud environments to install the Entire CLI. - Implement Cursor Cloud Agent wiring via a new
CloudCLIInstallercapability that patches an existing.cursor/environment.jsoninstallcommand to run the bootstrap step. - Add unit tests and documentation covering detection, patching behavior, and the “do not create environment.json” constraint.
Reviewed changes
Copilot reviewed 15 out of 15 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| README.md | Documents Cloud Agent/remote environment behavior and expectations. |
| docs/architecture/agent-integration-checklist.md | Adds checklist item for Cloud Agent CLI bootstrap wiring. |
| docs/architecture/agent-guide.md | Documents the new CloudCLIInstaller capability contract. |
| cmd/entire/cli/setup.go | Wires cloud install into enable flows; writes the shared install script. |
| cmd/entire/cli/setup_test.go | Adds enable tests for Cursor patch vs “hint only” behavior. |
| cmd/entire/cli/agent/cursor/environment.go | Implements CloudCLIInstaller for Cursor via cloudenv. |
| cmd/entire/cli/agent/cloudenv/install.sh | Adds the embedded install helper used by remote environments. |
| cmd/entire/cli/agent/cloudenv/install.go | Writes .entire/install-cli.sh and detects whether install already provides entire. |
| cmd/entire/cli/agent/cloudenv/install_test.go | Tests install detection and script writing idempotency. |
| cmd/entire/cli/agent/cloudenv/embed.go | Embeds the install script into the binary. |
| cmd/entire/cli/agent/cloudenv/cursor.go | Patches/removes the Cursor .cursor/environment.json install step when applicable. |
| cmd/entire/cli/agent/cloudenv/cursor_test.go | Tests Cursor patching, preservation behavior, and repo dogfood expectations. |
| cmd/entire/cli/agent/cloud_cli.go | Introduces the CloudCLIInstaller interface and result type. |
| cmd/entire/cli/agent/capabilities.go | Exposes AsCloudCLIInstaller capability helper. |
| cmd/entire/cli/agent/architecture_test.go | Excludes cloudenv from agent package discovery checks. |
Suppressed comments (1)
cmd/entire/cli/agent/cloudenv/cursor.go:107
- RemoveCursorEnvironment also rewrites .cursor/environment.json with a hard-coded 0644 mode, which can loosen permissions compared to the existing file. Preserving the original mode avoids unexpected permission changes during uninstall/agent removal.
output, err := jsonutil.MarshalIndentWithNewline(raw, "", " ")
if err != nil {
return fmt.Errorf("marshal %s: %w", EnvironmentJSONRel, err)
}
return os.WriteFile(envPath, output, 0o644) //nolint:gosec // project JSON
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| for _, ag := range addedAgents { | ||
| if _, err := setupAgentHooks(ctx, ag, opts.ForceHooks); err != nil { | ||
| errs = append(errs, fmt.Errorf("failed to setup %s hooks: %w", ag.Type(), err)) | ||
| } else if err := setupCloudCLIInstall(ctx, w, ag); err != nil { | ||
| errs = append(errs, fmt.Errorf("failed to setup %s cloud environment: %w", ag.Type(), err)) |
| } else if err := setupCloudCLIInstall(ctx, w, ag); err != nil { | ||
| errs = append(errs, fmt.Errorf("failed to setup %s cloud environment: %w", ag.Type(), err)) |
| output, err := jsonutil.MarshalIndentWithNewline(raw, "", " ") | ||
| if err != nil { | ||
| return CursorEnvironmentResult{}, fmt.Errorf("marshal %s: %w", EnvironmentJSONRel, err) | ||
| } | ||
| if err := os.WriteFile(envPath, output, 0o644); err != nil { //nolint:gosec // project JSON, same as hooks.json | ||
| return CursorEnvironmentResult{}, fmt.Errorf("write %s: %w", EnvironmentJSONRel, err) | ||
| } |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit b14effd. Configure here.
| if _, err := setupAgentHooks(ctx, ag, opts.ForceHooks); err != nil { | ||
| errs = append(errs, fmt.Errorf("failed to setup %s hooks: %w", ag.Type(), err)) | ||
| } else if err := setupCloudCLIInstall(ctx, w, ag); err != nil { | ||
| errs = append(errs, fmt.Errorf("failed to setup %s cloud environment: %w", ag.Type(), err)) |
There was a problem hiding this comment.
Agent removal skips cloud cleanup
Medium Severity
applyAgentChanges uninstalls Cursor hooks when the agent is deselected but never calls RemoveCloudCLIInstall. The other teardown paths (uninstallDeselectedAgentHooks and removeAgentHooks) do strip the Entire step from .cursor/environment.json. Deselecting Cursor via the manage-agents flow therefore leaves bash .entire/install-cli.sh in a committed environment file after Entire is no longer enabled.
Additional Locations (2)
Reviewed by Cursor Bugbot for commit b14effd. Configure here.
Wrap Cursor environment errors, keep applyAgentChanges complexity stable by sharing setupAgentHooksAndCloud, and restore the helper script's executable bit. Co-authored-by: Stefan Haubold <Soph@users.noreply.github.com>
Windows does not set execute bits, so the mode check only applies on Unix. Co-authored-by: Stefan Haubold <Soph@users.noreply.github.com>
Cloud Agents never create ~/.cursor/projects/.../agent-transcripts (history is remote-only), so TurnEnd hard-failed after PrepareTranscript polled for 5s. Skip the flush wait and continue turn-end with git-based capture when the transcript parent directory is missing; prefer CURSOR_TRANSCRIPT_PATH when Cursor exposes a real local path. Entire-Checkpoint: 01M0WKDDKA2XBMNHSCQYXQQWS7
Entire-Checkpoint: 01M0WKR7T7EPF2PQ6PKWACNF5Z
applyAgentChanges uninstalled hooks but left bash .entire/install-cli.sh in an existing .cursor/environment.json. Preserve that file's mode on rewrite and stop double-wrapping enable errors. Entire-Checkpoint: 01M0WMRRPVDZYMV2CDWBKNMPYX


https://entire.io/gh/entireio/cli/trails/1140
Summary
Cursor Cloud Agents do not inherit a developer’s local
entireinstall, so committed hooks that invokeentiresilently no-op there.entire enablenow writes a shared, idempotent helper (.entire/install-cli.sh) that any remote environment can run. When Cursor is selected and the repo already has.cursor/environment.json, enable appendsbash .entire/install-cli.shto that file’sinstallcommand.It does not create
.cursor/environment.json. A committed copy overrides dashboard-managed personal/team Cloud Agent environments. If the file is missing, enable prints the one-liner to add in the dashboard. Ifinstallalready puts Entire on PATH (this repo’s.cursor/install.shis the example), it is left unchanged.Agents that boot in a remote environment can implement
CloudCLIInstallerthe same way Cursor does.Cursor Cloud transcripts
Managed Cursor Cloud VMs can send
transcript_path: nulland never create the local~/.cursor/projects/.../agent-transcriptsdirectory; conversation history remains remote-only. Previously Entire fabricated that local path, waited five seconds on everystop, then aborted turn-end before git-based file detection, stop-hook token capture, phase transition, or checkpoint creation.Cursor transcript resolution now uses this precedence:
transcript_pathCURSOR_TRANSCRIPT_PATHhook environment variableIf the fallback’s parent directory is absent, Cursor skips the impossible flush wait and turn-end continues without transcript bytes. The fail-open is restricted to Cursor; other agents and missing files under an existing parent retain the hard error.
#2112 separately fixes Cursor presence detection and is intentionally not duplicated here.
Review follow-up
Deselecting Cursor through the manage-agents path (
applyAgentChanges) now callsRemoveCloudCLIInstall, matchingentire disableand a laterentire enablethat drops the agent. Rewrites of an existing.cursor/environment.jsonkeep the file’s previous permission bits. Setup/reinstall errors are no longer wrapped a second time as “failed to setup hooks”.Classification
entire enablestays user-owned. This only extends what enable already writes for a selected agent; it does not promote a command into agent-help listing or read-only.Testing
mise run check(fmt, lint, unit, integration, e2e canary)TestApplyAgentChanges_RemovesCursorCloudInstallcovers manage-agents teardown0600is preserved when rewritingenvironment.jsonmise run dup:stagedreports only a pre-existing duplicate pair inlifecycle_test.go(SessionStart_EmptySessionID/TurnStart_EmptySessionID); no new duplication was reported.