@intentius/chant 0.0.8 → 0.0.10

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 (62) hide show
  1. package/package.json +2 -1
  2. package/src/bench.test.ts +1 -1
  3. package/src/cli/commands/doctor.ts +8 -3
  4. package/src/cli/commands/init.test.ts +44 -4
  5. package/src/cli/commands/init.ts +55 -23
  6. package/src/cli/commands/lint.ts +27 -13
  7. package/src/cli/handlers/init.ts +1 -0
  8. package/src/cli/lsp/server.ts +1 -1
  9. package/src/cli/main.ts +4 -0
  10. package/src/cli/mcp/server.test.ts +28 -2
  11. package/src/cli/mcp/tools/scaffold.ts +21 -3
  12. package/src/cli/registry.ts +1 -0
  13. package/src/cli/reporters/stylish.test.ts +212 -1
  14. package/src/cli/reporters/stylish.ts +133 -36
  15. package/src/codegen/docs-rules.test.ts +112 -0
  16. package/src/codegen/docs-rules.ts +129 -0
  17. package/src/codegen/docs.ts +3 -1
  18. package/src/codegen/generate-typescript.test.ts +64 -0
  19. package/src/codegen/generate-typescript.ts +13 -3
  20. package/src/codegen/package.ts +1 -1
  21. package/src/composite.test.ts +83 -16
  22. package/src/composite.ts +7 -5
  23. package/src/detectLexicon.test.ts +2 -2
  24. package/src/discovery/collect.test.ts +2 -2
  25. package/src/discovery/collect.ts +1 -1
  26. package/src/index.ts +1 -0
  27. package/src/lexicon-schema.ts +8 -0
  28. package/src/lexicon.ts +13 -1
  29. package/src/lint/declarative.ts +6 -0
  30. package/src/lint/engine.test.ts +287 -11
  31. package/src/lint/engine.ts +101 -23
  32. package/src/lint/rule-registry.test.ts +112 -0
  33. package/src/lint/rule-registry.ts +118 -0
  34. package/src/lint/rule.ts +8 -0
  35. package/src/lint/rules/cor017-composite-name-match.ts +2 -1
  36. package/src/lint/rules/cor018-composite-prefer-lexicon-type.ts +4 -3
  37. package/src/lint/rules/declarable-naming-convention.ts +1 -0
  38. package/src/lint/rules/evl001-non-literal-expression.ts +1 -0
  39. package/src/lint/rules/evl002-control-flow-resource.ts +1 -0
  40. package/src/lint/rules/evl003-dynamic-property-access.ts +1 -0
  41. package/src/lint/rules/evl004-spread-non-const.ts +1 -0
  42. package/src/lint/rules/evl005-resource-block-body.ts +1 -0
  43. package/src/lint/rules/evl007-invalid-siblings.ts +1 -0
  44. package/src/lint/rules/evl009-composite-no-constant.ts +1 -0
  45. package/src/lint/rules/evl010-composite-no-transform.ts +1 -0
  46. package/src/lint/rules/export-required.ts +1 -0
  47. package/src/lint/rules/file-declarable-limit.ts +1 -0
  48. package/src/lint/rules/flat-declarations.test.ts +8 -7
  49. package/src/lint/rules/flat-declarations.ts +2 -3
  50. package/src/lint/rules/no-cyclic-declarable-ref.ts +1 -0
  51. package/src/lint/rules/no-redundant-type-import.ts +1 -0
  52. package/src/lint/rules/no-redundant-value-cast.ts +1 -0
  53. package/src/lint/rules/no-string-ref.ts +1 -0
  54. package/src/lint/rules/no-unused-declarable-import.ts +1 -0
  55. package/src/lint/rules/no-unused-declarable.test.ts +8 -0
  56. package/src/lint/rules/no-unused-declarable.ts +4 -0
  57. package/src/lint/rules/single-concern-file.ts +1 -0
  58. package/src/lsp/lexicon-providers.ts +7 -0
  59. package/src/lsp/types.ts +1 -0
  60. package/src/resource-attributes.test.ts +79 -0
  61. package/src/resource-attributes.ts +42 -0
  62. package/src/runtime.ts +4 -3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intentius/chant",
