# sayou

**A file-system inspired context store for AI agents.**

Built to replace the databases of the web era. Open source. File-first. SQL-compatible.

[![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](LICENSE)
[![Python](https://img.shields.io/badge/python-3.11%2B-blue.svg)](https://www.python.org)

Databases were designed for transactions — they reduce nuance to fit a schema. Agents think deeply, then forget everything when the session ends. sayou is where reasoning persists, context accumulates, and knowledge compounds over time.

- **Files that hold what databases can't** — Frontmatter for structure. Markdown for context. Versioned. Auditable.
- **One read. Full context.** — Every read accepts a `token_budget`. Returns summaries with section pointers when content exceeds the budget.
- **Knowledge that compounds** — Append-only version history. Every change is a new version. Full audit trail and time-travel reads.
- **Any agent can connect** — MCP server, Python library, REST API, or CLI. Zero config to get started.

## Quickstart: Python API

```bash
pip install sayou
```

```python
import asyncio
from sayou import Workspace

async def main():
    async with Workspace(org_id="my-org", user_id="alice") as ws:
        # Write a file with YAML frontmatter
        await ws.write("notes/hello.md", """\
---
status: active
tags: [demo, quickstart]
---
# Hello from sayou
This file is versioned and searchable.
""")

        # Read it back
        doc = await ws.read("notes/hello.md")
        print(doc["content"])

        # Search by frontmatter
        results = await ws.search(filters={"status": "active"})
        print(f"Found {results['total']} active files")

        # List a folder
        folder = await ws.list("notes/")
        print(f"{folder['file_count']} files in notes/")

asyncio.run(main())
```

By default this creates a local SQLite database (`./sayou.db`) — zero config, nothing to install.

See [`examples/quickstart.py`](examples/quickstart.py) for a runnable version.

## Quickstart: MCP Server

Add sayou to Claude Code or any MCP-compatible client:

```json
{
  "mcpServers": {
    "sayou": {
      "command": "sayou",
      "env": {
        "SAYOU_ORG_ID": "my-org",
        "SAYOU_USER_ID": "alice"
      }
    }
  }
}
```

That's it. The agent gets 17 tools:

| Tool | Description |
|------|-------------|
| `workspace_write` | Write or update a file (text or binary) |
| `workspace_read` | Read latest or specific version of a file |
| `workspace_list` | List files and subfolders with auto-generated index |
| `workspace_search` | Search by full-text query and/or frontmatter filters |
| `workspace_delete` | Soft-delete a file (history preserved) |
| `workspace_history` | Get version history with timestamps and hashes |
| `workspace_glob` | Find files matching a glob pattern |
| `workspace_grep` | Search file contents with context lines |
| `workspace_diff` | Unified diff between any two versions |
| `workspace_move` | Move or rename a file |
| `workspace_copy` | Duplicate a file |
| `workspace_read_section` | Read a specific line range from a file |
| `workspace_audit` | Query the mutation audit log |
| `workspace_kv_get` | Get a key-value entry |
| `workspace_kv_set` | Set a key-value entry (with optional TTL) |
| `workspace_kv_delete` | Delete a key-value entry |
| `workspace_kv_list` | List key-value entries by prefix |

## Quickstart: CLI

```bash
# File operations
sayou read notes/hello.md
sayou write notes/hello.md "# Hello World"
sayou list /
sayou search --query "hello" --filter status=active
sayou glob "**/*.md"
sayou history notes/hello.md
sayou diff notes/hello.md 1 2
sayou move notes/old.md archive/old.md
sayou copy notes/template.md notes/new.md

# KV store
sayou kv set config.theme '"dark"'
sayou kv get config.theme
sayou kv list --prefix config.
sayou kv delete config.theme

# Audit log
sayou audit --limit 20
```

## Quickstart: CLI Agent Example

Seed a workspace with sample data, then chat with it using an OpenAI-backed agent:

```bash
pip install sayou[examples]

python examples/seed_data.py      # writes ~28 files across 5 folders
python examples/ask.py            # natural language agent (needs OPENAI_API_KEY)
```

```
ask> what competitors have we analyzed?
  [list_files] {"path": "competitors/", "recursive": false}
  [read_file] {"path": "competitors/notion.md"}

We've analyzed 6 competitors: Notion, Linear, Airtable, Figma, Retool, and Vercel...

ask> add a competitor analysis for Shopify
  [write_file] {"path": "competitors/shopify.md", "content": "---\ncompany: Shopify\n..."}

Wrote competitors/shopify.md (v1, 1,204 bytes)
```

See [`examples/seed_data.py`](examples/seed_data.py) and [`examples/ask.py`](examples/ask.py) for the full source.

## Examples

| Example | What it shows |
|---------|---------------|
| [`quickstart.py`](examples/quickstart.py) | Hello World — write, read, search, list in 30 lines |
| [`kv_config.py`](examples/kv_config.py) | KV store for config, feature flags, caching with TTL |
| [`version_control.py`](examples/version_control.py) | Version history, diff, time-travel reads, section drill-down |
| [`file_operations.py`](examples/file_operations.py) | Move, copy, binary files, glob patterns |
| [`multi_agent.py`](examples/multi_agent.py) | Multi-agent collaboration with shared workspace and audit trail |
| [`research_agent.py`](examples/research_agent.py) | All methods exercised — the comprehensive reference |
| [`seed_data.py`](examples/seed_data.py) | Seed a workspace with ~28 research files |
| [`ask.py`](examples/ask.py) | Natural language agent over a workspace (needs OpenAI key) |

## API Reference

All methods are async. The `Workspace` class binds identity (`org_id`, `user_id`) once at construction.

### File Operations

| Method | Signature | Description |
|--------|-----------|-------------|
| `write` | `(path, content, *, source=None, content_type=None)` | Write or update a file (text or binary). Returns version info. |
| `read` | `(path, *, token_budget=4000, version=None)` | Read latest (or specific) version. Summarizes when over budget. |
| `list` | `(path="/", *, recursive=False)` | List files and subfolders with auto-generated index. |
| `delete` | `(path, *, source=None)` | Soft-delete a file. Version history is preserved. |
| `move` | `(source_path, dest_path, *, source=None)` | Move or rename a file. Preserves version history. |
| `copy` | `(source_path, dest_path, *, source=None)` | Create an independent copy of a file. |
| `read_section` | `(path, *, line_start, line_end)` | Read a specific line range from a file. |

### Search

| Method | Signature | Description |
|--------|-----------|-------------|
| `search` | `(*, query=None, filters=None)` | Search by full-text query and/or frontmatter filters. |
| `glob` | `(pattern)` | Find files matching a glob pattern (`**/*.md`). |
| `grep` | `(query, *, path_pattern=None, context_lines=2)` | Search file contents for a string, with context. |

### Version History

| Method | Signature | Description |
|--------|-----------|-------------|
| `history` | `(path, *, limit=20)` | Get version history with timestamps and hashes. |
| `diff` | `(path, version_a, version_b)` | Unified diff between any two versions. |

### Key-Value Store

| Method | Signature | Description |
|--------|-----------|-------------|
| `kv_set` | `(key, value, *, ttl_seconds=None)` | Store a value. JSON-serializable. Optional TTL for auto-expiry. |
| `kv_get` | `(key)` | Retrieve a value by key. |
| `kv_list` | `(*, prefix=None)` | List keys, optionally filtered by prefix. |
| `kv_delete` | `(key)` | Delete a key-value entry. |

### Audit

| Method | Signature | Description |
|--------|-----------|-------------|
| `audit` | `(*, path=None, action=None, agent_id=None, since=None, until=None, limit=50)` | Query the mutation log. Filter by file, action, agent, or time range. |

## Storage Backends

| Backend | Config | Use case |
|---------|--------|----------|
| **SQLite + local disk** (default) | No config needed | Local dev, single-machine agents, MCP server |
| **MySQL + S3** | Set `database_url`, S3 credentials | Production, multi-agent, shared workspaces |

```python
# Local (default) — just works
async with Workspace() as ws: ...

# Production
async with Workspace(
    database_url="mysql+aiomysql://user:pass@host/sayou",
    s3_bucket="my-bucket",
    s3_access_key_id="...",
    s3_secret_access_key="...",
) as ws: ...
```

## What sayou is NOT

- **Not a vector database.** Pinecone, Weaviate, and Chroma store embeddings for similarity search. sayou stores structured files that agents read, write, and reason over.
- **Not a memory layer.** Mem0 and similar tools store conversation snippets. sayou stores work product — research, client records, project documentation — that compounds over time.
- **Not a sandbox.** E2B provides ephemeral execution environments. sayou provides persistent storage that outlives any single execution.
- **Not a filesystem.** AgentFS intercepts syscalls to virtualize file operations. A knowledge workspace with versioning and indexing.

## Philosophy

Read [PHILOSOPHY.md](./PHILOSOPHY.md) for the founding vision and design principles.

## Contributing

See [CONTRIBUTING.md](./CONTRIBUTING.md).

## License

Apache 2.0 — See [LICENSE](./LICENSE)
