Skip to content

SessionStart always-on hook silently no-ops when CLAUDE_PLUGIN_ROOT is not exported (Claude Code desktop app, macOS) #129

Description

@swhite1122

Summary

On the Claude Code desktop app (macOS, Claude Code 2.1.241), the always-on SessionStart hook silently does nothing. No banner, no error, exit 0. ADHD mode never loads and there is no signal that anything went wrong.

The shipped hook command is:

"command": "node -e \"(async()=>{const root=process.env.CLAUDE_PLUGIN_ROOT||process.env.PLUGIN_ROOT;if(root)await import(...)})().catch(()=>{})\""

The desktop app substitutes ${CLAUDE_PLUGIN_ROOT} into hook args, but does not export CLAUDE_PLUGIN_ROOT into the hook process environment. So root is undefined, the if (root) guard skips the import, and the process exits 0 having done nothing.

This appears to be a regression introduced by the fix for #70: moving from sh "${CLAUDE_PLUGIN_ROOT}/hooks/always-on.sh" (template substitution) to reading the env var at runtime. #70 at least failed loudly; this fails silently.

Evidence

Same machine, same install, same ~/.claude/.i-have-adhd-always flag present, plugin enabled=true.

Terminal claude — works:

[DEBUG] Loading hooks from plugin: i-have-adhd
[DEBUG] Hook SessionStart:startup (SessionStart) success:
ADHD MODE ACTIVE (always-on). ...

Desktop app, same session start type (SessionStart:clear) — no banner. Another plugin's SessionStart hook in the same session fired normally, so hooks themselves are working; that plugin uses "args": ["${CLAUDE_PLUGIN_ROOT}/hooks/activate.js"] (substitution) rather than reading the env var.

Reproducing the no-op directly:

$ env -u CLAUDE_PLUGIN_ROOT -u PLUGIN_ROOT node -e "(async()=>{const root=process.env.CLAUDE_PLUGIN_ROOT||process.env.PLUGIN_ROOT;if(root)await import(...)})().catch(()=>{})"
$ echo $?
0

No output, no error.

Why this is worse than a crash

The if (root) guard and the .catch(()=>{}) together make every failure invisible. A user with always-on enabled has no way to tell that the ruleset isn't loading — the agent just quietly behaves normally. I lost a while assuming the plugin wasn't installed.

Suggested fix

The repo already ships hooks/always-on.sh, which resolves its own path from $0 and needs no env var. The Node path just never falls back to it. Two options:

  1. Fall back when root is missing. Resolve relative to the hook script's own location — e.g. derive from import.meta.url / __filename instead of trusting the env var, which removes the dependency entirely.
  2. Chain in hooks.json: keep the Node hook, add the POSIX hook as a second SessionStart entry guarded so only one produces output.

Option 1 seems cleaner and would also have prevented #70.

At minimum, consider making the failure visible — a one-line stderr note when root is absent would have made this a two-minute diagnosis instead of a hunt.

Environment

  • macOS 15 (Darwin 25.6.0)
  • Claude Code 2.1.241, desktop app
  • i-have-adhd 0.2.0, installed via marketplace, enabled=true
  • CLAUDE_CONFIG_DIR unset, flag at ~/.claude/.i-have-adhd-always present

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions