@inkeep/open-knowledge 0.20.0-beta.0 → 0.20.0-beta.10
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 +2 -2
- package/dist/assets/skills/packs/codebase-wiki/SKILL.md +1 -1
- package/dist/assets/skills/packs/entity-vault/SKILL.md +6 -1
- package/dist/assets/skills/project/SKILL.md +69 -455
- package/dist/assets/skills/project/references/anti-patterns.md +33 -0
- package/dist/assets/skills/project/references/cadence-and-logs.md +17 -0
- package/dist/assets/skills/project/references/components-and-visuals.md +53 -0
- package/dist/assets/skills/project/references/conflict-resolution.md +28 -0
- package/dist/assets/skills/project/references/corpus-qa.md +14 -0
- package/dist/assets/skills/project/references/doc-editing.md +11 -0
- package/dist/assets/skills/project/references/folder-model.md +42 -0
- package/dist/assets/skills/project/references/ingest-and-sources.md +24 -0
- package/dist/assets/skills/project/references/linking.md +16 -0
- package/dist/assets/skills/project/references/media-and-assets.md +14 -0
- package/dist/assets/skills/project/references/preview.md +51 -0
- package/dist/assets/skills/project/references/setup.md +2 -2
- package/dist/assets/skills/project/references/template-authoring.md +65 -0
- package/dist/assets/skills/project/references/workflow-guides.md +30 -0
- package/dist/assets/skills/project/references/writing.md +9 -0
- package/dist/assets/skills/write-skill/SKILL.md +16 -5
- package/dist/cli.mjs +54 -54
- package/dist/constants-CrTxXw1w.mjs +2 -0
- package/dist/{dist-CZWb6kyF.mjs → dist-CQqrcHvR.mjs} +31 -27
- package/dist/{dist-DsbAPFC5.mjs → dist-DTGzP9at.mjs} +1 -1
- package/dist/{gh-detect-DsccWGZs.mjs → gh-detect-BDIdc7oG.mjs} +2 -2
- package/dist/index.mjs +1 -1
- package/dist/init-BSCPsyJd.mjs +1 -0
- package/dist/{init-DeupkkJE.mjs → init-C-8HqD7u.mjs} +6 -6
- package/dist/{loader-BqOvMwUA.mjs → loader-Cj9_FOBM.mjs} +3 -3
- package/dist/loader-NjsXkmf7.mjs +1 -0
- package/dist/preview-BXlCCBCC.mjs +1 -0
- package/dist/{preview-D1nIjzUr.mjs → preview-CUcmvmH1.mjs} +2 -2
- package/dist/public/assets/{ActivityModeContent-BS_Y62XF.js → ActivityModeContent-DGw5iUid.js} +1 -1
- package/dist/public/assets/{DocumentContext-BzU_wuZ1.js → DocumentContext-CmO1rPM6.js} +16 -16
- package/dist/public/assets/{GraphPanel-EZGWljSP.js → GraphPanel-BVt0SfrM.js} +1 -1
- package/dist/public/assets/{ManagedArtifactProperties-D2EjV-Hv.js → ManagedArtifactProperties-Cr8eMUCF.js} +1 -1
- package/dist/public/assets/{PropertyPanel-CctJB0ZN.js → PropertyPanel-ze_4e3n_.js} +1 -1
- package/dist/public/assets/{SettingsDialogBody-DzkJTs5S.js → SettingsDialogBody-hP9Vzn5G.js} +2 -2
- package/dist/public/assets/{SkillEditorActions-CnqozQbp.js → SkillEditorActions-Y_e1zC_b.js} +1 -1
- package/dist/public/assets/{SourceEditor-Mgjjq3HZ.js → SourceEditor-4T4loRia.js} +2 -2
- package/dist/public/assets/{TerminalPanel-DiJi11zw.js → TerminalPanel-C9rmhBG2.js} +2 -2
- package/dist/public/assets/{config-validation-events-8rtpnIAB.js → config-validation-events-CA8jFSci.js} +4 -4
- package/dist/public/assets/{dist-C0uDGnS5.js → dist-Dst_hB5H.js} +102 -102
- package/dist/public/assets/{documents-events-BTsmrZJT.js → documents-events-DMS5a4By.js} +1 -1
- package/dist/public/assets/index-BHFFl2Q_.css +1 -0
- package/dist/public/assets/index-DNKaMY7F.js +2004 -0
- package/dist/public/assets/{keyboard-shortcuts-BzwnpNd_.js → keyboard-shortcuts-DGu1nhPQ.js} +1 -1
- package/dist/public/assets/{open-managed-artifact-tab-VuHt1k6e.js → open-managed-artifact-tab-Bvt3X7yV.js} +1 -1
- package/dist/public/assets/{prop-types-D6SQBoDq.js → prop-types-Cf7ylXsu.js} +1 -1
- package/dist/public/assets/{selection-context-Rr9JEzTX.js → selection-context-kDVHDAV2.js} +1 -1
- package/dist/public/assets/{skill-actions-SUqrJBew.js → skill-actions-CQ-n4sOB.js} +1 -1
- package/dist/public/assets/{skills-api-F2HCxVlt.js → skills-api-q4V5pwth.js} +1 -1
- package/dist/public/assets/{target-navigation-intent-BrunJmhI.js → target-navigation-intent-CCRb_XMp.js} +1 -1
- package/dist/public/assets/{typing-burst-detector-BmNkqfX8.js → typing-burst-detector-zGrvqSf8.js} +2 -2
- package/dist/public/index.html +15 -15
- package/dist/{repair-launch-json-zIhZWAzC.mjs → repair-launch-json-Bdz75Fp8.mjs} +2 -2
- package/dist/{repair-mcp-configs-Bfwl_HpO.mjs → repair-mcp-configs-Cc8mxsS2.mjs} +2 -2
- package/dist/{repair-skills-JPqTnxCJ.mjs → repair-skills-BaI9FXaD.mjs} +2 -2
- package/dist/repair-skills-DO_ayx3E.mjs +1 -0
- package/dist/server-lock-8Lv6A-Xa-iDqi_bbE.mjs +1 -0
- package/dist/{server-lock-CN2YHwpP-CemdeDui.mjs → server-lock-CN2YHwpP-COM23IWb.mjs} +42 -42
- package/dist/{src-TPlxmg1w.mjs → src-C2V2v9_F.mjs} +2 -2
- package/dist/start-CDfxD4Ac.mjs +1 -0
- package/dist/{start-CGYA17Ml.mjs → start-DtoTEBi4.mjs} +2 -2
- package/dist/{write-project-skill-CKnzNtFY.mjs → write-project-skill-CsHCvxax.mjs} +3 -3
- package/package.json +1 -1
- package/dist/constants-Ta2NnZpA.mjs +0 -2
- package/dist/init-Dg6Zw1fN.mjs +0 -1
- package/dist/loader-DrdZ_qLV.mjs +0 -1
- package/dist/preview-CzOvwx8Y.mjs +0 -1
- package/dist/public/assets/index-i9A7tPDf.js +0 -2004
- package/dist/public/assets/index-xDiw2wLq.css +0 -1
- package/dist/repair-skills-C7a7M7AA.mjs +0 -1
- package/dist/server-lock-8Lv6A-Xa-C_9VS9L6.mjs +0 -1
- package/dist/start-DH4IdYkc.mjs +0 -1
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Anti-patterns — full table
|
|
2
|
+
|
|
3
|
+
(Core inlines the top offenders. This is the complete reference table.)
|
|
4
|
+
|
|
5
|
+
| Task | Don't | Do |
|
|
6
|
+
| ----------------------------------------------- | ---------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- |
|
|
7
|
+
| List a markdown-heavy dir | `Bash: ls specs/` | `exec("ls -A specs/")` |
|
|
8
|
+
| Find all SPEC.md files | `Glob: **/SPEC.md` | `exec("find specs -name SPEC.md")` |
|
|
9
|
+
| Find the most relevant page for a query | `Grep: "pattern" *.md` then read three files | `search({ query: "pattern" })` (ranked: title + body BM25 + recency) |
|
|
10
|
+
| Find every literal occurrence of a phrase | `Grep: "pattern" *.md` | `exec("grep -rn pattern <dir>")` (literal, grouped by file, with frontmatter) |
|
|
11
|
+
| Read an individual doc | `Read: specs/foo/SPEC.md` | `exec("cat specs/foo/SPEC.md")` |
|
|
12
|
+
| Explore a markdown-heavy dir | `Agent(Explore): "..."` | Do `exec`-based exploration yourself |
|
|
13
|
+
| Answer a direct business question from the corpus | answer in chat and move on (it evaporates), OR save every answer as a new doc | answer with citations; *offer* to persist only when durable + multi-doc + not already covered (`references/corpus-qa.md`) |
|
|
14
|
+
| 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 |
|
|
15
|
+
| 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 |
|
|
16
|
+
| 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 |
|
|
17
|
+
| 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 |
|
|
18
|
+
| Reference another doc | `` `[text](./page.md)` `` (backticked) or HTML `<a>` | `[text](./page.md)` (raw markdown) |
|
|
19
|
+
| Embed an image | `<img src="...">` (HTML), a `localhost:<port>` / `preview_url` server URL, or hot-linked external URL | Fetch + save locally + doc-relative `` |
|
|
20
|
+
| Write a factual claim in a KB doc | plausible prose without citation, OR inline `[source](https://URL)` | `ingest` the source first, then cite the local path per Grounding |
|
|
21
|
+
| 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 |
|
|
22
|
+
| 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 |
|
|
23
|
+
| Add an image | empty alt `` or generic alt `` | meaningful alt + source caption below |
|
|
24
|
+
| Catalog folder contents | create `INDEX.md` hub file | `edit({ folder: { path, frontmatter } })` writes `<folder>/.ok/frontmatter.yml` |
|
|
25
|
+
| 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 |
|
|
26
|
+
| 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 |
|
|
27
|
+
| 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 |
|
|
28
|
+
| 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 |
|
|
29
|
+
| 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 |
|
|
30
|
+
| 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 |
|
|
31
|
+
| Scaffold a new folder for a doc category | set folder frontmatter and stop there | pair `edit({ folder })` with `write({ template })` in the same turn |
|
|
32
|
+
| Delete a markdown doc | `Bash: rm` / `unlink` / native deletion on in-scope `.md` | `delete({ document })` — `checkpoint()` first if rollback may be needed |
|
|
33
|
+
| Fork a skill and expect no stomp | Edit installed SKILL.md | `npx skills remove` before CLI upgrade |
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Cadence + log discipline
|
|
2
|
+
|
|
3
|
+
## Cadence
|
|
4
|
+
|
|
5
|
+
When you make a multi-step change (batch of new docs, folder restructure), pause between steps to let the browser preview catch up. The CRDT edit streams live; the preview follows your edit cadence. Don't batch 10 writes in a row — interleave the writes so the user watching the browser sees the narrative progress.
|
|
6
|
+
|
|
7
|
+
This does not conflict with *Persist incrementally* (§Writing): a checkpoint-write per section/source is naturally spaced by the work that produces that unit (read a source → write its findings → read the next), so those writes *are* the interleaved cadence. The anti-pattern is firing many writes back-to-back with no intervening work — not persisting completed work as you go. When in tension, durability wins: never hold finished work back from the KB to smooth cadence.
|
|
8
|
+
|
|
9
|
+
This is primarily a human-watchability concern — the user watches edits land in the preview; interleaved cadence makes the narrative legible. When the batch is done, navigate the preview to the primary deliverable (see "End a turn on the deliverable" in `references/preview.md`).
|
|
10
|
+
|
|
11
|
+
**Hub docs.** Don't *create* `INDEX.md` / `README.md` hub files solely to catalog children — `exec("ls -A <folder>")` returns the same view live, with per-file frontmatter + backlink counts. But if a hub doc *already exists* from prior work, keep it updated as children change — interleave: write child → update hub → write next child, rather than batching five child edits and a single trailing hub update.
|
|
12
|
+
|
|
13
|
+
## Log discipline — check for a project log when KB content changes
|
|
14
|
+
|
|
15
|
+
Some projects keep an append-only project log to make agent activity auditable. **After any turn that creates, edits, or restructures docs in the knowledge base, check for a project log:** look for a `log.md` at the project root (or at the seed `rootDir` if `ok seed --root <dir>` was used). If one exists, follow whatever its frontmatter `description:` and in-file comment say — they carry the project-specific contract (entry shape, cadence, categories). Different projects log differently — some treat the log as a wiki audit trail, others as an LLM-brain history, others as a spec changelog. If no `log.md` exists, no log discipline applies; don't fabricate one.
|
|
16
|
+
|
|
17
|
+
The skill carries the trigger ("KB content changed this turn — go look"). The file owns the policy.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Components + visuals — markdown-native forms and `html preview` embeds
|
|
2
|
+
|
|
3
|
+
## Components — write the markdown-native form, not JSX
|
|
4
|
+
|
|
5
|
+
OK auto-promotes markdown-native syntax into themed canonical components at parse time. **Write the markdown-native form — don't reach for JSX when one exists.** The promoted component is themed, accessible, and part of the content graph; hand-rolled JSX is none of those, and it fights the model's markdown prior instead of using it.
|
|
6
|
+
|
|
7
|
+
| Want | Write this (markdown-native) | Promotes to |
|
|
8
|
+
| --- | --- | --- |
|
|
9
|
+
| Callout / admonition | `> [!NOTE]` + body — 15 types (NOTE, TIP, IMPORTANT, WARNING, CAUTION, …); append `+` / `-` (`> [!NOTE]+`) to make it foldable | themed Callout |
|
|
10
|
+
| Collapsible section | `<details><summary>Title</summary>` … `</details>` | themed Accordion |
|
|
11
|
+
| Diagram | a ` ```mermaid ` fenced block (flowchart, sequence, class, state, ER, gantt, pie) — label-text pitfalls + escapes: `palette({ components: ["Mermaid"] })`; parse failures come back as `warnings` entries on write/edit | Mermaid diagram |
|
|
12
|
+
| Math | `$x$` inline, `$$…$$` block | KaTeX Math |
|
|
13
|
+
| Inline a doc or asset | `![[file]]` | wiki embed |
|
|
14
|
+
|
|
15
|
+
`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.
|
|
16
|
+
|
|
17
|
+
**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.
|
|
18
|
+
|
|
19
|
+
**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.
|
|
20
|
+
|
|
21
|
+
## `html preview` — themed interactive embeds
|
|
22
|
+
|
|
23
|
+
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.
|
|
24
|
+
|
|
25
|
+
**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.
|
|
26
|
+
|
|
27
|
+
**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.
|
|
28
|
+
|
|
29
|
+
````
|
|
30
|
+
```html preview
|
|
31
|
+
<div style="font-family:system-ui;padding:20px;color:var(--foreground)">
|
|
32
|
+
<h3 style="margin:0 0 10px">Themed embed</h3>
|
|
33
|
+
<div style="display:flex;gap:8px">
|
|
34
|
+
<div style="flex:1;height:48px;background:var(--chart-1);border-radius:var(--radius)"></div>
|
|
35
|
+
<div style="flex:1;height:48px;background:var(--chart-2);border-radius:var(--radius)"></div>
|
|
36
|
+
<div style="flex:1;height:48px;background:var(--chart-3);border-radius:var(--radius)"></div>
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
39
|
+
```
|
|
40
|
+
````
|
|
41
|
+
|
|
42
|
+
Done wrong, that same embed is `body{background:#fff;color:#1a1a1a}` with a `background:#2563eb` bar — a white box with a hardcoded blue, blind to the reader's theme.
|
|
43
|
+
|
|
44
|
+
**Charts.** A pure-CSS or inline-SVG chart wired to `var(--chart-*)` re-skins on a theme toggle for free — prefer it. A JS charting library (Chart.js, D3) works too, but a themed `body` does NOT theme the colors you pass the library in JS — read the token at runtime instead of hardcoding:
|
|
45
|
+
|
|
46
|
+
```js
|
|
47
|
+
const c1 = getComputedStyle(document.documentElement).getPropertyValue('--chart-1').trim();
|
|
48
|
+
// → pass c1 to Chart.js / D3 as the series color
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
**Boundary.** Reach for a canonical (via its markdown-native form) when one matches the semantic need — it is themed and integrated. Reach for ` ```html preview ` for interactive or bespoke content no canonical covers. ` ```<lang> ` fences for other languages are plain syntax-highlighted code, no preview.
|
|
52
|
+
|
|
53
|
+
**External resources load directly.** The preview iframe has open network access — an embed can load external stylesheets, `fetch` live data, pull map tiles / remote images, use web fonts, or embed third-party iframes over `https:`. A Leaflet map, a live-`fetch` chart, or a Google-Font embed renders with no extra setup. The iframe is a sandboxed null-origin frame, so an embed can reach the network but can never read the knowledge base, cookies, or auth. (`'unsafe-eval'` is not granted — Chart.js / Leaflet / Plotly don't need it; a library that compiles expression strings at runtime won't run.)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Conflict-aware writes
|
|
2
|
+
|
|
3
|
+
Projects with GitHub sync enabled may carry docs in a merge-conflict state. The MCP server refuses every mutating call against such a doc with a structured RFC 9457 response:
|
|
4
|
+
|
|
5
|
+
```json
|
|
6
|
+
{
|
|
7
|
+
"type": "urn:ok:error:doc-in-conflict",
|
|
8
|
+
"title": "Document is in conflict.",
|
|
9
|
+
"status": 409,
|
|
10
|
+
"detail": "The document is in a merge-conflict state. Call conflicts({ kind: 'content' }) + resolve_conflict before retrying.",
|
|
11
|
+
"file": "notes/sso.md",
|
|
12
|
+
"resolutionOptions": ["mine", "theirs", "content", "delete"]
|
|
13
|
+
}
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
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.
|
|
17
|
+
|
|
18
|
+
**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.
|
|
19
|
+
|
|
20
|
+
**Resolution flow.** Three tools compose:
|
|
21
|
+
|
|
22
|
+
1. `conflicts({ kind: 'list' })` → enumerate every doc currently tracked in conflict.
|
|
23
|
+
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).
|
|
24
|
+
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).
|
|
25
|
+
|
|
26
|
+
`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.
|
|
27
|
+
|
|
28
|
+
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.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Answering direct questions from the corpus
|
|
2
|
+
|
|
3
|
+
A direct question you can answer from existing documents — "which customers have non-standard indemnity?", "can we use Alloy's logo?", "what did we decide about X?" — does **not** need the words "research" or "report" to route here. Retrieve with `search` / `exec`, read the relevant docs, and **answer in chat with inline citations to the source docs you used**. That is the complete, correct default — most questions end here. This is NOT `workflow({ kind: 'research' })`: research gathers and synthesizes *external* sources behind a scoping gate; a corpus question just reads what the knowledge base already holds. (Inside an active `workflow({ kind: 'research' })` session, research's own "file valuable Q&A back" step governs how answers are persisted — not this section.)
|
|
4
|
+
|
|
5
|
+
**Offer to persist the answer only when it is durable knowledge the KB is currently missing** — when ALL of these hold:
|
|
6
|
+
|
|
7
|
+
- it **synthesizes across multiple docs** or surfaces a non-obvious fact a reader couldn't get from a single doc in one read — two docs that independently state the *same* fact are NOT synthesis; synthesis means combining information no single source holds in isolation;
|
|
8
|
+
- it's **reusable** — likely to be asked again, or it records a decision / reference others will need;
|
|
9
|
+
- **no existing doc already answers it** — scan first (`search`, `exec("grep …")`); if one does, point the user to it instead of writing a near-duplicate;
|
|
10
|
+
- the answer is **sourced** per §Grounding, not speculation.
|
|
11
|
+
|
|
12
|
+
When all hold, *offer* — don't write yet: "This pulls together [N docs] — want me to save it as `<slug>.md` under `<folder>` so it's findable next time?" On a yes, `write` it with frontmatter + inline citations to the source docs (§Grounding, §Linking). **Never auto-create the page.** A single-doc lookup, a navigational question, or anything you'd hesitate to call durable does NOT warrant an offer — answer in chat and stop; don't even prompt to save it. When in doubt, stay in chat: a missing page costs one re-query; a junk page pollutes the corpus permanently.
|
|
13
|
+
|
|
14
|
+
**Headless / no user to ask** (autonomous run): still produce the answer — surface it with inline citations in the tool / run output as you would in chat, so the run log is the record. Default to NOT persisting unless the four criteria are unambiguously met; never persist on a maybe.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Editing frontmatter vs body
|
|
2
|
+
|
|
3
|
+
`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" } })`.
|
|
4
|
+
|
|
5
|
+
**Stale-session symptom.** 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 `OpenKnowledge MCP unavailable:` and report the inconsistency. Don't loop on retries — the symptom is structural, not transient.
|
|
6
|
+
|
|
7
|
+
**Delete / move.** 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.
|
|
8
|
+
|
|
9
|
+
**MDX.** To author an MDX doc (the KB renders MDX/JSX components), set `extension: ".mdx"` on the create: `write({ document: { path: "guides/widget", content, extension: ".mdx", position: "replace" } })` lands `guides/widget.mdx`. A `.mdx` suffix typed into `path` works too (the `extension` field wins if you pass both); omit both and it lands `.md`. An existing doc keeps its on-disk extension regardless — changing it in place isn't available via the MCP today.
|
|
10
|
+
|
|
11
|
+
**Advisory warnings on writes.** `write` and `edit` responses may include `structuredContent.document.warnings` (batch: per-doc `structuredContent.documents[].warnings`) — advisory entries discriminated by `kind`, each also summarized as a `⚠` line in the response text. The write always landed; the entries tell you what to do next. Write-integrity kinds mean re-read the doc (`exec("cat <path>")`) before continuing: `content-divergence` (`{ kind, intendedBytes, actualBytes, byteDelta, hint }` — the converged Y.Text doesn't match what the payload composed to: concurrent peer residue, or — rare — a primitive regression) and `disk-edit-reconciled` (an out-of-band disk edit was folded in before your write landed on top). The renderability kind `mermaid-parse-error` (`{ kind, fenceIndex, fenceFirstLine, message, line? }`) means that mermaid fence will not render — fix the fence and re-edit. (A deprecated single-valued `warning` field on the HTTP body mirrors the highest-precedence integrity entry for older consumers.) Distinct from the preview-attach `warning` field (`action: "attach-preview-once" | "start-ui"`), which stays at the top level — separate keys, can coexist.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Folder model — frontmatter + templates structure
|
|
2
|
+
|
|
3
|
+
(Core carries the MUST gates: read the folder before writing, use a template when one fits, bake recurring properties into a template. This file is the structural model.)
|
|
4
|
+
|
|
5
|
+
Every `.md` / `.mdx` file needs YAML frontmatter — `title` + `description` required, `tags` recommended:
|
|
6
|
+
|
|
7
|
+
```yaml
|
|
8
|
+
---
|
|
9
|
+
title: Article Title
|
|
10
|
+
description: Brief summary
|
|
11
|
+
tags: [relevant, tags]
|
|
12
|
+
---
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Two folder mechanisms, both opt-in and nested: **folder frontmatter** in `<folder>/.ok/frontmatter.yml` (the folder's own properties — open-shape like a doc's, with `title` / `description` / `tags` as conventional keys the UI surfaces; describes the folder, self-only, does NOT flow into child docs) and **templates** in `<folder>/.ok/templates/` (the single mechanism for what new docs in a folder start with). **Most folders have NO `.ok/`** — sparse, lazy-create, auto-clean. A folder gets one only when it carries its own frontmatter or a template.
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
content-root/
|
|
19
|
+
├── .ok/ ← project root .ok/ (config.yml, cache)
|
|
20
|
+
├── meetings/
|
|
21
|
+
│ ├── .ok/
|
|
22
|
+
│ │ ├── frontmatter.yml ← this folder's own title/description/tags
|
|
23
|
+
│ │ └── templates/
|
|
24
|
+
│ │ └── prep-notes.md ← what new meeting docs start with
|
|
25
|
+
│ └── 2026-05-01.md
|
|
26
|
+
└── research/ ← no .ok/
|
|
27
|
+
└── auth-providers.md
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
A doc's frontmatter is exactly its own on-disk YAML — folder frontmatter never overlays values onto it. Give new docs starting properties with a template, not with folder frontmatter.
|
|
31
|
+
|
|
32
|
+
## Read the folder before writing (MUST) — full checklist
|
|
33
|
+
|
|
34
|
+
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.
|
|
35
|
+
|
|
36
|
+
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' })`. Skip on subsequent writes once confirmed.
|
|
37
|
+
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.)
|
|
38
|
+
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).
|
|
39
|
+
3. **Read recent siblings** — new docs should match the shape of existing ones (filename, frontmatter, body structure).
|
|
40
|
+
4. **Confirm content scope** — `content.dir` (`.ok/config.yml`) defines the root. `.gitignore` / `.okignore` (nested at any depth) define exclusions.
|
|
41
|
+
|
|
42
|
+
**Once per folder per session** — the checklist doesn't repeat unless you (or the user) changed a folder rule or template since.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Binary-source wrappers (`ingest`-produced)
|
|
2
|
+
|
|
3
|
+
Docs that wrap a co-located binary file under `external-sources/` carry extra frontmatter so the wrapper-binary pair is fully described:
|
|
4
|
+
|
|
5
|
+
```yaml
|
|
6
|
+
---
|
|
7
|
+
title: ...
|
|
8
|
+
description: ...
|
|
9
|
+
source_url: https://example.com/file.pdf
|
|
10
|
+
source_path: ./<slug>.<ext> # relative to this wrapper
|
|
11
|
+
media_type: application/pdf
|
|
12
|
+
bytes: 1234567
|
|
13
|
+
sha256: <64-char hex> # of the embedded binary
|
|
14
|
+
date_fetched: YYYY-MM-DD
|
|
15
|
+
preservation: binary # OR: text-only / text-extracted
|
|
16
|
+
supersedes: # OPTIONAL — dated-sibling re-ingest
|
|
17
|
+
- <prior-slug>.md
|
|
18
|
+
tags: [source, immutable, layer-ingest, binary]
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
![[<slug>.<ext>]]
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Body is just the wiki-embed. PDFs/opaque attachments render as a click-dispatching File row; `<Pdf src="./<slug>.pdf" />` is the opt-in inline viewer. See `ingest`'s tool body for full re-ingest / size / executable rules.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Linking — mechanics
|
|
2
|
+
|
|
3
|
+
(Core carries the MUST: link noun-phrases with standard markdown links, every link resolves, read `brokenLinks` on each write/edit. This file carries the full rule set.)
|
|
4
|
+
|
|
5
|
+
- **Every noun-phrase that names another document should be linked** using standard markdown link syntax. Two forms are valid: **relative** (`[text](./sibling.md)`, `[text](../folder/doc.md)`) — the recommended default, native in GitHub / Obsidian / VS Code and what published sites expect — and **root-absolute** (`[text](/folder/doc.md)`, leading slash = content root), handy for a cross-folder link.
|
|
6
|
+
- **Never glue `./` onto a content-root path.** `./wiki/modules/tasks`, written from a doc already inside `wiki/`, resolves to the doubled garbage `wiki/wiki/modules/tasks` — correct resolution of a malformed path, i.e. a silently broken link. Pick ONE form: a relative path from your doc (`./tasks.md`, `../modules/tasks.md`), or a `/`-rooted absolute path (`/wiki/modules/tasks.md`). The `./` prefix and a content-root path never combine.
|
|
7
|
+
- **External web sources are NOT inline body links.** Per the Grounding rule, web URLs live in the `source_url:` frontmatter of an ingested doc under `external-sources/` (or the project's equivalent raw-sources folder); the body cites the local path: `[source name](./external-sources/source-slug.md)`. A raw `[source](https://...)` inline in the body is a TODO, not a citation — see Grounding for the closed-loop contract.
|
|
8
|
+
- **Internal cross-refs between OK docs** → `[text](./other-doc.md)` — link liberally to aid navigation.
|
|
9
|
+
- **Every link must resolve to a doc that exists by the time you're done.** Within a single multi-doc authoring pass, linking a page you'll create later in the same pass is fine — `brokenLinks` reports it as `no-such-doc` until the target lands, an expected transient forward-reference rather than a wrong-path error. What's not acceptable is *leaving* a dead link behind: create the target 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.
|
|
10
|
+
- **Never wrap a link in backticks.** `` `[text](./foo.md)` `` is a bug — the backticks make it render as literal code rather than a link.
|
|
11
|
+
- **Never use HTML anchors** (`<a href="...">`). Markdown link syntax only.
|
|
12
|
+
- **`brokenLinks` on the write/edit response is your primary check — read it before walking away.** Every `write`/`edit` returns `brokenLinks` in the SAME payload: `[]` means every outbound link resolves; a populated list names each broken `href` with its `resolvedTo` + `reason` (`no-such-doc` = resolved to a doc that doesn't exist; `no-such-file` = resolved to a linked asset / source file that isn't on disk at that path; `unresolvable` = the path escapes the content root, usually one `../` too many). This validates **every** local link, not just doc links: a wrong-depth `[src](../../foo.py)` to a source file is caught too. It is report-only — the write landed regardless; fix or remove every one in a follow-up `edit`.
|
|
13
|
+
- **`links({ kind: "dead" })` is the authoritative end-state audit.** For a corpus-wide sweep across many docs: `links({ kind: "dead", sourceDocuments: ["your/doc"] })`. Companion `links` kinds: `backlinks` (incoming), `forward` (outgoing), `orphans` (no incoming), `hubs` (high-incoming), `suggest` (untextualized mentions worth linking).
|
|
14
|
+
- **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.
|
|
15
|
+
|
|
16
|
+
**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.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Media — images and attachments
|
|
2
|
+
|
|
3
|
+
- **Markdown syntax only:** ``. Do NOT emit HTML `<img>` tags — they don't participate in OK's content graph and don't render consistently across Fumadocs / preview surfaces. Paths resolve relative to the doc.
|
|
4
|
+
- **Always a doc-relative path — never a server URL.** Reference an asset by its path relative to the doc (`./image.png`, `../assets/foo.png`), never an absolute `http://localhost:<port>/…`, `127.0.0.1`, or other server URL. `preview_url`'s `url` navigates the *preview* — it is NOT an asset path; never paste it (or any `localhost` base) into an `![]()`. An asset already in the tree is the same rule: find its path with `exec("ls -A <dir>")` and write the relative link. (Upload via `write({ asset })` hands you the exact relative `` to copy.)
|
|
5
|
+
- **Save locally, don't hot-link.** Hot-linked external image URLs rot when the source disappears. Fetch (`WebFetch` / `curl`), save to a local path, reference via relative markdown link, cite the source below.
|
|
6
|
+
- **Placement model.** Free-form image embeds → co-located alongside the referencing doc (sha256 same-directory dedup). Raw sources via `ingest` → `external-sources/<slug>.<ext>` + `external-sources/<slug>.md` (the wrapper-binary pair). Check via `exec("ls -A")` if the project uses a different convention.
|
|
7
|
+
- **Cannot fetch** (no network, paywall) → don't invent a local path. Omit, or mark inline `(TODO: image needs sourcing from <URL>)`.
|
|
8
|
+
- **Meaningful alt text required** — describes WHAT the image shows, not what it is. `![]()` / `![image]()` / `![filename.png]()` all fail. OK indexes alt text — it's both accessibility AND searchability.
|
|
9
|
+
- **Cite web image sources** below the image (Grounding rule):
|
|
10
|
+
```markdown
|
|
11
|
+

|
|
12
|
+
*Source: [Avatar Wiki — Aang](https://avatar.fandom.com/wiki/Aang#Avatar_State)*
|
|
13
|
+
```
|
|
14
|
+
Original diagrams/screenshots may caption `*Original*` or omit. Unattributed web images are equivalent to unsourced factual claims.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Preview — full multi-host contract
|
|
2
|
+
|
|
3
|
+
## Contents
|
|
4
|
+
- Re-navigation and end-of-turn discipline
|
|
5
|
+
- `previewUrl` is a route, not a URL
|
|
6
|
+
- The three first-class apps (Claude Code Desktop, Cursor, Codex desktop)
|
|
7
|
+
- Claude Code CLI track (no browser)
|
|
8
|
+
- Four attach signals
|
|
9
|
+
- `previewUrl: null` semantics, server lifecycle, read-only mirror, no-screenshots
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
The user watches your edits land in a live browser preview. Open it once at session start, then keep working. Re-navigate only when the user asks to open a different doc, or to land them on a finished deliverable (see below) — not to re-check your own edits.
|
|
14
|
+
|
|
15
|
+
**End a turn on the deliverable, not your scratch space.** Keep the preview steady *during* a multi-doc task — don't yank it around to re-check your own edits. But when a turn created or substantially changed user-facing docs, navigate the preview to the primary deliverable before you hand back: the hub / overview / index page when you created several docs, or the changed doc when you changed one. Don't step the user through every supporting source card — the user is watching, so leave them on the result.
|
|
16
|
+
|
|
17
|
+
**`previewUrl` is a route, not a URL to open.** Every read response (per-doc, on `exec` / `search` / `links` rows) and every write response carries a `previewUrl` — a route fragment like `/#/specs/foo/SPEC`, with **no scheme, host, or port**. It identifies *which doc* to preview, not a URL to hand a browser by itself. Never construct or guess preview URLs.
|
|
18
|
+
|
|
19
|
+
**OK ships first-class preview support for three apps — Claude Code Desktop, Cursor, and the Codex desktop app — plus the Claude Code CLI on a separate track (below). Make the preview seamless in each.** Match on the tool you actually have (capability, not host name): if a tool can navigate to a URL, it counts as an in-app browser. The three apps map to:
|
|
20
|
+
|
|
21
|
+
- **Claude Code Desktop — 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`.
|
|
22
|
+
- **Cursor / Codex desktop — no `preview_*` tool, but you have an in-app / built-in browser tool** → 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. Drive the tool your host gives you:
|
|
23
|
+
- **Cursor** → its built-in **Browser** tool, the **`Navigate`** action (`browser_navigate`, via Cursor's own `cursor-ide-browser`). Navigate it to the `url` yourself — don't print the URL or shell out to the system browser. (A *surfaced* link in Cursor follows its "Browser Tab" vs "Google Chrome" picker and may open the system browser; you calling `Navigate` avoids that. A third-party MCP like OK cannot push a URL into the pane — only the agent's own `Navigate` can.)
|
|
24
|
+
- **Codex desktop app** → its in-app **Browser** plugin (`@Browser`); drive it to the `url` (Codex navigates via `tab.goto`).
|
|
25
|
+
- **Any other host** with a URL-navigation tool (`browser`, `view_url`, `open_url`, `web.browse`, …) → navigate it to the `url`. **This is also the fallback when a named tool above isn't present under that exact name** (hosts rename tools): match on the capability, not the name. If no URL-navigation tool exists at all, drop to the Claude Code CLI track below.
|
|
26
|
+
- **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.
|
|
27
|
+
|
|
28
|
+
**Claude Code CLI — a separate track (no browser).** The CLI is pure stdio: don't open or fake a browser. For an "open `<doc>`/`<folder>`" request, run **`ok open <doc>`** (`--folder` for a folder) — it deep-links the doc into OK Desktop (folders open in the browser); an action you run, not a URL to print. Any other pure-stdio host with **no** URL-navigation tool is on this track too — but if you *do* have a tool that navigates to a URL, use the in-app branch above, not this track. The Codex **CLI**, **IDE extension**, and **Cloud** also live here (web search only, no localhost browser). 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.
|
|
29
|
+
|
|
30
|
+
**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.
|
|
31
|
+
|
|
32
|
+
**Four signals to check if the preview is already attached** (read these from each write response):
|
|
33
|
+
|
|
34
|
+
1. You opened/navigated earlier this session → don't reopen.
|
|
35
|
+
2. Write response has `previewUrl` (non-null route) and NO `warning` → a browser is attached somewhere; do nothing.
|
|
36
|
+
3. `warning: { action: "attach-preview-once", previewUrl, message }` → UI reachable, no browser attached; navigate one-shot (`preview_start`, or `preview_url` → in-app browser).
|
|
37
|
+
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.
|
|
38
|
+
|
|
39
|
+
Warnings fire at most once per session in the fresh-start case.
|
|
40
|
+
|
|
41
|
+
**Re-point at the end of a multi-doc workflow; don't claim a doc is on screen unless you put it there.** The one-shot attach (signal 3) opens the preview *once* — later writes do NOT move the pane; it stays on the doc you last navigated to. When a turn touches several docs, finish by navigating the preview to the doc the user should land on, using your host's move mechanism (`preview_eval` setting `window.location.hash` from the response `previewUrl`, or `preview_url` → in-app browser; honor `autoOpen`). Until you have navigated there *this* turn, don't tell the user a doc is "open" / "on screen" — at most, say the preview may still be on the doc you opened earlier.
|
|
42
|
+
|
|
43
|
+
**`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` auto-starts the backend on demand (same `OK_MCP_AUTOSTART` gate as writes; a cold first call can take seconds) and reports `running: false` + `url: null` only when no UI could be reached — its hint names the right command.
|
|
44
|
+
|
|
45
|
+
If you see `"Hocuspocus server is not running"`, run `ok start` and retry.
|
|
46
|
+
|
|
47
|
+
OK Electron and `ok ui` share `ui.lock`; when a second UI binds a different port, the OK lock-collision proxy bridges it to the live server transparently. That is exactly why `previewUrl` is route-only — the port behind the proxy is not the agent's to use. **Do not nudge the user to quit OK Electron to free a port** — the proxy handles it, and quitting tears down a UI in active use.
|
|
48
|
+
|
|
49
|
+
**The preview is read-only for the agent — it is the user's view, not a surface you read back.** You cannot click or type to drive edits — the CRDT flow is one-way (agent → MCP → CRDT → preview).
|
|
50
|
+
|
|
51
|
+
**No screenshots to confirm edits, no generic verification loop.** Do NOT take `preview_screenshot` (host tool, not OK MCP) after a write, and do not run a generic snapshot/eval/screenshot verification loop — OK's preview is a read-only, one-way mirror, so the CRDT tool response *is* the confirmation that an edit landed. Screenshot only when debugging a visual rendering issue or when the user explicitly asks to see the preview — never to confirm an edit landed. (Navigating the pane with `preview_eval` by setting `window.location.hash` is fine — that drives the view, it is not a read-back verification loop.)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
# Setting up
|
|
1
|
+
# Setting up OpenKnowledge
|
|
2
2
|
|
|
3
|
-
This repository is an
|
|
3
|
+
This repository is an OpenKnowledge (OK) project: its `.md` / `.mdx` files are CRDT documents, and agents work with them through the OpenKnowledge MCP server. If your agent's `mcp__open-knowledge__*` tools aren't available, the project just isn't connected on this machine yet. Connect it at whatever depth you need — each rung adds capability, and you can stop at the first one that covers your task.
|
|
4
4
|
|
|
5
5
|
**Canonical, always-current instructions:** <https://openknowledge.ai/docs/get-started/quickstart>. The rungs below are a quick orientation; the docs are authoritative (release channels and download links move, so this file deliberately points there rather than pinning them).
|
|
6
6
|
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Template authoring + folder editing
|
|
2
|
+
|
|
3
|
+
## When to create a template
|
|
4
|
+
|
|
5
|
+
Templates make folder structure durable. Create them proactively:
|
|
6
|
+
|
|
7
|
+
- 2+ sibling docs share a skeleton in a folder with no template → extract via `write({ template })`.
|
|
8
|
+
- About to write a doc in a folder where no template fits, AND the shape is reusable → save as template the same turn.
|
|
9
|
+
- Scaffolding a new folder for a doc category → pair `write({ folder })` (or `edit({ folder })`) with `write({ template })` in the same turn.
|
|
10
|
+
- The user describes a recurring doc shape ("we always log meetings with attendees, agenda, action items") → author the template once.
|
|
11
|
+
|
|
12
|
+
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.
|
|
13
|
+
|
|
14
|
+
**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.
|
|
15
|
+
|
|
16
|
+
## Editing a folder's own description
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
edit({
|
|
20
|
+
folder: {
|
|
21
|
+
path: "meetings",
|
|
22
|
+
frontmatter: { title: "Meetings", description: "Meeting notes", tags: ["meeting"] },
|
|
23
|
+
},
|
|
24
|
+
})
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
`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.
|
|
28
|
+
|
|
29
|
+
## Creating templates
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
write({
|
|
33
|
+
template: {
|
|
34
|
+
path: "meetings/prep-notes",
|
|
35
|
+
content: "# {Meeting Title}\n\n**Attendees:** \n**Date:** \n\n## Agenda\n- \n",
|
|
36
|
+
frontmatter: {
|
|
37
|
+
title: "Meeting Prep Notes", // REQUIRED — TEMPLATE_TITLE_REQUIRED if missing
|
|
38
|
+
description: "Use before a meeting.", // recommended — soft warning if absent
|
|
39
|
+
tags: ["meeting", "prep"],
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
})
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
**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/`).
|
|
46
|
+
|
|
47
|
+
## Creating a doc from a template
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
// Inspect the menu (already done in the pre-write checklist).
|
|
51
|
+
exec("ls -A meetings/")
|
|
52
|
+
// → templates_available: [{ name: "prep-notes", title: "Meeting Prep Notes", scope: "local" }, ...]
|
|
53
|
+
|
|
54
|
+
// Instantiate. `template` and `content` are mutually exclusive.
|
|
55
|
+
write({
|
|
56
|
+
document: {
|
|
57
|
+
path: "meetings/2026-05-02-roadmap-sync",
|
|
58
|
+
template: "prep-notes",
|
|
59
|
+
},
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
// Fill the `{shape}` placeholders via follow-up edit calls.
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
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.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Workflow tools — depth
|
|
2
|
+
|
|
3
|
+
(Core carries the dispatch table + "these are your default move". This file carries the operating detail.)
|
|
4
|
+
|
|
5
|
+
One MCP tool — `workflow` — builds on the read/write primitives, 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.
|
|
6
|
+
|
|
7
|
+
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.
|
|
8
|
+
|
|
9
|
+
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).
|
|
10
|
+
|
|
11
|
+
**Autonomy gates vs session-level autonomy.** Per-tool STOP gates (e.g. `research`'s scoping gate, `consolidate`'s decision-confirmation gate) are not overridden by session-level "work without stopping for clarifying questions" hints. The session-level hint covers trivial back-and-forth ("which file did you mean?"); per-tool gates exist for 1-way-door decisions where the tool deliberately wants confirmation before continuing. When in doubt, treat the per-tool gate as authoritative and the session-level autonomy hint as a default for the in-between turns.
|
|
12
|
+
|
|
13
|
+
**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.
|
|
14
|
+
|
|
15
|
+
**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.
|
|
16
|
+
|
|
17
|
+
**Project scaffolding — two paths.** **Empty repo:** run `ok seed` once from a terminal (scaffolds the layout + seeds `log.md` + folder defaults). **Existing content:** invoke `workflow({ kind: 'discover' })`. Neither is required; the four workflow kinds work against any folder structure. Only mention each when explicitly relevant.
|
|
18
|
+
|
|
19
|
+
**Starter packs — reference for inspiration.** The `ok` CLI (a Bash surface beside the MCP tools; other verbs `ok start` / `ok open` are documented in the core) ships proven layouts you can study to build a *similar* structure of your own — adapt the idea, don't clone the pack:
|
|
20
|
+
|
|
21
|
+
- `knowledge-base` — source-grounded research articles
|
|
22
|
+
- `software-lifecycle` — proposals, decisions, specs
|
|
23
|
+
- `codebase-wiki` — agent-authored wiki of your codebase
|
|
24
|
+
- `plain-notes` — notes + daily journal
|
|
25
|
+
- `worldbuilding` — fiction story wiki
|
|
26
|
+
- `writing-pipeline` — drafts → published
|
|
27
|
+
- `entity-vault` — people / companies / meetings (personal CRM)
|
|
28
|
+
- `okf` — Open Knowledge Format–conformant base
|
|
29
|
+
|
|
30
|
+
To reference one **without installing it**: `ok seed --list-packs` (the menu) → `ok seed --pack <name> --dry-run` (its folders + the *why* of each folder + templates; writes nothing). Then either adapt the ideas into your own folders (`write({ folder })` + a template) or adopt the pack as-is by re-running without `--dry-run`. Reach for this when a user wants structure and an archetype fits — propose a tailored variant, not a verbatim copy.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Writing — depth
|
|
2
|
+
|
|
3
|
+
(Core carries the MUSTs: route through `write`/`edit` never native, persist incrementally, pass a `summary`. This file carries the supporting detail.)
|
|
4
|
+
|
|
5
|
+
**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`.
|
|
6
|
+
|
|
7
|
+
**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: see `references/components-and-visuals.md`.
|
|
8
|
+
|
|
9
|
+
For the write-response advisory warnings (`content-divergence`, `disk-edit-reconciled`, `mermaid-parse-error`), MDX authoring, and delete/move mechanics, see `references/doc-editing.md`.
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: open-knowledge-write-skill
|
|
3
|
-
description: "Use when the user wants to create, author, write, or design a new Agent Skill (a SKILL.md) — for
|
|
4
|
-
compatibility: "
|
|
3
|
+
description: "Use when the user wants to create, author, write, or design a new Agent Skill (a SKILL.md) — for OpenKnowledge or for their editors — including requests like 'help me write a skill', 'make a skill that…', 'turn this workflow into a skill', or improving an existing skill's triggering and discipline. Also use when capturing reusable agent guidance that should live as an installable skill rather than a one-off prompt. Covers choosing scope (project vs global), the SKILL.md frontmatter contract, progressive-disclosure structure, evaluating the skill, and installing it into the user's editors."
|
|
4
|
+
compatibility: "OpenKnowledge project recommended (uses the `write` / `edit` / `install` MCP verbs). Authoring + validation are pure file ops; live preview + eval want a running server (`ok start`)."
|
|
5
5
|
metadata:
|
|
6
6
|
version: "0.9.1"
|
|
7
7
|
author: "Inkeep"
|
|
8
8
|
repository: "https://github.com/inkeep/open-knowledge"
|
|
9
9
|
---
|
|
10
10
|
|
|
11
|
-
# Writing an
|
|
11
|
+
# Writing an OpenKnowledge skill
|
|
12
12
|
|
|
13
13
|
You are helping the user author an **Agent Skill** — a `SKILL.md` file (plus
|
|
14
14
|
optional `references/` and `scripts/`) that teaches an AI agent how to do a
|
|
15
|
-
recurring task. In
|
|
15
|
+
recurring task. In OpenKnowledge a skill is a first-class, versioned,
|
|
16
16
|
installable artifact: you author it with the `write` / `edit` skill verbs, then
|
|
17
17
|
`install` it into the user's editors.
|
|
18
18
|
|
|
@@ -23,6 +23,15 @@ it says. Work the stages below in order, but jump to where the user already is.
|
|
|
23
23
|
|
|
24
24
|
## Stage 1 — Capture intent and classify the skill
|
|
25
25
|
|
|
26
|
+
**Gate — does this already exist? Check BEFORE you build.** Scan the installed
|
|
27
|
+
skills (the host surfaces the full catalog when this skill loads) for one whose
|
|
28
|
+
role or triggers already cover the task. If an existing skill covers most of it,
|
|
29
|
+
STOP and **recommend reuse** — a near-duplicate with overlapping triggers
|
|
30
|
+
mis-fires and dilutes both. Build a new skill only when it is genuinely distinct,
|
|
31
|
+
or a deliberately tighter companion whose `description` explicitly hands off to
|
|
32
|
+
the existing one. Surface the overlap and decide WITH the user before drafting or
|
|
33
|
+
writing anything — never discover it after the skill is written.
|
|
34
|
+
|
|
26
35
|
Ask only what you can't infer:
|
|
27
36
|
|
|
28
37
|
- **What recurring task** should this skill handle? Get one concrete example.
|
|
@@ -158,7 +167,9 @@ build, so they have no history to restore.
|
|
|
158
167
|
|
|
159
168
|
## Reminders
|
|
160
169
|
|
|
161
|
-
- Prefer ONE good skill over many overlapping ones; split only when triggers diverge.
|
|
170
|
+
- Prefer ONE good skill over many overlapping ones; split only when triggers diverge. (The Stage 1 gate is where you ENFORCE this — don't leave overlap to discover later.)
|
|
171
|
+
- Scope is the only placement decision — don't fold harness/format/toolchain assumptions into it, and don't bake one into the scope question's wording. You are authoring an OpenKnowledge skill: write it with `write({ skill })` and project it with `install`; never hand-write skill files into editor dirs (`.claude/skills/`, `.cursor/skills/`, `.codex/skills/`) — `install` owns those and overwrites them. If you load this flow, author through it.
|
|
172
|
+
- Ground claims about how skills behave (versioning, install targets, scope semantics) in this guide or the tool descriptions — don't assert system facts from assumption.
|
|
162
173
|
- Avoid blanket ALWAYS/NEVER rules without a stated reason — they read as noise and
|
|
163
174
|
get ignored. Explain the why.
|
|
164
175
|
- A skill that ships executable `scripts/` is projected verbatim into another
|