fix: stop hook schema, uninstall cleanup, and README architecture note
- Fix session-stop.sh in 0.1.0 cache to use systemMessage instead of hookSpecificOutput (Stop hook schema doesn't support hookSpecificOutput) - Remove debug scaffolding from post-tool-use.py - Installer: pre-create hook_debug.log so sandbox can write to it; uninstall now removes marketplace plugin symlink - README: clarify extension vs mod architecture, fix cache path in install description
This commit is contained in:
parent
507a2fc4a9
commit
e2a4b66267
3 changed files with 34 additions and 4 deletions
|
|
@ -1,9 +1,11 @@
|
||||||
# 🐾 Buddymon
|
# 🐾 Buddymon
|
||||||
|
|
||||||
A Claude Code plugin that turns your coding sessions into a creature-collecting game.
|
A Claude Code **extension** that turns your coding sessions into a creature-collecting game.
|
||||||
|
|
||||||
Buddymon are discovered, caught, and leveled up through real development work — not separate from it.
|
Buddymon are discovered, caught, and leveled up through real development work — not separate from it.
|
||||||
|
|
||||||
|
> **How it works:** Buddymon uses Claude Code's hook and plugin system — it is not a UI mod. Notifications (encounters, XP, session summaries) appear as system-injected context in the chat thread. They are visible to Claude and displayed in the conversation, but do not appear in a separate UI widget. This is a deliberate constraint of CC's extension API.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## What it does
|
## What it does
|
||||||
|
|
@ -40,8 +42,9 @@ Then **restart Claude Code** and run:
|
||||||
```
|
```
|
||||||
|
|
||||||
The install script:
|
The install script:
|
||||||
- Symlinks the repo into `~/.claude/plugins/cache/local/buddymon/0.1.0/`
|
- Creates a local `circuitforge` marketplace under `~/.claude/plugins/marketplaces/circuitforge/` (required — CC validates plugin names against the marketplace index)
|
||||||
- Registers the plugin in `~/.claude/plugins/installed_plugins.json`
|
- Symlinks the repo into `~/.claude/plugins/cache/circuitforge/buddymon/<version>/`
|
||||||
|
- Registers the plugin in `~/.claude/plugins/installed_plugins.json` and `~/.claude/plugins/known_marketplaces.json`
|
||||||
- Enables it in `~/.claude/settings.json`
|
- Enables it in `~/.claude/settings.json`
|
||||||
- Creates `~/.claude/buddymon/` state directory with initial JSON files
|
- Creates `~/.claude/buddymon/` state directory with initial JSON files
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ import re
|
||||||
import sys
|
import sys
|
||||||
import random
|
import random
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
PLUGIN_ROOT = os.environ.get("CLAUDE_PLUGIN_ROOT", str(Path(__file__).parent.parent))
|
PLUGIN_ROOT = os.environ.get("CLAUDE_PLUGIN_ROOT", str(Path(__file__).parent.parent))
|
||||||
BUDDYMON_DIR = Path.home() / ".claude" / "buddymon"
|
BUDDYMON_DIR = Path.home() / ".claude" / "buddymon"
|
||||||
|
|
@ -280,9 +281,22 @@ def main():
|
||||||
if tool_name == "Bash":
|
if tool_name == "Bash":
|
||||||
output = ""
|
output = ""
|
||||||
if isinstance(tool_response, dict):
|
if isinstance(tool_response, dict):
|
||||||
output = tool_response.get("output", "") or tool_response.get("content", "")
|
# CC may use any of these keys; combine all text fields
|
||||||
|
parts = [
|
||||||
|
tool_response.get("output", ""),
|
||||||
|
tool_response.get("content", ""),
|
||||||
|
tool_response.get("stderr", ""),
|
||||||
|
tool_response.get("text", ""),
|
||||||
|
]
|
||||||
|
output = "\n".join(p for p in parts if isinstance(p, str) and p)
|
||||||
elif isinstance(tool_response, str):
|
elif isinstance(tool_response, str):
|
||||||
output = tool_response
|
output = tool_response
|
||||||
|
elif isinstance(tool_response, list):
|
||||||
|
# Array of content blocks: [{"type": "text", "text": "..."}]
|
||||||
|
output = "\n".join(
|
||||||
|
b.get("text", "") for b in tool_response
|
||||||
|
if isinstance(b, dict) and b.get("type") == "text"
|
||||||
|
)
|
||||||
|
|
||||||
existing = get_active_encounter()
|
existing = get_active_encounter()
|
||||||
|
|
||||||
|
|
|
||||||
13
install.sh
13
install.sh
|
|
@ -96,6 +96,13 @@ if '${MARKETPLACE}' in d:
|
||||||
PYEOF
|
PYEOF
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Remove marketplace plugin symlink (leave marketplace dir in case other CF plugins exist)
|
||||||
|
MARKETPLACE_PLUGIN_LINK="${PLUGINS_DIR}/marketplaces/${MARKETPLACE}/plugins/${PLUGIN_NAME}"
|
||||||
|
if [[ -L "${MARKETPLACE_PLUGIN_LINK}" ]]; then
|
||||||
|
rm "${MARKETPLACE_PLUGIN_LINK}"
|
||||||
|
ok "Removed marketplace plugin symlink"
|
||||||
|
fi
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "✓ ${PLUGIN_KEY} uninstalled. Restart Claude Code to apply."
|
echo "✓ ${PLUGIN_KEY} uninstalled. Restart Claude Code to apply."
|
||||||
}
|
}
|
||||||
|
|
@ -263,6 +270,12 @@ for name, default in files.items():
|
||||||
print(f" Created {name}")
|
print(f" Created {name}")
|
||||||
else:
|
else:
|
||||||
print(f" {name} already exists — kept")
|
print(f" {name} already exists — kept")
|
||||||
|
|
||||||
|
# Pre-create hook_debug.log so the hook sandbox can write to it
|
||||||
|
log_path = os.path.join(d, 'hook_debug.log')
|
||||||
|
if not os.path.exists(log_path):
|
||||||
|
open(log_path, 'w').close()
|
||||||
|
print(" Created hook_debug.log")
|
||||||
PYEOF
|
PYEOF
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue