From 544ec48625d108363bc04e39d3d894e47a5bb198 Mon Sep 17 00:00:00 2001 From: tegwick Date: Mon, 16 Mar 2026 00:33:15 +0100 Subject: [PATCH] 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 --- install.sh | 65 ++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 46 insertions(+), 19 deletions(-) diff --git a/install.sh b/install.sh index 067f44a..9a2dcfe 100755 --- a/install.sh +++ b/install.sh @@ -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 [--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 [--max-iterations N]" echo "" echo " Restart Claude Code for the skill to appear."