Sno Station
Sno Station은 AI 에이전트를 하나의 팀으로 묶는 오픈소스 워크스테이션으로, 로컬 머신에서 에이전트 협업을 가능하게 함
터미널에서 코딩 에이전트를 위한 예약 작업, 프롬프트와 크론 한 줄, 실행마다 새로운 git 워크트리를 제공합니다
Scheduled tasks for your coding agents, in the terminal. A prompt, a cron line, and a fresh git worktree per run — on Herdr. One YAML file, no store, prebuilt binaries, per-automation model, catch-up
플러그인Go · ShellGitHub @DnzzL직접 설치운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
Scheduled tasks for your coding agents, in the terminal. A prompt, a cron line, and a fresh git worktree per run — on Herdr. One YAML file, no store, prebuilt binaries, per-automation model, catch-up after sleep, and a live board.
저장소 README에서 가져옴
README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
Scheduled tasks for your coding agents. A prompt, a cron line, and a fresh git worktree per run.
If you have used ChatGPT's Scheduled tasks, this is the same boundary — a prompt, a
schedule, a model, and a choice between an isolated worktree and the repo itself —
except it runs in your terminal, on your machine, against your repos, and the config
is one YAML file you can keep in git. Unlike agent schedulers that ship an account
and a webhook relay (Open Run, for one), nothing leaves the machine and nothing
installs outside the plugin.README 전문 보기 ↗Scheduled tasks for your coding agents. A prompt, a cron line, and a fresh git worktree per run.
automations:
- name: issue-triage
cron: "0 9 * * 1-5"
repo: ~/Projects/myapp
model: sonnet
prompt: |
Triage new GitHub issues: label them, close duplicates,
draft replies for the ones needing more info.
If you have used ChatGPT's Scheduled tasks, this is the same boundary — a prompt, a schedule, a model, and a choice between an isolated worktree and the repo itself — except it runs in your terminal, on your machine, against your repos, and the config is one YAML file you can keep in git. Unlike agent schedulers that ship an account and a webhook relay (Open Run, for one), nothing leaves the machine and nothing installs outside the plugin.
Every weekday at 9:00, a Claude (or Codex, or opencode…) agent spawns in a fresh
worktree of myapp, gets that prompt, and works until it's done. You arrive to
triaged issues, a branch to review, and a run log.
What it does to your machine is stated outright.
Herdr keeps your agents alive when you close the laptop — but you still have to start them. Recurring chores — issue triage, nightly flake hunts, a Friday digest — deserve better than you retyping the same prompt every morning.
auto/<name>-<timestamp> in a fresh worktree, so a run that went sideways is a diff you throw away, not a mess in your working copy. workspace: root when the task must see uncommitted state, workspace: existing when a run an hour would bury the sidebarmodel: per automation — a nightly chore has no business on your most expensive model. Set it where you read it; a kind that takes no --model is caught when the file loads, not at 3amcatch_up_minutes) instead of vanishing; anything too late is recorded as missedlist reports every overlap in the week ahead. Herdr starts them all; the plugin never quietly holds one back, it just makes sure you knewherdr agent start supports: claude, codex, opencode, gemini, cursor, …mcp_config: path.json hands the agent its MCP servers (GitHub, Slack, your DB…)scheduled → running → done | failed | skipped | missed, with workspace and pane IDs to jump back intor to run now, enter to jump straight into the workspace a run createdAn automation is one prompt on a clock. Not a sequencer, not a workflow engine, not a place to put branching logic. One prompt, one schedule, one run.
No store, no daemon to install, no state you can't read. Two files: your
automations.yaml, and history.jsonl. Uninstalling leaves both behind.
Nothing happens that the file doesn't say. The scheduler doesn't queue, throttle, stagger or clean up behind your back. Where that could surprise you — overlapping schedules, accumulating worktrees — it reports and waits for you.
herdr plugin install DnzzL/herdr-automations # prebuilt binary, no toolchain needed
herdr plugin action invoke install-cli --plugin dnzzl.automations # shim in ~/.local/bin
herdr-automations add # wizard: validates cron, previews next 3 runs
Prebuilt for macOS and Linux (arm64/amd64), checksum-verified at install; anything else builds from source and needs Go.
Herdr doesn't put plugin binaries on PATH, so install-cli writes a small
herdr-automations shim that asks Herdr where the plugin lives on every call:
herdr plugin update keeps it working. The action runs in the background and
prints nothing; if herdr-automations is then not found, add ~/.local/bin to
your PATH. To put the shim in another directory:
"$(herdr plugin list --plugin dnzzl.automations --json | sed -n 's/.*"plugin_root":"\([^"]*\)".*/\1/p')"/bin/herdr-automations install-cli ~/bin
Three ways to add an automation, all writing the same file:
herdr-automations add — the wizard: validates the cron, previews the next three
runs, and warns if another automation is already due at the same time
Edit the YAML — herdr plugin config-dir dnzzl.automations → automations.yaml
(or press e on the board to open it at the right line). Full reference in
automations.example.yaml
Ask your agent — see below
The daemon picks up edits within 30 seconds, no restart.
herdr-automations list # schedule, model and last run per automation
herdr-automations run issue-triage # trigger now, don't wait for cron
herdr-automations history # what ran, when, how it ended
herdr-automations cleanup # drop the run worktrees whose work already landed
The plugin ships a skill that teaches coding agents this file format. Install it
once with npx skills, which links it
into every agent it finds (Claude Code, Codex, OpenCode, …):
npx skills add DnzzL/herdr-automations
Then, in any new agent session, plain language is enough:
"Every Monday at 9am, review the open PRs and tickets on myapp and propose a sprint."
"Every weekday morning, triage the new errors and file the real ones as tickets."
"Stop the flaky-hunt automation for now."
The agent writes the YAML entry, and the daemon picks it up within 30 seconds. The
skill is a copy: after upgrading the plugin, run npx skills update to match.
A live overlay listing every automation with its next run and last status:
herdr plugin pane open --plugin dnzzl.automations --entrypoint board --placement overlay
| Key | Does |
|---|---|
enter | Jump to the last run — focuses the workspace that automation spawned, so you land right in the agent's terminal |
r | Run the selected automation now |
e | Open automations.yaml in $EDITOR, cursor on that automation's line |
c | Clean up the run worktrees whose work already landed — it counts them, asks y/n, and touches nothing else |
j / k | Move · q closes |
Bind it to a chord so you never type that command again — in ~/.config/herdr/config.toml:
[[keys.command]]
key = "prefix+a"
type = "shell"
command = "herdr plugin pane open --plugin dnzzl.automations --entrypoint board --placement overlay"
The plugin also registers an action (herdr plugin action invoke run-now --plugin dnzzl.automations)
that prompts for which automation to fire.
flowchart LR
A[cron tick] --> B{already<br>running?}
B -- yes --> S[skip + log]
B -- no --> C[herdr worktree create<br>branch auto/name-ts]
C --> D[herdr agent start<br>--kind claude -- --model … --mcp-config …]
D --> E[herdr agent prompt<br>--wait --timeout]
E --> F[history.jsonl<br>done / failed]
Everything goes through the herdr CLI — the same socket API agents themselves use. No private hooks, nothing to break on Herdr upgrades.
automations:
- name: issue-triage # unique, kebab-case
cron: "0 9 * * 1-5" # 5-field crontab, or @daily / @hourly / @weekly
repo: ~/Projects/myapp
workspace: worktree # worktree (default) | root | existing
session: automations # optional Herdr session the run happens in
agent: claude # any `herdr agent start --kind`
model: sonnet # optional → --model; kinds without the flag are caught on load
prompt: "…" # OR workflow: <name> (delegates to hwf run)
mcp_config: ~/.config/mcp/github.json # optional → --mcp-config
agent_args: ["--verbose"] # optional, verbatim agent flags
timeout_minutes: 60 # optional bound on the run
catch_up_minutes: 120 # how late a sleep-delayed run may still start; -1 never
disabled: true # optional: keep it, don't schedule it
Create a workspace once with herdr workspace create --label Automations --no-focus,
or find an existing one with herdr workspace list. Use its returned ID:
automations:
- name: daily-summary
cron: "@daily"
repo: ~/Projects/myapp
workspace: existing
workspace_id: w7 # replace with your workspace's ID
agent: claude
prompt: "Summarize recent changes."
Point several automations at the same workspace_id to collect their runs in one
space. Each run opens a fresh tab labeled with its automation name and timestamp,
uses repo as its working directory, and leaves your focus unchanged. Earlier tabs
remain open for review; each run gets a distinct agent name.
Like root, this mode works directly in repo and creates no Git worktree. Runs
sharing a directory can see each other's file changes. workspace_id is required
only for existing mode and belongs to the automation's Herdr session. If
you close that workspace, runs fail until you update the ID; the plugin does not
create a replacement — see ADR 0009.
The add wizard also offers this mode.
Keep cron runs out of the sessions you work in by naming one:
automations:
- name: issue-triage
cron: "0 9 * * 1-5"
repo: ~/Projects/myapp
session: automations
prompt: "Triage new GitHub issues."
Every run of that automation, from the cron, r on the board, or run, is
provisioned in automations. Without session:, a run lands where it is
triggered from: the board's or shell's session, or for the cron, the session
that started the daemon. The plugin never starts a session. Start it yourself
(herdr --session automations server runs it headless), and a run aimed at a
stopped one fails with Herdr's own error. list names the automations whose
session is not running.
One scheduler serves every session on the machine. It outlives the session that
started it and stops once no session is running. Failure toasts appear in every
running session, so a dedicated one cannot swallow them. enter on the board
jumps only within its own session. For a run elsewhere, it tells you which
session to attach — see ADR 0010.
herdr-automations schedules and records. It never runs your prompt itself: it asks
Herdr to provision a workspace or tab and start an agent, and that agent runs under whatever
permissions Herdr gives it. So the honest statement is about the scheduler, and the
agent's own posture is Herdr's to describe.
herdr plugin config-dir dnzzl.automations) and ~/.local/state/herdr/plugins/dnzzl.automations for the run log. Nothing else on disk is touched by the plugin itselfauto/<name>-<timestamp>. It never removes one on its own — herdr-automations cleanup is the only thing that deletes, it asks first, and it only touches auto/ branches already contained in your default branchherdr plugin install fetching a checksum-verified release binaryautomations.yaml entry — in every running Herdr session: a panel inside the Herdr window, not an OS notification, and not sent anywhereherdr plugin uninstall removes the plugin and leaves your config and history where they areDoes my machine need to be awake? For the run to start, yes — and laptops sleep.
So the scheduler works off the wall clock, not a timer: an occurrence that came due
while the machine was asleep runs when it wakes, as long as it's within
catch_up_minutes (default 2 hours). Anything older is recorded as missed in the
history rather than silently skipped, so herdr-automations history always tells you
what didn't happen. Set catch_up_minutes: -1 for automations that are pointless late.
Timers alone don't survive sleep — macOS suspends the monotonic clock, so a job armed for 9am Monday can simply never fire. Hence the wall-clock loop.
Two automations due at the same minute — what runs? Both. Herdr is a multi-agent
runtime and 0 9 * * 1 means 9:00, so the scheduler doesn't queue, throttle or
quietly stagger anything behind your back. What it does instead is tell you: the
wizard warns while you're still choosing the cron, and herdr-automations list
reports every overlap in the next week. If running them together isn't what you
want, move a cron — one character, and the file still says what happens.
What happens to the tabs workspace: existing opens? Same answer, one level
down: they accumulate, and nothing reaps them. A tab you have read is a tab you
close. cleanup has nothing to say about them — there is no worktree and no
branch, so the only trace a run leaves is its line in history.jsonl.
What happens to the worktrees? They accumulate on purpose: a run whose workspace is still open is a run nobody has looked at, which makes the Herdr sidebar the inbox. Closing it is how you say you're done.
herdr-automations cleanup removes only the ones whose work already landed — the
branch is an ancestor of the default branch, so either it was merged or the agent
produced no commit at all. Anything with a workspace still open, or commits that
exist nowhere else, is kept and told why. It prints its verdicts, asks once, and
neither the worktree nor the branch is removed by force: git refuses a dirty
checkout and refuses an unmerged branch, and those refusals are worth more than
the tidiness. It never runs on a schedule — a reaper would delete the very
signal that says which runs still want you.
What about multi-step chores? An automation is one prompt on a clock. If your
chore is a sequence, put the sequence in a
herdr-workflows workflow and
schedule it with workflow: <name> instead of prompt:. The run waits for the
workflow and fails when it does, and it refuses to start at all when hwf isn't
installed.
Event triggers? On push, on PR? No, and not planned. Herdr's plugin manifest does
support [[events]], but the full list, as of Herdr 0.8.2, is workspace lifecycle and
nothing else: worktree.created, worktree.removed, workspace.created,
workspace.closed, workspace.focused, workspace.renamed, workspace.updated,
pane.created, pane.closed, pane.exited, pane.focused, pane.updated,
tab.created, tab.closed, tab.focused, tab.renamed, layout.updated. There is
no git event, no push, no pull request, and no webhook endpoint. Turning a GitHub push
into a local run means something hosted in the middle holding your repo names and
listening for the hook, which is the opposite of the two paragraphs above. If you want
work to happen on push, your forge's CI is already there. If you want it locally, a
cron automation that polls and does nothing when there is nothing new (gh pr list,
git fetch) is one entry in this file. See ADR 0008
for the full reasoning.
An automation is one prompt on a clock. That is the whole boundary.
go build -o bin/herdr-automations . && go test ./...
herdr plugin link . # use your checkout as the installed plugin
PRs welcome — especially agent kinds tested in the wild, cleanup policies, and notification shapes.