wadi-mcp 0.1.24 → 0.1.25

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 (3) hide show
  1. package/README.md +1 -0
  2. package/dist/server.mjs +201 -128
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -16,6 +16,7 @@ repo; this server replaces them for agents that speak MCP.)
16
16
  | Tool | What it does |
17
17
  | --- | --- |
18
18
  | `wadi_check` | Compile + validate a `.wdl`: parse, resolve formulas/grids, schema + wall/roof geometry, and the structural conventions (C1/C2/C3). Returns pass/fail + errors/warnings. **Run after every edit.** |
19
+ | `wadi_scope` | Resolve a `.wdl`'s **variables, points & grid lines** to their actual values (with each variable's formula). See what `main.x2` / `House.W/2` resolves to before placing an object. |
19
20
  | `wadi_preview` | Render a `.wdl` to **PNG images** you can look at — floor plans, elevations, roof top view. Confirm layout/sizes/openings/roof visually. |
20
21
  | `wadi_examples` | List, or fetch the full source of, a validated example `.wdl` (`minimal` / `two_room` / `two_story` / `coastal` / `complete`). Copy from these. |
21
22
  | `wadi_reference` | The authoring docs, embedded: `guide`, `dsl`, `conventions`, `coordinate-system`, `parametric-conventions`, `roof-v2-guide`, `data-model`. |
