goat
by phin-tech
README.md
# goat
A personal assistant backend. It stores facts in Postgres or SQLite, fills them
from importers on a schedule, and answers questions about them over Slack, MCP,
SMS, or a terminal REPL.
Facts carry two timestamps: when the fact is *about*, and when goat learned it.
Re-running an importer with unchanged data writes nothing.
## Install
```sh
uv add "goat-butler @ git+https://github.com/phin-tech/goat"
```
Extras: `postgres`, `agent` (pydantic-ai), `cli`, `mcp`, `slack`, `sms`,
`weather`, `migrations`, and `all` for every one of them. SQLite needs none.
```sh
uv add "goat-butler[all] @ git+https://github.com/phin-tech/goat"
```
To work on goat itself:
```sh
git clone https://github.com/phin-tech/goat
cd goat
uv sync
uv run pytest
```
The suite runs against both SQLite and Postgres. Postgres comes from
`docker compose up -d`.
## Start a shed
A shed is one deployment: a directory with a `shed.py` that builds a `Shed`, and
a `.env` holding the credentials. `goat init` writes both.
```sh
uv run goat init ~/my-shed
cd ~/my-shed
uv sync
goat migrate
goat ingest
goat repl
```
Defaults to SQLite, so this runs with no database server and no API key. Set a
model provider key in `.env` for `goat brief` and the REPL's `ask`.
`.env` can hold 1Password references instead of secrets. goat resolves them at
startup via the `op` CLI, so the file is safe to read aloud or paste:
```
OPENROUTER_API_KEY=op://Private/OpenRouter/credential
```
## Commands
| | |
|---|---|
| `goat init` | scaffold a new shed |
| `goat migrate` | create or update the schema |
| `goat ingest` | run every importer |
| `goat brief` | compose a brief; `--send` delivers it |
| `goat facts`, `goat history` | inspect the store |
| `goat repl` | interactive session |
| `goat mcp` | serve the store over MCP on stdio |
## What goes where
goat holds the mechanism: the store, importers, delivery, tools, the CLI. A shed
holds the values: coordinates, model, credentials, prompt, schedule.
`OpenMeteoImporter` lives in goat because a latitude is a constructor argument.
The latitude lives in your shed.
## Docs
- [`docs/design/fact-store.md`](docs/design/fact-store.md) — schema, decisions, and the ones that were reversed
- [`docs/mcp.md`](docs/mcp.md) — MCP server and Claude Desktop config
- [`docs/slack.md`](docs/slack.md) — Slack app setup
- [`docs/weather.md`](docs/weather.md) — weather sources
- [`skills/`](skills/) — Claude Code skills for setup and daily use
This server cannot be deployed
Maintenance
ActivitySlowing
ResponsivenessNo issues