oh-my-second-brain 0.1.8 → 0.2.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 (466) hide show
  1. package/CHANGELOG.md +89 -0
  2. package/README.ko.md +127 -0
  3. package/README.md +128 -0
  4. package/adapters/README.md +8 -8
  5. package/adapters/claude-code/.claude-plugin/plugin.json +3 -2
  6. package/adapters/claude-code/.mcp.json +8 -0
  7. package/adapters/claude-code/CLAUDE.md +5 -6
  8. package/adapters/claude-code/skills/compile/SKILL.md +10 -35
  9. package/adapters/claude-code/skills/distill/SKILL.md +6 -26
  10. package/adapters/claude-code/skills/link/SKILL.md +87 -0
  11. package/adapters/claude-code/skills/retrieve/SKILL.md +4 -7
  12. package/adapters/claude-code/skills/setup/SKILL.md +5 -6
  13. package/adapters/claude-code/skills/uninstall/SKILL.md +1 -1
  14. package/adapters/claude-code/skills/wiki/SKILL.md +49 -24
  15. package/adapters/claude-code/skills/write/SKILL.md +16 -0
  16. package/adapters/codex/.codex-plugin/plugin.json +1 -1
  17. package/adapters/codex/AGENTS.md +2 -2
  18. package/adapters/codex/rules/oms.md +1 -1
  19. package/adapters/codex/skills/oms-compile/SKILL.md +7 -9
  20. package/adapters/codex/skills/oms-distill/SKILL.md +4 -9
  21. package/adapters/codex/skills/oms-link/SKILL.md +35 -0
  22. package/adapters/codex/skills/oms-retrieve/SKILL.md +5 -6
  23. package/adapters/codex/skills/oms-wiki/SKILL.md +28 -8
  24. package/adapters/codex/skills/oms-write/SKILL.md +10 -0
  25. package/adapters/hermes/README.md +3 -1
  26. package/adapters/hermes/SOUL.md +2 -3
  27. package/adapters/hermes/manifest.json +1 -1
  28. package/adapters/hermes/skills/compile/SKILL.md +5 -7
  29. package/adapters/hermes/skills/distill/SKILL.md +1 -3
  30. package/adapters/hermes/skills/link/SKILL.md +18 -0
  31. package/adapters/hermes/skills/retrieve/SKILL.md +5 -6
  32. package/adapters/hermes/skills/setup/SKILL.md +5 -2
  33. package/adapters/hermes/skills/wiki/SKILL.md +4 -2
  34. package/adapters/hermes/skills/write/SKILL.md +10 -0
  35. package/core/AGENTS.md +4 -2
  36. package/core/agents/librarian.md +3 -2
  37. package/core/ontology/concepts/term.yaml +12 -0
  38. package/core/ontology/taxonomy.yaml +3 -0
  39. package/core/skills/compile/SKILL.md +19 -18
  40. package/core/skills/distill/SKILL.md +13 -24
  41. package/core/skills/link/SKILL.md +130 -0
  42. package/core/skills/retrieve/SKILL.md +0 -1
  43. package/core/skills/setup/SKILL.md +12 -14
  44. package/core/skills/vault-lint/SKILL.md +1 -1
  45. package/core/skills/wiki/SKILL.md +109 -30
  46. package/core/skills/write/SKILL.md +15 -0
  47. package/dist/capture/safe.d.ts +61 -1
  48. package/dist/capture/safe.js +489 -66
  49. package/dist/capture/safe.js.map +1 -1
  50. package/dist/cli/args.d.ts +29 -0
  51. package/dist/cli/args.js +156 -0
  52. package/dist/cli/args.js.map +1 -0
  53. package/dist/cli/audit.d.ts +6 -0
  54. package/dist/cli/audit.js +93 -0
  55. package/dist/cli/audit.js.map +1 -0
  56. package/dist/cli/claude-install-plan.d.ts +10 -0
  57. package/dist/cli/claude-install-plan.js +26 -0
  58. package/dist/cli/claude-install-plan.js.map +1 -0
  59. package/dist/cli/doctor-lint.d.ts +11 -0
  60. package/dist/cli/doctor-lint.js +86 -0
  61. package/dist/cli/doctor-lint.js.map +1 -0
  62. package/dist/cli/global-writeback.d.ts +31 -0
  63. package/dist/cli/global-writeback.js +73 -0
  64. package/dist/cli/global-writeback.js.map +1 -0
  65. package/dist/cli/host-commands.d.ts +30 -0
  66. package/dist/cli/host-commands.js +89 -0
  67. package/dist/cli/host-commands.js.map +1 -0
  68. package/dist/cli/link-command.d.ts +9 -0
  69. package/dist/cli/link-command.js +47 -0
  70. package/dist/cli/link-command.js.map +1 -0
  71. package/dist/cli/linkify.d.ts +23 -0
  72. package/dist/cli/linkify.js +118 -0
  73. package/dist/cli/linkify.js.map +1 -0
  74. package/dist/cli/oms.d.ts +8 -32
  75. package/dist/cli/oms.js +100 -703
  76. package/dist/cli/oms.js.map +1 -1
  77. package/dist/cli/semantic-args.d.ts +1 -2
  78. package/dist/cli/semantic-args.js +4 -7
  79. package/dist/cli/semantic-args.js.map +1 -1
  80. package/dist/cli/semantic-http.d.ts +0 -3
  81. package/dist/cli/semantic-http.js +40 -45
  82. package/dist/cli/semantic-http.js.map +1 -1
  83. package/dist/cli/semantic-usage.js +10 -9
  84. package/dist/cli/semantic-usage.js.map +1 -1
  85. package/dist/cli/semantic.js +99 -137
  86. package/dist/cli/semantic.js.map +1 -1
  87. package/dist/cli/setup-command.d.ts +11 -0
  88. package/dist/cli/setup-command.js +196 -0
  89. package/dist/cli/setup-command.js.map +1 -0
  90. package/dist/cli/setup-documents.d.ts +15 -0
  91. package/dist/cli/setup-documents.js +149 -0
  92. package/dist/cli/setup-documents.js.map +1 -0
  93. package/dist/cli/update-notice.d.ts +10 -0
  94. package/dist/cli/update-notice.js +42 -0
  95. package/dist/cli/update-notice.js.map +1 -0
  96. package/dist/cli/usage.d.ts +4 -0
  97. package/dist/cli/usage.js +102 -0
  98. package/dist/cli/usage.js.map +1 -0
  99. package/dist/conventions/lint.js +12 -33
  100. package/dist/conventions/lint.js.map +1 -1
  101. package/dist/conventions/report.d.ts +62 -0
  102. package/dist/conventions/report.js +217 -0
  103. package/dist/conventions/report.js.map +1 -0
  104. package/dist/conventions/validate.d.ts +1 -1
  105. package/dist/conventions/vault-walk.d.ts +38 -0
  106. package/dist/conventions/vault-walk.js +80 -0
  107. package/dist/conventions/vault-walk.js.map +1 -0
  108. package/dist/conventions/write-contract.d.ts +27 -0
  109. package/dist/conventions/write-contract.js +91 -0
  110. package/dist/conventions/write-contract.js.map +1 -0
  111. package/dist/conventions/write-protocol.d.ts +39 -0
  112. package/dist/conventions/write-protocol.js +44 -0
  113. package/dist/conventions/write-protocol.js.map +1 -0
  114. package/dist/core/ontology/loader.d.ts +2 -0
  115. package/dist/{ontology → core/ontology}/loader.js +11 -20
  116. package/dist/core/ontology/loader.js.map +1 -0
  117. package/dist/core/ontology/resolver.d.ts +2 -0
  118. package/dist/{ontology → core/ontology}/resolver.js +1 -15
  119. package/dist/core/ontology/resolver.js.map +1 -0
  120. package/dist/core/ontology/types.d.ts +47 -0
  121. package/dist/core/ontology/types.js +2 -0
  122. package/dist/core/ontology/types.js.map +1 -0
  123. package/dist/core/runtime/assets.d.ts +20 -0
  124. package/dist/core/runtime/assets.js +66 -0
  125. package/dist/core/runtime/assets.js.map +1 -0
  126. package/dist/engine/assemble.d.ts +28 -72
  127. package/dist/engine/assemble.js +77 -61
  128. package/dist/engine/assemble.js.map +1 -1
  129. package/dist/engine/conventions/vault-lint.d.ts +30 -2
  130. package/dist/engine/conventions/vault-lint.js +93 -97
  131. package/dist/engine/conventions/vault-lint.js.map +1 -1
  132. package/dist/engine/embed/deferred.d.ts +3 -4
  133. package/dist/engine/embed/deferred.js +10 -7
  134. package/dist/engine/embed/deferred.js.map +1 -1
  135. package/dist/engine/embed/identity.d.ts +11 -0
  136. package/dist/engine/embed/identity.js +15 -0
  137. package/dist/engine/embed/identity.js.map +1 -0
  138. package/dist/engine/embed/provider.d.ts +12 -13
  139. package/dist/engine/embed/provider.js +30 -27
  140. package/dist/engine/embed/provider.js.map +1 -1
  141. package/dist/engine/embed/store.d.ts +48 -27
  142. package/dist/engine/embed/store.js +281 -98
  143. package/dist/engine/embed/store.js.map +1 -1
  144. package/dist/engine/embed/sync.d.ts +17 -47
  145. package/dist/engine/embed/sync.js +233 -69
  146. package/dist/engine/embed/sync.js.map +1 -1
  147. package/dist/engine/graph/builder.d.ts +1 -1
  148. package/dist/engine/graph/builder.js +1 -1
  149. package/dist/engine/graph/explore.d.ts +2 -2
  150. package/dist/engine/graph/explore.js +0 -0
  151. package/dist/engine/graph/resolver.d.ts +17 -0
  152. package/dist/engine/graph/resolver.js +46 -11
  153. package/dist/engine/graph/resolver.js.map +1 -1
  154. package/dist/engine/linkify/apply.d.ts +56 -0
  155. package/dist/engine/linkify/apply.js +65 -0
  156. package/dist/engine/linkify/apply.js.map +1 -0
  157. package/dist/engine/linkify/josa.d.ts +58 -0
  158. package/dist/engine/linkify/josa.js +133 -0
  159. package/dist/engine/linkify/josa.js.map +1 -0
  160. package/dist/engine/linkify/mask.d.ts +29 -0
  161. package/dist/engine/linkify/mask.js +173 -0
  162. package/dist/engine/linkify/mask.js.map +1 -0
  163. package/dist/engine/linkify/suggest.d.ts +52 -0
  164. package/dist/engine/linkify/suggest.js +181 -0
  165. package/dist/engine/linkify/suggest.js.map +1 -0
  166. package/dist/engine/linkify/types.d.ts +68 -0
  167. package/dist/engine/linkify/types.js +12 -0
  168. package/dist/engine/linkify/types.js.map +1 -0
  169. package/dist/engine/mcp/facade.d.ts +12 -18
  170. package/dist/engine/mcp/facade.js +30 -80
  171. package/dist/engine/mcp/facade.js.map +1 -1
  172. package/dist/engine/mcp/op-mappers.d.ts +3 -9
  173. package/dist/engine/mcp/op-mappers.js +2 -30
  174. package/dist/engine/mcp/op-mappers.js.map +1 -1
  175. package/dist/engine/mcp/types.d.ts +5 -29
  176. package/dist/engine/tracer.js +6 -2
  177. package/dist/engine/tracer.js.map +1 -1
  178. package/dist/engine/types.d.ts +8 -3
  179. package/dist/graph/cache.d.ts +1 -11
  180. package/dist/graph/cache.js +1 -44
  181. package/dist/graph/cache.js.map +1 -1
  182. package/dist/graph/explore.js +88 -137
  183. package/dist/graph/explore.js.map +1 -1
  184. package/dist/harness/surface-registry.d.ts +56 -0
  185. package/dist/harness/surface-registry.js +397 -0
  186. package/dist/harness/surface-registry.js.map +1 -0
  187. package/dist/harness/validation.d.ts +9 -0
  188. package/dist/harness/validation.js +201 -0
  189. package/dist/harness/validation.js.map +1 -0
  190. package/dist/hook/post-tool-use.js +2 -2
  191. package/dist/hook/post-tool-use.js.map +1 -1
  192. package/dist/index.d.ts +7 -5
  193. package/dist/index.js +7 -5
  194. package/dist/index.js.map +1 -1
  195. package/dist/install/adapter-source.d.ts +14 -0
  196. package/dist/install/adapter-source.js +45 -0
  197. package/dist/install/adapter-source.js.map +1 -0
  198. package/dist/install/claude-hooks.d.ts +13 -0
  199. package/dist/install/claude-hooks.js +411 -0
  200. package/dist/install/claude-hooks.js.map +1 -0
  201. package/dist/install/claude-marketplace.d.ts +44 -0
  202. package/dist/install/claude-marketplace.js +89 -0
  203. package/dist/install/claude-marketplace.js.map +1 -0
  204. package/dist/install/claude.d.ts +4 -0
  205. package/dist/install/claude.js +216 -0
  206. package/dist/install/claude.js.map +1 -0
  207. package/dist/install/codex.d.ts +24 -0
  208. package/dist/install/codex.js +193 -0
  209. package/dist/install/codex.js.map +1 -0
  210. package/dist/install/common.d.ts +34 -0
  211. package/dist/install/common.js +122 -0
  212. package/dist/install/common.js.map +1 -0
  213. package/dist/install/hermes.d.ts +4 -0
  214. package/dist/install/hermes.js +78 -0
  215. package/dist/install/hermes.js.map +1 -0
  216. package/dist/install/hosts.d.ts +6 -56
  217. package/dist/install/hosts.js +65 -610
  218. package/dist/install/hosts.js.map +1 -1
  219. package/dist/install/types.d.ts +34 -0
  220. package/dist/install/types.js +2 -0
  221. package/dist/install/types.js.map +1 -0
  222. package/dist/link/convention-note.d.ts +10 -0
  223. package/dist/link/convention-note.js +59 -0
  224. package/dist/link/convention-note.js.map +1 -0
  225. package/dist/link/global-config.d.ts +22 -0
  226. package/dist/link/global-config.js +69 -0
  227. package/dist/link/global-config.js.map +1 -0
  228. package/dist/link/link.d.ts +101 -0
  229. package/dist/link/link.js +265 -0
  230. package/dist/link/link.js.map +1 -0
  231. package/dist/mcp/engine-morning-backend.d.ts +12 -17
  232. package/dist/mcp/engine-morning-backend.js +25 -131
  233. package/dist/mcp/engine-morning-backend.js.map +1 -1
  234. package/dist/mcp/link-tools.d.ts +81 -0
  235. package/dist/mcp/link-tools.js +157 -0
  236. package/dist/mcp/link-tools.js.map +1 -0
  237. package/dist/mcp/semantic-engine.d.ts +32 -0
  238. package/dist/mcp/semantic-engine.js +52 -0
  239. package/dist/mcp/semantic-engine.js.map +1 -0
  240. package/dist/mcp/semantic-retrieve-args.d.ts +1 -5
  241. package/dist/mcp/semantic-retrieve-args.js +1 -26
  242. package/dist/mcp/semantic-retrieve-args.js.map +1 -1
  243. package/dist/mcp/semantic-retrieve-schema.d.ts +0 -14
  244. package/dist/mcp/semantic-retrieve-schema.js +0 -5
  245. package/dist/mcp/semantic-retrieve-schema.js.map +1 -1
  246. package/dist/mcp/semantic-retrieve.d.ts +12 -18
  247. package/dist/mcp/semantic-retrieve.js +40 -71
  248. package/dist/mcp/semantic-retrieve.js.map +1 -1
  249. package/dist/mcp/semantic-tool-schemas.js +7 -24
  250. package/dist/mcp/semantic-tool-schemas.js.map +1 -1
  251. package/dist/mcp/server.d.ts +7 -0
  252. package/dist/mcp/server.js +224 -162
  253. package/dist/mcp/server.js.map +1 -1
  254. package/dist/mcp/update-notice.d.ts +51 -0
  255. package/dist/mcp/update-notice.js +196 -0
  256. package/dist/mcp/update-notice.js.map +1 -0
  257. package/dist/ontology/active.d.ts +7 -0
  258. package/dist/ontology/active.js +41 -0
  259. package/dist/ontology/active.js.map +1 -0
  260. package/dist/retrieve/morning.d.ts +10 -18
  261. package/dist/retrieve/morning.js +2 -26
  262. package/dist/retrieve/morning.js.map +1 -1
  263. package/dist/retrieve/semantic-contract.d.ts +10 -0
  264. package/dist/retrieve/semantic-contract.js +2 -0
  265. package/dist/retrieve/semantic-contract.js.map +1 -0
  266. package/dist/setup/axis.d.ts +22 -3
  267. package/dist/setup/axis.js +82 -35
  268. package/dist/setup/axis.js.map +1 -1
  269. package/dist/update/update.d.ts +12 -0
  270. package/dist/update/update.js +2 -2
  271. package/dist/update/update.js.map +1 -1
  272. package/docs/install.md +43 -7
  273. package/docs/release.md +109 -17
  274. package/package.json +11 -3
  275. package/scripts/install.sh +1 -1
  276. package/adapters/claude-code/skills/capture/SKILL.md +0 -50
  277. package/adapters/codex/skills/oms-capture/SKILL.md +0 -16
  278. package/adapters/hermes/skills/capture/SKILL.md +0 -16
  279. package/core/skills/capture/SKILL.md +0 -50
  280. package/dist/adapt/HostAdapter.d.ts +0 -21
  281. package/dist/adapt/HostAdapter.js +0 -10
  282. package/dist/adapt/HostAdapter.js.map +0 -1
  283. package/dist/adapt/claudeCode.d.ts +0 -9
  284. package/dist/adapt/claudeCode.js +0 -15
  285. package/dist/adapt/claudeCode.js.map +0 -1
  286. package/dist/adapt/codex.d.ts +0 -9
  287. package/dist/adapt/codex.js +0 -15
  288. package/dist/adapt/codex.js.map +0 -1
  289. package/dist/adapt/hermes.d.ts +0 -9
  290. package/dist/adapt/hermes.js +0 -15
  291. package/dist/adapt/hermes.js.map +0 -1
  292. package/dist/cli/semantic-metadata-commands.d.ts +0 -3
  293. package/dist/cli/semantic-metadata-commands.js +0 -115
  294. package/dist/cli/semantic-metadata-commands.js.map +0 -1
  295. package/dist/engine/compile/cascade.d.ts +0 -35
  296. package/dist/engine/compile/cascade.js +0 -59
  297. package/dist/engine/compile/cascade.js.map +0 -1
  298. package/dist/engine/compile/cot.d.ts +0 -30
  299. package/dist/engine/compile/cot.js +0 -153
  300. package/dist/engine/compile/cot.js.map +0 -1
  301. package/dist/engine/compile/phases.d.ts +0 -47
  302. package/dist/engine/compile/phases.js +0 -101
  303. package/dist/engine/compile/phases.js.map +0 -1
  304. package/dist/engine/compile/provenance.d.ts +0 -45
  305. package/dist/engine/compile/provenance.js +0 -103
  306. package/dist/engine/compile/provenance.js.map +0 -1
  307. package/dist/engine/compile/sha-cache.d.ts +0 -42
  308. package/dist/engine/compile/sha-cache.js +0 -86
  309. package/dist/engine/compile/sha-cache.js.map +0 -1
  310. package/dist/engine/compile/types.d.ts +0 -71
  311. package/dist/engine/compile/types.js +0 -9
  312. package/dist/engine/compile/types.js.map +0 -1
  313. package/dist/engine/compile/worker.d.ts +0 -47
  314. package/dist/engine/compile/worker.js +0 -75
  315. package/dist/engine/compile/worker.js.map +0 -1
  316. package/dist/engine/distill/analyzer.d.ts +0 -51
  317. package/dist/engine/distill/analyzer.js +0 -279
  318. package/dist/engine/distill/analyzer.js.map +0 -1
  319. package/dist/engine/distill/clean-room.d.ts +0 -46
  320. package/dist/engine/distill/clean-room.js +0 -131
  321. package/dist/engine/distill/clean-room.js.map +0 -1
  322. package/dist/engine/distill/report.d.ts +0 -23
  323. package/dist/engine/distill/report.js +0 -107
  324. package/dist/engine/distill/report.js.map +0 -1
  325. package/dist/engine/distill/types.d.ts +0 -89
  326. package/dist/engine/distill/types.js +0 -9
  327. package/dist/engine/distill/types.js.map +0 -1
  328. package/dist/engine/embed/pull.d.ts +0 -51
  329. package/dist/engine/embed/pull.js +0 -90
  330. package/dist/engine/embed/pull.js.map +0 -1
  331. package/dist/engine/graph/community.d.ts +0 -38
  332. package/dist/engine/graph/community.js +0 -122
  333. package/dist/engine/graph/community.js.map +0 -1
  334. package/dist/engine/index.d.ts +0 -5
  335. package/dist/engine/index.js +0 -2
  336. package/dist/engine/index.js.map +0 -1
  337. package/dist/engine/mcp/index.d.ts +0 -15
  338. package/dist/engine/mcp/index.js +0 -19
  339. package/dist/engine/mcp/index.js.map +0 -1
  340. package/dist/engine/mcp/retrieve-mapper.d.ts +0 -38
  341. package/dist/engine/mcp/retrieve-mapper.js +0 -96
  342. package/dist/engine/mcp/retrieve-mapper.js.map +0 -1
  343. package/dist/engine/setup/ambiguity.d.ts +0 -39
  344. package/dist/engine/setup/ambiguity.js +0 -50
  345. package/dist/engine/setup/ambiguity.js.map +0 -1
  346. package/dist/engine/setup/types.d.ts +0 -71
  347. package/dist/engine/setup/types.js +0 -22
  348. package/dist/engine/setup/types.js.map +0 -1
  349. package/dist/engine/setup/writer.d.ts +0 -32
  350. package/dist/engine/setup/writer.js +0 -77
  351. package/dist/engine/setup/writer.js.map +0 -1
  352. package/dist/engine/wiki/collection.d.ts +0 -91
  353. package/dist/engine/wiki/collection.js +0 -188
  354. package/dist/engine/wiki/collection.js.map +0 -1
  355. package/dist/engine/wiki/ledger.d.ts +0 -59
  356. package/dist/engine/wiki/ledger.js +0 -130
  357. package/dist/engine/wiki/ledger.js.map +0 -1
  358. package/dist/engine/wiki/lint.d.ts +0 -38
  359. package/dist/engine/wiki/lint.js +0 -186
  360. package/dist/engine/wiki/lint.js.map +0 -1
  361. package/dist/engine/wiki/navigation.d.ts +0 -40
  362. package/dist/engine/wiki/navigation.js +0 -83
  363. package/dist/engine/wiki/navigation.js.map +0 -1
  364. package/dist/engine/wiki/types.d.ts +0 -54
  365. package/dist/engine/wiki/types.js +0 -7
  366. package/dist/engine/wiki/types.js.map +0 -1
  367. package/dist/ontology/loader.d.ts +0 -13
  368. package/dist/ontology/loader.js.map +0 -1
  369. package/dist/ontology/resolver.d.ts +0 -13
  370. package/dist/ontology/resolver.js.map +0 -1
  371. package/dist/ontology/types.d.ts +0 -72
  372. package/dist/ontology/types.js +0 -12
  373. package/dist/ontology/types.js.map +0 -1
  374. package/dist/retrieve/embedding-sync.d.ts +0 -3
  375. package/dist/retrieve/embedding-sync.js +0 -21
  376. package/dist/retrieve/embedding-sync.js.map +0 -1
  377. package/dist/runtime/assets.d.ts +0 -7
  378. package/dist/runtime/assets.js +0 -13
  379. package/dist/runtime/assets.js.map +0 -1
  380. package/dist/search/semantic-collections.d.ts +0 -32
  381. package/dist/search/semantic-collections.js +0 -68
  382. package/dist/search/semantic-collections.js.map +0 -1
  383. package/dist/search/semantic-contexts.d.ts +0 -21
  384. package/dist/search/semantic-contexts.js +0 -28
  385. package/dist/search/semantic-contexts.js.map +0 -1
  386. package/dist/search/semantic-diagnostics.d.ts +0 -16
  387. package/dist/search/semantic-diagnostics.js +0 -110
  388. package/dist/search/semantic-diagnostics.js.map +0 -1
  389. package/dist/search/semantic-documents.d.ts +0 -3
  390. package/dist/search/semantic-documents.js +0 -150
  391. package/dist/search/semantic-documents.js.map +0 -1
  392. package/dist/search/semantic-embedding-hash.d.ts +0 -3
  393. package/dist/search/semantic-embedding-hash.js +0 -30
  394. package/dist/search/semantic-embedding-hash.js.map +0 -1
  395. package/dist/search/semantic-embedding-provider.d.ts +0 -9
  396. package/dist/search/semantic-embedding-provider.js +0 -58
  397. package/dist/search/semantic-embedding-provider.js.map +0 -1
  398. package/dist/search/semantic-index-build.d.ts +0 -13
  399. package/dist/search/semantic-index-build.js +0 -101
  400. package/dist/search/semantic-index-build.js.map +0 -1
  401. package/dist/search/semantic-index-core.d.ts +0 -21
  402. package/dist/search/semantic-index-core.js +0 -106
  403. package/dist/search/semantic-index-core.js.map +0 -1
  404. package/dist/search/semantic-index-io.d.ts +0 -19
  405. package/dist/search/semantic-index-io.js +0 -214
  406. package/dist/search/semantic-index-io.js.map +0 -1
  407. package/dist/search/semantic-index-types.d.ts +0 -50
  408. package/dist/search/semantic-index-types.js +0 -2
  409. package/dist/search/semantic-index-types.js.map +0 -1
  410. package/dist/search/semantic-index.d.ts +0 -4
  411. package/dist/search/semantic-index.js +0 -4
  412. package/dist/search/semantic-index.js.map +0 -1
  413. package/dist/search/semantic-listing.d.ts +0 -12
  414. package/dist/search/semantic-listing.js +0 -64
  415. package/dist/search/semantic-listing.js.map +0 -1
  416. package/dist/search/semantic-maintenance-shared.d.ts +0 -15
  417. package/dist/search/semantic-maintenance-shared.js +0 -43
  418. package/dist/search/semantic-maintenance-shared.js.map +0 -1
  419. package/dist/search/semantic-maintenance-types.d.ts +0 -103
  420. package/dist/search/semantic-maintenance-types.js +0 -2
  421. package/dist/search/semantic-maintenance-types.js.map +0 -1
  422. package/dist/search/semantic-maintenance.d.ts +0 -4
  423. package/dist/search/semantic-maintenance.js +0 -5
  424. package/dist/search/semantic-maintenance.js.map +0 -1
  425. package/dist/search/semantic-provider-types.d.ts +0 -34
  426. package/dist/search/semantic-provider-types.js +0 -2
  427. package/dist/search/semantic-provider-types.js.map +0 -1
  428. package/dist/search/semantic-query-types.d.ts +0 -83
  429. package/dist/search/semantic-query-types.js +0 -2
  430. package/dist/search/semantic-query-types.js.map +0 -1
  431. package/dist/search/semantic-query.d.ts +0 -2
  432. package/dist/search/semantic-query.js +0 -188
  433. package/dist/search/semantic-query.js.map +0 -1
  434. package/dist/search/semantic-sqlite-db.d.ts +0 -18
  435. package/dist/search/semantic-sqlite-db.js +0 -87
  436. package/dist/search/semantic-sqlite-db.js.map +0 -1
  437. package/dist/search/semantic-sqlite-query.d.ts +0 -2
  438. package/dist/search/semantic-sqlite-query.js +0 -128
  439. package/dist/search/semantic-sqlite-query.js.map +0 -1
  440. package/dist/search/semantic-sqlite-read.d.ts +0 -5
  441. package/dist/search/semantic-sqlite-read.js +0 -104
  442. package/dist/search/semantic-sqlite-read.js.map +0 -1
  443. package/dist/search/semantic-sqlite-write.d.ts +0 -7
  444. package/dist/search/semantic-sqlite-write.js +0 -66
  445. package/dist/search/semantic-sqlite-write.js.map +0 -1
  446. package/dist/search/semantic-store.d.ts +0 -15
  447. package/dist/search/semantic-store.js +0 -17
  448. package/dist/search/semantic-store.js.map +0 -1
  449. package/dist/search/semantic-sync-types.d.ts +0 -47
  450. package/dist/search/semantic-sync-types.js +0 -2
  451. package/dist/search/semantic-sync-types.js.map +0 -1
  452. package/dist/search/semantic-sync.d.ts +0 -13
  453. package/dist/search/semantic-sync.js +0 -131
  454. package/dist/search/semantic-sync.js.map +0 -1
  455. package/dist/search/semantic-test-fixtures.d.ts +0 -1
  456. package/dist/search/semantic-test-fixtures.js +0 -38
  457. package/dist/search/semantic-test-fixtures.js.map +0 -1
  458. package/dist/search/semantic-token.d.ts +0 -25
  459. package/dist/search/semantic-token.js +0 -114
  460. package/dist/search/semantic-token.js.map +0 -1
  461. package/dist/search/semantic-types.d.ts +0 -5
  462. package/dist/search/semantic-types.js +0 -2
  463. package/dist/search/semantic-types.js.map +0 -1
  464. package/dist/search/semantic.d.ts +0 -6
  465. package/dist/search/semantic.js +0 -6
  466. package/dist/search/semantic.js.map +0 -1
@@ -1,8 +1,10 @@
1
- import { mkdir, readFile, writeFile, appendFile } from "node:fs/promises";
1
+ import { access, appendFile, mkdir, readFile, stat, writeFile } from "node:fs/promises";
2
2
  import path from "node:path";
3
3
  import { stringify as yamlStringify } from "yaml";
4
- import { validateFrontmatter } from "../conventions/validate.js";
5
- import { resolveConcept } from "../ontology/resolver.js";
4
+ import { parseNote } from "../conventions/frontmatter.js";
5
+ import { evaluateWriteContract, routingLawStrictFolders, writeFieldDescriptors, } from "../conventions/write-contract.js";
6
+ import { rejection, } from "../conventions/write-protocol.js";
7
+ import { resolveConcept } from "../core/ontology/resolver.js";
6
8
  function slugify(value) {
7
9
  const slug = value
8
10
  .toLowerCase()
@@ -26,12 +28,84 @@ function defaultInboxFolder(ontology) {
26
28
  const inboxConcept = ontology.concepts.get("inbox");
27
29
  return inboxConcept?.folder ?? "inbox";
28
30
  }
29
- function requiredMissing(concept, frontmatter) {
30
- if (!concept)
31
- return [];
32
- return validateFrontmatter(frontmatter, concept).violations
33
- .filter((violation) => violation.rule === "required")
34
- .map((violation) => violation.field);
31
+ function mergeFrontmatter(base, patch) {
32
+ return { ...base, ...patch };
33
+ }
34
+ function emptyWriteFields() {
35
+ return [];
36
+ }
37
+ function contractFields(concept) {
38
+ return concept ? writeFieldDescriptors(concept) : emptyWriteFields();
39
+ }
40
+ function writeResult(partial) {
41
+ return partial;
42
+ }
43
+ function inboxResult(mode, ontology, filename, frontmatter, reason) {
44
+ const inbox = defaultInboxFolder(ontology);
45
+ const inboxConcept = findConcept(ontology, "inbox");
46
+ return writeResult({
47
+ status: "inbox",
48
+ mode,
49
+ notePath: `${inbox}/${filename}`,
50
+ concept: "inbox",
51
+ folder: inbox,
52
+ fields: contractFields(inboxConcept),
53
+ frontmatter,
54
+ missingFields: [],
55
+ violations: [],
56
+ reason,
57
+ });
58
+ }
59
+ function pathUnsafeRejection(reason) {
60
+ return rejection("admission", "path-unsafe", reason, "pass a vault-relative `notePath` ending in .md that stays inside the vault and avoids hidden or internal folders");
61
+ }
62
+ function conceptUnboundRejection() {
63
+ return rejection("admission", "concept-unbound", "Cannot commit capture: notePath does not resolve to a concept binding", "move the note into a folder bound to a concept in taxonomy.yaml, or target an existing bound note");
64
+ }
65
+ function rejectedResult(mode, notePath, concept, frontmatter, violations, reason, rejectionPayload) {
66
+ return writeResult({
67
+ status: "rejected",
68
+ mode,
69
+ notePath,
70
+ concept: concept?.concept ?? null,
71
+ folder: notePath.split("/")[0] ?? "",
72
+ fields: contractFields(concept),
73
+ frontmatter,
74
+ missingFields: violations.map((violation) => violation.field),
75
+ violations,
76
+ reason,
77
+ ...(rejectionPayload ? { rejection: rejectionPayload } : {}),
78
+ });
79
+ }
80
+ function askResult(mode, notePath, concept, frontmatter, violations) {
81
+ return writeResult({
82
+ status: "ask",
83
+ mode,
84
+ notePath,
85
+ concept: concept.concept,
86
+ folder: notePath.split("/")[0] ?? concept.folder,
87
+ fields: contractFields(concept),
88
+ frontmatter,
89
+ missingFields: violations.map((violation) => violation.field),
90
+ violations,
91
+ rejection: rejection("admission", "contract-violation", "Capture does not satisfy the concept contract yet", `provide or correct these fields, then retry: ${violations
92
+ .map((violation) => violation.field)
93
+ .join(", ")}`),
94
+ });
95
+ }
96
+ function writtenResult(mode, notePath, concept, frontmatter, receipt) {
97
+ return writeResult({
98
+ status: "written",
99
+ mode,
100
+ notePath,
101
+ concept: concept?.concept ?? null,
102
+ folder: notePath.split("/")[0] ?? "",
103
+ fields: contractFields(concept),
104
+ frontmatter,
105
+ missingFields: [],
106
+ violations: [],
107
+ ...(receipt ? { receipt } : {}),
108
+ });
35
109
  }
36
110
  export function safeVaultNotePath(vault, notePath) {
37
111
  if (path.isAbsolute(notePath)) {
@@ -55,6 +129,22 @@ export function safeVaultNotePath(vault, notePath) {
55
129
  }
56
130
  return resolved;
57
131
  }
132
+ function trySafeNotePath(vault, notePath) {
133
+ try {
134
+ const fullPath = safeVaultNotePath(vault, notePath);
135
+ return {
136
+ ok: true,
137
+ fullPath,
138
+ normalized: path.relative(vault, fullPath).replace(/\\/g, "/"),
139
+ };
140
+ }
141
+ catch (error) {
142
+ return {
143
+ ok: false,
144
+ reason: error instanceof Error ? error.message : String(error),
145
+ };
146
+ }
147
+ }
58
148
  function safeCaptureFilename(requested, title) {
59
149
  if (!requested) {
60
150
  return {
@@ -72,96 +162,429 @@ function safeCaptureFilename(requested, title) {
72
162
  safe,
73
163
  };
74
164
  }
75
- export function prepareCapture(input) {
76
- const frontmatter = input.frontmatter ?? {};
77
- const requestedConcept = findConcept(input.ontology, input.concept);
78
- const folder = input.folder ?? requestedConcept?.folder ?? defaultInboxFolder(input.ontology);
79
- const folderConcepts = conceptsForFolder(input.ontology, folder);
80
- const resolvedConcept = requestedConcept ?? findConcept(input.ontology, folderConcepts[0]);
165
+ function planCreatePlacement(ontology, conceptName, folderName, filename, frontmatter) {
166
+ const requestedConcept = findConcept(ontology, conceptName);
167
+ const folder = folderName ?? requestedConcept?.folder ?? defaultInboxFolder(ontology);
168
+ const folderConcepts = conceptsForFolder(ontology, folder);
169
+ const resolvedConcept = requestedConcept ?? findConcept(ontology, folderConcepts[0]);
81
170
  const titleValue = frontmatter["title"];
82
171
  const title = typeof titleValue === "string" ? titleValue : "untitled";
83
- const filenamePlan = safeCaptureFilename(input.filename, title);
84
- const filename = filenamePlan.filename;
85
- const notePath = `${folder}/${filename}`;
172
+ const filenamePlan = safeCaptureFilename(filename, title);
86
173
  if (!filenamePlan.safe) {
87
- const inbox = defaultInboxFolder(input.ontology);
174
+ return {
175
+ kind: "inbox",
176
+ notePath: `${defaultInboxFolder(ontology)}/${filenamePlan.filename}`,
177
+ concept: findConcept(ontology, "inbox"),
178
+ folder: defaultInboxFolder(ontology),
179
+ filename: filenamePlan.filename,
180
+ reason: "Requested filename was unsafe; planned a safe inbox capture path.",
181
+ };
182
+ }
183
+ if (!resolvedConcept || !folderConcepts.includes(resolvedConcept.concept)) {
184
+ return {
185
+ kind: "inbox",
186
+ notePath: `${defaultInboxFolder(ontology)}/${filenamePlan.filename}`,
187
+ concept: findConcept(ontology, "inbox"),
188
+ folder: defaultInboxFolder(ontology),
189
+ filename: filenamePlan.filename,
190
+ reason: "No safe folder/concept binding matched the requested capture.",
191
+ };
192
+ }
193
+ return {
194
+ kind: "bound",
195
+ notePath: `${folder}/${filenamePlan.filename}`,
196
+ concept: resolvedConcept,
197
+ folder,
198
+ filename: filenamePlan.filename,
199
+ };
200
+ }
201
+ function formatNote(frontmatter, body) {
202
+ return `---\n${yamlStringify(frontmatter).trimEnd()}\n---\n\n${body.trim()}\n`;
203
+ }
204
+ function hasErrorCode(error, code) {
205
+ return error instanceof Error && "code" in error && error.code === code;
206
+ }
207
+ async function fileExists(fullPath) {
208
+ try {
209
+ await access(fullPath);
210
+ return true;
211
+ }
212
+ catch (error) {
213
+ if (hasErrorCode(error, "ENOENT")) {
214
+ return false;
215
+ }
216
+ throw error;
217
+ }
218
+ }
219
+ async function isDirectory(fullPath) {
220
+ try {
221
+ return (await stat(fullPath)).isDirectory();
222
+ }
223
+ catch (error) {
224
+ if (hasErrorCode(error, "ENOENT") || hasErrorCode(error, "ENOTDIR")) {
225
+ return false;
226
+ }
227
+ throw error;
228
+ }
229
+ }
230
+ export function prepareCapture(input) {
231
+ const frontmatter = input.frontmatter ?? {};
232
+ const planned = planCreatePlacement(input.ontology, input.concept, input.folder, input.filename, frontmatter);
233
+ if (planned.kind === "inbox") {
88
234
  return {
89
235
  action: "route-to-inbox",
90
236
  concept: "inbox",
91
- folder: inbox,
92
- notePath: `${inbox}/${filename}`,
237
+ folder: planned.folder,
238
+ notePath: planned.notePath,
93
239
  missingFields: [],
94
240
  frontmatter,
95
- reason: "Requested filename was unsafe; planned a safe inbox capture path.",
241
+ fields: contractFields(planned.concept),
242
+ violations: [],
243
+ reason: planned.reason,
96
244
  };
97
245
  }
98
- if (!resolvedConcept || !folderConcepts.includes(resolvedConcept.concept)) {
99
- const inbox = defaultInboxFolder(input.ontology);
246
+ const concept = planned.concept;
247
+ if (!concept) {
100
248
  return {
101
249
  action: "route-to-inbox",
102
250
  concept: "inbox",
103
- folder: inbox,
104
- notePath: `${inbox}/${filename}`,
251
+ folder: defaultInboxFolder(input.ontology),
252
+ notePath: planned.notePath,
105
253
  missingFields: [],
106
254
  frontmatter,
255
+ fields: [],
256
+ violations: [],
107
257
  reason: "No safe folder/concept binding matched the requested capture.",
108
258
  };
109
259
  }
110
- const missingFields = requiredMissing(resolvedConcept, frontmatter);
260
+ const contract = evaluateWriteContract(frontmatter, concept, planned.notePath, routingLawStrictFolders(input.ontology));
111
261
  return {
112
- action: missingFields.length > 0 ? "ask-missing-fields" : "ready",
113
- concept: resolvedConcept.concept,
114
- folder,
115
- notePath,
116
- missingFields,
262
+ action: contract.valid ? "ready" : "ask-missing-fields",
263
+ concept: concept.concept,
264
+ folder: planned.folder,
265
+ notePath: planned.notePath,
266
+ missingFields: contract.violations.map((violation) => violation.field),
117
267
  frontmatter,
268
+ fields: contractFields(concept),
269
+ violations: contract.violations,
118
270
  };
119
271
  }
120
- function formatNote(frontmatter, body) {
121
- return `---\n${yamlStringify(frontmatter).trimEnd()}\n---\n\n${body.trim()}\n`;
272
+ /**
273
+ * Admission Rules - target verification.
274
+ *
275
+ * Runs before ANY disk mutation. `cwd` resolution is unverified for the write
276
+ * surface (the process may have booted anywhere). A `global` registry pointer
277
+ * must still point at a real `.oms` ontology; every other source is trusted
278
+ * without an ontology presence check so the bundled-ontology fallback keeps
279
+ * working.
280
+ */
281
+ async function admitTarget(target) {
282
+ if (target.source === "cwd") {
283
+ return rejection("admission", "target-unverified", `Refusing to write: the target vault was inferred from the current directory (${target.vault}), which is not a verified Oh My Second Brain vault`, "run `oms setup` in your Obsidian vault (or set OMS_VAULT / register the vault in ~/.oms/config.yaml), then retry");
284
+ }
285
+ if (target.source === "global") {
286
+ const hasOntology = (await isDirectory(path.join(target.vault, ".oms", "concepts"))) ||
287
+ (await fileExists(path.join(target.vault, ".oms", "taxonomy.yaml")));
288
+ if (!hasOntology) {
289
+ return rejection("admission", "target-invalid", `Refusing to write: the registered global vault target has no .oms ontology: ${target.vault}`, "update ~/.oms/config.yaml to point at your vault, or run `oms setup` in that vault, then retry");
290
+ }
291
+ }
292
+ return undefined;
122
293
  }
123
- function hasErrorCode(error, code) {
124
- return error instanceof Error && "code" in error && error.code === code;
294
+ function targetRejectedResult(mode, input, frontmatter, rejectionPayload) {
295
+ return rejectedResult(mode, input.notePath ?? "", undefined, frontmatter, [], rejectionPayload.message, rejectionPayload);
125
296
  }
126
- export async function commitCapture(input) {
127
- const fullPath = safeVaultNotePath(input.vault, input.notePath);
128
- const normalizedNotePath = path.relative(input.vault, fullPath).replace(/\\/g, "/");
129
- const concept = resolveConcept(input.ontology, normalizedNotePath);
130
- if (!concept) {
131
- throw new Error("Cannot commit capture: notePath does not resolve to a concept binding");
297
+ /**
298
+ * Acceptance Criteria - staged evaluation (pre-persist).
299
+ *
300
+ * Parses the note content that is ABOUT to be written and re-runs the kernel
301
+ * write contract against the RENDERED frontmatter. This catches divergence
302
+ * between the caller-supplied frontmatter and what actually lands on disk
303
+ * (yaml round-trip artifacts such as `NaN` rendering as `.nan` and parsing
304
+ * back as `null`).
305
+ */
306
+ export function evaluateStagedNote(stagedContent, concept, notePath, strictZones) {
307
+ const parsed = parseNote(stagedContent);
308
+ const contract = evaluateWriteContract(parsed.frontmatter, concept, notePath, strictZones);
309
+ return {
310
+ ok: contract.valid,
311
+ violations: contract.violations,
312
+ frontmatter: parsed.frontmatter,
313
+ body: parsed.body,
314
+ };
315
+ }
316
+ function sameViolationSet(left, right) {
317
+ if (left.length !== right.length) {
318
+ return false;
132
319
  }
133
- const validation = validateFrontmatter(input.frontmatter, concept);
134
- if (!validation.valid) {
135
- const fields = validation.violations.map((violation) => violation.field).join(", ");
136
- throw new Error(`Cannot commit capture: frontmatter violates the concept contract (${fields})`);
320
+ const key = (violation) => `${violation.field}\u0000${violation.rule}\u0000${violation.message}`;
321
+ const leftKeys = left.map(key).sort();
322
+ const rightKeys = right.map(key).sort();
323
+ return leftKeys.every((entry, index) => entry === rightKeys[index]);
324
+ }
325
+ /**
326
+ * Acceptance rejection for a staged render that diverges from the (already
327
+ * passing) input evaluation. Callers only reach this when the input-level
328
+ * contract check passed, so the violation is a genuine render divergence.
329
+ */
330
+ function stagedRejectedResult(mode, notePath, concept, frontmatter, violations) {
331
+ const fields = violations.map((violation) => violation.field).join(", ");
332
+ return rejectedResult(mode, notePath, concept, frontmatter, violations, "Cannot write: the rendered note violates the concept contract", rejection("acceptance", "contract-violation", `Staged note render violates the concept contract (${fields})`, `these values do not survive the note render unchanged - correct them, then retry: ${fields}`));
333
+ }
334
+ function stagedBodyMissingResult(mode, notePath, concept, frontmatter) {
335
+ return rejectedResult(mode, notePath, concept, frontmatter, [], "create requires a body", rejection("acceptance", "body-missing", "Staged note render carries no body content", "pass a non-empty `body` for the note, then retry"));
336
+ }
337
+ function postconditionRejectedResult(mode, notePath, fullPath, concept, frontmatter, violations, detail) {
338
+ return rejectedResult(mode, notePath, concept, frontmatter, violations, `Postcondition failed after writing ${notePath}: ${detail}`, rejection("acceptance", "postcondition-failed", `Postcondition failed after writing ${notePath}: ${detail}`, `inspect the persisted file at ${fullPath} (it was NOT removed) and repair or delete it before retrying`));
339
+ }
340
+ function receiptFor(input, mode, notePath, concept) {
341
+ return {
342
+ resolvedVault: input.target.vault,
343
+ resolutionSource: input.target.source,
344
+ notePath,
345
+ mode,
346
+ concept: concept?.concept ?? null,
347
+ postconditionVerified: true,
348
+ };
349
+ }
350
+ function normalizeBody(body) {
351
+ return body.replace(/\s+$/, "");
352
+ }
353
+ /**
354
+ * Acceptance Criteria - postcondition (post-persist).
355
+ *
356
+ * Re-reads the persisted note through the `readBack` seam, re-runs the write
357
+ * contract on what is actually on disk, and asserts the intended body content
358
+ * landed. The persisted file is never auto-deleted on failure.
359
+ */
360
+ async function verifyPostcondition(input, fullPath, notePath, concept, strictZones, expectation) {
361
+ const read = input.readBack ?? ((target) => readFile(target, "utf-8"));
362
+ const persisted = await read(fullPath);
363
+ const parsed = parseNote(persisted);
364
+ const contract = evaluateWriteContract(parsed.frontmatter, concept, notePath, strictZones);
365
+ if (!contract.valid) {
366
+ return {
367
+ ok: false,
368
+ violations: contract.violations,
369
+ detail: `persisted frontmatter violates the concept contract (${contract.violations
370
+ .map((violation) => violation.field)
371
+ .join(", ")})`,
372
+ };
373
+ }
374
+ const bodyOk = expectation.kind === "full"
375
+ ? normalizeBody(parsed.body) === normalizeBody(expectation.body)
376
+ : normalizeBody(persisted).endsWith(expectation.body.trim());
377
+ if (!bodyOk) {
378
+ return { ok: false, violations: [], detail: "persisted body does not match the staged content" };
379
+ }
380
+ return { ok: true, violations: [], detail: "" };
381
+ }
382
+ export async function writeNote(input) {
383
+ const frontmatter = input.frontmatter ?? {};
384
+ const strictZones = routingLawStrictFolders(input.ontology);
385
+ const targetRejection = await admitTarget(input.target);
386
+ if (targetRejection) {
387
+ return targetRejectedResult(input.mode, input, frontmatter, targetRejection);
388
+ }
389
+ if (input.mode === "update") {
390
+ return writeUpdate(input, frontmatter, strictZones);
137
391
  }
138
- await mkdir(path.dirname(fullPath), { recursive: true });
139
392
  if (input.mode === "append") {
140
- try {
141
- await readFile(fullPath, "utf-8");
142
- await appendFile(fullPath, `\n\n${input.body.trim()}\n`, "utf-8");
393
+ return writeAppend(input, frontmatter, strictZones);
394
+ }
395
+ return writeCreate(input, frontmatter, strictZones);
396
+ }
397
+ async function writeCreate(input, frontmatter, strictZones) {
398
+ let notePath;
399
+ let concept;
400
+ if (input.notePath) {
401
+ const safe = trySafeNotePath(input.target.vault, input.notePath);
402
+ if (!safe.ok) {
403
+ return rejectedResult("create", input.notePath, undefined, frontmatter, [], safe.reason, pathUnsafeRejection(safe.reason));
143
404
  }
144
- catch (error) {
145
- if (!hasErrorCode(error, "ENOENT")) {
146
- throw error;
147
- }
148
- await writeFile(fullPath, formatNote(input.frontmatter, input.body), "utf-8");
405
+ notePath = safe.normalized;
406
+ concept = resolveConcept(input.ontology, notePath);
407
+ if (!concept) {
408
+ const filename = path.posix.basename(notePath);
409
+ return inboxResult("create", input.ontology, filename, frontmatter, "No safe folder/concept binding matched the requested capture.");
149
410
  }
150
411
  }
151
412
  else {
152
- try {
153
- await writeFile(fullPath, formatNote(input.frontmatter, input.body), {
154
- encoding: "utf-8",
155
- flag: "wx",
156
- });
413
+ const planned = planCreatePlacement(input.ontology, input.concept, input.folder, input.filename, frontmatter);
414
+ if (planned.kind === "inbox") {
415
+ return inboxResult("create", input.ontology, planned.filename, frontmatter, planned.reason ?? "No safe folder/concept binding matched the requested capture.");
157
416
  }
158
- catch (error) {
159
- if (hasErrorCode(error, "EEXIST")) {
160
- throw new Error("Cannot create capture: target note already exists");
161
- }
162
- throw error;
417
+ notePath = planned.notePath;
418
+ concept = planned.concept;
419
+ }
420
+ if (!concept) {
421
+ return inboxResult("create", input.ontology, path.posix.basename(notePath), frontmatter, "No safe folder/concept binding matched the requested capture.");
422
+ }
423
+ const contract = evaluateWriteContract(frontmatter, concept, notePath, strictZones);
424
+ if (!contract.valid) {
425
+ return askResult("create", notePath, concept, frontmatter, contract.violations);
426
+ }
427
+ if (input.body === undefined) {
428
+ return rejectedResult("create", notePath, concept, frontmatter, [], "create requires a body", rejection("admission", "body-missing", "create requires a body", "pass a non-empty `body` for the note, then retry"));
429
+ }
430
+ const safe = trySafeNotePath(input.target.vault, notePath);
431
+ if (!safe.ok) {
432
+ return rejectedResult("create", notePath, concept, frontmatter, [], safe.reason, pathUnsafeRejection(safe.reason));
433
+ }
434
+ if (await fileExists(safe.fullPath)) {
435
+ return rejectedResult("create", notePath, concept, frontmatter, [], "Cannot create capture: target note already exists", rejection("admission", "note-exists", "Cannot create capture: target note already exists", "use mode `append` or `update` for an existing note, or choose a different filename"));
436
+ }
437
+ // Write Attempt: build the staged note in memory.
438
+ const staged = formatNote(frontmatter, input.body);
439
+ // Evaluation (pre-persist): the staged render is a second gate that only
440
+ // fires on divergence from the input evaluation, which already passed above.
441
+ const stagedEvaluation = evaluateStagedNote(staged, concept, notePath, strictZones);
442
+ if (!stagedEvaluation.ok && !sameViolationSet(stagedEvaluation.violations, contract.violations)) {
443
+ return stagedRejectedResult("create", notePath, concept, frontmatter, stagedEvaluation.violations);
444
+ }
445
+ if (stagedEvaluation.body.trim() === "") {
446
+ return stagedBodyMissingResult("create", notePath, concept, frontmatter);
447
+ }
448
+ if (input.dryRun) {
449
+ return writtenResult("create", notePath, concept, frontmatter);
450
+ }
451
+ // Persist.
452
+ await mkdir(path.dirname(safe.fullPath), { recursive: true });
453
+ await writeFile(safe.fullPath, staged, {
454
+ encoding: "utf-8",
455
+ flag: "wx",
456
+ });
457
+ // Postcondition.
458
+ const postcondition = await verifyPostcondition(input, safe.fullPath, notePath, concept, strictZones, { kind: "full", body: stagedEvaluation.body });
459
+ if (!postcondition.ok) {
460
+ return postconditionRejectedResult("create", notePath, safe.fullPath, concept, frontmatter, postcondition.violations, postcondition.detail);
461
+ }
462
+ return writtenResult("create", notePath, concept, frontmatter, receiptFor(input, "create", notePath, concept));
463
+ }
464
+ async function writeAppend(input, frontmatter, strictZones) {
465
+ if (!input.notePath) {
466
+ return rejectedResult("append", "", undefined, frontmatter, [], "append requires notePath", rejection("admission", "args-invalid", "append requires notePath", "pass the vault-relative `notePath` of the note to append to, then retry"));
467
+ }
468
+ if (input.body === undefined) {
469
+ return rejectedResult("append", input.notePath, undefined, frontmatter, [], "append requires a body", rejection("admission", "body-missing", "append requires a body", "pass a non-empty `body` to append, then retry"));
470
+ }
471
+ const safe = trySafeNotePath(input.target.vault, input.notePath);
472
+ if (!safe.ok) {
473
+ return rejectedResult("append", input.notePath, undefined, frontmatter, [], safe.reason, pathUnsafeRejection(safe.reason));
474
+ }
475
+ const exists = await fileExists(safe.fullPath);
476
+ if (!exists) {
477
+ const created = await writeCreate({
478
+ target: input.target,
479
+ ontology: input.ontology,
480
+ mode: "create",
481
+ dryRun: input.dryRun,
482
+ notePath: safe.normalized,
483
+ frontmatter,
484
+ body: input.body,
485
+ }, frontmatter, strictZones);
486
+ if (created.status !== "written") {
487
+ return created;
163
488
  }
489
+ return writeResult({
490
+ ...created,
491
+ mode: "append",
492
+ ...(created.receipt ? { receipt: { ...created.receipt, mode: "append" } } : {}),
493
+ });
494
+ }
495
+ const raw = await readFile(safe.fullPath, "utf-8");
496
+ const parsed = parseNote(raw);
497
+ const concept = resolveConcept(input.ontology, safe.normalized);
498
+ if (!concept) {
499
+ return rejectedResult("append", safe.normalized, undefined, parsed.frontmatter, [], "Cannot commit capture: notePath does not resolve to a concept binding", conceptUnboundRejection());
500
+ }
501
+ const contract = evaluateWriteContract(parsed.frontmatter, concept, safe.normalized, strictZones);
502
+ if (!contract.valid) {
503
+ return rejectedResult("append", safe.normalized, concept, parsed.frontmatter, contract.violations, "Cannot append: existing frontmatter violates the concept contract");
504
+ }
505
+ // Write Attempt: the staged note is exactly what the file will hold after the
506
+ // append (current content + the appended block).
507
+ const appended = `\n\n${input.body.trim()}\n`;
508
+ const staged = `${raw}${appended}`;
509
+ // Evaluation (pre-persist).
510
+ const stagedEvaluation = evaluateStagedNote(staged, concept, safe.normalized, strictZones);
511
+ if (!stagedEvaluation.ok && !sameViolationSet(stagedEvaluation.violations, contract.violations)) {
512
+ return stagedRejectedResult("append", safe.normalized, concept, parsed.frontmatter, stagedEvaluation.violations);
513
+ }
514
+ if (input.dryRun) {
515
+ return writtenResult("append", safe.normalized, concept, parsed.frontmatter);
516
+ }
517
+ // Persist.
518
+ await appendFile(safe.fullPath, appended, "utf-8");
519
+ // Postcondition.
520
+ const postcondition = await verifyPostcondition(input, safe.fullPath, safe.normalized, concept, strictZones, { kind: "suffix", body: input.body });
521
+ if (!postcondition.ok) {
522
+ return postconditionRejectedResult("append", safe.normalized, safe.fullPath, concept, parsed.frontmatter, postcondition.violations, postcondition.detail);
523
+ }
524
+ return writtenResult("append", safe.normalized, concept, parsed.frontmatter, receiptFor(input, "append", safe.normalized, concept));
525
+ }
526
+ async function writeUpdate(input, frontmatter, strictZones) {
527
+ if (!input.notePath) {
528
+ return rejectedResult("update", "", undefined, frontmatter, [], "update requires notePath", rejection("admission", "args-invalid", "update requires notePath", "pass the vault-relative `notePath` of the note to update, then retry"));
529
+ }
530
+ if (input.frontmatter === undefined && input.body === undefined) {
531
+ return rejectedResult("update", input.notePath, undefined, frontmatter, [], "update requires frontmatter or body", rejection("admission", "args-invalid", "update requires frontmatter or body", "pass `frontmatter`, `body`, or both, then retry"));
532
+ }
533
+ const safe = trySafeNotePath(input.target.vault, input.notePath);
534
+ if (!safe.ok) {
535
+ return rejectedResult("update", input.notePath, undefined, frontmatter, [], safe.reason, pathUnsafeRejection(safe.reason));
536
+ }
537
+ if (!(await fileExists(safe.fullPath))) {
538
+ return rejectedResult("update", safe.normalized, undefined, frontmatter, [], "Cannot update capture: target note does not exist", rejection("admission", "note-missing", "Cannot update capture: target note does not exist", "create the note first (mode `create`) or correct `notePath`, then retry"));
539
+ }
540
+ const raw = await readFile(safe.fullPath, "utf-8");
541
+ const parsed = parseNote(raw);
542
+ const merged = input.frontmatter === undefined ? parsed.frontmatter : mergeFrontmatter(parsed.frontmatter, frontmatter);
543
+ const body = input.body === undefined ? parsed.body : input.body;
544
+ const concept = resolveConcept(input.ontology, safe.normalized);
545
+ if (!concept) {
546
+ return rejectedResult("update", safe.normalized, undefined, merged, [], "Cannot commit capture: notePath does not resolve to a concept binding", conceptUnboundRejection());
547
+ }
548
+ const contract = evaluateWriteContract(merged, concept, safe.normalized, strictZones);
549
+ if (!contract.valid) {
550
+ return rejectedResult("update", safe.normalized, concept, merged, contract.violations, "Cannot update capture: resulting frontmatter violates the concept contract");
551
+ }
552
+ // Write Attempt: build the staged note in memory.
553
+ const staged = formatNote(merged, body);
554
+ // Evaluation (pre-persist).
555
+ const stagedEvaluation = evaluateStagedNote(staged, concept, safe.normalized, strictZones);
556
+ if (!stagedEvaluation.ok && !sameViolationSet(stagedEvaluation.violations, contract.violations)) {
557
+ return stagedRejectedResult("update", safe.normalized, concept, merged, stagedEvaluation.violations);
558
+ }
559
+ if (input.dryRun) {
560
+ return writtenResult("update", safe.normalized, concept, merged);
561
+ }
562
+ // Persist.
563
+ await writeFile(safe.fullPath, staged, "utf-8");
564
+ // Postcondition.
565
+ const postcondition = await verifyPostcondition(input, safe.fullPath, safe.normalized, concept, strictZones, { kind: "full", body: stagedEvaluation.body });
566
+ if (!postcondition.ok) {
567
+ return postconditionRejectedResult("update", safe.normalized, safe.fullPath, concept, merged, postcondition.violations, postcondition.detail);
568
+ }
569
+ return writtenResult("update", safe.normalized, concept, merged, receiptFor(input, "update", safe.normalized, concept));
570
+ }
571
+ export async function commitCapture(input) {
572
+ const result = await writeNote({
573
+ target: { vault: input.vault, source: input.source },
574
+ ontology: input.ontology,
575
+ mode: input.mode,
576
+ dryRun: false,
577
+ notePath: input.notePath,
578
+ frontmatter: input.frontmatter,
579
+ body: input.body,
580
+ });
581
+ if (result.status === "written") {
582
+ return { written: true, mode: input.mode, notePath: result.notePath };
583
+ }
584
+ if (result.violations.length > 0) {
585
+ const fields = result.violations.map((violation) => violation.field).join(", ");
586
+ throw new Error(`Cannot commit capture: frontmatter violates the concept contract (${fields})`);
164
587
  }
165
- return { written: true, mode: input.mode, notePath: normalizedNotePath };
588
+ throw new Error(result.reason ?? "Cannot commit capture");
166
589
  }
167
590
  //# sourceMappingURL=safe.js.map