# How Hooks Ensure Cross-Platform Compatibility in the i‑have‑adhd Plugin

> Discover how i-have-adhd plugin's Node.js hooks ensure cross-platform compatibility. Get ADHD-friendly responses on macOS, Linux, and Windows.

- Repository: [Ayoub Ghriss/i-have-adhd](https://github.com/ayghri/i-have-adhd)
- Tags: internals
- Published: 2026-08-18

---

**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`](https://github.com/ayghri/i-have-adhd/blob/main/hooks/hooks.json), which declares a **SessionStart** hook that matches common session commands.

```json
{
  "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:

```javascript
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 platform
- **`path.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`](https://github.com/ayghri/i-have-adhd/blob/main/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`](https://github.com/ayghri/i-have-adhd/blob/main/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`](https://github.com/ayghri/i-have-adhd/blob/main/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:**

```bash
mkdir -p "$HOME/.claude"
touch "$HOME/.claude/.i-have-adhd-always"

```

**Windows PowerShell:**

```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`](https://github.com/ayghri/i-have-adhd/blob/main/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:

```bash
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:

```bash

# POSIX systems

sh hooks/always-on.sh

# Windows

.\hooks\always-on.ps1

```

## Summary

- **Declarative JSON registration** in [`hooks/hooks.json`](https://github.com/ayghri/i-have-adhd/blob/main/hooks/hooks.json) defines when and how hooks trigger, independent of platform
- **Node.js core APIs** in `hooks/always-on.mjs` provide write-once-run-anywhere execution for the primary code path
- **Shell fallbacks** ([`always-on.sh`](https://github.com/ayghri/i-have-adhd/blob/main/always-on.sh) and `always-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`](https://github.com/ayghri/i-have-adhd/blob/main/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`](https://github.com/ayghri/i-have-adhd/blob/main/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`](https://github.com/ayghri/i-have-adhd/blob/main/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.