{"record":{"id":"8f39487760585d1b","repo":"JuliusBrussee/caveman","slug":"profile-display-name-integration-is-degraded-run-caveman","errorCode":null,"errorMessage":"${profile.display_name} integration is degraded; run `caveman doctor ${agent} --fix` before changing it","messagePattern":"(.+?) integration is degraded; run `caveman doctor (.+?) --fix` before changing it","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/cli/src/index.ts","lineNumber":8144,"sourceCode":"  if (profiles.length === 0) {\n    console.error(detected ? \"no supported native agent detected on PATH\" : `caveman enable: supported agents are claude, codex, hermes, gemini, opencode, pi, and aider (got ${target ?? \"\"})`);\n    process.exit(1);\n  }\n  const gw = gatewayURL();\n  for (const profile of profiles) {\n    const agent = profile.id as NativeAgent;\n    const outcome = withIntegrationLock(agent, () => {\n      recoverPendingNativeInstallUnlocked(agent);\n      if (!which(binOf(profile))) throw new Error(`${profile.display_name} not found on PATH`);\n      const mcpBinary = agent === \"aider\" ? undefined : nativeMcpBinaryRequired();\n      nativeProxyBinaryRequired(gw);\n      const existing = nativeIntegrationStatus(agent);\n      if (existing.installed) {\n        if (existing.state === \"installed\") return \"already\" as const;\n        // `--fix` on purpose: bare `caveman doctor <agent>` prints JSON that says\n        // `degraded` and nothing that says how to leave that state, so pointing\n        // at it alone dead-ends the user who followed this line here (#1049).\n        throw new Error(`${profile.display_name} integration is degraded; run \\`caveman doctor ${agent} --fix\\` before changing it`);\n      }\n      const mutations = nativeMutationsFor(agent, gw, mcpBinary);\n      const route = mutations.find((item) => typeof item.owned?.route === \"string\")?.owned?.route;\n      process.stderr.write(`caveman enable ${agent}: planned user-scoped writes\\n`);\n      for (const mutation of mutations) process.stderr.write(`  ${mutation.kind}: ${mutation.file}\\n`);\n      if (typeof route === \"string\") process.stderr.write(`  routing: ${route}\\n`);\n      process.stderr.write(agent === \"aider\" ? \"  recovery MCP: unavailable in Aider\\n\" : `  recovery MCP: ${mcpBinary}\\n`);\n      process.stderr.write(agent === \"aider\"\n        ? `  Core: read-only ${aiderCorePath()}; lifecycle/tool interception unavailable; Ledger observational\\n`\n        : agent === \"pi\"\n          ? `  lifecycle/Core/tool rewrite: bundled Pi extension -> ${nativeHookCommand(agent)}\\n`\n          // Codex gets the lifecycle line without the \"tool rewrite\" claim: since\n          // #1037 shrink-hook declines every Codex tool event, so promising one here\n          // would be the same false claim `doctor` used to report.\n          : agent === \"codex\"\n            ? `  lifecycle/Core: ${nativeHookCommand(agent)}; command-output rewrite unavailable in Codex\\n`\n            : `  lifecycle/Core/tool rewrite: ${nativeHookCommand(agent)}${agent === \"hermes\" ? \" via native plugin\" : ` + ${cavemanBinForHook()} shrink-hook`}\\n`);\n      applyNativeMutations(agent, profile, mutations);","sourceCodeStart":8126,"sourceCodeEnd":8162,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/cli/src/index.ts#L8126-L8162","documentation":"This error is thrown when enabling a native integration for an agent (claude/codex/gemini) whose existing integration status reports a degraded (not clean 'installed') state. The CLI refuses to layer a new enable on top of a broken integration and instead directs the user to repair it first with `caveman doctor <agent> --fix`. A bare `caveman doctor <agent>` only prints JSON stating 'degraded' without remediation, so the message deliberately points at the `--fix` flag (issue #1049).","triggerScenarios":"Calling `caveman enable <agent>` when `nativeIntegrationStatus(agent)` returns `existing.installed === true` and `existing.state !== 'installed'` (i.e. state is 'degraded' or similar).","commonSituations":"A previous partial enable/disable left the agent's config in a mixed state; a manual edit removed part of the integration; a crashed run between journal writes; upgrading caveman changed hook/MCP entry shapes so the old install no longer matches the expected canonical form.","solutions":["Run `caveman doctor <agent> --fix` to repair the degraded integration, then retry the enable.","Inspect `caveman doctor <agent>` JSON output to see which component is degraded before fixing.","If repair is impossible, fully disable/revert the integration (restore config from backups or the journal) and re-enable cleanly.","Check the agent's settings file for leftover caveman hooks/MCP entries from an older version and remove them manually, then re-run enable."],"exampleFix":"// before\ncaveman enable claude   # throws: claude integration is degraded\n\n// after\ncaveman doctor claude --fix\ncaveman enable claude","handlingStrategy":"validation","validationCode":"import { execFileSync } from \"node:child_process\";\nconst status = JSON.parse(execFileSync(\"caveman\", [\"doctor\", \"claude\"], { encoding: \"utf8\" }));\nif (status.installed && status.state !== \"installed\") {\n  execFileSync(\"caveman\", [\"doctor\", \"claude\", \"--fix\"], { stdio: \"inherit\" });\n}\n// now safe: caveman enable claude","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always run `caveman doctor <agent>` before enabling to confirm state is clean.","Avoid hand-editing agent settings files while a caveman integration is enabled.","After upgrading caveman, run `doctor --fix` for each agent to reconcile schema changes."],"tags":["cli","integration","degraded-state","config"],"backgroundTag":"invalid-state-transition","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}