@vpxa/aikit 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (140) hide show
  1. package/README.md +8 -8
  2. package/package.json +1 -1
  3. package/packages/aikit-client/dist/types.d.ts +3 -3
  4. package/packages/cli/dist/aikit-init.d.ts +1 -1
  5. package/packages/cli/dist/aikit-init.js +1 -1
  6. package/packages/cli/dist/commands/context-cmds.js +1 -1
  7. package/packages/cli/dist/commands/environment.js +1 -1
  8. package/packages/cli/dist/commands/init/config.d.ts +1 -1
  9. package/packages/cli/dist/commands/init/config.js +2 -2
  10. package/packages/cli/dist/commands/init/constants.d.ts +3 -3
  11. package/packages/cli/dist/commands/init/index.d.ts +4 -4
  12. package/packages/cli/dist/commands/init/index.js +4 -4
  13. package/packages/cli/dist/commands/init/templates.js +12 -12
  14. package/packages/cli/dist/commands/init/user.d.ts +3 -3
  15. package/packages/cli/dist/commands/init/user.js +2 -2
  16. package/packages/cli/dist/commands/search.js +1 -1
  17. package/packages/cli/dist/commands/system.js +4 -4
  18. package/packages/cli/dist/commands/upgrade.js +1 -1
  19. package/packages/cli/dist/commands/workspace.js +1 -1
  20. package/packages/cli/dist/helpers.js +1 -1
  21. package/packages/cli/dist/index.d.ts +1 -1
  22. package/packages/core/dist/constants.d.ts +3 -3
  23. package/packages/core/dist/global-registry.d.ts +7 -1
  24. package/packages/core/dist/global-registry.js +1 -1
  25. package/packages/core/dist/index.d.ts +2 -2
  26. package/packages/core/dist/index.js +1 -1
  27. package/packages/core/dist/types.d.ts +4 -2
  28. package/packages/dashboard/dist/assets/{index-BjA4YODs.js → index-CO2S9BKY.js} +2 -2
  29. package/packages/dashboard/dist/assets/index-CO2S9BKY.js.map +1 -0
  30. package/packages/dashboard/dist/index.html +2 -2
  31. package/packages/enterprise-bridge/dist/er-client.d.ts +1 -1
  32. package/packages/indexer/dist/incremental-indexer.js +1 -1
  33. package/packages/kb-client/dist/__tests__/direct-client.test.d.ts +1 -0
  34. package/packages/kb-client/dist/__tests__/mcp-client.test.d.ts +1 -0
  35. package/packages/kb-client/dist/__tests__/parsers.test.d.ts +1 -0
  36. package/packages/kb-client/dist/direct-client.d.ts +38 -0
  37. package/packages/kb-client/dist/direct-client.js +1 -0
  38. package/packages/kb-client/dist/index.d.ts +4 -0
  39. package/packages/kb-client/dist/index.js +1 -0
  40. package/packages/kb-client/dist/mcp-client.d.ts +19 -0
  41. package/packages/kb-client/dist/mcp-client.js +4 -0
  42. package/packages/kb-client/dist/parsers.d.ts +32 -0
  43. package/packages/kb-client/dist/parsers.js +2 -0
  44. package/packages/kb-client/dist/types.d.ts +59 -0
  45. package/packages/kb-client/dist/types.js +1 -0
  46. package/packages/present/dist/index.html +3 -3
  47. package/packages/server/dist/background-task.d.ts +47 -0
  48. package/packages/server/dist/background-task.js +1 -0
  49. package/packages/server/dist/config.js +1 -1
  50. package/packages/server/dist/idle-timer.d.ts +29 -0
  51. package/packages/server/dist/idle-timer.js +1 -0
  52. package/packages/server/dist/index.js +1 -1
  53. package/packages/server/dist/memory-monitor.d.ts +37 -0
  54. package/packages/server/dist/memory-monitor.js +1 -0
  55. package/packages/server/dist/prompts.js +5 -5
  56. package/packages/server/dist/resource-links.d.ts +1 -1
  57. package/packages/server/dist/resource-links.js +1 -1
  58. package/packages/server/dist/resources/curated-resources.d.ts +2 -2
  59. package/packages/server/dist/resources/curated-resources.js +2 -2
  60. package/packages/server/dist/resources/resource-notifier.d.ts +1 -1
  61. package/packages/server/dist/resources/resource-notifier.js +1 -1
  62. package/packages/server/dist/resources/resources.js +1 -1
  63. package/packages/server/dist/server.d.ts +3 -1
  64. package/packages/server/dist/server.js +3 -3
  65. package/packages/server/dist/tool-metadata.d.ts +1 -1
  66. package/packages/server/dist/tool-metadata.js +1 -1
  67. package/packages/server/dist/tool-timeout.d.ts +27 -0
  68. package/packages/server/dist/tool-timeout.js +1 -0
  69. package/packages/server/dist/tools/bridge.tools.d.ts +1 -1
  70. package/packages/server/dist/tools/bridge.tools.js +3 -3
  71. package/packages/server/dist/tools/evolution.tools.js +1 -1
  72. package/packages/server/dist/tools/infra.tools.js +1 -1
  73. package/packages/server/dist/tools/onboard.tool.js +1 -1
  74. package/packages/server/dist/tools/present/browser.js +4 -4
  75. package/packages/server/dist/tools/present/tool.js +1 -1
  76. package/packages/server/dist/tools/reindex.tool.js +1 -1
  77. package/packages/server/dist/tools/search.tool.js +1 -1
  78. package/packages/server/dist/tools/status.tool.d.ts +1 -1
  79. package/packages/server/dist/tools/status.tool.js +2 -2
  80. package/packages/tools/dist/checkpoint.js +1 -1
  81. package/packages/tools/dist/config-extractor.js +1 -1
  82. package/packages/tools/dist/evidence-map.js +2 -2
  83. package/packages/tools/dist/find.d.ts +1 -1
  84. package/packages/tools/dist/forge-ground.d.ts +1 -1
  85. package/packages/tools/dist/guide.js +1 -1
  86. package/packages/tools/dist/lane.js +1 -1
  87. package/packages/tools/dist/onboard.d.ts +2 -2
  88. package/packages/tools/dist/onboard.js +2 -2
  89. package/packages/tools/dist/queue.js +1 -1
  90. package/packages/tools/dist/replay.js +1 -1
  91. package/packages/tools/dist/response-envelope.d.ts +1 -1
  92. package/packages/tools/dist/snippet.js +1 -1
  93. package/packages/tools/dist/stash.js +1 -1
  94. package/packages/tools/dist/synthesis-engine.js +2 -2
  95. package/packages/tools/dist/workset.js +1 -1
  96. package/packages/tui/dist/{App-DU2KEylW.js → App-B2-KJPt4.js} +1 -1
  97. package/packages/tui/dist/App.d.ts +1 -1
  98. package/packages/tui/dist/App.js +1 -1
  99. package/packages/tui/dist/LogPanel-E_1Do4-j.js +3 -0
  100. package/packages/tui/dist/hooks/useKBClient.d.ts +1 -1
  101. package/packages/tui/dist/{index-BXafekwr.d.ts → index-MXJeXmCf.d.ts} +3 -3
  102. package/packages/tui/dist/index.d.ts +1 -1
  103. package/packages/tui/dist/index.js +1 -1
  104. package/packages/tui/dist/panels/LogPanel.js +1 -1
  105. package/scaffold/README.md +192 -192
  106. package/scaffold/definitions/bodies.mjs +16 -16
  107. package/scaffold/definitions/plugins.mjs +1 -1
  108. package/scaffold/definitions/prompts.mjs +6 -6
  109. package/scaffold/definitions/protocols.mjs +12 -12
  110. package/scaffold/definitions/tools.mjs +1 -1
  111. package/scaffold/flows/aikit-advanced/skills/execute/SKILL.md +124 -124
  112. package/scaffold/flows/aikit-advanced/skills/plan/SKILL.md +100 -100
  113. package/scaffold/flows/aikit-advanced/skills/spec/SKILL.md +100 -100
  114. package/scaffold/flows/aikit-advanced/skills/task/SKILL.md +99 -99
  115. package/scaffold/flows/aikit-advanced/skills/verify/SKILL.md +122 -122
  116. package/scaffold/flows/aikit-basic/skills/assess/SKILL.md +82 -82
  117. package/scaffold/flows/aikit-basic/skills/implement/SKILL.md +105 -105
  118. package/scaffold/flows/aikit-basic/skills/verify/SKILL.md +96 -96
  119. package/scaffold/general/agents/Debugger.agent.md +2 -2
  120. package/scaffold/general/agents/Documenter.agent.md +2 -2
  121. package/scaffold/general/agents/Explorer.agent.md +2 -2
  122. package/scaffold/general/agents/Frontend.agent.md +1 -1
  123. package/scaffold/general/agents/Implementer.agent.md +2 -2
  124. package/scaffold/general/agents/Orchestrator.agent.md +1 -1
  125. package/scaffold/general/agents/Planner.agent.md +2 -2
  126. package/scaffold/general/agents/Refactor.agent.md +2 -2
  127. package/scaffold/general/agents/Security.agent.md +2 -2
  128. package/scaffold/general/agents/_shared/architect-reviewer-base.md +1 -1
  129. package/scaffold/general/agents/_shared/code-agent-base.md +6 -6
  130. package/scaffold/general/agents/_shared/code-reviewer-base.md +1 -1
  131. package/scaffold/general/agents/_shared/forge-protocol.md +1 -1
  132. package/scaffold/general/agents/_shared/researcher-base.md +3 -3
  133. package/scaffold/general/prompts/ask.prompt.md +4 -4
  134. package/scaffold/general/prompts/debug.prompt.md +1 -1
  135. package/scaffold/general/prompts/plan.prompt.md +1 -1
  136. package/scaffold/general/skills/aikit/SKILL.md +5 -5
  137. package/scaffold/general/skills/multi-agents-development/SKILL.md +2 -2
  138. package/scaffold/general/skills/present/SKILL.md +1 -1
  139. package/packages/dashboard/dist/assets/index-BjA4YODs.js.map +0 -1
  140. package/packages/tui/dist/LogPanel-Bo8a8QXB.js +0 -3
@@ -3,13 +3,13 @@
3
3
  <head>
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
- <title>KB Dashboard</title>
6
+ <title>AI Kit Dashboard</title>
7
7
  <style>
8
8
  * { margin: 0; padding: 0; box-sizing: border-box; }
9
9
  html, body, #root { width: 100%; height: 100%; overflow: hidden; }
10
10
  body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #0d1117; color: #c9d1d9; }
11
11
  </style>
12
- <script type="module" crossorigin src="/_dashboard/assets/index-BjA4YODs.js"></script>
12
+ <script type="module" crossorigin src="/_dashboard/assets/index-CO2S9BKY.js"></script>
13
13
  <link rel="stylesheet" crossorigin href="/_dashboard/assets/index-CHpVij2M.css">
14
14
  </head>
15
15
  <body>
