---
name: ao-update
description: "Update AO files from a newer version while preserving project state."
---

# AO Update

Update AO core files from a newer version while preserving your project-specific state.

## Usage

```
/ao-update [source_path] [--dry-run] [--yes] [--from-home]
```

## Arguments

- `source_path` - (Optional) Path to AO version, or leave empty to be prompted
- `--dry-run` - Show what would change without making changes
- `--yes` - Skip confirmation prompts
- `--from-home` - Auto-clone from https://github.com/weholt/ao.git

## Interactive Mode (Default)

When no source_path is provided, ask the user where to update from:

```
Where would you like to update AO from?

[1] From official repository (https://github.com/weholt/ao.git)
    - Will clone to temp folder automatically
    - Uses cached clone if available (faster)

[2] From local path
    - Specify a local folder path

[3] From custom URL
    - Specify a git repository URL

[4] Cancel
```

## Procedure

### 0. Interactive Source Selection (if no source_path provided)

```bash
# Default home repository
AO_HOME_REPO="${AO_UPDATE_HOME_REPO:-https://github.com/weholt/ao.git}"

# Cache location (system temp, kept for faster updates)
CACHE_DIR="${AO_UPDATE_CACHE_DIR:-${TMPDIR:-/tmp}/ao-cache}"
CACHE_CLONE="$CACHE_DIR/repo"

# Determine source
SOURCE_DIR=""

if [ -z "$1" ] && [ "$1" != "--from-home" ]; then
  # Ask user where to update from (via AskUserQuestion or interview)
  # See "Interactive Questions" section below
elif [ "$1" = "--from-home" ]; then
  SOURCE_DIR="$CACHE_CLONE"
fi

# Ensure source exists
if [ ! -d "$SOURCE_DIR" ]; then
  # Will clone in next step
  NEED_CLONE=true
fi
```

### 1. Clone From Repository (if needed)

```bash
# Default repo
AO_HOME_REPO="${AO_UPDATE_HOME_REPO:-https://github.com/weholt/ao.git}"
CACHE_DIR="${AO_UPDATE_CACHE_DIR:-${TMPDIR:-/tmp}/ao-cache}"
CACHE_CLONE="$CACHE_DIR/repo"

clone_from_repo() {
  local repo_url="$1"
  local target_dir="$2"

  echo "Cloning AO from $repo_url..."

  # Create cache directory
  mkdir -p "$CACHE_DIR"

  # Clone or update
  if [ -d "$target_dir" ]; then
    echo "Updating cached clone..."
    git -C "$target_dir" fetch origin
    git -C "$target_dir" checkout main
    git -C "$target_dir" reset --hard origin/main
  else
    echo "Cloning to cache (this will be kept for future updates)..."
    git clone --depth 1 "$repo_url" "$target_dir"
  fi

  echo "✓ Source ready: $target_dir"
}

# If user selected "From official repository" or --from-home
if [ "$FROM_HOME" = "true" ] || [ "$1" = "--from-home" ]; then
  clone_from_repo "$AO_HOME_REPO" "$CACHE_CLONE"
  SOURCE_DIR="$CACHE_CLONE"
fi

# If user specified custom URL
if [ -n "$CUSTOM_URL" ]; then
  TEMP_CLONE="$CACHE_DIR/custom-$(date +%s)"
  clone_from_repo "$CUSTOM_URL" "$TEMP_CLONE"
  SOURCE_DIR="$TEMP_CLONE"
fi
```

### 2. Pre-flight Checks

