Getting Codex to clean up after itself

Terminal listing Codex app-server-daemon release directories

A pet peeve of mine is software that doesn’t clean up after itself. If you are a Codex user, check out your ~/.codex/packages/app-server-daemon/releases I’m a relatively recent codex CLI user and already 12 releases of the app-server-daemon left there. Why? Laziness probably. If I left these files in place and they accumulated at this rate, we would have 219 of them one year in. They are around 330 MB apiece, so we are talking about 72 GB of wasted space.

Hazel to the rescue

On macOS when I have an automated filesystem cleanup or organizational task, I turn to Hazel. Just point Hazel to the directory you want to watch, give it some criteria and actions to perform on matching files and/or directories. My goal here is to keep only the two most recent releases. Honestly we could keep only the most recent release, but I’m being conservative. To clean up the releases, first add a folder to Hazel pointed to ~/.codex/packages/app-server-daemon/releases then a new rule “Clean previous .codex app server daemon releases”.

Add two (“All”) critera:

  1. Kind is Folder
  2. Passes shell script

For the script:

#!/bin/zsh

# Directory being evaluated
candidate="$1"
name="${candidate:t}"
parent="${candidate:h}"
platform='aarch64-apple-darwin'

# Check release-directory names without repeating a long expression.
is_release() {
    [[ $1 =~ ^[0-9]+\.[0-9]+\.[0-9]+-$platform$ ]]
}

is_release "$name" || exit 1

# Collect all valid release directories
versions=()

for path in "$parent"/*-aarch64-apple-darwin(N/); do
    version="${path:t}"

    if is_release "$version"; then
        versions+=("$version")
    fi
done

# Never delete anything if two or fewer versions exist
(( ${#versions} > 2 )) || exit 1

# Sort by numeric version components (ascending)
sorted=("${(@f)$(printf '%s\n' "${versions[@]}" |
    /usr/bin/sort -t. -k1,1n -k2,2n -k3,3n)}")

# Match only if this directory is NOT one of the newest two
[[ "$name" != "$sorted[-1]" && "$name" != "$sorted[-2]" ]]

Under the hood with the criterion script

Hazel supplies the full path of the folder it is evaluating as $1. The script saves that path in candidate, then uses zsh’s :t and :h modifiers to obtain its last path component (the release directory name) and its parent directory. Quoting those variables matters: it keeps the script correct even if a path contains spaces.1

The is_release helper is the guard rail. It refuses to match a candidate unless its name has the expected major.minor.patch-aarch64-apple-darwin form. The script calls it again while building the list, so an unrelated directory in releases cannot be selected for deletion. This version is deliberately specific to Apple Silicon. If Codex changes its release naming convention, or if you use an Intel Mac, adjust the platform value before enabling the rule.

(N/) is a zsh glob qualifier. The / limits the glob to directories, while N makes an empty match expand to nothing instead of producing an error. The versions array therefore contains only release directories that passed the name check.

The script exits with a non-zero status until there are at least three valid versions. Hazel treats that as a failed condition, so its Trash action cannot run. When there are three or more, sort compares the three numeric components of each version number in order. In zsh, sorted[-1] and sorted[-2] mean the last and second-to-last array items. The final test succeeds only for a directory that is neither of those two newest entries, which makes that directory eligible for the rule’s action.

We only match diretories that do not have the two most recent version numbers.

For the rule action, it’s simple Move to folder → Trash.

This is a modest workaround for a modest annoyance, but it is the sort of housekeeping that should not require attention every few weeks. Hazel keeps the release directory bounded, and moving old releases to the Trash leaves a recovery path if an update turns out to need an older copy.

I need to look for more leftover garbage. Where there’s one example of sloppiness, there’s probably more.

If you know of a better way to manage Codex’s retained releases, or have questions about the Hazel rule, please get in touch through my contact page.


  1. These are Zsh-specific expansion modifiers that won’t work in Bash; that’s why we have to specify /bin/zsh as the interpreter for the criterion shell script. ↩︎