gnosys 5.9.5 → 5.11.0

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 (490) hide show
  1. package/README.md +91 -771
  2. package/dist/cli.d.ts +0 -1
  3. package/dist/cli.js +403 -141
  4. package/dist/index.d.ts +3 -1
  5. package/dist/index.js +427 -348
  6. package/dist/lib/archive.d.ts +3 -4
  7. package/dist/lib/archive.js +0 -1
  8. package/dist/lib/ask.d.ts +3 -4
  9. package/dist/lib/ask.js +0 -1
  10. package/dist/lib/atomicWrite.d.ts +7 -0
  11. package/dist/lib/atomicWrite.js +46 -0
  12. package/dist/lib/attachments.d.ts +0 -1
  13. package/dist/lib/attachments.js +0 -1
  14. package/dist/lib/audioExtract.d.ts +0 -1
  15. package/dist/lib/audioExtract.js +0 -1
  16. package/dist/lib/audit.d.ts +0 -1
  17. package/dist/lib/audit.js +0 -1
  18. package/dist/lib/bootstrap.d.ts +1 -2
  19. package/dist/lib/bootstrap.js +0 -1
  20. package/dist/lib/centralize.d.ts +20 -0
  21. package/dist/lib/centralize.js +34 -0
  22. package/dist/lib/chat/SlashPalette.d.ts +2 -3
  23. package/dist/lib/chat/SlashPalette.js +0 -1
  24. package/dist/lib/chat/boot-splash.d.ts +1 -2
  25. package/dist/lib/chat/boot-splash.js +0 -1
  26. package/dist/lib/chat/choose.d.ts +0 -1
  27. package/dist/lib/chat/choose.js +0 -2
  28. package/dist/lib/chat/commands.d.ts +1 -2
  29. package/dist/lib/chat/commands.js +0 -1
  30. package/dist/lib/chat/components/CitationText.d.ts +1 -2
  31. package/dist/lib/chat/components/CitationText.js +0 -1
  32. package/dist/lib/chat/components/MarkdownRenderer.d.ts +0 -1
  33. package/dist/lib/chat/components/MarkdownRenderer.js +0 -1
  34. package/dist/lib/chat/components/ToolCallCard.d.ts +1 -2
  35. package/dist/lib/chat/components/ToolCallCard.js +0 -1
  36. package/dist/lib/chat/focus.d.ts +3 -3
  37. package/dist/lib/chat/focus.js +0 -1
  38. package/dist/lib/chat/index.d.ts +3 -4
  39. package/dist/lib/chat/index.js +2 -2
  40. package/dist/lib/chat/intent.d.ts +1 -17
  41. package/dist/lib/chat/intent.js +2 -3
  42. package/dist/lib/chat/llmTurn.d.ts +5 -6
  43. package/dist/lib/chat/llmTurn.js +0 -1
  44. package/dist/lib/chat/recall.d.ts +2 -3
  45. package/dist/lib/chat/recall.js +1 -2
  46. package/dist/lib/chat/render.d.ts +2 -3
  47. package/dist/lib/chat/render.js +0 -1
  48. package/dist/lib/chat/session.d.ts +0 -1
  49. package/dist/lib/chat/session.js +0 -1
  50. package/dist/lib/chat/theme.d.ts +0 -1
  51. package/dist/lib/chat/theme.js +0 -1
  52. package/dist/lib/chat/toolFence.d.ts +2 -2
  53. package/dist/lib/chat/toolFence.js +0 -1
  54. package/dist/lib/chat/tools.d.ts +0 -1
  55. package/dist/lib/chat/tools.js +0 -1
  56. package/dist/lib/chat/types.d.ts +0 -1
  57. package/dist/lib/chat/types.js +0 -1
  58. package/dist/lib/chat/write.d.ts +5 -5
  59. package/dist/lib/chat/write.js +0 -1
  60. package/dist/lib/chunkSplitter.d.ts +0 -1
  61. package/dist/lib/chunkSplitter.js +0 -1
  62. package/dist/lib/cleanup.d.ts +0 -1
  63. package/dist/lib/cleanup.js +0 -1
  64. package/dist/lib/config.d.ts +0 -1
  65. package/dist/lib/config.js +3 -3
  66. package/dist/lib/dashboard.d.ts +3 -4
  67. package/dist/lib/dashboard.js +5 -2
  68. package/dist/lib/db.d.ts +24 -1
  69. package/dist/lib/db.js +162 -16
  70. package/dist/lib/dbSearch.d.ts +3 -4
  71. package/dist/lib/dbSearch.js +0 -1
  72. package/dist/lib/dbWrite.d.ts +2 -3
  73. package/dist/lib/dbWrite.js +0 -1
  74. package/dist/lib/desktopNotify.d.ts +0 -1
  75. package/dist/lib/desktopNotify.js +0 -1
  76. package/dist/lib/docxExtract.d.ts +0 -1
  77. package/dist/lib/docxExtract.js +22 -1
  78. package/dist/lib/dream.d.ts +2 -3
  79. package/dist/lib/dream.js +2 -2
  80. package/dist/lib/embeddings.d.ts +0 -3
  81. package/dist/lib/embeddings.js +7 -3
  82. package/dist/lib/export.d.ts +6 -2
  83. package/dist/lib/export.js +24 -2
  84. package/dist/lib/exportProject.d.ts +4 -3
  85. package/dist/lib/exportProject.js +3 -1
  86. package/dist/lib/federated.d.ts +3 -3
  87. package/dist/lib/federated.js +6 -4
  88. package/dist/lib/fileDetect.d.ts +0 -1
  89. package/dist/lib/fileDetect.js +0 -1
  90. package/dist/lib/graph.d.ts +4 -4
  91. package/dist/lib/graph.js +0 -1
  92. package/dist/lib/heartbeat.d.ts +0 -13
  93. package/dist/lib/heartbeat.js +1 -2
  94. package/dist/lib/hybridSearch.d.ts +5 -21
  95. package/dist/lib/hybridSearch.js +0 -1
  96. package/dist/lib/idFormat.d.ts +0 -2
  97. package/dist/lib/idFormat.js +1 -2
  98. package/dist/lib/imageExtract.d.ts +0 -1
  99. package/dist/lib/imageExtract.js +0 -1
  100. package/dist/lib/import.d.ts +6 -5
  101. package/dist/lib/import.js +3 -33
  102. package/dist/lib/importProject.d.ts +4 -4
  103. package/dist/lib/importProject.js +1 -2
  104. package/dist/lib/ingest.d.ts +3 -4
  105. package/dist/lib/ingest.js +0 -1
  106. package/dist/lib/lensing.d.ts +1 -2
  107. package/dist/lib/lensing.js +0 -1
  108. package/dist/lib/llm.d.ts +6 -45
  109. package/dist/lib/llm.js +27 -9
  110. package/dist/lib/lock.d.ts +0 -1
  111. package/dist/lib/lock.js +0 -1
  112. package/dist/lib/log.d.ts +4 -0
  113. package/dist/lib/log.js +85 -0
  114. package/dist/lib/machineConfig.d.ts +86 -0
  115. package/dist/lib/machineConfig.js +157 -0
  116. package/dist/lib/machineMigrate.d.ts +38 -0
  117. package/dist/lib/machineMigrate.js +102 -0
  118. package/dist/lib/maintenance.d.ts +4 -5
  119. package/dist/lib/maintenance.js +0 -1
  120. package/dist/lib/mcpClientConfig.d.ts +20 -0
  121. package/dist/lib/mcpClientConfig.js +62 -0
  122. package/dist/lib/mcpHttp.d.ts +52 -0
  123. package/dist/lib/mcpHttp.js +227 -0
  124. package/dist/lib/migrate.d.ts +1 -2
  125. package/dist/lib/migrate.js +1 -1
  126. package/dist/lib/modelValidation.d.ts +0 -1
  127. package/dist/lib/modelValidation.js +0 -1
  128. package/dist/lib/multimodalIngest.d.ts +1 -2
  129. package/dist/lib/multimodalIngest.js +0 -1
  130. package/dist/lib/packageManager.d.ts +5 -0
  131. package/dist/lib/packageManager.js +30 -0
  132. package/dist/lib/paths.d.ts +11 -7
  133. package/dist/lib/paths.js +14 -2
  134. package/dist/lib/pdfExtract.d.ts +0 -1
  135. package/dist/lib/pdfExtract.js +0 -1
  136. package/dist/lib/portfolio.d.ts +1 -2
  137. package/dist/lib/portfolio.js +10 -3
  138. package/dist/lib/portfolioHtml.d.ts +1 -2
  139. package/dist/lib/portfolioHtml.js +0 -1
  140. package/dist/lib/preferences.d.ts +6 -2
  141. package/dist/lib/preferences.js +19 -1
  142. package/dist/lib/progress.d.ts +2 -2
  143. package/dist/lib/progress.js +0 -1
  144. package/dist/lib/projectIdentity.d.ts +1 -2
  145. package/dist/lib/projectIdentity.js +5 -5
  146. package/dist/lib/projectPaths.d.ts +60 -0
  147. package/dist/lib/projectPaths.js +99 -0
  148. package/dist/lib/projectScan.d.ts +40 -0
  149. package/dist/lib/projectScan.js +96 -0
  150. package/dist/lib/recall.d.ts +6 -6
  151. package/dist/lib/recall.js +0 -1
  152. package/dist/lib/remote.d.ts +3 -20
  153. package/dist/lib/remote.js +44 -2
  154. package/dist/lib/remoteWizard.d.ts +2 -3
  155. package/dist/lib/remoteWizard.js +0 -1
  156. package/dist/lib/resolver.d.ts +1 -2
  157. package/dist/lib/resolver.js +2 -3
  158. package/dist/lib/retry.d.ts +0 -1
  159. package/dist/lib/retry.js +1 -2
  160. package/dist/lib/rulesGen.d.ts +2 -8
  161. package/dist/lib/rulesGen.js +1 -2
  162. package/dist/lib/search.d.ts +1 -2
  163. package/dist/lib/search.js +1 -1
  164. package/dist/lib/searchTypes.d.ts +18 -0
  165. package/dist/lib/searchTypes.js +2 -0
  166. package/dist/lib/setup/coldStart.d.ts +0 -1
  167. package/dist/lib/setup/coldStart.js +0 -1
  168. package/dist/lib/setup/configSetRender.d.ts +0 -1
  169. package/dist/lib/setup/configSetRender.js +0 -1
  170. package/dist/lib/setup/dreamRender.d.ts +0 -1
  171. package/dist/lib/setup/dreamRender.js +0 -1
  172. package/dist/lib/setup/dreamState.d.ts +2 -2
  173. package/dist/lib/setup/dreamState.js +0 -1
  174. package/dist/lib/setup/modelsRender.d.ts +0 -1
  175. package/dist/lib/setup/modelsRender.js +0 -1
  176. package/dist/lib/setup/remoteRender.d.ts +0 -1
  177. package/dist/lib/setup/remoteRender.js +0 -1
  178. package/dist/lib/setup/routingRender.d.ts +0 -1
  179. package/dist/lib/setup/routingRender.js +0 -1
  180. package/dist/lib/setup/sections/ides.d.ts +1 -2
  181. package/dist/lib/setup/sections/ides.js +3 -2
  182. package/dist/lib/setup/sections/preferences.d.ts +1 -2
  183. package/dist/lib/setup/sections/preferences.js +3 -2
  184. package/dist/lib/setup/sections/routing.d.ts +1 -2
  185. package/dist/lib/setup/sections/routing.js +0 -1
  186. package/dist/lib/setup/storePath.d.ts +0 -1
  187. package/dist/lib/setup/storePath.js +0 -1
  188. package/dist/lib/setup/summary.d.ts +1 -2
  189. package/dist/lib/setup/summary.js +0 -1
  190. package/dist/lib/setup/syncProjectsRender.d.ts +0 -1
  191. package/dist/lib/setup/syncProjectsRender.js +0 -1
  192. package/dist/lib/setup/ui/diff.d.ts +0 -1
  193. package/dist/lib/setup/ui/diff.js +0 -1
  194. package/dist/lib/setup/ui/footer.d.ts +0 -1
  195. package/dist/lib/setup/ui/footer.js +0 -1
  196. package/dist/lib/setup/ui/header.d.ts +0 -1
  197. package/dist/lib/setup/ui/header.js +0 -1
  198. package/dist/lib/setup/ui/index.d.ts +0 -1
  199. package/dist/lib/setup/ui/index.js +0 -1
  200. package/dist/lib/setup/ui/menu.d.ts +0 -1
  201. package/dist/lib/setup/ui/menu.js +0 -1
  202. package/dist/lib/setup/ui/panel.d.ts +0 -1
  203. package/dist/lib/setup/ui/panel.js +0 -1
  204. package/dist/lib/setup/ui/prompt.d.ts +0 -1
  205. package/dist/lib/setup/ui/prompt.js +0 -1
  206. package/dist/lib/setup/ui/safePrompt.d.ts +0 -1
  207. package/dist/lib/setup/ui/safePrompt.js +0 -1
  208. package/dist/lib/setup/ui/spinner.d.ts +0 -1
  209. package/dist/lib/setup/ui/spinner.js +0 -1
  210. package/dist/lib/setup/ui/status.d.ts +0 -1
  211. package/dist/lib/setup/ui/status.js +0 -1
  212. package/dist/lib/setup/ui/table.d.ts +0 -1
  213. package/dist/lib/setup/ui/table.js +0 -1
  214. package/dist/lib/setup/ui/title.d.ts +0 -1
  215. package/dist/lib/setup/ui/title.js +0 -1
  216. package/dist/lib/setup/ui/tokens.d.ts +0 -1
  217. package/dist/lib/setup/ui/tokens.js +0 -1
  218. package/dist/lib/setup.d.ts +2 -31
  219. package/dist/lib/setup.js +13 -9
  220. package/dist/lib/staticSearch.d.ts +0 -1
  221. package/dist/lib/staticSearch.js +0 -1
  222. package/dist/lib/store.d.ts +0 -1
  223. package/dist/lib/store.js +0 -1
  224. package/dist/lib/structuredIngest.d.ts +0 -1
  225. package/dist/lib/structuredIngest.js +0 -1
  226. package/dist/lib/tags.d.ts +0 -1
  227. package/dist/lib/tags.js +0 -1
  228. package/dist/lib/timeline.d.ts +2 -3
  229. package/dist/lib/timeline.js +3 -4
  230. package/dist/lib/trace.d.ts +1 -16
  231. package/dist/lib/trace.js +0 -1
  232. package/dist/lib/upgrade.d.ts +0 -1
  233. package/dist/lib/upgrade.js +0 -1
  234. package/dist/lib/videoExtract.d.ts +0 -1
  235. package/dist/lib/videoExtract.js +0 -1
  236. package/dist/lib/webIndex.d.ts +1 -3
  237. package/dist/lib/webIndex.js +0 -1
  238. package/dist/lib/webIngest.d.ts +13 -1
  239. package/dist/lib/webIngest.js +98 -24
  240. package/dist/lib/wikilinks.d.ts +3 -3
  241. package/dist/lib/wikilinks.js +0 -1
  242. package/dist/postinstall.d.ts +0 -1
  243. package/dist/postinstall.js +0 -1
  244. package/dist/sandbox/client.d.ts +0 -1
  245. package/dist/sandbox/client.js +0 -1
  246. package/dist/sandbox/helper-template.d.ts +0 -1
  247. package/dist/sandbox/helper-template.js +0 -1
  248. package/dist/sandbox/manager.d.ts +0 -9
  249. package/dist/sandbox/manager.js +2 -3
  250. package/dist/sandbox/server.d.ts +2 -3
  251. package/dist/sandbox/server.js +7 -7
  252. package/docs/logo.svg +25 -0
  253. package/package.json +18 -6
  254. package/prompts/synthesize.md +2 -0
  255. package/dist/cli.d.ts.map +0 -1
  256. package/dist/cli.js.map +0 -1
  257. package/dist/index.d.ts.map +0 -1
  258. package/dist/index.js.map +0 -1
  259. package/dist/lib/archive.d.ts.map +0 -1
  260. package/dist/lib/archive.js.map +0 -1
  261. package/dist/lib/ask.d.ts.map +0 -1
  262. package/dist/lib/ask.js.map +0 -1
  263. package/dist/lib/attachments.d.ts.map +0 -1
  264. package/dist/lib/attachments.js.map +0 -1
  265. package/dist/lib/audioExtract.d.ts.map +0 -1
  266. package/dist/lib/audioExtract.js.map +0 -1
  267. package/dist/lib/audit.d.ts.map +0 -1
  268. package/dist/lib/audit.js.map +0 -1
  269. package/dist/lib/bootstrap.d.ts.map +0 -1
  270. package/dist/lib/bootstrap.js.map +0 -1
  271. package/dist/lib/chat/SlashPalette.d.ts.map +0 -1
  272. package/dist/lib/chat/SlashPalette.js.map +0 -1
  273. package/dist/lib/chat/boot-splash.d.ts.map +0 -1
  274. package/dist/lib/chat/boot-splash.js.map +0 -1
  275. package/dist/lib/chat/choose.d.ts.map +0 -1
  276. package/dist/lib/chat/choose.js.map +0 -1
  277. package/dist/lib/chat/commands.d.ts.map +0 -1
  278. package/dist/lib/chat/commands.js.map +0 -1
  279. package/dist/lib/chat/components/CitationText.d.ts.map +0 -1
  280. package/dist/lib/chat/components/CitationText.js.map +0 -1
  281. package/dist/lib/chat/components/MarkdownRenderer.d.ts.map +0 -1
  282. package/dist/lib/chat/components/MarkdownRenderer.js.map +0 -1
  283. package/dist/lib/chat/components/ToolCallCard.d.ts.map +0 -1
  284. package/dist/lib/chat/components/ToolCallCard.js.map +0 -1
  285. package/dist/lib/chat/focus.d.ts.map +0 -1
  286. package/dist/lib/chat/focus.js.map +0 -1
  287. package/dist/lib/chat/index.d.ts.map +0 -1
  288. package/dist/lib/chat/index.js.map +0 -1
  289. package/dist/lib/chat/intent.d.ts.map +0 -1
  290. package/dist/lib/chat/intent.js.map +0 -1
  291. package/dist/lib/chat/llmTurn.d.ts.map +0 -1
  292. package/dist/lib/chat/llmTurn.js.map +0 -1
  293. package/dist/lib/chat/recall.d.ts.map +0 -1
  294. package/dist/lib/chat/recall.js.map +0 -1
  295. package/dist/lib/chat/render.d.ts.map +0 -1
  296. package/dist/lib/chat/render.js.map +0 -1
  297. package/dist/lib/chat/session.d.ts.map +0 -1
  298. package/dist/lib/chat/session.js.map +0 -1
  299. package/dist/lib/chat/theme.d.ts.map +0 -1
  300. package/dist/lib/chat/theme.js.map +0 -1
  301. package/dist/lib/chat/toolFence.d.ts.map +0 -1
  302. package/dist/lib/chat/toolFence.js.map +0 -1
  303. package/dist/lib/chat/tools.d.ts.map +0 -1
  304. package/dist/lib/chat/tools.js.map +0 -1
  305. package/dist/lib/chat/types.d.ts.map +0 -1
  306. package/dist/lib/chat/types.js.map +0 -1
  307. package/dist/lib/chat/write.d.ts.map +0 -1
  308. package/dist/lib/chat/write.js.map +0 -1
  309. package/dist/lib/chunkSplitter.d.ts.map +0 -1
  310. package/dist/lib/chunkSplitter.js.map +0 -1
  311. package/dist/lib/cleanup.d.ts.map +0 -1
  312. package/dist/lib/cleanup.js.map +0 -1
  313. package/dist/lib/config.d.ts.map +0 -1
  314. package/dist/lib/config.js.map +0 -1
  315. package/dist/lib/dashboard.d.ts.map +0 -1
  316. package/dist/lib/dashboard.js.map +0 -1
  317. package/dist/lib/db.d.ts.map +0 -1
  318. package/dist/lib/db.js.map +0 -1
  319. package/dist/lib/dbSearch.d.ts.map +0 -1
  320. package/dist/lib/dbSearch.js.map +0 -1
  321. package/dist/lib/dbWrite.d.ts.map +0 -1
  322. package/dist/lib/dbWrite.js.map +0 -1
  323. package/dist/lib/desktopNotify.d.ts.map +0 -1
  324. package/dist/lib/desktopNotify.js.map +0 -1
  325. package/dist/lib/docxExtract.d.ts.map +0 -1
  326. package/dist/lib/docxExtract.js.map +0 -1
  327. package/dist/lib/dream.d.ts.map +0 -1
  328. package/dist/lib/dream.js.map +0 -1
  329. package/dist/lib/embeddings.d.ts.map +0 -1
  330. package/dist/lib/embeddings.js.map +0 -1
  331. package/dist/lib/export.d.ts.map +0 -1
  332. package/dist/lib/export.js.map +0 -1
  333. package/dist/lib/exportProject.d.ts.map +0 -1
  334. package/dist/lib/exportProject.js.map +0 -1
  335. package/dist/lib/federated.d.ts.map +0 -1
  336. package/dist/lib/federated.js.map +0 -1
  337. package/dist/lib/fileDetect.d.ts.map +0 -1
  338. package/dist/lib/fileDetect.js.map +0 -1
  339. package/dist/lib/graph.d.ts.map +0 -1
  340. package/dist/lib/graph.js.map +0 -1
  341. package/dist/lib/heartbeat.d.ts.map +0 -1
  342. package/dist/lib/heartbeat.js.map +0 -1
  343. package/dist/lib/history.d.ts +0 -39
  344. package/dist/lib/history.d.ts.map +0 -1
  345. package/dist/lib/history.js +0 -112
  346. package/dist/lib/history.js.map +0 -1
  347. package/dist/lib/hybridSearch.d.ts.map +0 -1
  348. package/dist/lib/hybridSearch.js.map +0 -1
  349. package/dist/lib/idFormat.d.ts.map +0 -1
  350. package/dist/lib/idFormat.js.map +0 -1
  351. package/dist/lib/imageExtract.d.ts.map +0 -1
  352. package/dist/lib/imageExtract.js.map +0 -1
  353. package/dist/lib/import.d.ts.map +0 -1
  354. package/dist/lib/import.js.map +0 -1
  355. package/dist/lib/importProject.d.ts.map +0 -1
  356. package/dist/lib/importProject.js.map +0 -1
  357. package/dist/lib/ingest.d.ts.map +0 -1
  358. package/dist/lib/ingest.js.map +0 -1
  359. package/dist/lib/lensing.d.ts.map +0 -1
  360. package/dist/lib/lensing.js.map +0 -1
  361. package/dist/lib/llm.d.ts.map +0 -1
  362. package/dist/lib/llm.js.map +0 -1
  363. package/dist/lib/lock.d.ts.map +0 -1
  364. package/dist/lib/lock.js.map +0 -1
  365. package/dist/lib/maintenance.d.ts.map +0 -1
  366. package/dist/lib/maintenance.js.map +0 -1
  367. package/dist/lib/migrate.d.ts.map +0 -1
  368. package/dist/lib/migrate.js.map +0 -1
  369. package/dist/lib/modelValidation.d.ts.map +0 -1
  370. package/dist/lib/modelValidation.js.map +0 -1
  371. package/dist/lib/multimodalIngest.d.ts.map +0 -1
  372. package/dist/lib/multimodalIngest.js.map +0 -1
  373. package/dist/lib/paths.d.ts.map +0 -1
  374. package/dist/lib/paths.js.map +0 -1
  375. package/dist/lib/pdfExtract.d.ts.map +0 -1
  376. package/dist/lib/pdfExtract.js.map +0 -1
  377. package/dist/lib/portfolio.d.ts.map +0 -1
  378. package/dist/lib/portfolio.js.map +0 -1
  379. package/dist/lib/portfolioHtml.d.ts.map +0 -1
  380. package/dist/lib/portfolioHtml.js.map +0 -1
  381. package/dist/lib/preferences.d.ts.map +0 -1
  382. package/dist/lib/preferences.js.map +0 -1
  383. package/dist/lib/progress.d.ts.map +0 -1
  384. package/dist/lib/progress.js.map +0 -1
  385. package/dist/lib/projectIdentity.d.ts.map +0 -1
  386. package/dist/lib/projectIdentity.js.map +0 -1
  387. package/dist/lib/recall.d.ts.map +0 -1
  388. package/dist/lib/recall.js.map +0 -1
  389. package/dist/lib/remote.d.ts.map +0 -1
  390. package/dist/lib/remote.js.map +0 -1
  391. package/dist/lib/remoteWizard.d.ts.map +0 -1
  392. package/dist/lib/remoteWizard.js.map +0 -1
  393. package/dist/lib/resolver.d.ts.map +0 -1
  394. package/dist/lib/resolver.js.map +0 -1
  395. package/dist/lib/retry.d.ts.map +0 -1
  396. package/dist/lib/retry.js.map +0 -1
  397. package/dist/lib/rulesGen.d.ts.map +0 -1
  398. package/dist/lib/rulesGen.js.map +0 -1
  399. package/dist/lib/search.d.ts.map +0 -1
  400. package/dist/lib/search.js.map +0 -1
  401. package/dist/lib/setup/coldStart.d.ts.map +0 -1
  402. package/dist/lib/setup/coldStart.js.map +0 -1
  403. package/dist/lib/setup/configSetRender.d.ts.map +0 -1
  404. package/dist/lib/setup/configSetRender.js.map +0 -1
  405. package/dist/lib/setup/dreamRender.d.ts.map +0 -1
  406. package/dist/lib/setup/dreamRender.js.map +0 -1
  407. package/dist/lib/setup/dreamState.d.ts.map +0 -1
  408. package/dist/lib/setup/dreamState.js.map +0 -1
  409. package/dist/lib/setup/modelsRender.d.ts.map +0 -1
  410. package/dist/lib/setup/modelsRender.js.map +0 -1
  411. package/dist/lib/setup/remoteRender.d.ts.map +0 -1
  412. package/dist/lib/setup/remoteRender.js.map +0 -1
  413. package/dist/lib/setup/routingRender.d.ts.map +0 -1
  414. package/dist/lib/setup/routingRender.js.map +0 -1
  415. package/dist/lib/setup/sections/ides.d.ts.map +0 -1
  416. package/dist/lib/setup/sections/ides.js.map +0 -1
  417. package/dist/lib/setup/sections/preferences.d.ts.map +0 -1
  418. package/dist/lib/setup/sections/preferences.js.map +0 -1
  419. package/dist/lib/setup/sections/routing.d.ts.map +0 -1
  420. package/dist/lib/setup/sections/routing.js.map +0 -1
  421. package/dist/lib/setup/storePath.d.ts.map +0 -1
  422. package/dist/lib/setup/storePath.js.map +0 -1
  423. package/dist/lib/setup/summary.d.ts.map +0 -1
  424. package/dist/lib/setup/summary.js.map +0 -1
  425. package/dist/lib/setup/syncProjectsRender.d.ts.map +0 -1
  426. package/dist/lib/setup/syncProjectsRender.js.map +0 -1
  427. package/dist/lib/setup/ui/diff.d.ts.map +0 -1
  428. package/dist/lib/setup/ui/diff.js.map +0 -1
  429. package/dist/lib/setup/ui/footer.d.ts.map +0 -1
  430. package/dist/lib/setup/ui/footer.js.map +0 -1
  431. package/dist/lib/setup/ui/header.d.ts.map +0 -1
  432. package/dist/lib/setup/ui/header.js.map +0 -1
  433. package/dist/lib/setup/ui/index.d.ts.map +0 -1
  434. package/dist/lib/setup/ui/index.js.map +0 -1
  435. package/dist/lib/setup/ui/menu.d.ts.map +0 -1
  436. package/dist/lib/setup/ui/menu.js.map +0 -1
  437. package/dist/lib/setup/ui/panel.d.ts.map +0 -1
  438. package/dist/lib/setup/ui/panel.js.map +0 -1
  439. package/dist/lib/setup/ui/prompt.d.ts.map +0 -1
  440. package/dist/lib/setup/ui/prompt.js.map +0 -1
  441. package/dist/lib/setup/ui/safePrompt.d.ts.map +0 -1
  442. package/dist/lib/setup/ui/safePrompt.js.map +0 -1
  443. package/dist/lib/setup/ui/spinner.d.ts.map +0 -1
  444. package/dist/lib/setup/ui/spinner.js.map +0 -1
  445. package/dist/lib/setup/ui/status.d.ts.map +0 -1
  446. package/dist/lib/setup/ui/status.js.map +0 -1
  447. package/dist/lib/setup/ui/table.d.ts.map +0 -1
  448. package/dist/lib/setup/ui/table.js.map +0 -1
  449. package/dist/lib/setup/ui/title.d.ts.map +0 -1
  450. package/dist/lib/setup/ui/title.js.map +0 -1
  451. package/dist/lib/setup/ui/tokens.d.ts.map +0 -1
  452. package/dist/lib/setup/ui/tokens.js.map +0 -1
  453. package/dist/lib/setup.d.ts.map +0 -1
  454. package/dist/lib/setup.js.map +0 -1
  455. package/dist/lib/staticSearch.d.ts.map +0 -1
  456. package/dist/lib/staticSearch.js.map +0 -1
  457. package/dist/lib/store.d.ts.map +0 -1
  458. package/dist/lib/store.js.map +0 -1
  459. package/dist/lib/structuredIngest.d.ts.map +0 -1
  460. package/dist/lib/structuredIngest.js.map +0 -1
  461. package/dist/lib/tags.d.ts.map +0 -1
  462. package/dist/lib/tags.js.map +0 -1
  463. package/dist/lib/timeline.d.ts.map +0 -1
  464. package/dist/lib/timeline.js.map +0 -1
  465. package/dist/lib/trace.d.ts.map +0 -1
  466. package/dist/lib/trace.js.map +0 -1
  467. package/dist/lib/upgrade.d.ts.map +0 -1
  468. package/dist/lib/upgrade.js.map +0 -1
  469. package/dist/lib/videoExtract.d.ts.map +0 -1
  470. package/dist/lib/videoExtract.js.map +0 -1
  471. package/dist/lib/webIndex.d.ts.map +0 -1
  472. package/dist/lib/webIndex.js.map +0 -1
  473. package/dist/lib/webIngest.d.ts.map +0 -1
  474. package/dist/lib/webIngest.js.map +0 -1
  475. package/dist/lib/wikilinks.d.ts.map +0 -1
  476. package/dist/lib/wikilinks.js.map +0 -1
  477. package/dist/postinstall.d.ts.map +0 -1
  478. package/dist/postinstall.js.map +0 -1
  479. package/dist/sandbox/client.d.ts.map +0 -1
  480. package/dist/sandbox/client.js.map +0 -1
  481. package/dist/sandbox/helper-template.d.ts.map +0 -1
  482. package/dist/sandbox/helper-template.js.map +0 -1
  483. package/dist/sandbox/index.d.ts +0 -10
  484. package/dist/sandbox/index.d.ts.map +0 -1
  485. package/dist/sandbox/index.js +0 -10
  486. package/dist/sandbox/index.js.map +0 -1
  487. package/dist/sandbox/manager.d.ts.map +0 -1
  488. package/dist/sandbox/manager.js.map +0 -1
  489. package/dist/sandbox/server.d.ts.map +0 -1
  490. package/dist/sandbox/server.js.map +0 -1
package/dist/index.js CHANGED
@@ -45,7 +45,6 @@ import { GnosysSearch } from "./lib/search.js";
45
45
  import { GnosysTagRegistry } from "./lib/tags.js";
46
46
  import { GnosysResolver } from "./lib/resolver.js";
47
47
  import { applyLens } from "./lib/lensing.js";
48
- import { getFileHistory, rollbackToCommit, hasGitHistory } from "./lib/history.js";
49
48
  import { groupByPeriod, computeStats } from "./lib/timeline.js";
50
49
  import { buildLinkGraph, getBacklinks, getOutgoingLinks, formatGraphSummary } from "./lib/wikilinks.js";
51
50
  import { loadConfig, DEFAULT_CONFIG } from "./lib/config.js";
@@ -55,7 +54,7 @@ import { initAudit, readAuditLog, formatAuditTimeline } from "./lib/audit.js";
55
54
  import { GnosysDB } from "./lib/db.js";
56
55
  import { syncMemoryToDb, syncUpdateToDb, syncDearchiveToDb, syncReinforcementToDb, auditToDb } from "./lib/dbWrite.js";
57
56
  import { createProjectIdentity, readProjectIdentity } from "./lib/projectIdentity.js";
58
- import { setPreference, getPreference, getAllPreferences, deletePreference } from "./lib/preferences.js";
57
+ import { setPreference, getPreference, getAllPreferences, deletePreference, KNOWN_PREFERENCE_KEYS, suggestPreferenceKey } from "./lib/preferences.js";
59
58
  import { syncRules, generateRulesBlock } from "./lib/rulesGen.js";
60
59
  import { federatedSearch, detectAmbiguity, generateBriefing, generateAllBriefings, getWorkingSet, formatWorkingSet, detectCurrentProject } from "./lib/federated.js";
61
60
  import { generatePortfolio, formatPortfolioCompact, formatPortfolioMarkdown, generateStatusPrompt } from "./lib/portfolio.js";
@@ -67,6 +66,23 @@ const server = new McpServer({
67
66
  name: "gnosys",
68
67
  version: "2.0.0",
69
68
  });
69
+ const _registrations = [];
70
+ // Typed to the McpServer methods so call-site generic inference (Zod schema →
71
+ // handler arg types) is preserved; the body just collects a replay thunk.
72
+ const regTool = ((...args) => {
73
+ _registrations.push((s) => s.tool(...args));
74
+ });
75
+ const regPrompt = ((...args) => {
76
+ _registrations.push((s) => s.prompt(...args));
77
+ });
78
+ const regResource = ((...args) => {
79
+ _registrations.push((s) => s.resource(...args));
80
+ });
81
+ /** Replay all collected capability registrations onto a server instance. */
82
+ export function registerCapabilities(s) {
83
+ for (const r of _registrations)
84
+ r(s);
85
+ }
70
86
  /**
71
87
  * v5.4.1: Format MCP errors. Detects DB corruption and replaces the raw
72
88
  * "database disk image is malformed" with actionable recovery instructions.
@@ -155,7 +171,7 @@ async function resolveToolContext(projectRoot) {
155
171
  const scopedWriteTarget = scopedResolver.getWriteTarget();
156
172
  const scopedStorePath = scopedWriteTarget?.store.getStorePath() || "";
157
173
  let scopedConfig = DEFAULT_CONFIG;
158
- let scopedDb = null;
174
+ const scopedDb = null;
159
175
  let scopedSearch = null;
160
176
  // v3.0: Read project identity
161
177
  const identity = await readProjectIdentity(path.resolve(projectRoot));
@@ -220,7 +236,7 @@ function resolveWriteScope(ctx, targetStore) {
220
236
  return { ok: true, scope: "project", projectId: ctx.projectId };
221
237
  }
222
238
  // ─── Tool: gnosys_discover ──────────────────────────────────────────────
223
- server.tool("gnosys_discover", "Discover relevant memories by describing what you're working on. Searches relevance keyword clouds across all stores. Returns lightweight metadata (title, path, relevance keywords) — NO file contents. Use gnosys_read to load specific memories you need. Call this FIRST when starting a task to find what Gnosys knows.", {
239
+ regTool("gnosys_discover", "Discover relevant memories by describing what you're working on. Searches relevance keyword clouds across all stores. Returns lightweight metadata (title, path, relevance keywords) — NO file contents. Use gnosys_read to load specific memories you need. Call this FIRST when starting a task to find what Gnosys knows.", {
224
240
  query: z
225
241
  .string()
226
242
  .describe("Describe what you're working on or looking for. Use keywords, not sentences. Example: 'auth JWT session tokens' or 'deployment CI/CD pipeline'"),
@@ -274,7 +290,7 @@ server.tool("gnosys_discover", "Discover relevant memories by describing what yo
274
290
  };
275
291
  });
276
292
  // ─── Tool: gnosys_read ───────────────────────────────────────────────────
277
- server.tool("gnosys_read", "Read a specific memory. Accepts a memory ID (e.g., 'arch-012') or layer-prefixed path (e.g., 'project:decisions/why-not-rag.md'). Without a prefix, searches all stores in precedence order.", {
293
+ regTool("gnosys_read", "Read a specific memory. Accepts a memory ID (e.g., 'arch-012') or layer-prefixed path (e.g., 'project:decisions/why-not-rag.md'). Without a prefix, searches all stores in precedence order.", {
278
294
  path: z.string().describe("Memory ID or path, optionally prefixed with store layer"),
279
295
  projectRoot: projectRootParam,
280
296
  }, async ({ path: memPath, projectRoot }) => {
@@ -284,7 +300,7 @@ server.tool("gnosys_read", "Read a specific memory. Accepts a memory ID (e.g., '
284
300
  const dbMem = ctx.centralDb.getMemory(memPath);
285
301
  if (dbMem) {
286
302
  const tags = dbMem.tags || "[]";
287
- const header = [
303
+ const headerLines = [
288
304
  `---`,
289
305
  `id: ${dbMem.id}`,
290
306
  `title: '${dbMem.title}'`,
@@ -298,8 +314,14 @@ server.tool("gnosys_read", "Read a specific memory. Accepts a memory ID (e.g., '
298
314
  `tier: ${dbMem.tier}`,
299
315
  `created: '${dbMem.created}'`,
300
316
  `modified: '${dbMem.modified}'`,
301
- `---`,
302
- ].join("\n");
317
+ ];
318
+ if (dbMem.source_file) {
319
+ headerLines.push(`source_file: ${dbMem.source_file}${dbMem.source_page != null ? ` (page ${Number(dbMem.source_page)})` : ""}`);
320
+ }
321
+ if (dbMem.source_path)
322
+ headerLines.push(`source_path: ${dbMem.source_path}`);
323
+ headerLines.push(`---`);
324
+ const header = headerLines.join("\n");
303
325
  return {
304
326
  content: [{ type: "text", text: `[Source: gnosys.db]\n\n${header}\n\n${dbMem.content}` }],
305
327
  };
@@ -314,7 +336,13 @@ server.tool("gnosys_read", "Read a specific memory. Accepts a memory ID (e.g., '
314
336
  isError: true,
315
337
  };
316
338
  }
317
- const raw = await fs.readFile(memory.filePath, "utf-8");
339
+ let raw;
340
+ try {
341
+ raw = await fs.readFile(memory.filePath, "utf-8");
342
+ }
343
+ catch (err) {
344
+ return { content: [{ type: "text", text: formatMcpError("reading memory", err) }], isError: true };
345
+ }
318
346
  return {
319
347
  content: [
320
348
  {
@@ -325,7 +353,7 @@ server.tool("gnosys_read", "Read a specific memory. Accepts a memory ID (e.g., '
325
353
  };
326
354
  });
327
355
  // ─── Tool: gnosys_search ─────────────────────────────────────────────────
328
- server.tool("gnosys_search", "Search memories by keyword across all stores. Returns matching file paths with relevance snippets.", {
356
+ regTool("gnosys_search", "Search memories by keyword across all stores. Returns matching file paths with relevance snippets.", {
329
357
  query: z.string().describe("Search query (keywords)"),
330
358
  limit: z.number().optional().describe("Max results (default 20)"),
331
359
  projectRoot: projectRootParam,
@@ -377,7 +405,7 @@ server.tool("gnosys_search", "Search memories by keyword across all stores. Retu
377
405
  };
378
406
  });
379
407
  // ─── Tool: gnosys_list ───────────────────────────────────────────────────
380
- server.tool("gnosys_list", "List memories across all stores, optionally filtered by category, tag, or store layer.", {
408
+ regTool("gnosys_list", "List memories across all stores, optionally filtered by category, tag, or store layer.", {
381
409
  category: z.string().optional().describe("Filter by category"),
382
410
  tag: z.string().optional().describe("Filter by tag"),
383
411
  store: z.string().optional().describe("Filter by store layer (project/personal/global/optional)"),
@@ -463,7 +491,7 @@ server.tool("gnosys_list", "List memories across all stores, optionally filtered
463
491
  };
464
492
  });
465
493
  // ─── Tool: gnosys_add ────────────────────────────────────────────────────
466
- server.tool("gnosys_add", "Add a new memory. Accepts raw text — an LLM structures it into an atomic memory. Writes to the project store by default. Use store='personal' for cross-project knowledge, or store='global' to explicitly write to shared org knowledge.", {
494
+ regTool("gnosys_add", "Add a new memory. Accepts raw text — an LLM structures it into an atomic memory. Writes to the project store by default. Use store='personal' for cross-project knowledge, or store='global' to explicitly write to shared org knowledge.", {
467
495
  input: z
468
496
  .string()
469
497
  .describe("Raw text input. Can be a decision, concept, fact, observation, or any knowledge."),
@@ -577,7 +605,7 @@ server.tool("gnosys_add", "Add a new memory. Accepts raw text — an LLM structu
577
605
  }
578
606
  });
579
607
  // ─── Tool: gnosys_add_structured ─────────────────────────────────────────
580
- server.tool("gnosys_add_structured", "Add a memory with structured input (no LLM needed). Writes to the project store by default. Use store='global' to explicitly write to shared org knowledge.", {
608
+ regTool("gnosys_add_structured", "Add a memory with structured input (no LLM needed). Writes to the project store by default. Use store='global' to explicitly write to shared org knowledge.", {
581
609
  title: z.string().describe("Memory title"),
582
610
  category: z.string().describe("Category directory name"),
583
611
  tags: z
@@ -656,7 +684,7 @@ server.tool("gnosys_add_structured", "Add a memory with structured input (no LLM
656
684
  }
657
685
  });
658
686
  // ─── Tool: gnosys_tags ───────────────────────────────────────────────────
659
- server.tool("gnosys_tags", "List all tags in the registry, grouped by category.", { projectRoot: projectRootParam }, async ({ projectRoot }) => {
687
+ regTool("gnosys_tags", "List all tags in the registry, grouped by category.", { projectRoot: projectRootParam }, async ({ projectRoot }) => {
660
688
  // Tag registry is module-level and shared across projects, projectRoot is for API consistency
661
689
  if (!tagRegistry) {
662
690
  return { content: [{ type: "text", text: "Tag registry not loaded." }], isError: true };
@@ -671,7 +699,7 @@ server.tool("gnosys_tags", "List all tags in the registry, grouped by category."
671
699
  return { content: [{ type: "text", text: lines.join("\n") }] };
672
700
  });
673
701
  // ─── Tool: gnosys_tags_add ───────────────────────────────────────────────
674
- server.tool("gnosys_tags_add", "Add a new tag to the registry.", {
702
+ regTool("gnosys_tags_add", "Add a new tag to the registry.", {
675
703
  category: z.string().describe("Tag category (domain, type, concern, status_tag)"),
676
704
  tag: z.string().describe("The new tag to add"),
677
705
  projectRoot: projectRootParam,
@@ -691,7 +719,7 @@ server.tool("gnosys_tags_add", "Add a new tag to the registry.", {
691
719
  };
692
720
  });
693
721
  // ─── Tool: gnosys_reinforce ──────────────────────────────────────────────
694
- server.tool("gnosys_reinforce", "Signal whether a memory was useful. 'useful' reinforces it (resets decay). 'not_relevant' means routing was wrong, not the memory (memory unchanged). 'outdated' flags for review.", {
722
+ regTool("gnosys_reinforce", "Signal whether a memory was useful. 'useful' reinforces it (resets decay). 'not_relevant' means routing was wrong, not the memory (memory unchanged). 'outdated' flags for review.", {
695
723
  memory_id: z.string().describe("The memory ID (from frontmatter)"),
696
724
  signal: z
697
725
  .enum(["useful", "not_relevant", "outdated"])
@@ -699,46 +727,51 @@ server.tool("gnosys_reinforce", "Signal whether a memory was useful. 'useful' re
699
727
  context: z.string().optional().describe("Why this signal was given"),
700
728
  projectRoot: projectRootParam,
701
729
  }, async ({ memory_id, signal, context, projectRoot }) => {
702
- const ctx = await resolveToolContext(projectRoot);
703
- // Log to the first writable store's .config directory
704
- const writeTarget = ctx.resolver.getWriteTarget();
705
- if (writeTarget) {
706
- const logPath = path.join(writeTarget.store.getStorePath(), ".config", "reinforcement.log");
707
- const entry = JSON.stringify({
708
- memory_id,
709
- signal,
710
- context,
711
- timestamp: new Date().toISOString(),
712
- });
713
- await fs.appendFile(logPath, entry + "\n", "utf-8");
714
- }
715
- // If 'useful', find the memory across all stores and update if writable
716
- if (signal === "useful") {
717
- const allMemories = await ctx.resolver.getAllMemories();
718
- const memory = allMemories.find((m) => m.frontmatter.id === memory_id);
719
- if (memory) {
720
- const sourceStore = ctx.resolver
721
- .getStores()
722
- .find((s) => s.label === memory.sourceLabel);
723
- if (sourceStore) {
724
- const count = (memory.frontmatter.reinforcement_count || 0) + 1;
725
- // Write reinforcement to DB only (SQLite is sole source of truth)
726
- if (ctx.centralDb?.isAvailable()) {
727
- syncReinforcementToDb(ctx.centralDb, memory_id, count);
728
- auditToDb(ctx.centralDb, "reinforce", memory_id, { signal, context });
730
+ try {
731
+ const ctx = await resolveToolContext(projectRoot);
732
+ // Log to the first writable store's .config directory
733
+ const writeTarget = ctx.resolver.getWriteTarget();
734
+ if (writeTarget) {
735
+ const logPath = path.join(writeTarget.store.getStorePath(), ".config", "reinforcement.log");
736
+ const entry = JSON.stringify({
737
+ memory_id,
738
+ signal,
739
+ context,
740
+ timestamp: new Date().toISOString(),
741
+ });
742
+ await fs.appendFile(logPath, entry + "\n", "utf-8");
743
+ }
744
+ // If 'useful', find the memory across all stores and update if writable
745
+ if (signal === "useful") {
746
+ const allMemories = await ctx.resolver.getAllMemories();
747
+ const memory = allMemories.find((m) => m.frontmatter.id === memory_id);
748
+ if (memory) {
749
+ const sourceStore = ctx.resolver
750
+ .getStores()
751
+ .find((s) => s.label === memory.sourceLabel);
752
+ if (sourceStore) {
753
+ const count = (memory.frontmatter.reinforcement_count || 0) + 1;
754
+ // Write reinforcement to DB only (SQLite is sole source of truth)
755
+ if (ctx.centralDb?.isAvailable()) {
756
+ syncReinforcementToDb(ctx.centralDb, memory_id, count);
757
+ auditToDb(ctx.centralDb, "reinforce", memory_id, { signal, context });
758
+ }
729
759
  }
730
760
  }
731
761
  }
762
+ const messages = {
763
+ useful: `Memory ${memory_id} reinforced. Decay clock reset.`,
764
+ not_relevant: `Routing feedback logged for ${memory_id}. Memory unchanged — consider reviewing its relevance keywords or tags.`,
765
+ outdated: `Memory ${memory_id} flagged for review as outdated.`,
766
+ };
767
+ return { content: [{ type: "text", text: messages[signal] }] };
768
+ }
769
+ catch (err) {
770
+ return { content: [{ type: "text", text: formatMcpError("reinforcing memory", err) }], isError: true };
732
771
  }
733
- const messages = {
734
- useful: `Memory ${memory_id} reinforced. Decay clock reset.`,
735
- not_relevant: `Routing feedback logged for ${memory_id}. Memory unchanged — consider reviewing its relevance keywords or tags.`,
736
- outdated: `Memory ${memory_id} flagged for review as outdated.`,
737
- };
738
- return { content: [{ type: "text", text: messages[signal] }] };
739
772
  });
740
773
  // ─── Tool: gnosys_init ───────────────────────────────────────────────────
741
- server.tool("gnosys_init", "Initialize Gnosys in a project directory. Creates .gnosys/ with project identity (gnosys.json), registers the project in the central DB (~/.gnosys/gnosys.db), and sets up tag registry. You MUST run this before any other Gnosys tool in a new project. Pass the full absolute path to the project root.", {
774
+ regTool("gnosys_init", "Initialize Gnosys in a project directory. Creates .gnosys/ with project identity (gnosys.json), registers the project in the central DB (~/.gnosys/gnosys.db), and sets up tag registry. You MUST run this before any other Gnosys tool in a new project. Pass the full absolute path to the project root.", {
742
775
  directory: z
743
776
  .string()
744
777
  .describe("Absolute path to the project directory to initialize. Required."),
@@ -812,7 +845,7 @@ server.tool("gnosys_init", "Initialize Gnosys in a project directory. Creates .g
812
845
  };
813
846
  });
814
847
  // ─── Tool: gnosys_migrate ────────────────────────────────────────────────
815
- server.tool("gnosys_migrate", "Migrate a Gnosys store (.gnosys/) from one directory to another. Updates the project name, working directory, and central DB registration. Use this when a project has moved or you want to consolidate stores.", {
848
+ regTool("gnosys_migrate", "Migrate a Gnosys store (.gnosys/) from one directory to another. Updates the project name, working directory, and central DB registration. Use this when a project has moved or you want to consolidate stores.", {
816
849
  sourcePath: z.string().describe("Directory that currently contains .gnosys/ (absolute path)"),
817
850
  targetPath: z.string().describe("Directory to move .gnosys/ into (absolute path)"),
818
851
  newName: z.string().optional().describe("New project name (default: basename of target directory)"),
@@ -884,7 +917,7 @@ server.tool("gnosys_migrate", "Migrate a Gnosys store (.gnosys/) from one direct
884
917
  }
885
918
  });
886
919
  // ─── Tool: gnosys_update ─────────────────────────────────────────────────
887
- server.tool("gnosys_update", "Update an existing memory's frontmatter and/or content. Specify the memory path and the fields to change.", {
920
+ regTool("gnosys_update", "Update an existing memory's frontmatter and/or content. Specify the memory path and the fields to change.", {
888
921
  path: z
889
922
  .string()
890
923
  .describe("Path to memory, optionally prefixed with store layer (e.g., 'project:decisions/auth.md')"),
@@ -990,7 +1023,7 @@ server.tool("gnosys_update", "Update an existing memory's frontmatter and/or con
990
1023
  };
991
1024
  });
992
1025
  // ─── Tool: gnosys_stale ─────────────────────────────────────────────────
993
- server.tool("gnosys_stale", "Find memories that haven't been modified or reviewed within a given number of days. Useful for identifying knowledge that may be outdated.", {
1026
+ regTool("gnosys_stale", "Find memories that haven't been modified or reviewed within a given number of days. Useful for identifying knowledge that may be outdated.", {
994
1027
  days: z
995
1028
  .number()
996
1029
  .optional()
@@ -998,46 +1031,51 @@ server.tool("gnosys_stale", "Find memories that haven't been modified or reviewe
998
1031
  limit: z.number().optional().describe("Max results (default 20)"),
999
1032
  projectRoot: projectRootParam,
1000
1033
  }, async ({ days, limit, projectRoot }) => {
1001
- const ctx = await resolveToolContext(projectRoot);
1002
- const threshold = days || 90;
1003
- const maxResults = limit || 20;
1004
- const cutoff = new Date();
1005
- cutoff.setDate(cutoff.getDate() - threshold);
1006
- const cutoffStr = cutoff.toISOString().split("T")[0];
1007
- const allMemories = await ctx.resolver.getAllMemories();
1008
- const stale = allMemories
1009
- .filter((m) => {
1010
- const lastTouched = m.frontmatter.last_reviewed || m.frontmatter.modified;
1011
- return lastTouched && lastTouched < cutoffStr;
1012
- })
1013
- .sort((a, b) => {
1014
- const aDate = a.frontmatter.last_reviewed || a.frontmatter.modified;
1015
- const bDate = b.frontmatter.last_reviewed || b.frontmatter.modified;
1016
- return (aDate || "").localeCompare(bDate || "");
1017
- })
1018
- .slice(0, maxResults);
1019
- if (stale.length === 0) {
1034
+ try {
1035
+ const ctx = await resolveToolContext(projectRoot);
1036
+ const threshold = days || 90;
1037
+ const maxResults = limit || 20;
1038
+ const cutoff = new Date();
1039
+ cutoff.setDate(cutoff.getDate() - threshold);
1040
+ const cutoffStr = cutoff.toISOString().split("T")[0];
1041
+ const allMemories = await ctx.resolver.getAllMemories();
1042
+ const stale = allMemories
1043
+ .filter((m) => {
1044
+ const lastTouched = m.frontmatter.last_reviewed || m.frontmatter.modified;
1045
+ return lastTouched && lastTouched < cutoffStr;
1046
+ })
1047
+ .sort((a, b) => {
1048
+ const aDate = a.frontmatter.last_reviewed || a.frontmatter.modified;
1049
+ const bDate = b.frontmatter.last_reviewed || b.frontmatter.modified;
1050
+ return (aDate || "").localeCompare(bDate || "");
1051
+ })
1052
+ .slice(0, maxResults);
1053
+ if (stale.length === 0) {
1054
+ return {
1055
+ content: [
1056
+ {
1057
+ type: "text",
1058
+ text: `No memories older than ${threshold} days found. Everything is fresh.`,
1059
+ },
1060
+ ],
1061
+ };
1062
+ }
1063
+ const lines = stale.map((m) => `- **${m.frontmatter.title}** (${m.sourceLabel}:${m.relativePath})\n Last modified: ${m.frontmatter.modified}${m.frontmatter.last_reviewed ? `, Last reviewed: ${m.frontmatter.last_reviewed}` : ""}`);
1020
1064
  return {
1021
1065
  content: [
1022
1066
  {
1023
1067
  type: "text",
1024
- text: `No memories older than ${threshold} days found. Everything is fresh.`,
1068
+ text: `Found ${stale.length} memories not touched in ${threshold}+ days:\n\n${lines.join("\n\n")}\n\nUse gnosys_read to review, then gnosys_update or gnosys_reinforce as needed.`,
1025
1069
  },
1026
1070
  ],
1027
1071
  };
1028
1072
  }
1029
- const lines = stale.map((m) => `- **${m.frontmatter.title}** (${m.sourceLabel}:${m.relativePath})\n Last modified: ${m.frontmatter.modified}${m.frontmatter.last_reviewed ? `, Last reviewed: ${m.frontmatter.last_reviewed}` : ""}`);
1030
- return {
1031
- content: [
1032
- {
1033
- type: "text",
1034
- text: `Found ${stale.length} memories not touched in ${threshold}+ days:\n\n${lines.join("\n\n")}\n\nUse gnosys_read to review, then gnosys_update or gnosys_reinforce as needed.`,
1035
- },
1036
- ],
1037
- };
1073
+ catch (err) {
1074
+ return { content: [{ type: "text", text: formatMcpError("finding stale memories", err) }], isError: true };
1075
+ }
1038
1076
  });
1039
1077
  // ─── Tool: gnosys_commit_context ────────────────────────────────────────
1040
- server.tool("gnosys_commit_context", "Pre-compaction memory sweep. Call this before context is lost (e.g., before a long conversation compacts). Extracts important decisions, facts, and insights from the conversation and commits novel ones to memory. Checks existing memories to avoid duplicates — only adds what's genuinely new or augments what's changed.", {
1078
+ regTool("gnosys_commit_context", "Pre-compaction memory sweep. Call this before context is lost (e.g., before a long conversation compacts). Extracts important decisions, facts, and insights from the conversation and commits novel ones to memory. Checks existing memories to avoid duplicates — only adds what's genuinely new or augments what's changed.", {
1041
1079
  context: z
1042
1080
  .string()
1043
1081
  .describe("Summary of the conversation or context to extract memories from. Include key decisions, facts, insights, and observations."),
@@ -1199,17 +1237,15 @@ Output ONLY the JSON array, no markdown fences.`,
1199
1237
  };
1200
1238
  });
1201
1239
  // ─── Tool: gnosys_history ────────────────────────────────────────────────
1202
- server.tool("gnosys_history", "View version history for a memory. Shows what changed and when. Every memory write/update creates a git commit, so the full evolution is available.", {
1240
+ regTool("gnosys_history", "View audit history for a memory. Shows what changed and when based on the audit log.", {
1203
1241
  path: z.string().describe("Path to memory, optionally layer-prefixed"),
1204
1242
  limit: z.number().optional().describe("Max history entries (default 20)"),
1205
1243
  projectRoot: projectRootParam,
1206
1244
  }, async ({ path: memPath, limit, projectRoot }) => {
1207
1245
  const ctx = await resolveToolContext(projectRoot);
1208
- // DB-first: resolve memory ID and show timestamps
1209
1246
  if (ctx.centralDb?.isAvailable()) {
1210
1247
  const dbMem = ctx.centralDb.getMemory(memPath);
1211
1248
  if (dbMem) {
1212
- // Query audit_log for this memory
1213
1249
  const audits = ctx.centralDb.getAuditLog(dbMem.id, limit || 20);
1214
1250
  if (audits.length > 0) {
1215
1251
  const lines = audits.map((e) => `- ${e.timestamp.split("T")[0]} — ${e.operation}${e.details ? ` (${e.details})` : ""}`);
@@ -1228,60 +1264,10 @@ server.tool("gnosys_history", "View version history for a memory. Shows what cha
1228
1264
  };
1229
1265
  }
1230
1266
  }
1231
- // Legacy file-based fallback
1232
- const memory = await ctx.resolver.readMemory(memPath);
1233
- if (!memory) {
1234
- return { content: [{ type: "text", text: `Memory not found: ${memPath}` }], isError: true };
1235
- }
1236
- const sourceStore = ctx.resolver.getStores().find((s) => s.label === memory.sourceLabel);
1237
- if (!sourceStore || !hasGitHistory(sourceStore.path)) {
1238
- return { content: [{ type: "text", text: "No git history available for this store." }], isError: true };
1239
- }
1240
- const history = getFileHistory(sourceStore.path, memory.relativePath, limit || 20);
1241
- if (history.length === 0) {
1242
- return { content: [{ type: "text", text: "No history found for this memory." }] };
1243
- }
1244
- const lines = history.map((e) => `- \`${e.commitHash.substring(0, 7)}\` ${e.date} — ${e.message}`);
1245
- return {
1246
- content: [{
1247
- type: "text",
1248
- text: `History for **${memory.frontmatter.title}** (${history.length} entries):\n\n${lines.join("\n")}\n\nUse gnosys_rollback with a commit hash to revert to a prior version.`,
1249
- }],
1250
- };
1251
- });
1252
- // ─── Tool: gnosys_rollback ──────────────────────────────────────────────
1253
- server.tool("gnosys_rollback", "Rollback a memory to its state at a specific commit. Non-destructive: creates a new commit with the reverted content. Use gnosys_history first to find the target commit hash.", {
1254
- path: z.string().describe("Path to memory, optionally layer-prefixed"),
1255
- commitHash: z.string().describe("Git commit hash to revert to (full or abbreviated)"),
1256
- projectRoot: projectRootParam,
1257
- }, async ({ path: memPath, commitHash, projectRoot }) => {
1258
- const ctx = await resolveToolContext(projectRoot);
1259
- const memory = await ctx.resolver.readMemory(memPath);
1260
- if (!memory) {
1261
- return { content: [{ type: "text", text: `Memory not found: ${memPath}` }], isError: true };
1262
- }
1263
- const sourceStore = ctx.resolver.getStores().find((s) => s.label === memory.sourceLabel);
1264
- if (!sourceStore?.writable) {
1265
- return { content: [{ type: "text", text: "Cannot rollback: store is read-only." }], isError: true };
1266
- }
1267
- const success = rollbackToCommit(sourceStore.path, memory.relativePath, commitHash);
1268
- if (!success) {
1269
- return { content: [{ type: "text", text: `Rollback failed. Verify the commit hash with gnosys_history.` }], isError: true };
1270
- }
1271
- // Reindex after rollback
1272
- if (ctx.search)
1273
- await reindexAllStores();
1274
- // Read the reverted memory
1275
- const reverted = await ctx.resolver.readMemory(memPath);
1276
- return {
1277
- content: [{
1278
- type: "text",
1279
- text: `Rolled back **${memory.frontmatter.title}** to commit ${commitHash.substring(0, 7)}.\n\nCurrent state: ${reverted?.frontmatter.title} [${reverted?.frontmatter.status}] (confidence: ${reverted?.frontmatter.confidence})`,
1280
- }],
1281
- };
1267
+ return { content: [{ type: "text", text: `Memory not found: ${memPath}` }], isError: true };
1282
1268
  });
1283
1269
  // ─── Tool: gnosys_lens ──────────────────────────────────────────────────
1284
- server.tool("gnosys_lens", "Filtered view of memories. Combine criteria to focus on specific subsets — e.g., 'active decisions about auth with confidence > 0.8'. Use AND (default) to require all criteria, or OR to match any.", {
1270
+ regTool("gnosys_lens", "Filtered view of memories. Combine criteria to focus on specific subsets — e.g., 'active decisions about auth with confidence > 0.8'. Use AND (default) to require all criteria, or OR to match any.", {
1285
1271
  category: z.string().optional().describe("Filter by category"),
1286
1272
  tags: z.array(z.string()).optional().describe("Filter by tags"),
1287
1273
  tagMatchMode: z.enum(["any", "all"]).optional().describe("'any' = has any listed tag (default), 'all' = must have every listed tag"),
@@ -1297,71 +1283,82 @@ server.tool("gnosys_lens", "Filtered view of memories. Combine criteria to focus
1297
1283
  operator: z.enum(["AND", "OR"]).optional().describe("Compound operator when multiple filter groups are provided (default: AND)"),
1298
1284
  projectRoot: projectRootParam,
1299
1285
  }, async ({ category, tags, tagMatchMode, status, author, authority, minConfidence, maxConfidence, createdAfter, createdBefore, modifiedAfter, modifiedBefore, projectRoot }) => {
1300
- const ctx = await resolveToolContext(projectRoot);
1301
- const allMemories = await ctx.resolver.getAllMemories();
1302
- const lens = {};
1303
- if (category)
1304
- lens.category = category;
1305
- if (tags) {
1306
- lens.tags = tags;
1307
- lens.tagMatchMode = tagMatchMode || "any";
1308
- }
1309
- if (status)
1310
- lens.status = status;
1311
- if (author)
1312
- lens.author = author;
1313
- if (authority)
1314
- lens.authority = authority;
1315
- if (minConfidence !== undefined)
1316
- lens.minConfidence = minConfidence;
1317
- if (maxConfidence !== undefined)
1318
- lens.maxConfidence = maxConfidence;
1319
- if (createdAfter)
1320
- lens.createdAfter = createdAfter;
1321
- if (createdBefore)
1322
- lens.createdBefore = createdBefore;
1323
- if (modifiedAfter)
1324
- lens.modifiedAfter = modifiedAfter;
1325
- if (modifiedBefore)
1326
- lens.modifiedBefore = modifiedBefore;
1327
- const result = applyLens(allMemories, lens);
1328
- if (result.length === 0) {
1329
- return { content: [{ type: "text", text: "No memories match the lens filter." }] };
1330
- }
1331
- const lines = result.map((m) => `- **${m.frontmatter.title}** [${m.frontmatter.status}] (${m.frontmatter.confidence})\n ${m.sourceLabel ? m.sourceLabel + ":" : ""}${m.relativePath}`);
1332
- return {
1333
- content: [{ type: "text", text: `${result.length} memories match:\n\n${lines.join("\n\n")}` }],
1334
- };
1286
+ try {
1287
+ const ctx = await resolveToolContext(projectRoot);
1288
+ const allMemories = await ctx.resolver.getAllMemories();
1289
+ const lens = {};
1290
+ if (category)
1291
+ lens.category = category;
1292
+ if (tags) {
1293
+ lens.tags = tags;
1294
+ lens.tagMatchMode = tagMatchMode || "any";
1295
+ }
1296
+ if (status)
1297
+ lens.status = status;
1298
+ if (author)
1299
+ lens.author = author;
1300
+ if (authority)
1301
+ lens.authority = authority;
1302
+ if (minConfidence !== undefined)
1303
+ lens.minConfidence = minConfidence;
1304
+ if (maxConfidence !== undefined)
1305
+ lens.maxConfidence = maxConfidence;
1306
+ if (createdAfter)
1307
+ lens.createdAfter = createdAfter;
1308
+ if (createdBefore)
1309
+ lens.createdBefore = createdBefore;
1310
+ if (modifiedAfter)
1311
+ lens.modifiedAfter = modifiedAfter;
1312
+ if (modifiedBefore)
1313
+ lens.modifiedBefore = modifiedBefore;
1314
+ const result = applyLens(allMemories, lens);
1315
+ if (result.length === 0) {
1316
+ return { content: [{ type: "text", text: "No memories match the lens filter." }] };
1317
+ }
1318
+ const lines = result.map((m) => `- **${m.frontmatter.title}** [${m.frontmatter.status}] (${m.frontmatter.confidence})\n ${m.sourceLabel ? m.sourceLabel + ":" : ""}${m.relativePath}`);
1319
+ return {
1320
+ content: [{ type: "text", text: `${result.length} memories match:\n\n${lines.join("\n\n")}` }],
1321
+ };
1322
+ }
1323
+ catch (err) {
1324
+ return { content: [{ type: "text", text: formatMcpError("applying memory lens", err) }], isError: true };
1325
+ }
1335
1326
  });
1336
1327
  // ─── Tool: gnosys_timeline ───────────────────────────────────────────────
1337
- server.tool("gnosys_timeline", "View memory creation and modification activity over time. Shows how knowledge evolves by grouping memories into time periods.", {
1328
+ regTool("gnosys_timeline", "View memory creation and modification activity over time. Shows how knowledge evolves by grouping memories into time periods.", {
1338
1329
  period: z.enum(["day", "week", "month", "year"]).optional().describe("Grouping period (default: month)"),
1339
1330
  projectRoot: projectRootParam,
1340
1331
  }, async ({ period, projectRoot }) => {
1341
- const ctx = await resolveToolContext(projectRoot);
1342
- const allMemories = await ctx.resolver.getAllMemories();
1343
- const entries = groupByPeriod(allMemories, period || "month");
1344
- if (entries.length === 0) {
1345
- return { content: [{ type: "text", text: "No memories found for timeline." }] };
1332
+ try {
1333
+ const ctx = await resolveToolContext(projectRoot);
1334
+ const allMemories = await ctx.resolver.getAllMemories();
1335
+ const entries = groupByPeriod(allMemories, period || "month");
1336
+ if (entries.length === 0) {
1337
+ return { content: [{ type: "text", text: "No memories found for timeline." }] };
1338
+ }
1339
+ const lines = entries.map((e) => `**${e.period}** — ${e.created} created, ${e.modified} modified\n ${e.titles.slice(0, 5).join(", ")}${e.titles.length > 5 ? ` (+${e.titles.length - 5} more)` : ""}`);
1340
+ return {
1341
+ content: [{ type: "text", text: `Knowledge Timeline (by ${period || "month"}):\n\n${lines.join("\n\n")}` }],
1342
+ };
1343
+ }
1344
+ catch (err) {
1345
+ return { content: [{ type: "text", text: formatMcpError("building timeline", err) }], isError: true };
1346
1346
  }
1347
- const lines = entries.map((e) => `**${e.period}** — ${e.created} created, ${e.modified} modified\n ${e.titles.slice(0, 5).join(", ")}${e.titles.length > 5 ? ` (+${e.titles.length - 5} more)` : ""}`);
1348
- return {
1349
- content: [{ type: "text", text: `Knowledge Timeline (by ${period || "month"}):\n\n${lines.join("\n\n")}` }],
1350
- };
1351
1347
  });
1352
1348
  // ─── Tool: gnosys_stats ─────────────────────────────────────────────────
1353
- server.tool("gnosys_stats", "Summary statistics across all memories — totals by category, status, author, authority, average confidence, and date ranges.", { projectRoot: projectRootParam }, async ({ projectRoot }) => {
1354
- const ctx = await resolveToolContext(projectRoot);
1355
- const allMemories = await ctx.resolver.getAllMemories();
1356
- const stats = computeStats(allMemories);
1357
- if (stats.totalCount === 0) {
1358
- return { content: [{ type: "text", text: "No memories found." }] };
1359
- }
1360
- const catLines = Object.entries(stats.byCategory).map(([k, v]) => ` ${k}: ${v}`).join("\n");
1361
- const statusLines = Object.entries(stats.byStatus).map(([k, v]) => ` ${k}: ${v}`).join("\n");
1362
- const authorLines = Object.entries(stats.byAuthor).map(([k, v]) => ` ${k}: ${v}`).join("\n");
1363
- const authLines = Object.entries(stats.byAuthority).map(([k, v]) => ` ${k}: ${v}`).join("\n");
1364
- const text = `Gnosys Memory Statistics
1349
+ regTool("gnosys_stats", "Summary statistics across all memories — totals by category, status, author, authority, average confidence, and date ranges.", { projectRoot: projectRootParam }, async ({ projectRoot }) => {
1350
+ try {
1351
+ const ctx = await resolveToolContext(projectRoot);
1352
+ const allMemories = await ctx.resolver.getAllMemories();
1353
+ const stats = computeStats(allMemories);
1354
+ if (stats.totalCount === 0) {
1355
+ return { content: [{ type: "text", text: "No memories found." }] };
1356
+ }
1357
+ const catLines = Object.entries(stats.byCategory).map(([k, v]) => ` ${k}: ${v}`).join("\n");
1358
+ const statusLines = Object.entries(stats.byStatus).map(([k, v]) => ` ${k}: ${v}`).join("\n");
1359
+ const authorLines = Object.entries(stats.byAuthor).map(([k, v]) => ` ${k}: ${v}`).join("\n");
1360
+ const authLines = Object.entries(stats.byAuthority).map(([k, v]) => ` ${k}: ${v}`).join("\n");
1361
+ const text = `Gnosys Memory Statistics
1365
1362
  Total: ${stats.totalCount} memories
1366
1363
 
1367
1364
  By Category:
@@ -1380,10 +1377,14 @@ Average Confidence: ${stats.averageConfidence.toFixed(2)}
1380
1377
  Oldest: ${stats.oldestCreated || "—"}
1381
1378
  Newest: ${stats.newestCreated || "—"}
1382
1379
  Last Modified: ${stats.lastModified || "—"}`;
1383
- return { content: [{ type: "text", text }] };
1380
+ return { content: [{ type: "text", text }] };
1381
+ }
1382
+ catch (err) {
1383
+ return { content: [{ type: "text", text: formatMcpError("computing statistics", err) }], isError: true };
1384
+ }
1384
1385
  });
1385
1386
  // ─── Tool: gnosys_links ─────────────────────────────────────────────────
1386
- server.tool("gnosys_links", "Show wikilinks for a specific memory — outgoing [[links]] and backlinks from other memories. Obsidian-compatible [[Title]] and [[path|display]] syntax.", {
1387
+ regTool("gnosys_links", "Show wikilinks for a specific memory — outgoing [[links]] and backlinks from other memories. Obsidian-compatible [[Title]] and [[path|display]] syntax.", {
1387
1388
  path: z.string().describe("Path to memory, optionally layer-prefixed"),
1388
1389
  projectRoot: projectRootParam,
1389
1390
  }, async ({ path: memPath, projectRoot }) => {
@@ -1447,17 +1448,22 @@ server.tool("gnosys_links", "Show wikilinks for a specific memory — outgoing [
1447
1448
  return { content: [{ type: "text", text: parts.join("\n") }] };
1448
1449
  });
1449
1450
  // ─── Tool: gnosys_graph ─────────────────────────────────────────────────
1450
- server.tool("gnosys_graph", "Show the full cross-reference graph across all memories. Reveals clusters, orphaned links, and the most-connected memories.", { projectRoot: projectRootParam }, async ({ projectRoot }) => {
1451
- const ctx = await resolveToolContext(projectRoot);
1452
- const allMemories = await ctx.resolver.getAllMemories();
1453
- if (allMemories.length === 0) {
1454
- return { content: [{ type: "text", text: "No memories found." }] };
1451
+ regTool("gnosys_graph", "Show the full cross-reference graph across all memories. Reveals clusters, orphaned links, and the most-connected memories.", { projectRoot: projectRootParam }, async ({ projectRoot }) => {
1452
+ try {
1453
+ const ctx = await resolveToolContext(projectRoot);
1454
+ const allMemories = await ctx.resolver.getAllMemories();
1455
+ if (allMemories.length === 0) {
1456
+ return { content: [{ type: "text", text: "No memories found." }] };
1457
+ }
1458
+ const graph = buildLinkGraph(allMemories);
1459
+ return { content: [{ type: "text", text: formatGraphSummary(graph) }] };
1460
+ }
1461
+ catch (err) {
1462
+ return { content: [{ type: "text", text: formatMcpError("building graph", err) }], isError: true };
1455
1463
  }
1456
- const graph = buildLinkGraph(allMemories);
1457
- return { content: [{ type: "text", text: formatGraphSummary(graph) }] };
1458
1464
  });
1459
1465
  // ─── Tool: gnosys_bootstrap ─────────────────────────────────────────────
1460
- server.tool("gnosys_bootstrap", "Batch-import existing documents from a directory into the memory store. Scans for markdown files and creates memories. Use dry_run=true to preview.", {
1466
+ regTool("gnosys_bootstrap", "Batch-import existing documents from a directory into the memory store. Scans for markdown files and creates memories. Use dry_run=true to preview.", {
1461
1467
  sourceDir: z.string().describe("Absolute path to directory containing documents to import"),
1462
1468
  patterns: z.array(z.string()).optional().describe("File glob patterns (default: ['**/*.md'])"),
