diff --git a/README.md b/README.md index 977e1a5..d79a117 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ Optionally run the setup script to share agent state (`.claude`, `.pi`, `.agents ~/Projects/agent-container/setup.sh ``` -It moves existing state into the container home and symlinks it back into `$HOME`, so containerized tools and the `*-local` variants share logins, settings, and history. See "Sharing agent state between host and container via symlinks" below. The script is safe to re-run and skips items it cannot handle safely. +It shows a plan first and asks for confirmation before moving anything (`--dry-run` previews without changes, `--yes` skips the question). Existing state is moved into the container home and symlinked back into `$HOME`, so containerized tools and the `*-local` variants share logins, settings, and history. See "Sharing agent state between host and container via symlinks" below. The script is safe to re-run and skips items it cannot handle safely. The container home directory at `$XDG_DATA_HOME/agent-container/container-home/` serves as a central `$HOME` inside every container, independent of which project directory you start in. Everything written to `$HOME` inside the container persists there. diff --git a/setup.sh b/setup.sh index bbc2ac3..983e085 100755 --- a/setup.sh +++ b/setup.sh @@ -8,7 +8,9 @@ # share logins, settings, and history. See README.md, section "Sharing agent # state between host and container via symlinks". # -# Safe to re-run: already shared items are detected and left alone. +# Shows a plan and asks for confirmation before changing anything: +# --dry-run (-n) show the plan without changing anything +# --yes (-y) apply the plan without asking set -euo pipefail @@ -34,8 +36,57 @@ FILES=( info() { printf ' %s\n' "$*"; } warn() { printf ' warning: %s\n' "$*" >&2; } -# Print the action taken for an item, shared by both helpers below. -_report() { +usage() { + cat <&2; exit 1 ;; + esac + shift + done + printf 'Sharing agent state via %s\n\n' "$CONTAINER_HOME" - local item - for item in "${DIRS[@]}"; do share_item "$item" no; done - for item in "${FILES[@]}"; do share_item "$item" yes; done + # Plan everything up front; decide() changes nothing, so applying the + # stored actions afterwards is exactly what the plan describes. + local -a items=() actions=() + local item action + for item in "${DIRS[@]}"; do items+=("$item"); actions+=("$(decide "$item" no)"); done + for item in "${FILES[@]}"; do items+=("$item"); actions+=("$(decide "$item" yes)"); done + + printf 'Plan:\n' + local i + for i in "${!items[@]}"; do + describe "${actions[$i]}" "${items[$i]}" + done + + if [ "$dry_run" = "yes" ]; then + printf '\nDry run: nothing changed.\n' + return + fi + + if [ "$ask" = "yes" ]; then + if [ -t 0 ]; then + local reply + printf '\nApply these changes? [y/N] ' + read -r reply + case "$reply" in + y|Y|yes|Yes) ;; + *) printf 'Aborted, nothing changed.\n'; return ;; + esac + else + printf '\nNot running interactively; pass --yes to apply the plan.\n' >&2 + exit 1 + fi + fi + + printf '\n' + for i in "${!items[@]}"; do + apply "${items[$i]}" "${actions[$i]}" + done if [ -f "$HOME/.gitconfig" ] && [ ! -e "$CONTAINER_HOME/.gitconfig" ]; then printf '\n'