diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index 757baae..18d9382 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -5,5 +5,6 @@ "author": { "name": "Dietrich Gebert", "url": "https://github.com/DietrichGebert" - } + }, + "hooks": "./hooks/claude-codex-hooks.json" } diff --git a/.codex-plugin/plugin.json b/.codex-plugin/plugin.json index 3595c65..dad3120 100644 --- a/.codex-plugin/plugin.json +++ b/.codex-plugin/plugin.json @@ -11,6 +11,7 @@ "license": "MIT", "keywords": ["yagni", "minimalism", "code-review", "productivity"], "skills": "./skills/", + "hooks": "./hooks/claude-codex-hooks.json", "interface": { "displayName": "Ponytail", "shortDescription": "Lazy senior developer mode", diff --git a/README.md b/README.md index 7aad189..1c95619 100644 --- a/README.md +++ b/README.md @@ -163,6 +163,7 @@ gemini extensions install https://github.com/DietrichGebert/ponytail ``` Loads the ruleset as always-on context every session and registers the `/ponytail` commands; the `skills/` ship too, activated when a task needs them. +The Gemini adapter intentionally does not ship a root `hooks/hooks.json`: Gemini auto-loads that path, while Ponytail's lifecycle hooks use Claude/Codex event names. ### Antigravity CLI diff --git a/docs/agent-portability.md b/docs/agent-portability.md index 5f965f5..cd01805 100644 --- a/docs/agent-portability.md +++ b/docs/agent-portability.md @@ -8,11 +8,11 @@ to load in a given agent. | Host | Files | Notes | |------|-------|-------| -| Claude Code | `.claude-plugin/`, `commands/`, `hooks/` | Full plugin install with session activation, mode tracking, commands, and statusline support. | -| Codex | `.codex-plugin/plugin.json`, `hooks/hooks.json`, `hooks/`, `skills/` | Plugin install with the same skills plus lifecycle hooks for activation and mode tracking. | +| Claude Code | `.claude-plugin/plugin.json`, `commands/`, `hooks/claude-codex-hooks.json`, `hooks/` | Full plugin install with session activation, mode tracking, commands, and statusline support. | +| Codex | `.codex-plugin/plugin.json`, `hooks/claude-codex-hooks.json`, `hooks/`, `skills/` | Plugin install with the same skills plus lifecycle hooks for activation and mode tracking. | | OpenCode | `.opencode/plugins/ponytail.mjs`, `.opencode/command/`, `hooks/`, `skills/` | Server plugin injects the ruleset each turn via `experimental.chat.system.transform` and persists `/ponytail` switches; reuses the shared instruction builder. | | pi | `pi-extension/`, `skills/`, `hooks/` | Package extension: injects the ruleset each turn through the shared instruction builder and registers the `/ponytail` commands. | -| Gemini CLI | `gemini-extension.json`, `AGENTS.md`, `commands/`, `skills/` | Extension manifest points `contextFileName` at `AGENTS.md` for always-on rules, and reuses the existing `commands/*.toml` and `skills/`, which Gemini CLI auto-discovers. | +| Gemini CLI | `gemini-extension.json`, `AGENTS.md`, `commands/`, `skills/` | Extension manifest points `contextFileName` at `AGENTS.md` for always-on rules, and reuses the existing `commands/*.toml` and `skills/`, which Gemini CLI auto-discovers. The Claude/Codex hook map is not placed at Gemini's auto-discovered `hooks/hooks.json` path. | | Cursor | `.cursor/rules/ponytail.mdc` | Always-on project rule. | | Windsurf | `.windsurf/rules/ponytail.md` | Project rule. | | Cline | `.clinerules/ponytail.md` | Project rule. | diff --git a/hooks/hooks.json b/hooks/claude-codex-hooks.json similarity index 100% rename from hooks/hooks.json rename to hooks/claude-codex-hooks.json diff --git a/tests/gemini-extension.test.js b/tests/gemini-extension.test.js index 0f24338..db09661 100644 --- a/tests/gemini-extension.test.js +++ b/tests/gemini-extension.test.js @@ -25,6 +25,10 @@ const VERSIONED_MANIFESTS = [ // Gemini auto-discovers these by directory; the manifest is only useful if they exist. const REUSED_COMMANDS = ['commands/ponytail.toml', 'commands/ponytail-review.toml']; const REUSED_SKILLS = ['skills/ponytail/SKILL.md']; +// Gemini CLI auto-loads this exact path for extension hooks. Ponytail's +// Claude/Codex hook map uses events Gemini does not support, so it must stay +// behind the host-specific plugin manifests instead. +const GEMINI_AUTO_HOOKS = 'hooks/hooks.json'; // Same load-bearing phrases asserted by scripts/check-rule-copies.js: the file // contextFileName points at must actually carry the rules, not just exist. const RULE_INVARIANTS = [ @@ -77,3 +81,11 @@ test('the commands and skills the adapter reuses are present', () => { assert.ok(fs.existsSync(path.join(root, rel)), `reused file missing: ${rel}`); } }); + +test('Gemini cannot auto-discover Claude/Codex hook events', () => { + assert.equal( + fs.existsSync(path.join(root, GEMINI_AUTO_HOOKS)), + false, + `${GEMINI_AUTO_HOOKS} is auto-loaded by Gemini CLI; keep Claude/Codex hooks on manifest paths`, + ); +}); diff --git a/tests/hooks-windows.test.js b/tests/hooks-windows.test.js index f7b5353..b72f357 100644 --- a/tests/hooks-windows.test.js +++ b/tests/hooks-windows.test.js @@ -11,7 +11,11 @@ const fs = require('fs'); const path = require('path'); const root = path.join(__dirname, '..'); -const HOOKS_JSON = 'hooks/hooks.json'; +const HOOKS_JSON = 'hooks/claude-codex-hooks.json'; +const HOST_PLUGIN_MANIFESTS = [ + '.claude-plugin/plugin.json', + '.codex-plugin/plugin.json', +]; // cmd.exe variable syntax (%FOO%); PowerShell leaves it literal, breaking the path. const CMD_VAR_SYNTAX = /%[A-Za-z_][A-Za-z0-9_]*%/; // Pull the hooks/