How the Always-On Hook Mechanism Works in the i-have-adhd Claude Plugin (Plus Why It Fails)

The always-on hook in the i-have-adhd plugin is a SessionStart hook that injects ADHD formatting rules into every Claude Code response, but it fails silently when users haven't created the required opt-in flag file or when environment variables point to the wrong config directory.

The i-have-adhd plugin by Ayoub Ghribi provides ADHD-friendly AI responses through Claude Code's plugin system. Its always-on hook mechanism ensures these formatting rules apply automatically to every conversation—when properly configured. Understanding how this hook works and why it can silently fail helps users troubleshoot missing functionality.

How the Always-On Hook Executes

Claude Code triggers the hook at session start through the SessionStart event defined in [hooks/hooks.json](https://github.com/ayghri/i-have-adhd/blob/main/hooks/hooks.json):

{
  "type": "command",
  "command": "sh \"${CLAUDE_PLUGIN_ROOT}/hooks/always-on.sh\"",
  "timeout": 5,
  "statusMessage": "Checking i-have-adhd always-on flag..."
}

The hook delegates to [hooks/always-on.sh](https://github.com/ayghri/i-have-adhd/blob/main/hooks/always-on.sh), a POSIX shell script that performs five sequential operations.

Step 1: Resolve the Config Directory

claude_dir="${CLAUDE_CONFIG_DIR:-$HOME/.claude}"

The script first determines where to look for user configuration. It respects the $CLAUDE_CONFIG_DIR environment variable, falling back to ~/.claude when unset. This allows users to relocate their Claude configuration while maintaining compatibility with standard setups.

Step 2: Check the Opt-In Flag

flag_path="$claude_dir/.i-have-adhd-always"
[ -f "$flag_path" ] || exit 0

The always-on flag file (~/.claude/.i-have-adhd-always) acts as a user consent mechanism. The plugin only activates when this file exists. Critically, the script exits with status 0 when absent—this silent success prevents session start failures for users who haven't opted in.

Step 3: Locate the Skill Ruleset

script_dir=$(dirname -- "$0")
skill_path="$script_dir/../skills/i-have-adhd/SKILL.md"
[ -f "$skill_path" ] || exit 0

The script computes the path to [skills/i-have-adhd/SKILL.md](https://github.com/ayghri/i-have-adhd/blob/main/skills/i-have-adhd/SKILL.md), which contains the full ADHD formatting guidelines. Another exit 0 guards against repository layout changes or missing files.

Step 4: Strip YAML Front-Matter

body=$(awk '
  NR == 1 && $0 ~ /^---[[:space:]]*$/ { in_fm = 1; next }
  in_fm && $0 ~ /^---[[:space:]]*$/   { in_fm = 0; next }
  !in_fm                              { print }
' "$skill_path")

An AWK block processes SKILL.md to remove YAML front-matter (content between --- delimiters). Only the actual rule content—not metadata like title or description—is injected into conversations.

Step 5: Output the Active Ruleset

printf 'ADHD MODE ACTIVE (always‑on). The ruleset below applies to every response for this session.\n\n'
printf '%s...\n' "$skill_path"
printf '\n%s\n' "$body"

The script prints a confirmation header, the full skill path, and the extracted rules. This output becomes part of Claude's system context for the session.

Why the Always-On Hook Appears to Fail

The hook's fail-safe design (always exiting 0) creates silent failures. Users see no error messages—just missing ADHD formatting. Here are the seven primary failure scenarios:

Missing Flag File (Most Common)

  • Symptom: No ADHD rules appear; session starts normally
  • Cause: User never created ~/.claude/.i-have-adhd-always
  • Detection: Run ls -la ~/.claude/.i-have-adhd-always to verify existence

Wrong Config Directory

  • Symptom: Flag file exists elsewhere but rules don't inject
  • Cause: $CLAUDE_CONFIG_DIR overrides the default path; script still searches computed $claude_dir
  • Fix: Ensure flag file exists at $CLAUDE_CONFIG_DIR/.i-have-adhd-always

Permission Problems

  • Symptom: Silent exit despite flag file presence
  • Cause: always-on.sh lacks execute permission, or flag file isn't readable
  • Check: ls -l hooks/always-on.sh and verify r-x permissions

Missing or Relocated SKILL.md

  • Symptom: "ADHD MODE ACTIVE" header appears, but no rules follow
  • Cause: Relative path $script_dir/../skills/i-have-adhd/SKILL.md doesn't resolve
  • Common trigger: Repository manually moved or cloned to non-standard location

Corrupted Front-Matter

  • Symptom: Full file content printed (including metadata), or empty output
  • Cause: AWK pattern fails to detect closing ---—missing newline, extra whitespace, or malformed delimiters
  • Note: The regex ^---[[:space:]]*$ requires clean line endings

Undefined Environment Variables

  • Symptom: Hook fails before script execution
  • Cause: $CLAUDE_PLUGIN_ROOT expands empty in hooks.json, producing sh ""
  • Context: Rare in standard Claude Code installations; affects custom plugin loaders

Timeout Exceeded

  • Symptom: Hook aborted mid-execution, status message hangs
  • Cause: 5-second timeout in hooks.json exceeded by slow filesystem (network drives, encrypted volumes) or hanging AWK process

Enabling and Disabling the Always-On Hook

Enable Always-On Mode


# Create config directory if needed

mkdir -p "${CLAUDE_CONFIG_DIR:-$HOME/.claude}"

# Create the opt-in flag file

touch "${CLAUDE_CONFIG_DIR:-$HOME/.claude}/.i-have-adhd-always"

Disable for Current Session Only

Send the plugin's recognized command within Claude Code:

stop adhd mode

Disable Permanently

rm "${CLAUDE_CONFIG_DIR:-$HOME/.claude}/.i-have-adhd-always"

Debugging the Hook: Stand-Alone Reproduction

Test the mechanism independently of Claude Code:

#!/usr/bin/env sh

# Save as test-hook.sh and run from repository root

export CLAUDE_CONFIG_DIR="${CLAUDE_CONFIG_DIR:-$HOME/.claude}"

claude_dir="$CLAUDE_CONFIG_DIR"
flag_path="$claude_dir/.i-have-adhd-always"

echo "Checking flag at: $flag_path"

[ -f "$flag_path" ] || { echo "FAIL: Flag file missing"; exit 0; }

script_dir="hooks"
skill_path="$script_dir/../skills/i-have-adhd/SKILL.md"

[ -f "$skill_path" ] || { echo "FAIL: SKILL.md not found at $skill_path"; exit 0; }

body=$(awk '
  NR == 1 && $0 ~ /^---[[:space:]]*$/ { in_fm = 1; next }
  in_fm && $0 ~ /^---[[:space:]]*$/   { in_fm = 0; next }
  !in_fm                              { print }
' "$skill_path")

[ -z "$body" ] && echo "WARNING: Extracted body is empty"

echo "--- OUTPUT ---"
printf 'ADHD MODE ACTIVE (always‑on). The ruleset below applies to every response.\n\n'
printf '%s...\n' "$skill_path"
printf '\n%s\n' "$body"

This expanded version adds diagnostic output while preserving the original logic structure from [hooks/always-on.sh](https://github.com/ayghri/i-have-adhd/blob/main/hooks/always-on.sh).

Key Files in the Always-On Mechanism

File Purpose
[hooks/hooks.json](https://github.com/ayghri/i-have-adhd/blob/main/hooks/hooks.json) Registers the SessionStart hook with 5-second timeout
[hooks/always-on.sh](https://github.com/ayghri/i-have-adhd/blob/main/hooks/always-on.sh) Implements flag checking, path resolution, and ruleset injection
[skills/i-have-adhd/SKILL.md](https://github.com/ayghri/i-have-adhd/blob/main/skills/i-have-adhd/SKILL.md) Contains the ADHD formatting rules extracted by the hook
~/.claude/.i-have-adhd-always User-created opt-in flag (not in repository)

Summary

  • The always-on hook is a POSIX shell script executed via Claude Code's SessionStart event
  • Activation requires creating ~/.claude/.i-have-adhd-always (or equivalent path under $CLAUDE_CONFIG_DIR)
  • The script is intentionally fail-safe: any error exits 0 to prevent blocking session start
  • Silent failures occur when flag files are missing, paths are misconfigured, permissions are incorrect, or environment variables are undefined
  • The mechanism parses [skills/i-have-adhd/SKILL.md](https://github.com/ayghri/i-have-adhd/blob/main/skills/i-have-adhd/SKILL.md) with AWK to strip YAML front-matter before injection

Frequently Asked Questions

Why does the always-on hook fail silently instead of showing an error?

The hook exits with status 0 in all error conditions—missing flag, missing skill file, AWK failures—to ensure Claude Code sessions start successfully regardless of plugin state. This design prioritizes session reliability over debugging visibility. Users who haven't opted in (no flag file) shouldn't see errors, so the same silent behavior applies to actual failures. Check file existence and permissions manually when troubleshooting.

What file permissions does the always-on hook require?

The always-on.sh script needs read and execute permissions (chmod 755 or chmod +x). The flag file ~/.claude/.i-have-adhd-always only needs read permission (chmod 644 or touch defaults). The hook runs via sh explicitly, so execute permission on the script itself is technically optional but recommended. The skill file SKILL.md requires read access for the AWK extraction to succeed.

Can I relocate the always-on flag file to a different directory?

The flag file location is computed as ${CLAUDE_CONFIG_DIR:-$HOME/.claude}/.i-have-adhd-always. To relocate it, set the CLAUDE_CONFIG_DIR environment variable before Claude Code starts. The hook doesn't support arbitrary flag locations—it's hardcoded to this path pattern in [hooks/always-on.sh](https://github.com/ayghri/i-have-adhd/blob/main/hooks/always-on.sh).

How do I verify the always-on hook is actually running?

Run the hook script directly with tracing: sh -x hooks/always-on.sh from the repository root. Check for:

  • Correct $CLAUDE_CONFIG_DIR expansion
  • Flag file detection ([ -f "$flag_path" ])
  • SKILL.md path resolution
  • Non-empty body variable after AWK processing

In Claude Code, the status message "Checking i-have-adhd always-on flag..." appears briefly if hooks.json is registered—its absence suggests plugin loading issues rather than hook logic failures.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →