oh-my-second-brain 0.2.0 → 0.6.1

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 (494) hide show
  1. package/.claude-plugin/marketplace.json +41 -0
  2. package/{adapters/claude-code/.claude-plugin → .claude-plugin}/plugin.json +7 -8
  3. package/{adapters/codex/.codex-plugin → .codex-plugin}/plugin.json +4 -4
  4. package/ACKNOWLEDGMENTS.md +162 -0
  5. package/CHANGELOG-assets.md +23 -0
  6. package/CHANGELOG-cli.md +24 -0
  7. package/CHANGELOG-kernel.md +48 -0
  8. package/CHANGELOG-mcp.md +36 -0
  9. package/CHANGELOG-vendors.md +26 -0
  10. package/CHANGELOG.md +51 -7
  11. package/README.ko.md +2 -2
  12. package/README.md +16 -16
  13. package/{adapters/claude-code → assets/claude}/CLAUDE.md +4 -4
  14. package/{adapters → assets}/codex/AGENTS.md +3 -3
  15. package/{adapters → assets}/codex/rules/oms.md +5 -5
  16. package/{adapters → assets}/hermes/README.md +1 -1
  17. package/{adapters → assets}/hermes/SOUL.md +3 -3
  18. package/{adapters/hermes/manifest.json → assets/hermes-manifest.json} +3 -4
  19. package/assets/skills/distill/SKILL.md +20 -0
  20. package/assets/skills/doctor/SKILL.md +23 -0
  21. package/assets/skills/link/SKILL.md +24 -0
  22. package/assets/skills/search/SKILL.md +25 -0
  23. package/assets/skills/status/SKILL.md +22 -0
  24. package/assets/skills/write/SKILL.md +30 -0
  25. package/core/ontology/schemas/README.md +20 -14
  26. package/dist/assets/shared-skills.d.ts +4 -0
  27. package/dist/assets/shared-skills.js +8 -0
  28. package/dist/assets/shared-skills.js.map +1 -0
  29. package/dist/cli/args.d.ts +3 -1
  30. package/dist/cli/args.js +14 -1
  31. package/dist/cli/args.js.map +1 -1
  32. package/dist/cli/audit.js +3 -3
  33. package/dist/cli/audit.js.map +1 -1
  34. package/dist/cli/claude-install-plan.js +2 -2
  35. package/dist/cli/claude-install-plan.js.map +1 -1
  36. package/dist/cli/doctor-lint.js +8 -8
  37. package/dist/cli/doctor-lint.js.map +1 -1
  38. package/dist/cli/global-writeback.js +1 -1
  39. package/dist/cli/global-writeback.js.map +1 -1
  40. package/dist/cli/host-commands.d.ts +1 -1
  41. package/dist/cli/host-commands.js +24 -5
  42. package/dist/cli/host-commands.js.map +1 -1
  43. package/dist/cli/link-command.d.ts +1 -1
  44. package/dist/cli/link-command.js +2 -2
  45. package/dist/cli/link-command.js.map +1 -1
  46. package/dist/cli/linkify.js +16 -71
  47. package/dist/cli/linkify.js.map +1 -1
  48. package/dist/cli/oms.d.ts +1 -1
  49. package/dist/cli/oms.js +45 -11
  50. package/dist/cli/oms.js.map +1 -1
  51. package/dist/cli/semantic-args.d.ts +1 -1
  52. package/dist/cli/semantic-args.js +102 -3
  53. package/dist/cli/semantic-args.js.map +1 -1
  54. package/dist/cli/semantic-http.js +3 -3
  55. package/dist/cli/semantic-http.js.map +1 -1
  56. package/dist/cli/semantic-usage.js +1 -1
  57. package/dist/cli/semantic-usage.js.map +1 -1
  58. package/dist/cli/semantic.js +31 -12
  59. package/dist/cli/semantic.js.map +1 -1
  60. package/dist/cli/setup-command.d.ts +17 -0
  61. package/dist/cli/setup-command.js +89 -145
  62. package/dist/cli/setup-command.js.map +1 -1
  63. package/dist/cli/update-notice.d.ts +1 -1
  64. package/dist/cli/update-notice.js +2 -2
  65. package/dist/cli/update-notice.js.map +1 -1
  66. package/dist/cli/usage.d.ts +1 -1
  67. package/dist/cli/usage.js +2 -2
  68. package/dist/cli/usage.js.map +1 -1
  69. package/dist/{capture → kernel/capture}/safe.d.ts +11 -1
  70. package/dist/{capture → kernel/capture}/safe.js +3 -3
  71. package/dist/kernel/capture/safe.js.map +1 -0
  72. package/dist/kernel/contracts/index.d.ts +144 -0
  73. package/dist/kernel/contracts/index.js +401 -0
  74. package/dist/kernel/contracts/index.js.map +1 -0
  75. package/dist/kernel/conventions/frontmatter.js.map +1 -0
  76. package/dist/kernel/conventions/lint.js.map +1 -0
  77. package/dist/kernel/conventions/report.js.map +1 -0
  78. package/dist/{conventions → kernel/conventions}/validate.d.ts +1 -1
  79. package/dist/kernel/conventions/validate.js.map +1 -0
  80. package/dist/kernel/conventions/vault-walk.js.map +1 -0
  81. package/dist/{conventions → kernel/conventions}/write-contract.d.ts +1 -1
  82. package/dist/kernel/conventions/write-contract.js.map +1 -0
  83. package/dist/kernel/conventions/write-protocol.js.map +1 -0
  84. package/dist/kernel/doctor/service.d.ts +71 -0
  85. package/dist/kernel/doctor/service.js +70 -0
  86. package/dist/kernel/doctor/service.js.map +1 -0
  87. package/dist/{engine → kernel/engine}/assemble.d.ts +46 -1
  88. package/dist/kernel/engine/assemble.js +483 -0
  89. package/dist/kernel/engine/assemble.js.map +1 -0
  90. package/dist/kernel/engine/axes/store.d.ts +90 -0
  91. package/dist/kernel/engine/axes/store.js +400 -0
  92. package/dist/kernel/engine/axes/store.js.map +1 -0
  93. package/dist/{engine → kernel/engine}/conventions/vault-lint.d.ts +1 -1
  94. package/dist/{engine → kernel/engine}/conventions/vault-lint.js +1 -1
  95. package/dist/kernel/engine/conventions/vault-lint.js.map +1 -0
  96. package/dist/kernel/engine/embed/chunker.js.map +1 -0
  97. package/dist/kernel/engine/embed/deferred.js.map +1 -0
  98. package/dist/{engine → kernel/engine}/embed/hash-stub.test-helper.d.ts +1 -1
  99. package/dist/{engine → kernel/engine}/embed/hash-stub.test-helper.js +1 -1
  100. package/dist/kernel/engine/embed/hash-stub.test-helper.js.map +1 -0
  101. package/dist/kernel/engine/embed/identity.d.ts +21 -0
  102. package/dist/kernel/engine/embed/identity.js +47 -0
  103. package/dist/kernel/engine/embed/identity.js.map +1 -0
  104. package/dist/kernel/engine/embed/model.d.ts +134 -0
  105. package/dist/kernel/engine/embed/model.js +302 -0
  106. package/dist/kernel/engine/embed/model.js.map +1 -0
  107. package/dist/{engine → kernel/engine}/embed/provider.d.ts +36 -5
  108. package/dist/kernel/engine/embed/provider.js +403 -0
  109. package/dist/kernel/engine/embed/provider.js.map +1 -0
  110. package/dist/{engine → kernel/engine}/embed/store.d.ts +13 -1
  111. package/dist/kernel/engine/embed/store.js +730 -0
  112. package/dist/kernel/engine/embed/store.js.map +1 -0
  113. package/dist/kernel/engine/embed/sync.d.ts +100 -0
  114. package/dist/kernel/engine/embed/sync.js +753 -0
  115. package/dist/kernel/engine/embed/sync.js.map +1 -0
  116. package/dist/{engine → kernel/engine}/graph/builder.d.ts +9 -5
  117. package/dist/{engine → kernel/engine}/graph/builder.js +213 -79
  118. package/dist/kernel/engine/graph/builder.js.map +1 -0
  119. package/dist/kernel/engine/graph/explore.js.map +1 -0
  120. package/dist/{engine → kernel/engine}/graph/node.d.ts +49 -1
  121. package/dist/kernel/engine/graph/node.js +315 -0
  122. package/dist/kernel/engine/graph/node.js.map +1 -0
  123. package/dist/kernel/engine/graph/resolver.js.map +1 -0
  124. package/dist/kernel/engine/graph/traverse.js.map +1 -0
  125. package/dist/kernel/engine/linkify/apply.js.map +1 -0
  126. package/dist/kernel/engine/linkify/josa.js.map +1 -0
  127. package/dist/kernel/engine/linkify/mask.js.map +1 -0
  128. package/dist/kernel/engine/linkify/suggest.js.map +1 -0
  129. package/dist/kernel/engine/linkify/types.js.map +1 -0
  130. package/dist/{engine → kernel/engine}/mcp/facade.d.ts +27 -1
  131. package/dist/kernel/engine/mcp/facade.js +1144 -0
  132. package/dist/kernel/engine/mcp/facade.js.map +1 -0
  133. package/dist/kernel/engine/mcp/op-mappers.js.map +1 -0
  134. package/dist/{engine → kernel/engine}/mcp/query-mapper.d.ts +27 -2
  135. package/dist/kernel/engine/mcp/query-mapper.js +247 -0
  136. package/dist/kernel/engine/mcp/query-mapper.js.map +1 -0
  137. package/dist/{engine → kernel/engine}/mcp/types.d.ts +69 -2
  138. package/dist/kernel/engine/mcp/types.js.map +1 -0
  139. package/dist/{engine → kernel/engine}/retrieval/dispatcher.d.ts +23 -1
  140. package/dist/{engine → kernel/engine}/retrieval/dispatcher.js +92 -14
  141. package/dist/kernel/engine/retrieval/dispatcher.js.map +1 -0
  142. package/dist/{engine → kernel/engine}/retrieval/index.d.ts +4 -1
  143. package/dist/{engine → kernel/engine}/retrieval/index.js +6 -3
  144. package/dist/kernel/engine/retrieval/index.js.map +1 -0
  145. package/dist/kernel/engine/retrieval/reranker.d.ts +124 -0
  146. package/dist/kernel/engine/retrieval/reranker.js +204 -0
  147. package/dist/kernel/engine/retrieval/reranker.js.map +1 -0
  148. package/dist/{engine → kernel/engine}/retrieval/rrf.d.ts +10 -2
  149. package/dist/kernel/engine/retrieval/rrf.js +77 -0
  150. package/dist/kernel/engine/retrieval/rrf.js.map +1 -0
  151. package/dist/{engine → kernel/engine}/tracer.d.ts +13 -0
  152. package/dist/{engine → kernel/engine}/tracer.js +63 -6
  153. package/dist/kernel/engine/tracer.js.map +1 -0
  154. package/dist/{engine → kernel/engine}/types.d.ts +4 -4
  155. package/dist/kernel/engine/types.js.map +1 -0
  156. package/dist/{graph → kernel/graph}/cache.d.ts +1 -1
  157. package/dist/{graph → kernel/graph}/cache.js +165 -21
  158. package/dist/kernel/graph/cache.js.map +1 -0
  159. package/dist/kernel/graph/explore.js.map +1 -0
  160. package/dist/{harness → kernel/harness}/surface-registry.d.ts +0 -1
  161. package/dist/kernel/harness/surface-registry.js +170 -0
  162. package/dist/kernel/harness/surface-registry.js.map +1 -0
  163. package/dist/{harness → kernel/harness}/validation.js +67 -4
  164. package/dist/kernel/harness/validation.js.map +1 -0
  165. package/dist/kernel/index.d.ts +13 -0
  166. package/dist/kernel/index.js +14 -0
  167. package/dist/kernel/index.js.map +1 -0
  168. package/dist/{install → kernel/install}/adapter-source.d.ts +10 -1
  169. package/dist/kernel/install/adapter-source.js +76 -0
  170. package/dist/kernel/install/adapter-source.js.map +1 -0
  171. package/dist/kernel/install/common.js.map +1 -0
  172. package/dist/{install → kernel/install}/hosts.d.ts +3 -3
  173. package/dist/{install → kernel/install}/hosts.js +3 -22
  174. package/dist/kernel/install/hosts.js.map +1 -0
  175. package/dist/{core/ontology → kernel/install}/types.js.map +1 -1
  176. package/dist/{link → kernel/link}/convention-note.js +2 -2
  177. package/dist/kernel/link/convention-note.js.map +1 -0
  178. package/dist/kernel/link/global-config.js.map +1 -0
  179. package/dist/kernel/link/link.js.map +1 -0
  180. package/dist/kernel/link/workflow.d.ts +55 -0
  181. package/dist/kernel/link/workflow.js +107 -0
  182. package/dist/kernel/link/workflow.js.map +1 -0
  183. package/dist/kernel/measurement/no-default-contract.d.ts +17 -0
  184. package/dist/kernel/measurement/no-default-contract.js +84 -0
  185. package/dist/kernel/measurement/no-default-contract.js.map +1 -0
  186. package/dist/{ontology → kernel/ontology}/active.d.ts +1 -1
  187. package/dist/{ontology → kernel/ontology}/active.js +2 -2
  188. package/dist/kernel/ontology/active.js.map +1 -0
  189. package/dist/kernel/ontology/loader.js.map +1 -0
  190. package/dist/kernel/ontology/resolver.js.map +1 -0
  191. package/dist/{install → kernel/ontology}/types.js.map +1 -1
  192. package/dist/kernel/plugin/index.d.ts +1 -0
  193. package/dist/kernel/plugin/index.js +2 -0
  194. package/dist/kernel/plugin/index.js.map +1 -0
  195. package/dist/{core → kernel}/runtime/assets.d.ts +2 -4
  196. package/dist/{core → kernel}/runtime/assets.js +1 -3
  197. package/dist/kernel/runtime/assets.js.map +1 -0
  198. package/dist/kernel/search/morning-test-fixtures.js.map +1 -0
  199. package/dist/{retrieve → kernel/search}/morning.d.ts +0 -1
  200. package/dist/kernel/search/morning.js.map +1 -0
  201. package/dist/kernel/search/semantic-contract.js.map +1 -0
  202. package/dist/kernel/searchbackend/engine-search-backend.d.ts +28 -0
  203. package/dist/kernel/searchbackend/engine-search-backend.js +153 -0
  204. package/dist/kernel/searchbackend/engine-search-backend.js.map +1 -0
  205. package/dist/kernel/searchbackend/index.d.ts +2 -0
  206. package/dist/kernel/searchbackend/index.js +2 -0
  207. package/dist/kernel/searchbackend/index.js.map +1 -0
  208. package/dist/kernel/searchbackend/search-backend.d.ts +57 -0
  209. package/dist/kernel/searchbackend/search-backend.js +110 -0
  210. package/dist/kernel/searchbackend/search-backend.js.map +1 -0
  211. package/dist/{mcp → kernel/semantic}/semantic-engine.d.ts +3 -2
  212. package/dist/kernel/semantic/semantic-engine.js +78 -0
  213. package/dist/kernel/semantic/semantic-engine.js.map +1 -0
  214. package/dist/{mcp → kernel/semantic}/semantic-retrieve-args.d.ts +20 -3
  215. package/dist/{mcp → kernel/semantic}/semantic-retrieve-args.js +81 -15
  216. package/dist/kernel/semantic/semantic-retrieve-args.js.map +1 -0
  217. package/dist/{mcp → kernel/semantic}/semantic-retrieve-schema.d.ts +0 -24
  218. package/dist/{mcp → kernel/semantic}/semantic-retrieve-schema.js +0 -8
  219. package/dist/kernel/semantic/semantic-retrieve-schema.js.map +1 -0
  220. package/dist/{mcp → kernel/semantic}/semantic-retrieve.js +5 -9
  221. package/dist/kernel/semantic/semantic-retrieve.js.map +1 -0
  222. package/dist/kernel/semantic/semantic-schemas.js.map +1 -0
  223. package/dist/{mcp → kernel/semantic}/semantic-tool-schemas.js +0 -28
  224. package/dist/kernel/semantic/semantic-tool-schemas.js.map +1 -0
  225. package/dist/{setup → kernel/setup}/axis.d.ts +1 -1
  226. package/dist/{setup → kernel/setup}/axis.js +1 -1
  227. package/dist/kernel/setup/axis.js.map +1 -0
  228. package/dist/{cli/setup-documents.d.ts → kernel/setup/documents.d.ts} +1 -1
  229. package/dist/{cli/setup-documents.js → kernel/setup/documents.js} +15 -49
  230. package/dist/kernel/setup/documents.js.map +1 -0
  231. package/dist/kernel/setup/service.d.ts +44 -0
  232. package/dist/kernel/setup/service.js +97 -0
  233. package/dist/kernel/setup/service.js.map +1 -0
  234. package/dist/{update → kernel/update}/update.d.ts +7 -0
  235. package/dist/{update → kernel/update}/update.js +106 -8
  236. package/dist/kernel/update/update.js.map +1 -0
  237. package/dist/mcp/engine-morning-backend.d.ts +4 -4
  238. package/dist/mcp/engine-morning-backend.js +4 -18
  239. package/dist/mcp/engine-morning-backend.js.map +1 -1
  240. package/dist/mcp/link-tools.d.ts +9 -56
  241. package/dist/mcp/link-tools.js +5 -129
  242. package/dist/mcp/link-tools.js.map +1 -1
  243. package/dist/mcp/server.d.ts +9 -2
  244. package/dist/mcp/server.js +373 -324
  245. package/dist/mcp/server.js.map +1 -1
  246. package/dist/mcp/update-notice.d.ts +1 -1
  247. package/dist/mcp/update-notice.js +1 -1
  248. package/dist/mcp/update-notice.js.map +1 -1
  249. package/dist/{install → vendors/claude}/claude-hooks.d.ts +1 -1
  250. package/dist/{install → vendors/claude}/claude-hooks.js +1 -1
  251. package/dist/vendors/claude/claude-hooks.js.map +1 -0
  252. package/dist/{install → vendors/claude}/claude-marketplace.d.ts +3 -3
  253. package/dist/{install → vendors/claude}/claude-marketplace.js +6 -9
  254. package/dist/vendors/claude/claude-marketplace.js.map +1 -0
  255. package/dist/{install → vendors/claude}/claude.d.ts +2 -2
  256. package/dist/{install → vendors/claude}/claude.js +2 -2
  257. package/dist/vendors/claude/claude.js.map +1 -0
  258. package/dist/{hook → vendors/claude/hook}/post-tool-use.js +5 -5
  259. package/dist/vendors/claude/hook/post-tool-use.js.map +1 -0
  260. package/dist/vendors/claude/hook/pre-tool-use.js.map +1 -0
  261. package/dist/vendors/claude/hook/stdin.js.map +1 -0
  262. package/dist/vendors/codex/codex.d.ts +10 -0
  263. package/dist/{install → vendors/codex}/codex.js +19 -37
  264. package/dist/vendors/codex/codex.js.map +1 -0
  265. package/dist/{install → vendors/hermes}/hermes.d.ts +2 -2
  266. package/dist/{install → vendors/hermes}/hermes.js +15 -7
  267. package/dist/vendors/hermes/hermes.js.map +1 -0
  268. package/docs/adapters.md +15 -0
  269. package/docs/install.md +10 -11
  270. package/package.json +23 -10
  271. package/adapters/README.md +0 -99
  272. package/adapters/claude-code/skills/compile/SKILL.md +0 -27
  273. package/adapters/claude-code/skills/define/SKILL.md +0 -51
  274. package/adapters/claude-code/skills/distill/SKILL.md +0 -28
  275. package/adapters/claude-code/skills/doctor/SKILL.md +0 -58
  276. package/adapters/claude-code/skills/link/SKILL.md +0 -87
  277. package/adapters/claude-code/skills/retrieve/SKILL.md +0 -99
  278. package/adapters/claude-code/skills/setup/SKILL.md +0 -78
  279. package/adapters/claude-code/skills/uninstall/SKILL.md +0 -34
  280. package/adapters/claude-code/skills/update/SKILL.md +0 -45
  281. package/adapters/claude-code/skills/vault-decision-record/SKILL.md +0 -52
  282. package/adapters/claude-code/skills/vault-lint/SKILL.md +0 -47
  283. package/adapters/claude-code/skills/vault-scaffold/SKILL.md +0 -47
  284. package/adapters/claude-code/skills/wiki/SKILL.md +0 -82
  285. package/adapters/claude-code/skills/write/SKILL.md +0 -16
  286. package/adapters/codex/skills/oms-compile/SKILL.md +0 -16
  287. package/adapters/codex/skills/oms-distill/SKILL.md +0 -14
  288. package/adapters/codex/skills/oms-doctor/SKILL.md +0 -14
  289. package/adapters/codex/skills/oms-install/SKILL.md +0 -19
  290. package/adapters/codex/skills/oms-link/SKILL.md +0 -35
  291. package/adapters/codex/skills/oms-retrieve/SKILL.md +0 -32
  292. package/adapters/codex/skills/oms-setup/SKILL.md +0 -34
  293. package/adapters/codex/skills/oms-uninstall/SKILL.md +0 -20
  294. package/adapters/codex/skills/oms-update/SKILL.md +0 -22
  295. package/adapters/codex/skills/oms-vault-lint/SKILL.md +0 -19
  296. package/adapters/codex/skills/oms-wiki/SKILL.md +0 -39
  297. package/adapters/codex/skills/oms-write/SKILL.md +0 -10
  298. package/adapters/hermes/skills/compile/SKILL.md +0 -13
  299. package/adapters/hermes/skills/distill/SKILL.md +0 -10
  300. package/adapters/hermes/skills/doctor/SKILL.md +0 -14
  301. package/adapters/hermes/skills/install/SKILL.md +0 -19
  302. package/adapters/hermes/skills/link/SKILL.md +0 -18
  303. package/adapters/hermes/skills/retrieve/SKILL.md +0 -32
  304. package/adapters/hermes/skills/setup/SKILL.md +0 -37
  305. package/adapters/hermes/skills/uninstall/SKILL.md +0 -20
  306. package/adapters/hermes/skills/update/SKILL.md +0 -22
  307. package/adapters/hermes/skills/wiki/SKILL.md +0 -14
  308. package/adapters/hermes/skills/write/SKILL.md +0 -10
  309. package/core/agents/librarian.md +0 -44
  310. package/core/agents/retriever.md +0 -62
  311. package/core/skills/compile/CHANGELOG.md +0 -24
  312. package/core/skills/compile/SKILL.md +0 -57
  313. package/core/skills/define/SKILL.md +0 -59
  314. package/core/skills/distill/CHANGELOG.md +0 -39
  315. package/core/skills/distill/SKILL.md +0 -62
  316. package/core/skills/doctor/SKILL.md +0 -49
  317. package/core/skills/link/SKILL.md +0 -130
  318. package/core/skills/retrieve/SKILL.md +0 -78
  319. package/core/skills/setup/CHANGELOG.md +0 -49
  320. package/core/skills/setup/SKILL.md +0 -246
  321. package/core/skills/uninstall/SKILL.md +0 -34
  322. package/core/skills/vault-decision-record/CHANGELOG.md +0 -24
  323. package/core/skills/vault-decision-record/SKILL.md +0 -93
  324. package/core/skills/vault-lint/CHANGELOG.md +0 -35
  325. package/core/skills/vault-lint/SKILL.md +0 -58
  326. package/core/skills/vault-scaffold/CHANGELOG.md +0 -21
  327. package/core/skills/vault-scaffold/SKILL.md +0 -72
  328. package/core/skills/wiki/CHANGELOG.md +0 -23
  329. package/core/skills/wiki/SKILL.md +0 -159
  330. package/core/skills/write/SKILL.md +0 -15
  331. package/dist/capture/safe.js.map +0 -1
  332. package/dist/cli/setup-documents.js.map +0 -1
  333. package/dist/conventions/frontmatter.js.map +0 -1
  334. package/dist/conventions/lint.js.map +0 -1
  335. package/dist/conventions/report.js.map +0 -1
  336. package/dist/conventions/validate.js.map +0 -1
  337. package/dist/conventions/vault-walk.js.map +0 -1
  338. package/dist/conventions/write-contract.js.map +0 -1
  339. package/dist/conventions/write-protocol.js.map +0 -1
  340. package/dist/core/ontology/loader.js.map +0 -1
  341. package/dist/core/ontology/resolver.js.map +0 -1
  342. package/dist/core/runtime/assets.js.map +0 -1
  343. package/dist/engine/assemble.js +0 -162
  344. package/dist/engine/assemble.js.map +0 -1
  345. package/dist/engine/conventions/vault-lint.js.map +0 -1
  346. package/dist/engine/embed/chunker.js.map +0 -1
  347. package/dist/engine/embed/deferred.js.map +0 -1
  348. package/dist/engine/embed/hash-stub.test-helper.js.map +0 -1
  349. package/dist/engine/embed/identity.d.ts +0 -11
  350. package/dist/engine/embed/identity.js +0 -15
  351. package/dist/engine/embed/identity.js.map +0 -1
  352. package/dist/engine/embed/provider.js +0 -266
  353. package/dist/engine/embed/provider.js.map +0 -1
  354. package/dist/engine/embed/store.js +0 -416
  355. package/dist/engine/embed/store.js.map +0 -1
  356. package/dist/engine/embed/sync.d.ts +0 -45
  357. package/dist/engine/embed/sync.js +0 -321
  358. package/dist/engine/embed/sync.js.map +0 -1
  359. package/dist/engine/graph/builder.js.map +0 -1
  360. package/dist/engine/graph/explore.js.map +0 -1
  361. package/dist/engine/graph/node.js +0 -110
  362. package/dist/engine/graph/node.js.map +0 -1
  363. package/dist/engine/graph/resolver.js.map +0 -1
  364. package/dist/engine/graph/traverse.js.map +0 -1
  365. package/dist/engine/linkify/apply.js.map +0 -1
  366. package/dist/engine/linkify/josa.js.map +0 -1
  367. package/dist/engine/linkify/mask.js.map +0 -1
  368. package/dist/engine/linkify/suggest.js.map +0 -1
  369. package/dist/engine/linkify/types.js.map +0 -1
  370. package/dist/engine/mcp/facade.js +0 -628
  371. package/dist/engine/mcp/facade.js.map +0 -1
  372. package/dist/engine/mcp/op-mappers.js.map +0 -1
  373. package/dist/engine/mcp/query-mapper.js +0 -102
  374. package/dist/engine/mcp/query-mapper.js.map +0 -1
  375. package/dist/engine/mcp/types.js.map +0 -1
  376. package/dist/engine/retrieval/dispatcher.js.map +0 -1
  377. package/dist/engine/retrieval/index.js.map +0 -1
  378. package/dist/engine/retrieval/reranker.d.ts +0 -46
  379. package/dist/engine/retrieval/reranker.js +0 -29
  380. package/dist/engine/retrieval/reranker.js.map +0 -1
  381. package/dist/engine/retrieval/rrf.js +0 -54
  382. package/dist/engine/retrieval/rrf.js.map +0 -1
  383. package/dist/engine/tracer.js.map +0 -1
  384. package/dist/engine/types.js.map +0 -1
  385. package/dist/graph/cache.js.map +0 -1
  386. package/dist/graph/explore.js.map +0 -1
  387. package/dist/harness/surface-registry.js +0 -397
  388. package/dist/harness/surface-registry.js.map +0 -1
  389. package/dist/harness/validation.js.map +0 -1
  390. package/dist/hook/post-tool-use.js.map +0 -1
  391. package/dist/hook/pre-tool-use.js.map +0 -1
  392. package/dist/hook/stdin.js.map +0 -1
  393. package/dist/index.d.ts +0 -12
  394. package/dist/index.js +0 -13
  395. package/dist/index.js.map +0 -1
  396. package/dist/install/adapter-source.js +0 -45
  397. package/dist/install/adapter-source.js.map +0 -1
  398. package/dist/install/claude-hooks.js.map +0 -1
  399. package/dist/install/claude-marketplace.js.map +0 -1
  400. package/dist/install/claude.js.map +0 -1
  401. package/dist/install/codex.d.ts +0 -24
  402. package/dist/install/codex.js.map +0 -1
  403. package/dist/install/common.js.map +0 -1
  404. package/dist/install/hermes.js.map +0 -1
  405. package/dist/install/hosts.js.map +0 -1
  406. package/dist/link/convention-note.js.map +0 -1
  407. package/dist/link/global-config.js.map +0 -1
  408. package/dist/link/link.js.map +0 -1
  409. package/dist/mcp/semantic-engine.js +0 -52
  410. package/dist/mcp/semantic-engine.js.map +0 -1
  411. package/dist/mcp/semantic-retrieve-args.js.map +0 -1
  412. package/dist/mcp/semantic-retrieve-schema.js.map +0 -1
  413. package/dist/mcp/semantic-retrieve.js.map +0 -1
  414. package/dist/mcp/semantic-schemas.js.map +0 -1
  415. package/dist/mcp/semantic-tool-schemas.js.map +0 -1
  416. package/dist/ontology/active.js.map +0 -1
  417. package/dist/retrieve/morning-test-fixtures.js.map +0 -1
  418. package/dist/retrieve/morning.js.map +0 -1
  419. package/dist/retrieve/semantic-contract.js.map +0 -1
  420. package/dist/setup/axis.js.map +0 -1
  421. package/dist/update/update.js.map +0 -1
  422. package/docs/release.md +0 -188
  423. /package/{adapters/codex/.mcp.json → .mcp.codex.json} +0 -0
  424. /package/{adapters/claude-code/.mcp.json → .mcp.json} +0 -0
  425. /package/{adapters/claude-code → assets/claude}/hooks/oms-guard.mjs +0 -0
  426. /package/{adapters/claude-code → assets/claude}/hooks/oms-post-guard.mjs +0 -0
  427. /package/dist/{conventions → kernel/conventions}/frontmatter.d.ts +0 -0
  428. /package/dist/{conventions → kernel/conventions}/frontmatter.js +0 -0
  429. /package/dist/{conventions → kernel/conventions}/lint.d.ts +0 -0
  430. /package/dist/{conventions → kernel/conventions}/lint.js +0 -0
  431. /package/dist/{conventions → kernel/conventions}/report.d.ts +0 -0
  432. /package/dist/{conventions → kernel/conventions}/report.js +0 -0
  433. /package/dist/{conventions → kernel/conventions}/validate.js +0 -0
  434. /package/dist/{conventions → kernel/conventions}/vault-walk.d.ts +0 -0
  435. /package/dist/{conventions → kernel/conventions}/vault-walk.js +0 -0
  436. /package/dist/{conventions → kernel/conventions}/write-contract.js +0 -0
  437. /package/dist/{conventions → kernel/conventions}/write-protocol.d.ts +0 -0
  438. /package/dist/{conventions → kernel/conventions}/write-protocol.js +0 -0
  439. /package/dist/{engine → kernel/engine}/embed/chunker.d.ts +0 -0
  440. /package/dist/{engine → kernel/engine}/embed/chunker.js +0 -0
  441. /package/dist/{engine → kernel/engine}/embed/deferred.d.ts +0 -0
  442. /package/dist/{engine → kernel/engine}/embed/deferred.js +0 -0
  443. /package/dist/{engine → kernel/engine}/graph/explore.d.ts +0 -0
  444. /package/dist/{engine → kernel/engine}/graph/explore.js +0 -0
  445. /package/dist/{engine → kernel/engine}/graph/resolver.d.ts +0 -0
  446. /package/dist/{engine → kernel/engine}/graph/resolver.js +0 -0
  447. /package/dist/{engine → kernel/engine}/graph/traverse.d.ts +0 -0
  448. /package/dist/{engine → kernel/engine}/graph/traverse.js +0 -0
  449. /package/dist/{engine → kernel/engine}/linkify/apply.d.ts +0 -0
  450. /package/dist/{engine → kernel/engine}/linkify/apply.js +0 -0
  451. /package/dist/{engine → kernel/engine}/linkify/josa.d.ts +0 -0
  452. /package/dist/{engine → kernel/engine}/linkify/josa.js +0 -0
  453. /package/dist/{engine → kernel/engine}/linkify/mask.d.ts +0 -0
  454. /package/dist/{engine → kernel/engine}/linkify/mask.js +0 -0
  455. /package/dist/{engine → kernel/engine}/linkify/suggest.d.ts +0 -0
  456. /package/dist/{engine → kernel/engine}/linkify/suggest.js +0 -0
  457. /package/dist/{engine → kernel/engine}/linkify/types.d.ts +0 -0
  458. /package/dist/{engine → kernel/engine}/linkify/types.js +0 -0
  459. /package/dist/{engine → kernel/engine}/mcp/op-mappers.d.ts +0 -0
  460. /package/dist/{engine → kernel/engine}/mcp/op-mappers.js +0 -0
  461. /package/dist/{engine → kernel/engine}/mcp/types.js +0 -0
  462. /package/dist/{engine → kernel/engine}/types.js +0 -0
  463. /package/dist/{graph → kernel/graph}/explore.d.ts +0 -0
  464. /package/dist/{graph → kernel/graph}/explore.js +0 -0
  465. /package/dist/{harness → kernel/harness}/validation.d.ts +0 -0
  466. /package/dist/{install → kernel/install}/common.d.ts +0 -0
  467. /package/dist/{install → kernel/install}/common.js +0 -0
  468. /package/dist/{install → kernel/install}/types.d.ts +0 -0
  469. /package/dist/{core/ontology → kernel/install}/types.js +0 -0
  470. /package/dist/{link → kernel/link}/convention-note.d.ts +0 -0
  471. /package/dist/{link → kernel/link}/global-config.d.ts +0 -0
  472. /package/dist/{link → kernel/link}/global-config.js +0 -0
  473. /package/dist/{link → kernel/link}/link.d.ts +0 -0
  474. /package/dist/{link → kernel/link}/link.js +0 -0
  475. /package/dist/{core → kernel}/ontology/loader.d.ts +0 -0
  476. /package/dist/{core → kernel}/ontology/loader.js +0 -0
  477. /package/dist/{core → kernel}/ontology/resolver.d.ts +0 -0
  478. /package/dist/{core → kernel}/ontology/resolver.js +0 -0
  479. /package/dist/{core → kernel}/ontology/types.d.ts +0 -0
  480. /package/dist/{install → kernel/ontology}/types.js +0 -0
  481. /package/dist/{retrieve → kernel/search}/morning-test-fixtures.d.ts +0 -0
  482. /package/dist/{retrieve → kernel/search}/morning-test-fixtures.js +0 -0
  483. /package/dist/{retrieve → kernel/search}/morning.js +0 -0
  484. /package/dist/{retrieve → kernel/search}/semantic-contract.d.ts +0 -0
  485. /package/dist/{retrieve → kernel/search}/semantic-contract.js +0 -0
  486. /package/dist/{mcp → kernel/semantic}/semantic-retrieve.d.ts +0 -0
  487. /package/dist/{mcp → kernel/semantic}/semantic-schemas.d.ts +0 -0
  488. /package/dist/{mcp → kernel/semantic}/semantic-schemas.js +0 -0
  489. /package/dist/{mcp → kernel/semantic}/semantic-tool-schemas.d.ts +0 -0
  490. /package/dist/{hook → vendors/claude/hook}/post-tool-use.d.ts +0 -0
  491. /package/dist/{hook → vendors/claude/hook}/pre-tool-use.d.ts +0 -0
  492. /package/dist/{hook → vendors/claude/hook}/pre-tool-use.js +0 -0
  493. /package/dist/{hook → vendors/claude/hook}/stdin.d.ts +0 -0
  494. /package/dist/{hook → vendors/claude/hook}/stdin.js +0 -0
@@ -0,0 +1,1144 @@
1
+ /**
2
+ * McpEngineAdapter — engine-side adapter facade for live MCP semantic ops.
3
+ *
4
+ * Receives DispatcherDeps + the vault root as INJECTED dependencies; never
5
+ * instantiates VectorStore, EmbeddingProvider, or any other backend. Backend
6
+ * construction is the assemble step (assemble.ts), which passes the real
7
+ * EngineStore + EmbeddingProvider via DispatcherDeps and the vault path here.
8
+ *
9
+ * This class owns the translation between MCP op inputs/outputs and the
10
+ * engine's retrieval / sync / graph contracts:
11
+ * - semantic_query → dispatch() over the RRF pipeline
12
+ * - sync_embeddings → syncEngineStore() (vault scan → chunk → embed → upsert)
13
+ * - semantic_status / collections / contexts → caps probe of DispatcherDeps
14
+ * - semantic_cleanup → orphan diff (store paths − live vault paths)
15
+ * - graph_build / status → builder.ts edge graph + node index, cached on disk
16
+ * - retrieve_by_axis → node-index axis filter + lexical score
17
+ *
18
+ * Live oms_retrieve_context is handled by retrieveMorningContext(), not this class.
19
+ *
20
+ * R18: NO import from src/search.
21
+ */
22
+ import path from "node:path";
23
+ import { existsSync, readFileSync } from "node:fs";
24
+ import { parse as parseYaml } from "yaml";
25
+ import { resolveBundledAssetPaths } from "../../runtime/assets.js";
26
+ import { loadContract } from "../../contracts/index.js";
27
+ import { retrieve } from "../retrieval/index.js";
28
+ import { acquireEngineStoreWriterLock, syncEngineStore, walkMarkdown, } from "../embed/sync.js";
29
+ import { openEngineStore } from "../embed/store.js";
30
+ import { buildGraph, saveCachedGraph, loadCachedGraphMeta, buildNodeIndex, saveNodeIndex, loadNodeIndex, nodeSourceSignature, } from "../graph/builder.js";
31
+ import { filterNodesByAxis, filterNodesByQueryAxes, queryFacets, searchScore, } from "../graph/node.js";
32
+ import { axisStorePath, collectVaultAxisObservations, openAxisStore, } from "../axes/store.js";
33
+ import { normalizeQueryOptions, retrievalResultsToQueryResult, queryResultUnavailable, } from "./query-mapper.js";
34
+ import { engineSyncResultToMcp, syncResultUnavailable, capsToEngineStatusResult, engineStatusResultToMcp, statusResultUnavailable, engineStatusToCollectionResult, engineStatusToContextResult, cleanupResultUnavailable, graphBuildOptionsToEngineArgs, engineGraphBuildResultToMcp, engineGraphBuildToStatusResult, } from "./op-mappers.js";
35
+ function positiveInt(value) {
36
+ if (!value || !/^\d+$/u.test(value))
37
+ return undefined;
38
+ const parsed = Number(value);
39
+ return parsed > 0 ? parsed : undefined;
40
+ }
41
+ /**
42
+ * Strip a resource scheme to a vault-relative path.
43
+ *
44
+ * `oms://<collection>/<path>` is URL-decoded; plain targets get backslashes and
45
+ * a leading "./" normalized away. The `qmd://` branch was removed with ADR-009:
46
+ * qmd compatibility is no longer a product contract, and keeping an input
47
+ * tolerance for a retired scheme is a compatibility layer with no caller.
48
+ */
49
+ function normalizeDocScheme(target) {
50
+ if (target.startsWith("oms://")) {
51
+ const rest = target.slice("oms://".length);
52
+ const slash = rest.indexOf("/");
53
+ return slash >= 0 ? decodeURIComponent(rest.slice(slash + 1)) : "";
54
+ }
55
+ return target.replace(/\\/g, "/").replace(/^\.?\//u, "");
56
+ }
57
+ /** Split a trailing line range off a path, supporting both colon forms. */
58
+ function splitDocRange(value) {
59
+ const parts = value.split(":");
60
+ // src/search colon form: "file:FROM:COUNT" (>= 3 parts, last two positive ints).
61
+ if (parts.length >= 3) {
62
+ const count = positiveInt(parts.at(-1));
63
+ const from = positiveInt(parts.at(-2));
64
+ if (from !== undefined && count !== undefined) {
65
+ return { filePath: parts.slice(0, -2).join(":"), fromLine: from, lineCount: count };
66
+ }
67
+ }
68
+ const colonIdx = value.lastIndexOf(":");
69
+ if (colonIdx > 0) {
70
+ const rangePart = value.slice(colonIdx + 1);
71
+ const filePart = value.slice(0, colonIdx);
72
+ const dash = /^(\d+)-(\d+)$/u.exec(rangePart);
73
+ if (dash) {
74
+ const from = parseInt(dash[1], 10);
75
+ const to = parseInt(dash[2], 10);
76
+ return { filePath: filePart, fromLine: from, lineCount: Math.max(0, to - from + 1) };
77
+ }
78
+ if (/^\d+$/u.test(rangePart)) {
79
+ return { filePath: filePart, fromLine: parseInt(rangePart, 10), lineCount: 1 };
80
+ }
81
+ }
82
+ return { filePath: value };
83
+ }
84
+ /**
85
+ * Parse target forms: "#docid", "file.md", "file.md:N", "file.md:N-M",
86
+ * "file.md:FROM:COUNT", "dir/*.md" (glob), and oms:// resource URIs.
87
+ */
88
+ function parseDocTarget(target) {
89
+ if (target.startsWith("#")) {
90
+ return { filePath: target.slice(1), isDocid: true, isGlob: false };
91
+ }
92
+ const scheme = normalizeDocScheme(target);
93
+ const { filePath, fromLine, lineCount } = splitDocRange(scheme);
94
+ return { filePath, fromLine, lineCount, isDocid: false, isGlob: filePath.includes("*") };
95
+ }
96
+ /** Mirror src/search globRegex: "*" → one path segment, "**" → any depth. */
97
+ function docGlobToRegex(pattern) {
98
+ let source = "";
99
+ for (let index = 0; index < pattern.length; index++) {
100
+ const char = pattern[index] ?? "";
101
+ const next = pattern[index + 1] ?? "";
102
+ if (char === "*" && next === "*") {
103
+ source += ".*";
104
+ index++;
105
+ }
106
+ else if (char === "*") {
107
+ source += "[^/]*";
108
+ }
109
+ else if ("|\\{}()[]^$+?.".includes(char)) {
110
+ source += `\\${char}`;
111
+ }
112
+ else {
113
+ source += char;
114
+ }
115
+ }
116
+ return new RegExp(`^${source}$`, "u");
117
+ }
118
+ /** Filesystem glob over the vault's markdown files; sorted for determinism. */
119
+ async function globVaultDocs(vault, pattern) {
120
+ const regex = docGlobToRegex(pattern);
121
+ const matches = [];
122
+ for await (const rel of walkMarkdown(vault, vault)) {
123
+ if (regex.test(rel))
124
+ matches.push(rel);
125
+ }
126
+ return matches.sort((a, b) => a.localeCompare(b));
127
+ }
128
+ /** Return true if resolving filePath inside vaultRoot would escape the vault. */
129
+ function isUnsafeVaultPath(filePath, vaultRoot) {
130
+ if (path.isAbsolute(filePath))
131
+ return true;
132
+ const root = path.resolve(vaultRoot);
133
+ const resolved = path.resolve(vaultRoot, filePath);
134
+ return resolved !== root && !resolved.startsWith(root + path.sep);
135
+ }
136
+ /** Slice and optionally number lines. lineNumbers format: "N\tline". */
137
+ function sliceDocLines(content, opts) {
138
+ const lines = content.split(/\r?\n/u);
139
+ const start = Math.max(0, (opts.fromLine ?? 1) - 1);
140
+ const count = opts.lineCount ?? opts.lineLimit ?? lines.length;
141
+ const selected = lines.slice(start, start + Math.max(0, count));
142
+ return selected
143
+ .map((line, index) => (opts.lineNumbers === true ? `${start + index + 1}\t${line}` : line))
144
+ .join("\n");
145
+ }
146
+ /** Cheaply extract a title from the first 20 lines (# H1 or frontmatter title:). */
147
+ function extractDocTitle(content) {
148
+ for (const line of content.split(/\r?\n/u).slice(0, 20)) {
149
+ const h1 = /^#\s+(.+)$/u.exec(line);
150
+ if (h1)
151
+ return h1[1].trim();
152
+ const fm = /^title:\s*(.+)$/u.exec(line);
153
+ if (fm)
154
+ return fm[1].trim();
155
+ }
156
+ return undefined;
157
+ }
158
+ /**
159
+ * First body lines (YAML frontmatter stripped, heading markers removed) as a
160
+ * single-line preview, capped to maxChars. The engine's retrieval is
161
+ * document-level (no chunk offsets), so this is an honest doc-head preview, not
162
+ * a match-centered excerpt.
163
+ */
164
+ function docHeadSnippet(content, maxChars = 200) {
165
+ let body = content;
166
+ const fm = /^---\r?\n[\s\S]*?\r?\n---\r?\n?/u.exec(body);
167
+ if (fm && fm.index === 0)
168
+ body = body.slice(fm[0].length);
169
+ const preview = body
170
+ .split(/\r?\n/u)
171
+ .map((line) => line.replace(/^#{1,6}\s+/u, "").trim())
172
+ .filter((line) => line.length > 0)
173
+ .join(" ");
174
+ return preview.length > maxChars ? `${preview.slice(0, maxChars).trimEnd()}…` : preview;
175
+ }
176
+ /**
177
+ * Best-effort title + doc-head snippet for ranked hits. Retrieval returns
178
+ * document-level paths only, so the snippet is the note's opening body (not a
179
+ * passage match) — practical parity with the src/search hit preview without
180
+ * faking relevance. Out-of-vault paths or read failures degrade to the bare hit
181
+ * (empty snippet, no title), never throwing.
182
+ */
183
+ function enrichQueryHits(result, vault) {
184
+ if (!result.available || result.hits.length === 0)
185
+ return result;
186
+ const hits = result.hits.map((hit) => {
187
+ if (isUnsafeVaultPath(hit.path, vault))
188
+ return hit;
189
+ let raw;
190
+ try {
191
+ raw = readFileSync(path.join(vault, hit.path), "utf-8");
192
+ }
193
+ catch {
194
+ return hit;
195
+ }
196
+ const title = extractDocTitle(raw);
197
+ return { ...hit, snippet: docHeadSnippet(raw), ...(title !== undefined ? { title } : {}) };
198
+ });
199
+ return { ...result, available: true, hits };
200
+ }
201
+ /** Read folder intent declarations without creating or updating vault state. */
202
+ function folderIntents(vault) {
203
+ const candidates = [
204
+ path.join(vault, ".oms", "taxonomy.yaml"),
205
+ path.join(vault, "taxonomy.yaml"),
206
+ ];
207
+ try {
208
+ candidates.push(path.join(resolveBundledAssetPaths().ontologyDir, "taxonomy.yaml"));
209
+ }
210
+ catch {
211
+ // A package without bundled ontology still serves axis results; intents
212
+ // remain optional metadata.
213
+ }
214
+ for (const candidate of candidates) {
215
+ try {
216
+ const parsed = parseYaml(readFileSync(candidate, "utf-8"));
217
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
218
+ continue;
219
+ const folders = parsed["folders"];
220
+ if (!folders || typeof folders !== "object" || Array.isArray(folders))
221
+ continue;
222
+ const intents = new Map();
223
+ for (const [folder, value] of Object.entries(folders)) {
224
+ if (!value || typeof value !== "object" || Array.isArray(value))
225
+ continue;
226
+ const intent = value["intent"];
227
+ if (typeof intent === "string" && intent.trim())
228
+ intents.set(folder, intent.trim());
229
+ }
230
+ return intents;
231
+ }
232
+ catch {
233
+ // A missing optional taxonomy is not an error; a malformed one simply
234
+ // contributes no intent and never causes a write.
235
+ }
236
+ }
237
+ return new Map();
238
+ }
239
+ function facetIntent(facet, intents, queryIntent) {
240
+ if (facet.axis === "folder")
241
+ return intents.get(facet.value) ?? `Folder axis: ${facet.value}`;
242
+ if (queryIntent !== undefined && queryIntent.trim().length > 0)
243
+ return queryIntent.trim();
244
+ if (facet.axis === "field")
245
+ return `Frontmatter field axis: ${facet.key ?? "unknown"}`;
246
+ return `Link axis: ${facet.value}`;
247
+ }
248
+ function isLexOnlySubQueries(subQueries) {
249
+ return subQueries.length > 0 && subQueries.every((subQuery) => subQuery.type === "lex");
250
+ }
251
+ /** Return true when the caller explicitly requested an embedding channel. */
252
+ function hasExplicitEmbeddingSearch(opts) {
253
+ if (opts.mode === "vsearch")
254
+ return true;
255
+ if (typeof opts.vec === "string" && opts.vec.trim().length > 0)
256
+ return true;
257
+ if (typeof opts.hyde === "string" && opts.hyde.trim().length > 0)
258
+ return true;
259
+ return (opts.searches ?? []).some((search) => search.type === "vec" || search.type === "hyde");
260
+ }
261
+ const UNBOUNDED_CANDIDATE_LIMIT = Number.MAX_SAFE_INTEGER;
262
+ /**
263
+ * Collection aggregation asks each child query for every candidate and applies
264
+ * one global page after merging. A direct query without a limit still keeps
265
+ * the public ten-hit default.
266
+ */
267
+ function resultPageLimit(opts) {
268
+ return opts.limit ?? (opts.collectionPath === undefined ? undefined : UNBOUNDED_CANDIDATE_LIMIT);
269
+ }
270
+ /** Read the dedicated EAV snapshot without creating `.oms` state. */
271
+ async function loadAxisSnapshot(vault) {
272
+ const dbPath = axisStorePath(vault);
273
+ if (!existsSync(dbPath))
274
+ return null;
275
+ const store = openAxisStore(dbPath, { readOnly: true });
276
+ try {
277
+ const storedSignature = store.sourceSignature();
278
+ if (storedSignature === null || storedSignature !== await nodeSourceSignature(vault))
279
+ return null;
280
+ return store.list();
281
+ }
282
+ finally {
283
+ store.close();
284
+ }
285
+ }
286
+ function observationValue(value) {
287
+ if (value instanceof Date)
288
+ return value.toISOString().toLowerCase();
289
+ return String(value).trim().toLowerCase();
290
+ }
291
+ function contractAxisType(type) {
292
+ switch (type) {
293
+ case "number":
294
+ return "number";
295
+ case "boolean":
296
+ case "checkbox":
297
+ return "boolean";
298
+ case "date":
299
+ case "datetime":
300
+ return "date";
301
+ case "text":
302
+ case "string":
303
+ case "select":
304
+ case "list":
305
+ case "multitext":
306
+ case "multi":
307
+ case "tags":
308
+ case "aliases":
309
+ case "file":
310
+ return "string";
311
+ default:
312
+ return undefined;
313
+ }
314
+ }
315
+ /**
316
+ * Compare live frontmatter/EAV observations with the tracked JSON contract.
317
+ * Missing fields are allowed (required-field validation owns that concern);
318
+ * an observed undeclared key, type, or closed-vocabulary value is drift.
319
+ */
320
+ function hasContractObservationDrift(contract, nodes, observations) {
321
+ if (contract === null)
322
+ return false;
323
+ const declared = new Set();
324
+ const types = new Map();
325
+ const allowed = new Map();
326
+ const addAllowed = (key, values) => {
327
+ if (values === undefined)
328
+ return;
329
+ const normalizedKey = key.trim().toLowerCase();
330
+ const target = allowed.get(normalizedKey) ?? new Set();
331
+ for (const item of values) {
332
+ const value = typeof item === "string"
333
+ ? item
334
+ : item && typeof item === "object" && typeof item.value === "string"
335
+ ? item.value
336
+ : undefined;
337
+ if (value !== undefined)
338
+ target.add(observationValue(value));
339
+ }
340
+ allowed.set(normalizedKey, target);
341
+ };
342
+ for (const axis of contract.axes) {
343
+ if (axis.kind !== "field")
344
+ continue;
345
+ const key = axis.key.trim().toLowerCase();
346
+ if (key === "concept")
347
+ continue;
348
+ declared.add(key);
349
+ types.set(key, axis.type);
350
+ addAllowed(key, axis.allowedValues);
351
+ }
352
+ for (const [key, type] of Object.entries(contract.types)) {
353
+ const normalizedKey = key.trim().toLowerCase();
354
+ if (normalizedKey === "concept")
355
+ continue;
356
+ declared.add(normalizedKey);
357
+ types.set(normalizedKey, type);
358
+ }
359
+ for (const [key, values] of Object.entries(contract.allowedValues)) {
360
+ const normalizedKey = key.trim().toLowerCase();
361
+ if (normalizedKey === "concept")
362
+ continue;
363
+ declared.add(normalizedKey);
364
+ addAllowed(normalizedKey, values);
365
+ }
366
+ const observedKeys = new Set();
367
+ const observedValues = [];
368
+ // Node axes are the live source of truth even when no EAV snapshot exists.
369
+ for (const node of nodes) {
370
+ for (const [key, rawValues] of Object.entries(node.axes)) {
371
+ const normalizedKey = key.trim().toLowerCase();
372
+ if (normalizedKey === "concept")
373
+ continue;
374
+ observedKeys.add(normalizedKey);
375
+ const values = Array.isArray(rawValues) ? rawValues : [rawValues];
376
+ for (const value of values) {
377
+ // String values can also represent YAML dates after node-cache
378
+ // serialisation, so only infer unambiguous primitive types here.
379
+ const valueType = typeof value === "number"
380
+ ? "number"
381
+ : typeof value === "boolean"
382
+ ? "boolean"
383
+ : undefined;
384
+ observedValues.push({ key: normalizedKey, value, valueType });
385
+ }
386
+ }
387
+ }
388
+ for (const row of observations ?? []) {
389
+ const normalizedKey = row.axisKey.trim().toLowerCase();
390
+ if (row.axisKind !== "field" || normalizedKey === "concept")
391
+ continue;
392
+ observedKeys.add(normalizedKey);
393
+ observedValues.push({
394
+ key: normalizedKey,
395
+ value: row.value,
396
+ valueType: row.valueType,
397
+ normalizedValue: row.normalizedValue,
398
+ });
399
+ }
400
+ for (const observed of observedValues) {
401
+ if (!declared.has(observed.key))
402
+ return true;
403
+ const expectedType = contractAxisType(types.get(observed.key) ?? "");
404
+ if (expectedType !== undefined && observed.valueType !== undefined && expectedType !== observed.valueType) {
405
+ return true;
406
+ }
407
+ const values = allowed.get(observed.key);
408
+ if (values !== undefined && !values.has(observed.normalizedValue ?? observationValue(observed.value))) {
409
+ return true;
410
+ }
411
+ }
412
+ for (const key of observedKeys) {
413
+ if (!declared.has(key))
414
+ return true;
415
+ }
416
+ return false;
417
+ }
418
+ async function contractObservationDrift(vault, nodes, observations) {
419
+ return hasContractObservationDrift(await loadContract(vault), nodes, observations);
420
+ }
421
+ /** Overlay typed EAV values onto the lexical node snapshot. */
422
+ function applyAxisSnapshot(nodes, observations) {
423
+ const byNote = new Map();
424
+ for (const observation of observations) {
425
+ const rows = byNote.get(observation.notePath) ?? [];
426
+ rows.push(observation);
427
+ byNote.set(observation.notePath, rows);
428
+ }
429
+ return nodes.map((node) => {
430
+ const rows = byNote.get(node.path);
431
+ if (rows === undefined)
432
+ return node;
433
+ const fields = {};
434
+ const links = [];
435
+ let folder = node.folder;
436
+ for (const row of rows) {
437
+ const value = row.value instanceof Date ? row.value.toISOString() : row.value;
438
+ if (row.axisKind === "folder" && typeof value === "string") {
439
+ folder = value;
440
+ }
441
+ else if (row.axisKind === "link" && typeof value === "string") {
442
+ links.push(value);
443
+ }
444
+ else if (row.axisKind === "field") {
445
+ const values = fields[row.axisKey] ?? [];
446
+ values.push(value);
447
+ fields[row.axisKey] = values;
448
+ }
449
+ }
450
+ return {
451
+ ...node,
452
+ folder,
453
+ // The node DTO keeps its legacy string-array declaration, while this
454
+ // runtime overlay deliberately carries typed EAV scalars.
455
+ axes: fields,
456
+ // An existing snapshot is authoritative: an empty link/field set must
457
+ // clear stale values from an older JSON node-index cache.
458
+ wikilinks: [...new Set(links)],
459
+ };
460
+ });
461
+ }
462
+ /** Contract field names are the allow-list for public field-axis queries. */
463
+ async function loadContractFieldKeys(vault) {
464
+ const contract = await loadContract(vault);
465
+ if (contract === null)
466
+ return undefined;
467
+ const keys = new Set();
468
+ for (const axis of contract.axes) {
469
+ if (axis.kind === "field")
470
+ keys.add(axis.key.trim().toLowerCase());
471
+ }
472
+ for (const key of Object.keys(contract.types))
473
+ keys.add(key.trim().toLowerCase());
474
+ return keys;
475
+ }
476
+ function validateKnownFieldAxes(axes, knownFields) {
477
+ if (axes === undefined || knownFields === undefined || axes.field === undefined)
478
+ return;
479
+ if (axes.field === null || typeof axes.field !== "object" || Array.isArray(axes.field)) {
480
+ throw new Error("Field axis must be an object mapping field names to values.");
481
+ }
482
+ for (const key of Object.keys(axes.field)) {
483
+ if (key.trim().toLocaleLowerCase() === "concept") {
484
+ throw new Error('Unknown query field axis "concept".');
485
+ }
486
+ if (!knownFields.has(key.trim().toLowerCase())) {
487
+ throw new Error(`Unknown field axis "${key}" (not declared by the JSON contract).`);
488
+ }
489
+ }
490
+ }
491
+ // ---------------------------------------------------------------------------
492
+ // Adapter facade
493
+ // ---------------------------------------------------------------------------
494
+ /**
495
+ * Engine-side adapter for the 10 MCP ops.
496
+ *
497
+ * Construct with injected DispatcherDeps + the vault root; the assemble step
498
+ * wires the real EngineStore + EmbeddingProvider into those deps.
499
+ */
500
+ export class McpEngineAdapter {
501
+ deps;
502
+ vaultPath;
503
+ config;
504
+ reranker;
505
+ implicitLexicalSync;
506
+ persistLexicalSync;
507
+ /**
508
+ * @param deps - Injected backend dependencies (store + embed required).
509
+ * @param vaultPath - Absolute vault root, used by graph / sync / cleanup /
510
+ * retrieve ops that are vault-scoped. Required so tsc
511
+ * enforces it at every construction site (RISK-4).
512
+ * @param config - Optional embedding identity config (provider/model).
513
+ * This is used to enforce mismatch policy and to thread
514
+ * identity into syncEmbeddings when the call omits it.
515
+ */
516
+ constructor(deps, vaultPath, config, reranker,
517
+ /** Explicit assembly policy; read-only engines suppress this refresh. */
518
+ implicitLexicalSync = false, persistLexicalSync = true) {
519
+ this.deps = deps;
520
+ this.vaultPath = vaultPath;
521
+ this.config = config;
522
+ this.reranker = reranker;
523
+ this.implicitLexicalSync = implicitLexicalSync;
524
+ this.persistLexicalSync = persistLexicalSync;
525
+ }
526
+ // -------------------------------------------------------------------------
527
+ // Cache-path + node-index helpers
528
+ // -------------------------------------------------------------------------
529
+ graphCachePath(vault) {
530
+ return path.join(vault, ".oms", "cache", "engine", "graph.json");
531
+ }
532
+ nodeCachePath(vault) {
533
+ return path.join(vault, ".oms", "cache", "engine", "node-index.json");
534
+ }
535
+ /** Load the node index from cache, building it live on a cache miss. */
536
+ async loadOrBuildNodes(vault = this.vaultPath) {
537
+ // A direct adapter caller may supply a store-backed query without a
538
+ // filesystem vault (for example an MCP transport test). Node enrichment is
539
+ // optional in that case; never turn a valid retrieval result into an ENOENT.
540
+ if (!existsSync(vault))
541
+ return [];
542
+ const cached = await loadNodeIndex(this.nodeCachePath(vault), await nodeSourceSignature(vault));
543
+ if (cached !== null)
544
+ return cached;
545
+ return buildNodeIndex({ vaultPath: vault });
546
+ }
547
+ // -------------------------------------------------------------------------
548
+ // 1. oms_semantic_query (centerpiece)
549
+ // -------------------------------------------------------------------------
550
+ /**
551
+ * Evaluate an overview/axis query directly from the read-only node index.
552
+ * This path is intentionally model-free and does not open the embedding
553
+ * store, so a query issued outside a vault or against an empty vault cannot
554
+ * create `.oms/` state as a side effect.
555
+ */
556
+ async queryNodeAxes(opts, subQueries) {
557
+ const vault = opts.vault ?? this.vaultPath;
558
+ const baseNodes = await this.loadOrBuildNodes(vault);
559
+ const snapshot = await loadAxisSnapshot(vault);
560
+ const drift = await contractObservationDrift(vault, baseNodes, snapshot);
561
+ const observedFields = snapshot === null
562
+ ? undefined
563
+ : new Set(snapshot.filter((row) => row.axisKind === "field").map((row) => row.axisKey.toLowerCase()));
564
+ const liveObservedFields = new Set(baseNodes.flatMap((node) => Object.keys(node.axes).map((key) => key.toLowerCase())));
565
+ const knownFields = await loadContractFieldKeys(vault)
566
+ ?? (observedFields === undefined ? liveObservedFields : new Set([...observedFields, ...liveObservedFields]));
567
+ validateKnownFieldAxes(opts.axes, knownFields);
568
+ const nodes = snapshot === null ? baseNodes : applyAxisSnapshot(baseNodes, snapshot);
569
+ const axisFiltered = opts.axes === undefined
570
+ ? nodes
571
+ : filterNodesByQueryAxes(nodes, opts.axes);
572
+ const collection = opts.collectionPath;
573
+ const filtered = collection === undefined
574
+ ? axisFiltered
575
+ : axisFiltered.filter((node) => node.path === collection || node.path.startsWith(`${collection}/`));
576
+ const query = subQueries.find((subQuery) => subQuery.type === "lex")?.query
577
+ ?? (subQueries.length === 0 ? opts.query ?? "" : "");
578
+ const scored = filtered
579
+ .map((node) => {
580
+ const score = searchScore(node, query);
581
+ return {
582
+ docPath: node.path,
583
+ score,
584
+ ...(query.trim().length > 0 ? { perTypeScores: { lex: score } } : {}),
585
+ };
586
+ })
587
+ // An axis constraint narrows the corpus; it is not lexical evidence.
588
+ // Once a lexical query is supplied, do not return axis-only zero-score
589
+ // documents as query hits.
590
+ .filter((result) => query.trim().length === 0 || result.score > 0)
591
+ .sort((left, right) => right.score - left.score || left.docPath.localeCompare(right.docPath));
592
+ const facetNodes = opts.minScore === undefined
593
+ ? filtered
594
+ : filtered.filter((node) => searchScore(node, query) >= opts.minScore);
595
+ const intents = folderIntents(vault);
596
+ const facets = queryFacets(facetNodes).map((facet) => ({
597
+ ...facet,
598
+ intent: facetIntent(facet, intents, opts.intent),
599
+ }));
600
+ const candidateLimit = opts.candidateLimit ?? UNBOUNDED_CANDIDATE_LIMIT;
601
+ const candidates = scored.slice(0, candidateLimit);
602
+ const shouldRerank = opts.rerank === true && opts.noRerank !== true;
603
+ if (shouldRerank && this.reranker === undefined) {
604
+ return queryResultUnavailable("reranking requires a configured Reranker; pass one to assembleEngine() or createOMSMcpServer().");
605
+ }
606
+ if (shouldRerank && query.trim().length === 0) {
607
+ return queryResultUnavailable("reranking requires a non-empty natural-language query.");
608
+ }
609
+ const reranker = this.reranker;
610
+ const ranked = shouldRerank
611
+ ? await reranker.rerank(query, candidates.map((result) => ({
612
+ docPath: result.docPath,
613
+ chunkOrdinal: 0,
614
+ score: result.score,
615
+ }))).then((reranked) => {
616
+ const candidateByPath = new Map(candidates.map((candidate) => [candidate.docPath, candidate]));
617
+ return reranked.flatMap((hit) => {
618
+ const candidate = candidateByPath.get(hit.docPath);
619
+ return candidate === undefined
620
+ ? []
621
+ : [{ ...candidate, score: hit.score, perTypeScores: { lex: hit.score } }];
622
+ });
623
+ })
624
+ : candidates;
625
+ const hasNonLexSearch = subQueries.some((search) => search.type !== "lex");
626
+ const result = retrievalResultsToQueryResult(ranked, {
627
+ minScore: opts.minScore,
628
+ limit: resultPageLimit(opts),
629
+ cursor: opts.cursor,
630
+ intent: opts.intent,
631
+ facetValues: facets,
632
+ // Axis queries are evaluated by the model-free node matcher. A vector
633
+ // request is reported as approximated rather than falsely claiming vec
634
+ // evidence in the receipt.
635
+ usedChannels: query.trim().length > 0 ? ["lex"] : [],
636
+ approximated: hasNonLexSearch,
637
+ drift,
638
+ });
639
+ return enrichQueryHits(result, vault);
640
+ }
641
+ /**
642
+ * Execute a semantic query and return MCP-shaped results.
643
+ * Maps opts → TypedSubQuery[] → dispatch() → McpSemanticQueryResult.
644
+ */
645
+ async semanticQuery(opts) {
646
+ let normalized;
647
+ try {
648
+ normalized = normalizeQueryOptions(opts);
649
+ }
650
+ catch (err) {
651
+ return queryResultUnavailable(err instanceof Error ? err.message : String(err));
652
+ }
653
+ const { subQueries } = normalized;
654
+ if (opts.axes !== undefined && hasExplicitEmbeddingSearch(opts)) {
655
+ return queryResultUnavailable("Axis queries support lexical matching only; explicit vector/HyDE retrieval requires " +
656
+ "OMS_EMBEDDING_PROVIDER + OMS_EMBEDDING_MODEL.");
657
+ }
658
+ if (opts.axes !== undefined || normalized.overview) {
659
+ try {
660
+ return await this.queryNodeAxes(opts, subQueries);
661
+ }
662
+ catch (err) {
663
+ return queryResultUnavailable(err instanceof Error ? err.message : String(err));
664
+ }
665
+ }
666
+ try {
667
+ // A core engine has a real lexical store but deliberately no embedding
668
+ // provider. The default query mode is hybrid for vector-capable engines;
669
+ // on a model-less engine, retain the useful lexical half unless the
670
+ // caller explicitly asked for vec/HyDE (which must fail loudly).
671
+ const vecAvailable = typeof this.deps.store.capabilities === "function"
672
+ ? this.deps.store.capabilities().vecAvailable
673
+ : true;
674
+ const modelLessFallback = !vecAvailable &&
675
+ !hasExplicitEmbeddingSearch(opts) &&
676
+ subQueries.some((subQuery) => subQuery.type === "vec");
677
+ const effectiveSubQueries = modelLessFallback
678
+ ? subQueries.filter((subQuery) => subQuery.type === "lex")
679
+ : subQueries;
680
+ if (this.implicitLexicalSync && isLexOnlySubQueries(effectiveSubQueries)) {
681
+ const syncResult = await syncEngineStore({
682
+ vault: opts.vault ?? this.vaultPath,
683
+ collection: opts.collection,
684
+ embed: false,
685
+ store: this.deps.store,
686
+ persist: this.persistLexicalSync,
687
+ });
688
+ if (!syncResult.available) {
689
+ return queryResultUnavailable(syncResult.reason ?? "Lexical index sync unavailable");
690
+ }
691
+ }
692
+ // Without an explicit candidate limit, retrieve the complete ranked
693
+ // stream so totalCount and offset cursors remain accurate on pages past
694
+ // the first 50 results. A caller-supplied candidateLimit remains an
695
+ // intentional cap (not an implementation truncation).
696
+ const k = opts.candidateLimit ?? UNBOUNDED_CANDIDATE_LIMIT;
697
+ const shouldRerank = opts.rerank === true && opts.noRerank !== true;
698
+ if (shouldRerank && this.reranker === undefined) {
699
+ return queryResultUnavailable("reranking requires a configured Reranker; pass one to assembleEngine() or createOMSMcpServer().");
700
+ }
701
+ if (shouldRerank && !opts.query?.trim()) {
702
+ return queryResultUnavailable("reranking requires a non-empty natural-language query.");
703
+ }
704
+ const results = await retrieve({
705
+ subQueries: [...effectiveSubQueries],
706
+ deps: this.deps,
707
+ k,
708
+ collection: opts.collectionPath,
709
+ query: opts.query,
710
+ reranker: shouldRerank ? this.reranker : undefined,
711
+ });
712
+ let facetValues;
713
+ const vault = opts.vault ?? this.vaultPath;
714
+ const baseNodes = await this.loadOrBuildNodes(vault);
715
+ const snapshot = await loadAxisSnapshot(vault);
716
+ const nodes = snapshot === null ? baseNodes : applyAxisSnapshot(baseNodes, snapshot);
717
+ const drift = await contractObservationDrift(vault, baseNodes, snapshot);
718
+ const scoped = opts.collectionPath === undefined
719
+ ? nodes
720
+ : nodes.filter((node) => node.path === opts.collectionPath || node.path.startsWith(`${opts.collectionPath}/`));
721
+ const intents = folderIntents(vault);
722
+ facetValues = queryFacets(scoped).map((facet) => ({
723
+ ...facet,
724
+ intent: facetIntent(facet, intents, opts.intent),
725
+ }));
726
+ const requestedChannels = [...new Set(effectiveSubQueries.map((search) => search.type))]
727
+ .filter((type) => type === "lex" || type === "vec" || type === "hyde");
728
+ const mapped = retrievalResultsToQueryResult(results, {
729
+ ...opts,
730
+ limit: resultPageLimit(opts),
731
+ facetValues,
732
+ usedChannels: requestedChannels,
733
+ approximated: modelLessFallback ||
734
+ effectiveSubQueries.some((search) => search.type !== "lex" && search.type !== "vec"),
735
+ drift,
736
+ });
737
+ // Fill title + doc-head snippet from disk so engine hits reach practical
738
+ // parity with the src/search preview (the pure mapper stays text-free).
739
+ return enrichQueryHits(mapped, opts.vault ?? this.vaultPath);
740
+ }
741
+ catch (err) {
742
+ return queryResultUnavailable(err instanceof Error ? err.message : String(err));
743
+ }
744
+ }
745
+ // -------------------------------------------------------------------------
746
+ // 2. oms_sync_embeddings
747
+ // -------------------------------------------------------------------------
748
+ /**
749
+ * Sync the vault into the engine store: scan → chunk → embed → upsert.
750
+ *
751
+ * Delegates to syncEngineStore() (embed/sync.ts), which performs SHA-256
752
+ * incremental diffing so unchanged chunks are skipped. The mandatory
753
+ * `status` field is synthesized from deps.embed + the run counters.
754
+ *
755
+ * Note: syncEngineStore opens its own provider+store internally; the GGUF
756
+ * pool deduplicates by loadPromise per process so this is safe (RISK-1).
757
+ */
758
+ async syncEmbeddings(opts) {
759
+ const activeDbPath = this.config?.dbPath ??
760
+ path.join(path.resolve(opts.vault), ".oms", "engine-store.sqlite");
761
+ let swapHandleClosed = false;
762
+ try {
763
+ const syncResult = await syncEngineStore({
764
+ vault: opts.vault,
765
+ collection: opts.collection,
766
+ collectionPath: opts.collectionPath,
767
+ // Embedding identity is owned by the assemble-time canonical config
768
+ // (OMS_EMBEDDING_PROVIDER / OMS_EMBEDDING_MODEL) threaded into the
769
+ // adapter; the MCP call no longer carries modelPath. syncEngineStore
770
+ // resolves the real provider and fails fast (ADR-007) if it is missing.
771
+ embeddingProvider: this.config?.embeddingProvider,
772
+ embeddingModel: this.config?.embeddingModel,
773
+ embeddingDescriptor: this.config?.embeddingDescriptor,
774
+ embeddingDimensions: this.config?.embeddingDimensions,
775
+ embeddingContext: this.config?.embeddingContext,
776
+ embeddingContextLength: this.config?.embeddingContextLength,
777
+ embeddingContextTokens: this.config?.embeddingContextTokens,
778
+ embeddingMrlDim: this.config?.embeddingMrlDim,
779
+ embeddingNormalization: this.config?.embeddingNormalization,
780
+ embeddingPrefixScheme: this.config?.embeddingPrefixScheme,
781
+ dbPath: this.config?.dbPath,
782
+ embed: opts.embed ?? true,
783
+ force: opts.force ?? false,
784
+ onGenerationSwapPrepare: () => {
785
+ // The adapter's store outlives one sync call. Close it before the
786
+ // active WAL/SHM sidecars are removed, not after rename.
787
+ swapHandleClosed = true;
788
+ this.deps.store.close();
789
+ },
790
+ onGenerationSwapComplete: () => {
791
+ if (!swapHandleClosed)
792
+ return;
793
+ const reboundStore = openEngineStore(activeDbPath, this.deps.embed.dimensions);
794
+ this.deps.store = reboundStore;
795
+ this.config?.onStoreRebind?.(reboundStore);
796
+ swapHandleClosed = false;
797
+ },
798
+ });
799
+ if (!syncResult.available) {
800
+ return syncResultUnavailable(syncResult.reason ?? "sync unavailable", opts);
801
+ }
802
+ const engineResult = {
803
+ upserted: syncResult.added + syncResult.updated,
804
+ skipped: syncResult.skipped,
805
+ errors: 0,
806
+ };
807
+ const statusSnapshot = {
808
+ available: true,
809
+ storage: opts.storage ?? "oms-native-json",
810
+ models: { embedding: this.deps.embed.model },
811
+ index: {
812
+ documents: {
813
+ total: syncResult.scanned,
814
+ vectors: syncResult.added + syncResult.updated,
815
+ },
816
+ },
817
+ };
818
+ return engineSyncResultToMcp(engineResult, opts, statusSnapshot);
819
+ }
820
+ catch (err) {
821
+ return syncResultUnavailable(err instanceof Error ? err.message : String(err), opts);
822
+ }
823
+ }
824
+ // -------------------------------------------------------------------------
825
+ // 3. oms_semantic_status
826
+ // -------------------------------------------------------------------------
827
+ /** Return status derived from the injected embed + store capabilities. */
828
+ semanticStatus(_opts) {
829
+ try {
830
+ const engineResult = capsToEngineStatusResult(this.deps.embed, this.deps.store);
831
+ return engineStatusResultToMcp(engineResult);
832
+ }
833
+ catch (err) {
834
+ return statusResultUnavailable(err instanceof Error ? err.message : String(err));
835
+ }
836
+ }
837
+ // -------------------------------------------------------------------------
838
+ // 4. oms_semantic_collections
839
+ // -------------------------------------------------------------------------
840
+ /** List embedding collections visible through the injected store. */
841
+ listCollections(_opts) {
842
+ try {
843
+ const engineResult = capsToEngineStatusResult(this.deps.embed, this.deps.store);
844
+ return engineStatusToCollectionResult(engineResult);
845
+ }
846
+ catch (err) {
847
+ return {
848
+ available: false,
849
+ reason: err instanceof Error ? err.message : String(err),
850
+ collections: [],
851
+ };
852
+ }
853
+ }
854
+ // -------------------------------------------------------------------------
855
+ // 5. oms_semantic_contexts
856
+ // -------------------------------------------------------------------------
857
+ /** List semantic contexts from the injected store. */
858
+ listContexts(_opts) {
859
+ try {
860
+ const engineResult = capsToEngineStatusResult(this.deps.embed, this.deps.store);
861
+ return engineStatusToContextResult(engineResult);
862
+ }
863
+ catch (err) {
864
+ return {
865
+ available: false,
866
+ reason: err instanceof Error ? err.message : String(err),
867
+ contexts: [],
868
+ };
869
+ }
870
+ }
871
+ // -------------------------------------------------------------------------
872
+ // 6. oms_semantic_cleanup
873
+ // -------------------------------------------------------------------------
874
+ /**
875
+ * Remove orphaned documents from the store: any stored doc_path that no
876
+ * longer exists in the live vault is cleared (meta + vec + FTS).
877
+ */
878
+ async cleanup(_opts) {
879
+ let releaseLock;
880
+ try {
881
+ const dbPath = this.config?.dbPath ??
882
+ path.join(path.resolve(this.vaultPath), ".oms", "engine-store.sqlite");
883
+ releaseLock = acquireEngineStoreWriterLock(dbPath);
884
+ const store = this.deps.store;
885
+ const livePaths = new Set();
886
+ for await (const rel of walkMarkdown(this.vaultPath, this.vaultPath)) {
887
+ livePaths.add(rel);
888
+ }
889
+ const storePaths = store.listDocPaths();
890
+ let removed = 0;
891
+ for (const docPath of storePaths) {
892
+ if (!livePaths.has(docPath)) {
893
+ store.clearDocument(docPath);
894
+ removed++;
895
+ }
896
+ }
897
+ return {
898
+ available: true,
899
+ storage: "oms-native-json",
900
+ removedDocuments: removed,
901
+ remainingDocuments: storePaths.length - removed,
902
+ collections: 1,
903
+ };
904
+ }
905
+ catch (err) {
906
+ return cleanupResultUnavailable(err instanceof Error ? err.message : String(err));
907
+ }
908
+ finally {
909
+ releaseLock?.();
910
+ }
911
+ }
912
+ // -------------------------------------------------------------------------
913
+ // 7. oms_graph_build
914
+ // -------------------------------------------------------------------------
915
+ /**
916
+ * Build the edge graph + node index and persist both to .oms/cache/engine/.
917
+ * On dryRun, report stats from the existing cache without rebuilding.
918
+ */
919
+ async graphBuild(opts, vaultPath) {
920
+ const args = graphBuildOptionsToEngineArgs(opts, vaultPath);
921
+ const graphCachePath = this.graphCachePath(args.vaultPath);
922
+ if (args.dryRun) {
923
+ const meta = await loadCachedGraphMeta(graphCachePath);
924
+ if (meta !== null) {
925
+ const noteSet = new Set(meta.edges.flatMap((e) => [e.from, e.to]));
926
+ return engineGraphBuildResultToMcp({
927
+ notes: noteSet.size,
928
+ edges: meta.edges.length,
929
+ generatedAt: meta.generatedAt,
930
+ });
931
+ }
932
+ return engineGraphBuildResultToMcp({
933
+ notes: 0,
934
+ edges: 0,
935
+ generatedAt: new Date().toISOString(),
936
+ });
937
+ }
938
+ const edges = await buildGraph({ vaultPath: args.vaultPath });
939
+ await saveCachedGraph(graphCachePath, edges);
940
+ const nodes = await buildNodeIndex({ vaultPath: args.vaultPath });
941
+ const sourceSignature = await nodeSourceSignature(args.vaultPath);
942
+ await saveNodeIndex(this.nodeCachePath(args.vaultPath), nodes, sourceSignature);
943
+ // Graph build is the explicit writable refresh boundary for the typed
944
+ // EAV snapshot. Reconcile it atomically with the current markdown set.
945
+ const axisStore = await collectVaultAxisObservations(args.vaultPath);
946
+ axisStore.close();
947
+ const noteSet = new Set(edges.flatMap((e) => [e.from, e.to]));
948
+ return engineGraphBuildResultToMcp({
949
+ notes: noteSet.size,
950
+ edges: edges.length,
951
+ generatedAt: new Date().toISOString(),
952
+ });
953
+ }
954
+ // -------------------------------------------------------------------------
955
+ // 8a. oms_graph_status
956
+ // -------------------------------------------------------------------------
957
+ /** Report graph cache status (notes / edges / generatedAt) from disk. */
958
+ async graphStatus(vaultPath) {
959
+ const meta = await loadCachedGraphMeta(this.graphCachePath(vaultPath));
960
+ if (meta === null)
961
+ return engineGraphBuildToStatusResult(null);
962
+ const noteSet = new Set(meta.edges.flatMap((e) => [e.from, e.to]));
963
+ return engineGraphBuildToStatusResult({
964
+ notes: noteSet.size,
965
+ edges: meta.edges.length,
966
+ generatedAt: meta.generatedAt,
967
+ });
968
+ }
969
+ // -------------------------------------------------------------------------
970
+ // 8b. oms_retrieve_by_axis
971
+ // -------------------------------------------------------------------------
972
+ /**
973
+ * Filter the node index by axis (concept / folder / property / value /
974
+ * wikilink), rank by lexical overlap with the optional query, return hits.
975
+ * Axis metadata (concept / folder / axes / wikilinks) is JSON-encoded into
976
+ * the hit's `context` field for callers that need it (RISK-6).
977
+ */
978
+ async retrieveByAxis(filters) {
979
+ try {
980
+ const baseNodes = await this.loadOrBuildNodes();
981
+ const snapshot = await loadAxisSnapshot(this.vaultPath);
982
+ const drift = await contractObservationDrift(this.vaultPath, baseNodes, snapshot);
983
+ const nodes = snapshot === null ? baseNodes : applyAxisSnapshot(baseNodes, snapshot);
984
+ const limit = Math.max(1, filters.limit ?? 10);
985
+ const filtered = filterNodesByAxis(nodes, {
986
+ concept: filters.concept,
987
+ folder: filters.folder,
988
+ property: filters.property,
989
+ value: filters.value,
990
+ wikilink: filters.wikilink,
991
+ });
992
+ const query = filters.query ?? "";
993
+ const scored = filtered
994
+ .map((node) => ({ node, score: searchScore(node, query) }))
995
+ .sort((a, b) => b.score - a.score || a.node.path.localeCompare(b.node.path))
996
+ .slice(0, limit);
997
+ const hits = scored.map(({ node, score }) => ({
998
+ docid: node.path,
999
+ score,
1000
+ uri: node.path,
1001
+ path: node.path,
1002
+ snippet: node.bodyPreview,
1003
+ context: JSON.stringify({
1004
+ concept: node.concept,
1005
+ folder: node.folder,
1006
+ axes: node.axes,
1007
+ wikilinks: node.wikilinks,
1008
+ }),
1009
+ evidence: { lexical: true, vector: false },
1010
+ }));
1011
+ const intents = folderIntents(this.vaultPath);
1012
+ const facets = queryFacets(filtered).map((facet) => ({
1013
+ ...facet,
1014
+ intent: facetIntent(facet, intents, undefined),
1015
+ }));
1016
+ return {
1017
+ available: true,
1018
+ hits,
1019
+ totalCount: filtered.length,
1020
+ facets,
1021
+ cursor: scored.length < filtered.length ? String(scored.length) : null,
1022
+ receipt: { usedChannels: ["lex"], approximated: false, drift },
1023
+ };
1024
+ }
1025
+ catch (err) {
1026
+ return queryResultUnavailable(err instanceof Error ? err.message : String(err));
1027
+ }
1028
+ }
1029
+ // -------------------------------------------------------------------------
1030
+ // 9. oms_get — single-document file-based hydration (GAP-9)
1031
+ // -------------------------------------------------------------------------
1032
+ /**
1033
+ * Hydrate one document from disk by real vault-relative path (ADR-008).
1034
+ * Supports "file.md", "file.md:N" (single line), "file.md:N-M" (range),
1035
+ * and "#docid" (resolved via store.listDocPaths). No embedding model needed.
1036
+ */
1037
+ async getDocument(opts) {
1038
+ const vault = opts.vault ?? this.vaultPath;
1039
+ const parsed = parseDocTarget(opts.target);
1040
+ let resolvedPath = parsed.filePath;
1041
+ if (parsed.isDocid) {
1042
+ const store = this.deps.store;
1043
+ const matched = store.listDocPaths().find((p) => p === parsed.filePath);
1044
+ if (!matched) {
1045
+ return { available: false, reason: `No OMS document matched "${opts.target}".`, documents: [] };
1046
+ }
1047
+ resolvedPath = matched;
1048
+ }
1049
+ else if (parsed.isGlob) {
1050
+ const [matched] = await globVaultDocs(vault, parsed.filePath);
1051
+ if (!matched) {
1052
+ return { available: false, reason: `No OMS document matched "${opts.target}".`, documents: [] };
1053
+ }
1054
+ resolvedPath = matched;
1055
+ }
1056
+ if (isUnsafeVaultPath(resolvedPath, vault)) {
1057
+ return { available: false, reason: "OMS semantic document target must stay inside the vault.", documents: [] };
1058
+ }
1059
+ let raw;
1060
+ try {
1061
+ raw = readFileSync(path.join(vault, resolvedPath), "utf-8");
1062
+ }
1063
+ catch {
1064
+ return { available: false, reason: `No OMS document matched "${opts.target}".`, documents: [] };
1065
+ }
1066
+ const content = sliceDocLines(raw, {
1067
+ fromLine: opts.fromLine ?? parsed.fromLine,
1068
+ lineCount: opts.lineCount ?? parsed.lineCount,
1069
+ lineNumbers: opts.lineNumbers,
1070
+ });
1071
+ const doc = {
1072
+ target: opts.target,
1073
+ path: opts.fullPath === true ? path.join(vault, resolvedPath) : resolvedPath,
1074
+ content,
1075
+ docid: resolvedPath,
1076
+ title: extractDocTitle(raw),
1077
+ };
1078
+ return { available: true, documents: [doc] };
1079
+ }
1080
+ // -------------------------------------------------------------------------
1081
+ // 10. oms_multi_get — batch file-based hydration (GAP-9)
1082
+ // -------------------------------------------------------------------------
1083
+ /**
1084
+ * Hydrate multiple documents from disk. De-dups by resolved path. Honors
1085
+ * lineLimit per doc and stops early when accumulated bytes would exceed
1086
+ * maxBytes (returns available:true with partial results, mirroring src/search).
1087
+ */
1088
+ async multiGetDocuments(opts) {
1089
+ const vault = opts.vault ?? this.vaultPath;
1090
+ const documents = [];
1091
+ const seen = new Set();
1092
+ let usedBytes = 0;
1093
+ for (const rawTarget of opts.targets) {
1094
+ const parsed = parseDocTarget(rawTarget);
1095
+ let resolvedPaths;
1096
+ if (parsed.isDocid) {
1097
+ const store = this.deps.store;
1098
+ const matched = store.listDocPaths().find((p) => p === parsed.filePath);
1099
+ resolvedPaths = matched ? [matched] : [];
1100
+ }
1101
+ else if (parsed.isGlob) {
1102
+ resolvedPaths = await globVaultDocs(vault, parsed.filePath);
1103
+ }
1104
+ else {
1105
+ resolvedPaths = [parsed.filePath];
1106
+ }
1107
+ for (const resolvedPath of resolvedPaths) {
1108
+ if (isUnsafeVaultPath(resolvedPath, vault)) {
1109
+ return { available: false, reason: "OMS semantic document target must stay inside the vault.", documents: [] };
1110
+ }
1111
+ if (seen.has(resolvedPath))
1112
+ continue;
1113
+ let raw;
1114
+ try {
1115
+ raw = readFileSync(path.join(vault, resolvedPath), "utf-8");
1116
+ }
1117
+ catch {
1118
+ continue;
1119
+ }
1120
+ const content = sliceDocLines(raw, {
1121
+ fromLine: parsed.fromLine,
1122
+ lineCount: parsed.lineCount,
1123
+ lineLimit: opts.lineLimit,
1124
+ lineNumbers: opts.lineNumbers,
1125
+ });
1126
+ const nextBytes = Buffer.byteLength(content, "utf-8");
1127
+ if (opts.maxBytes && usedBytes + nextBytes > opts.maxBytes) {
1128
+ return { available: true, documents };
1129
+ }
1130
+ seen.add(resolvedPath);
1131
+ usedBytes += nextBytes;
1132
+ documents.push({
1133
+ target: rawTarget,
1134
+ path: opts.fullPath === true ? path.join(vault, resolvedPath) : resolvedPath,
1135
+ content,
1136
+ docid: resolvedPath,
1137
+ title: extractDocTitle(raw),
1138
+ });
1139
+ }
1140
+ }
1141
+ return { available: true, documents };
1142
+ }
1143
+ }
1144
+ //# sourceMappingURL=facade.js.map