@intentius/chant 0.0.5 → 0.0.8
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.
- package/bin/chant +20 -0
- package/package.json +18 -17
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/plugin.ts +0 -25
- package/src/cli/commands/__snapshots__/init-lexicon.test.ts.snap +0 -25
- package/src/cli/commands/build.ts +1 -2
- package/src/cli/commands/import.ts +2 -2
- package/src/cli/commands/init-lexicon.test.ts +0 -3
- package/src/cli/commands/init-lexicon.ts +1 -79
- package/src/cli/commands/init.ts +14 -3
- package/src/cli/commands/list.ts +2 -2
- package/src/cli/commands/update.ts +5 -3
- package/src/cli/conflict-check.test.ts +0 -1
- package/src/cli/handlers/dev.ts +1 -9
- package/src/cli/main.ts +13 -3
- package/src/cli/mcp/server.test.ts +207 -4
- package/src/cli/mcp/server.ts +6 -0
- package/src/cli/mcp/tools/explain.ts +134 -0
- package/src/cli/mcp/tools/scaffold.ts +107 -0
- package/src/cli/mcp/tools/search.ts +98 -0
- package/src/codegen/generate-registry.test.ts +1 -1
- package/src/codegen/generate-registry.ts +2 -3
- package/src/codegen/generate-typescript.test.ts +6 -6
- package/src/codegen/generate-typescript.ts +2 -6
- package/src/codegen/generate.ts +1 -12
- package/src/codegen/typecheck.ts +6 -11
- package/src/config.ts +4 -0
- package/src/index.ts +1 -1
- package/src/lexicon-integrity.ts +5 -4
- package/src/lexicon.ts +2 -6
- package/src/lint/config.ts +8 -6
- package/src/runtime-adapter.ts +158 -0
- package/src/serializer-walker.test.ts +0 -9
- package/src/serializer-walker.ts +1 -3
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/codegen/rollback.ts +0 -45
- package/src/codegen/case.test.ts +0 -30
- package/src/codegen/case.ts +0 -11
- package/src/codegen/rollback.test.ts +0 -92
- package/src/codegen/rollback.ts +0 -115
package/bin/chant
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# Cross-runtime CLI wrapper. Tries bun first, falls back to npx tsx.
|
|
3
|
+
set -e
|
|
4
|
+
|
|
5
|
+
# Resolve symlinks to find the real script location
|
|
6
|
+
SELF="$0"
|
|
7
|
+
while [ -L "$SELF" ]; do
|
|
8
|
+
DIR="$(cd "$(dirname "$SELF")" && pwd)"
|
|
9
|
+
SELF="$(readlink "$SELF")"
|
|
10
|
+
# Handle relative symlink targets
|
|
11
|
+
case "$SELF" in /*) ;; *) SELF="$DIR/$SELF" ;; esac
|
|
12
|
+
done
|
|
13
|
+
SCRIPT_DIR="$(cd "$(dirname "$SELF")" && pwd)"
|
|
14
|
+
MAIN_TS="$SCRIPT_DIR/../src/cli/main.ts"
|
|
15
|
+
|
|
16
|
+
if command -v bun >/dev/null 2>&1; then
|
|
17
|
+
exec bun "$MAIN_TS" "$@"
|
|
18
|
+
else
|
|
19
|
+
exec npx tsx "$MAIN_TS" "$@"
|
|
20
|
+
fi
|
package/package.json
CHANGED
|
@@ -1,23 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@intentius/chant",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.8",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"files": ["src/"],
|
|
6
|
+
"files": ["src/", "bin/"],
|
|
7
7
|
"publishConfig": {
|
|
8
|
-
|
|
9
|
-
},
|
|
10
|
-
"bin": {
|
|
11
|
-
|
|
12
|
-
},
|
|
13
|
-
"exports": {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
},
|
|
19
|
-
"dependencies": {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
8
|
+
"access": "public"
|
|
9
|
+
},
|
|
10
|
+
"bin": {
|
|
11
|
+
"chant": "./bin/chant"
|
|
12
|
+
},
|
|
13
|
+
"exports": {
|
|
14
|
+
".": "./src/index.ts",
|
|
15
|
+
"./cli": "./src/cli/index.ts",
|
|
16
|
+
"./cli/*": "./src/cli/*",
|
|
17
|
+
"./*": "./src/*"
|
|
18
|
+
},
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"fflate": "^0.8.2",
|
|
21
|
+
"picomatch": "^4.0.3",
|
|
22
|
+
"zod": "^4.3.6"
|
|
23
|
+
}
|
|
23
24
|
}
|
|
@@ -42,31 +42,6 @@ export const fixturePlugin: LexiconPlugin = {
|
|
|
42
42
|
console.error(`Packaged ${stats.resources} resources, ${stats.ruleCount} rules, ${stats.skillCount} skills`);
|
|
43
43
|
},
|
|
44
44
|
|
|
45
|
-
async rollback(options?: { restore?: string; verbose?: boolean }): Promise<void> {
|
|
46
|
-
const { listSnapshots, restoreSnapshot } = await import("./codegen/rollback");
|
|
47
|
-
const { join, dirname } = await import("path");
|
|
48
|
-
const { fileURLToPath } = await import("url");
|
|
49
|
-
|
|
50
|
-
const pkgDir = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
51
|
-
const snapshotsDir = join(pkgDir, ".snapshots");
|
|
52
|
-
|
|
53
|
-
if (options?.restore) {
|
|
54
|
-
const generatedDir = join(pkgDir, "src", "generated");
|
|
55
|
-
restoreSnapshot(String(options.restore), generatedDir);
|
|
56
|
-
console.error(`Restored snapshot: ${options.restore}`);
|
|
57
|
-
} else {
|
|
58
|
-
const snapshots = listSnapshots(snapshotsDir);
|
|
59
|
-
if (snapshots.length === 0) {
|
|
60
|
-
console.error("No snapshots available.");
|
|
61
|
-
} else {
|
|
62
|
-
console.error(`Available snapshots (${snapshots.length}):`);
|
|
63
|
-
for (const s of snapshots) {
|
|
64
|
-
console.error(` ${s.timestamp} ${s.resources} resources ${s.path}`);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
},
|
|
69
|
-
|
|
70
45
|
// ── Optional extensions (uncomment and implement as needed) ───
|
|
71
46
|
|
|
72
47
|
// lintRules(): LintRule[] {
|
|
@@ -152,31 +152,6 @@ export const fixturePlugin: LexiconPlugin = {
|
|
|
152
152
|
console.error(\`Packaged \${stats.resources} resources, \${stats.ruleCount} rules, \${stats.skillCount} skills\`);
|
|
153
153
|
},
|
|
154
154
|
|
|
155
|
-
async rollback(options?: { restore?: string; verbose?: boolean }): Promise<void> {
|
|
156
|
-
const { listSnapshots, restoreSnapshot } = await import("./codegen/rollback");
|
|
157
|
-
const { join, dirname } = await import("path");
|
|
158
|
-
const { fileURLToPath } = await import("url");
|
|
159
|
-
|
|
160
|
-
const pkgDir = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
161
|
-
const snapshotsDir = join(pkgDir, ".snapshots");
|
|
162
|
-
|
|
163
|
-
if (options?.restore) {
|
|
164
|
-
const generatedDir = join(pkgDir, "src", "generated");
|
|
165
|
-
restoreSnapshot(String(options.restore), generatedDir);
|
|
166
|
-
console.error(\`Restored snapshot: \${options.restore}\`);
|
|
167
|
-
} else {
|
|
168
|
-
const snapshots = listSnapshots(snapshotsDir);
|
|
169
|
-
if (snapshots.length === 0) {
|
|
170
|
-
console.error("No snapshots available.");
|
|
171
|
-
} else {
|
|
172
|
-
console.error(\`Available snapshots (\${snapshots.length}):\`);
|
|
173
|
-
for (const s of snapshots) {
|
|
174
|
-
console.error(\` \${s.timestamp} \${s.resources} resources \${s.path}\`);
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
},
|
|
179
|
-
|
|
180
155
|
// ── Optional extensions (uncomment and implement as needed) ───
|
|
181
156
|
|
|
182
157
|
// lintRules(): LintRule[] {
|
|
@@ -5,7 +5,7 @@ import { runPostSynthChecks } from "../../lint/post-synth";
|
|
|
5
5
|
import type { PostSynthCheck } from "../../lint/post-synth";
|
|
6
6
|
import { formatError, formatWarning, formatSuccess, formatBold, formatInfo } from "../format";
|
|
7
7
|
import { writeFileSync } from "fs";
|
|
8
|
-
import { resolve } from "path";
|
|
8
|
+
import { resolve, dirname, join } from "path";
|
|
9
9
|
import { watchDirectory, formatTimestamp, formatChangedFiles } from "../watch";
|
|
10
10
|
|
|
11
11
|
/**
|
|
@@ -172,7 +172,6 @@ export async function buildCommand(options: BuildOptions): Promise<BuildResult>
|
|
|
172
172
|
|
|
173
173
|
// Write additional files (e.g. nested stack templates) alongside the primary output
|
|
174
174
|
if (additionalFiles.size > 0) {
|
|
175
|
-
const { dirname, join } = require("path");
|
|
176
175
|
const outputDir = dirname(outputPath);
|
|
177
176
|
for (const [filename, content] of additionalFiles) {
|
|
178
177
|
let fileContent = content;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, writeFileSync, readFileSync } from "fs";
|
|
1
|
+
import { existsSync, mkdirSync, writeFileSync, readFileSync, readdirSync } from "fs";
|
|
2
2
|
import { join, resolve, basename } from "path";
|
|
3
3
|
import { formatSuccess, formatWarning, formatError } from "../format";
|
|
4
4
|
import type { TemplateIR, ResourceIR, TemplateParser } from "../../import/parser";
|
|
@@ -266,7 +266,7 @@ export async function importCommand(options: ImportOptions): Promise<ImportResul
|
|
|
266
266
|
|
|
267
267
|
// Check output directory
|
|
268
268
|
if (existsSync(outputDir) && !options.force) {
|
|
269
|
-
const files =
|
|
269
|
+
const files = readdirSync(outputDir);
|
|
270
270
|
if (files.length > 0) {
|
|
271
271
|
warnings.push(`Output directory ${outputDir} is not empty. Use --force to overwrite.`);
|
|
272
272
|
}
|
|
@@ -40,7 +40,6 @@ describe("initLexiconCommand", () => {
|
|
|
40
40
|
"src/codegen/generate-cli.ts",
|
|
41
41
|
"src/codegen/naming.ts",
|
|
42
42
|
"src/codegen/package.ts",
|
|
43
|
-
"src/codegen/rollback.ts",
|
|
44
43
|
"src/codegen/docs.ts",
|
|
45
44
|
"src/spec/fetch.ts",
|
|
46
45
|
"src/spec/parse.ts",
|
|
@@ -65,7 +64,6 @@ describe("initLexiconCommand", () => {
|
|
|
65
64
|
"docs/src/content/docs/index.mdx",
|
|
66
65
|
"src/generated/.gitkeep",
|
|
67
66
|
"examples/getting-started/.gitkeep",
|
|
68
|
-
".snapshots/.gitkeep",
|
|
69
67
|
];
|
|
70
68
|
|
|
71
69
|
for (const file of expectedFiles) {
|
|
@@ -83,7 +81,6 @@ describe("initLexiconCommand", () => {
|
|
|
83
81
|
expect(pluginContent).toContain("async validate(");
|
|
84
82
|
expect(pluginContent).toContain("async coverage(");
|
|
85
83
|
expect(pluginContent).toContain("async package(");
|
|
86
|
-
expect(pluginContent).toContain("async rollback(");
|
|
87
84
|
});
|
|
88
85
|
|
|
89
86
|
test("package name uses the provided lexicon name", async () => {
|
|
@@ -106,31 +106,6 @@ export const ${names.pluginVarName}: LexiconPlugin = {
|
|
|
106
106
|
console.error(\`Packaged \${stats.resources} resources, \${stats.ruleCount} rules, \${stats.skillCount} skills\`);
|
|
107
107
|
},
|
|
108
108
|
|
|
109
|
-
async rollback(options?: { restore?: string; verbose?: boolean }): Promise<void> {
|
|
110
|
-
const { listSnapshots, restoreSnapshot } = await import("./codegen/rollback");
|
|
111
|
-
const { join, dirname } = await import("path");
|
|
112
|
-
const { fileURLToPath } = await import("url");
|
|
113
|
-
|
|
114
|
-
const pkgDir = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
115
|
-
const snapshotsDir = join(pkgDir, ".snapshots");
|
|
116
|
-
|
|
117
|
-
if (options?.restore) {
|
|
118
|
-
const generatedDir = join(pkgDir, "src", "generated");
|
|
119
|
-
restoreSnapshot(String(options.restore), generatedDir);
|
|
120
|
-
console.error(\`Restored snapshot: \${options.restore}\`);
|
|
121
|
-
} else {
|
|
122
|
-
const snapshots = listSnapshots(snapshotsDir);
|
|
123
|
-
if (snapshots.length === 0) {
|
|
124
|
-
console.error("No snapshots available.");
|
|
125
|
-
} else {
|
|
126
|
-
console.error(\`Available snapshots (\${snapshots.length}):\`);
|
|
127
|
-
for (const s of snapshots) {
|
|
128
|
-
console.error(\` \${s.timestamp} \${s.resources} resources \${s.path}\`);
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
},
|
|
133
|
-
|
|
134
109
|
// ── Optional extensions (uncomment and implement as needed) ───
|
|
135
110
|
|
|
136
111
|
// lintRules(): LintRule[] {
|
|
@@ -382,55 +357,6 @@ export async function packageLexicon(options?: { verbose?: boolean; force?: bool
|
|
|
382
357
|
`;
|
|
383
358
|
}
|
|
384
359
|
|
|
385
|
-
function generateCodegenRollbackTs(): string {
|
|
386
|
-
return `import { existsSync, readdirSync, readFileSync, mkdirSync, writeFileSync, cpSync } from "fs";
|
|
387
|
-
import { join, basename } from "path";
|
|
388
|
-
|
|
389
|
-
export interface Snapshot {
|
|
390
|
-
timestamp: string;
|
|
391
|
-
resources: number;
|
|
392
|
-
path: string;
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
/**
|
|
396
|
-
* List available generation snapshots.
|
|
397
|
-
*/
|
|
398
|
-
export function listSnapshots(snapshotsDir: string): Snapshot[] {
|
|
399
|
-
if (!existsSync(snapshotsDir)) return [];
|
|
400
|
-
|
|
401
|
-
return readdirSync(snapshotsDir)
|
|
402
|
-
.filter((d) => !d.startsWith("."))
|
|
403
|
-
.sort()
|
|
404
|
-
.reverse()
|
|
405
|
-
.map((dir) => {
|
|
406
|
-
const fullPath = join(snapshotsDir, dir);
|
|
407
|
-
const metaPath = join(fullPath, "meta.json");
|
|
408
|
-
let resources = 0;
|
|
409
|
-
if (existsSync(metaPath)) {
|
|
410
|
-
try {
|
|
411
|
-
const meta = JSON.parse(readFileSync(metaPath, "utf-8"));
|
|
412
|
-
resources = meta.resources ?? 0;
|
|
413
|
-
} catch {}
|
|
414
|
-
}
|
|
415
|
-
return { timestamp: dir, resources, path: fullPath };
|
|
416
|
-
});
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
/**
|
|
420
|
-
* Restore a snapshot to the generated directory.
|
|
421
|
-
*/
|
|
422
|
-
export function restoreSnapshot(timestamp: string, generatedDir: string): void {
|
|
423
|
-
const snapshotsDir = join(generatedDir, "..", "..", ".snapshots");
|
|
424
|
-
const snapshotDir = join(snapshotsDir, timestamp);
|
|
425
|
-
if (!existsSync(snapshotDir)) {
|
|
426
|
-
throw new Error(\`Snapshot not found: \${timestamp}\`);
|
|
427
|
-
}
|
|
428
|
-
mkdirSync(generatedDir, { recursive: true });
|
|
429
|
-
cpSync(snapshotDir, generatedDir, { recursive: true });
|
|
430
|
-
}
|
|
431
|
-
`;
|
|
432
|
-
}
|
|
433
|
-
|
|
434
360
|
function generateCodegenDocsTs(name: string): string {
|
|
435
361
|
return `import { docsPipeline, writeDocsSite } from "@intentius/chant/codegen/docs";
|
|
436
362
|
|
|
@@ -735,8 +661,7 @@ package: generate validate
|
|
|
735
661
|
}
|
|
736
662
|
|
|
737
663
|
function generateGitignore(): string {
|
|
738
|
-
return
|
|
739
|
-
dist/
|
|
664
|
+
return `dist/
|
|
740
665
|
node_modules/
|
|
741
666
|
.cache/
|
|
742
667
|
`;
|
|
@@ -899,7 +824,6 @@ export async function initLexiconCommand(options: InitLexiconOptions): Promise<I
|
|
|
899
824
|
"docs/src/content",
|
|
900
825
|
"docs/src/content/docs",
|
|
901
826
|
"examples/getting-started",
|
|
902
|
-
".snapshots",
|
|
903
827
|
];
|
|
904
828
|
|
|
905
829
|
for (const dir of dirs) {
|
|
@@ -918,7 +842,6 @@ export async function initLexiconCommand(options: InitLexiconOptions): Promise<I
|
|
|
918
842
|
"src/codegen/generate-cli.ts": generateCodegenGenerateCliTs(),
|
|
919
843
|
"src/codegen/naming.ts": generateCodegenNamingTs(),
|
|
920
844
|
"src/codegen/package.ts": generateCodegenPackageTs(name),
|
|
921
|
-
"src/codegen/rollback.ts": generateCodegenRollbackTs(),
|
|
922
845
|
"src/codegen/docs.ts": generateCodegenDocsTs(name),
|
|
923
846
|
"src/spec/fetch.ts": generateSpecFetchTs(),
|
|
924
847
|
"src/spec/parse.ts": generateSpecParseTs(),
|
|
@@ -947,7 +870,6 @@ export async function initLexiconCommand(options: InitLexiconOptions): Promise<I
|
|
|
947
870
|
const gitkeeps = [
|
|
948
871
|
"src/generated/.gitkeep",
|
|
949
872
|
"examples/getting-started/.gitkeep",
|
|
950
|
-
".snapshots/.gitkeep",
|
|
951
873
|
];
|
|
952
874
|
|
|
953
875
|
for (const gk of gitkeeps) {
|
package/src/cli/commands/init.ts
CHANGED
|
@@ -426,18 +426,29 @@ export async function initCommand(options: InitOptions): Promise<InitResult> {
|
|
|
426
426
|
}
|
|
427
427
|
|
|
428
428
|
// Install skills from the lexicon's plugin
|
|
429
|
+
// Write to both .chant/skills/ (chant's own location) and .claude/skills/ (Claude Code discovery)
|
|
429
430
|
try {
|
|
430
431
|
const plugin = await loadPlugin(options.lexicon);
|
|
431
432
|
if (plugin.skills) {
|
|
432
433
|
const skills = plugin.skills();
|
|
433
434
|
if (skills.length > 0) {
|
|
434
|
-
|
|
435
|
-
|
|
435
|
+
// .chant/skills/ — chant's own skill storage
|
|
436
|
+
const chantSkillsDir = join(targetDir, ".chant", "skills", options.lexicon);
|
|
437
|
+
mkdirSync(chantSkillsDir, { recursive: true });
|
|
436
438
|
for (const skill of skills) {
|
|
437
|
-
const skillPath = join(
|
|
439
|
+
const skillPath = join(chantSkillsDir, `${skill.name}.md`);
|
|
438
440
|
writeFileSync(skillPath, skill.content);
|
|
439
441
|
createdFiles.push(`.chant/skills/${options.lexicon}/${skill.name}.md`);
|
|
440
442
|
}
|
|
443
|
+
|
|
444
|
+
// .claude/skills/ — Claude Code skill discovery format
|
|
445
|
+
for (const skill of skills) {
|
|
446
|
+
const claudeSkillDir = join(targetDir, ".claude", "skills", skill.name);
|
|
447
|
+
mkdirSync(claudeSkillDir, { recursive: true });
|
|
448
|
+
const claudeSkillPath = join(claudeSkillDir, "SKILL.md");
|
|
449
|
+
writeFileSync(claudeSkillPath, skill.content);
|
|
450
|
+
createdFiles.push(`.claude/skills/${skill.name}/SKILL.md`);
|
|
451
|
+
}
|
|
441
452
|
}
|
|
442
453
|
}
|
|
443
454
|
} catch {
|
package/src/cli/commands/list.ts
CHANGED
|
@@ -51,8 +51,8 @@ export async function listCommand(options: ListOptions): Promise<ListResult> {
|
|
|
51
51
|
for (const [name, decl] of result.entities) {
|
|
52
52
|
entities.push({
|
|
53
53
|
name,
|
|
54
|
-
lexicon: decl.lexicon,
|
|
55
|
-
entityType: decl.entityType,
|
|
54
|
+
lexicon: decl.lexicon ?? "",
|
|
55
|
+
entityType: decl.entityType ?? "",
|
|
56
56
|
kind: decl.kind ?? "resource",
|
|
57
57
|
});
|
|
58
58
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, writeFileSync, cpSync, readdirSync, statSync } from "fs";
|
|
1
|
+
import { existsSync, mkdirSync, writeFileSync, cpSync, readdirSync, statSync, readFileSync } from "fs";
|
|
2
2
|
import { join, resolve } from "path";
|
|
3
|
+
import { createRequire } from "module";
|
|
3
4
|
import { formatSuccess, formatWarning, formatError } from "../format";
|
|
4
5
|
import { loadChantConfig } from "../../config";
|
|
5
6
|
import { loadPlugins } from "../plugins";
|
|
@@ -63,13 +64,14 @@ function copyTypeFiles(src: string, dest: string): number {
|
|
|
63
64
|
function resolvePackagePath(packageName: string, projectDir: string): string | undefined {
|
|
64
65
|
// Try resolve from project dir
|
|
65
66
|
try {
|
|
66
|
-
const
|
|
67
|
+
const _require = createRequire(join(projectDir, "package.json"));
|
|
68
|
+
const entryPoint = _require.resolve(packageName);
|
|
67
69
|
// Walk up from entry point to find package root
|
|
68
70
|
let dir = entryPoint;
|
|
69
71
|
while (dir !== "/") {
|
|
70
72
|
dir = join(dir, "..");
|
|
71
73
|
if (existsSync(join(dir, "package.json"))) {
|
|
72
|
-
const pkg = JSON.parse(
|
|
74
|
+
const pkg = JSON.parse(readFileSync(join(dir, "package.json"), "utf-8"));
|
|
73
75
|
if (pkg.name === packageName) return dir;
|
|
74
76
|
}
|
|
75
77
|
}
|
package/src/cli/handlers/dev.ts
CHANGED
|
@@ -21,18 +21,10 @@ export async function runDevPublish(ctx: CommandContext): Promise<number> {
|
|
|
21
21
|
return 0;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
export async function runDevRollback(ctx: CommandContext): Promise<number> {
|
|
25
|
-
for (const plugin of ctx.plugins) {
|
|
26
|
-
await plugin.rollback({ verbose: ctx.args.verbose });
|
|
27
|
-
console.error(formatSuccess(`${plugin.name}: rollback complete`));
|
|
28
|
-
}
|
|
29
|
-
return 0;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
24
|
export async function runDevUnknown(ctx: CommandContext): Promise<number> {
|
|
33
25
|
console.error(formatError({
|
|
34
26
|
message: `Unknown dev subcommand: ${ctx.args.path}`,
|
|
35
|
-
hint: "Available: chant dev generate, chant dev publish
|
|
27
|
+
hint: "Available: chant dev generate, chant dev publish",
|
|
36
28
|
}));
|
|
37
29
|
return 1;
|
|
38
30
|
}
|
package/src/cli/main.ts
CHANGED
|
@@ -4,9 +4,11 @@ import { resolve } from "node:path";
|
|
|
4
4
|
import { formatSuccess, formatError } from "./format";
|
|
5
5
|
import { loadPlugins, resolveProjectLexicons } from "./plugins";
|
|
6
6
|
import { resolveCommand, type CommandDef, type ParsedArgs } from "./registry";
|
|
7
|
+
import { loadChantConfig } from "../config";
|
|
8
|
+
import { initRuntime } from "../runtime-adapter";
|
|
7
9
|
import { runBuild } from "./handlers/build";
|
|
8
10
|
import { runLint } from "./handlers/lint";
|
|
9
|
-
import { runDevGenerate, runDevPublish,
|
|
11
|
+
import { runDevGenerate, runDevPublish, runDevUnknown } from "./handlers/dev";
|
|
10
12
|
import { runServeLsp, runServeMcp, runServeUnknown } from "./handlers/serve";
|
|
11
13
|
import { runInit, runInitLexicon } from "./handlers/init";
|
|
12
14
|
import { runList, runImport, runUpdate, runDoctor } from "./handlers/misc";
|
|
@@ -89,7 +91,6 @@ Commands:
|
|
|
89
91
|
Lexicon development:
|
|
90
92
|
dev generate Generate lexicon artifacts (+ validate + coverage)
|
|
91
93
|
dev publish Package lexicon for distribution
|
|
92
|
-
dev rollback List or restore generation snapshots
|
|
93
94
|
|
|
94
95
|
Servers:
|
|
95
96
|
serve lsp Start the LSP server (stdio)
|
|
@@ -167,7 +168,6 @@ const registry: CommandDef[] = [
|
|
|
167
168
|
// Dev subcommands
|
|
168
169
|
{ name: "dev generate", requiresPlugins: true, handler: runDevGenerate },
|
|
169
170
|
{ name: "dev publish", requiresPlugins: true, handler: runDevPublish },
|
|
170
|
-
{ name: "dev rollback", requiresPlugins: true, handler: runDevRollback },
|
|
171
171
|
|
|
172
172
|
// Serve subcommands
|
|
173
173
|
{ name: "serve lsp", requiresPlugins: true, handler: runServeLsp },
|
|
@@ -189,6 +189,16 @@ async function main(): Promise<void> {
|
|
|
189
189
|
process.exit(args.help ? 0 : 1);
|
|
190
190
|
}
|
|
191
191
|
|
|
192
|
+
// Initialize runtime adapter early — before plugins or commands run
|
|
193
|
+
const projectPath0 = resolve(args.path === "." ? "." : args.path);
|
|
194
|
+
try {
|
|
195
|
+
const { config } = await loadChantConfig(projectPath0);
|
|
196
|
+
initRuntime(config.runtime);
|
|
197
|
+
} catch {
|
|
198
|
+
// Config may not exist yet (e.g. `chant init`); auto-detect runtime
|
|
199
|
+
initRuntime();
|
|
200
|
+
}
|
|
201
|
+
|
|
192
202
|
const match = resolveCommand(args, registry);
|
|
193
203
|
if (!match) {
|
|
194
204
|
console.error(formatError({
|