@@ -12,7 +12,7 @@ declare class ERClient {
12
12
  private static readonly FAILURE_THRESHOLD;
13
13
  private static readonly RESET_TIMEOUT_MS;
14
14
  constructor(config: ERBridgeConfig);
15
- /** Search ER knowledge base */
15
+ /** Search ER AI Kit */
16
16
  search(query: string, maxResults?: number): Promise<ERSearchResult[]>;
17
17
  /** Push curated knowledge to ER via MCP tools endpoint */
18
18
  push(request: ERPushRequest): Promise<ERPushResponse>;
@@ -1 +1 @@
1
- import{generateRecordId as e,hashContent as t}from"./file-hasher.js";import{FilesystemCrawler as n}from"./filesystem-crawler.js";import{extractGraph as r}from"./graph-extractor.js";import{AIKIT_PATHS as i,createLogger as a,detectContentType as o,serializeError as s}from"../../core/dist/index.js";import{availableParallelism as c,loadavg as l}from"node:os";import{createChunkerSync as u}from"../../chunker/dist/index.js";const d=a(`indexer`);async function f(e,t,n,r){let i=0;async function a(){for(;i<e.length;){let n=i++;try{await t(e[n])}catch(t){r?.(e[n],t)}}}await Promise.all(Array.from({length:Math.min(n,e.length)},()=>a()))}function p(e){let t=c(),n=l()[0]/t;return n>1.5?2:n>1?Math.max(2,Math.floor(e/2)):e}const m=Math.max(2,Math.min(4,Math.floor(c()*.5)));var h=class{crawler;indexing=!1;graphStore;hashCache;get isIndexing(){return this.indexing}constructor(e,t){this.embedder=e,this.store=t,this.crawler=new n}setGraphStore(e){this.graphStore=e}setHashCache(e){this.hashCache=e}async index(e,t){if(this.indexing)throw Error(`Indexing is already in progress`);this.indexing=!0;try{return await this.doIndex(e,t,{})}finally{this.indexing=!1}}async doIndex(n,a,c={}){let l=Date.now(),h=0,g=0,_=0,v=0,y=n.indexing.concurrency??m;a?.({phase:`crawling`,filesTotal:0,filesProcessed:0,chunksTotal:0,chunksProcessed:0});let b=(await Promise.all(n.sources.map(e=>this.crawler.crawl({rootDir:e.path,excludePatterns:e.excludePatterns})))).flat(),x,S;if(c.skipHashCheck)x=b,S=[];else{let e=await this.store.listSourcePaths(),n=new Set(b.map(e=>e.relativePath));S=e.filter(e=>!n.has(e)&&!e.startsWith(`${i.aiCurated}/`)),x=[],await f(b,async e=>{let n=t(e.content);if(this.hashCache){if(this.hashCache.get(e.relativePath)===n){g++;return}}else{let t=await this.store.getBySourcePath(e.relativePath);if(t.length>0&&t[0].fileHash===n){g++;return}}x.push(e)},p(y),(e,t)=>d.error(`Hash check failed`,{sourcePath:e.relativePath,...s(t)}))}let C=x.length,w=[],T=[],E=0,D=[],O=[],k=new Map,A=0,j=async()=>{if(D.length===0)return;let e=D,t=O,n=k;D=[],O=[],k=new Map,A=0,await this.store.upsert(e,t);for(let[e,t]of n)this.hashCache?.set(e,t)},M=async()=>{if(this.graphStore){try{w.length>0&&await this.graphStore.upsertNodes(w),T.length>0&&await this.graphStore.upsertEdges(T)}catch(e){d.warn(`Graph batch flush failed`,s(e))}w=[],T=[],E=0}};return await f(x,async n=>{a?.({phase:`chunking`,filesTotal:C,filesProcessed:h,chunksTotal:_,chunksProcessed:_,currentFile:n.relativePath});let i=o(n.relativePath),l=u(n.extension).chunk(n.content,{sourcePath:n.relativePath,contentType:i});if(l.length===0)return;a?.({phase:`embedding`,filesTotal:C,filesProcessed:h,chunksTotal:_+l.length,chunksProcessed:_,currentFile:n.relativePath});let f=await this.embedder.embedBatch(l.map(e=>e.text)),p=t(n.content),m=l.map((t,r)=>({id:e(n.relativePath,r),content:t.text,sourcePath:t.sourcePath,contentType:t.contentType,headingPath:t.headingPath,chunkIndex:t.chunkIndex,totalChunks:t.totalChunks,startLine:t.startLine,endLine:t.endLine,fileHash:p,indexedAt:new Date().toISOString(),origin:`indexed`,tags:[],version:1}));if(a?.({phase:`storing`,filesTotal:C,filesProcessed:h,chunksTotal:_+l.length,chunksProcessed:_,currentFile:n.relativePath}),D.push(...m),O.push(...f),k.set(n.relativePath,p),A++,A>=20&&await j(),this.graphStore)try{c.graphCleared||await this.graphStore.deleteBySourcePath(n.relativePath);let e=r(n.content,n.relativePath);e.nodes.length>0&&w.push(...e.nodes),e.edges.length>0&&T.push(...e.edges),E++,E>=50&&await M()}catch(e){d.warn(`Graph extraction failed`,{sourcePath:n.relativePath,...s(e)})}h++,_+=l.length},p(y),(e,t)=>d.error(`Processing failed`,{sourcePath:e.relativePath,...s(t)})),await j(),await M(),S.length>0&&(a?.({phase:`cleanup`,filesTotal:C,filesProcessed:h,chunksTotal:_,chunksProcessed:_}),await f(S,async e=>{await this.store.deleteBySourcePath(e),this.hashCache?.delete(e),this.graphStore&&await this.graphStore.deleteBySourcePath(e).catch(t=>d.warn(`Graph cleanup failed`,{sourcePath:e,...s(t)})),v++},p(y),(e,t)=>d.error(`Cleanup failed`,{sourcePath:e,...s(t)}))),this.hashCache?.flush(),a?.({phase:`done`,filesTotal:C,filesProcessed:h,chunksTotal:_,chunksProcessed:_}),{filesProcessed:h,filesSkipped:g,chunksCreated:_,filesRemoved:v,durationMs:Date.now()-l}}async reindexAll(e,t){if(this.indexing)throw Error(`Indexing is already in progress`);this.indexing=!0;try{if(await this.store.dropTable(),this.graphStore)try{let e=await this.graphStore.getStats();e.nodeCount>0&&(await this.graphStore.clear(),d.info(`Graph store cleared`,{nodeCount:e.nodeCount,edgeCount:e.edgeCount}))}catch(e){d.warn(`Graph store clear failed`,s(e))}return await this.doReindex(e,t)}catch(e){throw this.indexing=!1,e}}async doReindex(e,t){try{return await this.doIndex(e,t,{skipHashCheck:!0,graphCleared:!0})}finally{this.indexing=!1}}async getStats(){return this.store.getStats()}};export{h as IncrementalIndexer};
1
+ import{generateRecordId as e,hashContent as t}from"./file-hasher.js";import{FilesystemCrawler as n}from"./filesystem-crawler.js";import{extractGraph as r}from"./graph-extractor.js";import{AIKIT_PATHS as i,createLogger as a,detectContentType as o,serializeError as s}from"../../core/dist/index.js";import{availableParallelism as c,loadavg as l}from"node:os";import{createChunkerSync as u}from"../../chunker/dist/index.js";const d=a(`indexer`),f=()=>new Promise(e=>setImmediate(e));async function p(e,t,n,r){let i=0;async function a(){for(;i<e.length;){let n=i++;try{await t(e[n])}catch(t){r?.(e[n],t)}n%10==0&&await f()}}await Promise.all(Array.from({length:Math.min(n,e.length)},()=>a()))}function m(e){let t=c(),n=l()[0]/t;return n>1.5?2:n>1?Math.max(2,Math.floor(e/2)):e}const h=Math.max(2,Math.min(4,Math.floor(c()*.5)));var g=class{crawler;indexing=!1;graphStore;hashCache;get isIndexing(){return this.indexing}constructor(e,t){this.embedder=e,this.store=t,this.crawler=new n}setGraphStore(e){this.graphStore=e}setHashCache(e){this.hashCache=e}async index(e,t){if(this.indexing)throw Error(`Indexing is already in progress`);this.indexing=!0;try{return await this.doIndex(e,t,{})}finally{this.indexing=!1}}async doIndex(n,a,c={}){let l=Date.now(),f=0,g=0,_=0,v=0,y=n.indexing.concurrency??h;a?.({phase:`crawling`,filesTotal:0,filesProcessed:0,chunksTotal:0,chunksProcessed:0});let b=(await Promise.all(n.sources.map(e=>this.crawler.crawl({rootDir:e.path,excludePatterns:e.excludePatterns})))).flat(),x,S;if(c.skipHashCheck)x=b,S=[];else{let e=await this.store.listSourcePaths(),n=new Set(b.map(e=>e.relativePath));S=e.filter(e=>!n.has(e)&&!e.startsWith(`${i.aiCurated}/`)),x=[],await p(b,async e=>{let n=t(e.content);if(this.hashCache){if(this.hashCache.get(e.relativePath)===n){g++;return}}else{let t=await this.store.getBySourcePath(e.relativePath);if(t.length>0&&t[0].fileHash===n){g++;return}}x.push(e)},m(y),(e,t)=>d.error(`Hash check failed`,{sourcePath:e.relativePath,...s(t)}))}let C=x.length,w=[],T=[],E=0,D=[],O=[],k=new Map,A=0,j=async()=>{if(D.length===0)return;let e=D,t=O,n=k;D=[],O=[],k=new Map,A=0,await this.store.upsert(e,t);for(let[e,t]of n)this.hashCache?.set(e,t)},M=async()=>{if(this.graphStore){try{w.length>0&&await this.graphStore.upsertNodes(w),T.length>0&&await this.graphStore.upsertEdges(T)}catch(e){d.warn(`Graph batch flush failed`,s(e))}w=[],T=[],E=0}};return await p(x,async n=>{a?.({phase:`chunking`,filesTotal:C,filesProcessed:f,chunksTotal:_,chunksProcessed:_,currentFile:n.relativePath});let i=o(n.relativePath),l=u(n.extension).chunk(n.content,{sourcePath:n.relativePath,contentType:i});if(l.length===0)return;a?.({phase:`embedding`,filesTotal:C,filesProcessed:f,chunksTotal:_+l.length,chunksProcessed:_,currentFile:n.relativePath});let p=await this.embedder.embedBatch(l.map(e=>e.text)),m=t(n.content),h=l.map((t,r)=>({id:e(n.relativePath,r),content:t.text,sourcePath:t.sourcePath,contentType:t.contentType,headingPath:t.headingPath,chunkIndex:t.chunkIndex,totalChunks:t.totalChunks,startLine:t.startLine,endLine:t.endLine,fileHash:m,indexedAt:new Date().toISOString(),origin:`indexed`,tags:[],version:1}));if(a?.({phase:`storing`,filesTotal:C,filesProcessed:f,chunksTotal:_+l.length,chunksProcessed:_,currentFile:n.relativePath}),D.push(...h),O.push(...p),k.set(n.relativePath,m),A++,A>=20&&await j(),this.graphStore)try{c.graphCleared||await this.graphStore.deleteBySourcePath(n.relativePath);let e=r(n.content,n.relativePath);e.nodes.length>0&&w.push(...e.nodes),e.edges.length>0&&T.push(...e.edges),E++,E>=50&&await M()}catch(e){d.warn(`Graph extraction failed`,{sourcePath:n.relativePath,...s(e)})}f++,_+=l.length},m(y),(e,t)=>d.error(`Processing failed`,{sourcePath:e.relativePath,...s(t)})),await j(),await M(),S.length>0&&(a?.({phase:`cleanup`,filesTotal:C,filesProcessed:f,chunksTotal:_,chunksProcessed:_}),await p(S,async e=>{await this.store.deleteBySourcePath(e),this.hashCache?.delete(e),this.graphStore&&await this.graphStore.deleteBySourcePath(e).catch(t=>d.warn(`Graph cleanup failed`,{sourcePath:e,...s(t)})),v++},m(y),(e,t)=>d.error(`Cleanup failed`,{sourcePath:e,...s(t)}))),this.hashCache?.flush(),a?.({phase:`done`,filesTotal:C,filesProcessed:f,chunksTotal:_,chunksProcessed:_}),{filesProcessed:f,filesSkipped:g,chunksCreated:_,filesRemoved:v,durationMs:Date.now()-l}}async reindexAll(e,t){if(this.indexing)throw Error(`Indexing is already in progress`);this.indexing=!0;try{if(await this.store.dropTable(),this.graphStore)try{let e=await this.graphStore.getStats();e.nodeCount>0&&(await this.graphStore.clear(),d.info(`Graph store cleared`,{nodeCount:e.nodeCount,edgeCount:e.edgeCount}))}catch(e){d.warn(`Graph store clear failed`,s(e))}return await this.doReindex(e,t)}catch(e){throw this.indexing=!1,e}}async doReindex(e,t){try{return await this.doIndex(e,t,{skipHashCheck:!0,graphCleared:!0})}finally{this.indexing=!1}}async getStats(){return this.store.getStats()}};export{g as IncrementalIndexer};
@@ -0,0 +1,38 @@
1
+ /**
2
+ * DirectKBClient — In-process IKBClient using store/embedder directly.
3
+ * Used by the TUI and other in-process consumers.
4
+ */
5
+ import type { IEmbedder } from '@kb/embeddings';
6
+ import type { IGraphStore, IKnowledgeStore } from '@kb/store';
7
+ import type { IKBClient, KBGraphData, KBKnowledgeEntry, KBSearchResult, KBStatus } from './types.js';
8
+ interface CuratedEntry {
9
+ path: string;
10
+ title: string;
11
+ category: string;
12
+ tags: string[];
13
+ content: string;
14
+ }
15
+ export interface DirectClientDeps {
16
+ store: IKnowledgeStore;
17
+ embedder: IEmbedder;
18
+ graphStore?: IGraphStore;
19
+ /** Function to list curated entries */
20
+ listCurated?: () => Promise<CuratedEntry[]>;
21
+ /** Function to read a single curated entry */
22
+ readCurated?: (path: string) => Promise<CuratedEntry | null>;
23
+ }
24
+ export declare class DirectKBClient implements IKBClient {
25
+ private readonly deps;
26
+ constructor(deps: DirectClientDeps);
27
+ getStatus(): Promise<KBStatus>;
28
+ search(query: string, options?: {
29
+ limit?: number;
30
+ mode?: 'hybrid' | 'semantic' | 'keyword';
31
+ }): Promise<KBSearchResult[]>;
32
+ listKnowledge(): Promise<KBKnowledgeEntry[]>;
33
+ readKnowledge(path: string): Promise<KBKnowledgeEntry | null>;
34
+ getGraph(query?: string): Promise<KBGraphData>;
35
+ getFileTree(): Promise<string[]>;
36
+ private getEdgesForNodes;
37
+ }
38
+ export {};
@@ -0,0 +1 @@
1
+ const e=1e3;var t=class{constructor(e){this.deps=e}async getStatus(){let e=await this.deps.store.getStats();return{totalRecords:e.totalRecords,totalFiles:e.totalFiles,lastIndexedAt:e.lastIndexedAt??null,onboarded:e.totalRecords>0}}async search(e,t){let n=t?.limit??10,i=t?.mode??`hybrid`;if(i===`keyword`)return(await this.deps.store.ftsSearch(e,{limit:n})).map(r);let a=await this.deps.embedder.embedQuery(e);if(i===`semantic`)return(await this.deps.store.search(a,{limit:n})).map(r);let[o,s]=await Promise.all([this.deps.store.search(a,{limit:n*2}),this.deps.store.ftsSearch(e,{limit:n*2}).catch(()=>[])]),c=new Map;for(let e of[...o,...s]){let t=c.get(e.record.id);(!t||e.score>t.score)&&c.set(e.record.id,e)}return[...c.values()].sort((e,t)=>t.score-e.score).slice(0,n).map(r)}async listKnowledge(){return this.deps.listCurated?(await this.deps.listCurated()).map(n):[]}async readKnowledge(e){if(!this.deps.readCurated)return null;let t=await this.deps.readCurated(e);return t?n(t):null}async getGraph(t){if(!this.deps.graphStore)return{nodes:[],edges:[]};let n=await this.deps.graphStore.findNodes(t?{namePattern:t,limit:500}:{limit:500});if(n.length===0)return{nodes:[],edges:[]};let r=t?await this.getEdgesForNodes(n):await this.deps.graphStore.findEdges({limit:e});return{nodes:n.map(e=>({id:e.id,name:e.name,type:e.type,...e.sourcePath?{sourcePath:e.sourcePath}:{}})),edges:i(r).map(e=>({fromId:e.fromId,toId:e.toId,type:e.type}))}}async getFileTree(){return this.deps.store.listSourcePaths()}async getEdgesForNodes(t){let n=this.deps.graphStore;if(!n)return[];let r=t.flatMap(t=>[n.findEdges({fromId:t.id,limit:e}),n.findEdges({toId:t.id,limit:e})]);return(await Promise.all(r)).flat()}};function n(e){return{path:e.path,title:e.title,category:e.category,tags:e.tags,content:e.content}}function r(e){return{sourcePath:e.record.sourcePath,contentType:e.record.contentType,score:e.score,content:e.record.content,headingPath:e.record.headingPath,startLine:e.record.startLine,endLine:e.record.endLine}}function i(e){let t=new Map;for(let n of e)t.set(n.id,n);return[...t.values()]}export{t as DirectKBClient};
@@ -0,0 +1,4 @@
1
+ export { type DirectClientDeps, DirectKBClient } from './direct-client.js';
2
+ export { McpKBClient } from './mcp-client.js';
3
+ export { extractStructured, extractText, type ParsedContent, parseToolResult, tryParseJson, } from './parsers.js';
4
+ export type { IKBClient, KBGraphData, KBKnowledgeEntry, KBSearchResult, KBStatus, } from './types.js';
@@ -0,0 +1 @@
1
+ import{DirectKBClient as e}from"./direct-client.js";import{extractStructured as t,extractText as n,parseToolResult as r,tryParseJson as i}from"./parsers.js";import{McpKBClient as a}from"./mcp-client.js";export{e as DirectKBClient,a as McpKBClient,t as extractStructured,n as extractText,r as parseToolResult,i as tryParseJson};
@@ -0,0 +1,19 @@
1
+ /**
2
+ * McpKBClient — IKBClient over MCP transport.
3
+ * Used by the Dashboard connecting to KB server via HTTP.
4
+ */
5
+ import type { Client } from '@modelcontextprotocol/sdk/client/index.js';
6
+ import type { IKBClient, KBGraphData, KBKnowledgeEntry, KBSearchResult, KBStatus } from './types.js';
7
+ export declare class McpKBClient implements IKBClient {
8
+ private readonly client;
9
+ constructor(client: Client);
10
+ getStatus(): Promise<KBStatus>;
11
+ search(query: string, options?: {
12
+ limit?: number;
13
+ mode?: 'hybrid' | 'semantic' | 'keyword';
14
+ }): Promise<KBSearchResult[]>;
15
+ listKnowledge(): Promise<KBKnowledgeEntry[]>;
16
+ readKnowledge(path: string): Promise<KBKnowledgeEntry | null>;
17
+ getGraph(query?: string): Promise<KBGraphData>;
18
+ getFileTree(): Promise<string[]>;
19
+ }
@@ -0,0 +1,4 @@
1
+ import{extractStructured as e,extractText as t}from"./parsers.js";var n=class{constructor(e){this.client=e}async getStatus(){let e=await this.client.callTool({name:`status`,arguments:{}}),t=i(e);if(t)return{totalRecords:l(t.totalRecords),totalFiles:l(t.totalFiles),lastIndexedAt:d(t.lastIndexedAt),onboarded:!!t.onboarded};let n=r(e);return{totalRecords:a(n,/Records:\s*(\d+)/i),totalFiles:a(n,/Files:\s*(\d+)/i),lastIndexedAt:null,onboarded:!/not onboarded/i.test(n)}}async search(e,t){let n=await this.client.callTool({name:`search`,arguments:{query:e,limit:t?.limit??10,search_mode:t?.mode??`hybrid`}}),a=i(n);return a?.results?a.results.map(e=>({sourcePath:f(e.sourcePath??e.path),contentType:f(e.contentType,`unknown`),score:l(e.score),content:f(e.content??e.snippet),headingPath:p(e.headingPath),startLine:u(e.startLine),endLine:u(e.endLine)})):o(r(n))}async listKnowledge(){let e=i(await this.client.callTool({name:`list`,arguments:{}}));return e?.entries?e.entries.map(e=>({path:f(e.path),title:f(e.title),category:f(e.category),tags:m(e.tags),content:f(e.content)})):[]}async readKnowledge(e){try{let t=r(await this.client.callTool({name:`read`,arguments:{path:e}}));return!t||/not found/i.test(t)?null:{path:e,title:e.split(`/`).pop()??e,category:e.split(`/`).at(-2)??`uncategorized`,tags:[],content:t}}catch{return null}}async getGraph(e){let t=s(r(await this.client.callTool({name:`graph`,arguments:{action:`find_nodes`,...e?{name_pattern:e}:{},limit:500}})));return t.length===0?{nodes:[],edges:[]}:{nodes:t,edges:c(r(await this.client.callTool({name:`graph`,arguments:{action:`find_edges`,limit:1e3}})))}}async getFileTree(){let e=await this.client.callTool({name:`find`,arguments:{glob:`**/*`,limit:1e4}}),t=i(e);return t?.results?t.results.map(e=>typeof e==`string`?e:e.path??``).filter(e=>e.length>0):r(e).split(`
2
+ `).map(e=>e.trim()).filter(e=>e.length>0)}};function r(e){return t(e)}function i(t){return e(t)}function a(e,t){let n=t.exec(e);return n?Number.parseInt(n[1],10):0}function o(e){let t=[],n=e.split(/\n(?=\d+\.\s|\*\*)/);for(let e of n){let n=/(?:\*\*|`)([^*`]+?)(?:\*\*|`)/.exec(e),r=/score:\s*([\d.]+)/i.exec(e);n&&t.push({sourcePath:n[1].trim(),contentType:`unknown`,score:r?Number.parseFloat(r[1]):0,content:e.replace(/^.*\n/,``).trim().slice(0,200)})}return t}function s(e){let t=[];for(let n of e.split(`
3
+ `)){let e=/\*\*(.+?)\*\*\s*\(([^,)]+)(?:,\s*id:\s*`?([\w-]+)`?)?/.exec(n);e&&t.push({id:e[3]??`node-${t.length}`,name:e[1],type:e[2]})}return t}function c(e){let t=[];for(let n of e.split(`
4
+ `)){let e=/`?([\w-]+)`?\s*[—-]+\[(\w+)\][→>]\s*`?([\w-]+)`?/.exec(n);e&&t.push({fromId:e[1],toId:e[3],type:e[2]})}return t}function l(e){return typeof e==`number`?e:0}function u(e){return typeof e==`number`?e:void 0}function d(e){return typeof e==`string`?e:null}function f(e,t=``){return typeof e==`string`?e:t}function p(e){return typeof e==`string`?e:void 0}function m(e){return Array.isArray(e)?e.filter(e=>typeof e==`string`):[]}export{n as McpKBClient};
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Content parsers for MCP tool responses.
3
+ * Used by McpKBClient to parse structuredContent from tool calls.
4
+ */
5
+ export interface ParsedContent<T = unknown> {
6
+ text: string;
7
+ structured?: T;
8
+ }
9
+ /**
10
+ * Extract text content from an MCP tool result.
11
+ */
12
+ export declare function extractText(result: {
13
+ content?: unknown;
14
+ } | null | undefined): string;
15
+ /**
16
+ * Extract structured content from an MCP tool result.
17
+ */
18
+ export declare function extractStructured<T>(result: {
19
+ structuredContent?: unknown;
20
+ } | null | undefined): T | undefined;
21
+ /**
22
+ * Parse a tool result, returning both text and structured content.
23
+ */
24
+ export declare function parseToolResult<T = unknown>(result: {
25
+ content?: unknown;
26
+ structuredContent?: unknown;
27
+ } | null | undefined): ParsedContent<T>;
28
+ /**
29
+ * Try to parse JSON from a text tool result.
30
+ * Returns undefined if parsing fails.
31
+ */
32
+ export declare function tryParseJson<T = unknown>(text: string): T | undefined;
@@ -0,0 +1,2 @@
1
+ function e(e){return!Array.isArray(e?.content)||e.content.length===0?``:e.content.filter(e=>typeof e==`object`&&!!e).filter(e=>e.type===`text`&&typeof e.text==`string`).map(e=>e.text).join(`
2
+ `)}function t(e){return e?.structuredContent}function n(n){return{text:e(n),structured:t(n)}}function r(e){try{return JSON.parse(e)}catch{return}}export{t as extractStructured,e as extractText,n as parseToolResult,r as tryParseJson};
@@ -0,0 +1,59 @@
1
+ /**
2
+ * IKBClient — Unified data access interface for KB consumers.
3
+ *
4
+ * Implemented by:
5
+ * - DirectKBClient (in-process, used by TUI)
6
+ * - McpKBClient (over MCP transport, used by Dashboard)
7
+ */
8
+ export interface KBStatus {
9
+ totalRecords: number;
10
+ totalFiles: number;
11
+ lastIndexedAt: string | null;
12
+ onboarded: boolean;
13
+ }
14
+ export interface KBSearchResult {
15
+ sourcePath: string;
16
+ contentType: string;
17
+ score: number;
18
+ content: string;
19
+ headingPath?: string;
20
+ startLine?: number;
21
+ endLine?: number;
22
+ }
23
+ export interface KBKnowledgeEntry {
24
+ path: string;
25
+ title: string;
26
+ category: string;
27
+ tags: string[];
28
+ content: string;
29
+ }
30
+ export interface KBGraphData {
31
+ nodes: Array<{
32
+ id: string;
33
+ name: string;
34
+ type: string;
35
+ sourcePath?: string;
36
+ }>;
37
+ edges: Array<{
38
+ fromId: string;
39
+ toId: string;
40
+ type: string;
41
+ }>;
42
+ }
43
+ export interface IKBClient {
44
+ /** Get KB status. */
45
+ getStatus(): Promise<KBStatus>;
46
+ /** Search the knowledge base. */
47
+ search(query: string, options?: {
48
+ limit?: number;
49
+ mode?: 'hybrid' | 'semantic' | 'keyword';
50
+ }): Promise<KBSearchResult[]>;
51
+ /** List curated knowledge entries. */
52
+ listKnowledge(): Promise<KBKnowledgeEntry[]>;
53
+ /** Read a specific curated entry. */
54
+ readKnowledge(path: string): Promise<KBKnowledgeEntry | null>;
55
+ /** Get knowledge graph data. */
56
+ getGraph(query?: string): Promise<KBGraphData>;
57
+ /** Get file tree of indexed sources. */
58
+ getFileTree(): Promise<string[]>;
59
+ }
@@ -0,0 +1 @@
1
+ export{};
@@ -3,7 +3,7 @@
3
3
  <head>
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
- <title>KB Present</title>
6
+ <title>AI Kit Present</title>
7
7
  <style>
8
8
  :root {
9
9
  /* Base tokens (browser-mode defaults) */
@@ -22,7 +22,7 @@
22
22
  --shadow: 0 1px 3px rgba(0,0,0,0.08), 0 1px 2px rgba(0,0,0,0.06);
23
23
  --font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
24
24
  --font-mono: 'Cascadia Code', 'Fira Code', 'JetBrains Mono', Consolas, monospace;
25
- /* KB semantic tokens — cascade: host var → base var */
25
+ /* AI Kit semantic tokens — cascade: host var → base var */
26
26
  --aikit-bg: var(--color-background-primary, var(--bg));
27
27
  --aikit-surface: var(--color-background-secondary, var(--surface));
28
28
  --aikit-surface2: var(--color-background-tertiary, var(--surface));
@@ -696,7 +696,7 @@ container holding the app. Specify either width or maxWidth, and either height o
696
696
  .tree-metadata { color: var(--aikit-muted, #9ca3af); font: 500 11px var(--aikit-font-sans, Inter, sans-serif); }
697
697
  .tree-children { margin-left: 18px; padding-left: 12px; border-left: 1px dashed var(--aikit-border, #3c3c3c); }
698
698
  .tree-children[hidden] { display: none; }
699
- `;function Lu(e){return typeof e==`object`&&!!e&&typeof e.label==`string`}var Ru={name:`tree`,label:`Tree`,detect(e){if(Array.isArray(e)){let t=e[0];return Lu(t)&&Array.isArray(t.children)?.7:0}if(typeof e!=`object`||!e)return 0;let t=e;if(Array.isArray(t.root)){let e=t.root[0];return Lu(e)&&Array.isArray(e.children)?.85:0}return Lu(t.root)&&Array.isArray(t.root.children)?.85:0},render(e,t,n){q(`tpl-tree`,Iu);let r=t,i=Array.isArray(r)?r:Array.isArray(r.root)?r.root:[r.root],a=J(`div`,`block tpl-tree`),o=(e,t)=>{let n=J(`div`,`tree-node`),r=J(`div`,`tree-row`);if(r.id=Al(e.id??`${e.label}-${t}`),e.children?.length){let i=document.createElement(`button`),a=t<1;i.className=`tree-toggle`,i.textContent=a?`▼`:`▶`,r.appendChild(i);let s=J(`div`,`tree-children`);s.dataset.depth=String(t),s.hidden=!a;for(let n of e.children)s.appendChild(o(n,t+1));i.addEventListener(`click`,()=>{s.hidden=!s.hidden,i.textContent=s.hidden?`▶`:`▼`}),n.appendChild(r),n.appendChild(s)}else r.appendChild(J(`span`,`tree-spacer`)),n.appendChild(r);if(e.icon&&r.appendChild(J(`span`,`tree-icon`,e.icon)),r.appendChild(J(`span`,`tree-label`,e.label)),e.metadata&&Object.keys(e.metadata).length>0){let t=J(`span`,`tree-metadata`);t.textContent=Object.entries(e.metadata).map(([e,t])=>`${e}: ${kl(t)}`).join(` · `),r.appendChild(t)}return n};for(let e of i)a.appendChild(o(e,0));e.appendChild(a)},styles(){return Iu}},Z=new Tl({name:`KB Present`,version:`1.0.0`}),zu=document.getElementById(`app`);if(!zu)throw Error(`Missing #app element`);var Q=zu,$=new fu;$.register(ju),$.register(yu),$.register(Nu),$.register(Tu),$.register(Du),$.register(Fu),$.register(_u),$.register(ku),$.register(Ru),$.register(Su);var Bu=su(Z),Vu={app:Z,data:cu(Z),emitAction:Bu},Hu=new hu,Uu=new du,Wu=new lu(Z);Z.connect().then(()=>{let e=Z.getHostContext();e&&(Hu.apply(e),Uu.apply(e),Wu.apply(e))}),Z.onhostcontextchanged=e=>{Hu.apply(e),Uu.apply(e),Wu.apply(e),qu?.onThemeChange&&qu.onThemeChange(Hu.getTheme())};var Gu;Z.ontoolinputpartial=e=>{let t=e.arguments;t&&(Q.dataset.streaming=`true`,clearTimeout(Gu),Gu=setTimeout(()=>{try{let e=t.content;if(!e)return;Ku({title:t.title,template:t.template,content:e})}catch{}},80))},Z.ontoolinput=()=>{clearTimeout(Gu),delete Q.dataset.streaming};function Ku(e){if(Q.innerHTML=``,e.title){let t=J(`div`,`header`);t.appendChild(J(`h1`,``,e.title)),t.appendChild(J(`span`,`badge streaming`,`⟳`)),Q.appendChild(t)}let t=J(`div`,`content`);if(Array.isArray(e.content)&&e.content.length>0&&jl(e.content[0]))for(let n of e.content)jl(n)&&t.appendChild(Zl(n));else t.appendChild(Yl(e.content));Q.appendChild(t)}Z.ontoolresult=e=>{if(clearTimeout(Gu),delete Q.dataset.streaming,e.structuredContent)try{Yu(e.structuredContent);return}catch(e){Ju(`Render error`,e);return}let t=e.content?.find(e=>e.type===`text`)?.text;if(!t){Q.innerHTML=`<p>No data received.</p>`;return}try{Yu(JSON.parse(t))}catch{Yu({content:t})}};var qu=null;function Ju(e,t){Q.innerHTML=``;let n=J(`div`,`error-panel`);n.appendChild(J(`h3`,``,`⚠ ${e}`)),n.appendChild(J(`pre`,``,t instanceof Error?t.message:String(t))),Q.appendChild(n)}function Yu(e){if(qu?.destroy&&qu.destroy(),qu=null,Q.innerHTML=``,!e.content){Q.innerHTML=`<p class="no-data">No content data received.</p>`;return}if(e.title){let t=J(`div`,`header`);t.appendChild(J(`h1`,``,e.title)),t.appendChild(J(`span`,`badge`,`KB`)),Q.appendChild(t)}let t=e.template?$.get(e.template):$.detect(e.content),n=J(`div`,`content`);if(t)qu=t,t.render(n,e.content,Vu);else if(Array.isArray(e.content)&&e.content.length>0&&jl(e.content[0]))for(let t of e.content)jl(t)&&n.appendChild(Zl(t));else n.appendChild(Yl(e.content));Q.appendChild(n),e.actions?.length&&Q.appendChild(ou(e.actions,Vu))}</script>
699
+ `;function Lu(e){return typeof e==`object`&&!!e&&typeof e.label==`string`}var Ru={name:`tree`,label:`Tree`,detect(e){if(Array.isArray(e)){let t=e[0];return Lu(t)&&Array.isArray(t.children)?.7:0}if(typeof e!=`object`||!e)return 0;let t=e;if(Array.isArray(t.root)){let e=t.root[0];return Lu(e)&&Array.isArray(e.children)?.85:0}return Lu(t.root)&&Array.isArray(t.root.children)?.85:0},render(e,t,n){q(`tpl-tree`,Iu);let r=t,i=Array.isArray(r)?r:Array.isArray(r.root)?r.root:[r.root],a=J(`div`,`block tpl-tree`),o=(e,t)=>{let n=J(`div`,`tree-node`),r=J(`div`,`tree-row`);if(r.id=Al(e.id??`${e.label}-${t}`),e.children?.length){let i=document.createElement(`button`),a=t<1;i.className=`tree-toggle`,i.textContent=a?`▼`:`▶`,r.appendChild(i);let s=J(`div`,`tree-children`);s.dataset.depth=String(t),s.hidden=!a;for(let n of e.children)s.appendChild(o(n,t+1));i.addEventListener(`click`,()=>{s.hidden=!s.hidden,i.textContent=s.hidden?`▶`:`▼`}),n.appendChild(r),n.appendChild(s)}else r.appendChild(J(`span`,`tree-spacer`)),n.appendChild(r);if(e.icon&&r.appendChild(J(`span`,`tree-icon`,e.icon)),r.appendChild(J(`span`,`tree-label`,e.label)),e.metadata&&Object.keys(e.metadata).length>0){let t=J(`span`,`tree-metadata`);t.textContent=Object.entries(e.metadata).map(([e,t])=>`${e}: ${kl(t)}`).join(` · `),r.appendChild(t)}return n};for(let e of i)a.appendChild(o(e,0));e.appendChild(a)},styles(){return Iu}},Z=new Tl({name:`AI Kit Present`,version:`1.0.0`}),zu=document.getElementById(`app`);if(!zu)throw Error(`Missing #app element`);var Q=zu,$=new fu;$.register(ju),$.register(yu),$.register(Nu),$.register(Tu),$.register(Du),$.register(Fu),$.register(_u),$.register(ku),$.register(Ru),$.register(Su);var Bu=su(Z),Vu={app:Z,data:cu(Z),emitAction:Bu},Hu=new hu,Uu=new du,Wu=new lu(Z);Z.connect().then(()=>{let e=Z.getHostContext();e&&(Hu.apply(e),Uu.apply(e),Wu.apply(e))}),Z.onhostcontextchanged=e=>{Hu.apply(e),Uu.apply(e),Wu.apply(e),qu?.onThemeChange&&qu.onThemeChange(Hu.getTheme())};var Gu;Z.ontoolinputpartial=e=>{let t=e.arguments;t&&(Q.dataset.streaming=`true`,clearTimeout(Gu),Gu=setTimeout(()=>{try{let e=t.content;if(!e)return;Ku({title:t.title,template:t.template,content:e})}catch{}},80))},Z.ontoolinput=()=>{clearTimeout(Gu),delete Q.dataset.streaming};function Ku(e){if(Q.innerHTML=``,e.title){let t=J(`div`,`header`);t.appendChild(J(`h1`,``,e.title)),t.appendChild(J(`span`,`badge streaming`,`⟳`)),Q.appendChild(t)}let t=J(`div`,`content`);if(Array.isArray(e.content)&&e.content.length>0&&jl(e.content[0]))for(let n of e.content)jl(n)&&t.appendChild(Zl(n));else t.appendChild(Yl(e.content));Q.appendChild(t)}Z.ontoolresult=e=>{if(clearTimeout(Gu),delete Q.dataset.streaming,e.structuredContent)try{Yu(e.structuredContent);return}catch(e){Ju(`Render error`,e);return}let t=e.content?.find(e=>e.type===`text`)?.text;if(!t){Q.innerHTML=`<p>No data received.</p>`;return}try{Yu(JSON.parse(t))}catch{Yu({content:t})}};var qu=null;function Ju(e,t){Q.innerHTML=``;let n=J(`div`,`error-panel`);n.appendChild(J(`h3`,``,`⚠ ${e}`)),n.appendChild(J(`pre`,``,t instanceof Error?t.message:String(t))),Q.appendChild(n)}function Yu(e){if(qu?.destroy&&qu.destroy(),qu=null,Q.innerHTML=``,!e.content){Q.innerHTML=`<p class="no-data">No content data received.</p>`;return}if(e.title){let t=J(`div`,`header`);t.appendChild(J(`h1`,``,e.title)),t.appendChild(J(`span`,`badge`,`KB`)),Q.appendChild(t)}let t=e.template?$.get(e.template):$.detect(e.content),n=J(`div`,`content`);if(t)qu=t,t.render(n,e.content,Vu);else if(Array.isArray(e.content)&&e.content.length>0&&jl(e.content[0]))for(let t of e.content)jl(t)&&n.appendChild(Zl(t));else n.appendChild(Yl(e.content));Q.appendChild(n),e.actions?.length&&Q.appendChild(ou(e.actions,Vu))}</script>
700
700
  </head>
701
701
  <body>
702
702
  <div id="app">
@@ -0,0 +1,47 @@
1
+ //#region packages/server/src/background-task.d.ts
2
+ /**
3
+ * Background task scheduler — runs heavy operations without blocking MCP request handling.
4
+ *
5
+ * Instead of worker_threads (which would duplicate ~200 MB+ ONNX models per thread),
6
+ * this scheduler:
7
+ * 1. Queues tasks and runs them sequentially (prevents OOM from parallel heavy ops)
8
+ * 2. Provides `yieldToEventLoop()` that heavy loops can call between iterations to
9
+ * keep the MCP JSON-RPC transport responsive
10
+ * 3. Tracks running/pending state for status reporting
11
+ *
12
+ * Why not worker_threads?
13
+ * - ONNX WASM model (~200 MB) must be loaded per thread — doubles memory
14
+ * - tree-sitter WASM and sql.js WASM cannot be shared across threads
15
+ * - The existing adaptive-concurrency indexer already parallelises I/O within the main thread
16
+ */
17
+ interface BackgroundTask {
18
+ name: string;
19
+ fn: () => Promise<void>;
20
+ }
21
+ /**
22
+ * Yield control to the event loop so pending I/O (MCP transport, timers)
23
+ * can be processed. Call this inside tight loops that would otherwise
24
+ * starve the event loop (e.g., embedding 500 chunks in a row).
25
+ *
26
+ * Cost: ~1 ms per call on most systems — negligible for batch work when
27
+ * called every 10-50 iterations.
28
+ */
29
+ declare function yieldToEventLoop(): Promise<void>;
30
+ declare class BackgroundTaskScheduler {
31
+ private readonly queue;
32
+ private running;
33
+ /** Whether a task is currently executing. */
34
+ get isRunning(): boolean;
35
+ /** Name of the currently running task, or null. */
36
+ get currentTask(): string | null;
37
+ /** Number of tasks waiting in the queue. */
38
+ get pendingCount(): number;
39
+ /**
40
+ * Enqueue a task. Returns a Promise that resolves when the task completes
41
+ * (or rejects if it throws). Tasks run sequentially in FIFO order.
42
+ */
43
+ schedule(task: BackgroundTask): Promise<void>;
44
+ private processQueue;
45
+ }
46
+ //#endregion
47
+ export { BackgroundTask, BackgroundTaskScheduler, yieldToEventLoop };
@@ -0,0 +1 @@
1
+ import{createLogger as e}from"../../core/dist/index.js";const t=e(`background-task`);function n(){return new Promise(e=>setImmediate(e))}var r=class{queue=[];running=null;get isRunning(){return this.running!==null}get currentTask(){return this.running}get pendingCount(){return this.queue.length}schedule(e){return new Promise((t,n)=>{this.queue.push({...e,resolve:t,reject:n}),this.running||this.processQueue()})}async processQueue(){for(;this.queue.length>0;){let e=this.queue.shift();if(!e)break;this.running=e.name,t.info(`Background task started`,{task:e.name,pending:this.queue.length});let n=Date.now();try{await e.fn();let r=Date.now()-n;t.info(`Background task completed`,{task:e.name,durationMs:r}),e.resolve()}catch(r){let i=Date.now()-n;t.error(`Background task failed`,{task:e.name,durationMs:i,err:r}),e.reject(r instanceof Error?r:Error(String(r)))}}this.running=null}};export{r as BackgroundTaskScheduler,n as yieldToEventLoop};
@@ -1 +1 @@
1
- import{existsSync as e,readFileSync as t}from"node:fs";import{dirname as n,resolve as r}from"node:path";import{fileURLToPath as i}from"node:url";import{AIKIT_PATHS as a,createLogger as o,getPartitionDir as s,isUserInstalled as c,registerWorkspace as l,serializeError as u}from"../../core/dist/index.js";const d=n(i(import.meta.url)),f=o(`server`);function p(e,t,n){let i=r(e),a=r(t);if(!i.startsWith(a))throw Error(`Config ${n} path escapes workspace root: ${e} is not under ${t}`);return i}function m(){let i=process.env.AIKIT_CONFIG_PATH??(e(r(process.cwd(),`kb.config.json`))?r(process.cwd(),`kb.config.json`):r(d,`..`,`..`,`..`,`kb.config.json`));try{if(!e(i))return f.info(`No config file found, using defaults`,{configPath:i}),h();let o=t(i,`utf-8`),s=JSON.parse(o);if(!s.sources||!Array.isArray(s.sources)||s.sources.length===0)throw Error(`Config must have at least one source`);if(!s.store?.path)throw Error(`Config must specify store.path`);let c=n(i);return s.sources=s.sources.map(e=>({...e,path:p(r(c,e.path),c,`source`)})),s.store.path=p(r(c,s.store.path),c,`store`),s.curated=s.curated??{path:a.aiCurated},s.curated.path=p(r(c,s.curated.path),c,`curated`),g(s,c),s}catch(e){return f.error(`Failed to load config`,{configPath:i,...u(e)}),f.warn(`Falling back to default configuration`,{configPath:i}),h()}}function h(){let e=process.env.AIKIT_WORKSPACE_ROOT??process.cwd(),t={sources:[{path:e,excludePatterns:[`node_modules/**`,`dist/**`,`.git/**`,`coverage/**`,`*.lock`,`pnpm-lock.yaml`]}],serverName:`knowledge-base`,indexing:{chunkSize:1500,chunkOverlap:200,minChunkSize:100},embedding:{model:`mixedbread-ai/mxbai-embed-large-v1`,dimensions:1024},store:{backend:`lancedb`,path:r(e,a.data)},curated:{path:r(e,a.aiCurated)},onboardDir:r(e,a.aiKb)};return g(t,e),t}function g(e,t){if(!c())return;let n=t,i=l(n);e.store.path=r(s(i.partition)),e.onboardDir=r(s(i.partition),`onboard`),e.curated||={path:r(n,a.aiCurated)}}function _(e,t){f.info(`Reconfiguring for workspace root`,{workspaceRoot:t});try{process.chdir(t),f.info(`Changed process cwd to workspace root`,{cwd:process.cwd()})}catch(e){f.warn(`Failed to chdir to workspace root`,{workspaceRoot:t,...u(e)})}e.sources=[{path:t,excludePatterns:e.sources[0]?.excludePatterns??[`node_modules/**`,`dist/**`,`.git/**`,`coverage/**`,`*.lock`,`pnpm-lock.yaml`]}],e.store.path=r(t,a.data),e.curated={path:r(t,a.aiCurated)},e.onboardDir=r(t,a.aiKb),g(e,t)}export{m as loadConfig,_ as reconfigureForWorkspace};
1
+ import{existsSync as e,readFileSync as t}from"node:fs";import{dirname as n,resolve as r}from"node:path";import{fileURLToPath as i}from"node:url";import{AIKIT_PATHS as a,createLogger as o,getPartitionDir as s,isUserInstalled as c,registerWorkspace as l,serializeError as u}from"../../core/dist/index.js";const d=n(i(import.meta.url)),f=o(`server`);function p(e,t,n){let i=r(e),a=r(t);if(!i.startsWith(a))throw Error(`Config ${n} path escapes workspace root: ${e} is not under ${t}`);return i}function m(){let i=process.env.AIKIT_CONFIG_PATH??(e(r(process.cwd(),`aikit.config.json`))?r(process.cwd(),`aikit.config.json`):r(d,`..`,`..`,`..`,`aikit.config.json`));try{if(!e(i))return f.info(`No config file found, using defaults`,{configPath:i}),h();let o=t(i,`utf-8`),s=JSON.parse(o);if(!s.sources||!Array.isArray(s.sources)||s.sources.length===0)throw Error(`Config must have at least one source`);if(!s.store?.path)throw Error(`Config must specify store.path`);let c=n(i);return s.sources=s.sources.map(e=>({...e,path:p(r(c,e.path),c,`source`)})),s.store.path=p(r(c,s.store.path),c,`store`),s.curated=s.curated??{path:a.aiCurated},s.curated.path=p(r(c,s.curated.path),c,`curated`),g(s,c),s}catch(e){return f.error(`Failed to load config`,{configPath:i,...u(e)}),f.warn(`Falling back to default configuration`,{configPath:i}),h()}}function h(){let e=process.env.AIKIT_WORKSPACE_ROOT??process.cwd(),t={sources:[{path:e,excludePatterns:[`node_modules/**`,`dist/**`,`.git/**`,`coverage/**`,`*.lock`,`pnpm-lock.yaml`]}],serverName:`aikit`,indexing:{chunkSize:1500,chunkOverlap:200,minChunkSize:100},embedding:{model:`mixedbread-ai/mxbai-embed-large-v1`,dimensions:1024},store:{backend:`lancedb`,path:r(e,a.data)},curated:{path:r(e,a.aiCurated)},onboardDir:r(e,a.aiKb),stateDir:r(e,a.state)};return g(t,e),t}function g(e,t){if(!c())return;let n=t,i=l(n);e.store.path=r(s(i.partition)),e.onboardDir=r(s(i.partition),`onboard`),e.stateDir=r(s(i.partition),`state`),e.curated||={path:r(n,a.aiCurated)}}function _(e,t){f.info(`Reconfiguring for workspace root`,{workspaceRoot:t});try{process.chdir(t),f.info(`Changed process cwd to workspace root`,{cwd:process.cwd()})}catch(e){f.warn(`Failed to chdir to workspace root`,{workspaceRoot:t,...u(e)})}e.sources=[{path:t,excludePatterns:e.sources[0]?.excludePatterns??[`node_modules/**`,`dist/**`,`.git/**`,`coverage/**`,`*.lock`,`pnpm-lock.yaml`]}],e.store.path=r(t,a.data),e.curated={path:r(t,a.aiCurated)},e.onboardDir=r(t,a.aiKb),e.stateDir=r(t,a.state),g(e,t)}export{m as loadConfig,_ as reconfigureForWorkspace};
@@ -0,0 +1,29 @@
1
+ //#region packages/server/src/idle-timer.d.ts
2
+ /**
3
+ * Resource lifecycle manager — tracks tool activity and closes idle resources.
4
+ *
5
+ * When no tool has been invoked for `idleMs`, the manager calls the registered
6
+ * cleanup callbacks. Resources are re-initialised lazily on next tool call.
7
+ */
8
+ interface IdleTimerOptions {
9
+ /** Milliseconds of inactivity before cleanup fires. @default 300_000 */
10
+ idleMs?: number;
11
+ }
12
+ declare class IdleTimer {
13
+ private timer;
14
+ private readonly cleanupFns;
15
+ private readonly idleMs;
16
+ private disposed;
17
+ constructor(opts?: IdleTimerOptions);
18
+ /** Register a cleanup callback that runs when idle threshold is reached. */
19
+ onIdle(fn: () => void | Promise<void>): void;
20
+ /** Call this on every tool invocation to reset the idle countdown. */
21
+ touch(): void;
22
+ /** Cancel the pending idle timer without running cleanup. */
23
+ cancel(): void;
24
+ /** Stop the timer permanently and release all callbacks. */
25
+ dispose(): void;
26
+ private runCleanup;
27
+ }
28
+ //#endregion
29
+ export { IdleTimer, IdleTimerOptions };
@@ -0,0 +1 @@
1
+ import{createLogger as e}from"../../core/dist/index.js";const t=e(`idle-timer`);var n=class{timer=null;cleanupFns=[];idleMs;disposed=!1;constructor(e){this.idleMs=e?.idleMs??3e5}onIdle(e){this.cleanupFns.push(e)}touch(){this.disposed||(this.cancel(),this.timer=setTimeout(()=>{this.runCleanup()},this.idleMs),this.timer.unref&&this.timer.unref())}cancel(){this.timer&&=(clearTimeout(this.timer),null)}dispose(){this.cancel(),this.cleanupFns.length=0,this.disposed=!0}async runCleanup(){t.info(`Idle for ${this.idleMs/1e3}s — running cleanup`);for(let e of this.cleanupFns)try{await e()}catch(e){t.warn(`Idle cleanup callback failed`,{error:String(e)})}}};export{n as IdleTimer};
@@ -1 +1 @@
1
- import{readFileSync as e}from"node:fs";import{dirname as t,resolve as n}from"node:path";import{fileURLToPath as r}from"node:url";import{createLogger as i,serializeError as a}from"../../core/dist/index.js";import{parseArgs as o}from"node:util";const s=t(r(import.meta.url)),c=(()=>{try{let t=n(s,`..`,`..`,`..`,`package.json`);return JSON.parse(e(t,`utf-8`)).version??`0.0.0`}catch{return`0.0.0`}})(),l=i(`server`),{values:u}=o({options:{transport:{type:`string`,default:process.env.AIKIT_TRANSPORT??`stdio`},port:{type:`string`,default:process.env.AIKIT_PORT??`3210`}}});async function d(){if(process.on(`unhandledRejection`,e=>{l.error(`Unhandled rejection`,a(e))}),l.info(`Starting MCP Knowledge Base server`,{version:c}),u.transport===`http`){let[{default:e},{loadConfig:t},{registerDashboardRoutes:n,resolveDashboardDir:r}]=await Promise.all([import(`express`),import(`./config.js`),import(`./dashboard-static.js`)]),i=t();l.info(`Config loaded`,{sourceCount:i.sources.length,storePath:i.store.path});let o=e();o.use(e.json());let s=Number(u.port);o.use((e,t,n)=>{if(t.setHeader(`Access-Control-Allow-Origin`,process.env.AIKIT_CORS_ORIGIN??`http://localhost:${s}`),t.setHeader(`Access-Control-Allow-Methods`,`GET, POST, DELETE, OPTIONS`),t.setHeader(`Access-Control-Allow-Headers`,`Content-Type, Authorization`),e.method===`OPTIONS`){t.status(204).end();return}n()}),n(o,r(),l),o.get(`/health`,(e,t)=>{t.json({status:`ok`})});let c=!1,d=null,f=null,p=Promise.resolve();o.post(`/mcp`,async(e,t)=>{if(!c||!d||!f){t.status(503).json({jsonrpc:`2.0`,error:{code:-32603,message:`Server initializing — please retry in a few seconds`},id:null});return}let n=p,r;p=new Promise(e=>{r=e}),await n;try{let n=new f({sessionIdGenerator:void 0});await d.connect(n),await n.handleRequest(e,t,e.body),n.close()}catch(e){if(l.error(`MCP handler error`,a(e)),!t.headersSent){let n=e instanceof Error?e.message:String(e),r=n.includes(`Not Acceptable`);t.status(r?406:500).json({jsonrpc:`2.0`,error:{code:r?-32e3:-32603,message:r?n:`Internal server error`},id:null})}}finally{r()}}),o.get(`/mcp`,(e,t)=>{t.writeHead(405).end(JSON.stringify({jsonrpc:`2.0`,error:{code:-32e3,message:`Method not allowed.`},id:null}))}),o.delete(`/mcp`,(e,t)=>{t.writeHead(405).end(JSON.stringify({jsonrpc:`2.0`,error:{code:-32e3,message:`Method not allowed.`},id:null}))});let m=o.listen(s,`127.0.0.1`,()=>{l.info(`MCP server listening`,{url:`http://127.0.0.1:${s}/mcp`,port:s}),setTimeout(async()=>{try{let[{createLazyServer:e,ALL_TOOL_NAMES:t},{StreamableHTTPServerTransport:n},{checkForUpdates:r,autoUpgradeScaffold:o}]=await Promise.all([import(`./server.js`),import(`@modelcontextprotocol/sdk/server/streamableHttp.js`),import(`./version-check.js`)]);r(),o();let s=e(i);d=s.server,f=n,c=!0,l.info(`MCP server configured (lazy — KB initializing in background)`,{toolCount:t.length,resourceCount:2}),s.startInit(),process.env.AIKIT_AUTO_INDEX===`true`?s.ready.then(async()=>{try{let e=i.sources.map(e=>e.path).join(`, `);l.info(`Running initial index`,{sourcePaths:e}),await s.runInitialIndex(),l.info(`Initial index complete`)}catch(e){l.error(`Initial index failed; will retry on aikit_reindex`,a(e))}}).catch(e=>l.error(`KB init or indexing failed`,a(e))):(s.ready.catch(e=>l.error(`KB initialization failed`,a(e))),l.info(`Auto-index disabled in HTTP mode (set AIKIT_AUTO_INDEX=true to enable)`))}catch(e){l.error(`Failed to load server modules`,a(e))}},100)}),h=async e=>{l.info(`Shutdown signal received`,{signal:e}),m.close(),d&&await d.close(),process.exit(0)};process.on(`SIGINT`,()=>h(`SIGINT`)),process.on(`SIGTERM`,()=>h(`SIGTERM`))}else{let[{loadConfig:e,reconfigureForWorkspace:t},{createLazyServer:n},{checkForUpdates:i,autoUpgradeScaffold:o},{RootsListChangedNotificationSchema:s}]=await Promise.all([import(`./config.js`),import(`./server.js`),import(`./version-check.js`),import(`@modelcontextprotocol/sdk/types.js`)]),c=e();l.info(`Config loaded`,{sourceCount:c.sources.length,storePath:c.store.path}),i(),o();let{server:u,startInit:d,ready:f,runInitialIndex:p}=n(c),{StdioServerTransport:m}=await import(`@modelcontextprotocol/sdk/server/stdio.js`),h=new m;await u.connect(h),l.info(`MCP server started`,{transport:`stdio`});let g=e=>{if(e.length===0)return!1;let n=e[0].uri,i=n.startsWith(`file://`)?r(n):n;return l.info(`MCP roots resolved`,{rootUri:n,rootPath:i,rootCount:e.length}),t(c,i),!0},_=!1;try{_=g((await u.server.listRoots()).roots),_||l.info(`No MCP roots yet; waiting for roots/list_changed notification`)}catch(e){l.warn(`MCP roots/list not supported by client; using cwd fallback`,{cwd:process.cwd(),...a(e)}),_=!0}_||=await new Promise(e=>{let t=setTimeout(()=>{l.warn(`Timed out waiting for MCP roots/list_changed; using cwd fallback`,{cwd:process.cwd()}),e(!1)},5e3);u.server.setNotificationHandler(s,async()=>{clearTimeout(t);try{e(g((await u.server.listRoots()).roots))}catch(t){l.warn(`roots/list retry failed after notification`,a(t)),e(!1)}})}),d(),f.catch(e=>{l.error(`Initialization failed — server will continue with limited tools`,a(e))}),process.env.AIKIT_AUTO_INDEX===`false`?l.warn(`Auto-index disabled; use aikit_reindex to index manually`):p().catch(e=>l.error(`Initial index failed`,a(e)))}}d().catch(e=>{l.error(`Fatal error`,a(e)),process.exit(1)});export{};
1
+ import{readFileSync as e}from"node:fs";import{dirname as t,resolve as n}from"node:path";import{fileURLToPath as r}from"node:url";import{createLogger as i,serializeError as a}from"../../core/dist/index.js";import{parseArgs as o}from"node:util";const s=t(r(import.meta.url)),c=(()=>{try{let t=n(s,`..`,`..`,`..`,`package.json`);return JSON.parse(e(t,`utf-8`)).version??`0.0.0`}catch{return`0.0.0`}})(),l=i(`server`),{values:u}=o({options:{transport:{type:`string`,default:process.env.AIKIT_TRANSPORT??`stdio`},port:{type:`string`,default:process.env.AIKIT_PORT??`3210`}}});async function d(){if(process.on(`unhandledRejection`,e=>{l.error(`Unhandled rejection`,a(e))}),l.info(`Starting MCP AI Kit server`,{version:c}),u.transport===`http`){let[{default:e},{loadConfig:t},{registerDashboardRoutes:n,resolveDashboardDir:r}]=await Promise.all([import(`express`),import(`./config.js`),import(`./dashboard-static.js`)]),i=t();l.info(`Config loaded`,{sourceCount:i.sources.length,storePath:i.store.path});let o=e();o.use(e.json());let s=Number(u.port);o.use((e,t,n)=>{if(t.setHeader(`Access-Control-Allow-Origin`,process.env.AIKIT_CORS_ORIGIN??`http://localhost:${s}`),t.setHeader(`Access-Control-Allow-Methods`,`GET, POST, DELETE, OPTIONS`),t.setHeader(`Access-Control-Allow-Headers`,`Content-Type, Authorization`),e.method===`OPTIONS`){t.status(204).end();return}n()}),n(o,r(),l),o.get(`/health`,(e,t)=>{t.json({status:`ok`})});let c=!1,d=null,f=null,p=Promise.resolve();o.post(`/mcp`,async(e,t)=>{if(!c||!d||!f){t.status(503).json({jsonrpc:`2.0`,error:{code:-32603,message:`Server initializing — please retry in a few seconds`},id:null});return}let n=p,r;p=new Promise(e=>{r=e}),await n;try{let n=new f({sessionIdGenerator:void 0});await d.connect(n),await n.handleRequest(e,t,e.body),n.close()}catch(e){if(l.error(`MCP handler error`,a(e)),!t.headersSent){let n=e instanceof Error?e.message:String(e),r=n.includes(`Not Acceptable`);t.status(r?406:500).json({jsonrpc:`2.0`,error:{code:r?-32e3:-32603,message:r?n:`Internal server error`},id:null})}}finally{r()}}),o.get(`/mcp`,(e,t)=>{t.writeHead(405).end(JSON.stringify({jsonrpc:`2.0`,error:{code:-32e3,message:`Method not allowed.`},id:null}))}),o.delete(`/mcp`,(e,t)=>{t.writeHead(405).end(JSON.stringify({jsonrpc:`2.0`,error:{code:-32e3,message:`Method not allowed.`},id:null}))});let m=o.listen(s,`127.0.0.1`,()=>{l.info(`MCP server listening`,{url:`http://127.0.0.1:${s}/mcp`,port:s}),setTimeout(async()=>{try{let[{createLazyServer:e,ALL_TOOL_NAMES:t},{StreamableHTTPServerTransport:n},{checkForUpdates:r,autoUpgradeScaffold:o}]=await Promise.all([import(`./server.js`),import(`@modelcontextprotocol/sdk/server/streamableHttp.js`),import(`./version-check.js`)]);r(),o();let s=e(i);d=s.server,f=n,c=!0,l.info(`MCP server configured (lazy — AI Kit initializing in background)`,{toolCount:t.length,resourceCount:2}),s.startInit(),process.env.AIKIT_AUTO_INDEX===`true`?s.ready.then(async()=>{try{let e=i.sources.map(e=>e.path).join(`, `);l.info(`Running initial index`,{sourcePaths:e}),await s.runInitialIndex(),l.info(`Initial index complete`)}catch(e){l.error(`Initial index failed; will retry on aikit_reindex`,a(e))}}).catch(e=>l.error(`AI Kit init or indexing failed`,a(e))):(s.ready.catch(e=>l.error(`AI Kit initialization failed`,a(e))),l.info(`Auto-index disabled in HTTP mode (set AIKIT_AUTO_INDEX=true to enable)`))}catch(e){l.error(`Failed to load server modules`,a(e))}},100)}),h=async e=>{l.info(`Shutdown signal received`,{signal:e}),m.close(),d&&await d.close(),process.exit(0)};process.on(`SIGINT`,()=>h(`SIGINT`)),process.on(`SIGTERM`,()=>h(`SIGTERM`))}else{let[{loadConfig:e,reconfigureForWorkspace:t},{createLazyServer:n},{checkForUpdates:i,autoUpgradeScaffold:o},{RootsListChangedNotificationSchema:s}]=await Promise.all([import(`./config.js`),import(`./server.js`),import(`./version-check.js`),import(`@modelcontextprotocol/sdk/types.js`)]),c=e();l.info(`Config loaded`,{sourceCount:c.sources.length,storePath:c.store.path}),i(),o();let{server:u,startInit:d,ready:f,runInitialIndex:p}=n(c),{StdioServerTransport:m}=await import(`@modelcontextprotocol/sdk/server/stdio.js`),h=new m;await u.connect(h),l.info(`MCP server started`,{transport:`stdio`});let g=e=>{if(e.length===0)return!1;let n=e[0].uri,i=n.startsWith(`file://`)?r(n):n;return l.info(`MCP roots resolved`,{rootUri:n,rootPath:i,rootCount:e.length}),t(c,i),!0},_=!1;try{_=g((await u.server.listRoots()).roots),_||l.info(`No MCP roots yet; waiting for roots/list_changed notification`)}catch(e){l.warn(`MCP roots/list not supported by client; using cwd fallback`,{cwd:process.cwd(),...a(e)}),_=!0}_||=await new Promise(e=>{let t=setTimeout(()=>{l.warn(`Timed out waiting for MCP roots/list_changed; using cwd fallback`,{cwd:process.cwd()}),e(!1)},5e3);u.server.setNotificationHandler(s,async()=>{clearTimeout(t);try{e(g((await u.server.listRoots()).roots))}catch(t){l.warn(`roots/list retry failed after notification`,a(t)),e(!1)}})}),d();let v=null,y=()=>{v&&clearTimeout(v),v=setTimeout(()=>{l.info(`Auto-shutdown: no activity for 30 minutes — exiting`),process.exit(0)},18e5),v.unref&&v.unref()};y(),process.stdin.on(`data`,()=>y()),f.catch(e=>{l.error(`Initialization failed — server will continue with limited tools`,a(e))}),process.env.AIKIT_AUTO_INDEX===`false`?l.warn(`Auto-index disabled; use aikit_reindex to index manually`):p().catch(e=>l.error(`Initial index failed`,a(e)))}}d().catch(e=>{l.error(`Fatal error`,a(e)),process.exit(1)});export{};
@@ -0,0 +1,37 @@
1
+ //#region packages/server/src/memory-monitor.d.ts
2
+ /**
3
+ * Memory monitor — periodically checks RSS and logs warnings when thresholds are exceeded.
4
+ *
5
+ * Also exposes `getRssBytes()` for on-demand checks and a `onPressure` callback
6
+ * for triggering cache eviction or GC hints when memory is high.
7
+ */
8
+ interface MemoryMonitorOptions {
9
+ /** RSS bytes above which a warning is logged. @default 1 GB */
10
+ warningBytes?: number;
11
+ /** RSS bytes above which pressure callbacks fire. @default 2 GB */
12
+ criticalBytes?: number;
13
+ /** Polling interval in milliseconds. @default 60 000 */
14
+ intervalMs?: number;
15
+ }
16
+ type PressureLevel = 'normal' | 'warning' | 'critical';
17
+ declare class MemoryMonitor {
18
+ private timer;
19
+ private readonly warningBytes;
20
+ private readonly criticalBytes;
21
+ private readonly intervalMs;
22
+ private readonly pressureFns;
23
+ private lastLevel;
24
+ constructor(opts?: MemoryMonitorOptions);
25
+ /** Register a callback for memory pressure events. */
26
+ onPressure(fn: (level: PressureLevel, rssBytes: number) => void): void;
27
+ /** Start periodic monitoring. */
28
+ start(): void;
29
+ /** Stop monitoring. */
30
+ stop(): void;
31
+ /** Return current RSS in bytes. */
32
+ getRssBytes(): number;
33
+ /** Run a single check. */
34
+ check(): PressureLevel;
35
+ }
36
+ //#endregion
37
+ export { MemoryMonitor, MemoryMonitorOptions, PressureLevel };
@@ -0,0 +1 @@
1
+ import{createLogger as e}from"../../core/dist/index.js";const t=e(`memory-monitor`);var n=class{timer=null;warningBytes;criticalBytes;intervalMs;pressureFns=[];lastLevel=`normal`;constructor(e){this.warningBytes=e?.warningBytes??1073741824,this.criticalBytes=e?.criticalBytes??2147483648,this.intervalMs=e?.intervalMs??6e4}onPressure(e){this.pressureFns.push(e)}start(){this.timer||(this.timer=setInterval(()=>this.check(),this.intervalMs),this.timer.unref&&this.timer.unref(),t.info(`Memory monitor started`,{warningMB:Math.round(this.warningBytes/1024/1024),criticalMB:Math.round(this.criticalBytes/1024/1024),intervalSec:Math.round(this.intervalMs/1e3)}))}stop(){this.timer&&=(clearInterval(this.timer),null)}getRssBytes(){return process.memoryUsage.rss()}check(){let e=this.getRssBytes(),n=`normal`;if(e>=this.criticalBytes?n=`critical`:e>=this.warningBytes&&(n=`warning`),n!==this.lastLevel||n===`critical`){let r=Math.round(e/1024/1024);n===`critical`?t.warn(`Memory CRITICAL: ${r}MB RSS — consider restarting the server`):n===`warning`?t.warn(`Memory WARNING: ${r}MB RSS`):this.lastLevel!==`normal`&&t.info(`Memory returned to normal: ${r}MB RSS`),this.lastLevel=n}if(n!==`normal`)for(let t of this.pressureFns)try{t(n,e)}catch{}return n===`critical`&&typeof globalThis.gc==`function`&&globalThis.gc(),n}};export{n as MemoryMonitor};
@@ -1,11 +1,11 @@
1
- import{completeCheckpointNames as e,completeCuratedPaths as t,completeFilePaths as n,completeStashKeys as r,completeSymbolNames as i,completeWorksetNames as a}from"./completions.js";import{completable as o}from"@modelcontextprotocol/sdk/server/completable.js";import{z as s}from"zod";function c(c,l){if(c.registerPrompt(`ready`,{title:`KB Ready`,description:`Knowledge base is ready — quick-start guide for search, onboard, and workflows`},async()=>({messages:[{role:`user`,content:{type:`text`,text:[`Knowledge base is ready. Quick start:`,``,'• **New project?** → `onboard({ path: "." })` for full codebase analysis','• **Returning?** → `status({})` then `search({ query: "SESSION CHECKPOINT", origin: "curated" })`','• **Exploring?** → `guide({ goal: "your task" })` for workflow recommendations','• **Quick lookup?** → `search({ query: "your question" })`'].join(`
1
+ import{completeCheckpointNames as e,completeCuratedPaths as t,completeFilePaths as n,completeStashKeys as r,completeSymbolNames as i,completeWorksetNames as a}from"./completions.js";import{completable as o}from"@modelcontextprotocol/sdk/server/completable.js";import{z as s}from"zod";function c(c,l){if(c.registerPrompt(`ready`,{title:`AI Kit Ready`,description:`AI Kit is ready — quick-start guide for search, onboard, and workflows`},async()=>({messages:[{role:`user`,content:{type:`text`,text:[`AI Kit is ready. Quick start:`,``,'• **New project?** → `onboard({ path: "." })` for full codebase analysis','• **Returning?** → `status({})` then `search({ query: "SESSION CHECKPOINT", origin: "curated" })`','• **Exploring?** → `guide({ goal: "your task" })` for workflow recommendations','• **Quick lookup?** → `search({ query: "your question" })`'].join(`
2
2
  `)}}]})),c.registerPrompt(`onboard`,{title:`Onboard Codebase`,description:`Analyze the codebase for first-time onboarding — runs all analyzers and produces a knowledge summary`,argsSchema:{path:s.string().optional().describe(`Path to analyze (default: workspace root)`)}},async({path:e})=>({messages:[{role:`user`,content:{type:`text`,text:[`Run the full onboarding workflow for "${e??`.`}"`,``,`1. \`onboard({ path: "${e??`.`}" })\` — full codebase analysis`,`2. \`produce_knowledge({ path: "${e??`.`}" })\` — generate synthesis`,"3. `remember` key findings as curated entries","4. `status` to verify index health"].join(`
3
- `)}}]})),c.registerPrompt(`sessionStart`,{title:`Start KB Session`,description:`Initialize a KB session — check status, list knowledge, and resume from last checkpoint`},async()=>({messages:[{role:`user`,content:{type:`text`,text:[`Run the session start protocol:`,``,"1. `status({})` — check KB health and onboard state","2. `list()` — see stored knowledge entries",'3. `search({ query: "SESSION CHECKPOINT", origin: "curated" })` — resume prior work'].join(`
4
- `)}}]})),c.registerPrompt(`sessionEnd`,{title:`End KB Session`,description:`Persist decisions and create a session checkpoint before ending`,argsSchema:{summary:s.string().describe(`Brief summary of decisions made, blockers encountered, and next steps`)}},async({summary:e})=>({messages:[{role:`user`,content:{type:`text`,text:[`Run the session end protocol:`,``,'1. `remember({ title: "Session checkpoint: '+e.slice(0,60)+`...", content: "`+e.replace(/"/g,`\\"`)+'", category: "conventions" })` — persist findings',"2. `reindex({})` — refresh search index if files changed",`3. Confirm session data saved`].join(`
5
- `)}}]})),c.registerPrompt(`search`,{title:`Search Knowledge Base`,description:`Search the knowledge base with hybrid semantic + keyword search`,argsSchema:{query:s.string().describe(`Search query`)}},async({query:e})=>({messages:[{role:`user`,content:{type:`text`,text:`Search the knowledge base for: "${e}"\n\nUse \`search({ query: "${e.replace(/"/g,`\\"`)}" })\` to find relevant code, documentation, and curated knowledge.`}}]})),c.registerPrompt(`remember`,{title:`Remember Knowledge`,description:`Store a decision, convention, or finding as curated knowledge`,argsSchema:{title:s.string().describe(`Title of the knowledge entry`),content:s.string().describe(`Content to remember`),category:s.enum([`conventions`,`decisions`,`patterns`,`blockers`,`tasks`]).optional().describe(`Category (default: conventions)`)}},async({title:e,content:t,category:n})=>({messages:[{role:`user`,content:{type:`text`,text:`Store this knowledge:\n\n\`remember({ title: "${e.replace(/"/g,`\\"`)}", content: "${t.replace(/"/g,`\\"`).slice(0,200)}...", category: "${n??`conventions`}" })\``}}]})),c.registerPrompt(`planTask`,{title:`Plan a Task`,description:`Generate a reading plan and scope map for a development task`,argsSchema:{task:s.string().describe(`Description of the task to plan`)}},async({task:e})=>({messages:[{role:`user`,content:{type:`text`,text:[`Plan implementation for: "${e}"`,``,'1. `search({ query: "'+e.replace(/"/g,`\\"`)+'" })` — find related code and prior decisions','2. `scope_map({ task: "'+e.replace(/"/g,`\\"`)+'" })` — generate a reading plan',"3. For each recommended file, use `file_summary` then `compact` for detail","4. `blast_radius` on planned changes to assess impact"].join(`
3
+ `)}}]})),c.registerPrompt(`sessionStart`,{title:`Start AI Kit Session`,description:`Initialize an AI Kit session — check status, list knowledge, and resume from last checkpoint`},async()=>({messages:[{role:`user`,content:{type:`text`,text:[`Run the session start protocol:`,``,"1. `status({})` — check AI Kit health and onboard state","2. `list()` — see stored knowledge entries",'3. `search({ query: "SESSION CHECKPOINT", origin: "curated" })` — resume prior work'].join(`
4
+ `)}}]})),c.registerPrompt(`sessionEnd`,{title:`End AI Kit Session`,description:`Persist decisions and create a session checkpoint before ending`,argsSchema:{summary:s.string().describe(`Brief summary of decisions made, blockers encountered, and next steps`)}},async({summary:e})=>({messages:[{role:`user`,content:{type:`text`,text:[`Run the session end protocol:`,``,'1. `remember({ title: "Session checkpoint: '+e.slice(0,60)+`...", content: "`+e.replace(/"/g,`\\"`)+'", category: "conventions" })` — persist findings',"2. `reindex({})` — refresh search index if files changed",`3. Confirm session data saved`].join(`
5
+ `)}}]})),c.registerPrompt(`search`,{title:`Search AI Kit`,description:`Search AI Kit with hybrid semantic + keyword search`,argsSchema:{query:s.string().describe(`Search query`)}},async({query:e})=>({messages:[{role:`user`,content:{type:`text`,text:`Search AI Kit for: "${e}"\n\nUse \`search({ query: "${e.replace(/"/g,`\\"`)}" })\` to find relevant code, documentation, and curated knowledge.`}}]})),c.registerPrompt(`remember`,{title:`Remember Knowledge`,description:`Store a decision, convention, or finding as curated knowledge`,argsSchema:{title:s.string().describe(`Title of the knowledge entry`),content:s.string().describe(`Content to remember`),category:s.enum([`conventions`,`decisions`,`patterns`,`blockers`,`tasks`]).optional().describe(`Category (default: conventions)`)}},async({title:e,content:t,category:n})=>({messages:[{role:`user`,content:{type:`text`,text:`Store this knowledge:\n\n\`remember({ title: "${e.replace(/"/g,`\\"`)}", content: "${t.replace(/"/g,`\\"`).slice(0,200)}...", category: "${n??`conventions`}" })\``}}]})),c.registerPrompt(`planTask`,{title:`Plan a Task`,description:`Generate a reading plan and scope map for a development task`,argsSchema:{task:s.string().describe(`Description of the task to plan`)}},async({task:e})=>({messages:[{role:`user`,content:{type:`text`,text:[`Plan implementation for: "${e}"`,``,'1. `search({ query: "'+e.replace(/"/g,`\\"`)+'" })` — find related code and prior decisions','2. `scope_map({ task: "'+e.replace(/"/g,`\\"`)+'" })` — generate a reading plan',"3. For each recommended file, use `file_summary` then `compact` for detail","4. `blast_radius` on planned changes to assess impact"].join(`
6
6
  `)}}]})),c.registerPrompt(`investigate`,{title:`Investigate Bug`,description:`Bug investigation workflow — parse error, find symbols, trace data flow, assess impact`,argsSchema:{error:s.string().describe(`Error message, stack trace, or bug description`)}},async({error:e})=>({messages:[{role:`user`,content:{type:`text`,text:[`Investigate this error:`,"```",e,"```",``,`Follow the bug investigation workflow:`,"1. `parse_output({ text: <error> })` — extract structured data from the error","2. `symbol({ name: <relevant symbol> })` — find definition and references",'3. `trace({ symbol: <symbol>, direction: "backward" })` — trace data flow to find root cause',"4. `blast_radius({ changed_files: [<affected files>] })` — assess fix impact"].join(`
7
7
  `)}}]})),!l)return;let{curated:u,store:d,graphStore:f}=l;c.registerPrompt(`read_knowledge`,{title:`Read Knowledge`,description:`Read a curated knowledge entry by path — with autocomplete`,argsSchema:{path:o(s.string().describe(`Path of the curated entry (e.g. conventions/naming.md)`),async e=>t(u,e))}},async({path:e})=>({messages:[{role:`user`,content:{type:`text`,text:`Read the curated knowledge entry: \`read({ path: "${e}" })\``}}]})),c.registerPrompt(`update_knowledge`,{title:`Update Knowledge`,description:`Update an existing curated knowledge entry — with autocomplete`,argsSchema:{path:o(s.string().describe(`Path of the curated entry to update`),async e=>t(u,e)),content:s.string().describe(`New content for the entry`)}},async({path:e,content:t})=>({messages:[{role:`user`,content:{type:`text`,text:`Update the curated entry at "${e}":\n\n\`update({ path: "${e}", content: "${t.replace(/"/g,`\\"`).slice(0,200)}..." })\``}}]})),c.registerPrompt(`forget_knowledge`,{title:`Forget Knowledge`,description:`Delete a curated knowledge entry — with autocomplete`,argsSchema:{path:o(s.string().describe(`Path of the curated entry to delete`),async e=>t(u,e))}},async({path:e})=>({messages:[{role:`user`,content:{type:`text`,text:`Delete the curated entry: \`forget({ path: "${e}" })\``}}]})),c.registerPrompt(`lookup_file`,{title:`Lookup File`,description:`Look up an indexed file — with autocomplete`,argsSchema:{path:o(s.string().describe(`Source file path`),async e=>n(d,e))}},async({path:e})=>({messages:[{role:`user`,content:{type:`text`,text:`Look up the indexed file: \`file_summary({ path: "${e}" })\``}}]})),c.registerPrompt(`find_symbol`,{title:`Find Symbol`,description:`Find a symbol definition and references — with autocomplete`,argsSchema:{name:o(s.string().describe(`Symbol name`),async e=>i(f,e))}},async({name:e})=>({messages:[{role:`user`,content:{type:`text`,text:`Find symbol: \`symbol({ name: "${e}" })\``}}]})),c.registerPrompt(`get_stash`,{title:`Get Stash`,description:`Retrieve a stashed value by key — with autocomplete`,argsSchema:{key:o(s.string().describe(`Stash key`),e=>r(e))}},async({key:e})=>({messages:[{role:`user`,content:{type:`text`,text:`Get stashed value: \`stash({ action: "get", key: "${e}" })\``}}]})),c.registerPrompt(`get_workset`,{title:`Get Workset`,description:`Load a saved workset by name — with autocomplete`,argsSchema:{name:o(s.string().describe(`Workset name`),e=>a(e))}},async({name:e})=>({messages:[{role:`user`,content:{type:`text`,text:`Load workset: \`workset({ action: "load", name: "${e}" })\``}}]})),c.registerPrompt(`load_checkpoint`,{title:`Load Checkpoint`,description:`Restore a saved checkpoint by name — with autocomplete`,argsSchema:{name:o(s.string().describe(`Checkpoint name`),t=>e(t))}},async({name:e})=>({messages:[{role:`user`,content:{type:`text`,text:`Load checkpoint: \`checkpoint({ action: "load", name: "${e}" })\``}}]})),c.registerPrompt(`audit_workflow`,{title:`Audit Workflow`,description:`Run a full project quality audit: check → test_run → audit → produce recommendations`},async()=>({messages:[{role:`user`,content:{type:`text`,text:[`## Audit Workflow`,``,`Execute the following tools in sequence:`,``,"1. `check({})` — Run typecheck + lint","2. `test_run({})` — Run all tests","3. `audit({})` — Full project audit","4. Review findings and `remember` important patterns",``,`Report: pass/fail status, error counts, audit recommendations, and suggested next steps.`].join(`
8
- `)}}]})),c.registerPrompt(`refactor_workflow`,{title:`Refactor Workflow`,description:`Safe refactoring workflow: analyze → blast_radius → implement → verify`},async()=>({messages:[{role:`user`,content:{type:`text`,text:[`## Refactor Workflow`,``,`For the target files/symbols:`,``,"1. `analyze_structure({ path })` — Understand current structure","2. `analyze_dependencies({ path })` — Map dependency graph","3. `blast_radius({ changed_files })` — Assess impact scope",`4. Implement changes carefully`,"5. `check({})` + `test_run({})` — Verify nothing broke","6. `reindex({})` — Update KB with changes",``,`What would you like to refactor?`].join(`
8
+ `)}}]})),c.registerPrompt(`refactor_workflow`,{title:`Refactor Workflow`,description:`Safe refactoring workflow: analyze → blast_radius → implement → verify`},async()=>({messages:[{role:`user`,content:{type:`text`,text:[`## Refactor Workflow`,``,`For the target files/symbols:`,``,"1. `analyze_structure({ path })` — Understand current structure","2. `analyze_dependencies({ path })` — Map dependency graph","3. `blast_radius({ changed_files })` — Assess impact scope",`4. Implement changes carefully`,"5. `check({})` + `test_run({})` — Verify nothing broke","6. `reindex({})` — Update AI Kit with changes",``,`What would you like to refactor?`].join(`
9
9
  `)}}]})),c.registerPrompt(`forge_workflow`,{title:`FORGE Workflow`,description:`Full FORGE protocol: classify → ground → implement with quality gates`},async()=>({messages:[{role:`user`,content:{type:`text`,text:[`## FORGE Quality Gate Workflow`,``,"1. `forge_classify({ task })` — Determine complexity tier (floor/standard/critical)","2. `forge_ground({ task, files, root_path })` — Full grounding analysis",`3. Review typed unknowns and constraints`,`4. Implement with evidence tracking`,'5. `evidence_map({ action: "validate" })` — Verify all claims',"6. `check({})` + `test_run({})` — Final verification",``,`Describe the task to classify:`].join(`
10
10
  `)}}]})),c.registerPrompt(`investigate_workflow`,{title:`Investigate Issue`,description:`Bug investigation workflow: trace → analyze → find root cause → fix`},async()=>({messages:[{role:`user`,content:{type:`text`,text:[`## Bug Investigation Workflow`,``,"1. `parse_output({ output })` — Parse error messages if available","2. `symbol({ name })` — Find the failing symbol",'3. `trace({ symbol, direction: "backward" })` — Trace data flow to root cause',"4. `blast_radius({ changed_files })` — Assess impact of fix",`5. Implement fix`,"6. `check({})` + `test_run({})` — Verify fix works",``,`Describe the issue:`].join(`
11
11
  `)}}]})),c.registerPrompt(`context_workflow`,{title:`Build Context`,description:`Progressive context building: file_summary → compact → digest for deep understanding`},async()=>({messages:[{role:`user`,content:{type:`text`,text:[`## Context Building Workflow`,``,`Build understanding progressively:`,``,"1. `file_summary({ path })` — Structure overview (~50 tokens)","2. `compact({ path, query })` — Extract relevant sections (5-20x reduction)","3. `digest({ sources })` — Multi-file compressed summary","4. `stratum_card({ files })` — Reusable reference card",``,`For project-wide context:`,"- `scope_map({ task })` — Reading plan for a specific task",'- `workset({ action: "save" })` — Save working set for later',``,`What do you need to understand?`].join(`
@@ -1,7 +1,7 @@
1
1
  //#region packages/server/src/resource-links.d.ts
2
2
  /**
3
3
  * Helpers for emitting resource_link content blocks in tool results.
4
- * Centralises the `kb://curated/{path}` URI scheme so all tools stay consistent.
4
+ * Centralises the `aikit://curated/{path}` URI scheme so all tools stay consistent.
5
5
  */
6
6
  /** Shape of a resource_link content block (matches MCP SDK ResourceLink). */
7
7
  interface ResourceLinkBlock {
@@ -1 +1 @@
1
- function e(e,t,n){if(!(!e||typeof e!=`string`)&&!(e.includes(`..`)||e.startsWith(`/`)||e.startsWith(`[`)))return{type:`resource_link`,uri:`kb://curated/${e}`,name:t??e,mimeType:`text/markdown`,...n?{description:n}:{}}}function t(t){let n=new Set,r=[];for(let i of t){let t=e(i.path,i.title,i.category?`[${i.category}]`:void 0);t&&!n.has(t.uri)&&(n.add(t.uri),r.push(t))}return r}function n(e){if(e.startsWith(`.ai/curated/`))return e.slice(12)}export{e as curatedResourceLink,t as curatedResourceLinks,n as extractCuratedPath};
1
+ function e(e,t,n){if(!(!e||typeof e!=`string`)&&!(e.includes(`..`)||e.startsWith(`/`)||e.startsWith(`[`)))return{type:`resource_link`,uri:`aikit://curated/${e}`,name:t??e,mimeType:`text/markdown`,...n?{description:n}:{}}}function t(t){let n=new Set,r=[];for(let i of t){let t=e(i.path,i.title,i.category?`[${i.category}]`:void 0);t&&!n.has(t.uri)&&(n.add(t.uri),r.push(t))}return r}function n(e){if(e.startsWith(`.ai/curated/`))return e.slice(12)}export{e as curatedResourceLink,t as curatedResourceLinks,n as extractCuratedPath};
@@ -5,8 +5,8 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
5
  /**
6
6
  * Register curated knowledge as browsable MCP resources.
7
7
  *
8
- * - `kb://curated` — index listing all curated entries
9
- * - `kb://curated/{path}` — individual entry by path (e.g. `decisions/use-lancedb.md`)
8
+ * - `aikit://curated` — index listing all curated entries
9
+ * - `aikit://curated/{path}` — individual entry by path (e.g. `decisions/use-lancedb.md`)
10
10
  */
11
11
  declare function registerCuratedResources(server: McpServer, curated: CuratedKnowledgeManager): void;
12
12
  //#endregion
@@ -1,2 +1,2 @@
1
- import{ResourceTemplate as e}from"@modelcontextprotocol/sdk/server/mcp.js";function t(t,n){t.resource(`aikit-curated-index`,`kb://curated`,{description:`Index of all curated knowledge entries`,mimeType:`text/markdown`},async()=>{let e=(await n.list()).map(e=>`- [${e.title}](kb://curated/${e.path}) — ${e.category}`);return{contents:[{uri:`kb://curated`,text:`# Curated Knowledge Index\n\n${e.length>0?e.join(`
2
- `):`_No curated entries yet._`}`,mimeType:`text/markdown`}]}});let r=new e(`kb://curated/{+path}`,{list:async()=>({resources:(await n.list()).map(e=>({uri:`kb://curated/${e.path}`,name:e.title,description:`[${e.category}] ${e.contentPreview?.slice(0,80)??``}`,mimeType:`text/markdown`}))})});t.resource(`aikit-curated-entry`,r,{description:`A curated knowledge entry`,mimeType:`text/markdown`},async(e,t)=>{let r=t.path;if(!r)throw Error(`Missing path variable in curated resource URI`);let i=await n.read(r);return{contents:[{uri:e.toString(),text:`---\ntitle: ${i.title}\ncategory: ${i.category}\ntags: ${i.tags?.join(`, `)??``}\nversion: ${i.version??1}\n---\n\n${i.content??i.contentPreview??``}`,mimeType:`text/markdown`}]}})}export{t as registerCuratedResources};
1
+ import{ResourceTemplate as e}from"@modelcontextprotocol/sdk/server/mcp.js";function t(t,n){t.resource(`aikit-curated-index`,`aikit://curated`,{description:`Index of all curated knowledge entries`,mimeType:`text/markdown`},async()=>{let e=(await n.list()).map(e=>`- [${e.title}](aikit://curated/${e.path}) — ${e.category}`);return{contents:[{uri:`aikit://curated`,text:`# Curated Knowledge Index\n\n${e.length>0?e.join(`
2
+ `):`_No curated entries yet._`}`,mimeType:`text/markdown`}]}});let r=new e(`aikit://curated/{+path}`,{list:async()=>({resources:(await n.list()).map(e=>({uri:`aikit://curated/${e.path}`,name:e.title,description:`[${e.category}] ${e.contentPreview?.slice(0,80)??``}`,mimeType:`text/markdown`}))})});t.resource(`aikit-curated-entry`,r,{description:`A curated knowledge entry`,mimeType:`text/markdown`},async(e,t)=>{let r=t.path;if(!r)throw Error(`Missing path variable in curated resource URI`);let i=await n.read(r);return{contents:[{uri:e.toString(),text:`---\ntitle: ${i.title}\ncategory: ${i.category}\ntags: ${i.tags?.join(`, `)??``}\nversion: ${i.version??1}\n---\n\n${i.content??i.contentPreview??``}`,mimeType:`text/markdown`}]}})}export{t as registerCuratedResources};
@@ -5,7 +5,7 @@ declare class ResourceNotifier {
5
5
  private readonly mcpServer;
6
6
  constructor(mcpServer: McpServer);
7
7
  /**
8
- * Notify that the KB status resource has changed.
8
+ * Notify that the AI Kit status resource has changed.
9
9
  * Call after: reindex, onboard, remember, forget operations.
10
10
  */
11
11
  notifyStatusChanged(): Promise<void>;
@@ -1 +1 @@
1
- import{createLogger as e}from"../../../core/dist/index.js";const t=e(`resource-notifier`);var n=class{constructor(e){this.mcpServer=e}async notifyStatusChanged(){await this.sendUpdate(`kb://status`)}async notifyFileTreeChanged(){await this.sendUpdate(`kb://file-tree`)}async notifyCuratedIndexChanged(){await this.sendUpdate(`kb://curated`)}async notifyCuratedEntryChanged(e){await this.sendUpdate(`kb://curated/${e}`)}async notifyResourceListChanged(){try{await this.mcpServer.server.sendResourceListChanged()}catch(e){t.debug(`sendResourceListChanged failed`,{error:String(e)})}}async notifyAfterReindex(){await Promise.allSettled([this.notifyStatusChanged(),this.notifyFileTreeChanged()])}async notifyAfterCuratedWrite(e){let t=[this.notifyStatusChanged(),this.notifyCuratedIndexChanged()];e&&t.push(this.notifyCuratedEntryChanged(e)),await Promise.allSettled(t)}async sendUpdate(e){try{await this.mcpServer.server.sendResourceUpdated({uri:e})}catch(n){t.debug(`sendResourceUpdated failed`,{uri:e,error:String(n)})}}};const r={notifyStatusChanged:async()=>{},notifyFileTreeChanged:async()=>{},notifyCuratedIndexChanged:async()=>{},notifyCuratedEntryChanged:async()=>{},notifyResourceListChanged:async()=>{},notifyAfterReindex:async()=>{},notifyAfterCuratedWrite:async()=>{}};export{n as ResourceNotifier,r as noopResourceNotifier};
1
+ import{createLogger as e}from"../../../core/dist/index.js";const t=e(`resource-notifier`);var n=class{constructor(e){this.mcpServer=e}async notifyStatusChanged(){await this.sendUpdate(`aikit://status`)}async notifyFileTreeChanged(){await this.sendUpdate(`aikit://file-tree`)}async notifyCuratedIndexChanged(){await this.sendUpdate(`aikit://curated`)}async notifyCuratedEntryChanged(e){await this.sendUpdate(`aikit://curated/${e}`)}async notifyResourceListChanged(){try{await this.mcpServer.server.sendResourceListChanged()}catch(e){t.debug(`sendResourceListChanged failed`,{error:String(e)})}}async notifyAfterReindex(){await Promise.allSettled([this.notifyStatusChanged(),this.notifyFileTreeChanged()])}async notifyAfterCuratedWrite(e){let t=[this.notifyStatusChanged(),this.notifyCuratedIndexChanged()];e&&t.push(this.notifyCuratedEntryChanged(e)),await Promise.allSettled(t)}async sendUpdate(e){try{await this.mcpServer.server.sendResourceUpdated({uri:e})}catch(n){t.debug(`sendResourceUpdated failed`,{uri:e,error:String(n)})}}};const r={notifyStatusChanged:async()=>{},notifyFileTreeChanged:async()=>{},notifyCuratedIndexChanged:async()=>{},notifyCuratedEntryChanged:async()=>{},notifyResourceListChanged:async()=>{},notifyAfterReindex:async()=>{},notifyAfterCuratedWrite:async()=>{}};export{n as ResourceNotifier,r as noopResourceNotifier};
@@ -1,2 +1,2 @@
1
- import{registerCuratedResources as e}from"./curated-resources.js";function t(t,n,r){t.resource(`aikit-status`,`kb://status`,{description:`Current knowledge base status and statistics`,mimeType:`text/plain`},async()=>{let e=await n.getStats();return{contents:[{uri:`kb://status`,text:`Knowledge Base: ${e.totalRecords} records from ${e.totalFiles} files. Last indexed: ${e.lastIndexedAt??`Never`}`,mimeType:`text/plain`}]}}),t.resource(`aikit-file-tree`,`kb://file-tree`,{description:`List of all indexed source files`,mimeType:`text/plain`},async()=>({contents:[{uri:`kb://file-tree`,text:(await n.listSourcePaths()).sort().join(`
1
+ import{registerCuratedResources as e}from"./curated-resources.js";function t(t,n,r){t.resource(`aikit-status`,`aikit://status`,{description:`Current AI Kit status and statistics`,mimeType:`text/plain`},async()=>{let e=await n.getStats();return{contents:[{uri:`aikit://status`,text:`AI Kit: ${e.totalRecords} records from ${e.totalFiles} files. Last indexed: ${e.lastIndexedAt??`Never`}`,mimeType:`text/plain`}]}}),t.resource(`aikit-file-tree`,`aikit://file-tree`,{description:`List of all indexed source files`,mimeType:`text/plain`},async()=>({contents:[{uri:`aikit://file-tree`,text:(await n.listSourcePaths()).sort().join(`
2
2
  `),mimeType:`text/plain`}]})),e(t,r)}export{t as registerResources};
@@ -1,3 +1,4 @@
1
+ import { BackgroundTaskScheduler } from "./background-task.js";
1
2
  import { CuratedKnowledgeManager } from "./curated-manager.js";
2
3
  import { ResourceNotifier } from "./resources/resource-notifier.js";
3
4
  import { ISamplingClient } from "./sampling.js";
@@ -41,7 +42,8 @@ declare function createLazyServer(config: KBConfig): {
41
42
  server: McpServer; /** Call after MCP roots are resolved (or fallback decided) to start heavy init. */
42
43
  startInit: () => void;
43
44
  ready: Promise<void>;
44
- runInitialIndex: () => Promise<void>;
45
+ runInitialIndex: () => Promise<void>; /** Background task scheduler — use to queue heavy operations and check status. */
46
+ scheduler: BackgroundTaskScheduler;
45
47
  };
46
48
  //#endregion
47
49
  export { ALL_TOOL_NAMES, KnowledgeBaseComponents, createLazyServer, createMcpServer, createServer, initializeKnowledgeBase, registerMcpTools };