diff --git a/README.md b/README.md index 5e7d3a2..35dc732 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,36 @@ bash ~/src/mosaic-bootstrap/install.sh - Shared standards document: `~/.mosaic/STANDARDS.md` - Runtime adapter docs: `~/.mosaic/adapters/` - Shared wrapper commands: `~/.mosaic/bin/` +- Canonical skills directory: `~/.mosaic/skills` + +## Universal Skills + +The installer syncs skills from: + +- `https://git.mosaicstack.dev/mosaic/agent-skills` + +into: + +- `~/.mosaic/skills` + +Then links each skill into runtime directories: + +- `~/.claude/skills` +- `~/.codex/skills` +- `~/.config/opencode/skills` + +Manual commands: + +```bash +~/.mosaic/bin/mosaic-sync-skills +~/.mosaic/bin/mosaic-sync-skills --link-only +``` + +Opt-out during install: + +```bash +MOSAIC_SKIP_SKILLS_SYNC=1 bash ~/src/mosaic-bootstrap/install.sh +``` ## Usage diff --git a/bin/mosaic-sync-skills b/bin/mosaic-sync-skills new file mode 100755 index 0000000..10e14ff --- /dev/null +++ b/bin/mosaic-sync-skills @@ -0,0 +1,126 @@ +#!/usr/bin/env bash +set -euo pipefail + +MOSAIC_HOME="${MOSAIC_HOME:-$HOME/.mosaic}" +SKILLS_REPO_URL="${MOSAIC_SKILLS_REPO_URL:-https://git.mosaicstack.dev/mosaic/agent-skills.git}" +SKILLS_REPO_DIR="${MOSAIC_SKILLS_REPO_DIR:-$MOSAIC_HOME/sources/agent-skills}" +MOSAIC_SKILLS_DIR="$MOSAIC_HOME/skills" + +fetch=1 +link_only=0 + +usage() { + cat <&2 + usage >&2 + exit 1 + ;; + esac +done + +mkdir -p "$MOSAIC_HOME" "$MOSAIC_SKILLS_DIR" + +if [[ $fetch -eq 1 ]]; then + if [[ -d "$SKILLS_REPO_DIR/.git" ]]; then + echo "[mosaic-skills] Updating skills source: $SKILLS_REPO_DIR" + git -C "$SKILLS_REPO_DIR" pull --rebase + else + echo "[mosaic-skills] Cloning skills source to: $SKILLS_REPO_DIR" + mkdir -p "$(dirname "$SKILLS_REPO_DIR")" + git clone "$SKILLS_REPO_URL" "$SKILLS_REPO_DIR" + fi + + SOURCE_SKILLS_DIR="$SKILLS_REPO_DIR/skills" + if [[ ! -d "$SOURCE_SKILLS_DIR" ]]; then + echo "[mosaic-skills] Missing source skills dir: $SOURCE_SKILLS_DIR" >&2 + exit 1 + fi + + if command -v rsync >/dev/null 2>&1; then + rsync -a --delete "$SOURCE_SKILLS_DIR/" "$MOSAIC_SKILLS_DIR/" + else + rm -rf "$MOSAIC_SKILLS_DIR"/* + cp -R "$SOURCE_SKILLS_DIR"/* "$MOSAIC_SKILLS_DIR"/ + fi +fi + +if [[ ! -d "$MOSAIC_SKILLS_DIR" ]]; then + echo "[mosaic-skills] Canonical skills dir missing: $MOSAIC_SKILLS_DIR" >&2 + exit 1 +fi + +if [[ $link_only -eq 1 ]]; then + echo "[mosaic-skills] Canonical sync completed (link update skipped)" + exit 0 +fi + +link_targets=( + "$HOME/.claude/skills" + "$HOME/.codex/skills" + "$HOME/.config/opencode/skills" +) + +link_skill_into_target() { + local skill_path="$1" + local target_dir="$2" + + local name + name="$(basename "$skill_path")" + local link_path="$target_dir/$name" + + if [[ -L "$link_path" ]]; then + ln -sfn "$skill_path" "$link_path" + return + fi + + if [[ -e "$link_path" ]]; then + local backup="$link_path.mosaic-backup.$(date +%Y%m%d%H%M%S)" + mv "$link_path" "$backup" + echo "[mosaic-skills] Backed up existing entry: $link_path -> $backup" + fi + + ln -s "$skill_path" "$link_path" +} + +for target in "${link_targets[@]}"; do + mkdir -p "$target" + + while IFS= read -r -d '' skill; do + link_skill_into_target "$skill" "$target" + done < <(find "$MOSAIC_SKILLS_DIR" -mindepth 1 -maxdepth 1 -type d -print0) + + echo "[mosaic-skills] Linked skills into: $target" +done + +echo "[mosaic-skills] Complete" diff --git a/install.sh b/install.sh index 2b2ac53..c42dab9 100755 --- a/install.sh +++ b/install.sh @@ -17,4 +17,14 @@ chmod +x "$TARGET_DIR"/bin/* chmod +x "$TARGET_DIR"/install.sh echo "[mosaic-install] Installed framework to $TARGET_DIR" + +echo "[mosaic-install] Syncing universal skills" +if [[ "${MOSAIC_SKIP_SKILLS_SYNC:-0}" == "1" ]]; then + echo "[mosaic-install] Skipping skills sync (MOSAIC_SKIP_SKILLS_SYNC=1)" +else + if ! "$TARGET_DIR/bin/mosaic-sync-skills"; then + echo "[mosaic-install] WARNING: skills sync failed (framework install still complete)" >&2 + fi +fi + echo "[mosaic-install] Add to PATH: export PATH=\"$TARGET_DIR/bin:$PATH\""