Skip to content

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Caretaker

tests

A template for personal software with a resident agent: a small app, plus a Claude Code agent that lives on the same machine, runs the app, watches it, answers questions about it in Slack, and changes it on request.

Generative app builders made writing software cheap. Owning software — operating it, noticing when it breaks, adapting it as your needs drift — is still expensive, and that cost is why most personal software never gets built. The caretaker covers the owning part. Background and origin: docs/introducing-caretaker.md.

The pattern

Coding agents are usually summoned: you open a session, it does a task, it forgets. A caretaker is resident. Residency means four things:

  • A clock. systemd timers run the app on schedule and give the agent a nightly "dreaming" run: review the week's data and conversations, maintain memory, decide whether anything is worth telling you.
  • An inbox. A Slack bot maps each thread to a persistent claude -p session. @mention or DM it to start one; replies in the thread resume it. Screenshots and files come along as attachments the session can read.
  • Memory. agent/MEMORY.md is appended to every session's system prompt and rewritten each night. Operational knowledge — the quirks of your data, your preferences, what broke last month — accumulates outside the code.
  • Initiative. The agent posts when something is notable (a broken job, an anomaly, a milestone) and stays silent otherwise. Sundays it writes a weekly report.

The agent may edit the whole repository, including its own runtime, under a protocol: tests pass → commit → push → touch a restart flag. A root-owned watcher applies the flags ~75 s later, so restarts happen outside the agent's own process and a live turn can finish first.

flowchart LR
    you([you]) <-- "Slack thread" --> bot["bot.py<br/>(one claude -p session per thread)"]
    apptimer["app timer"] --> app["your app"]
    dreamtimer["dream timer<br/>(nightly)"] --> session
    bot --> session["agent session<br/>cwd agent/, MEMORY.md in system prompt"]
    app --> data[("data/ + logs/")]
    session -- "reads" --> data
    session -- "edits, commits, pushes" --> repo["the repo<br/>(app, agent/, its own runtime)"]
    session -- "touches .harness/restart-*" --> watcher["root watcher<br/>(applies restarts, not agent-writable)"]
Loading

Layout

Path Purpose
src/app.py Placeholder app (records free disk space to SQLite). Replace with yours.
src/bot.py Slack Socket Mode bot: one Claude session per thread, disk-backed queue with backoff, attachment downloads, NO_REPLY / REACT: sentinels.
src/dream.py, src/dream_prompt.md The nightly self-maintenance run.
agent/ The agent's home: its protocol (CLAUDE.md), memory, reports.
ops/ systemd units, git deploy hook, restart-flag applier, Slack app manifest, env example, server runbook.
tests/test_bot.py Self-check, stdlib only: python3 tests/test_bot.py. CI runs it.

Quickstart

  1. Server: create the caretaker user, install uv and the claude CLI, run claude setup-token, set up the bare repo and deploy hook — ops/README.md.
  2. Slack: create an app from ops/slack-app-manifest.yml, fill caretaker.env from the example.
  3. Install the systemd units, enable the timers, start the bot.
  4. @mention the bot. Ask it about the app. Ask it to change something.

Replacing the placeholder app

src/app.py records one number per day. Your real app only needs the same three properties: it runs from a timer or service, keeps state where the agent can inspect it (a SQLite file works well), and prints to stdout so failures land in journald. After swapping it in, update the app section of agent/CLAUDE.md and step 2 of src/dream_prompt.md so the agent knows what it is looking after — or tell the agent about the app in Slack and let it update those files itself.

Costs and risks

  • The agent runs claude -p --permission-mode bypassPermissions as its own user. Give it a machine you would give a contractor: a dedicated VM or container. The off-limits list in its protocol is convention plus file permissions; the pieces that must hold no matter what (deploy hook, restart applier) are root-owned, and secrets live in an env file outside the repo.
  • If the agent breaks src/bot.py, it crash-loops and goes silent, and the dream run shares the module — recovery is a push from your machine. The protocol warns it; the warning has held so far.
  • Claude usage: a subscription token from claude setup-token covers chat and the nightly run. That token expires roughly yearly and must be regenerated.
  • Slack is the only chat surface here. The transport is confined to src/bot.py; porting to another chat system means reimplementing one file.

Lineage

Extracted from a personal health-data pipeline that has run this way since August 2026 — the agent in that deployment reviews wearable data nightly, maintains its own memory and instructions, transcribes voice messages, and has shipped harness changes to itself. The pattern combines named ideas: Robin Sloan's home-cooked software, Ink & Switch's malleable software, ambient agents, and the reflection step from Generative Agents (Park et al., 2023).

MIT license.

About

Personal software with a resident agent: a small app plus a Claude Code session that runs, watches, and evolves it

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages