Skip to content

muse-hooks-valid

.muse/hooks.json must use Muse's events, matcher groups and handler fields

Severity error (disabled)
Autofix -
Since v0.20.0
Repo Types muse
Category Muse Code

Activation

This rule is opt-in while Muse loader compatibility and public repository coverage are still limited. Enable it with --rule muse-hooks-valid or:

rules:
  muse-hooks-valid:
    enabled: true

Hook discovery and the shared hooks-dangerous and hooks-prohibited rules keep their existing activation settings independently of this shape rule.

Why

.muse/hooks.json lets you automate shell commands during Muse Code agent lifecycle events — such as right before a tool runs, when a session starts, or when the agent finishes its work. Because hooks are committed to the repository, they provide a reliable, shared way to automate setup and checks for everyone on the team.

During headless runs, Muse Code executes hooks quietly without printing console warnings if a configuration option is unsupported. When a hook doesn't trigger, it can be hard to tell whether the event simply hasn't fired yet or was skipped due to an unrecognized field. This rule inspects .muse/hooks.json against Muse's supported events, matcher groups, and handler options so that your automations run smoothly and reliably.

The commands themselves are also scanned for risky patterns by hooks-dangerous and can be inventoried against an explicit allowlist with hooks-prohibited.

Severity

Severity reflects the impact on your hook configuration:

Errors — prevent the entire file, a matcher group, or a specific handler from running:

  • The whole file is skipped: invalid JSON, non-finite numbers (NaN, Infinity, -Infinity), an event value that is not an array, a matcher group that is not an object, a non-string matcher, a group missing its hooks array, a handler that is not an object, or a known handler field of the wrong type (such as timeout: "10" or async: 1).
  • A matcher group is skipped: keys other than matcher and hooks (such as a leftover description from a Claude Code hooks file). Sibling groups and other events continue to load.
  • A handler is skipped: a missing type, a type other than command, an empty command, an unrecognized handler key, or options unsupported by Muse (if, once: true, asyncRewake: true). Other handlers in the same group continue to run.

Warnings — the file loads, but specific hooks or matchers may not run as intended:

  • Unrecognized event names. Event names are case-sensitive (e.g. SessionStart vs sessionStart).
  • The Setup event, which Muse parses from Claude Code configurations but does not execute.
  • An empty hooks object, event array, or matcher-group hooks array (valid JSON, but configures no actions).
  • A regex matcher that does not compile under Rust's regex engine. Muse uses Rust's regex syntax, which supports Unicode property classes (\p{...}), character-class set operations (&&, --, ~~), named capture groups ((?<name>...)), and \z, but does not support lookarounds, backreferences, or conditional/atomic groups. Matchers longer than 1,000 characters are skipped to keep checks fast.
  • A handler defining commandWindows without a fallback command for Linux or macOS environments.

Info — advisory notices:

  • Events present in Muse's binary but omitted from official documentation (StopFailure, PostToolBatch). Be sure to test these in your environment before relying on them.

When an unknown key appears across multiple groups or handlers — common when migrating a file from another tool — skillsaw groups them into a single concise finding.

Examples

Bad — an unexpected key prevents the first matcher group from running, and an unsupported handler option is dropped:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "enabled": true,
        "hooks": [
          { "type": "command", "command": "./scripts/audit.sh", "args": ["--json"] }
        ]
      }
    ],
    "sessionStart": [
      { "hooks": [{ "type": "command", "command": "./scripts/bootstrap.sh" }] }
    ]
  }
}

Good — matcher groups using Muse's supported fields and PascalCase event names:

{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "startup",
        "hooks": [
          { "type": "command", "command": "./scripts/bootstrap.sh", "timeout": 30 }
        ]
      }
    ],
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "./scripts/audit.sh --json",
            "statusMessage": "Auditing command",
            "async": true
          }
        ]
      }
    ]
  }
}

How to fix

  • Give each matcher group only hooks and, optionally, matcher. Notes or descriptions can be kept in companion documentation or inside invoked hook scripts.
  • Give every handler "type": "command" and a non-empty command string. Include a POSIX command alongside any commandWindows so your hooks work across all platforms.
  • When migrating from Claude Code: combine args into the command string, set environment variables within the script, and handle conditional logic directly in the command script.
  • Specify timeout as a non-negative integer representing seconds (30, rather than 30.0 or "30").
  • Use Muse's standard PascalCase event names (e.g., SessionStart, PreToolUse).

If Muse introduces newer events, fields, or group keys, you can allow them directly in your .skillsaw.yaml configuration without disabling the rule:

rules:
  muse-hooks-valid:
    enabled: true
    # Additional event names dispatched by newer Muse releases:
    extra-events:
      - PreSomethingNew
    # Additional handler fields supported by newer Muse releases:
    extra-handler-fields:
      - retries
    # Additional matcher-group keys:
    extra-group-keys:
      - priority

Matcher check limits

Matcher validation is conservative: it translates Rust inline flags and braced hexadecimal escapes only for syntax checking. For example, Bash|(?i)Write, (?-u:\w+), (?U).*, \x{42}ash, \u{42}ash and \U{42}ash are accepted. Unclosed groups/classes and unsupported look-around/backreferences are still reported in the checked subset. Extended-mode (x) patterns are left unresolved because comments change tokenization. No finding is a complete Rust regex validation guarantee.

Notification and PostToolUseFailure are now documented Muse events and do not receive the undocumented-event advisory. Handler and group checks still apply to both events.

Configuration

rules:
  muse-hooks-valid:
    enabled: false  # true | false | auto
    severity: error
Parameter Description Default
extra-events Additional hook event names to accept, for events newer than this skillsaw release []
extra-handler-fields Additional handler field names to accept, for fields newer than this skillsaw release []
extra-group-keys Additional matcher-group key names to accept, for keys newer than this skillsaw release []

Run skillsaw explain muse-hooks-valid to see this documentation and the rule's effective configuration in your terminal.