ai-organsbuilding a personal system out of small programs

ai-organs is the personal system I run on my own machine. It includes separate tools for habits, health, finance, Korean study, writing, memory, goals, relationships, and search. Each tool owns its data and exposes a small command-line interface. A coding agent calls them together when I ask a question that crosses domains.
I arrived at this structure after building several versions around context. I collected notes, Markdown files, embeddings, and databases, then loaded the relevant material into a model. The answers were useful at first. They degraded as soon as the material went stale.
That failure changed what I was building. A folder of personal context can only describe what was true when I last updated it. I wanted components that could ingest new data, check their own state, and tell me when something had broken. I called those components organs.
An organ owns a domain and maintains it
My earlier systems treated the model as the only active part. Everything else was material for it to read. That made me responsible for keeping every fact, summary, and index current. The system consumed the same attention I had built it to save.
In ai-organs, each component owns a domain and the work required to maintain it. The habits organ records checks, renders weekly views, and detects problems with its database. The finance organ pulls transactions and tracks the freshness of its data. The writing organ has no database, though it still owns a stable operation: evaluate prose against a set of standards and return a verdict.
I made maintenance part of the interface:
export interface OrganLifecycle {
name: string;
setup(): Promise<Check[]>;
doctor(): Promise<Check[]>;
fix(checkName: string): Promise<void>;
}Twelve organs implement this lifecycle. setup prepares the organ on a new machine. doctor reports what is missing, stale, or misconfigured. fix handles repairs that can be automated. If I cannot write a meaningful doctor, the component has no way to distinguish healthy state from silent decay.

This definition also separates an organ from a context file. Two directories may contain identical data, while only one has a process responsible for keeping that data accurate. The difference is operational ownership.
I use organs for practices that lose to willpower
I needed an admission rule because almost anything can become a personal automation. "What takes time?" produced a list of minor chores. Most saved a few minutes from work I was already doing.
I now ask which useful practices repeatedly disappear when my attention gets thin. Financial review, habit tracking, prose review, and spaced study all have proven value for me. Each one works when I do it consistently, and each one tends to collapse during a bad month.
Those are good candidates for organs. A daily financial review may take ten minutes, yet automating it does not save ten minutes because I was often skipping it. The useful change is frequency: the review goes from occasional to daily. I am scaling a practice that my available willpower could not sustain.
This test keeps ai-organs focused on work I already understand. I do not automate a speculative routine and hope it becomes valuable. I automate the maintenance around a practice that has already paid for itself.
Developer tools gave me the initial inventory
Introspection gave me a short, arbitrary list of organs. My development environment gave me a better one because it already contained small programs selected through years of actual use.
Developer tools perform general information operations. A build system turns one representation into another. Version control makes history addressable. A linter evaluates work against a standard and returns errors that another process can act on. Those operations apply well outside software.
That analogy produced the current package inventory:
packages/
habits/ health/ finance/ korean/ social/
cortex/ memory/ macrostates/ log/ voice/
imagine/ google/ writing-tools/ digest-book/
ai-provider/ cli/ data/The directory contains sixteen organs and three infrastructure packages: the model provider, the CLI bindings, and the data resolver. Each organ has a domain, a command surface, and an output that another program or agent can inspect. A chat window does not meet that bar. A prose linter does, even though it stores nothing.
MCP gave the agent tools; the shell made them composable
I first considered exposing every organ through one MCP server. MCP lets a model choose a function, call it, and receive the result in its conversation. That works well for isolated operations. Cross-domain questions reveal the cost.
Suppose I ask for my average strain across the last thirty workouts, grouped by the weeks when I actually meditated. The agent needs two datasets and a join. Through MCP, it calls one tool and reads thirty workout records into the conversation. It calls another and reads a list of habit dates. It then performs the join inside its context.
The conversation becomes the storage layer for every intermediate result. Large outputs consume tokens even when the model only needs to pass them to the next operation. Reusing a result means keeping it in context or reading it again. MCP supplies function calls, while composition still has to happen inside the model.
The shell already has a place for intermediate data:
organs health strain -n 30 > /tmp/strain.json
organs habits show "Meditate" --json \
| jq -r 'select(.checked).date' > /tmp/med.txt
join /tmp/strain.json /tmp/med.txtThe two organs write files, and join combines them. The model can inspect the final result without reading every number along the way. Pipes pass output directly between programs; files and variables preserve results for later use.

This is why ai-organs is a CLI instead of a large tool server. I already had a filesystem, processes, pipes, variables, permissions, and remote execution. Recreating those features inside a model protocol would have given me a weaker version of the operating system on my laptop.
Functions, organs, and systems have different jobs
I use three levels of composition:
- A function performs one operation, such as checking a habit, reading today's strain, or appending a log entry.
- An organ groups functions around one domain and owns that domain's state.
- A system reads across several organs to answer a larger question.
The morning briefing is a system. It reads recovery from health, this week's adherence from habits, recent spending from finance, and current goals from macrostates. Each organ returns a narrow fact. Their combination can tell me that a week is coming apart.
I define systems as prompts containing organ calls:
Read this morning's state and tell me what it means, not what it says.
organs health strain organs habits day
organs finance tx -n 20 organs macrostates tree
Name the one thing that changed since yesterday. If nothing changed, say so.Writing a system takes a paragraph and a few commands. The organs remain independent, while the agent decides how to combine their outputs for the current task.
| Biology | Unix | ai-organs |
|---|---|---|
| cell function | a command such as grep | a function such as habits check |
| organ | a program such as git | an organ such as organs habits |
| organ system | a pipeline of programs | a prompt such as the morning briefing |
| organism | a shell session | an agent session |
| nervous system | the shell | the agent |
The model sits above the organs because it owns no domain state. It reads their outputs, chooses what to call next, and composes results. The shell handles the mechanical movement of data; the model handles decisions whose sequence cannot be fixed in advance.
I stopped designing a personal operating system
I had spent two years describing this project as a personal operating system. One architecture document specified a kernel context, a user context, a process messaging interface, and scoped databases for each process.
My laptop already provided those facilities. The coding agent could coordinate processes. The shell could connect them. The filesystem could store shared artifacts. SQLite could give each component a local database. iCloud could synchronize ordinary files.
I reduced the architecture to three assignments:
- Claude Code coordinates the work.
- The
organsCLI exposes each component's operations. - iCloud synchronizes the files.
That decision also killed two of my own packages. One was a wiki format that stored entries inside a custom database. Once writing entered that database, every editor, viewer, sync tool, and diff needed an integration. Markdown files already worked with all of them.
The habits command surface shows how little custom substrate I needed:
organs habits check "Meditate"
organs habits check "Exercise" -n "Ran 5km"
organs habits check "Exercise" -d 2026-03-17
organs habits uncheck "Exercise"
organs habits day
organs habits week
organs habits grid
organs habits stats
organs habits add "New Habit" -d "description"
organs habits archive "Old Habit"Two commands write entries, several read them at different resolutions, and two manage the habit list. The surrounding infrastructure is generic: a coding agent, a cloud drive, a shell, and SQLite.
Each organ owns its state and one resolver owns its location
The organs use different storage because their domains need different things:
| Organ | Domain | State |
|---|---|---|
habits | behavior tracking | SQLite |
health | body data | the WHOOP API |
finance | money | the Mercury API |
korean | learning | Markdown lessons |
write | prose quality | stateless |
memory | world model | Zep, a hosted temporal graph |
social | relationships | SQLite and one Markdown file per person |
macrostates | goals | Markdown folders encoding status |
I only enforce two boundaries. An organ cannot read or write another organ's private state. It also cannot hardcode the location of its own data. One resolver chooses the root for every package:
function resolveDataRoot(): string {
if (process.env.AI_ORGANS_DATA) return process.env.AI_ORGANS_DATA;
if (existsSync(ICLOUD_BASE)) return join(ICLOUD_BASE, DATA_DIR_NAME);
const xdg = process.env.XDG_DATA_HOME ?? join(homedir(), ".local", "share");
return join(xdg, DATA_DIR_NAME);
}The explicit override wins. A machine with my iCloud folder uses it. Other machines fall back to the platform data directory. No organ contains an absolute path.
I learned the value of this boundary when one package bypassed the resolver and opened a database at its hardcoded iCloud path during command registration. On a Linux machine without that path, every organs command crashed at startup. A broken task-list database prevented an unrelated habit check from running.
I fixed the path and delayed resource creation until the relevant command executes. Now a missing store takes down one operation at call time instead of the entire CLI at startup.
State ownership also requires a single writer. ai-organs runs on my laptop and a Linux box, while only the laptop writes to synchronized SQLite files. SQLite cannot merge concurrent histories. If both machines write, a habit check or log entry can disappear without a conflict I can resolve.

