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.
- package/CNAME +1 -0
- package/LAUNCH-PLAN.md +419 -0
- package/dist/adapters/node-fs.d.ts +17 -0
- package/dist/adapters/node-fs.d.ts.map +1 -0
- package/dist/adapters/node-fs.js +71 -0
- package/dist/adapters/node-fs.js.map +1 -0
- package/dist/ast-query.d.ts +44 -0
- package/dist/ast-query.d.ts.map +1 -0
- package/dist/ast-query.js +95 -0
- package/dist/ast-query.js.map +1 -0
- package/dist/base-model.d.ts +8 -0
- package/dist/base-model.d.ts.map +1 -0
- package/dist/base-model.js +12 -0
- package/dist/base-model.js.map +1 -0
- package/dist/cli/commands/action.d.ts +2 -0
- package/dist/cli/commands/action.d.ts.map +1 -0
- package/dist/cli/commands/action.js +73 -0
- package/dist/cli/commands/action.js.map +1 -0
- package/dist/cli/commands/api/endpoints/actions.d.ts +13 -0
- package/dist/cli/commands/api/endpoints/actions.d.ts.map +1 -0
- package/dist/cli/commands/api/endpoints/actions.js +28 -0
- package/dist/cli/commands/api/endpoints/actions.js.map +1 -0
- package/dist/cli/commands/api/endpoints/doc.d.ts +5 -0
- package/dist/cli/commands/api/endpoints/doc.d.ts.map +1 -0
- package/dist/cli/commands/api/endpoints/doc.js +186 -0
- package/dist/cli/commands/api/endpoints/doc.js.map +1 -0
- package/dist/cli/commands/api/endpoints/docs-index.d.ts +8 -0
- package/dist/cli/commands/api/endpoints/docs-index.d.ts.map +1 -0
- package/dist/cli/commands/api/endpoints/docs-index.js +23 -0
- package/dist/cli/commands/api/endpoints/docs-index.js.map +1 -0
- package/dist/cli/commands/api/endpoints/document.d.ts +47 -0
- package/dist/cli/commands/api/endpoints/document.d.ts.map +1 -0
- package/dist/cli/commands/api/endpoints/document.js +146 -0
- package/dist/cli/commands/api/endpoints/document.js.map +1 -0
- package/dist/cli/commands/api/endpoints/documents.d.ts +35 -0
- package/dist/cli/commands/api/endpoints/documents.d.ts.map +1 -0
- package/dist/cli/commands/api/endpoints/documents.js +56 -0
- package/dist/cli/commands/api/endpoints/documents.js.map +1 -0
- package/dist/cli/commands/api/endpoints/index.d.ts +22 -0
- package/dist/cli/commands/api/endpoints/index.d.ts.map +1 -0
- package/dist/cli/commands/api/endpoints/index.js +38 -0
- package/dist/cli/commands/api/endpoints/index.js.map +1 -0
- package/dist/cli/commands/api/endpoints/inspect.d.ts +10 -0
- package/dist/cli/commands/api/endpoints/inspect.d.ts.map +1 -0
- package/dist/cli/commands/api/endpoints/inspect.js +15 -0
- package/dist/cli/commands/api/endpoints/inspect.js.map +1 -0
- package/dist/cli/commands/api/endpoints/models.d.ts +5 -0
- package/dist/cli/commands/api/endpoints/models.d.ts.map +1 -0
- package/dist/cli/commands/api/endpoints/models.js +9 -0
- package/dist/cli/commands/api/endpoints/models.js.map +1 -0
- package/dist/cli/commands/api/endpoints/query.d.ts +40 -0
- package/dist/cli/commands/api/endpoints/query.d.ts.map +1 -0
- package/dist/cli/commands/api/endpoints/query.js +106 -0
- package/dist/cli/commands/api/endpoints/query.js.map +1 -0
- package/dist/cli/commands/api/endpoints/root.d.ts +5 -0
- package/dist/cli/commands/api/endpoints/root.d.ts.map +1 -0
- package/dist/cli/commands/api/endpoints/root.js +7 -0
- package/dist/cli/commands/api/endpoints/root.js.map +1 -0
- package/dist/cli/commands/api/endpoints/search-reindex.d.ts +10 -0
- package/dist/cli/commands/api/endpoints/search-reindex.d.ts.map +1 -0
- package/dist/cli/commands/api/endpoints/search-reindex.js +38 -0
- package/dist/cli/commands/api/endpoints/search-reindex.js.map +1 -0
- package/dist/cli/commands/api/endpoints/search-status.d.ts +7 -0
- package/dist/cli/commands/api/endpoints/search-status.d.ts.map +1 -0
- package/dist/cli/commands/api/endpoints/search-status.js +33 -0
- package/dist/cli/commands/api/endpoints/search-status.js.map +1 -0
- package/dist/cli/commands/api/endpoints/search.d.ts +24 -0
- package/dist/cli/commands/api/endpoints/search.d.ts.map +1 -0
- package/dist/cli/commands/api/endpoints/search.js +61 -0
- package/dist/cli/commands/api/endpoints/search.js.map +1 -0
- package/dist/cli/commands/api/endpoints/semantic-search.d.ts +28 -0
- package/dist/cli/commands/api/endpoints/semantic-search.d.ts.map +1 -0
- package/dist/cli/commands/api/endpoints/semantic-search.js +76 -0
- package/dist/cli/commands/api/endpoints/semantic-search.js.map +1 -0
- package/dist/cli/commands/api/endpoints/text-search.d.ts +17 -0
- package/dist/cli/commands/api/endpoints/text-search.d.ts.map +1 -0
- package/dist/cli/commands/api/endpoints/text-search.js +55 -0
- package/dist/cli/commands/api/endpoints/text-search.js.map +1 -0
- package/dist/cli/commands/api/endpoints/validate.d.ts +12 -0
- package/dist/cli/commands/api/endpoints/validate.d.ts.map +1 -0
- package/dist/cli/commands/api/endpoints/validate.js +27 -0
- package/dist/cli/commands/api/endpoints/validate.js.map +1 -0
- package/dist/cli/commands/api/helpers.d.ts +8 -0
- package/dist/cli/commands/api/helpers.d.ts.map +1 -0
- package/dist/cli/commands/api/helpers.js +89 -0
- package/dist/cli/commands/api/helpers.js.map +1 -0
- package/dist/cli/commands/console.d.ts +2 -0
- package/dist/cli/commands/console.d.ts.map +1 -0
- package/dist/cli/commands/console.js +117 -0
- package/dist/cli/commands/console.js.map +1 -0
- package/dist/cli/commands/create.d.ts +2 -0
- package/dist/cli/commands/create.d.ts.map +1 -0
- package/dist/cli/commands/create.js +185 -0
- package/dist/cli/commands/create.js.map +1 -0
- package/dist/cli/commands/embed.d.ts +2 -0
- package/dist/cli/commands/embed.d.ts.map +1 -0
- package/dist/cli/commands/embed.js +231 -0
- package/dist/cli/commands/embed.js.map +1 -0
- package/dist/cli/commands/export.d.ts +2 -0
- package/dist/cli/commands/export.d.ts.map +1 -0
- package/dist/cli/commands/export.js +55 -0
- package/dist/cli/commands/export.js.map +1 -0
- package/dist/cli/commands/extract.d.ts +2 -0
- package/dist/cli/commands/extract.d.ts.map +1 -0
- package/dist/cli/commands/extract.js +149 -0
- package/dist/cli/commands/extract.js.map +1 -0
- package/dist/cli/commands/help.d.ts +2 -0
- package/dist/cli/commands/help.d.ts.map +1 -0
- package/dist/cli/commands/help.js +58 -0
- package/dist/cli/commands/help.js.map +1 -0
- package/dist/cli/commands/index.d.ts +18 -0
- package/dist/cli/commands/index.d.ts.map +1 -0
- package/dist/cli/commands/index.js +19 -0
- package/dist/cli/commands/index.js.map +1 -0
- package/dist/cli/commands/init.d.ts +2 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +82 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/inspect.d.ts +2 -0
- package/dist/cli/commands/inspect.d.ts.map +1 -0
- package/dist/cli/commands/inspect.js +53 -0
- package/dist/cli/commands/inspect.js.map +1 -0
- package/dist/cli/commands/mcp.d.ts +2 -0
- package/dist/cli/commands/mcp.d.ts.map +1 -0
- package/dist/cli/commands/mcp.js +92 -0
- package/dist/cli/commands/mcp.js.map +1 -0
- package/dist/cli/commands/search.d.ts +2 -0
- package/dist/cli/commands/search.d.ts.map +1 -0
- package/dist/cli/commands/search.js +212 -0
- package/dist/cli/commands/search.js.map +1 -0
- package/dist/cli/commands/serve.d.ts +2 -0
- package/dist/cli/commands/serve.d.ts.map +1 -0
- package/dist/cli/commands/serve.js +298 -0
- package/dist/cli/commands/serve.js.map +1 -0
- package/dist/cli/commands/summary.d.ts +2 -0
- package/dist/cli/commands/summary.d.ts.map +1 -0
- package/dist/cli/commands/summary.js +58 -0
- package/dist/cli/commands/summary.js.map +1 -0
- package/dist/cli/commands/teach.d.ts +2 -0
- package/dist/cli/commands/teach.d.ts.map +1 -0
- package/dist/cli/commands/teach.js +80 -0
- package/dist/cli/commands/teach.js.map +1 -0
- package/dist/cli/commands/text-search.d.ts +2 -0
- package/dist/cli/commands/text-search.d.ts.map +1 -0
- package/dist/cli/commands/text-search.js +125 -0
- package/dist/cli/commands/text-search.js.map +1 -0
- package/dist/cli/commands/update.d.ts +2 -0
- package/dist/cli/commands/update.d.ts.map +1 -0
- package/dist/cli/commands/update.js +167 -0
- package/dist/cli/commands/update.js.map +1 -0
- package/dist/cli/commands/validate.d.ts +2 -0
- package/dist/cli/commands/validate.d.ts.map +1 -0
- package/dist/cli/commands/validate.js +129 -0
- package/dist/cli/commands/validate.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +84 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/load-collection.d.ts +7 -0
- package/dist/cli/load-collection.d.ts.map +1 -0
- package/dist/cli/load-collection.js +201 -0
- package/dist/cli/load-collection.js.map +1 -0
- package/dist/cli/registry.d.ts +17 -0
- package/dist/cli/registry.d.ts.map +1 -0
- package/dist/cli/registry.js +16 -0
- package/dist/cli/registry.js.map +1 -0
- package/dist/cnotes +0 -0
- package/dist/collection.d.ts +145 -0
- package/dist/collection.d.ts.map +1 -0
- package/dist/collection.js +684 -0
- package/dist/collection.js.map +1 -0
- package/dist/define-model.d.ts +45 -0
- package/dist/define-model.d.ts.map +1 -0
- package/dist/define-model.js +106 -0
- package/dist/define-model.js.map +1 -0
- package/dist/document.d.ts +119 -0
- package/dist/document.d.ts.map +1 -0
- package/dist/document.js +418 -0
- package/dist/document.js.map +1 -0
- package/dist/extract-sections.d.ts +46 -0
- package/dist/extract-sections.d.ts.map +1 -0
- package/dist/extract-sections.js +153 -0
- package/dist/extract-sections.js.map +1 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +31 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/helpers.d.ts +14 -0
- package/dist/mcp/helpers.d.ts.map +1 -0
- package/dist/mcp/helpers.js +7 -0
- package/dist/mcp/helpers.js.map +1 -0
- package/dist/mcp/model-info.d.ts +2 -0
- package/dist/mcp/model-info.d.ts.map +1 -0
- package/dist/mcp/model-info.js +94 -0
- package/dist/mcp/model-info.js.map +1 -0
- package/dist/mcp/prompts.d.ts +2 -0
- package/dist/mcp/prompts.d.ts.map +1 -0
- package/dist/mcp/prompts.js +180 -0
- package/dist/mcp/prompts.js.map +1 -0
- package/dist/mcp/readme.d.ts +2 -0
- package/dist/mcp/readme.d.ts.map +1 -0
- package/dist/mcp/readme.js +71 -0
- package/dist/mcp/readme.js.map +1 -0
- package/dist/mcp/resources.d.ts +2 -0
- package/dist/mcp/resources.d.ts.map +1 -0
- package/dist/mcp/resources.js +85 -0
- package/dist/mcp/resources.js.map +1 -0
- package/dist/mcp/server.d.ts +18 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +83 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/tools/mutation.d.ts +2 -0
- package/dist/mcp/tools/mutation.d.ts.map +1 -0
- package/dist/mcp/tools/mutation.js +155 -0
- package/dist/mcp/tools/mutation.js.map +1 -0
- package/dist/mcp/tools/query.d.ts +2 -0
- package/dist/mcp/tools/query.d.ts.map +1 -0
- package/dist/mcp/tools/query.js +130 -0
- package/dist/mcp/tools/query.js.map +1 -0
- package/dist/mcp/tools/search.d.ts +2 -0
- package/dist/mcp/tools/search.d.ts.map +1 -0
- package/dist/mcp/tools/search.js +168 -0
- package/dist/mcp/tools/search.js.map +1 -0
- package/dist/model-instance.d.ts +11 -0
- package/dist/model-instance.d.ts.map +1 -0
- package/dist/model-instance.js +208 -0
- package/dist/model-instance.js.map +1 -0
- package/dist/node-shortcuts.d.ts +25 -0
- package/dist/node-shortcuts.d.ts.map +1 -0
- package/dist/node-shortcuts.js +67 -0
- package/dist/node-shortcuts.js.map +1 -0
- package/dist/parse.d.ts +45 -0
- package/dist/parse.d.ts.map +1 -0
- package/dist/parse.js +98 -0
- package/dist/parse.js.map +1 -0
- package/dist/query/collection-query.d.ts +39 -0
- package/dist/query/collection-query.d.ts.map +1 -0
- package/dist/query/collection-query.js +184 -0
- package/dist/query/collection-query.js.map +1 -0
- package/dist/query/index.d.ts +8 -0
- package/dist/query/index.d.ts.map +1 -0
- package/dist/query/index.js +5 -0
- package/dist/query/index.js.map +1 -0
- package/dist/query/operators.d.ts +3 -0
- package/dist/query/operators.d.ts.map +1 -0
- package/dist/query/operators.js +18 -0
- package/dist/query/operators.js.map +1 -0
- package/dist/query/query-builder.d.ts +34 -0
- package/dist/query/query-builder.d.ts.map +1 -0
- package/dist/query/query-builder.js +84 -0
- package/dist/query/query-builder.js.map +1 -0
- package/dist/query/query-dsl.d.ts +36 -0
- package/dist/query/query-dsl.d.ts.map +1 -0
- package/dist/query/query-dsl.js +195 -0
- package/dist/query/query-dsl.js.map +1 -0
- package/dist/relationships/belongs-to.d.ts +9 -0
- package/dist/relationships/belongs-to.d.ts.map +1 -0
- package/dist/relationships/belongs-to.js +34 -0
- package/dist/relationships/belongs-to.js.map +1 -0
- package/dist/relationships/has-many.d.ts +23 -0
- package/dist/relationships/has-many.d.ts.map +1 -0
- package/dist/relationships/has-many.js +136 -0
- package/dist/relationships/has-many.js.map +1 -0
- package/dist/relationships/index.d.ts +26 -0
- package/dist/relationships/index.d.ts.map +1 -0
- package/dist/relationships/index.js +32 -0
- package/dist/relationships/index.js.map +1 -0
- package/dist/relationships/types.d.ts +2 -0
- package/dist/relationships/types.d.ts.map +1 -0
- package/dist/relationships/types.js +2 -0
- package/dist/relationships/types.js.map +1 -0
- package/dist/search/document-inputs.d.ts +30 -0
- package/dist/search/document-inputs.d.ts.map +1 -0
- package/dist/search/document-inputs.js +52 -0
- package/dist/search/document-inputs.js.map +1 -0
- package/dist/search/luca-semantic-search.d.ts +27 -0
- package/dist/search/luca-semantic-search.d.ts.map +1 -0
- package/dist/search/luca-semantic-search.js +56 -0
- package/dist/search/luca-semantic-search.js.map +1 -0
- package/dist/section.d.ts +21 -0
- package/dist/section.d.ts.map +1 -0
- package/dist/section.js +21 -0
- package/dist/section.js.map +1 -0
- package/dist/types.d.ts +185 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/index.d.ts +9 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +8 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/inflect.d.ts +6 -0
- package/dist/utils/inflect.d.ts.map +1 -0
- package/dist/utils/inflect.js +75 -0
- package/dist/utils/inflect.js.map +1 -0
- package/dist/utils/match-pattern.d.ts +25 -0
- package/dist/utils/match-pattern.d.ts.map +1 -0
- package/dist/utils/match-pattern.js +52 -0
- package/dist/utils/match-pattern.js.map +1 -0
- package/dist/utils/normalize-headings.d.ts +8 -0
- package/dist/utils/normalize-headings.d.ts.map +1 -0
- package/dist/utils/normalize-headings.js +19 -0
- package/dist/utils/normalize-headings.js.map +1 -0
- package/dist/utils/parse-table.d.ts +7 -0
- package/dist/utils/parse-table.d.ts.map +1 -0
- package/dist/utils/parse-table.js +17 -0
- package/dist/utils/parse-table.js.map +1 -0
- package/dist/utils/read-directory.d.ts +5 -0
- package/dist/utils/read-directory.d.ts.map +1 -0
- package/dist/utils/read-directory.js +29 -0
- package/dist/utils/read-directory.js.map +1 -0
- package/dist/utils/stringify-ast.d.ts +6 -0
- package/dist/utils/stringify-ast.d.ts.map +1 -0
- package/dist/utils/stringify-ast.js +8 -0
- package/dist/utils/stringify-ast.js.map +1 -0
- package/dist/utils/strip-markdown.d.ts +11 -0
- package/dist/utils/strip-markdown.d.ts.map +1 -0
- package/dist/utils/strip-markdown.js +48 -0
- package/dist/utils/strip-markdown.js.map +1 -0
- package/dist/validator.d.ts +10 -0
- package/dist/validator.d.ts.map +1 -0
- package/dist/validator.js +55 -0
- package/dist/validator.js.map +1 -0
- package/index.html +1636 -0
- package/package.json +5 -4
- package/src/adapters/node-fs.ts +80 -0
- package/src/cli/commands/api/endpoints/doc.ts +2 -2
- package/src/cli/commands/api/endpoints/document.ts +2 -2
- package/src/cli/commands/api/endpoints/search-reindex.ts +6 -25
- package/src/cli/commands/api/endpoints/search-status.ts +3 -20
- package/src/cli/commands/api/endpoints/search.ts +3 -33
- package/src/cli/commands/api/endpoints/semantic-search.ts +3 -33
- package/src/cli/commands/embed.ts +8 -81
- package/src/cli/commands/mcp.ts +5 -1128
- package/src/cli/commands/search.ts +4 -76
- package/src/cli/commands/serve.ts +7 -32
- package/src/cli/index.ts +1 -1
- package/src/cli/load-collection.ts +2 -2
- package/src/collection.ts +31 -30
- package/src/document.ts +18 -0
- package/src/extract-sections.ts +3 -0
- package/src/index.ts +10 -0
- package/src/mcp/helpers.ts +7 -0
- package/src/mcp/model-info.ts +102 -0
- package/src/mcp/prompts.ts +199 -0
- package/src/mcp/readme.ts +153 -0
- package/src/mcp/resources.ts +89 -0
- package/src/mcp/server.ts +103 -0
- package/src/mcp/tools/mutation.ts +176 -0
- package/src/mcp/tools/query.ts +141 -0
- package/src/mcp/tools/search.ts +180 -0
- package/src/parse.ts +5 -0
- package/src/query/query-dsl.ts +1 -1
- package/src/relationships/has-many.ts +2 -2
- package/src/search/document-inputs.ts +85 -0
- package/src/search/luca-semantic-search.ts +75 -0
- package/src/types.ts +54 -0
- package/src/utils/index.ts +2 -0
- package/src/utils/match-pattern.ts +2 -2
- 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
|
|
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
|
|
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
|
-
|
|
188
|
-
const dbDir = path.join(collection.rootPath, '.contentbase')
|
|
189
|
-
const hasIndex = existsSync(dbDir) && (() => {
|
|
189
|
+
if (hasSearchIndex(collection.rootPath)) {
|
|
190
190
|
try {
|
|
191
|
-
|
|
192
|
-
|
|
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('
|
|
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
|
|
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('
|
|
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
|
|
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
|
|
262
|
+
const entries = await this.#adapter.listFiles(this.rootPath, extensionPattern);
|
|
256
263
|
|
|
257
264
|
await Promise.all(
|
|
258
|
-
|
|
259
|
-
const pathId = this.getPathId(
|
|
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
|
|
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:
|
|
274
|
-
createdAt: stat.
|
|
275
|
-
updatedAt: stat.
|
|
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
|
|
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
|
-
|
|
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
|
|
494
|
-
const stat = await
|
|
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.
|
|
503
|
-
updatedAt: stat.
|
|
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
|
|
554
|
+
return this.#adapter.join(this.rootPath, ...args);
|
|
554
555
|
}
|
|
555
556
|
|
|
556
|
-
getPathId(
|
|
557
|
-
const relativePath =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
|
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"
|
package/src/extract-sections.ts
CHANGED
|
@@ -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,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
|
+
}
|