cto/install.sh
Svrnty 10f919746e feat(cto): v1.0 MVP — executable orchestrator + cto-worker.sh helper
skills/cto-agent/SKILL.md: bumped 0.1.0 → 1.0.0; drop "v0.1 stub" banner;
operating loop now concrete (no more "v1.0 will…"); add explicit kanban
worker contract (kanban_complete | kanban_block required at task end —
fixes the protocol-violation noise observed in CTO validation testing).
Routing table updated: Python → cto-python-toolkit, Angular →
cto-angular-toolkit (the dedicated stack skills built earlier).
Added sot/-spec frontmatter fields (tier T2, status active, owner, source,
last_reviewed) per PROFILE-DISTRIBUTION-PROTOCOL §2.1.

lib/cto-worker.sh: orchestrator helper. 3 commands:
  - sandcastle <work-id> <target> <prompt> [provider] → invoke sandcastle
    via npx tsx + claudeCode + docker (default). Blocks reads against
    read-only siblings (hermes-agent, hermes-webui, marketingskills,
    sandcastle).
  - open-pr <work-id> <target> <title> <body> → resolves github-pat via
    credbridge (never in argv), pushes branch, creates PR. Returns URL.
  - emit-5w <work-id> <status> <summary> → prints 5W block (stdout
    captured by Hermes into kanban completion).

install.sh: invokes `hermes profile install --yes --force` for dispatch
readiness; chmod +x cto-worker.sh; drops v0.1 scaffold messages; sandcastle
sibling now REQUIRED (was just a WARN). Adds matching DRY echoes.

manifest.yaml + distribution.yaml: version 0.1.0 → 1.0.0; distribution_owned
adds lib/.

README.md: status v0.1 scaffold → v1.0 MVP; layout reflects 3 skills + lib/;
roadmap table refactored (v1.0 current / v1.1 next / v2 deferred).

Verified: hermes profile install → "✓ Installed 'cto-planb' v1.0.0".

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 13:02:10 -04:00

89 lines
3.5 KiB
Bash
Executable File

#!/usr/bin/env bash
# install.sh — wire CTO profile distribution into Hermes.
# Idempotent. Creates ~/.hermes/$PROFILE_NAME symlink + registers skills in profile config.
# v0.1 scaffold: schema applied, skill registered, but cto-agent skill is a non-executable stub.
set -euo pipefail
REPO="$(cd "$(dirname "$0")" && pwd)"
PROFILE_NAME="cto-planb" # Hermes profile name (org-scoped); matches distribution.yaml → name + manifest.yaml → profile
HERMES_HOME="${HERMES_HOME:-$HOME/.hermes}"
PROFILE_CFG="$HERMES_HOME/profiles/$PROFILE_NAME/config.yaml"
# --dry-run flag for preflight without mutations
DRY_RUN=0
for arg in "$@"; do
case "$arg" in
--dry-run) DRY_RUN=1 ;;
esac
done
echo "== preflight =="
command -v hermes >/dev/null || { echo "ERROR: hermes CLI not on PATH"; exit 1; }
command -v python3 >/dev/null || { echo "ERROR: python3 not on PATH"; exit 1; }
command -v sqlite3 >/dev/null || { echo "ERROR: sqlite3 not on PATH"; exit 1; }
if [ ! -d "$HERMES_HOME" ]; then
echo "ERROR: HERMES_HOME=$HERMES_HOME does not exist"
exit 1
fi
echo " hermes ✓ python3 ✓ sqlite3 ✓ HERMES_HOME ✓"
# Check sandcastle sibling exists (CTO's primary tool)
SANDCASTLE_REPO="${SANDCASTLE_REPO:-$REPO/../sandcastle}"
if [ ! -d "$SANDCASTLE_REPO" ]; then
echo "ERROR: sandcastle sibling not found at $SANDCASTLE_REPO"
echo " CTO v1.0 requires it. Clone: git clone https://github.com/mattpocock/sandcastle.git $SANDCASTLE_REPO"
exit 1
else
echo " sandcastle ✓ ($SANDCASTLE_REPO)"
fi
if [ "$DRY_RUN" -eq 1 ]; then
echo "== DRY RUN — no mutations =="
echo " would: ln -sfn $REPO $HERMES_HOME/$PROFILE_NAME"
echo " would: append $REPO/skills to $PROFILE_CFG → skills.external_dirs"
echo " would: sqlite3 $HERMES_HOME/$PROFILE_NAME/cto.db < $REPO/schema.sql"
echo " would: hermes profile install '$REPO' --yes --force (dispatch-readiness)"
echo " would: chmod +x $REPO/lib/cto-worker.sh"
exit 0
fi
echo "== link repo → ~/.hermes/$PROFILE_NAME =="
ln -sfn "$REPO" "$HERMES_HOME/$PROFILE_NAME"
echo "== register skills in $PROFILE_NAME profile config =="
SKILL_DIR="$REPO/skills"
mkdir -p "$(dirname "$PROFILE_CFG")"
python3 - "$PROFILE_CFG" "$SKILL_DIR" <<'PY'
import sys, os, yaml
cfg, sk = sys.argv[1], sys.argv[2]
d = yaml.safe_load(open(cfg).read()) if os.path.exists(cfg) else {}
d = d or {}
d.setdefault('skills', {}).setdefault('external_dirs', [])
if sk not in d['skills']['external_dirs']:
d['skills']['external_dirs'].append(sk)
open(cfg, 'w').write(yaml.dump(d, sort_keys=False, allow_unicode=True))
print(" +", sk)
else:
print(" already registered:", sk)
PY
echo "== ${PROFILE_NAME}.db =="
sqlite3 "$HERMES_HOME/$PROFILE_NAME/cto.db" < "$REPO/schema.sql"
echo "== hermes-native profile install (dispatch-readiness) =="
if [ "$DRY_RUN" -eq 1 ]; then
echo "DRY: hermes profile install '$REPO' --yes --force"
else
hermes profile install "$REPO" --yes --force 2>&1 | tail -5 || \
echo " WARN: hermes profile install failed (legacy symlink still works)"
fi
echo "== ensure cto-worker.sh executable =="
chmod +x "$REPO/lib/cto-worker.sh" 2>/dev/null && echo " ✓ lib/cto-worker.sh executable"
echo ""
echo "== done. canonical install: hermes profile install $REPO =="
echo " verify: hermes -p $PROFILE_NAME skills list | grep cto-agent"
echo " verify assignee registered: hermes kanban assignees | grep $PROFILE_NAME"
echo " start gateway (when ready): hermes profile gateway start $PROFILE_NAME"