feat: add mosaic upgrade command to clean stale per-project files
Detects and cleans up files that are now centralized: - SOUL.md: removed (now global at ~/.config/mosaic/SOUL.md) - CLAUDE.md: replaced with thin pointer to global AGENTS.md - AGENTS.md: stale load-order sections stripped, project content preserved Supports --dry-run, --all (scan ~/src/*), and per-project paths. Creates .mosaic-bak backups before any modification. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -34,6 +34,7 @@ Management:
|
|||||||
doctor [args...] Audit runtime state and detect drift
|
doctor [args...] Audit runtime state and detect drift
|
||||||
sync [args...] Sync skills from canonical source
|
sync [args...] Sync skills from canonical source
|
||||||
bootstrap <path> Bootstrap a repo with Mosaic standards
|
bootstrap <path> Bootstrap a repo with Mosaic standards
|
||||||
|
upgrade [path] Clean up stale SOUL.md/CLAUDE.md in a project
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
-h, --help Show this help
|
-h, --help Show this help
|
||||||
@@ -146,6 +147,11 @@ run_bootstrap() {
|
|||||||
exec "$MOSAIC_HOME/bin/mosaic-bootstrap-repo" "$@"
|
exec "$MOSAIC_HOME/bin/mosaic-bootstrap-repo" "$@"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
run_upgrade() {
|
||||||
|
check_mosaic_home
|
||||||
|
exec "$MOSAIC_HOME/bin/mosaic-upgrade" "$@"
|
||||||
|
}
|
||||||
|
|
||||||
# Main router
|
# Main router
|
||||||
if [[ $# -eq 0 ]]; then
|
if [[ $# -eq 0 ]]; then
|
||||||
usage
|
usage
|
||||||
@@ -163,6 +169,7 @@ case "$command" in
|
|||||||
doctor) run_doctor "$@" ;;
|
doctor) run_doctor "$@" ;;
|
||||||
sync) run_sync "$@" ;;
|
sync) run_sync "$@" ;;
|
||||||
bootstrap) run_bootstrap "$@" ;;
|
bootstrap) run_bootstrap "$@" ;;
|
||||||
|
upgrade) run_upgrade "$@" ;;
|
||||||
help|-h|--help) usage ;;
|
help|-h|--help) usage ;;
|
||||||
version|-v|--version) echo "mosaic $VERSION" ;;
|
version|-v|--version) echo "mosaic $VERSION" ;;
|
||||||
*)
|
*)
|
||||||
|
|||||||
218
bin/mosaic-upgrade
Executable file
218
bin/mosaic-upgrade
Executable file
@@ -0,0 +1,218 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# mosaic-upgrade — Clean up stale per-project files after Mosaic centralization
|
||||||
|
#
|
||||||
|
# SOUL.md → Now global at ~/.config/mosaic/SOUL.md (remove from projects)
|
||||||
|
# CLAUDE.md → Now a thin pointer or removable (replace with pointer or remove)
|
||||||
|
# AGENTS.md → Keep project-specific content, strip stale load-order directives
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# mosaic-upgrade [path] Upgrade a specific project (default: current dir)
|
||||||
|
# mosaic-upgrade --all Scan ~/src/* for projects to upgrade
|
||||||
|
# mosaic-upgrade --dry-run Show what would change without touching anything
|
||||||
|
|
||||||
|
MOSAIC_HOME="${MOSAIC_HOME:-$HOME/.config/mosaic}"
|
||||||
|
|
||||||
|
# Colors (disabled if not a terminal)
|
||||||
|
if [[ -t 1 ]]; then
|
||||||
|
GREEN='\033[0;32m' YELLOW='\033[0;33m' RED='\033[0;31m'
|
||||||
|
CYAN='\033[0;36m' BOLD='\033[1m' DIM='\033[2m' RESET='\033[0m'
|
||||||
|
else
|
||||||
|
GREEN='' YELLOW='' RED='' CYAN='' BOLD='' DIM='' RESET=''
|
||||||
|
fi
|
||||||
|
|
||||||
|
ok() { echo -e " ${GREEN}✓${RESET} $1"; }
|
||||||
|
skip() { echo -e " ${DIM}–${RESET} $1"; }
|
||||||
|
warn() { echo -e " ${YELLOW}⚠${RESET} $1"; }
|
||||||
|
act() { echo -e " ${CYAN}→${RESET} $1"; }
|
||||||
|
|
||||||
|
DRY_RUN=false
|
||||||
|
ALL=false
|
||||||
|
TARGET=""
|
||||||
|
SEARCH_ROOT="${HOME}/src"
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat <<USAGE
|
||||||
|
mosaic-upgrade — Clean up stale per-project files
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
mosaic-upgrade [path] Upgrade a specific project (default: cwd)
|
||||||
|
mosaic-upgrade --all Scan ~/src/* for all git projects
|
||||||
|
mosaic-upgrade --dry-run Preview changes without writing
|
||||||
|
mosaic-upgrade --all --dry-run Preview all projects
|
||||||
|
|
||||||
|
After Mosaic centralization:
|
||||||
|
SOUL.md → Removed (now global at ~/.config/mosaic/SOUL.md)
|
||||||
|
CLAUDE.md → Replaced with thin pointer or removed
|
||||||
|
AGENTS.md → Stale load-order sections stripped; project content preserved
|
||||||
|
USAGE
|
||||||
|
}
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--dry-run) DRY_RUN=true; shift ;;
|
||||||
|
--all) ALL=true; shift ;;
|
||||||
|
--root) SEARCH_ROOT="$2"; shift 2 ;;
|
||||||
|
-h|--help) usage; exit 0 ;;
|
||||||
|
-*) echo "Unknown flag: $1" >&2; usage >&2; exit 1 ;;
|
||||||
|
*) TARGET="$1"; shift ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# Generate the thin CLAUDE.md pointer
|
||||||
|
CLAUDE_POINTER='# CLAUDE Compatibility Pointer
|
||||||
|
|
||||||
|
This file exists so Claude Code sessions load Mosaic standards.
|
||||||
|
|
||||||
|
## MANDATORY — Read Before Any Response
|
||||||
|
|
||||||
|
BEFORE responding to any user message, READ `~/.config/mosaic/AGENTS.md`.
|
||||||
|
|
||||||
|
That file is the universal agent configuration. Do NOT respond until you have loaded it.
|
||||||
|
Then read the project-local `AGENTS.md` in this repository for project-specific guidance.'
|
||||||
|
|
||||||
|
upgrade_project() {
|
||||||
|
local project_dir="$1"
|
||||||
|
local project_name
|
||||||
|
project_name="$(basename "$project_dir")"
|
||||||
|
local changed=false
|
||||||
|
|
||||||
|
echo -e "\n${BOLD}$project_name${RESET} ${DIM}($project_dir)${RESET}"
|
||||||
|
|
||||||
|
# ── SOUL.md ──────────────────────────────────────────────
|
||||||
|
local soul="$project_dir/SOUL.md"
|
||||||
|
if [[ -f "$soul" ]]; then
|
||||||
|
if [[ "$DRY_RUN" == "true" ]]; then
|
||||||
|
act "Would remove SOUL.md (now global at ~/.config/mosaic/SOUL.md)"
|
||||||
|
else
|
||||||
|
rm "$soul"
|
||||||
|
ok "Removed SOUL.md (now global)"
|
||||||
|
fi
|
||||||
|
changed=true
|
||||||
|
else
|
||||||
|
skip "No SOUL.md (already clean)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── CLAUDE.md ────────────────────────────────────────────
|
||||||
|
local claude_md="$project_dir/CLAUDE.md"
|
||||||
|
if [[ -f "$claude_md" ]]; then
|
||||||
|
local claude_content
|
||||||
|
claude_content="$(cat "$claude_md")"
|
||||||
|
|
||||||
|
# Check if it's already a thin pointer to AGENTS.md
|
||||||
|
if echo "$claude_content" | grep -q "READ.*~/.config/mosaic/AGENTS.md"; then
|
||||||
|
skip "CLAUDE.md already points to global AGENTS.md"
|
||||||
|
else
|
||||||
|
if [[ "$DRY_RUN" == "true" ]]; then
|
||||||
|
act "Would replace CLAUDE.md with thin pointer to global AGENTS.md"
|
||||||
|
else
|
||||||
|
# Back up the original
|
||||||
|
cp "$claude_md" "${claude_md}.mosaic-bak"
|
||||||
|
echo "$CLAUDE_POINTER" > "$claude_md"
|
||||||
|
ok "Replaced CLAUDE.md with pointer (backup: CLAUDE.md.mosaic-bak)"
|
||||||
|
fi
|
||||||
|
changed=true
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
skip "No CLAUDE.md"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── AGENTS.md (strip stale load-order, preserve project content) ─
|
||||||
|
local agents="$project_dir/AGENTS.md"
|
||||||
|
if [[ -f "$agents" ]]; then
|
||||||
|
# Detect stale load-order patterns
|
||||||
|
local has_stale=false
|
||||||
|
|
||||||
|
# Pattern 1: References to SOUL.md in load order
|
||||||
|
if grep -qE "(Read|READ|Load).*SOUL\.md" "$agents" 2>/dev/null; then
|
||||||
|
has_stale=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Pattern 2: Old "## Load Order" section that references centralized files
|
||||||
|
if grep -q "## Load Order" "$agents" 2>/dev/null && \
|
||||||
|
grep -qE "STANDARDS\.md|SOUL\.md" "$agents" 2>/dev/null; then
|
||||||
|
has_stale=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Pattern 3: Old ~/.mosaic/ path (pre-centralization)
|
||||||
|
if grep -q '~/.mosaic/' "$agents" 2>/dev/null; then
|
||||||
|
has_stale=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$has_stale" == "true" ]]; then
|
||||||
|
if [[ "$DRY_RUN" == "true" ]]; then
|
||||||
|
act "Would strip stale load-order section from AGENTS.md"
|
||||||
|
# Show what we detect
|
||||||
|
if grep -qn "## Load Order" "$agents" 2>/dev/null; then
|
||||||
|
local line
|
||||||
|
line=$(grep -n "## Load Order" "$agents" | head -1 | cut -d: -f1)
|
||||||
|
echo -e " ${DIM}Line $line: Found '## Load Order' section referencing SOUL.md/STANDARDS.md${RESET}"
|
||||||
|
fi
|
||||||
|
if grep -qn '~/.mosaic/' "$agents" 2>/dev/null; then
|
||||||
|
echo -e " ${DIM}Found references to old ~/.mosaic/ path${RESET}"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
cp "$agents" "${agents}.mosaic-bak"
|
||||||
|
|
||||||
|
# Strip the Load Order section (from "## Load Order" to next "##" or "---")
|
||||||
|
if grep -q "## Load Order" "$agents"; then
|
||||||
|
awk '
|
||||||
|
/^## Load Order/ { skip=1; next }
|
||||||
|
skip && /^(## |---)/ { skip=0 }
|
||||||
|
skip { next }
|
||||||
|
{ print }
|
||||||
|
' "${agents}.mosaic-bak" > "$agents"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Fix old ~/.mosaic/ → ~/.config/mosaic/
|
||||||
|
if grep -q '~/.mosaic/' "$agents"; then
|
||||||
|
sed -i 's|~/.mosaic/|~/.config/mosaic/|g' "$agents"
|
||||||
|
fi
|
||||||
|
|
||||||
|
ok "Stripped stale load-order from AGENTS.md (backup: AGENTS.md.mosaic-bak)"
|
||||||
|
fi
|
||||||
|
changed=true
|
||||||
|
else
|
||||||
|
skip "AGENTS.md has no stale directives"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
skip "No AGENTS.md"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── .claude/settings.json (leave alone) ──────────────────
|
||||||
|
# Project-specific settings are fine — don't touch them.
|
||||||
|
|
||||||
|
if [[ "$changed" == "false" ]]; then
|
||||||
|
echo -e " ${GREEN}Already up to date.${RESET}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Main ───────────────────────────────────────────────────
|
||||||
|
|
||||||
|
if [[ "$DRY_RUN" == "true" ]]; then
|
||||||
|
echo -e "${BOLD}Mode: DRY RUN (no files will be changed)${RESET}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$ALL" == "true" ]]; then
|
||||||
|
echo -e "${BOLD}Scanning $SEARCH_ROOT for projects...${RESET}"
|
||||||
|
count=0
|
||||||
|
for dir in "$SEARCH_ROOT"/*/; do
|
||||||
|
[[ -d "$dir/.git" ]] || continue
|
||||||
|
upgrade_project "$dir"
|
||||||
|
count=$((count + 1))
|
||||||
|
done
|
||||||
|
echo -e "\n${BOLD}Scanned $count projects.${RESET}"
|
||||||
|
elif [[ -n "$TARGET" ]]; then
|
||||||
|
if [[ ! -d "$TARGET" ]]; then
|
||||||
|
echo "[mosaic-upgrade] ERROR: $TARGET is not a directory." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
upgrade_project "$TARGET"
|
||||||
|
else
|
||||||
|
upgrade_project "$(pwd)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$DRY_RUN" == "true" ]]; then
|
||||||
|
echo -e "\n${YELLOW}This was a dry run. Run without --dry-run to apply changes.${RESET}"
|
||||||
|
fi
|
||||||
@@ -31,6 +31,7 @@ Management:
|
|||||||
doctor [args...] Audit runtime state and detect drift
|
doctor [args...] Audit runtime state and detect drift
|
||||||
sync [args...] Sync skills from canonical source
|
sync [args...] Sync skills from canonical source
|
||||||
bootstrap <path> Bootstrap a repo with Mosaic standards
|
bootstrap <path> Bootstrap a repo with Mosaic standards
|
||||||
|
upgrade [path] Clean up stale SOUL.md/CLAUDE.md in a project
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
-h, --help Show this help
|
-h, --help Show this help
|
||||||
@@ -140,6 +141,11 @@ switch ($command) {
|
|||||||
Write-Host "[mosaic] NOTE: mosaic-bootstrap-repo requires bash. Use Git Bash or WSL." -ForegroundColor Yellow
|
Write-Host "[mosaic] NOTE: mosaic-bootstrap-repo requires bash. Use Git Bash or WSL." -ForegroundColor Yellow
|
||||||
& (Join-Path $MosaicHome "bin\mosaic-bootstrap-repo") @remaining
|
& (Join-Path $MosaicHome "bin\mosaic-bootstrap-repo") @remaining
|
||||||
}
|
}
|
||||||
|
"upgrade" {
|
||||||
|
Assert-MosaicHome
|
||||||
|
Write-Host "[mosaic] NOTE: mosaic-upgrade requires bash. Use Git Bash or WSL." -ForegroundColor Yellow
|
||||||
|
& (Join-Path $MosaicHome "bin\mosaic-upgrade") @remaining
|
||||||
|
}
|
||||||
{ $_ -in "help", "-h", "--help" } { Show-Usage }
|
{ $_ -in "help", "-h", "--help" } { Show-Usage }
|
||||||
{ $_ -in "version", "-v", "--version" } { Write-Host "mosaic $Version" }
|
{ $_ -in "version", "-v", "--version" } { Write-Host "mosaic $Version" }
|
||||||
default {
|
default {
|
||||||
|
|||||||
Reference in New Issue
Block a user