Skip to content
Conduit

skill

watchjob

Run a long command under supervision and get told out loud when it ends. systemd-backed, never pgrep, announces failures too.

tommasobbianchi/watchjobcommunitylistedunreviewed

Set up

Skills are installed by placing their folder where your agent loads skills.

The download comes from github.com/tommasobbianchi/watchjob itself, not from our server, so what you get is exactly what its author published.

Install instructions come from the README

Skills do not use an MCP config. Follow the steps in the README below.

README

synced Oct 23, 2026

watchjob

skills.sh

Run a long command under supervision and get told out loud when it ends — locally or over ssh. An agent skill, and a pair of standalone shell scripts that work fine without one.

scripts/watchjob.sh orca-build --host me@buildbox -- \
  'cd ~/src/slicer && cmake --build build --config Release -j 14'

…blocks until the build ends, then says "orca build complete, 14 minutes." in the workshop and pushes the detail to your phone. On failure it says so, with the exit code and the last 8 journal lines.

The two bugs it exists to prevent

A build finished at 15:27 and was reported as "still linking" for the next 70 minutes. Two causes, both structural, both of which any agent with a shell will reproduce:

  1. The status check matched itself. pgrep -f "cmake --build build" run inside an ssh command whose own command line contains that string always matches. The observer was inside the observed set, so the answer was always "running".
  2. The job was not supervised. nohup … & inside an ssh session dies with the session, and a dead job is indistinguishable from a slow one.

Both vanish if the job is a transient systemd unit: systemd already records start, stop and exit status as events, answers from its own state instead of scanning /proc, and keeps the process alive independently of whoever launched it.

So: state comes from systemctl --user show, which cannot match the question being asked. The scripts never call pgrep and never background a bare process.

It announces failures too. A watcher that only speaks on success is indistinguishable from one still waiting — which is the bug it was written to prevent.

Use it directly

scripts/job run    <name> [--host H] -- <cmd...>   # start supervised
scripts/job status <name> [--host H]               # 0 active · 1 failed · 2 finished · 3 unknown
scripts/job wait   <name> [--host H] [max_s]       # block; exits with the job's own code
scripts/job log    <name> [--host H] [lines]
scripts/job list   [--host H]
scripts/job ledger 20                              # append-only history across all hosts

job status is the answer to "is it still running?" — never ps | grep.

Configuration

Copy scripts/config.example to ~/.config/watchjob/config. Every setting is optional. With no config file at all this is still a correct supervisor; it just stays quiet. Announcement channels layer on top and are independent — no Home Assistant means no speech, not a failure.

SettingEffect
WATCHJOB_HA_URLHome Assistant base URL. Empty disables speech entirely.
WATCHJOB_HA_TOKEN_FILE / WATCHJOB_HA_TOKENLong-lived token. Prefer the file.
WATCHJOB_SPEAKERmedia_player.* to speak on.
WATCHJOB_TTSTTS engine entity. Default tts.home_assistant_cloud.
WATCHJOB_TABLETSCompanion-app notify targets that speak on alarm_stream.
WATCHJOB_NOTIFY_CMDSecond channel: <cmd> <level> <job-name> <body>, level done/error.

Verified with no config present: success path exits 0, failure path propagates exit 9, and the missing Home Assistant is skipped rather than raising.

Design notes worth knowing

  • The spoken label is not the job name. orcacad-txp8 is unreadable aloud, so the label defaults to the name with hyphens as spaces and --label overrides it. Elapsed time is spoken only from 1 minute up: on a short job it is noise, and noise is what gets an announcement ignored.
  • Every announcement target is attempted independently. One dead speaker must not silence the others — that is the whole reason for announcing on more than one device.
  • The command is base64'd before it reaches systemd-run. It has to survive the local shell, ssh, systemd-run's own parsing and then bash -c; every quoting scheme leaked somewhere. Encoding removes the problem instead of layering more escapes.
  • --collect drops a unit once it succeeds, and keeps it when it fails, so the exit status is still readable exactly when you need it.
  • systemd --user over ssh needs XDG_RUNTIME_DIR and DBUS_SESSION_BUS_ADDRESS, which a non-login ssh shell has neither of. The script exports them itself.

Install

npx skills add tommasobbianchi/watchjob

Or copy skills/watchjob/ to ~/.claude/skills/watchjob/.

Requirements

systemd --user on whichever host runs the job (a live user session — consider loginctl enable-linger), python3 for the optional Home Assistant call, and passwordless ssh to any --host you use.

License

MIT

↑↓ navigate openesc close