From 9a2821be811dec572e52f49f95ff2d7eeb900b6d Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 30 Mar 2026 15:05:40 +0000 Subject: [PATCH 1/2] Fix ENOENT crash on blank execution by deferring file reads MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The CLI bundled all modules eagerly, so module-level arrays in check-compiler.ts and check-query-propagation.ts called fileCase() (which reads files via readFileSync) at import time — even for unrelated commands like `forgecad studio --blank`. Two fixes: 1. Use resolvePackagePath() for the brep-exportable example path so it resolves relative to the package root, not cwd. 2. Make COMPILER_CASES and QUERY_PROPAGATION_CASES lazy (computed on first access) so file I/O only happens when check commands actually run. Fixes KoStard/ForgeCAD#2 https://claude.ai/code/session_01AfqK6U2FZDi63KGea3Mp8n --- cli/check-compiler.ts | 14 ++++++++++---- cli/check-query-propagation.ts | 13 +++++++++---- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/cli/check-compiler.ts b/cli/check-compiler.ts index 77d37c8a..7a7ab12b 100644 --- a/cli/check-compiler.ts +++ b/cli/check-compiler.ts @@ -91,7 +91,10 @@ function fileCase(id: string, description: string, scriptPath: string): Compiler }; } -const COMPILER_CASES: CompilerCaseDefinition[] = [ +let _compilerCases: CompilerCaseDefinition[] | undefined; +function getCompilerCases(): CompilerCaseDefinition[] { + if (_compilerCases) return _compilerCases; + _compilerCases = [ inlineCase( 'exact-boolean-plate', 'Exact boolean plate with profile booleans and tapered extrude stays on the exact route.', @@ -335,9 +338,11 @@ return [{ name: 'Profile', sketch: profile }]; fileCase( 'example-brep-exportable', 'The public BREP-exportable example stays on the exact compiler route.', - 'examples/api/brep-exportable.forge.js', + resolvePackagePath(import.meta.url, 'examples', 'api', 'brep-exportable.forge.js'), ), -]; + ]; + return _compilerCases; +} function parseArgs(argv: string[]) { let update = false; @@ -380,7 +385,8 @@ function stripUndefinedDeep(value: T): T { } function generateSnapshots(caseId?: string): CompilerCaseSnapshot[] { - const selected = caseId ? COMPILER_CASES.filter((entry) => entry.id === caseId) : COMPILER_CASES; + const cases = getCompilerCases(); + const selected = caseId ? cases.filter((entry) => entry.id === caseId) : cases; if (selected.length === 0) { throw new Error(`Unknown compiler snapshot case: ${caseId}`); } diff --git a/cli/check-query-propagation.ts b/cli/check-query-propagation.ts index 53b325a2..d72e5c50 100644 --- a/cli/check-query-propagation.ts +++ b/cli/check-query-propagation.ts @@ -180,7 +180,10 @@ function compilerCorpusCase( return fileCase(part.id, description, part.scriptPath, expectedObjects); } -const QUERY_PROPAGATION_CASES: QueryPropagationCaseDefinition[] = [ +let _queryPropagationCases: QueryPropagationCaseDefinition[] | undefined; +function getQueryPropagationCases(): QueryPropagationCaseDefinition[] { + if (_queryPropagationCases) return _queryPropagationCases; + _queryPropagationCases = [ inlineCase( 'trim-and-split-created-faces', 'Trim and split-by-plane workflows keep the defended plane-cap created-face query visible for each surviving branch.', @@ -478,7 +481,9 @@ const QUERY_PROPAGATION_CASES: QueryPropagationCaseDefinition[] = [ }, ], ), -]; + ]; + return _queryPropagationCases; +} function parseArgs(argv: string[]) { let update = false; @@ -583,7 +588,7 @@ function summarizeShapeObject(object: CompilerShapeInspection): QueryPropagation } function generateSnapshots(caseId?: string): QueryPropagationCaseSnapshot[] { - const selected = caseId ? QUERY_PROPAGATION_CASES.filter((entry) => entry.id === caseId) : QUERY_PROPAGATION_CASES; + const selected = caseId ? getQueryPropagationCases().filter((entry) => entry.id === caseId) : getQueryPropagationCases(); if (selected.length === 0) { throw new Error(`Unknown query-propagation snapshot case: ${caseId}`); } @@ -626,7 +631,7 @@ function assertIncludesAll( } function assertExpectedCoverage(snapshots: QueryPropagationCaseSnapshot[]): void { - const definitions = new Map(QUERY_PROPAGATION_CASES.map((entry) => [entry.id, entry])); + const definitions = new Map(getQueryPropagationCases().map((entry) => [entry.id, entry])); for (const snapshot of snapshots) { const definition = definitions.get(snapshot.id); From 79dc48914d96cb996d148566c7e32a1ea25f8514 Mon Sep 17 00:00:00 2001 From: Ruben Kostandyan Date: Mon, 30 Mar 2026 21:38:51 +0200 Subject: [PATCH 2/2] Fix CI: build skill before vite to resolve dist-skill/CONTEXT.md AISkillDialog.tsx imports dist-skill/CONTEXT.md?raw at build time, but npm run build never ran build:skill:forgecad, so vite failed with "Could not resolve" in CI. Add skill build as a prerequisite. Co-Authored-By: Claude Sonnet 4.6 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e1988781..32b13ebb 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "build:solver": "node scripts/solver-build.mjs --release", "build:solver:dev": "node scripts/solver-build.mjs", "test:solver": "node scripts/solver-test.mjs", - "build": "npm run build:solver && tsc && vite build && npm run build:cli", + "build": "npm run build:solver && npm run build:skill:forgecad && tsc && vite build && npm run build:cli", "build:web": "npm run build:solver && tsc && FORGE_MODE=web vite build", "dev:cli": "tsup cli/forgecad.ts --format esm --platform node --target node20 --out-dir dist-cli --sourcemap --external typescript", "build:cli": "tsup cli/forgecad.ts --format esm --platform node --target node20 --out-dir dist-cli --clean --sourcemap --external typescript",