Update scripts to keep SOUL.md idempotent. Add release upgrade framework.
This commit is contained in:
51
README.md
51
README.md
@@ -30,6 +30,7 @@ The installer will:
|
||||
- Add `~/.config/mosaic/bin` to your PATH
|
||||
- Sync runtime adapters and skills
|
||||
- Run a health audit
|
||||
- Detect existing installs and prompt to keep or overwrite local files
|
||||
- Prompt you to run `mosaic init` to set up your agent identity
|
||||
|
||||
## First Run
|
||||
@@ -96,24 +97,58 @@ mosaic init # Generate SOUL.md (agent identity)
|
||||
mosaic doctor # Health audit — detect drift and missing files
|
||||
mosaic sync # Sync skills from canonical source
|
||||
mosaic bootstrap <path> # Bootstrap a repo with Mosaic standards
|
||||
mosaic upgrade [path] # Clean up stale per-project files (see below)
|
||||
mosaic upgrade --all # Upgrade all projects in ~/src
|
||||
mosaic upgrade --dry-run # Preview without changes
|
||||
mosaic upgrade check # Check release upgrade status (no changes)
|
||||
mosaic upgrade # Upgrade installed Mosaic release (keeps SOUL.md by default)
|
||||
mosaic upgrade --dry-run # Preview release upgrade without changes
|
||||
mosaic upgrade --ref main # Upgrade from a specific branch/tag/commit ref
|
||||
mosaic upgrade --overwrite # Upgrade release and overwrite local files
|
||||
mosaic upgrade project ... # Project file cleanup mode (see below)
|
||||
```
|
||||
|
||||
## Upgrading Mosaic Release
|
||||
|
||||
Upgrade the installed framework in place:
|
||||
|
||||
```bash
|
||||
# Default (safe): keep local SOUL.md + memory
|
||||
mosaic upgrade
|
||||
|
||||
# Check current/target release info without changing files
|
||||
mosaic upgrade check
|
||||
|
||||
# Non-interactive
|
||||
mosaic upgrade --yes
|
||||
|
||||
# Pull a specific ref
|
||||
mosaic upgrade --ref main
|
||||
|
||||
# Force full overwrite (fresh install semantics)
|
||||
mosaic upgrade --overwrite --yes
|
||||
```
|
||||
|
||||
`mosaic upgrade` re-runs the remote installer and passes install mode controls (`keep`/`overwrite`).
|
||||
This is the manual upgrade path today and is suitable for future app-driven update checks.
|
||||
|
||||
## Upgrading Projects
|
||||
|
||||
After centralizing AGENTS.md and SOUL.md, existing projects may have stale files:
|
||||
|
||||
```bash
|
||||
# Preview what would change across all projects
|
||||
mosaic upgrade --all --dry-run
|
||||
mosaic upgrade project --all --dry-run
|
||||
|
||||
# Apply to all projects
|
||||
mosaic upgrade --all
|
||||
mosaic upgrade project --all
|
||||
|
||||
# Apply to a specific project
|
||||
mosaic upgrade ~/src/my-project
|
||||
mosaic upgrade project ~/src/my-project
|
||||
```
|
||||
|
||||
Backward compatibility is preserved for historical usage:
|
||||
|
||||
```bash
|
||||
mosaic upgrade --all # still routes to project-upgrade
|
||||
mosaic upgrade ~/src/my-repo # still routes to project-upgrade
|
||||
```
|
||||
|
||||
What it does per project:
|
||||
@@ -186,6 +221,10 @@ Pull the latest and re-run the installer:
|
||||
cd ~/src/mosaic-bootstrap && git pull && bash install.sh
|
||||
```
|
||||
|
||||
If an existing install is detected, the installer prompts for:
|
||||
- `keep` (recommended): preserve local `SOUL.md` and `memory/`
|
||||
- `overwrite`: replace everything in `~/.config/mosaic`
|
||||
|
||||
Or use the one-liner again — it always pulls the latest:
|
||||
|
||||
```bash
|
||||
|
||||
55
bin/mosaic
55
bin/mosaic
@@ -14,6 +14,9 @@ set -euo pipefail
|
||||
# mosaic doctor [args...] Health audit
|
||||
# mosaic sync [args...] Sync skills
|
||||
# mosaic bootstrap <path> Bootstrap a repo
|
||||
# mosaic upgrade release Upgrade installed Mosaic release
|
||||
# mosaic upgrade check Check release upgrade status (no changes)
|
||||
# mosaic upgrade project [args] Upgrade project-local stale files
|
||||
|
||||
MOSAIC_HOME="${MOSAIC_HOME:-$HOME/.config/mosaic}"
|
||||
VERSION="0.1.0"
|
||||
@@ -34,7 +37,10 @@ Management:
|
||||
doctor [args...] Audit runtime state and detect drift
|
||||
sync [args...] Sync skills from canonical source
|
||||
bootstrap <path> Bootstrap a repo with Mosaic standards
|
||||
upgrade [path] Clean up stale SOUL.md/CLAUDE.md in a project
|
||||
upgrade [mode] [args] Upgrade release (default) or project files
|
||||
upgrade check Check release upgrade status (no changes)
|
||||
release-upgrade [...] Upgrade installed Mosaic release
|
||||
project-upgrade [...] Clean up stale SOUL.md/CLAUDE.md in a project
|
||||
|
||||
Options:
|
||||
-h, --help Show this help
|
||||
@@ -147,11 +153,54 @@ run_bootstrap() {
|
||||
exec "$MOSAIC_HOME/bin/mosaic-bootstrap-repo" "$@"
|
||||
}
|
||||
|
||||
run_upgrade() {
|
||||
run_release_upgrade() {
|
||||
check_mosaic_home
|
||||
exec "$MOSAIC_HOME/bin/mosaic-release-upgrade" "$@"
|
||||
}
|
||||
|
||||
run_project_upgrade() {
|
||||
check_mosaic_home
|
||||
exec "$MOSAIC_HOME/bin/mosaic-upgrade" "$@"
|
||||
}
|
||||
|
||||
run_upgrade() {
|
||||
check_mosaic_home
|
||||
|
||||
# Default: upgrade installed release
|
||||
if [[ $# -eq 0 ]]; then
|
||||
run_release_upgrade
|
||||
fi
|
||||
|
||||
case "$1" in
|
||||
release)
|
||||
shift
|
||||
run_release_upgrade "$@"
|
||||
;;
|
||||
check)
|
||||
shift
|
||||
run_release_upgrade --dry-run "$@"
|
||||
;;
|
||||
project)
|
||||
shift
|
||||
run_project_upgrade "$@"
|
||||
;;
|
||||
|
||||
# Backward compatibility for historical project-upgrade usage.
|
||||
--all|--root)
|
||||
run_project_upgrade "$@"
|
||||
;;
|
||||
--dry-run|--ref|--keep|--overwrite|-y|--yes)
|
||||
run_release_upgrade "$@"
|
||||
;;
|
||||
-*)
|
||||
run_release_upgrade "$@"
|
||||
;;
|
||||
*)
|
||||
run_project_upgrade "$@"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Main router
|
||||
if [[ $# -eq 0 ]]; then
|
||||
usage
|
||||
@@ -170,6 +219,8 @@ case "$command" in
|
||||
sync) run_sync "$@" ;;
|
||||
bootstrap) run_bootstrap "$@" ;;
|
||||
upgrade) run_upgrade "$@" ;;
|
||||
release-upgrade) run_release_upgrade "$@" ;;
|
||||
project-upgrade) run_project_upgrade "$@" ;;
|
||||
help|-h|--help) usage ;;
|
||||
version|-v|--version) echo "mosaic $VERSION" ;;
|
||||
*)
|
||||
|
||||
124
bin/mosaic-release-upgrade
Executable file
124
bin/mosaic-release-upgrade
Executable file
@@ -0,0 +1,124 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# mosaic-release-upgrade — Upgrade installed Mosaic framework release.
|
||||
#
|
||||
# This re-runs the remote installer with explicit install mode controls.
|
||||
# Default behavior is safe/idempotent (keep SOUL.md + memory).
|
||||
#
|
||||
# Usage:
|
||||
# mosaic-release-upgrade
|
||||
# mosaic-release-upgrade --ref main --keep
|
||||
# mosaic-release-upgrade --ref v0.2.0 --overwrite --yes
|
||||
|
||||
MOSAIC_HOME="${MOSAIC_HOME:-$HOME/.config/mosaic}"
|
||||
REMOTE_SCRIPT_URL="${MOSAIC_REMOTE_INSTALL_URL:-https://git.mosaicstack.dev/mosaic/bootstrap/raw/branch/main/remote-install.sh}"
|
||||
BOOTSTRAP_REF="${MOSAIC_BOOTSTRAP_REF:-main}"
|
||||
INSTALL_MODE="${MOSAIC_INSTALL_MODE:-keep}" # keep|overwrite
|
||||
YES=false
|
||||
DRY_RUN=false
|
||||
|
||||
usage() {
|
||||
cat <<USAGE
|
||||
Usage: $(basename "$0") [options]
|
||||
|
||||
Upgrade the installed Mosaic framework release.
|
||||
|
||||
Options:
|
||||
--ref <name> Bootstrap archive ref (branch/tag/commit). Default: main
|
||||
--keep Keep local files (SOUL.md, memory/) during upgrade (default)
|
||||
--overwrite Overwrite target install directory contents
|
||||
-y, --yes Skip confirmation prompt
|
||||
--dry-run Show actions without executing
|
||||
-h, --help Show this help
|
||||
USAGE
|
||||
}
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--ref)
|
||||
[[ $# -lt 2 ]] && { echo "Missing value for --ref" >&2; exit 1; }
|
||||
BOOTSTRAP_REF="$2"
|
||||
shift 2
|
||||
;;
|
||||
--keep)
|
||||
INSTALL_MODE="keep"
|
||||
shift
|
||||
;;
|
||||
--overwrite)
|
||||
INSTALL_MODE="overwrite"
|
||||
shift
|
||||
;;
|
||||
-y|--yes)
|
||||
YES=true
|
||||
shift
|
||||
;;
|
||||
--dry-run)
|
||||
DRY_RUN=true
|
||||
shift
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "Unknown argument: $1" >&2
|
||||
usage >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
case "$INSTALL_MODE" in
|
||||
keep|overwrite) ;;
|
||||
*)
|
||||
echo "[mosaic-release-upgrade] Invalid install mode: $INSTALL_MODE" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
current_version="unknown"
|
||||
if [[ -x "$MOSAIC_HOME/bin/mosaic" ]]; then
|
||||
current_version="$("$MOSAIC_HOME/bin/mosaic" --version 2>/dev/null | awk '{print $2}' || true)"
|
||||
[[ -n "$current_version" ]] || current_version="unknown"
|
||||
fi
|
||||
|
||||
echo "[mosaic-release-upgrade] Current version: $current_version"
|
||||
echo "[mosaic-release-upgrade] Target ref: $BOOTSTRAP_REF"
|
||||
echo "[mosaic-release-upgrade] Install mode: $INSTALL_MODE"
|
||||
echo "[mosaic-release-upgrade] Installer URL: $REMOTE_SCRIPT_URL"
|
||||
|
||||
if [[ "$DRY_RUN" == "true" ]]; then
|
||||
echo "[mosaic-release-upgrade] Dry run: no changes applied."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ "$YES" != "true" && -t 0 ]]; then
|
||||
printf "Proceed with Mosaic release upgrade? [y/N]: "
|
||||
read -r confirm
|
||||
case "${confirm:-n}" in
|
||||
y|Y|yes|YES) ;;
|
||||
*)
|
||||
echo "[mosaic-release-upgrade] Aborted."
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
if command -v curl >/dev/null 2>&1; then
|
||||
curl -sL "$REMOTE_SCRIPT_URL" | \
|
||||
MOSAIC_BOOTSTRAP_REF="$BOOTSTRAP_REF" \
|
||||
MOSAIC_INSTALL_MODE="$INSTALL_MODE" \
|
||||
MOSAIC_HOME="$MOSAIC_HOME" \
|
||||
sh
|
||||
elif command -v wget >/dev/null 2>&1; then
|
||||
wget -qO- "$REMOTE_SCRIPT_URL" | \
|
||||
MOSAIC_BOOTSTRAP_REF="$BOOTSTRAP_REF" \
|
||||
MOSAIC_INSTALL_MODE="$INSTALL_MODE" \
|
||||
MOSAIC_HOME="$MOSAIC_HOME" \
|
||||
sh
|
||||
else
|
||||
echo "[mosaic-release-upgrade] ERROR: curl or wget required." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
65
bin/mosaic-release-upgrade.ps1
Normal file
65
bin/mosaic-release-upgrade.ps1
Normal file
@@ -0,0 +1,65 @@
|
||||
# mosaic-release-upgrade.ps1 — Upgrade installed Mosaic framework release (Windows)
|
||||
#
|
||||
# Usage:
|
||||
# mosaic-release-upgrade.ps1
|
||||
# mosaic-release-upgrade.ps1 -Ref main -Keep
|
||||
# mosaic-release-upgrade.ps1 -Ref v0.2.0 -Overwrite -Yes
|
||||
#
|
||||
param(
|
||||
[string]$Ref = $(if ($env:MOSAIC_BOOTSTRAP_REF) { $env:MOSAIC_BOOTSTRAP_REF } else { "main" }),
|
||||
[switch]$Keep,
|
||||
[switch]$Overwrite,
|
||||
[switch]$Yes,
|
||||
[switch]$DryRun
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
$MosaicHome = if ($env:MOSAIC_HOME) { $env:MOSAIC_HOME } else { Join-Path $env:USERPROFILE ".config\mosaic" }
|
||||
$RemoteInstallerUrl = if ($env:MOSAIC_REMOTE_INSTALL_URL) {
|
||||
$env:MOSAIC_REMOTE_INSTALL_URL
|
||||
} else {
|
||||
"https://git.mosaicstack.dev/mosaic/bootstrap/raw/branch/main/remote-install.ps1"
|
||||
}
|
||||
|
||||
$installMode = if ($Overwrite) { "overwrite" } elseif ($Keep) { "keep" } elseif ($env:MOSAIC_INSTALL_MODE) { $env:MOSAIC_INSTALL_MODE } else { "keep" }
|
||||
if ($installMode -notin @("keep", "overwrite")) {
|
||||
Write-Host "[mosaic-release-upgrade] Invalid install mode: $installMode" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
$currentVersion = "unknown"
|
||||
$mosaicCmd = Join-Path $MosaicHome "bin\mosaic.ps1"
|
||||
if (Test-Path $mosaicCmd) {
|
||||
try {
|
||||
$currentVersion = (& $mosaicCmd --version) -replace '^mosaic\s+', ''
|
||||
}
|
||||
catch {
|
||||
$currentVersion = "unknown"
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host "[mosaic-release-upgrade] Current version: $currentVersion"
|
||||
Write-Host "[mosaic-release-upgrade] Target ref: $Ref"
|
||||
Write-Host "[mosaic-release-upgrade] Install mode: $installMode"
|
||||
Write-Host "[mosaic-release-upgrade] Installer URL: $RemoteInstallerUrl"
|
||||
|
||||
if ($DryRun) {
|
||||
Write-Host "[mosaic-release-upgrade] Dry run: no changes applied."
|
||||
exit 0
|
||||
}
|
||||
|
||||
if (-not $Yes) {
|
||||
$confirmation = Read-Host "Proceed with Mosaic release upgrade? [y/N]"
|
||||
if ($confirmation -notin @("y", "Y", "yes", "YES")) {
|
||||
Write-Host "[mosaic-release-upgrade] Aborted."
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
$env:MOSAIC_BOOTSTRAP_REF = $Ref
|
||||
$env:MOSAIC_INSTALL_MODE = $installMode
|
||||
$env:MOSAIC_HOME = $MosaicHome
|
||||
|
||||
Invoke-RestMethod -Uri $RemoteInstallerUrl | Invoke-Expression
|
||||
|
||||
@@ -31,7 +31,10 @@ Management:
|
||||
doctor [args...] Audit runtime state and detect drift
|
||||
sync [args...] Sync skills from canonical source
|
||||
bootstrap <path> Bootstrap a repo with Mosaic standards
|
||||
upgrade [path] Clean up stale SOUL.md/CLAUDE.md in a project
|
||||
upgrade [mode] [args] Upgrade release (default) or project files
|
||||
upgrade check Check release upgrade status (no changes)
|
||||
release-upgrade [...] Upgrade installed Mosaic release
|
||||
project-upgrade [...] Clean up stale SOUL.md/CLAUDE.md in a project
|
||||
|
||||
Options:
|
||||
-h, --help Show this help
|
||||
@@ -142,6 +145,48 @@ switch ($command) {
|
||||
& (Join-Path $MosaicHome "bin\mosaic-bootstrap-repo") @remaining
|
||||
}
|
||||
"upgrade" {
|
||||
Assert-MosaicHome
|
||||
if ($remaining.Count -eq 0) {
|
||||
& (Join-Path $MosaicHome "bin\mosaic-release-upgrade.ps1")
|
||||
break
|
||||
}
|
||||
|
||||
$mode = $remaining[0]
|
||||
$tail = if ($remaining.Count -gt 1) { $remaining[1..($remaining.Count - 1)] } else { @() }
|
||||
|
||||
switch -Regex ($mode) {
|
||||
"^release$" {
|
||||
& (Join-Path $MosaicHome "bin\mosaic-release-upgrade.ps1") @tail
|
||||
}
|
||||
"^check$" {
|
||||
& (Join-Path $MosaicHome "bin\mosaic-release-upgrade.ps1") -DryRun @tail
|
||||
}
|
||||
"^project$" {
|
||||
Write-Host "[mosaic] NOTE: mosaic-upgrade requires bash. Use Git Bash or WSL." -ForegroundColor Yellow
|
||||
& (Join-Path $MosaicHome "bin\mosaic-upgrade") @tail
|
||||
}
|
||||
"^(--all|--root)$" {
|
||||
Write-Host "[mosaic] NOTE: mosaic-upgrade requires bash. Use Git Bash or WSL." -ForegroundColor Yellow
|
||||
& (Join-Path $MosaicHome "bin\mosaic-upgrade") @remaining
|
||||
}
|
||||
"^(--dry-run|--ref|--keep|--overwrite|-y|--yes)$" {
|
||||
& (Join-Path $MosaicHome "bin\mosaic-release-upgrade.ps1") @remaining
|
||||
}
|
||||
"^-.*" {
|
||||
& (Join-Path $MosaicHome "bin\mosaic-release-upgrade.ps1") @remaining
|
||||
}
|
||||
default {
|
||||
Write-Host "[mosaic] NOTE: treating positional argument as project path." -ForegroundColor Yellow
|
||||
Write-Host "[mosaic] NOTE: mosaic-upgrade requires bash. Use Git Bash or WSL." -ForegroundColor Yellow
|
||||
& (Join-Path $MosaicHome "bin\mosaic-upgrade") @remaining
|
||||
}
|
||||
}
|
||||
}
|
||||
"release-upgrade" {
|
||||
Assert-MosaicHome
|
||||
& (Join-Path $MosaicHome "bin\mosaic-release-upgrade.ps1") @remaining
|
||||
}
|
||||
"project-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
|
||||
|
||||
98
install.ps1
98
install.ps1
@@ -8,16 +8,96 @@ $ErrorActionPreference = "Stop"
|
||||
|
||||
$SourceDir = $PSScriptRoot
|
||||
$TargetDir = if ($env:MOSAIC_HOME) { $env:MOSAIC_HOME } else { Join-Path $env:USERPROFILE ".config\mosaic" }
|
||||
$InstallMode = if ($env:MOSAIC_INSTALL_MODE) { $env:MOSAIC_INSTALL_MODE.ToLowerInvariant() } else { "prompt" } # prompt|keep|overwrite
|
||||
$PreservePaths = @("SOUL.md", "memory")
|
||||
|
||||
function Write-Ok { param([string]$Msg) Write-Host " ✓ " -ForegroundColor Green -NoNewline; Write-Host $Msg }
|
||||
function Write-Warn { param([string]$Msg) Write-Host " ⚠ " -ForegroundColor Yellow -NoNewline; Write-Host $Msg }
|
||||
function Write-Fail { param([string]$Msg) Write-Host " ✗ " -ForegroundColor Red -NoNewline; Write-Host $Msg }
|
||||
function Write-Step { param([string]$Msg) Write-Host ""; Write-Host $Msg -ForegroundColor White -BackgroundColor DarkGray }
|
||||
|
||||
function Test-ExistingInstall {
|
||||
if (-not (Test-Path $TargetDir)) { return $false }
|
||||
return (Test-Path (Join-Path $TargetDir "bin\mosaic")) -or (Test-Path (Join-Path $TargetDir "AGENTS.md")) -or (Test-Path (Join-Path $TargetDir "SOUL.md"))
|
||||
}
|
||||
|
||||
function Select-InstallMode {
|
||||
switch ($InstallMode) {
|
||||
"prompt" { }
|
||||
"keep" { return }
|
||||
"overwrite" { return }
|
||||
default {
|
||||
Write-Fail "Invalid MOSAIC_INSTALL_MODE '$InstallMode'. Use: prompt, keep, overwrite."
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
if (-not (Test-ExistingInstall)) {
|
||||
$script:InstallMode = "overwrite"
|
||||
return
|
||||
}
|
||||
|
||||
if (-not [Environment]::UserInteractive) {
|
||||
Write-Warn "Existing install detected without interactive input; defaulting to keep local files."
|
||||
$script:InstallMode = "keep"
|
||||
return
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "Existing Mosaic install detected at: $TargetDir"
|
||||
Write-Host "Choose reinstall mode:"
|
||||
Write-Host " 1) keep Keep local files (SOUL.md, memory/) while updating framework"
|
||||
Write-Host " 2) overwrite Replace everything in $TargetDir"
|
||||
Write-Host " 3) cancel Abort install"
|
||||
$selection = Read-Host "Selection [1/2/3] (default: 1)"
|
||||
|
||||
$normalizedSelection = if ($null -eq $selection) { "" } else { $selection.ToLowerInvariant() }
|
||||
switch ($normalizedSelection) {
|
||||
"" { $script:InstallMode = "keep" }
|
||||
"1" { $script:InstallMode = "keep" }
|
||||
"k" { $script:InstallMode = "keep" }
|
||||
"keep" { $script:InstallMode = "keep" }
|
||||
"2" { $script:InstallMode = "overwrite" }
|
||||
"o" { $script:InstallMode = "overwrite" }
|
||||
"overwrite" { $script:InstallMode = "overwrite" }
|
||||
"3" { Write-Fail "Install cancelled."; exit 1 }
|
||||
"c" { Write-Fail "Install cancelled."; exit 1 }
|
||||
"cancel" { Write-Fail "Install cancelled."; exit 1 }
|
||||
default {
|
||||
Write-Warn "Unrecognized selection '$selection'; defaulting to keep."
|
||||
$script:InstallMode = "keep"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# ── Install framework ────────────────────────────────────────
|
||||
Write-Step " Installing Mosaic framework "
|
||||
|
||||
if (-not (Test-Path $TargetDir)) { New-Item -ItemType Directory -Path $TargetDir -Force | Out-Null }
|
||||
Select-InstallMode
|
||||
|
||||
if ($InstallMode -eq "keep") {
|
||||
Write-Ok "Install mode: keep local SOUL.md/memory while updating framework"
|
||||
}
|
||||
else {
|
||||
Write-Ok "Install mode: overwrite existing files"
|
||||
}
|
||||
|
||||
$preserveTmp = $null
|
||||
if ($InstallMode -eq "keep") {
|
||||
$preserveTmp = Join-Path ([System.IO.Path]::GetTempPath()) ("mosaic-preserve-" + [Guid]::NewGuid().ToString("N"))
|
||||
New-Item -ItemType Directory -Path $preserveTmp -Force | Out-Null
|
||||
foreach ($relPath in $PreservePaths) {
|
||||
$src = Join-Path $TargetDir $relPath
|
||||
if (Test-Path $src) {
|
||||
$dstParent = Join-Path $preserveTmp (Split-Path $relPath -Parent)
|
||||
if (-not [string]::IsNullOrEmpty($dstParent) -and -not (Test-Path $dstParent)) {
|
||||
New-Item -ItemType Directory -Path $dstParent -Force | Out-Null
|
||||
}
|
||||
Copy-Item $src (Join-Path $preserveTmp $relPath) -Recurse -Force
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Get-ChildItem $TargetDir -Exclude ".git" | Remove-Item -Recurse -Force
|
||||
Get-ChildItem $SourceDir -Exclude ".git" | ForEach-Object {
|
||||
@@ -30,6 +110,24 @@ Get-ChildItem $SourceDir -Exclude ".git" | ForEach-Object {
|
||||
}
|
||||
}
|
||||
|
||||
if ($InstallMode -eq "keep" -and $null -ne $preserveTmp) {
|
||||
foreach ($relPath in $PreservePaths) {
|
||||
$src = Join-Path $preserveTmp $relPath
|
||||
if (Test-Path $src) {
|
||||
$dst = Join-Path $TargetDir $relPath
|
||||
if (Test-Path $dst) {
|
||||
Remove-Item $dst -Recurse -Force
|
||||
}
|
||||
$dstParent = Split-Path $dst -Parent
|
||||
if (-not (Test-Path $dstParent)) {
|
||||
New-Item -ItemType Directory -Path $dstParent -Force | Out-Null
|
||||
}
|
||||
Copy-Item $src $dst -Recurse -Force
|
||||
}
|
||||
}
|
||||
Remove-Item -Path $preserveTmp -Recurse -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
Write-Ok "Framework installed to $TargetDir"
|
||||
|
||||
# ── Post-install tasks ───────────────────────────────────────
|
||||
|
||||
118
install.sh
118
install.sh
@@ -3,6 +3,8 @@ set -euo pipefail
|
||||
|
||||
SOURCE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
TARGET_DIR="${MOSAIC_HOME:-$HOME/.config/mosaic}"
|
||||
INSTALL_MODE="${MOSAIC_INSTALL_MODE:-prompt}" # prompt|keep|overwrite
|
||||
PRESERVE_PATHS=("SOUL.md" "memory")
|
||||
|
||||
# Colors (disabled if not a terminal)
|
||||
if [[ -t 1 ]]; then
|
||||
@@ -17,17 +19,125 @@ warn() { echo -e " ${YELLOW}⚠${RESET} $1" >&2; }
|
||||
fail() { echo -e " ${RED}✗${RESET} $1" >&2; }
|
||||
step() { echo -e "\n${BOLD}$1${RESET}"; }
|
||||
|
||||
is_existing_install() {
|
||||
[[ -d "$TARGET_DIR" ]] || return 1
|
||||
[[ -f "$TARGET_DIR/bin/mosaic" || -f "$TARGET_DIR/AGENTS.md" || -f "$TARGET_DIR/SOUL.md" ]]
|
||||
}
|
||||
|
||||
select_install_mode() {
|
||||
case "$INSTALL_MODE" in
|
||||
keep|overwrite|prompt) ;;
|
||||
*)
|
||||
fail "Invalid MOSAIC_INSTALL_MODE='$INSTALL_MODE'. Use: prompt, keep, overwrite."
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
if ! is_existing_install; then
|
||||
INSTALL_MODE="overwrite"
|
||||
return
|
||||
fi
|
||||
|
||||
case "$INSTALL_MODE" in
|
||||
keep|overwrite)
|
||||
;;
|
||||
prompt)
|
||||
if [[ -t 0 ]]; then
|
||||
echo ""
|
||||
echo "Existing Mosaic install detected at: $TARGET_DIR"
|
||||
echo "Choose reinstall mode:"
|
||||
echo " 1) keep Keep local files (SOUL.md, memory/) while updating framework"
|
||||
echo " 2) overwrite Replace everything in $TARGET_DIR"
|
||||
echo " 3) cancel Abort install"
|
||||
printf "Selection [1/2/3] (default: 1): "
|
||||
read -r selection
|
||||
|
||||
case "${selection:-1}" in
|
||||
1|k|K|keep|KEEP) INSTALL_MODE="keep" ;;
|
||||
2|o|O|overwrite|OVERWRITE) INSTALL_MODE="overwrite" ;;
|
||||
3|c|C|cancel|CANCEL|n|N|no|NO)
|
||||
fail "Install cancelled."
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
warn "Unrecognized selection '$selection'; defaulting to keep."
|
||||
INSTALL_MODE="keep"
|
||||
;;
|
||||
esac
|
||||
else
|
||||
warn "Existing install detected without interactive input; defaulting to keep local files."
|
||||
INSTALL_MODE="keep"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
sync_framework() {
|
||||
local source_real target_real
|
||||
source_real="$(cd "$SOURCE_DIR" && pwd -P)"
|
||||
target_real="$(mkdir -p "$TARGET_DIR" && cd "$TARGET_DIR" && pwd -P)"
|
||||
|
||||
if [[ "$source_real" == "$target_real" ]]; then
|
||||
warn "Source and target are the same directory; skipping file sync."
|
||||
return
|
||||
fi
|
||||
|
||||
if command -v rsync >/dev/null 2>&1; then
|
||||
local rsync_args=(-a --delete --exclude ".git")
|
||||
|
||||
if [[ "$INSTALL_MODE" == "keep" ]]; then
|
||||
local path
|
||||
for path in "${PRESERVE_PATHS[@]}"; do
|
||||
rsync_args+=(--exclude "$path")
|
||||
done
|
||||
fi
|
||||
|
||||
rsync "${rsync_args[@]}" "$SOURCE_DIR/" "$TARGET_DIR/"
|
||||
return
|
||||
fi
|
||||
|
||||
local preserve_tmp=""
|
||||
if [[ "$INSTALL_MODE" == "keep" ]]; then
|
||||
preserve_tmp="$(mktemp -d "${TMPDIR:-/tmp}/mosaic-preserve-XXXXXX")"
|
||||
local path
|
||||
for path in "${PRESERVE_PATHS[@]}"; do
|
||||
if [[ -e "$TARGET_DIR/$path" ]]; then
|
||||
mkdir -p "$preserve_tmp/$(dirname "$path")"
|
||||
cp -R "$TARGET_DIR/$path" "$preserve_tmp/$path"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
find "$TARGET_DIR" -mindepth 1 -maxdepth 1 ! -name ".git" -exec rm -rf {} +
|
||||
cp -R "$SOURCE_DIR"/. "$TARGET_DIR"/
|
||||
rm -rf "$TARGET_DIR/.git"
|
||||
|
||||
if [[ -n "$preserve_tmp" ]]; then
|
||||
local path
|
||||
for path in "${PRESERVE_PATHS[@]}"; do
|
||||
if [[ -e "$preserve_tmp/$path" ]]; then
|
||||
rm -rf "$TARGET_DIR/$path"
|
||||
mkdir -p "$TARGET_DIR/$(dirname "$path")"
|
||||
cp -R "$preserve_tmp/$path" "$TARGET_DIR/$path"
|
||||
fi
|
||||
done
|
||||
rm -rf "$preserve_tmp"
|
||||
fi
|
||||
}
|
||||
|
||||
step "Installing Mosaic framework"
|
||||
|
||||
mkdir -p "$TARGET_DIR"
|
||||
select_install_mode
|
||||
|
||||
if command -v rsync >/dev/null 2>&1; then
|
||||
rsync -a --delete "$SOURCE_DIR/" "$TARGET_DIR/"
|
||||
if [[ "$INSTALL_MODE" == "keep" ]]; then
|
||||
ok "Install mode: keep local SOUL.md/memory while updating framework"
|
||||
else
|
||||
rm -rf "$TARGET_DIR"/*
|
||||
cp -R "$SOURCE_DIR"/* "$TARGET_DIR"/
|
||||
ok "Install mode: overwrite existing files"
|
||||
fi
|
||||
|
||||
sync_framework
|
||||
|
||||
chmod +x "$TARGET_DIR"/bin/*
|
||||
chmod +x "$TARGET_DIR"/install.sh
|
||||
|
||||
|
||||
@@ -8,12 +8,13 @@
|
||||
#
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
$ArchiveUrl = "https://git.mosaicstack.dev/mosaic/bootstrap/archive/main.zip"
|
||||
$BootstrapRef = if ($env:MOSAIC_BOOTSTRAP_REF) { $env:MOSAIC_BOOTSTRAP_REF } else { "main" }
|
||||
$ArchiveUrl = "https://git.mosaicstack.dev/mosaic/bootstrap/archive/$BootstrapRef.zip"
|
||||
$WorkDir = Join-Path $env:TEMP "mosaic-bootstrap-$PID"
|
||||
$ZipPath = "$WorkDir.zip"
|
||||
|
||||
try {
|
||||
Write-Host "[mosaic] Downloading bootstrap archive..."
|
||||
Write-Host "[mosaic] Downloading bootstrap archive (ref: $BootstrapRef)..."
|
||||
New-Item -ItemType Directory -Path $WorkDir -Force | Out-Null
|
||||
Invoke-WebRequest -Uri $ArchiveUrl -OutFile $ZipPath -UseBasicParsing
|
||||
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
#
|
||||
set -eu
|
||||
|
||||
ARCHIVE_URL="https://git.mosaicstack.dev/mosaic/bootstrap/archive/main.tar.gz"
|
||||
BOOTSTRAP_REF="${MOSAIC_BOOTSTRAP_REF:-main}"
|
||||
ARCHIVE_URL="https://git.mosaicstack.dev/mosaic/bootstrap/archive/${BOOTSTRAP_REF}.tar.gz"
|
||||
TMPDIR_BASE="${TMPDIR:-/tmp}"
|
||||
WORK_DIR="$TMPDIR_BASE/mosaic-bootstrap-$$"
|
||||
|
||||
@@ -18,7 +19,7 @@ cleanup() {
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
echo "[mosaic] Downloading bootstrap archive..."
|
||||
echo "[mosaic] Downloading bootstrap archive (ref: $BOOTSTRAP_REF)..."
|
||||
|
||||
mkdir -p "$WORK_DIR"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user