contentbase 0.0.2 → 0.0.4
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/.mcp.json +9 -0
- package/CLI.md +593 -0
- package/MCP-DESCRIPTIONS-REVIEW.md +122 -0
- package/MCP-SERVER-SPEC.md +453 -0
- package/PRIMER.md +540 -0
- package/README.md +289 -13
- package/bun.lock +43 -4
- package/dist/cnotes +0 -0
- package/docs/README.md +110 -0
- package/docs/TABLE-OF-CONTENTS.md +7 -0
- package/docs/models.ts +38 -0
- package/models.ts +38 -0
- package/package.json +12 -3
- package/public/web-demo/index.html +813 -0
- package/showcases/node_modules/.cache/.repl_history +3 -0
- package/showcases/vinyl-collection/artists/nina-simone.mdx +1 -1
- package/src/__tests__/semantic-search.integration.test.ts +284 -0
- package/src/api/endpoints/actions.ts +34 -0
- package/src/api/endpoints/doc.ts +187 -0
- package/src/api/endpoints/docs-index.ts +26 -0
- package/src/api/endpoints/document.ts +171 -0
- package/src/api/endpoints/documents.ts +71 -0
- package/src/api/endpoints/inspect.ts +16 -0
- package/src/api/endpoints/models.ts +10 -0
- package/src/api/endpoints/query.ts +88 -0
- package/src/api/endpoints/root.ts +7 -0
- package/src/api/endpoints/search-reindex.ts +65 -0
- package/src/api/endpoints/search-status.ts +55 -0
- package/src/api/endpoints/search.ts +104 -0
- package/src/api/endpoints/semantic-search.ts +120 -0
- package/src/api/endpoints/text-search.ts +63 -0
- package/src/api/endpoints/validate.ts +34 -0
- package/src/api/helpers.ts +97 -0
- package/src/base-model.ts +12 -0
- package/src/cli/commands/action.ts +82 -44
- package/src/cli/commands/console.ts +124 -0
- package/src/cli/commands/create.ts +179 -53
- package/src/cli/commands/embed.ts +323 -0
- package/src/cli/commands/export.ts +58 -24
- package/src/cli/commands/extract.ts +174 -0
- package/src/cli/commands/help.ts +81 -0
- package/src/cli/commands/index.ts +17 -0
- package/src/cli/commands/init.ts +72 -48
- package/src/cli/commands/inspect.ts +56 -46
- package/src/cli/commands/mcp.ts +1219 -0
- package/src/cli/commands/search.ts +285 -0
- package/src/cli/commands/serve.ts +348 -0
- package/src/cli/commands/summary.ts +60 -0
- package/src/cli/commands/teach.ts +86 -0
- package/src/cli/commands/text-search.ts +134 -0
- package/src/cli/commands/validate.ts +126 -64
- package/src/cli/index.ts +88 -19
- package/src/cli/load-collection.ts +144 -17
- package/src/cli/registry.ts +28 -0
- package/src/collection.ts +361 -10
- package/src/define-model.ts +101 -4
- package/src/document.ts +47 -1
- package/src/extract-sections.ts +1 -1
- package/src/index.ts +14 -2
- package/src/model-instance.ts +35 -5
- package/src/node-shortcuts.ts +1 -1
- package/src/query/collection-query.ts +96 -9
- package/src/query/index.ts +7 -0
- package/src/query/query-dsl.ts +259 -0
- package/src/relationships/has-many.ts +39 -0
- package/src/relationships/index.ts +7 -2
- package/src/section.ts +2 -0
- package/src/types.ts +23 -1
- package/src/utils/index.ts +1 -0
- package/src/utils/match-pattern.ts +65 -0
- package/src/validator.ts +18 -1
- package/test/collection.test.ts +118 -2
- package/test/fixtures/sdlc/MODELS.md +106 -0
- package/test/fixtures/sdlc/SKILL.md +404 -0
- package/test/fixtures/sdlc/index.ts +9 -0
- package/test/fixtures/sdlc/models.ts +8 -6
- package/test/fixtures/sdlc/stories/authentication/a-user-should-be-able-to-login.mdx +20 -0
- package/test/fixtures/sdlc/templates/epic.md +23 -0
- package/test/fixtures/sdlc/templates/story.md +19 -0
- package/test/pattern.test.ts +191 -0
- package/test/query-dsl.test.ts +431 -0
- package/test/query.test.ts +29 -0
- package/test/relationships.test.ts +130 -0
- package/test/section.test.ts +61 -0
- package/test/table-of-contents.test.ts +49 -5
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
import picomatch from 'picomatch'
|
|
3
|
+
import yaml from 'js-yaml'
|
|
4
|
+
import { commands } from '../registry.js'
|
|
5
|
+
import { loadCollection } from '../load-collection.js'
|
|
6
|
+
import { stringifyAst } from '../../utils/stringify-ast.js'
|
|
7
|
+
import type { Root, RootContent, Heading } from 'mdast'
|
|
8
|
+
|
|
9
|
+
const argsSchema = z.object({
|
|
10
|
+
sections: z.string().optional(),
|
|
11
|
+
s: z.string().optional(),
|
|
12
|
+
title: z.string().optional(),
|
|
13
|
+
t: z.string().optional(),
|
|
14
|
+
frontmatter: z.boolean().default(false),
|
|
15
|
+
noNormalizeHeadings: z.boolean().default(false),
|
|
16
|
+
contentFolder: z.string().optional(),
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
async function handler(options: z.infer<typeof argsSchema>, context: { container: any }) {
|
|
20
|
+
const collection = await loadCollection({
|
|
21
|
+
contentFolder: options.contentFolder,
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
const target = context.container.argv._[1] as string | undefined
|
|
25
|
+
if (!target) {
|
|
26
|
+
console.error('Usage: cnotes extract <target> --sections "Section1,Section2"')
|
|
27
|
+
process.exit(1)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const sectionsArg = options.sections || options.s
|
|
31
|
+
if (!sectionsArg) {
|
|
32
|
+
console.error('--sections is required')
|
|
33
|
+
process.exit(1)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const sectionNames = sectionsArg
|
|
37
|
+
.split(',')
|
|
38
|
+
.map((s) => s.trim())
|
|
39
|
+
.filter(Boolean)
|
|
40
|
+
const includeFrontmatter = options.frontmatter
|
|
41
|
+
const noNormalize = options.noNormalizeHeadings
|
|
42
|
+
const title = options.title || options.t
|
|
43
|
+
|
|
44
|
+
const isMatch = picomatch(target)
|
|
45
|
+
const matchingIds = collection.available.filter((id) => isMatch(id))
|
|
46
|
+
|
|
47
|
+
if (matchingIds.length === 0) {
|
|
48
|
+
console.error(`No documents matched: ${target}`)
|
|
49
|
+
process.exit(1)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const allNodes: RootContent[] = []
|
|
53
|
+
|
|
54
|
+
if (title) {
|
|
55
|
+
allNodes.push({
|
|
56
|
+
type: 'heading',
|
|
57
|
+
depth: 1,
|
|
58
|
+
children: [{ type: 'text', value: title }],
|
|
59
|
+
} as Heading)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
for (const id of matchingIds) {
|
|
63
|
+
const doc = collection.document(id)
|
|
64
|
+
|
|
65
|
+
if (includeFrontmatter && Object.keys(doc.meta).length > 0) {
|
|
66
|
+
allNodes.push({
|
|
67
|
+
type: 'yaml',
|
|
68
|
+
value: yaml.dump(doc.meta).trim(),
|
|
69
|
+
} as any)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const docNodes: RootContent[] = []
|
|
73
|
+
|
|
74
|
+
const titleNode = doc.nodes.firstHeading
|
|
75
|
+
if (titleNode) {
|
|
76
|
+
docNodes.push(titleNode as RootContent)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const leading = doc.nodes.leadingElementsAfterTitle
|
|
80
|
+
if (leading.length > 0) {
|
|
81
|
+
docNodes.push(...(leading as RootContent[]))
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
for (const name of sectionNames) {
|
|
85
|
+
try {
|
|
86
|
+
const sectionNodes = doc.extractSection(name)
|
|
87
|
+
if (sectionNodes.length > 0) {
|
|
88
|
+
docNodes.push(...(sectionNodes as RootContent[]))
|
|
89
|
+
}
|
|
90
|
+
} catch {
|
|
91
|
+
// Section not found — skip
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (!noNormalize && docNodes.length > 0) {
|
|
96
|
+
const headings = docNodes.filter(
|
|
97
|
+
(n): n is Heading => n.type === 'heading'
|
|
98
|
+
)
|
|
99
|
+
if (headings.length > 0) {
|
|
100
|
+
const minDepth = Math.min(...headings.map((h) => h.depth))
|
|
101
|
+
const targetDepth = title ? 2 : 1
|
|
102
|
+
const shift = targetDepth - minDepth
|
|
103
|
+
if (shift !== 0) {
|
|
104
|
+
for (const h of headings) {
|
|
105
|
+
h.depth = Math.max(1, Math.min(6, h.depth + shift)) as
|
|
106
|
+
| 1
|
|
107
|
+
| 2
|
|
108
|
+
| 3
|
|
109
|
+
| 4
|
|
110
|
+
| 5
|
|
111
|
+
| 6
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
allNodes.push(...docNodes)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const combinedAst: Root = { type: 'root', children: allNodes }
|
|
121
|
+
console.log(stringifyAst(combinedAst).trim())
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
commands.register('extract', {
|
|
125
|
+
description: 'Extract specific sections from documents',
|
|
126
|
+
help: `# cnotes extract
|
|
127
|
+
|
|
128
|
+
Extract specific sections from one or more documents. Supports glob patterns for matching multiple documents and outputs clean markdown with normalized headings.
|
|
129
|
+
|
|
130
|
+
## Usage
|
|
131
|
+
|
|
132
|
+
\`\`\`
|
|
133
|
+
cnotes extract <target> --sections "Section1,Section2" [options]
|
|
134
|
+
\`\`\`
|
|
135
|
+
|
|
136
|
+
## Arguments
|
|
137
|
+
|
|
138
|
+
| Argument | Description |
|
|
139
|
+
|----------|-------------|
|
|
140
|
+
| \`target\` | Path ID or glob pattern (e.g. \`epics/*\`, \`tasks/auth-*\`) |
|
|
141
|
+
|
|
142
|
+
## Options
|
|
143
|
+
|
|
144
|
+
| Option | Alias | Description |
|
|
145
|
+
|--------|-------|-------------|
|
|
146
|
+
| \`--sections\` | \`-s\` | **Required.** Comma-separated section headings to extract |
|
|
147
|
+
| \`--title\` | \`-t\` | Add a top-level H1 title to the output |
|
|
148
|
+
| \`--frontmatter\` | | Include YAML frontmatter in the output |
|
|
149
|
+
| \`--noNormalizeHeadings\` | | Skip heading depth normalization |
|
|
150
|
+
| \`--contentFolder\` | | Path to content folder |
|
|
151
|
+
|
|
152
|
+
## Heading Normalization
|
|
153
|
+
|
|
154
|
+
By default, extracted headings are re-leveled so the output starts at H1 (or H2 when \`--title\` is used). Use \`--noNormalizeHeadings\` to preserve original depths.
|
|
155
|
+
|
|
156
|
+
## Examples
|
|
157
|
+
|
|
158
|
+
\`\`\`bash
|
|
159
|
+
# Extract Overview from a single document
|
|
160
|
+
cnotes extract epics/auth-system --sections "Overview"
|
|
161
|
+
|
|
162
|
+
# Extract multiple sections
|
|
163
|
+
cnotes extract tasks/login-bug -s "Overview,Requirements,Acceptance Criteria"
|
|
164
|
+
|
|
165
|
+
# Extract from all epics with a title wrapper
|
|
166
|
+
cnotes extract "epics/*" -s "Overview" -t "Epic Summaries"
|
|
167
|
+
|
|
168
|
+
# Include frontmatter in output
|
|
169
|
+
cnotes extract epics/auth-system -s "Overview" --frontmatter
|
|
170
|
+
\`\`\`
|
|
171
|
+
`,
|
|
172
|
+
argsSchema,
|
|
173
|
+
handler,
|
|
174
|
+
})
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { commands } from '../registry.js'
|
|
2
|
+
|
|
3
|
+
async function handler(_options: any, context: { container: any }) {
|
|
4
|
+
const ui = context.container.feature('ui')
|
|
5
|
+
|
|
6
|
+
const lines: string[] = [
|
|
7
|
+
'# cnotes',
|
|
8
|
+
'',
|
|
9
|
+
'An ORM for structured Markdown/MDX files.',
|
|
10
|
+
'',
|
|
11
|
+
'## Usage',
|
|
12
|
+
'',
|
|
13
|
+
'```',
|
|
14
|
+
'cnotes <command> [options]',
|
|
15
|
+
'```',
|
|
16
|
+
'',
|
|
17
|
+
'## Commands',
|
|
18
|
+
'',
|
|
19
|
+
'| Command | Description |',
|
|
20
|
+
'|---------|-------------|',
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
for (const name of commands.available) {
|
|
24
|
+
const def = commands.get(name)!
|
|
25
|
+
lines.push(`| \`${name}\` | ${def.description} |`)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
lines.push(
|
|
29
|
+
'',
|
|
30
|
+
'## Global Options',
|
|
31
|
+
'',
|
|
32
|
+
'| Option | Description |',
|
|
33
|
+
'|--------|-------------|',
|
|
34
|
+
'| `--help`, `-h` | Show help for a command |',
|
|
35
|
+
'| `--contentFolder` | Path to content folder (most commands) |',
|
|
36
|
+
'',
|
|
37
|
+
'## Getting Help',
|
|
38
|
+
'',
|
|
39
|
+
'```bash',
|
|
40
|
+
'# Show this overview',
|
|
41
|
+
'cnotes help',
|
|
42
|
+
'',
|
|
43
|
+
'# Show detailed help for a command',
|
|
44
|
+
'cnotes help serve',
|
|
45
|
+
'cnotes serve --help',
|
|
46
|
+
'```',
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
console.log(ui.markdown(lines.join('\n')))
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
commands.register('help', {
|
|
53
|
+
description: 'Show available commands',
|
|
54
|
+
help: `# cnotes help
|
|
55
|
+
|
|
56
|
+
Show available commands and usage information.
|
|
57
|
+
|
|
58
|
+
## Usage
|
|
59
|
+
|
|
60
|
+
\`\`\`
|
|
61
|
+
cnotes help [command]
|
|
62
|
+
\`\`\`
|
|
63
|
+
|
|
64
|
+
## Arguments
|
|
65
|
+
|
|
66
|
+
| Argument | Description |
|
|
67
|
+
|----------|-------------|
|
|
68
|
+
| \`command\` | Show detailed help for a specific command |
|
|
69
|
+
|
|
70
|
+
## Examples
|
|
71
|
+
|
|
72
|
+
\`\`\`bash
|
|
73
|
+
# Show all commands
|
|
74
|
+
cnotes help
|
|
75
|
+
|
|
76
|
+
# Show help for the serve command
|
|
77
|
+
cnotes help serve
|
|
78
|
+
\`\`\`
|
|
79
|
+
`,
|
|
80
|
+
handler,
|
|
81
|
+
})
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// Side-effect imports register each command
|
|
2
|
+
import './init.js'
|
|
3
|
+
import './create.js'
|
|
4
|
+
import './inspect.js'
|
|
5
|
+
import './validate.js'
|
|
6
|
+
import './export.js'
|
|
7
|
+
import './action.js'
|
|
8
|
+
import './summary.js'
|
|
9
|
+
import './teach.js'
|
|
10
|
+
import './extract.js'
|
|
11
|
+
import './mcp.js'
|
|
12
|
+
import './serve.js'
|
|
13
|
+
import './console.js'
|
|
14
|
+
import './help.js'
|
|
15
|
+
import './text-search.js'
|
|
16
|
+
import './search.js'
|
|
17
|
+
import './embed.js'
|
package/src/cli/commands/init.ts
CHANGED
|
@@ -1,30 +1,17 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
},
|
|
16
|
-
},
|
|
17
|
-
async run({ args }) {
|
|
18
|
-
const name = (args.name as string) || "my-content";
|
|
19
|
-
const dir = path.resolve(process.cwd(), name);
|
|
20
|
-
|
|
21
|
-
await fs.mkdir(dir, { recursive: true });
|
|
22
|
-
await fs.mkdir(path.join(dir, "posts"), { recursive: true });
|
|
23
|
-
|
|
24
|
-
// Create a sample model file
|
|
25
|
-
await fs.writeFile(
|
|
26
|
-
path.join(dir, "models.ts"),
|
|
27
|
-
`import { defineModel, z } from "contentbase";
|
|
1
|
+
import fs from 'fs/promises'
|
|
2
|
+
import path from 'path'
|
|
3
|
+
import { commands } from '../registry.js'
|
|
4
|
+
|
|
5
|
+
async function handler(_options: any, context: { container: any }) {
|
|
6
|
+
const name = (context.container.argv._[1] as string) || 'my-content'
|
|
7
|
+
const dir = path.resolve(process.cwd(), name)
|
|
8
|
+
|
|
9
|
+
await fs.mkdir(dir, { recursive: true })
|
|
10
|
+
await fs.mkdir(path.join(dir, 'posts'), { recursive: true })
|
|
11
|
+
|
|
12
|
+
await fs.writeFile(
|
|
13
|
+
path.join(dir, 'models.ts'),
|
|
14
|
+
`import { defineModel, z } from "contentbase";
|
|
28
15
|
|
|
29
16
|
export const Post = defineModel("Post", {
|
|
30
17
|
prefix: "posts",
|
|
@@ -34,13 +21,12 @@ export const Post = defineModel("Post", {
|
|
|
34
21
|
}),
|
|
35
22
|
});
|
|
36
23
|
`,
|
|
37
|
-
|
|
38
|
-
|
|
24
|
+
'utf8'
|
|
25
|
+
)
|
|
39
26
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
`---
|
|
27
|
+
await fs.writeFile(
|
|
28
|
+
path.join(dir, 'posts', 'hello-world.md'),
|
|
29
|
+
`---
|
|
44
30
|
status: draft
|
|
45
31
|
author: me
|
|
46
32
|
---
|
|
@@ -49,13 +35,12 @@ author: me
|
|
|
49
35
|
|
|
50
36
|
Welcome to your contentbase project!
|
|
51
37
|
`,
|
|
52
|
-
|
|
53
|
-
|
|
38
|
+
'utf8'
|
|
39
|
+
)
|
|
54
40
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
`import { Collection } from "contentbase";
|
|
41
|
+
await fs.writeFile(
|
|
42
|
+
path.join(dir, 'index.ts'),
|
|
43
|
+
`import { Collection } from "contentbase";
|
|
59
44
|
import { Post } from "./models";
|
|
60
45
|
|
|
61
46
|
export const collection = new Collection({
|
|
@@ -64,12 +49,51 @@ export const collection = new Collection({
|
|
|
64
49
|
|
|
65
50
|
collection.register(Post);
|
|
66
51
|
`,
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
52
|
+
'utf8'
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
console.log(`Created contentbase project at ${dir}`)
|
|
56
|
+
console.log(` ${name}/models.ts`)
|
|
57
|
+
console.log(` ${name}/index.ts`)
|
|
58
|
+
console.log(` ${name}/posts/hello-world.md`)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
commands.register('init', {
|
|
62
|
+
description: 'Initialize a new contentbase project',
|
|
63
|
+
help: `# cnotes init
|
|
64
|
+
|
|
65
|
+
Scaffold a new contentbase project with a sample model and document.
|
|
66
|
+
|
|
67
|
+
## Usage
|
|
68
|
+
|
|
69
|
+
\`\`\`
|
|
70
|
+
cnotes init [name]
|
|
71
|
+
\`\`\`
|
|
72
|
+
|
|
73
|
+
## Arguments
|
|
74
|
+
|
|
75
|
+
| Argument | Description | Default |
|
|
76
|
+
|----------|-------------|---------|
|
|
77
|
+
| \`name\` | Directory name for the project | \`my-content\` |
|
|
78
|
+
|
|
79
|
+
## What It Creates
|
|
80
|
+
|
|
81
|
+
- \`<name>/models.ts\` — Model definitions with a sample Post model
|
|
82
|
+
- \`<name>/index.ts\` — Collection entry point
|
|
83
|
+
- \`<name>/posts/hello-world.md\` — Sample document
|
|
84
|
+
|
|
85
|
+
## Examples
|
|
86
|
+
|
|
87
|
+
\`\`\`bash
|
|
88
|
+
# Create with default name
|
|
89
|
+
cnotes init
|
|
90
|
+
|
|
91
|
+
# Create with custom name
|
|
92
|
+
cnotes init docs
|
|
93
|
+
|
|
94
|
+
# Create in a subdirectory
|
|
95
|
+
cnotes init content/blog
|
|
96
|
+
\`\`\`
|
|
97
|
+
`,
|
|
98
|
+
handler,
|
|
99
|
+
})
|
|
@@ -1,46 +1,56 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
import { commands } from '../registry.js'
|
|
3
|
+
import { loadCollection } from '../load-collection.js'
|
|
4
|
+
|
|
5
|
+
const argsSchema = z.object({
|
|
6
|
+
contentFolder: z.string().optional(),
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
async function handler(options: z.infer<typeof argsSchema>) {
|
|
10
|
+
const collection = await loadCollection({
|
|
11
|
+
contentFolder: options.contentFolder,
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
console.log(collection.generateModelSummary())
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
commands.register('inspect', {
|
|
18
|
+
description: 'Display collection info and registered models',
|
|
19
|
+
help: `# cnotes inspect
|
|
20
|
+
|
|
21
|
+
Display a summary of the collection: registered models, their fields, sections, relationships, and document counts.
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
\`\`\`
|
|
26
|
+
cnotes inspect [options]
|
|
27
|
+
\`\`\`
|
|
28
|
+
|
|
29
|
+
## Options
|
|
30
|
+
|
|
31
|
+
| Option | Description |
|
|
32
|
+
|--------|-------------|
|
|
33
|
+
| \`--contentFolder\` | Path to content folder |
|
|
34
|
+
|
|
35
|
+
## Output
|
|
36
|
+
|
|
37
|
+
Shows for each model:
|
|
38
|
+
- Prefix and document count
|
|
39
|
+
- Meta field definitions (name, type, required, default)
|
|
40
|
+
- Section headings
|
|
41
|
+
- Relationships (belongsTo, hasMany)
|
|
42
|
+
- Computed properties and named scopes
|
|
43
|
+
|
|
44
|
+
## Examples
|
|
45
|
+
|
|
46
|
+
\`\`\`bash
|
|
47
|
+
# Inspect current directory's collection
|
|
48
|
+
cnotes inspect
|
|
49
|
+
|
|
50
|
+
# Inspect a specific content folder
|
|
51
|
+
cnotes inspect --contentFolder test/fixtures/sdlc
|
|
52
|
+
\`\`\`
|
|
53
|
+
`,
|
|
54
|
+
argsSchema,
|
|
55
|
+
handler,
|
|
56
|
+
})
|