URGENT REGRESSION FIX. PR #2076 (Graphite gt workflow) gated the PostToolUse commit/push hooks with: "if": "Bash(git commit:*)|Bash(gt create:*)|Bash(gt modify:*)" "if": "Bash(git push:*)|Bash(gt submit:*)" mirroring the regex-OR idiom that `matcher` uses ("Edit|Write|MultiEdit|NotebookEdit"). But `if` is NOT a regex — it's a SINGLE permission-rule string. The CC harness's dispatch filter parses the entire `if` value as one rule of shape `ToolName(rule_content)` via: let firstParen = H.indexOf("("); let lastParen = H.lastIndexOf(")"); // searches from END if (lastParen !== H.length - 1) return { toolName: H }; let toolName = H.slice(0, firstParen); let ruleContent = H.slice(firstParen + 1, lastParen); Applied to the broken commit clause: toolName = "Bash" ruleContent = "git commit:*)|Bash(gt create:*)|Bash(gt modify:*" The garbled `ruleContent` never matches any real command, so the hook never fires — for ANY workflow, not just gt. The plugin's deepest review layer was dead in production for all users on builds shipping PR #2076. Fix shape: split into separate hook entries, each with its own well-formed single-rule `if` clause. The Python hook self-routes commit vs push via the bash-command regexes and dedups concurrent spawns via `_claim_bash_hook_once`, so multiple entries firing the same script is safe. This commit: 1. hooks.json: 5 precise entries (one per command shape) instead of the broken |-joined 2-entry form. Restores the original commit/ push behavior bit-for-bit (`Bash(git commit:*)` + `Bash(git push:*)` are unchanged from pre-#2076), and adds 3 separate entries for the Graphite commands (`Bash(gt create:*)`, `Bash(gt modify:*)`, `Bash(gt submit:*)`). No git behavior change. The earlier draft used the broader `Bash(git *)` + `Bash(gt *)` per the reporter's suggestion, but that has a real cost: every `git status` / `git log` / `git diff` would spawn the Python hook only to early-exit via the regex matcher. Precise per-command entries avoid the spawn overhead and match the pre-#2076 cost profile exactly. 2. security_reminder_hook.py: widen `_GIT_COMMIT_RE` to tolerate `git -C <path>` and `git -c k=v` global options between `git` and `commit` (mirrors `_GIT_PUSH_RE`'s long-standing tolerance). Without this, `git -C /repo commit` is silently dropped by the handler — reporter flagged this as the secondary finding. Verified locally on macOS Python 3.13: - hooks.json valid JSON, 5 `if` clauses each parses to a single `{toolName: "Bash", ruleContent: "<command>:*"}` pair. - py_compile security_reminder_hook.py clean. - 9-case regex sanity: all 4 commit forms match (bare, -C path, -c k=v, gt create/modify); 3 non-commit forms reject (status, gt submit, gt log). Pre-fix would reject -C path form. - 7 new tests in test_2089_if_clause_validity.py + 2 updated tests in test_gt_graphite_workflow.py: * 12 sanity tests for a Python parser mirroring harness's BA(H) — pinned so a future refactor can't silently start accepting the broken form. * 2 hooks.json validity: every `if` clause parses as a single valid rule; at least one if-gated hook exists. * 1 post-fix structure: separate entries cover git AND gt. * 2 updated gt-coverage: SOME clause covers git, SOME clause covers gt (no longer requires both in the same |-joined clause, which was the broken shape). TDD-verified the test catches the bug: temporarily restored main's broken |-joined hooks.json, ran the new test, saw `test_every_if_clause_is_single_valid_rule` fail with a clear error explaining #2089's cause. Restored fix, test passes. - Full suite: 336/353 pass (17 unrelated failures from open PRs #2078 / #2086 not in this branch). NOT verified end-to-end with a real CC instance triggering the hooks on a git or gt commit. The static-shape tests catch the regression class and the regex sanity tests pin the `git -C` tolerance, but the asyncRewake feedback loop needs runtime verification. Closes #2089. Restores the closes for #2048 that PR #2076 attempted. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Claude Code Plugins Directory
A curated directory of high-quality plugins for Claude Code.
⚠️ Important: Make sure you trust a plugin before installing, updating, or using it. Anthropic does not control what MCP servers, files, or other software are included in plugins and cannot verify that they will work as intended or that they won't change. See each plugin's homepage for more information.
Structure
/plugins- Internal plugins developed and maintained by Anthropic/external_plugins- Third-party plugins from partners and the community
Installation
Plugins can be installed directly from this marketplace via Claude Code's plugin system.
To install, run /plugin install {plugin-name}@claude-plugins-official
or browse for the plugin in /plugin > Discover
Contributing
Internal Plugins
Internal plugins are developed by Anthropic team members. See /plugins/example-plugin for a reference implementation.
External Plugins
Third-party partners can submit plugins for inclusion in the marketplace. External plugins must meet quality and security standards for approval. To submit a new plugin, use the plugin directory submission form.
Plugin Structure
Each plugin follows a standard structure:
plugin-name/
├── .claude-plugin/
│ └── plugin.json # Plugin metadata (required)
├── .mcp.json # MCP server configuration (optional)
├── commands/ # Slash commands (optional)
├── agents/ # Agent definitions (optional)
├── skills/ # Skill definitions (optional)
└── README.md # Documentation
Skill-bundle plugins
When a plugin's source repository ships skills (SKILL.md files) without a .claude-plugin/plugin.json manifest, the marketplace entry can declare the skills directly using strict: false and an explicit skills array.
{
"name": "example-bundle",
"description": "Brief description of the bundled skills.",
"author": { "name": "Author Name" },
"category": "development",
"source": {
"source": "git-subdir",
"url": "https://github.com/example-org/sdk.git",
"path": "packages/agent-skills",
"ref": "main",
"sha": "<commit sha>"
},
"strict": false,
"skills": [
"./skill-a",
"./skill-b",
"./skill-c"
],
"homepage": "https://github.com/example-org/sdk"
}
Each path in skills is relative to source.path and points at a directory containing a SKILL.md. Paths can reach deeper than a single level — for example, ["./libA/skill-1", "./libB/skill-2"] exposes a curated subset across multiple library subdirectories. Each skill is registered as <plugin-name>:<skill-name> in Claude Code.
For the underlying schema, see Strict mode in the marketplace documentation.
License
Please see each linked plugin for the relevant LICENSE file.
Documentation
For more information on developing Claude Code plugins, see the official documentation.