The machine now carries its own manual for AI hands
Claude-Session: https://claude.ai/code/session_01Ms2FbjQy31TVf3CEvQhGM8
This commit is contained in:
Executable
+78
@@ -0,0 +1,78 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Agent skills, shipped to every machine.
|
||||
#
|
||||
# Panama is a desktop that an agent is frequently asked to operate: open the
|
||||
# settings, check what is running, take a screenshot, run something as root. An
|
||||
# agent that has to infer all of that from the source reliably invents half of
|
||||
# it, so the machine carries its own manual and skills/ is where it lives.
|
||||
#
|
||||
# This stage is un-gated, unlike link-user. What it links is documentation of
|
||||
# this repository, not anybody's personal content, so a stranger who clones
|
||||
# Panama wants it for exactly the same reason its author does.
|
||||
#
|
||||
# ~/.claude/skills was a single symlink into user/agents/skills until now, and
|
||||
# a directory cannot be two things at once. So the destination becomes a real
|
||||
# directory and every skill -- shipped here, personal from user/ -- is linked
|
||||
# into it one at a time. link-user runs after this stage on purpose: it links
|
||||
# last, so a personal skill named like a shipped one wins, which is the
|
||||
# precedence Claude Code itself uses.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
log() { echo -e "\033[1;34m[INFO]\033[0m $*"; }
|
||||
|
||||
PANAMA_PATH="${PANAMA_PATH:-$HOME/.local/share/Panama}"
|
||||
SKILLS_DIR="$PANAMA_PATH/skills"
|
||||
PANAMA_OLD="$PANAMA_PATH/config/old"
|
||||
DESTINATION="$HOME/.claude/skills"
|
||||
|
||||
[[ -d "$SKILLS_DIR" ]] || { log "No skills/ in this checkout; nothing to link."; exit 0; }
|
||||
|
||||
mkdir -p "$PANAMA_OLD"
|
||||
|
||||
# Moves whatever is already at a destination out of the way, once -- the same
|
||||
# promise link-dotfiles and link-user make. A symlink is removed rather than
|
||||
# backed up: it is a pointer this installer made, and keeping copies of it
|
||||
# would grow config/old/ by one entry on every upgrade.
|
||||
displace() {
|
||||
local destination="$1" backup
|
||||
if [[ -L "$destination" ]]; then
|
||||
rm -f "$destination"
|
||||
return 0
|
||||
fi
|
||||
[[ -e "$destination" ]] || return 0
|
||||
|
||||
backup="$PANAMA_OLD/skills-$(printf '%s' "${destination#"$HOME"/}" | tr '/' '-')"
|
||||
if [[ -e "$backup" ]]; then
|
||||
backup="$backup.$(date +%s)"
|
||||
fi
|
||||
mv "$destination" "$backup"
|
||||
log "Moved existing $destination to $backup"
|
||||
}
|
||||
|
||||
# The destination itself has to be a real directory before anything can be
|
||||
# linked into it. An old whole-directory symlink is removed; a regular file
|
||||
# somebody left at this path is kept, in config/old/.
|
||||
mkdir -p "$(dirname "$DESTINATION")"
|
||||
if [[ -L "$DESTINATION" ]]; then
|
||||
rm -f "$DESTINATION"
|
||||
log "Removed the old $DESTINATION symlink; skills are linked one by one now"
|
||||
elif [[ -e "$DESTINATION" && ! -d "$DESTINATION" ]]; then
|
||||
displace "$DESTINATION"
|
||||
fi
|
||||
mkdir -p "$DESTINATION"
|
||||
|
||||
linked=0
|
||||
for skill in "$SKILLS_DIR"/*; do
|
||||
[[ -e "$skill" ]] || continue
|
||||
name="$(basename "$skill")"
|
||||
target="$DESTINATION/$name"
|
||||
|
||||
displace "$target"
|
||||
ln -s "$skill" "$target"
|
||||
log "Linked skills/$name → $target"
|
||||
linked=$(( linked + 1 ))
|
||||
done
|
||||
|
||||
log "Agent skills: $linked linked."
|
||||
@@ -95,6 +95,31 @@ while read -r kind source destination; do
|
||||
log "Linked $source → $dst"
|
||||
linked=$(( linked + 1 ))
|
||||
;;
|
||||
linkdir)
|
||||
# A destination that has to hold more than this one source. The
|
||||
# only one today is ~/.claude/skills, which now carries Panama's
|
||||
# own shipped skills as well as these -- so the directory itself
|
||||
# cannot be a symlink, and each child is linked into it instead.
|
||||
#
|
||||
# Whatever link-skills put there is kept; only same-named entries
|
||||
# are displaced, which is how a personal skill deliberately
|
||||
# shadows a shipped one. This stage runs last for that reason.
|
||||
if [[ -L "$dst" ]]; then
|
||||
rm -f "$dst"
|
||||
elif [[ -e "$dst" && ! -d "$dst" ]]; then
|
||||
displace "$dst"
|
||||
fi
|
||||
mkdir -p "$dst"
|
||||
|
||||
for child in "$src"/*; do
|
||||
[[ -e "$child" ]] || continue
|
||||
child_name="$(basename "$child")"
|
||||
displace "$dst/$child_name"
|
||||
ln -s "$child" "$dst/$child_name"
|
||||
log "Linked $source/$child_name → $dst/$child_name"
|
||||
linked=$(( linked + 1 ))
|
||||
done
|
||||
;;
|
||||
copy)
|
||||
if [[ -e "$dst" ]]; then
|
||||
log "Keeping existing $dst"
|
||||
|
||||
Reference in New Issue
Block a user