@intentius/chant 0.0.22 → 0.0.24

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 (32) hide show
  1. package/package.json +1 -1
  2. package/src/cli/commands/init-lexicon/templates/codegen.ts +188 -0
  3. package/src/cli/commands/init-lexicon/templates/docs.ts +81 -0
  4. package/src/cli/commands/init-lexicon/templates/examples.ts +35 -0
  5. package/src/cli/commands/init-lexicon/templates/lint.ts +30 -0
  6. package/src/cli/commands/init-lexicon/templates/lsp.ts +39 -0
  7. package/src/cli/commands/init-lexicon/templates/plugin.ts +110 -0
  8. package/src/cli/commands/init-lexicon/templates/project.ts +182 -0
  9. package/src/cli/commands/init-lexicon/templates/spec.ts +57 -0
  10. package/src/cli/commands/init-lexicon/templates/tests.ts +70 -0
  11. package/src/cli/commands/init-lexicon.ts +12 -774
  12. package/src/cli/conflict-check.test.ts +43 -0
  13. package/src/cli/main.ts +1 -1
  14. package/src/cli/mcp/resource-handlers.ts +227 -0
  15. package/src/cli/mcp/server.ts +20 -409
  16. package/src/cli/mcp/state-tools.ts +138 -0
  17. package/src/cli/mcp/types.ts +45 -0
  18. package/src/codegen/docs-file-markers.ts +69 -0
  19. package/src/codegen/docs-rule-scanning.ts +159 -0
  20. package/src/codegen/docs-sections.ts +159 -0
  21. package/src/codegen/docs-sidebar.ts +56 -0
  22. package/src/codegen/docs-types.ts +79 -0
  23. package/src/codegen/docs.ts +9 -495
  24. package/src/composite.test.ts +75 -0
  25. package/src/composite.ts +37 -0
  26. package/src/discovery/collect.test.ts +34 -0
  27. package/src/discovery/collect.ts +12 -0
  28. package/src/lexicon-plugin-helpers.ts +130 -0
  29. package/src/toml-emit.ts +182 -0
  30. package/src/toml-parse.ts +370 -0
  31. package/src/toml-utils.ts +60 -0
  32. package/src/toml.ts +5 -602
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intentius/chant",
3
- "version": "0.0.22",
3
+ "version": "0.0.24",
4
4
  "description": "Declarative infrastructure-as-code toolkit — TypeScript on Bun",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://intentius.io/chant",
@@ -0,0 +1,188 @@
1
+ /**
2
+ * Codegen template generators for init-lexicon scaffold.
3
+ */
4
+
5
+ export function generateCodegenGenerateTs(): string {
6
+ return `import { generatePipeline, writeGeneratedArtifacts } from "@intentius/chant/codegen/generate";
7
+ import type { GenerateResult } from "@intentius/chant/codegen/generate";
8
+ import { dirname } from "path";
9
+ import { fileURLToPath } from "url";
10
+
11
+ /**
12
+ * Run the code generation pipeline.
13
+ *
14
+ * Each callback has a TODO describing what to implement.
15
+ */
16
+ export async function generate(options?: { verbose?: boolean }): Promise<GenerateResult> {
17
+ const result = await generatePipeline({
18
+ // Must return Map<typeName, Buffer> — each entry is one schema file.
19
+ // Example: fetch a zip, extract JSON files, key by type name.
20
+ // See lexicons/aws/src/spec/fetch.ts for a working example.
21
+ fetchSchemas: async (opts) => {
22
+ throw new Error("TODO: implement fetchSchemas — download your upstream spec");
23
+ },
24
+
25
+ // Must return a ParsedResult (with propertyTypes[] and enums[] at minimum).
26
+ // Return null to skip a schema file.
27
+ // See lexicons/aws/src/spec/parse.ts for a working example.
28
+ parseSchema: (name, data) => {
29
+ throw new Error("TODO: implement parseSchema — parse a single schema file");
30
+ },
31
+
32
+ // Must return a NamingStrategy instance.
33
+ // See lexicons/aws/src/codegen/naming.ts and ./naming.ts for setup.
34
+ createNaming: (results) => {
35
+ throw new Error("TODO: implement createNaming — return a NamingStrategy instance");
36
+ },
37
+
38
+ // Must return a string of JSON (the lexicon registry).
39
+ // Use buildRegistry + serializeRegistry from @intentius/chant/codegen/generate-registry.
40
+ // See lexicons/aws/src/codegen/generate.ts for a working example.
41
+ generateRegistry: (results, naming) => {
42
+ throw new Error("TODO: implement generateRegistry — produce lexicon JSON");
43
+ },
44
+
45
+ // Must return a string of TypeScript declarations (.d.ts content).
46
+ // See lexicons/aws/src/codegen/generate.ts for a working example.
47
+ generateTypes: (results, naming) => {
48
+ throw new Error("TODO: implement generateTypes — produce .d.ts content");
49
+ },
50
+
51
+ // Must return a string of TypeScript (runtime index with factory exports).
52
+ // Use generateRuntimeIndex from @intentius/chant/codegen/generate-runtime-index.
53
+ // See lexicons/aws/src/codegen/generate.ts for a working example.
54
+ generateRuntimeIndex: (results, naming) => {
55
+ throw new Error("TODO: implement generateRuntimeIndex — produce index.ts content");
56
+ },
57
+ });
58
+
59
+ if (options?.verbose) {
60
+ console.error(\`Generated \${result.resources} resources, \${result.properties} property types\`);
61
+ }
62
+
63
+ return result;
64
+ }
65
+
66
+ /**
67
+ * Write generated files to the package directory.
68
+ */
69
+ export function writeGeneratedFiles(result: GenerateResult, pkgDir?: string): void {
70
+ const dir = pkgDir ?? dirname(dirname(fileURLToPath(import.meta.url)));
71
+ writeGeneratedArtifacts({
72
+ baseDir: dir,
73
+ files: {
74
+ "lexicon.json": result.lexiconJSON,
75
+ "index.d.ts": result.typesDTS,
76
+ "index.ts": result.indexTS,
77
+ },
78
+ });
79
+ }
80
+ `;
81
+ }
82
+
83
+ export function generateCodegenGenerateCliTs(): string {
84
+ return `#!/usr/bin/env bun
85
+ import { generate, writeGeneratedFiles } from "./generate";
86
+ import { dirname } from "path";
87
+ import { fileURLToPath } from "url";
88
+
89
+ const pkgDir = dirname(dirname(fileURLToPath(import.meta.url)));
90
+ const result = await generate({ verbose: true });
91
+ writeGeneratedFiles(result, pkgDir);
92
+ `;
93
+ }
94
+
95
+ export function generateCodegenNamingTs(): string {
96
+ return `import { NamingStrategy, type NamingConfig, type NamingInput } from "@intentius/chant/codegen/naming";
97
+
98
+ /**
99
+ * Naming configuration for this lexicon.
100
+ *
101
+ * TODO: Populate these tables with your provider's naming conventions.
102
+ */
103
+ export const namingConfig: NamingConfig = {
104
+ // High-priority short names for common resource types
105
+ priorityNames: {},
106
+
107
+ // Aliases for resource types that need alternate names
108
+ priorityAliases: {},
109
+
110
+ // Aliases for property types
111
+ priorityPropertyAliases: {},
112
+
113
+ // Abbreviations for service names (used in collision resolution)
114
+ serviceAbbreviations: {},
115
+
116
+ // Extract the short name from a fully-qualified type string
117
+ shortName: (typeName: string) => typeName.split("::").pop()!,
118
+
119
+ // Extract the service name from a fully-qualified type string
120
+ serviceName: (typeName: string) => typeName.split("::")[1] ?? typeName,
121
+ };
122
+
123
+ /**
124
+ * Create a NamingStrategy instance from parsed results.
125
+ */
126
+ export function createNaming(inputs: NamingInput[]): NamingStrategy {
127
+ return new NamingStrategy(inputs, namingConfig);
128
+ }
129
+ `;
130
+ }
131
+
132
+ export function generateCodegenPackageTs(name: string): string {
133
+ return `import { packagePipeline } from "@intentius/chant/codegen/package";
134
+ import type { PackagePipelineConfig } from "@intentius/chant/codegen/package";
135
+ import { generate } from "./generate";
136
+ import { dirname } from "path";
137
+ import { fileURLToPath } from "url";
138
+
139
+ /**
140
+ * Package the ${name} lexicon for distribution.
141
+ */
142
+ export async function packageLexicon(options?: { verbose?: boolean; force?: boolean }) {
143
+ const srcDir = dirname(fileURLToPath(import.meta.url));
144
+
145
+ const { spec, stats } = await packagePipeline({
146
+ generate: (opts) => generate({ verbose: opts?.verbose, force: opts?.force }),
147
+ buildManifest: (genResult) => ({
148
+ name: "${name}",
149
+ version: "0.0.1",
150
+ }),
151
+ srcDir,
152
+ collectSkills: () => new Map(),
153
+ });
154
+
155
+ console.error(\`Packaged \${stats.resources} resources, \${stats.ruleCount} rules\`);
156
+ return { spec, stats };
157
+ }
158
+ `;
159
+ }
160
+
161
+ export function generateCodegenDocsTs(name: string): string {
162
+ return `import { docsPipeline, writeDocsSite } from "@intentius/chant/codegen/docs";
163
+
164
+ /**
165
+ * Generate documentation site for the ${name} lexicon.
166
+ */
167
+ export async function generateDocs(options?: { verbose?: boolean }): Promise<void> {
168
+ const config = {
169
+ name: "${name}",
170
+ displayName: "${name.charAt(0).toUpperCase() + name.slice(1)}",
171
+ description: "${name} lexicon documentation",
172
+ distDir: "./dist",
173
+ outDir: "./docs",
174
+ // TODO: Implement service grouping for your provider
175
+ serviceFromType: (type: string) => type.split("::")[1] ?? type,
176
+ // TODO: Implement resource documentation URLs
177
+ resourceTypeUrl: (type: string) => \`#\${type}\`,
178
+ };
179
+
180
+ const result = docsPipeline(config);
181
+ writeDocsSite(config, result);
182
+
183
+ if (options?.verbose) {
184
+ console.error("Documentation generated");
185
+ }
186
+ }
187
+ `;
188
+ }
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Docs site template generators for init-lexicon scaffold.
3
+ */
4
+
5
+ export function generateDocsPackageJson(name: string): string {
6
+ return JSON.stringify(
7
+ {
8
+ name: `@intentius/chant-lexicon-${name}-docs`,
9
+ type: "module",
10
+ version: "0.0.1",
11
+ private: true,
12
+ scripts: {
13
+ dev: "astro dev",
14
+ build: "astro build",
15
+ preview: "astro preview",
16
+ },
17
+ dependencies: {
18
+ "@astrojs/starlight": "^0.37.6",
19
+ astro: "^5.6.1",
20
+ sharp: "^0.34.2",
21
+ },
22
+ },
23
+ null,
24
+ 2,
25
+ ) + "\n";
26
+ }
27
+
28
+ export function generateDocsTsConfig(): string {
29
+ return JSON.stringify(
30
+ {
31
+ extends: "astro/tsconfigs/strict",
32
+ include: [".astro/types.d.ts", "**/*"],
33
+ exclude: ["dist"],
34
+ },
35
+ null,
36
+ 2,
37
+ ) + "\n";
38
+ }
39
+
40
+ export function generateDocsAstroConfig(name: string): string {
41
+ const displayName = name.charAt(0).toUpperCase() + name.slice(1);
42
+ return `// @ts-check
43
+ import { defineConfig } from 'astro/config';
44
+ import starlight from '@astrojs/starlight';
45
+
46
+ export default defineConfig({
47
+ integrations: [
48
+ starlight({
49
+ title: '${displayName}',
50
+ sidebar: [
51
+ { label: 'Overview', slug: '' },
52
+ ],
53
+ }),
54
+ ],
55
+ });
56
+ `;
57
+ }
58
+
59
+ export function generateDocsContentConfig(): string {
60
+ return `import { defineCollection } from 'astro:content';
61
+ import { docsLoader } from '@astrojs/starlight/loaders';
62
+ import { docsSchema } from '@astrojs/starlight/schema';
63
+
64
+ export const collections = {
65
+ docs: defineCollection({ loader: docsLoader(), schema: docsSchema() }),
66
+ };
67
+ `;
68
+ }
69
+
70
+ export function generateDocsIndexMdx(name: string): string {
71
+ const displayName = name.charAt(0).toUpperCase() + name.slice(1);
72
+ return `---
73
+ title: Overview
74
+ description: ${displayName} lexicon for chant
75
+ ---
76
+
77
+ Welcome to the ${displayName} lexicon documentation.
78
+
79
+ Run \`just generate\` to populate this site with generated reference pages.
80
+ `;
81
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Example template generators for init-lexicon scaffold.
3
+ */
4
+
5
+ export function generateExamplePackageJson(name: string): string {
6
+ return JSON.stringify(
7
+ {
8
+ name: `@intentius/chant-lexicon-${name}-example-getting-started`,
9
+ version: "0.0.1",
10
+ private: true,
11
+ dependencies: {
12
+ [`@intentius/chant-lexicon-${name}`]: "workspace:*",
13
+ "@intentius/chant": "workspace:*",
14
+ },
15
+ },
16
+ null,
17
+ 2,
18
+ ) + "\n";
19
+ }
20
+
21
+ export function generateExampleInfraTs(name: string, names: { packageName: string }): string {
22
+ return `/**
23
+ * Getting-started example for the ${name} lexicon.
24
+ *
25
+ * TODO: Replace with a real infrastructure definition
26
+ * that uses resources from the ${name} lexicon.
27
+ */
28
+
29
+ // import { SomeResource } from "${names.packageName}";
30
+ //
31
+ // export const myResource = SomeResource("example", {
32
+ // // properties...
33
+ // });
34
+ `;
35
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Lint template generators for init-lexicon scaffold.
3
+ */
4
+
5
+ export function generateSampleRuleTs(names: { rulePrefix: string }): string {
6
+ return `import type { LintRule, LintDiagnostic, LintContext } from "@intentius/chant/lint/rule";
7
+
8
+ /**
9
+ * ${names.rulePrefix}001: Sample lint rule
10
+ *
11
+ * TODO: Replace with a real lint rule for your lexicon.
12
+ */
13
+ export const sampleRule: LintRule = {
14
+ id: "${names.rulePrefix}001",
15
+ severity: "warning",
16
+ category: "style",
17
+ description: "Sample lint rule — replace with real checks",
18
+
19
+ check(context: LintContext): LintDiagnostic[] {
20
+ // TODO: Implement rule logic
21
+ return [];
22
+ },
23
+ };
24
+ `;
25
+ }
26
+
27
+ export function generateLintRulesIndexTs(): string {
28
+ return `export { sampleRule } from "./sample";
29
+ `;
30
+ }
@@ -0,0 +1,39 @@
1
+ /**
2
+ * LSP template generators for init-lexicon scaffold.
3
+ */
4
+
5
+ export function generateLspCompletionsTs(name: string): string {
6
+ return `import type { CompletionContext, CompletionItem } from "@intentius/chant/lsp/types";
7
+ // import { LexiconIndex, lexiconCompletions } from "@intentius/chant/lsp/lexicon-providers";
8
+
9
+ /**
10
+ * Provide LSP completions for ${name} resources.
11
+ *
12
+ * TODO: Build a LexiconIndex from your generated lexicon data
13
+ * and delegate to lexiconCompletions().
14
+ */
15
+ export function completions(ctx: CompletionContext): CompletionItem[] {
16
+ // const index = new LexiconIndex(lexiconData);
17
+ // return lexiconCompletions(ctx, index, "${name} resource");
18
+ return [];
19
+ }
20
+ `;
21
+ }
22
+
23
+ export function generateLspHoverTs(name: string): string {
24
+ return `import type { HoverContext, HoverInfo } from "@intentius/chant/lsp/types";
25
+ // import { LexiconIndex, lexiconHover } from "@intentius/chant/lsp/lexicon-providers";
26
+
27
+ /**
28
+ * Provide LSP hover information for ${name} resources.
29
+ *
30
+ * TODO: Build a LexiconIndex from your generated lexicon data
31
+ * and delegate to lexiconHover().
32
+ */
33
+ export function hover(ctx: HoverContext): HoverInfo | undefined {
34
+ // const index = new LexiconIndex(lexiconData);
35
+ // return lexiconHover(ctx, index, myCustomHoverFormatter);
36
+ return undefined;
37
+ }
38
+ `;
39
+ }
@@ -0,0 +1,110 @@
1
+ /**
2
+ * Plugin and index template generators for init-lexicon scaffold.
3
+ */
4
+
5
+ export function generatePluginTs(name: string, names: { pluginVarName: string; serializerVarName: string }): string {
6
+ return `import type { LexiconPlugin, SkillDefinition, IntrinsicDef } from "@intentius/chant/lexicon";
7
+ import type { LintRule } from "@intentius/chant/lint/rule";
8
+ import type { PostSynthCheck } from "@intentius/chant/lint/post-synth";
9
+ import type { CompletionContext, CompletionItem, HoverContext, HoverInfo } from "@intentius/chant/lsp/types";
10
+ import type { McpToolContribution, McpResourceContribution } from "@intentius/chant/mcp/types";
11
+ import { ${names.serializerVarName} } from "./serializer";
12
+
13
+ /**
14
+ * ${name} lexicon plugin.
15
+ *
16
+ * Implements all required LexiconPlugin lifecycle methods.
17
+ */
18
+ export const ${names.pluginVarName}: LexiconPlugin = {
19
+ name: "${name}",
20
+ serializer: ${names.serializerVarName},
21
+
22
+ // ── Required lifecycle methods ────────────────────────────────
23
+
24
+ async generate(options?: { verbose?: boolean }): Promise<void> {
25
+ const { generate } = await import("./codegen/generate");
26
+ await generate(options);
27
+ },
28
+
29
+ async validate(options?: { verbose?: boolean }): Promise<void> {
30
+ const { validate } = await import("./validate");
31
+ const { printValidationResult } = await import("@intentius/chant/codegen/validate");
32
+ const result = await validate();
33
+ printValidationResult(result);
34
+ },
35
+
36
+ async coverage(options?: { verbose?: boolean; minOverall?: number }): Promise<void> {
37
+ // TODO: Implement coverage analysis
38
+ console.error("Coverage analysis not yet implemented");
39
+ },
40
+
41
+ async package(options?: { verbose?: boolean; force?: boolean }): Promise<void> {
42
+ const { packageLexicon } = await import("./codegen/package");
43
+ const { writeBundleSpec } = await import("@intentius/chant/codegen/package");
44
+ const { join, dirname } = await import("path");
45
+ const { fileURLToPath } = await import("url");
46
+
47
+ const { spec, stats } = await packageLexicon(options);
48
+ const pkgDir = dirname(dirname(fileURLToPath(import.meta.url)));
49
+ writeBundleSpec(spec, join(pkgDir, "dist"));
50
+
51
+ console.error(\`Packaged \${stats.resources} resources, \${stats.ruleCount} rules, \${stats.skillCount} skills\`);
52
+ },
53
+
54
+ // ── Optional extensions ────────────────────────────────────
55
+
56
+ lintRules() {
57
+ const { rules } = require("./lint/rules");
58
+ return rules;
59
+ },
60
+
61
+ postSynthChecks() {
62
+ return []; // TODO: Add post-synth checks
63
+ },
64
+
65
+ skills() {
66
+ return []; // TODO: Add skills
67
+ },
68
+
69
+ mcpTools() {
70
+ return []; // TODO: Implement MCP tools
71
+ },
72
+
73
+ mcpResources() {
74
+ return []; // TODO: Implement MCP resources
75
+ },
76
+
77
+ detectTemplate(data: unknown) {
78
+ return false; // TODO: Detect if a template belongs to this lexicon
79
+ },
80
+
81
+ completionProvider(ctx: CompletionContext) {
82
+ const { completions } = require("./lsp/completions");
83
+ return completions(ctx);
84
+ },
85
+
86
+ hoverProvider(ctx: HoverContext) {
87
+ const { hover } = require("./lsp/hover");
88
+ return hover(ctx);
89
+ },
90
+
91
+ async docs(options?) {
92
+ const { generateDocs } = await import("./codegen/docs");
93
+ return generateDocs(options);
94
+ },
95
+ };
96
+ `;
97
+ }
98
+
99
+ export function generateIndexTs(names: { pluginVarName: string; serializerVarName: string }): string {
100
+ return `// Plugin
101
+ export { ${names.pluginVarName} } from "./plugin";
102
+
103
+ // Serializer
104
+ export { ${names.serializerVarName} } from "./serializer";
105
+
106
+ // Generated resources — export everything from generated index
107
+ // After running \`chant generate\`, this re-exports all resource classes
108
+ // export * from "./generated/index";
109
+ `;
110
+ }
@@ -0,0 +1,182 @@
1
+ /**
2
+ * Project-level template generators for init-lexicon scaffold.
3
+ */
4
+
5
+ export function generatePackageJson(name: string, names: { packageName: string }): string {
6
+ const pkg = {
7
+ name: names.packageName,
8
+ version: "0.0.1",
9
+ type: "module",
10
+ private: true,
11
+ files: ["src/", "dist/"],
12
+ exports: {
13
+ ".": "./src/index.ts",
14
+ "./*": "./src/*",
15
+ "./manifest": "./dist/manifest.json",
16
+ "./meta": "./dist/meta.json",
17
+ "./types": "./dist/types/index.d.ts",
18
+ },
19
+ scripts: {
20
+ generate: "bun run src/codegen/generate-cli.ts",
21
+ validate: "bun run src/validate-cli.ts",
22
+ docs: "bun src/codegen/docs-cli.ts",
23
+ prepack: "bun run generate && bun run validate",
24
+ },
25
+ dependencies: {
26
+ "@intentius/chant": "workspace:*",
27
+ },
28
+ devDependencies: {
29
+ typescript: "^5.9.3",
30
+ },
31
+ };
32
+
33
+ return JSON.stringify(pkg, null, 2) + "\n";
34
+ }
35
+
36
+ export function generateTsConfig(): string {
37
+ const config = {
38
+ extends: "../../tsconfig.json",
39
+ compilerOptions: {
40
+ rootDir: "./src",
41
+ outDir: "./dist",
42
+ },
43
+ include: ["src/**/*"],
44
+ };
45
+
46
+ return JSON.stringify(config, null, 2) + "\n";
47
+ }
48
+
49
+ export function generateJustfile(name: string): string {
50
+ return `# Default recipe - list all available commands
51
+ default:
52
+ @just --list
53
+
54
+ # Generate types and metadata from upstream schemas
55
+ generate:
56
+ bun run src/codegen/generate-cli.ts
57
+
58
+ # Validate generated artifacts
59
+ validate:
60
+ bun run src/validate-cli.ts
61
+
62
+ # Generate docs site, install deps, and start dev server
63
+ docs:
64
+ bun run src/codegen/docs-cli.ts
65
+ bun install --cwd docs
66
+ bun --cwd docs dev
67
+
68
+ # Build docs site for production
69
+ docs-build:
70
+ bun run src/codegen/docs-cli.ts
71
+ bun install --cwd docs
72
+ bun --cwd docs build
73
+
74
+ # Package the lexicon (generate + validate)
75
+ package: generate validate
76
+ `;
77
+ }
78
+
79
+ export function generateGitignore(): string {
80
+ return `dist/
81
+ node_modules/
82
+ .cache/
83
+ `;
84
+ }
85
+
86
+ export function generateReadme(name: string, names: { packageName: string }): string {
87
+ return `# ${names.packageName}
88
+
89
+ ${name} lexicon plugin for [chant](https://github.com/intentius/chant).
90
+
91
+ ## Getting started
92
+
93
+ \`\`\`bash
94
+ # Generate types from upstream spec
95
+ just generate
96
+
97
+ # Validate generated artifacts
98
+ just validate
99
+
100
+ # Generate documentation
101
+ just docs
102
+ \`\`\`
103
+
104
+ ## Project structure
105
+
106
+ - \`src/plugin.ts\` — LexiconPlugin with all lifecycle methods
107
+ - \`src/serializer.ts\` — Build output serializer
108
+ - \`src/codegen/\` — Code generation pipeline
109
+ - \`src/spec/\` — Upstream schema fetching and parsing
110
+ - \`src/lint/rules/\` — Lint rules
111
+ - \`src/lsp/\` — LSP completions and hover
112
+ - \`src/generated/\` — Generated artifacts (do not edit)
113
+ `;
114
+ }
115
+
116
+ export function generateSerializerTs(name: string, names: { serializerVarName: string; rulePrefix: string }): string {
117
+ return `import type { Serializer, Declarable } from "@intentius/chant";
118
+
119
+ /**
120
+ * ${name} serializer — produces minimal JSON output.
121
+ *
122
+ * TODO: Replace with your lexicon's output format.
123
+ */
124
+ export const ${names.serializerVarName}: Serializer = {
125
+ name: "${name}",
126
+ rulePrefix: "${names.rulePrefix}",
127
+
128
+ serialize(entities: Map<string, Declarable>): string {
129
+ const resources: Record<string, unknown> = {};
130
+
131
+ for (const [entityName, entity] of entities) {
132
+ resources[entityName] = {
133
+ type: entity.entityType,
134
+ // TODO: Convert entity properties to your output format
135
+ };
136
+ }
137
+
138
+ return JSON.stringify({ resources }, null, 2);
139
+ },
140
+ };
141
+ `;
142
+ }
143
+
144
+ export function generateValidateTs(name: string): string {
145
+ return `/**
146
+ * Validate generated lexicon-${name} artifacts.
147
+ *
148
+ * Thin wrapper around the core validation framework
149
+ * with ${name}-specific configuration.
150
+ */
151
+
152
+ import { dirname } from "path";
153
+ import { fileURLToPath } from "url";
154
+ import { validateLexiconArtifacts, type ValidateResult } from "@intentius/chant/codegen/validate";
155
+
156
+ export type { ValidateCheck, ValidateResult } from "@intentius/chant/codegen/validate";
157
+
158
+ // TODO: Add names of required entities for your lexicon
159
+ const REQUIRED_NAMES: string[] = [];
160
+
161
+ /**
162
+ * Validate the generated lexicon-${name} artifacts.
163
+ */
164
+ export async function validate(opts?: { basePath?: string }): Promise<ValidateResult> {
165
+ const basePath = opts?.basePath ?? dirname(dirname(fileURLToPath(import.meta.url)));
166
+
167
+ return validateLexiconArtifacts({
168
+ lexiconJsonFilename: "lexicon-${name}.json",
169
+ requiredNames: REQUIRED_NAMES,
170
+ basePath,
171
+ });
172
+ }
173
+ `;
174
+ }
175
+
176
+ export function generateValidateCliTs(): string {
177
+ return `#!/usr/bin/env bun
178
+ import { validate } from "./validate";
179
+
180
+ await validate({ verbose: true });
181
+ `;
182
+ }