e49d9285c4
The skill directory at ~/.agents/skills/opencode-mempalace-bridge/ is a real dir containing a single SKILL.md symlink back into this repo — the 'colocated skill' pattern. Sibling reconcilers (skillset's deploy-skills.sh, cli_utils's agents-sync.zsh) already handle external dirs correctly via their existing 'leave real dirs alone' policies, but a machine-readable marker makes ownership explicit: # skill-source: mempalace-toolkit # repo: <absolute path> # url: ssh://git@gitea.jordbo.se:2222/joakimp/mempalace-toolkit.git The marker is the convention for any external repo that wants to ship a colocated skill. The name is generic (.skill-source, not .managed-by-mempalace-toolkit) so a second colocated skill from a different repo can reuse the same file name; the first line identifies the owner. --uninstall now also removes the marker (only if it still says mempalace-toolkit) and the now-empty skill dir. AGENTS.md + README.md describe the pattern and point at sibling docs in cli_utils/AGENTS-SYNC.md and skillset/README.md that mirror the convention.
220 lines
7.1 KiB
Bash
220 lines
7.1 KiB
Bash
#!/usr/bin/env bash
|
|
# install.sh — install mempalace-toolkit executables + companion agent skill
|
|
#
|
|
# Idempotent. Safe to re-run after container recreate.
|
|
|
|
set -euo pipefail
|
|
|
|
# ── locate self ──────────────────────────────────────
|
|
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
|
|
|
# ── targets ──────────────────────────────────────────
|
|
BIN_SRC="${SCRIPT_DIR}/bin"
|
|
BIN_DEST="${HOME}/.local/bin"
|
|
|
|
SKILL_SRC="${SCRIPT_DIR}/SKILL.md"
|
|
SKILL_DEST_DIR="${HOME}/.agents/skills/opencode-mempalace-bridge"
|
|
SKILL_DEST="${SKILL_DEST_DIR}/SKILL.md"
|
|
|
|
# ── args ─────────────────────────────────────────────
|
|
ACTION="install"
|
|
ASSUME_YES="no"
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--uninstall) ACTION="uninstall"; shift ;;
|
|
-y|--yes) ASSUME_YES="yes"; shift ;;
|
|
-h|--help)
|
|
cat <<EOF
|
|
install.sh — install mempalace-toolkit
|
|
|
|
Usage:
|
|
./install.sh # install (interactive confirm)
|
|
./install.sh --yes # install without prompt
|
|
./install.sh --uninstall # remove symlinks
|
|
|
|
What install does:
|
|
- Symlinks each executable in bin/ into ~/.local/bin/
|
|
- Symlinks SKILL.md into ~/.agents/skills/opencode-mempalace-bridge/SKILL.md
|
|
(auto-discovered by opencode; run agents-sync from cli_utils to also
|
|
reach Claude Code and Kiro)
|
|
- Drops a .skill-source marker in the skill dir so sibling tooling
|
|
(deploy-skills.sh, agents-sync.zsh) knows the dir is externally owned
|
|
|
|
What uninstall does:
|
|
- Removes symlinks in ~/.local/bin/ that point into this repo
|
|
- Removes the skill symlink if it points into this repo
|
|
- Removes the .skill-source marker and empty skill dir
|
|
EOF
|
|
exit 0 ;;
|
|
*) echo "Unknown flag: $1" >&2; exit 2 ;;
|
|
esac
|
|
done
|
|
|
|
# ── helpers ──────────────────────────────────────────
|
|
ok() { printf ' \e[32m✓\e[0m %s\n' "$*"; }
|
|
note() { printf '==> %s\n' "$*"; }
|
|
warn() { printf ' \e[33m!\e[0m %s\n' "$*" >&2; }
|
|
err() { printf ' \e[31m✗\e[0m %s\n' "$*" >&2; }
|
|
|
|
confirm() {
|
|
[[ "$ASSUME_YES" == "yes" ]] && return 0
|
|
read -r -p "Proceed? [y/N] " ans
|
|
[[ "$ans" =~ ^[Yy]$ ]]
|
|
}
|
|
|
|
link_if_into_repo() {
|
|
# Return 0 if $1 is a symlink pointing into $SCRIPT_DIR
|
|
local target
|
|
[[ -L "$1" ]] || return 1
|
|
target=$(readlink -f "$1")
|
|
[[ "$target" == "$SCRIPT_DIR"/* ]]
|
|
}
|
|
|
|
# ── install ──────────────────────────────────────────
|
|
install_bin() {
|
|
mkdir -p "$BIN_DEST"
|
|
note "Symlinking bin/ executables into $BIN_DEST"
|
|
local count=0
|
|
for src in "$BIN_SRC"/*; do
|
|
[[ -x "$src" && -f "$src" ]] || continue
|
|
local name; name=$(basename "$src")
|
|
local dest="$BIN_DEST/$name"
|
|
if [[ -e "$dest" || -L "$dest" ]]; then
|
|
if link_if_into_repo "$dest"; then
|
|
ok "Already linked: $name"
|
|
count=$((count+1))
|
|
continue
|
|
else
|
|
warn "Skipping $name: $dest exists and is not our symlink"
|
|
continue
|
|
fi
|
|
fi
|
|
ln -s "$src" "$dest"
|
|
ok "Linked $name → $src"
|
|
count=$((count+1))
|
|
done
|
|
echo
|
|
ok "Installed $count executable(s)"
|
|
}
|
|
|
|
install_skill() {
|
|
note "Linking companion agent skill"
|
|
mkdir -p "$SKILL_DEST_DIR"
|
|
|
|
# Drop a marker file so sibling tooling (deploy-skills.sh, agents-sync.zsh,
|
|
# and any future reconciler) can tell at a glance that this skill directory
|
|
# is owned by an external repo and shouldn't be clobbered. The convention
|
|
# is two lines: "# skill-source: <repo-name>" + "# url: <clone-url>".
|
|
# Any colocated skill from any repo can adopt the same convention.
|
|
local marker="$SKILL_DEST_DIR/.skill-source"
|
|
if [[ ! -e "$marker" ]]; then
|
|
cat > "$marker" <<EOF
|
|
# skill-source: mempalace-toolkit
|
|
# repo: $SCRIPT_DIR
|
|
# url: ssh://git@gitea.jordbo.se:2222/joakimp/mempalace-toolkit.git
|
|
# This skill directory is managed by an external repo's install.sh.
|
|
# deploy-skills.sh (skillset) and agents-sync.zsh (cli_utils) leave
|
|
# directories containing this marker alone. Do not move SKILL.md out
|
|
# of this directory without also updating the owning repo.
|
|
EOF
|
|
ok "Wrote $marker"
|
|
fi
|
|
|
|
if [[ -e "$SKILL_DEST" || -L "$SKILL_DEST" ]]; then
|
|
if link_if_into_repo "$SKILL_DEST"; then
|
|
ok "Skill already linked"
|
|
return 0
|
|
else
|
|
warn "Skipping skill: $SKILL_DEST exists and is not our symlink"
|
|
return 0
|
|
fi
|
|
fi
|
|
ln -s "$SKILL_SRC" "$SKILL_DEST"
|
|
ok "Linked SKILL.md → $SKILL_SRC"
|
|
}
|
|
|
|
check_path() {
|
|
case ":$PATH:" in
|
|
*":$BIN_DEST:"*) : ;;
|
|
*) warn "$BIN_DEST is not on \$PATH. Add to your shell rc:";
|
|
printf ' export PATH="%s:$PATH"\n' "\$HOME/.local/bin" ;;
|
|
esac
|
|
}
|
|
|
|
do_install() {
|
|
echo
|
|
echo "mempalace-toolkit installer"
|
|
echo "Repository: $SCRIPT_DIR"
|
|
echo
|
|
echo "==> Installation plan:"
|
|
echo " Symlink executables in bin/ into $BIN_DEST"
|
|
echo " Symlink SKILL.md into $SKILL_DEST"
|
|
echo
|
|
confirm || { echo "Aborted."; exit 0; }
|
|
echo
|
|
install_bin
|
|
echo
|
|
install_skill
|
|
echo
|
|
check_path
|
|
echo
|
|
ok "Done."
|
|
echo
|
|
echo "Next: ./bin/mempalace-session --dry-run"
|
|
echo " or: ./bin/mempalace-docs /path/to/project --dry-run"
|
|
}
|
|
|
|
# ── uninstall ────────────────────────────────────────
|
|
do_uninstall() {
|
|
echo
|
|
echo "mempalace-toolkit uninstaller"
|
|
echo "Repository: $SCRIPT_DIR"
|
|
echo
|
|
confirm || { echo "Aborted."; exit 0; }
|
|
echo
|
|
|
|
note "Removing executable symlinks from $BIN_DEST"
|
|
local removed=0
|
|
for src in "$BIN_SRC"/*; do
|
|
[[ -x "$src" && -f "$src" ]] || continue
|
|
local name; name=$(basename "$src")
|
|
local dest="$BIN_DEST/$name"
|
|
if link_if_into_repo "$dest"; then
|
|
rm "$dest"
|
|
ok "Removed $name"
|
|
removed=$((removed+1))
|
|
fi
|
|
done
|
|
ok "Removed $removed executable symlink(s)"
|
|
|
|
echo
|
|
note "Removing skill symlink"
|
|
if link_if_into_repo "$SKILL_DEST"; then
|
|
rm "$SKILL_DEST"
|
|
ok "Removed skill symlink"
|
|
else
|
|
ok "No skill symlink to remove"
|
|
fi
|
|
|
|
# Remove the marker and the now-empty skill directory, but only if
|
|
# the marker was written by us and the directory has nothing else in it.
|
|
local marker="$SKILL_DEST_DIR/.skill-source"
|
|
if [[ -f "$marker" ]] && grep -q '^# skill-source: mempalace-toolkit$' "$marker" 2>/dev/null; then
|
|
rm "$marker"
|
|
ok "Removed $marker"
|
|
fi
|
|
if [[ -d "$SKILL_DEST_DIR" ]] && [[ -z "$(ls -A "$SKILL_DEST_DIR" 2>/dev/null)" ]]; then
|
|
rmdir "$SKILL_DEST_DIR"
|
|
ok "Removed empty $SKILL_DEST_DIR"
|
|
fi
|
|
|
|
echo
|
|
ok "Done."
|
|
}
|
|
|
|
case "$ACTION" in
|
|
install) do_install ;;
|
|
uninstall) do_uninstall ;;
|
|
esac
|