@intentius/chant 0.1.1 → 0.1.5

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 (128) hide show
  1. package/bin/chant +1 -10
  2. package/package.json +3 -5
  3. package/src/attrref.test.ts +1 -1
  4. package/src/bench.test.ts +1 -1
  5. package/src/build.test.ts +3 -5
  6. package/src/builder.test.ts +1 -1
  7. package/src/cli/commands/__fixtures__/init-lexicon-output/justfile +8 -8
  8. package/src/cli/commands/__fixtures__/init-lexicon-output/package.json +4 -4
  9. package/src/cli/commands/__fixtures__/init-lexicon-output/src/codegen/generate-cli.ts +1 -1
  10. package/src/cli/commands/__fixtures__/init-lexicon-output/src/plugin.ts +3 -3
  11. package/src/cli/commands/__fixtures__/init-lexicon-output/src/validate-cli.ts +1 -1
  12. package/src/cli/commands/__snapshots__/init-lexicon.test.ts.snap +7 -7
  13. package/src/cli/commands/build.test.ts +1 -1
  14. package/src/cli/commands/diff.test.ts +1 -1
  15. package/src/cli/commands/doctor.test.ts +1 -1
  16. package/src/cli/commands/doctor.ts +7 -7
  17. package/src/cli/commands/import.test.ts +1 -1
  18. package/src/cli/commands/init-lexicon/templates/codegen.ts +1 -1
  19. package/src/cli/commands/init-lexicon/templates/plugin.ts +3 -3
  20. package/src/cli/commands/init-lexicon/templates/project.ts +13 -13
  21. package/src/cli/commands/init-lexicon/templates/tests.ts +4 -4
  22. package/src/cli/commands/init-lexicon.test.ts +2 -2
  23. package/src/cli/commands/init-lexicon.ts +1 -1
  24. package/src/cli/commands/init.test.ts +1 -1
  25. package/src/cli/commands/init.ts +1 -2
  26. package/src/cli/commands/lint.test.ts +3 -3
  27. package/src/cli/commands/list.test.ts +2 -2
  28. package/src/cli/commands/onboard.test.ts +33 -33
  29. package/src/cli/commands/onboard.ts +13 -13
  30. package/src/cli/commands/update.test.ts +1 -1
  31. package/src/cli/conflict-check.test.ts +7 -2
  32. package/src/cli/format.test.ts +1 -1
  33. package/src/cli/lsp/server.test.ts +8 -4
  34. package/src/cli/main.test.ts +1 -1
  35. package/src/cli/main.ts +6 -5
  36. package/src/cli/mcp/server.test.ts +1 -1
  37. package/src/cli/plugins.test.ts +1 -1
  38. package/src/cli/reporters/stylish.test.ts +1 -1
  39. package/src/cli/watch.test.ts +1 -1
  40. package/src/codegen/docs-interpolation.test.ts +3 -3
  41. package/src/codegen/docs-rules.test.ts +1 -1
  42. package/src/codegen/docs.ts +1 -1
  43. package/src/codegen/fetch.test.ts +1 -1
  44. package/src/codegen/generate-registry.test.ts +1 -1
  45. package/src/codegen/generate-runtime-index.test.ts +1 -1
  46. package/src/codegen/generate-typescript.test.ts +1 -1
  47. package/src/codegen/generate.test.ts +1 -1
  48. package/src/codegen/json-patch.test.ts +1 -1
  49. package/src/codegen/json-schema.test.ts +1 -1
  50. package/src/codegen/topo-sort.test.ts +1 -1
  51. package/src/codegen/typecheck.test.ts +1 -1
  52. package/src/codegen/typecheck.ts +18 -5
  53. package/src/codegen/validate.test.ts +1 -1
  54. package/src/composite.test.ts +17 -16
  55. package/src/composite.ts +5 -4
  56. package/src/config.test.ts +3 -3
  57. package/src/config.ts +0 -4
  58. package/src/declarable.test.ts +1 -1
  59. package/src/detectLexicon.test.ts +1 -1
  60. package/src/discovery/cache.test.ts +1 -1
  61. package/src/discovery/collect.test.ts +1 -1
  62. package/src/discovery/cycles.test.ts +1 -1
  63. package/src/discovery/files.test.ts +1 -1
  64. package/src/discovery/graph.test.ts +1 -1
  65. package/src/discovery/import.test.ts +4 -3
  66. package/src/discovery/index.test.ts +1 -1
  67. package/src/discovery/resolve.test.ts +1 -1
  68. package/src/errors.test.ts +1 -1
  69. package/src/import/base-parser.test.ts +1 -1
  70. package/src/import/ir-utils.test.ts +1 -1
  71. package/src/intrinsic-interpolation.test.ts +1 -1
  72. package/src/intrinsic.test.ts +1 -1
  73. package/src/lexicon-integrity.test.ts +2 -2
  74. package/src/lexicon-manifest.test.ts +1 -1
  75. package/src/lexicon-output.test.ts +1 -1
  76. package/src/lexicon-output.ts +5 -3
  77. package/src/lexicon-schema.test.ts +1 -1
  78. package/src/lint/config-overrides.test.ts +2 -2
  79. package/src/lint/config.test.ts +2 -2
  80. package/src/lint/config.ts +1 -1
  81. package/src/lint/declarative.test.ts +1 -1
  82. package/src/lint/discover.test.ts +1 -1
  83. package/src/lint/discover.ts +10 -0
  84. package/src/lint/engine.test.ts +1 -1
  85. package/src/lint/named-checks.test.ts +1 -1
  86. package/src/lint/parser.test.ts +2 -2
  87. package/src/lint/post-synth.test.ts +1 -1
  88. package/src/lint/rule-loader.test.ts +2 -2
  89. package/src/lint/rule-options.test.ts +2 -2
  90. package/src/lint/rule-registry.test.ts +1 -1
  91. package/src/lint/rule.test.ts +1 -1
  92. package/src/lint/rules/cor017-composite-name-match.test.ts +1 -1
  93. package/src/lint/rules/cor018-composite-prefer-lexicon-type.test.ts +1 -1
  94. package/src/lint/rules/declarable-naming-convention.test.ts +1 -1
  95. package/src/lint/rules/evl001-non-literal-expression.test.ts +1 -1
  96. package/src/lint/rules/evl002-control-flow-resource.test.ts +1 -1
  97. package/src/lint/rules/evl003-dynamic-property-access.test.ts +1 -1
  98. package/src/lint/rules/evl004-spread-non-const.test.ts +1 -1
  99. package/src/lint/rules/evl005-resource-block-body.test.ts +1 -1
  100. package/src/lint/rules/evl007-invalid-siblings.test.ts +1 -1
  101. package/src/lint/rules/evl009-composite-no-constant.test.ts +1 -1
  102. package/src/lint/rules/evl010-composite-no-transform.test.ts +1 -1
  103. package/src/lint/rules/export-required.test.ts +1 -1
  104. package/src/lint/rules/file-declarable-limit.test.ts +1 -1
  105. package/src/lint/rules/flat-declarations.test.ts +1 -1
  106. package/src/lint/rules/no-cyclic-declarable-ref.test.ts +1 -1
  107. package/src/lint/rules/no-redundant-type-import.test.ts +1 -1
  108. package/src/lint/rules/no-redundant-value-cast.test.ts +1 -1
  109. package/src/lint/rules/no-string-ref.test.ts +1 -1
  110. package/src/lint/rules/no-unused-declarable-import.test.ts +1 -1
  111. package/src/lint/rules/no-unused-declarable.test.ts +1 -1
  112. package/src/lint/rules/single-concern-file.test.ts +1 -1
  113. package/src/lint/selectors.test.ts +1 -1
  114. package/src/project-validation.test.ts +2 -2
  115. package/src/pseudo-parameter.test.ts +1 -1
  116. package/src/resource-attributes.test.ts +2 -2
  117. package/src/runtime-adapter.ts +13 -68
  118. package/src/runtime.ts +3 -3
  119. package/src/serializer-walker.test.ts +2 -1
  120. package/src/sort.test.ts +1 -1
  121. package/src/stack-output.ts +2 -2
  122. package/src/toml.test.ts +2 -2
  123. package/src/types.test.ts +1 -1
  124. package/src/utils.test.ts +1 -1
  125. package/src/utils.ts +2 -2
  126. package/src/validation.test.ts +1 -1
  127. package/src/yaml.test.ts +1 -1
  128. package/src/yaml.ts +4 -1
@@ -1,4 +1,4 @@
1
- import { describe, test, expect, beforeEach, afterEach } from "bun:test";
1
+ import { describe, test, expect, beforeEach, afterEach } from "vitest";
2
2
  import { mkdirSync, writeFileSync, readFileSync, rmSync } from "fs";
3
3
  import { join } from "path";
4
4
  import { tmpdir } from "os";
@@ -64,32 +64,32 @@ jobs:
64
64
  steps:
65
65
  - name: Generate lexicon artifacts
66
66
  run: |
67
- bun run --cwd lexicons/aws prepack
68
- bun run --cwd lexicons/gitlab prepack
69
- bun run --cwd lexicons/k8s prepack
67
+ npm run --prefix lexicons/aws prepack
68
+ npm run --prefix lexicons/gitlab prepack
69
+ npm run --prefix lexicons/k8s prepack
70
70
  - name: Run tests
71
- run: bun test
71
+ run: npx vitest run
72
72
 
73
73
  test:
74
74
  steps:
75
75
  - name: Generate lexicon artifacts
76
76
  run: |
77
- bun run --cwd lexicons/aws prepack
78
- bun run --cwd lexicons/gitlab prepack
79
- bun run --cwd lexicons/k8s prepack
77
+ npm run --prefix lexicons/aws prepack
78
+ npm run --prefix lexicons/gitlab prepack
79
+ npm run --prefix lexicons/k8s prepack
80
80
  - name: Run tests
