daftari 1.11.0 → 1.12.0

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.
Files changed (35) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/README.md +109 -433
  3. package/dist/server.d.ts.map +1 -1
  4. package/dist/server.js +2 -0
  5. package/dist/server.js.map +1 -1
  6. package/dist/tools/curation.d.ts.map +1 -1
  7. package/dist/tools/curation.js +6 -0
  8. package/dist/tools/curation.js.map +1 -1
  9. package/dist/tools/read.d.ts +8 -0
  10. package/dist/tools/read.d.ts.map +1 -1
  11. package/dist/tools/read.js +6 -0
  12. package/dist/tools/read.js.map +1 -1
  13. package/dist/tools/search.d.ts.map +1 -1
  14. package/dist/tools/search.js +9 -0
  15. package/dist/tools/search.js.map +1 -1
  16. package/dist/tools/themes.d.ts.map +1 -1
  17. package/dist/tools/themes.js +2 -0
  18. package/dist/tools/themes.js.map +1 -1
  19. package/dist/tools/write.d.ts.map +1 -1
  20. package/dist/tools/write.js +26 -3
  21. package/dist/tools/write.js.map +1 -1
  22. package/package.json +2 -1
  23. package/templates/reviewer-vault/.daftari/config.yaml +25 -0
  24. package/templates/reviewer-vault/.daftari/tensions.md +6 -0
  25. package/templates/reviewer-vault/_drafts/moonshot-agentic-curation.md +33 -0
  26. package/templates/reviewer-vault/_drafts/scratch-incomplete.md +18 -0
  27. package/templates/reviewer-vault/competitive-intel/aurora-pipelines-positioning.md +40 -0
  28. package/templates/reviewer-vault/competitive-intel/cirrus-realtime-ga.md +38 -0
  29. package/templates/reviewer-vault/competitive-intel/cirrus-realtime-preview.md +34 -0
  30. package/templates/reviewer-vault/competitive-intel/helios-connect-overview.md +40 -0
  31. package/templates/reviewer-vault/competitive-intel/northwind-governance-snapshot.md +35 -0
  32. package/templates/reviewer-vault/pricing/aurora-pricing-model.md +35 -0
  33. package/templates/reviewer-vault/pricing/cirrus-capacity-tiers.md +37 -0
  34. package/templates/reviewer-vault/pricing/helios-credits-model.md +37 -0
  35. package/templates/reviewer-vault/pricing/vega-pricing-strategy.md +36 -0
package/CHANGELOG.md CHANGED
@@ -7,6 +7,38 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [1.12.0] - 2026-05-26
11
+
12
+ ### Added
13
+
14
+ - **Cross-platform MCPB packaging (#66).** The `.mcpb` artifact now
15
+ boots on both macOS (arm64) and Windows (x64). A single universal
16
+ package bundles platform-tagged native binaries for `better-sqlite3`
17
+ (under `build/Release-${platform}-${arch}/`) and `sharp`, and a
18
+ one-line loader patch in `better-sqlite3`'s `lib/database.js`
19
+ selects the right binary at runtime from `process.platform` /
20
+ `process.arch`. The manifest's `compatibility.platforms` is back
21
+ to `["darwin", "win32"]`. `npm run pack:mcpb` (new) builds the
22
+ universal artifact from a darwin-arm64 host.
23
+
24
+ - **MCP tool annotations.** All 14 tools now carry a `title` and the
25
+ appropriate safety hint — `readOnlyHint` for read/search/analysis
26
+ tools, `destructiveHint` for write and curation tools. MCP clients
27
+ use these to label tools and to decide when to prompt for
28
+ confirmation before a call.
29
+
30
+ ### Fixed
31
+
32
+ - **`vault_write` no longer rejects writes that omit `updated` /
33
+ `updated_by`.** The server stamps both fields on every write, so requiring
34
+ callers to also supply them was redundant — and a caller who omitted them
35
+ (reasonably) got `invalid frontmatter: updated: missing required field;
36
+ updated_by: missing required field`. The fields are now filled in before
37
+ built-in schema validation runs, then re-stamped post-validation by
38
+ `performWrite` as before. Callers that still supply them keep working — the
39
+ server-side stamp wins, identical to the previous behavior. The MCP input
40
+ schema description now flags both fields as server-managed.
41
+
10
42
  ## [1.11.0] - 2026-05-21
11
43
 
12
44
  ### Added
package/README.md CHANGED
@@ -1,276 +1,119 @@
1
1
  # Daftari
2
2
 
3
- [![CI](https://github.com/mavaali/daftari/actions/workflows/ci.yml/badge.svg)](https://github.com/mavaali/daftari/actions/workflows/ci.yml)
4
- [![npm version](https://img.shields.io/npm/v/daftari.svg)](https://www.npmjs.com/package/daftari)
5
- [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
3
+ [![CI](https://github.com/mavaali/daftari/actions/workflows/ci.yml/badge.svg)](https://github.com/mavaali/daftari/actions/workflows/ci.yml) [![npm version](https://img.shields.io/npm/v/daftari.svg)](https://www.npmjs.com/package/daftari) [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
4
+
5
+ *Daftari* (دفتری) is the Urdu word for a ledger-keeper: the person in a
6
+ trading house who maintained the *daftar*, the bound register where every
7
+ transaction was recorded, cross-referenced, and preserved. The daftar was not a
8
+ filing cabinet. It was a living document. Entries referenced earlier entries.
9
+ Corrections were noted, not erased. The ledger got more valuable the longer it
10
+ was kept, because the accumulated record revealed patterns no single entry
11
+ could.
12
+
13
+ Daftari is an MCP server that gives AI agents the same thing: a persistent,
14
+ structured knowledge vault they can read, write, and curate over time. A
15
+ cortex, not a clipboard.
16
+
17
+ ## The problem
18
+
19
+ Every agent conversation starts from zero. RAG retrieves chunks and hopes the
20
+ model stitches them together. AGENTS.md gives static context that nobody
21
+ updates. The knowledge an agent builds during a session evaporates when the
22
+ session ends.
23
+
24
+ Daftari takes the other path: **compilation over retrieval.** The agent
25
+ synthesizes an answer once, writes it back as a durable document, and every
26
+ later read starts from that compiled result. The vault gets better the more it
27
+ is used.
28
+
29
+ A human cortex doesn’t re-derive everything from sensory input each time it
30
+ thinks. It consolidates: experiences become memories, memories become
31
+ structure, structure shapes future thought. Daftari gives agents the same
32
+ loop. Drafts consolidate into canonical knowledge. Contradictions surface as
33
+ tensions. Stale knowledge decays on a schedule. The vault is a living system,
34
+ not a filing cabinet.
35
+
36
+ ## What it is
37
+
38
+ A directory of markdown files with YAML frontmatter, exposed to agents as 14
39
+ MCP tools over stdio. The vault is plain text: you can read it in any editor,
40
+ `git log` it, grep it. Daftari adds the machinery agents need to treat it as a
41
+ shared workspace.
6
42
 
7
- **An MCP server that exposes a curated markdown vault to AI agents.**
8
-
9
- Daftari is not RAG. It is not a chatbot. It is a *living, agent-maintained
10
- knowledge vault* — a directory of markdown files that an AI agent reads from,
11
- writes to, and curates over time, so that knowledge **compounds** instead of
12
- being re-derived on every query.
13
-
14
- > *Daftari — from دفتر (daftar): notebook, ledger, register. A word shared
15
- > across Urdu, Hindi, Marathi, Arabic, Persian, and Turkish for the book you
16
- > write things down in so you don't forget.*
17
-
18
- RAG retrieves chunks and hopes the model stitches them together. Daftari takes
19
- the other path: the agent does the stitching *once*, writes the synthesized
20
- result back as a durable document, and every later read starts from that
21
- compiled answer. Karpathy's framing fits — **compilation over retrieval**. The
22
- vault gets better the more it is used.
23
-
24
- A vault is just markdown. You can read it, `git log` it, and edit it by hand.
25
- Daftari adds the machinery an agent needs to treat it as a shared workspace:
26
- access control, write safety, provenance, and curation.
27
-
28
- ---
29
-
30
- ## The four-layer model
31
-
32
- Daftari is built in four layers. The first two are table stakes. **The moat is
33
- layers 3 and 4** — anyone can store markdown and check a permission; keeping
34
- every write safe and attributable, and managing knowledge decay, is the hard
35
- part.
36
-
37
- | Layer | Concern | What Daftari provides |
38
- |------:|---------|-----------------------|
39
- | 1 | **Storage** | Markdown + YAML frontmatter on disk, a git history, a rebuildable SQLite index — FTS5 for lexical ranking, sqlite-vec for vector search. |
40
- | 2 | **Multi-tenant ACL** | Config-driven RBAC. Roles and per-collection read/write/promote permissions declared in `.daftari/config.yaml`. |
41
- | 3 | **Write safety** ⭐ | File-level write locks (SQLite-backed, 60s TTL) give single-writer-per-document safety — a competing writer fails cleanly instead of corrupting the file. This is a safety mechanism, not a coordination protocol. The ⭐ is for what is genuinely differentiated: every write auto-committed to git with a provenance log of who changed what and when. |
42
- | 4 | **Curation decay** ⭐ | The draft → canonical → deprecated lifecycle, TTL-based staleness, tension logging for contradictions, and an advisory linter. Knowledge that stops being true is surfaced, not silently trusted. |
43
-
44
- Layer 3 today is *safety*, not orchestration: the lock prevents file corruption
45
- and simultaneous writers, but a writer can still overwrite another's work if it
46
- composed its change against a since-changed version of the document. Closing
47
- that gap — with optimistic concurrency, not queuing — is the v2 direction; see
48
- [What's not in v1](#whats-not-in-v1).
49
-
50
- Layers 1–2 keep the vault *stored and scoped*. Layers 3–4 keep it *coherent as
51
- it grows* — which is the entire point of a vault that compounds.
52
-
53
- ---
54
-
55
- ## Quickstart
56
-
57
- ```bash
58
- # 1. Scaffold a new vault (collections, config, example documents, git, index)
43
+ ```
59
44
  npx daftari --init ./my-vault
60
-
61
- # 2. Start the MCP server against it, as an identity with a role
62
45
  npx daftari --vault ./my-vault --user me --role admin
63
46
  ```
64
47
 
65
- The server speaks the Model Context Protocol over stdio. Point any MCP client
66
- (Claude Desktop, an agent SDK, your own harness) at it. See
67
- [docs/getting-started.md](docs/getting-started.md) for the full walkthrough,
68
- including a `claude_desktop_config.json` snippet.
69
-
70
- ---
71
-
72
- ## The MCP tools
73
-
74
- Daftari exposes 13 tools, grouped by layer.
48
+ Point any MCP client (Claude Desktop, Claude Code, an agent SDK) at it.
75
49
 
76
- **Read path**
50
+ ## The four layers
77
51
 
78
- | Tool | Description |
79
- |------|-------------|
80
- | `vault_read` | Read one document: markdown body, parsed frontmatter, an advisory validation report, and an inline decay assessment. |
81
- | `vault_index` | List documents, filterable by collection, status, domain, or tags. |
82
- | `vault_status` | Vault health dashboard: total file count, per-collection counts, count of documents with invalid frontmatter, a staleness distribution (fresh/aging/stale), unresolved tensions, and recent write history. |
52
+ Storage and access control are table stakes. The moat is layers 3 and 4.
83
53
 
84
- **Search**
54
+ |Layer |What it does |Why it matters |
55
+ |---------------------|------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------|
56
+ |**Storage** |Markdown + frontmatter on disk, git history, rebuildable SQLite index for hybrid BM25 + vector search.|Plain text is the source of truth. Delete every `.db` file and rebuild.|
57
+ |**Access control** |Config-driven RBAC. Roles and per-collection read/write/promote permissions in `.daftari/config.yaml`.|Multiple agents, scoped access, no user-management system. |
58
+ |**Write arbitration**|File-level locks (60s TTL), auto-commit to git, structured provenance log. |Concurrent agents write safely. Every mutation is attributable. |
59
+ |**Curation** |Draft-to-canonical lifecycle, TTL-based staleness, tension logging, advisory linter. |Knowledge that stops being true gets surfaced, not silently trusted. |
85
60
 
86
- | Tool | Description |
87
- |------|-------------|
88
- | `vault_search` | Hybrid BM25 + vector search across the vault, with tunable ranking weights; each hit carries an inline decay assessment. |
89
- | `vault_search_related` | Find documents thematically related to a given document. |
90
- | `vault_themes` | Surface thematic clusters across the vault via k-means over document-pooled embeddings. Heuristic labels (TF-IDF), per-theme coherence, representative docs, secondary docs (cross-cutting documents from other clusters that also align here), deterministic output. |
91
- | `vault_reindex` | Rebuild the SQLite search index from the markdown files. |
61
+ ## The tools
92
62
 
93
- **Write safety**
63
+ **Read:** `vault_read`, `vault_index`, `vault_status`
94
64
 
95
- | Tool | Description |
96
- |------|-------------|
97
- | `vault_write` | Create or overwrite a document. Stamps `updated`/`updated_by`, preserves `created`, auto-commits. |
98
- | `vault_append` | Append a markdown section to a document. Re-stamps metadata, auto-commits. |
99
- | `vault_promote` | Promote a draft to canonical — refuses unless the draft's frontmatter is complete. |
100
- | `vault_deprecate` | Mark a document deprecated with a required reason and an optional `superseded_by`. |
65
+ **Search:** `vault_search` (hybrid BM25 + vector), `vault_search_related`, `vault_themes` (thematic clustering), `vault_reindex`
101
66
 
102
- **Curation**
67
+ **Write:** `vault_write`, `vault_append`, `vault_promote`, `vault_deprecate`
103
68
 
104
- | Tool | Description |
105
- |------|-------------|
106
- | `vault_tension_log` | Record a contradiction between two documents to the advisory tension log. Records; does not resolve. |
107
- | `vault_lint` | Run advisory curation checks: stale-past-TTL, orphans, old drafts, stagnant low-confidence files, deprecated-but-linked, unanswered questions. |
108
- | `vault_provenance` | Return a single document's full write history from the provenance log. |
69
+ **Curate:** `vault_tension_log`, `vault_lint`, `vault_provenance`
109
70
 
110
- The curation engine is **advisory**: `vault_lint` reports problems and
111
- `vault_tension_log` records contradictions neither auto-fixes anything. A
112
- human or a deliberate agent decision drives every change.
71
+ The curation engine is advisory: `vault_lint` reports problems and
72
+ `vault_tension_log` records contradictions. Neither auto-fixes anything. Every
73
+ change is a deliberate, attributable act.
113
74
 
114
- ---
75
+ ## Two kinds of knowledge
115
76
 
116
- ## What an agent call looks like
77
+ Every document declares a `domain`. The distinction drives how the curation
78
+ layer treats it.
117
79
 
118
- Daftari speaks the Model Context Protocol over stdio. An agent invokes a tool
119
- by name with JSON arguments; the server replies with a JSON text block. Here is
120
- `vault_search` against a freshly scaffolded vault (`npx daftari --init`):
80
+ **Accumulation** documents compile and compound. A competitive-intel note, a
81
+ pricing breakdown, a researched comparison. Each write builds on the last.
82
+ Going stale is a problem to fix.
121
83
 
122
- **Request**
123
-
124
- ```json
125
- { "method": "tools/call", "params": {
126
- "name": "vault_search",
127
- "arguments": { "query": "consumption pricing", "limit": 1 } } }
128
- ```
84
+ **Generative** documents speculate. A moonshot sketch, a brainstorm, a “what
85
+ if.” Going stale is expected, not a defect.
129
86
 
130
- **Response** `content[0].text`, parsed:
131
-
132
- ```json
133
- {
134
- "query": "consumption pricing",
135
- "count": 1,
136
- "vectorUsed": true,
137
- "weights": { "bm25": 0.5, "vector": 0.5 },
138
- "hits": [
139
- {
140
- "path": "pricing/helios-consumption-pricing.md",
141
- "title": "Helios Consumption Pricing (Compute Credit Model)",
142
- "collection": "pricing", "status": "canonical",
143
- "score": 1, "bm25Score": 1, "vectorScore": 1,
144
- "snippet": "# Helios Consumption Pricing (Compute Credit Model) Helios is a fictional platform…",
145
- "decay": null
146
- }
147
- ]
148
- }
149
- ```
87
+ The same curation rules applied uniformly would either nag about every
88
+ brainstorm or quietly trust every stale fact. The domain split lets the system
89
+ hold each to the right standard.
150
90
 
151
- ---
91
+ ## Access control
152
92
 
153
- ## Search internals
154
-
155
- `vault_search` is **hybrid**: a BM25 lexical score and a vector (semantic)
156
- score, blended with tunable weights. Both halves are SQL-native — they
157
- run inside SQLite, not in JavaScript.
158
-
159
- - **Lexical half.** An FTS5 virtual table (`documents_fts`) over title,
160
- tags, and body. SQLite's built-in BM25 ranks every MATCH'd row.
161
- Triggers on the regular `documents` table keep the FTS index in sync
162
- on every write, so the indexer never touches the virtual table
163
- directly. Free-text queries are tokenised, stopword-filtered, and
164
- prefix-OR'd (`cirrus pricing` becomes `cirrus* OR pricing*`) so a
165
- partial-keystroke or stem variation still matches.
166
-
167
- - **Vector half.** A sqlite-vec `vec0` virtual table
168
- (`embeddings_vec`), sized at the active provider's dim and indexed for
169
- KNN cosine queries. The durable `embeddings` cache (one row per
170
- `(content_hash, model)`) is the source of truth; `embeddings_vec`
171
- mirrors it for query-time access. Switching embedding providers
172
- triggers a drop-and-rebuild of the vec table at the new dim — the
173
- durable cache survives, so switching back is all cache hits.
174
-
175
- **Prerequisite.** sqlite-vec is a loadable SQLite extension. The
176
- `sqlite-vec` npm package ships pre-built binaries for darwin / linux /
177
- windows on x64 and arm64; `better-sqlite3`'s npm prebuilt enables
178
- extension loading by default. In the common case `npm install` is the
179
- only setup step. If a custom `better-sqlite3` build with extension
180
- loading disabled is in use, Daftari refuses to start with an actionable
181
- error: `npm rebuild better-sqlite3 --build-from-source`.
182
-
183
- The vector half is worth being explicit about, because a local-first
184
- tool should never leave you guessing whether a query leaves your
185
- machine.
186
-
187
- ### Embedding providers
188
-
189
- Daftari ships with two embedding backends. Pick one in
190
- `.daftari/config.yaml`:
93
+ No user-management system. Roles live in config, the server starts with one:
191
94
 
192
95
  ```yaml
193
- embeddings:
194
- provider: local-minilm # default. Other values: openai-3-small.
195
- ```
196
-
197
- - **`local-minilm`** (default). `all-MiniLM-L6-v2` (the
198
- `Xenova/all-MiniLM-L6-v2` build), a 384-dimension sentence-transformer.
199
- Runs entirely **local**: embeddings are computed in-process by
200
- [`@huggingface/transformers`](https://www.npmjs.com/package/@huggingface/transformers)
201
- (Transformers.js). No external embedding API — nothing is sent to
202
- Hugging Face, OpenAI, or anyone else at index or query time. Just
203
- `npm install` — no Python, no API key. The **first** reindex downloads
204
- the model weights (~25 MB) from the Hugging Face hub and caches them on
205
- disk; every run after that is fully offline. Slow on cold start
206
- (~25 min CPU on a 44k-chunk vault), but free.
207
-
208
- - **`openai-3-small`**. OpenAI's `text-embedding-3-small`, a 1536-dimension
209
- hosted embedding. **Sends chunk text to OpenAI** at reindex time —
210
- enable this only if you're comfortable with that. Requires
211
- `OPENAI_API_KEY` in the server's environment (it is never read from
212
- config files). ~10x faster than `local-minilm` on large vaults; on the
213
- 44k-chunk benchmark above, ~2 minutes and ~$0.10. Because Daftari's
214
- embedding cache is content-addressed by `(content_hash, model)`, the
215
- paid cost is a **one-time event per chunk text** — re-running
216
- `vault_reindex` on an unchanged vault embeds zero new chunks. Switching
217
- providers between server runs is safe: the cache keeps both providers'
218
- rows, so switching back to the other later re-uses what was previously
219
- embedded.
220
-
221
- - **Graceful degradation.** Whichever provider is active, if it cannot
222
- reach the model (no network on the very first `local-minilm` run, before
223
- the weights are cached; or OpenAI unreachable), `vault_reindex` still
224
- builds the FTS5 lexical index. The vector column is left empty,
225
- `vectorUsed` reports `false`, and search transparently falls back to
226
- lexical-only ranking.
227
-
228
- - **Quality tradeoff.** MiniLM is small and fast, which keeps Daftari
229
- dependency-light and snappy, but its recall/precision is below larger
230
- hosted embedding models. `openai-3-small` is the obvious next step.
231
- Pairing either with FTS5 BM25 covers the common case where a small
232
- model misses an exact-term match.
233
-
234
- ---
235
-
236
- ## RBAC
237
-
238
- Access is config-driven. There is no user-management system — roles and their
239
- per-collection permissions live in `.daftari/config.yaml`, and the server is
240
- started with `--role <name>` to select one:
241
-
242
- ```yaml
243
- version: 1
244
- vault_name: my-vault
245
-
246
96
  roles:
247
97
  analyst:
248
98
  read: [competitive-intel, pricing]
249
99
  write: [competitive-intel, _drafts]
250
100
  researcher:
251
- read: ["*"] # "*" matches every collection
101
+ read: ["*"]
252
102
  write: [moonshot, _drafts]
253
103
  admin:
254
104
  read: ["*"]
255
105
  write: ["*"]
256
- promote: true # only this role may promote drafts to canonical
106
+ promote: true
257
107
  ```
258
108
 
259
- - `read` collections the role may read and search
260
- - `write` — collections the role may create, append to, or deprecate in
261
- - `promote` — whether the role may promote a draft to canonical (default `false`)
262
-
263
- Starting the server with no `--role`, or with a name not in the config, falls
264
- back to a deny-all **guest**: every tool is denied.
265
-
266
- ---
109
+ No `--role` or an unknown name falls back to deny-all.
267
110
 
268
111
  ## File format
269
112
 
270
- Every document is a markdown file with a YAML frontmatter block. Frontmatter
271
- *is* the metadata layer — there is no separate database of record.
113
+ Markdown with YAML frontmatter. Frontmatter is the metadata layer; there is no
114
+ separate database.
272
115
 
273
- ```markdown
116
+ ```yaml
274
117
  ---
275
118
  title: "Aurora Pipelines — Positioning Overview"
276
119
  domain: accumulation
@@ -280,231 +123,64 @@ confidence: medium
280
123
  created: 2026-05-17
281
124
  updated: 2026-05-17
282
125
  updated_by: agent:claude-code
283
- provenance: direct
126
+ provenance: synthesized
284
127
  sources:
285
128
  - aurora-product-page
286
- superseded_by: null
287
129
  ttl_days: 120
288
130
  tags: [aurora, ingestion, competitive]
289
131
  questions_answered:
290
- - "How does Aurora frame the ingestion-vs-transformation boundary?"
132
+ - "How does Aurora frame the ingestion/transformation boundary?"
291
133
  questions_raised:
292
134
  - "Does an authored-pipeline model slow teams down at small scale?"
293
135
  ---
294
-
295
- # Aurora Pipelines — Positioning Overview
296
-
297
- Aurora Pipelines treats ingestion as an authored, version-controlled artifact
298
- rather than a managed black box.
299
-
300
- ## Questions Answered
301
- - How does Aurora frame the ingestion-vs-transformation boundary?
302
-
303
- ## Questions Raised
304
- - Does an authored-pipeline model slow teams down at small scale?
305
136
  ```
306
137
 
307
- The optional `questions_answered` / `questions_raised` frontmatter fields make
308
- a document's epistemic edges explicit and **queryable**: `vault_index` can
309
- filter on open questions and `vault_lint` flags questions no document answers.
310
- The matching `## Questions Answered` / `## Questions Raised` body sections are
311
- an optional human-readable mirror. Full field reference in
312
- [docs/file-format.md](docs/file-format.md).
138
+ Documents can make their epistemic edges explicit: `questions_answered` is what
139
+ later agents can take as settled, `questions_raised` is where to build next.
140
+ `vault_lint` turns the open questions across the vault into a coverage map.
313
141
 
314
- Daftari's built-in frontmatter covers most vaults out of the box. For
315
- domain-specific fields, add a `schema_extensions` block to
316
- `.daftari/config.yaml` — typed extension fields that participate in validation
317
- and serialize in a stable order, with no core schema change. See
318
- [docs/schema-extensions.md](docs/schema-extensions.md).
142
+ Full field reference in <docs/file-format.md>.
319
143
 
320
- ---
144
+ ## How it compares
321
145
 
322
- ## Vault hooks
146
+ | |AGENTS.md |RAG |Daftari |
147
+ |--------------------|-----------------|-----------------------------|-------------------------------------|
148
+ |Who writes? |Humans |Nobody (retrieval only) |Agents + humans |
149
+ |Scales? |One file, doesn’t|Scales storage, not coherence|Structured collections with lifecycle|
150
+ |Knowledge compounds?|No |No |Yes, draft → canonical → deprecated |
151
+ |Contradictions? |Invisible |Invisible |Tension log surfaces them |
152
+ |Staleness? |Silent |Silent |TTL-based decay with advisory lint |
323
153
 
324
- Hooks are vault-owner-supplied functions that run **before every write** to
325
- this vault. They let an organisation enforce conventions the built-in
326
- frontmatter validator does not know about — naming rules, status-transition
327
- guards, business-specific cross-field invariants, refusal lists — without
328
- forking daftari or wrapping the MCP server. A hook is a plain ES module that
329
- exports a default function and returns a list of `ValidationIssue` objects.
330
- Any issue blocks the write, exactly the way a built-in schema violation does.
154
+ ## What’s not in v1
331
155
 
332
- Hooks are declared in `.daftari/config.yaml`:
156
+ Deliberately deferred to keep the surface tight:
333
157
 
334
- ```yaml
335
- hooks:
336
- pre_write:
337
- - path: .daftari/hooks/forbid-status-skip.mjs
338
- - path: .daftari/hooks/require-decision-id.mjs
339
- ```
158
+ - **Cloud-hosted multi-tenant server** with S3/GCS backend and token auth
159
+ - **Remote MCP transport** for claude.ai web, mobile, and Cowork (v1 is a local desktop extension for Claude Desktop and Claude Code)
160
+ - **Conflict resolution beyond file-level locks** (CRDTs, semantic merge)
161
+ - **Background curation agent** running lint on a cadence
162
+ - **LLM reranking** of search results
163
+ - **Enforced domain separation** (v1 documents the convention; v2 enforces it)
340
164
 
341
- The `path` is vault-root-relative. Hooks run in declared order. Every hook
342
- runs on every write, even if an earlier hook produced issues — the caller
343
- gets one consolidated list back, not the first failure.
344
-
345
- A hook looks like this:
346
-
347
- ```ts
348
- // .daftari/hooks/forbid-status-skip.mjs
349
- //
350
- // ValidationIssue = { field: string; message: string }
351
- // context = { path: string; operation: 'create' | 'update' | 'append' }
352
- export default function forbidStatusSkip(frontmatter, context) {
353
- if (context.operation !== "update") return [];
354
- if (frontmatter.status === "canonical" && frontmatter.previous_status === "draft") {
355
- return [
356
- {
357
- field: "status",
358
- message: "draft → canonical is not allowed; promote via the dedicated tool",
359
- },
360
- ];
361
- }
362
- return [];
363
- }
364
- ```
365
-
366
- A hook is called with the already-stamped frontmatter the write is about to
367
- land (so `updated` and `updated_by` reflect this call, not the previous
368
- version on disk). The hook **must not mutate its inputs**; v1 is
369
- validate-only. Returning a non-array, or an array containing malformed issue
370
- objects, is itself reported as a blocking issue tagged with the hook path —
371
- hook bugs surface as loud failures, not silent passes.
372
-
373
- ### Transform hooks
374
-
375
- A `pre_write` hook can observe and reject, but it cannot *change* the
376
- frontmatter a write lands. **Transform hooks** can. A transform hook runs in an
377
- earlier phase — before built-in schema validation — so it can derive or
378
- override frontmatter fields the validator would otherwise reject as missing.
379
-
380
- Transform hooks are declared under their own key, `pre_write_transform`:
381
-
382
- ```yaml
383
- hooks:
384
- pre_write_transform:
385
- - path: .daftari/hooks/derive-status.mjs
386
- pre_write:
387
- - path: .daftari/hooks/forbid-status-skip.mjs
388
- ```
389
-
390
- The phase order is fixed regardless of how the config lists the blocks: every
391
- `pre_write_transform` hook runs (in declared order), then built-in schema
392
- validation, then every `pre_write` validator (in declared order). A transform
393
- always runs before any validator sees the frontmatter.
394
-
395
- A transform hook returns a `Partial<Frontmatter>` patch — *not* a list of
396
- issues:
397
-
398
- ```ts
399
- // .daftari/hooks/derive-status.mjs
400
- //
401
- // context = { path: string; operation: 'create' | 'update' | 'append' }
402
- export default function deriveStatus(frontmatter, context) {
403
- if (frontmatter.decision_status === "ACTIVE") {
404
- return { status: "canonical" };
405
- }
406
- return {}; // no change
407
- }
408
- ```
409
-
410
- The runner merges each patch into the candidate frontmatter **`Object.assign`
411
- style**: shallow, last-writer-wins. A key present in the patch replaces the
412
- existing value outright — arrays are replaced whole, never appended to or
413
- merged element-wise. When two transforms target the same field, the
414
- later-declared one wins. Each transform sees the merged output of every
415
- transform declared before it.
416
-
417
- A transform **refuses by throwing** — it does not return issues. A throw
418
- becomes a synthetic blocking issue tagged with the hook path, identical to the
419
- `pre_write` throw mechanism. Returning anything that is not an object (an
420
- array, a primitive, `null`) is likewise a blocking issue.
421
-
422
- Because transforms run before validation, a transform that sets an invalid
423
- value — a `status` outside the allowed set, say — is caught by the built-in
424
- validator exactly as a bad user-supplied value would be.
425
-
426
- ### Trust model
427
-
428
- Hooks are **trusted code**. They run in the same Node process as the daftari
429
- server, with the same filesystem and network access. v1 does no sandboxing,
430
- no permission prompts, no signature checking — the vault owner is responsible
431
- for the contents of `.daftari/hooks/`. Treat hook files the way you would
432
- treat `package.json` scripts or git hooks: review every change, never run a
433
- vault you don't trust, and pin hook code in source control next to the
434
- config that loads it. If you need stronger isolation than that, don't
435
- register hooks in v1.
436
-
437
- ### Scope and limits in v1
438
-
439
- - **Surfaces:** `pre_write` (validators) and `pre_write_transform`
440
- (field-deriving transforms). Future surfaces (`pre_read`, `post_write`,
441
- etc.) are reserved — unrecognised keys under `hooks:` are a loud config
442
- error, not a silent skip.
443
- - **Operations:** both hook surfaces fire for `vault_write` (create + update)
444
- and `vault_append`. `vault_promote` and `vault_deprecate` deliberately
445
- bypass hooks — they're narrow metadata mutations the server controls
446
- end-to-end.
447
- - **Two phases:** a `pre_write` hook returns a list of issues and can only
448
- reject. A `pre_write_transform` hook returns a `Partial<Frontmatter>` patch
449
- and can derive or override fields before validation — see "Transform
450
- hooks" above.
451
- - **Sync:** hook bodies are synchronous functions. The loader is async
452
- (it has to dynamic-import the module), but each individual hook call is
453
- not awaited.
454
- - **No caching across calls:** hooks are re-imported per write; expect to
455
- pay one ESM dynamic-import per declared hook per call. The next iteration
456
- may cache. Edits to a hook file are picked up on the next write — no
457
- server restart required.
458
-
459
- See [issue #29](https://github.com/mavaali/daftari/issues/29) for the design
460
- rationale and the alternatives that were rejected.
461
-
462
- ---
463
-
464
- ## What's not in v1
465
-
466
- A few capabilities were deliberately deferred so v1 ships with a tight,
467
- defensible surface — a server that does its core job well rather than a wide
468
- one that does many jobs partially. Not in this release:
469
-
470
- - **Self-hosted server mode** — a long-lived HTTP/SSE server multiple clients
471
- connect to, with pluggable cloud-storage backends (ADLS, S3, GCS) and OAuth
472
- authentication. Self-hosted by the operator, *not* a managed service. v1 runs
473
- against a local filesystem as a single stdio process.
474
- - **LLM reranking of search results** — a model pass over the BM25 + vector
475
- candidate set. v1 ships hybrid ranking without a rerank stage.
476
- - **Enforced domain separation** — v1 *documents* the convention that
477
- generative-domain documents are not cross-referenced into accumulation pages;
478
- the write tools do not yet enforce it. v2 will.
479
-
480
- Each of these is a clean increment on top of a surface that already works —
481
- deliberately deferred, not forgotten.
482
-
483
- ---
484
-
485
- ## Documentation
486
-
487
- - [docs/getting-started.md](docs/getting-started.md) — end-to-end walkthrough: scaffold, write, search, lint, promote, deprecate, and connect from Claude Desktop.
488
- - [docs/worked-example.md](docs/worked-example.md) — the compilation thesis shown, not argued: one document maturing across three agent writes, contrasted with RAG.
489
- - [docs/architecture.md](docs/architecture.md) — the layered architecture, the request path, and the accumulation-vs-generative domain split.
490
- - [docs/curation-workflow.md](docs/curation-workflow.md) — the reference curation loop: how an agent acts on `vault_lint` output instead of letting it pile up.
491
- - [docs/file-format.md](docs/file-format.md) — the complete frontmatter reference and markdown body conventions.
492
- - [docs/schema-extensions.md](docs/schema-extensions.md) — declaring typed, vault-specific frontmatter fields with a `schema_extensions` config block.
493
-
494
- ---
165
+ Each is a clean increment on a surface that already works.
495
166
 
496
167
  ## Development
497
168
 
498
- ```bash
169
+ ```
499
170
  npm install
500
- npm run build # compile TypeScript to dist/
501
- npm test # run the vitest suite
502
- npm run dev # run the server in watch mode against the sample vault
171
+ npm run build
172
+ npm test
503
173
  ```
504
174
 
505
175
  Design tenets: functions and types, no classes; tool handlers return
506
176
  `Result<T, Error>` rather than throwing; tests mirror the `src/` structure.
507
177
 
178
+ ## Documentation
179
+
180
+ - <docs/getting-started.md> — scaffold, write, search, lint, promote, deprecate
181
+ - <docs/architecture.md> — layered design, request path, accumulation vs. generative domains
182
+ - <docs/file-format.md> — complete frontmatter reference
183
+
508
184
  ## License
509
185
 
510
- MIT. Open source — `daftari` on npm, [`mavaali/daftari`](https://github.com/mavaali/daftari) on GitHub.
186
+ MIT.
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAEnE,OAAO,EAAE,KAAK,aAAa,EAAe,MAAM,kBAAkB,CAAC;AAOnE,eAAO,MAAM,WAAW,YAAY,CAAC;AAQrC,eAAO,MAAM,cAAc,QAAmB,CAAC;AAK/C,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,GAAE,aAA6B,GAAG,MAAM,CA2D7F"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAEnE,OAAO,EAAE,KAAK,aAAa,EAAe,MAAM,kBAAkB,CAAC;AAOnE,eAAO,MAAM,WAAW,YAAY,CAAC;AAQrC,eAAO,MAAM,cAAc,QAAmB,CAAC;AAK/C,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,GAAE,aAA6B,GAAG,MAAM,CA6D7F"}