Skip to content
Productivity
HotHook

Hooks

What i-have-adhd runs automatically, and when. A hook is a command Claude Code fires at a fixed moment, without you asking for it.

From plugin
i-have-adhd
49k1 skill1 hook
Install
> /plugin marketplace add ayghri/i-have-adhd
> /plugin install i-have-adhd@i-have-adhd

Ships with i-have-adhd. Installing the plugin gets these hooks.

What fires, and when

SessionStart

Fires once when a session begins, and again after a context compaction. It is where a plugin sets up its environment, or restores state the compaction dropped.

  • Matchesstartup|resume|clear|compactnode -e "(async()=>{const root=process.env.CLAUDE_PLUGIN_ROOT||process.env.PLUGIN_ROOT;if(root)await import(require('node:url').pathToFileURL(require('node:path').join(root,'hooks','always-on.mjs')).href)})().catch(()=>{})"
Read hooks/hooks.json

Where it lives

  • hooks/always-on.mjsGitHub
    Read the script
    // SessionStart hook: injects the full i-have-adhd ruleset when the user has
    // opted in by creating $CLAUDE_CONFIG_DIR/.i-have-adhd-always (default ~/.claude).
    // Never blocks session start: any failure exits 0.
    //
    // Runs under Node so it works on macOS, Linux, and Windows. The shared Claude
    // Code/Codex hook launches this module from the plugin-root environment rather
    // than relying on platform-specific shell expansion for the script path.
    // Native sh and PowerShell implementations remain available as fallbacks.
    
    import fs from "node:fs";
    import os from "node:os";
    import path from "node:path";
    import { fileURLToPath } from "node:url";
    
    try {
      const claudeDir = process.env.CLAUDE_CONFIG_DIR || path.join(os.homedir(), ".claude");
      const flagPath = path.join(claudeDir, ".i-have-adhd-always");
    
      // Only fire when the user has opted in.
      if (!fs.existsSync(flagPath)) process.exit(0);
    
      // Resolve SKILL.md relative to this script's own location, not a trusted env var.
      const scriptDir = path.dirname(fileURLToPath(import.meta.url));
      const skillPath = path.join(scriptDir, "..", "skills", "i-have-adhd", "SKILL.md");
      if (!fs.existsSync(skillPath)) process.exit(0);
    
      // Strip a leading YAML frontmatter block (--- ... --- at the very top of file).
      const body = fs
        .readFileSync(skillPath, "utf8")
        .replace(
          /^---[^\S\r\n]*\r?\n[\s\S]*?\r?\n---[^\S\r\n]*(?:\r?\n|$)/,
          "",
        )
        .replace(/(?:\r?\n)+$/, "");
    
      process.stdout.write(
        "ADHD MODE ACTIVE (always-on). The ruleset below applies to every response. " +
          '"stop adhd mode" turns it off for this session; ' +
          `delete ${flagPath} to turn always-on off for good.\n\n${body}\n`,
      );
    } catch {
      // Never block session start.
      process.exit(0);
    }
    
  • hooks/always-on.ps1GitHub
    Read the script
    # SessionStart hook fallback for Windows PowerShell. Injects the full
    # i-have-adhd ruleset when the user has opted in by creating
    # $CLAUDE_CONFIG_DIR/.i-have-adhd-always (default ~/.claude).
    # Never blocks session start: any failure exits 0.
    
    try {
      $claudeDir = if ($env:CLAUDE_CONFIG_DIR) {
        $env:CLAUDE_CONFIG_DIR
      } else {
        Join-Path ([Environment]::GetFolderPath("UserProfile")) ".claude"
      }
      $flagPath = Join-Path $claudeDir ".i-have-adhd-always"
    
      if (-not (Test-Path -LiteralPath $flagPath -PathType Leaf)) {
        exit 0
      }
    
      $scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
      $skillPath = Join-Path $scriptDir "../skills/i-have-adhd/SKILL.md"
      if (-not (Test-Path -LiteralPath $skillPath -PathType Leaf)) {
        exit 0
      }
    
      $lines = [System.IO.File]::ReadAllLines($skillPath)
      $bodyStart = 0
    
      if ($lines.Length -gt 0 -and $lines[0] -match '^---\s*$') {
        # Only treat the block as frontmatter when the closing delimiter exists;
        # an unterminated fence is not frontmatter, so keep the whole file.
        for ($i = 1; $i -lt $lines.Length; $i++) {
          if ($lines[$i] -match '^---\s*$') {
            $bodyStart = $i + 1
            break
          }
        }
      }
    
      $body = if ($bodyStart -lt $lines.Length) {
        [string]::Join([Environment]::NewLine, $lines[$bodyStart..($lines.Length - 1)])
      } else {
        ""
      }
    
      $banner = 'ADHD MODE ACTIVE (always-on). The ruleset below applies to every response. ' +
        '"stop adhd mode" turns it off for this session; delete '
      [Console]::Out.Write($banner + $flagPath + " to turn always-on off for good.`n`n" + $body + "`n")
    } catch {
      # Never block session start.
      exit 0
    }
    
  • hooks/always-on.shGitHub
    Read the script
    #!/usr/bin/env sh
    # SessionStart hook: injects the full i-have-adhd ruleset when the user has
    # opted in by creating $CLAUDE_CONFIG_DIR/.i-have-adhd-always (default ~/.claude).
    # Never blocks session start: any failure exits 0.
    #
    # POSIX fallback for environments where the default Node hook cannot run. It
    # works with sh on macOS/Linux and Git Bash on Windows without a Node install.
    
    claude_dir="${CLAUDE_CONFIG_DIR:-$HOME/.claude}"
    flag_path="$claude_dir/.i-have-adhd-always"
    
    # Only fire when the user has opted in.
    [ -f "$flag_path" ] || exit 0
    
    # $0 is the absolute script path substituted into hooks.json by Claude Code,
    # so resolve SKILL.md relative to it instead of trusting an exported env var.
    script_dir=$(dirname -- "$0")
    skill_path="$script_dir/../skills/i-have-adhd/SKILL.md"
    [ -f "$skill_path" ] || exit 0
    
    # Strip a leading YAML frontmatter block (--- ... --- at the very top of file).
    # An unterminated fence is not frontmatter, so the whole file is kept unless the
    # closing delimiter exists (two passes; matches the Node and PowerShell hooks).
    body=$(awk '
      NR == FNR {
        if (NR == 1 && $0 ~ /^---[[:space:]]*$/) { in_fm = 1; next }
        if (in_fm && $0 ~ /^---[[:space:]]*$/)   { in_fm = 0; closed = 1 }
        next
      }
      FNR == 1 { strip = closed }
      strip && FNR == 1 && $0 ~ /^---[[:space:]]*$/ { skipping = 1; next }
      skipping && $0 ~ /^---[[:space:]]*$/          { skipping = 0; next }
      !skipping { print }
    ' "$skill_path" "$skill_path") || exit 0
    
    printf 'ADHD MODE ACTIVE (always-on). The ruleset below applies to every response. "stop adhd mode" turns it off for this session; delete %s to turn always-on off for good.\n\n%s\n' \
      "$flag_path" "$body"
    

Read the script before you install anything that runs on your machine. This is the one part of a plugin that acts without being asked.

Ships withi-have-adhd

A skill to stop your coding agent from burying the answer. ADHD-friendly output.

Get the whole plugin
Stats
50,244
Stars
2,900
Forks
Active
Maintenance
Python
Language
MIT
License
3d ago
Last commit
4mo ago
Created

Repo: ayghri/i-have-adhd