datanotes 2.0.1 → 3.0.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 (181) hide show
  1. package/NOTICE +9 -0
  2. package/README.md +207 -28
  3. package/SECURITY.md +92 -45
  4. package/dist/THIRD-PARTY-NOTICES.txt +682 -0
  5. package/dist/api/catalog.d.cts +69 -0
  6. package/dist/api/catalog.d.ts +50 -2
  7. package/dist/api/cli-options.d.cts +39 -0
  8. package/dist/api/cli-options.d.ts +39 -0
  9. package/dist/api/client.d.cts +89 -0
  10. package/dist/api/client.d.ts +19 -30
  11. package/dist/api/docs.d.cts +211 -0
  12. package/dist/api/docs.d.ts +211 -0
  13. package/dist/api/guidance.d.cts +81 -0
  14. package/dist/api/guidance.d.ts +81 -0
  15. package/dist/api/index.cjs +6610 -2247
  16. package/dist/api/index.d.cts +13 -0
  17. package/dist/api/index.d.ts +10 -7
  18. package/dist/api/index.js +6610 -2247
  19. package/dist/api/local.cjs +118 -6
  20. package/dist/api/local.d.cts +95 -0
  21. package/dist/api/local.d.ts +70 -6
  22. package/dist/api/local.js +104 -6
  23. package/dist/api/mcp.cjs +17384 -0
  24. package/dist/api/mcp.d.cts +108 -0
  25. package/dist/api/mcp.d.ts +108 -0
  26. package/dist/api/mcp.js +17334 -0
  27. package/dist/api/otlp.d.cts +65 -0
  28. package/dist/api/redact.d.cts +17 -0
  29. package/dist/api/redact.d.ts +17 -0
  30. package/dist/api/requests.d.cts +26 -0
  31. package/dist/api/requests.d.ts +14 -16
  32. package/dist/api/results.d.cts +16 -0
  33. package/dist/api/results.d.ts +2 -1
  34. package/dist/api/tokens.d.cts +7 -0
  35. package/dist/datanotes.cjs +62511 -28069
  36. package/dist/docs/administration/configuration.md +59 -0
  37. package/dist/docs/administration/docker.md +80 -0
  38. package/dist/docs/administration/git-sync.md +71 -0
  39. package/dist/docs/administration/limits.md +69 -0
  40. package/dist/docs/administration/run-the-service.md +219 -0
  41. package/dist/docs/administration/status.md +61 -0
  42. package/dist/docs/administration/use-with-obsidian.md +75 -0
  43. package/dist/docs/api/operations/add_column.md +49 -0
  44. package/dist/docs/api/operations/add_constraint.md +55 -0
  45. package/dist/docs/api/operations/adopt_rows.md +50 -0
  46. package/dist/docs/api/operations/align_relations.md +41 -0
  47. package/dist/docs/api/operations/alter_column.md +46 -0
  48. package/dist/docs/api/operations/alter_table.md +67 -0
  49. package/dist/docs/api/operations/batch.md +48 -0
  50. package/dist/docs/api/operations/changes_stream.md +28 -0
  51. package/dist/docs/api/operations/check_table.md +29 -0
  52. package/dist/docs/api/operations/clone_table.md +48 -0
  53. package/dist/docs/api/operations/comment_on.md +42 -0
  54. package/dist/docs/api/operations/configure_database.md +52 -0
  55. package/dist/docs/api/operations/create_table.md +78 -0
  56. package/dist/docs/api/operations/create_view.md +54 -0
  57. package/dist/docs/api/operations/create_webhook.md +37 -0
  58. package/dist/docs/api/operations/define_base.md +52 -0
  59. package/dist/docs/api/operations/delete_rows.md +43 -0
  60. package/dist/docs/api/operations/delete_webhook.md +31 -0
  61. package/dist/docs/api/operations/describe_database.md +30 -0
  62. package/dist/docs/api/operations/describe_history.md +40 -0
  63. package/dist/docs/api/operations/describe_semantic_index.md +24 -0
  64. package/dist/docs/api/operations/describe_table.md +38 -0
  65. package/dist/docs/api/operations/docs.md +13 -0
  66. package/dist/docs/api/operations/drop_column.md +41 -0
  67. package/dist/docs/api/operations/drop_constraint.md +39 -0
  68. package/dist/docs/api/operations/drop_table.md +43 -0
  69. package/dist/docs/api/operations/drop_view.md +38 -0
  70. package/dist/docs/api/operations/engine_status.md +23 -0
  71. package/dist/docs/api/operations/graph_neighbors.md +32 -0
  72. package/dist/docs/api/operations/graph_path.md +32 -0
  73. package/dist/docs/api/operations/graph_traverse.md +43 -0
  74. package/dist/docs/api/operations/identify_session.md +24 -0
  75. package/dist/docs/api/operations/infer_table.md +43 -0
  76. package/dist/docs/api/operations/insert_row.md +48 -0
  77. package/dist/docs/api/operations/list_changes.md +35 -0
  78. package/dist/docs/api/operations/list_folder.md +28 -0
  79. package/dist/docs/api/operations/list_link_targets.md +44 -0
  80. package/dist/docs/api/operations/list_migrations.md +35 -0
  81. package/dist/docs/api/operations/list_tables.md +32 -0
  82. package/dist/docs/api/operations/list_webhooks.md +23 -0
  83. package/dist/docs/api/operations/manual_index.md +14 -0
  84. package/dist/docs/api/operations/manual_page.md +22 -0
  85. package/dist/docs/api/operations/merge_rows.md +44 -0
  86. package/dist/docs/api/operations/move_rows.md +52 -0
  87. package/dist/docs/api/operations/openapi.md +13 -0
  88. package/dist/docs/api/operations/openapi_post.md +20 -0
  89. package/dist/docs/api/operations/read_note.md +29 -0
  90. package/dist/docs/api/operations/recompute_columns.md +44 -0
  91. package/dist/docs/api/operations/record_tool_call.md +36 -0
  92. package/dist/docs/api/operations/rename_column.md +44 -0
  93. package/dist/docs/api/operations/rename_value.md +45 -0
  94. package/dist/docs/api/operations/restore_database.md +51 -0
  95. package/dist/docs/api/operations/restore_table.md +46 -0
  96. package/dist/docs/api/operations/search_notes.md +28 -0
  97. package/dist/docs/api/operations/select_rows.md +61 -0
  98. package/dist/docs/api/operations/semantic_search.md +34 -0
  99. package/dist/docs/api/operations/semantic_search_docs.md +26 -0
  100. package/dist/docs/api/operations/set_table_bases.md +44 -0
  101. package/dist/docs/api/operations/status.md +13 -0
  102. package/dist/docs/api/operations/stop_engine.md +23 -0
  103. package/dist/docs/api/operations/sync_semantic_index.md +33 -0
  104. package/dist/docs/api/operations/table_changes.md +31 -0
  105. package/dist/docs/api/operations/tags/auxiliary.md +14 -0
  106. package/dist/docs/api/operations/tags/change-feed.md +8 -0
  107. package/dist/docs/api/operations/tags/history.md +17 -0
  108. package/dist/docs/api/operations/tags/queries.md +15 -0
  109. package/dist/docs/api/operations/tags/rows-dml.md +16 -0
  110. package/dist/docs/api/operations/tags/schema-ddl.md +22 -0
  111. package/dist/docs/api/operations/tags/service.md +15 -0
  112. package/dist/docs/api/operations/tags/tokens.md +13 -0
  113. package/dist/docs/api/operations/tokens_create.md +34 -0
  114. package/dist/docs/api/operations/tokens_delete.md +29 -0
  115. package/dist/docs/api/operations/tokens_list.md +21 -0
  116. package/dist/docs/api/operations/tokens_me.md +20 -0
  117. package/dist/docs/api/operations/tokens_rotate.md +29 -0
  118. package/dist/docs/api/operations/tokens_update.md +34 -0
  119. package/dist/docs/api/operations/undo_migration.md +46 -0
  120. package/dist/docs/api/operations/update_row.md +60 -0
  121. package/dist/docs/api/operations/update_rows.md +46 -0
  122. package/dist/docs/api/operations/vacuum_table.md +37 -0
  123. package/dist/docs/api.md +135 -0
  124. package/dist/docs/develop/build-an-app.md +320 -0
  125. package/dist/docs/develop/build-on-datanotes.md +138 -0
  126. package/dist/docs/develop/change-feed-and-webhooks.md +70 -0
  127. package/dist/docs/develop/client.md +116 -0
  128. package/dist/docs/develop/concurrency.md +153 -0
  129. package/dist/docs/develop/http-api.md +79 -0
  130. package/dist/docs/develop/telemetry.md +86 -0
  131. package/dist/docs/guides/agents/docs-offline.md +99 -0
  132. package/dist/docs/guides/agents/how-agents-use-datanotes.md +124 -0
  133. package/dist/docs/guides/agents/prompts.md +495 -0
  134. package/dist/docs/guides/agents/reading-errors.md +112 -0
  135. package/dist/docs/guides/data-quality/check-constraints.md +379 -0
  136. package/dist/docs/guides/data-quality/check-table.md +63 -0
  137. package/dist/docs/guides/data-quality/hand-edits.md +507 -0
  138. package/dist/docs/guides/data-quality/sync-between-devices.md +51 -0
  139. package/dist/docs/guides/data-quality/validation.md +90 -0
  140. package/dist/docs/guides/database-objects/bases.md +480 -0
  141. package/dist/docs/guides/database-objects/catalogs-and-schemas.md +897 -0
  142. package/dist/docs/guides/database-objects/columns.md +1332 -0
  143. package/dist/docs/guides/database-objects/relations.md +1454 -0
  144. package/dist/docs/guides/database-objects/tables.md +1404 -0
  145. package/dist/docs/guides/database-objects/views.md +1003 -0
  146. package/dist/docs/guides/history/back-up.md +195 -0
  147. package/dist/docs/guides/history/change-data-feed.md +247 -0
  148. package/dist/docs/guides/history/journal-and-undo.md +262 -0
  149. package/dist/docs/guides/history/restore.md +341 -0
  150. package/dist/docs/guides/history/table-history.md +657 -0
  151. package/dist/docs/guides/history/vacuum.md +35 -0
  152. package/dist/docs/guides/read-and-write/automatic-links.md +768 -0
  153. package/dist/docs/guides/read-and-write/batch.md +139 -0
  154. package/dist/docs/guides/read-and-write/import-notes.md +896 -0
  155. package/dist/docs/guides/read-and-write/joins-and-aggregates.md +251 -0
  156. package/dist/docs/guides/read-and-write/read-and-filter.md +536 -0
  157. package/dist/docs/guides/read-and-write/write-rows.md +573 -0
  158. package/dist/docs/guides/search/link-graph.md +146 -0
  159. package/dist/docs/guides/search/semantic.md +133 -0
  160. package/dist/docs/guides/search/text.md +142 -0
  161. package/dist/docs/guides/security/security-model.md +110 -0
  162. package/dist/docs/guides/security/tokens.md +60 -0
  163. package/dist/docs/guides/security/who-did-what.md +42 -0
  164. package/dist/docs/index.md +113 -0
  165. package/dist/docs/llms.txt +33 -0
  166. package/dist/docs/reference/cli.md +136 -0
  167. package/dist/docs/reference/compatibility.md +63 -0
  168. package/dist/docs/reference/errors.md +72 -0
  169. package/dist/docs/reference/glossary.md +161 -0
  170. package/dist/docs/reference/operations/auxiliary.md +116 -0
  171. package/dist/docs/reference/operations/history.md +168 -0
  172. package/dist/docs/reference/operations/queries.md +169 -0
  173. package/dist/docs/reference/operations/rows.md +185 -0
  174. package/dist/docs/reference/operations/schema.md +316 -0
  175. package/dist/docs/reference/operations.md +96 -0
  176. package/dist/docs/reference/settings.md +31 -0
  177. package/dist/docs/release-notes.md +67 -0
  178. package/dist/docs/start/connect-agents.md +204 -0
  179. package/dist/docs/start/how-it-works.md +317 -0
  180. package/dist/docs/start/quick-start.md +126 -0
  181. package/package.json +58 -14
