memoryfield 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,114 @@
1
+ # OS — macOS (primary target), Linux, Windows
2
+ # macOS
3
+ .DS_Store
4
+ .DS_Store?
5
+ ._*
6
+ .Spotlight-V100
7
+ .Trashes
8
+ .fseventsd
9
+ .AppleDouble
10
+ .LSOverride
11
+ ehthumbs.db
12
+ Thumbs.db
13
+ Desktop.ini
14
+ Icon?
15
+ ehthumbs.db
16
+
17
+ # Editors / IDEs
18
+ .idea/
19
+ .vscode/
20
+ *.swp
21
+ *.swo
22
+ *~
23
+ *.sublime-project
24
+ *.sublime-workspace
25
+ .fleet/
26
+ .cursor/
27
+
28
+ # Python
29
+ __pycache__/
30
+ *.py[cod]
31
+ *$py.class
32
+ *.so
33
+ .Python
34
+ build/
35
+ develop-eggs/
36
+ dist/
37
+ downloads/
38
+ eggs/
39
+ .eggs/
40
+ lib/
41
+ lib64/
42
+ parts/
43
+ sdist/
44
+ var/
45
+ wheels/
46
+ *.egg-info/
47
+ .installed.cfg
48
+ *.egg
49
+ MANIFEST
50
+
51
+ # Virtual envs (project-local)
52
+ .venv/
53
+ venv/
54
+ env/
55
+ .python-version
56
+
57
+ # uv
58
+ uv.lock
59
+ .uv-cache/
60
+
61
+ # mypy / ruff / pytest
62
+ .mypy_cache/
63
+ .ruff_cache/
64
+ .pytest_cache/
65
+ .coverage
66
+ htmlcov/
67
+ .tox/
68
+
69
+ # Jupyter
70
+ .ipynb_checkpoints/
71
+
72
+ # Logs and local data
73
+ *.log
74
+ logs/
75
+ local/
76
+ tmp/
77
+ scratch/
78
+
79
+ # Repo-packing tool output (repomix), not project content
80
+ repomix-output.xml
81
+ .repomixignore
82
+
83
+ # Generated indexes and caches — never commit
84
+ mf.sqlite3
85
+ mf.sqlite3-journal
86
+ mf.sqlite3-wal
87
+ mf.sqlite3-shm
88
+ *.idx/
89
+ .cache/
90
+ indexes/
91
+
92
+ # Memoryfield derived data
93
+ # (canonical pages are committed; built indexes are not)
94
+ **/__pycache__/
95
+ **/.mfgpt/
96
+
97
+ # Model weights — large, never commit
98
+ models/
99
+ *.onnx
100
+ *.bin
101
+ *.safetensors
102
+ *.gguf
103
+ *.pt
104
+ *.pth
105
+ *.h5
106
+ *.tflite
107
+ *.pb
108
+
109
+ # Eval results (kept local until reported)
110
+ results/raw/
111
+ *.eval.json
112
+
113
+ # External fixtures fetched by eval/fetch_soapstones.py (pinned, not committed)
114
+ eval/fixtures/
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 whit3rabbit
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,248 @@
1
+ Metadata-Version: 2.5
2
+ Name: memoryfield
3
+ Version: 0.1.0
4
+ Summary: Memoryfield: a search-first, tiered-read memory tool for coding agents
5
+ Project-URL: Homepage, https://github.com/whit3rabbit/memoryfield
6
+ Project-URL: Repository, https://github.com/whit3rabbit/memoryfield
7
+ Author: whit3rabbit
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Classifier: Environment :: Console
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
19
+ Requires-Python: >=3.11
20
+ Requires-Dist: fastembed>=0.8
21
+ Requires-Dist: pyyaml>=6
22
+ Requires-Dist: sqlite-vec>=0.1
23
+ Provides-Extra: eval
24
+ Requires-Dist: fastembed>=0.8; extra == 'eval'
25
+ Provides-Extra: mcp
26
+ Requires-Dist: mcp<3,>=2.1; extra == 'mcp'
27
+ Provides-Extra: mlx
28
+ Requires-Dist: mlx-embedding-models>=0.0.11; (sys_platform == 'darwin' and platform_machine == 'arm64') and extra == 'mlx'
29
+ Description-Content-Type: text/markdown
30
+
31
+ # mf
32
+
33
+ Search-first memory for coding agents: plain Markdown pages, SQLite
34
+ search, and no LLM in the loop.
35
+
36
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
37
+ [![Python 3.11+](https://img.shields.io/badge/python-3.11%2B-blue.svg)](pyproject.toml)
38
+
39
+ [Install](#install) · [Quickstart](#quickstart) · [Design decisions](#design-decisions) · [Agents](#using-it-with-an-agent) · [Documentation](#documentation)
40
+
41
+ A field is a directory of Markdown pages with frontmatter. mf indexes
42
+ it into SQLite and answers a question with stubs, not pages: the agent
43
+ reads a one-line summary first and opens the body only when it needs
44
+ to.
45
+
46
+ The page format is Cal Paterson's
47
+ [memoryfield](https://calpaterson.com/memoryfields.html) spec
48
+ ([vendored copy](docs/upstream/SPEC.md)). Any spec field loads
49
+ unchanged, `mf pack --spec` writes one back out, and everything mf adds
50
+ on top is its own measured design.
51
+
52
+ Session-injected memory costs the same on every task, whether or not it
53
+ gets used. mf moves that cost to lookup time: about 100 tokens for a
54
+ default search and 55 for a point lookup, measured over 20 real agent
55
+ tasks ([Benchmarks, section 4](docs/BENCHMARKS.md#4-token-cost-benchmarks)).
56
+ Most lookups end at the stub.
57
+
58
+ ## Why mf
59
+
60
+ - **Stubs, not pages.** A search returns uuid, title, and a summary
61
+ written as the answer. Reads tier up only when the stub is not
62
+ enough.
63
+ - **A confidence line you can act on.** `high`, `low`, or `none` before
64
+ every result, from a gate calibrated on blind phrasing to demote
65
+ rather than overclaim.
66
+ - **A write path with a dedup gate.** `mf write` validates, checks for
67
+ near-duplicates, copies in, and indexes in one step.
68
+ - **Plain files, spec-compatible.** Pages stay Markdown you can read,
69
+ diff, and edit, and any memoryfield reader can load them.
70
+ - **Measured, not assumed.** Every ranking, gate, and default was run
71
+ through the real pipeline on queries written without seeing the
72
+ corpus before it was hardcoded.
73
+
74
+ ## Install
75
+
76
+ Python 3.11 or newer, installed through [uv](https://docs.astral.sh/uv/):
77
+
78
+ ```bash
79
+ uv tool install . # from a checkout
80
+ uv tool install git+https://github.com/whit3rabbit/memoryfield
81
+ ```
82
+
83
+ The first search downloads the embedding model (default
84
+ `snowflake-arctic-embed-xs`, 384-d, about 170 MB). The model is pinned
85
+ per field at `mf init`. Alternatives, and when to pick one:
86
+ [docs/models.md](docs/models.md).
87
+
88
+ `mf mcp` (an MCP server for `search`/`read`/`write`/`raw_add`) needs an
89
+ extra: `uv tool install ".[mcp]"`. It's optional because most usage is
90
+ the CLI directly or the Claude Code skill, and the MCP stack (roughly
91
+ a dozen extra packages) isn't worth pulling in for those.
92
+
93
+ ## Quickstart
94
+
95
+ Using this repo's eval corpus as sample pages:
96
+
97
+ ```console
98
+ $ mf init ~/field
99
+ Initialized empty field at ~/field/mf.sqlite3 (model snowflake-arctic-embed-xs, 384-d)
100
+
101
+ $ cp eval/corpus/codebase/*.md ~/field/ && mf index ~/field
102
+ 75 upserted, 0 unchanged, 0 deleted
103
+
104
+ $ mf search "how do we roll back a deploy" --field ~/field
105
+ confidence: low
106
+ - [code-deploy-rollback-cmd] Deploy: how to roll back a bad release
107
+ `kubectl rollout undo deployment/<service>`; rollback is a forward operation and takes ~90 seconds end-to-end.
108
+ - [code-deploy-pre-checklist] Deploy: pre-deploy checklist
109
+ Tests green, migrations applied to staging, dashboards reviewed, on-call notified, rollback plan documented.
110
+ ```
111
+
112
+ Read the confidence line before the results:
113
+
114
+ - `high`: the stub is safe to cite.
115
+ - `low`: a strong lead. `mf read <uuid>` for the page's answer section
116
+ before quoting it.
117
+ - `none`: do not cite it.
118
+
119
+ The gate errs toward demotion, not overclaiming.
120
+
121
+ When the stub is not enough, `mf read <uuid>` returns the page's answer
122
+ section, and `--tier L2` or `<uuid>#section` returns more.
123
+
124
+ New pages go
125
+ in through `mf write <draft> --field <dir>`, drafted outside the field.
126
+ Exit 2 means a near-duplicate was flagged. The calling contract an
127
+ agent should follow is in [docs/agents.md](docs/agents.md), and every
128
+ flag is in [docs/CLI.md](docs/CLI.md#mf-write).
129
+
130
+ ## Design decisions
131
+
132
+ Each choice below was measured on a 157-page corpus, blind phrasing
133
+ sets, and one field this project did not write. The numbers live
134
+ behind the links, not here, so they cannot drift.
135
+
136
+ - **Dense-first ranking.** The vector index ranks. FTS runs on every
137
+ query as a gate signal and a fallback, never as the primary ranker,
138
+ because fusing the two averaged keyword noise into good semantic
139
+ rankings. [Benchmarks, section 2](docs/BENCHMARKS.md#2-ranking-architecture-benchmarks)
140
+ - **A three-signal confidence gate.** A BM25 floor alone demoted nearly
141
+ half of the answerable blind queries and collapsed on small fields.
142
+ The gate now combines a dense distance floor, the BM25 score, and
143
+ top-1 agreement. [Benchmarks, section 3](docs/BENCHMARKS.md#3-confidence-gate-benchmarks)
144
+ - **Lean stubs by default.** Two stubs and no neighbors, because the
145
+ original five stubs and three neighbors cost more tokens than
146
+ exploring raw files did. [Benchmarks, section 4](docs/BENCHMARKS.md#4-token-cost-benchmarks)
147
+ - **A write-time dedup gate.** Cosine distance on title, summary, and
148
+ first section, with the threshold set on a labeled paraphrase set.
149
+ It catches copies and light rewordings, not thorough rewrites.
150
+ [Architecture, section 5](docs/architecture.md#5-write)
151
+ - **A small default embedder, pinned per field.** A 384-d model that
152
+ matched the larger ones on blind accuracy at a fraction of the load
153
+ time and storage. [docs/models.md](docs/models.md)
154
+ - **No LLM and no reranker inside the tool.** The host agent already in
155
+ context does extraction and judgment. mf stays deterministic, local,
156
+ and sub-second. [Architecture, "Stack"](docs/architecture.md#stack)
157
+
158
+ ## Using it with an agent
159
+
160
+ A Claude Code skill that teaches the lean calls, the confidence
161
+ contract, and the write path ships in
162
+ [.claude/skills/mf](.claude/skills/mf). Copy it into your project's
163
+ `.claude/skills/` to use mf there.
164
+
165
+ Two hooks, `mf hook stop` and `mf hook session-end`, ask the agent to
166
+ capture what it learned before it finishes and stage a transcript
167
+ pointer for later consolidation. Setup, the hooks snippet, and the
168
+ calling contract: [docs/agents.md](docs/agents.md).
169
+
170
+ ## Commands
171
+
172
+ Full arguments, flags, exit codes, and JSON outputs are documented in
173
+ [docs/CLI.md](docs/CLI.md).
174
+
175
+ | Command | What it does |
176
+ |---|---|
177
+ | `mf init [DIR]` | create `mf.sqlite3` in a field, pinning model and dimension |
178
+ | `mf index [DIR]` | scan the field's pages into the index |
179
+ | `mf search "<query>"` | stub-first lookup with the confidence gate |
180
+ | `mf read <uuid>[#section] ...` | read the answer section, one section, or L2 |
181
+ | `mf write <draft>` | validate, dedup-check, copy in, and index a draft |
182
+ | `mf raw add` | stage a freeform session extract under `raw/` |
183
+ | `mf lint [DIR]` | check writing conventions and index drift, `--check` for CI |
184
+ | `mf pack` / `mf unpack` | reproducible archive plus sha256 sidecar, verified extraction, `--spec` for other memoryfield readers |
185
+ | `mf import claude-memory <dir>` | turn a Claude Code memory directory into pages |
186
+ | `mf import wiki <dir>` | turn an index.md-style wiki into pages |
187
+ | `mf hook stop` / `mf hook session-end` | Claude Code hook handlers |
188
+ | `mf model list` | list available embedding models, dimensions, speeds, and cache status |
189
+ | `mf model install <name>` | download and cache an embedding model ahead of time |
190
+ | `mf claim <slug> --by <writer>` | atomically claim a slug before creating a page (multi-writer) |
191
+ | `mf consolidate --plan` | propose create/review actions from `raw/` entries |
192
+ | `mf mcp` | run an MCP server exposing `search`/`read`/`write`/`raw_add` over stdio |
193
+
194
+ ## Documentation
195
+
196
+ | Guide | What you can do |
197
+ |---|---|
198
+ | [Agents](docs/agents.md) | Wire mf into Claude Code: the skill, the hooks, and the lean-call contract. |
199
+ | [CLI reference](docs/CLI.md) | Look up every flag, exit code, and JSON shape. |
200
+ | [Models](docs/models.md) | Pick, pin, and pre-download an embedding model. |
201
+ | [Fields](docs/fields.md) | Write pages, lint, wire git hooks, import notes, and exchange fields with other memoryfield tools. |
202
+ | [Architecture](docs/architecture.md) | See the schema, how a search is ranked and gated, and the record of each decision. |
203
+ | [Benchmarks](docs/BENCHMARKS.md) | Read the numbers behind the design decisions. |
204
+ | [Docs index](docs/README.md) | Start from a task and find the right guide. |
205
+
206
+ ## Eval harness
207
+
208
+ The repo ships a 157-page labeled corpus, a 458-query set plus blind
209
+ vocabulary-mismatch sets, and six baselines (grep, FTS5, TF-IDF, nomic,
210
+ BGE-large, and hybrid).
211
+
212
+ The in-vocabulary scores sit near ceiling
213
+ because the queries share an authoring process with the corpus. Read
214
+ [docs/M0.5_REPORT.md](docs/M0.5_REPORT.md) with that in mind, and
215
+ [docs/BENCHMARKS.md](docs/BENCHMARKS.md) section 5 for the soapstones
216
+ field, the first corpus outside that process.
217
+
218
+ ```bash
219
+ uv sync --extra eval # fastembed into a local venv
220
+ uv sync --extra eval --extra mlx # optional, Apple Silicon MLX variants
221
+ uv run python3 -m eval.run_baselines # 45+ minutes wall time
222
+ uv run python3 -m eval.report # render the report
223
+ uv run python3 eval/fetch_soapstones.py # pinned foreign-field fixture
224
+ uv run python3 -m eval.calibrate_confidence_blind soapstones # ranking and gate on it
225
+ ```
226
+
227
+ ## Development
228
+
229
+ ```bash
230
+ uv sync --extra eval --group dev
231
+ uv run pytest tests/
232
+ ```
233
+
234
+ `uv sync` calls do not compose: each one resets the venv to exactly
235
+ what that call specifies. Pass `--extra eval` and `--group dev`
236
+ together, in one invocation.
237
+
238
+ ## Status
239
+
240
+ Read path, write path, and hooks/imports are built and tested. In
241
+ progress: multi-writer support (`mf claim`, `mf consolidate --plan`).
242
+ The per-item record of what was built, measured, and changed is in
243
+ [ROADMAP.md](ROADMAP.md). CLAUDE.md is the map for anyone working in
244
+ the repo.
245
+
246
+ ## License
247
+
248
+ MIT. See [LICENSE](LICENSE).
@@ -0,0 +1,218 @@
1
+ # mf
2
+
3
+ Search-first memory for coding agents: plain Markdown pages, SQLite
4
+ search, and no LLM in the loop.
5
+
6
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
7
+ [![Python 3.11+](https://img.shields.io/badge/python-3.11%2B-blue.svg)](pyproject.toml)
8
+
9
+ [Install](#install) · [Quickstart](#quickstart) · [Design decisions](#design-decisions) · [Agents](#using-it-with-an-agent) · [Documentation](#documentation)
10
+
11
+ A field is a directory of Markdown pages with frontmatter. mf indexes
12
+ it into SQLite and answers a question with stubs, not pages: the agent
13
+ reads a one-line summary first and opens the body only when it needs
14
+ to.
15
+
16
+ The page format is Cal Paterson's
17
+ [memoryfield](https://calpaterson.com/memoryfields.html) spec
18
+ ([vendored copy](docs/upstream/SPEC.md)). Any spec field loads
19
+ unchanged, `mf pack --spec` writes one back out, and everything mf adds
20
+ on top is its own measured design.
21
+
22
+ Session-injected memory costs the same on every task, whether or not it
23
+ gets used. mf moves that cost to lookup time: about 100 tokens for a
24
+ default search and 55 for a point lookup, measured over 20 real agent
25
+ tasks ([Benchmarks, section 4](docs/BENCHMARKS.md#4-token-cost-benchmarks)).
26
+ Most lookups end at the stub.
27
+
28
+ ## Why mf
29
+
30
+ - **Stubs, not pages.** A search returns uuid, title, and a summary
31
+ written as the answer. Reads tier up only when the stub is not
32
+ enough.
33
+ - **A confidence line you can act on.** `high`, `low`, or `none` before
34
+ every result, from a gate calibrated on blind phrasing to demote
35
+ rather than overclaim.
36
+ - **A write path with a dedup gate.** `mf write` validates, checks for
37
+ near-duplicates, copies in, and indexes in one step.
38
+ - **Plain files, spec-compatible.** Pages stay Markdown you can read,
39
+ diff, and edit, and any memoryfield reader can load them.
40
+ - **Measured, not assumed.** Every ranking, gate, and default was run
41
+ through the real pipeline on queries written without seeing the
42
+ corpus before it was hardcoded.
43
+
44
+ ## Install
45
+
46
+ Python 3.11 or newer, installed through [uv](https://docs.astral.sh/uv/):
47
+
48
+ ```bash
49
+ uv tool install . # from a checkout
50
+ uv tool install git+https://github.com/whit3rabbit/memoryfield
51
+ ```
52
+
53
+ The first search downloads the embedding model (default
54
+ `snowflake-arctic-embed-xs`, 384-d, about 170 MB). The model is pinned
55
+ per field at `mf init`. Alternatives, and when to pick one:
56
+ [docs/models.md](docs/models.md).
57
+
58
+ `mf mcp` (an MCP server for `search`/`read`/`write`/`raw_add`) needs an
59
+ extra: `uv tool install ".[mcp]"`. It's optional because most usage is
60
+ the CLI directly or the Claude Code skill, and the MCP stack (roughly
61
+ a dozen extra packages) isn't worth pulling in for those.
62
+
63
+ ## Quickstart
64
+
65
+ Using this repo's eval corpus as sample pages:
66
+
67
+ ```console
68
+ $ mf init ~/field
69
+ Initialized empty field at ~/field/mf.sqlite3 (model snowflake-arctic-embed-xs, 384-d)
70
+
71
+ $ cp eval/corpus/codebase/*.md ~/field/ && mf index ~/field
72
+ 75 upserted, 0 unchanged, 0 deleted
73
+
74
+ $ mf search "how do we roll back a deploy" --field ~/field
75
+ confidence: low
76
+ - [code-deploy-rollback-cmd] Deploy: how to roll back a bad release
77
+ `kubectl rollout undo deployment/<service>`; rollback is a forward operation and takes ~90 seconds end-to-end.
78
+ - [code-deploy-pre-checklist] Deploy: pre-deploy checklist
79
+ Tests green, migrations applied to staging, dashboards reviewed, on-call notified, rollback plan documented.
80
+ ```
81
+
82
+ Read the confidence line before the results:
83
+
84
+ - `high`: the stub is safe to cite.
85
+ - `low`: a strong lead. `mf read <uuid>` for the page's answer section
86
+ before quoting it.
87
+ - `none`: do not cite it.
88
+
89
+ The gate errs toward demotion, not overclaiming.
90
+
91
+ When the stub is not enough, `mf read <uuid>` returns the page's answer
92
+ section, and `--tier L2` or `<uuid>#section` returns more.
93
+
94
+ New pages go
95
+ in through `mf write <draft> --field <dir>`, drafted outside the field.
96
+ Exit 2 means a near-duplicate was flagged. The calling contract an
97
+ agent should follow is in [docs/agents.md](docs/agents.md), and every
98
+ flag is in [docs/CLI.md](docs/CLI.md#mf-write).
99
+
100
+ ## Design decisions
101
+
102
+ Each choice below was measured on a 157-page corpus, blind phrasing
103
+ sets, and one field this project did not write. The numbers live
104
+ behind the links, not here, so they cannot drift.
105
+
106
+ - **Dense-first ranking.** The vector index ranks. FTS runs on every
107
+ query as a gate signal and a fallback, never as the primary ranker,
108
+ because fusing the two averaged keyword noise into good semantic
109
+ rankings. [Benchmarks, section 2](docs/BENCHMARKS.md#2-ranking-architecture-benchmarks)
110
+ - **A three-signal confidence gate.** A BM25 floor alone demoted nearly
111
+ half of the answerable blind queries and collapsed on small fields.
112
+ The gate now combines a dense distance floor, the BM25 score, and
113
+ top-1 agreement. [Benchmarks, section 3](docs/BENCHMARKS.md#3-confidence-gate-benchmarks)
114
+ - **Lean stubs by default.** Two stubs and no neighbors, because the
115
+ original five stubs and three neighbors cost more tokens than
116
+ exploring raw files did. [Benchmarks, section 4](docs/BENCHMARKS.md#4-token-cost-benchmarks)
117
+ - **A write-time dedup gate.** Cosine distance on title, summary, and
118
+ first section, with the threshold set on a labeled paraphrase set.
119
+ It catches copies and light rewordings, not thorough rewrites.
120
+ [Architecture, section 5](docs/architecture.md#5-write)
121
+ - **A small default embedder, pinned per field.** A 384-d model that
122
+ matched the larger ones on blind accuracy at a fraction of the load
123
+ time and storage. [docs/models.md](docs/models.md)
124
+ - **No LLM and no reranker inside the tool.** The host agent already in
125
+ context does extraction and judgment. mf stays deterministic, local,
126
+ and sub-second. [Architecture, "Stack"](docs/architecture.md#stack)
127
+
128
+ ## Using it with an agent
129
+
130
+ A Claude Code skill that teaches the lean calls, the confidence
131
+ contract, and the write path ships in
132
+ [.claude/skills/mf](.claude/skills/mf). Copy it into your project's
133
+ `.claude/skills/` to use mf there.
134
+
135
+ Two hooks, `mf hook stop` and `mf hook session-end`, ask the agent to
136
+ capture what it learned before it finishes and stage a transcript
137
+ pointer for later consolidation. Setup, the hooks snippet, and the
138
+ calling contract: [docs/agents.md](docs/agents.md).
139
+
140
+ ## Commands
141
+
142
+ Full arguments, flags, exit codes, and JSON outputs are documented in
143
+ [docs/CLI.md](docs/CLI.md).
144
+
145
+ | Command | What it does |
146
+ |---|---|
147
+ | `mf init [DIR]` | create `mf.sqlite3` in a field, pinning model and dimension |
148
+ | `mf index [DIR]` | scan the field's pages into the index |
149
+ | `mf search "<query>"` | stub-first lookup with the confidence gate |
150
+ | `mf read <uuid>[#section] ...` | read the answer section, one section, or L2 |
151
+ | `mf write <draft>` | validate, dedup-check, copy in, and index a draft |
152
+ | `mf raw add` | stage a freeform session extract under `raw/` |
153
+ | `mf lint [DIR]` | check writing conventions and index drift, `--check` for CI |
154
+ | `mf pack` / `mf unpack` | reproducible archive plus sha256 sidecar, verified extraction, `--spec` for other memoryfield readers |
155
+ | `mf import claude-memory <dir>` | turn a Claude Code memory directory into pages |
156
+ | `mf import wiki <dir>` | turn an index.md-style wiki into pages |
157
+ | `mf hook stop` / `mf hook session-end` | Claude Code hook handlers |
158
+ | `mf model list` | list available embedding models, dimensions, speeds, and cache status |
159
+ | `mf model install <name>` | download and cache an embedding model ahead of time |
160
+ | `mf claim <slug> --by <writer>` | atomically claim a slug before creating a page (multi-writer) |
161
+ | `mf consolidate --plan` | propose create/review actions from `raw/` entries |
162
+ | `mf mcp` | run an MCP server exposing `search`/`read`/`write`/`raw_add` over stdio |
163
+
164
+ ## Documentation
165
+
166
+ | Guide | What you can do |
167
+ |---|---|
168
+ | [Agents](docs/agents.md) | Wire mf into Claude Code: the skill, the hooks, and the lean-call contract. |
169
+ | [CLI reference](docs/CLI.md) | Look up every flag, exit code, and JSON shape. |
170
+ | [Models](docs/models.md) | Pick, pin, and pre-download an embedding model. |
171
+ | [Fields](docs/fields.md) | Write pages, lint, wire git hooks, import notes, and exchange fields with other memoryfield tools. |
172
+ | [Architecture](docs/architecture.md) | See the schema, how a search is ranked and gated, and the record of each decision. |
173
+ | [Benchmarks](docs/BENCHMARKS.md) | Read the numbers behind the design decisions. |
174
+ | [Docs index](docs/README.md) | Start from a task and find the right guide. |
175
+
176
+ ## Eval harness
177
+
178
+ The repo ships a 157-page labeled corpus, a 458-query set plus blind
179
+ vocabulary-mismatch sets, and six baselines (grep, FTS5, TF-IDF, nomic,
180
+ BGE-large, and hybrid).
181
+
182
+ The in-vocabulary scores sit near ceiling
183
+ because the queries share an authoring process with the corpus. Read
184
+ [docs/M0.5_REPORT.md](docs/M0.5_REPORT.md) with that in mind, and
185
+ [docs/BENCHMARKS.md](docs/BENCHMARKS.md) section 5 for the soapstones
186
+ field, the first corpus outside that process.
187
+
188
+ ```bash
189
+ uv sync --extra eval # fastembed into a local venv
190
+ uv sync --extra eval --extra mlx # optional, Apple Silicon MLX variants
191
+ uv run python3 -m eval.run_baselines # 45+ minutes wall time
192
+ uv run python3 -m eval.report # render the report
193
+ uv run python3 eval/fetch_soapstones.py # pinned foreign-field fixture
194
+ uv run python3 -m eval.calibrate_confidence_blind soapstones # ranking and gate on it
195
+ ```
196
+
197
+ ## Development
198
+
199
+ ```bash
200
+ uv sync --extra eval --group dev
201
+ uv run pytest tests/
202
+ ```
203
+
204
+ `uv sync` calls do not compose: each one resets the venv to exactly
205
+ what that call specifies. Pass `--extra eval` and `--group dev`
206
+ together, in one invocation.
207
+
208
+ ## Status
209
+
210
+ Read path, write path, and hooks/imports are built and tested. In
211
+ progress: multi-writer support (`mf claim`, `mf consolidate --plan`).
212
+ The per-item record of what was built, measured, and changed is in
213
+ [ROADMAP.md](ROADMAP.md). CLAUDE.md is the map for anyone working in
214
+ the repo.
215
+
216
+ ## License
217
+
218
+ MIT. See [LICENSE](LICENSE).
@@ -0,0 +1,8 @@
1
+ """mf — the memoryfield CLI.
2
+
3
+ No commands are implemented yet. See ROADMAP.md Phase 1 (M1 read path):
4
+ `init`, `index`, `search`, `read` land there; `write` lands in Phase 2.
5
+ """
6
+ from __future__ import annotations
7
+
8
+ __version__ = "0.1.0"
@@ -0,0 +1,62 @@
1
+ """`mf claim` -- atomic conditional insert into the `claims` table.
2
+
3
+ Per PLAN.md's "Write" layer and ROADMAP.md 4.3: two writers racing to
4
+ create a page for the same topic should degrade to one create and one
5
+ update, not two pages. `claim slug` is the primitive that makes that
6
+ possible without a coordinator -- whichever writer's INSERT lands first
7
+ wins the slug, and the loser gets back the winner's identity so it can
8
+ look up the resulting page and `write --update` it instead.
9
+
10
+ Slug is the filename stem (ROADMAP.md 4.3's proposal, decided here):
11
+ that's the thing two writers actually collide on when they both title a
12
+ new page for the same topic without seeing each other's draft. See
13
+ mf.page.Page.slug.
14
+
15
+ SQLite serializes concurrent writers at the file level, so the
16
+ INSERT ... ON CONFLICT DO NOTHING below is atomic across processes, not
17
+ just within one connection: a second process's INSERT blocks until the
18
+ first commits, then sees the row already there.
19
+ """
20
+ from __future__ import annotations
21
+
22
+ from dataclasses import dataclass
23
+ from datetime import UTC, datetime
24
+ from sqlite3 import Connection
25
+
26
+
27
+ @dataclass
28
+ class ClaimResult:
29
+ slug: str
30
+ claimed: bool
31
+ claimed_by: str
32
+ claimed_at: str
33
+
34
+ def as_dict(self) -> dict:
35
+ return {
36
+ "slug": self.slug,
37
+ "claimed": self.claimed,
38
+ "claimed_by": self.claimed_by,
39
+ "claimed_at": self.claimed_at,
40
+ }
41
+
42
+
43
+ def claim_slug(conn: Connection, slug: str, claimed_by: str) -> ClaimResult:
44
+ """Try to claim `slug` for `claimed_by`. Returns claimed=True if this
45
+ call now holds it (either it won the race, or it already held it --
46
+ re-claiming your own slug is a no-op, not an error). claimed=False
47
+ means someone else got there first; `claimed_by`/`claimed_at` name
48
+ who and when, so the caller can look that page up and update it
49
+ instead of creating a duplicate.
50
+ """
51
+ now = datetime.now(UTC).isoformat()
52
+ conn.execute(
53
+ "INSERT INTO claims (slug, claimed_by, claimed_at) VALUES (?, ?, ?) "
54
+ "ON CONFLICT(slug) DO NOTHING",
55
+ (slug, claimed_by, now),
56
+ )
57
+ conn.commit()
58
+ row = conn.execute(
59
+ "SELECT claimed_by, claimed_at FROM claims WHERE slug = ?", (slug,)
60
+ ).fetchone()
61
+ assert row is not None, "just-inserted-or-existing row must be there"
62
+ return ClaimResult(slug=slug, claimed=(row[0] == claimed_by), claimed_by=row[0], claimed_at=row[1])