|
3 | 3 | A git-backed document store for low-volume, high-touch, human-scale data. |
4 | 4 |
|
5 | 5 | Records are TOML (or markdown-with-frontmatter) files in a git repository, laid |
6 | | -out by a per-sheet path template. Every mutation is a commit, so the log is the |
7 | | -audit trail. The engine is a shared **Rust core** with thin per-language |
8 | | -bindings: this repo ships the Node binding (`gitsheets` on npm) and a Python |
9 | | -binding, both running on the same core, so a write from either language produces |
10 | | -byte-identical commits. |
| 6 | +out by a per-sheet path template. Every write is validated against your schema, |
| 7 | +serialized to a canonical form (the same data always produces the same bytes), |
| 8 | +and lands as a commit. The log is your audit trail, the diff is your review |
| 9 | +tool, and a `git clone` is a complete copy of everything. The engine is a |
| 10 | +shared Rust core with thin per-language bindings: the `gitsheets` npm package |
| 11 | +and a Python binding both produce byte-identical commits. |
11 | 12 |
|
12 | | -The library and CLI live in [`packages/gitsheets`](packages/gitsheets); start |
13 | | -there. The Rust core and its bindings live under [`rust/`](rust/). |
| 13 | +## The whole idea in one terminal |
14 | 14 |
|
15 | | -## Spec-driven |
| 15 | +```console |
| 16 | +$ cat .gitsheets/contacts.toml |
| 17 | +[gitsheet] |
| 18 | +root = 'contacts' |
| 19 | +path = '${{ handle }}' |
| 20 | + |
| 21 | +[gitsheet.schema] |
| 22 | +type = 'object' |
| 23 | +required = ['handle', 'name'] |
| 24 | + |
| 25 | +$ npx -y gitsheets upsert contacts '{"handle": "ada", "name": "Ada Lovelace"}' |
| 26 | + |
| 27 | +$ git show HEAD --stat --oneline |
| 28 | +f3a91c2 contacts upsert ada |
| 29 | + contacts/ada.toml | 2 ++ |
| 30 | +``` |
| 31 | + |
| 32 | +A record is a file. A change is a commit. Everything git can do with commits |
| 33 | +(blame, revert, branch, push, diff, sign) now applies to your data. |
| 34 | + |
| 35 | +## What people run on it |
| 36 | + |
| 37 | +- **An application datastore.** [codeforphilly.org's rebuild](https://github.com/CodeForPhilly/codeforphilly-ng) |
| 38 | + uses gitsheets as its only public datastore: about 32,000 person records, |
| 39 | + where every mutating HTTP request becomes one commit authored as the acting |
| 40 | + user. The audit system is `git log`. |
| 41 | +- **Operational databanks.** Inventories and worklists that used to rot in |
| 42 | + spreadsheets, tracked as schema-validated records where every decision is a |
| 43 | + reviewable commit. |
| 44 | +- **AI evaluation corpora.** Agents and models write verdicts as records |
| 45 | + (keyed by subject and evaluator), one commit per pass, and nothing acts on a |
| 46 | + verdict until a human has read the diff. The schema rejects malformed model |
| 47 | + output at write time. |
| 48 | +- **Mirrors of external systems.** Extract an API into records, fix and merge |
| 49 | + data in git where diffs and review are free, then load only the deltas back. |
| 50 | + |
| 51 | +## Getting started |
| 52 | + |
| 53 | +### The library |
| 54 | + |
| 55 | +```console |
| 56 | +npm install gitsheets # Node >= 20; prebuilt engine, no Rust toolchain |
| 57 | +``` |
| 58 | + |
| 59 | +```typescript |
| 60 | +import { openRepo } from 'gitsheets'; |
| 61 | + |
| 62 | +const repo = await openRepo({ gitDir: 'data/.git' }); |
| 63 | + |
| 64 | +await repo.transact( |
| 65 | + { message: 'contacts upsert ada', author: { name: 'Ada', email: 'ada@example.org' } }, |
| 66 | + async (tx) => { |
| 67 | + await tx.sheet('contacts').upsert({ handle: 'ada', name: 'Ada Lovelace' }); |
| 68 | + }, |
| 69 | +); |
| 70 | +``` |
| 71 | + |
| 72 | +One handler, one commit, committed only on success. `openStore` layers typed |
| 73 | +sheets on top with Standard Schema validators (Zod, Valibot, ArkType), and a |
| 74 | +successful transaction auto-refreshes reads. See |
| 75 | +[the docs](https://jarvusinnovations.github.io/gitsheets/) for the API guide. |
| 76 | + |
| 77 | +### The CLI |
16 | 78 |
|
17 | | -[`specs/`](specs/) is the source of truth. Start at [`specs/README.md`](specs/README.md). |
| 79 | +Works in any repo with a `.gitsheets/` config, no install required: |
| 80 | + |
| 81 | +```console |
| 82 | +npx -y gitsheets query contacts --filter name='Ada Lovelace' |
| 83 | +npx -y gitsheets read contacts ada |
| 84 | +npx -y gitsheets normalize contacts |
| 85 | +``` |
| 86 | + |
| 87 | +Installed globally it also mounts as a git subcommand: `git sheet query …`. |
| 88 | + |
| 89 | +### For AI agents: gitsheets-axi |
| 90 | + |
| 91 | +`gitsheets-axi` is the same store behind a CLI designed for agents: compact |
| 92 | +token-efficient output, schemas surfaced with every listing, and bulk |
| 93 | +operations (upsert, patch, delete) that land as single reviewable commits. |
| 94 | + |
| 95 | +```console |
| 96 | +npx -y gitsheets-axi sheets # what's here, with schemas |
| 97 | +npx -y gitsheets-axi query contacts --group-by name |
| 98 | +jq -c '.[]' import.json | npx -y gitsheets-axi upsert contacts # one commit |
| 99 | +``` |
| 100 | + |
| 101 | +An agent working in a gitsheets repo can be given real write access: the |
| 102 | +schema gates every write and the history makes every action reversible. |
| 103 | + |
| 104 | +### The agent skill |
| 105 | + |
| 106 | +For Claude Code and compatible harnesses, an installable skill teaches the |
| 107 | +agent the config grammar, the API, and the axi tool, with session hooks that |
| 108 | +surface your sheets at startup: |
| 109 | + |
| 110 | +```console |
| 111 | +npx skills add JarvusInnovations/gitsheets -y --skill gitsheets |
| 112 | +``` |
| 113 | + |
| 114 | +### Python |
| 115 | + |
| 116 | +A Python binding ([`rust/gitsheets-py`](rust/gitsheets-py), pyo3) runs on the |
| 117 | +same core — a write from Python and a write from Node produce byte-identical |
| 118 | +commits, proven by cross-binding tests in CI. It builds from the repo today; |
| 119 | +PyPI publication is on the roadmap. |
| 120 | + |
| 121 | +## What it is not |
| 122 | + |
| 123 | +Every write is a commit, so sustained high write rates are the wrong fit. One |
| 124 | +process writes to a repo at a time. Queries are index-assisted scans with |
| 125 | +filters, grouping, and counts, not SQL joins. Large media belongs in object |
| 126 | +storage, not records. If a conventional database is serving you well, keep it. |
| 127 | + |
| 128 | +## Spec-driven |
18 | 129 |
|
19 | | -- [`specs/architecture.md`](specs/architecture.md) — stack and packaging |
20 | | -- [`specs/rust-core.md`](specs/rust-core.md) — the Rust core + thin-binding architecture |
21 | | -- [`specs/concepts.md`](specs/concepts.md) — Repository, Sheet, Record, Transaction, Store, Index |
22 | | -- [`specs/api/`](specs/api/) — per-symbol API contracts |
23 | | -- [`specs/behaviors/`](specs/behaviors/) — cross-cutting rules |
| 130 | +[`specs/`](specs/) is the source of truth for behavior; start at |
| 131 | +[`specs/README.md`](specs/README.md). The library and CLI live in |
| 132 | +[`packages/gitsheets`](packages/gitsheets); the Rust core and bindings live |
| 133 | +under [`rust/`](rust/). |
24 | 134 |
|
25 | 135 | ## License |
26 | 136 |
|
|
0 commit comments