i-have-adhd: readable responses, adapters and evidence
Read the i-have-adhd hook: markers, frontmatter and silent failures
Follow actual source branches and fourteen offline cases, including an empty rules file and preservation of an existing OpenCode command.
What you will learn
- Start with the early exits
- Read the regular expression as a contract
- The adapter protects existing configuration, not every interaction
Before you start
- Basic Git and command-line knowledge
- Ability to separate observed behavior from an untested claim
Explain the inspected mechanism, plan a reversible trial and interpret evidence without treating style as correctness.
Key takeaways
- Successful exit and successful injection are different assertions.
- Frontmatter stripping is not YAML validation.
- A passing edge-case test can document an undesirable behavior.
Start with the early exits
The Node hook first resolves a configuration directory and checks .i-have-adhd-always. Without the marker it exits successfully without output. With a marker, it resolves the rules path relative to the script and again exits silently if the file is absent. These branches explain why looking only at a host status code cannot distinguish an intentional opt-out from a broken installation.
Our fixtures create a temporary configuration directory and synthetic skill file, then launch the unmodified pinned hook with CLAUDE_CONFIG_DIR scoped to that directory. Cases cover no marker, missing rules, ordinary rules, LF and CRLF frontmatter. They execute actual upstream JavaScript, but no assistant host or language model participates.
Read the regular expression as a contract
The replacement pattern removes a YAML-style block only at the beginning of the file and only when it finds a closing delimiter. A deliberately unterminated fixture retains the opening block instead of being parsed as metadata. This is string processing, not a general YAML validator. The trailing-newline expression also trims line endings without interpreting the rule content.
Two edge cases matter operationally. An empty file still produces an activation header, and a body containing normal mode is printed rather than interpreted as a disable command. Both passed our expected-behavior checks; passing does not mean the behavior is ideal. In particular, an activation banner is weaker evidence than a verified nonempty rules body.
The adapter protects existing configuration, not every interaction
The OpenCode config hook adds its skills directory only if absent and preserves a pre-existing command with the same name. We invoke it twice in a fixture and verify that it does not duplicate the directory or replace the synthetic user command. Other cases check an empty system array, missing rules and a new turn while the marker remains present.
The transform appends to the last system string or creates one when the array is empty. It does not parse a conversational stop phrase or remove the opt-in marker. Our test passes freshly created output objects, not repeated mutation of the same host-owned object, and it does not verify OpenCode lifecycle guarantees. That boundary should guide any bug report about repeated injection.
Decision guide
| Criterion | Option A | Option B |
|---|---|---|
| Best when | You need predictable behavior and easy auditing | You need adaptive optimization and have reliable telemetry |
| Main risk | May leave performance on the table | Can become difficult to explain or debug |
Implementation steps
- 1
Trace marker and file-existence branches first.
- 2
Check emitted text as well as process status.
- 3
Test malformed and empty inputs in isolation.
- 4
Document host behavior that the fixture does not cover.
Copy-ready example
// Pattern used by the inspected hook: string processing, not YAML validation.
const strip = text => text.replace(
/^---[^\S\r\n]*\r?\n[\s\S]*?\r?\n---[^\S\r\n]*(?:\r?\n|$)/, ""
);
console.log(strip("---\nname: demo\n---\nRULE"));Frequently asked questions
Did you execute a reimplementation of the hook?
No. The fixture executes the pinned upstream hook and adapter; only rules and host objects are synthetic.
Does the hook itself understand stop phrases?
No. Its code checks a marker and emits text; it is not a conversational state parser.
Sources
- i-have-adhd / hooks/always-on.mjsSource checked 2026-09-12
- i-have-adhd / .opencode/plugins/i-have-adhd.mjsSource checked 2026-09-12
- i-have-adhd / skills/i-have-adhd/SKILL.mdSource checked 2026-09-12