3
- "version": "0.0.8",
3
+ "version": "0.0.10",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "files": ["src/", "bin/"],
@@ -19,6 +19,7 @@
19
19
  "dependencies": {
20
20
  "fflate": "^0.8.2",
21
21
  "picomatch": "^4.0.3",
22
+ "typescript": "^5.5.0",
22
23
  "zod": "^4.3.6"
23
24
  }
24
25
  }
package/src/bench.test.ts CHANGED
@@ -131,7 +131,7 @@ describe("performance benchmarks", () => {
131
131
  for (const size of sizes) {
132
132
  await withTestDir(async (dir) => {
133
133
  const files = await generateFixture(dir, size);
134
- const { avg } = await benchmark(() => runLint(files, coreRules), 3);
134
+ const { avg } = await benchmark(() => runLint(files, coreRules).then(r => r.diagnostics), 3);
135
135
  output.push(` ${pad(size.name, 10)} ${fmt(avg)} (avg of 3 runs)`);
136
136
  });
137
137
  }
@@ -1,6 +1,7 @@
1
1
  import { existsSync, readFileSync, readdirSync } from "fs";
2
2
  import { execSync } from "child_process";
3
- import { join, resolve } from "path";
3
+ import { join, resolve, dirname } from "path";
4
+ import { fileURLToPath } from "url";
4
5
  import { checkVersionCompatibility } from "../../lexicon-manifest";
5
6
  import { debug } from "../debug";
6
7
  import { loadPlugins, resolveProjectLexicons } from "../plugins";
@@ -109,8 +110,12 @@ export async function doctorCommand(path: string): Promise<DoctorReport> {
109
110
  try {
110
111
  const manifest = JSON.parse(readFileSync(manifestPath, "utf-8"));
111
112
  if (manifest.chantVersion) {
112
- // Use a placeholder current version for now
113
- const currentVersion = "0.1.0";
113
+ let currentVersion = "0.0.8";
114
+ try {
115
+ const pkgDir = dirname(dirname(dirname(dirname(dirname(fileURLToPath(import.meta.url))))));
116
+ const corePkg = JSON.parse(readFileSync(join(pkgDir, "package.json"), "utf-8"));
117
+ currentVersion = corePkg.version ?? currentVersion;
118
+ } catch { /* fallback */ }
114
119
  if (!checkVersionCompatibility(manifest.chantVersion, currentVersion)) {
115
120
  checks.push({ name: `lexicon-${lex}-compat`, status: "warn", message: `Lexicon ${lex} requires chant ${manifest.chantVersion}` });
116
121
  } else {
@@ -176,7 +176,7 @@ describe("initCommand", () => {
176
176
  });
177
177
  });
178
178
 
179
- test("does not generate barrel file", async () => {
179
+ test("does not generate re-export file", async () => {
180
180
  await withTestDir(async (testDir) => {
181
181
  const options: InitOptions = {
182
182
  path: testDir,
@@ -187,9 +187,9 @@ describe("initCommand", () => {
187
187
 
188
188
  await initCommand(options);
189
189
 
190
- // No _.ts barrel — direct imports are used instead
191
- const barrelPath = join(testDir, "src", "_.ts");
192
- expect(existsSync(barrelPath)).toBe(false);
190
+ // No _.ts re-export — direct imports are used instead
191
+ const reexportPath = join(testDir, "src", "_.ts");
192
+ expect(existsSync(reexportPath)).toBe(false);
193
193
 
194
194
  // No index.ts either
195
195
  const indexPath = join(testDir, "src", "index.ts");
@@ -309,5 +309,45 @@ describe("initCommand", () => {
309
309
  expect(existsSync(newDir)).toBe(true);
310
310
  });
311
311
  });
312
+
313
+ test("gitlab init creates src files", async () => {
314
+ await withTestDir(async (testDir) => {
315
+ const options: InitOptions = {
316
+ path: testDir,
317
+ lexicon: "gitlab",
318
+ skipMcp: true,
319
+ skipInstall: true,
320
+ };
321
+
322
+ const result = await initCommand(options);
323
+
324
+ expect(result.success).toBe(true);
325
+ expect(result.createdFiles).toContain("src/config.ts");
326
+ expect(result.createdFiles).toContain("src/pipeline.ts");
327
+
328
+ const pkg = JSON.parse(readFileSync(join(testDir, "package.json"), "utf-8"));
329
+ expect(pkg.scripts.build).toContain("gitlab");
330
+ });
331
+ });
332
+
333
+ test("gitlab init with --template passes template to plugin", async () => {
334
+ await withTestDir(async (testDir) => {
335
+ const options: InitOptions = {
336
+ path: testDir,
337
+ lexicon: "gitlab",
338
+ template: "node-pipeline",
339
+ skipMcp: true,
340
+ skipInstall: true,
341
+ };
342
+
343
+ const result = await initCommand(options);
344
+
345
+ expect(result.success).toBe(true);
346
+ expect(result.createdFiles).toContain("src/pipeline.ts");
347
+
348
+ const pipeline = readFileSync(join(testDir, "src", "pipeline.ts"), "utf-8");
349
+ expect(pipeline).toContain("NodePipeline");
350
+ });
351
+ });
312
352
  });
313
353
 
@@ -1,11 +1,23 @@
1
1
  import { existsSync, mkdirSync, writeFileSync, readdirSync, readFileSync } from "fs";
2
- import { join, resolve } from "path";
2
+ import { join, resolve, dirname } from "path";
3
+ import { fileURLToPath } from "url";
3
4
  import { homedir } from "os";
4
5
  import { createInterface } from "readline";
5
6
  import { z } from "zod";
6
7
  import { formatSuccess, formatWarning } from "../format";
7
8
  import { loadPlugin } from "../plugins";
8
9
 
10
+ /** Read the current chant package version from our own package.json. */
11
+ function getChantVersion(): string {
12
+ try {
13
+ const pkgDir = dirname(dirname(dirname(dirname(dirname(fileURLToPath(import.meta.url))))));
14
+ const pkg = JSON.parse(readFileSync(join(pkgDir, "package.json"), "utf-8"));
15
+ return pkg.version ?? "0.0.8";
16
+ } catch {
17
+ return "0.0.8";
18
+ }
19
+ }
20
+
9
21
  /**
10
22
  * Schema for validating generated package.json — catches template bugs early.
11
23
  */
@@ -25,6 +37,8 @@ export interface InitOptions {
25
37
  path?: string;
26
38
  /** Lexicon to use */
27
39
  lexicon: string;
40
+ /** Template name (e.g. "node-pipeline", "docker-build") */
41
+ template?: string;
28
42
  /** Force init even in non-empty directory */
29
43
  force?: boolean;
30
44
  /** Skip MCP config generation */
@@ -81,20 +95,22 @@ function getMcpConfigDir(ide: "claude-code" | "cursor" | "generic"): string {
81
95
  /**
82
96
  * Generate package.json content
83
97
  */
84
- function generatePackageJson(lexicon: string): string {
98
+ function generatePackageJson(lexicon: string, extraScripts?: Record<string, string>): string {
99
+ const ver = getChantVersion();
85
100
  const dependencies: Record<string, string> = {
86
- "@intentius/chant": "^0.1.0",
87
- [`@intentius/chant-lexicon-${lexicon}`]: "^0.1.0",
101
+ "@intentius/chant": `^${ver}`,
102
+ [`@intentius/chant-lexicon-${lexicon}`]: `^${ver}`,
88
103
  };
89
104
 
90
105
  const pkg = {
91
106
  name: "chant-project",
92
- version: "0.1.0",
107
+ version: ver,
93
108
  type: "module" as const,
94
109
  scripts: {
95
110
  build: `chant build src --lexicon ${lexicon}`,
96
111
  lint: "chant lint src",
97
112
  dev: `chant build src --lexicon ${lexicon} --watch`,
113
+ ...extraScripts,
98
114
  },
99
115
  dependencies,
100
116
  devDependencies: {
@@ -301,6 +317,17 @@ export async function initCommand(options: InitOptions): Promise<InitResult> {
301
317
  mkdirSync(targetDir, { recursive: true });
302
318
  }
303
319
 
320
+ // Load plugin early to get template set (used for scripts + source files)
321
+ let templateSet: import("../../lexicon").InitTemplateSet | undefined;
322
+ try {
323
+ const plugin = await loadPlugin(options.lexicon);
324
+ if (plugin.initTemplates) {
325
+ templateSet = plugin.initTemplates(options.template);
326
+ }
327
+ } catch {
328
+ // Plugin not yet installed — no source files to scaffold
329
+ }
330
+
304
331
  // Create src directory
305
332
  const srcDir = join(targetDir, "src");
306
333
  if (!existsSync(srcDir)) {
@@ -310,7 +337,7 @@ export async function initCommand(options: InitOptions): Promise<InitResult> {
310
337
  // Generate package.json
311
338
  writeIfNotExists(
312
339
  join(targetDir, "package.json"),
313
- generatePackageJson(options.lexicon),
340
+ generatePackageJson(options.lexicon, templateSet?.scripts),
314
341
  "package.json",
315
342
  createdFiles,
316
343
  warnings,
@@ -343,24 +370,29 @@ export async function initCommand(options: InitOptions): Promise<InitResult> {
343
370
  warnings,
344
371
  );
345
372
 
346
- // Generate source files from plugin (if available)
347
- let sourceFiles: Record<string, string> = {};
348
- try {
349
- const plugin = await loadPlugin(options.lexicon);
350
- if (plugin.initTemplates) {
351
- sourceFiles = plugin.initTemplates();
373
+ // Write source files from plugin template set
374
+ if (templateSet) {
375
+ for (const [filename, content] of Object.entries(templateSet.src)) {
376
+ writeIfNotExists(
377
+ join(srcDir, filename),
378
+ content,
379
+ `src/${filename}`,
380
+ createdFiles,
381
+ warnings,
382
+ );
383
+ }
384
+ // Write root scaffold files (e.g. index.js, test.js, Dockerfile)
385
+ if (templateSet.root) {
386
+ for (const [filename, content] of Object.entries(templateSet.root)) {
387
+ writeIfNotExists(
388
+ join(targetDir, filename),
389
+ content,
390
+ filename,
391
+ createdFiles,
392
+ warnings,
393
+ );
394
+ }
352
395
  }
353
- } catch {
354
- // Plugin not yet installed — no source files to scaffold
355
- }
356
- for (const [filename, content] of Object.entries(sourceFiles)) {
357
- writeIfNotExists(
358
- join(srcDir, filename),
359
- content,
360
- `src/${filename}`,
361
- createdFiles,
362
- warnings,
363
- );
364
396
  }
365
397
 
366
398
  // Scaffold .chant/types/core/ with embedded type definitions
@@ -243,19 +243,25 @@ export async function lintCommand(options: LintOptions): Promise<LintResult> {
243
243
 
244
244
  // Run lint — use per-file rules when overrides are present
245
245
  let diagnostics: LintDiagnostic[];
246
+ let suppressed: Array<LintDiagnostic & { reason?: string }> = [];
246
247
  if (options.rules) {
247
- diagnostics = await runLint(files, options.rules, undefined);
248
+ const result = await runLint(files, options.rules, undefined);
249
+ diagnostics = result.diagnostics;
250
+ suppressed = result.suppressed;
248
251
  } else if (hasOverrides) {
249
252
  diagnostics = [];
250
253
  for (const file of files) {
251
254
  const relativePath = file.slice(infraPath.length + 1);
252
255
  const { rules: fileRules, ruleOptions } = getDefaultRules(infraPath, relativePath, allRules);
253
- const fileDiagnostics = await runLint([file], fileRules, ruleOptions);
254
- diagnostics.push(...fileDiagnostics);
256
+ const result = await runLint([file], fileRules, ruleOptions);
257
+ diagnostics.push(...result.diagnostics);
258
+ suppressed.push(...result.suppressed);
255
259
  }
256
260
  } else {
257
261
  const { rules, ruleOptions } = getDefaultRules(infraPath, undefined, allRules);
258
- diagnostics = await runLint(files, rules, ruleOptions);
262
+ const result = await runLint(files, rules, ruleOptions);
263
+ diagnostics = result.diagnostics;
264
+ suppressed = result.suppressed;
259
265
  }
260
266
 
261
267
  // Apply fixes if requested
@@ -277,23 +283,26 @@ export async function lintCommand(options: LintOptions): Promise<LintResult> {
277
283
  }
278
284
 
279
285
  // Re-lint after fixes to get updated diagnostics
280
- let postFixDiagnostics: LintDiagnostic[];
281
286
  if (options.rules) {
282
- postFixDiagnostics = await runLint(files, options.rules, undefined);
287
+ const postResult = await runLint(files, options.rules, undefined);
288
+ diagnostics = postResult.diagnostics;
289
+ suppressed = postResult.suppressed;
283
290
  } else if (hasOverrides) {
284
- postFixDiagnostics = [];
291
+ diagnostics = [];
292
+ suppressed = [];
285
293
  for (const file of files) {
286
294
  const relativePath = file.slice(infraPath.length + 1);
287
295
  const { rules: fileRules, ruleOptions } = getDefaultRules(infraPath, relativePath, allRules);
288
- const fileDiagnostics = await runLint([file], fileRules, ruleOptions);
289
- postFixDiagnostics.push(...fileDiagnostics);
296
+ const postResult = await runLint([file], fileRules, ruleOptions);
297
+ diagnostics.push(...postResult.diagnostics);
298
+ suppressed.push(...postResult.suppressed);
290
299
  }
291
300
  } else {
292
301
  const { rules, ruleOptions } = getDefaultRules(infraPath, undefined, allRules);
293
- postFixDiagnostics = await runLint(files, rules, ruleOptions);
302
+ const postResult = await runLint(files, rules, ruleOptions);
303
+ diagnostics = postResult.diagnostics;
304
+ suppressed = postResult.suppressed;
294
305
  }
295
- diagnostics.length = 0;
296
- diagnostics.push(...postFixDiagnostics);
297
306
  }
298
307
 
299
308
  // Count errors and warnings
@@ -308,6 +317,11 @@ export async function lintCommand(options: LintOptions): Promise<LintResult> {
308
317
  }
309
318
  }
310
319
 
320
+ // Collect all loaded rules for SARIF enrichment
321
+ const allLoadedRules = options.rules
322
+ ? options.rules
323
+ : [...allRules.values()];
324
+
311
325
  // Format output
312
326
  let output: string;
313
327
  switch (options.format) {
@@ -315,7 +329,7 @@ export async function lintCommand(options: LintOptions): Promise<LintResult> {
315
329
  output = formatJson(diagnostics);
316
330
  break;
317
331
  case "sarif":
318
- output = formatSarif(diagnostics);
332
+ output = formatSarif(diagnostics, allLoadedRules, suppressed);
319
333
  break;
320
334
  case "stylish":
321
335
  default:
@@ -16,6 +16,7 @@ export async function runInit(ctx: CommandContext): Promise<number> {
16
16
  const result = await initCommand({
17
17
  path: args.path === "." ? undefined : args.path,
18
18
  lexicon: args.lexicon,
19
+ template: args.template,
19
20
  force: args.force,
20
21
  skipInstall: true,
21
22
  });
@@ -362,7 +362,7 @@ export class LspServer {
362
362
 
363
363
  if (rules.length === 0) return [];
364
364
 
365
- const diagnostics = await runLint([filePath], rules);
365
+ const { diagnostics } = await runLint([filePath], rules);
366
366
  return toLspDiagnostics(diagnostics);
367
367
  } catch {
368
368
  return [];
package/src/cli/main.ts CHANGED
@@ -27,6 +27,7 @@ export function parseArgs(args: string[]): ParsedArgs {
27
27
  force: undefined,
28
28
  fix: false,
29
29
  lexicon: undefined,
30
+ template: undefined,
30
31
  watch: false,
31
32
  verbose: false,
32
33
  help: false,
@@ -44,6 +45,8 @@ export function parseArgs(args: string[]): ParsedArgs {
44
45
  result.format = args[++i];
45
46
  } else if (arg === "--lexicon" || arg === "-d") {
46
47
  result.lexicon = args[++i];
48
+ } else if (arg === "--template" || arg === "-t") {
49
+ result.template = args[++i];
47
50
  } else if (arg === "--force") {
48
51
  result.force = true;
49
52
  } else if (arg === "--fix") {
@@ -107,6 +110,7 @@ Options:
107
110
  - list: text (default) or json
108
111
  - lint: stylish (default), json, or sarif
109
112
  -d, --lexicon <name> Build only the specified lexicon (e.g. aws, gitlab)
113
+ -t, --template <name> Init template (e.g. node-pipeline, docker-build)
110
114
  --fix Auto-fix fixable issues (lint command)
111
115
  --force Force overwrite existing files (import command)
112
116
  -w, --watch Watch for changes and rebuild/re-lint (build, lint)
@@ -378,10 +378,10 @@ describe("McpServer", () => {
378
378
  test("matches plugin init templates", async () => {
379
379
  const plugin = createMockPlugin({
380
380
  name: "test-lex",
381
- initTemplates: () => ({
381
+ initTemplates: () => ({ src: {
382
382
  "config.ts": "export const config = {};",
383
383
  "data-bucket.ts": "export const dataBucket = {};",
384
- }),
384
+ } }),
385
385
  });
386
386
 
387
387
  const s = new McpServer([plugin]);
@@ -397,6 +397,32 @@ describe("McpServer", () => {
397
397
  expect(parsed.files.length).toBe(1);
398
398
  expect(parsed.files[0].filename).toBe("data-bucket.ts");
399
399
  });
400
+
401
+ test("passes template name to initTemplates", async () => {
402
+ const plugin = createMockPlugin({
403
+ name: "test-lex",
404
+ initTemplates: (template?: string) => {
405
+ if (template === "special") {
406
+ return { src: { "special.ts": "export const special = {};" } };
407
+ }
408
+ return { src: { "default.ts": "export const def = {};" } };
409
+ },
410
+ });
411
+
412
+ const s = new McpServer([plugin]);
413
+ const response = await s.handleRequest({
414
+ jsonrpc: "2.0",
415
+ id: 1,
416
+ method: "tools/call",
417
+ params: { name: "scaffold", arguments: { pattern: "special", lexicon: "test-lex", template: "special" } },
418
+ });
419
+
420
+ const parsed = JSON.parse((response.result as { content: Array<{ text: string }> }).content[0].text);
421
+ expect(parsed.lexicon).toBe("test-lex");
422
+ expect(parsed.template).toBe("special");
423
+ expect(parsed.files.length).toBe(1);
424
+ expect(parsed.files[0].filename).toBe("special.ts");
425
+ });
400
426
  });
401
427
 
402
428
  // -----------------------------------------------------------------------
@@ -17,6 +17,10 @@ export const scaffoldTool = {
17
17
  type: "string",
18
18
  description: "Lexicon to use for scaffolding (e.g. 'aws', 'gitlab'). Auto-detected if omitted.",
19
19
  },
20
+ template: {
21
+ type: "string",
22
+ description: "Named init template (e.g. 'node-pipeline', 'docker-build'). When provided, returns the template's source files instead of pattern-matching.",
23
+ },
20
24
  },
21
25
  required: ["pattern"],
22
26
  },
@@ -31,6 +35,7 @@ export function createScaffoldHandler(
31
35
  return async (params) => {
32
36
  const pattern = params.pattern as string;
33
37
  const lexiconName = params.lexicon as string | undefined;
38
+ const templateName = params.template as string | undefined;
34
39
 
35
40
  // Try to find a matching plugin
36
41
  const candidates = lexiconName
@@ -39,14 +44,27 @@ export function createScaffoldHandler(
39
44
 
40
45
  // Search plugin init templates for a pattern match
41
46
  for (const plugin of candidates) {
42
- const templates = plugin.initTemplates?.();
43
- if (!templates) continue;
47
+ const templateSet = plugin.initTemplates?.(templateName);
48
+ if (!templateSet) continue;
49
+
50
+ // If a named template was requested, return all its source files
51
+ if (templateName) {
52
+ const files = Object.entries(templateSet.src).map(([filename, content]) => ({ filename, content }));
53
+ if (files.length > 0) {
54
+ return {
55
+ lexicon: plugin.name,
56
+ pattern,
57
+ template: templateName,
58
+ files,
59
+ };
60
+ }
61
+ }
44
62
 
45
63
  // Match template filenames against the pattern (case-insensitive substring)
46
64
  const lowerPattern = pattern.toLowerCase();
47
65
  const matched: Array<{ filename: string; content: string }> = [];
48
66
 
49
- for (const [filename, content] of Object.entries(templates)) {
67
+ for (const [filename, content] of Object.entries(templateSet.src)) {
50
68
  if (filename.toLowerCase().includes(lowerPattern)) {
51
69
  matched.push({ filename, content });
52
70
  }
@@ -14,6 +14,7 @@ export interface ParsedArgs {
14
14
  force?: boolean;
15
15
  fix: boolean;
16
16
  lexicon?: string;
17
+ template?: string;
17
18
  watch: boolean;
18
19
  verbose: boolean;
19
20
  help: boolean;