{"record":{"id":"d14ea1d3ad83a4e6","repo":"affaan-m/ECC","slug":"unable-to-resolve-claude-plugin-root-in-a-managed-hook","errorCode":null,"errorMessage":"Unable to resolve CLAUDE_PLUGIN_ROOT in a managed hook command; the hooks.json command prologue no longer matches the expected form","messagePattern":"Unable to resolve CLAUDE_PLUGIN_ROOT in a managed hook command; the hooks\\.json command prologue no longer matches the expected form","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"scripts/lib/install/claude-settings.js","lineNumber":270,"sourceCode":"      ])\n    );\n  }\n  return value;\n}\n\nfunction resolveManagedHookCommands(managedHooks, targetRoot) {\n  const encodedRoot = Buffer.from(targetRoot, 'utf8').toString('base64');\n  const rootExpression = `Buffer.from('${encodedRoot}','base64').toString('utf8')`;\n  const resolveCommand = command => {\n    // Leave invalid entries intact so managed-hook validation reports them.\n    if (typeof command !== 'string') {\n      return command;\n    }\n    const resolved = command\n      .split(PLUGIN_ROOT_ENV_PROLOGUE)\n      .join(`var e=${rootExpression};`);\n    if (PLUGIN_ROOT_ENV_READ.test(resolved)) {\n      throw new Error(\n        'Unable to resolve CLAUDE_PLUGIN_ROOT in a managed hook command; '\n        + 'the hooks.json command prologue no longer matches the expected form'\n      );\n    }\n    return resolved;\n  };\n  return Object.fromEntries(\n    Object.entries(managedHooks).map(([event, entries]) => [\n      event,\n      entries.map(entry => ({\n        ...entry,\n        hooks: entry.hooks.map(hook => ({\n          ...hook,\n          ...(typeof hook.command === 'string' || Array.isArray(hook.command)\n            ? {\n              command: Array.isArray(hook.command)\n                ? hook.command.map(resolveCommand)\n                : resolveCommand(hook.command),","sourceCodeStart":252,"sourceCodeEnd":288,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/scripts/lib/install/claude-settings.js#L252-L288","documentation":"Managed hook commands reference CLAUDE_PLUGIN_ROOT through an expected env-var prologue. After rewriting the prologue to an inline assignment, the resolver re-checks the command; if a CLAUDE_PLUGIN_ROOT reference still remains, the prologue did not match the expected form and the placeholder cannot be safely resolved, so it throws rather than emitting a command that would fail or leak an unresolved variable at hook runtime.","triggerScenarios":"Calling resolveCommand (via resolveManagedHookCommands/materializeManagedHooks) with a command string containing CLAUDE_PLUGIN_ROOT but not in the exact expected prologue form — e.g. \"echo $CLAUDE_PLUGIN_ROOT/x\" with no prologue, quoted variants, or a modified prologue after hand-editing hooks.json.","commonSituations":"A hook command was hand-edited or reformatted (different quoting, missing the standard prefix), a plugin shipped hooks.json deviating from the template, or an older format after a library upgrade changed the expected prologue.","solutions":["Restore the command to the standard prologue form the library expects (the exact PLUGIN_ROOT_ENV_PROLOGUE prefix followed by the command using the placeholder)","Regenerate hooks.json from the plugin template instead of hand-editing the command","Inline the absolute plugin path manually if you do not want placeholder resolution","Check the plugin version for hooks.json format drift and update"],"exampleFix":"// before\n\"command\": \"node \\\"$CLAUDE_PLUGIN_ROOT/scripts/hook.js\\\"\"\n// after\n\"command\": \"CLAUDE_PLUGIN_ROOT=\\\"${CLAUDE_PLUGIN_ROOT:-}\" node \\\"${CLAUDE_PLUGIN_ROOT}/scripts/hook.js\\\"\" — or restore the canonical placeholder command from hooks.json template","handlingStrategy":"try-catch","validationCode":"const usesPlaceholder = (cmd) => cmd.includes('CLAUDE_PLUGIN_ROOT');\n// before materializing: every command containing CLAUDE_PLUGIN_ROOT must match the canonical prologue template — verify against the pristine hooks.json from the plugin.","typeGuard":"null","tryCatchPattern":"try { materializeManagedHooks(cfg, root); } catch (e) { if (e.message.includes('Unable to resolve CLAUDE_PLUGIN_ROOT')) { console.error('hooks.json command deviates from the canonical prologue; restore the template command'); } throw e; }","preventionTips":["Never hand-edit the CLAUDE_PLUGIN_ROOT prologue in hook commands","Regenerate hooks.json from the plugin template after edits","Keep quoting/formatting of the prologue byte-identical to the template","Diff local hooks.json against upstream on plugin upgrades"],"tags":["hooks","plugin-root","command-resolution"],"backgroundTag":"invalid-config-value","analyzedSha":"8321021c54d670126ce3b2969d5deb880b4b0c2a","analyzedAt":"2026-09-16T10:08:13.343Z","contentChangedAt":"2026-09-16T10:08:13.343Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}