fix(install): register plugin in installed_plugins.json with commands/ layout

Claude Code requires skill files in commands/ subdirectory and a registration
entry in installed_plugins.json. The previous install.sh just copied files to
a bare directory which Claude Code never discovered.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-16 00:33:15 +01:00
parent f3174cebc9
commit 544ec48625

View File

@@ -1,5 +1,5 @@
#!/bin/bash
# install.sh — install the ralph-workplan skill into ~/.claude/plugins/
# install.sh — install the ralph-workplan skill into Claude Code's plugin system
#
# Usage:
# ./install.sh # install
@@ -8,32 +8,59 @@
set -euo pipefail
PLUGIN_NAME="ralph-workplan"
PLUGIN_SRC="$(cd "$(dirname "$0")/plugin" && pwd)"
PLUGIN_DEST="${HOME}/.claude/plugins/${PLUGIN_NAME}"
REPO_DIR="$(cd "$(dirname "$0")" && pwd)"
PLUGINS_DIR="${HOME}/.claude/plugins"
INSTALL_PATH="${PLUGINS_DIR}/cache/local/${PLUGIN_NAME}"
REGISTRY="${PLUGINS_DIR}/installed_plugins.json"
# ── Uninstall ──────────────────────────────────────────────────────────────────
if [[ "${1:-}" == "--uninstall" ]]; then
if [[ -d "$PLUGIN_DEST" ]]; then
rm -rf "$PLUGIN_DEST"
echo "✅ Uninstalled: $PLUGIN_DEST"
else
echo " Not installed (nothing to remove)"
if [[ -d "$INSTALL_PATH" ]]; then
rm -rf "$INSTALL_PATH"
echo "Removed: $INSTALL_PATH"
fi
# Remove from installed_plugins.json
if [[ -f "$REGISTRY" ]]; then
python3 - "$REGISTRY" "$PLUGIN_NAME" <<'PY'
import json, sys
path, name = sys.argv[1], sys.argv[2]
d = json.load(open(path))
d.setdefault("plugins", {}).pop(name, None)
json.dump(d, open(path, "w"), indent=2)
print(f"Removed '{name}' from {path}")
PY
fi
echo "✅ Uninstalled: $PLUGIN_NAME"
echo " Restart Claude Code to apply."
exit 0
fi
# Install
mkdir -p "${PLUGIN_DEST}/scripts"
# ── Install ────────────────────────────────────────────────────────────────────
mkdir -p "${INSTALL_PATH}/commands"
mkdir -p "${INSTALL_PATH}/scripts"
cp "${PLUGIN_SRC}/ralph-workplan.md" "${PLUGIN_DEST}/ralph-workplan.md"
cp "${PLUGIN_SRC}/scripts/check-done.sh" "${PLUGIN_DEST}/scripts/check-done.sh"
cp "${PLUGIN_SRC}/scripts/setup.sh" "${PLUGIN_DEST}/scripts/setup.sh"
# Skill file goes in commands/ — that's where Claude Code looks
cp "${REPO_DIR}/plugin/ralph-workplan.md" "${INSTALL_PATH}/commands/ralph-workplan.md"
cp "${REPO_DIR}/plugin/scripts/check-done.sh" "${INSTALL_PATH}/scripts/check-done.sh"
cp "${REPO_DIR}/plugin/scripts/setup.sh" "${INSTALL_PATH}/scripts/setup.sh"
chmod +x "${PLUGIN_DEST}/scripts/check-done.sh"
chmod +x "${PLUGIN_DEST}/scripts/setup.sh"
chmod +x "${INSTALL_PATH}/scripts/check-done.sh"
chmod +x "${INSTALL_PATH}/scripts/setup.sh"
echo "✅ Installed: $PLUGIN_DEST"
echo ""
echo " Skill: /ralph-workplan <workplan-file> [--max-iterations N]"
echo " Spec: $(cd "$(dirname "$0")" && pwd)/workplan-spec.md"
# Register in installed_plugins.json so Claude Code discovers the plugin
python3 - "$REGISTRY" "$PLUGIN_NAME" "$INSTALL_PATH" <<'PY'
import json, sys
path, name, install_path = sys.argv[1], sys.argv[2], sys.argv[3]
try:
d = json.load(open(path))
except (FileNotFoundError, json.JSONDecodeError):
d = {"version": 2, "plugins": {}}
d.setdefault("plugins", {})[name] = [{"scope": "user", "installPath": install_path}]
json.dump(d, open(path, "w"), indent=2)
PY
echo "✅ Installed: $PLUGIN_NAME"
echo " Path: $INSTALL_PATH"
echo " Skill: /ralph-workplan <workplan-file> [--max-iterations N]"
echo ""
echo " Restart Claude Code for the skill to appear."