@inkeep/open-knowledge 0.5.0-beta.24 → 0.5.0-beta.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/dist/assets/skills/open-knowledge/SKILL.md +66 -16
  2. package/dist/cli.mjs +4 -4
  3. package/dist/constants-2dtfcgrw.mjs +2 -0
  4. package/dist/{dist-CibAeq--.mjs → dist-CC_tWBen.mjs} +20 -16
  5. package/dist/{dist-C2u-rxF1.mjs → dist-iQJv8jfS.mjs} +1 -1
  6. package/dist/index.mjs +1 -1
  7. package/dist/init-5GzdDz9e.mjs +1 -0
  8. package/dist/{init-CWEExvCP.mjs → init-B8sYBoo0.mjs} +4 -4
  9. package/dist/loader-C4bOzPgL.mjs +1 -0
  10. package/dist/{loader-B8GTCsV6.mjs → loader-sBuuFJ5-.mjs} +2 -2
  11. package/dist/{preview-DDcsbT6L.mjs → preview-BKckrsQM.mjs} +2 -2
  12. package/dist/preview-CckqoGDk.mjs +1 -0
  13. package/dist/public/assets/ActivityModeContent-By7RAJuB.js +2 -0
  14. package/dist/public/assets/DocumentContext-BnBFXQXX.js +52 -0
  15. package/dist/public/assets/{GraphPanel-BGdpuLXE.js → GraphPanel-MJE4uTfy.js} +2 -2
  16. package/dist/public/assets/{SettingsDialog-rqnF7gS-.js → SettingsDialog-Cy31yDr9.js} +2 -2
  17. package/dist/public/assets/{SourceEditor-CZ1jZux8.js → SourceEditor-CEbexGO3.js} +1 -1
  18. package/dist/public/assets/{config-validation-events-Cg1WEsqb.js → config-validation-events-9vZggQx0.js} +3 -3
  19. package/dist/public/assets/{doc-hash-CGOOAxMs.js → doc-hash-8BtTwv21.js} +121 -120
  20. package/dist/public/assets/index-BLhfk-hT.js +1853 -0
  21. package/dist/public/assets/index-D4NdqT_p.css +1 -0
  22. package/dist/public/assets/{target-navigation-intent-B4Stg_GZ.js → target-navigation-intent-Nd4Mwd33.js} +1 -1
  23. package/dist/public/assets/{toggle-group-FOEgJa6h.js → toggle-group-4JFWFjGS.js} +1 -1
  24. package/dist/public/assets/{tooltip-1yQ0AYa7.js → tooltip-0G4OCxcX.js} +1 -1
  25. package/dist/public/assets/{typing-burst-detector-CPAaH_c3.js → typing-burst-detector-QPuBc4k7.js} +1 -1
  26. package/dist/public/index.html +9 -9
  27. package/dist/{repair-launch-json-CfW2OcfI.mjs → repair-launch-json-B6Dj3y_S.mjs} +2 -2
  28. package/dist/{repair-mcp-configs-C4h9Cere.mjs → repair-mcp-configs-C4jpEnuF.mjs} +2 -2
  29. package/dist/{src-DhYhRB3U.mjs → src-CVBca_9U.mjs} +2 -2
  30. package/dist/start-D_2Z0J-R.mjs +1 -0
  31. package/dist/{start-qGNJkMnp.mjs → start-swrBYjKo.mjs} +2 -2
  32. package/package.json +1 -1
  33. package/dist/constants-Deow6nk1.mjs +0 -2
  34. package/dist/init-BMRmWZ_f.mjs +0 -1
  35. package/dist/loader-NzmQDL0I.mjs +0 -1
  36. package/dist/preview-Bae0MG8_.mjs +0 -1
  37. package/dist/public/assets/ActivityModeContent-4zLTqVCC.js +0 -2
  38. package/dist/public/assets/DocumentContext-mhvvV02u.js +0 -52
  39. package/dist/public/assets/index-BMFglQhL.js +0 -1853
  40. package/dist/public/assets/index-slmhtD5o.css +0 -1
  41. package/dist/start-DL2EG5cj.mjs +0 -1
@@ -1,9 +1,9 @@
1
1
  ---
2
2
  name: open-knowledge
3
- description: "MUST invoke when the project contains a .ok/ directory — before any read or edit of .md / .mdx files, any mcp__open-knowledge__ tool call, and any write_document / edit_document. Skip if no .ok/ — not an Open Knowledge project. Carries preview-attach (open preview browser at session start; one-shot on `action: attach-preview-once`), STOP rules for native Read/Grep/Edit on in-scope markdown, grounding rules (every factual claim needs a source), standard markdown linking with get_dead_links verification, image sourcing + alt-text + source-citation rules, folder-first organization with opt-in nested .ok/ frontmatter + templates, and the anti-pattern table. Authoritative — MCP server instructions and AGENTS.md overlap but do not substitute for the full attach rule, grounding rule, media rules, dead-link verification, and failure-mode guidance carried only here."
3
+ description: "MUST invoke when the project contains a .ok/ directory — before any read or edit of .md / .mdx files, any mcp__open-knowledge__ tool call, and any write_document / edit_document. Skip if no .ok/ — not an Open Knowledge project. Carries preview-attach (open preview browser at session start; one-shot on `action: attach-preview-once`), STOP rules for native Read/Grep/Edit on in-scope markdown, grounding rules (every factual claim needs a source), standard markdown linking with get_dead_links verification, image sourcing + alt-text + source-citation rules, folder-first organization with opt-in nested .ok/ frontmatter + templates, and the anti-pattern table."
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.5.0-beta.24"
6
+ version: "0.5.0-beta.25"
7
7
  author: "Inkeep"
8
8
  repository: "https://github.com/inkeep/open-knowledge"
9
9
  ---
@@ -11,17 +11,28 @@ metadata:
11
11
 
12
12
  Open Knowledge (OK) is a markdown-CRDT collaboration platform exposed via MCP. This skill carries the behavioral rules agents need to use it fluently. Every section is a MUST unless marked otherwise.
13
13
 
14
- > Skill version: tracks `@inkeep/open-knowledge-server` package version. Check `cat ~/.ok/skill-state.yml` to see what's installed locally.
14
+ > **Authoritative source.** Where the MCP server's `instructions` echo overlaps with this skill, this skill wins — the full attach rule, grounding rule, media rules, dead-link verification, and failure-mode guidance live only here.
15
+
16
+ > Skill version: tracks `@inkeep/open-knowledge-server` package version. Check `cat ~/.ok/skill-state.yml` to see what's installed locally. **Version floor:** `ok seed` (referenced below) requires `@inkeep/open-knowledge` >= 0.4.0. If `ok seed` errors with `unknown command`, upgrade: `npm install -g @inkeep/open-knowledge`.
17
+
18
+ ## TL;DR — the 90% case
19
+
20
+ 1. **Reads:** `exec("cat …")` for a single doc, `exec("ls -A …")` for a directory, `search` for ranked, `grep` for literal. Native `Read` / `Grep` only on source code (`.ts` / `.py` / …), never on in-scope `.md` / `.mdx`.
21
+ 2. **Writes:** `write_document` for new or full-replace, `edit_document` for body-only patches. Frontmatter changes go through `write_document({ position: "replace" })` — `edit_document` rejects frontmatter edits with HTTP 400.
22
+ 3. **Preview:** open the browser at session start (or when a write response carries `warning: { action: "attach-preview-once" }`). Don't `preview_screenshot` after every edit.
23
+ 4. **Workflow tools** (`ingest` / `research` / `consolidate` / `discover`) return procedural guides, not data. Use them when the work fits the layer; follow their numbered steps.
24
+
25
+ Everything below is depth. Read on demand.
15
26
 
16
27
  ## STOP — native tools on in-scope `.md` / `.mdx`
17
28
 
18
29
  When this workspace has Open Knowledge MCP configured, do **not** use your host's native file tools on markdown paths inside the content directory. The ban covers every common rationalization:
19
30
 
20
31
  - **Native `Read` / `Grep` / `Glob` on in-scope `.md` / `.mdx`** — the original case.
21
- - **`Bash ls` / `Bash find` / `Bash cat` on dirs containing in-scope markdown** — use `exec("ls …")` / `exec("find … -name '*.md'")` / `exec("cat …")` instead. Native returns bare names; `exec` returns frontmatter, backlink counts, and recent activity per child.
32
+ - **`Bash ls` / `Bash find` / `Bash cat` on dirs containing in-scope markdown** — use `exec("ls -A …")` / `exec("find … -name '*.md'")` / `exec("cat …")` instead. Native returns bare names; `exec` returns frontmatter, backlink counts, and recent activity per child. `-A` shows hidden entries (`.ok/`, `.okignore`) which OK projects carry; omit `.` and `..` rows that `-a` would add.
22
33
  - **Glob patterns that target markdown** (`**/*.md`, any dir known to be markdown-heavy like `specs/**`, `reports/**`, `docs/**`) — use `exec` with `find`, or `list_documents({ dir })`.
23
34
  - **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` / `grep`. Subagents remain appropriate for **source-code** exploration.
24
- - **Reading `.ok/AGENTS.md` via native `Read`** observed failure mode during M1 testing. The `.ok/` directory is in-scope; treat its contents the same as any other knowledge-base file.
35
+ - **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.
25
36
 
26
37
  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` — pipes OK) and returns raw stdout plus enriched metadata per file.
27
38
 
@@ -34,7 +45,7 @@ Why: native tools skip frontmatter, backlinks, shadow-repo activity, and project
34
45
  ## Reads — examples
35
46
 
36
47
  - Read a file: `exec("cat <path>.md")` — contents + full rich enrichment
37
- - List a directory: `exec("ls <dir>")` — per-child frontmatter, recursive markdown counts, most-recently-updated doc per subdir
48
+ - List a directory: `exec("ls -A <dir>")` — per-child frontmatter, recursive markdown counts, most-recently-updated doc per subdir. Prefer `-A` over plain `ls`: OK projects carry dot-prefixed entries (`.ok/`, nested `.ok/`, `.okignore`) that plain `ls` omits. `-A` shows hidden entries without the noisy `.`/`..` rows that `-a` adds, and without the verbose long-format columns that `-la` adds (the per-child enrichment already carries the useful metadata `-l` would surface).
38
49
  - Search: `exec("grep -rn <term> <dir> | head -5")` — matches + enrichment on matched files
39
50
  - Typed tools (`read_document`, `search`, `grep`, `list_documents`) remain available — prefer them when a structured `structuredContent` shape is useful (e.g., passing results to another tool). For interactive reads, `exec` is lighter. **Pick the right search:** `search` for ranked retrieval (cmd-K parity — title boost + body BM25 + recency); `grep` for every literal-string occurrence grouped by file with frontmatter enrichment.
40
51
 
