Skip to content

fix: wrap hooks.json in top-level "hooks" object for Claude Code schema#3

Open
zxh wants to merge 1 commit into
Ahacad:mainfrom
zxh:fix/hooks-json-schema
Open

fix: wrap hooks.json in top-level "hooks" object for Claude Code schema#3
zxh wants to merge 1 commit into
Ahacad:mainfrom
zxh:fix/hooks-json-schema

Conversation

@zxh
Copy link
Copy Markdown

@zxh zxh commented Apr 18, 2026

Problem

On Claude Code v2.1.112 (and at least recent 2.1.x versions), installing gstack@gstack succeeds but the plugin shows as failed to load:

❯ gstack@gstack
    Version: 0.1.0
    Status: ✘ failed to load
    Error: Hook load failed: [
      {
        "expected": "record",
        "code": "invalid_type",
        "path": ["hooks"],
        "message": "Invalid input: expected record, received undefined"
      }
    ]

The schema validator expects a top-level hooks record whose keys are hook types (SessionStart, PreToolUse, etc.). The current hooks/hooks.json exposes SessionStart at the root, so the hooks key is undefined and validation fails. Net effect: the ensure-setup.sh SessionStart hook never runs, so gstack does nothing on fresh sessions.

Fix

Wrap the content in { "hooks": { ... } }, matching the format used by other published plugins (for example, obra/superpowers ships its hooks.json this way, and the same format is used by user-level ~/.claude/settings.json under hooks).

Also change the matcher from "" to ".*" — the documented "match all" pattern, again consistent with superpowers and what Claude Code accepts without warnings.

Diff

 {
-  "SessionStart": [
-    {
-      "matcher": "",
-      "hooks": [
-        {
-          "type": "command",
-          "command": "bash ${CLAUDE_PLUGIN_ROOT}/hooks/scripts/ensure-setup.sh",
-          "timeout": 120
-        }
-      ]
-    }
-  ]
+  "hooks": {
+    "SessionStart": [
+      {
+        "matcher": ".*",
+        "hooks": [
+          {
+            "type": "command",
+            "command": "bash ${CLAUDE_PLUGIN_ROOT}/hooks/scripts/ensure-setup.sh",
+            "timeout": 120
+          }
+        ]
+      }
+    ]
+  }
 }

Verification

Tested locally on macOS (Apple Silicon) with claude --version = 2.1.112 (Claude Code):

Before: Status: ✘ failed to load with the error above.
After applying this patch to the cached plugin copy (~/.claude/plugins/cache/gstack/gstack/0.1.0/hooks/hooks.json) and re-listing:

❯ gstack@gstack
    Version: 0.1.0
    Scope: user
    Status: ✔ enabled

No other changes in behavior — the same ensure-setup.sh runs on SessionStart as before; it just actually runs now.

Environment

Item Value
OS macOS (Apple Silicon)
Claude Code 2.1.112
Plugin gstack@0.1.0 (this repo, main)
Install method claude plugins install gstack@gstack after claude plugins marketplace add

Scope

One-file, behavior-preserving schema fix. No skills or scripts touched.

Claude Code (tested on v2.1.112) validates plugin hooks.json against a schema
that requires a top-level "hooks" record whose keys are hook types. The
current file exposes "SessionStart" directly, which fails validation with:

    Hook load failed: path ["hooks"] - Invalid input: expected record,
    received undefined

As a result, gstack installs successfully but shows as "failed to load" and
the SessionStart hook never runs.

Also changes the matcher from "" to ".*" which is the documented pattern for
"match all" and is consistent with the format used by other published
plugins (e.g. superpowers).

Verified locally: after this change, gstack loads successfully ("✔ enabled")
on Claude Code 2.1.112.
Copilot AI review requested due to automatic review settings April 18, 2026 08:04
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes Claude Code 2.1.x plugin hook schema validation by updating hooks/hooks.json to match the expected top-level structure so the SessionStart hook loads and runs reliably.

Changes:

  • Wrap hook definitions under a top-level "hooks" object (required by Claude Code schema validator).
  • Change the hook matcher from "" to ".*" to use the documented “match all” pattern.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants