Migrating from the Fish framework
If you came from the Fish framework in avelino/dotfiles (lib/agent.fish, agents/agent-orchestrator/), this guide maps every concept you already use to its dotagent equivalent. The plan is no big bang — old and new run side by side until you migrate the last agent.
Mapping at a glance
meta.json
agent.toml (richer, with comments and TOML types)
lib/agent.fish — agent_init
env vars dotagent injects + tmpdir auto-cleanup
lib/agent.fish — heartbeat
dotagent writes the same shape, before+after the run
lib/run-with-timeout.fish
built into dotagent run (agent.timeout_seconds)
agents/agent-orchestrator (tick)
dotagent daemon (adaptive, single plist) + dotagent tick (one-shot)
agents/agent-orchestrator --status
dotagent status (read-only dashboard)
agents/agent-orchestrator --daily-summary
dotagent daily-summary + the daemon's own fire at [daily_summary].time (default 22:45)
cron.nix launchd config
dotagent install (single run.avelino.dotagent plist)
agent_output_imessage
built-in notifier [[notifiers]] driver = "imessage" (with rate_limit_minutes)
lib/roam.fish (roam_publish)
plugin sink-roam (full port: sanitize + parse + idempotent replace)
inline WARP check
plugin preflight-warp
IMESSAGE_TO="+55..." hardcoded
per-plugin config.to, or profile defaults
retry loop in agent-orchestrator
WindowState-backed retry with max_retries + retry_backoff_minutes
(no equivalent)
dotagent logs, dotagent inspect, dotagent reload, dotagent run-now
Step-by-step
1. Install dotagent
Notifiers (
desktop,imessage,slack,ntfy,pushover,telegram) ship inside thedotagentbinary. No extracargo installneeded.
2. Convert meta.json → agent.toml
If you're managing your dotfiles with Nix / home-manager (as avelino/dotfiles does), the conversion is mechanical and there's a reference Nix module that does the work for every agent in one place — see
modules/home-manager/dotagent/in avelino/dotfiles. Copydefault.nixfrom there, adjust thebuiltinAgentslist, setdotagent.enable = true, and rebuild. Skip the manual conversion below if so.
For the team-standup agent (example), the Fish manifest…
…becomes:
3. Trim the Fish entrypoint
You can drop everything dotagent now owns. The reduced agent.fish:
Keeps your data collection + Claude invocation.
Removes
agent_init(env vars are already injected).Removes
agent_heartbeat_start/_agent_heartbeat_finish(dotagent writes the same heartbeat file shape).Removes any inline WARP check (now done by
preflight-warp).Removes
agent_output_imessagecalls (now done by the built-inimessagenotifier — declared under[[notifiers]]).Continues to use
roam_publish/lib/roam.fishwhilesink-roamis in progress. Oncesink-roamis feature-complete you can drop the Roam helpers too.
A thin shim (lib/agent-shim.fish) that reads the env vars and re-exports the legacy names ($AGENT_TMPDIR, $AGENT_HEARTBEAT_FILE, ...) lets you migrate without touching the agent body. Source it instead of the original lib/agent.fish.
4. Install the dotagent daemon
dotagent uses one plist for the entire system (the daemon manages every agent internally — no per-agent plist):
Now remove each migrated agent's entry from modules/home-manager/cron.nix and run darwin-rebuild switch. As soon as the entry is gone from cron.nix, the dotagent daemon picks up scheduling for that agent.
5. Verify the monitor
The Fish agent-orchestrator can be left running during the migration — both read/write the same heartbeat shape, so they don't fight (note the new path: ~/.config/dotagent/state/agents/... for dotagent vs. ~/.local/state/agents/... for the Fish framework). When you're confident, remove its launchd entry and delete agents/agent-orchestrator/.
6. (Optional) move the agent out of dotfiles
Since each agent is now self-contained (agent.toml + script + assets), you can lift it into its own repo or share it without dragging the rest of your dotfiles along.
Things that intentionally differ
No iMessage hardcoding. The phone number lives in the manifest's
[[notifiers]] driver = "imessage"block (or in a profile default). dotagent has no opinion about your notification target — pick the driver you want.No
agent_mcpwrappers. dotagent isn't an MCP proxy; themcpCLI you already use stays where it is. Agents continue to callmcpdirectly.tickis read-mostly. dotagent'stickdoesn't depend onclaude,osascript, orjq— it only does scheduling math. Notifications go through the daemon's built-in drivers ([[notifiers]]) or, for third-party services, through plugins.
Last updated
Was this helpful?