Skip to content

cli-tools autoupdate: keep the checkout current on a timer - #19

Merged
ralyodio merged 1 commit into
masterfrom
worktree-autoupdate
Aug 21, 2026
Merged

cli-tools autoupdate: keep the checkout current on a timer#19
ralyodio merged 1 commit into
masterfrom
worktree-autoupdate

Conversation

@ralyodio

Copy link
Copy Markdown
Contributor

cli-tools autoupdate --install writes a systemd user timer that runs cli-tools update --auto daily. --hours N changes the interval, --remove takes it away, bare autoupdate shows when it last ran.

cli-tools autoupdate --install
cli-tools update --auto --force        # ignore the once-a-day stamp
journalctl --user -u cli-tools-update  # what it decided, and why

Most of this is about deciding not to act

The install is symlinks into a working tree, so "update" means moving somebody's real checkout. An unattended git pull that discards work is far worse than a command being a day out of date. So update --auto proceeds only when all of these hold, and names the blocker otherwise:

  • the tree is clean,
  • HEAD is the default branch (origin/HEAD, else master),
  • nothing is unpushed,
  • and it is genuinely behind.

A checkout parked on a feature branch is left alone and says so. Verified on this very branch:

update --auto: skipped — on worktree-autoupdate, not master — a branch you are working on is yours to move

Two things that would have made this silently useless

The unit bakes today's PATH in. A user unit starts with roughly /usr/bin:/bin, while every command here runs through a npx --yes tsx shebang whose node is a version manager's shim under $HOME. Without the captured PATH the timer fires perfectly on schedule, fails to find node, and nothing anywhere looks broken.

Status comes from --porcelain=v2, not the human-readable "Your branch is behind by N commits" line — that string is localised, so on a non-English machine the check would never fire.

Smaller: the stamp is written before the work, so a failing fetch doesn't retry on every invocation while the network is down; and a stamp in the future reads as due, since a clock that moved could otherwise stall updates for days.

Also refreshed

  • /tools:install's command table, which had gone three commands stale (affiliate, ask-web, tts, plus generate-names which was never listed), with a note that cli-tools list reads bin/ and is right when the table isn't.
  • The key list in cli-tools --help, which still named only openai and anthropic.

Verification

  • 274 tests pass (21 new), pnpm typecheck clean.
  • The decision logic is tested without git by parsing real --porcelain=v2 output; the unit rendering is tested as pure strings.

🤖 Generated with Claude Code

`cli-tools autoupdate --install` writes a systemd user timer that runs
`cli-tools update --auto` daily. `--hours N` changes the interval,
`--remove` takes it away, bare `autoupdate` shows when it last ran.

Almost all of `update --auto` is about deciding not to act, and that is
the point. The install is symlinks into a working tree, so updating
moves somebody's real checkout — an unattended pull that discards work
is far worse than a command being a day out of date. It proceeds only
when the tree is clean, HEAD is the default branch, nothing is unpushed,
and it is genuinely behind. Every refusal names the specific blocker,
because the failure mode this guards against is concluding auto-update
is broken while it is working exactly as designed.

Two things that would have made this silently useless:

- The unit bakes today's PATH in. A user unit starts with roughly
  /usr/bin:/bin, while every command here runs through a `npx --yes tsx`
  shebang whose node is a version manager's shim under $HOME. Without
  it the timer fires on schedule, fails to find node, and nothing looks
  wrong anywhere.
- Status is read from `--porcelain=v2`, not the human-readable "Your
  branch is behind by N commits" line, which is localised — on a
  non-English machine that check would never fire.

The stamp is written before the work rather than after, so a fetch that
fails does not mean a retry on every invocation while the network is
down. A stamp in the future reads as due, since a moved clock could
otherwise stall updates for days.

Also refreshes the /tools:install command doc, whose table had gone
three commands stale, and the key list in `cli-tools --help`, which
still named only openai and anthropic.

Suite is 274 tests.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

ThreatCrush Security Scan

6 finding(s)

MEDIUM: 1 | LOW: 5

Severity Rule Location
MEDIUM redos-nested-quantifier src/domain-free.ts:56
LOW insecure-temp-file test/blog.test.ts:73
LOW insecure-temp-file test/blog.test.ts:74
LOW insecure-temp-file test/credentials.test.ts:43
LOW insecure-temp-file test/credentials.test.ts:44
LOW secret-generic-api-key test/credentials.test.ts:208

Snippets are redacted; ThreatCrush never prints matched credential material.

@ralyodio
ralyodio merged commit 63b53c3 into master Aug 21, 2026
5 checks passed
@ralyodio
ralyodio deleted the worktree-autoupdate branch August 21, 2026 14:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant