contentbase 0.2.0 → 0.4.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 (360) hide show
  1. package/CNAME +1 -0
  2. package/LAUNCH-PLAN.md +419 -0
  3. package/dist/adapters/node-fs.d.ts +17 -0
  4. package/dist/adapters/node-fs.d.ts.map +1 -0
  5. package/dist/adapters/node-fs.js +71 -0
  6. package/dist/adapters/node-fs.js.map +1 -0
  7. package/dist/ast-query.d.ts +44 -0
  8. package/dist/ast-query.d.ts.map +1 -0
  9. package/dist/ast-query.js +95 -0
  10. package/dist/ast-query.js.map +1 -0
  11. package/dist/base-model.d.ts +8 -0
  12. package/dist/base-model.d.ts.map +1 -0
  13. package/dist/base-model.js +12 -0
  14. package/dist/base-model.js.map +1 -0
  15. package/dist/cli/commands/action.d.ts +2 -0
  16. package/dist/cli/commands/action.d.ts.map +1 -0
  17. package/dist/cli/commands/action.js +73 -0
  18. package/dist/cli/commands/action.js.map +1 -0
  19. package/dist/cli/commands/api/endpoints/actions.d.ts +13 -0
  20. package/dist/cli/commands/api/endpoints/actions.d.ts.map +1 -0
  21. package/dist/cli/commands/api/endpoints/actions.js +28 -0
  22. package/dist/cli/commands/api/endpoints/actions.js.map +1 -0
  23. package/dist/cli/commands/api/endpoints/doc.d.ts +5 -0
  24. package/dist/cli/commands/api/endpoints/doc.d.ts.map +1 -0
  25. package/dist/cli/commands/api/endpoints/doc.js +186 -0
  26. package/dist/cli/commands/api/endpoints/doc.js.map +1 -0
  27. package/dist/cli/commands/api/endpoints/docs-index.d.ts +8 -0
  28. package/dist/cli/commands/api/endpoints/docs-index.d.ts.map +1 -0
  29. package/dist/cli/commands/api/endpoints/docs-index.js +23 -0
  30. package/dist/cli/commands/api/endpoints/docs-index.js.map +1 -0
  31. package/dist/cli/commands/api/endpoints/document.d.ts +47 -0
  32. package/dist/cli/commands/api/endpoints/document.d.ts.map +1 -0
  33. package/dist/cli/commands/api/endpoints/document.js +146 -0
  34. package/dist/cli/commands/api/endpoints/document.js.map +1 -0
  35. package/dist/cli/commands/api/endpoints/documents.d.ts +35 -0
  36. package/dist/cli/commands/api/endpoints/documents.d.ts.map +1 -0
  37. package/dist/cli/commands/api/endpoints/documents.js +56 -0
  38. package/dist/cli/commands/api/endpoints/documents.js.map +1 -0
  39. package/dist/cli/commands/api/endpoints/index.d.ts +22 -0
  40. package/dist/cli/commands/api/endpoints/index.d.ts.map +1 -0
  41. package/dist/cli/commands/api/endpoints/index.js +38 -0
  42. package/dist/cli/commands/api/endpoints/index.js.map +1 -0
  43. package/dist/cli/commands/api/endpoints/inspect.d.ts +10 -0
  44. package/dist/cli/commands/api/endpoints/inspect.d.ts.map +1 -0
  45. package/dist/cli/commands/api/endpoints/inspect.js +15 -0
  46. package/dist/cli/commands/api/endpoints/inspect.js.map +1 -0
  47. package/dist/cli/commands/api/endpoints/models.d.ts +5 -0
  48. package/dist/cli/commands/api/endpoints/models.d.ts.map +1 -0
  49. package/dist/cli/commands/api/endpoints/models.js +9 -0
  50. package/dist/cli/commands/api/endpoints/models.js.map +1 -0
  51. package/dist/cli/commands/api/endpoints/query.d.ts +40 -0
  52. package/dist/cli/commands/api/endpoints/query.d.ts.map +1 -0
  53. package/dist/cli/commands/api/endpoints/query.js +106 -0
  54. package/dist/cli/commands/api/endpoints/query.js.map +1 -0
  55. package/dist/cli/commands/api/endpoints/root.d.ts +5 -0
  56. package/dist/cli/commands/api/endpoints/root.d.ts.map +1 -0
  57. package/dist/cli/commands/api/endpoints/root.js +7 -0
  58. package/dist/cli/commands/api/endpoints/root.js.map +1 -0
  59. package/dist/cli/commands/api/endpoints/search-reindex.d.ts +10 -0
  60. package/dist/cli/commands/api/endpoints/search-reindex.d.ts.map +1 -0
  61. package/dist/cli/commands/api/endpoints/search-reindex.js +38 -0
  62. package/dist/cli/commands/api/endpoints/search-reindex.js.map +1 -0
  63. package/dist/cli/commands/api/endpoints/search-status.d.ts +7 -0
  64. package/dist/cli/commands/api/endpoints/search-status.d.ts.map +1 -0
  65. package/dist/cli/commands/api/endpoints/search-status.js +33 -0
  66. package/dist/cli/commands/api/endpoints/search-status.js.map +1 -0
  67. package/dist/cli/commands/api/endpoints/search.d.ts +24 -0
  68. package/dist/cli/commands/api/endpoints/search.d.ts.map +1 -0
  69. package/dist/cli/commands/api/endpoints/search.js +61 -0
  70. package/dist/cli/commands/api/endpoints/search.js.map +1 -0
  71. package/dist/cli/commands/api/endpoints/semantic-search.d.ts +28 -0
  72. package/dist/cli/commands/api/endpoints/semantic-search.d.ts.map +1 -0
  73. package/dist/cli/commands/api/endpoints/semantic-search.js +76 -0
  74. package/dist/cli/commands/api/endpoints/semantic-search.js.map +1 -0
  75. package/dist/cli/commands/api/endpoints/text-search.d.ts +17 -0
  76. package/dist/cli/commands/api/endpoints/text-search.d.ts.map +1 -0
  77. package/dist/cli/commands/api/endpoints/text-search.js +55 -0
  78. package/dist/cli/commands/api/endpoints/text-search.js.map +1 -0
  79. package/dist/cli/commands/api/endpoints/validate.d.ts +12 -0
  80. package/dist/cli/commands/api/endpoints/validate.d.ts.map +1 -0
  81. package/dist/cli/commands/api/endpoints/validate.js +27 -0
  82. package/dist/cli/commands/api/endpoints/validate.js.map +1 -0
  83. package/dist/cli/commands/api/helpers.d.ts +8 -0
  84. package/dist/cli/commands/api/helpers.d.ts.map +1 -0
  85. package/dist/cli/commands/api/helpers.js +89 -0
  86. package/dist/cli/commands/api/helpers.js.map +1 -0
  87. package/dist/cli/commands/console.d.ts +2 -0
  88. package/dist/cli/commands/console.d.ts.map +1 -0
  89. package/dist/cli/commands/console.js +117 -0
  90. package/dist/cli/commands/console.js.map +1 -0
  91. package/dist/cli/commands/create.d.ts +2 -0
  92. package/dist/cli/commands/create.d.ts.map +1 -0
  93. package/dist/cli/commands/create.js +185 -0
  94. package/dist/cli/commands/create.js.map +1 -0
  95. package/dist/cli/commands/embed.d.ts +2 -0
  96. package/dist/cli/commands/embed.d.ts.map +1 -0
  97. package/dist/cli/commands/embed.js +231 -0
  98. package/dist/cli/commands/embed.js.map +1 -0
  99. package/dist/cli/commands/export.d.ts +2 -0
  100. package/dist/cli/commands/export.d.ts.map +1 -0
  101. package/dist/cli/commands/export.js +55 -0
  102. package/dist/cli/commands/export.js.map +1 -0
  103. package/dist/cli/commands/extract.d.ts +2 -0
  104. package/dist/cli/commands/extract.d.ts.map +1 -0
  105. package/dist/cli/commands/extract.js +149 -0
  106. package/dist/cli/commands/extract.js.map +1 -0
  107. package/dist/cli/commands/help.d.ts +2 -0
  108. package/dist/cli/commands/help.d.ts.map +1 -0
  109. package/dist/cli/commands/help.js +58 -0
  110. package/dist/cli/commands/help.js.map +1 -0
  111. package/dist/cli/commands/index.d.ts +18 -0
  112. package/dist/cli/commands/index.d.ts.map +1 -0
  113. package/dist/cli/commands/index.js +19 -0
  114. package/dist/cli/commands/index.js.map +1 -0
  115. package/dist/cli/commands/init.d.ts +2 -0
  116. package/dist/cli/commands/init.d.ts.map +1 -0
  117. package/dist/cli/commands/init.js +82 -0
  118. package/dist/cli/commands/init.js.map +1 -0
  119. package/dist/cli/commands/inspect.d.ts +2 -0
  120. package/dist/cli/commands/inspect.d.ts.map +1 -0
  121. package/dist/cli/commands/inspect.js +53 -0
  122. package/dist/cli/commands/inspect.js.map +1 -0
  123. package/dist/cli/commands/mcp.d.ts +2 -0
  124. package/dist/cli/commands/mcp.d.ts.map +1 -0
  125. package/dist/cli/commands/mcp.js +92 -0
  126. package/dist/cli/commands/mcp.js.map +1 -0
  127. package/dist/cli/commands/search.d.ts +2 -0
  128. package/dist/cli/commands/search.d.ts.map +1 -0
  129. package/dist/cli/commands/search.js +212 -0
  130. package/dist/cli/commands/search.js.map +1 -0
  131. package/dist/cli/commands/serve.d.ts +2 -0
  132. package/dist/cli/commands/serve.d.ts.map +1 -0
  133. package/dist/cli/commands/serve.js +298 -0
  134. package/dist/cli/commands/serve.js.map +1 -0
  135. package/dist/cli/commands/summary.d.ts +2 -0
  136. package/dist/cli/commands/summary.d.ts.map +1 -0
  137. package/dist/cli/commands/summary.js +58 -0
  138. package/dist/cli/commands/summary.js.map +1 -0
  139. package/dist/cli/commands/teach.d.ts +2 -0
  140. package/dist/cli/commands/teach.d.ts.map +1 -0
  141. package/dist/cli/commands/teach.js +80 -0
  142. package/dist/cli/commands/teach.js.map +1 -0
  143. package/dist/cli/commands/text-search.d.ts +2 -0
  144. package/dist/cli/commands/text-search.d.ts.map +1 -0
  145. package/dist/cli/commands/text-search.js +125 -0
  146. package/dist/cli/commands/text-search.js.map +1 -0
  147. package/dist/cli/commands/update.d.ts +2 -0
  148. package/dist/cli/commands/update.d.ts.map +1 -0
  149. package/dist/cli/commands/update.js +167 -0
  150. package/dist/cli/commands/update.js.map +1 -0
  151. package/dist/cli/commands/validate.d.ts +2 -0
  152. package/dist/cli/commands/validate.d.ts.map +1 -0
  153. package/dist/cli/commands/validate.js +129 -0
  154. package/dist/cli/commands/validate.js.map +1 -0
  155. package/dist/cli/index.d.ts +3 -0
  156. package/dist/cli/index.d.ts.map +1 -0
  157. package/dist/cli/index.js +84 -0
  158. package/dist/cli/index.js.map +1 -0
  159. package/dist/cli/load-collection.d.ts +7 -0
  160. package/dist/cli/load-collection.d.ts.map +1 -0
  161. package/dist/cli/load-collection.js +201 -0
  162. package/dist/cli/load-collection.js.map +1 -0
  163. package/dist/cli/registry.d.ts +17 -0
  164. package/dist/cli/registry.d.ts.map +1 -0
  165. package/dist/cli/registry.js +16 -0
  166. package/dist/cli/registry.js.map +1 -0
  167. package/dist/cnotes +0 -0
  168. package/dist/collection.d.ts +145 -0
  169. package/dist/collection.d.ts.map +1 -0
  170. package/dist/collection.js +684 -0
  171. package/dist/collection.js.map +1 -0
  172. package/dist/define-model.d.ts +45 -0
  173. package/dist/define-model.d.ts.map +1 -0
  174. package/dist/define-model.js +106 -0
  175. package/dist/define-model.js.map +1 -0
  176. package/dist/document.d.ts +119 -0
  177. package/dist/document.d.ts.map +1 -0
  178. package/dist/document.js +418 -0
  179. package/dist/document.js.map +1 -0
  180. package/dist/extract-sections.d.ts +46 -0
  181. package/dist/extract-sections.d.ts.map +1 -0
  182. package/dist/extract-sections.js +153 -0
  183. package/dist/extract-sections.js.map +1 -0
  184. package/dist/index.d.ts +28 -0
  185. package/dist/index.d.ts.map +1 -0
  186. package/dist/index.js +31 -0
  187. package/dist/index.js.map +1 -0
  188. package/dist/mcp/helpers.d.ts +14 -0
  189. package/dist/mcp/helpers.d.ts.map +1 -0
  190. package/dist/mcp/helpers.js +7 -0
  191. package/dist/mcp/helpers.js.map +1 -0
  192. package/dist/mcp/model-info.d.ts +2 -0
  193. package/dist/mcp/model-info.d.ts.map +1 -0
  194. package/dist/mcp/model-info.js +94 -0
  195. package/dist/mcp/model-info.js.map +1 -0
  196. package/dist/mcp/prompts.d.ts +2 -0
  197. package/dist/mcp/prompts.d.ts.map +1 -0
  198. package/dist/mcp/prompts.js +180 -0
  199. package/dist/mcp/prompts.js.map +1 -0
  200. package/dist/mcp/readme.d.ts +2 -0
  201. package/dist/mcp/readme.d.ts.map +1 -0
  202. package/dist/mcp/readme.js +71 -0
  203. package/dist/mcp/readme.js.map +1 -0
  204. package/dist/mcp/resources.d.ts +2 -0
  205. package/dist/mcp/resources.d.ts.map +1 -0
  206. package/dist/mcp/resources.js +85 -0
  207. package/dist/mcp/resources.js.map +1 -0
  208. package/dist/mcp/server.d.ts +18 -0
  209. package/dist/mcp/server.d.ts.map +1 -0
  210. package/dist/mcp/server.js +83 -0
  211. package/dist/mcp/server.js.map +1 -0
  212. package/dist/mcp/tools/mutation.d.ts +2 -0
  213. package/dist/mcp/tools/mutation.d.ts.map +1 -0
  214. package/dist/mcp/tools/mutation.js +155 -0
  215. package/dist/mcp/tools/mutation.js.map +1 -0
  216. package/dist/mcp/tools/query.d.ts +2 -0
  217. package/dist/mcp/tools/query.d.ts.map +1 -0
  218. package/dist/mcp/tools/query.js +130 -0
  219. package/dist/mcp/tools/query.js.map +1 -0
  220. package/dist/mcp/tools/search.d.ts +2 -0
  221. package/dist/mcp/tools/search.d.ts.map +1 -0
  222. package/dist/mcp/tools/search.js +168 -0
  223. package/dist/mcp/tools/search.js.map +1 -0
  224. package/dist/model-instance.d.ts +11 -0
  225. package/dist/model-instance.d.ts.map +1 -0
  226. package/dist/model-instance.js +208 -0
  227. package/dist/model-instance.js.map +1 -0
  228. package/dist/node-shortcuts.d.ts +25 -0
  229. package/dist/node-shortcuts.d.ts.map +1 -0
  230. package/dist/node-shortcuts.js +67 -0
  231. package/dist/node-shortcuts.js.map +1 -0
  232. package/dist/parse.d.ts +45 -0
  233. package/dist/parse.d.ts.map +1 -0
  234. package/dist/parse.js +98 -0
  235. package/dist/parse.js.map +1 -0
  236. package/dist/query/collection-query.d.ts +39 -0
  237. package/dist/query/collection-query.d.ts.map +1 -0
  238. package/dist/query/collection-query.js +184 -0
  239. package/dist/query/collection-query.js.map +1 -0
  240. package/dist/query/index.d.ts +8 -0
  241. package/dist/query/index.d.ts.map +1 -0
  242. package/dist/query/index.js +5 -0
  243. package/dist/query/index.js.map +1 -0
  244. package/dist/query/operators.d.ts +3 -0
  245. package/dist/query/operators.d.ts.map +1 -0
  246. package/dist/query/operators.js +18 -0
  247. package/dist/query/operators.js.map +1 -0
  248. package/dist/query/query-builder.d.ts +34 -0
  249. package/dist/query/query-builder.d.ts.map +1 -0
  250. package/dist/query/query-builder.js +84 -0
  251. package/dist/query/query-builder.js.map +1 -0
  252. package/dist/query/query-dsl.d.ts +36 -0
  253. package/dist/query/query-dsl.d.ts.map +1 -0
  254. package/dist/query/query-dsl.js +195 -0
  255. package/dist/query/query-dsl.js.map +1 -0
  256. package/dist/relationships/belongs-to.d.ts +9 -0
  257. package/dist/relationships/belongs-to.d.ts.map +1 -0
  258. package/dist/relationships/belongs-to.js +34 -0
  259. package/dist/relationships/belongs-to.js.map +1 -0
  260. package/dist/relationships/has-many.d.ts +23 -0
  261. package/dist/relationships/has-many.d.ts.map +1 -0
  262. package/dist/relationships/has-many.js +136 -0
  263. package/dist/relationships/has-many.js.map +1 -0
  264. package/dist/relationships/index.d.ts +26 -0
  265. package/dist/relationships/index.d.ts.map +1 -0
  266. package/dist/relationships/index.js +32 -0
  267. package/dist/relationships/index.js.map +1 -0
  268. package/dist/relationships/types.d.ts +2 -0
  269. package/dist/relationships/types.d.ts.map +1 -0
  270. package/dist/relationships/types.js +2 -0
  271. package/dist/relationships/types.js.map +1 -0
  272. package/dist/search/document-inputs.d.ts +30 -0
  273. package/dist/search/document-inputs.d.ts.map +1 -0
  274. package/dist/search/document-inputs.js +52 -0
  275. package/dist/search/document-inputs.js.map +1 -0
  276. package/dist/search/luca-semantic-search.d.ts +27 -0
  277. package/dist/search/luca-semantic-search.d.ts.map +1 -0
  278. package/dist/search/luca-semantic-search.js +56 -0
  279. package/dist/search/luca-semantic-search.js.map +1 -0
  280. package/dist/section.d.ts +21 -0
  281. package/dist/section.d.ts.map +1 -0
  282. package/dist/section.js +21 -0
  283. package/dist/section.js.map +1 -0
  284. package/dist/types.d.ts +185 -0
  285. package/dist/types.d.ts.map +1 -0
  286. package/dist/types.js +2 -0
  287. package/dist/types.js.map +1 -0
  288. package/dist/utils/index.d.ts +9 -0
  289. package/dist/utils/index.d.ts.map +1 -0
  290. package/dist/utils/index.js +8 -0
  291. package/dist/utils/index.js.map +1 -0
  292. package/dist/utils/inflect.d.ts +6 -0
  293. package/dist/utils/inflect.d.ts.map +1 -0
  294. package/dist/utils/inflect.js +75 -0
  295. package/dist/utils/inflect.js.map +1 -0
  296. package/dist/utils/match-pattern.d.ts +25 -0
  297. package/dist/utils/match-pattern.d.ts.map +1 -0
  298. package/dist/utils/match-pattern.js +52 -0
  299. package/dist/utils/match-pattern.js.map +1 -0
  300. package/dist/utils/normalize-headings.d.ts +8 -0
  301. package/dist/utils/normalize-headings.d.ts.map +1 -0
  302. package/dist/utils/normalize-headings.js +19 -0
  303. package/dist/utils/normalize-headings.js.map +1 -0
  304. package/dist/utils/parse-table.d.ts +7 -0
  305. package/dist/utils/parse-table.d.ts.map +1 -0
  306. package/dist/utils/parse-table.js +17 -0
  307. package/dist/utils/parse-table.js.map +1 -0
  308. package/dist/utils/read-directory.d.ts +5 -0
  309. package/dist/utils/read-directory.d.ts.map +1 -0
  310. package/dist/utils/read-directory.js +29 -0
  311. package/dist/utils/read-directory.js.map +1 -0
  312. package/dist/utils/stringify-ast.d.ts +6 -0
  313. package/dist/utils/stringify-ast.d.ts.map +1 -0
  314. package/dist/utils/stringify-ast.js +8 -0
  315. package/dist/utils/stringify-ast.js.map +1 -0
  316. package/dist/utils/strip-markdown.d.ts +11 -0
  317. package/dist/utils/strip-markdown.d.ts.map +1 -0
  318. package/dist/utils/strip-markdown.js +48 -0
  319. package/dist/utils/strip-markdown.js.map +1 -0
  320. package/dist/validator.d.ts +10 -0
  321. package/dist/validator.d.ts.map +1 -0
  322. package/dist/validator.js +55 -0
  323. package/dist/validator.js.map +1 -0
  324. package/index.html +1636 -0
  325. package/package.json +5 -4
  326. package/src/adapters/node-fs.ts +80 -0
  327. package/src/cli/commands/api/endpoints/doc.ts +2 -2
  328. package/src/cli/commands/api/endpoints/document.ts +2 -2
  329. package/src/cli/commands/api/endpoints/search-reindex.ts +6 -25
  330. package/src/cli/commands/api/endpoints/search-status.ts +3 -20
  331. package/src/cli/commands/api/endpoints/search.ts +3 -33
  332. package/src/cli/commands/api/endpoints/semantic-search.ts +3 -33
  333. package/src/cli/commands/embed.ts +8 -81
  334. package/src/cli/commands/mcp.ts +5 -1128
  335. package/src/cli/commands/search.ts +4 -76
  336. package/src/cli/commands/serve.ts +7 -32
  337. package/src/cli/index.ts +1 -1
  338. package/src/cli/load-collection.ts +2 -2
  339. package/src/collection.ts +31 -30
  340. package/src/document.ts +18 -0
  341. package/src/extract-sections.ts +3 -0
  342. package/src/index.ts +10 -0
  343. package/src/mcp/helpers.ts +7 -0
  344. package/src/mcp/model-info.ts +102 -0
  345. package/src/mcp/prompts.ts +199 -0
  346. package/src/mcp/readme.ts +153 -0
  347. package/src/mcp/resources.ts +89 -0
  348. package/src/mcp/server.ts +103 -0
  349. package/src/mcp/tools/mutation.ts +176 -0
  350. package/src/mcp/tools/query.ts +141 -0
  351. package/src/mcp/tools/search.ts +180 -0
  352. package/src/parse.ts +5 -0
  353. package/src/query/query-dsl.ts +1 -1
  354. package/src/relationships/has-many.ts +2 -2
  355. package/src/search/document-inputs.ts +85 -0
  356. package/src/search/luca-semantic-search.ts +75 -0
  357. package/src/types.ts +54 -0
  358. package/src/utils/index.ts +2 -0
  359. package/src/utils/match-pattern.ts +2 -2
  360. package/src/utils/strip-markdown.ts +59 -0