```bash
# Verify we're in a git repository
git rev-parse --git-dir >/dev/null 2>&1 || {
  echo "Error: Not in a git repository. ao-update requires git for safety."
  exit 1
}

# Verify source folder exists
if [ ! -d "$SOURCE_DIR" ]; then
  echo "Error: Source directory '$SOURCE_DIR' does not exist."
  exit 1
fi

# Verify source folder has AO structure
if [ ! -d "$SOURCE_DIR/.ao" ] && [ ! -d "$SOURCE_DIR/.github/agents" ]; then
  echo "Error: Source directory does not appear to be an AO repository."
  echo "       Expected to find .ao/ or .github/agents/ in source."
  exit 1
fi

# Check working tree status
if ! git diff-index --quiet HEAD -- 2>/dev/null; then
  echo "Warning: Working tree has uncommitted changes."
  echo "         Recommend committing or stashing before update."
  if [ -z "$AO_UPDATE_YES" ] && [ "$YES_MODE" != "true" ]; then
    read -p "Continue anyway? [y/N] " -n 1 -r
    echo
    if [[ ! $REPLY =~ ^[Yy]$ ]]; then
      exit 1
    fi
  fi
fi
```

### 3. Create Branch

```bash
# Generate branch name
TIMESTAMP=$(date +%s)
BRANCH_PREFIX="${AO_UPDATE_BRANCH_PREFIX:-ao-update-}"
BRANCH_NAME="${BRANCH_PREFIX}${TIMESTAMP}"

# Create and checkout branch
git checkout -b "$BRANCH_NAME" || {
  echo "Error: Failed to create branch '$BRANCH_NAME'."
  exit 1
}

echo "✓ Created branch: $BRANCH_NAME"
```

### 4. Categorize and Scan Files

```bash
# File patterns
PROTECTED_PATTERNS=(".agent/ops")
MERGEABLE_PATTERNS=(".ao/skills/*/preferences.md" ".ao/skills/*/preferences.pattern")
OVERWRITABLE_PATTERNS=(
  ".ao/skills/*/*.md"
  ".ao/prompts/*.md"
  ".ao/agents/*.md"
  ".ao/reference/*.md"
  ".github/agents/*.md"
  ".github/prompts/*.md"
  ".claude/commands/ao-*.md"
  ".claude/agents/ao-*.md"
  ".opencode/commands/ao-*.md"
)

# Build file lists
declare -a PROTECTED_FILES MERGEABLE_FILES OVERWRITABLE_FILES
declare -a LOCAL_MODS

# Protected files
find .agent/ops -type f 2>/dev/null | while read -r f; do
  PROTECTED_FILES+=("$f")
done

# Mergeable files
for pattern in "${MERGEABLE_PATTERNS[@]}"; do
  compgen -G "$pattern" 2>/dev/null | while read -r f; do
    MERGEABLE_FILES+=("$f")
  done
done

# Overwritable files and local mods
for pattern in "${OVERWRITABLE_PATTERNS[@]}"; do
  git ls-files "$pattern" 2>/dev/null | while read -r f; do
    OVERWRITABLE_FILES+=("$f")
    # Check for local modifications
    if ! git diff --quiet -- "$f" 2>/dev/null; then
      LOCAL_MODS+=("$f")
    fi
  done
done
```

### 5. Dry-Run Summary

Always show summary first:

```bash
echo ""
echo "=== AO Update Summary ==="
echo "Source: $SOURCE_DIR"
echo "Branch: $BRANCH_NAME"
echo ""

echo "Files that will be PRESERVED (protected):"
printf "  - %s\n" "${PROTECTED_FILES[@]}" | head -5
if [ ${#PROTECTED_FILES[@]} -gt 5 ]; then
  echo "  ... and $((${#PROTECTED_FILES[@]} - 5)) more"
fi
echo ""

echo "Files that will be MERGED (your settings preserved):"
printf "  - %s\n" "${MERGEABLE_FILES[@]}"
echo ""

echo "Files that will be OVERWRITABLE:"
echo "  Total: ${#OVERWRITABLE_FILES[@]} files"
echo ""

if [ ${#LOCAL_MODS[@]} -gt 0 ]; then
  echo "⚠️  WARNING: Locally modified files that will be overwritten:"
  printf "  - %s\n" "${LOCAL_MODS[@]}"
  echo ""
fi
```