@@ -0,0 +1,317 @@
1
+ # How Datanotes turns notes into a database
2
+
3
+ > Tables, rows, the journal, the transaction log, the link graph and what the engine checks.
4
+
5
+ Datanotes puts artificial intelligence (AI) agents first, and serves people and programs too. An agent reads and writes your notes through the Model Context Protocol (MCP) server, `datanotes mcp`. A program uses the [HTTP API](https://datanotes.dev/develop/http-api/), and you use your editor; all three go through the same engine and the same checks. [How agents work with your vault](https://datanotes.dev/guides/agents/how-agents-use-datanotes/) follows an agent through it.
6
+
7
+ To make that safe, Datanotes runs your vault as a database. Catalogs hold schemas, schemas hold tables, and you name a table `catalog.schema.table`. The engine checks every write against its table, records it in a journal, and lets you undo it.
8
+
9
+ These docs use the same few words for notes and for the database:
10
+
11
+ * **Vault**: the folder of notes the engine serves, the one you pass with `--database` or `--vault`
12
+ * **Note**: a Markdown file in the vault
13
+ * **Front matter**: the YAML block at the top of a note, between two `---` lines
14
+ * **Wikilink**: a link written `[[Note]]`, or `[[Note|alias]]` with an alias as its display text
15
+ * **Backlinks**: the notes that link to a given note
16
+
17
+ A row is a note, and a column is a key of its front matter. A note is a row when its front matter key `table` names a table, wherever the note lives.
18
+
19
+ This page explains the parts that make that work:
20
+
21
+ * **Folders**: the database folder, and the vault around it when you use one
22
+ * **Metastore**: the file that defines every table and view
23
+ * **Rows**: the notes, and the `id` each one keeps
24
+ * **Transaction log**: every version of each table
25
+ * **Link graph**: the links between notes, read as a graph
26
+ * **Private store**: the folder outside the vault where the engine keeps its records
27
+
28
+ ## The engine runs on a database folder or on a vault
29
+
30
+ You start the engine on one folder, in one of two ways:
31
+
32
+ | Start | The folder is | `metastore.yaml` | A row of `film` | A note at the root |
33
+ | --------------------------------- | ------------------------------- | ----------------------------------------------- | ------------------------------------------ | ------------------ |
34
+ | `--database your_database_folder` | The database | At its root | `main/public/film/Metropolis.md` | `intro.md` |
35
+ | `--vault your_vault_folder` | A vault that holds the database | In a subfolder, `datanotes/` for a new database | `datanotes/main/public/film/Metropolis.md` | `intro.md` |
36
+
37
+ The engine names every note by its path relative to that folder. In both modes, it serves every note of the folder, not only the rows. [`read_note`](https://datanotes.dev/reference/operations/queries/#read_note), [`list_folder`](https://datanotes.dev/reference/operations/queries/#list_folder), [`search_notes`](https://datanotes.dev/reference/operations/queries/#search_notes) and [git sync](https://datanotes.dev/administration/git-sync/) reach plain notes too.
38
+
39
+ Use `--database` unless another app opens the same notes. It’s the normal way to run Datanotes: the folder holds your database and any notes you add next to it.
40
+
41
+ Use `--vault` when an app such as Obsidian opens a whole vault, and the database is one folder in it. Paths then start from the vault root, as the app’s do, so the app resolves the wikilinks the engine writes. You can also open a `--database` folder itself as an Obsidian vault: its root is then the vault root. [Use Datanotes with Obsidian](https://datanotes.dev/administration/use-with-obsidian/) compares the two setups.
42
+
43
+ Give one of the two options, not both: the engine refuses to start with both.
44
+
45
+ The folder gives the engine a fixed frame:
46
+
47
+ * **Stable paths**: every path is relative to the folder, wherever the folder lives
48
+ * **No writes outside**: the engine never writes outside the folder
49
+ * **One folder to follow**: the file watcher and the link index cover the folder and nothing else
50
+
51
+ ## The metastore defines every table
52
+
53
+ The metastore is one YAML file, `metastore.yaml`, that defines every table and view. Its folder is the database folder. Tables sit in a three-level hierarchy, `catalogs → schemas → tables`, and you name them `catalog.schema.table`. Shorter names resolve through `config.default_catalog`.
54
+
55
+ The file holds these parts:
56
+
57
+ * **`id`**: the database’s id, a UUID the engine writes when it creates the database; the engine finds its [private store](#the-engine-keeps-its-records-in-a-private-store) by it
58
+ * **`config`**: settings for the whole database, such as `default_catalog`
59
+ * **Columns**: each with `description`, `required`, `generated`, `identity` and `default`, which accepts `{now}` and `{today}`; the operations require a `description` on every table and column
60
+ * **`constraints`**: CHECK constraints, filters that every row of the table must pass
61
+ * **`options`**: partitioning, the file name rule, archiving, excluded folders and history
62
+ * **`row_template`** and **`tags`**: the text of every new row’s note, and key/value labels on the table
63
+ * **`views`**: saved queries over one or more tables
64
+ * **Descriptions**: catalogs and schemas carry a `description`, set with [`comment_on`](https://datanotes.dev/reference/operations/schema/#comment_on)
65
+
66
+ You can read the file freely, but only operations change it. The table, view and base operations, such as [`create_table`](https://datanotes.dev/reference/operations/schema/#create_table) and [`add_column`](https://datanotes.dev/reference/operations/schema/#add_column), validate each change, journal it and can undo it. [`configure_database`](https://datanotes.dev/reference/operations/auxiliary/#configure_database) validates `config` but doesn’t journal it: to revert, call it again with the previous values.
67
+
68
+ ## Rows are notes
69
+
70
+ A row is any note whose front matter key `table` names a table. The engine writes new rows to `your_catalog/your_schema/your_table/` in the database folder, and names each note with the table’s `filename` pattern.
71
+
72
+ Every metastore table starts with the system column `id` (`identity: uuid`). It holds a universally unique identifier (UUID) of version 7, which the engine assigns when it creates the row and never changes: renames, moves and [`alter_table`](https://datanotes.dev/reference/operations/schema/#alter_table) keep it. You can’t set, drop or alter the column, except for its label, description and aliases. A row you create by hand without an `id` gets one, and a duplicated note gets a new one through [`recompute_columns`](https://datanotes.dev/reference/operations/rows/#recompute_columns).
73
+
74
+ ## The transaction log keeps every version
75
+
76
+ The transaction log records each version of a table, so you can read or restore the data as it was at an earlier time. Whether a table keeps one depends on two settings:
77
+
78
+ * **The database’s `history`**: a database the engine creates gets `history: true` in the `config` of its `metastore.yaml`, so every table keeps a log. A `metastore.yaml` without the key, such as one written by hand or by a version before 3.0.0, means `false`.
79
+ * **The table’s own `history`**: set with [`alter_table`](https://datanotes.dev/reference/operations/schema/#alter_table) to `true` or `false`, it wins over the database’s. `"default"` removes it, so the table follows the database again.
80
+
81
+ Change the database’s setting with [`configure_database`](https://datanotes.dev/reference/operations/auxiliary/#configure_database) and `history`. [Table history and time travel](https://datanotes.dev/guides/history/table-history/) shows it at work.
82
+
83
+ Each table’s log lives in the engine’s store, in `transaction_log/your_catalog.your_schema.your_table/`, with one numbered JSON file per change. A commit holds these entries:
84
+
85
+ * **`commitInfo`**: the operation, its time and, when known, its actor
86
+ * **`metaData`**: the table’s definition, when the commit changes it
87
+ * **`add`**: the path, the hash and the full content of each row written
88
+ * **`remove`**: the path and the hash of each row removed
89
+
90
+ The engine writes a checkpoint, a full copy of the table, once the commits since the last checkpoint outweigh it. That means twice the table’s size and at least 256 KB, or 1,000 versions. The log therefore grows with what you write, not with the table’s size. Hand edits appear as `EXTERNAL_EDIT` commits.
91
+
92
+ History costs disk space in the store. Each version keeps the full text of every note it writes, so a note saved ten times is stored about ten times, plus the checkpoints. A table you rewrite often grows its log fastest. [`vacuum_table`](https://datanotes.dev/reference/operations/history/#vacuum_table) deletes the old versions, and `history: false` stops the log of a table that doesn’t need it.
93
+
94
+ The logs of all tables also work as one log for the whole database:
95
+
96
+ * An operation that changes more than one table commits to each with the same `migration_id`, and a batch’s operations carry the batch’s ID
97
+ * [`describe_history`](https://datanotes.dev/reference/operations/history/#describe_history) without a table lists the commits of every table together
98
+ * [`select_rows`](https://datanotes.dev/reference/operations/queries/#select_rows) with `timestamp` reads every table of a join, view or base as of that time
99
+ * [`restore_database`](https://datanotes.dev/reference/operations/history/#restore_database) brings every logged table back to a time, or to the moment before an operation, as one undoable operation
100
+
101
+ ## The journal records who changed what
102
+
103
+ The `X-Datanotes-Actor` header, or the client’s `actor` option, tells the engine on whose behalf you write. Its fields are `harness`, `model`, `session` and `tool`. The engine stores it as `actor` on the journal entry, and as `actor` and `userName` (“harness / model”) on the commit. The history and the migrations list (`by`) then show who changed what.
104
+
105
+ ## Links between notes form a graph
106
+
107
+ The engine reads your notes and their links as a labelled graph. Edges from link columns have the column’s name as their label, such as `cast` or `genre`. Other resolved links have the label `link`, and each row has its table as its label.
108
+
109
+ Three operations walk the graph:
110
+
111
+ * **[`graph_neighbors`](https://datanotes.dev/reference/operations/queries/#graph_neighbors)**: the notes one link away
112
+ * **[`graph_traverse`](https://datanotes.dev/reference/operations/queries/#graph_traverse)**: a breadth-first walk, filtered by direction, edge labels, result and pass-through tables, and depth
113
+ * **[`graph_path`](https://datanotes.dev/reference/operations/queries/#graph_path)**: the shortest path between two notes
114
+
115
+ ## Operations for tables, rows, queries and history
116
+
117
+ The operations are the [HTTP API](https://datanotes.dev/develop/http-api/), and the same calls through the [JavaScript client](https://datanotes.dev/develop/client/). The [operations reference](https://datanotes.dev/reference/operations/) lists each one, by kind. Together they cover:
118
+
119
+ * Table and column definitions, constraints, views and [`clone_table`](https://datanotes.dev/reference/operations/schema/#clone_table)
120
+ * Inserting, updating, merging, deleting and moving rows
121
+ * Queries with [`select_rows`](https://datanotes.dev/reference/operations/queries/#select_rows): joins, group by, views, `system.information_schema.*` and time travel by `version` or `timestamp`
122
+ * History of a table or of the whole database, the change data feed, and restoring a table or the whole database
123
+ * Vacuum, the migrations list and undo
124
+
125
+ A view can be a `UNION ALL` over more than one table; each row it returns has its table in `_source`. The engine journals every write to rows and to the schema, so you can undo it.
126
+
127
+ ## The engine keeps its records in a private store
128
+
129
+ The private store is a folder outside the vault, in the operating system’s folder for per-user application data. Each database has its own: `<data home>/<database id>/`, named after the `id` in `metastore.yaml`. [Run the engine as a service](https://datanotes.dev/administration/run-the-service/#where-the-engine-keeps-its-store) lists the data home of each system and the options that move it.
130
+
131
+ The store holds these files:
132
+
133
+ * **`settings.json`** and **`tokens.json`**: the engine’s settings, with the owner token, and the named tokens
134
+ * **`journal/YYYY-MM.jsonl`**: one line per operation, with the data to undo it; undoing appends an `undone` line
135
+ * **`blobs/your_hash.txt`**: texts of 2 KB or more, such as `metastore.yaml` before and after a change, stored once
136
+ * **`transaction_log/`**, **`changes/`** and **`semantic/`**: the history of tables, the change feed and the semantic index
137
+ * **`logs/YYYY-MM-DD.your_source.jsonl`**: the engine’s log records, when `fileLogEnabled` is on
138
+ * **`store.json`**: the vault and the database folder the store belongs to
139
+
140
+ When the engine starts, it drops journal entries older than `journalRetentionDays` (90) and deletes log files older than `logRetentionDays` (30).
141
+
142
+ Your vault holds only your notes: sync tools, git and Obsidian never see the store. The store belongs to the machine the engine runs on, so the journal and the history do too. Results name store files with a `store:` prefix, such as `store:journal/2026-10.jsonl`.
143
+
144
+ ### How the engine finds the database
145
+
146
+ With `--database`, the database is the folder itself, and `metastore.yaml` sits at its root. A new database creates it there.
147
+
148
+ With `--vault`, the database is the folder of the vault’s one `metastore.yaml`, `datanotes/` for a new database. When the vault holds more than one `metastore.yaml`, the engine refuses to start. [One database per vault](https://datanotes.dev/administration/run-the-service/#one-database-per-vault) gives the details.
149
+
150
+ ## Move the vault or the database folder
151
+
152
+ You can move or rename the vault folder. The store records the vault’s path, and the engine follows the vault to its new place at the next start. A copy is different: while the original vault still holds the database, the engine refuses to share its store with the copy. [Run a copy of a vault](https://datanotes.dev/administration/run-the-service/#run-a-copy-of-a-vault) shows how to give the copy its own id.
153
+
154
+ A vault holds one database folder. To rename or move it, use [`configure_database`](https://datanotes.dev/reference/operations/auxiliary/#configure_database) with `root`. The folder can go anywhere in the vault, also in a subfolder. With `root: ""`, the database moves to the root of the folder the engine serves, so a vault becomes a database folder. The engine refuses:
155
+
156
+ * `root: ""` when a catalog folder or a `metastore.yaml` already exists at the root
157
+ * a path with a folder whose name starts with a dot: editors hide such folders, so the rows would vanish from view
158
+ * a path that already exists, ignoring case
159
+ * a path inside the database folder
160
+
161
+ A rename that only changes case, such as `Datanotes` to `datanotes`, works on file systems that ignore case too.
162
+
163
+ You can also move or rename the folder by hand, with the engine running or stopped. The engine finds the database again by its id.
164
+
165
+ Either way, the move is one operation. The engine applies it before it answers any other request, so no client reads a half-moved vault:
166
+
167
+ * It rewrites every link into the old folder, across the vault, and recomputes the generated columns
168
+ * It records the move in the journal, as one `move_database` entry, and in the history of every table, as `MOVE DATABASE`
169
+ * It moves archive and exclude folders given as vault paths
170
+ * The change feed reports one `move` event per row, with the row as it is after the move
171
+ * [Git sync](https://datanotes.dev/administration/git-sync/) commits the move once
172
+ * A move by hand shows as a hand edit, in `datanotes status`, the change feed and the log
173
+
174
+ Undo and time travel read what was written before the move with the new paths. Undo of an operation made before the move finds its rows in the new folder, and a restored version comes back in the new folder. The move itself can’t be undone: move the folder back with `configure_database`.
175
+
176
+ ## What the operations do beyond reading and writing
177
+
178
+ These features do more than read or write one row:
179
+
180
+ * **[Whole writes](#every-write-applies-whole-or-not-at-all)**: a write that applies completely or not at all, even with many clients, while the engine runs
181
+ * **[Batches](#batches-succeed-or-fail-together)**: writes that succeed or fail together
182
+ * **[Bases](#one-query-reads-every-table-of-a-base)**: one query over every table that shares a set of columns
183
+ * **[Inverse links](#inverse-links-keep-both-sides-in-step)**: links the engine keeps on both sides
184
+ * **[Checks of hand edits](#the-engine-checks-hand-edits-but-never-refuses-them)**: what happens when you edit a note outside the engine
185
+ * **[Link repair](#the-engine-repairs-links-after-a-hand-move)**: links that follow a note you rename or move outside the engine
186
+ * **[Schema inference](#schema-inference-proposes-a-table-for-existing-notes)**: a table proposed from notes you already have
187
+ * **[Attribution](#attribution-names-who-made-each-write)**: who made each write
188
+
189
+ ### Every write applies whole or not at all
190
+
191
+ While the engine runs, a write changes notes only when all of it can apply. The engine makes sure of it in three ways:
192
+
193
+ * **Checked first**: the engine checks a write before it touches a file. A refused write changes nothing.
194
+ * **Checked on the latest data**: the engine applies writes one at a time. If another client’s write lands while yours is checked, the engine checks yours again on the new state.
195
+ * **Undone when a file fails**: a file can fail mid-write, for example when another program holds it. The engine then puts back the files it already wrote, and the answer lists what failed.
196
+
197
+ So two clients can’t both insert the same unique value, and a rule that follows a link holds when they race. [Concurrent writes](https://datanotes.dev/develop/concurrency/) shows how a program writes safely with `expect` and retries.
198
+
199
+ A crash is different. The engine writes each note atomically, so no note is ever left half written. But a write that touches many notes writes them one by one, and records itself in the journal last. A crash, a power cut or `kill -9` in the middle can leave part of that write applied and outside the journal. Run [`check_table`](https://datanotes.dev/guides/data-quality/check-table/) after such a stop to find the rows it left. Stop the engine with [`datanotes stop`](https://datanotes.dev/administration/run-the-service/#stop-the-engine), which lets the write finish.
200
+
201
+ ### Batches succeed or fail together
202
+
203
+ [`batch`](https://datanotes.dev/reference/operations/rows/#batch) runs a list of writes in order, each one seeing the previous ones. If the engine refuses one, it undoes the ones already applied. The batch is one journal entry, so [`undo_migration`](https://datanotes.dev/reference/operations/history/#undo_migration) reverts all of it. A dry run checks each operation against the current data.
204
+
205
+ A batch is all or nothing while the engine keeps running. A crash in the middle may leave part of it applied: the steps already done each keep a journal entry of their own, so `list_migrations` shows them and `undo_migration` reverts them one by one.
206
+
207
+ ### One query reads every table of a base
208
+
209
+ A base is a named set of columns that tables inherit, like a parent class; you define one with [`define_base`](https://datanotes.dev/reference/operations/schema/#define_base). [`select_rows`](https://datanotes.dev/reference/operations/queries/#select_rows) with `table: "base:record"` returns the rows of every table that extends the base `record`, directly or through other bases. It reads them on the base’s columns and tags each with `_source`. A new table that extends the base appears in the results without changes to any view.
210
+
211
+ ### Inverse links keep both sides in step
212
+
213
+ A link column with `inverse` keeps the other side of the link up to date. You declare it on one side only:
214
+
215
+ ```yaml
216
+ { name: cast, type: list, items: link, ref: main.public.actor, inverse: films }
217
+ ```
218
+
219
+ This column of `film` links back through the `films` column of `actor`: when `Charade` lists Cary Grant in its cast, Cary Grant lists `Charade` in `films`. Other pairs work the same way:
220
+
221
+ * **`friends` and `friends`**: one column of a table that links back to itself, so the link is symmetric
222
+ * **`children` and `parents`**: two columns of the same table
223
+ * **`partner` and `partner`**: a single value, so setting it replaces the other row’s previous partner
224
+
225
+ Writes add or remove the reciprocal links in the same operation, with one undo. The engine mirrors links you edit by hand about 2s later. [`check_table`](https://datanotes.dev/reference/operations/auxiliary/#check_table) reports links that aren’t reciprocated, and [`align_relations`](https://datanotes.dev/reference/operations/rows/#align_relations) adds the missing ones. [Relations and hierarchies](https://datanotes.dev/guides/database-objects/relations/) shows each kind.
226
+
227
+ ### The engine checks hand edits but never refuses them
228
+
229
+ You can create, change, rename or remove a row outside the engine: in an editor, with git or through a sync tool. The engine checks the edit a moment later, in a batch with the other edits of that moment, renames included and in order. It applies the same rules as [`check_table`](https://datanotes.dev/reference/operations/auxiliary/#check_table):
230
+
231
+ * Required values, types, ranges and enums
232
+ * Links and unique values
233
+ * CHECK constraints, including those that follow links
234
+ * Filtered link targets, `ref_where` and `ref_by`
235
+
236
+ The engine fixes generated columns, identity columns and inverse links by itself, so the check leaves them out.
237
+
238
+ An edit can also break rows that point to the edited one. When a link-following CHECK or a filtered link column of any table reads the edited row’s table, the engine checks the rows that link to the edited row again. For example, with the CHECK `{"genre.kind": "genre"}` on `film`, a genre that becomes an era re-checks the films in it. A row deleted, renamed or moved to another table re-checks the rows whose links pointed to it, and a link left with no row is a `broken_link`. After a rename, the engine first rewrites those links, as [the next section](#the-engine-repairs-links-after-a-hand-move) explains.
239
+
240
+ The check reports only what the edit breaks: the engine doesn’t blame the edit for a rule the row already broke. The result appears in three places:
241
+
242
+ * **Change feed**: the event’s `violations`, each with `path`, `code`, `column` (or `constraint` for a CHECK) and `message`; empty when nothing breaks, absent when the engine checked nothing
243
+ * **Transaction log**: the `EXTERNAL_EDIT` version’s `operationMetrics.violations`, the rules the table’s own rows break, when history is on
244
+ * **Log**: a WARN line
245
+
246
+ A hand edit of `metastore.yaml` that changes a table’s entry checks every row of that table, on the `schema` event. The rows always stay as you wrote them. [`check_table`](https://datanotes.dev/reference/operations/auxiliary/#check_table) lists the rows that are invalid now; without `table`, it checks every table, page by page. For a table without such rules, a check costs only reading the edited rows.
247
+
248
+ ### The engine repairs links after a hand move
249
+
250
+ When you rename or move a note outside the engine, links to it still name its old path. The engine rewrites them, as it does when it renames a note itself. This works for rows and plain notes alike, and covers the whole vault:
251
+
252
+ * Wikilinks and embeds, in the text and in the front matter
253
+ * Markdown links
254
+ * The moved note’s own relative links that would no longer lead where they did
255
+
256
+ The engine recognizes a move in three ways:
257
+
258
+ * **A row**: by its `id`, also when you edit the note in the same moment
259
+ * **Any note**: by its text, when the old note goes and a new one with the same text appears at once. The text has 32 characters or more, and the note keeps its name or its folder. Otherwise the two are likely different notes, such as two copies of a template.
260
+ * **A rename in case only**: by its name, such as `Plan.md` to `plan.md`
261
+
262
+ The engine also recognizes a move it sees while it writes, such as during a long operation or a bulk repair.
263
+
264
+ The repair is one operation, tied to the move:
265
+
266
+ * The journal lists it as `repair_links`, and [`undo_migration`](https://datanotes.dev/reference/operations/history/#undo_migration) takes it back
267
+ * The history of each table it writes gets a `REPAIR LINKS` commit, after the move’s `EXTERNAL_EDIT`
268
+ * The change feed reports its changes after the move’s event, each with the move in `cause`
269
+
270
+ Links that an editor already rewrote stay as they are. A row renamed by hand also keeps its [inverse links](#inverse-links-keep-both-sides-in-step): the engine doesn’t add them again to the rows it links to. The repair only rewrites links to the old path. It adds no [automatic links](https://datanotes.dev/guides/read-and-write/automatic-links/): a name in the text stays text until a write puts it there.
271
+
272
+ The move’s event doesn’t report the links to the old path as `broken_link`, since the repair fixes them. To leave links as they are after a hand move, set `repairLinksOnHandMove` to `false` in the [engine’s settings](https://datanotes.dev/reference/settings/). The move then reports each link it breaks as a `broken_link`.
273
+
274
+ #### When the repair runs
275
+
276
+ The engine waits until the vault is quiet: 4 seconds with no change to any file. It then plans the repair and writes it at once. If a file changes while it plans, it drops the plan and waits again. So the repair never mixes with an editor or a sync tool at work:
277
+
278
+ * **Obsidian** rewrites links on its own renames when **Automatically update internal links** is on (`alwaysUpdateLinks` in `.obsidian/app.json`). It writes them within the quiet time, so the engine finds them rewritten and only fixes the links Obsidian left. With the setting off, Obsidian asks first. The engine repairs the links after the quiet time anyway, and Obsidian then finds nothing to update. To keep Obsidian’s answer, set `repairLinksOnHandMove` to `false`.
279
+ * **Git**: the engine doesn’t repair while git sync pulls, or while the vault’s repository has a rebase, a merge or a git command under way (`rebase-merge`, `rebase-apply`, `MERGE_HEAD` or `index.lock` in its git folder). The repair waits and runs once git is done. The change feed reports the moves at once.
280
+
281
+ The engine repairs every move it saw meanwhile in one operation. Say `a.md` moves to `b.md`, then `c.md` takes the name `a.md`. A link to `a` written before the first move follows the note to `b`. A link to `a` in a note you edited after the first move stays: it may mean the new `a`.
282
+
283
+ #### Moves while the engine is stopped
284
+
285
+ The engine saves where each row is, by `id`, in its store. At the next start, a row it finds at another path was moved meanwhile: the engine repairs the links to its old path, as for a move it saw. Plain notes have no `id`, so the engine doesn’t repair the links to a plain note moved while it was stopped. Neither does it repair a row whose old path holds another note by then.
286
+
287
+ #### Renames in case only
288
+
289
+ A rename that only changes case also works on file systems that ignore case. The engine renames through a temporary name next to the file, `.datanotes-rename-<pid>-<n>--<new name>`, so the file takes its new name. If the engine stops between the two steps, it gives the file its new name at the next start. Git sync never commits such a temporary file. The engine reads a rename in case only made by hand as a rename too, so the vault keeps one row.
290
+
291
+ ### Schema inference proposes a table for existing notes
292
+
293
+ [`infer_table`](https://datanotes.dev/reference/operations/auxiliary/#infer_table) with a `folder` (or `paths`) reads the notes’ front matter and returns a [`create_table`](https://datanotes.dev/reference/operations/schema/#create_table) proposal. The proposal includes types, enums with their values, links with the table they point to (`ref`), required and unique columns, and a file name rule. Its descriptions are `null`, because the notes can’t say what each key means. You or the agent write them before `create_table`, which refuses a table or a column without one. It also returns the [`adopt_rows`](https://datanotes.dev/reference/operations/rows/#adopt_rows) call that brings the notes into the table.
294
+
295
+ With a `table` instead, it lists the keys the table’s rows use without declaring them. It never writes anything.
296
+
297
+ ### Attribution names who made each write
298
+
299
+ The `X-Datanotes-Actor` header, with `harness`, `model`, `session` and `tool`, names who made a write. The engine keeps it in the journal and in the history, as [the journal records who changed what](#the-journal-records-who-changed-what) explains.
300
+
301
+ ## How fast the engine is
302
+
303
+ The engine caches row notes by `stat`, modification time and size, together with their hash and parsed front matter, so operations read only the notes that changed. You can measure it on your machine with the benchmark:
304
+
305
+ ```bash
306
+ SIZES=1000,5000,20000 npm run bench
307
+ ```
308
+
309
+ The benchmark uses one table of rows of about 600 bytes, with history on and an in-memory file index. On a laptop running Windows Subsystem for Linux (WSL2), at 20,000 rows, it measured these times; treat them as indicative:
310
+
311
+ * **[`insert_row`](https://datanotes.dev/reference/operations/rows/#insert_row)**: about 130 ms, with a 95th percentile of about 160 ms
312
+ * **Updating one row**: about 130 ms
313
+ * **A filtered [`select_rows`](https://datanotes.dev/reference/operations/queries/#select_rows)**: about 45 ms, and about 1s for the first one after the engine starts
314
+ * **[`check_table`](https://datanotes.dev/reference/operations/auxiliary/#check_table)**: about 200 ms
315
+ * **Transaction log**: grows by about 0.8 KB per single-row write
316
+
317
+ At 1,000 rows, a write takes under 10 ms.
@@ -0,0 +1,126 @@
1
+ # Run Datanotes on your notes
2
+
3
+ > Start the engine on a folder of notes, create a table, add a row and connect your agents.
4
+
5
+ By the end of this page, the engine runs on your folder of notes and your agents can reach it. You’ll also have a table of the classic films you watch, with one row in it.
6
+
7
+ You need [Node.js](https://nodejs.org) 22 or newer and a folder for your database. It can be an empty folder, or a folder of Markdown notes.
8
+
9
+ On Windows, use Node.js 24.16 or later, or 26.1 or later. Node.js 24.0 to 24.15 and 26.0 can crash at random on local connections. `datanotes serve` warns at start on those versions.
10
+
11
+ 1. Start the engine on your folder:
12
+
13
+ ```bash
14
+ npx -y datanotes serve --database your_database_folder
15
+ ```
16
+
17
+ The folder is the database: the engine keeps `metastore.yaml` at its root, and serves every note in it. To use an app such as Obsidian on a vault that holds the database, start with `--vault` instead: [Use Datanotes with Obsidian](https://datanotes.dev/administration/use-with-obsidian/) explains it.
18
+
19
+ The engine serves the [HTTP API](https://datanotes.dev/develop/http-api/) on `http://127.0.0.1:27150`. It keeps its settings and your owner token in `settings.json`, in its store outside the folder. `npx datanotes store path --database your_database_folder` shows where.
20
+
21
+ To check that the engine is running and healthy, run `npx datanotes status --database your_database_folder` (see [Check the engine’s status](https://datanotes.dev/administration/status/#read-the-status-over-http)).
22
+
23
+ 2. Install the package, which includes a client for the HTTP API:
24
+
25
+ ```bash
26
+ npm install datanotes
27
+ ```
28
+
29
+ 3. Connect to the engine with your owner token. `localEngine` finds the engine that runs on your folder, and reads the token from its settings:
30
+
31
+ ```ts
32
+ import { DatanotesClient } from "datanotes/client";
33
+ import { localEngine } from "datanotes/local";
34
+
35
+
36
+ const engine = localEngine("your_database_folder");
37
+ if (!engine) throw new Error("Start the engine on the folder first");
38
+ const db = new DatanotesClient({ url: engine.url, token: engine.token });
39
+ ```
40
+
41
+ 4. Create a table of films with four columns. The table and each column need a `description`, which tells agents and people what they hold. `filename` names each note after the film’s title, `unique` allows one note per title, and `ordered` keeps the statuses in order:
42
+
43
+ ```ts
44
+ const created = await db.call("create_table", {
45
+ name: "film",
46
+ description: "Classic films I watched or want to watch",
47
+ filename: "{title}",
48
+ columns: [
49
+ {
50
+ name: "title",
51
+ type: "string",
52
+ required: true,
53
+ unique: true,
54
+ description: "The film's title, as on its poster",
55
+ },
56
+ {
57
+ name: "year",
58
+ type: "integer",
59
+ description: "Year the film first came out",
60
+ },
61
+ {
62
+ name: "minutes",
63
+ type: "integer",
64
+ min: 1,
65
+ description: "Running time in minutes",
66
+ },
67
+ {
68
+ name: "status",
69
+ type: "enum",
70
+ values: ["to watch", "watched"],
71
+ ordered: true,
72
+ description: "Where the film is on my list",
73
+ },
74
+ ],
75
+ });
76
+ if (!created.ok) throw new Error(String(created.value));
77
+ ```
78
+
79
+ The engine writes the table’s definition into `metastore.yaml`, at the root of your folder.
80
+
81
+ 5. Add a film:
82
+
83
+ ```ts
84
+ const r = await db.call("insert_row", {
85
+ table: "film",
86
+ values: { title: "Nosferatu", year: 1922, minutes: 94, status: "watched" },
87
+ });
88
+ type Planned = { planned_creates: string[] };
89
+ console.log(r.ok ? (r.value as Planned).planned_creates : r.value);
90
+ ```
91
+
92
+ When the engine accepts the row, `planned_creates` lists the new note. When it refuses the row, `r.value` explains which value broke which rule, and the engine writes nothing. For `Metropolis` with `minutes: 0` and the status `seen`, it answers:
93
+
94
+ ```text
95
+ insert_row refused — nothing was written:
96
+ invalid main/public/film/Metropolis.md: minutes must be >= 1
97
+ invalid main/public/film/Metropolis.md: status must be one of: to watch; watched
98
+ ```
99
+
100
+ The film is now a note, `main/public/film/Nosferatu.md`:
101
+
102
+ ```markdown
103
+ ---
104
+ table: main.public.film
105
+ id: 01a12230-73ea-7349-83d3-efdcc7cceb61
106
+ title: Nosferatu
107
+ year: 1922
108
+ minutes: 94
109
+ status: watched
110
+ ---
111
+ ```
112
+
113
+ Your `id` differs: the engine gives every new row its own.
114
+
115
+ You can edit the note by hand at any time. The engine reads your change and checks it against the table; [`check_table`](https://datanotes.dev/reference/operations/auxiliary/#check_table) lists any value that breaks a rule.
116
+
117
+ 6. Connect your agents through the Model Context Protocol (MCP) server, `datanotes mcp`: follow [Connect agents](https://datanotes.dev/start/connect-agents/).
118
+
119
+ ## Learn more about tables, clients and tokens
120
+
121
+ These pages explain what you used on this page:
122
+
123
+ * [How it works](https://datanotes.dev/start/how-it-works/): tables, rows and the journal
124
+ * [Operations](https://datanotes.dev/reference/operations/): everything the API can do
125
+ * [JavaScript client](https://datanotes.dev/develop/client/) and [HTTP API](https://datanotes.dev/develop/http-api/): the two ways your code reaches the engine
126
+ * [Security](https://datanotes.dev/guides/security/security-model/): what each token can do
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "datanotes",
3
- "version": "2.0.1",
3
+ "version": "3.0.0",
4
+ "mcpName": "io.github.davidefornelli/datanotes",
4
5
  "description": "The engine of your second brain: Markdown notes as database tables, for you and your agents.",
5
6
  "keywords": [
6
7
  "markdown",
@@ -10,7 +11,10 @@
10
11
  "second-brain",
11
12
  "agents",
12
13
  "frontmatter",
13
- "obsidian-vault"
14
+ "obsidian-vault",
15
+ "mcp-server",
16
+ "agent-memory",
17
+ "knowledge-base"
14
18
  ],
15
19
  "homepage": "https://datanotes.dev",
16
20
  "bugs": {
@@ -28,50 +32,90 @@
28
32
  },
29
33
  "exports": {
30
34
  ".": {
31
- "types": "./dist/api/index.d.ts",
32
- "import": "./dist/api/index.js",
33
- "require": "./dist/api/index.cjs"
35
+ "import": {
36
+ "types": "./dist/api/index.d.ts",
37
+ "default": "./dist/api/index.js"
38
+ },
39
+ "require": {
40
+ "types": "./dist/api/index.d.cts",
41
+ "default": "./dist/api/index.cjs"
42
+ }
34
43
  },
35
44
  "./client": {
36
- "types": "./dist/api/index.d.ts",
37
- "import": "./dist/api/index.js",
38
- "require": "./dist/api/index.cjs"
45
+ "import": {
46
+ "types": "./dist/api/index.d.ts",
47
+ "default": "./dist/api/index.js"
48
+ },
49
+ "require": {
50
+ "types": "./dist/api/index.d.cts",
51
+ "default": "./dist/api/index.cjs"
52
+ }
39
53
  },
40
54
  "./local": {
41
- "types": "./dist/api/local.d.ts",
42
- "import": "./dist/api/local.js",
43
- "require": "./dist/api/local.cjs"
55
+ "import": {
56
+ "types": "./dist/api/local.d.ts",
57
+ "default": "./dist/api/local.js"
58
+ },
59
+ "require": {
60
+ "types": "./dist/api/local.d.cts",
61
+ "default": "./dist/api/local.cjs"
62
+ }
63
+ },
64
+ "./mcp": {
65
+ "import": {
66
+ "types": "./dist/api/mcp.d.ts",
67
+ "default": "./dist/api/mcp.js"
68
+ },
69
+ "require": {
70
+ "types": "./dist/api/mcp.d.cts",
71
+ "default": "./dist/api/mcp.cjs"
72
+ }
44
73
  },
45
74
  "./package.json": "./package.json"
46
75
  },
47
76
  "files": [
48
77
  "dist/datanotes.cjs",
78
+ "dist/THIRD-PARTY-NOTICES.txt",
49
79
  "dist/api",
80
+ "dist/docs",
50
81
  "LICENSE",
51
82
  "NOTICE",
52
83
  "README.md",
53
84
  "SECURITY.md"
54
85
  ],
55
86
  "engines": {
56
- "node": ">=20"
87
+ "node": ">=22"
57
88
  },
58
89
  "scripts": {
59
90
  "test": "vitest run",
60
91
  "test:coverage": "vitest run --coverage",
61
92
  "test:watch": "vitest",
62
- "bench": "MEMINDEX=1 vitest run --config vitest.bench.config.ts",
93
+ "test:changed": "vitest run --changed",
94
+ "contract:update": "node scripts/contract-update.mjs",
95
+ "verify": "node scripts/verify.mjs",
96
+ "verify:fast": "node scripts/verify.mjs --fast",
97
+ "verify:no-docs": "node scripts/verify.mjs --no-docs",
98
+ "bench": "vitest run --config vitest.bench.config.ts",
63
99
  "check:layers": "node scripts/check-layers.mjs",
64
100
  "test:node": "vitest run --config vitest.node.config.ts",
65
101
  "build:service": "node scripts/check-layers.mjs && tsc -noEmit -skipLibCheck && node esbuild.service.mjs && npm run build:api",
66
102
  "prepack": "npm run build:service",
67
103
  "build:api": "tsc -p tsconfig.api.json && node esbuild.api.mjs",
104
+ "check:package": "node scripts/check-package.mjs",
68
105
  "build": "npm run build:service",
69
- "prepublishOnly": "node scripts/release-window.mjs"
106
+ "dev": "node scripts/dev-vault.mjs",
107
+ "test-vault:make": "node scripts/make-test-vault.mjs",
108
+ "test-vault:check": "vitest run tests/test-vault.test.ts",
109
+ "docs:copy": "node scripts/copy-docs.mjs",
110
+ "prepublishOnly": "node scripts/copy-docs.mjs --check"
70
111
  },
71
112
  "devDependencies": {
113
+ "@arethetypeswrong/cli": "0.18.5",
114
+ "@modelcontextprotocol/sdk": "^1.32.1",
72
115
  "@types/node": "^22.10.0",
73
116
  "@vitest/coverage-v8": "^2.1.9",
74
117
  "esbuild": "0.25.5",
118
+ "publint": "0.3.25",
75
119
  "swagger-ui-dist": "^5.33.1",
76
120
  "tslib": "2.8.1",
77
121
  "typescript": "^5.8.3",