What you build
A cron job stored in rousseau's own SQLite state (cron_jobs table, schema in internal/state/sqlite/cron.go) that fires at 18:00 local time on weekdays. It runs a prompt that asks the model to summarise git log --since=today and delivers the result to your phone over WhatsApp.
Estimated time: 10 minutes.
Prerequisites
- WhatsApp bridge already paired (see Quickstart step 4 or Transports: WhatsApp).
- The
rousseau whatsappdaemon running — the cron scheduler ininternal/cron/scheduler.gois booted by transport daemons viawiring.startCron(), not byrousseau chat. - A workspace containing the git repo you want summarised, bind-mounted into the container (or on the host if you run rousseau outside a container).
How rousseau cron works
rousseau cron add writes a row to the cron_jobs table (internal/state/sqlite/cron.go). Every ~15 seconds, scheduler.sync re-reads the table and reconciles the robfig/cron/v3 in-memory schedule. When a job fires, the scheduler emits cron.firing, runs the prompt through the configured provider, and delivers the result to deliver_to via the transport bridge that owns the process (WhatsApp in this tutorial).
Structured log names you'll see (from internal/cron/scheduler.go):
cron.started— scheduler booted withpoll_interval=….cron.scheduled— a job was accepted.cron.firing— a job is about to run.cron.completed— a job finished successfully.cron.run_failed,cron.delivery_failed,cron.record_failed— failure modes.
Step 1: add the job
rousseau cron add \
--name nightly-changelog \
--schedule "0 18 * * 1-5" \
--prompt "Summarise git log --since=yesterday under /workspace/rousseau-agent as a Slack-style bullet list. Keep it under 200 words. If nothing changed, reply with a single line 'no commits'." \
--deliver-to 447900123456@s.whatsapp.net
The cron expression is parsed by robfig/cron/v3 in newCronAddCmd (internal/cli/cron.go). Invalid expressions are rejected before write. The --deliver-to value is the E.164 JID for WhatsApp (<digits>@s.whatsapp.net); the delivery target format is transport-specific.
Step 2: verify
rousseau cron list
Output shape (from newCronListCmd):
NAME STATUS SCHEDULE PROMPT DELIVER-TO
nightly-changelog on 0 18 * * 1-5 Summarise git log … 447900123456@s.whatsapp.net
The list is also exposed over MCP as rousseau_cron_list (see internal/mcp/tools.go).
Step 3: dry-run
There is no built-in "fire now" trigger. To smoke-test, temporarily schedule the job one minute in the future:
rousseau cron remove nightly-changelog
rousseau cron add --name test --schedule "*/1 * * * *" --prompt "say hi" --deliver-to "$JID"
journalctl --user -u rousseau-agent -f | grep cron.
Expected log sequence:
INFO cron.scheduled job=test expr=*/1 * * * *
INFO cron.firing job=test
INFO cron.completed job=test
Remove the test job and re-add the real one when you're done.
Step 4: tighten the prompt
The best cron prompts are self-contained: the model has no memory of previous runs. Include the repo path, expected output format, and a fallback for the empty case. Example second iteration:
Summarise commits authored since 07:00 UTC today under
/workspace/rousseau-agent. Use this format:
- <short type>: <one-line summary> — <sha>
Group by author. If no commits landed, reply exactly: no commits.
Toggling and removing
rousseau cron disable nightly-changelog # keeps the row, stops firing
rousseau cron enable nightly-changelog
rousseau cron remove nightly-changelog # deletes the row
SetEnabled and Delete from internal/state/sqlite/cron.go are what these call.
Related
- Cron — reference for the scheduler.
- Guides: Scheduled tasks — deeper discussion.
- Transports: WhatsApp — how delivery-to works.
- Reference: CLI Commands — every
rousseau cronflag.