name: Automated Updates description: How the devbox automatically updates llm-agents (claude-code) via GitHub Actions and systemd timers. Use when debugging update failures or understanding the update flow.
Automated Updates
The devbox automatically keeps llm-agents packages up to date via a GitHub Actions + systemd timer pipeline.
What Gets Updated
The pipeline updates the llm-agents input in flake.lock, which provides:
- claude-code: Official Claude Code CLI
- ccusage: Usage analytics and statusline
- beads: Distributed issue tracker
- opencode: OpenCode CLI (alternative AI coding tool)
- ccusage-opencode: Usage tracking for OpenCode
All packages use Numtide's binary cache for fast updates.
Note: The oh-my-opencode plugin is NOT managed by this pipeline. It's an npm package installed per-machine via npx oh-my-opencode install. See the setting-up-oh-my-opencode skill for configuration details.
How It Works
┌─────────────────────────────────────────────────────────────┐
│ GitHub Actions (every 4 hours) │
│ │
│ 1. update-llm-agents.yml runs │
│ 2. Updates flake.lock (llm-agents input only) │
│ 3. Opens/updates PR on auto/update-llm-agents branch │
│ 4. Enables auto-merge (squash) │
│ │
│ CI (ci.yml) runs nix flake check on PR │
│ ↓ │
│ Checks pass → PR auto-merges to main │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ Devbox (systemd timer, every 4 hours) │
│ │
│ 1. pull-workstation.timer triggers │
│ 2. Fetches origin/main │
│ 3. If updates: git pull --ff-only │
│ 4. Runs: home-manager switch --flake .#dev │
└─────────────────────────────────────────────────────────────┘
Components
| Component | Location | Purpose |
|---|---|---|
ci.yml | .github/workflows/ | Runs nix flake check on PRs |
update-llm-agents.yml | .github/workflows/ | Updates llm-agents, opens PR with auto-merge |
UPDATE_TOKEN | GitHub Secrets | PAT for PR creation + CI triggers |
pull-workstation | ~/.local/bin/ | Script to pull + apply home-manager |
pull-workstation.timer | systemd user | Triggers every 4h + 10min after boot |
home-manager-auto-expire.timer | systemd user | Cleans old generations daily |
Checking Status
GitHub Side
# Recent workflow runs
gh run list --workflow=update-llm-agents.yml --limit=5
# Open update PRs
gh pr list --head auto/update-llm-agents
# CI status on a PR
gh pr checks <pr-number>
Devbox Side
# Timer status
systemctl --user status pull-workstation.timer
systemctl --user status home-manager-auto-expire.timer
# When timers will next run
systemctl --user list-timers
# Recent pull-workstation runs
journalctl --user -u pull-workstation -n 50
# Recent auto-expire runs
journalctl --user -u home-manager-auto-expire -n 50
Manual Trigger
Trigger GitHub Update
gh workflow run update-llm-agents.yml
Trigger Devbox Pull
~/.local/bin/pull-workstation
Or via systemd:
systemctl --user start pull-workstation
Troubleshooting
PR not being created
- Check workflow ran:
gh run list --workflow=update-llm-agents.yml --limit=1 - Check for errors:
gh run view <run-id> --log - Verify
UPDATE_TOKENsecret exists:gh secret list
PR not auto-merging
- Check CI passed:
gh pr checks <pr-number> - Check auto-merge is enabled:
gh pr view <pr-number> - Check branch protection: Settings → Branches → main
Devbox not pulling updates
- Check timer is active:
systemctl --user status pull-workstation.timer - Check for dirty working tree:
git -C ~/projects/workstation status - Check logs:
journalctl --user -u pull-workstation -n 50 - Manual test:
~/.local/bin/pull-workstation
"Working tree not clean" error
The pull script refuses to run if there are uncommitted changes:
cd ~/projects/workstation
git status
# Either commit, stash, or discard changes
SSH errors in pull-workstation
The script uses BatchMode=yes which fails if:
- SSH key missing: Check
~/.ssh/id_ed25519_githubexists - Host key missing: Run
ssh -T git@github.comonce manually
Old generations piling up
Check auto-expire is running:
systemctl --user status home-manager-auto-expire.timer
journalctl --user -u home-manager-auto-expire -n 20
Manual cleanup:
home-manager expire-generations "-7 days"
nix-collect-garbage
Configuration
Update Frequency
Both GitHub Action and devbox timer run every 4 hours. To change:
GitHub Action: Edit .github/workflows/update-llm-agents.yml:
schedule:
- cron: '0 */4 * * *' # Change */4 to desired interval
Devbox timer: Edit users/dev/home.linux.nix:
Timer = {
OnStartupSec = "10min";
OnUnitInactiveSec = "4h"; # Change to desired interval
};
Generation Retention
Edit users/dev/home.linux.nix:
services.home-manager.autoExpire = {
frequency = "daily";
timestamp = "-7 days"; # Keep generations from last 7 days
};