@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
package/bin/chant CHANGED
@@ -15,6 +15,9 @@ MAIN_TS="$SCRIPT_DIR/../src/cli/main.ts"
15
15
 
16
16
  if command -v bun >/dev/null 2>&1; then
17
17
  exec bun "$MAIN_TS" "$@"
18
- else
18
+ elif command -v npx >/dev/null 2>&1; then
19
19
  exec npx tsx "$MAIN_TS" "$@"
20
+ else
21
+ echo "error: chant requires Bun (https://bun.sh) or Node.js (https://nodejs.org)" >&2
22
+ exit 1
20
23
  fi
package/package.json CHANGED
@@ -1,7 +1,26 @@
1
1
  {
2
2
  "name": "@intentius/chant",
3
- "version": "0.0.18",
3
+ "version": "0.0.24",
4
+ "description": "Declarative infrastructure-as-code toolkit — TypeScript on Bun",
4
5
  "license": "Apache-2.0",
6
+ "homepage": "https://intentius.io/chant",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/intentius/chant.git",
10
+ "directory": "packages/core"
11
+ },
12
+ "bugs": {
13
+ "url": "https://github.com/intentius/chant/issues"
14
+ },
15
+ "keywords": [
16
+ "infrastructure-as-code",
17
+ "iac",
18
+ "typescript",
19
+ "declarative",
20
+ "cloudformation",
21
+ "aws",
22
+ "devops"
23
+ ],
5
24
  "type": "module",
6
25
  "files": [
7
26
  "src/",
package/src/build.test.ts CHANGED
@@ -189,10 +189,12 @@ export const betaEntity = {
189
189
  expect(result.outputs.has("alpha")).toBe(true);
190
190
  expect(result.outputs.has("beta")).toBe(true);
191
191
 
192
- const alphaOutput = JSON.parse(result.outputs.get("alpha")!);
192
+ const alphaRaw = result.outputs.get("alpha")!;
193
+ const alphaOutput = JSON.parse(typeof alphaRaw === "string" ? alphaRaw : alphaRaw.primary);
193
194
  expect(alphaOutput.alpha).toContain("alphaEntity");
194
195
 
195
- const betaOutput = JSON.parse(result.outputs.get("beta")!);
196
+ const betaRaw = result.outputs.get("beta")!;
197
+ const betaOutput = JSON.parse(typeof betaRaw === "string" ? betaRaw : betaRaw.primary);
196
198
  expect(betaOutput.beta).toContain("betaEntity");
197
199
  });
198
200
 
