How Hooks Ensure Cross-Platform Compatibility in the i‑have‑adhd Plugin
The i‑have‑adhd plugin uses a Node.js-based hook system with declarative JSON registration and native shell fallbacks to guarantee consistent ADHD-friendly response injection across macOS, Linux, and Windows.
The i‑have‑adhd plugin is a Claude/Codex plugin designed to inject ADHD-friendly response rules at the start of every session. According to the ayghri/i-have-adhd source code, cross-platform compatibility is achieved through a carefully layered hook architecture that abstracts OS differences while providing robust fallbacks for minimal environments.
Declarative Hook Registration
The foundation of the system is hooks/hooks.json, which declares a SessionStart hook that matches common session commands.
{
"type": "command",
"command": "node -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(()=>{})\"",
"timeout": 5,
"statusMessage": "Checking i-have-adhd always-on flag..."
}
This JSON manifest (lines 9‑12) registers a matcher for startup|resume|clear|compact commands. When any session event matches, the runtime executes the listed Node command — the same instruction regardless of host operating system.
Node.js as the Cross-Platform Abstraction Layer
The hook delegates all platform-specific work to Node.js, which provides identical APIs across macOS, Linux, and Windows.
In hooks/always-on.mjs (lines 10‑13), the module imports only standard Node core libraries:
import fs from 'node:fs';
import path from 'node:path';
import os from 'node:os';
import { pathToFileURL } from 'node:url';
These APIs handle all file-system operations:
os.homedir()— resolves the user's home directory correctly on every platformpath.join()— uses the appropriate path separator (/vs\)fs.existsSync()— checks for the opt-in flag file uniformly
The core logic (lines 15‑44) never blocks session start: it checks for $CLAUDE_CONFIG_DIR/.i-have-adhd-always, reads SKILL.md, strips YAML front-matter, and outputs the ruleset. Because Node.js abstracts OS differences, the same JavaScript code executes identically everywhere.
Native Shell Fallback Scripts
For environments where Node may be unavailable, the plugin ships platform-native alternatives:
| Fallback | File | Purpose |
|---|---|---|
| POSIX shell | hooks/always-on.sh |
Runs on macOS, Linux, and any sh-compatible environment |
| PowerShell | hooks/always-on.ps1 |
Runs on Windows without WSL or Node installed |
Both scripts replicate the same opt-in check and skill-file reading logic using native commands. For example, always-on.sh uses [ -f "$CLAUDE_CONFIG_DIR/.i-have-adhd-always" ] while always-on.ps1 uses Test-Path. This ensures cross-platform compatibility even on minimal systems that cannot spawn Node processes.
Enabling Always-On Mode
The opt-in mechanism uses a simple flag file that works uniformly across platforms because the path is constructed from environment variables or os.homedir().
macOS/Linux:
mkdir -p "$HOME/.claude"
touch "$HOME/.claude/.i-have-adhd-always"
Windows PowerShell:
$claudeDir = Join-Path $HOME ".claude"
New-Item -ItemType Directory -Path $claudeDir -Force
New-Item -ItemType File -Path (Join-Path $claudeDir ".i-have-adhd-always") -Force
Once enabled, any session matching the SessionStart hook automatically triggers the Node command, loads always-on.mjs, and injects the ADHD ruleset from skills/i-have-adhd/SKILL.md.
Manual Execution for Testing
You can verify the hook works on your platform by running the exact command the runtime executes:
node -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(()=>{})"
If Node is unavailable, fall back to the native scripts:
# POSIX systems
sh hooks/always-on.sh
# Windows
.\hooks\always-on.ps1
Summary
- Declarative JSON registration in
hooks/hooks.jsondefines when and how hooks trigger, independent of platform - Node.js core APIs in
hooks/always-on.mjsprovide write-once-run-anywhere execution for the primary code path - Shell fallbacks (
always-on.shandalways-on.ps1) guarantee functionality on systems without Node - Environment-based flag paths ensure the opt-in check works identically on macOS, Linux, and Windows
- Zero-blocking design prevents session start delays regardless of execution environment
Frequently Asked Questions
What makes the i‑have‑adhd hook system cross-platform?
The hook system relies on Node.js as an abstraction layer. The hooks/hooks.json file triggers a Node command that dynamically imports hooks/always-on.mjs. Since Node's fs, path, os, and url modules behave identically across macOS, Linux, and Windows, the same JavaScript code runs everywhere without platform-specific branches.
What happens if Node.js is not installed?
The plugin provides native shell fallbacks: hooks/always-on.sh for POSIX systems and hooks/always-on.ps1 for Windows. These scripts perform the same opt-in flag check and skill output using only shell-builtins, ensuring the hook functions even on minimal environments where Node cannot be spawned.
How does the plugin know where to find the opt-in flag file?
The always-on.mjs module constructs the flag path from process.env.CLAUDE_CONFIG_DIR or falls back to os.homedir(). On POSIX systems this resolves to $HOME/.claude/.i-have-adhd-always; on Windows it resolves to %USERPROFILE%\.claude\.i-have-adhd-always. The Node path module handles separator differences automatically.
Can I run the hook manually for testing?
Yes. The exact command from hooks/hooks.json can be pasted into any terminal with Node installed. Alternatively, execute sh hooks/always-on.sh (macOS/Linux) or .\hooks\always-on.ps1 (Windows) to test the fallback paths directly.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →