@inkeep/open-knowledge 0.0.0-dev-20260424044137 → 0.0.0-dev-20260424150009
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 +228 -0
- package/dist/{banner-cxbl_Ely.mjs → banner-CI3--er0.mjs} +2 -2
- package/dist/{chokidar-C6yIgAJZ.mjs → chokidar-Z9-8o-jG.mjs} +1 -1
- package/dist/cli.mjs +348 -274
- package/dist/colors-Cmha5EZJ.mjs +2 -0
- package/dist/colors-CxLEz90E.mjs +1 -0
- package/dist/constants-QFqPbiZ7.mjs +2 -0
- package/dist/{execAsync-DPeftusp.mjs → execAsync-CUbOIvpN.mjs} +1 -1
- package/dist/{execAsync-DhDvXMWQ.mjs → execAsync-CjzNx_Wg.mjs} +1 -1
- package/dist/{execAsync-RJZP-H-A.mjs → execAsync-xymjJFLm.mjs} +1 -1
- package/dist/{getMachineId-bsd-DcLiB1Fb.mjs → getMachineId-bsd-B_JlAoG9.mjs} +2 -2
- package/dist/{getMachineId-bsd-B-D2bOSl.mjs → getMachineId-bsd-CMAUw13R.mjs} +2 -2
- package/dist/{getMachineId-bsd-Dl-xGqjV.mjs → getMachineId-bsd-DrOrzTD2.mjs} +2 -2
- package/dist/{getMachineId-darwin-A7Xy885j.mjs → getMachineId-darwin-BQcf5peQ.mjs} +2 -2
- package/dist/{getMachineId-darwin-BQro2RrT.mjs → getMachineId-darwin-DC2qb-xd.mjs} +2 -2
- package/dist/{getMachineId-darwin-DPoYqu2E.mjs → getMachineId-darwin-DTGIdpu7.mjs} +2 -2
- package/dist/{getMachineId-linux-BwDCbiIf.mjs → getMachineId-linux-C3A6DqVu.mjs} +1 -1
- package/dist/{getMachineId-linux-DLyT3DBf.mjs → getMachineId-linux-CMta7vmz.mjs} +1 -1
- package/dist/{getMachineId-linux-B6DWkXLm.mjs → getMachineId-linux-nez-pYOw.mjs} +1 -1
- package/dist/{getMachineId-unsupported-CnUw0N-q.mjs → getMachineId-unsupported-BrxgPAkN.mjs} +1 -1
- package/dist/{getMachineId-unsupported-VfwZ1yF9.mjs → getMachineId-unsupported-gFGrtRr7.mjs} +1 -1
- package/dist/{getMachineId-unsupported-CQbaY6vv.mjs → getMachineId-unsupported-j-7WDfTT.mjs} +1 -1
- package/dist/{getMachineId-win-Bmzw0GhO.mjs → getMachineId-win-CfF1zVez.mjs} +2 -2
- package/dist/{getMachineId-win-DHI6zfnJ.mjs → getMachineId-win-D8Js6D_D.mjs} +2 -2
- package/dist/{getMachineId-win-BYymgu0m.mjs → getMachineId-win-DsIEIIis.mjs} +2 -2
- package/dist/index.d.mts +0 -7
- package/dist/index.mjs +1 -1
- package/dist/init-Bw4LWW1o.mjs +1 -0
- package/dist/init-D0FupYPY.mjs +1 -0
- package/dist/init-D2rQPwU1.mjs +120 -0
- package/dist/init-DUIYyxaK.mjs +5 -0
- package/dist/{is-object-DVVYT5oa.mjs → is-object-7k010cEq.mjs} +1 -1
- package/dist/{keepalive-BqSeN73F.mjs → keepalive-CfaIYv9f.mjs} +1 -1
- package/dist/{loader-BHPXz9Iy.mjs → loader-C3Wwbx8H.mjs} +2 -2
- package/dist/loader-DNeW8gS5.mjs +1 -0
- package/dist/{paths-BQgE70zV.mjs → paths-Ba3tNHQr.mjs} +2 -2
- package/dist/paths-ORyeejxG.mjs +1 -0
- package/dist/preview-BR34uHnQ.mjs +1 -0
- package/dist/{preview-OJqnWCcq.mjs → preview-QLsD0vVK.mjs} +2 -2
- package/dist/public/assets/McpConsentDialogBody-BpxsNx0A.js +1 -0
- package/dist/public/assets/index-Bnp3jcJM.js +27 -0
- package/dist/public/assets/index-tR5M38z1.css +1 -0
- package/dist/public/index.html +2 -2
- package/dist/src-CeiyJxyd.mjs +1 -0
- package/dist/{src-DQq7h88V.mjs → src-DIoA15kL.mjs} +25 -8
- package/dist/src-lFQj9tkB.mjs +1 -0
- package/dist/start-B9LXLMio.mjs +1 -0
- package/dist/{start-B0vMn8zP.mjs → start-C79L5QTe.mjs} +2 -2
- package/dist/{wrapper-DhAZwCvI.mjs → wrapper-BlhyhsXg.mjs} +1 -1
- package/package.json +6 -2
- package/scripts/postinstall.mjs +53 -0
- package/scripts/probe-exec.ts +100 -0
- package/scripts/probe-read-document.ts +111 -0
- package/dist/colors-CDmFQ1jq.mjs +0 -1
- package/dist/colors-CEQEtnHQ.mjs +0 -2
- package/dist/constants-XSxfsuBx.mjs +0 -2
- package/dist/init-BVuRoDMn.mjs +0 -230
- package/dist/init-CJTyH3QE.mjs +0 -1
- package/dist/init-Csa00nlq.mjs +0 -5
- package/dist/init-Dv8oz1_t.mjs +0 -1
- package/dist/loader-bSLuDz-u.mjs +0 -1
- package/dist/paths-vQp9sH9q.mjs +0 -1
- package/dist/preview-DllU-SIf.mjs +0 -1
- package/dist/public/assets/McpConsentDialogBody-DlJsSqo8.js +0 -1
- package/dist/public/assets/index-C7yqVgG5.css +0 -1
- package/dist/public/assets/index-COipWE0S.js +0 -27
- package/dist/src-DfYtlLCQ.mjs +0 -1
- package/dist/src-DuGm387u.mjs +0 -1
- package/dist/start-Be-LvEoc.mjs +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"./src-CB-sXsMW.mjs";import{Bt as e,ct as t,dt as n,ft as r,i,lt as a,n as o,r as s,ut as c}from"./src-DIoA15kL.mjs";import{f as l,r as u}from"./server-lock-4vgF5yho.mjs";export{o as ProjectGitInitError,t as UiLockCollisionError,a as acquireUiLock,i as bootServer,s as ensureProjectGit,e as getLogger,l as isProcessAlive,u as readServerLock,c as readUiLock,n as releaseUiLock,r as updateUiLockPort};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{o as e}from"./start-C79L5QTe.mjs";export{e as startCommand};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{
|
|
2
|
-
//# sourceMappingURL=start-
|
|
1
|
+
import{i as e}from"./constants-QFqPbiZ7.mjs";import{O as t}from"./src-CB-sXsMW.mjs";import{Command as n}from"commander";import{closeSync as r,existsSync as i,mkdirSync as a,openSync as o}from"node:fs";import{join as s}from"node:path";import{spawn as c}from"node:child_process";function l(){let e=process.execPath,t=process.argv[1];return t?{command:e,prefixArgs:[t]}:(console.warn(`[self-spawn] process.argv[1] is empty — falling back to \`npx @inkeep/open-knowledge\`. This re-introduces the version-drift surface that re-exec was fixing. Observed argv: ${JSON.stringify(process.argv)}`),{command:`npx`,prefixArgs:[`@inkeep/open-knowledge`]})}function u(e){return e.uiLock?e.isAlive(e.uiLock.pid)?{action:`skip`,reason:`alive`,pid:e.uiLock.pid,port:e.uiLock.port}:{action:`spawn`,reason:`stale`,stalePid:e.uiLock.pid}:{action:`spawn`,reason:`absent`}}function d(e){i(e.lockDir)||a(e.lockDir,{recursive:!0});let t=o(s(e.lockDir,`last-spawn-error.log`),`w`),n=e.spawn??c,{PORT:u,...d}=process.env,f=l();try{let r=n(f.command,[...f.prefixArgs,...e.args??[`ui`]],{detached:!0,stdio:[`ignore`,`ignore`,t],cwd:e.cwd,env:d});return r.unref(),r}finally{try{r(t)}catch{}}}async function f(e){let t=e.now()+e.timeoutMs;for(;e.now()<t;){let t=e.readUiLock();if(t&&t.port>0)return t.port;await e.sleep(e.pollIntervalMs)}let n=e.readUiLock();return n&&n.port>0?n.port:null}function p(e){let t=e.sigtermGraceMs??1e4,n=e.sigtermPollIntervalMs??200,r=e.sleep??(e=>new Promise(t=>setTimeout(t,e)));return async()=>{try{let i=e.readUiLock();if(i&&e.isAlive(i.pid))try{e.killPid(i.pid,`SIGTERM`),e.log?.info({pid:i.pid,port:i.port},`idle-shutdown: SIGTERM UI sibling`);let a=Date.now()+t;for(;Date.now()<a&&e.isAlive(i.pid);)await r(n);if(e.isAlive(i.pid))try{e.killPid(i.pid,`SIGKILL`),e.log?.warn({pid:i.pid,graceMs:t},`idle-shutdown: SIGTERM grace expired — escalated to SIGKILL`)}catch(t){e.log?.error({pid:i.pid,err:t instanceof Error?t.message:String(t)},`idle-shutdown: SIGKILL failed`)}}catch(t){e.log?.warn({pid:i.pid,err:t instanceof Error?t.message:String(t)},`idle-shutdown: failed to SIGTERM UI sibling`)}}catch(t){e.log?.warn({err:t instanceof Error?t.message:String(t)},`idle-shutdown: UI lookup failed; proceeding with destroy`)}await e.destroy()}}async function m(e){let{config:n,cwd:r}=e,i=e.skipAutoInit??!1,a=e.skipUiAutoSpawn??!1,o=e.idleThresholdMs??18e5,{existsSync:s,mkdirSync:c}=await import(`node:fs`),{resolve:l}=await import(`node:path`),{bootServer:m,ensureProjectGit:h,getLogger:g,isProcessAlive:_,readUiLock:v}=await import(`./src-lFQj9tkB.mjs`),{resolveContentDir:y}=await import(`./paths-ORyeejxG.mjs`),b=e.log??g(`start`),x=y(n,r);s(x)||(c(x,{recursive:!0}),b.info({contentDir:x},`Created content directory`));let S=!s(l(r,t)),C=i?void 0:async()=>{try{let{initContent:e}=await import(`./init-D0FupYPY.mjs`),t=e(r);return S||t.created.length>0}catch(e){return console.warn(`Auto-init failed:`,e instanceof Error?e.message:e),!1}},w=!1,T=l(x,t),E=n.server.openOnAgentEdit?()=>{if(w)return;let e=v(T);if(!e||e.port<=0||!_(e.pid))return;w=!0;let t=`http://localhost:${e.port}`;import(`./open-browser-T-bOz6ZQ.mjs`).then(({openBrowser:e})=>e(t)).catch(()=>{})}:void 0,D=null,O=await m({contentDir:x,projectDir:r,contentRoot:n.content.dir,port:n.server.port,host:n.server.host,quiet:!1,debounce:n.persistence.debounceMs,maxDebounce:n.persistence.maxDebounceMs,includePatterns:n.content.include,excludePatterns:n.content.exclude,onAgentWrite:E,localOpCliArgs:[process.execPath,process.argv[1]],attachUiSibling:!0,idleShutdownMs:o,skipAutoInit:i,autoInitFn:C,ensureProjectGitFn:i?void 0:()=>h(r),spawnUiSiblingFn:async({lockDir:t})=>{if(D=u({uiLock:v(t),isAlive:_}),D.action===`spawn`&&!a)try{d({lockDir:t,cwd:r,spawn:e.spawn}),b.info({reason:D.reason},`[start] auto-spawned ok ui sibling`)}catch(e){console.warn(`[start] failed to auto-spawn ok ui: ${e instanceof Error?e.message:String(e)}`)}else D.action===`skip`&&b.info({port:D.port,pid:D.pid},`UI already running at port ${D.port}`)},idleShutdownHandler:e=>p({readUiLock:()=>v(O.lockDir),isAlive:_,killPid:(e,t)=>{process.kill(e,t)},destroy:e,log:b}),log:b});D||={action:`skip`,reason:`alive`,pid:0,port:0};let k=D,A=null;if(k.action===`skip`)A=k.port>0?k.port:null;else if(!a){let t=e.uiBindTimeoutMs??3e3;A=await f({readUiLock:()=>v(O.lockDir),now:Date.now,sleep:e=>new Promise(t=>setTimeout(t,e)),timeoutMs:t,pollIntervalMs:50}),A===null&&b.warn({timeoutMs:t},`[start] ok ui did not bind within timeout — banner falls back to API URL`)}return{httpServer:O.httpServer,destroy:O.destroy,lockDir:O.lockDir,contentDir:x,port:O.port,ready:O.ready,degraded:O.degraded,uiSpawnDecision:D,resolvedUiPort:A,didAutoInit:O.didAutoInit,didGitInit:O.didGitInit}}function h(r){return new n(`start`).description(`Start the knowledge base collab server`).option(`-p, --port <port>`,`Server port`,void 0).option(`-H, --host <host>`,`Server host`,void 0).option(`--open`,`Open browser after start`).option(`--no-init`,`Skip auto-scaffolding of ${t}/`).action(async n=>{let{renderBanner:i}=await import(`./banner-CI3--er0.mjs`),{dim:a,error:o,info:s,warning:c}=await import(`./colors-CxLEz90E.mjs`),l=r(),u=process.cwd();n.port!==void 0&&(l.server.port=Number(n.port)),n.host!==void 0&&(l.server.host=n.host);let d;try{d=await m({config:l,cwd:u,skipAutoInit:n.init===!1})}catch(e){let{ProjectGitInitError:t}=await import(`./src-lFQj9tkB.mjs`);e instanceof t&&(console.error(o(`open-knowledge requires git to initialize a parent repo. Install git or run 'git init' yourself, then re-run.`)),e.stderr&&console.error(a(e.stderr.trim())),process.exit(1)),console.error(`${o(`Failed to start:`)} ${e instanceof Error?e.stack??e.message:String(e)}`),process.exit(1)}let f=!1,p=async e=>{if(!f){f=!0,console.log(a(`\nShutting down (${e})...`));try{await d.destroy()}catch(e){console.error(`${o(`destroy() failed:`)} ${e instanceof Error?e.stack??e.message:String(e)}`),process.exitCode=1}process.exit(process.exitCode??0)}};process.once(`SIGINT`,()=>{p(`SIGINT`)}),process.once(`SIGTERM`,()=>{p(`SIGTERM`)});let h=`http://${l.server.host}:${d.port}`,g=l.server.host===`0.0.0.0`||l.server.host===`::`?`http://0.0.0.0:${d.port}`:void 0,_=d.resolvedUiPort,v=_!==null&&_>0?`http://${l.server.host}:${_}`:h;console.log(i({name:`open-knowledge`,version:e,localUrl:v,apiUrl:v===h?void 0:h,networkUrl:g})),d.didAutoInit&&(console.log(` ${s(`✓`)} Scaffolded ${t}/ (first run)`),console.log(` ${a("Tip: Run `open-knowledge init` to register MCP tools for Claude Code")}\n`));let y={"shadow-repo":`Version history and branch-switch safety unavailable`,"file-watcher":`External file changes will not sync to the editor`,"head-watcher":`Git branch switches may cause document inconsistency`};d.ready.then(async()=>{if(d.degraded.length>0){console.log();for(let e of d.degraded){let t=y[e]??`${e} (check server logs for details)`;console.warn(` ${c(`⚠`)} ${c(e)}: ${a(t)}`)}console.log()}if(d.didAutoInit||d.didGitInit)if(d.didGitInit&&console.log(`\n ${s(`✓`)} Initialized git repo at ${u}/.git/ (default branch: main)`),d.didAutoInit)try{let{previewContent:e,formatPreviewBlock:t}=await import(`./preview-BR34uHnQ.mjs`),n=e({projectDir:u,contentDir:d.contentDir,include:l.content.include,exclude:l.content.exclude});console.log(`\n${t(n,u)}\n`)}catch(e){console.warn(`Content preview unavailable: ${e instanceof Error?e.message:String(e)}`)}else console.log();if(n.open){let{openBrowser:e}=await import(`./open-browser-T-bOz6ZQ.mjs`);e(v)}}).catch(e=>{console.error(` ${o(`Server initialization failed:`)} ${e instanceof Error?e.message:String(e)}`)})})}export{d as a,u as i,m as n,h as o,p as r,l as s,f as t};
|
|
2
|
+
//# sourceMappingURL=start-C79L5QTe.mjs.map
|
|
@@ -4,4 +4,4 @@ import{i as e,o as t,t as n}from"./chunk-FK9Q3tQk.mjs";var r=n(((e,t)=>{let n=[`
|
|
|
4
4
|
`)+`\r
|
|
5
5
|
\r
|
|
6
6
|
`+n)}function w(e,t,n,r,i,a){if(e.listenerCount(`wsClientError`)){let r=Error(i);Error.captureStackTrace(r,w),e.emit(`wsClientError`,r,n,t)}else C(n,r,i,a)}}));p(),d(),o(),c(),l(),m(),f();var g=t(h(),1).default;export{g as WebSocketServer};
|
|
7
|
-
//# sourceMappingURL=wrapper-
|
|
7
|
+
//# sourceMappingURL=wrapper-BlhyhsXg.mjs.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inkeep/open-knowledge",
|
|
3
|
-
"version": "0.0.0-dev-
|
|
3
|
+
"version": "0.0.0-dev-20260424150009",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
},
|
|
21
21
|
"files": [
|
|
22
22
|
"dist",
|
|
23
|
+
"scripts",
|
|
23
24
|
"!dist/**/*.map"
|
|
24
25
|
],
|
|
25
26
|
"engines": {
|
|
@@ -30,8 +31,11 @@
|
|
|
30
31
|
},
|
|
31
32
|
"scripts": {
|
|
32
33
|
"build:cli": "tsdown",
|
|
33
|
-
"build:
|
|
34
|
+
"build:app": "cp -r ../app/dist dist/public",
|
|
35
|
+
"build:skill-asset": "mkdir -p dist/assets/skills/open-knowledge && cp ../server/assets/skills/open-knowledge/SKILL.md dist/assets/skills/open-knowledge/SKILL.md",
|
|
36
|
+
"build:assets": "bun run build:app && bun run build:skill-asset",
|
|
34
37
|
"build": "bun run build:cli && bun run build:assets",
|
|
38
|
+
"postinstall": "node scripts/postinstall.mjs",
|
|
35
39
|
"test": "bun test",
|
|
36
40
|
"typecheck": "tsc --noEmit",
|
|
37
41
|
"prepublishOnly": "bun run build && bun run test"
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* `@inkeep/open-knowledge` postinstall hook.
|
|
4
|
+
*
|
|
5
|
+
* Fires `installUserSkill()` once per `npm install` / `bun install` / `npx`
|
|
6
|
+
* cache population. Non-fatal: any failure is swallowed so the host install
|
|
7
|
+
* never blocks.
|
|
8
|
+
*
|
|
9
|
+
* Spec: 2026-04-22-mcp-guidance-no-project-pollution (FR12 / D20).
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
async function run() {
|
|
13
|
+
let installUserSkill;
|
|
14
|
+
try {
|
|
15
|
+
// Relative import resolves inside the published npm tarball at
|
|
16
|
+
// <pkg-root>/dist/index.mjs — the tsdown-bundled entry that re-exports
|
|
17
|
+
// `installUserSkill` from @inkeep/open-knowledge-server.
|
|
18
|
+
const mod = await import('../dist/index.mjs');
|
|
19
|
+
installUserSkill = mod.installUserSkill;
|
|
20
|
+
} catch {
|
|
21
|
+
// Dev-install scenarios (e.g. running `npm install` against a tarball
|
|
22
|
+
// where dist/ is missing, `--ignore-scripts`, or source-only installs)
|
|
23
|
+
// never block — fall through to exit 0.
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (typeof installUserSkill !== 'function') return;
|
|
28
|
+
|
|
29
|
+
let result;
|
|
30
|
+
try {
|
|
31
|
+
result = await installUserSkill();
|
|
32
|
+
} catch {
|
|
33
|
+
// installUserSkill is documented as never-throws; this catch is pure
|
|
34
|
+
// defensive hardening so a broken build never blocks `npm install`.
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (result === 'installed') {
|
|
39
|
+
process.stdout.write('[open-knowledge] Agent Skill installed to detected agent hosts.\n');
|
|
40
|
+
} else if (result === 'failed') {
|
|
41
|
+
process.stderr.write(
|
|
42
|
+
'[open-knowledge] Agent Skill auto-install failed; run manually: ' +
|
|
43
|
+
"npx skills@~1.5.0 add <bundled-path> --agent '*' -g -y --copy\n",
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
// result === 'skip-current' → silent.
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Fire-and-forget. Always exit 0 regardless of outcome per SPEC D20 —
|
|
50
|
+
// `npm install` must never fail because skill-install hit an edge case.
|
|
51
|
+
run().finally(() => {
|
|
52
|
+
process.exit(0);
|
|
53
|
+
});
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* End-to-end probe: buildExecResult through the full pipeline
|
|
3
|
+
* (parseCommand → just-bash → extractPaths → enrichPath → dual-channel
|
|
4
|
+
* response). Hand-runnable — not part of the test suite.
|
|
5
|
+
*
|
|
6
|
+
* Run via: `bun run packages/cli/scripts/probe-exec.ts`
|
|
7
|
+
*/
|
|
8
|
+
import { mkdirSync, rmSync, writeFileSync } from 'node:fs';
|
|
9
|
+
import { tmpdir } from 'node:os';
|
|
10
|
+
import { resolve } from 'node:path';
|
|
11
|
+
import { commitWip, initShadowRepo, type WriterIdentity } from '@inkeep/open-knowledge-server';
|
|
12
|
+
import simpleGit from 'simple-git';
|
|
13
|
+
import { buildExecResult, type ExecStructuredResult } from '../src/mcp/tools/exec.ts';
|
|
14
|
+
|
|
15
|
+
const root = resolve(tmpdir(), `ok-exec-probe-${Date.now()}`);
|
|
16
|
+
mkdirSync(root, { recursive: true });
|
|
17
|
+
|
|
18
|
+
async function main(): Promise<void> {
|
|
19
|
+
const git = simpleGit(root);
|
|
20
|
+
await git.init();
|
|
21
|
+
await git.raw('config', 'user.name', 'Probe');
|
|
22
|
+
await git.raw('config', 'user.email', 'probe@t.test');
|
|
23
|
+
writeFileSync(resolve(root, 'README.md'), '# probe\n');
|
|
24
|
+
await git.add('README.md');
|
|
25
|
+
await git.commit('init');
|
|
26
|
+
|
|
27
|
+
const contentDir = resolve(root, 'articles');
|
|
28
|
+
mkdirSync(contentDir, { recursive: true });
|
|
29
|
+
writeFileSync(
|
|
30
|
+
resolve(contentDir, 'auth.md'),
|
|
31
|
+
'---\ntitle: Auth\ndescription: OAuth 2.0 flow\ntags:\n - auth\n - oauth\n---\n\n# Auth\n\nOAuth is...\n',
|
|
32
|
+
);
|
|
33
|
+
writeFileSync(
|
|
34
|
+
resolve(contentDir, 'sso.md'),
|
|
35
|
+
'---\ntitle: SSO\ntags:\n - auth\n---\n\n# SSO\n\noauth provider...\n',
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
// Shadow-repo activity
|
|
39
|
+
const shadow = await initShadowRepo(root);
|
|
40
|
+
const branch = (await simpleGit(root).revparse(['--abbrev-ref', 'HEAD'])).trim();
|
|
41
|
+
const agent: WriterIdentity = { id: 'agent-claude-7x', name: 'Claude (Tim)', email: 'a@ok.test' };
|
|
42
|
+
const human: WriterIdentity = { id: 'human-tim', name: 'Tim Cardona', email: 't@ok.test' };
|
|
43
|
+
await commitWip(shadow, human, contentDir, 'initial auth doc', branch);
|
|
44
|
+
await new Promise((r) => setTimeout(r, 1100));
|
|
45
|
+
writeFileSync(resolve(contentDir, 'auth.md'), '# Auth v2\n\noauth rewrite.\n');
|
|
46
|
+
await commitWip(shadow, agent, contentDir, 'rewrite §3 oauth examples', branch);
|
|
47
|
+
|
|
48
|
+
const deps = { projectDir: root, serverUrl: undefined as string | undefined };
|
|
49
|
+
|
|
50
|
+
const runs: Array<{ label: string; cmd: string }> = [
|
|
51
|
+
{ label: 'cat single file (rich enrichment)', cmd: 'cat articles/auth.md' },
|
|
52
|
+
{ label: 'ls directory (slim enrichment each)', cmd: 'ls articles/' },
|
|
53
|
+
{ label: 'grep | head pipe', cmd: 'grep -rn oauth articles/ | head -5' },
|
|
54
|
+
{ label: 'denied: awk (unknown_command)', cmd: 'awk BEGIN{print}' },
|
|
55
|
+
{ label: 'denied: redirection (write_blocked)', cmd: 'cat articles/auth.md > out.txt' },
|
|
56
|
+
{ label: 'denied: subshell (shell_construct_blocked)', cmd: 'cat `ls`' },
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
for (const { label, cmd } of runs) {
|
|
60
|
+
console.log('\n═══════════════════════════════════════════════════════════');
|
|
61
|
+
console.log(` ${label}`);
|
|
62
|
+
console.log(` exec(${JSON.stringify(cmd)})`);
|
|
63
|
+
console.log('═══════════════════════════════════════════════════════════');
|
|
64
|
+
const result = (await buildExecResult({ command: cmd }, deps)) as {
|
|
65
|
+
content: Array<{ text: string }>;
|
|
66
|
+
structuredContent: ExecStructuredResult;
|
|
67
|
+
isError?: boolean;
|
|
68
|
+
};
|
|
69
|
+
console.log(result.content[0].text.slice(0, 900));
|
|
70
|
+
if (result.structuredContent.error) {
|
|
71
|
+
console.log(`\n [error.category] ${result.structuredContent.error.category}`);
|
|
72
|
+
} else {
|
|
73
|
+
console.log(`\n [enrichedPaths] ${result.structuredContent.enrichedPaths.length} path(s)`);
|
|
74
|
+
for (const m of result.structuredContent.enrichedPaths) {
|
|
75
|
+
if ((m as { type?: string }).type === 'directory') {
|
|
76
|
+
const d = m as {
|
|
77
|
+
path: string;
|
|
78
|
+
recursiveMdCount: number;
|
|
79
|
+
childDirCount: number;
|
|
80
|
+
};
|
|
81
|
+
console.log(
|
|
82
|
+
` - ${d.path}/ (directory): ${d.recursiveMdCount} md, ${d.childDirCount} subdirs`,
|
|
83
|
+
);
|
|
84
|
+
} else {
|
|
85
|
+
const f = m as { path: string; title?: string; historySource: unknown };
|
|
86
|
+
const rich = f.historySource !== null ? ' (rich)' : '';
|
|
87
|
+
console.log(` - ${f.path}${rich}: ${f.title ?? '(no title)'}`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
console.log('\n═══════════════════════════════════════════════════════════');
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
try {
|
|
97
|
+
await main();
|
|
98
|
+
} finally {
|
|
99
|
+
rmSync(root, { recursive: true, force: true });
|
|
100
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* End-to-end probe: buildReadResult flowing through the shared enrichPath
|
|
3
|
+
* → readShadowLog → shadow-repo bare git reads. Confirms agent/human
|
|
4
|
+
* attribution lands in the rendered output (FR15/D12/D13/R3).
|
|
5
|
+
*
|
|
6
|
+
* Run via: `bun run packages/cli/scripts/probe-read-document.ts`
|
|
7
|
+
* Not part of the test suite — a hand-runnable probe.
|
|
8
|
+
*/
|
|
9
|
+
import { mkdirSync, rmSync, writeFileSync } from 'node:fs';
|
|
10
|
+
import { tmpdir } from 'node:os';
|
|
11
|
+
import { resolve } from 'node:path';
|
|
12
|
+
import {
|
|
13
|
+
commitUpstreamImport,
|
|
14
|
+
commitWip,
|
|
15
|
+
initShadowRepo,
|
|
16
|
+
type WriterIdentity,
|
|
17
|
+
} from '@inkeep/open-knowledge-server';
|
|
18
|
+
import simpleGit from 'simple-git';
|
|
19
|
+
import { buildReadResult } from '../src/mcp/tools/read-document.ts';
|
|
20
|
+
|
|
21
|
+
const root = resolve(tmpdir(), `ok-probe-${Date.now()}`);
|
|
22
|
+
mkdirSync(root, { recursive: true });
|
|
23
|
+
|
|
24
|
+
async function main(): Promise<void> {
|
|
25
|
+
// 1. Set up a real git-backed project
|
|
26
|
+
const git = simpleGit(root);
|
|
27
|
+
await git.init();
|
|
28
|
+
await git.raw('config', 'user.name', 'Probe');
|
|
29
|
+
await git.raw('config', 'user.email', 'probe@t.test');
|
|
30
|
+
writeFileSync(resolve(root, 'README.md'), '# probe\n');
|
|
31
|
+
await git.add('README.md');
|
|
32
|
+
await git.commit('init');
|
|
33
|
+
|
|
34
|
+
const contentDir = resolve(root, 'content');
|
|
35
|
+
mkdirSync(contentDir, { recursive: true });
|
|
36
|
+
writeFileSync(
|
|
37
|
+
resolve(contentDir, 'auth.md'),
|
|
38
|
+
`---
|
|
39
|
+
title: Auth
|
|
40
|
+
description: OAuth 2.0 flow for SSO
|
|
41
|
+
tags:
|
|
42
|
+
- auth
|
|
43
|
+
- oauth
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
# Auth
|
|
47
|
+
|
|
48
|
+
OAuth is a protocol that...
|
|
49
|
+
`,
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
// 2. Initialize the shadow repo and record some activity
|
|
53
|
+
const shadow = await initShadowRepo(root);
|
|
54
|
+
const branch = (await simpleGit(root).revparse(['--abbrev-ref', 'HEAD'])).trim();
|
|
55
|
+
|
|
56
|
+
const agent: WriterIdentity = {
|
|
57
|
+
id: 'agent-claude-code-7x',
|
|
58
|
+
name: 'Claude Code (Tim)',
|
|
59
|
+
email: 'agent@ok.test',
|
|
60
|
+
};
|
|
61
|
+
const human: WriterIdentity = {
|
|
62
|
+
id: 'human-tim',
|
|
63
|
+
name: 'Tim Cardona',
|
|
64
|
+
email: 'tim@ok.test',
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
await commitWip(shadow, human, contentDir, 'initial draft of auth doc', branch);
|
|
68
|
+
await new Promise((r) => setTimeout(r, 1100));
|
|
69
|
+
writeFileSync(resolve(contentDir, 'auth.md'), '# Auth v2\n\nRewrote §3 oauth examples.\n');
|
|
70
|
+
await commitWip(shadow, agent, contentDir, 'rewrite §3 oauth examples', branch);
|
|
71
|
+
await new Promise((r) => setTimeout(r, 1100));
|
|
72
|
+
writeFileSync(resolve(contentDir, 'auth.md'), '# Auth v3\n\nFixed typo.\n');
|
|
73
|
+
await commitWip(shadow, human, contentDir, 'typo fix', branch);
|
|
74
|
+
await new Promise((r) => setTimeout(r, 1100));
|
|
75
|
+
// And a fake "upstream" git pull import
|
|
76
|
+
const oldHead = 'a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0';
|
|
77
|
+
const newHead = 'f0e1d2c3b4a5f6e7d8c9b0a1f2e3d4c5b6a7f8e9';
|
|
78
|
+
await commitUpstreamImport(shadow, contentDir, oldHead, newHead, branch);
|
|
79
|
+
|
|
80
|
+
// 3. Call buildReadResult — the actual MCP-tool body, end to end
|
|
81
|
+
const output = await buildReadResult(
|
|
82
|
+
{ path: 'content/auth.md' },
|
|
83
|
+
{
|
|
84
|
+
projectDir: root,
|
|
85
|
+
serverUrl: undefined, // disk-only, no Hocuspocus → backlinkCount stays null
|
|
86
|
+
config: {
|
|
87
|
+
mcp: {
|
|
88
|
+
tools: {
|
|
89
|
+
read_document: { historyDepth: 5 },
|
|
90
|
+
// biome-ignore lint/suspicious/noExplicitAny: probe-only cast; full config not needed
|
|
91
|
+
} as any,
|
|
92
|
+
// biome-ignore lint/suspicious/noExplicitAny: probe-only cast
|
|
93
|
+
} as any,
|
|
94
|
+
// biome-ignore lint/suspicious/noExplicitAny: probe-only cast
|
|
95
|
+
} as any,
|
|
96
|
+
},
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
console.log('═══════════════════════════════════════════════════════════');
|
|
100
|
+
console.log(' end-to-end probe: buildReadResult via shadow-repo');
|
|
101
|
+
console.log('═══════════════════════════════════════════════════════════');
|
|
102
|
+
console.log(output);
|
|
103
|
+
console.log('═══════════════════════════════════════════════════════════');
|
|
104
|
+
console.log(` project root: ${root}`);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
try {
|
|
108
|
+
await main();
|
|
109
|
+
} finally {
|
|
110
|
+
rmSync(root, { recursive: true, force: true });
|
|
111
|
+
}
|
package/dist/colors-CDmFQ1jq.mjs
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{i as e,n as t,o as n,r}from"./colors-CEQEtnHQ.mjs";export{t as dim,r as error,e as info,n as warning};
|
package/dist/colors-CEQEtnHQ.mjs
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import e from"picocolors";const t=t=>e.red(t),n=t=>e.yellow(t),r=t=>e.cyan(t),i=t=>e.gray(t),a=t=>e.bold(t);function o(t,n){return e.isColorSupported?`\u001B]8;;${n}\u0007${t}\u001B]8;;\u0007`:t}export{o as a,r as i,i as n,n as o,t as r,a as t};
|
|
2
|
-
//# sourceMappingURL=colors-CEQEtnHQ.mjs.map
|
package/dist/init-BVuRoDMn.mjs
DELETED
|
@@ -1,230 +0,0 @@
|
|
|
1
|
-
import{n as e,r as t,t as n}from"./constants-XSxfsuBx.mjs";import{O as r}from"./src-CB-sXsMW.mjs";import{existsSync as i,mkdirSync as a,readFileSync as o,realpathSync as s,writeFileSync as c}from"node:fs";import{join as l,resolve as u}from"node:path";const d=`<!-- open-knowledge:begin -->`,f=`<!-- open-knowledge:end -->`,p=/<!-- open-knowledge:begin -->[\s\S]*?<!-- open-knowledge:end -->/,m=`# ${r}/ — Open Knowledge config
|
|
2
|
-
|
|
3
|
-
This directory holds Open Knowledge's configuration for this project. It's **not** where content lives — content lives wherever \`content.dir\` + \`content.include\` in \`config.yml\` point. The default is the repo root with \`**/*.md\`, so any markdown file in the project is fair game. Inspect \`config.yml\` for the actual setting.
|
|
4
|
-
|
|
5
|
-
## What's in here
|
|
6
|
-
|
|
7
|
-
- \`config.yml\` — workspace config (content dir, include/exclude globs, MCP tool settings)
|
|
8
|
-
- \`AGENTS.md\` — this file
|
|
9
|
-
- \`cache/\` — derived data (gitignored)
|
|
10
|
-
|
|
11
|
-
No scaffolded content directories. Organize knowledge wherever makes sense for the project — existing docs trees, topic-grouped subfolders, whatever. \`exec("ls <dir>")\` + per-file enrichment gives you a live overview of any directory on demand; there's no INDEX.md catalog to maintain.
|
|
12
|
-
|
|
13
|
-
## Navigation — prefer \`exec\` for all reads
|
|
14
|
-
|
|
15
|
-
\`exec\` is the primary MCP read surface. It runs a read-only bash command (cat, ls, grep, find, head, tail, wc, sort, uniq, cut — pipes OK) and returns raw stdout plus enriched metadata per file: title, description, tags, backlink count, recent shadow-repo activity with agent-vs-human attribution, and project git history.
|
|
16
|
-
|
|
17
|
-
Examples (adapt paths to this project's layout):
|
|
18
|
-
|
|
19
|
-
- Read a file: \`exec("cat <path>.md")\` — contents + full rich enrichment
|
|
20
|
-
- List a directory: \`exec("ls <dir>")\` — names + slim per-file enrichment
|
|
21
|
-
- Search: \`exec("grep -rn <term> <dir> | head -5")\` — matches + enrichment on matched files
|
|
22
|
-
|
|
23
|
-
Typed tools (\`read_document\`, \`search\`, \`list_documents\`, etc.) remain available as "Typed call sites (advanced)" — use them when you need the typed \`structuredContent\` shape for programmatic parsing.
|
|
24
|
-
|
|
25
|
-
## Suggested lifecycle (optional pattern)
|
|
26
|
-
|
|
27
|
-
Projects that want an explicit knowledge-maturation flow can organize as three tiers **relative to the content directory** — create the subfolders only when you need them:
|
|
28
|
-
|
|
29
|
-
1. **External sources** (e.g., \`external-sources/\` under \`content.dir\`) — raw content fetched from URLs, PDFs. No analysis, just preservation. Use the \`ingest\` MCP tool.
|
|
30
|
-
2. **Research** (e.g., \`research/\` under \`content.dir\`) — analysis and synthesis. Provisional findings, trade-offs, open questions. Use the \`research\` MCP tool.
|
|
31
|
-
3. **Articles** (e.g., \`articles/\` under \`content.dir\`) — canonical knowledge. Use the \`consolidate\` MCP tool to promote research → articles once decisions are made.
|
|
32
|
-
|
|
33
|
-
This is a pattern, not a requirement. Projects with existing layouts (\`specs/\`, \`reports/\`, \`docs/\`, etc.) should use those; the lifecycle exists as mental scaffolding, not as enforced filesystem structure.
|
|
34
|
-
|
|
35
|
-
## Linking — use \`[[wiki-links]]\` aggressively
|
|
36
|
-
|
|
37
|
-
**When writing or editing any document, link liberally to every other document it relates to.** Open Knowledge's value compounds with link density: backlinks surface cross-document context in every read, graph queries (\`get_hubs\` / \`get_orphans\`) reveal structure, and agents navigate the knowledge base by following links. A document with no outbound links is an island.
|
|
38
|
-
|
|
39
|
-
**Defaults when writing:**
|
|
40
|
-
|
|
41
|
-
- Every noun-phrase that names another document is a link. Write \`[[Page Title]]\` instead of plain prose when mentioning concepts, projects, decisions, or entities that have (or should have) their own page. Redlinks are fine — they signal "this should exist."
|
|
42
|
-
- Cross-link siblings: when creating a document in a folder, link to the 2–3 most related neighbors.
|
|
43
|
-
- Link back to sources instead of re-summarizing — the reader can follow.
|
|
44
|
-
- Prefer \`[[Page]]\` over Markdown \`[text](./page.md)\`. Wiki-links resolve by docName and participate in the backlinks index; Markdown links to other wiki files don't.
|
|
45
|
-
|
|
46
|
-
**Rule of thumb:** if a human reader would want to click a term to learn more, make it a link. Err on the side of too many links.
|
|
47
|
-
|
|
48
|
-
## Frontmatter Conventions
|
|
49
|
-
|
|
50
|
-
Open Knowledge has two metadata surfaces that merge at read time:
|
|
51
|
-
|
|
52
|
-
**Per-file frontmatter.** Every \`.md\` file that's part of the knowledge base should have YAML frontmatter:
|
|
53
|
-
|
|
54
|
-
\`\`\`yaml
|
|
55
|
-
---
|
|
56
|
-
title: Article Title (required)
|
|
57
|
-
description: Brief summary (required)
|
|
58
|
-
tags:
|
|
59
|
-
- relevant
|
|
60
|
-
- tags
|
|
61
|
-
---
|
|
62
|
-
\`\`\`
|
|
63
|
-
|
|
64
|
-
**Folder-level defaults via \`config.yml\` \`folders:\`.** Declare per-folder title/description/tags keyed by glob \`match:\` — see \`config.yml\` for the commented example. Rules apply in declaration order (later matches override earlier scalars), tags concat + dedup across all matching rules, and the file's own frontmatter always wins per-scalar. Folder defaults fill in blanks.
|
|
65
|
-
|
|
66
|
-
Folder metadata lives in \`config.yml\`, **not** in content files — this is intentionally different from the rejected \`INDEX.md\`-inside-content pattern. The merge is computed on every \`exec("ls <dir>")\` / \`read_document\` / \`search\` call and is never written back to disk.
|
|
67
|
-
|
|
68
|
-
## Scaffolding (first-time setup)
|
|
69
|
-
|
|
70
|
-
This directory was scaffolded by running \`open-knowledge init\` (or \`npx @inkeep/open-knowledge init\`) in the project root. That command:
|
|
71
|
-
|
|
72
|
-
1. Creates \`${r}/\` (config-only — no content subdirs)
|
|
73
|
-
2. Writes \`AGENTS.md\`, \`.gitignore\`, and \`config.yml\`
|
|
74
|
-
3. Registers the Open Knowledge MCP server in your selected editors' MCP config files (user-scoped by default)
|
|
75
|
-
|
|
76
|
-
If you're onboarding a new project and \`${r}/\` doesn't exist yet, run \`open-knowledge init\` from a terminal.
|
|
77
|
-
|
|
78
|
-
## Tools
|
|
79
|
-
|
|
80
|
-
- **\`exec\`** — primary read surface (cat / ls / grep / find / pipes) with enriched output
|
|
81
|
-
- **\`init-content\`** — bootstrap this knowledge base from the codebase
|
|
82
|
-
- **\`ingest\`** — capture an external source as raw reference material
|
|
83
|
-
- **\`research\`** — gather sources + write provisional findings
|
|
84
|
-
- **\`consolidate\`** — promote research into canonical articles
|
|
85
|
-
- **Writes** via \`write_document\` / \`edit_document\` — route through the server so shadow-repo attribution (agent vs human) is captured
|
|
86
|
-
- **Graph queries** via \`get_backlinks\`, \`get_forward_links\`, \`get_orphans\`, \`get_hubs\`
|
|
87
|
-
|
|
88
|
-
These tools are discovered via the standard MCP \`tools/list\` handshake and work in any MCP client (Claude Code, Claude Desktop, Cursor, VS Code, Windsurf, Codex, etc.). \`open-knowledge init\` registers a single global \`open-knowledge\` entry in each selected editor's config, and the server resolves the active project per tool call from explicit \`cwd\` values or client-reported workspace roots. Claude Desktop requires a full quit + relaunch to pick up new MCP servers; other clients may need a new session or editor restart if they are already open.
|
|
89
|
-
`,h=`# Open Knowledge — workspace configuration
|
|
90
|
-
#
|
|
91
|
-
# This file overrides built-in defaults for this workspace. Every key below
|
|
92
|
-
# is commented out and shows its current default value. Uncomment any key
|
|
93
|
-
# to override it.
|
|
94
|
-
#
|
|
95
|
-
# Precedence (lowest -> highest):
|
|
96
|
-
# Built-in defaults
|
|
97
|
-
# -> ~/${r}/config.yml (user defaults)
|
|
98
|
-
# -> ./${r}/config.yml (this file)
|
|
99
|
-
#
|
|
100
|
-
# Schema reference: packages/cli/src/config/schema.ts
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
# --- Content ---------------------------------------------------------------
|
|
104
|
-
# dir: where the CRDT editor reads/writes documents. Relative to the project
|
|
105
|
-
# root (the directory containing ${r}/), NOT to this file.
|
|
106
|
-
#
|
|
107
|
-
# include/exclude: glob patterns for tracked content files. Relative to the
|
|
108
|
-
# content directory (content.dir).
|
|
109
|
-
#
|
|
110
|
-
# content:
|
|
111
|
-
# dir: .
|
|
112
|
-
# include:
|
|
113
|
-
# - "**/*.md"
|
|
114
|
-
# exclude: []
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
# --- Server ----------------------------------------------------------------
|
|
118
|
-
# HTTP/WebSocket listener for the Hocuspocus server + static React app.
|
|
119
|
-
#
|
|
120
|
-
# openOnAgentEdit: when true, the browser opens automatically the first time
|
|
121
|
-
# an agent writes to the knowledge base in this server session. Debounced to
|
|
122
|
-
# one open per boot. Useful for pairing with Claude Code — you see the edit
|
|
123
|
-
# land live. Leave false for headless/CI.
|
|
124
|
-
#
|
|
125
|
-
# server:
|
|
126
|
-
# port: 3000
|
|
127
|
-
# host: localhost
|
|
128
|
-
# openOnAgentEdit: false
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
# --- Persistence -----------------------------------------------------------
|
|
132
|
-
# How aggressively CRDT updates are flushed to disk.
|
|
133
|
-
# persistence:
|
|
134
|
-
# debounceMs: 2000
|
|
135
|
-
# maxDebounceMs: 10000
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
# --- Folders: per-folder frontmatter defaults -------------------------------
|
|
139
|
-
#
|
|
140
|
-
# TL;DR glob gotcha: \`**\` is ONLY a multi-segment wildcard when it is a
|
|
141
|
-
# standalone path segment. \`foo-**\` behaves like \`foo-*\` (single segment, NOT
|
|
142
|
-
# descendants). Use \`foo/**\` or \`foo-*/**\` to match descendants.
|
|
143
|
-
#
|
|
144
|
-
# Declare title/description/tags defaults keyed by glob \`match:\`. Rules merge
|
|
145
|
-
# with a file's own frontmatter at read time:
|
|
146
|
-
# - Per scalar (title, description): the FILE wins when declared; folder
|
|
147
|
-
# rule fills in blanks.
|
|
148
|
-
# - Tags: concatenated across ALL matching rules (in declaration order) with
|
|
149
|
-
# file tags last; first-occurrence preserved on dedup.
|
|
150
|
-
# - Evaluation is positional — LATER rules in this array override earlier
|
|
151
|
-
# rules for scalars. Put general rules first, specific rules last.
|
|
152
|
-
#
|
|
153
|
-
# Picomatch glob cheatsheet:
|
|
154
|
-
# \`match: 'foo'\` — matches ONLY the folder \`foo\` itself
|
|
155
|
-
# \`match: 'foo/**'\` — matches \`foo\` AND all descendants (files + dirs)
|
|
156
|
-
# \`match: 'foo-*'\` — matches \`foo-1\`, \`foo-bar\` (single segment)
|
|
157
|
-
# \`match: 'foo-**'\` — behaves like \`foo-*\` (NOT multi-segment); see
|
|
158
|
-
# TL;DR above. Use \`foo-*/**\` if you want
|
|
159
|
-
# \`foo-X\` plus its descendants.
|
|
160
|
-
#
|
|
161
|
-
# Example:
|
|
162
|
-
# folders:
|
|
163
|
-
# - match: 'specs/**'
|
|
164
|
-
# frontmatter:
|
|
165
|
-
# title: Specifications
|
|
166
|
-
# description: Feature specifications and design documents
|
|
167
|
-
# tags: [spec]
|
|
168
|
-
# - match: 'specs/2026-*/**'
|
|
169
|
-
# frontmatter:
|
|
170
|
-
# title: 2026 Specifications
|
|
171
|
-
# tags: [2026]
|
|
172
|
-
`,g="**Preview before edit (REQUIRED).** You MUST follow this sequence every time you call `write_document` or `edit_document`:\n1. Call `get_preview_url` to obtain the browser URL for the target doc.\n - If it returns `null`, the UI isn't running yet. Start it with `open-knowledge ui` from a terminal — or in Claude Code, call `preview_start(\"open-knowledge-ui\")` (the `open-knowledge init` command scaffolds this `.claude/launch.json` entry, so it's ready to go). `open-knowledge ui` auto-spawns the collab server in the background; you don't need to run `open-knowledge start` separately. Then call `get_preview_url` again — the UI writes a lock file that this tool reads.\n - NEVER guess or manually construct the preview URL — always use the URL returned by `get_preview_url`.\n2. Open that URL in your preview browser so the user can see the document.\n3. Only then call `write_document` / `edit_document` — the CRDT edit will stream live into the already-open editor.\n\nNEVER call `write_document` or `edit_document` without first navigating the preview browser to the doc. The user expects to watch every edit land in real time. Write-tool responses include `previewUrl` (when resolvable) and a `warning` when no client is currently attached to the doc.",_=`${d}
|
|
173
|
-
## Open Knowledge
|
|
174
|
-
|
|
175
|
-
This repo uses Open Knowledge — collaborative markdown via MCP. **\`${r}/config.yml\`** (with optional \`~/${r}/config.yml\`; CLI/env may override) is the **path contract**: \`content.dir\` is the root for relative paths; \`content.include\` lists globs that **add** markdown; \`content.exclude\` lists globs that **remove** paths. Nothing else defines scope — not folder names, not "docs vs code." \`.gitignore\` still applies. When MCP is connected, the server's instructions echo the **resolved** \`dir\` / \`include\` / \`exclude\` for this session — treat that table and the YAML as two views of the same rules.
|
|
176
|
-
|
|
177
|
-
**Default mental model (no jargon):** unless this project narrowed \`content.include\`, **every \`.md\` and \`.mdx\` under \`content.dir\`** is an Open Knowledge document — including under \`specs/\`, \`reports/\`, \`docs/\`, etc. If \`content.include\` is non-default, read \`config.yml\` once per turn so you do not mis-classify paths.
|
|
178
|
-
|
|
179
|
-
**STOP — your host's built-in file tools on in-scope \`.md\` / \`.mdx\`.** When this workspace has Open Knowledge MCP configured (for example via a user-scoped editor config such as \`~/.claude.json\` or \`~/.cursor/mcp.json\`), you **must not** reach for native tools on in-scope markdown. Same failure mode as native \`Edit\` on them: no frontmatter, no backlinks, no shadow-repo activity, no recent-edit signal. The ban is broader than just \`Read\` / \`Grep\` / \`Glob\` — it names every common rationalization:
|
|
180
|
-
|
|
181
|
-
- **Native \`Read\` / \`Grep\` / \`Glob\` on in-scope \`.md\` / \`.mdx\`** — the original case.
|
|
182
|
-
- **\`Bash ls\` / \`Bash find\` / \`Bash cat\` on dirs containing in-scope markdown** — use \`exec("ls …")\` / \`exec("find … -name '*.md'")\` / \`exec("cat …")\` instead. Native returns bare names; \`exec\` returns frontmatter, backlink counts, and recent-activity per child.
|
|
183
|
-
- **Glob patterns that target markdown** (\`**/*.md\`, \`**/SPEC.md\`, or any dir known to be markdown-heavy like \`specs/**\`, \`reports/**\`, \`stories/**\`, \`projects/**\`, \`docs/**\`) — use \`exec\` with \`find\`, or \`list_documents({ dir })\`.
|
|
184
|
-
- **Dispatching the Explore / general-purpose subagent for markdown-heavy exploration** — subagents use native \`Read\` / \`Grep\` / \`Glob\` internally and bypass Open Knowledge entirely. Do markdown exploration yourself via \`exec\` / \`search\`. Subagents remain appropriate for **source-code** exploration (\`.ts\`, \`.py\`, configs, etc.).
|
|
185
|
-
|
|
186
|
-
**MCP wiring varies by client:** Claude Code, Codex, Cursor, Windsurf, VS Code–class clients, and others surface MCP differently — server labels are user-defined; tools may not appear as a top-level symbol named \`exec\`. **If Open Knowledge is registered**, route markdown reads through its \`exec\` / \`search\` / \`read_document\` tools using **your client's documented MCP invocation** (including any generic "call MCP tool" flow). **That counts as available.** Not seeing \`exec\` in a flat tool list is **not** the escape hatch.
|
|
187
|
-
|
|
188
|
-
**Escape hatch (narrow).** Native \`Read\` / \`Grep\` / \`Glob\` on \`.md\` / \`.mdx\` is allowed **only** when no Open Knowledge MCP server is registered for this project, **or** immediately after you **tried** an MCP call and it failed — then start a user-visible sentence with \`Open Knowledge MCP unavailable:\`. Never use the hatch because you skipped your client's MCP path.
|
|
189
|
-
|
|
190
|
-
**Reads and searches on markdown:** Open Knowledge \`exec\` (or \`read_document\` / \`search\`) — same payloads whether your client invokes them directly or through MCP. Examples: \`exec("cat docs/auth.md")\`, \`exec("ls reports/")\`, \`exec("grep -rn karpathy specs/ | head -10")\`.
|
|
191
|
-
|
|
192
|
-
**Listings too.** \`exec("ls <dir>/")\` is how you list a directory — it returns per-child frontmatter, recursive markdown counts, and the most-recently-updated doc per subdir. Plain \`Bash ls\` returns just names.
|
|
193
|
-
|
|
194
|
-
**Anti-patterns at a glance:**
|
|
195
|
-
|
|
196
|
-
| Task | Don't | Do |
|
|
197
|
-
| -------------------------------- | ---------------------------- | ----------------------------------------------- |
|
|
198
|
-
| List a markdown-heavy dir | \`Bash: ls specs/\` | \`exec("ls specs/")\` |
|
|
199
|
-
| Find all SPEC.md files | \`Glob: **/SPEC.md\` | \`exec("find specs -name SPEC.md")\` |
|
|
200
|
-
| Summarize specs across the repo | \`Agent(Explore): "…"\` | \`exec("head -25 specs/*/SPEC.md")\` + \`search\` |
|
|
201
|
-
| Search a phrase across markdown | \`Grep: "pattern" *.md\` | \`search({ query: "pattern" })\` |
|
|
202
|
-
| Read an individual spec | \`Read: specs/foo/SPEC.md\` | \`read_document({ path: "specs/foo/SPEC.md" })\` |
|
|
203
|
-
|
|
204
|
-
**Source code and everything else** (\`.ts\`, \`.py\`, \`package.json\`, …): native \`Read\` / \`Grep\` / \`Glob\`.
|
|
205
|
-
|
|
206
|
-
**Writing.** Edits to in-scope \`.md\` / \`.mdx\` go through \`write_document\` / \`edit_document\` only. Native \`Edit\` / \`sed\` land as anonymous \`upstream\` imports — you lose agent attribution in the shadow repo.
|
|
207
|
-
|
|
208
|
-
${g}
|
|
209
|
-
|
|
210
|
-
**No screenshots after edits.** Do NOT take \`preview_screenshot\` after every \`edit_document\` / \`write_document\`. Trust the CRDT tool response as confirmation the edit landed. Only screenshot when debugging a visual issue or when explicitly asked.
|
|
211
|
-
|
|
212
|
-
**Linking.** Link liberally with \`[[wiki-links]]\` — every noun-phrase naming another document should be a link; redlinks are fine and signal "this should exist." Backlink density is how this knowledge base stays navigable for the next agent.
|
|
213
|
-
|
|
214
|
-
- **What goes in the brackets:** the target's **docName** — folder path + filename without \`.md\` / \`.mdx\` (e.g. the file \`guides/auth-setup.md\` is linked as \`[[guides/auth-setup]]\`). NOT the human-readable title, NOT \`title:\` frontmatter, NOT \`aliases:\`. Wiki-links are absolute from the content root, never relative — \`[[foo]]\` means root-level \`foo.md\`, never \`guides/foo.md\` from inside \`guides/\`. Cross-folder links always need the full path.
|
|
215
|
-
- **Display text different from the key:** \`[[guides/auth-setup|Auth Setup]]\` — pipe separator, target on the left, rendered label on the right. Anchors work the same way: \`[[guides/auth-setup#quickstart]]\`, or combined: \`[[guides/auth-setup#quickstart|see the quickstart]]\`.
|
|
216
|
-
- **Verify before walking away:** after writing a doc, call \`get_dead_links({ sourceDocNames: ['your/doc/name'] })\` — every unresolved bracket-target in that doc is listed. Fix or accept the redlinks deliberately. The editor's red-underline visual tolerates a slug fallback (\`[[Auth Setup]]\` may look resolved if \`auth-setup.md\` exists at root), but the backlink graph is strict-exact — trust \`get_dead_links\`, not the visual.
|
|
217
|
-
|
|
218
|
-
**Organize by folders, not hub files.** Folders are the organizational unit — group related docs in a shared folder and let the directory listing do the cataloging. Per-folder metadata (title, description, tags) lives in \`.open-knowledge/config.yml\` under the \`folders:\` key (glob \`match:\` + frontmatter defaults that merge with each file's own frontmatter at read time). Don't maintain an \`INDEX.md\` / \`README.md\` hub file inside a folder solely to catalog its children — \`exec("ls <folder>")\` returns the same view live, with per-file frontmatter + backlink counts.
|
|
219
|
-
|
|
220
|
-
**Server must be running.** If \`write_document\` or \`edit_document\` returns a "Hocuspocus server is not running" error, start it with \`open-knowledge start\` (via Bash) and retry. NEVER fall back to native \`Edit\` / \`Write\` for in-scope markdown — always use the MCP write tools so edits go through the CRDT layer with proper attribution.
|
|
221
|
-
|
|
222
|
-
**Non-markdown files.** Use native \`Read\` / \`Edit\` / \`Grep\` / \`Bash\` for source code, configs, and anything outside the path contract in \`config.yml\`: under \`content.dir\`, matching \`content.include\`, not removed by \`content.exclude\` or \`.gitignore\`.
|
|
223
|
-
${f}`;function v(e,t,r){let a=[n,...r??[]],u=new Set,d=[];for(let n of a){let r=l(e,n),a=i(r),f=a?s(r):r;if(u.has(f)){d.push({file:n,path:r,action:`skipped-symlink`});continue}if(u.add(f),!a){c(r,`${_}\n`,`utf-8`),d.push({file:n,path:r,action:`created`});continue}let m=o(r,`utf-8`),h=p.test(m);if(h&&!t){d.push({file:n,path:r,action:`skipped-existing`});continue}if(h){c(r,m.replace(p,_),`utf-8`),d.push({file:n,path:r,action:`replaced`});continue}c(r,`${m.replace(/\n*$/,``)}\n\n${_}\n`,`utf-8`),d.push({file:n,path:r,action:`appended`})}return d}function y(e,t){return i(e)?!1:(c(e,t,`utf-8`),!0)}function b(e){let t=l(e,`.gitignore`),n=`# Open Knowledge — local editor config (not tracked upstream)\n${r}/\n`;if(!i(t))return c(t,n,`utf-8`),`created`;let a=o(t,`utf-8`),s=new Set([r,`${r}/`,`/${r}`,`/${r}/`]);return a.split(`
|
|
224
|
-
`).map(e=>e.trim()).some(e=>s.has(e))?`already-present`:(c(t,`${a}${a.length===0||a.endsWith(`
|
|
225
|
-
`)?``:`
|
|
226
|
-
`}${a.length>0&&!a.endsWith(`
|
|
227
|
-
|
|
228
|
-
`)?`
|
|
229
|
-
`:``}${n}`,`utf-8`),`appended`)}const x=[{name:n,content:m},{name:`.gitignore`,content:`${e}/\nserver.lock\nui.lock\nsync-state.json\n`},{name:t,content:h}];function S(t){let n=u(t,r),i=[],o=[];a(n,{recursive:!0}),a(l(n,e),{recursive:!0});for(let e of x)y(l(n,e.name),e.content)?i.push(e.name):o.push(e.name);return{created:i,skipped:o}}export{b as a,g as i,d as n,S as o,f as r,v as s,_ as t};
|
|
230
|
-
//# sourceMappingURL=init-BVuRoDMn.mjs.map
|
package/dist/init-CJTyH3QE.mjs
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{a as e,i as t,n,o as r,r as i,s as a,t as o}from"./init-BVuRoDMn.mjs";export{o as CLAUDE_MD_SECTION,n as OK_MARKER_BEGIN,i as OK_MARKER_END,t as PREVIEW_GUIDANCE,e as ensureOkGitignoredAtRoot,r as initContent,a as upsertRootInstructions};
|