@intentius/chant 0.0.18 → 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 (87) hide show
  1. package/bin/chant +4 -1
  2. package/package.json +20 -1
  3. package/src/build.test.ts +4 -2
  4. package/src/build.ts +3 -0
  5. package/src/builder.test.ts +3 -0
  6. package/src/cli/commands/__fixtures__/init-lexicon-output/docs/astro.config.mjs +0 -3
  7. package/src/cli/commands/build.ts +5 -12
  8. package/src/cli/commands/diff.test.ts +2 -1
  9. package/src/cli/commands/diff.ts +2 -1
  10. package/src/cli/commands/init-lexicon/templates/codegen.ts +188 -0
  11. package/src/cli/commands/init-lexicon/templates/docs.ts +81 -0
  12. package/src/cli/commands/init-lexicon/templates/examples.ts +35 -0
  13. package/src/cli/commands/init-lexicon/templates/lint.ts +30 -0
  14. package/src/cli/commands/init-lexicon/templates/lsp.ts +39 -0
  15. package/src/cli/commands/init-lexicon/templates/plugin.ts +110 -0
  16. package/src/cli/commands/init-lexicon/templates/project.ts +182 -0
  17. package/src/cli/commands/init-lexicon/templates/spec.ts +57 -0
  18. package/src/cli/commands/init-lexicon/templates/tests.ts +70 -0
  19. package/src/cli/commands/init-lexicon.test.ts +0 -9
  20. package/src/cli/commands/init-lexicon.ts +12 -868
  21. package/src/cli/commands/init.ts +2 -20
  22. package/src/cli/conflict-check.test.ts +43 -0
  23. package/src/cli/handlers/build.ts +3 -3
  24. package/src/cli/handlers/lint.ts +2 -2
  25. package/src/cli/handlers/spell.ts +396 -0
  26. package/src/cli/handlers/state.ts +230 -0
  27. package/src/cli/lsp/server.test.ts +4 -0
  28. package/src/cli/main.ts +37 -3
  29. package/src/cli/mcp/resource-handlers.ts +227 -0
  30. package/src/cli/mcp/server.test.ts +13 -9
  31. package/src/cli/mcp/server.ts +24 -199
  32. package/src/cli/mcp/state-tools.ts +138 -0
  33. package/src/cli/mcp/tools/build.ts +2 -1
  34. package/src/cli/mcp/types.ts +45 -0
  35. package/src/cli/plugins.ts +1 -1
  36. package/src/cli/reporters/stylish.test.ts +2 -2
  37. package/src/cli/reporters/stylish.ts +1 -1
  38. package/src/codegen/docs-file-markers.ts +69 -0
  39. package/src/codegen/docs-rule-scanning.ts +159 -0
  40. package/src/codegen/docs-sections.ts +159 -0
  41. package/src/codegen/docs-sidebar.ts +56 -0
  42. package/src/codegen/docs-types.ts +79 -0
  43. package/src/codegen/docs.ts +9 -495
  44. package/src/composite.test.ts +76 -1
  45. package/src/composite.ts +37 -0
  46. package/src/config.ts +4 -0
  47. package/src/declarable.test.ts +2 -1
  48. package/src/declarable.ts +1 -1
  49. package/src/discovery/collect.test.ts +34 -0
  50. package/src/discovery/collect.ts +12 -0
  51. package/src/discovery/graph.test.ts +40 -0
  52. package/src/discovery/import.test.ts +5 -5
  53. package/src/discovery/resolve.test.ts +20 -0
  54. package/src/discovery/resolve.ts +2 -2
  55. package/src/index.ts +2 -0
  56. package/src/lexicon-plugin-helpers.ts +130 -0
  57. package/src/lexicon.ts +24 -0
  58. package/src/lint/rule-options.test.ts +3 -3
  59. package/src/lint/rule-registry.test.ts +1 -1
  60. package/src/lint/rules/composite-scope.ts +1 -1
  61. package/src/serializer-walker.ts +2 -1
  62. package/src/spell/discovery.ts +183 -0
  63. package/src/spell/index.ts +3 -0
  64. package/src/spell/prompt.ts +133 -0
  65. package/src/spell/types.ts +89 -0
  66. package/src/state/digest.ts +88 -0
  67. package/src/state/git.ts +317 -0
  68. package/src/state/index.ts +4 -0
  69. package/src/state/snapshot.ts +179 -0
  70. package/src/state/types.ts +59 -0
  71. package/src/toml-emit.ts +182 -0
  72. package/src/toml-parse.ts +370 -0
  73. package/src/toml-utils.ts +60 -0
  74. package/src/toml.ts +5 -602
  75. package/src/types.ts +2 -1
  76. package/src/utils.test.ts +16 -3
  77. package/src/utils.ts +31 -1
  78. package/src/validation.test.ts +11 -0
  79. package/src/cli/commands/__fixtures__/init-lexicon-output/docs/src/content/docs/getting-started.mdx +0 -6
  80. package/src/cli/commands/__fixtures__/init-lexicon-output/docs/src/content/docs/lint-rules.mdx +0 -6
  81. package/src/cli/commands/__fixtures__/init-lexicon-output/docs/src/content/docs/serialization.mdx +0 -6
  82. package/src/cli/commands/__fixtures__/init-lexicon-output/src/actions/.gitkeep +0 -0
  83. package/src/cli/commands/__fixtures__/init-lexicon-output/src/composites/.gitkeep +0 -0
  84. package/src/cli/commands/__fixtures__/init-lexicon-output/src/coverage.ts +0 -11
  85. package/src/cli/commands/__fixtures__/init-lexicon-output/src/import/generator.ts +0 -10
  86. package/src/cli/commands/__fixtures__/init-lexicon-output/src/import/parser.ts +0 -10
  87. package/src/cli/commands/__fixtures__/init-lexicon-output/src/lint/post-synth/.gitkeep +0 -0
@@ -2,6 +2,17 @@ import { existsSync, mkdirSync, writeFileSync, readdirSync } from "fs";
2
2
  import { join, resolve } from "path";
3
3
  import { formatSuccess, formatWarning } from "../format";
4
4
 
5
+ // Template generators
6
+ import { generatePluginTs, generateIndexTs } from "./init-lexicon/templates/plugin";
7
+ import { generateCodegenGenerateTs, generateCodegenGenerateCliTs, generateCodegenNamingTs, generateCodegenPackageTs, generateCodegenDocsTs } from "./init-lexicon/templates/codegen";
8
+ import { generateSpecFetchTs, generateSpecParseTs } from "./init-lexicon/templates/spec";
9
+ import { generateSampleRuleTs, generateLintRulesIndexTs } from "./init-lexicon/templates/lint";
10
+ import { generateLspCompletionsTs, generateLspHoverTs } from "./init-lexicon/templates/lsp";
11
+ import { generatePackageJson, generateTsConfig, generateJustfile, generateGitignore, generateReadme, generateSerializerTs, generateValidateTs, generateValidateCliTs } from "./init-lexicon/templates/project";
12
+ import { generatePluginTestTs, generateSerializerTestTs, generateCompletionsTestTs, generateHoverTestTs } from "./init-lexicon/templates/tests";
13
+ import { generateDocsPackageJson, generateDocsTsConfig, generateDocsAstroConfig, generateDocsContentConfig, generateDocsIndexMdx } from "./init-lexicon/templates/docs";
14
+ import { generateExamplePackageJson, generateExampleInfraTs } from "./init-lexicon/templates/examples";
15
+
5
16
  /**
6
17
  * Init-lexicon command options
7
18
  */
@@ -32,7 +43,7 @@ function toCamelCase(name: string): string {
32
43
  return name.replace(/[-_]+(.)/g, (_, c) => c.toUpperCase());
33
44
  }
34
45
 
