Understanding Hook Matcher Patterns in i-have-adhd hooks.json

The matcher field in hooks/hooks.json uses pipe-separated regular expressions to trigger hook actions when users type specific commands at lifecycle events like SessionStart.

The i-have-adhd plugin implements a declarative hook system that responds to user commands through configurable patterns. By defining hook matcher patterns in hooks/hooks.json, developers control which commands execute custom scripts during specific runtime events without modifying core plugin logic.

Anatomy of the matcher Field

Each hook entry contains a matcher string that determines command eligibility. According to the source code in hooks/hooks.json (lines 5-6), this field accepts a pipe-separated list of keywords that the runtime evaluates as a regular expression.

The matcher supports three key characteristics:

  • OR Logic: The pipe character (|) separates alternative commands, matching any single token in the list
  • Case Insensitivity: The runtime normalizes all input to lowercase before testing against the pattern
  • Regex Extensibility: Beyond simple tokens, the field accepts full regular expressions (e.g., ^task-.*$) for complex pattern matching

In the built-in configuration, the SessionStart event uses the following matcher:

{
  "matcher": "startup|resume|clear|compact"
}

This pattern triggers associated actions when users type any of the four commands at session initialization.

Runtime Evaluation Logic

When the Cursor runtime processes a command, it executes a four-step evaluation chain:

  1. Normalization: Converts the incoming command string to lowercase
  2. Event Lookup: Identifies the current lifecycle context (e.g., SessionStart)
  3. Pattern Testing: Evaluates the matcher regex against the normalized command
  4. Action Queuing: Schedules the hook's defined actions (commands, scripts, or notifications) upon successful match

The evaluation occurs entirely within the runtime core; the plugin supplies only the declarative JSON configuration and referenced scripts.

Extending Matcher Patterns for Custom Commands

To fire hooks for additional commands, extend the matcher string with new pipe-separated tokens. For example, to trigger the always-on script when users type break or focus:

{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "startup|resume|clear|compact|break|focus",
        "hooks": [
          {
            "type": "command",
            "command": "node",
            "args": ["${CLAUDE_PLUGIN_ROOT}/hooks/always-on.mjs"],
            "timeout": 5,
            "statusMessage": "Checking i-have-adhd always-on flag..."
          }
        ]
      }
    ]
  }
}

The runtime automatically recognizes the new tokens break and focus because the matcher treats the entire string as an "or" list. The associated script in hooks/always-on.mjs can then access these commands via process.argv to implement conditional logic:

// hooks/always-on.mjs (simplified)
if (process.argv.includes('break')) {
  console.log('🧘‍♂️ Time for a break!');
}

Key Implementation Files

The hook matcher system spans three primary files in the repository:

  • hooks/hooks.json: Declares events, matcher strings, and action configurations
  • hooks/always-on.mjs: Example script executed when matchers succeed; handles the plugin's "always-on" flag checking
  • extensions/i-have-adhd.ts: TypeScript entry point that registers the plugin and exposes the hook system to the runtime

Summary

  • Hook matcher patterns in hooks/hooks.json define which user commands trigger lifecycle actions using pipe-separated regex syntax
  • Matching is case-insensitive and evaluates against normalized lowercase input
  • The SessionStart event uses "startup|resume|clear|compact" to trigger the always-on status check
  • Developers extend functionality by appending tokens to existing matcher strings or creating new hook entries
  • The runtime handles all pattern evaluation, while the plugin provides declarative JSON and executable scripts

Frequently Asked Questions

What syntax does the matcher field support?

The matcher field accepts standard JavaScript regular expression syntax. For simple command lists, use pipe-separated tokens like "cmd1|cmd2|cmd3". For advanced filtering, use full regex patterns such as "^task-.*$" to match any command starting with "task-".

Is the matcher case-sensitive?

No. The runtime normalizes all incoming commands to lowercase before evaluating them against the matcher pattern. Define your patterns in lowercase to ensure consistent matching regardless of user input casing.

How do I add a new command trigger to an existing hook?

Locate the relevant event in hooks/hooks.json (commonly SessionStart), find the matcher string, and append your new command separated by a pipe character. For example, change "startup|resume" to "startup|resume|mycommand". The hook actions will now execute when users type "mycommand" during that event.

Where does the matcher evaluation logic execute?

The pattern matching occurs within the Cursor/Claude runtime core, not in the plugin code itself. The runtime reads the matcher strings from hooks/hooks.json and tests them against normalized user input. If a match occurs, the runtime schedules the associated actions defined in the hook's hooks array, such as executing hooks/always-on.mjs.

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 →