claude-code - 💡(How to fix) Fix Plugin framework deletes external hooks/hooks.json from working tree [2 comments, 3 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
anthropics/claude-code#45296Fetched 2026-04-09 08:08:39
View on GitHub
Comments
2
Participants
3
Timeline
8
Reactions
0
Timeline (top)
labeled ×6commented ×2

Error Message

  • The hooks/ directory and all its contents are deleted from the working tree
  • This includes untracked files placed in hooks/ (not just hooks.json)
  • git status shows deleted: hooks/hooks.json
  • Restoring the file via git checkout HEAD -- hooks/hooks.json triggers another deletion within seconds
  • The deletion also occurs when plugin.json explicitly declares "hooks": "./hooks/hooks.json"
  • The deletion also occurs when pointing to a different path like "hooks": "./config/hooks.json" — the file at the resolved path is deleted
  • Files in directories not targeted by the hooks field are unaffected (confirmed by control test)

Fix Action

Workaround

Inline the hooks configuration directly in plugin.json using the object form:

{
  "name": "my-plugin",
  "hooks": {
    "hooks": {
      "SessionStart": [
        {
          "hooks": [
            {
              "type": "command",
              "command": "${CLAUDE_PLUGIN_ROOT}/scripts/my-hook.sh"
            }
          ]
        }
      ]
    }
  }
}

Code Example

my-plugin/
├── .claude-plugin/
│   └── plugin.json
├── hooks/
│   └── hooks.json    ← gets deleted
└── scripts/
    └── my-hook.sh

---

{
  "name": "my-plugin",
  "hooks": {
    "hooks": {
      "SessionStart": [
        {
          "hooks": [
            {
              "type": "command",
              "command": "${CLAUDE_PLUGIN_ROOT}/scripts/my-hook.sh"
            }
          ]
        }
      ]
    }
  }
}
RAW_BUFFERClick to expand / collapse

Bug description

When a plugin uses an external hooks/hooks.json file (the default location per plugin docs), Claude Code deletes the file and its parent directory from the working tree after loading it. This causes a persistent unstaged deletion in git status.

Reproduction steps

  1. Create a plugin with .claude-plugin/plugin.json and a hooks/hooks.json file:
my-plugin/
├── .claude-plugin/
│   └── plugin.json
├── hooks/
│   └── hooks.json    ← gets deleted
└── scripts/
    └── my-hook.sh
  1. Start a Claude Code session in the plugin directory (or use --plugin-dir)
  2. Observe that hooks/hooks.json (and the entire hooks/ directory) is deleted from disk within seconds

Observed behavior

  • The hooks/ directory and all its contents are deleted from the working tree
  • This includes untracked files placed in hooks/ (not just hooks.json)
  • git status shows deleted: hooks/hooks.json
  • Restoring the file via git checkout HEAD -- hooks/hooks.json triggers another deletion within seconds
  • The deletion also occurs when plugin.json explicitly declares "hooks": "./hooks/hooks.json"
  • The deletion also occurs when pointing to a different path like "hooks": "./config/hooks.json" — the file at the resolved path is deleted
  • Files in directories not targeted by the hooks field are unaffected (confirmed by control test)

Expected behavior

External hook files should remain on disk after being loaded, especially for local development plugins. The framework should read the file without deleting it.

Workaround

Inline the hooks configuration directly in plugin.json using the object form:

{
  "name": "my-plugin",
  "hooks": {
    "hooks": {
      "SessionStart": [
        {
          "hooks": [
            {
              "type": "command",
              "command": "${CLAUDE_PLUGIN_ROOT}/scripts/my-hook.sh"
            }
          ]
        }
      ]
    }
  }
}

Environment

  • Claude Code: latest (2026-04-08)
  • macOS Darwin 25.4.0
  • Plugin loaded via local working directory (.claude-plugin/plugin.json present in CWD)

Hypothesis

The plugin framework appears to apply a cache-and-cleanup pattern (designed for marketplace plugins copied to ~/.claude/plugins/cache/) to local development plugins as well, removing the source hook file after reading it into the hook registry.

extent analysis

TL;DR

Inline the hooks configuration directly in plugin.json to prevent deletion of external hook files.

Guidance

  • To mitigate the issue, use the object form to define hooks in plugin.json instead of referencing an external file.
  • Verify that the deletion stops by checking git status after making the change.
  • Be aware that this workaround requires maintaining the hooks configuration within plugin.json, which might not be desirable for all use cases.
  • If the external file is necessary, consider tracking the issue for a potential fix in future versions of Claude Code.

Example

{
  "name": "my-plugin",
  "hooks": {
    "hooks": {
      "SessionStart": [
        {
          "hooks": [
            {
              "type": "command",
              "command": "${CLAUDE_PLUGIN_ROOT}/scripts/my-hook.sh"
            }
          ]
        }
      ]
    }
  }
}

Notes

This workaround is based on the provided information and might not be applicable if the plugin's structure or requirements change.

Recommendation

Apply the workaround by inlining the hooks configuration in plugin.json to prevent file deletion, as it provides a reliable solution given the current behavior of Claude Code.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

FAQ

Expected behavior

External hook files should remain on disk after being loaded, especially for local development plugins. The framework should read the file without deleting it.

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING