@inkeep/open-knowledge 0.10.0-beta.2 → 0.10.0-beta.4
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/dist/assets/skills/discovery/SKILL.md +1 -1
- package/dist/assets/skills/packs/entity-vault/SKILL.md +1 -1
- package/dist/assets/skills/packs/knowledge-base/SKILL.md +4 -4
- package/dist/assets/skills/packs/software-lifecycle/SKILL.md +1 -1
- package/dist/assets/skills/packs/worldbuilding/SKILL.md +1 -1
- package/dist/assets/skills/packs/writing-pipeline/SKILL.md +1 -1
- package/dist/assets/skills/project/SKILL.md +96 -84
- package/dist/cli.mjs +44 -44
- package/dist/constants-CXSN-BsG.mjs +2 -0
- package/dist/{dist-BChJILrk.mjs → dist-DTrrqwCO.mjs} +1 -1
- package/dist/{dist-DmAclKYw.mjs → dist-dyLf1nW-.mjs} +101 -110
- package/dist/{gh-detect-DjiocciN.mjs → gh-detect-7CewBTHd.mjs} +2 -2
- package/dist/index.mjs +1 -1
- package/dist/{init-B1U5zssu.mjs → init-DdHidrtv.mjs} +6 -6
- package/dist/init-G7zh8Edu.mjs +1 -0
- package/dist/{loader-D-MyijDv.mjs → loader-Bf5lDOSq.mjs} +3 -3
- package/dist/loader-CJkwNnOb.mjs +1 -0
- package/dist/{preview-CgQ_96_Y.mjs → preview-DLFMqNAn.mjs} +2 -2
- package/dist/preview-h7eoPZPt.mjs +1 -0
- package/dist/public/assets/{ActivityModeContent-Cag3qsEp.js → ActivityModeContent-B70RZtoZ.js} +1 -1
- package/dist/public/assets/DocumentContext-B0H5i_bU.js +61 -0
- package/dist/public/assets/{GraphPanel-2TcKJgp2.js → GraphPanel-DWqoIFOB.js} +3 -3
- package/dist/public/assets/SettingsDialogBody-bwbxduq5.js +7 -0
- package/dist/public/assets/{SourceEditor-BDifJ_-m.js → SourceEditor-By_0QuiO.js} +1 -1
- package/dist/public/assets/config-validation-events-CDAap8wV.js +12 -0
- package/dist/public/assets/index-B3eQbsM3.css +1 -0
- package/dist/public/assets/index-CgrGK587.js +1915 -0
- package/dist/public/assets/keyboard-shortcuts-C6IgDAxF.js +1 -0
- package/dist/public/assets/prop-types-ClZR0brz.js +500 -0
- package/dist/public/assets/{target-navigation-intent-CxMMZ391.js → target-navigation-intent-DJ_3bEwP.js} +1 -1
- package/dist/public/assets/{typing-burst-detector-DMPAdHvx.js → typing-burst-detector-Cilqkggt.js} +2 -2
- package/dist/public/index.html +8 -8
- package/dist/{repair-launch-json-t7SBBUmJ.mjs → repair-launch-json-DJof9L_4.mjs} +2 -2
- package/dist/{repair-mcp-configs-CFt0S96K.mjs → repair-mcp-configs-kmPXzAQA.mjs} +2 -2
- package/dist/{repair-skills-4GbGPfQs.mjs → repair-skills-CeRN3hNW.mjs} +2 -2
- package/dist/repair-skills-Dg1YYNHi.mjs +1 -0
- package/dist/{server-lock-BpjJj3OD-CpgmesYM.mjs → server-lock-BpjJj3OD-wzz0WNKm.mjs} +88 -88
- package/dist/server-lock-CyhBidkz-DrsmjbY0.mjs +1 -0
- package/dist/{src-DyJezQur.mjs → src-Cezub-n-.mjs} +2 -2
- package/dist/{start-ddvEdLDZ.mjs → start-Bgvhm827.mjs} +2 -2
- package/dist/start-DJJ_IAJu.mjs +1 -0
- package/dist/{write-project-skill-Bc9GzRes.mjs → write-project-skill-_gdmx7XS.mjs} +2 -2
- package/package.json +1 -1
- package/dist/constants-Aooo5mZf.mjs +0 -2
- package/dist/init-C2apfDIg.mjs +0 -1
- package/dist/loader-BEfkXa6p.mjs +0 -1
- package/dist/preview-B7k4yYED.mjs +0 -1
- package/dist/public/assets/DocumentContext-CV395VYL.js +0 -61
- package/dist/public/assets/SettingsDialogBody-Co5_Xo6m.js +0 -7
- package/dist/public/assets/config-validation-events-B5G3zveS.js +0 -12
- package/dist/public/assets/index-BE5VlQ00.js +0 -1915
- package/dist/public/assets/index-CYUEwyxk.css +0 -1
- package/dist/public/assets/keyboard-shortcuts-B4aOgyRn.js +0 -1
- package/dist/public/assets/prop-types-mM2mU4oh.js +0 -500
- package/dist/repair-skills-CEYqx9v7.mjs +0 -1
- package/dist/server-lock-CyhBidkz-3npi9JMD.mjs +0 -1
- package/dist/start-BSDJmh5u.mjs +0 -1
|
@@ -3,7 +3,7 @@ name: open-knowledge-discovery
|
|
|
3
3
|
description: "Read when the user asks what Open Knowledge is, wants to install it on a repository, wants to share an Open Knowledge project with collaborators, or asks how `ok init` / `ok install-skill` / OK Desktop set up a project. Do NOT load to perform Open Knowledge reads/writes — the runtime guidance for editing markdown inside an initialized OK project ships as a separate project-local skill at `.claude/skills/open-knowledge/` whenever `ok init` runs. If the user appears to be editing markdown inside a `.ok/` project and this is the only OK skill loaded, advise them to re-run `ok init` to install the project-local skill."
|
|
4
4
|
compatibility: "Any agent host — no MCP server required. Pure discovery + install guidance."
|
|
5
5
|
metadata:
|
|
6
|
-
version: "0.10.0-beta.
|
|
6
|
+
version: "0.10.0-beta.4"
|
|
7
7
|
author: "Inkeep"
|
|
8
8
|
repository: "https://github.com/inkeep/open-knowledge"
|
|
9
9
|
---
|
|
@@ -45,4 +45,4 @@ This pack ships the Markdown half (folders + templates + this skill); OK is the
|
|
|
45
45
|
|
|
46
46
|
## Templates
|
|
47
47
|
|
|
48
|
-
Create with `
|
|
48
|
+
Create with `write({ document: { path: "<path>", template: "<name>" } })`. Templates carry the structure (including the compiled-truth / `--- timeline ---` separator) plus short inline reminders at the point of use; this skill holds the full convention, so prefer it as the canonical reference if the two ever disagree.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: open-knowledge-pack-knowledge-base
|
|
3
|
-
description: "How to work in a Knowledge Base project (the `knowledge-base` starter pack). Read when the project has the three-layer source-grounded layout — `external-sources/` → `research/` → `articles/` — wired to the ingest / research / consolidate
|
|
3
|
+
description: "How to work in a Knowledge Base project (the `knowledge-base` starter pack). Read when the project has the three-layer source-grounded layout — `external-sources/` → `research/` → `articles/` — wired to the `workflow` MCP tool's ingest / research / consolidate kinds. Carries the pack's workflow, per-folder rules, status flows, and log discipline so this guidance does NOT live inside template bodies or log.md. Complements the platform `open-knowledge` skill; does not replace it."
|
|
4
4
|
compatibility: "Claude Code, Claude Desktop, Claude Cowork, Claude.ai web. Requires Open Knowledge MCP server. Installed project-local by `ok seed --pack knowledge-base`."
|
|
5
5
|
metadata:
|
|
6
6
|
pack: "knowledge-base"
|
|
@@ -23,7 +23,7 @@ research/ provisional analysis (produced by `research`)
|
|
|
23
23
|
articles/ canonical, decided knowledge (produced by `consolidate`)
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
The loop is **ingest → research → consolidate
|
|
26
|
+
The loop is **ingest → research → consolidate** — each a `workflow({ kind })` guide you invoke (`workflow({ kind: 'ingest' })`, etc.). Every downstream claim traces upstream to a preserved source. Cite local paths in `external-sources/`, never bare web URLs — the KB must survive link rot.
|
|
27
27
|
|
|
28
28
|
## Per-folder rules
|
|
29
29
|
|
|
@@ -46,7 +46,7 @@ When a new article supersedes an older one, add the older article's path to the
|
|
|
46
46
|
|
|
47
47
|
There is a `log.md` at the project root. **Append one dated entry after any turn that creates, edits, or restructures content** — one entry per turn, not per file. Silent edits break the audit trail.
|
|
48
48
|
|
|
49
|
-
Log: `ingest` runs (new sources), `research` / `consolidate` runs (provisional or canonical articles), direct `
|
|
49
|
+
Log: `ingest` runs (new sources), `research` / `consolidate` runs (provisional or canonical articles), direct `write` / `edit` / `move` / `delete` outside the three loop tools, `discover` runs, folder restructures, and `.ok/config.yml` changes.
|
|
50
50
|
|
|
51
51
|
**Reference docs as markdown links, not bare paths** — `[path/to/doc](./path/to/doc.md)`, so the entry shows up in `links({ kind: "backlinks" })` for those docs. A bare path string does not register in the graph.
|
|
52
52
|
|
|
@@ -63,4 +63,4 @@ Entry shape:
|
|
|
63
63
|
|
|
64
64
|
## Templates
|
|
65
65
|
|
|
66
|
-
Each folder has a starter template (`clip`, `research-log`, `article`). Create with `
|
|
66
|
+
Each folder has a starter template (`clip`, `research-log`, `article`). Create with `write({ document: { path, template: "<name>" } })`. Templates carry only structure (headings + frontmatter scaffold) — the meaning of each field and section is described above, not repeated inside the document body.
|
|
@@ -40,4 +40,4 @@ guides/ how-to / onboarding / runbooks (referenced throughout)
|
|
|
40
40
|
|
|
41
41
|
## Templates
|
|
42
42
|
|
|
43
|
-
Create docs with `
|
|
43
|
+
Create docs with `write({ document: { path, template: "<name>" } })`. Templates carry only structure (headings + frontmatter scaffold); what each section is for is described above, not repeated in the document body.
|
|
@@ -30,4 +30,4 @@ This project is a fiction encyclopedia. The graph is the product: characters, se
|
|
|
30
30
|
|
|
31
31
|
## Templates
|
|
32
32
|
|
|
33
|
-
Create with `
|
|
33
|
+
Create with `write({ document: { path, template: "<name>" } })`. Templates carry only structure; section meaning is described here, not inside the document body.
|
|
@@ -27,4 +27,4 @@ published/ shipped work; treat as immutable
|
|
|
27
27
|
|
|
28
28
|
## Templates
|
|
29
29
|
|
|
30
|
-
Create with `
|
|
30
|
+
Create with `write({ document: { path, template: "<name>" } })`. Templates carry only structure; section meaning lives here, not in the document body.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: open-knowledge
|
|
3
|
-
description: "MUST invoke before reading or editing any `.md` / `.mdx` file, and before any `mcp__open-knowledge__*` tool call (`exec`, `search`, `
|
|
3
|
+
description: "MUST invoke before reading or editing any `.md` / `.mdx` file, and before any `mcp__open-knowledge__*` tool call (`exec`, `search`, `write`, `edit`, and the rest). This skill is installed into the repository by `ok init`, so its presence alone means this is an Open Knowledge project — its runtime contract governs every markdown file here, with no need to probe for a `.ok/` directory. Authoritative agent-runtime contract; supersedes the overlapping MCP server `instructions` echo."
|
|
4
4
|
compatibility: "Claude Code, Claude Desktop, Claude Cowork, Claude.ai web. Requires Open Knowledge MCP server + code execution."
|
|
5
5
|
metadata:
|
|
6
|
-
version: "0.10.0-beta.
|
|
6
|
+
version: "0.10.0-beta.4"
|
|
7
7
|
author: "Inkeep"
|
|
8
8
|
repository: "https://github.com/inkeep/open-knowledge"
|
|
9
9
|
---
|
|
@@ -18,20 +18,27 @@ Open Knowledge (OK) is a markdown-CRDT collaboration platform exposed via MCP. T
|
|
|
18
18
|
## TL;DR — the 90% case
|
|
19
19
|
|
|
20
20
|
1. **Reads:** `exec("cat …")` for a single doc, `exec("ls -A …")` for a directory (with folder defaults + template menu), `exec("grep …")` for literal, `search` for ranked retrieval. Native `Read` / `Grep` only on source code (`.ts` / `.py` / …), never on in-scope `.md` / `.mdx`.
|
|
21
|
-
2. **Writes:** `
|
|
22
|
-
3. **Preview:** every OK read/write response carries a route-only `previewUrl` (`/#/<doc>`, no host:port). If you have a `preview_*` tool, call `preview_start("open-knowledge-ui")`; if you have an in-app browser, call `
|
|
23
|
-
4. **Workflow
|
|
21
|
+
2. **Writes:** `write({ document: { path, content } })` for a new or full-replace doc; `edit({ document: { path, find, replace } })` for a body find/replace; `edit({ document: { path, frontmatter } })` for a frontmatter merge-patch (`null` deletes a key). `delete({ document })` removes, `move({ from, to })` moves/renames. Body find/replace is body-only — frontmatter goes through the `frontmatter` patch. Pass a one-line `summary` (≤80 chars, user-facing outcome) on every content write — it's the timeline change-note (see §Writing).
|
|
22
|
+
3. **Preview:** every OK read/write response carries a route-only `previewUrl` (`/#/<doc>`, no host:port). If you have a `preview_*` tool, call `preview_start("open-knowledge-ui")`; if you have an in-app browser, call `preview_url` once for the full browser URL and navigate to it; on the Claude Code CLI (no browser tool), run `ok open <doc>` to open it in the OK Desktop app. Surface to the user on a `start-ui` warning (no UI running). Don't `preview_screenshot` to confirm edits — the CRDT tool response is the confirmation.
|
|
23
|
+
4. **Workflow guides** — `workflow({ kind: 'ingest' | 'research' | 'consolidate' | 'discover' })` returns a procedural guide, not data. Use it when the work fits the layer; follow the numbered steps.
|
|
24
24
|
|
|
25
25
|
Everything below is depth. Read on demand.
|
|
26
26
|
|
|
27
|
-
## Tool index —
|
|
27
|
+
## Tool index — 17 tools
|
|
28
28
|
|
|
29
29
|
The full MCP surface, grouped by risk-level. Every tool's `kind` / `action` set is single-risk-level (never a read and a write behind one discriminator).
|
|
30
30
|
|
|
31
|
-
- **Reads** — `exec` (primary; shell-style `cat`/`ls`/`grep`/`find` with frontmatter + backlink + history enrichment), `search` (ranked, BM25 + recency), `
|
|
32
|
-
- **Writes** —
|
|
33
|
-
-
|
|
34
|
-
-
|
|
31
|
+
- **Reads** — `exec` (primary; shell-style `cat`/`ls`/`grep`/`find` with frontmatter + backlink + history enrichment), `search` (ranked, BM25 + recency), `history` (versions for a doc), `links` (`kind: 'backlinks'|'forward'|'dead'|'orphans'|'hubs'|'suggest'`, or an array of those for a one-call audit, e.g. `links({ kind: ["dead", "orphans", "hubs"] })`), `config` (resolved config), `palette` (markdown-native authoring forms + themed `html preview` embed starters + theme tokens; pass `components` for the canonical component JSX schemas), `preview_url` (browser-reachable preview URL on demand), `share_link` (GitHub-substrate share URL for a doc or folder; read-only against `.git/`, no commits/pushes — returns a clear error when the project has no GitHub remote, since agents do not publish projects).
|
|
32
|
+
- **Writes** — four native CRUD verbs, polymorphic over `document` / `folder` / `template` / `asset` (Pattern B: per-target fields nested inside the address key; pass EXACTLY ONE target):
|
|
33
|
+
- `write` — create/overwrite a `document` (supports `document.template` instantiation), create a `folder` (with open-shape frontmatter), create a `template`, or upload an `asset`.
|
|
34
|
+
- `edit` — modify a `document` (body find/replace OR frontmatter merge-patch), a `folder` (frontmatter merge-patch), or a `template`. (No asset — binary has no text body.)
|
|
35
|
+
- `delete` — remove a `document` (name or array), `folder`, `template`, or `asset`.
|
|
36
|
+
- `move` — move/rename a `document`, `folder`, or `asset`; rewrites referrers.
|
|
37
|
+
The OUTPUT mirrors the input: `write`/`edit`/`delete` nest their result under the same target key you passed (`{ document: {…} }` / `{ folder: {…} }` / `{ template: {…} }` / `{ asset: {…} }`, or `{ documents: [...] }` for a batch). The preview envelope (`previewUrl`, `warning`, `previousPreviewUrl`) stays at the top level — same for every tool.
|
|
38
|
+
Plus `checkpoint` (save a named version) and `restore_version` (roll a doc back to a prior version). A folder's own frontmatter is open-shape exactly like a doc's (self-only, does NOT cascade into child docs); templates are what new docs in a folder start with.
|
|
39
|
+
**Self-correcting on misuse:** the few constraints JSON Schema can't express — "pass exactly one target", "`find` needs a `replace`", body-XOR-frontmatter — are enforced by a *teaching error*: a wrong call returns `isError: true` with a one-line message naming the exact corrective shape. Read it and retry with that shape; don't guess.
|
|
40
|
+
- **GitHub-sync conflicts** — `conflicts` (`kind: 'list'` to enumerate, `kind: 'content'` for base/ours/theirs stages + lifecycle), `resolve_conflict` (write a chosen resolution + commit; destructive). Mutating writes against a doc in conflict return RFC 9457 `urn:ok:error:doc-in-conflict` (409); `exec("cat …")` returns `lifecycle: {status, reason} | null` so you can detect the state proactively. See *Conflict-aware writes*.
|
|
41
|
+
- **Workflow** — `workflow` (`kind: 'ingest' | 'research' | 'consolidate' | 'discover'`; returns procedural guides, not data).
|
|
35
42
|
|
|
36
43
|
Tools NOT in OK MCP (they belong to your agent host): `preview_start`, `preview_screenshot`, `WebFetch`, `WebSearch`, native `Read` / `Grep` / `Glob` / `Edit`. The STOP rule below governs which of those you may use on in-scope markdown.
|
|
37
44
|
|
|
@@ -45,7 +52,7 @@ When this workspace has Open Knowledge MCP configured, do **not** use your host'
|
|
|
45
52
|
- **Dispatching the Explore / general-purpose subagent for markdown-heavy exploration** — subagents use native `Read` / `Grep` / `Glob` internally and bypass Open Knowledge entirely. Do markdown exploration yourself via `exec` / `search`. Subagents remain appropriate for **source-code** exploration.
|
|
46
53
|
- **Native `Read` / `Grep` on any in-scope markdown inside `.ok/`** — the `.ok/` directory is in-scope; if it carries `.md` / `.mdx`, treat those the same as any other knowledge-base file.
|
|
47
54
|
|
|
48
|
-
Why: native tools skip frontmatter, backlinks, shadow-repo activity, and project git history that OK's tools return for every matched knowledge-base file. `exec` is the primary read surface; it runs read-only bash (`cat`, `ls`, `grep`, `find`, `head`, `tail`, `wc`, `sort`, `uniq`, `cut`
|
|
55
|
+
Why: native tools skip frontmatter, backlinks, shadow-repo activity, and project git history that OK's tools return for every matched knowledge-base file. `exec` is the primary read surface; it runs read-only bash (`cat`, `ls`, `grep`, `find`, `head`, `tail`, `wc`, `sort`, `uniq`, `cut`) and returns raw stdout plus enriched metadata per file. One command or a pipe (`|`) per call — it is NOT a shell, so `&&` / `;` / redirects are rejected; list several dirs with `ls -A a b c` or make separate calls.
|
|
49
56
|
|
|
50
57
|
**MCP tool visibility — not seeing `exec` is NOT the escape hatch.** MCP wiring varies by client. Claude Code, Cursor, Codex, Windsurf, VS Code — each surfaces MCP differently. Server labels are user-defined; tools may not appear as top-level symbols named `exec` in your specific UI. If Open Knowledge is registered as an MCP server in this workspace, route markdown reads through its `exec` / `search` via your client's documented MCP invocation (including any generic "call MCP tool" flow). Registration is the test, not top-level-symbol visibility.
|
|
51
58
|
|
|
@@ -70,10 +77,10 @@ The user watches your edits land in a live browser preview. Open it once at sess
|
|
|
70
77
|
|
|
71
78
|
**Pick how to open the preview by tool capability — not by host name.** Look at the tools actually available to you this session. If a tool can navigate to a URL, it counts as an in-app browser — match on the capability, not on what your host is called.
|
|
72
79
|
|
|
73
|
-
- **You have `preview_*` tools** (e.g. `preview_start` + `preview_eval`) → **First open of the session:** to land directly on a doc, arm it first with `
|
|
74
|
-
- **No `preview_*` tool, but you have an in-app / built-in browser tool** — Codex's built-in browser, or any host tool that navigates to a URL (`browser`, `view_url`, `open_url`, `web.browse`, etc.) → call `
|
|
75
|
-
- **Truly no browser-capable tool — if you have ANY tool that navigates to a URL, use the in-app branch above** (a pure stdio host with no URL-navigation tool at all, e.g. the Claude Code CLI) → for an "open `<doc>`/`<folder>`" request, run **`ok open <doc>`** (`--folder` for a folder) — opens the doc in OK Desktop via deep link (folders in the browser), with browser fallback; an action, not a URL to print. No `ok` on PATH or no shell → `
|
|
76
|
-
- **Honor `autoOpen`** (on `
|
|
80
|
+
- **You have `preview_*` tools** (e.g. `preview_start` + `preview_eval`) → **First open of the session:** to land directly on a doc, arm it first with `preview_url({ armPaneTarget: true, document })` (or `folder`), then `preview_start("open-knowledge-ui")` — `ok ui` redirects the base-open straight to the armed route, so the pane opens on the doc, not root. Plain `preview_start` (no arm) opens at root. **Moving between docs once the pane is open: do it in one `preview_eval` step — set `window.location.hash` to the target's route fragment from the response `previewUrl`, the part from `#` on (e.g. `window.location.hash = '#/specs/foo/SPEC'`).** That drives the SPA router directly. Arm + `preview_start` only redirects a *fresh* open; it can't move an already-open pane (`preview_start` reuses the live process without reloading), so use `preview_eval` there. Don't read or edit `.claude/launch.json` — host-managed; the OK lock-collision proxy handles the UI-already-running case. If `preview_start` fails, report it; don't "fix" `launch.json`.
|
|
81
|
+
- **No `preview_*` tool, but you have an in-app / built-in browser tool** — Codex's built-in browser, or any host tool that navigates to a URL (`browser`, `view_url`, `open_url`, `web.browse`, etc.) → call `preview_url` once for the **exact** target (`document` for a doc, `folder` for a folder) and navigate your **in-app browser** straight to the returned `url`. Open that deep URL directly — never the root then navigate. Omit both args only for the root.
|
|
82
|
+
- **Truly no browser-capable tool — if you have ANY tool that navigates to a URL, use the in-app branch above** (a pure stdio host with no URL-navigation tool at all, e.g. the Claude Code CLI) → for an "open `<doc>`/`<folder>`" request, run **`ok open <doc>`** (`--folder` for a folder) — opens the doc in OK Desktop via deep link (folders in the browser), with browser fallback; an action, not a URL to print. No `ok` on PATH or no shell → `preview_url`, then `open <url>` in the system browser as a last resort, and say so plainly. The system browser is the fallback, never the default.
|
|
83
|
+
- **Honor `autoOpen`** (on `preview_url`, or on `warning` for write tools). If `false`, do not open or refresh any preview UI; surface the URL only if asked.
|
|
77
84
|
|
|
78
85
|
**Opening or reading a file IS a preview navigation.** On any "open `<file>`" / "read `<file>`" request, navigate the browser to that doc's `previewUrl` route from the tool response — not a separate fetch, not a fresh system-browser launch.
|
|
79
86
|
|
|
@@ -81,12 +88,12 @@ The user watches your edits land in a live browser preview. Open it once at sess
|
|
|
81
88
|
|
|
82
89
|
1. You opened/navigated earlier this session → don't reopen.
|
|
83
90
|
2. Write response has `previewUrl` (non-null route) and NO `warning` → a browser is attached somewhere; do nothing.
|
|
84
|
-
3. `warning: { action: "attach-preview-once", previewUrl, message }` → UI reachable, no browser attached; navigate one-shot (`preview_start`, or `
|
|
91
|
+
3. `warning: { action: "attach-preview-once", previewUrl, message }` → UI reachable, no browser attached; navigate one-shot (`preview_start`, or `preview_url` → in-app browser).
|
|
85
92
|
4. `warning: { action: "start-ui", previewUrl: null, message }` → no UI running anywhere. Surface the message verbatim — recovery options are in the in-band copy. Don't loop on retries.
|
|
86
93
|
|
|
87
94
|
Warnings fire at most once per session in the fresh-start case.
|
|
88
95
|
|
|
89
|
-
**`previewUrl: null` only means "no UI reachable" on the
|
|
96
|
+
**`previewUrl: null` only means "no UI reachable" on the two attach-warning tools: `write` / `edit`.** Workflow tools return prose and don't carry `previewUrl`. `delete` / `move` emit `previousPreviewUrl` (different field, for closing stale tabs) and don't fire attach warnings. `preview_url` reports `running: false` + `url: null` when no UI is running.
|
|
90
97
|
|
|
91
98
|
If you see `"Hocuspocus server is not running"`, run `ok start` and retry.
|
|
92
99
|
|
|
@@ -98,17 +105,19 @@ OK Electron and `ok ui` share `ui.lock`; when a second UI binds a different port
|
|
|
98
105
|
|
|
99
106
|
## Writing
|
|
100
107
|
|
|
101
|
-
Call `
|
|
108
|
+
Call `write` / `edit` as soon as you have content. Native `Edit` / `sed` / direct `Write` on in-scope markdown is forbidden — it bypasses the CRDT and loses agent attribution in the shadow repo.
|
|
102
109
|
|
|
103
|
-
**Pass a `summary` on every content write (SHOULD).** `
|
|
110
|
+
**Pass a `summary` on every content write (SHOULD).** `write`, `edit`, and `move` each take a one-line `summary` (≤80 chars) describing the user-facing outcome of the change — "Add gear list and permit info", not "edited trip doc". It renders as a bullet under your name in the document timeline and is the only human-readable change-note persisted to the shadow-repo history; omit it and the timeline shows *that* you wrote but not *what changed*. Write it from the reader's perspective, keep it specific, and avoid secrets or PII (it lands in git history). Each entry in the batch `documents:` form carries its own `summary`.
|
|
104
111
|
|
|
105
|
-
**
|
|
112
|
+
**Reach for visual structure where it aids comprehension.** Default to the right OK primitive over flat prose: a Callout (`> [!NOTE]`) for a key caveat, a ` ```mermaid ` diagram for a process or relationship, a table for options or comparisons, an `html preview` chart for numbers. **Call the `palette` MCP tool as you draft** (and `palette({ components })` for a canonical's JSX schema) — it returns copy-ready markdown-native forms, themed `html preview` embed starters, and the theme tokens, so the visual lands themed and in the content graph instead of hand-rolled. Don't decorate — use a visual only when it carries the point better than prose would. Full catalog: §Components.
|
|
106
113
|
|
|
107
|
-
|
|
114
|
+
**Content-divergence warning (Site A gate).** `write` and `edit` responses may include a content-divergence warning when the converged Y.Text doesn't match the bytes the payload composed to (concurrent peer left residue, or — rare — a primitive regression). The write still landed; on this signal, re-read the doc (`exec("cat <path>")`) to see what actually converged before continuing. Single-doc shape: `structuredContent.document.contentDivergence = { kind: "content-divergence", intendedBytes, actualBytes, byteDelta, hint }` (the result nests under the `document` target key, mirroring the input). Batch shape: per-doc `structuredContent.documents[].contentDivergence` with the same fields. Distinct from the preview-attach `warning` field (`action: "attach-preview-once" | "start-ui"`), which stays at the top level — separate keys, can coexist.
|
|
108
115
|
|
|
109
|
-
To
|
|
116
|
+
To author an MDX doc (the KB renders MDX/JSX components), pass a `.mdx` path on the create: `write({ document: { path: "guides/widget.mdx", content, position: "replace" } })` lands `guides/widget.mdx`. A `.md` or extension-less path lands `.md`. An existing doc keeps its on-disk extension regardless of the suffix you pass — changing it in place isn't available via the MCP today.
|
|
110
117
|
|
|
111
|
-
|
|
118
|
+
To delete a doc, call `delete({ document })` — never `rm` / `unlink` / native `Bash` removal on in-scope markdown. The MCP path closes open agent sessions and unloads the doc from Hocuspocus before unlinking; native `rm` desynchronizes those. Deletion is irreversible — call `checkpoint()` first if you may need to roll back (it snapshots the whole project; afterwards restore the doc via `restore_version({ document, version })`, finding the `version` in `history`), and `links({ kind: "backlinks", document })` first if you want to fix referrers that will become redlinks. To move or rename a doc instead of delete + rewrite, use `move({ from, to })` — it auto-detects document vs folder vs asset and rewrites incoming references atomically.
|
|
119
|
+
|
|
120
|
+
**If `edit` returns "Text not found" on text you can verify exists on disk** (via `exec("cat …")`), the MCP session is likely stale (e.g., after a folder rename or server restart). Treat this as the escape-hatch trigger from the STOP block: prefix your next user-visible sentence with `Open Knowledge MCP unavailable:` and report the inconsistency. Don't loop on retries — the symptom is structural, not transient.
|
|
112
121
|
|
|
113
122
|
## Conflict-aware writes
|
|
114
123
|
|
|
@@ -119,23 +128,23 @@ Projects with GitHub sync enabled may carry docs in a merge-conflict state. The
|
|
|
119
128
|
"type": "urn:ok:error:doc-in-conflict",
|
|
120
129
|
"title": "Document is in conflict.",
|
|
121
130
|
"status": 409,
|
|
122
|
-
"detail": "The document is in a merge-conflict state. Call
|
|
131
|
+
"detail": "The document is in a merge-conflict state. Call conflicts({ kind: 'content' }) + resolve_conflict before retrying.",
|
|
123
132
|
"file": "notes/sso.md",
|
|
124
|
-
"resolutionOptions": ["mine", "theirs", "content"]
|
|
133
|
+
"resolutionOptions": ["mine", "theirs", "content", "delete"]
|
|
125
134
|
}
|
|
126
135
|
```
|
|
127
136
|
|
|
128
|
-
The gate covers `
|
|
137
|
+
The gate covers `write`, `edit`, `delete`, `move`, `restore_version`, and agent undo (the doc-CRDT write spine; template/folder ops are fs-direct). You cannot route around it by writing content that byte-matches one of the merge stages — the gate refuses on lifecycle state, not on body equality.
|
|
129
138
|
|
|
130
139
|
**Detect proactively.** `exec("cat <path>.md")` always returns `lifecycle: {status, reason} | null` alongside the body. When `status === 'conflict'`, switch to the resolution flow before attempting any mutation.
|
|
131
140
|
|
|
132
141
|
**Resolution flow.** Three tools compose:
|
|
133
142
|
|
|
134
|
-
1. `
|
|
135
|
-
2. `
|
|
136
|
-
3. `resolve_conflict({ file, strategy, content? })` → write the chosen bytes and commit. Strategies: `mine`
|
|
143
|
+
1. `conflicts({ kind: 'list' })` → enumerate every doc currently tracked in conflict.
|
|
144
|
+
2. `conflicts({ kind: 'content', file })` → returns `{ content: { base, ours, theirs, shape, lifecycleStatus } }` (the result nests under the `content` kind key). `ours` reflects the live Y.Text (what the human user sees in the editor) when the doc is loaded server-side and is marker-free; falls back to `git show :2:<file>` otherwise (e.g. after an editor reopen seeded markers into Y.Text).
|
|
145
|
+
3. `resolve_conflict({ file, strategy, content? })` → write the chosen bytes and commit. Strategies: `mine` runs `git checkout --ours` (your committed stage 2), `theirs` runs `git checkout --theirs` (their stage 3), `content` writes the bytes you supply, `delete` runs `git rm` (for delete-modify / modify-delete shapes where a stage is missing).
|
|
137
146
|
|
|
138
|
-
`file` is a `.md` / `.mdx` path relative to the project dir (extension included) — mirrors the on-disk shape, not the extension-less `
|
|
147
|
+
`file` is a `.md` / `.mdx` path relative to the project dir (extension included) — mirrors the on-disk shape, not the extension-less `document` path used by other tools.
|
|
139
148
|
|
|
140
149
|
The resolve operation is best-effort and NOT atomic: `git checkout --ours/--theirs && git add` may succeed but the subsequent `git commit --no-edit` can fail (pre-commit hook rejection, locked index). On commit failure the staged files are re-`git add`-ed back into the unmerged index and the tracked entry remains in `conflicts.json` — re-call `resolve_conflict` after the user clears the blocker.
|
|
141
150
|
|
|
@@ -151,17 +160,17 @@ OK auto-promotes markdown-native syntax into themed canonical components at pars
|
|
|
151
160
|
| Math | `$x$` inline, `$$…$$` block | KaTeX Math |
|
|
152
161
|
| Inline a doc or asset | `![[file]]` | wiki embed |
|
|
153
162
|
|
|
154
|
-
`Tabs` is the lone canonical with **no** markdown-native form — write the JSX directly (`<Tabs><Tab label="…">…</Tab></Tabs>`). For any canonical's full JSX prop schema, call `
|
|
163
|
+
`Tabs` is the lone canonical with **no** markdown-native form — write the JSX directly (`<Tabs><Tab label="…">…</Tab></Tabs>`). For any canonical's full JSX prop schema, call `palette({ components: [ids] })`. If no canonical fits, any `<TagName>…</TagName>` falls through as raw MDX — but prefer a canonical when one matches.
|
|
155
164
|
|
|
156
|
-
**Discover the palette in one call.** `
|
|
165
|
+
**Discover the palette in one call.** `palette` returns every markdown-native form (copy-ready `example` + `guidance`), the themed `html preview` embed starters, and the injected theme-token list — the source of truth for component-forward, themed authoring. Canonical names/counts beyond the markdown-native set are project-specific; the inventory in the `write` / `edit` descriptions and `palette({ components })` are authoritative for those.
|
|
157
166
|
|
|
158
|
-
**Show findings, don't just tell them.** When a point is quantitative or comparative — a trend over time, a breakdown, a before/after, a ranking, a distribution — present it visually: a chart or stat-card `html preview` embed, a ` ```mermaid ` diagram, a table, or a Callout for the headline takeaway. Prose-only buries the insight. This matters most where the document's job is to make findings legible — **`research` reports and `consolidate` articles especially**, and any write-up meant to present results. A research article with three dense paragraphs of numbers should have been a chart. Reach for `
|
|
167
|
+
**Show findings, don't just tell them.** When a point is quantitative or comparative — a trend over time, a breakdown, a before/after, a ranking, a distribution — present it visually: a chart or stat-card `html preview` embed, a ` ```mermaid ` diagram, a table, or a Callout for the headline takeaway. Prose-only buries the insight. This matters most where the document's job is to make findings legible — **`research` reports and `consolidate` articles especially**, and any write-up meant to present results. A research article with three dense paragraphs of numbers should have been a chart. Reach for `palette` as you draft, not after.
|
|
159
168
|
|
|
160
169
|
### `html preview` — themed interactive embeds
|
|
161
170
|
|
|
162
171
|
A ` ```html preview ` fence (also `htm` / `xml`) renders a standalone HTML/CSS/JS page as a live sandboxed iframe — the extend-to-anything primitive for charts, stat cards, custom SVG, calculators, demos. The iframe auto-sizes to its content; pass `h=` / `w=` (e.g. ` ```html preview h=400px `) only to pin a fixed size.
|
|
163
172
|
|
|
164
|
-
**Start from a starter — don't hand-roll.** `
|
|
173
|
+
**Start from a starter — don't hand-roll.** `palette` returns `embedPatterns` (chart, stat cards, custom SVG, interactive control), each already wired to the theme tokens. Copy one and fill in your data — that is the only path that cannot render unthemed. Hand-author a fence from scratch only when no starter is close.
|
|
165
174
|
|
|
166
175
|
**MUST — never hardcode colors in an `html preview` embed.** OK injects its theme tokens into every preview iframe; an embed that hardcodes hex / `rgb()` renders unthemed — a white box on a dark page, clashing with every component around it. This is the single most common embed mistake. Wire every color to a token: `var(--chart-1..5)` for chart series, `var(--foreground)` / `var(--muted-foreground)` for text, `var(--card)` / `var(--background)` for surfaces, plus `var(--border)`, `var(--primary)`, `var(--radius)`. Don't set a `body` background at all unless you specifically mean to — the iframe already carries a themed one.
|
|
167
176
|
|
|
@@ -213,7 +222,7 @@ Knowledge-base docs are factual artifacts — whether the project is a wiki, an
|
|
|
213
222
|
- **Every link must resolve to a doc that exists.** Never link to a doc that isn't written yet. If you want to reference something that should have its own page but doesn't: create that page in the same pass, or record it as a tracked task (`TaskCreate`, or your host's task tool — if the host has none, tell the user) and leave the mention as plain prose. A broken link is debt, not a to-do marker.
|
|
214
223
|
- **Never wrap a link in backticks.** `` `[text](./foo.md)` `` is a bug — the backticks make it render as literal code rather than a link.
|
|
215
224
|
- **Never use HTML anchors** (`<a href="...">`). Markdown link syntax only.
|
|
216
|
-
- **Verify before walking away.** After writing a doc, call `links({ kind: "dead",
|
|
225
|
+
- **Verify before walking away.** After writing a doc, call `links({ kind: "dead", sourceDocuments: ["your/doc"] })` to find broken references. Fix or remove every one — a dead link is never acceptable to leave behind. Companion `links` kinds: `backlinks` (incoming), `forward` (outgoing), `orphans` (no incoming), `hubs` (high-incoming), `suggest` (untextualized mentions worth linking).
|
|
217
226
|
- **The editor's red-underline visual lies.** Its dead-link detection tolerates slug-fallback (e.g., `foo` may appear resolved because `foo.md` exists at root). `links({ kind: "dead" })` is strict-exact — trust the tool, not the visual.
|
|
218
227
|
|
|
219
228
|
**Note on wiki-link syntax (`[[Page]]`):** the parser still handles it for legacy content, but it's NO LONGER the recommended default. Write new content with standard markdown links per above. Seed-pack templates (`ok seed --pack <name>`) may still emit `[[Page]]` placeholders inside template body text — those are legacy. When you instantiate a seed-pack template, replace the legacy placeholders with standard markdown links during the `{shape}`-fill pass.
|
|
@@ -263,11 +272,11 @@ A doc's frontmatter is exactly its own on-disk YAML — folder frontmatter never
|
|
|
263
272
|
|
|
264
273
|
### Read the folder before writing (MUST)
|
|
265
274
|
|
|
266
|
-
Before creating or editing docs in a folder, **always** call `exec("ls -A <folder>")` once. The response carries the folder's own `title`/`description`/`tags` + `templates_available` (the template menu for `
|
|
275
|
+
Before creating or editing docs in a folder, **always** call `exec("ls -A <folder>")` once. The response carries the folder's own `title`/`description`/`tags` + `templates_available` (the template menu for `write({ document: { template } })`). Skipping this is how agents land docs that violate folder discipline.
|
|
267
276
|
|
|
268
277
|
Pre-write checklist:
|
|
269
278
|
|
|
270
|
-
0. **First-contact check.** If the folder has no frontmatter of its own AND `templates_available` is empty AND `exec("ls -A")` shows substantial content elsewhere, the project hasn't been onboarded — STOP and invoke `discover` (Workflow
|
|
279
|
+
0. **First-contact check.** If the folder has no frontmatter of its own AND `templates_available` is empty AND `exec("ls -A")` shows substantial content elsewhere, the project hasn't been onboarded — STOP and invoke `workflow({ kind: 'discover' })` (Workflow guides below). Skip on subsequent writes once confirmed.
|
|
271
280
|
1. **Read the folder's description** — its `title`/`description`/`tags` tell you what the folder is for. (These describe the folder; they are NOT defaults the doc inherits.)
|
|
272
281
|
2. **Read `templates_available`** — each entry has `name`, `title`, `description`, `scope` (`local` / `inherited`). If one matches, **prefer it** over free-form markdown (it's the folder's contract — templates carry frontmatter + body structure hand-authored docs routinely miss).
|
|
273
282
|
3. **Read recent siblings** — new docs should match the shape of existing ones (filename, frontmatter, body structure).
|
|
@@ -277,53 +286,55 @@ Pre-write checklist:
|
|
|
277
286
|
|
|
278
287
|
### When to use a template (MUST when one fits)
|
|
279
288
|
|
|
280
|
-
Instantiate via `
|
|
289
|
+
Instantiate via `write({ document: { path, template } })`. Inherited templates (`scope: "inherited"`) are equally valid. Skip only when (a) `templates_available` is empty, (b) no entry matches, OR (c) the user asked for free-form. If you skip, briefly note why in chat.
|
|
281
290
|
|
|
282
291
|
### When to create a template
|
|
283
292
|
|
|
284
293
|
Templates make folder structure durable. Create them proactively:
|
|
285
294
|
|
|
286
|
-
- 2+ sibling docs share a skeleton in a folder with no template → extract via `
|
|
295
|
+
- 2+ sibling docs share a skeleton in a folder with no template → extract via `write({ template })`.
|
|
287
296
|
- About to write a doc in a folder where no template fits, AND the shape is reusable → save as template the same turn.
|
|
288
|
-
- Scaffolding a new folder for a doc category → pair
|
|
297
|
+
- Scaffolding a new folder for a doc category → pair `write({ folder })` (or `edit({ folder })`) with `write({ template })` in the same turn.
|
|
289
298
|
- The user describes a recurring doc shape ("we always log meetings with attendees, agenda, action items") → author the template once.
|
|
290
299
|
|
|
291
300
|
Note new templates in chat ("saved as a template at `meetings/.ok/templates/prep-notes.md` for next time") so the user sees the discipline grew.
|
|
292
301
|
|
|
302
|
+
**Keep starter content clean (MUST).** A template body is a reusable skeleton, not a meta-prompt: section headings, real frontmatter, and SHORT `{Stub}` placeholders (e.g. `# {Meeting Title}`). Do NOT bake a workflow's verbose `{...}` prompt-paragraphs (the `research` / `consolidate` shape guidance is for filling ONE doc, not for persisting into every new one), do NOT duplicate sections, and do NOT save a half-filled or in-progress doc as a template. Long "how to fill this" guidance belongs in the folder description, not in the body each new doc inherits. After saving, `exec("cat <folder>/.ok/templates/<name>.md")` and eyeball it — a template propagates to every doc made from it, so a garbled one is a recurring defect, not a one-off.
|
|
303
|
+
|
|
293
304
|
### When recurring per-doc properties emerge (MUST when a pattern emerges)
|
|
294
305
|
|
|
295
|
-
If you're writing the same frontmatter (tags, status, a title prefix) on multiple siblings, bake those starting values into a **template** (`
|
|
306
|
+
If you're writing the same frontmatter (tags, status, a title prefix) on multiple siblings, bake those starting values into a **template** (`write({ template })`) — that's the single mechanism for new-doc starting properties. Folder frontmatter does not cascade values into docs.
|
|
296
307
|
|
|
297
308
|
### Editing a folder's own description
|
|
298
309
|
|
|
299
310
|
```ts
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
311
|
+
edit({
|
|
312
|
+
folder: {
|
|
313
|
+
path: "meetings",
|
|
314
|
+
frontmatter: { title: "Meetings", description: "Meeting notes", tags: ["meeting"] },
|
|
315
|
+
},
|
|
305
316
|
})
|
|
306
317
|
```
|
|
307
318
|
|
|
308
|
-
`frontmatter` is open-shape — any key about the folder itself, exactly like a doc's frontmatter (`title` / `description` / `tags` are conventional keys the UI surfaces). It's self-only: it describes the folder and does NOT flow into child docs — put per-doc starting values in a template instead. Each
|
|
319
|
+
`frontmatter` is open-shape — any key about the folder itself, exactly like a doc's frontmatter (`title` / `description` / `tags` are conventional keys the UI surfaces). It's self-only: it describes the folder and does NOT flow into child docs — put per-doc starting values in a template instead. Each call targets a SINGLE folder by its own `path` (no globs). Use `write({ folder })` to create a new folder, `edit({ folder })` to change an existing one (merge-patch). Clear the folder's frontmatter by passing `frontmatter: {}`, or drop one key with `frontmatter: { key: null }` — the file deletes when empty and `.ok/` auto-cleans if no other tenant remains.
|
|
309
320
|
|
|
310
321
|
### Creating templates
|
|
311
322
|
|
|
312
323
|
```ts
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
324
|
+
write({
|
|
325
|
+
template: {
|
|
326
|
+
path: "meetings/prep-notes",
|
|
327
|
+
content: "# {Meeting Title}\n\n**Attendees:** \n**Date:** \n\n## Agenda\n- \n",
|
|
328
|
+
frontmatter: {
|
|
329
|
+
title: "Meeting Prep Notes", // REQUIRED — TEMPLATE_TITLE_REQUIRED if missing
|
|
330
|
+
description: "Use before a meeting.", // recommended — soft warning if absent
|
|
331
|
+
tags: ["meeting", "prep"],
|
|
332
|
+
},
|
|
322
333
|
},
|
|
323
334
|
})
|
|
324
335
|
```
|
|
325
336
|
|
|
326
|
-
**Substitution allowlist:** template bodies MAY use exactly two server-side substitutions — `{{date}}` (today's ISO-8601 date) and `{{user}}` (calling principal display name). Other `{{...}}` tokens are rejected at write time with `TEMPLATE_UNKNOWN_VARIABLE`. Plain `{shape}` placeholders (e.g., `{Meeting Title}`) are LITERAL — agents fill via subsequent `
|
|
337
|
+
**Substitution allowlist:** template bodies MAY use exactly two server-side substitutions — `{{date}}` (today's ISO-8601 date) and `{{user}}` (calling principal display name). Other `{{...}}` tokens are rejected at write time with `TEMPLATE_UNKNOWN_VARIABLE`. Plain `{shape}` placeholders (e.g., `{Meeting Title}`) are LITERAL — agents fill via subsequent `edit` calls. Delete a template via `delete({ template: { path } })` (auto-cleans empty `.ok/templates/` and `.ok/`).
|
|
327
338
|
|
|
328
339
|
### Creating a doc from a template
|
|
329
340
|
|
|
@@ -332,21 +343,22 @@ folder_config({
|
|
|
332
343
|
exec("ls -A meetings/")
|
|
333
344
|
// → templates_available: [{ name: "prep-notes", title: "Meeting Prep Notes", scope: "local" }, ...]
|
|
334
345
|
|
|
335
|
-
// Instantiate. `template` and `
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
346
|
+
// Instantiate. `template` and `content` are mutually exclusive.
|
|
347
|
+
write({
|
|
348
|
+
document: {
|
|
349
|
+
path: "meetings/2026-05-02-roadmap-sync",
|
|
350
|
+
template: "prep-notes",
|
|
351
|
+
},
|
|
340
352
|
})
|
|
341
353
|
|
|
342
|
-
// Fill the `{shape}` placeholders via follow-up
|
|
354
|
+
// Fill the `{shape}` placeholders via follow-up edit calls.
|
|
343
355
|
```
|
|
344
356
|
|
|
345
|
-
Templates resolve via leaf → root walk-up at the target's parent folder, closest-wins on filename collision. **`template` and `
|
|
357
|
+
Templates resolve via leaf → root walk-up at the target's parent folder, closest-wins on filename collision. **`template` and `content` are mutually exclusive** — passing both errors with `TEMPLATE_AND_CONTENT_BOTH_SET`. Substitution happens at instantiation time only; templates on disk show the raw `{{date}}` token.
|
|
346
358
|
|
|
347
359
|
### Editing frontmatter
|
|
348
360
|
|
|
349
|
-
`
|
|
361
|
+
`edit({ document: { path, find, replace } })` does NOT change frontmatter (body-only; frontmatter-intersecting find/replace returns HTTP 400). For metadata, use `edit({ document: { path, frontmatter: { key: value } } })` — JSON Merge Patch (RFC 7396), `null` deletes, field-level CRDT merge, atomic per-call. For a full rewrite (body + frontmatter together), call `write({ document: { path, content, frontmatter, position: "replace" } })`.
|
|
350
362
|
|
|
351
363
|
### Binary-source wrappers (`ingest`-produced)
|
|
352
364
|
|
|
@@ -398,7 +410,7 @@ The skill carries the trigger ("KB content changed this turn — go look"). The
|
|
|
398
410
|
| Read an individual doc | `Read: specs/foo/SPEC.md` | `exec("cat specs/foo/SPEC.md")` |
|
|
399
411
|
| Explore a markdown-heavy dir | `Agent(Explore): "..."` | Do `exec`-based exploration yourself |
|
|
400
412
|
| Wait for the server to tell you to open preview | Skip the session-start preview open and wait for the `attach-preview-once` hint | Open the preview browser at session start; the hint is a fallback when you didn't |
|
|
401
|
-
| Ignore the attach hint | Skip the `warning: { action: "attach-preview-once" }` hint in write-tool responses | Open the preview when the hint fires (`preview_start`, or `
|
|
413
|
+
| Ignore the attach hint | Skip the `warning: { action: "attach-preview-once" }` hint in write-tool responses | Open the preview when the hint fires (`preview_start`, or `preview_url`); otherwise do nothing |
|
|
402
414
|
| Make the Claude Code Desktop preview work | Read / diagnose / edit `.claude/launch.json` (host-managed config) | Call `preview_start("open-knowledge-ui")` and nothing else; the OK lock-collision proxy bridges any port mismatch transparently |
|
|
403
415
|
| Open a doc in the app from the Claude Code CLI | Print the `previewUrl` / `openknowledge://` string for the user to click | Run `ok open <doc>` — it opens the OK Desktop app (folders open in the browser), with browser fallback |
|
|
404
416
|
| Reference another doc | `` `[text](./page.md)` `` (backticked) or HTML `<a>` | `[text](./page.md)` (raw markdown) |
|
|
@@ -407,31 +419,31 @@ The skill carries the trigger ("KB content changed this turn — go look"). The
|
|
|
407
419
|
| Cite a web source you just fetched | inline `[source](https://...)` because YOU did the fetch (not the user) | `ingest` it — agent-initiated fetches are not exempt from the closed-loop rule |
|
|
408
420
|
| Finish a turn that changed KB content | move on without checking for a log | check for a `log.md` and follow its contract per Log discipline |
|
|
409
421
|
| Add an image | empty alt `` or generic alt `` | meaningful alt + source caption below |
|
|
410
|
-
| Catalog folder contents | create `INDEX.md` hub file | `
|
|
411
|
-
| Write a doc in an unfamiliar folder | go straight to `
|
|
412
|
-
| Land in an existing repo without orienting | go straight to `
|
|
413
|
-
| Author a doc when a matching template exists | `
|
|
414
|
-
| Change a doc's title / tags | `
|
|
415
|
-
| Repeat the same frontmatter on sibling docs | hand-set identical `tags` / `title` prefix on every new file | `
|
|
416
|
-
| Re-derive the same body skeleton repeatedly | copy-paste the structure from a sibling each time | `
|
|
417
|
-
| Scaffold a new folder for a doc category | set folder
|
|
418
|
-
| Delete a markdown doc | `Bash: rm` / `unlink` / native deletion on in-scope `.md` | `
|
|
422
|
+
| Catalog folder contents | create `INDEX.md` hub file | `edit({ folder: { path, frontmatter } })` writes `<folder>/.ok/frontmatter.yml` |
|
|
423
|
+
| Write a doc in an unfamiliar folder | go straight to `write` with hand-authored markdown | `exec("ls -A <folder>")` first — read the folder description + `templates_available` before writing |
|
|
424
|
+
| Land in an existing repo without orienting | go straight to `write` when no folder frontmatter / templates exist | invoke `workflow({ kind: 'discover' })` once for the project — extracts conventions from siblings, sets folder frontmatter + templates, activates the link graph |
|
|
425
|
+
| Author a doc when a matching template exists | `write({ document: { path, content: "..." } })` from scratch | `write({ document: { path, template } })` — templates carry the folder's frontmatter + body discipline |
|
|
426
|
+
| Change a doc's title / tags | `edit({ document: { path, find, replace } })` to swap the YAML (rejected — HTTP 400 frontmatter-intersect) | `edit({ document: { path, frontmatter } })` for metadata; `write({ document: { path, content, frontmatter, position: "replace" } })` for full rewrites |
|
|
427
|
+
| Repeat the same frontmatter on sibling docs | hand-set identical `tags` / `title` prefix on every new file | `write({ template })` once — new docs start from the template |
|
|
428
|
+
| Re-derive the same body skeleton repeatedly | copy-paste the structure from a sibling each time | `write({ template })` once, then pick from `templates_available` thereafter |
|
|
429
|
+
| Scaffold a new folder for a doc category | set folder frontmatter and stop there | pair `edit({ folder })` with `write({ template })` in the same turn |
|
|
430
|
+
| Delete a markdown doc | `Bash: rm` / `unlink` / native deletion on in-scope `.md` | `delete({ document })` — `checkpoint()` first if rollback may be needed |
|
|
419
431
|
| Fork a skill and expect no stomp | Edit installed SKILL.md | `npx skills remove` before CLI upgrade |
|
|
420
432
|
|
|
421
433
|
## Workflow tools — when to invoke them
|
|
422
434
|
|
|
423
|
-
|
|
435
|
+
One MCP tool — `workflow` — builds on the primitives above, dispatched on `kind`. **It returns *procedural guidance* (a multi-step instructional body), not fetched data.** Calling `workflow({ kind: 'ingest', source: "https://…" })` does not download and write a doc for you — it returns a multi-step plan you then execute. Same for the `research` / `consolidate` / `discover` kinds. Plan to follow the numbered steps in order; don't skip the STOP gates.
|
|
424
436
|
|
|
425
|
-
Three correspond to [Karpathy's three-layer knowledge-base pattern](https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f) (`ingest` / `research` / `consolidate`); the fourth (`discover`) operates at the project-metadata layer and is the brownfield counterpart to the greenfield `ok seed` CLI:
|
|
437
|
+
Three kinds correspond to [Karpathy's three-layer knowledge-base pattern](https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f) (`ingest` / `research` / `consolidate`); the fourth (`discover`) operates at the project-metadata layer and is the brownfield counterpart to the greenfield `ok seed` CLI:
|
|
426
438
|
|
|
427
|
-
|
|
|
439
|
+
| `kind` | Layer | When to invoke (via `workflow({ kind })`) |
|
|
428
440
|
| ------------- | ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
429
441
|
| `ingest` | Raw sources (immutable) | User shares a URL/PDF/file to preserve verbatim, **OR you fetched a URL** (`WebFetch` / `WebSearch` / equivalent) to ground a claim that's about to land in the knowledge base. The KB is closed-loop — agent-initiated fetches are not exempt. **Binary sources** (PDFs, images, audio, Office docs) are preserved verbatim, not text-scraped — the tool body documents the binary-vs-text classification, write-path STOP gates (executable, size, scheme), re-ingest semantics, and shell-less fallback. No analysis in the file itself — takeaways go back to the user in chat. |
|
|
430
442
|
| `research` | KB, provisional | User asks you to investigate, compare alternatives, or synthesize multiple sources. Produces a `status: provisional` article with a `sources:` list. Follows scan-first routing, a STOP scoping gate, 3P-external framing, and a validate checklist — the tool body enforces each step. |
|
|
431
443
|
| `consolidate` | KB, canonical | Team has actually decided after research and wants the outcome committed as source-of-truth. Starts with a STOP gate confirming the decision exists; writes a `status: canonical` article with a `supersedes:` chain. |
|
|
432
444
|
| `discover` | Project metadata | First arrival at a repo with existing content AND no folder frontmatter / templates set. Extracts conventions from siblings; activates the link graph (orphans, hubs, untextualized mentions); proposes folder frontmatter + templates + `.okignore`; per-phase user confirmation. Phases 1-4 run fs-direct; Phase 5 (link-graph activation) needs `ok start` (Phase 5 step 0 gates). Skip on empty repos (use `ok seed`). One-shot; idempotent on re-run. |
|
|
433
445
|
|
|
434
|
-
**These tools are your default move, not `
|
|
446
|
+
**These tools are your default move, not `write`.** When the work fits one of the three layers — preserving an external source, investigating/synthesizing, committing a decided outcome — invoke the corresponding tool instead of going straight to `write` / `edit`. The tool bodies enforce framing (sources, status, supersedes chains) that hand-written articles routinely miss. `write` is correct for everything that does **not** fit the three layers (specs, runbooks, scratch notes, project pages); for the three that do, lead with the tool. This is doubly true in projects that ran `ok seed` — a doc landing in `external-sources/` / `research/` / `articles/` should have come out of `ingest` / `research` / `consolidate`.
|
|
435
447
|
|
|
436
448
|
Typical day-2 flow: user shares a URL → `ingest` (preserve) → user asks "now research this" → `research` (provisional article + `ingest`s more sources as needed) → decision lands → `consolidate` (canonical article, supersedes the research).
|
|
437
449
|
|
|
@@ -439,18 +451,18 @@ Typical day-2 flow: user shares a URL → `ingest` (preserve) → user asks "now
|
|
|
439
451
|
|
|
440
452
|
**Do not chain silently.** After `ingest`, ask the user whether to proceed to `research`. After `research`, let the user decide whether the findings are ready to `consolidate`. Each tool completes on its own terms — the user drives the transitions.
|
|
441
453
|
|
|
442
|
-
**Repeat invocations.**
|
|
454
|
+
**Repeat invocations.** The `workflow` tool returns its full instructional body on every call, including 2nd / 3rd / Nth invocation in the same session. If you've already received a tool's body earlier this session, you can skim the repeat for changes (the body can evolve across server versions) but you don't need to re-internalize it — proceed to the next step with the new arguments.
|
|
443
455
|
|
|
444
|
-
**Project scaffolding — two paths.** **Empty repo:** run `ok seed` once from a terminal (scaffolds Karpathy three-layer + seeds `log.md` + registers folder defaults). **Existing content:** invoke `discover` (
|
|
456
|
+
**Project scaffolding — two paths.** **Empty repo:** run `ok seed` once from a terminal (scaffolds Karpathy three-layer + seeds `log.md` + registers folder defaults). **Existing content:** invoke `workflow({ kind: 'discover' })` (table above — extracts conventions from siblings, sets folder frontmatter + templates, curates `.okignore`, activates link graph; per-phase confirmation gates). Neither is required; the four workflow kinds work against any folder structure. Only mention each when explicitly relevant.
|
|
445
457
|
|
|
446
458
|
## Server lifecycle
|
|
447
459
|
|
|
448
|
-
If `
|
|
460
|
+
If `write` or `edit` returns a "Hocuspocus server is not running" error, start it with `ok start` (via Bash) and retry. Never fall back to native `Edit` / `Write` for in-scope markdown — always route through the MCP write tools so edits go through the CRDT with proper attribution.
|
|
449
461
|
|
|
450
462
|
## Scope recap
|
|
451
463
|
|
|
452
|
-
Open Knowledge looks for documents under the resolved `content.dir` (discoverable at runtime via `
|
|
464
|
+
Open Knowledge looks for documents under the resolved `content.dir` (discoverable at runtime via `config({ key: 'content.dir' })`). `.gitignore` and `.okignore` (at the project root and at any folder depth) define exclusions. A folder's own metadata + templates live in nested `<folder>/.ok/frontmatter.yml` + `<folder>/.ok/templates/` — NOT in `.ok/config.yml`.
|
|
453
465
|
|
|
454
466
|
Default mental model (no jargon): **every `.md` and `.mdx` under `content.dir`** not excluded by `.gitignore` or `.okignore` is an Open Knowledge document — including under `specs/`, `reports/`, `docs/`, etc. Read `.okignore` (and any nested `.okignore` files) once per turn to know what's excluded.
|
|
455
467
|
|
|
456
|
-
**First session in this project?** If substantial folders have no frontmatter of their own and no `templates_available`, the project isn't onboarded — invoke `
|
|
468
|
+
**First session in this project?** If substantial folders have no frontmatter of their own and no `templates_available`, the project isn't onboarded — invoke `workflow({ kind: 'discover' })` before writing.
|