Libraries contain domain logic; the CLI contains wiring
The organs began as independent packages. I wanted them to remain usable from TypeScript while still sharing one command convention.
Each package exports its data types, readers, renderers, lifecycle, and command registration:
export { HabitDB } from "./db";
export type { Habit, Entry } from "./db";
export { renderDay, renderWeek, renderMonth, renderStats } from "./views";
export { habitsLifecycle } from "./lifecycle";
export { registerHabitsCommands } from "./commands";The CLI package owns flags, output formatting, and command registration. Other code can call HabitDB or a renderer directly without pretending to be a terminal process.
The wiring layer is also the only place where one organ can break the common command surface. I keep registration lazy so importing an organ does not open files, connect to APIs, or construct services:
export function registerHabitsCommands(cmd: Command, providedDb?: HabitDB) {
const runWithDb = <T>(fn: (db: HabitDB) => T): T => {
const db = providedDb ?? new HabitDB();
try {
return fn(db);
} finally {
if (!providedDb) db.close();
}
};
}Registration describes the command. The handler opens the database when the command runs and closes it afterward. A machine without the habits database can still use every unrelated organ.

I designed the human input around bad weeks
Some state can only come from me. A wearable knows my strain, while it cannot know that I abandoned a goal or forgot to record a workout. The habits schema makes that ambiguity visible:
CREATE TABLE entries (
habit_id INTEGER NOT NULL REFERENCES habits(id),
date TEXT NOT NULL,
checked INTEGER NOT NULL DEFAULT 1,
notes TEXT,
UNIQUE(habit_id, date)
);Completing a habit writes a row. Skipping it and forgetting to record it both leave no row. More schema cannot recover information that never entered the system.
I therefore limit the system to two required moments of attention each day: a morning check and an evening check. Everything between them runs automatically or gets captured as I mention it to the agent. Two check-ins give me less information than continuous logging. They also survive weeks when continuous logging disappears entirely.

The shared log is one timestamped Markdown file per day. The agent appends entries as I work, so I do not have to open a form. I keep the log as plain text because I read and diff it directly; query performance matters less there than visibility.
The daily cycle gives maintenance a deadline. A continuously running personal system can become vaguely stale without a clear failure point. A morning and evening cycle tells both me and the agent when each check is due.
The interface can change without moving the state
A shell is a good composition layer and a poor dashboard. I wanted to see the day, habits, strain, balance, and recent log entries without asking the agent to assemble them every time.
I built a text interface with a navigation rail, a main view, an activity feed, glance metrics, and a command bar. Plain text entered in the bar goes directly into the daily log. The most common interaction is also the cheapest one.
The dashboard reads across several organs through a snapshot builder. It receives read access only. Every write still goes through the organ that owns the affected state, preserving the same boundary as the CLI.

Three different interfaces have now sat above the command surface. I could replace each one without migrating the underlying data. That is the practical payoff of state ownership.
The interfaces also exposed a limit in the architecture. When I stopped opening a dashboard, the interactions it had encouraged disappeared with it. Stable storage does not create attention. The surface still has to earn a place in my day.
ai-organs is the boundary between maintenance and composition
The system now has a simple division of work. Each organ maintains one domain, owns its state, and exposes commands. The operating system stores and moves intermediate results. The coding agent reads across organs and decides how to compose them for the question I am asking.
That boundary keeps the system small. Adding a domain means building one package and its maintenance lifecycle. Adding a cross-domain view means writing a prompt or snapshot that calls existing organs. Replacing an interface leaves the state untouched.
ai-organs runs on Markdown, SQLite, APIs, command-line programs, and a coding agent. The individual technologies are ordinary. The useful part is that I no longer need one model context, one database, or one application to contain my entire life. I need small components that stay accurate on their own and a reliable way to combine them.