@@ -62,6 +73,38 @@ Call `write_document` / `edit_document` as soon as you have content. Native `Edi
62
73
 
63
74
  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 from this tool — call `save_version` first if you may need to roll back, and `get_backlinks` first if you want to fix the referrers that will become redlinks.
64
75
 
76
+ **If `edit_document` 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.
77
+
78
+ ## Components — prefer canonicals when one fits
79
+
80
+ Some Open Knowledge projects ship a registry of custom JSX components (callouts, tabs, math, file attachments, …) that render with richer affordances than plain CommonMark / GFM. Discovery and authoring follow a three-step pattern — no separate listing call required:
81
+
82
+ 1. **Discover (point-of-authoring).** The JSON-schema `description` of `write_document` and `edit_document` carries an inline inventory of the canonical JSX components available for this project: a list of `{ id, displayName, description, kind }` entries. The agent's host renders that description in its tool-list view, so the catalog is visible at the moment of invoking either write tool. The `kind` is one of `jsx-block` (children-bearing JSX) or `jsx-void` (self-closing JSX).
83
+
84
+ 2. **Fetch full details.** Call `get_components({ ids: ["..."] })` with the ids you picked from the inventory. The response carries per-entry `example` (the literal source-form bytes to copy-paste) and a form-aware `params` schema (each prop's `type`, `values?` for enums, `required`, `defaultValue?`, `description`). Use whichever components are **semantically useful in any part of the doc** — a Callout for a warning, a Tabs/Tab pair for alternative content, etc.
85
+
86
+ 3. **Write.** Compose your markdown body inline using the `example` syntax + your chosen prop values, then call `write_document` or `edit_document` once. Generic placeholder shapes per `kind`:
87
+
88
+ ```mdx
89
+ <BlockComponent prop="value">
90
+ Body content here.
91
+ </BlockComponent>
92
+ ```
93
+
94
+ ```mdx
95
+ <VoidComponent prop="value" />
96
+ ```
97
+
98
+ **Fenced code blocks are a separate authoring path.** Open Knowledge renders certain fenced code blocks specially — author these the way you would in any markdown doc; they are NOT in the component inventory and don't need a `get_components` fetch:
99
+
100
+ - ` ```mermaid ` — renders the block as a Mermaid diagram (flowchart, sequence, class, state, ER, gantt, pie).
101
+ - ` ```html preview ` (also ` ```htm preview ` / ` ```xml preview `) — renders the block as a live iframe preview. Author a standalone HTML/JS/CSS page in the fence and it executes inside the editor. **Use this whenever you want anything interactive or JS-powered** (charts via Chart.js / D3, calculators, animations, demos of an idea, anything that needs live JavaScript). Optional fence-meta tokens: `h=<value>` and `w=<value>` set iframe size (e.g. ` ```html preview h=400px w=600px `).
102
+ - ` ```<lang> ` (any other language) — renders as a syntax-highlighted code block (no preview).
103
+
104
+ **No-canonical-fits fallback.** If none of the available canonical JSX components matches what you want to author, you can still write any `<TagName>...</TagName>` JSX — Open Knowledge renders unregistered components as raw MDX (the wildcard tolerance contract). Prefer canonicals when one matches; arbitrary JSX is the long-tail escape hatch.
105
+
106
+ **Don't enumerate first-party canonicals here.** Component names, counts, and instance-specific examples are project-specific — the source of truth is the inventory the write tools currently advertise. Reading SKILL.md never replaces calling `get_components` for current shape.
107
+
65
108
  ## Grounding — every factual claim needs a source (MUST)
66
109
 
67
110
  Knowledge-base docs are factual artifacts — whether the project is a wiki, an LLM brain, a spec collection, a research log, or anything else markdown-shaped. Every claim must be traceable, and **the source has to live inside the knowledge base**, not float on the public web.
@@ -88,7 +131,7 @@ Knowledge-base docs are factual artifacts — whether the project is a wiki, an
88
131
  - **Verify before walking away.** After writing a doc, call `get_dead_links({ sourceDocNames: ['your/doc'] })` to find broken references. Fix each redlink or explicitly accept it.
89
132
  - **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). `get_dead_links` is strict-exact — trust the tool, not the visual.
90
133
 
91
- **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.
134
+ **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.
92
135
 
93
136
  ## Media — images and attachments
94
137
 
@@ -105,7 +148,7 @@ Knowledge-base docs are factual artifacts — whether the project is a wiki, an
105
148
  1. Fetch it (`WebFetch` / `curl` / your host's equivalent) and save to a local path.
106
149
  2. Reference with a relative markdown image link.
107
150
  3. Cite the source in a caption (see §4 below).
108
- - **Conventional location:** `assets/images/<topic>/<filename>` under the content root. If the project already has a different convention, follow it — check via `exec("ls assets/")` or `exec("find . -type d -name images")` first.
151
+ - **Conventional location:** `assets/images/<topic>/<filename>` under the content root. If the project already has a different convention, follow it — check via `exec("ls -A assets/")` or `exec("find . -type d -name images")` first.
109
152
  - If you cannot fetch (no network, paywalled source, etc.): DON'T invent a local path. Either omit the image or mark inline `(TODO: image needs sourcing from <URL>)` for a human.
110
153
 
111
154
  ### 3. Alt-text discipline
@@ -143,13 +186,15 @@ tags:
143
186
 
144
187
  Folder-level defaults that any new doc here inherits live in opt-in nested `<folder>/.ok/frontmatter.yml` and merge at read time — see *Folder structure + metadata* below.
145
188
 
189
+ **Editing frontmatter.** `edit_document` does NOT change frontmatter — it's body-only by design; frontmatter-intersecting find/replace calls return HTTP 400. To change `title` / `description` / `tags`, call `write_document({ docName, position: "replace", markdown })` and include the new YAML block at the top of `markdown`. Folder defaults from the nested `.ok/frontmatter.yml` cascade still merge at read time, so you only need to declare keys that differ from the cascade. (Future server work may add a single-key frontmatter-patch tool with JSON Merge Patch semantics; until that lands, `write_document(replace)` is the canonical pathway.)
190
+
146
191
  ## Follow project conventions — read folder defaults before writing (MUST)
147
192
 
148
- Before creating or editing docs in a folder, **always** call `list_documents(<folder>)` (or `exec("ls <folder>")`) once and act on what it returns. Skipping this step is how agents land docs that violate the folder's discipline (wrong tags, no template, missing frontmatter shape) and force a follow-up cleanup pass.
193
+ Before creating or editing docs in a folder, **always** call `list_documents(<folder>)` (or `exec("ls -A <folder>")`) once and act on what it returns. Skipping this step is how agents land docs that violate the folder's discipline (wrong tags, no template, missing frontmatter shape) and force a follow-up cleanup pass.
149
194
 
150
195
  Pre-write checklist:
151
196
 
152
- 0. **First-contact check.** If `list_documents(<folder>)` returns empty `frontmatter_defaults` AND empty `templates_available` AND `exec("ls")` shows substantial content elsewhere, the project hasn't been onboarded — STOP and invoke `discover` (see Workflow tools table) before writing. Skip this check on subsequent writes in the same session once you've confirmed the project is configured.
197
+ 0. **First-contact check.** If `list_documents(<folder>)` returns empty `frontmatter_defaults` AND empty `templates_available` AND `exec("ls -A")` shows substantial content elsewhere, the project hasn't been onboarded — STOP and invoke `discover` (see Workflow tools table) before writing. Skip this check on subsequent writes in the same session once you've confirmed the project is configured.
153
198
 
154
199
  1. **Read `frontmatter_defaults`** — the merged folder defaults (title shape, description, tags) that any new doc here will inherit. Surfaces nested `<folder>/.ok/frontmatter.yml` cascade walked root → leaf, leaf wins per-key. **Don't redeclare** keys the cascade already provides — let inheritance carry them. Override per-file only when the file truly differs.
155
200
  2. **Read `templates_available`** — the menu of starter shapes for `write_document({ template })`. Each entry has `name`, `title`, `description`, and `scope` (`local` / `inherited` / `user`). If an entry matches, prefer it over free-form markdown — see "When to use a template" and "When to create a template" below.
@@ -275,7 +320,7 @@ write_document({
275
320
  // calls — those are author-fill markers, not server-side substitutions.
276
321
  ```
277
322
 
278
- Templates resolve via leaf → root walk-up at the target's parent folder, with closest-wins on filename collision (D7). The `scope` field has three values: `"local"` (template lives in this folder's `.ok/templates/`), `"inherited"` (template lives in an ancestor's), and `"user"` (template lives in `~/.ok/templates/`, surfaced when the request was served from the user-global cascade) all three are first-class menu entries. Descendant templates do NOT appear in the parent's array — they surface only inside `subfolders[].templates_available` when `list_documents` is called with `depth > 1`.
323
+ Templates resolve via leaf → root walk-up at the target's parent folder, with closest-wins on filename collision (D7). The `scope` field has three values: `"local"` (template lives in this folder's `.ok/templates/`), `"inherited"` (template lives in an ancestor's), and `"user"` (template lives in `~/.ok/templates/`, surfaced when the request was served from the user-global cascade). Treat `local` and `inherited` as first-class for the current folder; **skip `user`-scope entries whose `title` / `description` clearly don't match the folder's purpose** (e.g. a `daily-journal` user template won't help in `wiki/`). User-global templates surface in every folder by design — there's no per-folder opt-out today. Descendant templates do NOT appear in the parent's array — they surface only inside `subfolders[].templates_available` when `list_documents` is called with `depth > 1`.
279
324
 
280
325
  **`template` and `markdown` are mutually exclusive** (D21). Passing both errors with `TEMPLATE_AND_MARKDOWN_BOTH_SET`. The template body becomes the new doc's body verbatim (after `{{date}}`/`{{user}}` substitution); fill `{shape}`-style placeholders via subsequent `edit_document` calls.
281
326
 
@@ -285,7 +330,7 @@ When you make a multi-step change (batch of new docs, folder restructure), pause
285
330
 
286
331
  This is primarily a human-watchability concern — the user watches edits land in the preview; interleaved cadence makes the narrative legible.
287
332
 
288
- **Hub docs.** Don't *create* `INDEX.md` / `README.md` hub files solely to catalog children — `exec("ls <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.
333
+ **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.
289
334
 
290
335
  ## Log discipline — check for a project log when KB content changes
291
336
 
@@ -297,7 +342,7 @@ The skill carries the trigger ("KB content changed this turn — go look"). The
297
342
 
298
343
  | Task | Don't | Do |
299
344
  | ----------------------------------------------- | ---------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- |
300
- | List a markdown-heavy dir | `Bash: ls specs/` | `exec("ls specs/")` |
345
+ | List a markdown-heavy dir | `Bash: ls specs/` | `exec("ls -A specs/")` |
301
346
  | Find all SPEC.md files | `Glob: **/SPEC.md` | `exec("find specs -name SPEC.md")` |
302
347
  | Find the most relevant page for a query | `Grep: "pattern" *.md` then read three files | `search({ query: "pattern" })` (ranked: title + body BM25 + recency) |
303
348
  | Find every literal occurrence of a phrase | `Grep: "pattern" *.md` | `grep({ query: "pattern" })` (literal, grouped by file, with frontmatter) |
@@ -315,6 +360,7 @@ The skill carries the trigger ("KB content changed this turn — go look"). The
315
360
  | Write a doc in an unfamiliar folder | go straight to `write_document` with hand-authored markdown | `list_documents(<folder>)` first — read `frontmatter_defaults` + `templates_available` before writing |
316
361
  | Land in an existing repo without orienting | go straight to `write_document` or run the per-folder pre-write checklist ad-hoc when no folder frontmatter / templates exist | invoke `discover` once for the project — it extracts conventions from siblings, sets folder frontmatter, writes templates, and activates the link graph |
317
362
  | Author a doc when a matching template exists | `write_document({ markdown: "..." })` from scratch | `write_document({ template, position: "replace" })` — templates carry the folder's frontmatter + body discipline |
363
+ | Change a doc's title / tags | `edit_document` to swap the YAML (rejected — HTTP 400 frontmatter-intersect) | `write_document({ position: "replace", markdown })` with the new frontmatter block at the top of `markdown` |
318
364
  | Repeat the same frontmatter on sibling docs | hand-set identical `tags` / `title` prefix on every new file | `set_folder_rule(...)` once — the cascade carries it to every child |
319
365
  | 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 |
320
366
  | Scaffold a new folder for a doc category | `set_folder_rule` for frontmatter and stop there | pair `set_folder_rule` with `write_template` in the same turn — discipline + body shape |
@@ -324,7 +370,9 @@ The skill carries the trigger ("KB content changed this turn — go look"). The
324
370
 
325
371
  ## Workflow tools — when to invoke them
326
372
 
327
- Four MCP tools build on the primitives above. Three of them 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:
373
+ Four MCP tools build on the primitives above. **They return *procedural guidance* (a multi-step instructional body), not fetched data.** Calling `ingest("https://…")` does not download and write a doc for you — it returns a multi-step plan you then execute. Same for `research` / `consolidate` / `discover`. Plan to follow the numbered steps in order; don't skip the STOP gates.
374
+
375
+ 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:
328
376
 
329
377
  | Tool | Layer | When to invoke |
330
378
  | ------------- | ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
@@ -333,14 +381,16 @@ Four MCP tools build on the primitives above. Three of them correspond to [Karpa
333
381
  | `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. |
334
382
  | `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. Requires `open-knowledge start` running (Phase 1 step 0 gates). Skip on empty repos (use `ok seed`). One-shot; idempotent on re-run. |
335
383
 
336
- Each tool returns a multi-step instructional body when invoked. The bodies enforce their own gates — follow the numbered steps in order, don't skip the STOP gates.
337
-
338
384
  **These tools are your default move, not `write_document`.** 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_document` / `edit_document`. The tool bodies enforce framing (sources, status, supersedes chains) that hand-written articles routinely miss. `write_document` 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`.
339
385
 
340
386
  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).
341
387
 
388
+ **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.
389
+
342
390
  **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.
343
391
 