@@ -1,8 +1,9 @@
1
1
  import { z } from 'zod'
2
- import { existsSync, readdirSync } from 'node:fs'
3
2
  import path from 'node:path'
4
3
  import { commands } from '../registry.js'
5
4
  import { loadCollection } from '../load-collection.js'
5
+ import { collectDocumentInputs } from '../../search/document-inputs.js'
6
+ import { getInitializedSemanticSearch, hasSearchIndex } from '../../search/luca-semantic-search.js'
6
7
 
7
8
  const argsSchema = z.object({
8
9
  mode: z.enum(['hybrid', 'keyword', 'vector']).default('hybrid'),
@@ -15,31 +16,8 @@ const argsSchema = z.object({
15
16
  contentFolder: z.string().optional(),
16
17
  })
17
18
 
18
- function hasSearchIndex(rootPath: string): boolean {
19
- const dbDir = path.join(rootPath, '.contentbase')
20
- if (!existsSync(dbDir)) return false
21
- try {
22
- const files = readdirSync(dbDir) as string[]
23
- return files.some((f: string) => f.startsWith('search.') && f.endsWith('.sqlite'))
24
- } catch {
25
- return false
26
- }
27
- }
28
-
29
- async function getSemanticSearch(container: any, rootPath: string) {
30
- const { SemanticSearch } = await import('@soederpop/luca/agi')
31
- if (!container.features.available.includes('semanticSearch')) {
32
- SemanticSearch.attach(container)
33
- }
34
-
35
- const dbPath = path.join(rootPath, '.contentbase/search.sqlite')
36
- const ss = container.feature('semanticSearch', { dbPath })
37
- await ss.initDb()
38
- return ss
39
- }
40
-
41
19
  async function buildIndex(container: any, collection: any) {
42
- const ss = await getSemanticSearch(container, collection.rootPath)
20
+ const ss = await getInitializedSemanticSearch(container, collection.rootPath)
43
21
  const docs = collectDocumentInputs(collection)
44
22
 
45
23
  const toIndex = docs.filter((doc: any) => ss.needsReindex(doc))
@@ -61,56 +39,6 @@ async function buildIndex(container: any, collection: any) {
61
39
  return ss
62
40
  }
63
41
 
64
- function collectDocumentInputs(collection: any) {
65
- const inputs: any[] = []
66
- for (const pathId of collection.available) {
67
- const doc = collection.document(pathId)
68
- const modelDef = collection.findModelDefinition(pathId)
69
-
70
- const sections: any[] = []
71
- const lines = (doc.content as string).split('\n')
72
- let currentHeading: string | null = null
73
- let currentContent: string[] = []
74
-
75
- for (const line of lines) {
76
- const h2Match = line.match(/^## (.+)/)
77
- if (h2Match) {
78
- if (currentHeading) {
79
- sections.push({
80
- heading: currentHeading,
81
- headingPath: currentHeading,
82
- content: currentContent.join('\n').trim(),
83
- level: 2,
84
- })
85
- }
86
- currentHeading = h2Match[1].trim()
87
- currentContent = []
88
- } else if (currentHeading) {
89
- currentContent.push(line)
90
- }
91
- }
92
- if (currentHeading) {
93
- sections.push({
94
- heading: currentHeading,
95
- headingPath: currentHeading,
96
- content: currentContent.join('\n').trim(),
97
- level: 2,
98
- })
99
- }
100
-
101
- inputs.push({
102
- pathId,
103
- model: modelDef?.name ?? undefined,
104
- title: doc.title,
105
- slug: doc.slug,
106
- meta: doc.meta,
107
- content: doc.content,
108
- sections: sections.length > 0 ? sections : undefined,
109
- })
110
- }
111
- return inputs
112
- }
113
-
114
42
  async function handler(options: z.infer<typeof argsSchema>, { container }: { container: any }) {
115
43
  const ui = container.feature('ui')
116
44
  const query = container.argv._[1] as string | undefined
@@ -140,7 +68,7 @@ async function handler(options: z.infer<typeof argsSchema>, { container }: { con
140
68
  if (options.bootstrap && !hasSearchIndex(collection.rootPath)) {
141
69
  ss = await buildIndex(container, collection)
142
70
  } else {
143
- ss = await getSemanticSearch(container, collection.rootPath)
71
+ ss = await getInitializedSemanticSearch(container, collection.rootPath)
144
72
  }
145
73
 
146
74
  // Parse where clause: "key=value,key2=value2"
@@ -5,6 +5,8 @@ import { commands } from '../registry.js'
5
5
  import { loadCollection } from '../load-collection.js'
6
6
  import { buildSchemaJSON } from './api/helpers.js'
7
7
  import { builtinEndpoints } from './api/endpoints/index.js'
8
+ import { collectDocumentInputs } from '../../search/document-inputs.js'
9
+ import { getInitializedSemanticSearch, hasSearchIndex } from '../../search/luca-semantic-search.js'
8
10
 
9
11
  const argsSchema = z.object({
10
12
  port: z.number().default(8000),
@@ -184,39 +186,12 @@ async function handler(options: z.infer<typeof argsSchema>, context: { container
184
186
  // Search index auto-update
185
187
  // ---------------------------------------------------------------------------
186
188
  if (options.search) {
187
- const { existsSync, readdirSync } = await import('node:fs')
188
- const dbDir = path.join(collection.rootPath, '.contentbase')
189
- const hasIndex = existsSync(dbDir) && (() => {
189
+ if (hasSearchIndex(collection.rootPath)) {
190
190
  try {
191
- return (readdirSync(dbDir) as string[]).some((f: string) => f.startsWith('search.') && f.endsWith('.sqlite'))
192
- } catch { return false }
193
- })()
194
-
195
- if (hasIndex) {
196
- try {
197
- const { SemanticSearch } = await import('@soederpop/luca/agi')
198
- if (!container.features.available.includes('semanticSearch')) {
199
- SemanticSearch.attach(container)
200
- }
201
- const dbPath = path.join(collection.rootPath, '.contentbase/search.sqlite')
202
- const ss = container.feature('semanticSearch', { dbPath }) as any
203
- await ss.initDb()
204
-
205
- // Collect document inputs
206
- const docs: any[] = []
207
- for (const pathId of collection.available) {
208
- const doc = collection.document(pathId) as any
209
- docs.push({
210
- pathId,
211
- model: collection.findModelDefinition(pathId)?.name ?? undefined,
212
- title: doc.title,
213
- meta: doc.meta,
214
- content: doc.content,
215
- })
216
- }
217
-
218
- const stale = docs.filter((d: any) => ss.needsReindex(d))
219
- ss.removeStale(docs.map((d: any) => d.pathId))
191
+ const ss = await getInitializedSemanticSearch(container, collection.rootPath)
192
+ const docs = collectDocumentInputs(collection)
193
+ const stale = docs.filter((d) => ss.needsReindex(d))
194
+ ss.removeStale(docs.map((d) => d.pathId))
220
195
 
221
196
  if (stale.length > 0) {
222
197
  console.log(`[search] Updating ${stale.length} stale document(s)...`)
package/src/cli/index.ts CHANGED
@@ -12,7 +12,7 @@ async function renderMarkdown(container: any, text: string) {
12
12
 
13
13
  async function main() {
14
14
  // Dynamic import so the library stays luca-free; only the CLI pulls it in
15
- const luca = await import('@soederpop/luca/node')
15
+ const luca = await import('luca/node')
16
16
  const container = luca.default
17
17
 
18
18
  const commandName = container.argv._[0] as string | undefined
@@ -138,10 +138,10 @@ export async function loadCollection(options: {
138
138
  const cwd = process.cwd();
139
139
 
140
140
  // If no container was passed, try to grab the luca singleton.
141
- // This works when running inside the cnotes CLI (which imports @soederpop/luca/node).
141
+ // This works when running inside the cnotes CLI (which imports luca/node).
142
142
  if (!container) {
143
143
  try {
144
- const luca = await import('@soederpop/luca/node');
144
+ const luca = await import('luca/node');
145
145
  container = luca.default;
146
146
  } catch {
147
147
  // Not running in a luca context — that's fine, native imports will be used
package/src/collection.ts CHANGED
@@ -1,13 +1,12 @@
1
- import fs from "fs/promises";
2
1
  import path from "path";
3
2
  import matter from "gray-matter";
4
3
  import picomatch from "picomatch";
5
4
  import { Document } from "./document";
6
5
  import { CollectionQuery } from "./query/collection-query";
7
6
  import { createModelInstance } from "./model-instance";
8
- import { readDirectory } from "./utils/read-directory";
9
7
  import { pluralize } from "./utils/inflect";
10
8
  import { Base } from "./base-model";
9
+ import { NodeStorageAdapter } from "./adapters/node-fs";
11
10
  import type {
12
11
  ModelDefinition,
13
12
  CollectionItem,
@@ -15,6 +14,7 @@ import type {
15
14
  InferModelInstance,
16
15
  HasManyDefinition,
17
16
  RelationshipDefinition,
17
+ StorageAdapter,
18
18
  } from "./types";
19
19
 
20
20
  // ─── Zod schema introspection ───
@@ -110,6 +110,7 @@ export class Collection {
110
110
  readonly name: string;
111
111
  readonly extensions: string[];
112
112
 
113
+ #adapter: StorageAdapter;
113
114
  #items: Map<string, CollectionItem> = new Map();
114
115
  #documents: Map<string, Document> = new Map();
115
116
  #models: Map<string, ModelDefinition<any, any, any, any, any>> = new Map();
@@ -120,7 +121,13 @@ export class Collection {
120
121
  #moduleLoader?: (filePath: string) => Record<string, any> | Promise<Record<string, any>>;
121
122
 
122
123
  constructor(options: CollectionOptions) {
123
- this.rootPath = path.resolve(options.rootPath);
124
+ this.#adapter = options.adapter ?? new NodeStorageAdapter();
125
+ // Resolve to an absolute path for local FS (NodeStorageAdapter default).
126
+ // When a custom adapter is provided, rootPath is the backend-specific root
127
+ // (e.g. a bucket prefix for R2) and is stored as-is.
128
+ this.rootPath = options.adapter
129
+ ? options.rootPath
130
+ : path.resolve(options.rootPath);
124
131
  this.name = options.name ?? options.rootPath;
125
132
  this.extensions = options.extensions ?? ["mdx", "md"];
126
133
  this.#autoDiscover = options.autoDiscover ?? true;
@@ -252,27 +259,26 @@ export class Collection {
252
259
  `\\.(${this.extensions.join("|")})$`,
253
260
  "i"
254
261
  );
255
- const paths = await readDirectory(this.rootPath, extensionPattern);
262
+ const entries = await this.#adapter.listFiles(this.rootPath, extensionPattern);
256
263
 
257
264
  await Promise.all(
258
- paths.map(async (filePath) => {
259
- const pathId = this.getPathId(filePath);
265
+ entries.map(async ({ key, stat }) => {
266
+ const pathId = this.getPathId(key);
260
267
 
261
268
  // Globally exclude templates directory — templates are only used
262
269
  // for scaffolding and should never appear in queries or listings
263
270
  if (pathId.startsWith("templates/") || pathId === "templates") return;
264
271
 
265
- const raw = await fs.readFile(filePath, "utf8");
266
- const stat = await fs.stat(filePath);
272
+ const raw = await this.#adapter.readFile(key);
267
273
  const { data, content } = matter(raw);
268
274
 
269
275
  this.#items.set(pathId, {
270
276
  raw,
271
277
  content,
272
278
  meta: data,
273
- path: filePath,
274
- createdAt: stat.ctime,
275
- updatedAt: stat.mtime,
279
+ path: key,
280
+ createdAt: stat.createdAt,
281
+ updatedAt: stat.updatedAt,
276
282
  size: stat.size,
277
283
  });
278
284
  })
@@ -448,8 +454,7 @@ export class Collection {
448
454
  const item = this.#items.get(pathId)!;
449
455
  const filePath = item.path;
450
456
 
451
- await fs.mkdir(path.parse(filePath).dir, { recursive: true });
452
- await fs.writeFile(filePath, options.content, "utf8");
457
+ await this.#adapter.writeFile(filePath, options.content);
453
458
 
454
459
  // Update the stored item
455
460
  item.raw = options.content;
@@ -468,11 +473,7 @@ export class Collection {
468
473
  const item = this.#items.get(pathId);
469
474
  if (!item) return this;
470
475
 
471
- try {
472
- await fs.rm(item.path);
473
- } catch {
474
- // File might not exist
475
- }
476
+ await this.#adapter.deleteFile(item.path);
476
477
  this.#items.delete(pathId);
477
478
  this.#documents.delete(pathId);
478
479
  return this;
@@ -490,8 +491,8 @@ export class Collection {
490
491
  filePath = this.resolve(`${pathId}.${extension}`);
491
492
  }
492
493
 
493
- const raw = await fs.readFile(filePath, "utf8");
494
- const stat = await fs.stat(filePath);
494
+ const raw = await this.#adapter.readFile(filePath);
495
+ const stat = await this.#adapter.stat(filePath);
495
496
  const { data, content } = matter(raw);
496
497
 
497
498
  const item: CollectionItem = {
@@ -499,8 +500,8 @@ export class Collection {
499
500
  content,
500
501
  meta: data,
501
502
  path: filePath,
502
- createdAt: stat.ctime,
503
- updatedAt: stat.mtime,
503
+ createdAt: stat.createdAt,
504
+ updatedAt: stat.updatedAt,
504
505
  size: stat.size,
505
506
  };
506
507
 
@@ -550,11 +551,11 @@ export class Collection {
550
551
  // ─── Utilities ───
551
552
 
552
553
  resolve(...args: string[]): string {
553
- return path.resolve(this.rootPath, ...args);
554
+ return this.#adapter.join(this.rootPath, ...args);
554
555
  }
555
556
 
556
- getPathId(absolutePath: string): string {
557
- const relativePath = path.relative(this.rootPath, absolutePath);
557
+ getPathId(key: string): string {
558
+ const relativePath = this.#adapter.relative(this.rootPath, key);
558
559
  return relativePath.replace(/\.[a-z]+$/i, "");
559
560
  }
560
561
 
@@ -677,7 +678,7 @@ export class Collection {
677
678
 
678
679
  for (const pathId of sorted) {
679
680
  const item = this.#items.get(pathId)!;
680
- const ext = path.extname(item.path);
681
+ const ext = this.#adapter.extname(item.path);
681
682
  const fullPath = `${pathId}${ext}`;
682
683
  const parts = fullPath.split("/");
683
684
 
@@ -711,7 +712,7 @@ export class Collection {
711
712
 
712
713
  #tocEntry(pathId: string, basePath: string): string {
713
714
  const item = this.#items.get(pathId)!;
714
- const ext = path.extname(item.path);
715
+ const ext = this.#adapter.extname(item.path);
715
716
  const relativePath = `${basePath}/${pathId}${ext}`;
716
717
  const doc = this.document(pathId);
717
718
  return `- [${doc.title}](${relativePath})`;
@@ -782,12 +783,12 @@ export class Collection {
782
783
  */
783
784
  async saveModelSummary(options: { includeIds?: boolean } = {}): Promise<string> {
784
785
  const summary = this.generateModelSummary(options);
785
- const modelsPath = path.join(this.rootPath, "README.md");
786
+ const modelsPath = this.#adapter.join(this.rootPath, "README.md");
786
787
 
787
788
  // Preserve existing Overview section content
788
789
  let overview = "";
789
790
  try {
790
- const existing = await fs.readFile(modelsPath, "utf8");
791
+ const existing = await this.#adapter.readFile(modelsPath);
791
792
  const overviewStart = existing.indexOf("## Overview");
792
793
  if (overviewStart !== -1) {
793
794
  const contentStart = existing.indexOf("\n", overviewStart) + 1;
@@ -815,7 +816,7 @@ export class Collection {
815
816
  ];
816
817
 
817
818
  const markdown = lines.join("\n");
818
- await fs.writeFile(modelsPath, markdown, "utf8");
819
+ await this.#adapter.writeFile(modelsPath, markdown);
819
820
  return summary;
820
821
  }
821
822
 
package/src/document.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  import { unified } from "unified";
2
2
  import remarkParse from "remark-parse";
3
3
  import remarkGfm from "remark-gfm";
4
+ import remarkRehype from "remark-rehype";
5
+ import rehypeStringify from "rehype-stringify";
4
6
  import yaml from "js-yaml";
5
7
  import { toString } from "mdast-util-to-string";
6
8
  import { kebabCase } from "./utils/inflect";
@@ -9,6 +11,8 @@ import { NodeShortcuts } from "./node-shortcuts";
9
11
  import { stringifyAst } from "./utils/stringify-ast";
10
12
  import { normalizeHeadings } from "./utils/normalize-headings";
11
13
  import { parseTable } from "./utils/parse-table";
14
+ import { stripMarkdown } from "./utils/strip-markdown";
15
+ import type { StripMarkdownOptions } from "./utils/strip-markdown";
12
16
  import type { Root, Content, RootContent, Heading } from "mdast";
13
17
  import type { Collection } from "./collection";
14
18
 
@@ -443,6 +447,10 @@ export class Document {
443
447
  };
444
448
  }
445
449
 
450
+ stripMarkdown(options?: StripMarkdownOptions): string {
451
+ return stripMarkdown(this.ast, options);
452
+ }
453
+
446
454
  toText(
447
455
  filterFn: (node: Content) => boolean = () => true
448
456
  ): string {
@@ -457,6 +465,16 @@ export class Document {
457
465
  * Each heading is indented based on its depth relative to the
458
466
  * minimum heading depth found in the document.
459
467
  */
468
+ async toHtml(): Promise<string> {
469
+ const result = await unified()
470
+ .use(remarkParse)
471
+ .use(remarkGfm)
472
+ .use(remarkRehype)
473
+ .use(rehypeStringify)
474
+ .process(this.content);
475
+ return String(result);
476
+ }
477
+
460
478
  toOutline(): string {
461
479
  const headings = (this.ast.children as Content[]).filter(
462
480
  (n): n is Heading => n.type === "heading"
@@ -2,6 +2,8 @@ import { toString } from "mdast-util-to-string";
2
2
  import { AstQuery } from "./ast-query";
3
3
  import { NodeShortcuts } from "./node-shortcuts";
4
4
  import { stringifyAst } from "./utils/stringify-ast";
5
+ import { stripMarkdown } from "./utils/strip-markdown";
6
+ import type { StripMarkdownOptions } from "./utils/strip-markdown";
5
7
  import type { Root, Content, RootContent, Heading } from "mdast";
6
8
  import type { ParsedDocument } from "./parse";
7
9
 
@@ -150,6 +152,7 @@ function buildParsedDocument(ast: Root): ParsedDocument {
150
152
  nodes,
151
153
  title,
152
154
  stringify: (tree: Root = ast) => stringifyAst(tree),
155
+ stripMarkdown: (options?: StripMarkdownOptions) => stripMarkdown(ast, options),
153
156
  extractSection,
154
157
  querySection,
155
158
  };
package/src/index.ts CHANGED
@@ -39,8 +39,15 @@ export { validateDocument } from "./validator";
39
39
  // Pattern matching
40
40
  export { matchPattern, matchPatterns } from "./utils/match-pattern";
41
41
 
42
+ // Markdown utilities
43
+ export { stripMarkdown } from "./utils/strip-markdown";
44
+ export type { StripMarkdownOptions } from "./utils/strip-markdown";
45
+
42
46
  import { toString } from "mdast-util-to-string";
43
47
 
48
+ // Storage adapters
49
+ export { NodeStorageAdapter } from "./adapters/node-fs";
50
+
44
51
  // Types
45
52
  export type {
46
53
  ModelDefinition,
@@ -57,6 +64,9 @@ export type {
57
64
  SerializeOptions,
58
65
  SaveOptions,
59
66
  DocumentRef,
67
+ StorageAdapter,
68
+ FileStat,
69
+ FileEntry,
60
70
  } from "./types";
61
71
 
62
72
  // Re-export zod for convenience
@@ -0,0 +1,7 @@
1
+ export function errorResult(message: string) {
2
+ return { content: [{ type: 'text' as const, text: message }], isError: true }
3
+ }
4
+
5
+ export function textResult(text: string) {
6
+ return { content: [{ type: 'text' as const, text }] }
7
+ }
@@ -0,0 +1,102 @@
1
+ import { introspectMetaSchema } from '../index.js'
2
+
3
+ export function generateModelInfo(collection: any, def: any) {
4
+ const lines: string[] = []
5
+ const name = def.name as string
6
+ const prefix = def.prefix as string
7
+ const description = def.description || ''
8
+ const prefixDocs = collection.available.filter((id: string) => id.startsWith(prefix + '/'))
9
+
10
+ lines.push(`# Model: ${name}`, '')
11
+ lines.push(`- **Prefix:** \`${prefix}/\``)
12
+ lines.push(`- **Documents:** ${prefixDocs.length}`)
13
+ if (description) lines.push(`- **Description:** ${description}`)
14
+ lines.push('')
15
+
16
+ // Fields
17
+ const fields = introspectMetaSchema(def.meta)
18
+ if (fields.length > 0) {
19
+ lines.push('## Frontmatter Fields', '')
20
+ lines.push('| Field | Type | Required | Default | Description |')
21
+ lines.push('|-------|------|----------|---------|-------------|')
22
+ for (const f of fields as any[]) {
23
+ const req = f.required ? 'yes' : 'no'
24
+ const def_val = f.defaultValue !== undefined ? `\`${JSON.stringify(f.defaultValue)}\`` : ''
25
+ const desc = f.description || ''
26
+ lines.push(`| ${f.name} | ${f.type} | ${req} | ${def_val} | ${desc} |`)
27
+ }
28
+ lines.push('')
29
+ }
30
+
31
+ // Sections
32
+ const sections = Object.entries(def.sections || {})
33
+ if (sections.length > 0) {
34
+ lines.push('## Sections', '')
35
+ for (const [key, sec] of sections as [string, any][]) {
36
+ lines.push(`- **${sec.heading}** (key: \`${key}\`)${sec.schema ? ' — has schema validation' : ''}`)
37
+ if (sec.alternatives?.length) {
38
+ lines.push(` Alternatives: ${sec.alternatives.join(', ')}`)
39
+ }
40
+ }
41
+ lines.push('')
42
+ }
43
+
44
+ // Relationships
45
+ const relationships = Object.entries(def.relationships || {})
46
+ if (relationships.length > 0) {
47
+ lines.push('## Relationships', '')
48
+ for (const [key, rel] of relationships as [string, any][]) {
49
+ lines.push(`- \`${key}\` → ${rel.type} **${rel.model}**`)
50
+ }
51
+ lines.push('')
52
+ }
53
+
54
+ // Computed & Scopes
55
+ const computedKeys = Object.keys(def.computed || {})
56
+ if (computedKeys.length > 0) {
57
+ lines.push('## Computed Properties', '')
58
+ lines.push(computedKeys.map(k => `- \`${k}\``).join('\n'))
59
+ lines.push('')
60
+ }
61
+
62
+ const scopeKeys = Object.keys(def.scopes || {})
63
+ if (scopeKeys.length > 0) {
64
+ lines.push('## Named Scopes', '')
65
+ lines.push(scopeKeys.map(k => `- \`${k}\``).join('\n'))
66
+ lines.push('')
67
+ }
68
+
69
+ // Existing documents
70
+ if (prefixDocs.length > 0) {
71
+ lines.push('## Existing Documents', '')
72
+ for (const id of prefixDocs) {
73
+ lines.push(`- \`${id}\``)
74
+ }
75
+ lines.push('')
76
+ }
77
+
78
+ // Example scaffold
79
+ const defaultMeta: Record<string, any> = {}
80
+ for (const f of fields as any[]) {
81
+ if (f.defaultValue !== undefined) {
82
+ defaultMeta[f.name] = f.defaultValue
83
+ } else if (f.required) {
84
+ defaultMeta[f.name] = `<${f.type}>`
85
+ }
86
+ }
87
+ const sectionHeadings = sections.map(([, sec]: [string, any]) => `## ${sec.heading}\n\n`)
88
+
89
+ lines.push('## Example Document', '')
90
+ lines.push('```markdown')
91
+ lines.push('---')
92
+ for (const [k, v] of Object.entries(defaultMeta)) {
93
+ lines.push(`${k}: ${typeof v === 'string' ? v : JSON.stringify(v)}`)
94
+ }
95
+ lines.push('---')
96
+ lines.push(`# Your Title Here`)
97
+ lines.push('')
98
+ lines.push(sectionHeadings.join(''))
99
+ lines.push('```')
100
+
101
+ return lines.join('\n')
102
+ }