diff --git a/CLAUDE.md b/CLAUDE.md index e7396eb..9700ba2 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -18,7 +18,10 @@ is organized as a **portable `core/` + per-agent adapters** architecture: 3. **`adapters/pi/`** — Pi coding-agent adapter (in-process TUI widget). 4. **`adapters/claude-code/`** — Claude Code adapter: short-lived hooks mutate the state file via the campy CLI; a `campy watch` pane or the statusline - renders. Contains `hooks/`, `commands/`, `.claude-plugin/`, and `statusline.sh`. + renders. Wires only two reactive hooks — `PostToolUse` (scoped to + `Edit|Write|MultiEdit|NotebookEdit|Bash`) and `Stop` — plus a one-shot + `SessionStart`; read-only tools never spawn a process. Contains `hooks/`, + `commands/`, `.claude-plugin/`, and `statusline.sh`. 5. **`adapters/mcp/`** — MCP (stdio) server for agents whose native surface is MCP (Gemini CLI, Codex CLI, Cursor CLI). Watches the project dir → `file_edited` events, and exposes `campy_*` tools (status/feed/play/pet/switch) returning the @@ -31,8 +34,11 @@ is organized as a **portable `core/` + per-agent adapters** architecture: `campy setup` detects installed agents (via `core/detect.ts`) and wires each the native way; `campy install ` does one. Native mechanism per agent: -**Claude Code** → plugin marketplace (`.claude-plugin/marketplace.json`) or hooks -in settings.json. **OpenCode/Pi** → in-process extension. **Gemini CLI** → +**Claude Code** → plugin marketplace (`.claude-plugin/marketplace.json`); the +plugin auto-wires its hooks via `adapters/claude-code/hooks/hooks.json` and its +statusline via the `statusLine` field in `plugin.json` (both +`${CLAUDE_PLUGIN_ROOT}`-relative). Or wire the same hooks + statusline manually +in settings.json (printed by `campy install claude-code`). **OpenCode/Pi** → in-process extension. **Gemini CLI** → `~/.gemini/extensions/campy/` + `gemini-extension.json`. **Codex CLI** → `[mcp_servers.campy]` in `~/.codex/config.toml`. **Cursor CLI** → `~/.cursor/mcp.json`. **Aider** → `.git/hooks/post-commit`. For agents without a widget surface, @@ -102,7 +108,7 @@ campy/ │ ├── mcp/server.ts # MCP stdio server: file-watch events + campy_* tools │ ├── gemini/ # gemini-extension.json + GEMINI.md (git-install template) │ └── claude-code/ # Claude Code: hooks + statusline + slash commands -│ ├── hooks/ # session-start.sh, pre-tool-use.sh, post-tool-use.sh, stop.sh +│ ├── hooks/ # hooks.json + session-start.sh, post-tool-use.sh, stop.sh │ ├── commands/ # campy.md, feed.md, play.md, pet.md, switch.md │ ├── .claude-plugin/ # plugin.json │ └── statusline.sh # thin wrapper around `campy statusline` diff --git a/adapters/claude-code/.claude-plugin/plugin.json b/adapters/claude-code/.claude-plugin/plugin.json index cd15e69..ed853b1 100644 --- a/adapters/claude-code/.claude-plugin/plugin.json +++ b/adapters/claude-code/.claude-plugin/plugin.json @@ -1,5 +1,9 @@ { "name": "campy", "description": "Animated ASCII terminal pets for Claude Code — reacts to your coding with moods, speech bubbles, and happiness.", - "version": "1.0.0" + "version": "1.0.0", + "statusLine": { + "type": "command", + "command": "bash ${CLAUDE_PLUGIN_ROOT}/statusline.sh" + } } diff --git a/adapters/claude-code/hooks/hooks.json b/adapters/claude-code/hooks/hooks.json new file mode 100644 index 0000000..8a8c524 --- /dev/null +++ b/adapters/claude-code/hooks/hooks.json @@ -0,0 +1,26 @@ +{ + "hooks": { + "SessionStart": [ + { + "hooks": [ + { "type": "command", "command": "bash ${CLAUDE_PLUGIN_ROOT}/hooks/session-start.sh" } + ] + } + ], + "PostToolUse": [ + { + "matcher": "Edit|Write|MultiEdit|NotebookEdit|Bash", + "hooks": [ + { "type": "command", "command": "bash ${CLAUDE_PLUGIN_ROOT}/hooks/post-tool-use.sh" } + ] + } + ], + "Stop": [ + { + "hooks": [ + { "type": "command", "command": "bash ${CLAUDE_PLUGIN_ROOT}/hooks/stop.sh" } + ] + } + ] + } +} diff --git a/adapters/claude-code/hooks/pre-tool-use.sh b/adapters/claude-code/hooks/pre-tool-use.sh deleted file mode 100755 index b93ef54..0000000 --- a/adapters/claude-code/hooks/pre-tool-use.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)" - -if ! command -v bun &>/dev/null; then - exit 0 -fi - -bun "$ROOT/cli/campy.ts" event thinking diff --git a/cli/campy.ts b/cli/campy.ts index f4548b4..7234a14 100755 --- a/cli/campy.ts +++ b/cli/campy.ts @@ -122,8 +122,7 @@ const installClaudeCode = (): void => { statusLine: { type: "command", command: `bash ${statusbin}` }, hooks: { SessionStart: [{ hooks: [{ type: "command", command: `bash ${join(hooks, "session-start.sh")}` }] }], - PreToolUse: [{ matcher: "*", hooks: [{ type: "command", command: `bash ${join(hooks, "pre-tool-use.sh")}` }] }], - PostToolUse: [{ matcher: "*", hooks: [{ type: "command", command: `bash ${join(hooks, "post-tool-use.sh")}` }] }], + PostToolUse: [{ matcher: "Edit|Write|MultiEdit|NotebookEdit|Bash", hooks: [{ type: "command", command: `bash ${join(hooks, "post-tool-use.sh")}` }] }], Stop: [{ hooks: [{ type: "command", command: `bash ${join(hooks, "stop.sh")}` }] }], }, }, null, 2))