A small pi extension that watches files and directories and injects new content into the current session as it appears. It exists so an agent does not have to wait for the next human prompt to notice that something changed on disk.
The main way we use it: monitoring file-based inboxes used for agent-to-agent communication. A watcher can sit on an inbox directory and surface incoming message files immediately, so replies, handoffs, and watcher notifications do not sit unread until somebody remembers to run a manual inbox check.
- File targets are tailed, log-style. Only bytes appended after the watch starts are injected, not the whole file.
- Directory targets are watched flat, one level deep. Files already present when the watch starts are caught up once as backlog; files created afterward are treated as new arrivals.
- Boot backlog is passive. Existing inbox files are injected as context for the next natural turn, not as one interrupting turn per stale file.
- Re-arms do not replay. If the watch is rebuilt — for example because
session_startfires again on a model change — already-seen directory files and the file tail offset are carried across the re-arm. - New arrivals keep their mode. A live file landing in an active watched inbox still triggers an immediate turn; passive watches only queue context.
- Payloads are capped. Injected content is limited by
SIMPLEWATCHER_MAX_BYTESso a watched file/log cannot dump an unbounded blob into the context window.
Each watch target has one mode:
| Mode | Behavior |
|---|---|
active |
Inject and trigger an immediate turn, even while idle. Use for inboxes where a new file means “handle this now.” |
passive |
Inject as queued context only. It surfaces at the next natural turn and never speaks/acts unprompted. |
Default for manual watches is passive. The bundled session-start inbox watch
uses active, because incoming agent messages are meant to be seen promptly.
| Command | Effect |
|---|---|
/simplewatcher |
List current watches |
/simplewatcher <path> |
Add/replace a passive watch |
/simplewatcher <path> --active |
Add/replace an active watch |
/simplewatcher <path> --passive |
Add/replace a passive watch explicitly |
/simplewatcher <path> --active --persist |
Arm now and save project-locally |
/simplewatcher <path> --active --persist --global |
Arm now and save globally |
/simplewatcher persisted |
List persisted watches and config paths |
/simplewatcher remove <path> |
Stop watching and forget persisted entries for that path |
Examples:
/simplewatcher ~/Agents/_bus/inbox/fabricant --active --persist
/simplewatcher /var/log/myapp.log --passive
/simplewatcher persisted
/simplewatcher remove ~/Agents/_bus/inbox/fabricant
/simplewatcher
This repo follows the pi package layout: package.json is present and
pi.extensions points at ./src, with the entrypoint at src/index.ts.
Install from npm once published:
pi install npm:pi-simplewatcherInstall from GitHub:
pi install git:github.com/studioschade/pi-simplewatcher # global
# or
pi install git:github.com/studioschade/pi-simplewatcher -l # project-localUpdate/remove later, matching the source you installed from:
pi update npm:pi-simplewatcher
pi remove npm:pi-simplewatcher
# or, for a git install:
pi update git:github.com/studioschade/pi-simplewatcher
pi remove git:github.com/studioschade/pi-simplewatcherFor a manual source checkout, symlink the entrypoint into pi's extension auto-discovery path so the repo stays the single source of truth:
ln -s /path/to/pi-simplewatcher/src/index.ts ~/.pi/agent/extensions/simplewatcher.ts # global
# or
ln -s /path/to/pi-simplewatcher/src/index.ts .pi/extensions/simplewatcher.ts # project-localTo try it ad hoc without installing: pi -e /path/to/pi-simplewatcher/src/index.ts
AGENTS.md is policy, not mechanism. It is the right place for rules like
“handle inbox messages when they arrive,” “don’t ack an ack,” and “ask before
outward actions.” It is not a reliable way to make a filesystem watch come back
every session: a new session would have to read that instruction, decide to run
it, and run it correctly.
The mechanism belongs in the extension:
- Plain
/simplewatcher <path>watches last for the current session only. --persistsaves the watch to.pi/simplewatcher.jsonin the current project; add--globalto save to~/.pi/agent/simplewatcher.jsoninstead.- On
session_start, persisted watches are loaded global-first then project, so project config wins for the same resolved path. - The bundled default re-arms
$HOME/Agents/_bus/inbox/<agent>on everysession_startwhen that path exists and was not already armed by persistence.
To see what is armed now: /simplewatcher. To see what will come back next
session: /simplewatcher persisted. To stop and forget a watch:
/simplewatcher remove <path> — that stops the live watch and removes persisted
entries for the same resolved path from both project and global config. Manual
deletion is removing that object from watches[] or setting "enabled": false.
The bundled inbox default is controlled by SIMPLEWATCHER_AGENT / PI_AGENT /
AGENT_NAME and only arms if the resolved inbox directory exists.
On session_start, the extension arms one default watch:
$HOME/Agents/_bus/inbox/<agent> (active mode)
The agent name is resolved in this order:
SIMPLEWATCHER_AGENTPI_AGENTAGENT_NAME- fallback:
fabricant
That fallback keeps this repo compatible with its original home while letting sibling agents use the canonical source via an env override instead of keeping a patched fork.
| Variable | Default | Purpose |
|---|---|---|
SIMPLEWATCHER_AGENT |
fabricant fallback |
Agent name used for the default $HOME/Agents/_bus/inbox/<agent> watch. |
PI_AGENT / AGENT_NAME |
— | Fallback agent-name sources if SIMPLEWATCHER_AGENT is unset. |
SIMPLEWATCHER_MAX_BYTES |
32768 |
Maximum injected payload bytes. Larger content is truncated with a marker. |
In our setup, agents communicate by dropping message files into a local bus
inbox or by having another comms layer materialize messages there. simplewatcher
is the piece that makes those files visible to a live session immediately.
Important boundary: the watcher only surfaces content. It does not grant authority. A bus/inbox message is still data, not permission to spend money, publish outward, change another agent’s territory, or bypass the receiving agent’s own guardrails.
- Watch trusted paths. Active mode can wake the agent and start a turn from file content alone.
- Large injections are truncated by
SIMPLEWATCHER_MAX_BYTES; tune it rather than disabling the cap unless you really mean it. - Directory mode is intentionally flat and inbox-like. It is not a recursive file-sync or build watcher.
- If a watched path disappears or errors, the extension reports a watch error instead of throwing an unhandled watcher error.
- Empty injections are ignored: whitespace-only file content does not send a steer by itself.
- Node.js 22+ recommended for the standalone regression/import path.
- pi
- No runtime npm dependencies; only
node:fsandnode:path.
Source of truth for this project is src/index.ts. Keep the deployed pi
extension entrypoint pointed at that file (symlink preferred) when changing
behavior.
Run the self-test suite:
npm testUseful manual smoke checks:
- Watch a temp directory in passive mode, add a file, confirm it queues without triggering a turn.
- Watch an inbox in active mode, add a file, confirm it triggers a turn once.
- Re-arm the same watch and confirm no backlog replay.
- Write a file larger than
SIMPLEWATCHER_MAX_BYTESand confirm the injection is truncated with a marker.
GNU General Public License v3.0 only — see LICENSE.
Copyright (C) 2026 the simplewatcher contributors.