diff --git a/README.md b/README.md
index c045ed1..fac0049 100644
--- a/README.md
+++ b/README.md
@@ -103,6 +103,8 @@ Two presets get you started. **Recommended** turns Apple Intelligence, analytics
`apply`, `undo`, `background` and `clean` show the change first and ask. `--dry-run` shows it and stops, and `--yes` skips the question.
+Tab completion for zsh, bash and fish is in [completions](completions). Each script says where it goes.
+
diff --git a/completions/_removemacai b/completions/_removemacai
new file mode 100644
index 0000000..665e01a
--- /dev/null
+++ b/completions/_removemacai
@@ -0,0 +1,64 @@
+#compdef removemacai
+# zsh completion for removemacai. Put it in a folder on $fpath.
+
+_removemacai_features() {
+ local -a features
+ features=(${(f)"$(removemacai features 2>/dev/null | awk '{ n = $1; sub(/^[^ ]+ +/, ""); print n "[" $0 "]" }')"})
+ _values -s , feature $features
+}
+
+_removemacai_tweaks() {
+ local -a tweaks
+ tweaks=(${(f)"$(removemacai tweaks --json 2>/dev/null | sed -n 's/.*"id" : "\(.*\)".*/\1/p')"})
+ _wanted tweaks expl tweak compadd -a tweaks
+}
+
+# Only these can go in an exported profile; the rest are per-user settings.
+_removemacai_profile_tweaks() {
+ local -a tweaks
+ tweaks=(${(f)"$(removemacai tweaks --json 2>/dev/null |
+ awk '/"id" :/ { gsub(/[",]/, "", $3); id = $3 } /"profile" : true/ { print id }')"})
+ _wanted tweaks expl tweak compadd -a tweaks
+}
+
+_removemacai() {
+ local -a commands
+ commands=(
+ 'status:show what is on and how much space the models take'
+ 'off:turn Apple Intelligence off'
+ 'on:turn Apple Intelligence back on, keep the other tweaks'
+ 'features:list the feature names'
+ 'tweaks:list every tweak and whether it is applied'
+ 'apply:apply tweaks'
+ 'undo:undo tweaks'
+ "background:other apps' background items"
+ 'clean:show space to get back, move some to the Trash'
+ 'export:write the profile for an MDM'
+ 'revert:undo everything RemoveMacAI changed'
+ 'app:open the RemoveMacAI app'
+ )
+ if (( CURRENT == 2 )); then
+ _describe command commands
+ return
+ fi
+ shift words
+ (( CURRENT-- ))
+ local dry='--dry-run[show what would change, change nothing]' yes='--yes[do not ask]'
+ local keep='--keep[leave these features on]:feature:_removemacai_features'
+ local preset='--preset[apply a preset, Apple Intelligence off included]:preset:(recommended privacy)'
+ case $words[1] in
+ off) _arguments $keep $dry $yes ;;
+ tweaks) _arguments '--json[print JSON for scripts]' ;;
+ apply) _arguments $preset $dry $yes '*:tweak:_removemacai_tweaks' ;;
+ undo) _arguments $dry $yes '*:tweak:_removemacai_tweaks' ;;
+ background) _arguments $yes '1:action:(off on)' '*: :_guard "^-*" label' ;;
+ clean)
+ _arguments $dry $yes \
+ '*:item:(aerials installers ios-firmware derived-data device-support simulators caches snapshots)' ;;
+ export)
+ _arguments $preset $keep '--no-ai[leave Apple Intelligence out]' \
+ '1:file:_files -g "*.mobileconfig"' '*:tweak:_removemacai_profile_tweaks' ;;
+ esac
+}
+
+_removemacai "$@"
diff --git a/completions/removemacai.bash b/completions/removemacai.bash
new file mode 100644
index 0000000..e27f1c5
--- /dev/null
+++ b/completions/removemacai.bash
@@ -0,0 +1,58 @@
+# bash completion for removemacai. Source it from ~/.bash_profile.
+
+_removemacai_features() {
+ removemacai features 2>/dev/null | awk '{ print $1 }'
+}
+
+_removemacai_tweaks() {
+ removemacai tweaks --json 2>/dev/null | sed -n 's/.*"id" : "\(.*\)".*/\1/p'
+}
+
+# Only these can go in an exported profile; the rest are per-user settings.
+_removemacai_profile_tweaks() {
+ removemacai tweaks --json 2>/dev/null |
+ awk '/"id" :/ { gsub(/[",]/, "", $3); id = $3 } /"profile" : true/ { print id }'
+}
+
+_removemacai() {
+ local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]}
+ COMPREPLY=()
+ if [ "$COMP_CWORD" -eq 1 ]; then
+ COMPREPLY=($(compgen -W "status off on features tweaks apply undo background clean export revert app" -- "$cur"))
+ return
+ fi
+ case $prev in
+ --preset)
+ COMPREPLY=($(compgen -W "recommended privacy" -- "$cur"))
+ return ;;
+ --keep)
+ # The names are comma-separated, so complete the one after the last comma and leave room for the next.
+ local last=${cur##*,}
+ COMPREPLY=($(compgen -P "${cur%"$last"}" -W "$(_removemacai_features)" -- "$last"))
+ type compopt &>/dev/null && compopt -o nospace
+ return ;;
+ esac
+ local options= names= file
+ case ${COMP_WORDS[1]} in
+ off) options="--keep --dry-run --yes" ;;
+ tweaks) options="--json" ;;
+ apply) options="--preset --dry-run --yes" names=$(_removemacai_tweaks) ;;
+ undo) options="--dry-run --yes" names=$(_removemacai_tweaks) ;;
+ background) options="--yes"; [ "$COMP_CWORD" -eq 2 ] && names="off on" ;;
+ clean)
+ options="--dry-run --yes"
+ names="aerials installers ios-firmware derived-data device-support simulators caches snapshots" ;;
+ export)
+ options="--preset --keep --no-ai" names=$(_removemacai_profile_tweaks)
+ if [[ $cur != -* ]]; then
+ while IFS= read -r file; do COMPREPLY+=("$file"); done < <(compgen -f -- "$cur")
+ fi ;;
+ esac
+ if [[ $cur == -* ]]; then
+ COMPREPLY=($(compgen -W "$options" -- "$cur"))
+ else
+ COMPREPLY+=($(compgen -W "$names" -- "$cur"))
+ fi
+}
+
+complete -o filenames -F _removemacai removemacai
diff --git a/completions/removemacai.fish b/completions/removemacai.fish
new file mode 100644
index 0000000..49c50f5
--- /dev/null
+++ b/completions/removemacai.fish
@@ -0,0 +1,56 @@
+# fish completion for removemacai. Put it in ~/.config/fish/completions.
+
+# --keep takes comma-separated names, so each one keeps the names typed before it.
+function __removemacai_features
+ set -l before (string replace -ra -- '^--keep=|[^,]*$' '' (commandline -ct))
+ printf '%s\n' $before(removemacai features 2>/dev/null | string replace -r -- '\s+' \t)
+end
+
+function __removemacai_tweaks
+ removemacai tweaks --json 2>/dev/null | string replace -rf '.*"id" : "(.*)".*' '$1'
+end
+
+# Only these can go in an exported profile; the rest are per-user settings.
+function __removemacai_profile_tweaks
+ removemacai tweaks --json 2>/dev/null |
+ awk '/"id" :/ { gsub(/[",]/, "", $3); id = $3 } /"profile" : true/ { print id }'
+end
+
+# Checks the subcommand itself, so `background off` doesn't count as `off`.
+function __removemacai_using
+ set -l words (commandline -opc)
+ contains -- "$words[2]" $argv
+end
+
+set -l commands status off on features tweaks apply undo background clean export revert app
+set -l top "not __fish_seen_subcommand_from $commands"
+
+complete -c removemacai -f
+complete -c removemacai -n $top -a status -d 'Show what is on and how much space the models take'
+complete -c removemacai -n $top -a off -d 'Turn Apple Intelligence off'
+complete -c removemacai -n $top -a on -d 'Turn Apple Intelligence back on, keep the other tweaks'
+complete -c removemacai -n $top -a features -d 'List the feature names'
+complete -c removemacai -n $top -a tweaks -d 'List every tweak and whether it is applied'
+complete -c removemacai -n $top -a apply -d 'Apply tweaks'
+complete -c removemacai -n $top -a undo -d 'Undo tweaks'
+complete -c removemacai -n $top -a background -d "Other apps' background items"
+complete -c removemacai -n $top -a clean -d 'Show space to get back, move some to the Trash'
+complete -c removemacai -n $top -a export -d 'Write the profile for an MDM'
+complete -c removemacai -n $top -a revert -d 'Undo everything RemoveMacAI changed'
+complete -c removemacai -n $top -a app -d 'Open the RemoveMacAI app'
+
+complete -c removemacai -n '__removemacai_using off export' -l keep -x \
+ -a '(__removemacai_features)' -d 'Leave these features on'
+complete -c removemacai -n '__removemacai_using apply export' -l preset -x \
+ -a 'recommended privacy' -d 'Apply a preset, Apple Intelligence off included'
+complete -c removemacai -n '__removemacai_using off apply undo clean' -l dry-run \
+ -d 'Show what would change, change nothing'
+complete -c removemacai -n '__removemacai_using off apply undo background clean' -l yes -d 'Do not ask'
+complete -c removemacai -n '__removemacai_using tweaks' -l json -d 'Print JSON for scripts'
+complete -c removemacai -n '__removemacai_using export' -l no-ai -d 'Leave Apple Intelligence out'
+complete -c removemacai -n '__removemacai_using apply undo' -a '(__removemacai_tweaks)'
+complete -c removemacai -n '__removemacai_using export' -a '(__removemacai_profile_tweaks)'
+complete -c removemacai -n '__removemacai_using export' -F
+complete -c removemacai -n '__removemacai_using clean' \
+ -a 'aerials installers ios-firmware derived-data device-support simulators caches snapshots'
+complete -c removemacai -n '__removemacai_using background; and test (count (commandline -opc)) -eq 2' -a 'off on'