Ask for confirmation (unless `--yes` or `AO_UPDATE_YES`):

```bash
if [ -z "$AO_UPDATE_YES" ] && [ "$YES_MODE" != "true" ]; then
  read -p "Continue with update? [y/N] " -n 1 -r
  echo
  if [[ ! $REPLY =~ ^[Yy]$ ]]; then
    echo "Update cancelled."
    git checkout main
    git branch -D "$BRANCH_NAME"
    exit 3
  fi
fi
```

### 6. Execute Update

```bash
# Use rsync if available, else cp
if command -v rsync >/dev/null 2>&1; then
  echo "Copying files..."
  # Copy overwritable directories
  [ -d "$SOURCE_DIR/.ao/skills" ] && rsync -av --checksum --delete "$SOURCE_DIR/.ao/skills/" .ao/skills/
  [ -d "$SOURCE_DIR/.ao/prompts" ] && rsync -av --checksum --delete "$SOURCE_DIR/.ao/prompts/" .ao/prompts/
  [ -d "$SOURCE_DIR/.ao/agents" ] && rsync -av --checksum --delete "$SOURCE_DIR/.ao/agents/" .ao/agents/
  [ -d "$SOURCE_DIR/.ao/reference" ] && rsync -av --checksum --delete "$SOURCE_DIR/.ao/reference/" .ao/reference/
  [ -d "$SOURCE_DIR/.github/agents" ] && rsync -av --checksum "$SOURCE_DIR/.github/agents/" .github/agents/
  [ -d "$SOURCE_DIR/.github/prompts" ] && rsync -av --checksum "$SOURCE_DIR/.github/prompts/" .github/prompts/
  [ -d "$SOURCE_DIR/.claude/commands" ] && rsync -av --checksum "$SOURCE_DIR/.claude/commands/ao-"*.md .claude/commands/
  [ -d "$SOURCE_DIR/.claude/agents" ] && rsync -av --checksum "$SOURCE_DIR/.claude/agents/ao-"*.md .claude/agents/
  [ -d "$SOURCE_DIR/.opencode/commands" ] && rsync -av --checksum "$SOURCE_DIR/.opencode/commands/ao-"*.md .opencode/commands/
else
  echo "Copying files (cp)..."
  [ -d "$SOURCE_DIR/.ao/skills" ] && cp -rv "$SOURCE_DIR/.ao/skills/"* .ao/skills/
  # ... similar for other directories
fi
```

### 7. Merge Preference Files

For each preferences file, preserve user's keys:

```bash
merge_preferences() {
  local target_file="$1"
  local source_file="$2"

  # If target doesn't exist, just copy source
  [ ! -f "$target_file" ] && { cp "$source_file" "$target_file"; return; }
  # If source doesn't exist, keep target as-is
  [ ! -f "$source_file" ] && return

  # Extract user's keys from target (simple line-based approach)
  # For production, use proper YAML/frontmatter parser
  local temp_file=$(mktemp)

  # Start with source
  cp "$source_file" "$temp_file"

  # User's keys win - keep lines from target that aren't in source's frontmatter
  # This is simplified; real implementation should parse YAML properly
  awk '
    /^---/ { in_frontmatter = !in_frontmatter; next }
    in_frontmatter && /^[a-z_]+:/ { keys[$1] = 1; next }
  ' "$target_file"

  # For robust YAML merging, use a tool like yq or python
}

# Merge each preferences file
for pref_file in "${MERGEABLE_FILES[@]}"; do
  if [ -f "$SOURCE_DIR/$pref_file" ]; then
    merge_preferences "$pref_file" "$SOURCE_DIR/$pref_file"
  fi
done
```

### 8. Post-Update Summary