package/src/build.ts CHANGED
@@ -29,6 +29,8 @@ export interface BuildResult {
29
29
  outputs: Map<string, string | SerializerResult>;
30
30
  /** Map of entity name to Declarable entity */
31
31
  entities: Map<string, Declarable>;
32
+ /** Resource-level dependency graph from discovery */
33
+ dependencies: Map<string, Set<string>>;
32
34
  /** Array of warnings encountered during the build */
33
35
  warnings: string[];
34
36
  /** Array of errors encountered during discovery and build */
@@ -430,6 +432,7 @@ export async function build(
430
432
  return {
431
433
  outputs,
432
434
  entities: discoveryResult.entities,
435
+ dependencies: discoveryResult.dependencies,
433
436
  warnings,
434
437
  errors,
435
438
  manifest,
@@ -36,6 +36,7 @@ class SimpleResourceBuilder extends Builder<TestResource> {
36
36
  throw new Error("Name and type are required");
37
37
  }
38
38
  return {
39
+ lexicon: "test",
39
40
  entityType: "TestResource",
40
41
  [DECLARABLE_MARKER]: true,
41
42
  name: this.name,
@@ -72,6 +73,7 @@ class ValidatedResourceBuilder extends Builder<TestResource> {
72
73
  throw new Error("Name and type are required");
73
74
  }
74
75
  return {
76
+ lexicon: "test",
75
77
  entityType: "TestResource",
76
78
  [DECLARABLE_MARKER]: true,
77
79
  name: this.name,
@@ -103,6 +105,7 @@ class DefaultsResourceBuilder extends Builder<TestResource> {
103
105
 
104
106
  build(): TestResource {
105
107
  return {
108
+ lexicon: "test",
106
109
  entityType: "TestResource",
107
110
  [DECLARABLE_MARKER]: true,
108
111
  name: this.name,
@@ -8,9 +8,6 @@ export default defineConfig({
8
8
  title: 'Fixture',
9
9
  sidebar: [
10
10
  { label: 'Overview', slug: '' },
11
- { label: 'Getting Started', slug: 'getting-started' },
12
- { label: 'Serialization', slug: 'serialization' },
13
- { label: 'Lint Rules', slug: 'lint-rules' },
14
11
  ],
15
12
  }),
16
13
  ],
@@ -3,6 +3,7 @@ import type { Serializer, SerializerResult } from "../../serializer";
3
3
  import type { LexiconPlugin } from "../../lexicon";
4
4
  import { runPostSynthChecks } from "../../lint/post-synth";
5
5
  import type { PostSynthCheck } from "../../lint/post-synth";
6
+ import { sortedJsonReplacer } from "../../utils";
6
7
  import { formatError, formatWarning, formatSuccess, formatBold, formatInfo } from "../format";
7
8
  import { writeFileSync } from "fs";
8
9
  import { resolve, dirname, join } from "path";
@@ -214,6 +215,10 @@ export async function buildCommand(options: BuildOptions): Promise<BuildResult>
214
215
  const resourceCount = result.entities.size;
215
216
  const fileCount = result.sourceFileCount;
216
217
 
218
+ if (fileCount === 0 && errors.length === 0) {
219
+ console.error(formatInfo("No source files found — create .ts files in the target directory"));
220
+ }
221
+
217
222
  if (options.verbose && errors.length === 0) {
218
223
  console.error(
219
224
  formatSuccess(
@@ -231,18 +236,6 @@ export async function buildCommand(options: BuildOptions): Promise<BuildResult>
231
236
  };
232
237
  }
233
238
 
234
- /**
235
- * JSON.stringify replacer that sorts object keys for deterministic output
236
- */
237
- function sortedJsonReplacer(_key: string, value: unknown): unknown {
238
- if (value && typeof value === "object" && !Array.isArray(value)) {
239
- return Object.fromEntries(
240
- Object.entries(value as Record<string, unknown>).sort(([a], [b]) => a.localeCompare(b))
241
- );
242
- }
243
- return value;
244
- }
245
-
246
239
  /**
247
240
  * Simple JSON to YAML converter
248
241
  */
@@ -86,7 +86,8 @@ export const myBucket = {
86
86
  const combined: Record<string, unknown> = {};
87
87
  const sortedSerializerNames = [...buildResult.outputs.keys()].sort();
88
88
  for (const serializerName of sortedSerializerNames) {
89
- combined[serializerName] = JSON.parse(buildResult.outputs.get(serializerName)!);
89
+ const raw = buildResult.outputs.get(serializerName)!;
90
+ combined[serializerName] = JSON.parse(typeof raw === "string" ? raw : raw.primary);
90
91
  }
91
92
 
92
93
  // Sort keys to match diffCommand behavior
@@ -46,7 +46,8 @@ export async function diffCommand(options: DiffOptions): Promise<DiffResult> {
46
46
  const combined: Record<string, unknown> = {};
47
47
  const sortedLexiconNames = [...result.outputs.keys()].sort();
48
48
  for (const lexiconName of sortedLexiconNames) {
49
- combined[lexiconName] = JSON.parse(result.outputs.get(lexiconName)!);
49
+ const raw = result.outputs.get(lexiconName)!;
50
+ combined[lexiconName] = JSON.parse(typeof raw === "string" ? raw : raw.primary);
50
51
  }
51
52
  const currentOutput = JSON.stringify(combined, sortedJsonReplacer, 2);
52
53
 
@@ -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
+ }