81
- run: bun test
81
+ run: npx vitest run
82
82
 
83
83
  validate:
84
84
  steps:
85
85
  - name: Generate and validate AWS lexicon
86
- run: bun run --cwd lexicons/aws prepack
86
+ run: npm run --prefix lexicons/aws prepack
87
87
 
88
88
  - name: Generate and validate GitLab lexicon
89
- run: bun run --cwd lexicons/gitlab prepack
89
+ run: npm run --prefix lexicons/gitlab prepack
90
90
 
91
91
  - name: Generate and validate K8s lexicon
92
- run: bun run --cwd lexicons/k8s prepack
92
+ run: npm run --prefix lexicons/k8s prepack
93
93
  `;
94
94
 
95
95
  test("inserts prepack line in multi-line blocks", () => {
@@ -105,7 +105,7 @@ jobs:
105
105
  for (let i = 0; i <= lines.length; i++) {
106
106
  const isPrepack =
107
107
  i < lines.length &&
108
- lines[i].includes("bun run --cwd lexicons/") &&
108
+ lines[i].includes("npm run --prefix lexicons/") &&
109
109
  lines[i].includes("prepack");
110
110
  if (isPrepack && groupStart === -1) {
111
111
  groupStart = i;
@@ -155,13 +155,13 @@ jobs:
155
155
  const block = [
156
156
  "",
157
157
  " - name: Generate and validate Terraform lexicon",
158
- " run: bun run --cwd lexicons/terraform prepack",
158
+ " run: npm run --prefix lexicons/terraform prepack",
159
159
  ];
160
160
  lines.splice(lastValidateRunIdx + 1, 0, ...block);
161
161
  const result = lines.join("\n");
162
162
 
163
163
  expect(result).toContain("Generate and validate Terraform lexicon");
164
- expect(result).toContain("bun run --cwd lexicons/terraform prepack");
164
+ expect(result).toContain("npm run --prefix lexicons/terraform prepack");
165
165
  });
166
166
  });
167
167
 
@@ -175,20 +175,20 @@ on:
175
175
  jobs:
176
176
  test:
177
177
  steps:
178
- - run: bun run --cwd lexicons/aws prepack
179
- - run: bun run --cwd lexicons/gitlab prepack
180
- - run: bun run --cwd lexicons/k8s prepack
181
- - run: bun test
178
+ - run: npm run --prefix lexicons/aws prepack
179
+ - run: npm run --prefix lexicons/gitlab prepack
180
+ - run: npm run --prefix lexicons/k8s prepack
181
+ - run: npx vitest run
182
182
 
183
183
  publish:
184
184
  steps:
185
185
  - name: Publish @intentius/chant-lexicon-aws
186
186
  working-directory: lexicons/aws
187
- run: bun publish --access public --tolerate-republish
187
+ run: npm publish --access public
188
188
 
189
189
  - name: Publish @intentius/chant-lexicon-k8s
190
190
  working-directory: lexicons/k8s
191
- run: bun publish --access public --tolerate-republish
191
+ run: npm publish --access public
192
192
  `;
193
193
 
