Skip to content

Commit 110b96b

Browse files
Jammy2211claude
authored andcommitted
Add cross-organ skill installer to bin/ (relocated from admin_jammy)
Move the skill installer (install.sh) and line-count guard (check_skill_line_counts.sh) into PyAutoBrain/bin/. They are organism-wide infrastructure, not admin_jammy's own tooling, and admin_jammy is slated to leave PyAutoLabs/. - Repoint the vestigial admin_jammy discovery root to an absolute path so it self-skips once admin_jammy is gone; PYAUTO_ROOT (../..) still resolves to the PyAutoLabs root from bin/. - Add bin/README.md carrying the relocated installer docs. - Note the installer + guard in the top-level README. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 5c1530d commit 110b96b

4 files changed

Lines changed: 276 additions & 0 deletions

File tree

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,4 +96,10 @@ bin/pyauto-brain vitals # (faculty) one tick + the unified dashboard card (
9696
PyAutoBrain runs from its checkout (no pip install), resolving the sibling
9797
`pyauto-heart` and `autobuild` binaries from PATH or `~/Code/PyAutoLabs/`.
9898

99+
`bin/` also hosts the cross-organ Claude skill **installer** (`install.sh`) and
100+
line-count **guard** (`check_skill_line_counts.sh`) — organism-wide infrastructure
101+
that used to live in `admin_jammy/skills/`. Run `bash PyAutoBrain/bin/install.sh`
102+
to (re-)symlink every organ's skills/commands into `~/.claude/`. See
103+
[`bin/README.md`](bin/README.md).
104+
99105
See [`AGENTS.md`](AGENTS.md) for the full description.

bin/README.md

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# PyAutoBrain/bin/
2+
3+
Executable tooling for the PyAuto organism.
4+
5+
- **`pyauto-brain`** — the PyAutoBrain CLI (reasoning-layer entry point).
6+
- **`install.sh`** — the cross-organ Claude skill/command **installer**.
7+
- **`check_skill_line_counts.sh`** — the skill line-count **guard** (primary
8+
skill `.md` files must stay < 200 lines).
9+
10+
The installer and guard used to live in `admin_jammy/skills/`. They moved here
11+
because they are organism-wide infrastructure, not admin_jammy's own tooling —
12+
admin_jammy hosts no skills and is slated to leave `PyAutoLabs/`.
13+
14+
## install.sh
15+
16+
Auto-discovers (no hardcoded skill list) by scanning each organ repo's
17+
`skills/` dir for `*/` subdirs, then symlinks each into `~/.claude/`:
18+
19+
- **`PyAutoMind/skills/`** — registry-coupled skills (`create_issue`).
20+
- **`PyAutoBrain/skills/`** — development-workflow skills (`start_dev`,
21+
`start_dev_for_user`, `plan_branches`, `start_library`, `start_workspace`,
22+
`ship_library`, `ship_workspace`, `register_and_iterate`, `repo_cleanup`,
23+
`update_issue`).
24+
- **`PyAutoHeart/skills/`** — status / readiness / validation skills
25+
(`pyauto-status`, `pyauto-status-full`, `worktree_status`, `smoke_test`,
26+
`dep_audit`, `verify_install`, `review_release`, `audit_docs`, `cli_noise_clean`).
27+
- **`PyAutoBuild/skills/`** — release-execution skills **only** (`pre_build`).
28+
Build owns no dev-workflow skills; `ship_*` (Brain) only *call* its release step.
29+
- **`autolens_profiling/skills/`** — science-profiling skills (`profile_likelihood`).
30+
- **`admin_jammy/skills/`** — vestigial: hosts no skills; scanned only so an old
31+
checkout still resolves, auto-skipped once admin_jammy leaves.
32+
33+
Discovery rule per root:
34+
35+
- A directory with `SKILL.md`**skill**, symlinked to `~/.claude/skills/<name>/`.
36+
- A directory with `<dirname>.md` (and no `SKILL.md`) → **command**, symlinked to
37+
`~/.claude/commands/<name>.md`.
38+
39+
Roots that aren't checked out are skipped. It is idempotent — existing symlinks
40+
are replaced, non-symlink files are left alone, and broken symlinks pointing into
41+
a PyAuto root are pruned. Re-run it after pulling updates.
42+
43+
## Bootstrap on a new machine
44+
45+
```bash
46+
cd ~/Code/PyAutoLabs
47+
git clone git@github.com:PyAutoLabs/PyAutoBrain.git
48+
git clone git@github.com:PyAutoLabs/PyAutoMind.git
49+
git clone git@github.com:PyAutoLabs/PyAutoHeart.git
50+
bash PyAutoBrain/bin/install.sh
51+
```
52+
53+
## Execution environments
54+
55+
There is **no special "mobile" or "phone" mode**. Skills run the same logic in
56+
any environment and only differ in where they read state from: a `local-dev`
57+
checkout uses local git + task worktrees; a `web-github` / `ci-only` session uses
58+
the clones present in the working directory (and the GitHub API for branch
59+
state). The full environment model is in `PyAutoBrain/skills/WORKFLOW.md`.