1463
1469
  skipExisting: z.boolean().optional().describe("Skip files whose titles already exist (default: false)"),
@@ -1516,7 +1522,7 @@ server.tool("gnosys_bootstrap", "Batch-import existing documents from a director
1516
1522
  }
1517
1523
  });
1518
1524
  // ─── Tool: gnosys_import ─────────────────────────────────────────────────
1519
- server.tool("gnosys_import", "Bulk import structured data (CSV, JSON, JSONL) into Gnosys memories. Map source fields to title/category/content/tags/relevance. Use mode='llm' for smart ingestion with keyword clouds, or 'structured' for fast direct mapping. For large datasets (>100 records with LLM), the CLI is recommended: gnosys import <file>", {
1525
+ regTool("gnosys_import", "Bulk import structured data (CSV, JSON, JSONL) into Gnosys memories. Map source fields to title/category/content/tags/relevance. Use mode='llm' for smart ingestion with keyword clouds, or 'structured' for fast direct mapping. For large datasets (>100 records with LLM), the CLI is recommended: gnosys import <file>", {
1520
1526
  format: z.enum(["csv", "json", "jsonl"]).describe("Data format"),
1521
1527
  data: z.string().describe("File path, URL, or inline data"),
1522
1528
  mapping: z
@@ -1597,7 +1603,7 @@ server.tool("gnosys_import", "Bulk import structured data (CSV, JSON, JSONL) int
1597
1603
  }
1598
1604
  });
1599
1605
  // ─── Tool: gnosys_hybrid_search ──────────────────────────────────────────
1600
- server.tool("gnosys_hybrid_search", "Search memories using hybrid keyword + semantic search with Reciprocal Rank Fusion. Combines FTS5 keyword matching with embedding-based semantic similarity for best results. Run gnosys_reindex first if embeddings don't exist yet.", {
1606
+ regTool("gnosys_hybrid_search", "Search memories using hybrid keyword + semantic search with Reciprocal Rank Fusion. Combines FTS5 keyword matching with embedding-based semantic similarity for best results. Run gnosys_reindex first if embeddings don't exist yet.", {
1601
1607
  query: z.string().describe("Natural language search query"),
1602
1608
  limit: z.number().optional().describe("Max results (default 15)"),
1603
1609
  mode: z.enum(["keyword", "semantic", "hybrid"]).optional().describe("Search mode (default: hybrid)"),
@@ -1649,7 +1655,7 @@ server.tool("gnosys_hybrid_search", "Search memories using hybrid keyword + sema
1649
1655
  }
1650
1656
  });
1651
1657
  // ─── Tool: gnosys_semantic_search ────────────────────────────────────────
1652
- server.tool("gnosys_semantic_search", "Search memories using semantic similarity only (no keyword matching). Finds conceptually related memories even without exact keyword matches. Requires embeddings — run gnosys_reindex first.", {
1658
+ regTool("gnosys_semantic_search", "Search memories using semantic similarity only (no keyword matching). Finds conceptually related memories even without exact keyword matches. Requires embeddings — run gnosys_reindex first.", {
1653
1659
  query: z.string().describe("Natural language search query"),
1654
1660
  limit: z.number().optional().describe("Max results (default 15)"),
1655
1661
  projectRoot: projectRootParam,
@@ -1685,7 +1691,7 @@ server.tool("gnosys_semantic_search", "Search memories using semantic similarity
1685
1691
  }
1686
1692
  });
1687
1693
  // ─── Tool: gnosys_reindex ────────────────────────────────────────────────
1688
- server.tool("gnosys_reindex", "Rebuild all semantic embeddings from every memory file. Downloads the embedding model (~80 MB) on first run. Required before hybrid/semantic search can be used. Safe to re-run — fully regenerates the index.", { projectRoot: projectRootParam }, async ({ projectRoot }) => {
1694
+ regTool("gnosys_reindex", "Rebuild all semantic embeddings from every memory file. Downloads the embedding model (~80 MB) on first run. Required before hybrid/semantic search can be used. Safe to re-run — fully regenerates the index.", { projectRoot: projectRootParam }, async ({ projectRoot }) => {
1689
1695
  // Note: reindex operates on all stores, projectRoot is for API consistency
1690
1696
  (projectRoot); // quiets unused warning if any
1691
1697
  await ensureHeavyDeps();
@@ -1716,7 +1722,7 @@ server.tool("gnosys_reindex", "Rebuild all semantic embeddings from every memory
1716
1722
  }
1717
1723
  });
1718
1724
  // ─── Tool: gnosys_ask ────────────────────────────────────────────────────
1719
- server.tool("gnosys_ask", "Ask a natural-language question and get a synthesized answer with citations from the entire vault. Uses hybrid search to find relevant memories, then LLM to synthesize a cited response. Citations are Obsidian wikilinks [[filename.md]]. Requires an LLM provider (Anthropic or Ollama) and embeddings (run gnosys_reindex first).", {
1725
+ regTool("gnosys_ask", "Ask a natural-language question and get a synthesized answer with citations from the entire vault. Uses hybrid search to find relevant memories, then LLM to synthesize a cited response. Citations are Obsidian wikilinks [[filename.md]]. Requires an LLM provider (Anthropic or Ollama) and embeddings (run gnosys_reindex first).", {
1720
1726
  question: z.string().describe("Natural language question to answer from the vault"),
1721
1727
  limit: z.number().optional().describe("Max memories to retrieve (default 15)"),
1722
1728
  mode: z.enum(["keyword", "semantic", "hybrid"]).optional().describe("Search mode (default: hybrid)"),
@@ -1773,7 +1779,7 @@ server.tool("gnosys_ask", "Ask a natural-language question and get a synthesized
1773
1779
  }
1774
1780
  });
1775
1781
  // ─── Tool: gnosys_maintain ────────────────────────────────────────────────
1776
- server.tool("gnosys_maintain", "Run vault maintenance: detect duplicate memories, apply confidence decay, consolidate similar memories. Use --dry-run mode first to see what would change. Requires embeddings (run gnosys_reindex first).", {
1782
+ regTool("gnosys_maintain", "Run vault maintenance: detect duplicate memories, apply confidence decay, consolidate similar memories. Use --dry-run mode first to see what would change. Requires embeddings (run gnosys_reindex first).", {
1777
1783
  dryRun: z.boolean().optional().describe("Show what would change without modifying anything (default: true)"),
1778
1784
  autoApply: z.boolean().optional().describe("Automatically apply all changes (default: false)"),
1779
1785
  projectRoot: projectRootParam,
@@ -1808,7 +1814,7 @@ server.tool("gnosys_maintain", "Run vault maintenance: detect duplicate memories
1808
1814
  }
1809
1815
  });
1810
1816
  // ─── Tool: gnosys_dearchive ──────────────────────────────────────────────
1811
- server.tool("gnosys_dearchive", "Force-dearchive memories from archive.db back to active. Search the archive for memories matching a query, then restore them to the active layer. Used when you need specific archived knowledge that wasn't auto-dearchived by search/ask.", {
1817
+ regTool("gnosys_dearchive", "Force-dearchive memories from archive.db back to active. Search the archive for memories matching a query, then restore them to the active layer. Used when you need specific archived knowledge that wasn't auto-dearchived by search/ask.", {
1812
1818
  query: z.string().describe("Search query to find archived memories to restore"),
1813
1819
  limit: z.number().optional().describe("Max memories to dearchive (default 5)"),
1814
1820
  projectRoot: projectRootParam,
@@ -1826,7 +1832,7 @@ server.tool("gnosys_dearchive", "Force-dearchive memories from archive.db back t
1826
1832
  const archive = new GnosysArchive(writeTarget.path);
1827
1833
  if (!archive.isAvailable()) {
1828
1834
  return {
1829
- content: [{ type: "text", text: "Archive not available. Is better-sqlite3 installed?" }],
1835
+ content: [{ type: "text", text: "Archive not available. Install it with: npm install better-sqlite3" }],
1830
1836
  isError: true,
1831
1837
  };
1832
1838
  }
@@ -1863,7 +1869,7 @@ server.tool("gnosys_dearchive", "Force-dearchive memories from archive.db back t
1863
1869
  }
1864
1870
  });
1865
1871
  // ─── Tool: gnosys_reindex_graph ──────────────────────────────────────────
1866
- server.tool("gnosys_reindex_graph", "Build or rebuild the wikilink graph (.gnosys/graph.json). Parses all [[wikilinks]] across memories and generates a persistent JSON graph with nodes, edges, and stats.", { projectRoot: projectRootParam }, async ({ projectRoot }) => {
1872
+ regTool("gnosys_reindex_graph", "Build or rebuild the wikilink graph (.gnosys/graph.json). Parses all [[wikilinks]] across memories and generates a persistent JSON graph with nodes, edges, and stats.", { projectRoot: projectRootParam }, async ({ projectRoot }) => {
1867
1873
  const ctx = await resolveToolContext(projectRoot);
1868
1874
  try {
1869
1875
  const { reindexGraph, formatGraphStats } = await import("./lib/graph.js");
@@ -1880,54 +1886,59 @@ server.tool("gnosys_reindex_graph", "Build or rebuild the wikilink graph (.gnosy
1880
1886
  }
1881
1887
  });
1882
1888
  // ─── Tool: gnosys_dream ──────────────────────────────────────────────────
1883
- server.tool("gnosys_dream", "Run a Dream Mode cycle — idle-time consolidation that decays confidence, generates category summaries, discovers relationships, and creates review suggestions. NEVER deletes memories. Safe to run anytime.", {
1889
+ regTool("gnosys_dream", "Run a Dream Mode cycle — idle-time consolidation that decays confidence, generates category summaries, discovers relationships, and creates review suggestions. NEVER deletes memories. Safe to run anytime.", {
1884
1890
  maxRuntimeMinutes: z.number().int().min(1).max(120).default(30).optional().describe("Max runtime in minutes"),
1885
1891
  selfCritique: z.boolean().default(true).optional().describe("Enable self-critique scoring"),
1886
1892
  generateSummaries: z.boolean().default(true).optional().describe("Generate category summaries"),
1887
1893
  discoverRelationships: z.boolean().default(true).optional().describe("Discover relationships between memories"),
1888
1894
  projectRoot: projectRootParam,
1889
1895
  }, async (params) => {
1890
- const ctx = await resolveToolContext(params.projectRoot);
1891
- if (!ctx.centralDb || !ctx.centralDb.isAvailable() || !ctx.centralDb.isMigrated()) {
1896
+ try {
1897
+ const ctx = await resolveToolContext(params.projectRoot);
1898
+ if (!ctx.centralDb || !ctx.centralDb.isAvailable() || !ctx.centralDb.isMigrated()) {
1899
+ return {
1900
+ content: [
1901
+ {
1902
+ type: "text",
1903
+ text: "Dream Mode requires gnosys.db (v2.0). Run `gnosys migrate` first.",
1904
+ },
1905
+ ],
1906
+ };
1907
+ }
1908
+ // Record activity to reset idle timer (if scheduler is running)
1909
+ dreamScheduler?.recordActivity();
1910
+ const dreamConfig = {
1911
+ enabled: true,
1912
+ idleMinutes: 0, // Run immediately (manual trigger)
1913
+ maxRuntimeMinutes: params.maxRuntimeMinutes ?? 30,
1914
+ selfCritique: params.selfCritique ?? true,
1915
+ generateSummaries: params.generateSummaries ?? true,
1916
+ discoverRelationships: params.discoverRelationships ?? true,
1917
+ minMemories: 1, // No minimum for manual trigger
1918
+ provider: ctx.config?.dream?.provider || "ollama",
1919
+ model: ctx.config?.dream?.model,
1920
+ };
1921
+ // v5.9.1 (#100): dream engine pulls LLM provider machinery — load lazily.
1922
+ const { GnosysDreamEngine, formatDreamReport } = await import("./lib/dream.js");
1923
+ const engine = new GnosysDreamEngine(ctx.centralDb, ctx.config || DEFAULT_CONFIG, dreamConfig);
1924
+ const report = await engine.dream((phase, detail) => {
1925
+ console.error(`[dream:${phase}] ${detail}`);
1926
+ });
1892
1927
  return {
1893
1928
  content: [
1894
1929
  {
1895
1930
  type: "text",
1896
- text: "Dream Mode requires gnosys.db (v2.0). Run `gnosys migrate` first.",
1931
+ text: formatDreamReport(report),
1897
1932
  },
1898
1933
  ],
1899
1934
  };
1900
1935
  }
1901
- // Record activity to reset idle timer (if scheduler is running)
1902
- dreamScheduler?.recordActivity();
1903
- const dreamConfig = {
1904
- enabled: true,
1905
- idleMinutes: 0, // Run immediately (manual trigger)
1906
- maxRuntimeMinutes: params.maxRuntimeMinutes ?? 30,
1907
- selfCritique: params.selfCritique ?? true,
1908
- generateSummaries: params.generateSummaries ?? true,
1909
- discoverRelationships: params.discoverRelationships ?? true,
1910
- minMemories: 1, // No minimum for manual trigger
1911
- provider: ctx.config?.dream?.provider || "ollama",
1912
- model: ctx.config?.dream?.model,
1913
- };
1914
- // v5.9.1 (#100): dream engine pulls LLM provider machinery — load lazily.
1915
- const { GnosysDreamEngine, formatDreamReport } = await import("./lib/dream.js");
1916
- const engine = new GnosysDreamEngine(ctx.centralDb, ctx.config || DEFAULT_CONFIG, dreamConfig);
1917
- const report = await engine.dream((phase, detail) => {
1918
- console.error(`[dream:${phase}] ${detail}`);
1919
- });
1920
- return {
1921
- content: [
1922
- {
1923
- type: "text",
1924
- text: formatDreamReport(report),
1925
- },
1926
- ],
1927
- };
1936
+ catch (err) {
1937
+ return { content: [{ type: "text", text: formatMcpError("running dream mode", err) }], isError: true };
1938
+ }
1928
1939
  });
1929
1940
  // ─── Tool: gnosys_export ─────────────────────────────────────────────────
1930
- server.tool("gnosys_export", "Export gnosys.db to Obsidian-compatible vault — atomic Markdown files with YAML frontmatter, [[wikilinks]], category summaries, and relationship graph. One-way export, never modifies gnosys.db.", {
1941
+ regTool("gnosys_export", "Export gnosys.db to Obsidian-compatible vault — atomic Markdown files with YAML frontmatter, [[wikilinks]], category summaries, and relationship graph. One-way export, never modifies gnosys.db.", {
1931
1942
  targetDir: z.string().describe("Target directory path for export"),
1932
1943
  activeOnly: z.boolean().default(true).optional().describe("Only export active memories (default: true)"),
1933
1944
  overwrite: z.boolean().default(false).optional().describe("Overwrite existing files"),
@@ -1936,39 +1947,44 @@ server.tool("gnosys_export", "Export gnosys.db to Obsidian-compatible vault —
1936
1947
  includeGraph: z.boolean().default(true).optional().describe("Include relationship graph"),
1937
1948
  projectRoot: projectRootParam,
1938
1949
  }, async (params) => {
1939
- const ctx = await resolveToolContext(params.projectRoot);
1940
- if (!ctx.centralDb || !ctx.centralDb.isAvailable() || !ctx.centralDb.isMigrated()) {
1950
+ try {
1951
+ const ctx = await resolveToolContext(params.projectRoot);
1952
+ if (!ctx.centralDb || !ctx.centralDb.isAvailable() || !ctx.centralDb.isMigrated()) {
1953
+ return {
1954
+ content: [
1955
+ {
1956
+ type: "text",
1957
+ text: "Export requires gnosys.db (v2.0). Run `gnosys migrate` first.",
1958
+ },
1959
+ ],
1960
+ };
1961
+ }
1962
+ // v5.9.1 (#100): exporter pulls obsidian-flavored markdown gen — lazy.
1963
+ const { GnosysExporter, formatExportReport } = await import("./lib/export.js");
1964
+ const exporter = new GnosysExporter(ctx.centralDb);
1965
+ const report = await exporter.export({
1966
+ targetDir: params.targetDir,
1967
+ activeOnly: params.activeOnly ?? true,
1968
+ overwrite: params.overwrite ?? false,
1969
+ includeSummaries: params.includeSummaries ?? true,
1970
+ includeReviews: params.includeReviews ?? true,
1971
+ includeGraph: params.includeGraph ?? true,
1972
+ });
1941
1973
  return {
1942
1974
  content: [
1943
1975
  {
1944
1976
  type: "text",
1945
- text: "Export requires gnosys.db (v2.0). Run `gnosys migrate` first.",
1977
+ text: formatExportReport(report),
1946
1978
  },
1947
1979
  ],
1948
1980
  };
1949
1981
  }
1950
- // v5.9.1 (#100): exporter pulls obsidian-flavored markdown gen — lazy.
1951
- const { GnosysExporter, formatExportReport } = await import("./lib/export.js");
1952
- const exporter = new GnosysExporter(ctx.centralDb);
1953
- const report = await exporter.export({
1954
- targetDir: params.targetDir,
1955
- activeOnly: params.activeOnly ?? true,
1956
- overwrite: params.overwrite ?? false,
1957
- includeSummaries: params.includeSummaries ?? true,
1958
- includeReviews: params.includeReviews ?? true,
1959
- includeGraph: params.includeGraph ?? true,
1960
- });
1961
- return {
1962
- content: [
1963
- {
1964
- type: "text",
1965
- text: formatExportReport(report),
1966
- },
1967
- ],
1968
- };
1982
+ catch (err) {
1983
+ return { content: [{ type: "text", text: formatMcpError("exporting vault", err) }], isError: true };
1984
+ }
1969
1985
  });
1970
1986
  // ─── Tool: gnosys_dashboard ──────────────────────────────────────────────
1971
- server.tool("gnosys_dashboard", "Show the Gnosys system dashboard: memory counts, maintenance health, graph stats, LLM provider status. Returns structured JSON.", { projectRoot: projectRootParam }, async ({ projectRoot }) => {
1987
+ regTool("gnosys_dashboard", "Show the Gnosys system dashboard: memory counts, maintenance health, graph stats, LLM provider status. Returns structured JSON.", { projectRoot: projectRootParam }, async ({ projectRoot }) => {
1972
1988
  const ctx = await resolveToolContext(projectRoot);
1973
1989
  try {
1974
1990
  const { collectDashboardData, formatDashboardJSON } = await import("./lib/dashboard.js");
@@ -1985,40 +2001,45 @@ server.tool("gnosys_dashboard", "Show the Gnosys system dashboard: memory counts
1985
2001
  }
1986
2002
  });
1987
2003
  // ─── Tool: gnosys_stores ─────────────────────────────────────────────────
1988
- server.tool("gnosys_stores", "Debug tool — lists all detected Gnosys stores across registered projects, MCP workspace roots, cwd, and environment variables. Shows which store is active and helps diagnose multi-project routing.", {}, async () => {
1989
- const lines = [];
1990
- lines.push("GNOSYS STORES — Multi-Project Overview");
1991
- lines.push("=".repeat(45));
1992
- lines.push("");
1993
- // Active stores
1994
- lines.push("ACTIVE STORES:");
1995
- lines.push(resolver.getSummary());
1996
- lines.push("");
1997
- // MCP roots
1998
- const mcpRoots = GnosysResolver.getMcpRoots();
1999
- lines.push(`MCP WORKSPACE ROOTS (${mcpRoots.length}):`);
2000
- if (mcpRoots.length === 0) {
2001
- lines.push(" (none host may not support roots/list)");
2004
+ regTool("gnosys_stores", "Debug tool — lists all detected Gnosys stores across registered projects, MCP workspace roots, cwd, and environment variables. Shows which store is active and helps diagnose multi-project routing.", {}, async () => {
2005
+ try {
2006
+ const lines = [];
2007
+ lines.push("GNOSYS STORES — Multi-Project Overview");
2008
+ lines.push("=".repeat(45));
2009
+ lines.push("");
2010
+ // Active stores
2011
+ lines.push("ACTIVE STORES:");
2012
+ lines.push(resolver.getSummary());
2013
+ lines.push("");
2014
+ // MCP roots
2015
+ const mcpRoots = GnosysResolver.getMcpRoots();
2016
+ lines.push(`MCP WORKSPACE ROOTS (${mcpRoots.length}):`);
2017
+ if (mcpRoots.length === 0) {
2018
+ lines.push(" (none — host may not support roots/list)");
2019
+ }
2020
+ else {
2021
+ for (const root of mcpRoots) {
2022
+ lines.push(` ${root}`);
2023
+ }
2024
+ }
2025
+ lines.push("");
2026
+ // All detected stores
2027
+ const detected = await resolver.detectAllStores();
2028
+ lines.push(`ALL DETECTED STORES (${detected.length}):`);
2029
+ for (const d of detected) {
2030
+ const status = d.isActive ? "✓ ACTIVE" : d.hasGnosys ? "available" : "no .gnosys";
2031
+ lines.push(` [${d.source}] ${d.path} — ${status}`);
2032
+ }
2033
+ lines.push("");
2034
+ // Usage hint
2035
+ lines.push("USAGE:");
2036
+ lines.push(" Pass projectRoot to any tool to target a specific project:");
2037
+ lines.push(' e.g. gnosys_add({ projectRoot: "/path/to/my-project", ... })');
2038
+ return { content: [{ type: "text", text: lines.join("\n") }] };
2039
+ }
2040
+ catch (err) {
2041
+ return { content: [{ type: "text", text: formatMcpError("listing stores", err) }], isError: true };
2002
2042
  }
2003
- else {
2004
- for (const root of mcpRoots) {
2005
- lines.push(` ${root}`);
2006
- }
2007
- }
2008
- lines.push("");
2009
- // All detected stores
2010
- const detected = await resolver.detectAllStores();
2011
- lines.push(`ALL DETECTED STORES (${detected.length}):`);
2012
- for (const d of detected) {
2013
- const status = d.isActive ? "✓ ACTIVE" : d.hasGnosys ? "available" : "no .gnosys";
2014
- lines.push(` [${d.source}] ${d.path} — ${status}`);
2015
- }
2016
- lines.push("");
2017
- // Usage hint
2018
- lines.push("USAGE:");
2019
- lines.push(" Pass projectRoot to any tool to target a specific project:");
2020
- lines.push(' e.g. gnosys_add({ projectRoot: "/path/to/my-project", ... })');
2021
- return { content: [{ type: "text", text: lines.join("\n") }] };
2022
2043
  });
2023
2044
  // ─── Helper: reindex search across all stores ────────────────────────────
2024
2045
  async function reindexAllStores() {
@@ -2043,7 +2064,7 @@ async function reindexAllStores() {
2043
2064
  // injecting relevant memories into the model context — no tool call needed.
2044
2065
  //
2045
2066
  // Priority 1 + audience: assistant = hosts inject this before every message.
2046
- server.resource("gnosys_recall", "gnosys://recall", {
2067
+ regResource("gnosys_recall", "gnosys://recall", {
2047
2068
  description: "Automatic memory injection. Hosts read this resource on every turn to inject the most relevant memories as context. Returns a <gnosys-recall> block with [[wikilinks]] and relevance scores. Priority 1 (highest) — designed for always-on context injection without any tool call. Configure aggressiveness in gnosys.json: recall.aggressive (default: true).",
2048
2069
  mimeType: "text/markdown",
2049
2070
  annotations: {
@@ -2086,7 +2107,7 @@ server.resource("gnosys_recall", "gnosys://recall", {
2086
2107
  // ─── Tool: gnosys_recall (query-specific fallback) ──────────────────────
2087
2108
  // For hosts that don't support MCP Resources, or when the agent wants to
2088
2109
  // recall memories for a specific query. The resource above is preferred.
2089
- server.tool("gnosys_recall", "Fast memory recall — inject relevant memories as context. Returns <gnosys-recall> block. In aggressive mode (default), always returns top memories even at medium relevance. Prefer the gnosys://recall MCP Resource for automatic injection (no tool call needed).", {
2110
+ regTool("gnosys_recall", "Fast memory recall — inject relevant memories as context. Returns <gnosys-recall> block. In aggressive mode (default), always returns top memories even at medium relevance. Prefer the gnosys://recall MCP Resource for automatic injection (no tool call needed).", {
2090
2111
  query: z
2091
2112
  .string()
2092
2113
  .describe("What the agent is currently working on. Use keywords. Example: 'auth JWT middleware' or 'database migration schema'"),
@@ -2095,32 +2116,37 @@ server.tool("gnosys_recall", "Fast memory recall — inject relevant memories as
2095
2116
  aggressive: z.boolean().optional().describe("Override aggressive mode for this call. Default: from gnosys.json (true)"),
2096
2117
  projectRoot: projectRootParam,
2097
2118
  }, async ({ query, limit, traceId, aggressive, projectRoot }) => {
2098
- const ctx = await resolveToolContext(projectRoot);
2099
- if (!ctx.search) {
2119
+ try {
2120
+ const ctx = await resolveToolContext(projectRoot);
2121
+ if (!ctx.search) {
2122
+ return {
2123
+ content: [{ type: "text", text: "<gnosys: no-strong-recall-needed>" }],
2124
+ };
2125
+ }
2126
+ const storePath = ctx.resolver.getWriteTarget()?.store.getStorePath() || "";
2127
+ const recallConfig = {
2128
+ ...ctx.config.recall,
2129
+ ...(aggressive !== undefined ? { aggressive } : {}),
2130
+ };
2131
+ const result = await recall(query, {
2132
+ limit: Math.min(limit || recallConfig.maxMemories, 15),
2133
+ search: ctx.search,
2134
+ resolver: ctx.resolver,
2135
+ storePath,
2136
+ traceId,
2137
+ recallConfig,
2138
+ gnosysDb: ctx.centralDb || undefined,
2139
+ });
2100
2140
  return {
2101
- content: [{ type: "text", text: "<gnosys: no-strong-recall-needed>" }],
2141
+ content: [{ type: "text", text: formatRecall(result) }],
2102
2142
  };
2103
2143
  }
2104
- const storePath = ctx.resolver.getWriteTarget()?.store.getStorePath() || "";
2105
- const recallConfig = {
2106
- ...ctx.config.recall,
2107
- ...(aggressive !== undefined ? { aggressive } : {}),
2108
- };
2109
- const result = await recall(query, {
2110
- limit: Math.min(limit || recallConfig.maxMemories, 15),
2111
- search: ctx.search,
2112
- resolver: ctx.resolver,
2113
- storePath,
2114
- traceId,
2115
- recallConfig,
2116
- gnosysDb: ctx.centralDb || undefined,
2117
- });
2118
- return {
2119
- content: [{ type: "text", text: formatRecall(result) }],
2120
- };
2144
+ catch (err) {
2145
+ return { content: [{ type: "text", text: formatMcpError("recalling memories", err) }], isError: true };
2146
+ }
2121
2147
  });
2122
2148
  // ─── Tool: gnosys_audit ──────────────────────────────────────────────────
2123
- server.tool("gnosys_audit", "View the audit trail of all memory operations (reads, writes, reinforcements, dearchives, maintenance). Shows a timeline of what happened and when. Useful for debugging 'why did the agent forget X?'", {
2149
+ regTool("gnosys_audit", "View the audit trail of all memory operations (reads, writes, reinforcements, dearchives, maintenance). Shows a timeline of what happened and when. Useful for debugging 'why did the agent forget X?'", {
2124
2150
  days: z.number().optional().describe("Number of days to look back (default 7)"),
2125
2151
  operation: z.string().optional().describe("Filter by operation type: read, write, reinforce, dearchive, archive, maintain, search, ask, recall"),
2126
2152
  limit: z.number().optional().describe("Max entries to return (default 100)"),
@@ -2144,7 +2170,7 @@ server.tool("gnosys_audit", "View the audit trail of all memory operations (read
2144
2170
  };
2145
2171
  });
2146
2172
  // ─── Tool: gnosys_preference_set ─────────────────────────────────────────
2147
- server.tool("gnosys_preference_set", "Set a user preference. Preferences are stored in the central DB as user-scoped memories. They persist across all projects and are injected into agent rules files on `gnosys sync`. Use this to record workflow conventions, coding standards, tool preferences, etc.", {
2173
+ regTool("gnosys_preference_set", "Set a user preference. Preferences are stored in the central DB as user-scoped memories. They persist across all projects and are injected into agent rules files on `gnosys sync`. Use this to record workflow conventions, coding standards, tool preferences, etc.", {
2148
2174
  key: z.string().describe("Preference key, kebab-case. Examples: 'commit-convention', 'code-style', 'llm-provider', 'testing-approach', 'naming-convention'"),
2149
2175
  value: z.string().describe("The preference value. Can be a sentence or paragraph describing the convention."),
2150
2176
  title: z.string().optional().describe("Human-readable title. Auto-generated from key if omitted."),
@@ -2158,6 +2184,18 @@ server.tool("gnosys_preference_set", "Set a user preference. Preferences are sto
2158
2184
  };
2159
2185
  }
2160
2186
  try {
2187
+ if (!KNOWN_PREFERENCE_KEYS.includes(key)) {
2188
+ const suggestion = suggestPreferenceKey(key);
2189
+ if (suggestion) {
2190
+ return {
2191
+ isError: true,
2192
+ content: [{
2193
+ type: "text",
2194
+ text: `Unknown preference key \`${key}\` — did you mean \`${suggestion}\`?`,
2195
+ }],
2196
+ };
2197
+ }
2198
+ }
2161
2199
  const pref = setPreference(centralDb, key, value, { title, tags });
2162
2200
  return {
2163
2201
  content: [{
@@ -2178,7 +2216,7 @@ server.tool("gnosys_preference_set", "Set a user preference. Preferences are sto
2178
2216
  }
2179
2217
  });
2180
2218
  // ─── Tool: gnosys_preference_get ─────────────────────────────────────────
2181
- server.tool("gnosys_preference_get", "Get a user preference by key, or list all preferences.", {
2219
+ regTool("gnosys_preference_get", "Get a user preference by key, or list all preferences.", {
2182
2220
  key: z.string().optional().describe("Preference key to retrieve. Omit to list all preferences."),
2183
2221
  projectRoot: projectRootParam,
2184
2222
  }, async ({ key }) => {
@@ -2220,7 +2258,7 @@ server.tool("gnosys_preference_get", "Get a user preference by key, or list all
2220
2258
  };
2221
2259
  });
2222
2260
  // ─── Tool: gnosys_preference_delete ──────────────────────────────────────
2223
- server.tool("gnosys_preference_delete", "Delete a user preference by key.", {
2261
+ regTool("gnosys_preference_delete", "Delete a user preference by key.", {
2224
2262
  key: z.string().describe("Preference key to delete."),
2225
2263
  projectRoot: projectRootParam,
2226
2264
  }, async ({ key }) => {
@@ -2257,7 +2295,7 @@ server.tool("gnosys_preference_delete", "Delete a user preference by key.", {
2257
2295
  //
2258
2296
  // Routine in-session context flows through the SessionStart hook
2259
2297
  // (`gnosys recall`), not through this tool.
2260
- server.tool("gnosys_sync", "Get the current user preferences + project conventions formatted as a GNOSYS:START/GNOSYS:END block. By default returns the block as text only (no disk write). Pass commit_to_disk=true to write it into the detected agent rules file (CLAUDE.md, .cursor/rules/gnosys.mdc) — only do this if the user has explicitly asked to refresh the rules file. Routine session context is already injected via the SessionStart hook (`gnosys recall`); do NOT call this tool after every preference change.", {
2298
+ regTool("gnosys_sync", "Get the current user preferences + project conventions formatted as a GNOSYS:START/GNOSYS:END block. By default returns the block as text only (no disk write). Pass commit_to_disk=true to write it into the detected agent rules file (CLAUDE.md, .cursor/rules/gnosys.mdc) — only do this if the user has explicitly asked to refresh the rules file. Routine session context is already injected via the SessionStart hook (`gnosys recall`); do NOT call this tool after every preference change.", {
2261
2299
  projectRoot: projectRootParam,
2262
2300
  commit_to_disk: z
2263
2301
  .boolean()
@@ -2339,7 +2377,7 @@ server.tool("gnosys_sync", "Get the current user preferences + project conventio
2339
2377
  };
2340
2378
  });
2341
2379
  // ─── Tool: gnosys_federated_search ───────────────────────────────────────
2342
- server.tool("gnosys_federated_search", "Search across all scopes (project → user → global) with tier boosting. Results from the current project rank highest. Returns score breakdown showing which boosts were applied.", {
2380
+ regTool("gnosys_federated_search", "Search across all scopes (project → user → global) with tier boosting. Results from the current project rank highest. Returns score breakdown showing which boosts were applied.", {
2343
2381
  query: z.string().describe("Search query"),
2344
2382
  limit: z.number().optional().describe("Max results (default: 20)"),
2345
2383
  projectRoot: z.string().optional().describe("Project root directory for context detection"),
@@ -2369,7 +2407,7 @@ server.tool("gnosys_federated_search", "Search across all scopes (project → us
2369
2407
  };
2370
2408
  });
2371
2409
  // ─── Tool: gnosys_detect_ambiguity ──────────────────────────────────────
2372
- server.tool("gnosys_detect_ambiguity", "Check if a query matches memories in multiple projects. Use before write operations to confirm the target project when ambiguity exists.", {
2410
+ regTool("gnosys_detect_ambiguity", "Check if a query matches memories in multiple projects. Use before write operations to confirm the target project when ambiguity exists.", {
2373
2411
  query: z.string().describe("Query to check for cross-project ambiguity"),
2374
2412
  }, async ({ query }) => {
2375
2413
  if (!centralDb?.isAvailable()) {
@@ -2388,7 +2426,7 @@ server.tool("gnosys_detect_ambiguity", "Check if a query matches memories in mul
2388
2426
  };
2389
2427
  });
2390
2428
  // ─── Tool: gnosys_briefing ──────────────────────────────────────────────
2391
- server.tool("gnosys_briefing", "Generate a project briefing — a summary of memory state, categories, recent activity, and top tags. Use for dream mode pre-computation or quick project status.", {
2429
+ regTool("gnosys_briefing", "Generate a project briefing — a summary of memory state, categories, recent activity, and top tags. Use for dream mode pre-computation or quick project status.", {
2392
2430
  projectId: z.string().optional().describe("Project ID (auto-detects from cwd if omitted)"),
2393
2431
  all: z.boolean().optional().describe("Generate briefings for ALL projects"),
2394
2432
  projectRoot: z.string().optional().describe("Project root for auto-detection"),
@@ -2439,7 +2477,7 @@ server.tool("gnosys_briefing", "Generate a project briefing — a summary of mem
2439
2477
  return { content: [{ type: "text", text }] };
2440
2478
  });
2441
2479
  // ─── Tool: gnosys_portfolio ─────────────────────────────────────────────
2442
- server.tool("gnosys_portfolio", "Portfolio dashboard — shows all registered projects with memory counts, categories, status snapshots, roadmap items, and recent activity. Use for cross-project status overview.", {
2480
+ regTool("gnosys_portfolio", "Portfolio dashboard — shows all registered projects with memory counts, categories, status snapshots, roadmap items, and recent activity. Use for cross-project status overview.", {
2443
2481
  format: z.enum(["compact", "full"]).optional().describe("Output format: compact (default) or full markdown"),
2444
2482
  }, async ({ format }) => {
2445
2483
  if (!centralDb?.isAvailable()) {
@@ -2455,7 +2493,7 @@ server.tool("gnosys_portfolio", "Portfolio dashboard — shows all registered pr
2455
2493
  return { content: [{ type: "text", text }] };
2456
2494
  });
2457
2495
  // ─── Remote sync tools (v5.3.0) ─────────────────────────────────────────
2458
- server.tool("gnosys_remote_status", "Check the status of remote sync (multi-machine). Returns pending pushes, pulls, conflicts, and reachability. Agents should surface this to the user when there are pending changes or conflicts.", {}, async () => {
2496
+ regTool("gnosys_remote_status", "Check the status of remote sync (multi-machine). Returns pending pushes, pulls, conflicts, and reachability. Agents should surface this to the user when there are pending changes or conflicts.", {}, async () => {
2459
2497
  // Sync operations need explicit local DB access (not auto-routed remote).
2460
2498
  const localDb = GnosysDB.openLocal();
2461
2499
  try {
@@ -2483,7 +2521,7 @@ server.tool("gnosys_remote_status", "Check the status of remote sync (multi-mach
2483
2521
  localDb.close();
2484
2522
  }
2485
2523
  });
2486
- server.tool("gnosys_remote_push", "Push local memory changes to the remote (NAS) database. Uses skip-and-flag for conflicts by default. Call this when the user has approved pushing local changes.", {
2524
+ regTool("gnosys_remote_push", "Push local memory changes to the remote (NAS) database. Uses skip-and-flag for conflicts by default. Call this when the user has approved pushing local changes.", {
2487
2525
  newerWins: z.boolean().optional().describe("Auto-resolve conflicts by taking the newer version"),
2488
2526
  }, async ({ newerWins }) => {
2489
2527
  const localDb = GnosysDB.openLocal();
@@ -2507,7 +2545,7 @@ server.tool("gnosys_remote_push", "Push local memory changes to the remote (NAS)
2507
2545
  localDb.close();
2508
2546
  }
2509
2547
  });
2510
- server.tool("gnosys_remote_pull", "Pull remote memory changes to the local database. Uses skip-and-flag for conflicts by default. Call this when the user wants the latest from the remote.", {
2548
+ regTool("gnosys_remote_pull", "Pull remote memory changes to the local database. Uses skip-and-flag for conflicts by default. Call this when the user wants the latest from the remote.", {
2511
2549
  newerWins: z.boolean().optional().describe("Auto-resolve conflicts by taking the newer version"),
2512
2550
  }, async ({ newerWins }) => {
2513
2551
  const localDb = GnosysDB.openLocal();
@@ -2531,7 +2569,7 @@ server.tool("gnosys_remote_pull", "Pull remote memory changes to the local datab
2531
2569
  localDb.close();
2532
2570
  }
2533
2571
  });
2534
- server.tool("gnosys_remote_resolve", "Resolve a sync conflict by choosing which version to keep. Use after gnosys_remote_status reveals conflicts. The agent should present the local and remote versions to the user and call this with their choice.", {
2572
+ regTool("gnosys_remote_resolve", "Resolve a sync conflict by choosing which version to keep. Use after gnosys_remote_status reveals conflicts. The agent should present the local and remote versions to the user and call this with their choice.", {
2535
2573
  memoryId: z.string().describe("Memory ID with the conflict"),
2536
2574
  choice: z.enum(["local", "remote"]).describe("Which version to keep"),
2537
2575
  }, async ({ memoryId, choice }) => {
@@ -2558,7 +2596,7 @@ server.tool("gnosys_remote_resolve", "Resolve a sync conflict by choosing which
2558
2596
  }
2559
2597
  });
2560
2598
  // ─── Tool: gnosys_update_status ─────────────────────────────────────────
2561
- server.tool("gnosys_update_status", "Get the prompt/template for writing a dashboard-compatible status memory for this project. Returns instructions for creating a landscape memory with the correct heading format so the portfolio dashboard can parse it. Run this, then follow the instructions to analyze and write the status.", {
2599
+ regTool("gnosys_update_status", "Get the prompt/template for writing a dashboard-compatible status memory for this project. Returns instructions for creating a landscape memory with the correct heading format so the portfolio dashboard can parse it. Run this, then follow the instructions to analyze and write the status.", {
2562
2600
  projectRoot: z.string().optional().describe("Project root for auto-detection"),
2563
2601
  }, async ({ projectRoot }) => {
2564
2602
  if (!centralDb?.isAvailable()) {
@@ -2576,7 +2614,7 @@ server.tool("gnosys_update_status", "Get the prompt/template for writing a dashb
2576
2614
  return { content: [{ type: "text", text: prompt }] };
2577
2615
  });
2578
2616
  // ─── Tool: gnosys_working_set ───────────────────────────────────────────
2579
- server.tool("gnosys_working_set", "Get the implicit working set — recently modified memories for the current project. These represent the active context and get boosted in federated search.", {
2617
+ regTool("gnosys_working_set", "Get the implicit working set — recently modified memories for the current project. These represent the active context and get boosted in federated search.", {
2580
2618
  projectRoot: z.string().optional().describe("Project root for auto-detection"),
2581
2619
  windowHours: z.number().optional().describe("Lookback window in hours (default: 24)"),
2582
2620
  }, async ({ projectRoot, windowHours }) => {
@@ -2594,7 +2632,7 @@ server.tool("gnosys_working_set", "Get the implicit working set — recently mod
2594
2632
  return { content: [{ type: "text", text: formatted }] };
2595
2633
  });
2596
2634
  // ─── Tool: gnosys_ingest_file ────────────────────────────────────────────
2597
- server.tool("gnosys_ingest_file", "Ingest a file (PDF, DOCX, TXT, MD) into Gnosys memory. Extracts text, splits into chunks, and creates atomic memories. Supports LLM-powered structuring or fast structured mode.", {
2635
+ regTool("gnosys_ingest_file", "Ingest a file (PDF, DOCX, TXT, MD) into Gnosys memory. Extracts text, splits into chunks, and creates atomic memories. Supports LLM-powered structuring or fast structured mode.", {
2598
2636
  filePath: z.string().describe("Absolute path to the file to ingest"),
2599
2637
  mode: z.enum(["llm", "structured"]).default("llm").optional()
2600
2638
  .describe("Ingestion mode: 'llm' uses AI to structure each chunk, 'structured' uses keyword extraction (faster, no LLM needed)"),
@@ -2644,6 +2682,13 @@ server.tool("gnosys_ingest_file", "Ingest a file (PDF, DOCX, TXT, MD) into Gnosy
2644
2682
  lines.push(`- Chunk ${e.chunk}: ${e.error}`);
2645
2683
  }
2646
2684
  }
2685
+ if (!dryRun && ctx.centralDb?.isAvailable()) {
2686
+ auditToDb(ctx.centralDb, "ingest", undefined, {
2687
+ source_file: result.attachment.originalName,
2688
+ fileType: result.fileType,
2689
+ count: result.memories.length,
2690
+ }, result.duration);
2691
+ }
2647
2692
  if (dryRun) {
2648
2693
  lines.unshift("(dry run — no files were written)\n");
2649
2694
  }
@@ -2661,7 +2706,7 @@ server.tool("gnosys_ingest_file", "Ingest a file (PDF, DOCX, TXT, MD) into Gnosy
2661
2706
  // ─── MCP Prompts (slash commands) ────────────────────────────────────────
2662
2707
  // These appear as /gnosys-recall, /gnosys-discover, /gnosys-memorize in
2663
2708
  // Cursor, Claude Code, and Codex.
2664
- server.prompt("gnosys-recall", "Inject top Gnosys memories for the current project into context. Use this at the start of any task to load relevant knowledge.", async () => {
2709
+ regPrompt("gnosys-recall", "Inject top Gnosys memories for the current project into context. Use this at the start of any task to load relevant knowledge.", async () => {
2665
2710
  if (!centralDb?.isAvailable()) {
2666
2711
  return {
2667
2712
  messages: [
@@ -2706,7 +2751,7 @@ server.prompt("gnosys-recall", "Inject top Gnosys memories for the current proje
2706
2751
  ],
2707
2752
  };
2708
2753
  });
2709
- server.prompt("gnosys-discover", "Search Gnosys memories on a specific topic and inject results into context.", { topic: z.string().describe("Topic or keywords to search for") }, async ({ topic }) => {
2754
+ regPrompt("gnosys-discover", "Search Gnosys memories on a specific topic and inject results into context.", { topic: z.string().describe("Topic or keywords to search for") }, async ({ topic }) => {
2710
2755
  if (!centralDb?.isAvailable() || !centralDb?.isMigrated()) {
2711
2756
  return {
2712
2757
  messages: [
@@ -2745,7 +2790,7 @@ server.prompt("gnosys-discover", "Search Gnosys memories on a specific topic and
2745
2790
  ],
2746
2791
  };
2747
2792
  });
2748
- server.prompt("gnosys-memorize", "Analyze the current conversation and save new decisions, findings, and context as Gnosys memories. Checks for duplicates automatically.", async () => {
2793
+ regPrompt("gnosys-memorize", "Analyze the current conversation and save new decisions, findings, and context as Gnosys memories. Checks for duplicates automatically.", async () => {
2749
2794
  // Check for last memorized timestamp from preferences
2750
2795
  let lastMemorizedInfo = "This is the first time /gnosys-memorize has been run — analyze all conversation content.";
2751
2796
  if (centralDb?.isAvailable()) {
@@ -2971,6 +3016,38 @@ async function main() {
2971
3016
  // heavy module initialization in the background. Handlers that use the
2972
3017
  // module-level `ingestion` / `hybridSearch` / `askEngine` vars guard
2973
3018
  // against null and either await readiness or surface a clear error.
3019
+ // v5.12: HTTP transport (central-server topology) — opt-in via env, set by
3020
+ // `gnosys serve --transport http`. Each session gets its own McpServer
3021
+ // (registrations replayed); all share the module-global brain/search.
3022
+ if (process.env.GNOSYS_TRANSPORT === "http") {
3023
+ const { startMcpHttpServer } = await import("./lib/mcpHttp.js");
3024
+ const host = process.env.GNOSYS_HTTP_HOST || "127.0.0.1";
3025
+ const port = parseInt(process.env.GNOSYS_HTTP_PORT || "7777", 10);
3026
+ const authToken = process.env.GNOSYS_SERVE_TOKEN || undefined;
3027
+ try {
3028
+ await startMcpHttpServer({
3029
+ host,
3030
+ port,
3031
+ authToken,
3032
+ log: (m) => console.error(`Gnosys MCP[http]: ${m}`),
3033
+ makeServer: () => {
3034
+ const s = new McpServer({ name: "gnosys", version: "2.0.0" });
3035
+ registerCapabilities(s);
3036
+ return s;
3037
+ },
3038
+ });
3039
+ }
3040
+ catch (err) {
3041
+ console.error(`Gnosys MCP: ${err instanceof Error ? err.message : String(err)}`);
3042
+ process.exit(1);
3043
+ }
3044
+ console.error(`Gnosys MCP: HTTP transport ready on http://${host}:${port}/mcp${authToken ? " (bearer auth required)" : ""}`);
3045
+ void initHeavyDeps().catch((err) => {
3046
+ console.error(`Gnosys MCP: heavy-init failed — ${err instanceof Error ? err.message : err}`);
3047
+ });
3048
+ return;
3049
+ }
3050
+ registerCapabilities(server);
2974
3051
  const transport = new StdioServerTransport();
2975
3052
  await server.connect(transport);
2976
3053
  console.error("Gnosys MCP: handshake ready (heavy modules still loading)");
@@ -3014,8 +3091,10 @@ async function main() {
3014
3091
  // Notification handler setup failed — non-critical
3015
3092
  }
3016
3093
  }
3017
- main().catch((err) => {
3018
- console.error("Fatal error:", err);
3019
- process.exit(1);
3020
- });
3021
- //# sourceMappingURL=index.js.map
3094
+ const invokedAsScript = !!process.argv[1] && fileURLToPath(import.meta.url) === path.resolve(process.argv[1]);
3095
+ if (invokedAsScript) {
3096
+ main().catch((err) => {
3097
+ console.error("Fatal error:", err);
3098
+ process.exit(1);
3099
+ });
3100
+ }