Moves all Mosaic framework runtime files from the separate bootstrap repo into the monorepo as canonical source. The @mosaic/mosaic npm package now ships the complete framework — bin scripts, runtime configs, tools, and templates — enabling standalone installation via npm install. Structure: packages/mosaic/framework/ ├── bin/ 28 CLI scripts (mosaic, mosaic-doctor, mosaic-sync-skills, etc.) ├── runtime/ Runtime adapters (claude, codex, opencode, pi, mcp) ├── tools/ Shell tooling (git, prdy, orchestrator, quality, etc.) ├── templates/ Agent and repo templates ├── defaults/ Default identity files (AGENTS.md, STANDARDS.md, SOUL.md, etc.) ├── install.sh Legacy bash installer └── remote-install.sh One-liner remote installer Key files with Pi support and recent fixes: - bin/mosaic: launch_pi() with skills-local loop - bin/mosaic-doctor: --fix auto-wiring for all 4 harnesses - bin/mosaic-sync-skills: Pi as 4th link target, symlink-aware find - bin/mosaic-link-runtime-assets: Pi settings.json patching - bin/mosaic-migrate-local-skills: Pi skill roots, symlink find - runtime/pi/RUNTIME.md + mosaic-extension.ts Package ships 251 framework files in the npm tarball (278KB compressed).
184 lines
4.9 KiB
Bash
Executable File
184 lines
4.9 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
MOSAIC_HOME="${MOSAIC_HOME:-$HOME/.config/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"
|
|
MOSAIC_LOCAL_SKILLS_DIR="$MOSAIC_HOME/skills-local"
|
|
|
|
fetch=1
|
|
link_only=0
|
|
|
|
usage() {
|
|
cat <<USAGE
|
|
Usage: $(basename "$0") [options]
|
|
|
|
Sync canonical skills into ~/.config/mosaic/skills and link all Mosaic skills into runtime skill directories.
|
|
|
|
Options:
|
|
--link-only Skip git clone/pull and only relink from ~/.config/mosaic/{skills,skills-local}
|
|
--no-link Sync canonical skills but do not update runtime links
|
|
-h, --help Show help
|
|
|
|
Env:
|
|
MOSAIC_HOME Default: ~/.config/mosaic
|
|
MOSAIC_SKILLS_REPO_URL Default: https://git.mosaicstack.dev/mosaic/agent-skills.git
|
|
MOSAIC_SKILLS_REPO_DIR Default: ~/.config/mosaic/sources/agent-skills
|
|
USAGE
|
|
}
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--link-only)
|
|
fetch=0
|
|
shift
|
|
;;
|
|
--no-link)
|
|
link_only=1
|
|
shift
|
|
;;
|
|
-h|--help)
|
|
usage
|
|
exit 0
|
|
;;
|
|
*)
|
|
echo "Unknown argument: $1" >&2
|
|
usage >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
mkdir -p "$MOSAIC_HOME" "$MOSAIC_SKILLS_DIR" "$MOSAIC_LOCAL_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"
|
|
"$HOME/.pi/agent/skills"
|
|
)
|
|
|
|
canonical_real="$(readlink -f "$MOSAIC_SKILLS_DIR")"
|
|
|
|
link_skill_into_target() {
|
|
local skill_path="$1"
|
|
local target_dir="$2"
|
|
|
|
local name link_path
|
|
name="$(basename "$skill_path")"
|
|
|
|
# Do not distribute hidden/system skill directories globally.
|
|
if [[ "$name" == .* ]]; then
|
|
return
|
|
fi
|
|
|
|
link_path="$target_dir/$name"
|
|
|
|
if [[ -L "$link_path" ]]; then
|
|
ln -sfn "$skill_path" "$link_path"
|
|
return
|
|
fi
|
|
|
|
if [[ -e "$link_path" ]]; then
|
|
echo "[mosaic-skills] Preserve existing runtime-specific entry: $link_path"
|
|
return
|
|
fi
|
|
|
|
ln -s "$skill_path" "$link_path"
|
|
}
|
|
|
|
is_mosaic_skill_name() {
|
|
local name="$1"
|
|
# -d follows symlinks; -L catches broken symlinks that still indicate ownership
|
|
[[ -d "$MOSAIC_SKILLS_DIR/$name" || -L "$MOSAIC_SKILLS_DIR/$name" ]] && return 0
|
|
[[ -d "$MOSAIC_LOCAL_SKILLS_DIR/$name" || -L "$MOSAIC_LOCAL_SKILLS_DIR/$name" ]] && return 0
|
|
return 1
|
|
}
|
|
|
|
prune_stale_links_in_target() {
|
|
local target_dir="$1"
|
|
|
|
while IFS= read -r -d '' link_path; do
|
|
local name resolved
|
|
name="$(basename "$link_path")"
|
|
|
|
if is_mosaic_skill_name "$name"; then
|
|
continue
|
|
fi
|
|
|
|
resolved="$(readlink -f "$link_path" 2>/dev/null || true)"
|
|
if [[ -z "$resolved" ]]; then
|
|
rm -f "$link_path"
|
|
echo "[mosaic-skills] Removed stale broken skill link: $link_path"
|
|
continue
|
|
fi
|
|
|
|
if [[ "$resolved" == "$MOSAIC_HOME/"* ]]; then
|
|
rm -f "$link_path"
|
|
echo "[mosaic-skills] Removed stale retired skill link: $link_path"
|
|
fi
|
|
done < <(find "$target_dir" -mindepth 1 -maxdepth 1 -type l -print0)
|
|
}
|
|
|
|
for target in "${link_targets[@]}"; do
|
|
mkdir -p "$target"
|
|
|
|
# If target already resolves to canonical dir, skip to avoid self-link recursion/corruption.
|
|
target_real="$(readlink -f "$target" 2>/dev/null || true)"
|
|
if [[ -n "$target_real" && "$target_real" == "$canonical_real" ]]; then
|
|
echo "[mosaic-skills] Skip target (already canonical): $target"
|
|
continue
|
|
fi
|
|
|
|
prune_stale_links_in_target "$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)
|
|
|
|
if [[ -d "$MOSAIC_LOCAL_SKILLS_DIR" ]]; then
|
|
while IFS= read -r -d '' skill; do
|
|
link_skill_into_target "$skill" "$target"
|
|
done < <(find "$MOSAIC_LOCAL_SKILLS_DIR" -mindepth 1 -maxdepth 1 \( -type d -o -type l \) -print0)
|
|
fi
|
|
|
|
echo "[mosaic-skills] Linked skills into: $target"
|
|
done
|
|
|
|
echo "[mosaic-skills] Complete"
|