bin/check_skill_line_counts.sh

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
#!/usr/bin/env bash
2+
# Report any *primary* skill .md file over the line limit (default 200).
3+
#
4+
# Claude skill guidance keeps primary skill files short; long background,
5+
# templates and examples belong in supporting docs (`reference.md`, shared
6+
# `WORKFLOW.md`), which are exempt here. A "primary" file is a skill's `SKILL.md`
7+
# or its command body `<dirname>.md`.
8+
#
9+
# Scans the same discovery roots as install.sh. Exit 0 = all within limit,
10+
# exit 1 = at least one primary file is over the limit.
11+
#
12+
# Usage:
13+
# bash PyAutoBrain/bin/check_skill_line_counts.sh [limit]
14+
15+
set -euo pipefail
16+
17+
LIMIT="${1:-200}"
18+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
19+
PYAUTO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
20+
21+
ROOTS=(
22+
"$PYAUTO_ROOT/admin_jammy/skills" # vestigial (hosts no skills)
23+
"$PYAUTO_ROOT/PyAutoMind/skills"
24+
"$PYAUTO_ROOT/PyAutoBrain/skills"
25+
"$PYAUTO_ROOT/PyAutoHeart/skills"
26+
"$PYAUTO_ROOT/PyAutoBuild/skills"
27+
"$PYAUTO_ROOT/autolens_profiling/skills"
28+
)
29+
30+
over=0
31+
checked=0
32+
33+
for root in "${ROOTS[@]}"; do
34+
[ -d "$root" ] || continue
35+
for dir in "$root"/*/; do
36+
[ -d "$dir" ] || continue
37+
name="$(basename "$dir")"
38+
# Primary files: SKILL.md (dispatcher/frontmatter) and the command/body
39+
# <dirname>.md. Supporting docs (reference.md, examples, etc.) are exempt.
40+
for primary in "$dir/SKILL.md" "$dir/$name.md"; do
41+
[ -f "$primary" ] || continue
42+
checked=$((checked + 1))
43+
lines=$(wc -l < "$primary")
44+
if [ "$lines" -gt "$LIMIT" ]; then
45+
echo "OVER ${lines} ${primary#$PYAUTO_ROOT/}"
46+
over=$((over + 1))
47+
fi
48+
done
49+
done
50+
done
51+
52+
echo ""
53+
if [ "$over" -eq 0 ]; then
54+
echo "OK: all $checked primary skill files are within $LIMIT lines."
55+
exit 0
56+
else
57+
echo "FAIL: $over primary skill file(s) exceed $LIMIT lines — factor detail into reference.md."
58+
exit 1
59+
fi

bin/install.sh

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
#!/usr/bin/env bash
2+
# Bootstrap Claude Code skills/commands into ~/.claude/ from every PyAuto
3+
# organism repo that hosts skills.
4+
#
5+
# This installer lives in PyAutoBrain (the reasoning/orchestration organ). It
6+
# scans every organ repo's skills/ dir and symlinks their skills+commands into
7+
# ~/.claude/. Roots that aren't checked out are simply skipped.
8+
#
9+
# Discovery roots (scanned in order):
10+
# - admin_jammy/skills/ — vestigial: admin_jammy hosts no skills and is
11+
# slated to leave PyAutoLabs/; kept only so an old
12+
# checkout still resolves, auto-skipped once gone
13+
# - PyAutoMind/skills/ — registry-coupled skills (create_issue, handoff)
14+
# - PyAutoBrain/skills/ — development-workflow skills (start_*/ship_*/plan_branches/…)
15+
# - PyAutoHeart/skills/ — status / readiness / diagnostic skills
16+
# - PyAutoBuild/skills/ — release-execution skills ONLY (pre_build)
17+
# - autolens_profiling/skills/ — science-profiling skills (profile_likelihood)
18+
#
19+
# (PyAutoBuild's root is for its own release/packaging-execution skills only — it
20+
# owns NO dev-workflow skills; the ship_* skills merely call its release step.)
21+
#
22+
# Auto-discovers skills in each root:
23+
# - A directory with SKILL.md → installed as ~/.claude/skills/<name>/
24+
# - A directory with <name>.md (and no SKILL.md) → installed as a flat
25+
# command at ~/.claude/commands/<name>.md
26+
#
27+
# Safe to re-run — existing symlinks are replaced, non-symlink files are skipped.
28+
#
29+
# Usage:
30+
# bash PyAutoBrain/bin/install.sh
31+
32+
set -euo pipefail
33+
34+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
35+
PYAUTO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
36+
ADMIN_SKILLS_DIR="$PYAUTO_ROOT/admin_jammy/skills"
37+
MIND_SKILLS_DIR="$PYAUTO_ROOT/PyAutoMind/skills"
38+
BRAIN_SKILLS_DIR="$PYAUTO_ROOT/PyAutoBrain/skills"
39+
HEART_SKILLS_DIR="$PYAUTO_ROOT/PyAutoHeart/skills"
40+
BUILD_SKILLS_DIR="$PYAUTO_ROOT/PyAutoBuild/skills"
41+
PROFILING_SKILLS_DIR="$PYAUTO_ROOT/autolens_profiling/skills"
42+
43+
# ---------- Execution-environment note ----------
44+
#
45+
# Skill discovery is identical in every execution environment (local-dev,
46+
# web-github, ci-only). When a root repo is not checked out, it is simply
47+
# skipped — the skills from the roots that ARE present still install.
48+
49+
if [ -d "$PYAUTO_ROOT/PyAutoFit" ] || [ -d "$HOME/Code/PyAutoLabs/PyAutoFit" ]; then
50+
echo "Environment: local-dev (PyAuto repos detected)"
51+
else
52+
echo "Environment: web-github / ci-only (clone roots on demand)"
53+
fi
54+
echo ""
55+
56+
mkdir -p "$HOME/.claude/skills" "$HOME/.claude/commands"
57+
58+
# ---------- Prune stale symlinks ----------
59+
#
60+
# Self-healing: when a skill is removed or re-homed, its old ~/.claude symlink
61+
# would otherwise dangle. Remove only BROKEN symlinks whose target points into a
62+
# PyAuto root — never touch real files or symlinks that resolve, and never touch
63+
# links pointing outside the managed roots (e.g. user-added skills).
64+
65+
prune_stale_symlinks() {
66+
local dir="$1"
67+
[ -d "$dir" ] || return 0
68+
for link in "$dir"/*; do
69+
[ -L "$link" ] || continue
70+
[ -e "$link" ] && continue # resolves fine — keep
71+
local tgt; tgt="$(readlink "$link")"
72+
case "$tgt" in
73+
"$PYAUTO_ROOT"/*|"$HOME/Code/PyAutoLabs"/*)
74+
echo " PRUNE $(basename "$link") (stale → $tgt)"; rm -f "$link" ;;
75+
esac
76+
done
77+
}
78+
79+
prune_stale_symlinks "$HOME/.claude/skills"
80+
prune_stale_symlinks "$HOME/.claude/commands"
81+
82+
# ---------- Install one source dir's skills/commands ----------
83+
84+
install_from_dir() {
85+
local source_dir="$1"
86+
local label="$2"
87+
88+
if [ ! -d "$source_dir" ]; then
89+
echo "${label}: (source dir not present, skipping)"
90+
return
91+
fi
92+
93+
echo "$label"
94+
local installed_count=0
95+
96+
for entry in "$source_dir"/*/; do
97+
[ -d "$entry" ] || continue
98+
local name
99+
name=$(basename "$entry")
100+
101+
# Skip the install.sh dir itself, or any non-skill dirs
102+
[ "$name" = "skills" ] && continue
103+
104+
if [ -f "$entry/SKILL.md" ]; then
105+
# Skill — symlink the directory
106+
local dst="$HOME/.claude/skills/$name"
107+
_link_symlink "$entry" "$dst" "skill"
108+
installed_count=$((installed_count + 1))
109+
elif [ -f "$entry/$name.md" ]; then
110+
# Command — symlink the flat .md file
111+
local dst="$HOME/.claude/commands/$name.md"
112+
_link_symlink "$entry/$name.md" "$dst" "command"
113+
installed_count=$((installed_count + 1))
114+
else
115+
echo " SKIP $name (no SKILL.md or $name.md found)"
116+
fi
117+
done
118+
119+
echo " ($installed_count installed from $label)"
120+
echo ""
121+
}
122+
123+
_link_symlink() {
124+
local src="$1"
125+
local dst="$2"
126+
local kind="$3"
127+
local name
128+
name=$(basename "$dst")
129+
130+
if [ -L "$dst" ]; then
131+
rm "$dst"
132+
elif [ -e "$dst" ]; then
133+
echo " SKIP $name ($kind — non-symlink exists at $dst)"
134+
return
135+
fi
136+
137+
ln -s "$src" "$dst"
138+
echo " LINK $name ($kind)"
139+
}
140+
141+
# ---------- Run installs ----------
142+
143+
install_from_dir "$ADMIN_SKILLS_DIR" "admin_jammy/skills/ — vestigial (hosts no skills)"
144+
install_from_dir "$MIND_SKILLS_DIR" "PyAutoMind/skills/ — registry-coupled skills (Mind)"
145+
install_from_dir "$BRAIN_SKILLS_DIR" "PyAutoBrain/skills/ — development-workflow skills (Brain)"
146+
install_from_dir "$HEART_SKILLS_DIR" "PyAutoHeart/skills/ — status / readiness skills (Heart)"
147+
install_from_dir "$BUILD_SKILLS_DIR" "PyAutoBuild/skills/ — release-execution skills (Hands)"
148+
install_from_dir "$PROFILING_SKILLS_DIR" "autolens_profiling/skills/ — science-profiling skills"
149+
150+
# ---------- Summary ----------
151+
152+
echo "Done. Restart Claude Code to pick up changes."

0 commit comments

Comments
 (0)