392
+ **Repeat invocations.** Workflow tools return their 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.
393
+
344
394
  **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` (MCP tool, 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 tools work against any folder structure. Only mention each when explicitly relevant.
345
395
 
346
396
  ## Server lifecycle
package/dist/cli.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import{o as e}from"./chunk-FK9Q3tQk.mjs";import{t}from"./esm-CEs3LWY3.mjs";import{$ as n,Ai as r,Bn as i,Ci as a,D as o,Di as s,E as c,Ei as l,G as u,Gr as d,H as f,Hr as p,Jr as m,Kn as h,Kr as g,Kt as ee,Lr as _,Oi as te,On as ne,Si as v,Ti as y,Ur as re,V as b,Vr as x,Wr as ie,Yn as ae,Yr as oe,_i as se,bi as ce,cr as le,fi as ue,it as de,ji as fe,ki as pe,m as me,ot as he,pn as ge,wi as _e,xi as S,yi as ve}from"./dist-CibAeq--.mjs";import{n as ye,r as be,t as C}from"./dist-bundle-BSdjl7Rj.mjs";import{C as xe,D as Se,E as Ce,O as we,S as Te,T as Ee,a as De,b as Oe,f as ke,k as Ae,w as je,x as Me,y as Ne}from"./init-CWEExvCP.mjs";import{c as Pe,f as Fe,l as Ie,n as Le,s as w}from"./server-lock-D7DXNVql-BRVVzA6T.mjs";import{i as Re}from"./git-handle-DwfYp_z--CfyPz4Dz.mjs";import{n as ze}from"./constants-Deow6nk1.mjs";import{n as Be,t as Ve}from"./loader-B8GTCsV6.mjs";import{d as He,f as Ue,l as We,m as Ge,p as Ke,s as qe}from"./start-qGNJkMnp.mjs";import{c as Je,i as T,n as E,o as D,r as O,s as k,t as A}from"./colors-BNufdynn.mjs";import"./src-DhYhRB3U.mjs";import{execFileSync as Ye,spawn as Xe,spawnSync as j}from"node:child_process";import Ze,{basename as Qe,dirname as $e,isAbsolute as et,join as M,relative as tt,resolve as N}from"node:path";import{closeSync as nt,existsSync as P,mkdirSync as rt,mkdtempSync as it,openSync as at,readFileSync as F,readdirSync as ot,realpathSync as st,rmSync as ct,statSync as lt,unlinkSync as ut,writeFileSync as dt}from"node:fs";import ft from"node:process";import{homedir as pt,hostname as mt,tmpdir as ht}from"node:os";import{fileURLToPath as gt}from"node:url";import{AsyncLocalStorage as _t,AsyncResource as vt}from"node:async_hooks";import{stripVTControlCharacters as yt,styleText as I}from"node:util";import*as bt from"node:readline";import{randomUUID as xt}from"node:crypto";import{realpath as St}from"node:fs/promises";import{setTimeout as Ct}from"node:timers/promises";import{createInterface as wt}from"node:readline/promises";var Tt=fe();const Et=`open-knowledge`;var Dt=class{backend=`keyring`;async get(e){let{Entry:t}=await import(`@napi-rs/keyring`);try{let n=new t(Et,e).getPassword();return n==null?null:JSON.parse(n)}catch{return null}}async set(e,t,n,r){let{Entry:i}=await import(`@napi-rs/keyring`),a=new i(Et,e),o={login:t,token:n,...r};a.setPassword(JSON.stringify(o))}async clear(e){let{Entry:t}=await import(`@napi-rs/keyring`);try{new t(Et,e).deletePassword()}catch{}}},Ot=class{backend=`file`;authFile;constructor(e){this.authFile=e??M(pt(),`.ok`,`auth.yml`)}read(){if(!P(this.authFile))return{};try{return(0,Tt.parse)(F(this.authFile,`utf-8`))??{}}catch(e){let t=e instanceof Error?e.message:`unknown error`;return process.stderr.write(`[auth] Failed to parse ${this.authFile}: ${t}. Starting with empty credentials.\n`),{}}}write(e){let t=$e(this.authFile);P(t)||rt(t,{recursive:!0,mode:448}),dt(this.authFile,(0,Tt.stringify)(e),{mode:384})}async get(e){return this.read()[e]??null}async set(e,t,n,r){let i=this.read();i[e]={login:t,token:n,...r},this.write(i)}async clear(e){let t=this.read();delete t[e],this.write(t)}};async function kt(e){try{let{Entry:e}=await import(`@napi-rs/keyring`);return new e(Et,`__probe__`),process.stderr.write(`[auth] token storage: OS keychain
2
+ import{o as e}from"./chunk-FK9Q3tQk.mjs";import{t}from"./esm-CEs3LWY3.mjs";import{$ as n,Ai as r,Bn as i,Ci as a,D as o,Di as s,E as c,Ei as l,G as u,Gr as d,H as f,Hr as p,Jr as m,Kn as h,Kr as g,Kt as ee,Lr as _,Oi as te,On as ne,Si as v,Ti as y,Ur as re,V as b,Vr as x,Wr as ie,Yn as ae,Yr as oe,_i as se,bi as ce,cr as le,fi as ue,it as de,ji as fe,ki as pe,m as me,ot as he,pn as ge,wi as _e,xi as S,yi as ve}from"./dist-CC_tWBen.mjs";import{n as ye,r as be,t as C}from"./dist-bundle-BSdjl7Rj.mjs";import{C as xe,D as Se,E as Ce,O as we,S as Te,T as Ee,a as De,b as Oe,f as ke,k as Ae,w as je,x as Me,y as Ne}from"./init-B8sYBoo0.mjs";import{c as Pe,f as Fe,l as Ie,n as Le,s as w}from"./server-lock-D7DXNVql-BRVVzA6T.mjs";import{i as Re}from"./git-handle-DwfYp_z--CfyPz4Dz.mjs";import{n as ze}from"./constants-2dtfcgrw.mjs";import{n as Be,t as Ve}from"./loader-sBuuFJ5-.mjs";import{d as He,f as Ue,l as We,m as Ge,p as Ke,s as qe}from"./start-swrBYjKo.mjs";import{c as Je,i as T,n as E,o as D,r as O,s as k,t as A}from"./colors-BNufdynn.mjs";import"./src-CVBca_9U.mjs";import{execFileSync as Ye,spawn as Xe,spawnSync as j}from"node:child_process";import Ze,{basename as Qe,dirname as $e,isAbsolute as et,join as M,relative as tt,resolve as N}from"node:path";import{closeSync as nt,existsSync as P,mkdirSync as rt,mkdtempSync as it,openSync as at,readFileSync as F,readdirSync as ot,realpathSync as st,rmSync as ct,statSync as lt,unlinkSync as ut,writeFileSync as dt}from"node:fs";import ft from"node:process";import{homedir as pt,hostname as mt,tmpdir as ht}from"node:os";import{fileURLToPath as gt}from"node:url";import{AsyncLocalStorage as _t,AsyncResource as vt}from"node:async_hooks";import{stripVTControlCharacters as yt,styleText as I}from"node:util";import*as bt from"node:readline";import{randomUUID as xt}from"node:crypto";import{realpath as St}from"node:fs/promises";import{setTimeout as Ct}from"node:timers/promises";import{createInterface as wt}from"node:readline/promises";var Tt=fe();const Et=`open-knowledge`;var Dt=class{backend=`keyring`;async get(e){let{Entry:t}=await import(`@napi-rs/keyring`);try{let n=new t(Et,e).getPassword();return n==null?null:JSON.parse(n)}catch{return null}}async set(e,t,n,r){let{Entry:i}=await import(`@napi-rs/keyring`),a=new i(Et,e),o={login:t,token:n,...r};a.setPassword(JSON.stringify(o))}async clear(e){let{Entry:t}=await import(`@napi-rs/keyring`);try{new t(Et,e).deletePassword()}catch{}}},Ot=class{backend=`file`;authFile;constructor(e){this.authFile=e??M(pt(),`.ok`,`auth.yml`)}read(){if(!P(this.authFile))return{};try{return(0,Tt.parse)(F(this.authFile,`utf-8`))??{}}catch(e){let t=e instanceof Error?e.message:`unknown error`;return process.stderr.write(`[auth] Failed to parse ${this.authFile}: ${t}. Starting with empty credentials.\n`),{}}}write(e){let t=$e(this.authFile);P(t)||rt(t,{recursive:!0,mode:448}),dt(this.authFile,(0,Tt.stringify)(e),{mode:384})}async get(e){return this.read()[e]??null}async set(e,t,n,r){let i=this.read();i[e]={login:t,token:n,...r},this.write(i)}async clear(e){let t=this.read();delete t[e],this.write(t)}};async function kt(e){try{let{Entry:e}=await import(`@napi-rs/keyring`);return new e(Et,`__probe__`),process.stderr.write(`[auth] token storage: OS keychain
3
3
  `),new Dt}catch{return process.stderr.write(`[auth] token storage: file (~/.ok/auth.yml)
4
4
  `),new Ot(e)}}async function At(e,t,n){let r=jt(await Mt(e)).host??``;if(!r)return 1;let i=await n.get(r);if(i==null)return 1;let a=e=>e.replace(/[\r\n]/g,``);return t.write(`username=${a(i.login)}\npassword=${a(i.token)}\n`),0}function jt(e){let t={};for(let n of e.split(`
5
5
  `)){let e=n.trim();if(e===``)continue;let r=e.indexOf(`=`);r!==-1&&(t[e.slice(0,r)]=e.slice(r+1))}return t}function Mt(e){return new Promise((t,n)=>{let r=[];e.on(`data`,e=>r.push(e)),e.on(`end`,()=>t(Buffer.concat(r).toString(`utf-8`))),e.on(`error`,n)})}function Nt(e){let n=new t(`git-credential`);return n.description(`Git credential helper (git credential-helper protocol)`),n.command(`get`).description(`Lookup credentials from TokenStore (called by git)`).action(async()=>{let t=await e(),n=await At(process.stdin,process.stdout,t);process.exit(n)}),n}function Pt(e){let t=e.endpoint.DEFAULTS;return/^https:\/\/(api\.)?github\.com$/.test(t.baseUrl)?`https://github.com`:t.baseUrl.replace(`/api/v3`,``)}async function Ft(e,t,n){let r={baseUrl:Pt(e),headers:{accept:`application/json`},...n},i=await e(t,r);if(`error`in i.data){let n=new ye(`${i.data.error_description} (${i.data.error}, ${i.data.error_uri})`,400,{request:e.endpoint.merge(t,r)});throw n.response=i,n}return i}async function It(e){let t=e.request||C,n={client_id:e.clientId};return`scopes`in e&&Array.isArray(e.scopes)&&(n.scope=e.scopes.join(` `)),Ft(t,`POST /login/device/code`,n)}async function Lt(e){let t=await Ft(e.request||C,`POST /login/oauth/access_token`,{client_id:e.clientId,device_code:e.code,grant_type:`urn:ietf:params:oauth:grant-type:device_code`}),n={clientType:e.clientType,clientId:e.clientId,token:t.data.access_token,scopes:t.data.scope.split(/\s+/).filter(Boolean)};if(`clientSecret`in e&&(n.clientSecret=e.clientSecret),e.clientType===`github-app`){if(`refresh_token`in t.data){let e=new Date(t.headers.date).getTime();n.refreshToken=t.data.refresh_token,n.expiresAt=Rt(e,t.data.expires_in),n.refreshTokenExpiresAt=Rt(e,t.data.refresh_token_expires_in)}delete n.scopes}return{...t,authentication:n}}function Rt(e,t){return new Date(e+t*1e3).toISOString()}async function zt(e,t){let n=Bt(e,t.auth);if(n)return n;let{data:r}=await It({clientType:e.clientType,clientId:e.clientId,request:t.request||e.request,scopes:t.auth.scopes||e.scopes});await e.onVerification(r);let i=await Ht(t.request||e.request,e.clientId,e.clientType,r);return e.authentication=i,i}function Bt(e,t){if(t.refresh===!0||!e.authentication)return!1;if(e.clientType===`github-app`)return e.authentication;let n=e.authentication;return(`scopes`in t&&t.scopes||e.scopes).join(` `)===n.scopes.join(` `)?n:!1}async function Vt(e){await new Promise(t=>setTimeout(t,e*1e3))}async function Ht(e,t,n,r){try{let i={clientId:t,request:e,code:r.device_code},{authentication:a}=n===`oauth-app`?await Lt({...i,clientType:`oauth-app`}):await Lt({...i,clientType:`github-app`});return{type:`token`,tokenType:`oauth`,...a}}catch(i){if(!i.response)throw i;let a=i.response.data.error;if(a===`authorization_pending`)return await Vt(r.interval),Ht(e,t,n,r);if(a===`slow_down`)return await Vt(r.interval+7),Ht(e,t,n,r);throw i}}async function Ut(e,t){return zt(e,{auth:t})}async function Wt(e,t,n,r){let i=t.endpoint.merge(n,r);if(/\/login\/(oauth\/access_token|device\/code)$/.test(i.url))return t(i);let{token:a}=await zt(e,{request:t,auth:{type:`oauth`}});return i.headers.authorization=`token ${a}`,t(i)}var Gt=`0.0.0-development`;function Kt(e){let t=e.request||C.defaults({headers:{"user-agent":`octokit-auth-oauth-device.js/${Gt} ${be()}`}}),{request:n=t,...r}=e,i=e.clientType===`github-app`?{...r,clientType:`github-app`,request:n}:{...r,clientType:`oauth-app`,request:n,scopes:e.scopes||[]};if(!e.clientId)throw Error(`[@octokit/auth-oauth-device] "clientId" option must be set (https://github.com/octokit/auth-oauth-device.js#usage)`);if(!e.onVerification)throw Error(`[@octokit/auth-oauth-device] "onVerification" option must be a function (https://github.com/octokit/auth-oauth-device.js#usage)`);return Object.assign(Ut.bind(null,i),{hook:Wt.bind(null,i)})}async function qt(e){let{clientId:t,scopes:n=[`repo`,`read:user`,`user:email`],onVerification:r,host:i}=e,a=i&&i!==`github.com`?`https://${i}/api/v3`:`https://api.github.com`,o=Kt({clientType:`oauth-app`,clientId:t,scopes:n,onVerification:async e=>{await r({verificationUri:e.verification_uri,userCode:e.user_code,expiresIn:e.expires_in,interval:e.interval})},request:a===`https://api.github.com`?void 0:(await import(`./dist-bundle-BmF5_Mcp.mjs`)).request.defaults({baseUrl:a})}),s;try{s=await o({type:`oauth`})}catch(e){if(e instanceof Error){let t=e.message.toLowerCase();throw t.includes(`access_denied`)?Error(`Device-flow authorization was denied.`):t.includes(`expired_token`)||t.includes(`timeout`)||t.includes(`timed out`)?Error(`Device-flow code expired before authorization — please try again.`):Error(`GitHub sign-in failed: ${e.message}`)}throw e}return{token:s.token,tokenType:s.tokenType,scopes:s.scopes??[]}}const Jt={invalid_type:`invalid_type`,too_big:`too_big`,too_small:`too_small`,invalid_format:`invalid_format`,not_multiple_of:`not_multiple_of`,unrecognized_keys:`unrecognized_keys`,invalid_union:`invalid_union`,invalid_key:`invalid_key`,invalid_element:`invalid_element`,invalid_value:`invalid_value`,custom:`custom`};var Yt;(function(e){})(Yt||={});function Xt(e){return pe(ve,e)}function Zt(){return process.env.OPEN_KNOWLEDGE_GITHUB_CLIENT_ID??`Ov23liqlSd0V1MwR6rhI`}const Qt=new Set([`gitlab.com`,`bitbucket.org`,`codeberg.org`,`gitea.com`,`sr.ht`,`sourcehut.org`]);function $t(e){let t=e.toLowerCase().replace(/:\d+$/,``);Qt.has(t)&&(process.stderr.write(`Error: ${e} is not a GitHub host. Only GitHub and GitHub Enterprise Server are supported.\n`),process.exit(1))}function en(e,t){e&&process.stdout.write(`${JSON.stringify(t)}\n`)}async function tn(e,t,n=qt){let r=Zt(),{host:i,json:a}=e;$t(i),a||process.stderr.write(`Logging in to ${i}\n`);let o=await n({clientId:r,host:i===`github.com`?void 0:i,onVerification:e=>{e.userCode,e.verificationUri,a?en(!0,{type:`verification`,user_code:e.userCode,verification_uri:e.verificationUri,expires_in:e.expiresIn}):process.stderr.write(`Open: ${e.verificationUri}\nEnter code: ${e.userCode}\n`)}}),s=`unknown`,c,l;try{let e=i===`github.com`?`https://api.github.com`:`https://${i}/api/v3`,t=await fetch(`${e}/user`,{headers:{Authorization:`Bearer ${o.token}`,"User-Agent":`open-knowledge-cli`,Accept:`application/vnd.github+json`}});if(t.ok){let e=await t.json();s=e.login??s,c=e.name??void 0,l=e.email??void 0}}catch{}await t.set(i,s,o.token,{gitProtocol:`https`,name:c,email:l}),a?en(!0,{type:`complete`,host:i,login:s}):process.stderr.write(`✓ Logged in as ${s} on ${i}\n`)}function nn(e){return new t(`login`).description(`Authenticate with GitHub via Device Flow`).option(`--host <host>`,`GitHub or GitHub Enterprise hostname`,`github.com`).option(`--json`,`Output JSONL progress events`,!1).action(async t=>{await tn(t,await e())})}const rn=e=>e.name===`enter`||e.name===`return`;var an=class extends Error{name=`AbortPromptError`;message=`Prompt was aborted`;constructor(e){super(),this.cause=e?.cause}},on=class extends Error{name=`CancelPromptError`;message=`Prompt was canceled`},sn=class extends Error{name=`ExitPromptError`},cn=class extends Error{name=`HookError`},ln=class extends Error{name=`ValidationError`};const un=new _t;function dn(e){return{rl:e,hooks:[],hooksCleanup:[],hooksEffect:[],index:0,handleChange(){}}}function fn(e,t){let n=dn(e);return un.run(n,()=>{function e(e){n.handleChange=()=>{n.index=0,e()},n.handleChange()}return t(e)})}function L(){let e=un.getStore();if(!e)throw new cn(`[Inquirer] Hook functions can only be called from within a prompt`);return e}function pn(){return L().rl}function mn(e){return vt.bind((...t)=>{let n=L(),r=!1,i=n.handleChange;n.handleChange=()=>{r=!0};let a=e(...t);return r&&i(),n.handleChange=i,a})}function hn(e){let t=L(),{index:n}=t,r=e({get(){return t.hooks[n]},set(e){t.hooks[n]=e},initialized:n in t.hooks});return t.index++,r}function gn(){L().handleChange()}const R={queue(e){let t=L(),{index:n}=t;t.hooksEffect.push(()=>{t.hooksCleanup[n]?.();let r=e(pn());if(r!=null&&typeof r!=`function`)throw new ln(`useEffect return value must be a cleanup function or nothing.`);t.hooksCleanup[n]=r})},run(){let e=L();mn(()=>{e.hooksEffect.forEach(e=>{e()}),e.hooksEffect.length=0})()},clearAll(){let e=L();e.hooksCleanup.forEach(e=>{e?.()}),e.hooksEffect.length=0,e.hooksCleanup.length=0}};function _n(e){return typeof e==`function`}function z(e){return hn(t=>{let n=vt.bind(function(e){t.get()!==e&&(t.set(e),gn())});if(t.initialized)return[t.get(),n];let r=_n(e)?e():e;return t.set(r),[r,n]})}function vn(e,t){hn(n=>{let r=n.get();(!Array.isArray(r)||t.some((e,t)=>!Object.is(e,r[t])))&&R.queue(e),n.set(t)})}const yn={prefix:{idle:I(`blue`,`?`),done:I(`green`,Ae.tick)},spinner:{interval:80,frames:[`⠋`,`⠙`,`⠹`,`⠸`,`⠼`,`⠴`,`⠦`,`⠧`,`⠇`,`⠏`].map(e=>I(`yellow`,e))},style:{answer:e=>I(`cyan`,e),message:e=>I(`bold`,e),error:e=>I(`red`,`> ${e}`),defaultAnswer:e=>I(`dim`,`(${e})`),help:e=>I(`dim`,e),highlight:e=>I(`cyan`,e),key:e=>I(`cyan`,I(`bold`,`<${e}>`))}};function bn(e){if(typeof e!=`object`||!e)return!1;let t=e;for(;Object.getPrototypeOf(t)!==null;)t=Object.getPrototypeOf(t);return Object.getPrototypeOf(e)===t}function xn(...e){let t={};for(let n of e)for(let[e,r]of Object.entries(n)){let n=t[e];t[e]=bn(n)&&bn(r)?xn(n,r):r}return t}function Sn(...e){return xn(yn,...e.filter(e=>e!=null))}function Cn({status:e=`idle`,theme:t}){let[n,r]=z(!1),[i,a]=z(0),{prefix:o,spinner:s}=Sn(t);return vn(()=>{if(e===`loading`){let e,t=-1,n=setTimeout(()=>{r(!0),e=setInterval(()=>{t+=1,a(t%s.frames.length)},s.interval)},300);return()=>{clearTimeout(n),clearInterval(e)}}else r(!1)},[e]),n?s.frames[i]:typeof o==`string`?o:o[e===`loading`?`idle`:e]??o.idle}function wn(e){return z({current:e})[0]}function Tn(e){let t=wn(e);t.current=e,vn(e=>{let n=!1,r=mn((r,i)=>{n||t.current(i,e)});return e.input.on(`keypress`,r),()=>{n=!0,e.input.removeListener(`keypress`,r)}},[])}var En=e(we(),1);function Dn(e,t){return e.split(`
@@ -17,10 +17,10 @@ import{o as e}from"./chunk-FK9Q3tQk.mjs";import{t}from"./esm-CEs3LWY3.mjs";impor
17
17
  `),process.exit(1)}await t.set(r,c,a,{gitProtocol:`https`,name:l,email:u}),i?process.stdout.write(`${JSON.stringify({type:`complete`,host:r,login:c})}\n`):process.stderr.write(`✓ PAT stored for ${c} on ${r}\n`)}function Pr(e){return new t(`pat`).description(`Store a Personal Access Token`).option(`--host <host>`,`GitHub or GitHub Enterprise hostname`,`github.com`).option(`--json`,`Output JSON`,!1).action(async t=>{await Nr(t,await e())})}function Fr(e){try{let t=Ye(`gh`,[`auth`,`token`,...e?[`--hostname`,e]:[]],{encoding:`utf-8`,stdio:[`ignore`,`pipe`,`pipe`],timeout:5e3}).trim();return t.length===0?{available:!1}:{available:!0,token:t}}catch{return{available:!1}}}async function Ir(e,t,n=Fr){let r=n(e);return r.available&&r.token?r.token:(await t.get(e))?.token??null}async function Lr(e,t){let{host:n,json:r}=e;$t(n);let i=await Ir(n,t);i??(process.stderr.write(`Not logged in to ${n}\n`),process.exit(1));let a=n===`github.com`?void 0:`https://${n}/api/v3`,o=new Mr({auth:i,...a?{baseUrl:a}:{}}),s=[];for await(let e of o.paginate.iterator(o.repos.listForAuthenticatedUser,{per_page:100,sort:`updated`}))for(let t of e.data)s.push({full_name:t.full_name,clone_url:t.clone_url,private:t.private});if(r)process.stdout.write(`${JSON.stringify({type:`repos`,host:n,repos:s})}\n`);else for(let e of s)process.stdout.write(`${e.full_name} ${e.clone_url}\n`)}function Rr(e){return new t(`repos`).description(`List accessible repositories`).option(`--host <host>`,`GitHub or GitHub Enterprise hostname`,`github.com`).option(`--json`,`Output JSON`,!1).action(async t=>{await Lr(t,await e())})}async function zr(e,t){let{host:n}=e;await t.clear(n),process.stderr.write(`✓ Signed out from ${n}\n`)}function Br(e){return new t(`signout`).description(`Remove stored credentials`).option(`--host <host>`,`GitHub hostname`,`github.com`).action(async t=>{await zr(t,await e())})}async function Vr(e,t,n=Fr){let r=n(e);if(r.available&&r.token)return{tier:`A`,token:r.token};let i=await t.get(e);return i==null?{tier:`none`}:{tier:i.gitProtocol===`ssh`?`C`:`B`,token:i.token}}async function Hr(e,t){let{host:n,json:r}=e;$t(n);let i=await Vr(n,t);i.tier===`none`&&(r?process.stdout.write(`${JSON.stringify({type:`status`,host:n,authenticated:!1})}\n`):process.stderr.write(`Not logged in to ${n}\n`),process.exit(1));let a=n===`github.com`?void 0:`https://${n}/api/v3`,o=new Mr({auth:i.token,...a?{baseUrl:a}:{}});try{let{data:e}=await o.users.getAuthenticated();r?process.stdout.write(`${JSON.stringify({type:`status`,host:n,authenticated:!0,tier:i.tier,login:e.login,name:e.name,email:e.email})}\n`):process.stderr.write(`✓ Logged in as ${e.login} on ${n}\n`)}catch{r?process.stdout.write(JSON.stringify({type:`status`,host:n,authenticated:!1,error:`token invalid`})+`
18
18
  `):process.stderr.write(`✗ Token invalid for ${n}\n`),process.exit(1)}}function Ur(e){return new t(`status`).description(`Show authentication status`).option(`--host <host>`,`GitHub or GitHub Enterprise hostname`,`github.com`).option(`--json`,`Output JSON`,!1).action(async t=>{await Hr(t,await e())})}function Wr(){let e=new t(`auth`);e.description(`GitHub authentication management`);let n=()=>kt();return e.addCommand(nn(n)),e.addCommand(Ur(n)),e.addCommand(Rr(n)),e.addCommand(Br(n)),e.addCommand(Pr(n)),e.addCommand(Nt(n)),e}function V(e,t,n={}){let r=Ie(e,t);if(!P(r))return{status:`missing`,lockPath:r};let i;try{i=JSON.parse(F(r,`utf-8`))}catch{return{status:`corrupt`,lockPath:r}}if(!i||typeof i!=`object`||!Pe(i.pid))return{status:`corrupt`,lockPath:r};let a=i;if(!(n.isAlive??w)(a.pid))return{status:`dead-pid`,lockPath:r,lock:a};let o=n.host??mt();return a.hostname===o?{status:`alive`,lockPath:r,lock:a}:{status:`foreign-host`,lockPath:r,lock:a}}function Gr(e,t){let n=[];for(let[r,i]of[[`server`,e],[`ui`,t]])(i.status===`dead-pid`||i.status===`corrupt`)&&n.push({name:r,lockPath:i.lockPath,reason:i.status});return{prune:n}}function Kr(e){let t=e.inspect??(t=>V(e.lockDir,t)),n=e.unlink??(e=>ut(e)),r=e.log??(e=>console.log(e)),i=e.error??(e=>console.error(e)),a=Gr(t(`server`),t(`ui`));if(a.prune.length===0)return r(`No stale locks.`),{pruned:[],failed:[]};let o=[],s=[];for(let e of a.prune)try{n(e.lockPath),o.push(e)}catch(t){s.push({target:e,error:t instanceof Error?t.message:String(t)})}if(o.length>0){let e=o.map(e=>`${e.name} (${e.reason})`).join(`, `);r(`Pruned ${o.length} stale lock${o.length===1?``:`s`}: ${e}`)}return s.length>0&&i(`Failed to prune: ${s.map(({target:e,error:t})=>`${e.name} (${e.lockPath}): ${t}`).join(`; `)}`),{pruned:o,failed:s}}function qr(e){return new t(`clean`).description(`Prune stale / corrupt open-knowledge lock files (never touches live locks)`).action(()=>{e(),Kr({lockDir:ae(process.cwd())}).failed.length>0&&(process.exitCode=1)})}async function Jr(e,t,n={},r=Fr){if(!n.skipGhDetect&&r().available)return{tier:`A`,credentialArgs:[`-c`,`credential.helper=!gh auth git-credential`]};let i=await t.get(e);return i==null?{tier:`none`,credentialArgs:[]}:{tier:i.gitProtocol===`ssh`?`C`:`B`,credentialArgs:[`-c`,`credential.helper=!open-knowledge auth git-credential`]}}function Yr(e){return e.replace(/:\d+$/,``)}function Xr(e){let t=e.trim();if(!t)return null;{let e=/^https?:\/\/([^/?#]+)\/([\w.\-~%]+)\/([\w.\-~%]+?)(?:\.git)?\/?$/.exec(t);if(e)return{protocol:`https`,hostname:Yr(e[1]),owner:e[2],name:e[3]}}{let e=/^ssh:\/\/(?:[\w.-]+@)?([^/?#]+)\/([\w.\-~%]+)\/([\w.\-~%]+?)(?:\.git)?\/?$/.exec(t);if(e)return{protocol:`ssh`,hostname:Yr(e[1]),owner:e[2],name:e[3]}}{let e=/^git:\/\/([^/?#]+)\/([\w.\-~%]+)\/([\w.\-~%]+?)(?:\.git)?\/?$/.exec(t);if(e)return{protocol:`git`,hostname:Yr(e[1]),owner:e[2],name:e[3]}}{let e=/^(?:[\w.-]+@)?([\w.-]+):([\w.\-~%]+)\/([\w.\-~%]+?)(?:\.git)?$/.exec(t);if(e?.[1].includes(`.`)||e&&t.startsWith(`git@`))return{protocol:`ssh`,hostname:e[1],owner:e[2],name:e[3]}}{let e=/^git:([\w.-]+)\/([\w.\-~%]+)\/([\w.\-~%]+?)(?:\.git)?\/?$/.exec(t);if(e)return{protocol:`git`,hostname:e[1],owner:e[2],name:e[3]}}if(!t.includes(`://`)&&!t.includes(`@`)&&!t.startsWith(`/`)){let e=/^([\w.-]+)\/([\w.\-~%]+?)(?:\.git)?$/.exec(t);if(e)return{protocol:`https`,hostname:`github.com`,owner:e[1],name:e[2]}}return null}const Zr=[[`count`,0,10],[`compress`,10,20],[`receiv`,20,60],[`resolv`,60,100]];function Qr(e){let t=/^([\w ]+):\s+(\d+)%/.exec(e.trim());if(!t)return null;let n=t[1].toLowerCase(),r=Number(t[2]);for(let[e,i,a]of Zr)if(n.includes(e))return{stage:t[1],pct:Math.round(i+r/100*(a-i))};return null}function H(e,t){e&&process.stdout.write(`${JSON.stringify(t)}\n`)}async function $r(e,t,n,r=process.cwd()){let i=Xr(e);if(!i)throw Error(`Invalid git URL: ${e}`);let a=t.dir?N(r,t.dir):N(r,i.name);if(P(a)&&ot(a).length>0)throw Error(`Target directory is not empty: ${a}`);let o=await kt(),s=await Jr(i.hostname,o,{}),c=Re({baseDir:r,config:s.credentialArgs.length>=2?[s.credentialArgs[1]]:[],unsafe:{allowUnsafeCredentialHelper:!0}}).env({GIT_TERMINAL_PROMPT:`0`}),l=-1;c.outputHandler((e,n,r)=>{r.on(`data`,e=>{let n=e.toString(`utf-8`);for(let e of n.split(`
19
19
  `)){let n=Qr(e);n&&n.pct!==l&&(l=n.pct,H(t.json,{type:`progress`,pct:n.pct,stage:n.stage}),t.json||process.stderr.write(`\r Cloning ${n.pct}%`))}})}),await c.clone(e,a,[`--progress`]),t.json||process.stderr.write(`
20
- `);try{let{runInit:e}=await import(`./init-BMRmWZ_f.mjs`),n=await e({cwd:a,mcp:!1});if(n.contentUpdated.length>0){let e=`auto-init: updated ${n.contentUpdated.join(`, `)}`;t.json?H(!0,{type:`warning`,message:e}):process.stderr.write(` ${e}\n`)}}catch(e){let n=e instanceof Error?e.message:String(e);t.json?H(!0,{type:`warning`,message:`auto-init: ${n}`}):process.stderr.write(` auto-init: ${n}\n`)}try{ei(a)}catch(e){let n=e instanceof Error?e.message:String(e);t.json?H(!0,{type:`warning`,message:`git-exclude: ${n}`}):process.stderr.write(` git-exclude: ${n}\n`)}return a}function ei(e){let t=M(e,`.git`,`info`,`exclude`);if(!P(t))return`no-exclude`;let n=F(t,`utf-8`),r=new Set([`.ok`,`.ok/`,`/.ok`,`/.ok/`]);return n.split(`
20
+ `);try{let{runInit:e}=await import(`./init-5GzdDz9e.mjs`),n=await e({cwd:a,mcp:!1});if(n.contentUpdated.length>0){let e=`auto-init: updated ${n.contentUpdated.join(`, `)}`;t.json?H(!0,{type:`warning`,message:e}):process.stderr.write(` ${e}\n`)}}catch(e){let n=e instanceof Error?e.message:String(e);t.json?H(!0,{type:`warning`,message:`auto-init: ${n}`}):process.stderr.write(` auto-init: ${n}\n`)}try{ei(a)}catch(e){let n=e instanceof Error?e.message:String(e);t.json?H(!0,{type:`warning`,message:`git-exclude: ${n}`}):process.stderr.write(` git-exclude: ${n}\n`)}return a}function ei(e){let t=M(e,`.git`,`info`,`exclude`);if(!P(t))return`no-exclude`;let n=F(t,`utf-8`),r=new Set([`.ok`,`.ok/`,`/.ok`,`/.ok/`]);return n.split(`
21
21
  `).map(e=>e.trim()).some(e=>r.has(e))?`already-present`:(dt(t,`${n}${n.length===0||n.endsWith(`
22
22
  `)?``:`
23
- `}.ok/\n`,`utf-8`),`appended`)}function ti(e){return new t(`clone`).description(`Clone a git repository and open it`).argument(`<url>`,`Repository URL or owner/repo shorthand`).argument(`[dir]`,`Target directory (default: ./<repo-name>)`).option(`--json`,`Output JSONL progress events`,!1).action(async(t,n,r)=>{let i=e();try{let a=await $r(t,{json:r.json,dir:n},i);if(r.json)H(!0,{type:`complete`,dir:a});else{process.stderr.write(`✓ Cloned to ${a}\n`),process.chdir(a);let{startCommand:t}=await import(`./start-DL2EG5cj.mjs`);await t(e).parseAsync([],{from:`user`})}}catch(e){let t=e instanceof Error?e.message:String(e);r.json?H(!0,{type:`error`,message:t}):process.stderr.write(`✗ ${t}\n`),process.exitCode=1}})}const ni=[[`sync`],[`persistence`,`debounceMs`],[`persistence`,`maxDebounceMs`],[`server`,`port`],[`content`,`include`],[`content`,`exclude`]];function ri(e={}){let t=e.log??(e=>console.error(e)),n=e.error??(e=>console.error(e)),r=e.loadConfigFn??Be;try{let{sources:n}=r(e.cwd);return t(`✓ Configuration valid (sources: ${n.length===0?`defaults only`:n.join(`, `)})`),{ok:!0}}catch(e){return n(e instanceof Error?e.message:String(e)),{ok:!1}}}function ii(e){let t=(0,Tt.parseDocument)(F(e,`utf-8`));if(t.errors.length>0)throw Error(`Could not parse ${e}: ${t.errors.map(e=>e.message).join(`; `)}`);let n=[];for(let e of ni)t.hasIn(e)&&n.push(e.join(`.`));return n}function ai(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function oi(e){let t={};for(let n of e){let e=t;for(let t=0;t<n.length-1;t++){let r=n[t],i=e[r],a=ai(i)?i:{};e[r]=a,e=a}e[n[n.length-1]]=null}return t}async function si(e={}){let t=e.log??(e=>console.log(e)),n=e.error??(e=>console.error(e)),r=e.scope??`both`,i=e.dryRun??!1,a=e.cwd??process.cwd(),o=e.writeConfigPatchFn??oe,s=[];(r===`project`||r===`both`)&&s.push({scope:`project`,absPath:m(`project`,a,e.homedirOverride)}),(r===`user`||r===`both`)&&s.push({scope:`user`,absPath:m(`user`,a,e.homedirOverride)});let c=[],l=!0;for(let{scope:t,absPath:n}of s){if(!P(n)){c.push({path:n,scope:t,found:[],removed:[]});continue}let r;try{r=ii(n)}catch(e){let r=e instanceof Error?e.message:String(e);c.push({path:n,scope:t,found:[],removed:[],error:r}),l=!1;continue}if(r.length===0||i){c.push({path:n,scope:t,found:r,removed:[]});continue}let s=await o({cwd:a,scope:t,patch:oi(ni.filter(e=>r.includes(e.join(`.`)))),homedirOverride:e.homedirOverride});if(!s.ok){c.push({path:n,scope:t,found:r,removed:[],error:ue(s.error)}),l=!1;continue}c.push({path:n,scope:t,found:r,removed:r})}for(let e of c)e.error&&n(`✗ ${e.path}: ${e.error}`);let u=c.some(e=>e.error!==void 0),d=c.reduce((e,t)=>e+t.found.length,0);if(d===0&&!u)t(`No deprecated fields found.`);else if(d>0)for(let e of c)e.error||(e.found.length===0?t(` ${e.path}: no deprecated fields`):t(i?`[dry-run] ${e.path}: would remove ${e.found.length} field(s): ${e.found.join(`, `)}`:`✓ ${e.path}: removed ${e.removed.length} field(s): ${e.removed.join(`, `)}`));return{outcomes:c,ok:l}}function ci(){let e=new t(`config`).description(`Inspect and maintain Open Knowledge configuration files`);return e.command(`validate`).description(`Validate the merged config (defaults → user → project)`).action(()=>{ri({}).ok||(process.exitCode=1)}),e.command(`migrate`).description(`Remove deprecated config fields (sync.*, persistence.{debounceMs,maxDebounceMs}, server.port, content.{include,exclude}) idempotently`).option(`--scope <scope>`,`Which scope to migrate: project | user | both`,`both`).option(`--dry-run`,`Preview without writing`,!1).action(async e=>{let t=e.scope;if(t!==`project`&&t!==`user`&&t!==`both`){console.error(`Invalid --scope: ${t}. Expected: project | user | both`),process.exitCode=2;return}(await si({scope:t,dryRun:e.dryRun})).ok||(process.exitCode=1)}),e}var U=e(Je(),1);const W=2e3,li=[/cli\.mjs/,/(^|[\s/])(open-knowledge|ok)\s+(start|mcp|ui)(\s|$)/,/(^|[\s/])bun([\s/]).*?(run dev|packages\/app|vite|hocuspocus)/,/(^|[\s/])node([\s/]).*?(packages\/(cli|app)|vite|hocuspocus)/,/(^|\s)--ok-lock-dir-b64=/];function ui(e){return li.some(t=>t.test(e))}function di(e){let t=e.trim().split(/\s+/).find(e=>e.startsWith(`--ok-lock-dir-b64=`));if(t==null)return null;let n=t.slice(18);if(!n)return null;try{let e=Buffer.from(n,`base64url`).toString(`utf8`);return et(e)?e:null}catch{return null}}function fi(e){let t=[];for(let n of e.split(`
23
+ `}.ok/\n`,`utf-8`),`appended`)}function ti(e){return new t(`clone`).description(`Clone a git repository and open it`).argument(`<url>`,`Repository URL or owner/repo shorthand`).argument(`[dir]`,`Target directory (default: ./<repo-name>)`).option(`--json`,`Output JSONL progress events`,!1).action(async(t,n,r)=>{let i=e();try{let a=await $r(t,{json:r.json,dir:n},i);if(r.json)H(!0,{type:`complete`,dir:a});else{process.stderr.write(`✓ Cloned to ${a}\n`),process.chdir(a);let{startCommand:t}=await import(`./start-D_2Z0J-R.mjs`);await t(e).parseAsync([],{from:`user`})}}catch(e){let t=e instanceof Error?e.message:String(e);r.json?H(!0,{type:`error`,message:t}):process.stderr.write(`✗ ${t}\n`),process.exitCode=1}})}const ni=[[`sync`],[`persistence`,`debounceMs`],[`persistence`,`maxDebounceMs`],[`server`,`port`],[`content`,`include`],[`content`,`exclude`]];function ri(e={}){let t=e.log??(e=>console.error(e)),n=e.error??(e=>console.error(e)),r=e.loadConfigFn??Be;try{let{sources:n}=r(e.cwd);return t(`✓ Configuration valid (sources: ${n.length===0?`defaults only`:n.join(`, `)})`),{ok:!0}}catch(e){return n(e instanceof Error?e.message:String(e)),{ok:!1}}}function ii(e){let t=(0,Tt.parseDocument)(F(e,`utf-8`));if(t.errors.length>0)throw Error(`Could not parse ${e}: ${t.errors.map(e=>e.message).join(`; `)}`);let n=[];for(let e of ni)t.hasIn(e)&&n.push(e.join(`.`));return n}function ai(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function oi(e){let t={};for(let n of e){let e=t;for(let t=0;t<n.length-1;t++){let r=n[t],i=e[r],a=ai(i)?i:{};e[r]=a,e=a}e[n[n.length-1]]=null}return t}async function si(e={}){let t=e.log??(e=>console.log(e)),n=e.error??(e=>console.error(e)),r=e.scope??`both`,i=e.dryRun??!1,a=e.cwd??process.cwd(),o=e.writeConfigPatchFn??oe,s=[];(r===`project`||r===`both`)&&s.push({scope:`project`,absPath:m(`project`,a,e.homedirOverride)}),(r===`user`||r===`both`)&&s.push({scope:`user`,absPath:m(`user`,a,e.homedirOverride)});let c=[],l=!0;for(let{scope:t,absPath:n}of s){if(!P(n)){c.push({path:n,scope:t,found:[],removed:[]});continue}let r;try{r=ii(n)}catch(e){let r=e instanceof Error?e.message:String(e);c.push({path:n,scope:t,found:[],removed:[],error:r}),l=!1;continue}if(r.length===0||i){c.push({path:n,scope:t,found:r,removed:[]});continue}let s=await o({cwd:a,scope:t,patch:oi(ni.filter(e=>r.includes(e.join(`.`)))),homedirOverride:e.homedirOverride});if(!s.ok){c.push({path:n,scope:t,found:r,removed:[],error:ue(s.error)}),l=!1;continue}c.push({path:n,scope:t,found:r,removed:r})}for(let e of c)e.error&&n(`✗ ${e.path}: ${e.error}`);let u=c.some(e=>e.error!==void 0),d=c.reduce((e,t)=>e+t.found.length,0);if(d===0&&!u)t(`No deprecated fields found.`);else if(d>0)for(let e of c)e.error||(e.found.length===0?t(` ${e.path}: no deprecated fields`):t(i?`[dry-run] ${e.path}: would remove ${e.found.length} field(s): ${e.found.join(`, `)}`:`✓ ${e.path}: removed ${e.removed.length} field(s): ${e.removed.join(`, `)}`));return{outcomes:c,ok:l}}function ci(){let e=new t(`config`).description(`Inspect and maintain Open Knowledge configuration files`);return e.command(`validate`).description(`Validate the merged config (defaults → user → project)`).action(()=>{ri({}).ok||(process.exitCode=1)}),e.command(`migrate`).description(`Remove deprecated config fields (sync.*, persistence.{debounceMs,maxDebounceMs}, server.port, content.{include,exclude}) idempotently`).option(`--scope <scope>`,`Which scope to migrate: project | user | both`,`both`).option(`--dry-run`,`Preview without writing`,!1).action(async e=>{let t=e.scope;if(t!==`project`&&t!==`user`&&t!==`both`){console.error(`Invalid --scope: ${t}. Expected: project | user | both`),process.exitCode=2;return}(await si({scope:t,dryRun:e.dryRun})).ok||(process.exitCode=1)}),e}var U=e(Je(),1);const W=2e3,li=[/cli\.mjs/,/(^|[\s/])(open-knowledge|ok)\s+(start|mcp|ui)(\s|$)/,/(^|[\s/])bun([\s/]).*?(run dev|packages\/app|vite|hocuspocus)/,/(^|[\s/])node([\s/]).*?(packages\/(cli|app)|vite|hocuspocus)/,/(^|\s)--ok-lock-dir-b64=/];function ui(e){return li.some(t=>t.test(e))}function di(e){let t=e.trim().split(/\s+/).find(e=>e.startsWith(`--ok-lock-dir-b64=`));if(t==null)return null;let n=t.slice(18);if(!n)return null;try{let e=Buffer.from(n,`base64url`).toString(`utf8`);return et(e)?e:null}catch{return null}}function fi(e){let t=[];for(let n of e.split(`
24
24
  `)){let e=n.trim();if(!e)continue;let r=e.indexOf(` `);if(r===-1)continue;let i=e.slice(0,r),a=e.slice(r+1),o=Number.parseInt(i,10);!Number.isNaN(o)&&ui(a)&&t.push({pid:o,command:a})}return t}function pi(e){let t=[],n=e.split(`
25
25
  `);for(let e=1;e<n.length;e++){let r=n[e]?.trim();if(!r)continue;let i=r.indexOf(` `);if(i===-1)continue;let a=r.slice(0,i),o=r.slice(i+1).trim(),s=Number.parseInt(a,10);!Number.isNaN(s)&&ui(o)&&t.push({pid:s,command:o})}return t}async function mi(){let e=j(`pgrep`,[`-a`,`-f`,`cli\\.mjs|open-knowledge|--ok-lock-dir-b64=|(^|[ /])ok[ ]+(start|mcp|ui)([ ]|$)|packages/(cli|app)|hocuspocus|vite`],{encoding:`utf-8`,timeout:W});if(!(e.error!=null&&e.error.code===`ENOENT`)){let t=e.stdout??``,n=fi(t);if(n.length>0||t.trim()===``)return n}let t=j(`ps`,[`-axo`,`pid,command`],{encoding:`utf-8`,timeout:W});return t.error!=null||!t.stdout?[]:pi(t.stdout)}function hi(e){let t=e.trim().split(/\s+/).filter(Boolean);for(let e of t){if(e.startsWith(`@`))continue;let t=Qe(e);if(t===`open-knowledge`||t===`ok`||e.endsWith(`/packages/cli/src/cli.ts`)||e.endsWith(`/packages/cli/dist/cli.mjs`)||t===`cli.mjs`||t===`cli.ts`)return e}return null}function gi(e){let t=j(`ps`,[`-p`,String(e),`-o`,`command=`],{encoding:`utf-8`,timeout:W});return t.error!=null||!t.stdout?null:t.stdout.trim()||null}function _i(e){let t=j(`ps`,[`-p`,String(e),`-o`,`%cpu=,%mem=`],{encoding:`utf-8`,timeout:W});if(t.error!=null||!t.stdout)return null;let[n,r]=t.stdout.trim().split(/\s+/),i=Number.parseFloat(n??``),a=Number.parseFloat(r??``);return Number.isNaN(i)||Number.isNaN(a)?null:{cpuPercent:i,memPercent:a}}async function vi(e){let t=j(`lsof`,[`-p`,String(e),`-a`,`-d`,`cwd`,`-Fn`],{encoding:`utf-8`,timeout:W});if(t.error!=null)return null;let n=t.stdout??``;for(let e of n.split(`
26
26
  `))if(e.startsWith(`n`)&&e.length>1)return e.slice(1);return null}function yi(e){let t=[],n=e.split(`
@@ -57,7 +57,7 @@ setTimeout(() => process.exit(2), ${t+1e4});
57
57
  `,r),o=-1;if(i!==-1&&a!==-1?o=Math.min(i,a):i===-1?a!==-1&&(o=a):o=i===e.length-1?-1:i,o===-1){n=e.slice(r);break}else{let n=e.slice(r,o);t.push(n),r=o+1,e[r-1]===`\r`&&e[r]===`
58
58
  `&&r++}}return[t,n]}var co=class extends TransformStream{constructor({onError:e,onRetry:t,onComment:n}={}){let r;super({start(i){r=oo({onEvent:e=>{i.enqueue(e)},onError(t){e===`terminate`?i.error(t):typeof e==`function`&&e(t)},onRetry:t,onComment:n})},transform(e){r.feed(e)}})}};const lo={initialReconnectionDelay:1e3,maxReconnectionDelay:3e4,reconnectionDelayGrowFactor:1.5,maxRetries:2};var Y=class extends Error{constructor(e,t){super(`Streamable HTTP error: ${t}`),this.code=e}},uo=class{constructor(e,t){this._hasCompletedAuthFlow=!1,this._url=e,this._resourceMetadataUrl=void 0,this._scope=void 0,this._requestInit=t?.requestInit,this._authProvider=t?.authProvider,this._fetch=t?.fetch,this._fetchWithInit=qi(t?.fetch,t?.requestInit),this._sessionId=t?.sessionId,this._reconnectionOptions=t?.reconnectionOptions??lo}async _authThenStart(){if(!this._authProvider)throw new J(`No auth provider`);let e;try{e=await La(this._authProvider,{serverUrl:this._url,resourceMetadataUrl:this._resourceMetadataUrl,scope:this._scope,fetchFn:this._fetchWithInit})}catch(e){throw this.onerror?.(e),e}if(e!==`AUTHORIZED`)throw new J;return await this._startOrAuthSse({resumptionToken:void 0})}async _commonHeaders(){let e={};if(this._authProvider){let t=await this._authProvider.tokens();t&&(e.Authorization=`Bearer ${t.access_token}`)}this._sessionId&&(e[`mcp-session-id`]=this._sessionId),this._protocolVersion&&(e[`mcp-protocol-version`]=this._protocolVersion);let t=Ki(this._requestInit?.headers);return new Headers({...e,...t})}async _startOrAuthSse(e){let{resumptionToken:t}=e;try{let n=await this._commonHeaders();n.set(`Accept`,`text/event-stream`),t&&n.set(`last-event-id`,t);let r=await(this._fetch??fetch)(this._url,{method:`GET`,headers:n,signal:this._abortController?.signal});if(!r.ok){if(await r.body?.cancel(),r.status===401&&this._authProvider)return await this._authThenStart();if(r.status===405)return;throw new Y(r.status,`Failed to open SSE stream: ${r.statusText}`)}this._handleSseStream(r.body,e,!0)}catch(e){throw this.onerror?.(e),e}}_getNextReconnectionDelay(e){if(this._serverRetryMs!==void 0)return this._serverRetryMs;let t=this._reconnectionOptions.initialReconnectionDelay,n=this._reconnectionOptions.reconnectionDelayGrowFactor,r=this._reconnectionOptions.maxReconnectionDelay;return Math.min(t*n**+e,r)}_scheduleReconnection(e,t=0){let n=this._reconnectionOptions.maxRetries;if(t>=n){this.onerror?.(Error(`Maximum reconnection attempts (${n}) exceeded.`));return}let r=this._getNextReconnectionDelay(t);this._reconnectionTimeout=setTimeout(()=>{this._startOrAuthSse(e).catch(n=>{this.onerror?.(Error(`Failed to reconnect SSE stream: ${n instanceof Error?n.message:String(n)}`)),this._scheduleReconnection(e,t+1)})},r)}_handleSseStream(e,t,n){if(!e)return;let{onresumptiontoken:r,replayMessageId:i}=t,a,o=!1,s=!1;(async()=>{try{let t=e.pipeThrough(new TextDecoderStream).pipeThrough(new co({onRetry:e=>{this._serverRetryMs=e}})).getReader();for(;;){let{value:e,done:n}=await t.read();if(n)break;if(e.id&&(a=e.id,o=!0,r?.(e.id)),e.data&&(!e.event||e.event===`message`))try{let t=p.parse(JSON.parse(e.data));g(t)&&(s=!0,i!==void 0&&(t.id=i)),this.onmessage?.(t)}catch(e){this.onerror?.(e)}}(n||o)&&!s&&this._abortController&&!this._abortController.signal.aborted&&this._scheduleReconnection({resumptionToken:a,onresumptiontoken:r,replayMessageId:i},0)}catch(e){if(this.onerror?.(Error(`SSE stream disconnected: ${e}`)),(n||o)&&!s&&this._abortController&&!this._abortController.signal.aborted)try{this._scheduleReconnection({resumptionToken:a,onresumptiontoken:r,replayMessageId:i},0)}catch(e){this.onerror?.(Error(`Failed to reconnect: ${e instanceof Error?e.message:String(e)}`))}}})()}async start(){if(this._abortController)throw Error(`StreamableHTTPClientTransport already started! If using Client class, note that connect() calls start() automatically.`);this._abortController=new AbortController}async finishAuth(e){if(!this._authProvider)throw new J(`No auth provider`);if(await La(this._authProvider,{serverUrl:this._url,authorizationCode:e,resourceMetadataUrl:this._resourceMetadataUrl,scope:this._scope,fetchFn:this._fetchWithInit})!==`AUTHORIZED`)throw new J(`Failed to authorize`)}async close(){this._reconnectionTimeout&&=(clearTimeout(this._reconnectionTimeout),void 0),this._abortController?.abort(),this.onclose?.()}async send(e,t){try{let{resumptionToken:n,onresumptiontoken:r}=t||{};if(n){this._startOrAuthSse({resumptionToken:n,replayMessageId:d(e)?e.id:void 0}).catch(e=>this.onerror?.(e));return}let i=await this._commonHeaders();i.set(`content-type`,`application/json`),i.set(`accept`,`application/json, text/event-stream`);let a={...this._requestInit,method:`POST`,headers:i,body:JSON.stringify(e),signal:this._abortController?.signal},o=await(this._fetch??fetch)(this._url,a),s=o.headers.get(`mcp-session-id`);if(s&&(this._sessionId=s),!o.ok){let t=await o.text().catch(()=>null);if(o.status===401&&this._authProvider){if(this._hasCompletedAuthFlow)throw new Y(401,`Server returned 401 after successful authentication`);let{resourceMetadataUrl:t,scope:n}=Va(o);if(this._resourceMetadataUrl=t,this._scope=n,await La(this._authProvider,{serverUrl:this._url,resourceMetadataUrl:this._resourceMetadataUrl,scope:this._scope,fetchFn:this._fetchWithInit})!==`AUTHORIZED`)throw new J;return this._hasCompletedAuthFlow=!0,this.send(e)}if(o.status===403&&this._authProvider){let{resourceMetadataUrl:t,scope:n,error:r}=Va(o);if(r===`insufficient_scope`){let r=o.headers.get(`WWW-Authenticate`);if(this._lastUpscopingHeader===r)throw new Y(403,`Server returned 403 after trying upscoping`);if(n&&(this._scope=n),t&&(this._resourceMetadataUrl=t),this._lastUpscopingHeader=r??void 0,await La(this._authProvider,{serverUrl:this._url,resourceMetadataUrl:this._resourceMetadataUrl,scope:this._scope,fetchFn:this._fetch})!==`AUTHORIZED`)throw new J;return this.send(e)}}throw new Y(o.status,`Error POSTing to endpoint: ${t}`)}if(this._hasCompletedAuthFlow=!1,this._lastUpscopingHeader=void 0,o.status===202){await o.body?.cancel(),ie(e)&&this._startOrAuthSse({resumptionToken:void 0}).catch(e=>this.onerror?.(e));return}let c=(Array.isArray(e)?e:[e]).filter(e=>`method`in e&&`id`in e&&e.id!==void 0).length>0,l=o.headers.get(`content-type`);if(c)if(l?.includes(`text/event-stream`))this._handleSseStream(o.body,{onresumptiontoken:r},!1);else if(l?.includes(`application/json`)){let e=await o.json(),t=Array.isArray(e)?e.map(e=>p.parse(e)):[p.parse(e)];for(let e of t)this.onmessage?.(e)}else throw await o.body?.cancel(),new Y(-1,`Unexpected content type: ${l}`);else await o.body?.cancel()}catch(e){throw this.onerror?.(e),e}}get sessionId(){return this._sessionId}async terminateSession(){if(this._sessionId)try{let e=await this._commonHeaders(),t={...this._requestInit,method:`DELETE`,headers:e,signal:this._abortController?.signal},n=await(this._fetch??fetch)(this._url,t);if(await n.body?.cancel(),!n.ok&&n.status!==405)throw new Y(n.status,`Failed to terminate session: ${n.statusText}`);this._sessionId=void 0}catch(e){throw this.onerror?.(e),e}}setProtocolVersion(e){this._protocolVersion=e}get protocolVersion(){return this._protocolVersion}async resumeStream(e,t){await this._startOrAuthSse({resumptionToken:e,onresumptiontoken:t?.onresumptiontoken})}};function fo(e){return(t,n)=>{if((n?.method??`GET`).toUpperCase()===`GET`)return globalThis.fetch(t,n);let r=new AbortController,i=setTimeout(()=>r.abort(Error(`MCP request timed out after ${e}ms`)),e),a=n?.signal instanceof AbortSignal?AbortSignal.any([n.signal,r.signal]):r.signal;return globalThis.fetch(t,{...n,signal:a}).finally(()=>clearTimeout(i))}}function po(e){if(e===void 0||e===``)return;let t=Number.parseInt(e,10);if(!(Number.isNaN(t)||t<=0))return t}function mo(e){return e===`0.0.0.0`||e===`::`?`localhost`:e.includes(`:`)&&!e.startsWith(`[`)?`[${e}]`:e}function ho(e,t){return`http://${mo(e)}:${t}/mcp`}function go(e,t){return`ws://${mo(e)}:${t}`}function _o(e){let t=new URL(e);return t.protocol=t.protocol===`https:`?`wss:`:`ws:`,t.pathname=``,t.search=``,t.hash=``,t.toString().replace(/\/$/,``)}function vo(e,t){if(!(!e||e.port<=0)&&t(e.pid))return e.port}function yo(e){return P(e)?F(e,`utf-8`).trim():``}function bo(e,t){return`server did not start within ${e}ms${t?` stderr:\n${t}`:``}`}function xo(e){if(e&&typeof e==`object`&&`method`in e&&`id`in e)return e.id}function So(e){if(!e||typeof e!=`object`||!(`result`in e))return;let t=e.result;if(!t||typeof t!=`object`||!(`protocolVersion`in t))return;let n=t.protocolVersion;return typeof n==`string`?n:void 0}function Co(e,t){return{jsonrpc:`2.0`,id:e,error:{code:-32e3,message:t instanceof Error?t.message:String(t)}}}async function wo(e){let t=e.readLock??(()=>Fe(e.lockDir)),n=e.isAlive??w,r=e.sleep??(e=>Ct(e)),i=e.spawn??Xe,a=e.readErrorLog??yo,o=e.openErrorLog??(e=>at(e,`w`)),s=e.closeFd??nt,c=e.timeoutMs??5e3,l=e.pollIntervalMs??100;if(e.portOverride!==void 0){let t=Number.parseInt(e.portOverride,10);if(Number.isNaN(t)||t<=0)throw Error(`invalid --port value '${e.portOverride}' — HTTP MCP shim requires a positive port`);return ho(`localhost`,t)}let u=vo(t(),n);if(u!==void 0)return ho(`localhost`,u);if(e.envAutoStart===`0`)throw Error(`Open Knowledge server is not running and OK_MCP_AUTOSTART=0 disables auto-start.`);P(e.lockDir)||rt(e.lockDir,{recursive:!0});let d=M(e.lockDir,`last-spawn-error.log`),f=o(d),p,m,h=He();try{try{p=i(h.command,[...h.prefixArgs,`start`],{detached:!0,stdio:[`ignore`,`ignore`,f],cwd:e.contentDir,env:{...process.env,OK_LOCK_KIND:`mcp-spawned`}}),p.on(`error`,e=>{m=e instanceof Error?e.message:String(e)}),p.unref()}catch(e){m=e instanceof Error?e.message:String(e)}}finally{try{s(f)}catch{}}let g=Date.now()+c;for(;Date.now()<g;){if(m){let e=a(d),t=e?` stderr:\n${e}`:``;throw Error(`spawn failed: ${m}${t}`)}await r(l);let e=vo(t(),n);if(e!==void 0)return ho(`localhost`,e)}if(m){let e=a(d),t=e?` stderr:\n${e}`:``;throw Error(`spawn failed: ${m}${t}`)}throw Error(bo(c,a(d)))}function To(e,t){if(e.portOverride!==void 0)return _o(t);let n=e.readLock??(()=>Fe(e.lockDir)),r=e.isAlive??w,i=vo(n(),r);if(i!==void 0)return go(`localhost`,i)}async function Eo(e,t={}){let n=t.stderr??process.stderr,r=t.requestTimeoutMs??12e4,i=t.createStdioTransport?t.createStdioTransport(t.stdin,t.stdout):new Vi(t.stdin,t.stdout),a=t.createHttpTransport?t.createHttpTransport(new URL(e)):new uo(new URL(e),{fetch:fo(r),...t.connectionId===void 0?{}:{requestInit:{headers:{[c]:t.connectionId}}}}),o=!1,s=async()=>{o||(o=!0,t.onclose?.(),await Promise.allSettled([i.close(),a.close()]))};i.onerror=e=>{n.write(`[mcp-shim] stdio error: ${e.message}\n`)},a.onerror=e=>{n.write(`[mcp-shim] HTTP transport error: ${e.message}\n`)},i.onclose=()=>{s()},a.onclose=()=>{s()};let l=Promise.resolve();i.onmessage=e=>{l=l.then(async()=>{try{await a.send(e)}catch(t){let r=xo(e);if(r===void 0){n.write(`[mcp-shim] failed to forward stdio notification: ${t instanceof Error?t.message:String(t)}\n`);return}await i.send(Co(r,t)).catch(e=>{n.write(`[mcp-shim] failed to write stdio error response: ${e instanceof Error?e.message:String(e)}\n`)})}}).catch(e=>{n.write(`[mcp-shim] unexpected stdio forwarding failure: ${e instanceof Error?e.message:String(e)}\n`)})},a.onmessage=e=>{let t=So(e);t&&a.setProtocolVersion?.(t),i.send(e).catch(e=>{n.write(`[mcp-shim] failed to write stdio response: ${e instanceof Error?e.message:String(e)}\n`)})};try{await a.start(),await i.start()}catch(e){throw await s(),e}return{close:s}}async function Do(e){let t=e.stderr??process.stderr,n=e.bridgeFn??Eo,r=await wo(e),i=e.createConnectionId?.()??xt(),a=!1,o=(e.startKeepalive??Gi)({connectionId:i,resolveWsUrl:async()=>To(e,r),log:e=>t.write(`[mcp-shim] keepalive: ${e}\n`)});t.write(`[mcp-shim] proxying stdio to ${r}\n`);let s;try{s=await n(r,{stderr:t,connectionId:i,onclose:()=>{a||(o.close(),process.exit(0))}})}catch(e){throw o.close(),e}let c=()=>{a=!0,o.close(),s.close().finally(()=>{process.exit(0)})};process.once(`SIGINT`,c),process.once(`SIGTERM`,c)}const Oo=gt(import.meta.url);function ko(e){let t=N(e);for(;;){if(ge(t))return t;let n=$e(t);if(n===t)throw Error(`No Open Knowledge project found at or above ${e}. Pass an explicit \`cwd\` argument that points inside an OK project (a directory with a \`${se}\`).`);t=n}}function Ao(e){try{let t=new URL(e);return t.protocol===`file:`?gt(t):void 0}catch{return}}async function jo(e){if(!e.getClientCapabilities()?.roots)return;let t;try{t=await e.listRoots()}catch(t){e.log?.(`listRoots fallback failed: ${t instanceof Error?t.message:String(t)}`);return}let n=t.roots??[];if(n.length!==1)return;let r=Ao(n[0].uri);return r===void 0&&e.log?.(`single root URI not usable as fs path: ${n[0].uri}`),r}async function Mo(e,t){if(e!==void 0)return ko(e);let n=await t();if(n!==void 0)return ko(n);throw Error("`cwd` is required for tool calls against the global MCP server. Pass an absolute path inside an Open Knowledge project, or have the MCP client advertise a single root.")}async function No(e){let t=process.stderr,n=e.spawnTimeoutMs??po(process.env.OK_MCP_SPAWN_TIMEOUT_MS),r=e.envAutoStart??process.env.OK_MCP_AUTOSTART,a=Ve({startupCwd:e.startupCwd,startupConfig:e.startupConfig}),s=new x({name:o,version:Le},{instructions:he({dir:`.`})}),c=xt(),l={current:{connectionId:c,displayName:c,colorSeed:c}},u=new Map,d=e=>{if(u.has(e))return;let n=ee(e),r=l.current,i=Gi({connectionId:c,displayName:r.displayName,clientName:r.clientInfo?.name??r.displayName,colorSeed:r.colorSeed,resolveWsUrl:async()=>To({lockDir:n,contentDir:e},``),log:n=>t.write(`[mcp] keepalive[${e}]: ${n}\n`)});u.set(e,i)},f=()=>jo({getClientCapabilities:()=>s.server.getClientCapabilities(),listRoots:()=>s.server.listRoots(),log:e=>t.write(`[mcp] ${e}\n`)}),p=e=>Mo(e,f),m=async e=>{let t;if(e===void 0){let e=await f();if(e===void 0)return;t=ko(e)}else t=ko(e);let i=await a(t),o=await wo({lockDir:ee(t),contentDir:h(i,t),envAutoStart:r,...n===void 0?{}:{timeoutMs:n}});return d(t),o.replace(/\/mcp$/,``)};s.server.oninitialized=()=>{let e=s.server.getClientVersion(),t=le(e?.name,c);l.current={connectionId:c,clientInfo:e?{name:t,version:e.version}:void 0,displayName:t,colorSeed:t}},i(s,{serverUrl:m,resolveCwd:p,config:a,identityRef:l});let g=new Vi,_=!1,te,ne=async()=>{if(_)return;_=!0,te?.stop();for(let e of u.values())try{e.close()}catch(e){t.write(`[mcp] keepalive close error: ${e instanceof Error?e.message:String(e)}\n`)}u.clear();let e=await Promise.allSettled([s.close(),g.close()]);for(let n of e)if(n.status===`rejected`){let e=n.reason;t.write(`[mcp] shutdown close error: ${e instanceof Error?e.message:String(e)}\n`)}};await s.connect(g),t.write(`[mcp] global stdio server ready (per-call project routing)
59
59
  `);let v=!1,y=()=>{v||(v=!0,setTimeout(()=>{t.write(`[mcp] shutdown deadline (5s) reached — forcing exit(1)
60
- `),process.exit(1)},5e3).unref(),ne().finally(()=>{process.exit(0)}))};if(process.once(`SIGINT`,y),process.once(`SIGTERM`,y),process.platform===`darwin`){let e=Wi(Oo,{realpathSync:st,statInoSync:e=>lt(e).ino,log:e=>t.write(`${e}\n`)});if(e!==void 0){t.write(`[mcp] bundle identity anchor=${e.resolvedPath} inode=${e.inode} version=${Le}\n`);let{resolvedPath:n,inode:r}=e;te=Ui({detect:()=>Hi({bundleAnchorPath:Oo,currentInode:r,platform:process.platform,realpath:st,statInode:e=>lt(e).ino}),onReplaced:e=>{t.write(`[mcp] bundle replaced anchor=${n} bootInode=${e.currentInode} onDiskInode=${e.onDiskInode} version=${Le} — exiting for host respawn\n`),y()},log:e=>t.write(`[mcp] ${e}\n`)})}}return{close:ne}}function Po(e){return new t(`mcp`).description(`Start MCP stdio server for project knowledge base`).option(`-p, --port <port>`,`Override per-call routing and proxy stdio to this HTTP MCP port`,void 0).action(async t=>{try{let n=e(),r=process.cwd();if(t.port!==void 0){let e=po(process.env.OK_MCP_SPAWN_TIMEOUT_MS);await Do({lockDir:``,contentDir:``,portOverride:t.port,envAutoStart:process.env.OK_MCP_AUTOSTART,timeoutMs:e});return}await No({startupCwd:r,startupConfig:n})}catch(e){process.stderr.write(`MCP server failed to start: ${e instanceof Error?e.message:String(e)}\n`),process.exitCode=1}})}function Fo(e){return new t(`preview`).description(`Show what content the watcher will track (read-only)`).action(async()=>{let{previewContent:t,formatPreviewBlock:n}=await import(`./preview-Bae0MG8_.mjs`),r=e(),i=process.cwd(),a=h(r,i),o;try{o=t({projectDir:i,contentDir:a})}catch(e){console.error(`Content preview failed: ${e instanceof Error?e.message:String(e)}`),process.exitCode=1;return}process.stdout.write(`${n(o,i)}\n`),o.totalCount===0&&o.warnings.length>0&&(process.exitCode=1)})}function Io(e){return e==null?!1:e.includes(`--type=utility`)&&e.includes(`--utility-sub-type=node.mojom.NodeService`)}function Lo(e,t=Date.now()){let n=new Date(e).getTime();if(Number.isNaN(n))return`—`;let r=t-n,i=Math.floor(r/1e3);if(i<60)return`${i}s`;let a=Math.floor(i/60);if(a<60)return`${a}m ago`;let o=Math.floor(a/60);return o<24?`${o}h ago`:`${Math.floor(o/24)}d ago`}function Ro(e,t,n,r,i,a){if(t.status===`missing`||t.status===`corrupt`)return null;let o=t.lock,s=null;if(n.status!==`missing`&&n.status!==`corrupt`){let e=n.lock;s={port:e.port,status:n.status,pid:e.pid,startedAt:e.startedAt,usage:a}}return{directory:o.worktreeRoot,server:{port:o.port,status:t.status,pid:o.pid,startedAt:o.startedAt,usage:i},ui:s,hostname:o.hostname,lockPath:t.lockPath,binary:r==null?null:hi(r),command:r,isDesktop:Io(r)}}function zo(e){return e.ui==null?!1:e.ui.status===`alive`||e.ui.status===`foreign-host`}function Bo(e){let t=e.server.status;return t===`alive`||t===`foreign-host`?e.isDesktop?`desktop`:t===`alive`?`running`:`foreign`:t===`dead-pid`&&zo(e)?`ui-orphan`:`stale`}const Vo=new Set([`running`,`desktop`,`foreign`,`ui-orphan`]);function Ho(e){switch(e){case`running`:return U.default.green(e);case`desktop`:return U.default.blue(e);case`foreign`:return U.default.cyan(e);case`ui-orphan`:return U.default.magenta(e);case`stale`:return U.default.yellow(e)}}function Uo(e){return e==null?`—`:`${e.cpuPercent.toFixed(1)}% / ${e.memPercent.toFixed(1)}%`}function Wo(e){return`${Uo(e.server.usage)} | ${Uo(e.ui?.usage??null)}`}function Go(e){return`${e.server.port===0?`(starting)`:String(e.server.port)} / ${e.ui==null||e.ui.status===`dead-pid`?`—`:String(e.ui.port)}`}function Ko(e){if(e.length===0)return`No open-knowledge servers found.`;let t=[`DIRECTORY`,`PORTS (API/UI)`,`CPU/MEM (API | UI)`,`STATUS`,`PID`,`STARTED`,`BINARY`],n=e.map(e=>{let t=Bo(e),n=t===`ui-orphan`&&e.ui!=null?e.ui.pid:e.server.pid;return[e.directory,Go(e),Wo(e),t,String(n),Lo(e.server.startedAt),e.binary??`—`]}),r=t.length,i=t.map(e=>e.length);for(let e of n)for(let t=0;t<r;t++)i[t]=Math.max(i[t]??0,(e[t]??``).length);let a=t.map((e,t)=>e.padEnd(i[t]??0)).join(` `).trimEnd(),o=e.map((e,t)=>{let a=n[t]??[],o=[];for(let t=0;t<r;t++){let n=(a[t]??``).padEnd(i[t]??0);if(t===3){let r=a[t]??``;n=Ho(Bo(e))+` `.repeat(Math.max(0,(i[t]??0)-r.length))}o.push(n)}return o.join(` `).trimEnd()}),s=U.default.dim(`To stop a server: ok stop <port|pid|directory|all>`);return[a,...o,``,s].join(`
60
+ `),process.exit(1)},5e3).unref(),ne().finally(()=>{process.exit(0)}))};if(process.once(`SIGINT`,y),process.once(`SIGTERM`,y),process.platform===`darwin`){let e=Wi(Oo,{realpathSync:st,statInoSync:e=>lt(e).ino,log:e=>t.write(`${e}\n`)});if(e!==void 0){t.write(`[mcp] bundle identity anchor=${e.resolvedPath} inode=${e.inode} version=${Le}\n`);let{resolvedPath:n,inode:r}=e;te=Ui({detect:()=>Hi({bundleAnchorPath:Oo,currentInode:r,platform:process.platform,realpath:st,statInode:e=>lt(e).ino}),onReplaced:e=>{t.write(`[mcp] bundle replaced anchor=${n} bootInode=${e.currentInode} onDiskInode=${e.onDiskInode} version=${Le} — exiting for host respawn\n`),y()},log:e=>t.write(`[mcp] ${e}\n`)})}}return{close:ne}}function Po(e){return new t(`mcp`).description(`Start MCP stdio server for project knowledge base`).option(`-p, --port <port>`,`Override per-call routing and proxy stdio to this HTTP MCP port`,void 0).action(async t=>{try{let n=e(),r=process.cwd();if(t.port!==void 0){let e=po(process.env.OK_MCP_SPAWN_TIMEOUT_MS);await Do({lockDir:``,contentDir:``,portOverride:t.port,envAutoStart:process.env.OK_MCP_AUTOSTART,timeoutMs:e});return}await No({startupCwd:r,startupConfig:n})}catch(e){process.stderr.write(`MCP server failed to start: ${e instanceof Error?e.message:String(e)}\n`),process.exitCode=1}})}function Fo(e){return new t(`preview`).description(`Show what content the watcher will track (read-only)`).action(async()=>{let{previewContent:t,formatPreviewBlock:n}=await import(`./preview-CckqoGDk.mjs`),r=e(),i=process.cwd(),a=h(r,i),o;try{o=t({projectDir:i,contentDir:a})}catch(e){console.error(`Content preview failed: ${e instanceof Error?e.message:String(e)}`),process.exitCode=1;return}process.stdout.write(`${n(o,i)}\n`),o.totalCount===0&&o.warnings.length>0&&(process.exitCode=1)})}function Io(e){return e==null?!1:e.includes(`--type=utility`)&&e.includes(`--utility-sub-type=node.mojom.NodeService`)}function Lo(e,t=Date.now()){let n=new Date(e).getTime();if(Number.isNaN(n))return`—`;let r=t-n,i=Math.floor(r/1e3);if(i<60)return`${i}s`;let a=Math.floor(i/60);if(a<60)return`${a}m ago`;let o=Math.floor(a/60);return o<24?`${o}h ago`:`${Math.floor(o/24)}d ago`}function Ro(e,t,n,r,i,a){if(t.status===`missing`||t.status===`corrupt`)return null;let o=t.lock,s=null;if(n.status!==`missing`&&n.status!==`corrupt`){let e=n.lock;s={port:e.port,status:n.status,pid:e.pid,startedAt:e.startedAt,usage:a}}return{directory:o.worktreeRoot,server:{port:o.port,status:t.status,pid:o.pid,startedAt:o.startedAt,usage:i},ui:s,hostname:o.hostname,lockPath:t.lockPath,binary:r==null?null:hi(r),command:r,isDesktop:Io(r)}}function zo(e){return e.ui==null?!1:e.ui.status===`alive`||e.ui.status===`foreign-host`}function Bo(e){let t=e.server.status;return t===`alive`||t===`foreign-host`?e.isDesktop?`desktop`:t===`alive`?`running`:`foreign`:t===`dead-pid`&&zo(e)?`ui-orphan`:`stale`}const Vo=new Set([`running`,`desktop`,`foreign`,`ui-orphan`]);function Ho(e){switch(e){case`running`:return U.default.green(e);case`desktop`:return U.default.blue(e);case`foreign`:return U.default.cyan(e);case`ui-orphan`:return U.default.magenta(e);case`stale`:return U.default.yellow(e)}}function Uo(e){return e==null?`—`:`${e.cpuPercent.toFixed(1)}% / ${e.memPercent.toFixed(1)}%`}function Wo(e){return`${Uo(e.server.usage)} | ${Uo(e.ui?.usage??null)}`}function Go(e){return`${e.server.port===0?`(starting)`:String(e.server.port)} / ${e.ui==null||e.ui.status===`dead-pid`?`—`:String(e.ui.port)}`}function Ko(e){if(e.length===0)return`No open-knowledge servers found.`;let t=[`DIRECTORY`,`PORTS (API/UI)`,`CPU/MEM (API | UI)`,`STATUS`,`PID`,`STARTED`,`BINARY`],n=e.map(e=>{let t=Bo(e),n=t===`ui-orphan`&&e.ui!=null?e.ui.pid:e.server.pid;return[e.directory,Go(e),Wo(e),t,String(n),Lo(e.server.startedAt),e.binary??`—`]}),r=t.length,i=t.map(e=>e.length);for(let e of n)for(let t=0;t<r;t++)i[t]=Math.max(i[t]??0,(e[t]??``).length);let a=t.map((e,t)=>e.padEnd(i[t]??0)).join(` `).trimEnd(),o=e.map((e,t)=>{let a=n[t]??[],o=[];for(let t=0;t<r;t++){let n=(a[t]??``).padEnd(i[t]??0);if(t===3){let r=a[t]??``;n=Ho(Bo(e))+` `.repeat(Math.max(0,(i[t]??0)-r.length))}o.push(n)}return o.join(` `).trimEnd()}),s=U.default.dim(`To stop a server: ok stop <port|pid|directory|all>`);return[a,...o,``,s].join(`
61
61
  `)}async function qo(e={}){let t=e.discover??bi,n=e.inspect??V,r=e.log??(e=>console.log(e)),i=e.resolveCommand??gi,a=e.resolveUsage??_i,o=await t(),s=[];for(let e of o){let t=n(e,`server`),r=n(e,`ui`),o=Ro(e,t,r,t.status===`missing`||t.status===`corrupt`?null:i(t.lock.pid),t.status===`missing`||t.status===`corrupt`?null:a(t.lock.pid),r.status===`missing`||r.status===`corrupt`?null:a(r.lock.pid));o!=null&&s.push(o)}if(e.json){let e=s.map(e=>({...e,displayStatus:Bo(e)}));r(JSON.stringify(e,null,2));return}r(Ko(e.all?s:s.filter(e=>Vo.has(Bo(e)))))}function Jo(){return new t(`ps`).description(`List all running open-knowledge servers`).argument(`[modifier]`,`"all" to include stale (dead-pid) entries`).option(`--all`,`Include stale (dead-pid) entries (foreign-host shows by default)`).option(`--json`,`Emit structured JSON (always includes all statuses)`).action(async(e,t)=>{await qo({all:t.all===!0||e===`all`,json:t.json===!0})})}function X(e,t){e&&process.stdout.write(`${JSON.stringify(t)}\n`)}async function Yo(e,t,n=process.cwd()){let r=e.op??`sync`,i=Fe(ae(n));if(i&&i.port>0){let t=`http://127.0.0.1:${i.port}/api/sync/trigger`;e.json||process.stderr.write(`Triggering ${r} via running server (port ${i.port})\n`);try{let n=await fetch(t,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({op:r})});if(!n.ok){let e=await n.json().catch(()=>({}));throw Error(e.title??e.error??e.message??`Server responded with ${n.status}`)}X(e.json,{type:`triggered`,op:r,port:i.port}),e.json||process.stderr.write(`✓ ${r} triggered\n`);return}catch(t){let n=t instanceof Error?t.message:String(t);e.json||process.stderr.write(`Server trigger failed (${n}), running directly\n`)}}e.json||process.stderr.write(`Running ${r} directly (no live server)\n`);let a=Re({baseDir:n});if(r===`sync`||r===`pull`){X(e.json,{type:`step`,step:`pull`});let t=await a.pull();X(e.json,{type:`pull`,summary:t.summary}),e.json||process.stderr.write(` pull: ${t.summary.changes} changes\n`)}(r===`sync`||r===`push`)&&(X(e.json,{type:`step`,step:`push`}),await a.push(),X(e.json,{type:`push`,ok:!0}),e.json||process.stderr.write(` push: ok
62
62
  `)),X(e.json,{type:`complete`,op:r}),e.json||process.stderr.write(`✓ ${r} complete\n`)}function Xo(e){return new t(`sync`).description(`Commit, pull, and push to the remote`).option(`--json`,`Output JSONL progress events`,!1).action(async t=>{try{await Yo({json:t.json,op:`sync`},e())}catch(e){let n=e instanceof Error?e.message:String(e);t.json?process.stdout.write(`${JSON.stringify({type:`error`,message:n})}\n`):process.stderr.write(`✗ sync failed: ${n}\n`),process.exit(1)}})}function Zo(e){return new t(`pull`).description(`Pull changes from the remote`).option(`--json`,`Output JSONL progress events`,!1).action(async t=>{try{await Yo({json:t.json,op:`pull`},e())}catch(e){let n=e instanceof Error?e.message:String(e);t.json?process.stdout.write(`${JSON.stringify({type:`error`,message:n})}\n`):process.stderr.write(`✗ pull failed: ${n}\n`),process.exit(1)}})}function Qo(e){return new t(`push`).description(`Push commits to the remote`).option(`--json`,`Output JSONL progress events`,!1).action(async t=>{try{await Yo({json:t.json,op:`push`},e())}catch(e){let n=e instanceof Error?e.message:String(e);t.json?process.stdout.write(`${JSON.stringify({type:`error`,message:n})}\n`):process.stderr.write(`✗ push failed: ${n}\n`),process.exit(1)}})}function $o(e){return typeof e==`string`&&f.includes(e)}async function es(e={}){let t=N(e.cwd??process.cwd()),r=e.pack??`knowledge-base`;if(!b[r])return{status:`failed`,message:`${O(`Error:`)} Unknown pack "${r}". Available: ${f.join(`, `)}`,exitCode:1};let i;try{i=await ne({projectDir:t,rootDir:e.root,packId:r})}catch(e){return e instanceof u?{status:`prerequisite-missing`,message:`${O(`Error:`)} ${e.message}`,exitCode:1}:{status:`failed`,message:`${O(`Error:`)} ${e instanceof Error?e.message:String(e)}`,exitCode:1}}if(i.created.length===0){let t=b[r].name;if(e.personalTemplates&&!e.dryRun){let e=_();if(e.errors.length>0){let n=e.errors.map(e=>` ${k(`!`)} ${e.path}: ${e.error}`);return{status:`failed`,message:[`${D(`Your ${t} pack is already seeded.`)}`,`${k(`Personal templates: some writes failed:`)}`,...n].join(`
63
63
  `),plan:i,exitCode:1}}let n=e.written.length;if(n>0)return{status:`applied`,message:`${D(`Your ${t} pack is already seeded.`)}\n${D(`✓ Wrote ${n} personal template${n===1?``:`s`}`)} ${E(`to ~/.ok/templates/`)}`,plan:i,exitCode:0}}return{status:`no-op`,message:`${D(`Your ${t} pack is already seeded.`)}\n${E(`Nothing to do.`)}`,plan:i,exitCode:0}}if(e.dryRun)return{status:`dry-run`,message:`${A(`Plan (dry-run — no changes made):`)}\n\n${ns(i,t)}`,plan:i,exitCode:0};if(!e.yes&&!await rs(`${A(`Plan:`)}\n\n${ns(i,t)}\n\n${A(`Apply?`)} ${E(`[Y/n] `)}`,e.confirmStream))return{status:`cancelled`,message:E(`Cancelled.`),plan:i,exitCode:0};let a=await n(i,{projectDir:t,packId:r});if(a.errors.length>0){let e=a.errors.map(e=>` ${O(`✗`)} ${e.path}: ${e.error}`);return{status:`failed`,message:[`${k(`Applied`)} ${a.applied} entries, ${k(String(a.errors.length))} error(s):`,...e].join(`
@@ -0,0 +1,2 @@
1
+ import"./dist-CC_tWBen.mjs";var e=`0.5.0-beta.25`;const t=`config.yml`,n=e;export{n,t};
2
+ //# sourceMappingURL=constants-2dtfcgrw.mjs.map