194
194
  test("adds prepack line in test job", () => {
@@ -199,10 +199,10 @@ jobs:
199
199
  // Find contiguous prepack group in test job
200
200
  const insertAfter: number[] = [];
201
201
  for (let i = 0; i < lines.length; i++) {
202
- if (!lines[i].includes("bun run --cwd lexicons/") || !lines[i].includes("prepack")) continue;
202
+ if (!lines[i].includes("npm run --prefix lexicons/") || !lines[i].includes("prepack")) continue;
203
203
  const nextIsAlsoPrepack =
204
204
  i + 1 < lines.length &&
205
- lines[i + 1].includes("bun run --cwd lexicons/") &&
205
+ lines[i + 1].includes("npm run --prefix lexicons/") &&
206
206
  lines[i + 1].includes("prepack");
207
207
  if (!nextIsAlsoPrepack) insertAfter.push(i);
208
208
  }
@@ -224,7 +224,7 @@ jobs:
224
224
 
225
225
  let lastPublishRunIdx = -1;
226
226
  for (let i = 0; i < lines.length; i++) {
227
- if (lines[i].includes("bun publish --access public --tolerate-republish")) {
227
+ if (lines[i].includes("npm publish --access public")) {
228
228
  lastPublishRunIdx = i;
229
229
  }
230
230
  }
@@ -235,7 +235,7 @@ jobs:
235
235
  "",
236
236
  " - name: Publish @intentius/chant-lexicon-terraform",
237
237
  " working-directory: lexicons/terraform",
238
- " run: bun publish --access public --tolerate-republish",
238
+ " run: npm publish --access public",
239
239
  ];
240
240
  lines.splice(lastPublishRunIdx + 1, 0, ...block);
241
241
  const result = lines.join("\n");
@@ -248,13 +248,13 @@ jobs:
248
248
  // ── Dockerfile ──────────────────────────────────────────
249
249
 
250
250
  describe("Dockerfile patching", () => {
251
- const dockerContent = `FROM oven/bun:latest
251
+ const dockerContent = `FROM node:22-slim
252
252
  WORKDIR /app
253
253
  COPY . .
254
- RUN bun install
255
- RUN bun run --cwd lexicons/aws prepack
256
- RUN bun run --cwd lexicons/gitlab prepack
257
- RUN bun run --cwd lexicons/k8s prepack
254
+ RUN npm install
255
+ RUN npm run --prefix lexicons/aws prepack
256
+ RUN npm run --prefix lexicons/gitlab prepack
257
+ RUN npm run --prefix lexicons/k8s prepack
258
258
  COPY test/integration.sh /app/test/integration.sh
259
259
  `;
260
260
 
@@ -265,7 +265,7 @@ COPY test/integration.sh /app/test/integration.sh
265
265
 
266
266
  let lastIdx = -1;
267
267
  for (let i = 0; i < lines.length; i++) {
268
- if (lines[i].includes("bun run --cwd lexicons/") && lines[i].includes("prepack")) {
268
+ if (lines[i].includes("npm run --prefix lexicons/") && lines[i].includes("prepack")) {
269
269
  lastIdx = i;
270
270
  }
271
271
  }
@@ -276,7 +276,7 @@ COPY test/integration.sh /app/test/integration.sh
276
276
  lines.splice(lastIdx + 1, 0, newLine);
277
277
  const result = lines.join("\n");
278
278
 
279
- expect(result).toContain("RUN bun run --cwd lexicons/terraform prepack");
279
+ expect(result).toContain("RUN npm run --prefix lexicons/terraform prepack");
280
280
  // Should come after k8s line
281
281
  const k8sIdx = result.indexOf("lexicons/k8s prepack");
282
282
  const tfIdx = result.indexOf("lexicons/terraform prepack");
@@ -284,7 +284,7 @@ COPY test/integration.sh /app/test/integration.sh
284
284
  });
285
285
 
286
286
  test("does not add duplicate", () => {
287
- const withTerraform = dockerContent + "RUN bun run --cwd lexicons/terraform prepack\n";
287
+ const withTerraform = dockerContent + "RUN npm run --prefix lexicons/terraform prepack\n";
288
288
  writeFileSync(join(root, "test/Dockerfile.smoke"), withTerraform);
289
289
  const content = readFileSync(join(root, "test/Dockerfile.smoke"), "utf-8");
290
290
 
@@ -64,7 +64,7 @@ function insertPrepackInContiguousGroups(lines: string[], name: string): boolean
64
64
  for (let i = 0; i <= lines.length; i++) {
65
65
  const isPrepack =
66
66
  i < lines.length &&
67
- lines[i].includes("bun run --cwd lexicons/") &&
67
+ lines[i].includes("npm run --prefix lexicons/") &&
68
68
  lines[i].includes("prepack");
69
69
  if (isPrepack && groupStart === -1) {
70
70
  groupStart = i;
@@ -97,10 +97,10 @@ function insertPrepackAfterEach(lines: string[], name: string): boolean {
97
97
 
98
98
  const insertAfter: number[] = [];
99
99
  for (let i = 0; i < lines.length; i++) {
100
- if (!lines[i].includes("bun run --cwd lexicons/") || !lines[i].includes("prepack")) continue;
100
+ if (!lines[i].includes("npm run --prefix lexicons/") || !lines[i].includes("prepack")) continue;
101
101
  const nextIsAlsoPrepack =
102
102
  i + 1 < lines.length &&
103
- lines[i + 1].includes("bun run --cwd lexicons/") &&
103
+ lines[i + 1].includes("npm run --prefix lexicons/") &&
104
104
  lines[i + 1].includes("prepack");
105
105
  if (!nextIsAlsoPrepack) {
106
106
  insertAfter.push(i);
@@ -122,8 +122,8 @@ function insertPrepackAfterEach(lines: string[], name: string): boolean {
122
122
  * and add a new validate step.
123
123
  *
124
124
  * The file has two patterns:
125
- * 1. Multi-line blocks (check + test jobs): `run: |\n bun run --cwd lexicons/aws prepack\n ...`
126
- * 2. Standalone steps (validate job): `- name: Generate and validate ...\n run: bun run --cwd ...`
125
+ * 1. Multi-line blocks (check + test jobs): `run: |\n npm run --prefix lexicons/aws prepack\n ...`
126
+ * 2. Standalone steps (validate job): `- name: Generate and validate ...\n run: npm run --prefix ...`
127
127
  *
128
128
  * We only insert into pattern 1 (contiguous groups) and separately add a new pattern 2 step.
129
129
  */
@@ -160,7 +160,7 @@ function patchCiWorkflow(root: string, name: string): { patched: boolean; reason
160
160
  const block = [
161
161
  "",
162
162
  ` - name: ${validateStepName}`,
163
- ` run: bun run --cwd lexicons/${name} prepack`,
163
+ ` run: npm run --prefix lexicons/${name} prepack`,
164
164
  ];
165
165
  lines.splice(lastValidateRunIdx + 1, 0, ...block);
166
166
  }
@@ -190,7 +190,7 @@ function patchPublishWorkflow(root: string, name: string): { patched: boolean; r
190
190
  // Add publish step after the last existing publish step
191
191
  let lastPublishRunIdx = -1;
192
192
  for (let i = 0; i < lines.length; i++) {
193
- if (lines[i].includes("bun publish --access public --tolerate-republish")) {
193
+ if (lines[i].includes("npm publish --access public")) {
194
194
  lastPublishRunIdx = i;
195
195
  }
196
196
  }
@@ -200,7 +200,7 @@ function patchPublishWorkflow(root: string, name: string): { patched: boolean; r
200
200
  "",
201
201
  ` - name: Publish @intentius/chant-lexicon-${name}`,
202
202
  ` working-directory: lexicons/${name}`,
203
- " run: bun publish --access public --tolerate-republish",
203
+ " run: npm publish --access public",
204
204
  ];
205
205
  lines.splice(lastPublishRunIdx + 1, 0, ...block);
206
206
  }
@@ -251,15 +251,15 @@ export function onboardCommand(options: OnboardOptions): OnboardResult {
251
251
 
252
252
  // 4. Dockerfiles
253
253
  const dockerBun = join(root, "test/Dockerfile.smoke");
254
- const dockerNode = join(root, "test/Dockerfile.smoke-node");
254
+ const dockerNpm = join(root, "test/Dockerfile.smoke-npm");
255
255
 
256
256
  const db = patchDockerfile(dockerBun, options.name);
257
257
  if (db.patched) patched.push("Dockerfile.smoke (prepack)");
258
258
  else skipped.push(`Dockerfile.smoke: ${db.reason}`);
259
259
 
260
- const dn = patchDockerfile(dockerNode, options.name);
261
- if (dn.patched) patched.push("Dockerfile.smoke-node (prepack)");
262
- else skipped.push(`Dockerfile.smoke-node: ${dn.reason}`);
260
+ const dn = patchDockerfile(dockerNpm, options.name);
261
+ if (dn.patched) patched.push("Dockerfile.smoke-npm (prepack)");
262
+ else skipped.push(`Dockerfile.smoke-npm: ${dn.reason}`);
263
263
 
264
264
  return { success: true, patched, skipped };
265
265
  }
@@ -293,7 +293,7 @@ export async function printOnboardResult(result: OnboardResult, name: string): P
293
293
  console.log(` 1. Create an example: lexicons/${name}/examples/<example-name>/`);
294
294
  console.log(` (must depend on @intentius/chant-lexicon-${name} for workspace resolution)`);
295
295
  console.log(` 2. Add smoke tests to test/integration.sh`);
296
- console.log(` 3. Run: bun install (to update workspace links)`);
296
+ console.log(` 3. Run: npm install (to update workspace links)`);
297
297
  console.log(` 4. First npm publish: tag with v<version> and push`);
298
298
  console.log(` 5. Run: chant dev check-lexicon lexicons/${name} (to see completeness status)`);
299
299
  console.log(formatWarning({
@@ -1,4 +1,4 @@
1
- import { describe, test, expect } from "bun:test";
1
+ import { describe, test, expect } from "vitest";
2
2
  import { updateCommand } from "./update";
3
3
  import { withTestDir } from "@intentius/chant-test-utils";
4
4
  import { writeFileSync, mkdirSync, existsSync, readFileSync } from "fs";
@@ -1,7 +1,8 @@
1
- import { describe, test, expect } from "bun:test";
1
+ import { describe, test, expect } from "vitest";
2
2
  import { checkConflicts } from "./conflict-check";
3
3
  import type { LexiconPlugin } from "../lexicon";
4
4
 
5
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
5
6
  const mockSerializer = { name: "test", serialize: () => ({}) } as any;
6
7
 
7
8
  const noopAsync = async () => {};
@@ -31,6 +32,7 @@ function makePlugin(
31
32
  category: "correctness" as const,
32
33
  check: () => [],
33
34
  }));
35
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
34
36
  (plugin as any).lintRules = () => rules;
35
37
  }
36
38
 
@@ -40,6 +42,7 @@ function makePlugin(
40
42
  description: "",
41
43
  content: "",
42
44
  }));
45
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
43
46
  (plugin as any).skills = () => skills;
44
47
  }
45
48
 
@@ -50,6 +53,7 @@ function makePlugin(
50
53
  inputSchema: { type: "object" as const, properties: {} },
51
54
  handler: async () => "",
52
55
  }));
56
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
53
57
  (plugin as any).mcpTools = () => tools;
54
58
  }
55
59
 
@@ -60,6 +64,7 @@ function makePlugin(
60
64
  description: "",
61
65
  handler: async () => "",
62
66
  }));
67
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
63
68
  (plugin as any).mcpResources = () => resources;
64
69
  }
65
70
 
@@ -261,7 +266,7 @@ describe("skill naming consistency", () => {
261
266
  test("all skill names match chant-{lexicon}(-{topic})* pattern", async () => {
262
267
  const { readdirSync, readFileSync } = await import("fs");
263
268
  const { join } = await import("path");
264
- const lexiconsDir = join(import.meta.dir, "../../../../lexicons");
269
+ const lexiconsDir = join(import.meta.dirname, "../../../../lexicons");
265
270
  const lexiconNames = readdirSync(lexiconsDir, { withFileTypes: true })
266
271
  .filter((d) => d.isDirectory())
267
272
  .map((d) => d.name);
@@ -1,4 +1,4 @@
1
- import { describe, test, expect, beforeEach, afterEach } from "bun:test";
1
+ import { describe, test, expect, beforeEach, afterEach } from "vitest";
2
2
  import {
3
3
  formatError,
4
4
  formatWarning,
@@ -1,4 +1,4 @@
1
- import { describe, test, expect, beforeEach } from "bun:test";
1
+ import { describe, test, expect, beforeEach, vi } from "vitest";
2
2
  import { LspServer } from "./server";
3
3
  import { computeCapabilities } from "./capabilities";
4
4
  import { toLspDiagnostics } from "./diagnostics";
@@ -217,12 +217,16 @@ describe("LspServer", () => {
217
217
  method: "textDocument/didOpen",
218
218
  params: { textDocument: { uri: "file:///a.ts", text: "const x = 1;" } },
219
219
  });
220
- // Give async diagnostics time to emit
221
- await new Promise((r) => setTimeout(r, 50));
220
+ // Wait for async diagnostics to emit (dynamic import may take longer in Node.js)
221
+ await vi.waitFor(() => {
222
+ const diagNotif = server.sentNotifications.find(
223
+ (n) => n.method === "textDocument/publishDiagnostics",
224
+ );
225
+ expect(diagNotif).toBeDefined();
226
+ }, { timeout: 2000 });
222
227
  const diagNotif = server.sentNotifications.find(
223
228
  (n) => n.method === "textDocument/publishDiagnostics",
224
229
  );
225
- expect(diagNotif).toBeDefined();
226
230
  expect(diagNotif!.params.uri).toBe("file:///a.ts");
227
231
  });
228
232
  });
@@ -1,4 +1,4 @@
1
- import { describe, test, expect } from "bun:test";
1
+ import { describe, test, expect } from "vitest";
2
2
  import { parseArgs } from "./main";
3
3
  import { resolveCommand, type CommandDef, type ParsedArgs } from "./registry";
4
4
 
package/src/cli/main.ts CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env bun
1
+ #!/usr/bin/env tsx
2
2
 
3
3
  import { resolve } from "node:path";
4
4
  import { formatSuccess, formatError } from "./format";
@@ -234,10 +234,10 @@ async function main(): Promise<void> {
234
234
  // Initialize runtime adapter early — before plugins or commands run
235
235
  const projectPath0 = resolve(args.path === "." ? "." : args.path);
236
236
  try {
237
- const { config } = await loadChantConfig(projectPath0);
238
- initRuntime(config.runtime);
237
+ await loadChantConfig(projectPath0);
238
+ initRuntime();
239
239
  } catch {
240
- // Config may not exist yet (e.g. `chant init`); auto-detect runtime
240
+ // Config may not exist yet (e.g. `chant init`)
241
241
  initRuntime();
242
242
  }
243
243
 
@@ -263,7 +263,8 @@ async function main(): Promise<void> {
263
263
  }
264
264
 
265
265
  // Only run main when executed directly, not when imported
266
- if (import.meta.main) {
266
+ const isMain = import.meta.url === `file://${process.argv[1]}`;
267
+ if (isMain) {
267
268
  main().catch((err) => {
268
269
  const verbose = process.argv.includes("--verbose") || process.argv.includes("-v");
269
270
  if (verbose && err instanceof Error && err.stack) {
@@ -1,4 +1,4 @@
1
- import { describe, test, expect, beforeEach, afterEach } from "bun:test";
1
+ import { describe, test, expect, beforeEach, afterEach } from "vitest";
2
2
  import { McpServer } from "./server";
3
3
  import { mkdir, rm, writeFile } from "node:fs/promises";
4
4
  import { join } from "node:path";
@@ -1,4 +1,4 @@
1
- import { describe, test, expect } from "bun:test";
1
+ import { describe, test, expect } from "vitest";
2
2
  import { loadPlugin, loadPlugins } from "./plugins";
3
3
  import { isLexiconPlugin } from "../lexicon";
4
4
 
@@ -1,4 +1,4 @@
1
- import { describe, test, expect, beforeEach, afterEach } from "bun:test";
1
+ import { describe, test, expect, beforeEach, afterEach } from "vitest";
2
2
  import { formatStylish, formatSummary, formatJson, formatSarif } from "./stylish";
3
3
  import type { LintDiagnostic, LintRule } from "../../lint/rule";
4
4
 
@@ -1,4 +1,4 @@
1
- import { describe, test, expect } from "bun:test";
1
+ import { describe, test, expect } from "vitest";
2
2
  import { shouldWatch, formatTimestamp, formatChangedFiles } from "./watch";
3
3
 
4
4
  describe("shouldWatch", () => {
@@ -1,4 +1,4 @@
1
- import { describe, test, expect, beforeAll, afterAll } from "bun:test";
1
+ import { describe, test, expect, beforeAll, afterAll } from "vitest";
2
2
  import { mkdtempSync, writeFileSync, mkdirSync, rmSync } from "fs";
3
3
  import { join } from "path";
4
4
  import { tmpdir } from "os";
@@ -29,8 +29,8 @@ describe("expandFileMarkers", () => {
29
29
  expect(result).toContain('```typescript title="example.ts"');
30
30
  expect(result).toContain('import { Bucket } from "@intentius/chant-lexicon-aws";');
31
31
  expect(result).toContain("```");
32
- expect(result).toStartWith("Before\n\n");
33
- expect(result).toEndWith("\n\nAfter");
32
+ expect(result.startsWith("Before\n\n")).toBe(true);
33
+ expect(result.endsWith("\n\nAfter")).toBe(true);
34
34
  });
35
35
 
36
36
  test("expands line range", () => {
@@ -1,4 +1,4 @@
1
- import { describe, test, expect } from "bun:test";
1
+ import { describe, test, expect } from "vitest";
2
2
  import { generateRuleCatalog, generateRuleDetailPage } from "./docs-rules";
3
3
  import type { RuleEntry } from "../lint/rule-registry";
4
4
 
@@ -148,7 +148,7 @@ export function writeDocsPages(result: DocsResult, outDir: string): void {
148
148
  *
149
149
  * Writes: package.json, astro.config.mjs, tsconfig.json, and all content
150
150
  * pages under src/content/docs/. The resulting directory can be built with
151
- * `bun install && bun run build`.
151
+ * `npm install && npm run build`.
152
152
  */
153
153
  export function writeDocsSite(config: DocsConfig, result: DocsResult): void {
154
154
  const outDir = config.outDir;
@@ -1,4 +1,4 @@
1
- import { describe, test, expect } from "bun:test";
1
+ import { describe, test, expect } from "vitest";
2
2
  import { extractFromTar } from "./fetch";
3
3
 
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { describe, expect, test } from "bun:test";
1
+ import { describe, expect, test } from "vitest";
2
2
  import { buildRegistry, serializeRegistry, type RegistryResource, type RegistryConfig } from "./generate-registry";
3
3
  import { NamingStrategy, type NamingInput, type NamingConfig } from "./naming";
4
4
 
@@ -1,4 +1,4 @@
1
- import { describe, expect, test } from "bun:test";
1
+ import { describe, expect, test } from "vitest";
2
2
  import {
3
3
  generateRuntimeIndex,
4
4
  type RuntimeIndexEntry,
@@ -1,4 +1,4 @@
1
- import { describe, test, expect } from "bun:test";
1
+ import { describe, test, expect } from "vitest";
2
2
  import {
3
3
  writeResourceClass,
4
4
  writePropertyClass,
@@ -1,4 +1,4 @@
1
- import { describe, test, expect } from "bun:test";
1
+ import { describe, test, expect } from "vitest";
2
2
  import { generatePipeline, type GeneratePipelineConfig, type ParsedResult } from "./generate";
3
3
  import { NamingStrategy } from "./naming";
4
4
 
@@ -1,4 +1,4 @@
1
- import { describe, test, expect } from "bun:test";
1
+ import { describe, test, expect } from "vitest";
2
2
  import { rfc6902Apply } from "./json-patch";
3
3
 
4
4
  describe("rfc6902Apply", () => {
@@ -1,4 +1,4 @@
1
- import { describe, expect, test } from "bun:test";
1
+ import { describe, expect, test } from "vitest";
2
2
  import {
3
3
  resolvePropertyType,
4
4
  resolveRef,
@@ -1,4 +1,4 @@
1
- import { describe, test, expect } from "bun:test";
1
+ import { describe, test, expect } from "vitest";
2
2
  import { topoSort } from "./topo-sort";
3
3
 
4
4
  interface Node {
@@ -1,4 +1,4 @@
1
- import { describe, test, expect } from "bun:test";
1
+ import { describe, test, expect } from "vitest";
2
2
  import { typecheckDTS } from "./typecheck";
3
3
 
4
4
  describe("typecheckDTS", () => {
@@ -4,8 +4,20 @@
4
4
  import { writeFileSync, mkdirSync, rmSync } from "fs";
5
5
  import { join } from "path";
6
6
  import { tmpdir } from "os";
7
+ import { createRequire } from "module";
7
8
  import { getRuntime } from "../runtime-adapter";
8
9
 
10
+ // Resolve tsc binary path — works regardless of cwd when spawning.
11
+ function resolveTsc(): string {
12
+ try {
13
+ const req = createRequire(import.meta.url);
14
+ const tscPkg = req.resolve("typescript/bin/tsc");
15
+ return tscPkg;
16
+ } catch {
17
+ return "tsc";
18
+ }
19
+ }
20
+
9
21
  /**
10
22
  * Minimal TypeScript lib stub — declares just enough built-in types for
11
23
  * validating generated .d.ts files without needing the full standard library.
@@ -94,12 +106,13 @@ export async function typecheckDTS(content: string): Promise<TypeCheckResult> {
94
106
  };
95
107
  writeFileSync(join(dir, "tsconfig.json"), JSON.stringify(tsconfig, null, 2));
96
108
 
97
- // Run tsc
109
+ // Run tsc using absolute path to avoid npx/cwd resolution issues
98
110
  const rt = getRuntime();
99
- const { stdout, stderr, exitCode } = await rt.spawn(
100
- [rt.commands.exec, "tsc", "--noEmit", "--project", "tsconfig.json"],
101
- { cwd: dir },
102
- );
111
+ const tscBin = resolveTsc();
112
+ const cmd = tscBin === "tsc"
113
+ ? [rt.commands.exec, "tsc", "--noEmit", "--project", "tsconfig.json"]
114
+ : [rt.commands.runner, tscBin, "--noEmit", "--project", "tsconfig.json"];
115
+ const { stdout, stderr, exitCode } = await rt.spawn(cmd, { cwd: dir });
103
116
 
104
117
  // Parse diagnostics from stdout (tsc writes errors to stdout)
105
118
  const output = stdout + stderr;
@@ -1,4 +1,4 @@
1
- import { describe, test, expect } from "bun:test";
1
+ import { describe, test, expect } from "vitest";
2
2
  import { mkdirSync, writeFileSync, rmSync } from "fs";
3
3
  import { join } from "path";
4
4
  import { tmpdir } from "os";