obsidian-tc 1.9.0 → 1.10.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.
package/README.md CHANGED
@@ -9,7 +9,7 @@ See the [repo root README](../../README.md) for project overview and the
9
9
 
10
10
  ## Status
11
11
 
12
- ✅ **Shipped — v1.9.0.** The full tool surface (141 tools across 31 domains, milestones
12
+ ✅ **Shipped — v1.10.0.** The full tool surface (141 tools across 31 domains, milestones
13
13
  M0–M7) is implemented and released. Built on Bun + Hono with Zod 4 schemas; runs under
14
14
  Node `>=24` (the test suite runs vitest under Node for `node:sqlite`).
15
15
 
@@ -33,6 +33,7 @@ config + caches (no server needed):
33
33
  | `citation-infer --transcript <f>` | Stamp `cited_in_response` on retrieval events from a session transcript |
34
34
  | `contribution-report` | Per-note output-contribution report (top contributors + dead-retrieved) |
35
35
  | `prefetch [--vault id] [--ttl-hours N]` | Prewarm the session-bootstrap context cache (TTL enforced at read) |
36
+ | `densify-llm [--vault id]` | LLM Pass-3 semantic-edge densification via the local gateway (graph densification, experimental; refuses if no gateway resolves) |
36
37
  | `reflect [--max-judged N]` | Sleep-time pass: stamp episode eligibility + update the preference profile |
37
38
  | `metrics [--since ms] [--until ms] [--json f]` | Knowledge-health scorecard from the derive layer |
38
39
  | `gaps --queries <f> / --calibrate <golden.yaml>` | Knowledge-gap detector / threshold calibration |
package/SKILLS.md ADDED
@@ -0,0 +1,372 @@
1
+ # obsidian-tc Agent Skills
2
+
3
+ A working guide for AI agents, and the people configuring them, to use the
4
+ **obsidian-tc** MCP server well. obsidian-tc gives an agent *governed* access to
5
+ one or more Obsidian vaults: **141 capabilities across 31 domains**, every call
6
+ funneled through one dispatch pipeline (auth, scopes, folder ACL, read-only kill
7
+ switch, idempotency, throttle, human-in-the-loop, response governor, audit).
8
+
9
+ Drop this file into your agent's instruction context (a `CLAUDE.md` / `AGENTS.md`,
10
+ a system prompt, or a skill) so the agent drives obsidian-tc from the real surface
11
+ instead of guessing tool names. The **Vault conventions** section (§8) is one real
12
+ vault's house style, shown as a template: keep what fits, replace the rest.
13
+
14
+ ---
15
+
16
+ ## 1. Mental model
17
+
18
+ **Governed access, not raw filesystem.** Every tool call, with no exceptions, runs
19
+ through one pipeline:
20
+
21
+ ```
22
+ auth → scopes → folder ACL → read-only kill switch → idempotency
23
+ → throttle → human-in-the-loop → handler → response governor → audit
24
+ ```
25
+
26
+ You never have to remember to check permissions per call; the membrane does. An
27
+ agent that respects the pipeline cannot silently overwrite a year of notes, read a
28
+ folder it was denied, or fire a destructive op without a human tap.
29
+
30
+ **Two physical stores (the membrane).** Authored knowledge and disposable machine
31
+ signal never mix:
32
+
33
+ - **`cache.db`**, the durable index of your *authored* atoms (chunks, embeddings,
34
+ FTS, the wikilink graph). Rebuildable from the vault files.
35
+ - **`experiential.db`**, quarantined machine telemetry (retrieval logs, work
36
+ episodes, ACT-R activation). Disposable. Never surfaced as if it were a note.
37
+
38
+ **Local-first, no egress by default.** The default embedder is local Ollama; the
39
+ generative tier is off unless you wire a local inference gateway; derived graph
40
+ edges are never written back into your notes. Nothing leaves the machine unless you
41
+ configure a cloud provider or gateway.
42
+
43
+ **Live vs headless.** With the companion plugin's Local REST API reachable, the
44
+ vault runs *live* and bridge tools work (Bases, Canvas render, OCR, Templater, Git,
45
+ Remotely Save). Without it, the vault is *headless*: every filesystem tool still
46
+ works; bridge tools return `requires_live_obsidian`. Mode is resolved **once at
47
+ startup**.
48
+
49
+ ---
50
+
51
+ ## 2. Setup (new users)
52
+
53
+ **Install** (Node ≥ 24 or Bun ≥ 1.1; also ships as a Docker image, a one-click
54
+ `.mcpb` bundle, and standalone binaries):
55
+
56
+ ```bash
57
+ npm install -g obsidian-tc
58
+ ```
59
+
60
+ **Zero-config start.** Point it at a vault folder and it boots a single vault with
61
+ id `main` and every default:
62
+
63
+ ```bash
64
+ obsidian-tc serve /absolute/path/to/vault
65
+ ```
66
+
67
+ **Real config** is one JSON file (path as argv, or `OBSIDIAN_TC_CONFIG`). The
68
+ minimum is a vault list; every other block is fully defaulted:
69
+
70
+ ```json
71
+ { "vaults": [{ "id": "main", "path": "/absolute/path/to/vault" }] }
72
+ ```
73
+
74
+ Inspect and validate before wiring it up:
75
+
76
+ ```bash
77
+ obsidian-tc config validate ./config.json
78
+ obsidian-tc config show ./config.json # secrets redacted
79
+ ```
80
+
81
+ **Turn on live mode (recommended)** so bridge tools work: install the companion
82
+ plugin, enable the Local REST API's **non-encrypted loopback** server
83
+ (`http://127.0.0.1:27123`), and add its key to the vault entry:
84
+
85
+ ```json
86
+ { "vaults": [{
87
+ "id": "main",
88
+ "path": "/absolute/path/to/vault",
89
+ "restApiUrl": "http://127.0.0.1:27123",
90
+ "restApiKey": "<Local REST API key>"
91
+ }] }
92
+ ```
93
+
94
+ The bridge client does not trust LRA's self-signed HTTPS, so use the loopback HTTP
95
+ port. `obsidian-tc plugin install` copies the companion plugin into your vault.
96
+
97
+ **Wire into your AI.** Register obsidian-tc as an MCP server in your client
98
+ (Claude Desktop / Claude Code / any MCP host). Default transport is **stdio** (the
99
+ trusted local path). For many-client or remote use, enable Streamable HTTP under
100
+ `transports.http`, and note the fail-closed interlock: an unauthenticated server
101
+ (`auth.mode: "none"`) refuses to bind a non-loopback host. Put a JWT on anything
102
+ routable.
103
+
104
+ **First index.** On first serve the vault indexes automatically; you can force a
105
+ rebuild with the `index_vault` capability or the CLI. Changing the embedding
106
+ model/dimensions or `cacheDir` needs a fresh cache (see §9, dimension-lock).
107
+
108
+ **Verify.** Call `find_capability` with a plain-English need (below). If it returns
109
+ matches, you are wired up.
110
+
111
+ ---
112
+
113
+ ## 3. The core skill: discover, don't guess
114
+
115
+ By default the server advertises **three meta-tools**, not a wall of 141. This
116
+ keeps agent context lean. Learn this loop before anything else:
117
+
118
+ 1. **`find_capability`**, BM25 search over the capabilities *this caller can see*.
119
+ Ask in plain language: `"move a note to another folder"`, `"render a canvas"`,
120
+ `"what did we decide about auth"`.
121
+ 2. **`describe_capability`**, the chosen capability's input schema, required
122
+ scopes, and safety hints. Read this before calling anything unfamiliar.
123
+ 3. **`call_capability`**, invoke it by name with validated arguments. The call
124
+ routes through the exact same auth/scope/ACL/HITL/idempotency/throttle pipeline
125
+ as a direct call; the target's own schema validates the args.
126
+
127
+ Every capability also remains **directly callable by name**. `toolFacade.mode`
128
+ selects what `tools/list` shows: `triad` (default, 3 tools), `domain` (~a dozen
129
+ domain meta-tools like `notes`, `search`, `vault`), or `flat` (the whole surface).
130
+ The facade is boundary-only: no gate is bypassed in any mode.
131
+
132
+ > Rule for agents: when you need an operation you have not used before, run
133
+ > `find_capability` then `describe_capability`. Do not invent a tool name.
134
+
135
+ ---
136
+
137
+ ## 4. Capability map (by intent)
138
+
139
+ Use `find_capability` for exact names; this is the terrain, grouped by what you
140
+ want to do. Counts are approximate; the surface is 141 tools / 31 domains.
141
+
142
+ | You want to… | Reach for | Notes |
143
+ | --- | --- | --- |
144
+ | **Read / write notes** | read, write, append, patch, move, delete a note; frontmatter get/set; tags; links | Writes obey folder ACL + HITL + optional CAS. Delete is HITL-gated. |
145
+ | **Search the vault** | full-text (FTS5 BM25), semantic (vec0 kNN), hybrid, `find_notes_by_*` | Hybrid fuses lexical + dense via RRF. |
146
+ | **Multi-hop / GraphRAG** | `vault_graph_search` | Walks the wikilink graph, RRF-fuses seed + expansion + lexical streams. |
147
+ | **One-call context for a question** | `vault_context` (`get_context(query, token_budget)`) | Budget-packed graph-reranked chunks + synthesis patterns + open contradictions + proactive lessons. The single best entry point for "answer from my vault." |
148
+ | **Grounded synthesis** | `reflect` | Synthesis with source provenance, an adversarial challenge mode, a versioned preference profile. Needs the inference gateway. |
149
+ | **Red-team a claim** | `knowledge_challenge` | Retrieves decision notes to argue against a proposal. |
150
+ | **Structured formats** | Bases (`.base` DSL evaluator), Canvas, Kanban, Periodic notes, Tasks, Excalidraw, Bookmarks, Workspaces | Several need live mode (render/DSL). |
151
+ | **Attachments / OCR** | attachment tools, OCR route | OCR needs live mode + the plugin. |
152
+ | **Memory graph** | memory entities + `[[link]]` graph, workspace sessions + JSONL traces | Projections live in the `memory/` folder. |
153
+ | **Work-memory (experiential)** | retrieval-log readers, episode readers, `forget` | Quarantined store; eligible-only reads; see §5. |
154
+ | **Bulk / admin / URI** | bulk ops, `add_vault`, `reload_vault`, `reset_vault_cache`, `index_vault`, snapshots/`restore_note`, `session_bootstrap`, resources over `obsidian-tc://<vault>/<path>` | Bulk + admin are throttled hardest. |
155
+ | **Companion bridges** | Obsidian Git (status/diff/log/stage; commits behind a hard human-confirmation floor), Remotely Save (backup verification), Templater/execute | Live mode only. |
156
+ | **Run a vault command** | `execute_command` | Deny-by-default: needs `commands.enabled` + an allowlist entry, and is still HITL-gated. |
157
+
158
+ ---
159
+
160
+ ## 5. New and notable (what is genuinely different here)
161
+
162
+ These are the skills that set obsidian-tc apart. Teach your agent to prefer them.
163
+
164
+ **Measured retrieval, not asserted.** Every ranking change is gated by an n=136
165
+ multi-hop golden set with a statistical ship rule (paired permutation, BH-FDR,
166
+ a ΔnDCG ≥ 0.010 cost gate). The live champion: graph nDCG@10 **0.786**, recall@10
167
+ **0.871**, bridge recall **0.831**. Contextual chunk enrichment measured **+0.223
168
+ nDCG** and defaults on. The practical takeaway for an agent: **trust the default
169
+ retrieval**, it is the measured optimum for this vault, and experimental streams
170
+ that did not beat it ship *dark* behind flags (see below).
171
+
172
+ **`vault_context`, the one-call context primitive.** Instead of hand-orchestrating
173
+ search + graph + rerank, call `get_context(query, token_budget)` and get a
174
+ budget-packed, graph-reranked context bundle back. This is the highest-leverage
175
+ retrieval skill; reach for it first when answering from the vault.
176
+
177
+ **Quarantined experiential memory.** The server auto-captures its own work
178
+ (retrieval events + dispatch episodes) into `experiential.db`, physically separate
179
+ from your notes, with a pre-ingest secret/poison scanner and an outcome axis. By
180
+ default it records the *action* axis only (tool, status, sizes, hashes, **no
181
+ payloads**); `experiential.captureContent` opts into secret-scanned args. This is
182
+ the substrate for a knowledge flywheel (`metrics`, `gaps`, `activation-recompute`),
183
+ not something an agent reads as if it were authored content.
184
+
185
+ **`forget`, dependency-aware deletion.** Deleting a note propagates through derived
186
+ state, with tombstone-vs-erase modes and a **hash-chained audit log** where
187
+ tampering with any entry breaks verification. Use it instead of a raw delete when
188
+ provenance matters.
189
+
190
+ **Governance an agent can rely on.** Folder ACLs (per vault, per caller), a
191
+ read-only kill switch, HITL elicit on destructive ops, compare-and-swap on writes
192
+ (`writes.requireCas`), idempotency keys, a response-size governor, and a
193
+ ReDoS-bounded regex. These are not optional add-ons; they are the dispatch path.
194
+
195
+ **Snapshots + restore.** With `snapshots.enabled`, destructive writes capture prior
196
+ state (content-addressed) so `restore_note` can roll back a bad edit.
197
+
198
+ **Dark-by-default measured features.** Graph densification (`retrieval.densify.*`),
199
+ learned-sparse and ColBERT serve streams (`retrieval.sparse` / `retrieval.colbert`),
200
+ the class router (`retrieval.classRouter`), the polyglot model tier
201
+ (`embeddings.provider: "model-tier"`), and the ACT-R activation rerank
202
+ (`experiential.activationRerank`) all ship **off**, each pending an A/B on your
203
+ golden set. An agent should not flip these expecting a win; they are opt-in
204
+ experiments, not tuned defaults.
205
+
206
+ **The generative tier is opt-in and local.** `reflect` synthesis, `densify-llm`
207
+ edge building, and ambient `plane` consolidation only run when you set
208
+ `OBSIDIAN_TC_GATEWAY_URL` to a local inference gateway. No LLM calls happen
209
+ otherwise.
210
+
211
+ ---
212
+
213
+ ## 6. Operating safely (agent rules)
214
+
215
+ - **Discover before calling.** `find_capability` → `describe_capability` → call.
216
+ - **Expect HITL on destructive ops.** Delete, some bridge writes, and `execute_command`
217
+ return an elicit request; surface it to the human and wait for the single-use,
218
+ args-bound confirmation. Do not try to route around it.
219
+ - **Respect the ACL result.** A `forbidden` / `acl_denied` / `read_only_mode` is a
220
+ decision, not a transient error to retry. Report it.
221
+ - **Use CAS on overwrites when it matters.** Pass `prev_hash` to `write_note` /
222
+ `append_note` (required when `writes.requireCas` is on) so you never clobber a
223
+ note that changed under you.
224
+ - **Use idempotency keys for retriable writes/bulk.** A keyed call that already
225
+ committed replays its result on retry instead of running twice.
226
+ - **Page, do not raise the ceiling.** Responses over `governor.maxResponseBytes`
227
+ are rejected; use cursors/limits rather than asking for a bigger budget.
228
+ - **Name the vault.** Multi-vault calls take `vault: "<id>"`; HTTP tokens are bound
229
+ to a vault and reject a call that names another.
230
+ - **Config changes need a restart.** The server reads config at boot and resolves
231
+ live/headless once; `reload_vault` only re-validates. Restart the server (or your
232
+ MCP client) to apply changes.
233
+
234
+ ---
235
+
236
+ ## 7. Agent playbooks (recipes)
237
+
238
+ **Answer a question from the vault**
239
+ 1. `vault_context` with the question and a token budget → grounded bundle.
240
+ 2. If it is a "how do these connect" question, prefer `vault_graph_search`.
241
+ 3. Cite note paths; never present `experiential.db` signal as authored content.
242
+
243
+ **Write knowledge back**
244
+ 1. Pick the destination folder by content (§8 routing).
245
+ 2. Compose the note with correct frontmatter + wikilinks to related notes (no
246
+ orphans).
247
+ 3. `write_note` (or `append_note` for logs/handoffs). Pass `prev_hash` if updating.
248
+ 4. If it records an architectural decision, write a decision note (§8), the
249
+ substrate the `knowledge_challenge` red-teamer later retrieves.
250
+
251
+ **Do a destructive edit safely**
252
+ 1. `describe_capability` to confirm scopes + that it is HITL-gated.
253
+ 2. Call it; relay the elicit request to the human; wait for confirmation.
254
+ 3. Prefer `forget` over raw delete when the note has derived state.
255
+
256
+ **Onboard a new vault**
257
+ 1. `add_vault` (or add it to config + restart).
258
+ 2. `index_vault` to build the index.
259
+ 3. Set an ACL: `readPaths` / `writePaths` / `deletePaths`, and
260
+ `strictReadDefault: true` if the agent should fail closed on unlisted reads.
261
+
262
+ **Close a session (write-back)**
263
+ 1. Append open threads to the handoff note (`memory.folder`/`_next-session.md`).
264
+ 2. Write any decisions as decision notes.
265
+ 3. Leave the vault, not the chat, as the durable record.
266
+
267
+ ---
268
+
269
+ ## 8. Vault conventions (a synthetic example, adapt to yours)
270
+
271
+ This is a synthetic example convention set, not any real vault, following a
272
+ numbered-folder (PARA-style) taxonomy with generic placeholder categories. It is a
273
+ reasonable default; change the categories and fields to match yours. The point is
274
+ **consistency**: a vault only compounds if every write lands in the right place
275
+ with the right metadata.
276
+
277
+ **Folder routing**
278
+
279
+ | Folder | For | Filename |
280
+ | --- | --- | --- |
281
+ | `00-inbox/` | raw captures awaiting routing | `YYYY-MM-DD-topic.md` |
282
+ | `01-daily/` | daily log (append only) | `YYYY-MM-DD.md` |
283
+ | `02-projects/` | active projects, one per project | `kebab-case.md` |
284
+ | `03-areas/` | ongoing areas of responsibility (ACL-restrict any sensitive ones) | free-form |
285
+ | `04-writing/` | drafts and published pieces | `slug.md` |
286
+ | `05-resources/<topic>/` | reference material by topic | `Title Case.md` |
287
+ | `06-media/` | media project notes | per subtype |
288
+ | `07-people/` | one note per person | `First Last.md` |
289
+ | `08-research/<domain>/` | structured research | `Title Case.md` |
290
+ | `09-reference/` | evergreen protocols, registries, indexes | `Title Case.md` |
291
+ | `09-reference/decisions/` | **decision notes (immutable)** | `YYYY-MM-DD-decision-slug.md` |
292
+
293
+ **Frontmatter on every note.** ISO dates (`YYYY-MM-DD`), kebab-case tags, a `type`.
294
+ Minimum:
295
+
296
+ ```yaml
297
+ ---
298
+ created: 2026-07-15
299
+ updated: 2026-07-15
300
+ type: project | reference | decision | research | person | daily
301
+ tags: []
302
+ ---
303
+ ```
304
+
305
+ **Obsidian Flavored Markdown.** Wikilinks `[[Note Name]]` (never raw paths in
306
+ prose), `[[Note#Heading]]`, `[[Note|Alias]]`; callouts (`> [!note]`, `> [!warning]`);
307
+ task lists (`- [ ]`, `- [x]`, `- [/]`, `- [-]`); embeds `![[Note]]` / `![[img.png|300]]`.
308
+
309
+ **No em dashes anywhere in notes.** Use a comma, colon, or parentheses. Em dashes
310
+ silently break Mermaid node labels and read as machine-generated.
311
+
312
+ **Decision notes are mandatory** for any architectural choice, vendor/tool pick,
313
+ "we tried X and chose Y", or hard veto. One immutable note per decision in
314
+ `09-reference/decisions/` with: the decision (one line), context, alternatives,
315
+ rationale, implications, reversibility/kill-switch, and wikilinks to related notes.
316
+ These are what the red-teamer retrieves; a decision made without a note silently
317
+ degrades future review.
318
+
319
+ **No orphans.** Every new note links from at least one existing note. **One topic
320
+ per note**; split at ~500 lines. **Append, do not multiply** daily/handoff notes.
321
+
322
+ **Session write-back.** At session end, route new knowledge to the right folder,
323
+ write decisions as decision notes, and append open threads to the handoff note.
324
+ The vault is the layer that survives; the chat is not.
325
+
326
+ ---
327
+
328
+ ## 9. Config quick reference + gotchas
329
+
330
+ The complete option surface (every field, default, env var, and CLI command) is in
331
+ `docs/src/content/docs/configuration/config-yaml.md`. The knobs you touch most:
332
+
333
+ | Knob | Default | Why you would change it |
334
+ | --- | --- | --- |
335
+ | `vaults[].path` / `id` | - | Required: where the vault is, how tools name it. |
336
+ | `vaults[].restApiUrl` + `restApiKey` | - | Turn on live mode (bridges). |
337
+ | `embeddings.provider` / `model` / `dimensions` | `ollama` / `nomic-embed-text` / 768 | Swap the embedder. **Dimension-locked**: changing it needs a fresh `cacheDir`. |
338
+ | `acl.readPaths` / `writePaths` / `deletePaths` | read `**`, write `02-projects/**` | Scope what the agent may touch. |
339
+ | `acl.strictReadDefault` | false | Fail closed on unlisted reads. |
340
+ | `acl.readOnly` | false | The kill switch. |
341
+ | `writes.requireCas` | false | Force `prev_hash` on overwrite/append. |
342
+ | `auth.mode` | `none` | `jwt` for anything non-loopback (required by the interlock). |
343
+ | `toolFacade.mode` | `triad` | `flat` if your client prefers the full list. |
344
+ | `transports.http.enabled` | false | Many-client / remote (then set auth). |
345
+ | `experiential.captureContent` | false | Opt into storing (scanned) call args. |
346
+ | `OBSIDIAN_TC_GATEWAY_URL` (env) | unset | Enable the generative tier (`reflect`, `densify-llm`, `plane`). |
347
+
348
+ **Gotchas that bite new users**
349
+
350
+ - **Runs on Bun** (`bun:sqlite`): the shipped `dist/cli.js` boots under Bun; plain
351
+ `node dist/cli.js` fails on the `bun:` import. Use the global bin or Bun.
352
+ - **Config resolves at boot.** Restart the server/client to apply changes;
353
+ `reload_vault` only re-validates. Adding/removing a vault always needs a restart;
354
+ changing `path` / embeddings / `cacheDir` also needs `reset_vault_cache` or a
355
+ fresh cache.
356
+ - **Live vs headless is resolved once** at startup. If the plugin was not reachable
357
+ when the server started, bridge tools stay `requires_live_obsidian` until you
358
+ restart with it up.
359
+ - **`.obsidian/`, `.git/`, `.trash/` are always denied** (case-folded). Do not try
360
+ to read or write plugin config or git internals through the vault tools.
361
+ - **Dark features are dark on purpose.** Do not flip `retrieval.*` /
362
+ `experiential.activationRerank` / the model tier expecting a win; they are
363
+ unmeasured on your vault. Run the eval first.
364
+ - **Secrets** (`restApiKey`, embedding/gateway keys, JWT secret) resolve
365
+ config-then-env and never appear in logs, errors, or audit rows. Prefer the env
366
+ vars.
367
+
368
+ ---
369
+
370
+ *Generated as agent-onboarding guidance for obsidian-tc. The capability surface is
371
+ authoritative via `find_capability` / `describe_capability`; when this file and the
372
+ live schema disagree, trust the schema.*