package/dist/server.mjs CHANGED
@@ -14016,7 +14016,7 @@ function resolveGrids(config3, scope, warnings, defaultT) {
14016
14016
  const out = /* @__PURE__ */ new Map();
14017
14017
  const grids = config3.grids;
14018
14018
  if (!grids) return out;
14019
- const num3 = (v, where) => {
14019
+ const num4 = (v, where) => {
14020
14020
  if (typeof v === "number") return v;
14021
14021
  if (isFormula(v)) {
14022
14022
  const r2 = evalFormula(v, scope);
@@ -14037,9 +14037,9 @@ function resolveGrids(config3, scope, warnings, defaultT) {
14037
14037
  const thickMap = axis === "x" ? rg.xt : rg.yt;
14038
14038
  for (const ln of lines ?? []) {
14039
14039
  if (!ln || typeof ln.name !== "string") continue;
14040
- const pos = num3(ln.at, `grids/${id}/${axis}/${ln.name}`);
14040
+ const pos = num4(ln.at, `grids/${id}/${axis}/${ln.name}`);
14041
14041
  if (pos !== null) posMap.set(ln.name, pos);
14042
- const t = ln.thickness === void 0 ? defaultT : num3(ln.thickness, `grids/${id}/${axis}/${ln.name}/thickness`) ?? defaultT;
14042
+ const t = ln.thickness === void 0 ? defaultT : num4(ln.thickness, `grids/${id}/${axis}/${ln.name}/thickness`) ?? defaultT;
14043
14043
  thickMap.set(ln.name, t);
14044
14044
  }
14045
14045
  }
@@ -31568,15 +31568,15 @@ var init_layerDefaults = __esm({
31568
31568
  // ../editor/src/svg2d/config.ts
31569
31569
  function houseSpanUnits(hc) {
31570
31570
  const o = hc;
31571
- const num3 = (v) => typeof v === "number" && Number.isFinite(v) ? v : void 0;
31571
+ const num4 = (v) => typeof v === "number" && Number.isFinite(v) ? v : void 0;
31572
31572
  let best = 0;
31573
31573
  for (const fl of o?.floors ?? []) {
31574
31574
  let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
31575
31575
  for (const obj of fl.objects ?? []) {
31576
31576
  const t = obj.type;
31577
31577
  if (t === "floor_slab" || t === "beam" || t === "room") {
31578
- const x = num3(obj.x), y = num3(obj.y);
31579
- const w = num3(obj.width), l = num3(obj.length);
31578
+ const x = num4(obj.x), y = num4(obj.y);
31579
+ const w = num4(obj.width), l = num4(obj.length);
31580
31580
  if (x !== void 0 && y !== void 0 && w !== void 0 && l !== void 0) {
31581
31581
  if (x < minX) minX = x;
31582
31582
  if (y < minY) minY = y;
@@ -31584,8 +31584,8 @@ function houseSpanUnits(hc) {
31584
31584
  if (y + l > maxY) maxY = y + l;
31585
31585
  }
31586
31586
  } else if (t === "wall") {
31587
- const sx = num3(obj.start_x), sy = num3(obj.start_y);
31588
- const ex = num3(obj.end_x), ey = num3(obj.end_y);
31587
+ const sx = num4(obj.start_x), sy = num4(obj.start_y);
31588
+ const ex = num4(obj.end_x), ey = num4(obj.end_y);
31589
31589
  if (sx !== void 0 && sy !== void 0 && ex !== void 0 && ey !== void 0) {
31590
31590
  minX = Math.min(minX, sx, ex);
31591
31591
  maxX = Math.max(maxX, sx, ex);
@@ -31600,7 +31600,7 @@ function houseSpanUnits(hc) {
31600
31600
  }
31601
31601
  if (best > 0) return best;
31602
31602
  const s = o?.site;
31603
- return Math.max(num3(s?.plot_length) ?? 0, num3(s?.plot_width) ?? 0);
31603
+ return Math.max(num4(s?.plot_length) ?? 0, num4(s?.plot_width) ?? 0);
31604
31604
  }
31605
31605
  function computeTextScale(spanUnits) {
31606
31606
  if (!(spanUnits > 0)) return 1;
@@ -83764,8 +83764,8 @@ var require_three = __commonJS({
83764
83764
  function onSegment(p, q, r2) {
83765
83765
  return q.x <= Math.max(p.x, r2.x) && q.x >= Math.min(p.x, r2.x) && q.y <= Math.max(p.y, r2.y) && q.y >= Math.min(p.y, r2.y);
83766
83766
  }
83767
- function sign(num3) {
83768
- return num3 > 0 ? 1 : num3 < 0 ? -1 : 0;
83767
+ function sign(num4) {
83768
+ return num4 > 0 ? 1 : num4 < 0 ? -1 : 0;
83769
83769
  }
83770
83770
  function intersectsPolygon(a, b) {
83771
83771
  let p = a;
@@ -149717,7 +149717,7 @@ var require_ColladaExporter = __commonJS({
149717
149717
  const IS_END_TAG = /^<\//;
149718
149718
  const IS_SELF_CLOSING = /(\?>$)|(\/>$)/;
149719
149719
  const HAS_TEXT = /<[^>]+>[^<]*<\/[^<]+>/;
149720
- const pad = (ch, num3) => num3 > 0 ? ch + pad(ch, num3 - 1) : "";
149720
+ const pad = (ch, num4) => num4 > 0 ? ch + pad(ch, num4 - 1) : "";
149721
149721
  let tagnum = 0;
149722
149722
  return (_b = (_a3 = urdf.match(/(<[^>]+>[^<]+<\/[^<]+>)|(<[^>]+>)/g)) == null ? void 0 : _a3.map((tag) => {
149723
149723
  if (!HAS_TEXT.test(tag) && !IS_SELF_CLOSING.test(tag) && IS_END_TAG.test(tag)) {
@@ -160756,10 +160756,10 @@ var require_MMDExporter = __commonJS({
160756
160756
  console.warn("THREE.MMDExporter: parseVpd() requires SkinnedMesh instance.");
160757
160757
  return null;
160758
160758
  }
160759
- function toStringsFromNumber(num3) {
160760
- if (Math.abs(num3) < 1e-6)
160761
- num3 = 0;
160762
- let a = num3.toString();
160759
+ function toStringsFromNumber(num4) {
160760
+ if (Math.abs(num4) < 1e-6)
160761
+ num4 = 0;
160762
+ let a = num4.toString();
160763
160763
  if (a.indexOf(".") === -1) {
160764
160764
  a += ".";
160765
160765
  }
@@ -202252,8 +202252,8 @@ var require_FBXLoader = __commonJS({
202252
202252
  return result;
202253
202253
  }
202254
202254
  for (let i2 = 0; i2 < CORRECT.length; ++i2) {
202255
- const num3 = read(1);
202256
- if (num3 === CORRECT[i2]) {
202255
+ const num4 = read(1);
202256
+ if (num4 === CORRECT[i2]) {
202257
202257
  return false;
202258
202258
  }
202259
202259
  }
@@ -219131,8 +219131,8 @@ var require_KTX2Loader = __commonJS({
219131
219131
  this.transcoderPath = path;
219132
219132
  return this;
219133
219133
  }
219134
- setWorkerLimit(num3) {
219135
- this.workerPool.setWorkerLimit(num3);
219134
+ setWorkerLimit(num4) {
219135
+ this.workerPool.setWorkerLimit(num4);
219136
219136
  return this;
219137
219137
  }
219138
219138
  detectSupport(renderer) {
@@ -225396,8 +225396,8 @@ var require_lottie = __commonJS({
225396
225396
  this.renderableComponents.splice(this.renderableComponents.indexOf(component), 1);
225397
225397
  }
225398
225398
  },
225399
- prepareRenderableFrame: function(num3) {
225400
- this.checkLayerLimits(num3);
225399
+ prepareRenderableFrame: function(num4) {
225400
+ this.checkLayerLimits(num4);
225401
225401
  },
225402
225402
  checkTransparency: function() {
225403
225403
  if (this.finalTransform.mProp.o.v <= 0) {
@@ -225418,8 +225418,8 @@ var require_lottie = __commonJS({
225418
225418
  * current frame number in Layer's time
225419
225419
  *
225420
225420
  */
225421
- checkLayerLimits: function(num3) {
225422
- if (this.data.ip - this.data.st <= num3 && this.data.op - this.data.st > num3) {
225421
+ checkLayerLimits: function(num4) {
225422
+ if (this.data.ip - this.data.st <= num4 && this.data.op - this.data.st > num4) {
225423
225423
  if (this.isInRange !== true) {
225424
225424
  this.globalData._mdf = true;
225425
225425
  this._mdf = true;
@@ -226772,7 +226772,7 @@ var require_lottie = __commonJS({
226772
226772
  * if layers is currently in range
226773
226773
  *
226774
226774
  */
226775
- prepareProperties: function(num3, isVisible) {
226775
+ prepareProperties: function(num4, isVisible) {
226776
226776
  var i2;
226777
226777
  var len = this.dynamicProperties.length;
226778
226778
  for (i2 = 0; i2 < len; i2 += 1) {
@@ -226885,14 +226885,14 @@ var require_lottie = __commonJS({
226885
226885
  this.tm = data.tm ? PropertyFactory.getProp(this, data.tm, 0, globalData.frameRate, this) : { _placeholder: true };
226886
226886
  this.lv = PropertyFactory.getProp(this, data.au && data.au.lv ? data.au.lv : { k: [100] }, 1, 0.01, this);
226887
226887
  }
226888
- AudioElement.prototype.prepareFrame = function(num3) {
226889
- this.prepareRenderableFrame(num3, true);
226890
- this.prepareProperties(num3, true);
226888
+ AudioElement.prototype.prepareFrame = function(num4) {
226889
+ this.prepareRenderableFrame(num4, true);
226890
+ this.prepareProperties(num4, true);
226891
226891
  if (!this.tm._placeholder) {
226892
226892
  var timeRemapped = this.tm.v;
226893
226893
  this._currentTime = timeRemapped;
226894
226894
  } else {
226895
- this._currentTime = num3 / this.data.sr;
226895
+ this._currentTime = num4 / this.data.sr;
226896
226896
  }
226897
226897
  this._volume = this.lv.v[0];
226898
226898
  var totalVolume = this._volume * this._volumeMultiplier;
@@ -226946,7 +226946,7 @@ var require_lottie = __commonJS({
226946
226946
  };
226947
226947
  function BaseRenderer() {
226948
226948
  }
226949
- BaseRenderer.prototype.checkLayers = function(num3) {
226949
+ BaseRenderer.prototype.checkLayers = function(num4) {
226950
226950
  var i2;
226951
226951
  var len = this.layers.length;
226952
226952
  var data;
@@ -226954,7 +226954,7 @@ var require_lottie = __commonJS({
226954
226954
  for (i2 = len - 1; i2 >= 0; i2 -= 1) {
226955
226955
  if (!this.elements[i2]) {
226956
226956
  data = this.layers[i2];
226957
- if (data.ip - data.st <= num3 - this.layers[i2].st && data.op - data.st > num3 - this.layers[i2].st) {
226957
+ if (data.ip - data.st <= num4 - this.layers[i2].st && data.op - data.st > num4 - this.layers[i2].st) {
226958
226958
  this.buildItem(i2);
226959
226959
  }
226960
226960
  }
@@ -227702,10 +227702,10 @@ var require_lottie = __commonJS({
227702
227702
  },
227703
227703
  renderInnerContent: function() {
227704
227704
  },
227705
- prepareFrame: function(num3) {
227705
+ prepareFrame: function(num4) {
227706
227706
  this._mdf = false;
227707
- this.prepareRenderableFrame(num3);
227708
- this.prepareProperties(num3, this.isInRange);
227707
+ this.prepareRenderableFrame(num4);
227708
+ this.prepareProperties(num4, this.isInRange);
227709
227709
  this.checkTransparency();
227710
227710
  },
227711
227711
  destroy: function() {
@@ -227810,9 +227810,9 @@ var require_lottie = __commonJS({
227810
227810
  }
227811
227811
  elements.push(new ProcessedElement(elem, pos));
227812
227812
  },
227813
- prepareFrame: function(num3) {
227814
- this.prepareRenderableFrame(num3);
227815
- this.prepareProperties(num3, this.isInRange);
227813
+ prepareFrame: function(num4) {
227814
+ this.prepareRenderableFrame(num4);
227815
+ this.prepareProperties(num4, this.isInRange);
227816
227816
  }
227817
227817
  };
227818
227818
  const lineCapEnum = {
@@ -229978,10 +229978,10 @@ var require_lottie = __commonJS({
229978
229978
  this.hide();
229979
229979
  this.textAnimator.searchProperties(this.dynamicProperties);
229980
229980
  };
229981
- ITextElement.prototype.prepareFrame = function(num3) {
229981
+ ITextElement.prototype.prepareFrame = function(num4) {
229982
229982
  this._mdf = false;
229983
- this.prepareRenderableFrame(num3);
229984
- this.prepareProperties(num3, this.isInRange);
229983
+ this.prepareRenderableFrame(num4);
229984
+ this.prepareProperties(num4, this.isInRange);
229985
229985
  if (this.textProperty._mdf || this.textProperty._isFirstFrame) {
229986
229986
  this.buildNewText();
229987
229987
  this.textProperty._isFirstFrame = false;
@@ -230339,8 +230339,8 @@ var require_lottie = __commonJS({
230339
230339
  this.initTransform(data, globalData, comp);
230340
230340
  this.initHierarchy();
230341
230341
  }
230342
- NullElement.prototype.prepareFrame = function(num3) {
230343
- this.prepareProperties(num3, true);
230342
+ NullElement.prototype.prepareFrame = function(num4) {
230343
+ this.prepareProperties(num4, true);
230344
230344
  };
230345
230345
  NullElement.prototype.renderFrame = function() {
230346
230346
  };
@@ -230482,27 +230482,27 @@ var require_lottie = __commonJS({
230482
230482
  }
230483
230483
  }
230484
230484
  };
230485
- SVGRendererBase.prototype.renderFrame = function(num3) {
230486
- if (this.renderedFrame === num3 || this.destroyed) {
230485
+ SVGRendererBase.prototype.renderFrame = function(num4) {
230486
+ if (this.renderedFrame === num4 || this.destroyed) {
230487
230487
  return;
230488
230488
  }
230489
- if (num3 === null) {
230490
- num3 = this.renderedFrame;
230489
+ if (num4 === null) {
230490
+ num4 = this.renderedFrame;
230491
230491
  } else {
230492
- this.renderedFrame = num3;
230492
+ this.renderedFrame = num4;
230493
230493
  }
230494
- this.globalData.frameNum = num3;
230494
+ this.globalData.frameNum = num4;
230495
230495
  this.globalData.frameId += 1;
230496
- this.globalData.projectInterface.currentFrame = num3;
230496
+ this.globalData.projectInterface.currentFrame = num4;
230497
230497
  this.globalData._mdf = false;
230498
230498
  var i2;
230499
230499
  var len = this.layers.length;
230500
230500
  if (!this.completeLayers) {
230501
- this.checkLayers(num3);
230501
+ this.checkLayers(num4);
230502
230502
  }
230503
230503
  for (i2 = len - 1; i2 >= 0; i2 -= 1) {
230504
230504
  if (this.completeLayers || this.elements[i2]) {
230505
- this.elements[i2].prepareFrame(num3 - this.layers[i2].st);
230505
+ this.elements[i2].prepareFrame(num4 - this.layers[i2].st);
230506
230506
  }
230507
230507
  }
230508
230508
  if (this.globalData._mdf) {
@@ -230555,10 +230555,10 @@ var require_lottie = __commonJS({
230555
230555
  }
230556
230556
  this.hide();
230557
230557
  };
230558
- ICompElement.prototype.prepareFrame = function(num3) {
230558
+ ICompElement.prototype.prepareFrame = function(num4) {
230559
230559
  this._mdf = false;
230560
- this.prepareRenderableFrame(num3);
230561
- this.prepareProperties(num3, this.isInRange);
230560
+ this.prepareRenderableFrame(num4);
230561
+ this.prepareProperties(num4, this.isInRange);
230562
230562
  if (!this.isInRange && !this.data.xt) {
230563
230563
  return;
230564
230564
  }
@@ -230569,7 +230569,7 @@ var require_lottie = __commonJS({
230569
230569
  }
230570
230570
  this.renderedFrame = timeRemapped;
230571
230571
  } else {
230572
- this.renderedFrame = num3 / this.data.sr;
230572
+ this.renderedFrame = num4 / this.data.sr;
230573
230573
  }
230574
230574
  var i2;
230575
230575
  var len = this.elements.length;
@@ -231990,23 +231990,23 @@ var require_lottie = __commonJS({
231990
231990
  this.animationItem.container = null;
231991
231991
  this.destroyed = true;
231992
231992
  };
231993
- CanvasRendererBase.prototype.renderFrame = function(num3, forceRender) {
231994
- if (this.renderedFrame === num3 && this.renderConfig.clearCanvas === true && !forceRender || this.destroyed || num3 === -1) {
231993
+ CanvasRendererBase.prototype.renderFrame = function(num4, forceRender) {
231994
+ if (this.renderedFrame === num4 && this.renderConfig.clearCanvas === true && !forceRender || this.destroyed || num4 === -1) {
231995
231995
  return;
231996
231996
  }
231997
- this.renderedFrame = num3;
231998
- this.globalData.frameNum = num3 - this.animationItem._isFirstFrame;
231997
+ this.renderedFrame = num4;
231998
+ this.globalData.frameNum = num4 - this.animationItem._isFirstFrame;
231999
231999
  this.globalData.frameId += 1;
232000
232000
  this.globalData._mdf = !this.renderConfig.clearCanvas || forceRender;
232001
- this.globalData.projectInterface.currentFrame = num3;
232001
+ this.globalData.projectInterface.currentFrame = num4;
232002
232002
  var i2;
232003
232003
  var len = this.layers.length;
232004
232004
  if (!this.completeLayers) {
232005
- this.checkLayers(num3);
232005
+ this.checkLayers(num4);
232006
232006
  }
232007
232007
  for (i2 = 0; i2 < len; i2 += 1) {
232008
232008
  if (this.completeLayers || this.elements[i2]) {
232009
- this.elements[i2].prepareFrame(num3 - this.layers[i2].st);
232009
+ this.elements[i2].prepareFrame(num4 - this.layers[i2].st);
232010
232010
  }
232011
232011
  }
232012
232012
  if (this.globalData._mdf) {
@@ -232459,13 +232459,13 @@ var require_lottie = __commonJS({
232459
232459
  var $bm_mul = mul;
232460
232460
  var $bm_div = div;
232461
232461
  var $bm_mod = mod;
232462
- function clamp3(num3, min2, max) {
232462
+ function clamp3(num4, min2, max) {
232463
232463
  if (min2 > max) {
232464
232464
  var mm = max;
232465
232465
  max = min2;
232466
232466
  min2 = mm;
232467
232467
  }
232468
- return Math2.min(Math2.max(num3, min2), max);
232468
+ return Math2.min(Math2.max(num4, min2), max);
232469
232469
  }
232470
232470
  function radiansToDegrees(val) {
232471
232471
  return val / degToRads;
@@ -233334,12 +233334,12 @@ var require_opentype = __commonJS({
233334
233334
  }
233335
233335
  }
233336
233336
  var offs = new Uint16Array(16);
233337
- function tinf_build_tree(t, lengths2, off, num3) {
233337
+ function tinf_build_tree(t, lengths2, off, num4) {
233338
233338
  var i2, sum;
233339
233339
  for (i2 = 0; i2 < 16; ++i2) {
233340
233340
  t.table[i2] = 0;
233341
233341
  }
233342
- for (i2 = 0; i2 < num3; ++i2) {
233342
+ for (i2 = 0; i2 < num4; ++i2) {
233343
233343
  t.table[lengths2[off + i2]]++;
233344
233344
  }
233345
233345
  t.table[0] = 0;
@@ -233347,7 +233347,7 @@ var require_opentype = __commonJS({
233347
233347
  offs[i2] = sum;
233348
233348
  sum += t.table[i2];
233349
233349
  }
233350
- for (i2 = 0; i2 < num3; ++i2) {
233350
+ for (i2 = 0; i2 < num4; ++i2) {
233351
233351
  if (lengths2[off + i2]) {
233352
233352
  t.trans[offs[lengths2[off + i2]]++] = i2;
233353
233353
  }
@@ -233362,17 +233362,17 @@ var require_opentype = __commonJS({
233362
233362
  d.tag >>>= 1;
233363
233363
  return bit;
233364
233364
  }
233365
- function tinf_read_bits(d, num3, base) {
233366
- if (!num3) {
233365
+ function tinf_read_bits(d, num4, base) {
233366
+ if (!num4) {
233367
233367
  return base;
233368
233368
  }
233369
233369
  while (d.bitcount < 24) {
233370
233370
  d.tag |= d.source[d.sourceIndex++] << d.bitcount;
233371
233371
  d.bitcount += 8;
233372
233372
  }
233373
- var val = d.tag & 65535 >>> 16 - num3;
233374
- d.tag >>>= num3;
233375
- d.bitcount -= num3;
233373
+ var val = d.tag & 65535 >>> 16 - num4;
233374
+ d.tag >>>= num4;
233375
+ d.bitcount -= num4;
233376
233376
  return val + base;
233377
233377
  }
233378
233378
  function tinf_decode_symbol(d, t) {
@@ -233395,7 +233395,7 @@ var require_opentype = __commonJS({
233395
233395
  }
233396
233396
  function tinf_decode_trees(d, lt, dt) {
233397
233397
  var hlit, hdist, hclen;
233398
- var i2, num3, length;
233398
+ var i2, num4, length;
233399
233399
  hlit = tinf_read_bits(d, 5, 257);
233400
233400
  hdist = tinf_read_bits(d, 5, 1);
233401
233401
  hclen = tinf_read_bits(d, 4, 4);
@@ -233407,27 +233407,27 @@ var require_opentype = __commonJS({
233407
233407
  lengths[clcidx[i2]] = clen;
233408
233408
  }
233409
233409
  tinf_build_tree(code_tree, lengths, 0, 19);
233410
- for (num3 = 0; num3 < hlit + hdist; ) {
233410
+ for (num4 = 0; num4 < hlit + hdist; ) {
233411
233411
  var sym = tinf_decode_symbol(d, code_tree);
233412
233412
  switch (sym) {
233413
233413
  case 16:
233414
- var prev = lengths[num3 - 1];
233414
+ var prev = lengths[num4 - 1];
233415
233415
  for (length = tinf_read_bits(d, 2, 3); length; --length) {
233416
- lengths[num3++] = prev;
233416
+ lengths[num4++] = prev;
233417
233417
  }
233418
233418
  break;
233419
233419
  case 17:
233420
233420
  for (length = tinf_read_bits(d, 3, 3); length; --length) {
233421
- lengths[num3++] = 0;
233421
+ lengths[num4++] = 0;
233422
233422
  }
233423
233423
  break;
233424
233424
  case 18:
233425
233425
  for (length = tinf_read_bits(d, 7, 11); length; --length) {
233426
- lengths[num3++] = 0;
233426
+ lengths[num4++] = 0;
233427
233427
  }
233428
233428
  break;
233429
233429
  default:
233430
- lengths[num3++] = sym;
233430
+ lengths[num4++] = sym;
233431
233431
  break;
233432
233432
  }
233433
233433
  }
@@ -262207,8 +262207,8 @@ var require_IFFParser = __commonJS({
262207
262207
  }
262208
262208
  }
262209
262209
  };
262210
- function isEven(num3) {
262211
- return num3 % 2;
262210
+ function isEven(num4) {
262211
+ return num4 % 2;
262212
262212
  }
262213
262213
  function stringOffset(string6) {
262214
262214
  return string6.length + 1 + (isEven(string6.length + 1) ? 1 : 0);
@@ -262685,8 +262685,8 @@ var require_LWOLoader = __commonJS({
262685
262685
  return texture;
262686
262686
  }
262687
262687
  // 0 = Reset, 1 = Repeat, 2 = Mirror, 3 = Edge
262688
- getWrappingType(num3) {
262689
- switch (num3) {
262688
+ getWrappingType(num4) {
262689
+ switch (num4) {
262690
262690
  case 0:
262691
262691
  console.warn('LWOLoader: "Reset" texture wrapping type is not supported in three.js');
262692
262692
  return THREE.ClampToEdgeWrapping;
@@ -262837,7 +262837,7 @@ var require_LWOLoader = __commonJS({
262837
262837
  geometry.setAttribute("uv", new THREE.Float32BufferAttribute(remappedUVs, 2));
262838
262838
  }
262839
262839
  parseMorphTargets(geometry, layer) {
262840
- let num3 = 0;
262840
+ let num4 = 0;
262841
262841
  for (const name in layer.morphTargets) {
262842
262842
  const remappedPoints = geometry.attributes.position.array.slice();
262843
262843
  if (!geometry.morphAttributes.position)
@@ -262856,9 +262856,9 @@ var require_LWOLoader = __commonJS({
262856
262856
  remappedPoints[i2 * 3 + 2] = morphPoints[j * 3 + 2];
262857
262857
  }
262858
262858
  });
262859
- geometry.morphAttributes.position[num3] = new THREE.Float32BufferAttribute(remappedPoints, 3);
262860
- geometry.morphAttributes.position[num3].name = name;
262861
- num3++;
262859
+ geometry.morphAttributes.position[num4] = new THREE.Float32BufferAttribute(remappedPoints, 3);
262860
+ geometry.morphAttributes.position[num4].name = name;
262861
+ num4++;
262862
262862
  }
262863
262863
  geometry.morphTargetsRelative = false;
262864
262864
  }
@@ -310866,8 +310866,8 @@ var require_hls = __commonJS({
310866
310866
  function toHex(x) {
310867
310867
  return ("0" + x.toString(16).toUpperCase()).slice(-2);
310868
310868
  }
310869
- function addLeadingZero(num3) {
310870
- return (num3 < 10 ? "0" : "") + num3;
310869
+ function addLeadingZero(num4) {
310870
+ return (num4 < 10 ? "0" : "") + num4;
310871
310871
  }
310872
310872
  function patchEncyptionData(initSegment, decryptdata) {
310873
310873
  if (!initSegment || !decryptdata) {
@@ -311680,13 +311680,13 @@ var require_hls = __commonJS({
311680
311680
  }
311681
311681
  function codecsSetSelectionPreferenceValue(codecSet) {
311682
311682
  var limitedHevcSupport = userAgentHevcSupportIsInaccurate();
311683
- return codecSet.split(",").reduce(function(num3, fourCC) {
311683
+ return codecSet.split(",").reduce(function(num4, fourCC) {
311684
311684
  var lowerPriority = limitedHevcSupport && isHEVC(fourCC);
311685
311685
  var preferenceValue = lowerPriority ? 9 : sampleEntryCodesISO.video[fourCC];
311686
311686
  if (preferenceValue) {
311687
- return (preferenceValue * 2 + num3) / (num3 ? 3 : 2);
311687
+ return (preferenceValue * 2 + num4) / (num4 ? 3 : 2);
311688
311688
  }
311689
- return (sampleEntryCodesISO.audio[fourCC] + num3) / (num3 ? 2 : 1);
311689
+ return (sampleEntryCodesISO.audio[fourCC] + num4) / (num4 ? 2 : 1);
311690
311690
  }, 0);
311691
311691
  }
311692
311692
  var CODEC_COMPATIBLE_NAMES = {};
@@ -404296,7 +404296,7 @@ var DefaultHydrator = class {
404296
404296
  hydrateReference(reference, node, name, context) {
404297
404297
  return this.linker.buildReference(node, name, context.cstNodes.get(reference.$refNode), reference.$refText);
404298
404298
  }
404299
- hydrateCstNode(cstNode, context, num3 = 0) {
404299
+ hydrateCstNode(cstNode, context, num4 = 0) {
404300
404300
  const cstNodeObj = context.cstNodes.get(cstNode);
404301
404301
  if (typeof cstNode.grammarSource === "number") {
404302
404302
  cstNodeObj.grammarSource = this.getGrammarElement(cstNode.grammarSource);
@@ -404304,7 +404304,7 @@ var DefaultHydrator = class {
404304
404304
  cstNodeObj.astNode = context.astNodes.get(cstNode.astNode);
404305
404305
  if (isCompositeCstNode(cstNodeObj)) {
404306
404306
  for (const child of cstNode.content) {
404307
- const hydrated = this.hydrateCstNode(child, context, num3++);
404307
+ const hydrated = this.hydrateCstNode(child, context, num4++);
404308
404308
  cstNodeObj.content.push(hydrated);
404309
404309
  }
404310
404310
  }
@@ -416937,6 +416937,41 @@ function moduleExports3(text) {
416937
416937
 
416938
416938
  // src/pipeline.ts
416939
416939
  init_resolve();
416940
+
416941
+ // ../editor/src/param/refsView.ts
416942
+ init_resolve();
416943
+ var isFormula2 = (v) => typeof v === "string" && v.trimStart().startsWith("=");
416944
+ var num = (v) => typeof v === "number" && Number.isFinite(v) ? v : null;
416945
+ function buildRefsView(config3) {
416946
+ const scope = scopeForConfig(config3);
416947
+ const c = config3;
416948
+ const variables = Object.entries(c?.variables ?? {}).map(([name, val]) => ({
416949
+ name,
416950
+ value: num(scope[name]),
416951
+ formula: isFormula2(val) ? val : void 0
416952
+ }));
416953
+ const points = Object.keys(c?.points ?? {}).map((name) => ({
416954
+ name,
416955
+ x: num(scope[`${name}.x`]),
416956
+ y: num(scope[`${name}.y`])
416957
+ }));
416958
+ const grids = /* @__PURE__ */ new Map();
416959
+ for (const [key, val] of Object.entries(scope)) {
416960
+ const m = key.match(/^(\w+)\.([xy])(.+)$/);
416961
+ if (!m) continue;
416962
+ const [, id, axis, line] = m;
416963
+ if (!grids.has(id)) grids.set(id, { xLines: [], yLines: [] });
416964
+ const g = grids.get(id);
416965
+ (axis === "x" ? g.xLines : g.yLines).push({ name: line, value: num(val) });
416966
+ }
416967
+ return {
416968
+ variables,
416969
+ points,
416970
+ grids: [...grids].map(([id, g]) => ({ id, ...g }))
416971
+ };
416972
+ }
416973
+
416974
+ // src/pipeline.ts
416940
416975
  init_houseConfig();
416941
416976
  init_promote();
416942
416977
 
@@ -418771,7 +418806,7 @@ function deriveOne(cfg, wallTopZ, framing, wallThickness) {
418771
418806
  init_config();
418772
418807
 
418773
418808
  // ../editor/src/estimate/wallArea.ts
418774
- var num = (v, d = 0) => typeof v === "number" && isFinite(v) ? v : d;
418809
+ var num2 = (v, d = 0) => typeof v === "number" && isFinite(v) ? v : d;
418775
418810
  function inAnyRoom(rects, px, py, eps = 1) {
418776
418811
  for (const r2 of rects) {
418777
418812
  if (px > r2.x + eps && px < r2.x + r2.w - eps && py > r2.y + eps && py < r2.y + r2.l - eps) return true;
@@ -418790,7 +418825,7 @@ function buildRoomRects(config3) {
418790
418825
  for (let fi = 0; fi < floors.length; fi++) {
418791
418826
  for (const o of floors[fi].objects ?? []) {
418792
418827
  if (o.type !== "room" || o.enabled === false) continue;
418793
- out.push({ x: num(o.x), y: num(o.y), w: num(o.width), l: num(o.length), floor: fi });
418828
+ out.push({ x: num2(o.x), y: num2(o.y), w: num2(o.width), l: num2(o.length), floor: fi });
418794
418829
  }
418795
418830
  }
418796
418831
  return out;
@@ -418814,7 +418849,7 @@ function roomSideOpenToWeather(rects, rx, ry, rw, rl, side2, wallT) {
418814
418849
  // ../editor/src/lint/structural.ts
418815
418850
  init_expand();
418816
418851
  var ALL_SIDES = ["north", "south", "east", "west"];
418817
- function num2(v) {
418852
+ function num3(v) {
418818
418853
  return typeof v === "number" ? v : Number(v) || 0;
418819
418854
  }
418820
418855
  function cap(s) {
@@ -418822,7 +418857,7 @@ function cap(s) {
418822
418857
  }
418823
418858
  function floorLabel(fl) {
418824
418859
  const nm = typeof fl.name === "string" && fl.name ? ` "${fl.name}"` : "";
418825
- return `floor ${num2(fl.floor_number)}${nm}`;
418860
+ return `floor ${num3(fl.floor_number)}${nm}`;
418826
418861
  }
418827
418862
  function objLabel(o) {
418828
418863
  if (typeof o.name === "string" && o.name) return `"${o.name}"`;
@@ -418851,10 +418886,10 @@ function collectWallSegments(floors) {
418851
418886
  if (o.type === "room") {
418852
418887
  const declared = declaredSides(o.walls);
418853
418888
  const sides = declared === "all" ? ALL_SIDES : [...declared];
418854
- const x = num2(o.x), y = num2(o.y), w = num2(o.width), l = num2(o.length);
418889
+ const x = num3(o.x), y = num3(o.y), w = num3(o.width), l = num3(o.length);
418855
418890
  for (const s of sides) segs.push(sideSegment(x, y, w, l, s));
418856
418891
  } else if (o.type === "wall") {
418857
- const sx = num2(o.start_x), sy = num2(o.start_y), ex = num2(o.end_x), ey = num2(o.end_y);
418892
+ const sx = num3(o.start_x), sy = num3(o.start_y), ex = num3(o.end_x), ey = num3(o.end_y);
418858
418893
  if (Math.abs(sy - ey) < 1) segs.push({ horiz: true, at: sy, lo: Math.min(sx, ex), hi: Math.max(sx, ex) });
418859
418894
  else if (Math.abs(sx - ex) < 1) segs.push({ horiz: false, at: sx, lo: Math.min(sy, ey), hi: Math.max(sy, ey) });
418860
418895
  }
@@ -418881,12 +418916,12 @@ function collectOpeningSegs(objs) {
418881
418916
  if (o.type === "room") {
418882
418917
  const walls = o.walls;
418883
418918
  if (!walls || typeof walls !== "object" || Array.isArray(walls)) continue;
418884
- const rx = num2(o.x), ry = num2(o.y), rw = num2(o.width), rl = num2(o.length);
418919
+ const rx = num3(o.x), ry = num3(o.y), rw = num3(o.width), rl = num3(o.length);
418885
418920
  for (const side2 of ALL_SIDES) {
418886
418921
  const ops = walls[side2]?.openings;
418887
418922
  if (!Array.isArray(ops)) continue;
418888
418923
  for (const op of ops) {
418889
- const off = num2(op.offset), w = num2(op.width);
418924
+ const off = num3(op.offset), w = num3(op.width);
418890
418925
  const owner = `room ${objLabel(o)} ${side2}`;
418891
418926
  const label = openLabel(op);
418892
418927
  if (side2 === "north") out.push({ horiz: true, at: ry, lo: rx + off, hi: rx + off + w, owner, label });
@@ -418898,12 +418933,12 @@ function collectOpeningSegs(objs) {
418898
418933
  } else if (o.type === "wall") {
418899
418934
  const ops = o.openings;
418900
418935
  if (!Array.isArray(ops)) continue;
418901
- const sx = num2(o.start_x), sy = num2(o.start_y), ex = num2(o.end_x), ey = num2(o.end_y);
418936
+ const sx = num3(o.start_x), sy = num3(o.start_y), ex = num3(o.end_x), ey = num3(o.end_y);
418902
418937
  const horiz = Math.abs(sy - ey) < 1, vert = Math.abs(sx - ex) < 1;
418903
418938
  if (!horiz && !vert) continue;
418904
418939
  const start = horiz ? Math.min(sx, ex) : Math.min(sy, ey);
418905
418940
  for (const op of ops) {
418906
- const off = num2(op.offset), w = num2(op.width);
418941
+ const off = num3(op.offset), w = num3(op.width);
418907
418942
  out.push({ horiz, at: horiz ? sy : sx, lo: start + off, hi: start + off + w, owner: `wall ${objLabel(o)}`, label: openLabel(op) });
418908
418943
  }
418909
418944
  }
@@ -418926,24 +418961,24 @@ function itemMetersToUnits(m, units) {
418926
418961
  function itemBox(it, units) {
418927
418962
  const asset2 = it.asset;
418928
418963
  if (!asset2?.dimensions) return null;
418929
- const scale = it.scale != null ? num2(it.scale) : 1;
418964
+ const scale = it.scale != null ? num3(it.scale) : 1;
418930
418965
  const fw = itemMetersToUnits(asset2.dimensions[0], units) * scale;
418931
418966
  const fd = itemMetersToUnits(asset2.dimensions[2], units) * scale;
418932
- const th = num2(it.rotation) * Math.PI / 180;
418967
+ const th = num3(it.rotation) * Math.PI / 180;
418933
418968
  const c = Math.abs(Math.cos(th)), s = Math.abs(Math.sin(th));
418934
418969
  const hx = fw / 2 * c + fd / 2 * s;
418935
418970
  const hy = fw / 2 * s + fd / 2 * c;
418936
- const cx = num2(it.x), cy = num2(it.y);
418971
+ const cx = num3(it.x), cy = num3(it.y);
418937
418972
  return { x0: cx - hx, y0: cy - hy, x1: cx + hx, y1: cy + hy };
418938
418973
  }
418939
418974
  function lintStructure(config3) {
418940
418975
  const cfg = config3;
418941
418976
  const floors = cfg.floors ?? [];
418942
418977
  const defaults = cfg.defaults ?? {};
418943
- const wallTDefault = num2(defaults.wall_thickness ?? DEFAULT_GLOBAL_CONFIG.wall_thickness);
418944
- const slabDefault = defaults.slab_thickness != null ? num2(defaults.slab_thickness) : DEFAULT_GLOBAL_CONFIG.floor_slab_thickness;
418945
- const floorHeightDefault = defaults.floor_height != null ? num2(defaults.floor_height) : DEFAULT_GLOBAL_CONFIG.floor_height;
418946
- const wallHeightDefault = defaults.wall_height != null ? num2(defaults.wall_height) : DEFAULT_GLOBAL_CONFIG.wall_height;
418978
+ const wallTDefault = num3(defaults.wall_thickness ?? DEFAULT_GLOBAL_CONFIG.wall_thickness);
418979
+ const slabDefault = defaults.slab_thickness != null ? num3(defaults.slab_thickness) : DEFAULT_GLOBAL_CONFIG.floor_slab_thickness;
418980
+ const floorHeightDefault = defaults.floor_height != null ? num3(defaults.floor_height) : DEFAULT_GLOBAL_CONFIG.floor_height;
418981
+ const wallHeightDefault = defaults.wall_height != null ? num3(defaults.wall_height) : DEFAULT_GLOBAL_CONFIG.wall_height;
418947
418982
  const findings = [];
418948
418983
  const rects = buildRoomRects(config3);
418949
418984
  const wallSegs = collectWallSegments(floors);
@@ -418958,9 +418993,9 @@ function lintStructure(config3) {
418958
418993
  for (let fi = 0; fi < floors.length; fi++) {
418959
418994
  const fl = floors[fi];
418960
418995
  const objs = activeObjects2(fl);
418961
- const fnum = num2(fl.floor_number);
418996
+ const fnum = num3(fl.floor_number);
418962
418997
  const floorBaseZ = baseZ;
418963
- baseZ += fl.height != null ? num2(fl.height) : floorHeightDefault;
418998
+ baseZ += fl.height != null ? num3(fl.height) : floorHeightDefault;
418964
418999
  const plinths = objs.filter((o) => o.type === "plinth");
418965
419000
  if (plinths.length) {
418966
419001
  const fh = fl.height;
@@ -418975,14 +419010,14 @@ function lintStructure(config3) {
418975
419010
  for (const p of plinths) {
418976
419011
  const ph = p.height;
418977
419012
  if (ph == null) continue;
418978
- if (fh != null && Math.abs(num2(fh) - num2(ph)) > 1e-3) {
418979
- const diff = num2(fh) - num2(ph);
419013
+ if (fh != null && Math.abs(num3(fh) - num3(ph)) > 1e-3) {
419014
+ const diff = num3(fh) - num3(ph);
418980
419015
  findings.push({
418981
419016
  rule: "C1",
418982
419017
  level: "error",
418983
419018
  floor: fnum,
418984
419019
  where: objLabel(p),
418985
- message: `${cap(floorLabel(fl))}: floor \`height\` (${num2(fh)}) \u2260 plinth ${objLabel(p)} height (${num2(ph)}). The floor above ${diff > 0 ? "floats" : "sinks into the plinth by"} ${Math.abs(diff)} units. Make the floor height and the plinth height equal.`
419020
+ message: `${cap(floorLabel(fl))}: floor \`height\` (${num3(fh)}) \u2260 plinth ${objLabel(p)} height (${num3(ph)}). The floor above ${diff > 0 ? "floats" : "sinks into the plinth by"} ${Math.abs(diff)} units. Make the floor height and the plinth height equal.`
418986
419021
  });
418987
419022
  }
418988
419023
  }
@@ -418991,7 +419026,7 @@ function lintStructure(config3) {
418991
419026
  const deckObjs = objs.filter((o) => o.type === "room" || o.type === "wall");
418992
419027
  if (!hasSlab && deckObjs.length) {
418993
419028
  const explicit = fl.slab_thickness;
418994
- const eff = explicit != null ? num2(explicit) : slabDefault;
419029
+ const eff = explicit != null ? num3(explicit) : slabDefault;
418995
419030
  if (eff !== 0) {
418996
419031
  findings.push({
418997
419032
  rule: "C3",
@@ -419003,9 +419038,9 @@ function lintStructure(config3) {
419003
419038
  }
419004
419039
  const hasFloorAbove = fi < floors.length - 1;
419005
419040
  if (deckObjs.length && hasFloorAbove && !plinths.length) {
419006
- const h = fl.height != null ? num2(fl.height) : floorHeightDefault;
419007
- const wh = fl.wall_height != null ? num2(fl.wall_height) : wallHeightDefault;
419008
- const st = fl.slab_thickness != null ? num2(fl.slab_thickness) : slabDefault;
419041
+ const h = fl.height != null ? num3(fl.height) : floorHeightDefault;
419042
+ const wh = fl.wall_height != null ? num3(fl.wall_height) : wallHeightDefault;
419043
+ const st = fl.slab_thickness != null ? num3(fl.slab_thickness) : slabDefault;
419009
419044
  const gap = h - (wh + st);
419010
419045
  if (Math.abs(gap) > 1e-3) {
419011
419046
  findings.push({
@@ -419018,13 +419053,13 @@ function lintStructure(config3) {
419018
419053
  }
419019
419054
  for (const o of objs) {
419020
419055
  if (o.type !== "staircase") continue;
419021
- const riser = num2(o.step_rise);
419056
+ const riser = num3(o.step_rise);
419022
419057
  if (riser <= 0) continue;
419023
- const belowH = fi > 0 && floors[fi - 1].height != null ? num2(floors[fi - 1].height) : floorHeightDefault;
419024
- const riseHeight = o.rise_height != null && num2(o.rise_height) > 0 ? num2(o.rise_height) : belowH;
419058
+ const belowH = fi > 0 && floors[fi - 1].height != null ? num3(floors[fi - 1].height) : floorHeightDefault;
419059
+ const riseHeight = o.rise_height != null && num3(o.rise_height) > 0 ? num3(o.rise_height) : belowH;
419025
419060
  const totalRise = Math.max(1, Math.round(riseHeight / riser)) * riser;
419026
- const slabT = fl.slab_thickness != null ? num2(fl.slab_thickness) : slabDefault;
419027
- const topZ = o.z_offset != null ? num2(o.z_offset) : slabT;
419061
+ const slabT = fl.slab_thickness != null ? num3(fl.slab_thickness) : slabDefault;
419062
+ const topZ = o.z_offset != null ? num3(o.z_offset) : slabT;
419028
419063
  const bottomZ = floorBaseZ + (topZ - totalRise);
419029
419064
  if (bottomZ < -1) {
419030
419065
  findings.push({
@@ -419042,8 +419077,8 @@ function lintStructure(config3) {
419042
419077
  if (declared === "all") continue;
419043
419078
  const missing = ALL_SIDES.filter((s) => !declared.has(s));
419044
419079
  if (!missing.length) continue;
419045
- const wallT = num2(o.wall_thickness ?? o.thickness ?? wallTDefault);
419046
- const rx = num2(o.x), ry = num2(o.y), rw = num2(o.width), rl = num2(o.length);
419080
+ const wallT = num3(o.wall_thickness ?? o.thickness ?? wallTDefault);
419081
+ const rx = num3(o.x), ry = num3(o.y), rw = num3(o.width), rl = num3(o.length);
419047
419082
  for (const side2 of missing) {
419048
419083
  if (sideHasWall(wallSegs, sideSegment(rx, ry, rw, rl, side2))) continue;
419049
419084
  if (roomSideOpenToWeather(rects, rx, ry, rw, rl, side2, wallT)) {
@@ -422689,7 +422724,7 @@ house TwoRoom {
422689
422724
  var DOCS = {
422690
422725
  "guide": {
422691
422726
  "title": "How to author a Wadi house (the workflow)",
422692
- "body": '# Wadi architect \u2014 author `.wdl` house designs with a live 3D preview\n\nYou are the architect/editor. The user describes a house (in words, or with a\nsketch/photo); you write or update its **`.wdl`** file (the Wadi DSL), and the **Wadi\napp\'s DSL editor renders the model live** from that same file. The user watches and\nsteers with follow-up messages \u2014 and can edit the `.wdl` alongside you.\n\nYou author in the **Wadi DSL** (`.wdl`), not raw JSON. It\'s more direct (formulas,\ngrids, and the configurator are one-liners), the grammar enforces structure, and the\ncompiler reports parse errors with `line:col`. The DSL is **complete** \u2014 every object\ntype has first-class syntax \u2014 so it can express any house the model can hold.\n**`reference/dsl.md` is your syntax reference; read it first.**\n\n**The `.wdl` IS the design \u2014 do not produce a `.wadi`.** The Wadi app\'s DSL previewer\ncompiles + renders the `.wdl` live (in-app), so a single `.wdl` file on disk is the\n**shared source you and the human co-edit**. You never convert it to JSON; you just\nauthor the `.wdl` and verify it with `check.sh`.\n\nThis skill is **agent-neutral** \u2014 it is plain instructions + reference docs + scripts,\nusable by any coding agent (Claude Code, Google Antigravity, \u2026). Agent-specific\nentrypoints live outside this folder (e.g. `.claude/skills/wadi-architect/` for Claude\nCode, `AGENTS.md` at the repo root); they just point here.\n\n## What you need to run this\n\n- **Read/write files** and **run shell + Node** (the only capabilities assumed).\n- The **Wadi repo checked out**, with deps installed once:\n `npm --prefix editor install` and `npm --prefix wadi-dsl install` (the latter also\n regenerates the DSL parser via its `prepare` hook). The `check` and `preview` scripts\n reuse the app\'s own TypeScript, so they flag exactly what the app would.\n- For the *live* render: the **Wadi app\'s DSL editor** open on the `.wdl` you edit \u2014\n desktop **\u2318\u21E7D \u2192 Open**, or the browser playground at `wadi.house/dsl`. It compiles +\n renders the `.wdl` live. Without it you can still author + `check` + render preview\n images yourself.\n\n## The live loop (read this first, every session)\n\nThe **`.wdl` file is the single shared source.** You edit it; the Wadi app\'s DSL editor\n\u2014 open on that same file \u2014 compiles and re-renders it live; the human can edit it too (in\nthe DSL editor, in their own editor, or by asking you). **You never produce or touch a\n`.wadi`** \u2014 the app does the `.wdl` \u2192 render conversion.\n\n**Starting a NEW model:**\n1. Write a starter `house.wdl` (copy the shape from `../../wadi-dsl/examples/` \u2014 start\n from `minimal.wdl`, or `coastal.wdl` for a full cottage) at a path the user wants,\n e.g. `~/Documents/<name>.wdl`.\n2. Tell the user to open it in the **Wadi DSL editor** (**\u2318\u21E7D \u2192 Open** in the desktop\n app) so it renders live and watches the file.\n3. Build the house by editing `house.wdl`. Run `check.sh` after each change to confirm it\n still compiles + validates; the editor re-renders as the file changes.\n\n**Editing an EXISTING model:** edit its `.wdl`. Because it\'s a **co-edited** file, **read\nit first** (the human may have changed it since your last edit) and make a **minimal\npatch** \u2014 change only the lines you need and preserve the rest verbatim, so your edit\nmerges cleanly with theirs.\n\n**Always:**\n- **Tell the user which `.wdl` you are editing**, and edit only that path.\n- **Run `check.sh` after every edit.** It runs the DSL compiler + validator against a\n **throwaway temp** (it does NOT create a `.wadi`) purely for feedback \u2014 fix any\n reported error before telling the user anything changed. A broken `.wdl` just makes the\n editor show a diagnostic and keep the last good render; it never corrupts anything.\n\n## Two ways users work with you\n\n- **Brief \u2192 house.** They describe requirements ("3-bed L-shaped bungalow, ~1500 sq\n ft, hip roof"). You *derive* the base config. Keep a running **design brief** as the\n source of truth so later changes re-derive cleanly \u2014 see `prompts/design-brief.md`.\n This is Wadi\'s "higher-level language": the brief is the intent, the config is the\n derived artifact, you are the compiler.\n- **Drawings \u2192 house (recreate an existing design).** **Do NOT try to read dimensions\n off an architect\'s drawing yourself \u2014 it\'s unreliable and produces the wrong house.**\n Instead run a **guided dialog**: the USER reads their drawing and describes each\n piece in plain terms (names, sizes in feet, where things sit), and YOU do all the\n coordinate / scale / convention work, building **one room at a time** and rendering\n after each so they confirm. Ask **spatially** ("how big? which corner? next to\n what?"), never for coordinates. Never build the whole plan from one answer. Full\n method in `prompts/sketch-to-config.md`.\n- **Iterate.** They react to the live model ("make Bedroom 2 bigger", "add a north\n window"). Apply a **minimal patch** \u2014 see `prompts/update-existing.md`.\n\n## The data model & references (read lazily \u2014 don\'t dump them all up front)\n\n- **`reference/dsl.md`** \u2014 **your primary reference: the complete `.wdl` SYNTAX** \u2014\n the skeleton, the parametric core (var/point/grid/configurator/formulas), the common\n attribute tail (`enabled`/`z_offset`/`layer`/`material`), and the first-class syntax\n for every object type (rooms/walls/openings, slab/beam/plinth/ground/pillar,\n staircase/kitchen/item, roof, components, layers) plus the `raw` escape. Read this\n first, then the semantics references below.\n- **`reference/data-model.md`** \u2014 the underlying **`.wadi` schema** the DSL compiles to:\n every object type, field, type, requiredness, units, semantics. **Generated from the\n Zod schema** (`editor/src/schema/houseConfig.ts`) so it can\'t drift \u2014 regenerate with\n `scripts/gen-schema-doc.mjs` after any schema change. It\'s the field reference for the\n `raw` escape and for understanding exactly what your `.wdl` produces.\n- **`reference/coordinate-system.md`** \u2014 the #1 source of mistakes. X\u2192right, **Y\u2192DOWN**\n (Inkscape frame, not Y-up), Z\u2192up. Units: **10 project units = 1 ft** by default. Read\n before placing anything.\n- **`reference/roof-v2-guide.md`** \u2014 the unified `roof` object (segments, hip vs gable,\n joints, trusses, shed). Its fields are freeform in the schema (validated at\n derivation), so this guide is the field reference for roofs. Read before touching a\n roof.\n- **`reference/conventions.md`** \u2014 the **structural coding conventions** every house\n must follow (plinth-floor height must match the plinth block, rooms must wall every\n exterior side, a floor with no slab must set `slab_thickness 0`, \u2026). These are\n *enforced* by `check.sh` \u2014 errors fail, warnings are advisory \u2014 and shown in the DSL\n editor\'s status. Read it so you author sound houses the first time.\n- **`reference/parametric-conventions.md`** \u2014 how to build a **fully-parametric** model\n (a reusable template that stays valid under any knob change): the **grid-first**\n recipe \u2014 variables (knobs) \u2192 named grid lines (formulas of the house size) \u2192 rooms &\n columns that reference those lines \u2014 plus `coord_convention:center`, proportional grid\n lines with minimums, the `pilInset` flush-column rule, opening placement, and the\n scale-sweep verification. Read before authoring or editing a parametric template.\n- **`../../wadi-dsl/examples/*.wdl`** \u2014 correct, validated `.wdl` houses to copy shapes\n from (every one passes the compile \u2192 resolve \u2192 schema+geometry pipeline in the DSL\'s\n test suite, so **copy from these, not from memory**): `minimal.wdl` (smallest house),\n `two_room.wdl` (no grid), `two_story.wdl` (multi-floor + hip roof), `coastal.wdl`\n (grid + configurator + full cottage), `complete.wdl` (every entity \u2014 beam, wall,\n kitchen, item, component, gable roof, layers). `examples/coastal_konkan.wadi` in this\n folder is a resolved `.wadi` for reference on the compiled output shape.\n\n## Check after every edit (get compiler feedback \u2014 no `.wadi` produced)\n\n`check.sh` runs the DSL end-to-end for **feedback only**: it parses the `.wdl` (reporting\nparse errors with `line:col`), resolves formulas/grids into numbers, and runs the real\nschema + roof/wall geometry validator (catches zero-length roof segments, missing slope,\nbad openings that the schema alone misses), and finally the **structural conventions**\nlinter (`reference/conventions.md`: floating floors, open exterior walls, no-slab floors)\n\u2014 all against a **throwaway temp** that is deleted. It never writes a `.wadi`; the app\'s\nDSL previewer does the real conversion.\n\nConvention **errors** (`\u2716 [C\u2026]`) fail the check \u2014 fix them. Convention **warnings**\n(`\u26A0 [C\u2026]`) are printed but don\'t fail; fix them, or keep them if the open side is a\ndeliberate verandah.\n\n```bash\nwadi-skill/architect/scripts/check.sh "<ABS>.wdl"\n```\n\nExit 0 = compiles + valid. Non-zero prints the exact `parse: \u2026` or `/path: message`\nerror \u2014 fix the `.wdl` and re-run before telling the user anything is ready.\n\n**If you have the `wadi_*` MCP tools** (from the `wadi-mcp` server) instead of a repo\ncheckout, use them \u2014 they\'re the same pipeline, no clone needed: `wadi_check(wdl)` for\nthis step, `wadi_preview(wdl)` for the images below, and `wadi_examples` / `wadi_reference`\nfor the examples and these docs.\n\n## See your work (don\'t author blind)\n\nYou can\'t see the app\'s live render, but you CAN render your `.wdl` to images and **read\nthem** to check your own edit \u2014 layout, sizes, openings, and roof are exactly where\nmistakes happen. `preview.sh` takes the `.wdl` directly (it compiles to a throwaway temp\njust for rendering \u2014 again, no persistent `.wadi`):\n\n```bash\nwadi-skill/architect/scripts/preview.sh "<ABS>.wdl"\n```\n\nIt writes (and prints paths to) `plans.png` (floor plans \u2014 room layout + sizes),\n`elevations.png` (front/back/left/right \u2014 heights + **roof profile**, the 2D view of the\nbuilt 3D model), and `roof.png` (roof top view), plus all SVGs under `.../2d/`. **Read\nthe PNGs** after a non-trivial edit and confirm what you built matches the request (rooms\nin the right place, not overlapping, correct sizes in feet, roof over the plinth). **If\nit doesn\'t match, fix the `.wdl` and re-render \u2014 repeat until it looks right, and only\nthen tell the user it\'s ready.** It reuses the app\'s own generators, so it matches the\napp\'s 2D tabs byte-for-byte. See `prompts/verify-visually.md`.\n\n## Top pitfalls (memorize)\n\n- **Y is DOWN.** A room "to the north" has a *smaller* Y. Never treat Y as up.\n- **Dimensions are project units, not feet.** `width: 120` is 12 ft. Multiply feet by\n 10 (the default; `units.per_unit` can change it).\n- **Set `"coord_convention": "center"`.** Then a room\'s `x,y,width,length` are wall\n **CENTRELINES**, and **two rooms that share a wall just ABUT on the shared line** \u2014 room\n A spans X `[0,150]`, room B `[150,300]`; the wall is centred on `150` and shared. No\n overlap, no `wall_thickness` math (the system grows to the outer face at render). On a\n grid, a room is simply `x:"= main.x1", width:"= main.x2 - main.x1"`. (Legacy files\n without the flag use the OUTER convention where adjacent rooms must overlap by\n `wall_thickness`.) See `coordinate-system.md` \u2192 "the centreline convention".\n- **The roof footprint must cover the plinth footprint.** Roof segment widths and\n positions come from the walls they sit on, not arbitrary numbers.\n- **The roof lives on its OWN top floor** (`floor_number` ABOVE the floors it covers,\n containing only the roof), and you **never set its Z** \u2014 the base height is computed\n from the plinth height + the `height`s of the floors below it. To move the roof\n up/down, change floor heights, not the roof. (See `reference/roof-v2-guide.md`.)\n- **Floors stack; `floor_number` is 0-based** (the plinth floor = 0). Heights are\n independent per floor (`height`, `wall_height`, `slab_thickness` are unrelated\n fields) \u2014 *except* where the **structural conventions** require a match: the\n plinth-floor `height` must equal the plinth block height, and a floor with no slab\n must set `slab_thickness 0`, or the floors above float. See `reference/conventions.md`.\n- **A partial `wall` list makes a room a whitelist.** A bare room (no `wall` lines) is\n enclosed on all four sides; add one `wall` line and every side you don\'t list becomes a\n hole. Wall every **exterior** side (convention C2). List plain sides compactly:\n `wall east west`, then the opening sides separately.\n- **Minimal patches.** Preserve unchanged `.wdl` lines verbatim so the user (and diffs)\n can see exactly what moved.\n- **Formulas are bare expressions in the DSL** \u2014 write `at (main.x1, main.yA)`, not\n `"= main.x1"`; the compiler emits the `= \u2026` form. Quote only `name "\u2026"` strings;\n `room`/`pillar`/`var`/grid-line names are bare identifiers.\n- **Errors are caught by `check.sh`.** A parse error (bad syntax) or a `.strict()` schema\n error (unknown/misspelled key inside a `raw` block) fails `check.sh` with the exact\n location \u2014 and the app\'s DSL editor shows the same squiggle live; nothing reaches the\n model silently.\n'
422727
+ "body": '# Wadi architect \u2014 author `.wdl` house designs with a live 3D preview\n\nYou are the architect/editor. The user describes a house (in words, or with a\nsketch/photo); you write or update its **`.wdl`** file (the Wadi DSL), and the **Wadi\napp\'s DSL editor renders the model live** from that same file. The user watches and\nsteers with follow-up messages \u2014 and can edit the `.wdl` alongside you.\n\nYou author in the **Wadi DSL** (`.wdl`), not raw JSON. It\'s more direct (formulas,\ngrids, and the configurator are one-liners), the grammar enforces structure, and the\ncompiler reports parse errors with `line:col`. The DSL is **complete** \u2014 every object\ntype has first-class syntax \u2014 so it can express any house the model can hold.\n**`reference/dsl.md` is your syntax reference; read it first.**\n\n**The `.wdl` IS the design \u2014 do not produce a `.wadi`.** The Wadi app\'s DSL previewer\ncompiles + renders the `.wdl` live (in-app), so a single `.wdl` file on disk is the\n**shared source you and the human co-edit**. You never convert it to JSON; you just\nauthor the `.wdl` and verify it with `check.sh`.\n\nThis skill is **agent-neutral** \u2014 it is plain instructions + reference docs + scripts,\nusable by any coding agent (Claude Code, Google Antigravity, \u2026). Agent-specific\nentrypoints live outside this folder (e.g. `.claude/skills/wadi-architect/` for Claude\nCode, `AGENTS.md` at the repo root); they just point here.\n\n## What you need to run this\n\n- **Read/write files** and **run shell + Node** (the only capabilities assumed).\n- The **Wadi repo checked out**, with deps installed once:\n `npm --prefix editor install` and `npm --prefix wadi-dsl install` (the latter also\n regenerates the DSL parser via its `prepare` hook). The `check` and `preview` scripts\n reuse the app\'s own TypeScript, so they flag exactly what the app would.\n- For the *live* render: the **Wadi app\'s DSL editor** open on the `.wdl` you edit \u2014\n desktop **\u2318\u21E7D \u2192 Open**, or the browser playground at `wadi.house/dsl`. It compiles +\n renders the `.wdl` live. Without it you can still author + `check` + render preview\n images yourself.\n\n## The live loop (read this first, every session)\n\nThe **`.wdl` file is the single shared source.** You edit it; the Wadi app\'s DSL editor\n\u2014 open on that same file \u2014 compiles and re-renders it live; the human can edit it too (in\nthe DSL editor, in their own editor, or by asking you). **You never produce or touch a\n`.wadi`** \u2014 the app does the `.wdl` \u2192 render conversion.\n\n**Starting a NEW model:**\n1. Write a starter `house.wdl` (copy the shape from `../../wadi-dsl/examples/` \u2014 start\n from `minimal.wdl`, or `coastal.wdl` for a full cottage) at a path the user wants,\n e.g. `~/Documents/<name>.wdl`.\n2. Tell the user to open it in the **Wadi DSL editor** (**\u2318\u21E7D \u2192 Open** in the desktop\n app) so it renders live and watches the file.\n3. Build the house by editing `house.wdl`. Run `check.sh` after each change to confirm it\n still compiles + validates; the editor re-renders as the file changes.\n\n**Editing an EXISTING model:** edit its `.wdl`. Because it\'s a **co-edited** file, **read\nit first** (the human may have changed it since your last edit) and make a **minimal\npatch** \u2014 change only the lines you need and preserve the rest verbatim, so your edit\nmerges cleanly with theirs.\n\n**Always:**\n- **Tell the user which `.wdl` you are editing**, and edit only that path.\n- **Run `check.sh` after every edit.** It runs the DSL compiler + validator against a\n **throwaway temp** (it does NOT create a `.wadi`) purely for feedback \u2014 fix any\n reported error before telling the user anything changed. A broken `.wdl` just makes the\n editor show a diagnostic and keep the last good render; it never corrupts anything.\n\n## Two ways users work with you\n\n- **Brief \u2192 house.** They describe requirements ("3-bed L-shaped bungalow, ~1500 sq\n ft, hip roof"). You *derive* the base config. Keep a running **design brief** as the\n source of truth so later changes re-derive cleanly \u2014 see `prompts/design-brief.md`.\n This is Wadi\'s "higher-level language": the brief is the intent, the config is the\n derived artifact, you are the compiler.\n- **Drawings \u2192 house (recreate an existing design).** **Do NOT try to read dimensions\n off an architect\'s drawing yourself \u2014 it\'s unreliable and produces the wrong house.**\n Instead run a **guided dialog**: the USER reads their drawing and describes each\n piece in plain terms (names, sizes in feet, where things sit), and YOU do all the\n coordinate / scale / convention work, building **one room at a time** and rendering\n after each so they confirm. Ask **spatially** ("how big? which corner? next to\n what?"), never for coordinates. Never build the whole plan from one answer. Full\n method in `prompts/sketch-to-config.md`.\n- **Iterate.** They react to the live model ("make Bedroom 2 bigger", "add a north\n window"). Apply a **minimal patch** \u2014 see `prompts/update-existing.md`.\n\n## The data model & references (read lazily \u2014 don\'t dump them all up front)\n\n- **`reference/dsl.md`** \u2014 **your primary reference: the complete `.wdl` SYNTAX** \u2014\n the skeleton, the parametric core (var/point/grid/configurator/formulas), the common\n attribute tail (`enabled`/`z_offset`/`layer`/`material`), and the first-class syntax\n for every object type (rooms/walls/openings, slab/beam/plinth/ground/pillar,\n staircase/kitchen/item, roof, components, layers) plus the `raw` escape. Read this\n first, then the semantics references below.\n- **`reference/data-model.md`** \u2014 the underlying **`.wadi` schema** the DSL compiles to:\n every object type, field, type, requiredness, units, semantics. **Generated from the\n Zod schema** (`editor/src/schema/houseConfig.ts`) so it can\'t drift \u2014 regenerate with\n `scripts/gen-schema-doc.mjs` after any schema change. It\'s the field reference for the\n `raw` escape and for understanding exactly what your `.wdl` produces.\n- **`reference/coordinate-system.md`** \u2014 the #1 source of mistakes. X\u2192right, **Y\u2192DOWN**\n (Inkscape frame, not Y-up), Z\u2192up. Units: **10 project units = 1 ft** by default. Read\n before placing anything.\n- **`reference/roof-v2-guide.md`** \u2014 the unified `roof` object (segments, hip vs gable,\n joints, trusses, shed). Its fields are freeform in the schema (validated at\n derivation), so this guide is the field reference for roofs. Read before touching a\n roof.\n- **`reference/conventions.md`** \u2014 the **structural coding conventions** every house\n must follow (plinth-floor height must match the plinth block, rooms must wall every\n exterior side, a floor with no slab must set `slab_thickness 0`, \u2026). These are\n *enforced* by `check.sh` \u2014 errors fail, warnings are advisory \u2014 and shown in the DSL\n editor\'s status. Read it so you author sound houses the first time.\n- **`reference/parametric-conventions.md`** \u2014 how to build a **fully-parametric** model\n (a reusable template that stays valid under any knob change): the **grid-first**\n recipe \u2014 variables (knobs) \u2192 named grid lines (formulas of the house size) \u2192 rooms &\n columns that reference those lines \u2014 plus `coord_convention:center`, proportional grid\n lines with minimums, the `pilInset` flush-column rule, opening placement, and the\n scale-sweep verification. Read before authoring or editing a parametric template.\n- **`../../wadi-dsl/examples/*.wdl`** \u2014 correct, validated `.wdl` houses to copy shapes\n from (every one passes the compile \u2192 resolve \u2192 schema+geometry pipeline in the DSL\'s\n test suite, so **copy from these, not from memory**): `minimal.wdl` (smallest house),\n `two_room.wdl` (no grid), `two_story.wdl` (multi-floor + hip roof), `coastal.wdl`\n (grid + configurator + full cottage), `complete.wdl` (every entity \u2014 beam, wall,\n kitchen, item, component, gable roof, layers). `examples/coastal_konkan.wadi` in this\n folder is a resolved `.wadi` for reference on the compiled output shape.\n\n## Check after every edit (get compiler feedback \u2014 no `.wadi` produced)\n\n`check.sh` runs the DSL end-to-end for **feedback only**: it parses the `.wdl` (reporting\nparse errors with `line:col`), resolves formulas/grids into numbers, and runs the real\nschema + roof/wall geometry validator (catches zero-length roof segments, missing slope,\nbad openings that the schema alone misses), and finally the **structural conventions**\nlinter (`reference/conventions.md`: floating floors, open exterior walls, no-slab floors)\n\u2014 all against a **throwaway temp** that is deleted. It never writes a `.wadi`; the app\'s\nDSL previewer does the real conversion.\n\nConvention **errors** (`\u2716 [C\u2026]`) fail the check \u2014 fix them. Convention **warnings**\n(`\u26A0 [C\u2026]`) are printed but don\'t fail; fix them, or keep them if the open side is a\ndeliberate verandah.\n\n```bash\nwadi-skill/architect/scripts/check.sh "<ABS>.wdl"\n```\n\nExit 0 = compiles + valid. Non-zero prints the exact `parse: \u2026` or `/path: message`\nerror \u2014 fix the `.wdl` and re-run before telling the user anything is ready.\n\n**If you have the `wadi_*` MCP tools** (from the `wadi-mcp` server) instead of a repo\ncheckout, use them \u2014 they\'re the same pipeline, no clone needed: `wadi_check(wdl)` for\nthis step, `wadi_preview(wdl)` for the images below, `wadi_scope(wdl)` to see the actual\nresolved value of every variable/point/grid line before you place an object on it, and\n`wadi_examples` / `wadi_reference` for the examples and these docs.\n\n## See your work (don\'t author blind)\n\nYou can\'t see the app\'s live render, but you CAN render your `.wdl` to images and **read\nthem** to check your own edit \u2014 layout, sizes, openings, and roof are exactly where\nmistakes happen. `preview.sh` takes the `.wdl` directly (it compiles to a throwaway temp\njust for rendering \u2014 again, no persistent `.wadi`):\n\n```bash\nwadi-skill/architect/scripts/preview.sh "<ABS>.wdl"\n```\n\nIt writes (and prints paths to) `plans.png` (floor plans \u2014 room layout + sizes),\n`elevations.png` (front/back/left/right \u2014 heights + **roof profile**, the 2D view of the\nbuilt 3D model), and `roof.png` (roof top view), plus all SVGs under `.../2d/`. **Read\nthe PNGs** after a non-trivial edit and confirm what you built matches the request (rooms\nin the right place, not overlapping, correct sizes in feet, roof over the plinth). **If\nit doesn\'t match, fix the `.wdl` and re-render \u2014 repeat until it looks right, and only\nthen tell the user it\'s ready.** It reuses the app\'s own generators, so it matches the\napp\'s 2D tabs byte-for-byte. See `prompts/verify-visually.md`.\n\n## Top pitfalls (memorize)\n\n- **Y is DOWN.** A room "to the north" has a *smaller* Y. Never treat Y as up.\n- **Dimensions are project units, not feet.** `width: 120` is 12 ft. Multiply feet by\n 10 (the default; `units.per_unit` can change it).\n- **Set `"coord_convention": "center"`.** Then a room\'s `x,y,width,length` are wall\n **CENTRELINES**, and **two rooms that share a wall just ABUT on the shared line** \u2014 room\n A spans X `[0,150]`, room B `[150,300]`; the wall is centred on `150` and shared. No\n overlap, no `wall_thickness` math (the system grows to the outer face at render). On a\n grid, a room is simply `x:"= main.x1", width:"= main.x2 - main.x1"`. (Legacy files\n without the flag use the OUTER convention where adjacent rooms must overlap by\n `wall_thickness`.) See `coordinate-system.md` \u2192 "the centreline convention".\n- **The roof footprint must cover the plinth footprint.** Roof segment widths and\n positions come from the walls they sit on, not arbitrary numbers.\n- **The roof lives on its OWN top floor** (`floor_number` ABOVE the floors it covers,\n containing only the roof), and you **never set its Z** \u2014 the base height is computed\n from the plinth height + the `height`s of the floors below it. To move the roof\n up/down, change floor heights, not the roof. (See `reference/roof-v2-guide.md`.)\n- **Floors stack; `floor_number` is 0-based** (the plinth floor = 0). Heights are\n independent per floor (`height`, `wall_height`, `slab_thickness` are unrelated\n fields) \u2014 *except* where the **structural conventions** require a match: the\n plinth-floor `height` must equal the plinth block height, and a floor with no slab\n must set `slab_thickness 0`, or the floors above float. See `reference/conventions.md`.\n- **A partial `wall` list makes a room a whitelist.** A bare room (no `wall` lines) is\n enclosed on all four sides; add one `wall` line and every side you don\'t list becomes a\n hole. Wall every **exterior** side (convention C2). List plain sides compactly:\n `wall east west`, then the opening sides separately.\n- **Minimal patches.** Preserve unchanged `.wdl` lines verbatim so the user (and diffs)\n can see exactly what moved.\n- **Formulas are bare expressions in the DSL** \u2014 write `at (main.x1, main.yA)`, not\n `"= main.x1"`; the compiler emits the `= \u2026` form. Quote only `name "\u2026"` strings;\n `room`/`pillar`/`var`/grid-line names are bare identifiers.\n- **Errors are caught by `check.sh`.** A parse error (bad syntax) or a `.strict()` schema\n error (unknown/misspelled key inside a `raw` block) fails `check.sh` with the exact\n location \u2014 and the app\'s DSL editor shows the same squiggle live; nothing reaches the\n model silently.\n'
422693
422728
  },
422694
422729
  "dsl": {
422695
422730
  "title": "The Wadi DSL (.wdl) \u2014 syntax reference",
@@ -422738,6 +422773,11 @@ function compileConfig(wdl) {
422738
422773
  const { config: config3 } = resolveParametric(compiled);
422739
422774
  return config3;
422740
422775
  }
422776
+ function scopeWdl(wdl) {
422777
+ const compiled = compileDsl(wdl, { resolveModule: stdResolveModule });
422778
+ registerExposedComponents(compiled);
422779
+ return buildRefsView(compiled);
422780
+ }
422741
422781
  function checkWdl(wdl) {
422742
422782
  let config3;
422743
422783
  try {
@@ -422864,6 +422904,39 @@ server.registerTool(
422864
422904
  return { content: [{ type: "text", text: lines.join("\n") }], structuredContent: r2 };
422865
422905
  }
422866
422906
  );
422907
+ server.registerTool(
422908
+ "wadi_scope",
422909
+ {
422910
+ title: "Resolve a Wadi design's variables, points & grid lines",
422911
+ description: "Compile a .wdl and return every symbol you can reference in an `= formula` with its RESOLVED numeric value: variables (with their formula source), point coordinates (x/y, also referenceable as .w/.l), and grid lines (`<grid>.x1`, `<grid>.yA`, \u2026). Use it to see the ACTUAL numbers a formula will produce before you place an object (e.g. what `main.x2` or `House.W/2` resolves to). \u26A0 = unresolved.",
422912
+ inputSchema: { wdl: external_exports.string().describe("The full .wdl source text.") }
422913
+ },
422914
+ async ({ wdl }) => {
422915
+ let view;
422916
+ try {
422917
+ view = scopeWdl(wdl);
422918
+ } catch (e) {
422919
+ return {
422920
+ content: [{ type: "text", text: "\u274C Could not compile: " + e.message + "\nRun wadi_check for details." }],
422921
+ isError: true
422922
+ };
422923
+ }
422924
+ const fmt = (v) => v === null ? "\u26A0" : Number.isInteger(v) ? String(v) : String(Math.round(v * 1e3) / 1e3);
422925
+ const lines = [];
422926
+ lines.push("Variables:");
422927
+ if (!view.variables.length) lines.push(" (none)");
422928
+ for (const v of view.variables) lines.push(` ${v.name} = ${fmt(v.value)}${v.formula ? ` ${v.formula}` : ""}`);
422929
+ lines.push("Points (x/y, also .w/.l):");
422930
+ if (!view.points.length) lines.push(" (none)");
422931
+ for (const p of view.points) lines.push(` ${p.name}: x ${fmt(p.x)} \xB7 y ${fmt(p.y)}`);
422932
+ for (const g of view.grids) {
422933
+ lines.push(`Grid "${g.id}":`);
422934
+ for (const l of g.xLines) lines.push(` ${g.id}.x${l.name} = ${fmt(l.value)}`);
422935
+ for (const l of g.yLines) lines.push(` ${g.id}.y${l.name} = ${fmt(l.value)}`);
422936
+ }
422937
+ return { content: [{ type: "text", text: lines.join("\n") }], structuredContent: view };
422938
+ }
422939
+ );
422867
422940
  server.registerTool(
422868
422941
  "wadi_preview",
422869
422942
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wadi-mcp",
3
- "version": "0.1.24",
3
+ "version": "0.1.25",
4
4
  "description": "MCP server for the Wadi house designer — check, preview, and reference the Wadi DSL (.wdl) without the repo.",
5
5
  "type": "module",
6
6
  "license": "MIT",