brepjs-verify 0.2.1 → 0.3.0

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/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.3.0](https://github.com/andymai/brepjs/compare/brepjs-verify-v0.2.1...brepjs-verify-v0.3.0) (2026-06-04)
4
+
5
+
6
+ ### Features
7
+
8
+ * **brepjs-verify:** live text-to-cad eval flywheel ([#1215](https://github.com/andymai/brepjs/issues/1215)) ([4e81fc4](https://github.com/andymai/brepjs/commit/4e81fc4053491ce3e08182d57d76bd649252ea3c))
9
+ * **brepjs-verify:** standalone bundled CLI + rename from brepjs-cad ([#1211](https://github.com/andymai/brepjs/issues/1211)) ([05b3799](https://github.com/andymai/brepjs/commit/05b3799a0e9ee4968d4cac92f3a2ea236e39cd35))
10
+
11
+
12
+ ### Bug Fixes
13
+
14
+ * **brepjs-verify:** correct fillet/chamfer arg order in no-edges hints ([#1218](https://github.com/andymai/brepjs/issues/1218)) ([835f13a](https://github.com/andymai/brepjs/commit/835f13ac966b4264ba56a5cfc371bbbbbd1a0f01))
15
+
3
16
  ## [0.2.1](https://github.com/andymai/brepjs/compare/brepjs-cad-v0.2.0...brepjs-cad-v0.2.1) (2026-06-04)
4
17
 
5
18
 
package/README.md CHANGED
@@ -71,6 +71,23 @@ Few-shot examples live under `skill/examples/<name>.brep.ts`, each with a `<name
71
71
 
72
72
  `npm run eval` (`bench/run.ts`) replays every `skill/examples/*.brep.ts` with a sibling `*.expected.json` through the public `runPart` runtime, compares measured volume/area/validity/shape-type against the recorded baseline within each file's tolerance (default 0.5%), prints a PASS/FAIL scorecard, and exits non-zero on any regression. It is deterministic — no LLM or API key — so it runs in CI as the package's regression net. Refresh a baseline by re-recording the example's `*.expected.json` after an intentional geometry change.
73
73
 
74
+ ### Live eval (`npm run eval:live`)
75
+
76
+ The measurement flywheel: sends ~18 natural-language part prompts (`bench/prompts.ts`) to a real model — using the **deployed `SKILL.md` as the system prompt**, so it measures the actual skill — then verifies each generated part two ways:
77
+
78
+ - **Auto (objective):** `runPart --check` → valid solid + any pinned dims within tolerance.
79
+ - **Judge (intent):** a multimodal Claude call looks at the rendered iso/front/top/right snapshots and decides whether the part matches the request + rubric.
80
+
81
+ The scorecard reports per-category `valid` / `judge` / `both` rates and stamps the model + **resolved brepjs version** + date (so trend lines don't mix kernel versions).
82
+
83
+ ```bash
84
+ ANTHROPIC_API_KEY=sk-... npm run eval:live -w brepjs-verify # opus by default
85
+ ANTHROPIC_API_KEY=sk-... npm run eval:live -w brepjs-verify -- --model claude-sonnet-4-6
86
+ # --only <id|category> run a subset --keep keep the generated parts
87
+ ```
88
+
89
+ Opt-in and **billed** (real API calls), so it does _not_ run in CI — the deterministic replay above is the CI gate. Snapshots (hence the judge) need `puppeteer`/Chrome; without them the run scores on auto-verify alone and notes the skipped judge.
90
+
74
91
  ## Programmatic API
75
92
 
76
93
  ```ts
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_diff = require("./diff-CZ4mLtrf.cjs");
2
+ const require_diff = require("./diff-mxAOOl_m.cjs");
3
3
  exports.DEFAULT_TOLERANCE_PCT = require_diff.DEFAULT_TOLERANCE_PCT;
4
4
  exports.TYPECHECK_CODE = require_diff.TYPECHECK_CODE;
5
5
  exports.emptyReport = require_diff.emptyReport;
@@ -1,2 +1,2 @@
1
- import { a as typecheckPart, c as isExpectedDims, d as emptyReport, i as TYPECHECK_CODE, l as pctDelta, m as serializeReport, n as runMeasure, o as DEFAULT_TOLERANCE_PCT, r as runPart, s as evaluateExpected, t as runDiff, u as runChecks } from "./diff-D7ZBNRJG.js";
1
+ import { a as typecheckPart, c as isExpectedDims, d as emptyReport, i as TYPECHECK_CODE, l as pctDelta, m as serializeReport, n as runMeasure, o as DEFAULT_TOLERANCE_PCT, r as runPart, s as evaluateExpected, t as runDiff, u as runChecks } from "./diff-Covv1XuJ.js";
2
2
  export { DEFAULT_TOLERANCE_PCT, TYPECHECK_CODE, emptyReport, evaluateExpected, isExpectedDims, pctDelta, runChecks, runDiff, runMeasure, runPart, serializeReport, typecheckPart };
package/dist/cli/main.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const require_diff = require("../diff-CZ4mLtrf.cjs");
3
+ const require_diff = require("../diff-mxAOOl_m.cjs");
4
4
  let node_url = require("node:url");
5
5
  let node_fs = require("node:fs");
6
6
  let node_path = require("node:path");
package/dist/cli/main.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { f as pushError, h as loadBrep, m as serializeReport, n as runMeasure, p as reportOk, r as runPart, t as runDiff } from "../diff-D7ZBNRJG.js";
2
+ import { f as pushError, h as loadBrep, m as serializeReport, n as runMeasure, p as reportOk, r as runPart, t as runDiff } from "../diff-Covv1XuJ.js";
3
3
  import { fileURLToPath } from "node:url";
4
4
  import { existsSync, mkdirSync, realpathSync, watch, writeFileSync } from "node:fs";
5
5
  import { basename, dirname, join, resolve } from "node:path";
@@ -39,6 +39,14 @@ function loadBrep() {
39
39
  }
40
40
  return cached;
41
41
  }
42
+ var kernelReady;
43
+ function initOcctWasm(brep) {
44
+ if (!kernelReady) kernelReady = (async () => {
45
+ const kernel = await (await import("occt-wasm")).OcctKernel.init();
46
+ brep.registerKernel("occt-wasm", brep.OcctWasmAdapter.fromKernel(kernel));
47
+ })();
48
+ return kernelReady;
49
+ }
42
50
  //#endregion
43
51
  //#region src/verify/report.ts
44
52
  function emptyReport() {
@@ -60,7 +68,7 @@ function reportOk(r) {
60
68
  return r.errors.length === 0 && r.checks.every((c) => c.passed) && r.assertions.every((a) => a.passed);
61
69
  }
62
70
  /**
63
- * Local, brepjs-cad-owned advice keyed on `BrepErrorCode` values (see `brepjs`'s public
71
+ * Local, brepjs-verify-owned advice keyed on `BrepErrorCode` values (see `brepjs`'s public
64
72
  * `BrepErrorCode`). Intentionally not importing the library's internal `getSuggestionForCode`:
65
73
  * this table is the agent loop's own actionable `fix` + `nextStep` guidance, and the library's
66
74
  * public `BrepError.suggestion` is still surfaced alongside it on each hint.
@@ -68,11 +76,11 @@ function reportOk(r) {
68
76
  var HINT_TABLE = {
69
77
  FILLET_NO_EDGES: {
70
78
  fix: "Select real edges before filleting — pass an edge query (e.g. find edges by direction/position) or a non-empty edge list, not the whole solid.",
71
- nextStep: "List the solid’s edges, pick the ones to round, then call fillet(solid, radius, edges)."
79
+ nextStep: "List the solid’s edges, pick the ones to round, then call fillet(solid, edges, radius)."
72
80
  },
73
81
  CHAMFER_NO_EDGES: {
74
82
  fix: "Select real edges before chamfering — pass a non-empty edge query/list rather than relying on a default that matched nothing.",
75
- nextStep: "Enumerate the solid’s edges, choose the target edges, then call chamfer(solid, distance, edges)."
83
+ nextStep: "Enumerate the solid’s edges, choose the target edges, then call chamfer(solid, edges, distance)."
76
84
  },
77
85
  INVALID_FILLET_RADIUS: {
78
86
  fix: "Use a fillet radius that is > 0 and small enough to fit the adjacent faces (well under half the thinnest wall).",
@@ -499,7 +507,7 @@ async function runPart(modulePath, opts = {}) {
499
507
  let brep;
500
508
  try {
501
509
  brep = await loadBrep();
502
- await brep.init();
510
+ await initOcctWasm(brep);
503
511
  } catch (e) {
504
512
  pushError(report, toErrorInfo("kernel init failed", e));
505
513
  return finalize({
@@ -41,6 +41,14 @@ function loadBrep() {
41
41
  }
42
42
  return cached;
43
43
  }
44
+ var kernelReady;
45
+ function initOcctWasm(brep) {
46
+ if (!kernelReady) kernelReady = (async () => {
47
+ const kernel = await (await import("occt-wasm")).OcctKernel.init();
48
+ brep.registerKernel("occt-wasm", brep.OcctWasmAdapter.fromKernel(kernel));
49
+ })();
50
+ return kernelReady;
51
+ }
44
52
  //#endregion
45
53
  //#region src/verify/report.ts
46
54
  function emptyReport() {
@@ -62,7 +70,7 @@ function reportOk(r) {
62
70
  return r.errors.length === 0 && r.checks.every((c) => c.passed) && r.assertions.every((a) => a.passed);
63
71
  }
64
72
  /**
65
- * Local, brepjs-cad-owned advice keyed on `BrepErrorCode` values (see `brepjs`'s public
73
+ * Local, brepjs-verify-owned advice keyed on `BrepErrorCode` values (see `brepjs`'s public
66
74
  * `BrepErrorCode`). Intentionally not importing the library's internal `getSuggestionForCode`:
67
75
  * this table is the agent loop's own actionable `fix` + `nextStep` guidance, and the library's
68
76
  * public `BrepError.suggestion` is still surfaced alongside it on each hint.
@@ -70,11 +78,11 @@ function reportOk(r) {
70
78
  var HINT_TABLE = {
71
79
  FILLET_NO_EDGES: {
72
80
  fix: "Select real edges before filleting — pass an edge query (e.g. find edges by direction/position) or a non-empty edge list, not the whole solid.",
73
- nextStep: "List the solid’s edges, pick the ones to round, then call fillet(solid, radius, edges)."
81
+ nextStep: "List the solid’s edges, pick the ones to round, then call fillet(solid, edges, radius)."
74
82
  },
75
83
  CHAMFER_NO_EDGES: {
76
84
  fix: "Select real edges before chamfering — pass a non-empty edge query/list rather than relying on a default that matched nothing.",
77
- nextStep: "Enumerate the solid’s edges, choose the target edges, then call chamfer(solid, distance, edges)."
85
+ nextStep: "Enumerate the solid’s edges, choose the target edges, then call chamfer(solid, edges, distance)."
78
86
  },
79
87
  INVALID_FILLET_RADIUS: {
80
88
  fix: "Use a fillet radius that is > 0 and small enough to fit the adjacent faces (well under half the thinnest wall).",
@@ -501,7 +509,7 @@ async function runPart(modulePath, opts = {}) {
501
509
  let brep;
502
510
  try {
503
511
  brep = await loadBrep();
504
- await brep.init();
512
+ await initOcctWasm(brep);
505
513
  } catch (e) {
506
514
  pushError(report, toErrorInfo("kernel init failed", e));
507
515
  return finalize({
@@ -11,7 +11,7 @@ async function probe(port) {
11
11
  const res = await fetch(`http://127.0.0.1:${port}/__cad/server`, { signal: ctrl.signal });
12
12
  if (!res.ok) return false;
13
13
  const d = await res.json();
14
- return d.app === "brepjs-cad-viewer" && d.dynamicRoot === true && typeof d.serverApiVersion === "number" && d.serverApiVersion >= 1;
14
+ return d.app === "brepjs-verify-viewer" && d.dynamicRoot === true && typeof d.serverApiVersion === "number" && d.serverApiVersion >= 1;
15
15
  } catch {
16
16
  return false;
17
17
  } finally {
@@ -10,7 +10,7 @@ async function probe(port) {
10
10
  const res = await fetch(`http://127.0.0.1:${port}/__cad/server`, { signal: ctrl.signal });
11
11
  if (!res.ok) return false;
12
12
  const d = await res.json();
13
- return d.app === "brepjs-cad-viewer" && d.dynamicRoot === true && typeof d.serverApiVersion === "number" && d.serverApiVersion >= 1;
13
+ return d.app === "brepjs-verify-viewer" && d.dynamicRoot === true && typeof d.serverApiVersion === "number" && d.serverApiVersion >= 1;
14
14
  } catch {
15
15
  return false;
16
16
  } finally {
@@ -45,7 +45,7 @@ async function handle(req, res, port) {
45
45
  const url = new URL(req.url ?? "/", `http://127.0.0.1:${port}`);
46
46
  if (url.pathname === "/__cad/server") {
47
47
  const d = {
48
- app: "brepjs-cad-viewer",
48
+ app: "brepjs-verify-viewer",
49
49
  port,
50
50
  dynamicRoot: true,
51
51
  serverApiVersion: 1
@@ -8,7 +8,7 @@ export interface StaticServer {
8
8
  close(): Promise<void>;
9
9
  }
10
10
  export interface ServerDescriptor {
11
- app: 'brepjs-cad-viewer';
11
+ app: 'brepjs-verify-viewer';
12
12
  port: number;
13
13
  dynamicRoot: true;
14
14
  serverApiVersion: number;
@@ -44,7 +44,7 @@ async function handle(req, res, port) {
44
44
  const url = new URL(req.url ?? "/", `http://127.0.0.1:${port}`);
45
45
  if (url.pathname === "/__cad/server") {
46
46
  const d = {
47
- app: "brepjs-cad-viewer",
47
+ app: "brepjs-verify-viewer",
48
48
  port,
49
49
  dynamicRoot: true,
50
50
  serverApiVersion: 1
@@ -2,3 +2,4 @@ import type * as Brep from 'brepjs';
2
2
  export type BrepNs = typeof Brep;
3
3
  export declare function toolDir(): string;
4
4
  export declare function loadBrep(): Promise<BrepNs>;
5
+ export declare function initOcctWasm(brep: BrepNs): Promise<void>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brepjs-verify",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "description": "Agent skill + verify/preview tooling for authoring parametric brepjs CAD code",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -44,6 +44,7 @@
44
44
  "lint": "eslint src tests viewer bench",
45
45
  "test": "vitest run",
46
46
  "eval": "tsx bench/run.ts",
47
+ "eval:live": "tsx bench/live.ts",
47
48
  "smoke": "node dist/cli/main.js verify tests/fixtures/validBox.brep.ts | node -e \"const r=JSON.parse(require('fs').readFileSync(0,'utf8'));if(r.ok!==true||!(r.measurements.volume>0))process.exit(1)\"",
48
49
  "smoke:standalone": "node scripts/smokeStandalone.mjs",
49
50
  "prepack": "npm run build"
@@ -58,6 +59,7 @@
58
59
  "puppeteer": "^25.0.4"
59
60
  },
60
61
  "devDependencies": {
62
+ "@anthropic-ai/sdk": "0.100.1",
61
63
  "@react-three/drei": "^10.7.7",
62
64
  "@react-three/fiber": "^9.6.1",
63
65
  "@types/node": "^25.9.1",
@@ -73,6 +75,7 @@
73
75
  "tsx": "^4.22.3",
74
76
  "vite": "^8.0.0",
75
77
  "vite-plugin-dts": "^5.0.1",
76
- "vitest": "^4.0.0"
78
+ "vitest": "^4.0.0",
79
+ "zod": "4.4.3"
77
80
  }
78
81
  }