```bash
echo ""
echo "=== Update Complete ==="
echo ""
echo "Branch: $BRANCH_NAME"
echo ""
echo "Files changed:"
git diff --stat main
echo ""
echo "To review changes:"
echo "  git diff main"
echo ""
echo "To commit and merge:"
echo "  git add -A"
echo "  git commit -m 'Update AO to latest version'"
echo "  git checkout main"
echo "  git merge $BRANCH_NAME"
echo ""
echo "=== Rollback Instructions ==="
echo "If something went wrong:"
echo "  git checkout main           # Return to main branch"
echo "  git branch -D $BRANCH_NAME  # Delete update branch"
echo "Your project state is restored."
echo ""
```

## Interactive Questions

When no arguments are provided, ask the user using `AskUserQuestion` (Claude) or interview tool (OpenCode):

### Question 1: Where to update from?

```
Where would you like to update AO from?

Options:
  [1] From official repository
      - Clone from https://github.com/weholt/ao.git
      - Uses cached clone if available (faster)
      - Always gets latest version

  [2] From local path
      - Use a local folder
      - Specify path below

  [3] From custom URL
      - Clone from a different repository
      - Specify URL below

  [4] Cancel
      - Cancel the update
```

### Question 2: (If option 2) Specify local path

```
Enter the local path to the AO version:
(Prompt for path input)
```

### Question 3: (If option 3) Specify repository URL

```
Enter the git repository URL:
(Prompt for URL input)
```

### Question 4: Dry-run or execute?

```
What would you like to do?

Options:
  [1] Preview changes (dry-run)
      - Show what would change
      - No files will be modified

  [2] Execute update
      - Create branch and copy files
      - You'll review changes before committing
```

## Environment Variables

| Variable | Purpose | Default |
|----------|---------|---------|
| `AO_UPDATE_HOME_REPO` | Default home repository URL | `https://github.com/weholt/ao.git` |
| `AO_UPDATE_CACHE_DIR` | Cache directory for cloned repos | `$TMPDIR/ao-cache` (Unix) or `%TEMP%\ao-cache` (Windows) |
| `AO_UPDATE_BRANCH_PREFIX` | Branch name prefix | `ao-update-` |
| `AO_UPDATE_YES` | Auto-confirm prompts | (empty) |

## Examples

### Interactive mode (default)
```bash
/ao-update
# Will prompt: "Where would you like to update AO from?"
```

### From official repository
```bash
/ao-update --from-home
# Auto-clones from github.com/weholt/ao.git
```

### From local path
```bash
/ao-update /path/to/ao/folder
```

### Dry-run only
```bash
/ao-update --from-home --dry-run
```

### Auto-confirm
```bash
/ao-update --from-home --yes
```

## Cache Behavior

The official repository is cached in:
- Unix/Linux/Mac: `/tmp/ao-cache/repo` (or `$TMPDIR/ao-cache/repo`)
- Windows: `%TEMP%\ao-cache\repo`

**Cache is kept between updates** for faster subsequent updates.

On each `--from-home` update:
1. If cache exists: `git fetch` + `git reset --hard origin/main`
2. If cache doesn't exist: `git clone --depth 1`

## Safety Features

1. **Always creates git branch first** - Your work is safe on main branch
2. **Preserves .agent/ops/** - Project state never touched
3. **Merges preferences** - Your settings win on conflict
4. **Warns about local mods** - Shows what would be overwritten
5. **Interactive by default** - Asks before making changes
6. **Rollback documented** - Clear instructions if something goes wrong
7. **Cached updates** - Faster on subsequent runs

## File Categories

| Category | Files | Action |
|----------|-------|--------|
| **Protected** | `.agent/ops/*` | Never touch |
| **Mergeable** | `.ao/skills/*/preferences.md` | Your keys win |
| **Overwritable** | Core AO files | Copy from source |

## Rollback

If update causes problems:

```bash
git checkout main           # Return to main
git branch -D ao-update-*   # Delete update branch
# Your state is restored
```

---

## Next Steps

After update completes, ask what to do next:

- [Review] Review the changes with git diff
- [Commit] Create commit and merge to main
- [Rollback] Revert the update
- [Help] Show all available options
