# Hooke – Run agents on schedule or file changes with just one file

> Source: <https://github.com/aditya-azad/hooke>
> Published: 2026-10-25 19:46:50+00:00

Agentic workflow automator: a systemd daemon that watches files and schedules periodic tasks, running each automation through your agent with its available MCP servers.

## Table of contents

Every project accumulates chores that an agent could do if it just knew when to start: "run the tests whenever `src/` changes", "summarize new notes every morning", "check dependencies daily". Cron handles *when* but not *what* — it has no model, no tools, no context. Hooke gives you the cron half for free (systemd, already running) and hands each trigger to a real agent session with your project's context and your configured MCP servers.

Requires Python 3.14+ and [pi](https://github.com/earendil-works/pi) or other supported agents on PATH.

1. Install the CLI/daemon onto PATH

```
uv tool install /path/to/hooke        # or: uv tool install hooke (once published)
```

1. Install + start the systemd daemon (templated per user, starts at boot)

```
hooke install
hooke install --user                  # user unit + loginctl enable-linger
```

Check it's alive — you should see the version and a pid:

``` bash
$ systemctl status hooke@$USER
bash
$ hooke status
```

In any project directory:

```
cd ~/code/my-webapp
hooke init                # writes a commented hooke.toml
```

Edit `hooke.toml`:

```
[project]
name = "my-webapp"                 # unique id, collisions rejected at deploy
schema = 1

[automation.deps-review]
prompt = "Check package manifests for outdated or vulnerable dependencies and summarize findings in DEPENDENCIES.md"
agent = "pi"                       # which agent runs this automation (default: pi)
model = "claude-opus-4-5"          # optional per-automation model override
mcp_servers = ["papers"]           # explicit allowlist — least privilege
schedule = { every = "6h" }
resume_on_restart = false
continuity = true                  # reuse the persisted pi session between runs

[automation.test-on-change]
prompt = "Source files changed. Run the test suite and fix any failures you introduced."
watch = { paths = ["src/**", "tests/**"], ignore = ["**/.git/**", "**/node_modules/**"], debounce = "2s" }
resume_on_restart = true
```

Per-automation fields:

| Field | Default | Meaning | 
|---|---|---|
| `prompt` | required | The agent's task. Trigger context (changed files, resume note) is appended automatically. | 
| `agent` | `"pi"` | Which registered agent backend runs this automation (see `hooke.agents` ;`pi` ,`claude` ,`codex` ). | 
| `mcp_servers` | `[]` | MCP servers this automation may use; validated against the agent's own MCP config at deploy ( `~/.pi/agent/mcp.json` ,`~/.claude.json` , or`~/.codex/config.toml` ). | 
| `schedule` | — | `{ every = "6h" }` — durations like`90s` ,`5m` ,`6h` ,`1d` . | 
| `watch` | — | `{ paths = [...], ignore = [...], debounce = "2s" }` — globs relative to the project root. | 
| `resume_on_restart` | `false` | Re-run automatically after a daemon restart interrupted it. | 
| `continuity` | `false` | Persist the agent session and reuse it for later runs of this automation. | 
| `model` /`thinking` | agent defaults | Per-run overrides passed to the agent ( `thinking` : off…max). | 
| `timeout` | `30m` | Kill the agent process tree after this long. | 
| `concurrency` | `1` | Skip-if-running: overlapping triggers are dropped, never queued. | 

Ready-made configurations live in [`examples/`](https://github.com/aditya-azad/hooke/blob/main/examples/README.md) — copy one in as `hooke.toml` and deploy.

```
hooke deploy              # in the project dir (or: hooke deploy ~/code/my-webapp)
hooke status              # what's armed, next due times, recent runs
hooke logs -n 20          # run history
hooke logs --run <id>     # one run's full record + its pi event stream
hooke disable my-webapp   # kept in the registry, inert
hooke enable my-webapp
hooke remove my-webapp    # gone from the registry
```

`deploy` is the only way the daemon's registry changes. It validates fully first — TOML, schema version, glob sanity, MCP server existence, project trust — so mistakes fail loudly now instead of silently at 3am. An identical redeploy is a no-op ("up to date, nothing to do"); a changed one swaps atomically without resetting unchanged automations' timers.

```
journalctl --user -u hooke -f              # --user install
sudo journalctl -u hooke@$USER -f          # system install
```

- `hooke status` says "cannot reach hooke daemon" → the unit isn't running; check`systemctl status hooke@$USER` and the journal above.
- A run shows `failed` → read its event stream:`hooke logs --run <id>` .
- A run shows `timed_out` → raise`timeout` in`hooke.toml` and redeploy.
- Config errors at deploy quote the exact `hooke.toml` problem; fix and redeploy.

- **Bugs and questions:**[open an issue](https://github.com/aditya-azad/hooke/issues) — include your`hooke.toml` fragment, the daemon version, and the run's output from`hooke logs --run <id>` (redact anything private).

-  Publish to PyPI (`uv tool install hooke` )
- Boot-time catch-up scan of watched globs (currently fires only on the next change)
-  `hooke run` — trigger an automation once, on demand
-  Per-automation notification hooks (e.g. notify on `failed` /`timed_out` )

PRs are welcome! See [CONTRIBUTING.md](https://github.com/aditya-azad/hooke/blob/main/CONTRIBUTING.md) for setup, the test/lint/type commands CI runs, and how to submit changes.

```
uv sync
uv run pytest tests -q
uv run ruff check src tests && uv run pyrefly check src tests
uv run hooke daemon        # run the daemon in the foreground
```


