@inkeep/open-knowledge 0.5.0-beta.34 → 0.5.0-beta.36
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/skills/open-knowledge/SKILL.md +1 -1
- package/dist/cli.mjs +37 -37
- package/dist/constants-0DGajs0S.mjs +2 -0
- package/dist/dist-B4nknkyx.mjs +1 -0
- package/dist/{dist-glNrnHP0.mjs → dist-GJE8NgVb.mjs} +22 -22
- package/dist/index.mjs +1 -1
- package/dist/init-DJPy7j-l.mjs +1 -0
- package/dist/{init-BpIc-g0N.mjs → init-mO3B7tHj.mjs} +8 -8
- package/dist/loader-5BoR7yfI.mjs +1 -0
- package/dist/{loader-CtFvlinY.mjs → loader-BvNa-UGH.mjs} +3 -3
- package/dist/preview-CS6d-INQ.mjs +1 -0
- package/dist/{preview-FVZNAI7h.mjs → preview-I_sZoeGy.mjs} +2 -2
- package/dist/public/assets/{ActivityModeContent-DaTVFKNP.js → ActivityModeContent-D4mJve47.js} +1 -1
- package/dist/public/assets/{DocumentContext-DIryeJDZ.js → DocumentContext-DlKhIM1r.js} +14 -14
- package/dist/public/assets/{SettingsDialog-D_QZNx5I.js → SettingsDialog-BmYi4srd.js} +2 -2
- package/dist/public/assets/{SourceEditor-C6qXNflf.js → SourceEditor-FcRXzF2H.js} +2 -2
- package/dist/public/assets/{config-validation-events-DrekQdtM.js → config-validation-events-DYeWLZ7O.js} +4 -4
- package/dist/public/assets/index-CNH2e6Vb.js +1918 -0
- package/dist/public/assets/index-DquyxBKM.css +1 -0
- package/dist/public/assets/{typing-burst-detector-DY26wxc3.js → typing-burst-detector-rlH8kimG.js} +2 -2
- package/dist/public/index.html +5 -5
- package/dist/{repair-launch-json-Dy3tOf1L.mjs → repair-launch-json-CIWa8qxg.mjs} +2 -2
- package/dist/{repair-mcp-configs-BwCwMD-K.mjs → repair-mcp-configs-2kPz8OcL.mjs} +2 -2
- package/dist/{src-CY_xim1u.mjs → src-BxHt_W7H.mjs} +2 -2
- package/dist/start-DeC-wai5.mjs +1 -0
- package/dist/{start-Biv7po6h.mjs → start-f0mu1iQf.mjs} +2 -2
- package/package.json +1 -1
- package/dist/constants-WWN6oJ4h.mjs +0 -2
- package/dist/dist-DfGdFarN.mjs +0 -1
- package/dist/init-CnbFuJA7.mjs +0 -1
- package/dist/loader-DHfDN1M1.mjs +0 -1
- package/dist/preview-B1X8auY3.mjs +0 -1
- package/dist/public/assets/index-Bj0NfTt2.css +0 -1
- package/dist/public/assets/index-C30DOh_U.js +0 -1918
- package/dist/start-D7KRPRx3.mjs +0 -1
|
@@ -2279,8 +2279,8 @@ superseded_by: <path-to-new-canonical-article>.md
|
|
|
2279
2279
|
- **Don't rewrite research prose verbatim** — canonical articles have a different voice (direct, decided) than research (exploratory, provisional)
|
|
2280
2280
|
- **Don't skip the supersedes / superseded_by links** — the audit trail matters for future readers
|
|
2281
2281
|
`}const DESCRIPTION$27=[`Promote research into a canonical article inside the project content directory. Canonical, not provisional — the output is the source of truth for future agents.`,``,`**Use when:**`,`- A team has made a decision after research and wants the outcome committed as canonical knowledge`,`- Compacting several provisional research notes into one authoritative article`,`- A developer asks to "consolidate" or "finalize" knowledge on a topic`,``,`**Triggers on:**`,`- "consolidate", "finalize", "promote to canonical", "make this official"`,`- User says the team has decided and wants the outcome written as canonical`,`- Research has stabilized and a destination article is needed`].join(`
|
|
2282
|
-
`);function register$27(e,t){e.
|
|
2283
|
-
`);function parseDeletedDocNames(e){return Array.isArray(e)?e.filter(e=>typeof e==`string`):[]}function register$26(e,t){e.
|
|
2282
|
+
`);function register$27(e,t){e.registerTool(`consolidate`,{description:DESCRIPTION$27,inputSchema:{topic:string$2().describe(`The topic to consolidate into a canonical article`),cwd:string$2().optional().describe(ROUTED_CWD_DESCRIPTION)}},buildWorkflowHandler(`consolidate`,t,`topic`,buildBody$2))}const PREVIEW_URL_SOURCES=[`electron-protocol`,`lock`],ELECTRON_PROTOCOL_ENV_VAR=`OK_ELECTRON_PROTOCOL_HOST`;function encodeDocName(e){return e.split(`/`).map(encodeURIComponent).join(`/`)}async function resolvePreviewUrlForTool(e,t,S){let w=S??await t.resolveCwd(),E=resolveContentDir(await resolveConfig(t.config,w),w);return resolvePreviewUrl(e,{lockDir:resolveLockDir(w),contentDir:E})}function resolveUiInfo(e){try{let t=readUiLock(e.lockDir);if(t&&t.port>0)return{baseUrl:`http://localhost:${t.port}`,port:t.port}}catch(t){process.stderr.write(`[preview-url] readUiLock failed at ${e.lockDir} while building ui block: ${t instanceof Error?t.message:String(t)}\n`)}return{baseUrl:null,port:null}}async function buildListResolver(e,t){let S=t??await e.resolveCwd(),w=resolveContentDir(await resolveConfig(e.config,S),S),E={lockDir:resolveLockDir(S),contentDir:w};return{resolve:e=>resolvePreviewUrl(e,E),ui:resolveUiInfo(E)}}function docNameFromPath(e){let t=e.toLowerCase();return t.endsWith(`.md`)?e.slice(0,-3):t.endsWith(`.mdx`)?e.slice(0,-4):e}function resolvePreviewUrl(e,t){let S=`/#/${encodeDocName(e)}`;if(process.env[ELECTRON_PROTOCOL_ENV_VAR]===`1`&&t.contentDir)try{let S=realpathSync(t.contentDir);return{url:`openknowledge://open?project=${encodeURIComponent(S)}&doc=${encodeURIComponent(e)}`,source:`electron-protocol`}}catch(e){process.stderr.write(`[preview-url] realpathSync failed for ${t.contentDir}, falling through to http sources: ${e instanceof Error?e.message:String(e)}\n`)}try{let e=readUiLock(t.lockDir);if(e&&e.port>0)return{url:`http://localhost:${e.port}${S}`,source:`lock`}}catch(e){process.stderr.write(`[preview-url] readUiLock failed at ${t.lockDir}: ${e instanceof Error?e.message:String(e)}\n`)}return null}const DESCRIPTION$26=["[Requires: Hocuspocus server] Delete a document through the managed delete flow at `POST /api/delete-path` (kind: file).",`Closes all open agent sessions for the doc, unloads it from Hocuspocus, and removes the file from disk.`,``,`**Parameters:**`,"- `docName` — Document name, typically without extension. A trailing `.md` or `.mdx` is stripped automatically.",``,`**Notes:**`,'- Inbound wiki-links to the deleted doc become dead links (redlinks) — they are NOT rewritten. Call `get_backlinks({ docName: "your-doc" })` BEFORE deleting to see which docs link here, then update or remove those references first.',"- Deletion is irreversible from this tool. Use `save_version` beforehand if you may need to roll back.","- The structured response includes `previousPreviewUrl` (when a preview source resolves) so agents can close any stale preview tab pointing at the deleted doc.",``,`**Errors:**`,"- 400 — `docName` is not a valid relative content path.",`- 404 — document does not exist.`].join(`
|
|
2283
|
+
`);function parseDeletedDocNames(e){return Array.isArray(e)?e.filter(e=>typeof e==`string`):[]}function register$26(e,t){e.registerTool(`delete_document`,{description:DESCRIPTION$26,inputSchema:{docName:string$2().describe(`Document name to delete`),cwd:string$2().optional().describe(ROUTED_CWD_DESCRIPTION)}},async e=>{let S=await resolveProjectServerContext(t.resolveCwd,t.config,t.serverUrl,e.cwd);if(!S.ok)return textResult(`Error: ${S.error}`,!0);let{cwd:w,url:E}=S;if(!E)return textResult(HOCUSPOCUS_NOT_RUNNING_ERROR,!0);let D=normalizeDocName(e.docName);if(!D.ok)return textResult(D.error,!0);let O=t.identityRef?.current,k=await httpPost(E,`/api/delete-path`,{kind:`file`,path:D.docName,...O?{agentId:O.connectionId,agentName:O.displayName,clientName:O.clientInfo?.name,colorSeed:O.colorSeed}:{}});if(!k.ok){let e=k.error,t={ok:!1,error:e};return textPlusStructured(`Error: ${e}`,t,!0)}let j=parseDeletedDocNames(k.deletedDocNames),F=await resolvePreviewUrlForTool(D.docName,{config:t.config,resolveCwd:t.resolveCwd},w),L={ok:!0,deletedDocNames:j.length>0?j:[D.docName],...F?{previousPreviewUrl:F.url}:{}};return textPlusStructured(L.deletedDocNames.length===1?`Deleted ${L.deletedDocNames[0]}.`:`Deleted ${L.deletedDocNames.length} documents: ${L.deletedDocNames.join(`, `)}.`,L)})}const DESCRIPTION$25=[`[Operates on disk; no running OK server required] Delete a template.`,``,"Deletes `<folder>/.ok/templates/<name>.md` from the current project. Idempotent: deleting a template that does not exist returns success with `existed: false`. Auto-cleans empty `<folder>/.ok/templates/` and `<folder>/.ok/` directories.",``,`**Parameters:**`,"- `folder` — Project-root-relative folder. Empty / `.` means project root.","- `name` — Template filename without `.md` extension."].join(`
|
|
2284
2284
|
`),InputSchema$7={folder:string$2().describe("Project-root-relative folder. Empty / `.` means project root."),name:string$2().min(1).regex(/^[A-Za-z0-9_-]+$/,"Template name must use letters, digits, `_`, or `-` only.").describe("Template filename without `.md` extension."),cwd:string$2().optional().describe(ROUTED_CWD_DESCRIPTION)},OutputSchema$7=outputSchemaWithText({result:union([object$1({ok:literal(!0),path:string$2(),existed:boolean$1(),cleanedEmpty:object$1({templatesDir:boolean$1(),okDir:boolean$1()})}),object$1({ok:literal(!1),error:object$1({code:string$2(),message:string$2()})})])});function register$25(e,t){e.registerTool(`delete_template`,{description:DESCRIPTION$25,inputSchema:object$1(InputSchema$7).strict(),outputSchema:OutputSchema$7,annotations:{readOnlyHint:!1,idempotentHint:!0,destructiveHint:!0}},async e=>{let S=await resolveProjectConfigContext(t.resolveCwd,t.config,e.cwd);if(!S.ok)return{isError:!0,content:[{type:`text`,text:`Error: ${S.error}`}]};let{cwd:w}=S,E=applyTemplateDelete({projectDir:w,folder:e.folder,name:e.name});if(!E.ok)return{isError:!0,structuredContent:{result:E},content:[{type:`text`,text:`${E.error.code}: ${E.error.message}`}]};let D=[E.existed?`Deleted template at ${E.path}`:`Template at ${E.path} did not exist (no-op)`];return E.cleanedEmpty.templatesDir&&D.push(`Removed empty .ok/templates/ directory`),E.cleanedEmpty.okDir&&D.push(`Removed empty .ok/ directory`),textPlusStructured(D.join(`
|
|
2285
2285
|
`),{result:E})})}function buildDiscoverBody(e){return`# Discover — Project Convention Extraction + Link Graph Activation
|
|
2286
2286
|
|
|
@@ -2599,7 +2599,7 @@ Link-graph activation:
|
|
|
2599
2599
|
- **Validation failure** (Phase 6) → don't pretend to succeed; surface the failed step to the user.
|
|
2600
2600
|
`}const DESCRIPTION$24=[`Set up an existing repo for Open Knowledge by extracting conventions from existing siblings + activating the link graph. Multi-step instructional body with seven phases and per-phase user-confirmation STOP gates.`,``,`**Use when:**`,`- First arrival at a repo that has existing markdown content AND no folder frontmatter / templates configured`,"- `list_documents` returns empty `frontmatter_defaults` + empty `templates_available` for substantial folders",`- User explicitly asks to onboard / set up / configure an existing repo`,``,`**Do NOT invoke when:**`,"- The repo is empty (< 5 .md files) — use `ok seed` instead",`- The repo is already fully configured (every substantial folder has folder frontmatter + templates)`,``,'One-shot per project. Idempotent on re-run — detects prior configuration and switches to "extend mode" (proposes additions only, never re-proposes existing artifacts). **Requires the OK Hocuspocus server running** — most primitives the body composes (`list_documents`, `read_document`, link-graph) hit the server. Phase 1 step 0 enforces this; if the server is down, the tool exits cleanly with a "run `open-knowledge start`" instruction. Only `exec`, `set_folder_rule`, `write_template`, and the `.okignore` write are fs-direct.',``,"**Composes existing primitives only** — no new MCP tools, no schema changes. Uses `exec`, `list_documents`, `read_document`, `search`, `set_folder_rule`, `write_template`, `get_orphans`, `get_hubs`, `get_dead_links`, `get_backlinks`, `get_forward_links`, `suggest_links`, `edit_document`."].join(`
|
|
2601
2601
|
`),InputSchema$6={cwd:string$2().optional().describe(ROUTED_CWD_DESCRIPTION)},OutputSchema$6=outputSchemaWithText({previewUrl:_null()});function register$24(e,t){e.registerTool(`discover`,{description:DESCRIPTION$24,inputSchema:InputSchema$6,outputSchema:OutputSchema$6,annotations:{readOnlyHint:!1,idempotentHint:!0,destructiveHint:!1}},async e=>{let S=await resolveProjectConfigContext(t.resolveCwd,t.config,e.cwd);return S.ok?textPlusStructured(buildDiscoverBody(S.config.content.dir),{previewUrl:null}):textResult(`Error: ${S.error}`,!0)})}const DESCRIPTION$23=`${[`[Requires: Hocuspocus server] Find-and-replace on a live document via the CRDT layer.`,`The patch is applied through Hocuspocus and propagated to all connected editors in real-time.`,"Use `offset` when you need to patch an exact occurrence; omit it to preserve first-match behavior.",``,'**Body-only.** Frontmatter-intersecting find/replace calls are rejected with HTTP 400. Frontmatter editing via MCP is currently unavailable — to change frontmatter, use `write_document` with `position: "replace"` and a payload that includes the new YAML block.',``,"**When rewriting prose, add `[[wiki-links]]` aggressively.** If the replacement mentions other documents or entities that should have their own page, link them as `[[Page Name]]`. Over-linking is the goal; underlinked documents lose their value in backlink-driven navigation.",``,`**Parameters:**`,"- `docName` — Document name, typically without extension. A trailing `.md` or `.mdx` is stripped automatically.","- `find` — Text to find (exact match)","- `replace` — Replacement text","- `offset` (optional) — Exact occurrence to patch, as a JavaScript string offset in the current markdown. If the document changed and the text no longer matches there, the server returns a stale-target error; re-run `suggest_links` to get fresh offsets.",'- `summary` — Optional one-line user-outcome description of this edit (≤80 chars). Appears as a bullet in the document timeline so readers can scan intent without opening every diff. Prefer outcome phrasing ("Fixed token-refresh race") over structural ("Changed 1 line"). Avoid including secrets or PII — summaries are persisted to git history.'].join(`
|
|
2602
|
-
`)}\n${renderInventoryFooter()}`;function register$23(e,t){e.
|
|
2602
|
+
`)}\n${renderInventoryFooter()}`;function register$23(e,t){e.registerTool(`edit_document`,{description:DESCRIPTION$23,inputSchema:{docName:string$2().describe(`Document name to edit`),find:string$2().describe(`Text to find (exact match)`),replace:string$2().describe(`Replacement text`),offset:number$1().int().min(0).optional().describe(`Exact occurrence to patch, as a JavaScript string offset in the current markdown`),summary:summaryArgSchema,cwd:string$2().optional().describe(ROUTED_CWD_DESCRIPTION)}},async e=>{let S=await resolveProjectServerContext(t.resolveCwd,t.config,t.serverUrl,e.cwd);if(!S.ok)return textResult(`Error: ${S.error}`,!0);let{cwd:w,url:E}=S;if(!E)return textResult(HOCUSPOCUS_NOT_RUNNING_ERROR,!0);let D=normalizeDocName(e.docName);if(!D.ok)return textResult(D.error,!0);let O=t.identityRef?.current,k=await httpPost(E,`/api/agent-patch`,{docName:D.docName,find:e.find,replace:e.replace,offset:e.offset,...e.summary===void 0?{}:{summary:e.summary},...O?{agentId:O.connectionId,agentName:O.displayName,clientName:O.clientInfo?.name,colorSeed:O.colorSeed}:{}});if(!k.ok)return textResult(`Error: ${k.error}`,!0);let j=resolveLockDir(w),F=resolvePreviewUrl(D.docName,{lockDir:j}),L=typeof k.subscriberCount==`number`?k.subscriberCount:void 0,B=(typeof k.systemSubscriberCount==`number`?k.systemSubscriberCount:void 0)===0,H=L===0,q=k.summary&&typeof k.summary==`object`?k.summary:void 0,J=typeof q?.hint==`string`?q.hint:void 0,Y=[`Edit applied successfully.`];F&&Y.push(`Preview: ${F.url}`),B&&Y.push(F?`Open ${F.url} in your preview browser.`:`No preview attached. Start the UI.`),J&&Y.push(J);let ee=Y.join(`
|
|
2603
2603
|
`);if(!F&&!B&&!H&&!q)return textResult(ee);let te={};return F&&(te.previewUrl=F.url,te.previewUrlSource=F.source),B&&(te.warning={message:`Open the previewUrl in your preview browser.`,action:`attach-preview-once`,previewUrl:F?.url??null}),q&&(te.summary=q),textPlusStructured(ee,te)})}const PRODUCER_COMMANDS=new Set([`cat`,`ls`,`grep`,`find`]),PATH_FALLBACK_RE=/\b[\w./-]+\.(md|mdx)\b/g;function isWikiPath(e){return/\.(md|mdx)$/.test(e)}function normalize$1(e){let t=e.trim();return t?(t=t.replace(/\/+/g,`/`),t.startsWith(`./`)&&(t=t.slice(2)),t.endsWith(`/`)&&(t=t.slice(0,-1)),t):``}function argsOf(e){return e.args.slice(1)}function nonFlagArgs(e){return e.filter(e=>!e.startsWith(`-`))}function extractFromCat(e){return nonFlagArgs(argsOf(e)).filter(isWikiPath)}function extractFromLs(e,t){let S=nonFlagArgs(argsOf(t)),w=S.length>0?S[S.length-1]:``,E=w&&w!==`.`?normalize$1(w):``,D=[];E&&D.push(E);for(let t of e.split(`
|
|
2604
2604
|
`)){let e=t.trim();if(!e||/\.[a-z0-9]+$/i.test(e)&&!isWikiPath(e))continue;let S=E?`${E}/${e}`:e;D.push(S)}return D}function extractFromGrep(e){let t=[];for(let S of e.split(`
|
|
2605
2605
|
`)){if(!S)continue;let e=S.indexOf(`:`);if(e<0)continue;let w=normalize$1(S.slice(0,e));isWikiPath(w)&&t.push(w)}return t}function extractFromFind(e){let t=[];for(let S of e.split(`
|
|
@@ -2610,15 +2610,15 @@ Link-graph activation:
|
|
|
2610
2610
|
`),k=S-D;return{text:`${O}\n<truncated: ${k} more lines — re-run with a more-specific query>`,truncated:!0,omittedLines:k}}function detectBinaryArgs(e){let t=[];for(let S of e)if(S.command===`cat`)for(let e of S.args.slice(1))e.startsWith(`-`)||BINARY_EXT_RE.test(e)&&t.push(e);return t}function extractHeadTailLimit(e){for(let t=1;t<e.length;t++){let S=e[t],w=S.match(/^--lines=(\d+)$/);if(w)return Number(w[1]);if(S===`--lines`&&t+1<e.length){let S=Number(e[t+1]);if(Number.isFinite(S))return S}if(S===`-n`&&t+1<e.length){let S=Number(e[t+1]);if(Number.isFinite(S))return S}let E=S.match(/^-n(\d+)$/);if(E)return Number(E[1]);let D=S.match(/^-(\d+)$/);if(D)return Number(D[1])}return 10}function detectHeadTailTruncation(e,t){if(e.length<2)return null;let S=e[e.length-1];if(S.command!==`head`&&S.command!==`tail`)return null;let w=extractHeadTailLimit(S.args),E=t.split(`
|
|
2611
2611
|
`),D=E[E.length-1]===``?E.length-1:E.length;if(D<w)return null;let O=E.slice(0,D),k=new Set(O.map(e=>{let t=e.indexOf(`:`);return t>0?e.slice(0,t):e})).size,j=e.slice(0,-1).map(e=>e.command).join(` | `);return{banner:`Output hit \`${S.command} -${w}\` cap (${D} lines, ${k} unique file${k===1?``:`s`}). The \`${j}\` stage may have had more matches that never reached stdout. For existence checks across many files, prefer \`grep -rl PATTERN <dir>\` (list files only, no head). For enumeration, drop the \`| ${S.command}\` or widen the cap.`}}function isDirectoryMeta(e){return e.type===`directory`}function formatDirectoryEntry(e){let t=[e.title?`**${e.title}** (${e.path}/)`:`**${e.path}/** (directory)`];e.description&&t.push(e.description),e.tags&&e.tags.length>0&&t.push(`tags: ${e.tags.join(`, `)}`);let S=[];return S.push(`${e.recursiveMdCount} md file${e.recursiveMdCount===1?``:`s`}`),e.childDirCount>0&&S.push(`${e.childDirCount} subdir${e.childDirCount===1?``:`s`}`),t.push(S.join(`, `)),e.mostRecentMd&&t.push(`most recent: ${e.mostRecentMd.title??e.mostRecentMd.path} (${e.mostRecentMd.path})`),e.truncated&&t.push(`scan truncated`),`- ${t.join(` — `)}`}function formatFileEntry(e){let t=[`**${e.title??e.path}** (${e.path})`];if(e.description&&t.push(e.description),e.tags.length>0&&t.push(`tags: ${e.tags.join(`, `)}`),e.backlinkCount!==null&&t.push(`backlinks: ${e.backlinkCount}`),e.forwardLinkCount!==null&&t.push(`forward links: ${e.forwardLinkCount}`),e.history&&e.history.length>0){let S=e.history.map(e=>{let t=e.writerClassification===`agent`?`agent: ${e.writerName}`:e.writerClassification===`principal`?`human: ${e.writerName}`:`${e.writerClassification}: ${e.writerName}`;return`${e.hash.slice(0,7)} [${t}] ${e.message}`});t.push(`OK edits: ${S.join(` · `)}`)}if(e.projectHistory&&e.projectHistory.length>0){let S=e.projectHistory.map(e=>`${e.hash.slice(0,7)} ${e.authorName}: ${e.subject}`);t.push(`commits: ${S.join(` · `)}`)}return`- ${t.join(` — `)}`}function buildStdoutProvenance(e,t,S){let w=null;for(let t=e.length-1;t>=0;t--){let S=e[t],E=S.command;if(E===`ls`||E===`cat`){w=S;break}if((E===`head`||E===`tail`)&&nonFlagArgs(argsOf(S)).length>0){w=S;break}}if(!w)return``;let E=nonFlagArgs(argsOf(w));if(w.command===`ls`){let e=E[E.length-1];if(!e||e===`.`)return``;let S=e.replace(/\/+/g,`/`);return S.startsWith(`./`)&&(S=S.slice(2)),S.endsWith(`/`)&&(S=S.slice(0,-1)),!S||!t.has(S)?``:`${S}/:\n`}let D=E.filter(e=>/\.(md|mdx)$/.test(e)&&S.has(e));return D.length===1?`==> ${D[0]} <==\n`:``}function formatEnrichedBlock(e){if(e.length===0)return``;let t=[``,`### Referenced files`,``];for(let S of e)t.push(isDirectoryMeta(S)?formatDirectoryEntry(S):formatFileEntry(S));return t.join(`
|
|
2612
2612
|
`)}async function classifyPaths(e,t){let S=[],w=[];return await Promise.all(t.map(async t=>{try{let E=await stat$1(resolve(e,t));E.isDirectory()?w.push(t):E.isFile()&&S.push(t)}catch{/\.(md|mdx)$/i.test(t)&&S.push(t)}})),{files:S,dirs:w}}function errorCategoryResult(e,t){return textPlusStructured(t,{enrichedPaths:[],error:{category:e,message:t}},!0)}function withPreviewUrls(e,t){return e.map(e=>{let S=t(docNameFromPath(e.path));return{...e,previewUrl:S?.url??null,...S?{previewUrlSource:S.source}:{}}})}async function buildExecResult(e,t){let S=await resolveProjectServerContext(t.resolveCwd,t.config,t.serverUrl,e.cwd);if(!S.ok)return errorCategoryResult(`shell_construct_blocked`,`exec failed: ${S.error}`);let{cwd:w,config:E,url:D}=S,O=parseCommand(e.command);if(`error`in O)return errorCategoryResult(O.error.category,O.error.message);let k=augmentStagesWithExcludes(O.stages),j=serializeStages(k),F=await snapshotMtimes(w),L=createBashInstance(w),B=``,H=``;try{let e=await execBash(L,j);B=e.stdout,H=e.stderr}catch(e){return e instanceof StdoutOverflowError?errorCategoryResult(`output_overflow`,`Output exceeded 16 MB buffer. Narrow the command (e.g., add a more specific grep pattern, use head, restrict the path).`):errorCategoryResult(`shell_construct_blocked`,`exec failed: ${e instanceof Error?e.message:String(e)}`)}let q=await snapshotMtimes(w),J=diffMtimes(F.snapshot,q.snapshot);if(J.changed.length>0)return errorCategoryResult(`security_invariant_violation`,`Security invariant violated: file(s) in the content directory were modified during a read-only exec call: ${J.changed.join(`, `)}. This indicates a parser bug; the command has been logged.`);let Y=applySoftCap(B),ee=extractReferencedPaths(B,k).filter(e=>resolveWithinRoot(w,e).ok),{files:te,dirs:ne}=await classifyPaths(w,ee),ae=k.length===1&&k[0].command===`cat`&&te.length===1,oe=await Promise.all(te.map(e=>enrichPath(e,{projectDir:w,serverUrl:D},{includeRichFields:ae}).catch(()=>({path:e,tags:[],frontmatter:{},backlinkCount:null,backlinks:null,forwardLinkCount:null,forwardLinks:null,history:null,historySource:null,projectHistory:null,projectHistorySource:null})))),se=await Promise.all(ne.map(e=>enrichDirectory(e,{projectDir:w}).catch(()=>({path:e,type:`directory`,directMdCount:0,recursiveMdCount:0,childDirCount:0,truncated:!1}))));if(!ae&&D&&oe.length>0){let e=await fetchBacklinkCountsBatch(D,oe.map(e=>pathToDocName$1(e.path))).catch(()=>null);if(e)for(let t of oe){let S=e.get(pathToDocName$1(t.path));typeof S==`number`&&(t.backlinkCount=S)}}let ce=new Map(oe.map(e=>[e.path,e])),ue=new Map(se.map(e=>[e.path,e])),de=[];for(let e of ee){let t=ce.get(e);if(t){de.push(t);continue}let S=ue.get(e);S&&de.push(S)}let fe=detectBinaryArgs(k),me=[];fe.length>0&&me.push(`File${fe.length>1?`s`:``} ${fe.join(`, `)} appear${fe.length===1?`s`:``} to be binary (image/PDF/etc.) — exec returns text only (NG8). For binary retrieval, use native Read.`);let ge=detectHeadTailTruncation(k,B);ge&&me.push(ge.banner),H&&me.push(`stderr: ${H.trim()}`);let _e=me.length>0?`${me.join(`
|
|
2613
|
-
`)}\n\n`:``,ve=buildStdoutProvenance(k,ue,ce)+Y.text,ye=`${_e}${ve}${formatEnrichedBlock(de)}`,{resolve:Ce,ui:we}=await buildListResolver({config:E,resolveCwd:async()=>w}),Te=withPreviewUrls(de,Ce),De=we;return textPlusStructured(ye,{enrichedPaths:Te,stdout:ve,stdoutTruncated:Y.truncated,cwd:w,...De?{ui:De}:{},...me.length>0?{warnings:me}:{}})}function register$22(e,t){e.
|
|
2614
|
-
`);function register$21(e,t){e.
|
|
2613
|
+
`)}\n\n`:``,ve=buildStdoutProvenance(k,ue,ce)+Y.text,ye=`${_e}${ve}${formatEnrichedBlock(de)}`,{resolve:Ce,ui:we}=await buildListResolver({config:E,resolveCwd:async()=>w}),Te=withPreviewUrls(de,Ce),De=we;return textPlusStructured(ye,{enrichedPaths:Te,stdout:ve,stdoutTruncated:Y.truncated,cwd:w,...De?{ui:De}:{},...me.length>0?{warnings:me}:{}})}function register$22(e,t){e.registerTool(`exec`,{description:DESCRIPTION$22,inputSchema:{command:string$2().describe(`Read-only bash command (allowlist: cat, ls, grep, find, head, tail, wc, sort, uniq, cut; pipes OK)`),cwd:string$2().optional().describe("Absolute host path to run the command from. Defaults only when the MCP client advertises exactly one root; otherwise pass `cwd` explicitly.")}},async e=>{try{return await buildExecResult(e,t)}catch(e){return errorCategoryResult(`shell_construct_blocked`,`exec handler error: ${e instanceof Error?e.message:String(e)}`)}})}const DESCRIPTION$21=[`[Requires: Hocuspocus server] Find all pages that link to a given page.`,`Returns source page names, resolved titles, and context snippets as JSON.`,``,`**Parameters:**`,'- `docName` — Target page docName, typically without extension (for example, "articles/project-alpha"). A trailing `.md` or `.mdx` is stripped automatically.'].join(`
|
|
2614
|
+
`);function register$21(e,t){e.registerTool(`get_backlinks`,{description:DESCRIPTION$21,inputSchema:{docName:string$2().describe(`Target page docName`),cwd:string$2().optional().describe(ROUTED_CWD_DESCRIPTION)}},async e=>{let S=await resolveProjectServerContext(t.resolveCwd,t.config,t.serverUrl,e.cwd);if(!S.ok)return textResult(`Error: ${S.error}`,!0);let{cwd:w,url:E}=S;if(!E)return textResult(HOCUSPOCUS_NOT_RUNNING_ERROR,!0);let D=normalizeDocName(e.docName);if(!D.ok)return textResult(D.error,!0);let O=await httpGet(E,`/api/backlinks?docName=${encodeURIComponent(D.docName)}`);if(!O.ok)return textResult(`Error: ${O.error}`,!0);let{ok:k,...j}=O,F=j,{resolve:L,ui:B}=await buildListResolver(t,w),H=(F.backlinks??[]).map(e=>{let t=typeof e.source==`string`?e.source:null,S=t?L(t):null;return{...e,previewUrl:S?.url??null,...S?{previewUrlSource:S.source}:{}}}),q={...F,backlinks:H,ui:B,cwd:w};return textPlusStructured(JSON.stringify(q,null,2),q)})}const ID_CARDINALITY_CAP=32,DESCRIPTION$20=["[Operates on registry data; no running OK server required] Return full per-entry details for the canonical components an agent picked from the inventory rendered in the `write_document` / `edit_document` tool descriptions.",``,"Each entry carries: literal source-form `example` (copy-pasteable) + form-aware `params` (every prop, with `type` / `values?` / `required` / `defaultValue?` / `description`). Unmatched ids surface in `notFound` rather than failing the call.",``,`**Parameters:**`,`- \`ids\` — Array of canonical ids picked from the write-tool inventory (e.g. \`["Callout", "Tabs"]\`). Max ${ID_CARDINALITY_CAP} ids per call. Case-sensitive — must match the inventory exactly (\`Callout\` not \`callout\`; \`img\` not \`Image\`).`,"- `cwd` (optional) — Project root (see `cwd` description below)."].join(`
|
|
2615
2615
|
`),InputSchema$5={ids:array(string$2().min(1)).max(ID_CARDINALITY_CAP).describe(`Array of canonical ids to fetch. Max ${ID_CARDINALITY_CAP} ids per call. Empty array returns an empty components list.`),cwd:string$2().optional().describe(ROUTED_CWD_DESCRIPTION)},ParamSchema=object$1({name:string$2(),type:_enum([`string`,`boolean`,`number`,`enum`,`reactnode`]),values:array(string$2()).optional(),required:boolean$1(),defaultValue:union([string$2(),boolean$1(),number$1()]).optional(),description:string$2().optional(),omitOnDefault:literal(!0).optional(),advanced:literal(!0).optional(),language:_enum([`mermaid`,`latex`,`html`,`json`,`yaml`,`javascript`,`markdown`]).optional(),accept:array(string$2()).optional()}),ComponentEntrySchema=object$1({id:string$2(),displayName:string$2(),description:string$2(),kind:_enum([`jsx-block`,`jsx-void`]),example:string$2(),params:array(ParamSchema)}),OutputSchema$5=outputSchemaWithText({version:literal(1).describe(`Schema version stamp — bump for breaking shape changes.`),components:array(ComponentEntrySchema).describe(`Full per-entry details for ids that matched.`),notFound:array(string$2()).describe(`Ids that did not match any canonical descriptor.`)});function register$20(e,t){e.registerTool(`get_components`,{description:DESCRIPTION$20,inputSchema:InputSchema$5,outputSchema:OutputSchema$5,annotations:{readOnlyHint:!0,idempotentHint:!0}},async e=>{e.cwd;let t=getAgentCanonicalDescriptors(),S=new Map(t.map(e=>[e.name,e])),w=[],E=[],D=new Set;for(let t of e.ids){if(D.has(t))continue;D.add(t);let e=S.get(t);if(e===void 0){E.push(t);continue}w.push(projectFull(e))}let O={version:1,components:w,notFound:E};return textPlusStructured(JSON.stringify(O,null,2),O)})}const DESCRIPTION$19=[`[Operates on disk; no running OK server required] Read the effective merged Open Knowledge config (defaults → user → project).`,``,`Use this when you need to inspect the config mid-session — e.g., after a write that may have changed disk state, or to re-confirm the value of a field before reading it again.`,``,"Read returns the FULL merged config or a sub-tree when `path` is provided. There is no allowlist on reads — every field is readable.",``,"Note: the `server.*`, `mcp.*`, `github.*`, and `preview.*` config sub-trees were removed; their values are now built-in constants in `@inkeep/open-knowledge-core` (or hardcoded behavior, in the case of `preview.*`). Reading those paths returns `exists: false`.",``,`**Parameters:**`,'- `path` (optional) — Dotted-segments array. `["content"]` returns the content sub-tree; `["appearance", "theme"]` returns just that leaf. Omit for full config.',"- `cwd` (optional) — Project root (see `cwd` description below)."].join(`
|
|
2616
2616
|
`),InputSchema$4={path:array(string$2()).optional().describe(`Dotted path as array of segments (e.g. ["appearance","theme"]). Omit to return the full merged config.`),cwd:string$2().optional().describe(ROUTED_CWD_DESCRIPTION)},OutputSchema$4=outputSchemaWithText({value:unknown$2().describe(`Resolved config value at the requested path (or full config).`),exists:boolean$1().optional().describe('Whether the requested path resolved to a value. `false` distinguishes "path absent" from "path explicitly set to null"; absent on full-config reads.'),path:array(string$2()).optional().describe(`Echo of the requested dotted path (empty when reading the full config).`)});function readConfigPath(e,t){let S=e;for(let e of t){if(typeof S!=`object`||!S)return;S=S[e]}return S}function register$19(e,t){e.registerTool(`get_config`,{description:DESCRIPTION$19,inputSchema:InputSchema$4,outputSchema:OutputSchema$4,annotations:{readOnlyHint:!0,idempotentHint:!0}},async e=>{let S=await resolveProjectConfigContext(t.resolveCwd,t.config,e.cwd);if(!S.ok)return{isError:!0,content:[{type:`text`,text:`Error: ${S.error}`}]};let w=e.path??[],E=w.length>0?readConfigPath(S.config,w):S.config;return w.length>0&&E===void 0?textPlusStructured(`(no value at ${w.join(`.`)})`,{value:null,exists:!1,path:w}):textPlusStructured(JSON.stringify(E,null,2),{value:E,exists:!0,path:w})})}const DESCRIPTION$18=[`[Requires: Hocuspocus server] Find missing internal page targets across the corpus.`,`Returns grouped dead links keyed by missing target with source-doc rows as JSON.`,``,`**Parameters:**`,"- `sourceDocNames` (optional) — Referring source docs to narrow the audit with OR semantics"].join(`
|
|
2617
|
-
`);function register$18(e,t){e.
|
|
2618
|
-
`);function register$17(e,t){e.
|
|
2619
|
-
`);function register$16(e,t){e.
|
|
2620
|
-
`);function register$15(e,t){e.
|
|
2621
|
-
`);function register$14(e,t){e.
|
|
2617
|
+
`);function register$18(e,t){e.registerTool(`get_dead_links`,{description:DESCRIPTION$18,inputSchema:{sourceDocNames:array(string$2()).optional().describe(`Referring source docs to narrow the audit with OR semantics`),cwd:string$2().optional().describe(ROUTED_CWD_DESCRIPTION)}},async e=>{let S=await resolveProjectServerContext(t.resolveCwd,t.config,t.serverUrl,e.cwd);if(!S.ok)return textResult(`Error: ${S.error}`,!0);let{cwd:w,url:E}=S;if(!E)return textResult(HOCUSPOCUS_NOT_RUNNING_ERROR,!0);let D=new URLSearchParams;for(let t of e.sourceDocNames??[]){let e=normalizeDocName(t);if(!e.ok)return textResult(e.error,!0);D.append(`sourceDocName`,e.docName)}let O=D.toString(),k=await httpGet(E,`/api/dead-links${O?`?${O}`:``}`);if(!k.ok)return textResult(`Error: ${k.error}`,!0);let{ok:j,...F}=k,L=F,{resolve:B,ui:H}=await buildListResolver(t,w),q=(L.deadLinks??[]).map(e=>{let t=typeof e.target==`string`?e.target:null,S=t?B(t):null,w=(e.sources??[]).map(e=>{let t=typeof e.source==`string`?e.source:null,S=t?B(t):null;return{...e,previewUrl:S?.url??null,...S?{previewUrlSource:S.source}:{}}});return{...e,sources:w,previewUrl:S?.url??null,...S?{previewUrlSource:S.source}:{}}}),J={...L,deadLinks:q,ui:H,cwd:w};return textPlusStructured(JSON.stringify(J,null,2),J)})}const DESCRIPTION$17=[`[Requires: Hocuspocus server] Find all pages that a given page links to.`,`Returns forward links as JSON.`,``,`**Parameters:**`,"- `docName` — Source page docName, typically without extension. A trailing `.md` or `.mdx` is stripped automatically."].join(`
|
|
2618
|
+
`);function register$17(e,t){e.registerTool(`get_forward_links`,{description:DESCRIPTION$17,inputSchema:{docName:string$2().describe(`Source page docName`),cwd:string$2().optional().describe(ROUTED_CWD_DESCRIPTION)}},async e=>{let S=await resolveProjectServerContext(t.resolveCwd,t.config,t.serverUrl,e.cwd);if(!S.ok)return textResult(`Error: ${S.error}`,!0);let{cwd:w,url:E}=S;if(!E)return textResult(HOCUSPOCUS_NOT_RUNNING_ERROR,!0);let D=normalizeDocName(e.docName);if(!D.ok)return textResult(D.error,!0);let O=await httpGet(E,`/api/forward-links?docName=${encodeURIComponent(D.docName)}`);if(!O.ok)return textResult(`Error: ${O.error}`,!0);let{ok:k,...j}=O,F=j,{resolve:L,ui:B}=await buildListResolver(t,w),H=(F.forwardLinks??[]).map(e=>{let t=e.kind===`doc`&&typeof e.docName==`string`?e.docName:null,S=t?L(t):null;return{...e,previewUrl:S?.url??null,...S?{previewUrlSource:S.source}:{}}}),q={...F,forwardLinks:H,ui:B,cwd:w};return textPlusStructured(JSON.stringify(q,null,2),q)})}const DESCRIPTION$16=[`[Requires: Hocuspocus server] List version history for a document.`,`Returns timeline entries from the shadow repo, sorted by timestamp descending.`,"Each entry includes a commit SHA that can be passed to `rollback_to_version`.",``,`**Parameters:**`,"- `docName` — Document name to query history for, typically without extension. A trailing `.md` or `.mdx` is stripped automatically.","- `branch` (optional) — Branch name (default: current branch)","- `limit` (optional) — Maximum entries to return (default 50, max 200)","- `offset` (optional) — Number of entries to skip for pagination (default 0)",'- `type` (optional) — Filter by entry type: "checkpoint", "upstream", or "wip"',"- `author` (optional) — Filter to entries by this author name or email","- `excludeAuthor` (optional) — Exclude entries by this author name or email"].join(`
|
|
2619
|
+
`);function register$16(e,t){e.registerTool(`get_history`,{description:DESCRIPTION$16,inputSchema:{docName:string$2().describe(`Document name to query history for`),branch:string$2().optional().describe(`Branch name (default: current branch)`),limit:number$1().int().min(1).max(200).optional().describe(`Maximum entries to return (default 50, max 200)`),offset:number$1().int().min(0).optional().describe(`Number of entries to skip for pagination (default 0)`),type:_enum([`checkpoint`,`upstream`,`wip`]).optional().describe(`Filter by entry type`),author:string$2().optional().describe(`Filter to entries by this author name or email`),excludeAuthor:string$2().optional().describe(`Exclude entries by this author name or email`),cwd:string$2().optional().describe(ROUTED_CWD_DESCRIPTION)}},async e=>{let S=await resolveProjectServerContext(t.resolveCwd,t.config,t.serverUrl,e.cwd);if(!S.ok)return textResult(`Error: ${S.error}`,!0);let{cwd:w,url:E}=S;if(!E)return textResult(HOCUSPOCUS_NOT_RUNNING_ERROR,!0);let D=normalizeDocName(e.docName);if(!D.ok)return textResult(D.error,!0);let O=new URLSearchParams;O.set(`docName`,D.docName),e.branch&&O.set(`branch`,e.branch),e.limit!=null&&O.set(`limit`,String(e.limit)),e.offset!=null&&O.set(`offset`,String(e.offset)),e.type&&O.set(`type`,e.type),e.author&&O.set(`author`,e.author),e.excludeAuthor&&O.set(`excludeAuthor`,e.excludeAuthor);let k=await httpGet(E,`/api/history?${O.toString()}`);if(!k.ok)return textResult(`Error: ${k.error}`,!0);let{ok:j,...F}=k,L=await resolvePreviewUrlForTool(D.docName,{config:t.config,resolveCwd:t.resolveCwd},w);return textPlusStructured(JSON.stringify(F,null,2),{...F,previewUrl:L?.url??null,...L?{previewUrlSource:L.source}:{}})})}const DESCRIPTION$15=[`[Requires: Hocuspocus server] Find the most-linked pages in the knowledge graph.`,`Returns hub pages ordered by inbound link count as JSON.`,``,`**Parameters:**`,"- `limit` (optional) — Maximum number of hubs to return (default 20)"].join(`
|
|
2620
|
+
`);function register$15(e,t){e.registerTool(`get_hubs`,{description:DESCRIPTION$15,inputSchema:{limit:number$1().int().positive().optional().describe(`Maximum number of hubs to return`),cwd:string$2().optional().describe(ROUTED_CWD_DESCRIPTION)}},async e=>{let S=await resolveProjectServerContext(t.resolveCwd,t.config,t.serverUrl,e.cwd);if(!S.ok)return textResult(`Error: ${S.error}`,!0);let{cwd:w,url:E}=S;if(!E)return textResult(HOCUSPOCUS_NOT_RUNNING_ERROR,!0);let D=await httpGet(E,`/api/hubs${e.limit?`?limit=${encodeURIComponent(String(e.limit))}`:``}`);if(!D.ok)return textResult(`Error: ${D.error}`,!0);let{ok:O,...k}=D,j=k,{resolve:F,ui:L}=await buildListResolver(t,w),B=(j.hubs??[]).map(e=>{let t=typeof e.docName==`string`?e.docName:null,S=t?F(t):null;return{...e,previewUrl:S?.url??null,...S?{previewUrlSource:S.source}:{}}}),H={...j,hubs:B,ui:L,cwd:w};return textPlusStructured(JSON.stringify(H,null,2),H)})}const DESCRIPTION$14=[`[Requires: Hocuspocus server] Find disconnected pages in the knowledge graph.`,`Returns orphaned pages as JSON.`,``,`**Parameters:**`,"- `mode` (optional) — Orphan lens: `incoming`, `outgoing`, or `both` (default `both`)"].join(`
|
|
2621
|
+
`);function register$14(e,t){e.registerTool(`get_orphans`,{description:DESCRIPTION$14,inputSchema:{mode:_enum(ORPHAN_MODES).optional().describe(`Filter which type of graph disconnection to surface`),cwd:string$2().optional().describe(ROUTED_CWD_DESCRIPTION)}},async e=>{let S=await resolveProjectServerContext(t.resolveCwd,t.config,t.serverUrl,e.cwd);if(!S.ok)return textResult(`Error: ${S.error}`,!0);let{cwd:w,url:E}=S;if(!E)return textResult(HOCUSPOCUS_NOT_RUNNING_ERROR,!0);let D=await httpGet(E,`/api/orphans${e.mode?`?mode=${encodeURIComponent(e.mode)}`:``}`);if(!D.ok)return textResult(`Error: ${D.error}`,!0);let{ok:O,...k}=D,j=k,{resolve:F,ui:L}=await buildListResolver(t,w),B=(j.orphans??[]).map(e=>{let t=typeof e.docName==`string`?e.docName:null,S=t?F(t):null;return{...e,previewUrl:S?.url??null,...S?{previewUrlSource:S.source}:{}}}),H={...j,orphans:B,ui:L,cwd:w};return textPlusStructured(JSON.stringify(H,null,2),H)})}const BUILTIN_SKIP_DIRS=new Set([`node_modules`,`.venv`,`venv`,`env`,`__pycache__`,`vendor`,`dist`,`build`,`out`,`output`,`.next`,`.nuxt`,`.svelte-kit`,`.astro`,`.turbo`,`.cache`,`.parcel-cache`,`coverage`,`.git`,`.ok`,`.open-knowledge`,`.openknowledge`,`Library`,`Applications`,`.Trash`]),IGNORE_FILE_NAMES=[`.gitignore`,`.okignore`];function createContentFilter(e){let{projectDir:t,contentDir:S,onAfterRebuild:w}=e,E=relative(t,S),D=E.startsWith(`..`),O,k,j,F=0,L=0,B=0;function H(){let e=(0,import_ignore.default)();e.add(`.git`);let w=[],H=0,q=0;for(let S of IGNORE_FILE_NAMES){let E=join(t,S);if(existsSync(E))try{let t=readFileSync(E,`utf-8`);H+=t.length;let S=parseIgnorePatterns(t);w.push(...S),e.add(S)}catch(e){console.warn(`[content-filter] Failed to read ${S} at ${E}:`,e)}}if(E&&!D)for(let t of IGNORE_FILE_NAMES){let w=join(S,t);if(existsSync(w))try{let t=readFileSync(w,`utf-8`);H+=t.length,q++;let S=parseIgnorePatterns(t).map(e=>prefixPattern(e,E));e.add(S)}catch(e){console.warn(`[content-filter] Failed to read ${t} at ${w}:`,e)}}let J={value:H};q+=loadNestedIgnoreFiles(S,t,e,J),H=J.value;let Y=w.filter(e=>e.length>0&&!e.startsWith(`!`)&&!e.startsWith(`#`));return O=e,k=w,j=Y,F=w.length,L=q,B=H,{patternCount:F,nestedFileCount:L,bytes:B}}H();let q=new Map;function J(e){if(D)return!1;let t=E?`${E}/${e}`:e;return O.ignores(t)}populateDirCount(S,``,J,q);function Y(e){let t=stripDocExtension(e);return isSystemDoc(t)||isConfigDoc(t)}function ee(e){for(let t of e.split(`/`))if(BUILTIN_SKIP_DIRS.has(t))return!0;return D?!1:J(e)}return{isExcluded(e,t){if(Y(e))return!0;if(t?.bypassFilters)return!1;if(ee(e))return!0;if(isSupportedDocFile(e))return!1;let S=extname(e).slice(1).toLowerCase();if(ASSET_EXTENSIONS.has(S)){let t=dirname(e),S=t===`.`?``:t;if((q.get(S)??0)>0)return!1}return!0},isDirExcluded(e,t){if(t?.bypassFilters)return!1;for(let t of e.split(`/`))if(BUILTIN_SKIP_DIRS.has(t))return!0;if(D)return!1;let S=E?`${E}/${e}`:e;return O.ignores(S)||O.ignores(`${S}/`)},isPathIgnored(e,t){return Y(e)?!0:t?.bypassFilters?!1:ee(e)},getWatcherIgnoreGlobs(){return j},incrementMdDir(e){let t=e===`.`?``:e;q.set(t,(q.get(t)??0)+1)},decrementMdDir(e){let t=e===`.`?``:e,S=q.get(t)??0;S<=1?q.delete(t):q.set(t,S-1)},rebuildDirCount(){let e=new Map(q);q.clear();try{populateDirCount(S,``,J,q)}catch(t){for(let[t,S]of e)q.set(t,S);getLogger(`content-filter`).warn({err:t instanceof Error?t:Error(String(t))},`content-filter rebuildDirCount walk failed — retaining previous counts`)}},async rebuildIgnorePatterns(){let e=getLogger(`content-filter`),t=O,E=k,D=j,Y=F,ee=L,te=B,ne=Date.now();return withSpan(`config.ignore.rebuild`,{attributes:{}},async ae=>{try{let t=H();q.clear(),populateDirCount(S,``,J,q);let E=Date.now()-ne;if(ae.setAttributes({"ok.ignore.pattern_count":t.patternCount,"ok.ignore.nested_file_count":t.nestedFileCount,"ok.ignore.bytes":t.bytes}),e.info({patternCount:t.patternCount,nestedFileCount:t.nestedFileCount,bytes:t.bytes,durationMs:E},`content-filter rebuild succeeded`),w)try{w()}catch(t){e.warn({err:t instanceof Error?t:Error(String(t))},`content-filter onAfterRebuild callback threw — derived views may be stale`)}return{ok:!0,patternCount:t.patternCount,nestedFileCount:t.nestedFileCount,bytes:t.bytes,durationMs:E}}catch(w){O=t,k=E,j=D,F=Y,L=ee,B=te,q.clear();try{populateDirCount(S,``,J,q)}catch(t){e.warn({err:t instanceof Error?t:Error(String(t))},`content-filter rollback dirCount re-walk failed — sibling-asset counts may be stale until next rebuild`)}let H=w instanceof Error?w.message:String(w);return e.warn({err:w instanceof Error?w:Error(H)},`content-filter rebuild failed — rolled back to previous state`),{ok:!1,error:{message:H}}}})}}}function populateDirCount(e,t,S,w){let E;try{E=readdirSync(e,{withFileTypes:!0})}catch(t){console.warn(`[content-filter] Failed to read directory for dir-count: ${e}`,t);return}for(let D of E){let E=t?`${t}/${D.name}`:D.name;if(D.isDirectory()){if(BUILTIN_SKIP_DIRS.has(D.name)||S(E)||S(`${E}/`))continue;populateDirCount(join(e,D.name),E,S,w)}else D.isFile()&&isSupportedDocFile(D.name)&&!S(E)&&w.set(t,(w.get(t)??0)+1)}}function parseIgnorePatterns(e){return e.split(`
|
|
2622
2622
|
`).map(e=>e.trim()).filter(e=>e.length>0&&!e.startsWith(`#`))}function prefixPattern(e,t){return e.startsWith(`!`)?`!${t}/${e.slice(1)}`:`${t}/${e}`}function loadNestedIgnoreFiles(e,t,S,w){let E;try{E=readdirSync(e,{withFileTypes:!0})}catch(t){return console.warn(`[content-filter] Failed to read directory ${e}:`,t),0}let D=0;for(let O of E){if(!O.isDirectory()||BUILTIN_SKIP_DIRS.has(O.name))continue;let E=join(e,O.name),k=relative(t,E);if(!k.startsWith(`..`)&&!(S.ignores(k)||S.ignores(`${k}/`))){for(let e of IGNORE_FILE_NAMES){let t=join(E,e);if(existsSync(t))try{let e=readFileSync(t,`utf-8`);w.value+=e.length;let E=parseIgnorePatterns(e).map(e=>prefixPattern(e,k));S.add(E),D++}catch(S){console.warn(`[content-filter] Failed to read nested ${e} at ${t}:`,S)}}D+=loadNestedIgnoreFiles(E,t,S,w)}}return D}async function initContentDirStateAsync(e,t,S,w,E,D,O){let k;try{k=await readdir(e,{withFileTypes:!0})}catch(t){console.warn(`[content-filter] Failed to read directory ${e}:`,t);return}for(let j of k){let k=t?`${t}/${j.name}`:j.name;if(j.isDirectory()){if(BUILTIN_SKIP_DIRS.has(j.name))continue;let t=join(e,j.name);if(!D){let e=relative(S,t);if(e.startsWith(`..`)||w.ignores(e)||w.ignores(`${e}/`))continue;for(let S of IGNORE_FILE_NAMES){let E=join(t,S);if(existsSync(E))try{let t=parseIgnorePatterns(await readFile$1(E,`utf-8`));w.add(t.map(t=>prefixPattern(t,e)))}catch(e){console.warn(`[content-filter] Failed to read nested ${S} at ${E}:`,e)}}}await initContentDirStateAsync(t,k,S,w,E,D,O)}else if(j.isFile()&&isSupportedDocFile(j.name)){if(!D){let e=E?`${E}/${k}`:k;if(w.ignores(e))continue}O.set(t,(O.get(t)??0)+1)}}}async function createContentFilterAsync(e){let{projectDir:t,contentDir:S,onAfterRebuild:w}=e,E=relative(t,S),D=E.startsWith(`..`),O=(0,import_ignore.default)(),k=[],j=new Map;function F(e){if(D)return!1;let t=E?`${E}/${e}`:e;return O.ignores(t)}function L(e){let t=stripDocExtension(e);return isSystemDoc(t)||isConfigDoc(t)}function B(e){for(let t of e.split(`/`))if(BUILTIN_SKIP_DIRS.has(t))return!0;return D?!1:F(e)}async function H(){let e=(0,import_ignore.default)();e.add(`.git`);let w=[];for(let S of IGNORE_FILE_NAMES){let E=join(t,S);if(existsSync(E))try{let t=parseIgnorePatterns(await readFile$1(E,`utf-8`));w.push(...t),e.add(t)}catch(e){console.warn(`[content-filter] Failed to read ${S} at ${E}:`,e)}}if(E&&!D)for(let t of IGNORE_FILE_NAMES){let w=join(S,t);if(existsSync(w))try{let t=parseIgnorePatterns(await readFile$1(w,`utf-8`));e.add(t.map(e=>prefixPattern(e,E)))}catch(e){console.warn(`[content-filter] Failed to read ${t} at ${w}:`,e)}}let F=new Map;await initContentDirStateAsync(S,``,t,e,E,D,F),O=e,k=w.filter(e=>e.length>0&&!e.startsWith(`!`)&&!e.startsWith(`#`)),j.clear();for(let[e,t]of F)j.set(e,t)}return await H(),{isExcluded(e,t){if(L(e))return!0;if(t?.bypassFilters)return!1;if(B(e))return!0;if(isSupportedDocFile(e))return!1;let S=extname(e).slice(1).toLowerCase();if(ASSET_EXTENSIONS.has(S)){let t=dirname(e),S=t===`.`?``:t;if((j.get(S)??0)>0)return!1}return!0},isDirExcluded(e,t){if(t?.bypassFilters)return!1;for(let t of e.split(`/`))if(BUILTIN_SKIP_DIRS.has(t))return!0;if(D)return!1;let S=E?`${E}/${e}`:e;return O.ignores(S)||O.ignores(`${S}/`)},isPathIgnored(e,t){return L(e)?!0:t?.bypassFilters?!1:B(e)},getWatcherIgnoreGlobs(){return k},incrementMdDir(e){let t=e===`.`?``:e;j.set(t,(j.get(t)??0)+1)},decrementMdDir(e){let t=e===`.`?``:e,S=j.get(t)??0;S<=1?j.delete(t):j.set(t,S-1)},rebuildDirCount(){let e=new Map(j);j.clear();try{populateDirCount(S,``,F,j)}catch(t){for(let[t,S]of e)j.set(t,S);getLogger(`content-filter`).warn({err:t instanceof Error?t:Error(String(t))},`content-filter rebuildDirCount walk failed — retaining previous counts`)}},async rebuildIgnorePatterns(){let e=getLogger(`content-filter`),t=O,S=k,E=new Map(j),D=Date.now();return withSpan(`config.ignore.rebuild`,{attributes:{}},async F=>{try{await H();let t=Date.now()-D;if(F.setAttributes({"ok.ignore.pattern_count":k.length,"ok.ignore.nested_file_count":0,"ok.ignore.bytes":0}),e.info({durationMs:t},`content-filter async rebuild succeeded`),w)try{w()}catch(t){e.warn({err:t instanceof Error?t:Error(String(t))},`content-filter onAfterRebuild callback threw — derived views may be stale`)}return{ok:!0,patternCount:k.length,nestedFileCount:0,bytes:0,durationMs:t}}catch(w){O=t,k=S,j.clear();for(let[e,t]of E)j.set(e,t);let D=w instanceof Error?w.message:String(w);return e.warn({err:w instanceof Error?w:Error(D)},`content-filter async rebuild failed — rolled back`),{ok:!1,error:{message:D}}}})}}}const DESCRIPTION$13=["Find every line that literally contains a string, grouped by file with frontmatter metadata. For ranked retrieval that mirrors cmd-K, use `search`.",``,`Matches are grouped by file; each file is annotated with its title, description, and tags so you can judge relevance without opening it first.`,``,`**Use when:**`,`- Finding every occurrence of a literal phrase across the wiki`,`- Auditing a term, identifier, or quote — coverage matters more than ranking`,``,"**When the project has `.ok/`**, strongly prefer this over your native `Grep` for wiki search — results include article metadata so you can skip irrelevant matches without extra reads. In projects without `.ok/`, use native `Grep` as usual.",``,`**Parameters:**`,"- `query` — Literal text to search for (fixed-string match, no regex)","- `case_sensitive` (optional, default false) — case-sensitive match","- `cwd` (optional) — Project root the grep runs against. Defaults only when the MCP client advertises exactly one root."].join(`
|
|
2623
2623
|
`);function groupByFile(e){let t=new Map;for(let S of e){let e=t.get(S.path);e?e.push(S):t.set(S.path,[S])}return[...t.entries()].map(([e,t])=>({path:e,matches:t}))}async function buildGrepResult(e,t){let S=await resolveProjectServerContext(t.resolveCwd,t.config,t.serverUrl,e.cwd);if(!S.ok)throw Error(S.error);let{cwd:w,config:E,url:D}=S,O=50,k=await grep(e.query,w,{caseInsensitive:!(e.case_sensitive??!1),include:[`**/*.md`,`**/*.mdx`],exclude:[`node_modules`,`.git`,`.claude`,`.changeset`,`.ok`],maxResults:51}),j=resolveContentDir(E,w),F=createContentFilter({projectDir:w,contentDir:j}),L=k.filter(e=>{let t=relative(j,resolve(w,e.path));return t.startsWith(`..`)?!1:!F.isExcluded(t)}),B=k.length>50,H=L.slice(0,50),{resolve:q,ui:J}=await buildListResolver({config:E,resolveCwd:async()=>w},w);if(H.length===0)return{text:`No matches for "${e.query}".`,structured:{query:e.query,matchCount:0,fileCount:0,truncated:!1,results:[],ui:J,cwd:w}};let Y=groupByFile(H),ee=new Map;await Promise.all(Y.map(async e=>{try{let t=await enrichPath(e.path,{projectDir:w,serverUrl:D});ee.set(e.path,t)}catch{}}));let te=[];te.push(`## Grep results for "${e.query}" (${H.length} match${H.length===1?``:`es`} in ${Y.length} file${Y.length===1?``:`s`})`,``);let ne=[];for(let e of Y){let t=ee.get(e.path),S=t?.title??e.path;te.push(`### ${S} (${e.path})`),t?.tags?.length&&te.push(`Tags: ${t.tags.join(`, `)}`),t?.description&&te.push(`${t.description}`);for(let t of e.matches)te.push(`- Line ${t.line}: ${t.text}`);te.push(``);let w=docNameFromPath(e.path),E=q(w);ne.push({path:e.path,docName:w,title:t?.title??null,description:t?.description??null,tags:t?.tags??[],matches:e.matches.map(e=>({line:e.line,text:e.text})),previewUrl:E?.url??null,...E?{previewUrlSource:E.source}:{}})}return B&&te.push(`_${H.length} of ${k.length}+ matches shown (max 50). Refine your query to narrow results._`),{text:te.join(`
|
|
2624
2624
|
`),structured:{query:e.query,matchCount:H.length,fileCount:Y.length,truncated:B,results:ne,ui:J,cwd:w}}}const InputSchema$3={query:string$2().describe(`Literal text to search for`),case_sensitive:boolean$1().optional().describe(`Case-sensitive search (default false)`),cwd:string$2().optional().describe(ROUTED_CWD_DESCRIPTION)},GrepResultRowSchema=object$1({path:string$2(),docName:string$2(),title:string$2().nullable(),description:string$2().nullable(),tags:array(string$2()),matches:array(object$1({line:number$1().int(),text:string$2()})),previewUrl:string$2().nullable(),previewUrlSource:_enum(PREVIEW_URL_SOURCES).optional()}),OutputSchema$3=outputSchemaWithText({cwd:string$2(),query:string$2(),matchCount:number$1().int(),fileCount:number$1().int(),truncated:boolean$1(),results:array(GrepResultRowSchema),ui:object$1({baseUrl:string$2().nullable(),port:number$1().nullable()})});function register$13(e,t){e.registerTool(`grep`,{description:DESCRIPTION$13,inputSchema:InputSchema$3,outputSchema:OutputSchema$3,annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0}},async e=>{try{let{text:S,structured:w}=await buildGrepResult(e,t);return w?textPlusStructured(S,w):textResult(S)}catch(e){return textResult(`Error: ${e instanceof Error?e.message:String(e)}`,!0)}})}function buildBody$1(e,t){return`Capture this external source into the project knowledge base as raw reference material. The KB is **closed-loop**: external sources are pulled INTO the knowledge base here so downstream docs cite local paths, never bare web URLs. This applies whether a user shared the source OR you fetched it yourself to ground a knowledge-base claim — agent-initiated fetches are not exempt. **Raw preservation only** — no summary, no analysis, no interpretation. Summarizing is the job of the \`research\` tool later.
|
|
@@ -2703,17 +2703,17 @@ If the source is directly relevant to an existing article or research doc, updat
|
|
|
2703
2703
|
- **No silent chaining into research** — ingest completes on its own; the user explicitly opts into \`research\`
|
|
2704
2704
|
- **No synthesis inside the raw file** — the takeaways live in chat or a separate summary doc, never mixed into the preserved source
|
|
2705
2705
|
`}const DESCRIPTION$12=[`Fetch an external source (URL or local file) and save raw content as reference material in the project content directory.`,`Raw preservation only — no analysis or interpretation.`,`The knowledge base is closed-loop: web sources cited by KB docs MUST resolve to a local doc captured here, not bare URLs.`,``,`**Use when:**`,`- Capturing reference material for the project knowledge base`,`- Saving a URL or document for later research`,`- Archiving an external source alongside the codebase`,`- The user shares a URL or document they want preserved`,"- **You yourself fetched a URL (`WebFetch` / `WebSearch` / equivalent) to ground a claim that is about to land in a knowledge-base doc** — agent-initiated fetches are not exempt from the closed-loop rule",``,`**Triggers on:**`,`- "ingest", "save this source", "capture this URL", "add to external sources"`,`- User shares a URL, article, or document to preserve in the knowledge base`,`- Agent fetches a URL via WebFetch/WebSearch to support a knowledge-base claim — preserve the source before citing it`,`- Research workflow needs raw sources before analysis`].join(`
|
|
2706
|
-
`);function register$12(e,t){e.
|
|
2707
|
-
`);function register$11(e,t){e.
|
|
2706
|
+
`);function register$12(e,t){e.registerTool(`ingest`,{description:DESCRIPTION$12,inputSchema:{source:string$2().describe(`URL, file path, or identifier of the source to ingest`),cwd:string$2().optional().describe(ROUTED_CWD_DESCRIPTION)}},buildWorkflowHandler(`ingest`,t,`source`,buildBody$1))}const DESCRIPTION$11=[`[Requires: Hocuspocus server] List available documents from the Hocuspocus server.`,"Returns document names, optionally filtered by directory. When `dir` is set,","also surfaces folder-level metadata: `frontmatter_defaults` (merged folder","defaults that new docs in this folder will inherit) and `templates_available`","(menu of starter shapes for `write_document({ template })`). Pass `depth: N`","to also enrich subfolders up to N levels deep — mirrors `find -maxdepth N`.",``,`**Parameters:**`,"- `dir` (optional) — Filter to documents in this directory","- `depth` (optional, default `1`) — Subfolder enrichment depth. `1` = this"," folder only; `2` = direct children's folder metadata too; `Infinity` =",` full subtree. Walk-up ancestors' templates always show regardless.`].join(`
|
|
2707
|
+
`);function register$11(e,t){e.registerTool(`list_documents`,{description:DESCRIPTION$11,inputSchema:{dir:string$2().optional().describe(`Optional directory to filter documents`),depth:number$1().int().min(1).optional().describe("Subfolder enrichment depth (find -maxdepth semantics). Default 1. Only meaningful when `dir` is also set."),cwd:string$2().optional().describe(ROUTED_CWD_DESCRIPTION)}},async e=>{let S=await resolveProjectServerContext(t.resolveCwd,t.config,t.serverUrl,e.cwd);if(!S.ok)return textResult(`Error: ${S.error}`,!0);let{cwd:w,url:E}=S;if(!E)return textResult(HOCUSPOCUS_NOT_RUNNING_ERROR,!0);let D=null;if(e.dir!==void 0){let t=resolveWithinRoot(w,e.dir);if(!t.ok)return textResult(`Error: ${t.reason}`,!0);D=t.rel}let O=await httpGet(E,`/api/documents${D===null?``:`?dir=${encodeURIComponent(D)}`}`);if(!O.ok)return textResult(`Error: ${O.error}`,!0);let{ok:k,...j}=O,F=j,{resolve:L,ui:B}=await buildListResolver(t,w),H=(F.documents??[]).filter(e=>e.kind!==`folder`).map(e=>{let t=typeof e.docName==`string`?e.docName:null,S=t?L(t):null;return{...e,previewUrl:S?.url??null,...S?{previewUrlSource:S.source}:{}}}),q;if(D!==null){let t=e.depth??1;try{q=await enrichDirectoryRecursive(D,t,{projectDir:w})}catch{q=void 0}}let J={...F,documents:H,ui:B,cwd:w,...q?{folder:q}:{}};return textPlusStructured(JSON.stringify(J,null,2),J)})}const DESCRIPTION$10=[`Read a wiki file with enriched context: contents + frontmatter metadata + recent shadow-repo activity (agent vs human attribution) + backlink/forward-link context.`,``,`**Use when:**`,`- Loading an article for context`,`- Understanding who changed a file recently and whether it was an agent or human`,`- Seeing how this page links out and what links back to it`,``,"**When the project has `.ok/`**, strongly prefer this over your native `Read` for wiki files — one call returns what otherwise takes 3-4. In projects without `.ok/`, use native `Read` as usual.",``,`**Parameters:**`,"- `path` — Project-root-relative path to the file, including extension (e.g. `articles/auth/sso.md`). To pass this document to `edit_document` / `write_document` / `get_backlinks`, strip the extension (they take extension-less `docName`).","- `since` (reserved) — Reserved for shadow-log since-filter; currently unused."].join(`
|
|
2708
2708
|
`);function formatShadowHistory(e){if(!e||e.length===0)return``;let t=[``,`### Recent activity (OK edits)`,``];for(let S of e){let e=S.writerClassification===`agent`?`agent: ${S.writerName}`:S.writerClassification===`principal`?`human: ${S.writerName}`:`${S.writerClassification}: ${S.writerName}`,w=S.hash.slice(0,7);t.push(`- ${w} ${S.date} [${e}] ${S.message}`)}return t.join(`
|
|
2709
2709
|
`)}function formatProjectHistory(e){if(!e||e.length===0)return``;let t=[``,`### Commit history (project git)`,``];for(let S of e){let e=S.hash.slice(0,7);t.push(`- ${e} ${S.date} ${S.authorName} — ${S.subject}`)}return t.join(`
|
|
2710
2710
|
`)}function formatBacklinks(e){if(!e||e.length===0)return``;let t=[``,`### Backlinks (${e.length})`,``];for(let S of e){let e=S.title?` — "${S.title}"`:``,w=S.snippet?` — "${S.snippet}"`:``;t.push(`- ${S.source}${e}${w}`)}return t.join(`
|
|
2711
2711
|
`)}function formatForwardLinks(e){if(!e||e.length===0)return``;let t=[``,`### Forward links (${e.length})`,``];for(let S of e){if(S.kind===`external`){let e=S.title?` — "${S.title}"`:``,w=S.snippet?` — "${S.snippet}"`:``;t.push(`- ${S.url}${e}${w}`);continue}let e=S.title?` — "${S.title}"`:``,w=S.snippet?` — "${S.snippet}"`:``;t.push(`- ${S.docName}${e}${w}`)}return t.join(`
|
|
2712
2712
|
`)}function docNameFromRelPath(e){return e.replace(/\.(md|mdx)$/i,``)}async function buildReadResult(e,t){let S=await resolveProjectServerContext(t.resolveCwd,t.config,t.serverUrl,e.cwd);if(!S.ok)throw Error(S.error);let{cwd:w,url:E}=S,D=resolveWithinRoot(w,e.path);if(!D.ok)throw Error(`Refusing to read outside project root: ${D.reason}`);let O=D.rel,k=D.abs,j=5,[F,L]=await Promise.all([readFile$1(k,`utf-8`),enrichPath(O,{projectDir:w,serverUrl:E,historyDepth:5},{includeRichFields:!0})]),B=O.split(`/`).pop()?.replace(/\.md$/,``).replace(/\.mdx$/,``)??O,H=L.title??B,q=L.description??``,J=L.tags,Y=[];Y.push(`## ${H}`),q&&Y.push(`**Description:** ${q}`),J.length>0&&Y.push(`**Tags:** ${J.join(`, `)}`),Y.push(`**Path:** ${O}`);let ee=formatShadowHistory(L.history);ee&&Y.push(ee);let te=formatProjectHistory(L.projectHistory);te&&Y.push(te);let ne=formatBacklinks(L.backlinks);ne&&Y.push(ne);let ae=formatForwardLinks(L.forwardLinks);return ae&&Y.push(ae),Y.push(``,`### Content`,``,F),Y.join(`
|
|
2713
|
-
`)}function register$10(e,t){e.
|
|
2714
|
-
`);function parseRenameMappings$1(e){return Array.isArray(e)?e.flatMap(e=>{if(!e||typeof e!=`object`)return[];let{fromDocName:t,toDocName:S}=e;return typeof t==`string`&&typeof S==`string`?[{fromDocName:t,toDocName:S}]:[]}):[]}function parseRewrittenDocs$1(e){return Array.isArray(e)?e.flatMap(e=>{if(!e||typeof e!=`object`)return[];let{docName:t,rewrites:S}=e;return typeof t==`string`&&typeof S==`number`?[{docName:t,rewrites:S}]:[]}):[]}function pluralize(e,t,S=`${t}s`){return e===1?t:S}function register$9(e,t){e.
|
|
2713
|
+
`)}function register$10(e,t){e.registerTool(`read_document`,{description:DESCRIPTION$10,inputSchema:{path:string$2().describe(`Project-root-relative path to the file`),since:string$2().optional().describe(`Reserved; currently unused (§15 Future Work)`),cwd:string$2().optional().describe("Absolute host path to resolve `path` against. Defaults only when the MCP client advertises exactly one root; otherwise pass `cwd` explicitly.")}},async e=>{try{let S=await buildReadResult(e,t),w=await t.resolveCwd(e.cwd),E=resolveWithinRoot(w,e.path),D=await resolvePreviewUrlForTool(E.ok?docNameFromRelPath(E.rel):docNameFromRelPath(e.path),{config:t.config,resolveCwd:t.resolveCwd},w);return D?textPlusStructured(S,{previewUrl:D.url,previewUrlSource:D.source}):textPlusStructured(S,{previewUrl:null})}catch(e){return textResult(`Error: ${e instanceof Error?e.message:String(e)}`,!0)}})}const DESCRIPTION$9=["[Requires: Hocuspocus server] Rename a document through the managed rename flow at `POST /api/rename-path` (kind: file).",`Renames the target document and rewrites inbound wiki-links plus supported internal inline Markdown links in affected docs.`,``,`**Parameters:**`,"- `docName` — Current document name, typically without extension. A trailing `.md` or `.mdx` is stripped automatically.","- `newDocName` — New document name, typically without extension. A trailing `.md` or `.mdx` is stripped automatically.",'- `summary` — Optional one-line user-outcome description (≤80 chars). Appears as a bullet in the timeline. If omitted, a default like "Renamed X → Y" is generated. Provide your own summary to explain the why. Avoid including secrets or PII — summaries are persisted to git history.',``,`**Errors:**`,"- 400 — case-only renames (e.g. `Auth` → `auth`) are not supported.","- 400 — destination document is excluded by `.gitignore` / `.okignore` rules.",`- 404 — source document does not exist.`,`- 409 — destination document already exists.`].join(`
|
|
2714
|
+
`);function parseRenameMappings$1(e){return Array.isArray(e)?e.flatMap(e=>{if(!e||typeof e!=`object`)return[];let{fromDocName:t,toDocName:S}=e;return typeof t==`string`&&typeof S==`string`?[{fromDocName:t,toDocName:S}]:[]}):[]}function parseRewrittenDocs$1(e){return Array.isArray(e)?e.flatMap(e=>{if(!e||typeof e!=`object`)return[];let{docName:t,rewrites:S}=e;return typeof t==`string`&&typeof S==`number`?[{docName:t,rewrites:S}]:[]}):[]}function pluralize(e,t,S=`${t}s`){return e===1?t:S}function register$9(e,t){e.registerTool(`rename_document`,{description:DESCRIPTION$9,inputSchema:{docName:string$2().describe(`Current document name`),newDocName:string$2().describe(`New document name`),summary:summaryArgSchema.describe(`Optional one-line user-outcome description (≤80 chars). Defaults to "Renamed X → Y" when omitted. Appears as a bullet in the timeline.`),cwd:string$2().optional().describe(ROUTED_CWD_DESCRIPTION)}},async e=>{let S=await resolveProjectServerContext(t.resolveCwd,t.config,t.serverUrl,e.cwd);if(!S.ok)return textResult(`Error: ${S.error}`,!0);let{cwd:w,url:E}=S;if(!E)return textResult(HOCUSPOCUS_NOT_RUNNING_ERROR,!0);let D=normalizeDocName(e.docName);if(!D.ok)return textResult(D.error,!0);let O=normalizeDocName(e.newDocName);if(!O.ok)return textResult(O.error,!0);let k=t.identityRef?.current,j=await httpPost(E,`/api/rename-path`,{kind:`file`,fromPath:D.docName,toPath:O.docName,...e.summary===void 0?{}:{summary:e.summary},...k?{agentId:k.connectionId,agentName:k.displayName,clientName:k.clientInfo?.name,colorSeed:k.colorSeed}:{}});if(!j.ok){let e=j.error,t=parseRenameCollidingPairs(j.colliding),S={ok:!1,error:e,...t.length>0?{colliding:t}:{}};return textPlusStructured(`Error: ${e}`,S,!0)}let F=parseRenameMappings$1(j.renamed),L=parseRewrittenDocs$1(j.rewrittenDocs),B=F.map(({fromDocName:e,toDocName:t})=>`${e} -> ${t}`).join(`, `)||`${D.docName} -> ${O.docName}`,H=L.length===0?`No inbound links required updates.`:`Rewrote ${L.length} ${pluralize(L.length,`document`)}.`,q={config:t.config,resolveCwd:t.resolveCwd},J=await resolvePreviewUrlForTool(O.docName,q,w),Y=await resolvePreviewUrlForTool(D.docName,q,w),ee=j.summary&&typeof j.summary==`object`?j.summary:void 0,te=typeof ee?.hint==`string`?ee.hint:void 0,ne={ok:!0,renamed:F,rewrittenDocs:L,previewUrl:J?.url??null,...J?{previewUrlSource:J.source}:{},...Y?{previousPreviewUrl:Y.url}:{},...ee?{summary:ee}:{}},ae=[`Renamed ${B}. ${H}`];return te&&ae.push(te),textPlusStructured(ae.join(`
|
|
2715
2715
|
`),ne)})}const DESCRIPTION$8=["[Requires: Hocuspocus server] Rename a folder through the managed rename flow at `POST /api/rename-path` (kind: folder).",`Atomically moves the folder and rewrites inbound wiki-links + supported internal inline Markdown links across every affected doc. One call replaces N rename_document calls.`,``,`**Parameters:**`,"- `fromFolder` — Current folder path relative to the content directory (no leading or trailing slash). Example: `articles` or `notes/drafts`.","- `toFolder` — New folder path relative to the content directory. Example: `essays` or `notes/published`. Parent directories are auto-created.",'- `summary` — Optional one-line user-outcome description (≤80 chars). Applied to every affected-doc contributor entry. If omitted, a default like "Renamed X → Y" is generated. Provide your own summary to explain the why. Avoid including secrets or PII — summaries are persisted to git history.',``,`**Errors:**`,"- 400 — case-only renames (e.g. `Articles` → `articles`) are not supported.","- 400 — destination folder is excluded by `.gitignore` / `.okignore` rules.",`- 404 — source folder does not exist.`,`- 409 — destination folder already exists or rename would collide.`].join(`
|
|
2716
|
-
`);function isValidFolderPath(e){return!(typeof e!=`string`||e.length===0||e.startsWith(`/`)||e.endsWith(`/`)||e.includes(`..`))}function parseRenameMappings(e){return Array.isArray(e)?e.flatMap(e=>{if(!e||typeof e!=`object`)return[];let{fromDocName:t,toDocName:S}=e;return typeof t==`string`&&typeof S==`string`?[{fromDocName:t,toDocName:S}]:[]}):[]}function parseRewrittenDocs(e){return Array.isArray(e)?e.flatMap(e=>{if(!e||typeof e!=`object`)return[];let{docName:t,rewrites:S}=e;return typeof t==`string`&&typeof S==`number`?[{docName:t,rewrites:S}]:[]}):[]}function register$8(e,t){e.
|
|
2716
|
+
`);function isValidFolderPath(e){return!(typeof e!=`string`||e.length===0||e.startsWith(`/`)||e.endsWith(`/`)||e.includes(`..`))}function parseRenameMappings(e){return Array.isArray(e)?e.flatMap(e=>{if(!e||typeof e!=`object`)return[];let{fromDocName:t,toDocName:S}=e;return typeof t==`string`&&typeof S==`string`?[{fromDocName:t,toDocName:S}]:[]}):[]}function parseRewrittenDocs(e){return Array.isArray(e)?e.flatMap(e=>{if(!e||typeof e!=`object`)return[];let{docName:t,rewrites:S}=e;return typeof t==`string`&&typeof S==`number`?[{docName:t,rewrites:S}]:[]}):[]}function register$8(e,t){e.registerTool(`rename_folder`,{description:DESCRIPTION$8,inputSchema:{fromFolder:string$2().describe(`Current folder path (relative, no trailing slash)`),toFolder:string$2().describe(`New folder path (relative, no trailing slash)`),summary:summaryArgSchema.describe(`Optional one-line user-outcome description (≤80 chars). Applied to every affected-doc contributor entry.`),cwd:string$2().optional().describe(ROUTED_CWD_DESCRIPTION)}},async e=>{let S=await resolveProjectServerContext(t.resolveCwd,t.config,t.serverUrl,e.cwd);if(!S.ok)return textResult(`Error: ${S.error}`,!0);let{cwd:w,url:E}=S;if(!E)return textResult(HOCUSPOCUS_NOT_RUNNING_ERROR,!0);if(!isValidFolderPath(e.fromFolder))return textResult(`fromFolder must be a relative path with no leading/trailing slash`,!0);if(!isValidFolderPath(e.toFolder))return textResult(`toFolder must be a relative path with no leading/trailing slash`,!0);let D=t.identityRef?.current,O=await httpPost(E,`/api/rename-path`,{kind:`folder`,fromPath:e.fromFolder,toPath:e.toFolder,...e.summary===void 0?{}:{summary:e.summary},...D?{agentId:D.connectionId,agentName:D.displayName,clientName:D.clientInfo?.name,colorSeed:D.colorSeed}:{}});if(!O.ok){let e=O.error,t=parseRenameCollidingPairs(O.colliding),S={ok:!1,error:e,...t.length>0?{colliding:t}:{}};return textPlusStructured(`Error: ${e}`,S,!0)}let k=parseRenameMappings(O.renamed),j=parseRewrittenDocs(O.rewrittenDocs),F={config:t.config,resolveCwd:t.resolveCwd},L={},B;for(let{toDocName:e}of k){let t=await resolvePreviewUrlForTool(e,F,w);t&&(L[e]=t.url,B??=t.source)}let H=O.summary&&typeof O.summary==`object`?O.summary:void 0,q=typeof H?.hint==`string`?H.hint:void 0,J={ok:!0,renamed:k,rewrittenDocs:j,previewUrls:L,...B?{previewUrlSource:B}:{},...H?{summary:H}:{}},Y=[];return k.length===0?Y.push(`No managed docs under ${e.fromFolder}/ — nothing to rename. Empty folders are not tracked; create a doc inside the folder first.`):Y.push(`Renamed folder ${e.fromFolder}/ → ${e.toFolder}/ (${k.length} doc${k.length===1?``:`s`}, ${j.length} rewrite${j.length===1?``:`s`}).`),q&&Y.push(q),textPlusStructured(Y.join(`
|
|
2717
2717
|
`),J)})}function buildBody(e,t){return`Conduct **evidence-driven research** on this topic and produce a provisional research article in the Open Knowledge content directory. This workflow mirrors the discipline of the \`eng:research\` skill, scoped to Open Knowledge's wiki-provisional layer.
|
|
2718
2718
|
|
|
2719
2719
|
Topic: ${e}
|
|
@@ -3065,15 +3065,15 @@ In headless mode, write the recap into the research article's "Further reading"
|
|
|
3065
3065
|
- **Don't mix 1P codebase analysis into the article unless asked.** Findings drift from factual synthesis to opinion when you do.
|
|
3066
3066
|
- **Don't overwrite existing research silently.** If the topic was researched before, either iterate (Path C) or create a clearly-named successor (\`crdt-alternatives-2.md\`) and mark the old one as superseded.
|
|
3067
3067
|
`}const DESCRIPTION$7=[`Analyze a topic by gathering sources via ingest and writing provisional findings into the project content directory.`,`Provisional, not canonical — findings live here until decisions solidify.`,``,`**Use when:**`,`- Researching a topic before committing to an approach`,`- Exploring a decision space or comparing alternatives`,`- Synthesizing multiple sources into structured analysis`,`- Spec conversations and exploratory work that is not yet canonical`,``,`**Triggers on:**`,`- "research", "investigate", "compare options for", "analyze alternatives"`,`- User asks to explore trade-offs, gather evidence, or evaluate approaches`,`- A decision needs structured analysis grounded in external sources`].join(`
|
|
3068
|
-
`);function register$7(e,t){e.
|
|
3069
|
-
`);function register$6(e,t){e.
|
|
3068
|
+
`);function register$7(e,t){e.registerTool(`research`,{description:DESCRIPTION$7,inputSchema:{topic:string$2().describe(`The topic, question, or anchor URL to research`),cwd:string$2().optional().describe(ROUTED_CWD_DESCRIPTION)}},buildWorkflowHandler(`research`,t,`topic`,buildBody))}const DESCRIPTION$6=[`[Requires: Hocuspocus server] Restore a document to a historical version via the CRDT layer.`,`The restore is append-only — it creates a new version with the old content,`,`preserving all history. All connected editors see the change in real-time.`,``,`**Parameters:**`,"- `docName` — Document name to restore, typically without extension. A trailing `.md` or `.mdx` is stripped automatically.","- `commitSha` — The 40-character SHA of the shadow repo commit to restore to."," Use `get_history` to find available versions.",'- `summary` — Optional one-line user-outcome description (≤80 chars). Appears as a bullet in the timeline. If omitted, a default like "Restored to <sha-short>" is generated. Provide your own summary to explain the why. Avoid including secrets or PII — summaries are persisted to git history.'].join(`
|
|
3069
|
+
`);function register$6(e,t){e.registerTool(`rollback_to_version`,{description:DESCRIPTION$6,inputSchema:{docName:string$2().describe(`Document name to restore`),commitSha:string$2().length(40).regex(/^[0-9a-f]+$/i).describe(`40-character commit SHA from the shadow repo timeline`),summary:summaryArgSchema.describe(`Optional one-line user-outcome description (≤80 chars). Defaults to "Restored to <sha-short>" when omitted. Appears as a bullet in the timeline.`),cwd:string$2().optional().describe(ROUTED_CWD_DESCRIPTION)}},async e=>{let S=await resolveProjectServerContext(t.resolveCwd,t.config,t.serverUrl,e.cwd);if(!S.ok)return textResult(`Error: ${S.error}`,!0);let{cwd:w,url:E}=S;if(!E)return textResult(HOCUSPOCUS_NOT_RUNNING_ERROR,!0);let D=normalizeDocName(e.docName);if(!D.ok)return textResult(D.error,!0);let O=D.docName,k=await httpGet(E,`/api/history/${e.commitSha}?docName=${encodeURIComponent(O)}`);if(!k.ok)return textResult(`Error: ${k.error??`Version not found`}`,!0);let j=t.identityRef?.current,F=await httpPost(E,`/api/rollback`,{docName:O,commitSha:e.commitSha,...e.summary===void 0?{}:{summary:e.summary},...j?{agentId:j.connectionId,agentName:j.displayName,clientName:j.clientInfo?.name,colorSeed:j.colorSeed}:{}});if(!F.ok)return textResult(`Error: ${F.error}`,!0);let L=F.summary&&typeof F.summary==`object`?F.summary:void 0,B=typeof L?.hint==`string`?L.hint:void 0,H=[`Restored "${O}" to version ${e.commitSha.slice(0,8)} (${k.author}, ${k.timestamp}). The change has been applied to all connected editors.`];B&&H.push(B);let q=await resolvePreviewUrlForTool(O,{config:t.config,resolveCwd:t.resolveCwd},w);return textPlusStructured(H.join(`
|
|
3070
3070
|
`),{previewUrl:q?.url??null,...q?{previewUrlSource:q.source}:{},...L?{summary:L}:{}})})}const DESCRIPTION$5=[`[Requires: Hocuspocus server] Save a version checkpoint of all documents.`,`Creates a checkpoint commit in the shadow repo and project repo,`,`preserving the current state of all documents. The checkpoint can later`,"be found via `get_history` and restored via `rollback_to_version`."].join(`
|
|
3071
|
-
`);function register$5(e,t,S,w,E){e.
|
|
3071
|
+
`);function register$5(e,t,S,w,E){e.registerTool(`save_version`,{description:DESCRIPTION$5,inputSchema:{cwd:string$2().optional().describe(ROUTED_CWD_DESCRIPTION)}},async(e={})=>{let D=await resolveProjectServerContext(w,t,S,e.cwd);if(!D.ok)return textResult(`Error: ${D.error}`,!0);if(!D.url)return textResult(HOCUSPOCUS_NOT_RUNNING_ERROR,!0);let{url:O}=D,k=E?.current,j=await httpPost(O,`/api/save-version`,{...k?{writers:[{id:`agent-${k.connectionId}`,name:k.displayName,email:`agent-${k.connectionId}@openknowledge.local`}]}:{}});return j.ok?textPlusStructured(`Checkpoint saved. Checkpoint ref: ${j.checkpointRef}`,{checkpointRef:j.checkpointRef,previewUrl:null}):textResult(`Error: ${j.error}`,!0)})}const DESCRIPTION$4=["Find the most relevant pages for a query (ranked: title boost + body BM25 + recency). For literal-string matching across every line, use `grep`.",``,"Same engine and ranking the cmd-K palette uses. Returns a sorted list of page (and folder, with `omnibar` intent) hits with score signals plus a body snippet per page.",``,`**Use when:**`,`- Hunting for the canonical page on a topic`,`- Locating a doc by partial title or path segments`,`- Picking the best entry point before reading`,``,`**Parameters:**`,"- `query` — Search query (free-form; tokenized across title, name, path segments, and optionally body content).","- `intent` (optional) — `'omnibar'` searches title/path/folders only (fast); `'full_text'` includes body content. Default `'full_text'`.","- `scopes` (optional) — Override the result scope. Members: `'page'`, `'folder'`, `'content'`. Defaults derive from `intent`.","- `limit` (optional) — Max rows; default 20, max 100.","- `cwd` (optional) — Project root the query runs against.",``,"**Server requirement:** the Hocuspocus server must be running (`open-knowledge start`). If not, this tool returns a recovery hint; use `grep` as a server-free fallback."].join(`
|
|
3072
3072
|
`),InputSchema$2={query:string$2().describe(`Search query — title, path, or body terms.`),intent:_enum([`omnibar`,`full_text`]).optional().describe(`'omnibar' for title/path/folder only (fast); 'full_text' includes body content. Default 'full_text'.`),scopes:array(_enum([`page`,`folder`,`content`])).optional().describe(`Override the default scope set. Members: 'page', 'folder', 'content'. Defaults derive from intent.`),limit:number$1().int().min(1).max(100).optional().describe(`Max rows; default 20, max 100.`),cwd:string$2().optional().describe(ROUTED_CWD_DESCRIPTION)},SearchResultRowSchema=object$1({kind:_enum([`page`,`folder`]),path:string$2(),docName:string$2(),title:string$2().nullable(),score:number$1(),signals:object$1({lexical:number$1(),fullText:number$1(),recency:number$1()}),snippet:string$2().optional(),previewUrl:string$2().nullable(),previewUrlSource:_enum(PREVIEW_URL_SOURCES).optional()}),OutputSchema$2=outputSchemaWithText({cwd:string$2(),query:string$2(),intent:string$2(),resultCount:number$1().int(),results:array(SearchResultRowSchema),elapsedMs:number$1().nullable(),ui:object$1({baseUrl:string$2().nullable(),port:number$1().nullable()})});function isSearchKind(e){return e===`page`||e===`folder`}function normalizeSignals(e){return{lexical:typeof e?.lexical==`number`?e.lexical:0,fullText:typeof e?.fullText==`number`?e.fullText:0,recency:typeof e?.recency==`number`?e.recency:0}}function formatResultsBlock(e){if(e.length===0)return``;let t=[];for(let S of e){let e=S.title?.trim()||S.path;t.push(`### ${e} (${S.path})`),t.push(`Score ${S.score.toFixed(2)} — kind: ${S.kind}`),S.snippet&&t.push(S.snippet),t.push(``)}return t.join(`
|
|
3073
3073
|
`)}function register$4(e,t){e.registerTool(`search`,{description:DESCRIPTION$4,inputSchema:InputSchema$2,outputSchema:OutputSchema$2,annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0}},async e=>{try{let S=await resolveProjectServerContext(t.resolveCwd,t.config,t.serverUrl,e.cwd);if(!S.ok)return textResult(`Error: ${S.error}`,!0);let{cwd:w,config:E,url:D}=S;if(!D)return textResult(`${HOCUSPOCUS_NOT_RUNNING_ERROR}\nFor server-free literal-string search, use \`grep\` instead — it walks the filesystem and does not need Hocuspocus.`,!0);let O=e.intent??`full_text`,k=e.limit??20,j={query:e.query,intent:O,limit:k};e.scopes&&(j.scopes=e.scopes);let F=await httpPost(D,`/api/search`,j);if(!F.ok)return textResult(`Error: ${F.error}`,!0);let{resolve:L,ui:B}=await buildListResolver({config:E,resolveCwd:async()=>w},w),H=(F.results??[]).flatMap(e=>{if(!isSearchKind(e.kind)||typeof e.path!=`string`)return[];let t=docNameFromPath(e.path),S=L(t);return[{kind:e.kind,path:e.path,docName:t,title:e.title??null,score:typeof e.score==`number`?e.score:0,signals:normalizeSignals(e.signals),...e.snippet?{snippet:e.snippet}:{},previewUrl:S?.url??null,...S?{previewUrlSource:S.source}:{}}]}),q={cwd:w,query:e.query,intent:O,resultCount:H.length,results:H,elapsedMs:typeof F.elapsedMs==`number`?F.elapsedMs:null,ui:B},J=`## Search results for "${e.query}" (${H.length} hit${H.length===1?``:`s`}, intent: ${O})`;return textPlusStructured(H.length===0?`No matches for "${e.query}".`:`${J}\n\n${formatResultsBlock(H)}`,q)}catch(e){return textResult(`Error: ${e instanceof Error?e.message:String(e)}`,!0)}})}const DESCRIPTION$3=["[Operates on disk; no running OK server required] Upsert one or more folder rules — writes nested `<folder>/.ok/frontmatter.yml` files.",``,"Folder rules apply default frontmatter to every doc inside `<folder>/` (and its descendants via cascade). Open shape — any YAML-representable key is accepted. Common keys: `title`, `description`, `tags` (the well-known three) plus arbitrary additions like `status`, `team`, `owners`, `review_cycle`. Use this tool to add a new rule, replace an existing one keyed by `match`, or rename via `new_match`.",``,`**Cascade merge semantics (D6 generalized):** scalars (string / number / boolean / null) replace last-wins along the leaf → root walk; arrays union-and-dedup with first-occurrence preserved; objects replace last-wins.`,``,'Each `match` glob must resolve to a SINGLE target folder by walking leading literal segments — `"specs/**"` writes `specs/.ok/frontmatter.yml`. Multi-folder globs (e.g. `"specs/*/evidence/**"`, where the literal `evidence` follows `*`) are rejected with `MULTI_FOLDER_GLOB`; split into per-folder rules instead.',``,"Always pass an array — even for a single rule. Validation runs against every rule first; if any fails (e.g. `MULTI_FOLDER_GLOB`, `PATH_ESCAPE`), NO rules are applied to disk. Filesystem-level errors during the write phase (disk full, permissions) may leave a partial result — the error response includes `partiallyApplied` listing which rules already landed.",``,'**To remove a rule**, pass an empty `frontmatter: {}` — the merge collapses, the file is deleted, and `<folder>/.ok/` is auto-cleaned if no other tenant remains. To clear a SPECIFIC key while keeping others, set that key to `null` / `""` / `[]` in the patch.',``,`**Parameters:**`,"- `rules` — Array of `{match, frontmatter, new_match?}`.",' - `match` — Glob pattern that identifies the target folder (e.g. `"specs/**"`, `"meetings/prep-notes/**"`). Required.'," - `frontmatter` — Open `Record<string, unknown>` of key/value defaults. Common: `{title?, description?, tags?: string[]}`. Any other key persists too (`status`, `team`, etc.)."," - `new_match` — If set, move the rule from `match` to this new glob (deletes the old folder's frontmatter.yml + auto-cleans).","- `cwd` (optional) — Project root."].join(`
|
|
3074
3074
|
`),FolderRuleUpsertInputSchema=object$1({match:string$2().min(1).describe(`Glob pattern (e.g. "specs/**", "meetings/prep-notes/**") that identifies the target folder.`),frontmatter:record(string$2(),unknown$2()).refine(e=>!(`title`in e&&e.title!==null&&typeof e.title!=`string`||`description`in e&&e.description!==null&&typeof e.description!=`string`||`tags`in e&&e.tags!==null&&!Array.isArray(e.tags)),{message:"Well-known keys must match expected types when present: `title` (string|null), `description` (string|null), `tags` (string[]|null)."}).describe('Default frontmatter to apply to matched docs. Open shape — any YAML-representable key. Common: `{title?, description?, tags?: string[]}`; arbitrary keys (`status`, `team`, `owners`, `review_cycle`, …) also persist. Pass `{}` to remove the rule (auto-cleans `.ok/` if empty). Pass `key: null | "" | []` to clear that specific key while keeping others.'),new_match:string$2().min(1).optional().describe("If set, move the rule from `match` to this new folder (deletes the old folder's frontmatter.yml + auto-cleans).")}),InputSchema$1={rules:array(FolderRuleUpsertInputSchema).min(1).describe("One or more folder rules to upsert. Always an array — pass `[{...}]` for a single rule."),cwd:string$2().optional().describe(ROUTED_CWD_DESCRIPTION)},AppliedEntrySchema=object$1({match:string$2(),path:string$2(),action:_enum([`written`,`deleted`])}),SuccessOutputSchema=object$1({ok:literal(!0),applied:array(AppliedEntrySchema)}),ErrorOutputSchema=object$1({ok:literal(!1),error:object$1({code:_enum([`MULTI_FOLDER_GLOB`,`PATH_ESCAPE`,`BAD_PROJECT_DIR`,`WRITE_ERROR`]),message:string$2(),rule:string$2().optional()}),partiallyApplied:array(AppliedEntrySchema).optional()}),OutputSchema$1=outputSchemaWithText({result:union([SuccessOutputSchema,ErrorOutputSchema])});function register$3(e,t){e.registerTool(`set_folder_rule`,{description:DESCRIPTION$3,inputSchema:InputSchema$1,outputSchema:OutputSchema$1,annotations:{readOnlyHint:!1,idempotentHint:!0,destructiveHint:!1}},async e=>{let S=await resolveProjectConfigContext(t.resolveCwd,t.config,e.cwd);if(!S.ok)return{isError:!0,content:[{type:`text`,text:`Error: ${S.error}`}]};let{cwd:w}=S,E=applyNestedFolderRulesUpsert({projectDir:w,rules:e.rules.map(e=>({match:e.match,frontmatter:e.frontmatter??{},...e.new_match===void 0?{}:{new_match:e.new_match}}))});return E.ok?textPlusStructured(JSON.stringify(E,null,2),{result:E}):{isError:!0,structuredContent:{result:E},content:[{type:`text`,text:`${E.error.code}: ${E.error.message}`}]}})}const DESCRIPTION$2=[`[Requires: Hocuspocus server] Find missing link candidates for a target page.`,"Returns JSON with structure: `{ target: { docName, title, aliases }, mentions: [{ source, excerpt, offset }], truncated }`.","Each mention includes an `offset` you can pass to `edit_document` for precision patching.","When `truncated` is true, the scan hit its time budget before reading every admitted document.",``,`**Parameters:**`,'- `docName` — Target page docName, typically without extension (for example, "articles/project-alpha"). A trailing `.md` or `.mdx` is stripped automatically.'].join(`
|
|
3075
|
-
`);function register$2(e,t){e.
|
|
3076
|
-
`)}\n${renderInventoryFooter()}`;function register$1(e,t){e.
|
|
3075
|
+
`);function register$2(e,t){e.registerTool(`suggest_links`,{description:DESCRIPTION$2,inputSchema:{docName:string$2().describe(`Target page docName`),cwd:string$2().optional().describe(ROUTED_CWD_DESCRIPTION)}},async e=>{let S=await resolveProjectServerContext(t.resolveCwd,t.config,t.serverUrl,e.cwd);if(!S.ok)return textResult(`Error: ${S.error}`,!0);let{cwd:w,url:E}=S;if(!E)return textResult(HOCUSPOCUS_NOT_RUNNING_ERROR,!0);let D=normalizeDocName(e.docName);if(!D.ok)return textResult(D.error,!0);let O=await httpGet(E,`/api/suggest-links?docName=${encodeURIComponent(D.docName)}`);if(!O.ok)return textResult(`Error: ${O.error}`,!0);let{ok:k,...j}=O,F=await resolvePreviewUrlForTool(D.docName,{config:t.config,resolveCwd:t.resolveCwd},w);return textPlusStructured(JSON.stringify(j,null,2),{...j,previewUrl:F?.url??null,...F?{previewUrlSource:F.source}:{}})})}const DESCRIPTION$1=`${[`[Requires: Hocuspocus server] Write markdown content to a document via the CRDT layer.`,`Content is applied through Hocuspocus and propagated to all connected editors in real-time.`,``,'**Frontmatter.** The markdown payload may include a YAML frontmatter block (`---`-fenced). To change frontmatter, call this tool with `position: "replace"` and a payload that includes the new YAML block — per-property MCP editing is currently unavailable. `edit_document` rejects frontmatter-intersecting find/replace calls.',``,'**Link liberally.** Every noun-phrase that names another document in this knowledge base should be a `[[wiki-link]]`, not plain prose. Backlinks are the primary navigation surface — underlinked documents become islands. Redlinks (links to pages that don\'t exist yet) are fine; they signal "this should exist." Prefer `[[Page Name]]` over Markdown `[text](./page.md)` — only wiki-links participate in the backlinks index.',``,"**Templates.** When creating a new doc, pass `template: \"<name>\"` to instantiate from a folder-scoped template. The name resolves against `templates_available` for the doc's parent folder (leaf → root walk-up; check `list_documents({ dir, depth: 1 })` to see the menu). The template's body + frontmatter become the doc's starting content; folder cascade merges naturally at read time. Templates scoped to a descendant subfolder are rejected for parent-folder targets. **`template` and `markdown` are mutually exclusive** — pass exactly one. To start from the template body and then customize, instantiate via `template` first, then call `edit_document` to fill placeholders.",``,`**Parameters:**`,'- `docName` — Document name, typically without extension (e.g., "my-doc" or "notes/meeting"). A trailing `.md` or `.mdx` is stripped automatically. New documents are created as `.md` by default; to create a `.mdx` file, first place it on disk, then use this tool for edits.',"- `markdown` — Markdown content to write. Required unless `template` is set (mutually exclusive — pass exactly one).","- `template` (optional) — Template name resolved against the parent folder's `templates_available`. The template body becomes the new doc verbatim. Cannot be combined with `markdown`.",'- `position` — Where to insert: "append", "prepend", or "replace". When `template` is set, `position` is forced to "replace".','- `summary` — Optional one-line user-outcome description of this edit (≤80 chars). Appears as a bullet in the document timeline so readers can scan intent without opening every diff. Prefer outcome phrasing ("Fixed token-refresh race") over structural ("Added 3 lines"). Avoid including secrets or PII — summaries are persisted to git history.'].join(`
|
|
3076
|
+
`)}\n${renderInventoryFooter()}`;function register$1(e,t){e.registerTool(`write_document`,{description:DESCRIPTION$1,inputSchema:{docName:string$2().describe(`Document name to write to`),markdown:string$2().optional().describe("Markdown content to write. Optional when `template` is set — the template body is used."),template:string$2().min(1).optional().describe(`Template name resolved against parent folder's templates_available (leaf → root walk-up; closest-wins on collision). See list_documents({ dir, depth: 1 }) to inspect the menu.`),position:_enum([`append`,`prepend`,`replace`]).describe(`Where to insert the content`),summary:summaryArgSchema,cwd:string$2().optional().describe(ROUTED_CWD_DESCRIPTION)}},async e=>{let S=await resolveProjectServerContext(t.resolveCwd,t.config,t.serverUrl,e.cwd);if(!S.ok)return textResult(`Error: ${S.error}`,!0);let{cwd:w,url:E}=S;if(!E)return textResult(HOCUSPOCUS_NOT_RUNNING_ERROR,!0);let D=normalizeDocName(e.docName);if(!D.ok)return textResult(D.error,!0);let O=t.identityRef?.current;if(e.template===void 0&&e.markdown===void 0)return textResult("Error: either `markdown` or `template` must be provided. Omitting both would write empty content.",!0);if(e.template!==void 0&&e.markdown!==void 0)return textResult("Error: TEMPLATE_AND_MARKDOWN_BOTH_SET — `template` and `markdown` are mutually exclusive. Pass one. The template body becomes the new doc verbatim; fill placeholders via subsequent `edit_document` calls.",!0);let k=e.markdown??``,j=e.position;if(e.template!==void 0){let t=parentFolderOf(D.docName),S=resolveTemplatesAvailable(w,t,{depth:1}),E=S.find(t=>t.name===e.template);if(!E)return textResult(`Error: template "${e.template}" not found for folder "${t||`.`}". Available: ${S.length===0?`(none)`:S.map(e=>`${e.name} [${e.scope}]`).join(`, `)}. Templates are resolved by walk-up; check list_documents({ dir, depth: 1 }) at the parent folder to see the menu.`,!0);let F;try{F=readFileSync(resolve(w,E.path),`utf-8`)}catch(e){return textResult(`Error: failed to read template at ${E.path}: ${e.message}`,!0)}k=applySubstitution(F,{date:todayIsoUtc(),user:O?.displayName??``}),j=`replace`}let F=await httpPost(E,`/api/agent-write-md`,{docName:D.docName,markdown:k,position:j,...e.summary===void 0?{}:{summary:e.summary},...O?{agentId:O.connectionId,agentName:O.displayName,clientName:O.clientInfo?.name,colorSeed:O.colorSeed}:{}});if(!F.ok)return textResult(`Error: ${F.error}`,!0);let L=resolveLockDir(w),B=resolvePreviewUrl(D.docName,{lockDir:L}),H=typeof F.subscriberCount==`number`?F.subscriberCount:void 0,q=(typeof F.systemSubscriberCount==`number`?F.systemSubscriberCount:void 0)===0,J=H===0,Y=Array.isArray(F.hints)?F.hints:void 0,ee=F.summary&&typeof F.summary==`object`?F.summary:void 0,te=typeof ee?.hint==`string`?ee.hint:void 0,ne=[e.template===void 0?`Written successfully (${j}).`:`Written successfully (instantiated from template "${e.template}").`];if(B&&ne.push(`Preview: ${B.url}`),q&&ne.push(B?`Open ${B.url} in your preview browser.`:`No preview attached. Start the UI.`),te&&ne.push(te),Y)for(let e of Y)e.message&&ne.push(e.message);let ae=ne.join(`
|
|
3077
3077
|
`);if(!B&&!q&&!J&&!Y&&!ee)return textResult(ae);let oe={};return B&&(oe.previewUrl=B.url,oe.previewUrlSource=B.source),q&&(oe.warning={message:`Open the previewUrl in your preview browser.`,action:`attach-preview-once`,previewUrl:B?.url??null}),Y&&(oe.hints=Y),ee&&(oe.summary=ee),textPlusStructured(ae,oe)})}const DESCRIPTION=[`[Operates on disk; no running OK server required] Create or update a template.`,``,"Templates are markdown starter shapes that agents pick from when creating a new doc — `write_document({ template: <name> })` resolves the name against the folder cascade and instantiates the body + frontmatter.",``,'Templates live at `<folder>/.ok/templates/<name>.md`. The `folder` argument selects which project folder owns it (project-wide via `folder: ""`, or scoped to a subfolder).',``,"**`title` is required** in the frontmatter — it is the menu surface agents pick from. Missing or empty title returns `TEMPLATE_TITLE_REQUIRED`. `description` is recommended (soft warning when absent) — it disambiguates similarly-named templates.",``,"**Substitution allowlist.** The body MAY contain `{{date}}` (today, ISO-8601) and `{{user}}` (calling principal display name). Any other `{{...}}` token is rejected at write time with `TEMPLATE_UNKNOWN_VARIABLE`. Substitution happens at instantiation time (when `write_document({ template })` materializes the doc), not at template-write time — templates on disk show the raw `{{date}}` token.",``,`**Parameters:**`,'- `folder` — Project-root-relative folder (e.g. `"meetings"`, `"meetings/prep-notes"`). Empty / `.` means project root.',"- `name` — Template filename without `.md`. Letters, digits, `_`, `-` only.","- `body` — Markdown body. May use `{{date}}` / `{{user}}` substitution tokens. Other placeholder text in `{shape}` form (e.g. `{Meeting Title}`) is LITERAL — agents fill it in via subsequent `edit_document` calls.","- `frontmatter` — `{title (required), description?, tags?: string[]}` for the template menu."].join(`
|
|
3078
3078
|
`),InputSchema={folder:string$2().describe("Project-root-relative folder. Empty / `.` means project root."),name:string$2().min(1).regex(/^[A-Za-z0-9_-]+$/,"Template name must use letters, digits, `_`, or `-` only (no slashes, dots, or spaces).").describe("Template filename without `.md` extension."),body:string$2().describe(`Markdown body for the template.`),frontmatter:object$1({title:string$2().min(1,"Template `title` is required — it is the menu surface agents pick from.").describe(`Required. The menu surface agents pick from. Empty / missing returns TEMPLATE_TITLE_REQUIRED.`),description:string$2().optional().describe(`Recommended. Disambiguates similarly-named templates. Soft warning when absent.`),tags:array(string$2()).optional().describe(`Optional. Concatenated with cascade tags at instantiation time.`)}).describe("Template menu metadata. `title` MUST be present (hard error if missing). `description` SHOULD be present (warning)."),cwd:string$2().optional().describe(ROUTED_CWD_DESCRIPTION)},OutputSchema=outputSchemaWithText({result:union([object$1({ok:literal(!0),path:string$2(),created:boolean$1(),warnings:array(string$2())}),object$1({ok:literal(!1),error:object$1({code:string$2(),message:string$2()})})])});function register(e,t){e.registerTool(`write_template`,{description:DESCRIPTION,inputSchema:object$1(InputSchema).strict(),outputSchema:OutputSchema,annotations:{readOnlyHint:!1,idempotentHint:!0,destructiveHint:!1}},async e=>{let S=await resolveProjectConfigContext(t.resolveCwd,t.config,e.cwd);if(!S.ok)return{isError:!0,content:[{type:`text`,text:`Error: ${S.error}`}]};let{cwd:w}=S,E=applyTemplateWrite({projectDir:w,folder:e.folder,name:e.name,body:e.body,frontmatter:e.frontmatter});if(!E.ok)return{isError:!0,structuredContent:{result:E},content:[{type:`text`,text:`${E.error.code}: ${E.error.message}`}]};let D=[`${E.created?`Created`:`Updated`} template at ${E.path}`];if(E.warnings.length>0){D.push(``,`Warnings:`);for(let e of E.warnings)D.push(` - ${e}`)}return textPlusStructured(D.join(`
|
|
3079
3079
|
`),{result:E})})}function registerAllTools(e,t){let S=t.logger,w=createLoggedServer(e,{logger:t.logger,identityRef:t.identityRef}),E=e=>async w=>{try{let E=await t.resolveCwd(w);return(getCurrentMcpLogger()??S)?.debug(`tool cwd resolved`,{tool:e,cwd:E,...w?{explicit:w}:{}}),E}catch(t){throw(getCurrentMcpLogger()??S)?.warn(`tool call failed`,{tool:e,error:t instanceof Error?t.message:String(t),...w?{explicit:w}:{}}),t}};register$22(w,{resolveCwd:E(`exec`),serverUrl:t.serverUrl,config:t.config}),register$12(w,{config:t.config,resolveCwd:E(`ingest`)}),register$7(w,{config:t.config,resolveCwd:E(`research`)}),register$27(w,{config:t.config,resolveCwd:E(`consolidate`)}),register$24(w,{config:t.config,resolveCwd:E(`discover`)}),register$10(w,{resolveCwd:E(`read_document`),config:t.config,serverUrl:t.serverUrl}),register$4(w,{resolveCwd:E(`search`),config:t.config,serverUrl:t.serverUrl}),register$13(w,{resolveCwd:E(`grep`),config:t.config,serverUrl:t.serverUrl}),register$2(w,{serverUrl:t.serverUrl,config:t.config,resolveCwd:E(`suggest_links`)}),register$1(w,{serverUrl:t.serverUrl,config:t.config,resolveCwd:E(`write_document`),identityRef:t.identityRef}),register$23(w,{serverUrl:t.serverUrl,config:t.config,resolveCwd:E(`edit_document`),identityRef:t.identityRef}),register$26(w,{serverUrl:t.serverUrl,config:t.config,resolveCwd:E(`delete_document`),identityRef:t.identityRef}),register$9(w,{serverUrl:t.serverUrl,config:t.config,resolveCwd:E(`rename_document`),identityRef:t.identityRef}),register$8(w,{serverUrl:t.serverUrl,config:t.config,resolveCwd:E(`rename_folder`),identityRef:t.identityRef}),register$16(w,{serverUrl:t.serverUrl,config:t.config,resolveCwd:E(`get_history`)}),register$5(w,t.config,t.serverUrl,E(`save_version`),t.identityRef),register$6(w,{serverUrl:t.serverUrl,config:t.config,resolveCwd:E(`rollback_to_version`),identityRef:t.identityRef}),register$11(w,{serverUrl:t.serverUrl,config:t.config,resolveCwd:E(`list_documents`)}),register$21(w,{serverUrl:t.serverUrl,config:t.config,resolveCwd:E(`get_backlinks`)}),register$20(w,{resolveCwd:E(`get_components`),config:t.config}),register$17(w,{serverUrl:t.serverUrl,config:t.config,resolveCwd:E(`get_forward_links`)}),register$14(w,{serverUrl:t.serverUrl,config:t.config,resolveCwd:E(`get_orphans`)}),register$15(w,{serverUrl:t.serverUrl,config:t.config,resolveCwd:E(`get_hubs`)}),register$18(w,{serverUrl:t.serverUrl,config:t.config,resolveCwd:E(`get_dead_links`)}),register$19(w,{config:t.config,resolveCwd:E(`get_config`)}),register$3(w,{config:t.config,resolveCwd:E(`set_folder_rule`)}),register(w,{config:t.config,resolveCwd:E(`write_template`)}),register$25(w,{config:t.config,resolveCwd:E(`delete_template`)})}function firstHeader(e){return Array.isArray(e)?e[0]:e}function writePlain(e,t,S){e.writableEnded||(e.statusCode=t,e.setHeader(`Content-Type`,`text/plain; charset=utf-8`),e.end(S))}function createSessionServer(e,t,S){let w=e.config,E=new McpServer({name:MCP_SERVER_NAME,version:RUNTIME_VERSION},{instructions:buildInstructions(w.content)});installPrettyZodErrors(E);let D=S??randomUUID(),O={current:{connectionId:D,displayName:D,colorSeed:D}};E.server.oninitialized=()=>{let e=E.server.getClientVersion(),t=sanitizeClientName(e?.name,D);O.current={connectionId:D,clientInfo:e?{name:t,version:e.version}:void 0,displayName:t,colorSeed:t}};let k=e.projectDir??e.contentDir;return registerAllTools(E,{serverUrl:async()=>e.getServerUrl(),resolveCwd:async e=>{if(e===void 0)return k;let t=resolveWithinRoot(k,e);if(!t.ok)throw Error(`cwd "${e}" is not within the configured project root: ${t.reason}`);return t.abs},config:w,identityRef:O}),{server:E,transport:t}}function createMcpHttpHandler(e){let t=new Map,S=e.sessionTtlMs??1800*1e3,w=e.maxSessions??100;async function E(S,w){let E=t.get(S);if(!E)return;t.delete(S),E.ttlTimer!==void 0&&clearTimeout(E.ttlTimer);let D=await Promise.allSettled([E.server.close(),E.transport.close()]);for(let t of D)t.status===`rejected`&&e.log?.warn?.({err:t.reason,sessionId:S,reason:w},`MCP HTTP session close failed`);e.log?.info?.({sessionId:S,reason:w},`MCP HTTP session closed`)}function D(t,w){w.ttlTimer!==void 0&&clearTimeout(w.ttlTimer),w.ttlTimer=setTimeout(()=>{E(t,`ttl-expired`).catch(S=>{e.log?.warn?.({err:S,sessionId:t},`MCP HTTP session TTL cleanup failed`)})},S),w.ttlTimer.unref?.()}return{async handle(S,O){let k=firstHeader(S.headers[`mcp-session-id`]);if(k){let e=t.get(k);if(!e){writePlain(O,404,`MCP session not found`);return}D(k,e),await e.transport.handleRequest(S,O);return}if(S.method!==`POST`){writePlain(O,400,`Missing MCP session. Initialize with POST /mcp first.`);return}if(t.size>=w){e.log?.warn?.({activeSessions:t.size,maxSessions:w},`MCP HTTP session cap reached`),writePlain(O,503,`Too many active MCP sessions`);return}let j=firstHeader(S.headers[MCP_CONNECTION_ID_HEADER]),F=validateAgentId(j)??void 0;j!==void 0&&F===void 0&&e.log?.warn?.({headerLength:j.length},`MCP HTTP forwarded connectionId header failed validation; falling back to randomUUID`);let L=new StreamableHTTPServerTransport({sessionIdGenerator:()=>randomUUID(),enableJsonResponse:!0,onsessioninitialized:async S=>{try{let w=createSessionServer(e,L,F);await w.server.connect(L),t.set(S,w),D(S,w),e.log?.info?.({sessionId:S},`MCP HTTP session initialized`)}catch(w){throw t.delete(S),e.log?.error?.({err:w,sessionId:S},`MCP HTTP session initialization failed`),w}}});L.onerror=t=>{e.log?.warn?.({err:t},`MCP HTTP transport error`)},L.onclose=()=>{let t=L.sessionId;if(!t){e.log?.info?.({sessionId:t,reason:`transport-closed`},`MCP HTTP session closed`);return}E(t,`transport-closed`).catch(S=>{e.log?.warn?.({err:S,sessionId:t},`MCP HTTP transport-close cleanup failed`)})},await L.handleRequest(S,O)},async close(){let e=[...t.entries()];await Promise.allSettled(e.map(([e])=>E(e,`handler-close`)))}}}const DEFAULT_KEEPALIVE_GRACE_MS=1e4,MAX_COLLAB_MESSAGE_BYTES=1024*1024,MCP_CORS_HEADERS={"Access-Control-Allow-Methods":`GET, POST, DELETE, OPTIONS`,"Access-Control-Allow-Headers":`Content-Type, Authorization, traceparent, tracestate, baggage, mcp-session-id, mcp-protocol-version`,"Access-Control-Max-Age":`86400`};function mountMcpAndApi(e){let{httpServer:t,hocuspocus:S,mcpHttpHandler:w,log:E,sessionManager:D,agentFocusBroadcaster:O,agentPresenceBroadcaster:k,contentAssetMiddleware:j}=e,F=e.keepaliveGraceMs??DEFAULT_KEEPALIVE_GRACE_MS,L=new import_websocket_server$1.default({noServer:!0,maxPayload:MAX_COLLAB_MESSAGE_BYTES});L.on(`error`,e=>{E.error({err:e},`WebSocketServer error`)});let B=new Map,H=new Set,q=!1;return t.on(`request`,(e,t)=>{let D=e.url?.split(`?`)[0];if(w!==void 0&&D===`/mcp`){let S=e.headers.origin,D=Array.isArray(e.headers[`mcp-session-id`])?e.headers[`mcp-session-id`][0]:e.headers[`mcp-session-id`];if(!isLoopbackAddress(e.socket.remoteAddress)){errorResponse(t,403,`urn:ok:error:loopback-required`,`Loopback access required.`,{handler:`mcp`});return}if(!isAllowedWorkspaceHostHeader(e.headers.host)){errorResponse(t,403,`urn:ok:error:host-not-allowed`,`Host header not allowed.`,{handler:`mcp`});return}if(S!==void 0&&!isAllowedApiOrigin(S)){errorResponse(t,403,`urn:ok:error:invalid-origin`,`Origin not allowed.`,{handler:`mcp`});return}S!==void 0&&(t.setHeader(`Access-Control-Allow-Origin`,S),t.setHeader(`Vary`,`Origin`));for(let[e,S]of Object.entries(MCP_CORS_HEADERS))t.setHeader(e,S);if(e.method===`OPTIONS`){t.writeHead(204),t.end();return}w.handle(e,t).catch(e=>{E.error({err:e,sessionId:D},`Unhandled MCP HTTP error`),!t.writableEnded&&!t.headersSent?errorResponse(t,500,`urn:ok:error:internal-server-error`,`Internal server error.`,{handler:`mcp`,cause:e}):t.writableEnded||t.end()});return}if(D?.startsWith(`/api/`)){S.hooks(`onRequest`,{request:e,response:t}).then(()=>{t.writableEnded||t.headersSent||errorResponse(t,404,`urn:ok:error:not-found`,`API endpoint not found.`,{handler:`mcp-mount`,detail:`No handler for ${e.method??`GET`} ${D}`})}).catch(e=>{E.error({err:e},`Unhandled onRequest error`),!t.writableEnded&&!t.headersSent?errorResponse(t,500,`urn:ok:error:internal-server-error`,`Internal server error.`,{handler:`mcp-mount`,cause:e}):t.writableEnded||t.end()});return}if(j!==void 0){try{j(e,t,()=>{t.writableEnded||t.headersSent||errorResponse(t,404,`urn:ok:error:not-found`,`Not found.`,{handler:`mcp-mount`,detail:`No handler for ${D??`/`}`})})}catch(e){E.error({err:e},`Unhandled content-asset middleware error`),!t.writableEnded&&!t.headersSent?errorResponse(t,500,`urn:ok:error:internal-server-error`,`Internal server error.`,{handler:`mcp-mount`,cause:e}):t.writableEnded||t.end()}return}errorResponse(t,404,`urn:ok:error:not-found`,`Not found.`,{handler:`mcp-mount`,detail:`The React UI is served by \`ok ui\` (run \`ok ui\` and check \`ui.lock.port\`). No handler for ${D??`/`}`})}),t.on(`upgrade`,(e,t,w)=>{if(e.url?.startsWith(`/collab/keepalive`)){if(!isLoopbackAddress(e.socket.remoteAddress)||!isAllowedWorkspaceHostHeader(e.headers.host)){t.destroy();return}t.on(`error`,e=>{handleCollabSocketError(e)||E.error({err:e},`MCP keepalive socket error`)}),L.handleUpgrade(e,t,w,t=>{let S=parseKeepaliveConnectionId(e.url);if(S){let e=B.get(S);e!==void 0&&(clearTimeout(e),B.delete(S),E.info({connectionId:S},`[keepalive] reconnect during grace — timer cancelled`))}if(S&&k){let t=parseKeepaliveIdentity(e.url);if(t)try{let e=iconFromClientName(t.clientName),w=AGENT_ICON_COLORS[e]??colorFromSeed(t.colorSeed);k.setPresence(toBroadcasterKey(S),{displayName:t.displayName,icon:e,color:w,currentDoc:`(connected)`,mode:`idle`,ts:Date.now()})}catch(e){E.error({err:e,connectionId:S},`[keepalive] presence bootstrap failed`)}}let w=setInterval(()=>{try{t.ping()}catch{}},3e4);w.unref?.();let j=S?setInterval(()=>{k?.bumpPresenceTs(toBroadcasterKey(S))},3e3):null;j?.unref?.(),t.on(`close`,()=>{if(clearInterval(w),j!==null&&clearInterval(j),!S)return;let e=setTimeout(()=>{if(B.delete(S),q)return;let e=(async()=>{E.info({connectionId:S},`[keepalive] grace expired — cleaning up sessions`);try{await D?.closeAllForAgent(S)}catch(e){E.error({err:e,connectionId:S},`[keepalive] closeAllForAgent failed`)}try{O?.clearFocus(S)}catch(e){E.error({err:e,connectionId:S},`[keepalive] clearFocus failed`)}try{k?.clearPresence(toBroadcasterKey(S))}catch(e){E.error({err:e,connectionId:S},`[keepalive] clearPresence failed`)}})();H.add(e),e.finally(()=>H.delete(e))},F);e.unref?.(),B.set(S,e),E.info({connectionId:S,graceMs:F},`[keepalive] disconnected — grace timer started`)}),t.on(`error`,e=>{handleCollabSocketError(e)||E.error({err:e},`MCP keepalive WS error`),t.terminate()})});return}if(e.url?.startsWith(`/collab`)){t.on(`error`,e=>{handleCollabSocketError(e)||E.error({err:e},`Upgrade socket error`)}),L.handleUpgrade(e,t,w,t=>{let w=S.handleConnection(t,e),D=!1;t.on(`message`,e=>{if(D)return;let S=e.byteLength;if(S>MAX_COLLAB_MESSAGE_BYTES){D=!0,incrementCollabMessageTooLarge(),E.warn({event:`collab-message-too-large`,bytes:S,limit:MAX_COLLAB_MESSAGE_BYTES},`Collab WebSocket message rejected before Yjs processing`),t.close(1009,`Message Too Big`);return}w.handleMessage(new Uint8Array(e))}),t.on(`close`,(e,t)=>{w.handleClose({code:e,reason:t.toString()})}),t.on(`error`,e=>{if(e.code===`WS_ERR_UNSUPPORTED_MESSAGE_LENGTH`){incrementCollabMessageTooLarge(),E.warn({event:`collab-message-too-large`,limit:MAX_COLLAB_MESSAGE_BYTES},`Collab WebSocket frame rejected by ws maxPayload before Yjs processing`),t.terminate();return}handleCollabSocketError(e)||E.error({err:e},`WebSocket error`),t.terminate()})});return}t.destroy()}),{wss:L,shutdown:async()=>{if(!q){q=!0;for(let e of B.values())clearTimeout(e);B.clear(),H.size>0&&await Promise.allSettled([...H])}}}}function parseKeepaliveConnectionId(e){if(!e)return null;try{return validateAgentId(new URL(e,`http://localhost`).searchParams.get(`connectionId`))}catch{return null}}const MAX_KEEPALIVE_IDENTITY_LEN=256;function sanitizeIdentityField(e){return e===null||e.length===0||e.length>MAX_KEEPALIVE_IDENTITY_LEN||/[-]/.test(e)?null:e}function parseKeepaliveIdentity(e){if(!e)return null;try{let t=new URL(e,`http://localhost`),S=sanitizeIdentityField(t.searchParams.get(`displayName`)),w=sanitizeIdentityField(t.searchParams.get(`clientName`)),E=sanitizeIdentityField(t.searchParams.get(`colorSeed`));return S===null||w===null||E===null?null:{displayName:S,clientName:w,colorSeed:E}}catch{return null}}const MISSING_OK_CONFIG_MESSAGE=`Open Knowledge config not found at .ok/config.yml. Run ok init to scaffold OK in this directory.`;var MissingOkConfigError=class extends Error{kind;projectDir;constructor(e,t,S){super(MISSING_OK_CONFIG_MESSAGE,S),this.name=`MissingOkConfigError`,this.kind=e,this.projectDir=t}};async function startConfigFileWatcher(e,t){let S=getLogger(`config-file-watcher`),{watch:w}=await import(`./chokidar-CxU7f6JW.mjs`),E=dirname(e);try{tracedMkdirSync(E,{recursive:!0})}catch(e){e.code!==`EEXIST`&&S.warn({err:e,watchDir:E},`failed to create watch directory; watcher may be inert`)}let D=w(E,{ignoreInitial:!0,depth:0,usePolling:!0,interval:200,awaitWriteFinish:{stabilityThreshold:100,pollInterval:50},ignored:t=>t!==E&&t!==e});await new Promise(e=>{D.once(`ready`,e)});let O=null;try{O=readFileSync(e,`utf-8`)}catch{}let k=(w,E=!0)=>{if(w!==e)return;let D;try{D=readFileSync(w,`utf-8`)}catch(e){if(e.code===`ENOENT`){E&&S.debug({path:w},`config file disappeared between event and read; dropping`);return}S.warn({err:e,path:w},`config file read failed; dropping event`);return}if(D!==O){O=D;try{t(D)}catch(e){S.warn({err:e,path:w},`config file change handler threw`)}}},j=e=>k(e);D.on(`add`,j),D.on(`change`,j),D.on(`unlink`,t=>{t===e&&S.debug({path:t},`config file unlinked; Y.Text retained at current state`)}),D.on(`error`,t=>{S.warn({err:t,watchDir:E,absPath:e},`[config-file-watcher] chokidar error while watching ${e}`)});let F=0,L=setInterval(()=>{F++,k(e,!1),F>=20&&clearInterval(L)},500);L.unref?.();let B=!1;return async()=>{B||(B=!0,clearInterval(L),await D.close())}}async function startMultiPathConfigFileWatcher(e,t){if(e.length===0)throw Error(`startMultiPathConfigFileWatcher requires at least one absolute path`);let S=getLogger(`config-file-watcher`),{watch:w}=await import(`./chokidar-CxU7f6JW.mjs`),E=new Set(e),D=Array.from(new Set(Array.from(E,e=>dirname(e))));for(let e of D)try{tracedMkdirSync(e,{recursive:!0})}catch(t){t.code!==`EEXIST`&&S.warn({err:t,dir:e},`failed to create watch directory; watcher may be inert`)}let O=w(D,{ignoreInitial:!0,depth:0,usePolling:!0,interval:200,awaitWriteFinish:{stabilityThreshold:100,pollInterval:50},ignored:e=>!E.has(e)&&!D.includes(e)});await new Promise(e=>{O.once(`ready`,e)});let k=new Map;for(let e of E)try{k.set(e,readFileSync(e,`utf-8`))}catch{k.set(e,null)}let j=(e,w=!0)=>{if(!E.has(e))return;let D;try{D=readFileSync(e,`utf-8`)}catch(t){if(t.code===`ENOENT`){w&&S.debug({path:e},`config file disappeared between event and read; dropping`);return}S.warn({err:t,path:e},`config file read failed; dropping event`);return}if(D!==k.get(e)){k.set(e,D);try{t(e,D)}catch(t){S.warn({err:t,path:e},`config file change handler threw`)}}},F=e=>j(e);O.on(`add`,F),O.on(`change`,F),O.on(`unlink`,e=>{E.has(e)&&S.debug({path:e},`config file unlinked; downstream state retained`)}),O.on(`error`,e=>{S.warn({err:e,watchDirs:D,paths:Array.from(E)},`[config-file-watcher] chokidar error in multi-path watcher`)});let L=0,B=setInterval(()=>{L++;for(let e of E)j(e,!1);L>=20&&clearInterval(B)},500);B.unref?.();let H=!1;return async()=>{H||(H=!0,clearInterval(B),await O.close())}}const QUIET_WINDOW_MS=100,BATCH_TIMEOUT_MS=3e4,WATCHED_FILES=new Set([`HEAD`,`MERGE_HEAD`,`ORIG_HEAD`,`index.lock`]);function readHeadSha(e){try{let t=readFileSync(resolve(e,`HEAD`),`utf-8`).trim();if(t.startsWith(`ref: `)){let S=resolve(e,t.slice(5));try{return readFileSync(S,`utf-8`).trim()}catch{try{let S=readFileSync(resolve(e,`packed-refs`),`utf-8`),w=t.slice(5),E=S.split(`
|
|
@@ -3087,5 +3087,5 @@ In headless mode, write the recap into the research article's "Further reading"
|
|
|
3087
3087
|
`,`
|
|
3088
3088
|
`).split(`
|
|
3089
3089
|
`),S=[],w=!1,E=``;for(let e of t){let t=/^\s{0,3}([`~]{3,})/.exec(e);if(t){w?RegExp(`^\\s{0,3}${E[0]==="`"?"`":`~`}{${E.length},}\\s*$`).test(e)&&(w=!1,E=``):(w=!0,E=t[1]);continue}if(w)continue;let D=stripInlineCodeSpans(e);for(TAG_VALUE_RE.lastIndex=0;;){let e=TAG_VALUE_RE.exec(D);if(e===null)break;let t=e[2];t&&S.push(t)}}return S}var TagIndex=class{contentDir;contentFilter;state=createEmptyState();constructor(e){this.contentDir=e.contentDir,this.contentFilter=e.contentFilter}updateDocumentFromMarkdown(e,t){if(!(isSystemDoc(e)||isConfigDoc(e)))try{let{frontmatter:S,body:w}=stripFrontmatter(t),E=extractFrontmatterTags(S?unwrapFrontmatterFences(S):``),D=extractInlineTagsFromBody(w),O=new Set([...E,...D]),k=new Set;for(let e of O)for(let t of expandTagToHierarchy(e))k.add(t);this.applyDocSnapshot(e,O,k)}catch(t){console.warn(`[tag-index] Failed to scan ${e} for tag extraction:`,t),this.deleteDocument(e)}}deleteDocument(e){if(isSystemDoc(e)||isConfigDoc(e))return;let t=this.state.byDoc.get(e);if(t){for(let S of t){let t=this.state.byTag.get(S);t&&(t.delete(e),t.size===0&&this.state.byTag.delete(S))}this.state.byDoc.delete(e),this.state.byDocLiteral.delete(e)}}renameDocument(e,t,S){this.deleteDocument(e),this.updateDocumentFromMarkdown(t,S)}getDocsForTag(e){let t=this.state.byTag.get(e);return t?[...t].sort((e,t)=>e.localeCompare(t)):[]}getDocsForTagWithMatches(e){let t=this.state.byTag.get(e);if(!t)return[];let S=[];for(let w of t){let t=this.state.byDocLiteral.get(w);if(!t)continue;let E=tagsMatchingPrefix(t,e);S.push({docName:w,matchingTags:[...E].sort((e,t)=>e.localeCompare(t))})}return S.sort((e,t)=>e.docName.localeCompare(t.docName))}getAllTags(){let e=[...this.state.byTag.entries()],t=e.map(([e])=>e),S=new Set;for(let e of t){let t=e.indexOf(`/`);t>0&&S.add(e.slice(0,t));let w=t;for(;w>0;)S.add(e.slice(0,w)),w=e.indexOf(`/`,w+1)}return e.map(([e,t])=>({name:e,count:t.size,isLeaf:!S.has(e)})).sort((e,t)=>e.name.localeCompare(t.name))}init(){if(this.state=createEmptyState(),!existsSync(this.contentDir))return;let e=this.listDocsWithPaths();for(let{docName:t,filePath:S}of e)try{let e=readFileSync(S,`utf-8`);this.updateDocumentFromMarkdown(t,e)}catch(e){console.warn(`[tag-index] Failed to read ${t} during init:`,e)}}applyDocSnapshot(e,t,S){let w=this.state.byDoc.get(e)??new Set;for(let t of w){if(S.has(t))continue;let w=this.state.byTag.get(t);w&&(w.delete(e),w.size===0&&this.state.byTag.delete(t))}for(let t of S){let S=this.state.byTag.get(t);S||(S=new Set,this.state.byTag.set(t,S)),S.add(e)}S.size===0?(this.state.byDoc.delete(e),this.state.byDocLiteral.delete(e)):(this.state.byDoc.set(e,S),this.state.byDocLiteral.set(e,t))}listDocsWithPaths(){let e=[];this.walkContentDir(this.contentDir,e),e.sort((e,t)=>e.docName===t.docName?t.filePath.localeCompare(e.filePath):e.docName.localeCompare(t.docName));let t=new Set;return e.filter(({docName:e})=>t.has(e)?!1:(t.add(e),!0))}walkContentDir(e,t){let S;try{S=readdirSync(e,{withFileTypes:!0})}catch(t){console.warn(`[tag-index] Failed to read directory ${e}:`,t);return}for(let w of S){let S=join(e,w.name);if(w.isDirectory()){let e=relative(this.contentDir,S);if(this.contentFilter&&e&&this.contentFilter.isDirExcluded(e))continue;this.walkContentDir(S,t);continue}if(!w.isFile()||!isSupportedDocFile(w.name))continue;let E=relative(this.contentDir,S);this.contentFilter?.isExcluded(E)||t.push({docName:stripDocExtension(E),filePath:S})}}};const PARK_SNAPSHOT_ORIGIN=(()=>{let e=Object.freeze({origin:`park-snapshot`,paired:!0});return Object.freeze({source:`local`,skipStoreHooks:!1,context:e})})();function createServer$1(e){let{contentDir:t,projectDir:S=t,quiet:w=!0,debounce:E=2e3,maxDebounce:D=1e4,gitEnabled:O=!0,commitDebounceMs:k=3e4,wipRef:j=`refs/wip/main`,configHomedirOverride:F,enableTestRoutes:L=!1,shadowRepo:B,contentRoot:H,destroyTimeoutMs:q=1e4,localOpCliArgs:J,skipStateManifestCheck:Y=!1}=e,ee=getLogger(`server`);function te(){let e=readConfigSafely({absPath:resolveConfigPath(`project-local`,S),sideline:!1,warn:e=>ee.warn({message:e},`[config] could not read project-local config`)}),t=e.value.autoSync?.enabled;return t==null?(e.valid||ee.warn({},`[config] project-local autoSync.enabled unavailable (config invalid) — falling back to project config`),readConfigSafely({absPath:resolveConfigPath(`project`,S),sideline:!1,warn:e=>ee.warn({message:e},`[config] could not read project config`)}).value.autoSync?.enabled===!0):t===!0}initTelemetry();let ne=randomUUID(),ae=getLocalDir(S);if(acquireServerLock(ae,{port:e.port??0,worktreeRoot:S,kind:e.lockKind??`interactive`,capabilities:[`http`,`ws`]}),!Y)try{assertCompatibleStateManifest({lockDir:ae,shadowRepoDir:resolveShadowDir(S)})}catch(e){throw releaseServerLock(ae),e}let oe=createBasenameIndex(),se=(e,t)=>oe.resolveEmbed(e,t),ce=(e,S)=>{let w=oe.resolveEmbed(e,S);if(!w&&e.includes(`/`)&&(w=e.replace(/^\.?\//,``)),!w)return null;let E=resolve(t,w),D=resolve(t);if(E!==D&&!E.startsWith(`${D}/`))return null;try{let e=statSync(E);return e.isFile()?e.size:null}catch{return null}},ue,de,fe,me,ge,_e,ve,ye=null,Ce=null,we=null,Te=null,De=new Set,je=!1,Me,Pe,Ie,Re=new Promise((e,t)=>{Pe=e,Ie=t});function ze(e){ye?.signal(e)}let Be=2e3,Ve=null;function Ue(){Ve!==null&&clearTimeout(Ve),Ve=setTimeout(()=>{Ve=null,de.saveToDisk().catch(e=>{console.warn(`[backlinks] Failed to persist debounced cache:`,e)})},2e3)}let We=new RecentlyRemovedDocs(void 0,{onEviction:()=>incrementRecentlyRemovedDocsEviction(),onSizeChange:e=>setRecentlyRemovedDocsSize(e)}),Ge=(e,t)=>{isSystemDoc(e)||isConfigDoc(e)||We.setRenamed(e,t)},Ke=e=>{if(!(isSystemDoc(e)||isConfigDoc(e))){if(We.peek(e)?.kind===`renamed`){console.info(JSON.stringify({event:`recently-removed-docs-unpaired-delete-suppressed`,docName:e,source:`watcher-delete`}));return}We.setDeleted(e)}},Xe=e=>{isSystemDoc(e)||isConfigDoc(e)||We.delete(e)};try{ue=createContentFilter({projectDir:S,contentDir:t,onAfterRebuild:()=>{de.rebuildFromDisk(getActiveBranch()).catch(e=>{getLogger(`server-factory`).warn({err:e},`[content-filter] backlink-index rebuild failed after onAfterRebuild`)});try{fe.init()}catch(e){getLogger(`server-factory`).warn({err:e},`[content-filter] tag-index rebuild failed after onAfterRebuild`)}try{let e=Lt?.pruneFileIndexNowExcluded()??0;e>0&&getLogger(`server-factory`).info({pruned:e},`[content-filter] pruned now-excluded entries from fileIndex`)}catch(e){getLogger(`server-factory`).warn({err:e},`[content-filter] fileIndex prune failed after onAfterRebuild`)}}}),de=new BacklinkIndex({projectDir:S,contentDir:t,contentFilter:ue}),fe=new TagIndex({contentDir:t,contentFilter:ue});try{fe.init()}catch(e){console.warn(`[server-factory] tag-index init failed; continuing with empty index:`,e)}me={current:B},ge=createPersistenceExtension({contentDir:t,projectDir:S,gitEnabled:O,commitDebounceMs:k,wipRef:j,shadowRef:me,contentRoot:H,backlinkIndex:de,configHomedirOverride:F,getCurrentBranch:()=>Rt?.getLastKnownBranch()??null,resolveEmbed:se,resolveSize:ce,getPrincipal:()=>Te,onAgentCommit:()=>ye?.signal(`session-activity`),onDiskFlush:(e,t)=>ye?.emitDiskAck(e,t),onConfigRejected:(e,t)=>ye?.emitConfigValidationRejected(e,t),mdManager:e.mdManager}),_e=new Hocuspocus({quiet:w,debounce:E,maxDebounce:D,extensions:[ge.extension]});let q=_e.shouldUnloadDocument.bind(_e);_e.shouldUnloadDocument=e=>{if(De.has(e)||je&&q(e))return!0;let t=e.name;return isSystemDoc(t)||isConfigDoc(t)||getReconciledBase(t)!==void 0||e.getXmlFragment(`default`).length!==0||e.getText(`source`).length!==0?!1:q(e)},Me=async e=>{De.add(e);try{await _e.unloadDocument(e)}finally{De.delete(e)}},ye=new CC1Broadcaster(_e),Ce=new AgentFocusBroadcaster(_e),we=new AgentPresenceBroadcaster(_e),ve=new AgentSessionManager(_e);let Y=createLiveDerivedIndexExtension({backlinkIndex:de,tagIndex:fe,signalChannel:ze});_e.configuration.extensions.push(Y),_e.configuration.extensions.push({__kind:`principal-auth`,async onAuthenticate(e){let t=e.token,S=parseHocuspocusAuthToken(t),w=S?.expectedServerInstanceId;if(typeof w==`string`&&w.length>0&&w!==ne)throw new HocuspocusAuthRejection(`server-instance-mismatch`,`server instance mismatch: client claimed ${w}, this server is ${ne}`);let E=S?.expectedBranch,D=getActiveBranch();if(typeof E==`string`&&E.length>0&&E!==D)throw new HocuspocusAuthRejection(`branch-mismatch`,`branch mismatch: client claimed ${E}, server is on ${D}`);if(!S)return;let O=e.context;typeof S.principalId==`string`&&(Te&&S.principalId===Te.id?O.principalId=Te.id:Te?console.warn(JSON.stringify({event:`principal-token-mismatch`,claimed:S.principalId,loaded:Te.id})):O.principalId=S.principalId),typeof S.tabSessionId==`string`&&(O.tabSessionId=S.tabSessionId),O.kind=`human`}}),_e.configuration.extensions.push({__kind:`config-doc-admission-guard`,async onAuthenticate(e){if(!isConfigDoc(e.documentName))return;let t=e.request,S=t.socket?.remoteAddress;if(S!==void 0&&!isLoopbackAddress(S))throw Error(`config-doc admission requires loopback peer (peer=${S}, doc=${e.documentName})`);let w=e.requestHeaders,E=(w&&typeof w.get==`function`?w.get(`host`):null)??t.headers?.host??void 0;if(!isAllowedWorkspaceHostHeader(E))throw Error(`config-doc admission requires loopback Host header (host=${E??`<absent>`}, doc=${e.documentName})`)}});let ee=resolve(t);function te(e){if(!isSafeDocName(e))return null;let t=resolve(ee,`${e}${getDocExtension(e)}`);return!t.startsWith(`${ee}/`)&&t!==ee?null:t}_e.configuration.extensions.push({__kind:`removal-redirect-guard`,async onAuthenticate(e){await runRemovalRedirectGuard(e.documentName,{recentlyRemovedDocs:We,resolveFilePath:te,fileExists:existsSync})}}),_e.configuration.extensions.push({__kind:`system-doc-broadcast-guard`,async beforeHandleMessage(e){if(e.documentName!==`__system__`)return;let t=new IncomingMessage(e.update);if(t.readVarString(),t.readVarUint()===MessageType.BroadcastStateless)throw Error(`inbound BroadcastStateless on ${SYSTEM_DOC_NAME} rejected — server-only channel`)}});let ae=createApiExtension({hocuspocus:_e,sessionManager:ve,contentDir:t,contentFilter:ue,serverInstanceId:ne,getFileIndex:()=>Lt?Lt.getFileIndex():new Map,getFolderIndex:()=>Lt?Lt.getFolderIndex():new Map,getAliasMap:()=>Lt?Lt.getAliasMap():new Map,rescanFiles:()=>Lt?.rescanFromDisk(),enableTestRoutes:L,shadowRef:me,flushGitCommit:()=>ge.flushPendingGitCommit(),flushContributors:()=>ge.flushContributors(),getCurrentBranch:()=>Rt?.getLastKnownBranch()??null,getDiskAckSVs:()=>ye?.getLatestDiskAckSVsAsBase64()??{},contentRoot:H,backlinkIndex:de,tagIndex:fe,signalChannel:ze,agentFocusBroadcaster:Ce,agentPresenceBroadcaster:we,onAgentWrite:e.onAgentWrite,getSyncEngine:()=>zt,localOpCliArgs:J,projectDir:S,resolveEmbed:se,getPrincipal:()=>Te,forceUnloadDocument:Me,ready:Re,recentlyRemovedDocs:We});_e.configuration.extensions.push(ae),_e.configuration.extensions.push(createServerObserverExtension({mdManager,schema,shadowRef:me,contentRoot:H,getCurrentBranch:()=>Rt?.getLastKnownBranch()??null,resolveEmbed:se,resolveSize:ce})),_e.configuration.extensions.push(createSyncHandshakeSpanExtension())}catch(e){throw releaseServerLock(ae),e}let $e=null,nt=new Map,rt=[];function it(e,t){let S=resolve(e,`rescue`),w=resolve(S,`${t}${getDocExtension(t)}`);return w.startsWith(`${S}/`)?w:null}function at(e){let t=_e.documents.get(e);if(!t)return null;let{frontmatter:S,body:w}=stripFrontmatter(t.getText(`source`).toString());return prependFrontmatter(S,w)}let st=(e,t)=>applyExternalChange(_e,e,t,se,ce),vt=e=>{if(!e)return;let t=`[[${e}]]`;for(let[S]of _e.documents){if(isSystemDoc(S)||isConfigDoc(S))continue;let w=_e.documents.get(S);if(!w)continue;let E=w.getText(`source`).toString();if(E.includes(t))try{w.transact(()=>{applyDiskContentToDoc(w,E,se,S)},FILE_WATCHER_ORIGIN)}catch(t){ee.error({err:t,docName:S,assetBasename:e},`[asset-event] failed to re-render ${S} for asset basename ${e}`)}}},Tt=null,Et=e=>{e&&(Tt===null&&(Tt=new Set,setImmediate(()=>{let e=Tt;if(Tt=null,e)try{for(let t of e)vt(t)}catch(t){ee.error({err:t,basenames:[...e]},`[asset-event] dedup rerender pass crashed`)}})),Tt.add(e))};function Dt(e){switch(e.kind){case`rename`:return e.newDocName;case`asset-create`:case`asset-delete`:case`folder-create`:case`folder-delete`:return e.relativePath;case`create`:case`update`:case`delete`:case`conflict`:return e.docName;default:return assertNeverDiskEvent(e)}}async function jt(e){try{switch(e.kind){case`create`:ee.info({docName:e.docName},`[reconcile] create: ${e.docName}`),de.updateDocumentFromMarkdown(e.docName,e.content),Ue(),fe.updateDocumentFromMarkdown(e.docName,e.content),ze(`files`),ze(`backlinks`),ze(`graph`),ze(`tags`),Xe(e.docName);break;case`update`:{let{docName:t,content:S}=e,w=_e.documents.get(t);if(!w){de.updateDocumentFromMarkdown(t,S),Ue(),fe.updateDocumentFromMarkdown(t,S),ze(`backlinks`),ze(`graph`),ze(`tags`);return}let E=getReconciledBase(t)??``,D=at(t)??E,O=reconcile({docName:t,base:E,ours:D,theirs:S}),k=contentHash(E).slice(0,6),j=contentHash(D).slice(0,6),F=contentHash(S).slice(0,6);switch(ee.info({docName:t,base:k,ours:j,theirs:F,result:O.kind},`[reconcile] ${t} base=${k} ours=${j} theirs=${F} result=${O.kind}`),O.kind){case`noop`:de.updateDocumentFromMarkdown(t,S),Ue(),fe.updateDocumentFromMarkdown(t,S),ze(`backlinks`),ze(`graph`),ze(`tags`);break;case`clean`:try{st(t,O.newContent),setReconciledBase(t,O.newContent),incrementReconcile(),de.updateDocumentFromMarkdown(t,S),Ue(),fe.updateDocumentFromMarkdown(t,S),ze(`backlinks`),ze(`graph`),ze(`tags`)}catch(e){ee.error({err:e,docName:t},`[reconcile] failed to apply clean content to Y.Doc for ${t}`),setReconciledBase(t,S)}break;case`merged`:try{st(t,O.newContent),setReconciledBase(t,O.newContent),incrementReconcile(),de.updateDocumentFromMarkdown(t,S),Ue(),fe.updateDocumentFromMarkdown(t,S),ze(`backlinks`),ze(`graph`),ze(`tags`)}catch(e){ee.error({err:e,docName:t},`[reconcile] failed to apply merged content to Y.Doc for ${t}`),setReconciledBase(t,S)}break;case`conflicts`:try{st(t,O.newContent),setReconciledBase(t,O.newContent),incrementReconcile(),incrementConflict(),de.updateDocumentFromMarkdown(t,S),Ue(),fe.updateDocumentFromMarkdown(t,S),ze(`backlinks`),ze(`graph`),ze(`tags`)}catch(e){ee.error({err:e,docName:t},`[reconcile] failed to apply conflict content to Y.Doc for ${t}`),setReconciledBase(t,S)}break;case`refused`:{incrementConflict();let e=w.getMap(`lifecycle`);e.set(`status`,`conflict`),e.set(`reason`,O.reason);break}}break}case`delete`:{let{docName:t}=e,S=_e.documents.get(t);if(!S){de.deleteDocument(t),Ue(),fe.deleteDocument(t),ze(`files`),ze(`backlinks`),ze(`graph`),ze(`tags`),Ke(t),console.info(JSON.stringify({event:`recently-removed-docs-populate`,docName:t,kind:`deleted`,source:`watcher-delete`}));return}let w=getReconciledBase(t)??``,E=at(t)??``,D=E!==w;if(D&&me.current){let e=me.current,S=Rt?.getLastKnownBranch()??`main`;queueMicrotask(()=>{saveInMemoryCheckpoint(e,H??``,{kind:`external-change-rescue`,docName:t,contents:E,label:`External change recovered @ ${new Date().toISOString()}`,branch:S,metadata:{incomingDiskSha:``}}).then(()=>{incrementRescueBuffer(),ee.info({docName:t},`[reconcile] rescue checkpoint saved (delete): ${t}`)}).catch(e=>{ee.error({docName:t,err:e},`[reconcile] rescue checkpoint write failed: ${t}`)})})}S.getMap(`lifecycle`).set(`status`,`deleted-upstream`),deleteReconciledBase(t),de.deleteDocument(t),Ue(),fe.deleteDocument(t),ee.info({docName:t,isDirty:D},`[reconcile] delete: ${t} (dirty=${D})`),_e.closeConnections(t),await Me(S),ze(`files`),ze(`backlinks`),ze(`graph`),ze(`tags`),Ke(t),console.info(JSON.stringify({event:`recently-removed-docs-populate`,docName:t,kind:`deleted`,source:`watcher-delete`}));break}case`rename`:{let{oldDocName:t,newDocName:S,content:w}=e,E=_e.documents.get(t);if(deleteReconciledBase(t),setReconciledBase(S,w),de.renameDocument(t,S,w),Ue(),fe.renameDocument(t,S,w),E){let e=E.getMap(`lifecycle`);e.set(`status`,`renamed`),e.set(`newPath`,S)}ee.info({oldDocName:t,newDocName:S},`[reconcile] rename: ${t} → ${S}`),ze(`files`),ze(`backlinks`),ze(`graph`),ze(`tags`),Ge(t,S),console.info(JSON.stringify({event:`recently-removed-docs-populate`,from:t,to:S,kind:`renamed`,source:`watcher-rename`}));break}case`conflict`:{let{docName:t}=e,S=_e.documents.get(t);if(!S)return;let w=S.getMap(`lifecycle`);w.set(`status`,`conflict`),w.set(`reason`,`conflict-markers`),ee.info({docName:t},`[reconcile] conflict markers detected: ${t}`);break}case`asset-create`:oe.add(e.relativePath),ze(`files`),Et(basename(e.relativePath));break;case`asset-delete`:oe.remove(e.relativePath),ze(`files`),Et(basename(e.relativePath));break;case`folder-create`:case`folder-delete`:ze(`files`);break;default:assertNeverDiskEvent(e)}}catch(t){let S=Dt(e);ee.error({err:t,kind:e.kind,label:S},`[reconcile] failed to handle ${e.kind} for ${S}`)}}let Mt=[];async function Ft(e){if(isBatchInProgress()){Mt.push(e);return}await jt(e)}async function It(){let e=Mt.splice(0,Mt.length);for(let t of e)await jt(t)}let Lt=null,Rt=null,zt=null,Bt=null;async function Vt(e){if(_e.documents.size===0)return;let t=!1,S=new Promise(e=>{_e.configuration.extensions.push({async afterUnloadDocument({instance:S}){!t&&S.getDocumentsCount()===0&&(t=!0,e())}})}),w=Array.from(_e.documents.keys());_e.closeConnections(),_e.flushPendingStores();for(let e of _e.documents.values())e.getConnectionsCount()===0&&_e.unloadDocument(e).catch(t=>{console.warn(JSON.stringify({event:`ok-shutdown-unload-document-failed`,docName:e.name,reason:t instanceof Error?t.message:String(t)}))});let E,D=new Promise((S,D)=>{E=setTimeout(()=>{t=!0;let S=Array.from(_e.documents.keys()),E=[],O=[];if(me.current){for(let e of S)if(!(isSystemDoc(e)||isConfigDoc(e)))try{let t=at(e);if(t===null){ee.warn({docName:e},`[rescue] skipping ${e} — document dropped from map mid-rescue`),O.push(e);continue}let S=it(me.current.gitDir,e);if(!S){ee.warn({docName:e,gitDir:me.current.gitDir},`[rescue] path-traversal guard rejected docName: ${e}`),O.push(e);continue}mkdirSync(dirname(S),{recursive:!0}),writeFileSync(S,t,`utf-8`),incrementRescueBuffer(),E.push(e),ee.info({docName:e},`[rescue] rescue buffer saved on flush timeout: ${e}`)}catch(t){O.push(e),ee.error({err:t,docName:e},`[rescue] failed to write rescue buffer for ${e}`)}}else ee.warn({stillLoadedCount:S.length},`[rescue] shadow repo unavailable at flush timeout — ${S.length} doc(s) will be lost: [${S.join(`, `)}]`),O.push(...S);let k=E.length>0||O.length>0?` — rescued [${E.join(`, `)}]${O.length>0?`, lost [${O.join(`, `)}]`:``}`:``;D(Error(`flushAllStoresAndWait timeout after ${e}ms — ${S.length}/${w.length} docs did not unload: [${S.join(`, `)}]${k}`))},e)});try{await Promise.race([S,D])}finally{E!==void 0&&clearTimeout(E)}}async function Ht(){return Bt||(Bt=(async()=>{let e=Date.now(),t=[];je=!0,Ve!==null&&(clearTimeout(Ve),Ve=null);let w,E=await Promise.race([Re.then(()=>`completed`,e=>(ee.debug({err:e},`[server] init incomplete during shutdown`),`failed`)),new Promise(e=>{w=setTimeout(()=>e(`timeout`),5e3)})]);w!==void 0&&clearTimeout(w),E===`timeout`&&ee.warn({},`[server] init did not complete within 5s during shutdown`);let D=_e.documents.size;try{try{try{Rt&&=(await Rt.unsubscribe(),null),Lt&&=(await Lt.unsubscribe(),null);for(let{docName:e,cleanup:t}of rt)try{await t()}catch(t){ee.warn({err:t,docName:e},`[server] failed to stop config-file-watcher for ${e}`)}rt.length=0}catch(e){t.push({phase:`watcher-unsubscribe`,error:e instanceof Error?e.message:String(e)}),ee.error({err:e},`[server] shutdown phase-1 watcher unsubscribe failed`)}try{ye?.destroy(),we?.destroy(),$e&&=(await $e.disconnect(),null);for(let[e,t]of nt)try{await t.disconnect()}catch(t){ee.warn({err:t,docName:e},`[server] failed to disconnect ${e} during shutdown`)}nt.clear()}catch(e){t.push({phase:`cc1-teardown`,error:e instanceof Error?e.message:String(e)}),ee.error({err:e},`[server] shutdown phase-1b CC1 teardown failed`)}try{await ve.closeAll()}catch(e){t.push({phase:`agent-session-drain`,error:e instanceof Error?e.message:String(e)}),ee.error({err:e},`[server] shutdown phase-2 agent session drain failed`)}try{await Vt(q)}catch(e){t.push({phase:`flush-all-stores`,error:e instanceof Error?e.message:String(e)}),ee.error({err:e},`[server] shutdown phase-3 flush failed`)}let e;try{await Promise.race([(async()=>{await ge.flushPendingGitCommit(),await ge.waitForPendingCommits()})(),new Promise((t,S)=>{e=setTimeout(()=>S(Error(`L2 git flush timeout`)),q)})])}catch(e){t.push({phase:`git-commit-flush`,error:e instanceof Error?e.message:String(e)}),ee.error({err:e},`[server] shutdown phase-4 git commit flush failed`)}finally{e!==void 0&&clearTimeout(e)}try{zt&&=(await zt.destroy(),null)}catch(e){t.push({phase:`sync-engine-stop`,error:e instanceof Error?e.message:String(e)}),ee.error({err:e},`[server] shutdown sync-engine-stop failed`)}}finally{if(me.current){try{let e=(await esm_default({baseDir:S,timeout:{block:5e3}}).revparse(`HEAD`)).trim();e&&writeFileSync(resolve(me.current.gitDir,`last-known-head`),e,`utf-8`)}catch{}try{destroyShadowRepo(me.current)}catch(e){t.push({phase:`shadow-repo-release`,error:e instanceof Error?e.message:String(e)}),ee.error({err:e},`[server] shutdown phase-5 destroyShadowRepo failed`)}}let w=Date.now()-e;t.length===0?ee.info({documentCount:D,durationMs:w},`[server] shutdown flushed ${D} documents in ${w}ms`):ee.warn({documentCount:D,durationMs:w,phaseErrors:t},`[server] shutdown flushed ${D} documents in ${w}ms with ${t.length} phase error(s)`)}}finally{try{releaseServerLock(ae)}catch(e){t.push({phase:`server-lock-release`,error:e instanceof Error?e.message:String(e)}),ee.error({err:e},`[server] shutdown phase-6 releaseServerLock failed`)}try{await shutdownTelemetry()}catch(e){t.push({phase:`telemetry-shutdown`,error:e instanceof Error?e.message:String(e)})}}})(),Bt)}let Ut=[];async function Wt(){try{Te=await loadPrincipal(S),ee.info({principalId:Te.id},`[server] principal loaded`)}catch(e){ee.warn({err:e},`[server] principal load failed — browser writes will use SERVICE_WRITER`)}if(!me.current)try{me.current=await initShadowRepo(S),ee.info({gitDir:me.current.gitDir},`[server] history repo initialized at ${me.current.gitDir}`)}catch(e){ee.error({err:e},`[server] history repo init failed`),Ut.push(`shadow-repo`)}if(me.current){let e=null;try{e=loadRenameLogIndex(me.current.gitDir),sweepLazyPopOrphans(me.current.gitDir,e),setRenameLogIndex(me.current.gitDir,e),ee.info({entries:e.byTo.size},`[server] rename log loaded (${e.byTo.size} entries)`)}catch(e){ee.warn({err:e},`[rename-log] boot-time load/sweep failed; rename history unavailable`)}if(e){let t=1e4;try{await Promise.race([gcRenameLog(me.current,e,{rebuild:!0}),new Promise((e,S)=>setTimeout(()=>S(Error(`boot-time GC exceeded ${t}ms`)),t))])}catch(e){ee.warn({err:e},`[rename-log] boot-time GC/rebuild failed; index loaded without GC`)}}}if(me.current)try{await shadowGit(me.current).raw(`rev-parse`,`--git-dir`)}catch(e){let t=e instanceof Error?e.message:String(e);if(t.includes(`not a git repository`)||t.includes(`invalid object`)){ee.warn({},`[server] history repo appears corrupted — reinitializing`);try{me.current=await initShadowRepo(S)}catch(e){ee.error({err:e},`[server] history repo reinit failed`),me.current=void 0,Ut.includes(`shadow-repo`)||Ut.push(`shadow-repo`)}}else ee.error({err:e},`[server] history repo check failed (transient?)`)}if(me.current)try{let e=resolve(me.current.gitDir,`last-known-head`),t=null;try{t=readFileSync(e,`utf-8`).trim()||null}catch{}let w=null;try{w=(await esm_default({baseDir:S,timeout:{block:1e4}}).revparse(`HEAD`)).trim()||null}catch{}if(w!==null){if(w!==t){let e=`main`;try{let t=(await esm_default({baseDir:S,timeout:{block:1e4}}).raw(`rev-parse`,`--abbrev-ref`,`HEAD`)).trim();t&&t!==`HEAD`&&(e=t)}catch{}ee.info({lastKnownHead:t,currentHead:w,branch:e},`[head-drift] lastKnownHead=${t??`null`}, currentHead=${w}, action=import`);try{await commitUpstreamImport(me.current,H??``,t,w,e),incrementUpstreamImport()}catch(e){ee.warn({err:e},`[head-drift] commitUpstreamImport failed — continuing`)}}else ee.info({currentHead:w},`[head-drift] lastKnownHead=${t??`null`}, currentHead=${w}, action=noop`);try{writeFileSync(e,w,`utf-8`)}catch(e){ee.warn({err:e},`[head-drift] failed to write last-known-head`)}}}catch(e){ee.warn({err:e},`[head-drift] check failed — continuing`)}try{let e=recoverPendingManagedRename(t,S);if(e.recovered&&e.journal){let t=e.journal.version===2?e.journal.fromPath:e.journal.sourceDocName,S=e.journal.version===2?e.journal.toPath:e.journal.destinationDocName;ee.warn({journalVersion:e.journal.version,fromPath:t,toPath:S,restoredDocNames:e.restoredDocNames},`[managed-rename] recovered pending rename ${t} -> ${S}`)}}catch(e){ee.error({err:e},`[server] managed rename recovery failed`),Ut.push(`managed-rename-recovery`)}try{let e=cleanupOrphanUploadTempfiles(S);(e.deleted>0||e.errors>0)&&ee.info({scanned:e.scanned,deleted:e.deleted,errors:e.errors},`[upload-tempfile-sweep] swept ${e.deleted} orphan tempfile(s)`)}catch(e){ee.error({err:e},`[server] upload-tempfile sweep failed`),Ut.push(`upload-tempfile-sweep`)}try{$e=await _e.openDirectConnection(SYSTEM_DOC_NAME),ye?.emitServerInfo(ne,getActiveBranch())}catch(e){ee.error({err:e},`[server] failed to open __system__ direct connection — CC1 push disabled`),Ut.push(`cc1-push`)}for(let e of CONFIG_DOC_NAMES)try{let t=await _e.openDirectConnection(e);nt.set(e,t)}catch(t){ee.error({err:t,docName:e},`[server] failed to open ${e} direct connection — config bind degraded`),Ut.push(`config-doc:${e}`)}let e=new Map([[CONFIG_DOC_NAME_PROJECT,resolveConfigPath(`project`,S)],[CONFIG_DOC_NAME_PROJECT_LOCAL,resolveConfigPath(`project-local`,S)],[CONFIG_DOC_NAME_USER,resolveConfigPath(`user`,S,F)]]);for(let t of CONFIG_DOC_NAMES){let S=e.get(t);if(S)try{ee.info({docName:t,path:S},`[config-file-watcher] starting`);let e=await startConfigFileWatcher(S,e=>{let S=_e.documents.get(t);ee.info({docName:t,hasDocument:S!==void 0,contentLength:e.length},`[config-file-watcher] file changed`);let w=applyExternalConfigChange(S??null,t,e,ge.configPersistenceCtx);if(ee.info({docName:t,outcome:w},`[config-file-watcher] applyExternalConfigChange outcome`),t===`__config__/project`||t===`__local__/project`){let e=te();zt?.setEnabled(e).catch(t=>{ee.warn({err:t,enabled:e},`[sync] failed to apply autoSync.enabled from config`)})}});rt.push({docName:t,cleanup:e}),ee.info({docName:t,path:S},`[config-file-watcher] started`)}catch(e){ee.warn({err:e,docName:t,path:S},`[config-file-watcher] failed to start for ${t}`),Ut.push(`config-file-watcher:${t}`)}}try{let e=resolve(t,`.okignore`),w=resolve(S,`.gitignore`),E=ee;E.info({okignorePath:e,gitignorePath:w},`[ignore-watcher] starting multi-path watcher for .okignore + .gitignore`);let D=await startMultiPathConfigFileWatcher([e,w],(t,w)=>{(async()=>{if(t===e)try{let e=applyExternalConfigChange(_e.documents.get(`__config__/okignore`)??null,CONFIG_DOC_NAME_OKIGNORE,w,ge.configPersistenceCtx);E.info({docName:CONFIG_DOC_NAME_OKIGNORE,outcome:e},`[ignore-watcher] applyExternalConfigChange outcome`)}catch(e){E.error({err:e,changedPath:relative(S,t)},`[ignore-watcher] applyExternalConfigChange failed; rebuild proceeds independently`)}let D=await ue.rebuildIgnorePatterns();if(D.ok)E.info({changedPath:relative(S,t),patternCount:D.patternCount,nestedFileCount:D.nestedFileCount,durationMs:D.durationMs},`[ignore-watcher] rebuild succeeded — broadcasting files channel`),ye?.signal(`files`);else{let e=relative(S,t)||`.`;E.warn({changedPath:e,error:D.error.message},`[ignore-watcher] rebuild failed — emitting config-ignore-nested-error`),ye?.emitConfigIgnoreNestedError(e,D.error.message)}})().catch(e=>{E.error({err:e,changedPath:relative(S,t)||`.`},`[ignore-watcher] handler threw`)})});rt.push({docName:`__ignore-files__`,cleanup:D}),E.info({okignorePath:e,gitignorePath:w},`[ignore-watcher] multi-path watcher started`)}catch(e){ee.warn({err:e,projectDir:S,contentDir:t},`[ignore-watcher] failed to start multi-path watcher`),Ut.push(`ignore-files-watcher`)}let w=resolveGitDir(S),E=w?readBranchFromHead(w)??`main`:`main`;switchReconciledBaseScope(E),de.switchBranch(E);try{{let e=getActiveBranch();try{if(await de.loadFromDisk(e)){let t=await de.reconcileWithDisk(e);(t.added>0||t.updated>0||t.deleted>0)&&ee.info(t,`[backlinks] startup reconcile: offline changes applied`)}else await de.rebuildFromDisk(e);de.saveToDisk().catch(t=>{console.warn(`[backlinks] Failed to persist startup cache for ${e}:`,t)})}catch(t){ee.error({err:t,branch:e},`[backlinks] startup init failed; index will populate incrementally via watcher`)}}Lt=await startWatcher(t,Ft,ue),fe.init();let e=0;try{seedBasenameIndex({contentDir:t,contentFilter:ue,basenameIndex:oe,onSkip:(t,S,w)=>{e++,ee.warn({reason:t,code:S,path:w},`[basename-index] skipped entry during seed (${t}${S?` ${S}`:``})`)}}),e>0&&(ee.warn({count:e},`[basename-index] startup seed completed with ${e} skipped entries — embeds under inaccessible subtrees will not resolve`),Ut.push(`basename-index-partial`))}catch(e){ee.error({err:e},`[basename-index] startup seed failed`),Ut.push(`basename-index`)}}catch(e){ee.error({err:e},`[server] disk bridge watcher failed to start`),Ut.push(`file-watcher`)}try{Rt=await startHeadWatcher(S,async({trigger:e})=>{if(ee.info({trigger:e},`[batch] begin trigger=${e}`),incrementBatch(),_e.flushPendingStores(),await ge.flushPendingGitCommit(),setBatchInProgress(!0),me.current){let e=getActiveBranch(),t=resolveGitDir(S),w=t?readBranchFromHead(t)??e:e,E=[];for(let[e,t]of _e.documents){if(isSystemDoc(e)||isConfigDoc(e))continue;let S=null;if(t.transact(()=>{S=at(e)},PARK_SNAPSHOT_ORIGIN),S===null)continue;let w=getReconciledBase(e)??S;E.push({docName:e,markdown:S,diskSnapshot:w})}if(E.length>0)try{let t=await parkBranch(me.current,e,SERVICE_WRITER.id,E,w);t&&(incrementPark(),ee.info({count:E.length,branch:e,sha:t.slice(0,8)},`[history] parked ${E.length} docs on ${e} → ${t.slice(0,8)}`))}catch(e){ee.error({err:e},`[shadow] park failed`)}}},async e=>{let S=Mt.length,w=e.newBranch??`main`;if(ee.info({kind:e.batchKind,headMoved:e.headMoved,docs:S,timeout:!!e.timeout},`[batch] end kind=${e.batchKind} headMoved=${e.headMoved} docs=${S}${e.timeout?` timeout`:``}`),e.batchKind===`within-branch`)setBatchInProgress(!1),await It(),await ge.flushDeferredStores(`within-branch`);else{incrementBranchSwitch(),Mt.splice(0,Mt.length),switchReconciledBaseScope(w),Ve!==null&&(clearTimeout(Ve),Ve=null),de.switchBranch(w),ue.rebuildDirCount();try{let e=0;oe.clear(),seedBasenameIndex({contentDir:t,contentFilter:ue,basenameIndex:oe,onSkip:(t,S,E)=>{e++,ee.warn({reason:t,code:S,path:E,branch:w},`[basename-index] skipped entry during branch-switch reseed (${t}${S?` ${S}`:``})`)}}),e>0&&(ee.warn({count:e,branch:w},`[basename-index] branch-switch reseed completed with ${e} skipped entries — embeds under inaccessible subtrees will not resolve on this branch`),Ut.includes(`basename-index-partial`)||Ut.push(`basename-index-partial`))}catch(e){ee.error({err:e,branch:w},`[basename-index] branch-switch reseed failed`)}for(let[e,S]of _e.documents)if(!(isSystemDoc(e)||isConfigDoc(e)))try{let E=safeContentPath(e,t);if(!existsSync(E)){let t=getReconciledBase(e)??``,E=at(e)??``;if(E!==t&&me.current){let t=me.current;queueMicrotask(()=>{saveInMemoryCheckpoint(t,H??``,{kind:`external-change-rescue`,docName:e,contents:E,label:`External change recovered @ ${new Date().toISOString()}`,branch:w,metadata:{incomingDiskSha:``}}).then(()=>{incrementRescueBuffer(),ee.info({docName:e},`[reconcile] rescue checkpoint saved on branch switch: ${e}`)}).catch(t=>{ee.error({docName:e,err:t},`[reconcile] rescue checkpoint write failed: ${e}`)})})}S.getMap(`lifecycle`).set(`status`,`deleted-upstream`),ee.info({docName:e,branch:w},`[branch-switch] tombstone: ${e} (not on ${w})`);continue}let D=readFileSync(E,`utf-8`);st(e,D),setReconciledBase(e,D),ee.info({docName:e},`[branch-switch] reset: ${e}`)}catch(t){ee.error({err:t,docName:e},`[branch-switch] failed to reset ${e}`)}ee.info({branch:w,docCount:_e.documents.size},`[branch-switch] loaded branch ${w} (${_e.documents.size} docs)`);try{if(await de.loadFromDisk(w)){let e=await de.reconcileWithDisk(w);(e.added>0||e.updated>0||e.deleted>0)&&ee.info(e,`[backlinks] branch-switch reconcile for ${w}`)}else await de.rebuildFromDisk(w);de.saveToDisk(w).catch(e=>{console.warn(`[backlinks] Failed to persist branch cache for ${w}:`,e)})}catch(e){ee.error({err:e,branch:w},`[backlinks] branch-switch rebuild failed; backlinks may be stale`)}if(fe.init(),me.current&&e.batchKind===`cross-branch`){let e=0;for(let[t]of _e.documents)if(!(isSystemDoc(t)||isConfigDoc(t)))try{let S=await readParkedState(me.current,w,SERVICE_WRITER.id,t);if(!S||S.markdown===S.diskSnapshot)continue;let E=getReconciledBase(t);if(!E)continue;let D=reconcile({docName:t,base:S.diskSnapshot,ours:S.markdown,theirs:E});switch(D.kind){case`merged`:case`clean`:st(t,D.newContent),setReconciledBase(t,D.newContent),e++;break;case`conflicts`:st(t,D.newContent),setReconciledBase(t,D.newContent),incrementConflict(),e++;break;case`noop`:case`refused`:break}}catch(e){ee.error({err:e,docName:t},`[branch-switch] restore WIP failed for ${t}`)}e>0&&ee.info({count:e,branch:w},`[branch-switch] restored ${e} parked docs on ${w}`)}if(e.oldBranch?.startsWith(`detached-`)&&me.current)try{let t=shadowGit(me.current),S=(await t.raw(`for-each-ref`,`refs/wip/${e.oldBranch}/`,`--format=%(refname)`)).trim();if(S){for(let e of S.split(`
|
|
3090
|
-
`))e&&await t.raw(`update-ref`,`-d`,e);ee.info({context:e.oldBranch},`[branch-switch] cleaned up detached context ${e.oldBranch}`)}}catch(e){ee.error({err:e},`[branch-switch] detached cleanup failed`)}setBatchInProgress(!1),await ge.flushDeferredStores(`discard-stale`),ye?.emitBranchSwitched(w)}if(e.headMoved&&e.newHead&&me.current&&S>0){let t=H??`.`;try{let S=await commitUpstreamImport(me.current,t,e.oldHead,e.newHead,w);incrementUpstreamImport(),ee.info({oldHead:e.oldHead?.slice(0,8)??`null`,newHead:e.newHead.slice(0,8),sha:S.slice(0,8)},`[history] upstream-import from ${e.oldHead?.slice(0,8)??`null`}..${e.newHead.slice(0,8)} → ${S.slice(0,8)}`)}catch(e){ee.error({err:e},`[shadow] upstream-import failed`)}}})}catch(e){ee.error({err:e},`[server] HEAD watcher failed to start`),Ut.push(`head-watcher`)}let D=J?.[0]??`open-knowledge`,O=[`-c`,`credential.helper=!${J&&J.length>1?J.join(` `):D} auth git-credential`];try{zt=new SyncEngine({projectDir:S,contentDir:t,contentFilter:ue,contentRoot:H,syncEnabled:te(),credentialArgs:O,cc1Broadcaster:ye,setBatchInProgress:e=>{setBatchInProgress(e),e||ge.flushDeferredStores(`within-branch`).catch(e=>{ee.error({err:e},`[persistence] deferred store drain failed after sync batch`)})},onStateChange:e=>{ee.info({state:e},`[sync] state → ${e}`)},onAutoDisable:async e=>{ee.warn({reason:e},`[sync] auto-disabled — persisting to project-local config`);let t=await writeConfigPatch({cwd:S,scope:`project-local`,patch:{autoSync:{enabled:!1}}});t.ok||ee.error({result:t,reason:e,humanError:humanFormat(t.error),configPath:resolveConfigPath(`project-local`,S)},`[sync] failed to persist auto-disable — next restart WILL re-enable sync and re-trigger the same failure. Check permissions on the config path.`)}}),await zt.start()}catch(e){ee.warn({err:e},`[server] SyncEngine failed to start — sync disabled`),zt=null}ze(`files`),ze(`backlinks`),ze(`graph`),ze(`tags`)}return Wt().then(Pe,Ie),{hocuspocus:_e,sessionManager:ve,cc1Broadcaster:ye,agentFocusBroadcaster:Ce,agentPresenceBroadcaster:we,contentFilter:ue,basenameIndex:oe,serverInstanceId:ne,destroy:Ht,ready:Re,degraded:Ut,lockDir:ae,get syncEngine(){return zt}}}const LEGACY_RUNTIME_FILENAMES=[`server.lock`,`ui.lock`,`state.json`,`principal.json`,`sync-state.json`,`conflicts.json`,`last-spawn-error.log`],LEGACY_RUNTIME_DIRNAMES=[`cache`,`tmp`];function findLegacyRuntimeFiles(e){let t=resolve(e,LOCAL_DIR);if(!(()=>{if(!existsSync(t))return!0;try{return readdirSync(t).length===0}catch{return!0}})())return[];let S=[];for(let t of LEGACY_RUNTIME_FILENAMES)existsSync(resolve(e,t))&&S.push(t);for(let t of LEGACY_RUNTIME_DIRNAMES){let w=resolve(e,t);try{existsSync(w)&&statSync(w).isDirectory()&&S.push(`${t}/`)}catch{}}return S}function computeWorktreeAttributes(e){let t=resolveGitDirDetailed(e);switch(t.kind){case`directory`:return{kind:`main`,gitdir:t.path};case`linked`:return{kind:`linked`,gitdir:t.path};case`malformed-pointer`:return{kind:`linked`,gitdir:null};case`inaccessible`:case`absent`:return{kind:`main`,gitdir:null}}}const DEFAULT_IDLE_THRESHOLD_MS=1800*1e3,DESTROY_STEP_TIMEOUT_MS=5e3;async function bootServer(e){initTelemetry();let{kind:t,gitdir:S}=computeWorktreeAttributes(e.projectDir??e.contentDir),w={"ok.worktree.kind":t};return S!==null&&(w[`ok.worktree.gitdir`]=normalizeFsPath(S)),withSpan(`ok.boot`,{attributes:w},async()=>bootServerInner(e))}async function bootServerInner(e){let t=e.skipAutoInit??!1,S=e.attachUiSibling??!0,w=e.idleShutdownMs,E=e.log??getLogger(`boot`),D=process.env.OK_LOCK_KIND===`mcp-spawned`||process.env.OK_LOCK_KIND===`interactive`?process.env.OK_LOCK_KIND:void 0,O=e.lockKind??D??`interactive`,{createServer:k}=await import(`node:http`),{updateServerLockPort:j}=await import(`./server-lock-D7DXNVql-B5nINglj.mjs`).then(e=>e.a),F=!1;if(!t&&e.autoInitFn)try{F=!!await e.autoInitFn()}catch(e){E.warn({err:e},`autoInitFn failed`)}let L=e.projectDir??e.contentDir,B=resolve(L,`.ok`);if(!existsSync(resolve(B,`config.yml`)))throw new MissingOkConfigError(existsSync(B)?`config`:`okdir`,L);existsSync(resolve(B,`.gitignore`))||console.warn("[boot] Note: .ok/.gitignore is missing — per-machine state files in .ok/ may show up as untracked changes. Run `ok init` to add the recommended ignore entries.");let H=findLegacyRuntimeFiles(B);H.length>0&&console.warn(`[boot] Found legacy runtime files at .ok/${H.join(`, `)}. Delete .ok/ and re-init — these files moved to .ok/${LOCAL_DIR}/.`);let q=createServer$1({contentDir:e.contentDir,projectDir:e.projectDir,contentRoot:e.contentRoot,port:e.port,host:e.host,quiet:e.quiet??!1,debounce:e.debounce,maxDebounce:e.maxDebounce,gitEnabled:e.gitEnabled,commitDebounceMs:e.commitDebounceMs,wipRef:e.wipRef,enableTestRoutes:e.enableTestRoutes,shadowRepo:e.shadowRepo,destroyTimeoutMs:e.destroyTimeoutMs,localOpCliArgs:e.localOpCliArgs,onAgentWrite:e.onAgentWrite,lockKind:O,skipStateManifestCheck:e.skipStateManifestCheck}),{hocuspocus:J,destroy:Y,ready:ee,degraded:te,lockDir:ne,sessionManager:ae,agentFocusBroadcaster:oe,agentPresenceBroadcaster:se}=q,ce=(()=>{let t=e.host??`localhost`;return t===`0.0.0.0`||t===`::`?`localhost`:t.includes(`:`)&&!t.startsWith(`[`)?`[${t}]`:t})(),ue=e.port??0,de=createMcpHttpHandler({contentDir:e.contentDir,projectDir:e.projectDir??e.contentDir,config:e.config,getServerUrl:()=>`http://${ce}:${ue}`,log:E}),fe=k();fe.headersTimeout=3e4,fe.requestTimeout=6e4;let me=e.serveContentAssets?createAssetServeMiddleware({contentFilter:q.contentFilter,contentSirv:build_default(e.contentDir,{dev:!0,dotfiles:!1}),inlineExtensions:INLINE_RENDERABLE_EXTENSIONS,assetExtensions:ASSET_EXTENSIONS,blocklistExtensions:EXECUTABLE_BLOCKLIST_EXTENSIONS}):void 0,ge=mountMcpAndApi({httpServer:fe,hocuspocus:J,mcpHttpHandler:de,log:E,sessionManager:ae,agentFocusBroadcaster:oe,agentPresenceBroadcaster:se,keepaliveGraceMs:e.keepaliveGraceMs,contentAssetMiddleware:me}),_e=null;w!==null&&(_e=attachIdleShutdown({httpServer:fe,thresholdMs:w??DEFAULT_IDLE_THRESHOLD_MS,log:E,onShutdown:(e.idleShutdownHandler??(e=>async()=>{await e()}))(async()=>{await Y()})})),await new Promise((t,S)=>{let w=e=>S(e);fe.once(`error`,w),fe.listen(e.port,e.host,()=>{fe.removeListener(`error`,w),t()})});let ve=fe.address(),ye=typeof ve==`object`&&ve?ve.port:e.port??0;if(ue=ye,j(ne,ye),S&&e.spawnUiSiblingFn)try{await e.spawnUiSiblingFn({lockDir:ne,log:E})}catch(e){E.warn({err:e},`spawnUiSiblingFn failed`)}let Ce=!1,we=async(e,t)=>{let S;try{await Promise.race([t(),new Promise((t,w)=>{S=setTimeout(()=>{w(Error(`${e} timed out after ${DESTROY_STEP_TIMEOUT_MS}ms`))},DESTROY_STEP_TIMEOUT_MS),S.unref?.()})])}finally{S!==void 0&&clearTimeout(S)}};return{httpServer:fe,destroy:async()=>{if(Ce)return;Ce=!0;let e=[],t=async(t,S)=>{try{await we(t,S)}catch(S){e.push(S),E.warn({err:S,step:t},`bootServer destroy step failed`)}};try{_e?.detach()}catch(t){e.push(t),E.warn({err:t,step:`idleHandle.detach`},`bootServer destroy step failed`)}if(await t(`mount.shutdown`,()=>ge.shutdown()),await t(`mcpHttpHandler.close`,()=>de.close()),await t(`mount.wss.close`,()=>new Promise((e,t)=>{ge.wss.close(S=>S?t(S):e())})),await t(`httpServer.closeAllConnections`,async()=>{fe.closeAllConnections?.()}),await t(`httpServer.close`,()=>new Promise((e,t)=>{fe.close(S=>S&&S.code!==`ERR_SERVER_NOT_RUNNING`?t(S):e())})),await t(`destroyHocuspocus`,()=>Y()),await t(`shutdownTelemetry`,()=>shutdownTelemetry()),e.length>0)throw AggregateError(e,`bootServer destroy completed with errors`)},lockDir:ne,contentDir:e.contentDir,port:ye,ready:ee,degraded:te,didAutoInit:F,serverInstance:q}}const ConfigSchema=ConfigSchema$1;function detectClaudeDesktopPresence(e={}){let t=e.home??homedir(),S=e.platformName??process.platform,w=e.env??process.env;return S===`darwin`?existsSync(join(t,`Library`,`Application Support`,`Claude`)):S===`win32`?existsSync(join(w.APPDATA??join(t,`AppData`,`Roaming`),`Claude`)):!1}const ANCESTOR_WALK_DEPTH_LIMIT=30,GIT_MARKER=`.git`;function findEnclosingGitRoot(e){let t=resolve(e),S=0;for(;S<ANCESTOR_WALK_DEPTH_LIMIT;){let e=!1;try{e=existsSync(resolve(t,GIT_MARKER))}catch{e=!1}if(e)return{gitRoot:t,distance:S};let w=dirname(t);if(w===t)return null;t=w,S+=1}return null}const execFileAsync=promisify(execFile);var ProjectGitInitError=class extends Error{stderr;constructor(e,t=``,S){super(e,S),this.name=`ProjectGitInitError`,this.stderr=t}};async function isInsideExistingWorkTree(e){try{let{stdout:t}=await execFileAsync(`git`,[`rev-parse`,`--is-inside-work-tree`],{cwd:e});return t.trim()===`true`}catch{return!1}}async function ensureProjectGit(e){let t=resolve(e),S=resolve(t,`.git`),w=resolve(S,`HEAD`),E=!1;if(existsSync(S)){if(!statSync(S).isDirectory()||existsSync(w))return{didInit:!1};console.log(`[project-git] detected partial .git/ — running git init to repair`),E=!0}else if(await isInsideExistingWorkTree(t))return{didInit:!1};let D=``;try{D=(await execFileAsync(`git`,[`init`,`--initial-branch=main`,t])).stderr??``}catch(e){let S=typeof e==`object`&&e&&`stderr`in e?String(e.stderr??``):``;throw new ProjectGitInitError(`git init failed at ${t}: ${e instanceof Error?e.message:String(e)}`,S,{cause:e})}if(!existsSync(w))throw new ProjectGitInitError(`git init reported success but ${S}/HEAD is missing (partial init detected)`,D);return E?(console.log(`[project-git] backfilled missing .git/HEAD at ${t}`),{didInit:!0,repaired:!0}):(console.log(`[project-git] initialized .git/ at ${t} (branch: main)`),{didInit:!0})}async function resolvePackageVersion(e,t){let S;try{S=createRequire(t).resolve(e)}catch(e){if(e?.code===`MODULE_NOT_FOUND`)return;throw e}for(let t=dirname(S),w=0;w<32;w+=1){let S=join(t,`package.json`);if(existsSync(S))try{let t=JSON.parse(await readFile$1(S,`utf-8`));if(t.name===e&&typeof t.version==`string`)return t.version}catch{}let w=dirname(t);if(w===t)return;t=w}}export{assertNeverDiskEvent as $,runAuthStatusSubprocess as $n,DEFAULT_GITHUB_OAUTH_CLIENT_ID as $r,initShadowRepo as $t,MissingOkConfigError as A,readSkillInstallStateSnapshot as An,updateUiLockPort as Ar,createStreamingErrorWriter as At,STARTER_PACK_IDS as B,releaseUiLock as Bn,JSONRPCMessageSchema as Br,formatAuthRejectionWire as Bt,LOG_MD_TEMPLATE as C,number$1 as Ci,parseHocuspocusAuthToken as Cn,tracedRenameSync as Cr,createExternalChangeHandler as Ct,MCP_SERVER_NAME as D,_coercedNumber as Di,readAllTargets as Dn,tracedWriteFile as Dr,createPersistenceExtension as Dt,MCP_CONNECTION_ID_HEADER as E,url as Ei,planSeed as En,tracedUnlinkSync as Er,createOsProbe as Et,SERVICE_WRITER as F,reconcile as Fn,withSpanSync as Fr,errorResponse as Ft,StateManifestError as G,resolveCursorBinaryDefault as Gn,readConfigSafely as Gr,getLogger as Gt,STATE_MANIFEST_FILENAME as H,resetMetrics as Hn,isInitializedNotification as Hr,gcCheckpointRefs as Ht,SKILL_INSTALL_EVENTS_FILE_REL as I,recordContributor as In,writeStateManifest as Ir,evictStaleTrackerEntries as It,acquireUiLock as J,resolvePack as Jn,GitDirAccessError as Jr,getTracer as Jt,TagIndex as K,resolveCursorSpawnInvocation as Kn,resolveConfigPath as Kr,getMeter as Kt,STARTER_FOLDERS as L,recordSkillInstallEvent as Ln,writeTargetVersion as Lr,extractWikiLinksFromMarkdown as Lt,PinoLogger as M,readTargetRecordedAt as Mn,validateCloneInputs as Mr,detectClaudeDesktopPresence as Mt,ProjectGitInitError as N,readTargetVersion as Nn,validateSkillZip as Nr,detectProjectShape as Nt,MISSING_OK_CONFIG_MESSAGE as O,NEVER as Oi,readBranchFromHead as On,tracedWriteFileSync as Or,createServer$1 as Ot,ROLLBACK_ORIGIN as P,readUiLock as Pn,withSpan as Pr,ensureProjectGit as Pt,assertCompatibleStateManifest as Q,runAuthReposSubprocess as Qn,CC1_CONTRACT_VERSION as Qr,incrementServerObserverFire as Qt,STARTER_FOLDER_FRONTMATTER_FILENAME as R,registerAllTools as Rn,writeTracker as Rr,findEnclosingGitRoot as Rt,LIVE_DERIVED_INDEX_DEBOUNCE_MS as S,looseObject as Si,parseAuthRejectionWire as Sn,tracedRename as Sr,createContentFilterAsync as St,MAX_AGENT_SESSIONS as T,string$2 as Ti,pathToDocName as Tn,tracedRmdirSync as Tr,createMcpHttpHandler as Tt,SeedPrerequisiteError as U,resolveBundledSkillDir as Un,isJSONRPCRequest as Ur,getCurrentMcpLogger as Ut,STARTER_TEMPLATES as V,removeLastKnownHash as Vn,LATEST_PROTOCOL_VERSION as Vr,formatContributors as Vt,SeedRootDirError as W,resolveContentDir as Wn,isJSONRPCResultResponse as Wr,getLocalDir as Wt,applyExternalChange as X,rewriteMarkdownLinksForDocumentRename as Xn,ALL_EDITOR_IDS as Xr,handleSpawnCursor as Xt,applyAgentMarkdownWrite as Y,resolvePackageVersion as Yn,MalformedGitPointerError as Yr,handleCollabSocketError as Yt,applySeed as Z,rewriteWikiLinksForDocumentRename as Zn,ASSET_EXTENSIONS as Zr,incrementCollabSocketFilteredError as Zt,GIT_UPSTREAM_WRITER as _,ZodNumber$1 as _i,listStarterPacks as _n,toBroadcasterKey as _r,contentHash as _t,AgentPresenceBroadcaster as a,ProblemDetailsSchema as ai,isAllowedWorkspaceHostHeader as an,safetyCheckpoint as ar,buildReadResult as at,HocuspocusAuthTokenSchema as b,boolean$1 as bi,mountMcpAndApi as bn,tracedMkdir as br,createAssetServeMiddleware as bt,BacklinkIndex as c,colorFromSeed as ci,isKnownPackId as cn,saveVersion as cr,buildWipTree as ct,CURSOR_BUNDLE_PATHS_BY_PLATFORM as d,iconFromClientName as di,isPathWithinDir as dn,shadowGit as dr,clearContributors as dt,DEFAULT_SERVER_HOST as ei,initTelemetry as en,runCloneSubprocess as er,attachIdleShutdown as et,ConfigSchema as f,isOrphanMode as fi,isProjectRoot as fn,shutdownTelemetry as fr,coercePackId as ft,FILE_WATCHER_ORIGIN as g,locateIssue as gi,listRescueCheckpoints as gn,streamingProblemEvent as gr,containsConflictMarkers as gt,FILE_SYSTEM_WRITER as h,OK_PROJECT_MARKER as hi,lastKnownHash as hn,startWatcher as hr,commitWipFromTree as ht,AgentFocusBroadcaster as i,ORPHAN_MODES as ii,isAllowedApiOrigin as in,safeSubdir as ir,buildInstructions as it,OBSERVER_SYNC_ORIGIN as j,readStateManifest as jn,validateAgentId as jr,createTestLogger as jt,McpLogger as k,require_dist$2 as ki,readServerPackageVersion as kn,updateLastKnownHash as kr,createServerObserverExtension as kt,CC1Broadcaster as l,defaultScheduler as li,isLoopbackAddress as ln,seedBasenameIndex as lr,classifyEvents as lt,DEFAULT_PACK_ID as m,LOCAL_DIR as mi,isSystemDoc as mn,splitMarkdownBlocks as mr,commitWip as mt,AGENT_ID_RE as n,EXECUTABLE_BLOCKLIST_EXTENSIONS as ni,installTestLoggers as nn,runWithMcpLogger as nr,buildAndOpenSkill as nt,AgentSessionCapacityError as o,SKILL_STATE_TARGETS as oi,isConfigDoc as on,sanitizeClientName as or,buildSkillZip as ot,DEFAULT_CHECKPOINT_RETENTION as p,CONFIG_SCHEMA_MAJOR_PATH as pi,isSelfWrite as pn,spawnDetached as pr,commitUpstreamImport as pt,UiLockCollisionError as q,resolveLockDir as qn,writeConfigPatch as qr,getMetrics as qt,AGENT_WRITE_ORIGIN as r,INLINE_RENDERABLE_EXTENSIONS as ri,installUserSkill as rn,safeContentPath as rr,buildExecResult as rt,AgentSessionManager as s,SYSTEM_DOC_NAME as si,isHocuspocusAuthRejectionReason as sn,saveInMemoryCheckpoint as sr,buildStarterFolderFrontmatterYaml as st,AGENT_ID_MAX_LEN as t,EDITOR_LABELS as ti,installPrettyZodErrors as tn,runDeviceFlowSubprocess as tr,bootServer as tt,CONFLICT_MARKER_RE as u,humanFormat as ui,isPairedWriteOrigin as un,setActiveSpanAttributes as ur,classifyFsPath as ut,HOCUSPOCUS_AUTH_REJECTION_REASONS as v,any$1 as vi,loadPrincipal as vn,tracedAppendFileSync as vr,contributorCount as vt,MANAGED_RENAME_ORIGIN as w,object$1 as wi,parseKeepaliveConnectionId as wn,tracedRmSync as wr,createLiveDerivedIndexExtension as wt,INSTALLED_AGENTS_SCHEMES as x,literal as xi,normalizeFsPath as xn,tracedMkdirSync as xr,createContentFilter as xt,HocuspocusAuthRejection as y,array as yi,loggerFactory as yn,tracedLinkSync as yr,createApiExtension as yt,STARTER_PACKS as z,registerWrite as zn,McpServer as zr,findEnclosingProjectRoot as zt};
|
|
3091
|
-
//# sourceMappingURL=dist-
|
|
3090
|
+
`))e&&await t.raw(`update-ref`,`-d`,e);ee.info({context:e.oldBranch},`[branch-switch] cleaned up detached context ${e.oldBranch}`)}}catch(e){ee.error({err:e},`[branch-switch] detached cleanup failed`)}setBatchInProgress(!1),await ge.flushDeferredStores(`discard-stale`),ye?.emitBranchSwitched(w)}if(e.headMoved&&e.newHead&&me.current&&S>0){let t=H??`.`;try{let S=await commitUpstreamImport(me.current,t,e.oldHead,e.newHead,w);incrementUpstreamImport(),ee.info({oldHead:e.oldHead?.slice(0,8)??`null`,newHead:e.newHead.slice(0,8),sha:S.slice(0,8)},`[history] upstream-import from ${e.oldHead?.slice(0,8)??`null`}..${e.newHead.slice(0,8)} → ${S.slice(0,8)}`)}catch(e){ee.error({err:e},`[shadow] upstream-import failed`)}}})}catch(e){ee.error({err:e},`[server] HEAD watcher failed to start`),Ut.push(`head-watcher`)}let D=J?.[0]??`open-knowledge`,O=[`-c`,`credential.helper=!${J&&J.length>1?J.join(` `):D} auth git-credential`];try{zt=new SyncEngine({projectDir:S,contentDir:t,contentFilter:ue,contentRoot:H,syncEnabled:te(),credentialArgs:O,cc1Broadcaster:ye,setBatchInProgress:e=>{setBatchInProgress(e),e||ge.flushDeferredStores(`within-branch`).catch(e=>{ee.error({err:e},`[persistence] deferred store drain failed after sync batch`)})},onStateChange:e=>{ee.info({state:e},`[sync] state → ${e}`)},onAutoDisable:async e=>{ee.warn({reason:e},`[sync] auto-disabled — persisting to project-local config`);let t=await writeConfigPatch({cwd:S,scope:`project-local`,patch:{autoSync:{enabled:!1}}});t.ok||ee.error({result:t,reason:e,humanError:humanFormat(t.error),configPath:resolveConfigPath(`project-local`,S)},`[sync] failed to persist auto-disable — next restart WILL re-enable sync and re-trigger the same failure. Check permissions on the config path.`)}}),await zt.start()}catch(e){ee.warn({err:e},`[server] SyncEngine failed to start — sync disabled`),zt=null}ze(`files`),ze(`backlinks`),ze(`graph`),ze(`tags`)}return Wt().then(Pe,Ie),{hocuspocus:_e,sessionManager:ve,cc1Broadcaster:ye,agentFocusBroadcaster:Ce,agentPresenceBroadcaster:we,contentFilter:ue,basenameIndex:oe,serverInstanceId:ne,destroy:Ht,ready:Re,degraded:Ut,lockDir:ae,get syncEngine(){return zt}}}const LEGACY_RUNTIME_FILENAMES=[`server.lock`,`ui.lock`,`state.json`,`principal.json`,`sync-state.json`,`conflicts.json`,`last-spawn-error.log`],LEGACY_RUNTIME_DIRNAMES=[`cache`,`tmp`];function findLegacyRuntimeFiles(e){let t=resolve(e,LOCAL_DIR);if(!(()=>{if(!existsSync(t))return!0;try{return readdirSync(t).length===0}catch{return!0}})())return[];let S=[];for(let t of LEGACY_RUNTIME_FILENAMES)existsSync(resolve(e,t))&&S.push(t);for(let t of LEGACY_RUNTIME_DIRNAMES){let w=resolve(e,t);try{existsSync(w)&&statSync(w).isDirectory()&&S.push(`${t}/`)}catch{}}return S}function computeWorktreeAttributes(e){let t=resolveGitDirDetailed(e);switch(t.kind){case`directory`:return{kind:`main`,gitdir:t.path};case`linked`:return{kind:`linked`,gitdir:t.path};case`malformed-pointer`:return{kind:`linked`,gitdir:null};case`inaccessible`:case`absent`:return{kind:`main`,gitdir:null}}}const DEFAULT_IDLE_THRESHOLD_MS=1800*1e3,DESTROY_STEP_TIMEOUT_MS=5e3;async function bootServer(e){initTelemetry();let{kind:t,gitdir:S}=computeWorktreeAttributes(e.projectDir??e.contentDir),w={"ok.worktree.kind":t};return S!==null&&(w[`ok.worktree.gitdir`]=normalizeFsPath(S)),withSpan(`ok.boot`,{attributes:w},async()=>bootServerInner(e))}async function bootServerInner(e){let t=e.skipAutoInit??!1,S=e.attachUiSibling??!0,w=e.idleShutdownMs,E=e.log??getLogger(`boot`),D=process.env.OK_LOCK_KIND===`mcp-spawned`||process.env.OK_LOCK_KIND===`interactive`?process.env.OK_LOCK_KIND:void 0,O=e.lockKind??D??`interactive`,{createServer:k}=await import(`node:http`),{updateServerLockPort:j}=await import(`./server-lock-D7DXNVql-B5nINglj.mjs`).then(e=>e.a),F=!1;if(!t&&e.autoInitFn)try{F=!!await e.autoInitFn()}catch(e){E.warn({err:e},`autoInitFn failed`)}let L=e.projectDir??e.contentDir,B=resolve(L,`.ok`);if(!existsSync(resolve(B,`config.yml`)))throw new MissingOkConfigError(existsSync(B)?`config`:`okdir`,L);existsSync(resolve(B,`.gitignore`))||console.warn("[boot] Note: .ok/.gitignore is missing — per-machine state files in .ok/ may show up as untracked changes. Run `ok init` to add the recommended ignore entries.");let H=findLegacyRuntimeFiles(B);H.length>0&&console.warn(`[boot] Found legacy runtime files at .ok/${H.join(`, `)}. Delete .ok/ and re-init — these files moved to .ok/${LOCAL_DIR}/.`);let q=createServer$1({contentDir:e.contentDir,projectDir:e.projectDir,contentRoot:e.contentRoot,port:e.port,host:e.host,quiet:e.quiet??!1,debounce:e.debounce,maxDebounce:e.maxDebounce,gitEnabled:e.gitEnabled,commitDebounceMs:e.commitDebounceMs,wipRef:e.wipRef,enableTestRoutes:e.enableTestRoutes,shadowRepo:e.shadowRepo,destroyTimeoutMs:e.destroyTimeoutMs,localOpCliArgs:e.localOpCliArgs,onAgentWrite:e.onAgentWrite,lockKind:O,skipStateManifestCheck:e.skipStateManifestCheck}),{hocuspocus:J,destroy:Y,ready:ee,degraded:te,lockDir:ne,sessionManager:ae,agentFocusBroadcaster:oe,agentPresenceBroadcaster:se}=q,ce=(()=>{let t=e.host??`localhost`;return t===`0.0.0.0`||t===`::`?`localhost`:t.includes(`:`)&&!t.startsWith(`[`)?`[${t}]`:t})(),ue=e.port??0,de=createMcpHttpHandler({contentDir:e.contentDir,projectDir:e.projectDir??e.contentDir,config:e.config,getServerUrl:()=>`http://${ce}:${ue}`,log:E}),fe=k();fe.headersTimeout=3e4,fe.requestTimeout=6e4;let me=e.serveContentAssets?createAssetServeMiddleware({contentFilter:q.contentFilter,contentSirv:build_default(e.contentDir,{dev:!0,dotfiles:!1}),inlineExtensions:INLINE_RENDERABLE_EXTENSIONS,assetExtensions:ASSET_EXTENSIONS,blocklistExtensions:EXECUTABLE_BLOCKLIST_EXTENSIONS}):void 0,ge=mountMcpAndApi({httpServer:fe,hocuspocus:J,mcpHttpHandler:de,log:E,sessionManager:ae,agentFocusBroadcaster:oe,agentPresenceBroadcaster:se,keepaliveGraceMs:e.keepaliveGraceMs,contentAssetMiddleware:me}),_e=async()=>{throw Error(`bootServer: destroy() invoked before initialization — boot did not complete`)},ve=null;w!==null&&(ve=attachIdleShutdown({httpServer:fe,thresholdMs:w??DEFAULT_IDLE_THRESHOLD_MS,log:E,onShutdown:(e.idleShutdownHandler??(e=>async()=>{await e()}))(async()=>{await _e()})})),await new Promise((t,S)=>{let w=e=>S(e);fe.once(`error`,w),fe.listen(e.port,e.host,()=>{fe.removeListener(`error`,w),t()})});let ye=fe.address(),Ce=typeof ye==`object`&&ye?ye.port:e.port??0;if(ue=Ce,j(ne,Ce),S&&e.spawnUiSiblingFn)try{await e.spawnUiSiblingFn({lockDir:ne,log:E})}catch(e){E.warn({err:e},`spawnUiSiblingFn failed`)}let we=!1,Te=async(e,t)=>{let S;try{await Promise.race([t(),new Promise((t,w)=>{S=setTimeout(()=>{w(Error(`${e} timed out after ${DESTROY_STEP_TIMEOUT_MS}ms`))},DESTROY_STEP_TIMEOUT_MS),S.unref?.()})])}finally{S!==void 0&&clearTimeout(S)}};return _e=async()=>{if(we)return;we=!0;let e=[],t=async(t,S)=>{try{await Te(t,S)}catch(S){e.push(S),E.warn({err:S,step:t},`bootServer destroy step failed`)}};try{ve?.detach()}catch(t){e.push(t),E.warn({err:t,step:`idleHandle.detach`},`bootServer destroy step failed`)}if(await t(`mount.shutdown`,()=>ge.shutdown()),await t(`mcpHttpHandler.close`,()=>de.close()),await t(`mount.wss.close`,()=>new Promise((e,t)=>{ge.wss.close(S=>S?t(S):e())})),await t(`httpServer.closeAllConnections`,async()=>{fe.closeAllConnections?.()}),await t(`httpServer.close`,()=>new Promise((e,t)=>{fe.close(S=>S&&S.code!==`ERR_SERVER_NOT_RUNNING`?t(S):e())})),await t(`destroyHocuspocus`,()=>Y()),await t(`shutdownTelemetry`,()=>shutdownTelemetry()),e.length>0)throw AggregateError(e,`bootServer destroy completed with errors`)},{httpServer:fe,destroy:_e,lockDir:ne,contentDir:e.contentDir,port:Ce,ready:ee,degraded:te,didAutoInit:F,serverInstance:q}}const ConfigSchema=ConfigSchema$1;function detectClaudeDesktopPresence(e={}){let t=e.home??homedir(),S=e.platformName??process.platform,w=e.env??process.env;return S===`darwin`?existsSync(join(t,`Library`,`Application Support`,`Claude`)):S===`win32`?existsSync(join(w.APPDATA??join(t,`AppData`,`Roaming`),`Claude`)):!1}const ANCESTOR_WALK_DEPTH_LIMIT=30,GIT_MARKER=`.git`;function findEnclosingGitRoot(e){let t=resolve(e),S=0;for(;S<ANCESTOR_WALK_DEPTH_LIMIT;){let e=!1;try{e=existsSync(resolve(t,GIT_MARKER))}catch{e=!1}if(e)return{gitRoot:t,distance:S};let w=dirname(t);if(w===t)return null;t=w,S+=1}return null}const execFileAsync=promisify(execFile);var ProjectGitInitError=class extends Error{stderr;constructor(e,t=``,S){super(e,S),this.name=`ProjectGitInitError`,this.stderr=t}};async function isInsideExistingWorkTree(e){try{let{stdout:t}=await execFileAsync(`git`,[`rev-parse`,`--is-inside-work-tree`],{cwd:e});return t.trim()===`true`}catch{return!1}}async function ensureProjectGit(e){let t=resolve(e),S=resolve(t,`.git`),w=resolve(S,`HEAD`),E=!1;if(existsSync(S)){if(!statSync(S).isDirectory()||existsSync(w))return{didInit:!1};console.log(`[project-git] detected partial .git/ — running git init to repair`),E=!0}else if(await isInsideExistingWorkTree(t))return{didInit:!1};let D=``;try{D=(await execFileAsync(`git`,[`init`,`--initial-branch=main`,t])).stderr??``}catch(e){let S=typeof e==`object`&&e&&`stderr`in e?String(e.stderr??``):``;throw new ProjectGitInitError(`git init failed at ${t}: ${e instanceof Error?e.message:String(e)}`,S,{cause:e})}if(!existsSync(w))throw new ProjectGitInitError(`git init reported success but ${S}/HEAD is missing (partial init detected)`,D);return E?(console.log(`[project-git] backfilled missing .git/HEAD at ${t}`),{didInit:!0,repaired:!0}):(console.log(`[project-git] initialized .git/ at ${t} (branch: main)`),{didInit:!0})}async function resolvePackageVersion(e,t){let S;try{S=createRequire(t).resolve(e)}catch(e){if(e?.code===`MODULE_NOT_FOUND`)return;throw e}for(let t=dirname(S),w=0;w<32;w+=1){let S=join(t,`package.json`);if(existsSync(S))try{let t=JSON.parse(await readFile$1(S,`utf-8`));if(t.name===e&&typeof t.version==`string`)return t.version}catch{}let w=dirname(t);if(w===t)return;t=w}}export{assertNeverDiskEvent as $,rewriteWikiLinksForDocumentRename as $n,ALL_EDITOR_IDS as $r,incrementServerObserverFire as $t,MissingOkConfigError as A,_coercedNumber as Ai,readServerPackageVersion as An,tracedWriteFile as Ar,createStreamingErrorWriter as At,STARTER_PACK_IDS as B,registerWrite as Bn,writeTargetVersion as Br,formatAuthRejectionWire as Bt,LOG_MD_TEMPLATE as C,boolean$1 as Ci,parseAuthRejectionWire as Cn,tracedMkdir as Cr,createExternalChangeHandler as Ct,MCP_SERVER_NAME as D,object$1 as Di,planSeed as Dn,tracedRmSync as Dr,createPersistenceExtension as Dt,MCP_CONNECTION_ID_HEADER as E,number$1 as Ei,pathToDocName as En,tracedRenameSync as Er,createOsProbe as Et,SERVICE_WRITER as F,readUiLock as Fn,validateCloneInputs as Fr,errorResponse as Ft,StateManifestError as G,resolveContentDir as Gn,isInitializedNotification as Gr,getLocalDir as Gt,STATE_MANIFEST_FILENAME as H,removeLastKnownHash as Hn,McpServer as Hr,formatContributorsFrom as Ht,SKILL_INSTALL_EVENTS_FILE_REL as I,reconcile as In,validateSkillZip as Ir,evictStaleTrackerEntries as It,acquireUiLock as J,resolveLockDir as Jn,readConfigSafely as Jr,getMetrics as Jt,TagIndex as K,resolveCursorBinaryDefault as Kn,isJSONRPCRequest as Kr,getLogger as Kt,STARTER_FOLDERS as L,recordContributor as Ln,withSpan as Lr,extractWikiLinksFromMarkdown as Lt,PinoLogger as M,require_dist$2 as Mi,readStateManifest as Mn,updateLastKnownHash as Mr,detectClaudeDesktopPresence as Mt,ProjectGitInitError as N,readTargetRecordedAt as Nn,updateUiLockPort as Nr,detectProjectShape as Nt,MISSING_OK_CONFIG_MESSAGE as O,string$2 as Oi,readAllTargets as On,tracedRmdirSync as Or,createServer$1 as Ot,ROLLBACK_ORIGIN as P,readTargetVersion as Pn,validateAgentId as Pr,ensureProjectGit as Pt,assertCompatibleStateManifest as Q,rewriteMarkdownLinksForDocumentRename as Qn,MalformedGitPointerError as Qr,incrementCollabSocketFilteredError as Qt,STARTER_FOLDER_FRONTMATTER_FILENAME as R,recordSkillInstallEvent as Rn,withSpanSync as Rr,findEnclosingGitRoot as Rt,LIVE_DERIVED_INDEX_DEBOUNCE_MS as S,array as Si,normalizeFsPath as Sn,tracedLinkSync as Sr,createContentFilterAsync as St,MAX_AGENT_SESSIONS as T,looseObject as Ti,parseKeepaliveConnectionId as Tn,tracedRename as Tr,createMcpHttpHandler as Tt,SeedPrerequisiteError as U,resetMetrics as Un,JSONRPCMessageSchema as Ur,gcCheckpointRefs as Ut,STARTER_TEMPLATES as V,releaseUiLock as Vn,writeTracker as Vr,formatContributors as Vt,SeedRootDirError as W,resolveBundledSkillDir as Wn,LATEST_PROTOCOL_VERSION as Wr,getCurrentMcpLogger as Wt,applyExternalChange as X,resolvePackageVersion as Xn,writeConfigPatch as Xr,handleCollabSocketError as Xt,applyAgentMarkdownWrite as Y,resolvePack as Yn,resolveConfigPath as Yr,getTracer as Yt,applySeed as Z,restoreContributors as Zn,GitDirAccessError as Zr,handleSpawnCursor as Zt,GIT_UPSTREAM_WRITER as _,LOCAL_DIR as _i,listRescueCheckpoints as _n,startWatcher as _r,contentHash as _t,AgentPresenceBroadcaster as a,EXECUTABLE_BLOCKLIST_EXTENSIONS as ai,isAllowedApiOrigin as an,safeContentPath as ar,buildReadResult as at,HocuspocusAuthTokenSchema as b,ZodNumber$1 as bi,loggerFactory as bn,toBroadcasterKey as br,createAssetServeMiddleware as bt,BacklinkIndex as c,ProblemDetailsSchema as ci,isHocuspocusAuthRejectionReason as cn,sanitizeClientName as cr,buildWipTree as ct,CURSOR_BUNDLE_PATHS_BY_PLATFORM as d,colorFromSeed as di,isPairedWriteOrigin as dn,seedBasenameIndex as dr,clearContributors as dt,ASSET_EXTENSIONS as ei,initShadowRepo as en,runAuthReposSubprocess as er,attachIdleShutdown as et,ConfigSchema as f,defaultScheduler as fi,isPathWithinDir as fn,setActiveSpanAttributes as fr,coercePackId as ft,FILE_WATCHER_ORIGIN as g,CONFIG_SCHEMA_MAJOR_PATH as gi,lastKnownHash as gn,splitMarkdownBlocks as gr,containsConflictMarkers as gt,FILE_SYSTEM_WRITER as h,isOrphanMode as hi,isSystemDoc as hn,spawnDetached as hr,commitWipFromTree as ht,AgentFocusBroadcaster as i,EDITOR_LABELS as ii,installUserSkill as in,runWithMcpLogger as ir,buildInstructions as it,OBSERVER_SYNC_ORIGIN as j,NEVER as ji,readSkillInstallStateSnapshot as jn,tracedWriteFileSync as jr,createTestLogger as jt,McpLogger as k,url as ki,readBranchFromHead as kn,tracedUnlinkSync as kr,createServerObserverExtension as kt,CC1Broadcaster as l,SKILL_STATE_TARGETS as li,isKnownPackId as ln,saveInMemoryCheckpoint as lr,classifyEvents as lt,DEFAULT_PACK_ID as m,iconFromClientName as mi,isSelfWrite as mn,shutdownTelemetry as mr,commitWip as mt,AGENT_ID_RE as n,DEFAULT_GITHUB_OAUTH_CLIENT_ID as ni,installPrettyZodErrors as nn,runCloneSubprocess as nr,buildAndOpenSkill as nt,AgentSessionCapacityError as o,INLINE_RENDERABLE_EXTENSIONS as oi,isAllowedWorkspaceHostHeader as on,safeSubdir as or,buildSkillZip as ot,DEFAULT_CHECKPOINT_RETENTION as p,humanFormat as pi,isProjectRoot as pn,shadowGit as pr,commitUpstreamImport as pt,UiLockCollisionError as q,resolveCursorSpawnInvocation as qn,isJSONRPCResultResponse as qr,getMeter as qt,AGENT_WRITE_ORIGIN as r,DEFAULT_SERVER_HOST as ri,installTestLoggers as rn,runDeviceFlowSubprocess as rr,buildExecResult as rt,AgentSessionManager as s,ORPHAN_MODES as si,isConfigDoc as sn,safetyCheckpoint as sr,buildStarterFolderFrontmatterYaml as st,AGENT_ID_MAX_LEN as t,CC1_CONTRACT_VERSION as ti,initTelemetry as tn,runAuthStatusSubprocess as tr,bootServer as tt,CONFLICT_MARKER_RE as u,SYSTEM_DOC_NAME as ui,isLoopbackAddress as un,saveVersion as ur,classifyFsPath as ut,HOCUSPOCUS_AUTH_REJECTION_REASONS as v,OK_PROJECT_MARKER as vi,listStarterPacks as vn,streamingProblemEvent as vr,contributorCount as vt,MANAGED_RENAME_ORIGIN as w,literal as wi,parseHocuspocusAuthToken as wn,tracedMkdirSync as wr,createLiveDerivedIndexExtension as wt,INSTALLED_AGENTS_SCHEMES as x,any$1 as xi,mountMcpAndApi as xn,tracedAppendFileSync as xr,createContentFilter as xt,HocuspocusAuthRejection as y,locateIssue as yi,loadPrincipal as yn,swapContributors as yr,createApiExtension as yt,STARTER_PACKS as z,registerAllTools as zn,writeStateManifest as zr,findEnclosingProjectRoot as zt};
|
|
3091
|
+
//# sourceMappingURL=dist-GJE8NgVb.mjs.map
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{_ as e,d as t,h as n,m as r,o as i,p as a,r as o,u as s,v as c}from"./init-
|
|
1
|
+
import{_ as e,d as t,h as n,m as r,o as i,p as a,r as o,u as s,v as c}from"./init-mO3B7tHj.mjs";import{i as l,n as u,r as d,t as f}from"./src-BxHt_W7H.mjs";import{n as p}from"./loader-BvNa-UGH.mjs";import{n as m}from"./preview-I_sZoeGy.mjs";export{a as ALL_EDITOR_IDS,r as EDITOR_LABELS,n as EDITOR_TARGETS,o as detectInstalledEditors,c as initContent,p as loadConfig,d as parseGitHubBlobUrl,l as parseGitUrl,m as previewContent,i as readExistingMcpEntry,e as resolveProjectRoot,u as validateLocalFolderForShare,s as writeEditorMcpConfig,f as writeProjectAiIntegrations,t as writeUserMcpConfigs};
|