Publish skills through the agent-neutral skill home
This commit is contained in:
+41
-20
@@ -2,7 +2,7 @@
|
||||
|
||||
# The manual this machine hands an agent.
|
||||
#
|
||||
# skills/ and .claude/skills/panama exist because an agent asked to do anything
|
||||
# skills/ and .agents/skills/panama exist because an agent asked to do anything
|
||||
# on a Panama desktop will otherwise infer it from the source and get half of it
|
||||
# wrong. That only helps if what the skills say is true -- and a skill is worse
|
||||
# than no skill when it is stale, because an agent believes it verbatim and does
|
||||
@@ -16,7 +16,7 @@
|
||||
# That is a convention on the prose -- name things exactly, in backticks --
|
||||
# and it is how they should be written anyway.
|
||||
# 3. The delivery works: link-skills is a stage, in the right place, and the
|
||||
# personal manifest hands ~/.claude/skills over to the linkdir kind.
|
||||
# personal manifest hands both shared skill homes to the linkdir kind.
|
||||
#
|
||||
# Sections 1 and 2 report clearly and keep going when a skill is not written
|
||||
# yet, so this contract is useful while the skills are still being authored.
|
||||
@@ -32,9 +32,21 @@ manifest="$repo_dir/user/manifest"
|
||||
findings=()
|
||||
note() { findings+=("$1"); }
|
||||
|
||||
# The three, and where each is delivered from. The two under skills/ are shipped
|
||||
# to every machine; the third is project-level and needs no delivery at all.
|
||||
SKILL_DIRS=(skills/panama-desktop skills/panama-sudo .claude/skills/panama)
|
||||
# Every directory under skills/ ships to every machine. The project-level
|
||||
# panama skill stays in this repository and needs no home-directory delivery.
|
||||
SKILL_DIRS=()
|
||||
for directory in "$repo_dir"/skills/*; do
|
||||
[[ -d "$directory" ]] && SKILL_DIRS+=("${directory#"$repo_dir"/}")
|
||||
done
|
||||
SKILL_DIRS+=(.agents/skills/panama)
|
||||
|
||||
# The project skill has one agent-neutral source. Claude gets a compatibility
|
||||
# symlink, while Codex and other Agent Skills readers use .agents directly.
|
||||
[[ -L "$repo_dir/.claude/skills/panama" ]] \
|
||||
|| note '.claude/skills/panama is not a compatibility symlink to the agent-neutral source'
|
||||
[[ "$(readlink -f "$repo_dir/.claude/skills/panama" 2>/dev/null)" == \
|
||||
"$(readlink -f "$repo_dir/.agents/skills/panama" 2>/dev/null)" ]] \
|
||||
|| note '.claude and .agents resolve the project panama skill differently'
|
||||
|
||||
# ── 1. Each skill loads ─────────────────────────────────────────────────────
|
||||
|
||||
@@ -54,7 +66,7 @@ for relative in "${SKILL_DIRS[@]}"; do
|
||||
present+=("$directory")
|
||||
|
||||
# Frontmatter is the first --- delimited block, and a skill without one is
|
||||
# not a skill: Claude Code skips the directory entirely.
|
||||
# not a skill: agent loaders skip the directory entirely.
|
||||
frontmatter="$(awk 'NR==1 { if ($0 != "---") exit 1; next } $0 == "---" { exit } { print }' "$file")"
|
||||
if [[ -z "$frontmatter" ]]; then
|
||||
note "$relative/SKILL.md does not open with a --- frontmatter block"
|
||||
@@ -207,12 +219,12 @@ else
|
||||
work="$(mktemp -d)"
|
||||
trap 'rm -rf "$work"' EXIT
|
||||
|
||||
# A checkout and a home of its own. Never the real ones: ~/.claude/skills on
|
||||
# this machine is somebody's live agent setup, and a contract that broke it
|
||||
# mid-session would be worse than the bug it was looking for.
|
||||
# A checkout and a home of its own. Never the real ones: these are somebody's
|
||||
# live agent setup, and a contract that broke them mid-session would be worse
|
||||
# than the bug it was looking for.
|
||||
checkout="$work/Panama"
|
||||
home="$work/home"
|
||||
mkdir -p "$checkout/setup/scripts" "$checkout/skills/shipped" "$home/.claude"
|
||||
mkdir -p "$checkout/setup/scripts" "$checkout/skills/shipped" "$home/.claude" "$home/.agents"
|
||||
cp "$linker" "$checkout/setup/scripts/link-skills"
|
||||
printf 'a shipped skill\n' >"$checkout/skills/shipped/SKILL.md"
|
||||
|
||||
@@ -220,6 +232,7 @@ else
|
||||
# symlink, which is what link-user used to leave here.
|
||||
mkdir -p "$work/personal"
|
||||
ln -s "$work/personal" "$home/.claude/skills"
|
||||
ln -s "$work/personal" "$home/.agents/skills"
|
||||
|
||||
run() { HOME="$home" PANAMA_PATH="$checkout" "$checkout/setup/scripts/link-skills" >"$work/log" 2>&1; }
|
||||
|
||||
@@ -231,22 +244,30 @@ else
|
||||
|| note 'link-skills left ~/.claude/skills a symlink, so nothing else can be linked into it'
|
||||
[[ -L "$home/.claude/skills/shipped" ]] \
|
||||
|| note 'link-skills did not link each shipped skill as a child of ~/.claude/skills'
|
||||
[[ -d "$home/.agents/skills" && ! -L "$home/.agents/skills" ]] \
|
||||
|| note 'link-skills left ~/.agents/skills a symlink, so shipped and personal skills cannot coexist'
|
||||
[[ -L "$home/.agents/skills/shipped" ]] \
|
||||
|| note 'link-skills did not link each shipped skill as a child of ~/.agents/skills'
|
||||
grep -q 'Agent skills: 1 linked' "$work/log" \
|
||||
|| note 'link-skills does not report how many skills it linked'
|
||||
|
||||
# A real directory at a shipped skill's name is somebody's work: it moves to
|
||||
# config/old rather than being deleted, the same promise the other stages
|
||||
# make. A symlink is not, and must not accumulate there.
|
||||
rm "$home/.claude/skills/shipped"
|
||||
mkdir -p "$home/.claude/skills/shipped"
|
||||
printf 'installed by hand\n' >"$home/.claude/skills/shipped/SKILL.md"
|
||||
mkdir -p "$home/.claude/skills/untouched"
|
||||
for skill_home in "$home/.claude/skills" "$home/.agents/skills"; do
|
||||
rm -f "$skill_home/shipped"
|
||||
mkdir -p "$skill_home/shipped"
|
||||
printf 'installed by hand\n' >"$skill_home/shipped/SKILL.md"
|
||||
mkdir -p "$skill_home/untouched"
|
||||
done
|
||||
|
||||
run
|
||||
grep -rq 'installed by hand' "$checkout/config/old" 2>/dev/null \
|
||||
|| note 'link-skills destroyed a real skill instead of moving it to config/old'
|
||||
[[ -d "$home/.claude/skills/untouched" ]] \
|
||||
|| note 'link-skills removed a skill it does not ship'
|
||||
[[ "$(grep -rl 'installed by hand' "$checkout/config/old" 2>/dev/null | wc -l)" == 2 ]] \
|
||||
|| note 'link-skills did not preserve real skills from both agent homes'
|
||||
for skill_home in "$home/.claude/skills" "$home/.agents/skills"; do
|
||||
[[ -d "$skill_home/untouched" ]] \
|
||||
|| note "link-skills removed an unshipped skill from $skill_home"
|
||||
done
|
||||
|
||||
before="$(find "$checkout/config/old" | wc -l)"
|
||||
run
|
||||
@@ -287,8 +308,8 @@ fi
|
||||
|
||||
grep -qE '^\s*linkdir\s+agents/skills\s+~/\.claude/skills\s*$' "$manifest" \
|
||||
|| note 'the manifest does not use linkdir for ~/.claude/skills, so personal skills would replace the directory'
|
||||
grep -qE '^\s*link\s+agents/skills\s+~/\.agents/skills\s*$' "$manifest" \
|
||||
|| note '~/.agents/skills is no longer a whole-directory link, and nothing else claims that path'
|
||||
grep -qE '^\s*linkdir\s+agents/skills\s+~/\.agents/skills\s*$' "$manifest" \
|
||||
|| note 'the manifest does not use linkdir for ~/.agents/skills, so personal skills would replace shipped skills'
|
||||
grep -q 'linkdir)' "$user_linker" \
|
||||
|| note 'link-user does not implement the linkdir kind the manifest asks for'
|
||||
grep -q 'linkdir' "$repo_dir/user/README.md" \
|
||||
|
||||
@@ -73,7 +73,7 @@ cat >"$checkout/user/manifest" <<'FIXTURE'
|
||||
# a comment, and a blank line follow
|
||||
|
||||
link agents/AGENTS.md ~/.claude/CLAUDE.md
|
||||
link agents/skills ~/.agents/skills
|
||||
linkdir agents/skills ~/.agents/skills
|
||||
linkdir agents/skills ~/.claude/skills
|
||||
copy plain.txt ~/.config/plain.txt
|
||||
link missing.txt ~/.config/missing.txt
|
||||
@@ -102,11 +102,12 @@ PANAMA_USER_CONTENT=no run
|
||||
|
||||
# ── 2. Saying yes links, and keeps what was there ───────────────────────────
|
||||
|
||||
# ~/.claude/skills is shared now: link-skills has already made it a real
|
||||
# directory and linked Panama's own skills into it. A linkdir entry has to land
|
||||
# beside those rather than replace the directory holding them.
|
||||
mkdir -p "$home/.claude/skills"
|
||||
# Both skill homes are shared now: link-skills has already made them real
|
||||
# directories and linked Panama's own skills into each. Personal linkdir
|
||||
# entries land beside those rather than replacing either directory.
|
||||
mkdir -p "$home/.claude/skills" "$home/.agents/skills"
|
||||
ln -s "$checkout/skills/shipped" "$home/.claude/skills/shipped"
|
||||
ln -s "$checkout/skills/shipped" "$home/.agents/skills/shipped"
|
||||
|
||||
PANAMA_USER_CONTENT=yes run
|
||||
|
||||
@@ -114,8 +115,12 @@ PANAMA_USER_CONTENT=yes run
|
||||
|| note 'CLAUDE.md was not replaced with a symlink into the checkout'
|
||||
[[ "$(cat "$home/.claude/CLAUDE.md")" == "tracked instructions" ]] \
|
||||
|| note 'the CLAUDE.md link does not resolve to the tracked file'
|
||||
[[ -L "$home/.agents/skills" && -f "$home/.agents/skills/example/SKILL.md" ]] \
|
||||
|| note 'the skills directory was not linked as a directory'
|
||||
[[ -d "$home/.agents/skills" && ! -L "$home/.agents/skills" ]] \
|
||||
|| note 'a linkdir entry replaced ~/.agents/skills with a symlink'
|
||||
[[ -L "$home/.agents/skills/example" && -f "$home/.agents/skills/example/SKILL.md" ]] \
|
||||
|| note 'a linkdir entry did not publish the personal skill for agent-neutral readers'
|
||||
[[ -L "$home/.agents/skills/shipped" ]] \
|
||||
|| note 'a linkdir entry removed the shipped skill from ~/.agents/skills'
|
||||
[[ -d "$home/.claude/skills" && ! -L "$home/.claude/skills" ]] \
|
||||
|| note 'a linkdir entry replaced its destination directory with a symlink'
|
||||
[[ -L "$home/.claude/skills/example" && -f "$home/.claude/skills/example/SKILL.md" ]] \
|
||||
|
||||
Reference in New Issue
Block a user