fix(agent-setup): stop writing hooks for absent agents; add `agents hooks remove` (SUPER-3170)

Closed
HA
1d ago
+946-46

Links

Summary

  • B: Setup no longer creates or edits an agent's hook config when the agent is absent. Installed means one of these: the hook file exists; a real binary is on PATH (Paradev's wrapper dirs, and links into them, do not count); or a path only the agent writes exists (~/.claude/projects, ~/.codex/sessions, ~/.codex/auth.json). The config dir does not count, because Paradev's skills and MCP sync create it. This covers the 8 ensureManagedJsonHooks agents: Claude, Codex, Droid, Cursor, Gemini, Mastra, Devin, Antigravity.
  • C: superset agents hooks remove --all | --agent <id> runs each agent's teardown without the app. Docs gain an Uninstall section that lists every file Paradev touches, plus a troubleshooting entry for this error.
  • D: Managed commands now start with : paradev-notify; / : paradev-artifact-guard;. The first 80 characters Devin prints for a failed hook now name Paradev.

Why / Context

Paradev writes notify hooks into Devin's global ~/.config/devin/config.json on every boot, including on machines without Devin. Devin runs them in every session, including ones started outside Paradev (e.g. Conductor). When the session cwd was deleted, the spawn fails before any shell starts (Failed to spawn command: No such file or directory (os error 2)), so no guard in the command can prevent it. I reproduced this with the real Devin CLI.

Why not A (scope Devin's hooks with --config)

I tested Devin CLI 3000.11.234 in a throwaway HOME (details on the page):

  • --config replaces the user config. User hooks were not loaded.
  • Devin writes into the --config file: it added version and changed the file mode to 0600. The binary also saves permissions, org_id and subagent settings to "the user config". A generated copy would silently drop settings saved during Paradev sessions.
  • Devin also runs Claude's global hooks (~/.claude/settings.json), with or without --config, and Paradev writes the same notify hook there. Scoping Devin's config alone would not stop the error for anyone who also has Claude.

So this PR ships B + C + D. Scoped registration for all agents is the real follow-up.

How It Works

  • real-binary.ts: isRealBinaryOnPath(names) walks PATH, skipping the dirs the wrapper's find_real_binary skips.
  • ManagedJsonHooksSpec.binaryNames (required) and agentOwnedPaths (optional). ensureManagedJsonHooks returns early, with a log line, when the agent is absent. Teardown is unchanged.
  • After the login-shell PATH is known, setup runs once more, only if that PATH adds something. CLI host: applyLoginShellEnvToProcess resolves whether PATH grew, and then reprovisionAgentHooks() re-runs the hook setup only (no plugin downloads). Desktop: getProcessEnvWithShellPath(); when it differs, it calls setupAgentIntegrations({ agentSearchPath }) with fresh settings. withAgentSearchPath scopes the installed check to that PATH without mutating process.env.
  • teardownAgents(ids) returns the labels of failed actions. The CLI routes agent-setup's console.log output to stderr so --json stays parsable.
  • The label is a shell no-op. Matchers look for $SUPERSET_HOME_DIR/hooks/notify.sh / SUPERSET_HOOK_HARNESS=<id> anywhere in the command, so old entries are recognized and replaced on the next merge.

Manual QA Checklist

  • Boot setup in an empty HOME, PATH=/usr/bin:/bin. main creates 8 agent configs (.claude/settings.json, .config/devin/config.json, …); this branch creates none of them and logs Skipped … is not installed.
  • superset agents hooks remove --all --json against a copy of a real Devin config: stdout is valid JSON; managed entries are removed; a user hook and theme_mode are kept; no files are created for other agents.
  • superset agents hooks remove with no flags errors and lists the valid agent ids.
  • Labelled command in real Devin (ACP SessionStart): it runs notify.sh when SUPERSET_HOME_DIR is set and is a silent no-op when it is unset.
  • Not run: macOS, or Devin's interactive TUI (needs a login).

Testing

  • bun test in packages/agent-setup: 480 pass, 2 fail. The 2 are shell-wrappers › falls back to system binaries…, which fail identically on clean main in this sandbox.
  • bun test in packages/cli: 485 pass, 0 fail.
  • tsc --noEmit in agent-setup, cli, desktop, host-service: clean.
  • biome check on the changed dirs: clean. fumadocs-mdx: clean.
  • packages/cli/scripts/headless-e2e.sh on a fresh linux-x64 dist (throwaway HOME, SUPERSET_* unset): ALL HEADLESS E2E CHECKS PASSED, all 15 matrix rows PASS, plus the new "no config for absent devin" check.

Review fixes

cubic and CodeRabbit, round 2 (commit f91bf888): the desktop's second pass saw an unchanged PATH, and the host re-ran all provisioning every boot. Both are fixed as described in How It Works.

superset-home (commit a0e7e51f):

  • Boot 2 re-added Claude's hooks, because managed skills create ~/.claude. Install markers are now paths Paradev never writes, with a two-boot regression test that fails on the old rule.
  • The Build CLI e2e failed on a runner without agents. Stub agents are now on the host PATH, and an absent devin is asserted to get no config.
  • Object.hasOwn: already fixed in 39f69ebd.

cubic and CodeRabbit (commits 39f69ebd, b9751759):

  • Object.hasOwn for agent ids (with a test).
  • --all together with --agent is rejected.
  • Windows .exe/.cmd/.com are detected.
  • A symlink to a wrapper does not count as installed (real paths compared).
  • ~/.gemini, which is shared with Antigravity, no longer counts as Gemini being installed. Superseded by the general rule.
  • The hooks switch now applies when Claude/Codex account profiles are provisioned.
  • Docs corrections.
  • Muse env-var ownership is documented only (tracking it needs new state).

Known Limitations

  • An existing config file still counts as installed, so a stale ~/.config/devin/config.json keeps being maintained. Remove it once with superset agents hooks remove --agent devin or the Settings switch.
  • An agent found only on the login-shell PATH relies on the second setup pass. The host path runs in the e2e; the desktop path is typechecked but was not run in the app. No end-to-end test covers that case.
  • No Settings "remove all" button: the per-agent switch exists, and a new button needs a desktop procedure plus translations in every locale.

Follow-ups

  • Apply the same check to the TOML/plugin writers (Kimi, Vibe, Grok, Amp, Muse, OMP, Pi) and to managed skills.
  • Launch- or project-scoped hook registration for all agents.

Risks / Rollout

  • Risk: an installed agent that is invisible on PATH even after the shell merge, and has no hook file or session data yet, gets no hooks until it is found.
  • Rollback: revert the PR. The next boot rewrites the old unlabelled entries.

Summary by CodeRabbit

  • New Features
    • Added superset agents hooks remove to remove Paradev hooks from all supported agents or selected agents. The command works without signing in and preserves unrelated hooks and settings.
    • Hook setup now detects installed agents, including those available through the login-shell PATH, and skips configuration for agents that are not present. User hooks are retained when managed hooks are removed.
    • Removed hooks may be added again at startup unless that agent’s integration is disabled in settings.
  • Documentation
    • Added guidance on uninstalling hooks, manual removal, controlling whether hooks return at startup, and troubleshooting hook failures in other tools.