35
- function deriveNames(name: string) {
46
+ export function deriveNames(name: string) {
36
47
  const camel = toCamelCase(name);
37
48
  return {
38
49
  pluginVarName: `${camel}Plugin`,
@@ -59,860 +70,6 @@ function writeIfNotExists(
59
70
  }
60
71
  }
61
72
 
62
- // ── Template generators ──────────────────────────────────────────────
63
-
64
- function generatePluginTs(name: string, names: ReturnType<typeof deriveNames>): string {
65
- return `import type { LexiconPlugin, SkillDefinition, IntrinsicDef } from "@intentius/chant/lexicon";
66
- import type { LintRule } from "@intentius/chant/lint/rule";
67
- import type { PostSynthCheck } from "@intentius/chant/lint/post-synth";
68
- import type { CompletionContext, CompletionItem, HoverContext, HoverInfo } from "@intentius/chant/lsp/types";
69
- import type { McpToolContribution, McpResourceContribution } from "@intentius/chant/mcp/types";
70
- import { ${names.serializerVarName} } from "./serializer";
71
-
72
- /**
73
- * ${name} lexicon plugin.
74
- *
75
- * Implements all required LexiconPlugin lifecycle methods.
76
- */
77
- export const ${names.pluginVarName}: LexiconPlugin = {
78
- name: "${name}",
79
- serializer: ${names.serializerVarName},
80
-
81
- // ── Required lifecycle methods ────────────────────────────────
82
-
83
- async generate(options?: { verbose?: boolean }): Promise<void> {
84
- const { generate } = await import("./codegen/generate");
85
- await generate(options);
86
- },
87
-
88
- async validate(options?: { verbose?: boolean }): Promise<void> {
89
- const { validate } = await import("./validate");
90
- const { printValidationResult } = await import("@intentius/chant/codegen/validate");
91
- const result = await validate();
92
- printValidationResult(result);
93
- },
94
-
95
- async coverage(options?: { verbose?: boolean; minOverall?: number }): Promise<void> {
96
- // TODO: Implement coverage analysis
97
- console.error("Coverage analysis not yet implemented");
98
- },
99
-
100
- async package(options?: { verbose?: boolean; force?: boolean }): Promise<void> {
101
- const { packageLexicon } = await import("./codegen/package");
102
- const { writeBundleSpec } = await import("@intentius/chant/codegen/package");
103
- const { join, dirname } = await import("path");
104
- const { fileURLToPath } = await import("url");
105
-
106
- const { spec, stats } = await packageLexicon(options);
107
- const pkgDir = dirname(dirname(fileURLToPath(import.meta.url)));
108
- writeBundleSpec(spec, join(pkgDir, "dist"));
109
-
110
- console.error(\`Packaged \${stats.resources} resources, \${stats.ruleCount} rules, \${stats.skillCount} skills\`);
111
- },
112
-
113
- // ── Optional extensions ────────────────────────────────────
114
-
115
- lintRules() {
116
- const { rules } = require("./lint/rules");
117
- return rules;
118
- },
119
-
120
- postSynthChecks() {
121
- return []; // TODO: Add post-synth checks
122
- },
123
-
124
- skills() {
125
- return []; // TODO: Add skills
126
- },
127
-
128
- mcpTools() {
129
- return []; // TODO: Implement MCP tools
130
- },
131
-
132
- mcpResources() {
133
- return []; // TODO: Implement MCP resources
134
- },
135
-
136
- detectTemplate(data: unknown) {
137
- return false; // TODO: Detect if a template belongs to this lexicon
138
- },
139
-
140
- completionProvider(ctx: CompletionContext) {
141
- const { completions } = require("./lsp/completions");
142
- return completions(ctx);
143
- },
144
-
145
- hoverProvider(ctx: HoverContext) {
146
- const { hover } = require("./lsp/hover");
147
- return hover(ctx);
148
- },
149
-
150
- async docs(options?) {
151
- const { generateDocs } = await import("./codegen/docs");
152
- return generateDocs(options);
153
- },
154
- };
155
- `;
156
- }
157
-
158
- function generateIndexTs(names: ReturnType<typeof deriveNames>): string {
159
- return `// Plugin
160
- export { ${names.pluginVarName} } from "./plugin";
161
-
162
- // Serializer
163
- export { ${names.serializerVarName} } from "./serializer";
164
-
165
- // Generated resources — export everything from generated index
166
- // After running \`chant generate\`, this re-exports all resource classes
167
- // export * from "./generated/index";
168
- `;
169
- }
170
-
171
- function generateSerializerTs(name: string, names: ReturnType<typeof deriveNames>): string {
172
- return `import type { Serializer, Declarable } from "@intentius/chant";
173
-
174
- /**
175
- * ${name} serializer — produces minimal JSON output.
176
- *
177
- * TODO: Replace with your lexicon's output format.
178
- */
179
- export const ${names.serializerVarName}: Serializer = {
180
- name: "${name}",
181
- rulePrefix: "${names.rulePrefix}",
182
-
183
- serialize(entities: Map<string, Declarable>): string {
184
- const resources: Record<string, unknown> = {};
185
-
186
- for (const [entityName, entity] of entities) {
187
- resources[entityName] = {
188
- type: entity.entityType,
189
- // TODO: Convert entity properties to your output format
190
- };
191
- }
192
-
193
- return JSON.stringify({ resources }, null, 2);
194
- },
195
- };
196
- `;
197
- }
198
-
199
- function generateCodegenGenerateTs(): string {
200
- return `import { generatePipeline, writeGeneratedArtifacts } from "@intentius/chant/codegen/generate";
201
- import type { GenerateResult } from "@intentius/chant/codegen/generate";
202
- import { dirname } from "path";
203
- import { fileURLToPath } from "url";
204
-
205
- /**
206
- * Run the code generation pipeline.
207
- *
208
- * Each callback has a TODO describing what to implement.
209
- */
210
- export async function generate(options?: { verbose?: boolean }): Promise<GenerateResult> {
211
- const result = await generatePipeline({
212
- // Must return Map<typeName, Buffer> — each entry is one schema file.
213
- // Example: fetch a zip, extract JSON files, key by type name.
214
- // See lexicons/aws/src/spec/fetch.ts for a working example.
215
- fetchSchemas: async (opts) => {
216
- throw new Error("TODO: implement fetchSchemas — download your upstream spec");
217
- },
218
-
219
- // Must return a ParsedResult (with propertyTypes[] and enums[] at minimum).
220
- // Return null to skip a schema file.
221
- // See lexicons/aws/src/spec/parse.ts for a working example.
222
- parseSchema: (name, data) => {
223
- throw new Error("TODO: implement parseSchema — parse a single schema file");
224
- },
225
-
226
- // Must return a NamingStrategy instance.
227
- // See lexicons/aws/src/codegen/naming.ts and ./naming.ts for setup.
228
- createNaming: (results) => {
229
- throw new Error("TODO: implement createNaming — return a NamingStrategy instance");
230
- },
231
-
232
- // Must return a string of JSON (the lexicon registry).
233
- // Use buildRegistry + serializeRegistry from @intentius/chant/codegen/generate-registry.
234
- // See lexicons/aws/src/codegen/generate.ts for a working example.
235
- generateRegistry: (results, naming) => {
236
- throw new Error("TODO: implement generateRegistry — produce lexicon JSON");
237
- },
238
-
239
- // Must return a string of TypeScript declarations (.d.ts content).
240
- // See lexicons/aws/src/codegen/generate.ts for a working example.
241
- generateTypes: (results, naming) => {
242
- throw new Error("TODO: implement generateTypes — produce .d.ts content");
243
- },
244
-
245
- // Must return a string of TypeScript (runtime index with factory exports).
246
- // Use generateRuntimeIndex from @intentius/chant/codegen/generate-runtime-index.
247
- // See lexicons/aws/src/codegen/generate.ts for a working example.
248
- generateRuntimeIndex: (results, naming) => {
249
- throw new Error("TODO: implement generateRuntimeIndex — produce index.ts content");
250
- },
251
- });
252
-
253
- if (options?.verbose) {
254
- console.error(\`Generated \${result.resources} resources, \${result.properties} property types\`);
255
- }
256
-
257
- return result;
258
- }
259
-
260
- /**
261
- * Write generated files to the package directory.
262
- */
263
- export function writeGeneratedFiles(result: GenerateResult, pkgDir?: string): void {
264
- const dir = pkgDir ?? dirname(dirname(fileURLToPath(import.meta.url)));
265
- writeGeneratedArtifacts({
266
- baseDir: dir,
267
- files: {
268
- "lexicon.json": result.lexiconJSON,
269
- "index.d.ts": result.typesDTS,
270
- "index.ts": result.indexTS,
271
- },
272
- });
273
- }
274
- `;
275
- }
276
-
277
- function generateCodegenGenerateCliTs(): string {
278
- return `#!/usr/bin/env bun
279
- import { generate, writeGeneratedFiles } from "./generate";
280
- import { dirname } from "path";
281
- import { fileURLToPath } from "url";
282
-
283
- const pkgDir = dirname(dirname(fileURLToPath(import.meta.url)));
284
- const result = await generate({ verbose: true });
285
- writeGeneratedFiles(result, pkgDir);
286
- `;
287
- }
288
-
289
- function generateCodegenNamingTs(): string {
290
- return `import { NamingStrategy, type NamingConfig, type NamingInput } from "@intentius/chant/codegen/naming";
291
-
292
- /**
293
- * Naming configuration for this lexicon.
294
- *
295
- * TODO: Populate these tables with your provider's naming conventions.
296
- */
297
- export const namingConfig: NamingConfig = {
298
- // High-priority short names for common resource types
299
- priorityNames: {},
300
-
301
- // Aliases for resource types that need alternate names
302
- priorityAliases: {},
303
-
304
- // Aliases for property types
305
- priorityPropertyAliases: {},
306
-
307
- // Abbreviations for service names (used in collision resolution)
308
- serviceAbbreviations: {},
309
-
310
- // Extract the short name from a fully-qualified type string
311
- shortName: (typeName: string) => typeName.split("::").pop()!,
312
-
313
- // Extract the service name from a fully-qualified type string
314
- serviceName: (typeName: string) => typeName.split("::")[1] ?? typeName,
315
- };
316
-
317
- /**
318
- * Create a NamingStrategy instance from parsed results.
319
- */
320
- export function createNaming(inputs: NamingInput[]): NamingStrategy {
321
- return new NamingStrategy(inputs, namingConfig);
322
- }
323
- `;
324
- }
325
-
326
- function generateCodegenPackageTs(name: string): string {
327
- return `import { packagePipeline } from "@intentius/chant/codegen/package";
328
- import type { PackagePipelineConfig } from "@intentius/chant/codegen/package";
329
- import { generate } from "./generate";
330
- import { dirname } from "path";
331
- import { fileURLToPath } from "url";
332
-
333
- /**
334
- * Package the ${name} lexicon for distribution.
335
- */
336
- export async function packageLexicon(options?: { verbose?: boolean; force?: boolean }) {
337
- const srcDir = dirname(fileURLToPath(import.meta.url));
338
-
339
- const { spec, stats } = await packagePipeline({
340
- generate: (opts) => generate({ verbose: opts?.verbose, force: opts?.force }),
341
- buildManifest: (genResult) => ({
342
- name: "${name}",
343
- version: "0.0.1",
344
- }),
345
- srcDir,
346
- collectSkills: () => new Map(),
347
- });
348
-
349
- console.error(\`Packaged \${stats.resources} resources, \${stats.ruleCount} rules\`);
350
- return { spec, stats };
351
- }
352
- `;
353
- }
354
-
355
- function generateCodegenDocsTs(name: string): string {
356
- return `import { docsPipeline, writeDocsSite } from "@intentius/chant/codegen/docs";
357
-
358
- /**
359
- * Generate documentation site for the ${name} lexicon.
360
- */
361
- export async function generateDocs(options?: { verbose?: boolean }): Promise<void> {
362
- const config = {
363
- name: "${name}",
364
- displayName: "${name.charAt(0).toUpperCase() + name.slice(1)}",
365
- description: "${name} lexicon documentation",
366
- distDir: "./dist",
367
- outDir: "./docs",
368
- // TODO: Implement service grouping for your provider
369
- serviceFromType: (type: string) => type.split("::")[1] ?? type,
370
- // TODO: Implement resource documentation URLs
371
- resourceTypeUrl: (type: string) => \`#\${type}\`,
372
- };
373
-
374
- const result = docsPipeline(config);
375
- writeDocsSite(config, result);
376
-
377
- if (options?.verbose) {
378
- console.error("Documentation generated");
379
- }
380
- }
381
- `;
382
- }
383
-
384
- function generateSpecFetchTs(): string {
385
- return `import { fetchWithCache, extractFromZip } from "@intentius/chant/codegen/fetch";
386
-
387
- // TODO: Set this to your upstream schema source URL
388
- const SCHEMA_URL = "https://example.com/schemas.zip";
389
- const CACHE_FILE = ".cache/schemas.zip";
390
-
391
- /**
392
- * Fetch upstream schemas with caching.
393
- *
394
- * TODO: Point SCHEMA_URL at your real upstream schema source.
395
- */
396
- export async function fetchSchemas(options?: { force?: boolean }): Promise<Map<string, string>> {
397
- const zipData = await fetchWithCache({
398
- url: SCHEMA_URL,
399
- cacheFile: CACHE_FILE,
400
- force: options?.force,
401
- });
402
-
403
- // TODO: Adjust the filter to match your schema file names
404
- return extractFromZip(zipData, (name) => name.endsWith(".json"));
405
- }
406
- `;
407
- }
408
-
409
- function generateSpecParseTs(): string {
410
- return `/**
411
- * Parsed schema result for a single schema file.
412
- */
413
- export interface ParseResult {
414
- typeName: string;
415
- description?: string;
416
- properties: Map<string, ParsedProperty>;
417
- attributes: string[];
418
- }
419
-
420
- export interface ParsedProperty {
421
- name: string;
422
- type: string;
423
- required: boolean;
424
- description?: string;
425
- }
426
-
427
- /**
428
- * Parse a single schema file into a ParseResult.
429
- *
430
- * TODO: Implement parsing for your schema format.
431
- */
432
- export function parseSchema(name: string, content: string): ParseResult {
433
- throw new Error(\`TODO: implement parseSchema for \${name}\`);
434
- }
435
- `;
436
- }
437
-
438
- function generateSampleRuleTs(names: ReturnType<typeof deriveNames>): string {
439
- return `import type { LintRule, LintDiagnostic, LintContext } from "@intentius/chant/lint/rule";
440
-
441
- /**
442
- * ${names.rulePrefix}001: Sample lint rule
443
- *
444
- * TODO: Replace with a real lint rule for your lexicon.
445
- */
446
- export const sampleRule: LintRule = {
447
- id: "${names.rulePrefix}001",
448
- severity: "warning",
449
- category: "style",
450
- description: "Sample lint rule — replace with real checks",
451
-
452
- check(context: LintContext): LintDiagnostic[] {
453
- // TODO: Implement rule logic
454
- return [];
455
- },
456
- };
457
- `;
458
- }
459
-
460
- function generateLintRulesIndexTs(): string {
461
- return `export { sampleRule } from "./sample";
462
- `;
463
- }
464
-
465
- function generateLspCompletionsTs(name: string): string {
466
- return `import type { CompletionContext, CompletionItem } from "@intentius/chant/lsp/types";
467
- // import { LexiconIndex, lexiconCompletions } from "@intentius/chant/lsp/lexicon-providers";
468
-
469
- /**
470
- * Provide LSP completions for ${name} resources.
471
- *
472
- * TODO: Build a LexiconIndex from your generated lexicon data
473
- * and delegate to lexiconCompletions().
474
- */
475
- export function completions(ctx: CompletionContext): CompletionItem[] {
476
- // const index = new LexiconIndex(lexiconData);
477
- // return lexiconCompletions(ctx, index, "${name} resource");
478
- return [];
479
- }
480
- `;
481
- }
482
-
483
- function generateLspHoverTs(name: string): string {
484
- return `import type { HoverContext, HoverInfo } from "@intentius/chant/lsp/types";
485
- // import { LexiconIndex, lexiconHover } from "@intentius/chant/lsp/lexicon-providers";
486
-
487
- /**
488
- * Provide LSP hover information for ${name} resources.
489
- *
490
- * TODO: Build a LexiconIndex from your generated lexicon data
491
- * and delegate to lexiconHover().
492
- */
493
- export function hover(ctx: HoverContext): HoverInfo | undefined {
494
- // const index = new LexiconIndex(lexiconData);
495
- // return lexiconHover(ctx, index, myCustomHoverFormatter);
496
- return undefined;
497
- }
498
- `;
499
- }
500
-
501
- function generateImportParserTs(name: string): string {
502
- return `import type { TemplateParser } from "@intentius/chant/import/parser";
503
-
504
- /**
505
- * Template parser for importing external ${name} templates.
506
- *
507
- * TODO: Implement the TemplateParser interface for your format.
508
- */
509
- // export class ${name.charAt(0).toUpperCase() + name.slice(1)}Parser implements TemplateParser {
510
- // parse(data: unknown): IR { ... }
511
- // }
512
- `;
513
- }
514
-
515
- function generateImportGeneratorTs(name: string): string {
516
- return `import type { TypeScriptGenerator } from "@intentius/chant/import/generator";
517
-
518
- /**
519
- * TypeScript generator for converting imported ${name} templates.
520
- *
521
- * TODO: Implement the TypeScriptGenerator interface for your format.
522
- */
523
- // export class ${name.charAt(0).toUpperCase() + name.slice(1)}Generator implements TypeScriptGenerator {
524
- // generate(ir: IR): string { ... }
525
- // }
526
- `;
527
- }
528
-
529
- function generateCoverageTs(name: string): string {
530
- return `/**
531
- * Coverage analysis for the ${name} lexicon.
532
- *
533
- * TODO: Implement coverage analysis that checks how much of the
534
- * upstream spec is covered by the generated types.
535
- */
536
- export async function analyzeCoverage(options?: { verbose?: boolean }): Promise<void> {
537
- console.error("Coverage analysis not yet implemented");
538
- // TODO: Read generated lexicon JSON, compare against upstream spec,
539
- // and report coverage metrics.
540
- }
541
- `;
542
- }
543
-
544
- function generateValidateTs(name: string): string {
545
- return `/**
546
- * Validate generated lexicon-${name} artifacts.
547
- *
548
- * Thin wrapper around the core validation framework
549
- * with ${name}-specific configuration.
550
- */
551
-
552
- import { dirname } from "path";
553
- import { fileURLToPath } from "url";
554
- import { validateLexiconArtifacts, type ValidateResult } from "@intentius/chant/codegen/validate";
555
-
556
- export type { ValidateCheck, ValidateResult } from "@intentius/chant/codegen/validate";
557
-
558
- // TODO: Add names of required entities for your lexicon
559
- const REQUIRED_NAMES: string[] = [];
560
-
561
- /**
562
- * Validate the generated lexicon-${name} artifacts.
563
- */
564
- export async function validate(opts?: { basePath?: string }): Promise<ValidateResult> {
565
- const basePath = opts?.basePath ?? dirname(dirname(fileURLToPath(import.meta.url)));
566
-
567
- return validateLexiconArtifacts({
568
- lexiconJsonFilename: "lexicon-${name}.json",
569
- requiredNames: REQUIRED_NAMES,
570
- basePath,
571
- });
572
- }
573
- `;
574
- }
575
-
576
- function generateValidateCliTs(): string {
577
- return `#!/usr/bin/env bun
578
- import { validate } from "./validate";
579
-
580
- await validate({ verbose: true });
581
- `;
582
- }
583
-
584
- function generatePackageJson(name: string, names: ReturnType<typeof deriveNames>): string {
585
- const pkg = {
586
- name: names.packageName,
587
- version: "0.0.1",
588
- type: "module",
589
- private: true,
590
- files: ["src/", "dist/"],
591
- exports: {
592
- ".": "./src/index.ts",
593
- "./*": "./src/*",
594
- "./manifest": "./dist/manifest.json",
595
- "./meta": "./dist/meta.json",
596
- "./types": "./dist/types/index.d.ts",
597
- },
598
- scripts: {
599
- generate: "bun run src/codegen/generate-cli.ts",
600
- validate: "bun run src/validate-cli.ts",
601
- docs: "bun src/codegen/docs-cli.ts",
602
- prepack: "bun run generate && bun run validate",
603
- },
604
- dependencies: {
605
- "@intentius/chant": "workspace:*",
606
- },
607
- devDependencies: {
608
- typescript: "^5.9.3",
609
- },
610
- };
611
-
612
- return JSON.stringify(pkg, null, 2) + "\n";
613
- }
614
-
615
- function generateTsConfig(): string {
616
- const config = {
617
- extends: "../../tsconfig.json",
618
- compilerOptions: {
619
- rootDir: "./src",
620
- outDir: "./dist",
621
- },
622
- include: ["src/**/*"],
623
- };
624
-
625
- return JSON.stringify(config, null, 2) + "\n";
626
- }
627
-
628
- function generateJustfile(name: string): string {
629
- return `# Default recipe - list all available commands
630
- default:
631
- @just --list
632
-
633
- # Generate types and metadata from upstream schemas
634
- generate:
635
- bun run src/codegen/generate-cli.ts
636
-
637
- # Validate generated artifacts
638
- validate:
639
- bun run src/validate-cli.ts
640
-
641
- # Generate docs site, install deps, and start dev server
642
- docs:
643
- bun run src/codegen/docs-cli.ts
644
- bun install --cwd docs
645
- bun --cwd docs dev
646
-
647
- # Build docs site for production
648
- docs-build:
649
- bun run src/codegen/docs-cli.ts
650
- bun install --cwd docs
651
- bun --cwd docs build
652
-
653
- # Package the lexicon (generate + validate)
654
- package: generate validate
655
- `;
656
- }
657
-
658
- function generateGitignore(): string {
659
- return `dist/
660
- node_modules/
661
- .cache/
662
- `;
663
- }
664
-
665
- function generateReadme(name: string, names: ReturnType<typeof deriveNames>): string {
666
- return `# ${names.packageName}
667
-
668
- ${name} lexicon plugin for [chant](https://github.com/intentius/chant).
669
-
670
- ## Getting started
671
-
672
- \`\`\`bash
673
- # Generate types from upstream spec
674
- just generate
675
-
676
- # Validate generated artifacts
677
- just validate
678
-
679
- # Generate documentation
680
- just docs
681
- \`\`\`
682
-
683
- ## Project structure
684
-
685
- - \`src/plugin.ts\` — LexiconPlugin with all lifecycle methods
686
- - \`src/serializer.ts\` — Build output serializer
687
- - \`src/codegen/\` — Code generation pipeline
688
- - \`src/spec/\` — Upstream schema fetching and parsing
689
- - \`src/lint/rules/\` — Lint rules
690
- - \`src/lsp/\` — LSP completions and hover
691
- - \`src/generated/\` — Generated artifacts (do not edit)
692
- `;
693
- }
694
-
695
- // ── Test file generators ─────────────────────────────────────────────
696
-
697
- function generatePluginTestTs(name: string, names: ReturnType<typeof deriveNames>): string {
698
- return `import { describe, expect, it } from "bun:test";
699
- import { ${names.pluginVarName} } from "./plugin";
700
- import { isLexiconPlugin } from "@intentius/chant/lexicon";
701
-
702
- describe("${name} plugin", () => {
703
- it("is a valid LexiconPlugin", () => {
704
- expect(isLexiconPlugin(${names.pluginVarName})).toBe(true);
705
- });
706
-
707
- it("has the correct name", () => {
708
- expect(${names.pluginVarName}.name).toBe("${name}");
709
- });
710
-
711
- it("has a serializer", () => {
712
- expect(${names.pluginVarName}.serializer).toBeDefined();
713
- });
714
- });
715
- `;
716
- }
717
-
718
- function generateSerializerTestTs(name: string, names: ReturnType<typeof deriveNames>): string {
719
- return `import { describe, expect, it } from "bun:test";
720
- import { ${names.serializerVarName} } from "./serializer";
721
-
722
- describe("${name} serializer", () => {
723
- it("serializes an empty map to valid JSON", () => {
724
- const result = ${names.serializerVarName}.serialize(new Map());
725
- expect(typeof result).toBe("string");
726
- expect(() => JSON.parse(result)).not.toThrow();
727
- });
728
-
729
- it("has the correct name", () => {
730
- expect(${names.serializerVarName}.name).toBe("${name}");
731
- });
732
- });
733
- `;
734
- }
735
-
736
- function generateCompletionsTestTs(): string {
737
- return `import { describe, expect, it } from "bun:test";
738
- import { completions } from "./completions";
739
-
740
- describe("LSP completions", () => {
741
- it("returns an array", () => {
742
- // TODO: Replace with a real CompletionContext
743
- const result = completions({} as any);
744
- expect(Array.isArray(result)).toBe(true);
745
- });
746
- });
747
- `;
748
- }
749
-
750
- function generateHoverTestTs(): string {
751
- return `import { describe, expect, it } from "bun:test";
752
- import { hover } from "./hover";
753
-
754
- describe("LSP hover", () => {
755
- it("returns undefined for unknown context", () => {
756
- // TODO: Replace with a real HoverContext
757
- const result = hover({} as any);
758
- expect(result).toBeUndefined();
759
- });
760
- });
761
- `;
762
- }
763
-
764
- // ── Example generators ───────────────────────────────────────────────
765
-
766
- function generateExamplePackageJson(name: string): string {
767
- return JSON.stringify(
768
- {
769
- name: `@intentius/chant-lexicon-${name}-example-getting-started`,
770
- version: "0.0.1",
771
- private: true,
772
- dependencies: {
773
- [`@intentius/chant-lexicon-${name}`]: "workspace:*",
774
- "@intentius/chant": "workspace:*",
775
- },
776
- },
777
- null,
778
- 2,
779
- ) + "\n";
780
- }
781
-
782
- function generateExampleInfraTs(name: string, names: ReturnType<typeof deriveNames>): string {
783
- return `/**
784
- * Getting-started example for the ${name} lexicon.
785
- *
786
- * TODO: Replace with a real infrastructure definition
787
- * that uses resources from the ${name} lexicon.
788
- */
789
-
790
- // import { SomeResource } from "${names.packageName}";
791
- //
792
- // export const myResource = SomeResource("example", {
793
- // // properties...
794
- // });
795
- `;
796
- }
797
-
798
- // ── Additional doc page generators ───────────────────────────────────
799
-
800
- function generateDocsGettingStartedMdx(name: string): string {
801
- const displayName = name.charAt(0).toUpperCase() + name.slice(1);
802
- return `---
803
- title: Getting Started
804
- description: Get started with the ${displayName} lexicon
805
- ---
806
-
807
- TODO: Document how to set up a project using the ${displayName} lexicon.
808
- `;
809
- }
810
-
811
- function generateDocsSerializationMdx(name: string): string {
812
- const displayName = name.charAt(0).toUpperCase() + name.slice(1);
813
- return `---
814
- title: Serialization
815
- description: ${displayName} output format
816
- ---
817
-
818
- TODO: Document the ${displayName} serialization format and output structure.
819
- `;
820
- }
821
-
822
- function generateDocsLintRulesMdx(name: string): string {
823
- const displayName = name.charAt(0).toUpperCase() + name.slice(1);
824
- return `---
825
- title: Lint Rules
826
- description: ${displayName} lint rules reference
827
- ---
828
-
829
- TODO: Document the lint rules provided by the ${displayName} lexicon.
830
- `;
831
- }
832
-
833
- // ── Docs site skeleton generators ────────────────────────────────────
834
-
835
- function generateDocsPackageJson(name: string): string {
836
- return JSON.stringify(
837
- {
838
- name: `@intentius/chant-lexicon-${name}-docs`,
839
- type: "module",
840
- version: "0.0.1",
841
- private: true,
842
- scripts: {
843
- dev: "astro dev",
844
- build: "astro build",
845
- preview: "astro preview",
846
- },
847
- dependencies: {
848
- "@astrojs/starlight": "^0.37.6",
849
- astro: "^5.6.1",
850
- sharp: "^0.34.2",
851
- },
852
- },
853
- null,
854
- 2,
855
- ) + "\n";
856
- }
857
-
858
- function generateDocsTsConfig(): string {
859
- return JSON.stringify(
860
- {
861
- extends: "astro/tsconfigs/strict",
862
- include: [".astro/types.d.ts", "**/*"],
863
- exclude: ["dist"],
864
- },
865
- null,
866
- 2,
867
- ) + "\n";
868
- }
869
-
870
- function generateDocsAstroConfig(name: string): string {
871
- const displayName = name.charAt(0).toUpperCase() + name.slice(1);
872
- return `// @ts-check
873
- import { defineConfig } from 'astro/config';
874
- import starlight from '@astrojs/starlight';
875
-
876
- export default defineConfig({
877
- integrations: [
878
- starlight({
879
- title: '${displayName}',
880
- sidebar: [
881
- { label: 'Overview', slug: '' },
882
- { label: 'Getting Started', slug: 'getting-started' },
883
- { label: 'Serialization', slug: 'serialization' },
884
- { label: 'Lint Rules', slug: 'lint-rules' },
885
- ],
886
- }),
887
- ],
888
- });
889
- `;
890
- }
891
-
892
- function generateDocsContentConfig(): string {
893
- return `import { defineCollection } from 'astro:content';
894
- import { docsLoader } from '@astrojs/starlight/loaders';
895
- import { docsSchema } from '@astrojs/starlight/schema';
896
-
897
- export const collections = {
898
- docs: defineCollection({ loader: docsLoader(), schema: docsSchema() }),
899
- };
900
- `;
901
- }
902
-
903
- function generateDocsIndexMdx(name: string): string {
904
- const displayName = name.charAt(0).toUpperCase() + name.slice(1);
905
- return `---
906
- title: Overview
907
- description: ${displayName} lexicon for chant
908
- ---
909
-
910
- Welcome to the ${displayName} lexicon documentation.
911
-
912
- Run \`just generate\` to populate this site with generated reference pages.
913
- `;
914
- }
915
-
916
73
  // ── Main command ─────────────────────────────────────────────────────
917
74
 
918
75
  /**
@@ -953,10 +110,6 @@ export async function initLexiconCommand(options: InitLexiconOptions): Promise<I
953
110
  "src/lint",
954
111
  "src/lint/rules",
955
112
  "src/lsp",
956
- "src/import",
957
- "src/composites",
958
- "src/actions",
959
- "src/lint/post-synth",
960
113
  "src/generated",
961
114
  "docs",
962
115
  "docs/src",
@@ -991,9 +144,6 @@ export async function initLexiconCommand(options: InitLexiconOptions): Promise<I
991
144
  "src/lsp/hover.ts": generateLspHoverTs(name),
992
145
  "src/lsp/completions.test.ts": generateCompletionsTestTs(),
993
146
  "src/lsp/hover.test.ts": generateHoverTestTs(),
994
- "src/import/parser.ts": generateImportParserTs(name),
995
- "src/import/generator.ts": generateImportGeneratorTs(name),
996
- "src/coverage.ts": generateCoverageTs(name),
997
147
  "src/plugin.test.ts": generatePluginTestTs(name, names),
998
148
  "src/serializer.test.ts": generateSerializerTestTs(name, names),
999
149
  "src/validate.ts": generateValidateTs(name),
@@ -1008,9 +158,6 @@ export async function initLexiconCommand(options: InitLexiconOptions): Promise<I
1008
158
  "docs/astro.config.mjs": generateDocsAstroConfig(name),
1009
159
  "docs/src/content.config.ts": generateDocsContentConfig(),
1010
160
  "docs/src/content/docs/index.mdx": generateDocsIndexMdx(name),
1011
- "docs/src/content/docs/getting-started.mdx": generateDocsGettingStartedMdx(name),
1012
- "docs/src/content/docs/serialization.mdx": generateDocsSerializationMdx(name),
1013
- "docs/src/content/docs/lint-rules.mdx": generateDocsLintRulesMdx(name),
1014
161
  "examples/getting-started/package.json": generateExamplePackageJson(name),
1015
162
  "examples/getting-started/src/infra.ts": generateExampleInfraTs(name, names),
1016
163
  };
@@ -1018,9 +165,6 @@ export async function initLexiconCommand(options: InitLexiconOptions): Promise<I
1018
165
  // Write .gitkeep files
1019
166
  const gitkeeps = [
1020
167
  "src/generated/.gitkeep",
1021
- "src/composites/.gitkeep",
1022
- "src/actions/.gitkeep",
1023
- "src/lint/post-synth/.gitkeep",
1024
168
  ];
1025
169
 
1026
170
  for (const gk of gitkeeps) {