opentakeoff-mcp 0.9.85 → 0.9.86

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 (2) hide show
  1. package/dist/server-core.js +136 -87
  2. package/package.json +2 -2
@@ -1461,8 +1461,8 @@ function sweepHatchRuns(segs, meta, ws) {
1461
1461
  clusters.pop();
1462
1462
  }
1463
1463
  }
1464
- const median2 = (arr2) => {
1465
- const a = arr2.slice().sort((x, y) => x - y);
1464
+ const median2 = (arr3) => {
1465
+ const a = arr3.slice().sort((x, y) => x - y);
1466
1466
  return a[a.length >> 1];
1467
1467
  };
1468
1468
  for (const members of clusters) {
@@ -1590,8 +1590,8 @@ function classifyOffsetAnnotationSegs(segs, meta, ws, maxOffsetPx, minOffsetPx,
1590
1590
  t1: Math.max(c.x1 * dxu + c.y1 * dyu, c.x2 * dxu + c.y2 * dyu),
1591
1591
  w: c.w
1592
1592
  };
1593
- const arr2 = bins.get(b);
1594
- if (arr2) arr2.push(r);
1593
+ const arr3 = bins.get(b);
1594
+ if (arr3) arr3.push(r);
1595
1595
  else bins.set(b, [r]);
1596
1596
  }
1597
1597
  for (const runs of bins.values()) {
@@ -1768,8 +1768,8 @@ function sweepDimensionStrings(segs, meta, ws, ftPx, rejects, dimTexts) {
1768
1768
  t0: Math.min(c.x1 * dx + c.y1 * dy, c.x2 * dx + c.y2 * dy),
1769
1769
  t1: Math.max(c.x1 * dx + c.y1 * dy, c.x2 * dx + c.y2 * dy)
1770
1770
  };
1771
- const arr2 = bins.get(b);
1772
- if (arr2) arr2.push(r);
1771
+ const arr3 = bins.get(b);
1772
+ if (arr3) arr3.push(r);
1773
1773
  else bins.set(b, [r]);
1774
1774
  }
1775
1775
  for (const [b, rows] of bins) {
@@ -6658,12 +6658,12 @@ function applyRuleToProject(rule, shapes, sheetData) {
6658
6658
  const d = sheetData.get(s.sheet_id);
6659
6659
  if (!d) continue;
6660
6660
  const ring = s.verts_norm.map(([nx, ny]) => [nx * d.imgW, ny * d.imgH]);
6661
- let arr2 = deductsBySheet.get(s.sheet_id);
6662
- if (!arr2) {
6663
- arr2 = [];
6664
- deductsBySheet.set(s.sheet_id, arr2);
6661
+ let arr3 = deductsBySheet.get(s.sheet_id);
6662
+ if (!arr3) {
6663
+ arr3 = [];
6664
+ deductsBySheet.set(s.sheet_id, arr3);
6665
6665
  }
6666
- arr2.push(ring);
6666
+ arr3.push(ring);
6667
6667
  }
6668
6668
  const out = [];
6669
6669
  const acceptedBySheet = /* @__PURE__ */ new Map();
@@ -6688,12 +6688,12 @@ function applyRuleToProject(rule, shapes, sheetData) {
6688
6688
  verts_norm: ring.map(([x, y]) => [x / d.imgW, y / d.imgH]),
6689
6689
  area_sf: +areaSf.toFixed(2)
6690
6690
  });
6691
- let arr2 = acceptedBySheet.get(room.sheet_id);
6692
- if (!arr2) {
6693
- arr2 = [];
6694
- acceptedBySheet.set(room.sheet_id, arr2);
6691
+ let arr3 = acceptedBySheet.get(room.sheet_id);
6692
+ if (!arr3) {
6693
+ arr3 = [];
6694
+ acceptedBySheet.set(room.sheet_id, arr3);
6695
6695
  }
6696
- arr2.push(ring);
6696
+ arr3.push(ring);
6697
6697
  }
6698
6698
  }
6699
6699
  return out;
@@ -7712,11 +7712,82 @@ function drawMarks(ctx, toCanvas, marks, longEdge) {
7712
7712
  return drawn;
7713
7713
  }
7714
7714
 
7715
+ // ../web/src/lib/takeoffDocument.js
7716
+ var arr = (v) => Array.isArray(v) ? v : [];
7717
+ var obj = (v) => v && typeof v === "object" && !Array.isArray(v) ? v : {};
7718
+ var TAKEOFF_DOCUMENT_KEYS = Object.freeze([
7719
+ "schema",
7720
+ "project_name",
7721
+ "units",
7722
+ "client_info",
7723
+ "sheets",
7724
+ "conditions",
7725
+ "condition_columns",
7726
+ "shape_labels",
7727
+ "palette",
7728
+ "shapes",
7729
+ "markups",
7730
+ "rfis",
7731
+ "approvals",
7732
+ "proposals",
7733
+ "condition_edit_proposals",
7734
+ "rules",
7735
+ "sheet_group",
7736
+ "last_group",
7737
+ "sheet_tabs",
7738
+ "stitches",
7739
+ "sheet_levels",
7740
+ "layer_overrides",
7741
+ "provenance_counters"
7742
+ ]);
7743
+ function sheetEntry({ sheet_id, units_per_px, scale_source = void 0, scale_confirmed = void 0 }) {
7744
+ return {
7745
+ sheet_id,
7746
+ units_per_px,
7747
+ ...scale_source ? { scale_source } : {},
7748
+ ...scale_confirmed === false ? { scale_confirmed: false } : {}
7749
+ };
7750
+ }
7751
+ function buildTakeoffDocument(f = {}) {
7752
+ const conditions = arr(f.conditions);
7753
+ const pinned = arr(f.palette).filter((id) => conditions.some((c) => c && c.id === id));
7754
+ const clientInfo = obj(f.client_info);
7755
+ const hasClient = Object.values(clientInfo).some((v) => v && String(v).trim());
7756
+ const sheetLevels = obj(f.sheet_levels);
7757
+ const layerOverrides = obj(f.layer_overrides);
7758
+ const prov = f.provenance_counters;
7759
+ const hasProv = !!(prov && prov.shapes_deleted && Object.keys(prov.shapes_deleted).length);
7760
+ return {
7761
+ schema: TAKEOFF_SCHEMA,
7762
+ project_name: typeof f.project_name === "string" ? f.project_name : "",
7763
+ ...f.units === "metric" ? { units: "metric" } : {},
7764
+ ...hasClient ? { client_info: clientInfo } : {},
7765
+ sheets: arr(f.sheets),
7766
+ conditions,
7767
+ ...arr(f.condition_columns).length ? { condition_columns: f.condition_columns } : {},
7768
+ ...arr(f.shape_labels).length ? { shape_labels: f.shape_labels } : {},
7769
+ ...pinned.length ? { palette: pinned } : {},
7770
+ shapes: arr(f.shapes),
7771
+ markups: arr(f.markups),
7772
+ rfis: arr(f.rfis),
7773
+ ...arr(f.approvals).length ? { approvals: f.approvals } : {},
7774
+ ...arr(f.proposals).length ? { proposals: f.proposals } : {},
7775
+ ...arr(f.condition_edit_proposals).length ? { condition_edit_proposals: f.condition_edit_proposals } : {},
7776
+ ...arr(f.rules).length ? { rules: f.rules } : {},
7777
+ sheet_group: arr(f.sheet_group),
7778
+ last_group: arr(f.last_group),
7779
+ sheet_tabs: arr(f.sheet_tabs),
7780
+ ...arr(f.stitches).length ? { stitches: f.stitches } : {},
7781
+ ...Object.keys(sheetLevels).length ? { sheet_levels: sheetLevels } : {},
7782
+ ...Object.keys(layerOverrides).length ? { layer_overrides: layerOverrides } : {},
7783
+ ...hasProv ? { provenance_counters: prov } : {}
7784
+ };
7785
+ }
7786
+
7715
7787
  // src/session.ts
7716
7788
  var mintUuid2 = () => globalThis.crypto && typeof globalThis.crypto.randomUUID === "function" ? globalThis.crypto.randomUUID() : `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
7717
7789
  var uid = (p) => `${p}-${mintUuid2()}`;
7718
7790
  var nowIso2 = () => (/* @__PURE__ */ new Date()).toISOString();
7719
- var ANN_SCHEMA = TAKEOFF_SCHEMA;
7720
7791
  var sanitizeApprovals2 = sanitizeApprovals;
7721
7792
  var applyApprovalCommand2 = applyApprovalCommand;
7722
7793
  var CONTEXT_MIN_LEN_PX = 2;
@@ -8430,6 +8501,7 @@ var Session = class _Session {
8430
8501
  const lc = nextPaletteColor(this.conditions.length);
8431
8502
  c = {
8432
8503
  id: uid("cnd"),
8504
+ created_at: nowIso2(),
8433
8505
  finish_tag: tag,
8434
8506
  color: lc,
8435
8507
  fill: lc,
@@ -9637,9 +9709,9 @@ var Session = class _Session {
9637
9709
  }
9638
9710
  const tableRegions = /* @__PURE__ */ new Map();
9639
9711
  for (const tb of graph.tables) {
9640
- const arr2 = tableRegions.get(tb.sheet) ?? [];
9641
- arr2.push(tb.region);
9642
- tableRegions.set(tb.sheet, arr2);
9712
+ const arr3 = tableRegions.get(tb.sheet) ?? [];
9713
+ arr3.push(tb.region);
9714
+ tableRegions.set(tb.sheet, arr3);
9643
9715
  }
9644
9716
  const VAL_RE = /^[0-9][0-9,]{0,6}$/;
9645
9717
  const perMark = /* @__PURE__ */ new Map();
@@ -11505,41 +11577,18 @@ var Session = class _Session {
11505
11577
  }
11506
11578
  exportPayload() {
11507
11579
  if (!this.docs.size) throw new UserError("No plan loaded \u2014 call load_plan first.");
11508
- return {
11509
- schema: ANN_SCHEMA,
11580
+ return buildTakeoffDocument({
11510
11581
  project_name: "",
11511
11582
  units: "imperial",
11512
- sheets: [...this.sheets.values()].filter((s) => s.upp != null).map((s) => ({
11513
- sheet_id: s.key,
11514
- units_per_px: s.upp,
11515
- // provenance rides the payload (it used to be dropped here): the canvas
11516
- // hydrates scale_source for its report and scale_confirmed for the
11517
- // scale gate's confirm affordance — absent = confirmed (pre-flag docs)
11518
- ...s.scaleSource ? { scale_source: s.scaleSource } : {},
11519
- ...s.scaleConfirmed === false ? { scale_confirmed: false } : {}
11520
- })),
11583
+ sheets: [...this.sheets.values()].filter((s) => s.upp != null).map((s) => sheetEntry({ sheet_id: s.key, units_per_px: s.upp, scale_source: s.scaleSource, scale_confirmed: s.scaleConfirmed })),
11521
11584
  conditions: this.conditions,
11522
11585
  shapes: this.shapes,
11523
11586
  markups: this.markups,
11524
- // approvals ride the payload additively (#176) — present only when any
11525
- // exist, exactly the canvas buildPayload's convention, so a verdict-free
11526
- // export stays byte-identical to a pre-#176 one
11527
- ...this.approvals.length ? { approvals: this.approvals } : {},
11528
- // RFIs (#364): the panel's own records, tombstones stripped — the app
11529
- // has no tombstone notion (its delete is a removal), and a withdrawn
11530
- // question must not resurface there as a stray Void row. Same
11531
- // present-only-when-any convention as approvals.
11532
- ...this.liveRfis().length ? { rfis: this.liveRfis() } : {},
11533
- // proposals (#365): the batches and the pending condition diffs ride
11534
- // the payload as transport — present only when any exist, so a
11535
- // proposal-free export stays byte-identical to a pre-#365 one.
11536
- ...this.proposals.length ? { proposals: this.proposals } : {},
11537
- ...this.conditionEditProposals.length ? { condition_edit_proposals: this.conditionEditProposals } : {},
11538
- sheet_group: [],
11539
- last_group: [],
11540
- sheet_tabs: [],
11541
- sheet_levels: {}
11542
- };
11587
+ rfis: this.liveRfis(),
11588
+ approvals: this.approvals,
11589
+ proposals: this.proposals,
11590
+ condition_edit_proposals: this.conditionEditProposals
11591
+ });
11543
11592
  }
11544
11593
  /** The computed Report document — "opentakeoff.report.v1", the SAME schema
11545
11594
  * and math as the canvas Report's JSON export (web reportJson, totals.js):
@@ -12169,7 +12218,7 @@ var exportDxfOutput = {
12169
12218
  var exportTakeoffOutput = {
12170
12219
  schema: z.string(),
12171
12220
  project_name: z.string(),
12172
- units: z.string(),
12221
+ units: z.string().optional().describe("Present only for a metric project; absent means imperial \u2014 the app's own diff-only convention"),
12173
12222
  sheets: z.array(z.object({
12174
12223
  sheet_id: z.string(),
12175
12224
  units_per_px: z.number(),
@@ -12201,7 +12250,7 @@ var exportTakeoffOutput = {
12201
12250
  sheet_group: z.array(z.unknown()),
12202
12251
  last_group: z.array(z.unknown()),
12203
12252
  sheet_tabs: z.array(z.unknown()),
12204
- sheet_levels: z.object({}).passthrough(),
12253
+ sheet_levels: z.object({}).passthrough().optional().describe("Present only when a sheet carries a level label (the app omits it when empty)"),
12205
12254
  proposals: z.array(z.object({ id: z.string(), label: z.string(), rationale: z.string(), created_at: z.string(), withdrawn_at: z.string().optional() }).passthrough()).optional().describe("Proposal batches (#365) \u2014 present only when any exist. Shapes reference them by origin.proposal_id; the canvas shows one Accept per batch"),
12206
12255
  condition_edit_proposals: z.array(z.object({ id: z.string(), condition_id: z.string(), proposed: z.object({}).passthrough(), rationale: z.string(), proposed_at: z.string() }).passthrough()).optional().describe("Pending condition-edit diffs (#365) \u2014 present only when any exist. Nothing on the condition changes until the estimator accepts in the canvas")
12207
12256
  };
@@ -13520,9 +13569,9 @@ async function buildMarkedSetPdf({ projectName, dark, sheets, shapes, markups, a
13520
13569
  const { PDFDocument, StandardFonts, rgb, degrees, LineCapStyle } = await import("pdf-lib");
13521
13570
  const condById = Object.fromEntries(conditions.map((c) => [c.id, c]));
13522
13571
  const rfiNum = new Map((rfis || []).map((r) => [r.id, r.number]));
13523
- const byKey = (arr2) => {
13572
+ const byKey = (arr3) => {
13524
13573
  const m = /* @__PURE__ */ new Map();
13525
- for (const s of arr2) {
13574
+ for (const s of arr3) {
13526
13575
  const a = m.get(s.sheet_id) || [];
13527
13576
  a.push(s);
13528
13577
  m.set(s.sheet_id, a);
@@ -14253,7 +14302,7 @@ function parseTakeoffImport(text) {
14253
14302
  }
14254
14303
  return doc;
14255
14304
  }
14256
- var arr = (v) => Array.isArray(v) ? v : [];
14305
+ var arr2 = (v) => Array.isArray(v) ? v : [];
14257
14306
  var tagKey = (t) => String(t || "").trim().toUpperCase();
14258
14307
  var freeId = (id, taken) => {
14259
14308
  let n = 2, next = id;
@@ -14262,11 +14311,11 @@ var freeId = (id, taken) => {
14262
14311
  };
14263
14312
  function mergeTakeoffImport(current, imported, knownFiles = null) {
14264
14313
  const cur = current && typeof current === "object" ? current : {};
14265
- const impShapes = arr(imported.shapes).filter((s) => s && typeof s === "object" && typeof s.sheet_id === "string" && typeof s.id === "string").map(normalizeAgentReview);
14266
- const impConds = arr(imported.conditions).filter((c) => c && typeof c === "object" && typeof c.id === "string");
14267
- const localScales = new Map(arr(cur.sheets).filter((s) => typeof s?.sheet_id === "string").map((s) => [s.sheet_id, s.units_per_px]));
14268
- const incomingScales = new Map(arr(imported.sheets).filter((s) => typeof s?.sheet_id === "string").map((s) => [s.sheet_id, s.units_per_px]));
14269
- const existingIds = new Set(arr(cur.shapes).map((s) => s.id));
14314
+ const impShapes = arr2(imported.shapes).filter((s) => s && typeof s === "object" && typeof s.sheet_id === "string" && typeof s.id === "string").map(normalizeAgentReview);
14315
+ const impConds = arr2(imported.conditions).filter((c) => c && typeof c === "object" && typeof c.id === "string");
14316
+ const localScales = new Map(arr2(cur.sheets).filter((s) => typeof s?.sheet_id === "string").map((s) => [s.sheet_id, s.units_per_px]));
14317
+ const incomingScales = new Map(arr2(imported.sheets).filter((s) => typeof s?.sheet_id === "string").map((s) => [s.sheet_id, s.units_per_px]));
14318
+ const existingIds = new Set(arr2(cur.shapes).map((s) => s.id));
14270
14319
  for (const s of impShapes) {
14271
14320
  if (existingIds.has(s.id) || s.measure_role === "count") continue;
14272
14321
  const local = localScales.get(s.sheet_id), incoming = incomingScales.get(s.sheet_id);
@@ -14280,20 +14329,20 @@ function mergeTakeoffImport(current, imported, knownFiles = null) {
14280
14329
  return [...new Set(added.map((s) => String(s.sheet_id).split("#")[0]).filter((f) => !known.has(f)))];
14281
14330
  };
14282
14331
  const pendingCount = (shapes) => shapes.filter((s) => s.origin?.reviewed === false).length;
14283
- if (!arr(cur.shapes).length && !arr(cur.markups).length && !arr(cur.approvals).length && !arr(cur.sheets).some((s) => s?.units_per_px > 0)) {
14332
+ if (!arr2(cur.shapes).length && !arr2(cur.markups).length && !arr2(cur.approvals).length && !arr2(cur.sheets).some((s) => s?.units_per_px > 0)) {
14284
14333
  const payload2 = {
14285
14334
  ...imported,
14286
14335
  shapes: impShapes,
14287
- ...arr(imported.sheet_tabs).length ? {} : { sheet_tabs: arr(cur.sheet_tabs) },
14288
- ...arr(imported.sheet_group).length ? {} : { sheet_group: arr(cur.sheet_group) },
14289
- ...arr(imported.last_group).length ? {} : { last_group: arr(cur.last_group) }
14336
+ ...arr2(imported.sheet_tabs).length ? {} : { sheet_tabs: arr2(cur.sheet_tabs) },
14337
+ ...arr2(imported.sheet_group).length ? {} : { sheet_group: arr2(cur.sheet_group) },
14338
+ ...arr2(imported.last_group).length ? {} : { last_group: arr2(cur.last_group) }
14290
14339
  };
14291
14340
  return {
14292
14341
  payload: payload2,
14293
- note: { replaced: true, shapes_added: impShapes.length, shapes_pending: pendingCount(impShapes), conditions_merged: 0, conditions_added: impConds.length, scales_adopted: arr(imported.sheets).length, unknown_files: unknownFiles(impShapes) }
14342
+ note: { replaced: true, shapes_added: impShapes.length, shapes_pending: pendingCount(impShapes), conditions_merged: 0, conditions_added: impConds.length, scales_adopted: arr2(imported.sheets).length, unknown_files: unknownFiles(impShapes) }
14294
14343
  };
14295
14344
  }
14296
- const conditions = [...arr(cur.conditions)];
14345
+ const conditions = [...arr2(cur.conditions)];
14297
14346
  const byTag = new Map(conditions.map((c) => [tagKey(c.finish_tag), c.id]));
14298
14347
  const condIds = new Set(conditions.map((c) => c.id));
14299
14348
  const condMap = /* @__PURE__ */ new Map();
@@ -14329,23 +14378,23 @@ function mergeTakeoffImport(current, imported, knownFiles = null) {
14329
14378
  conditions[i] = next;
14330
14379
  }
14331
14380
  }
14332
- const shapeIds = new Set(arr(cur.shapes).map((s) => s.id));
14381
+ const shapeIds = new Set(arr2(cur.shapes).map((s) => s.id));
14333
14382
  const addedShapes = impShapes.filter((s) => !shapeIds.has(s.id)).map((s) => condMap.has(s.condition_id) && condMap.get(s.condition_id) !== s.condition_id ? { ...s, condition_id: condMap.get(s.condition_id) } : s);
14334
- const markupIds = new Set(arr(cur.markups).map((m) => m?.id).filter(Boolean));
14335
- const addedMarkups = arr(imported.markups).filter((m) => m && typeof m === "object" && (!m.id || !markupIds.has(m.id)));
14336
- const rfiIds = new Set(arr(cur.rfis).map((r) => r?.id).filter(Boolean));
14337
- const addedRfis = arr(imported.rfis).filter((r) => r && typeof r === "object" && r.id && !rfiIds.has(r.id));
14338
- const proposalIds = new Set(arr(cur.proposals).map((p) => p?.id).filter(Boolean));
14339
- const addedProposals = arr(imported.proposals).filter((p) => p && typeof p === "object" && typeof p.id === "string" && !proposalIds.has(p.id));
14340
- const editIds = new Set(arr(cur.condition_edit_proposals).map((p) => p?.id).filter(Boolean));
14383
+ const markupIds = new Set(arr2(cur.markups).map((m) => m?.id).filter(Boolean));
14384
+ const addedMarkups = arr2(imported.markups).filter((m) => m && typeof m === "object" && (!m.id || !markupIds.has(m.id)));
14385
+ const rfiIds = new Set(arr2(cur.rfis).map((r) => r?.id).filter(Boolean));
14386
+ const addedRfis = arr2(imported.rfis).filter((r) => r && typeof r === "object" && r.id && !rfiIds.has(r.id));
14387
+ const proposalIds = new Set(arr2(cur.proposals).map((p) => p?.id).filter(Boolean));
14388
+ const addedProposals = arr2(imported.proposals).filter((p) => p && typeof p === "object" && typeof p.id === "string" && !proposalIds.has(p.id));
14389
+ const editIds = new Set(arr2(cur.condition_edit_proposals).map((p) => p?.id).filter(Boolean));
14341
14390
  const takenTags = new Set(conditions.map((c) => tagKey(c.finish_tag)));
14342
- const addedEdits = arr(imported.condition_edit_proposals).filter((p) => p && typeof p === "object" && typeof p.id === "string" && !editIds.has(p.id) && p.proposed && typeof p.proposed === "object").map((p) => condMap.has(p.condition_id) ? { ...p, condition_id: condMap.get(p.condition_id) } : p).filter((p) => condIds.has(p.condition_id)).filter((p) => p.proposed.finish_tag === void 0 || !takenTags.has(tagKey(p.proposed.finish_tag)) || tagKey(p.proposed.finish_tag) === tagKey(conditions.find((c) => c.id === p.condition_id)?.finish_tag)).filter((p) => !arr(cur.condition_edit_proposals).some((q) => q?.condition_id === p.condition_id));
14343
- const approvalIds = new Set(arr(cur.approvals).map((a) => a?.id).filter(Boolean));
14391
+ const addedEdits = arr2(imported.condition_edit_proposals).filter((p) => p && typeof p === "object" && typeof p.id === "string" && !editIds.has(p.id) && p.proposed && typeof p.proposed === "object").map((p) => condMap.has(p.condition_id) ? { ...p, condition_id: condMap.get(p.condition_id) } : p).filter((p) => condIds.has(p.condition_id)).filter((p) => p.proposed.finish_tag === void 0 || !takenTags.has(tagKey(p.proposed.finish_tag)) || tagKey(p.proposed.finish_tag) === tagKey(conditions.find((c) => c.id === p.condition_id)?.finish_tag)).filter((p) => !arr2(cur.condition_edit_proposals).some((q) => q?.condition_id === p.condition_id));
14392
+ const approvalIds = new Set(arr2(cur.approvals).map((a) => a?.id).filter(Boolean));
14344
14393
  const addedApprovals = sanitizeApprovals(imported.approvals).filter((a) => !approvalIds.has(a.id));
14345
- const sheets = [...arr(cur.sheets)];
14394
+ const sheets = [...arr2(cur.sheets)];
14346
14395
  const scaled = new Set(sheets.map((s) => s?.sheet_id));
14347
14396
  let scalesAdopted = 0;
14348
- for (const s of arr(imported.sheets)) {
14397
+ for (const s of arr2(imported.sheets)) {
14349
14398
  if (s && typeof s === "object" && s.sheet_id && s.units_per_px && !scaled.has(s.sheet_id)) {
14350
14399
  sheets.push(s);
14351
14400
  scaled.add(s.sheet_id);
@@ -14355,12 +14404,12 @@ function mergeTakeoffImport(current, imported, knownFiles = null) {
14355
14404
  const payload = {
14356
14405
  ...cur,
14357
14406
  conditions,
14358
- shapes: [...arr(cur.shapes), ...addedShapes],
14359
- markups: [...arr(cur.markups), ...addedMarkups],
14360
- ...addedRfis.length ? { rfis: [...arr(cur.rfis), ...addedRfis] } : {},
14361
- ...addedProposals.length ? { proposals: [...arr(cur.proposals), ...addedProposals] } : {},
14362
- ...addedEdits.length ? { condition_edit_proposals: [...arr(cur.condition_edit_proposals), ...addedEdits] } : {},
14363
- ...addedApprovals.length ? { approvals: [...arr(cur.approvals), ...addedApprovals] } : {},
14407
+ shapes: [...arr2(cur.shapes), ...addedShapes],
14408
+ markups: [...arr2(cur.markups), ...addedMarkups],
14409
+ ...addedRfis.length ? { rfis: [...arr2(cur.rfis), ...addedRfis] } : {},
14410
+ ...addedProposals.length ? { proposals: [...arr2(cur.proposals), ...addedProposals] } : {},
14411
+ ...addedEdits.length ? { condition_edit_proposals: [...arr2(cur.condition_edit_proposals), ...addedEdits] } : {},
14412
+ ...addedApprovals.length ? { approvals: [...arr2(cur.approvals), ...addedApprovals] } : {},
14364
14413
  sheets
14365
14414
  };
14366
14415
  return {
@@ -15089,7 +15138,7 @@ No review gate: the pencil-not-ink rule exists to stop an agent inventing geomet
15089
15138
  import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
15090
15139
 
15091
15140
  // src/wiki.generated.ts
15092
- var WIKI_VERSION = "0.9.85";
15141
+ var WIKI_VERSION = "0.9.86";
15093
15142
  var WIKI_PAGES = [
15094
15143
  {
15095
15144
  "key": "index",
@@ -15527,7 +15576,7 @@ function nameTheStageInRefusals(server) {
15527
15576
  // package.json
15528
15577
  var package_default = {
15529
15578
  name: "opentakeoff-mcp",
15530
- version: "0.9.85",
15579
+ version: "0.9.86",
15531
15580
  mcpName: "io.github.Kentucky-ai/opentakeoff",
15532
15581
  type: "module",
15533
15582
  description: "OpenTakeoff MCP server \u2014 drive the takeoff engine from your MCP client over stdio.",
@@ -15547,7 +15596,7 @@ var package_default = {
15547
15596
  "check:wiki": "node scripts/check-wiki.mjs",
15548
15597
  "check:versions": "node ../scripts/check-version-consistency.mjs",
15549
15598
  "check:tool-count": "node --import tsx scripts/check-tool-count.mjs",
15550
- test: "node --import tsx --test test/checkers.test.ts test/conformance.test.ts test/constants.test.ts test/context.test.ts test/dxf.test.ts test/e2e.test.ts test/gate.test.ts test/graphEvalRowsym.test.ts test/labels.test.ts test/mep.test.ts test/overlap.test.ts test/parity.test.ts test/proposals.test.ts test/raster.test.ts test/resources.test.ts test/safewrite.test.ts test/scalewarn.test.ts test/scope.test.ts test/session.test.ts test/staging.test.ts test/sweepguard.test.ts test/tools.test.ts test/transitions.test.ts test/twins.test.ts test/view.test.ts test/wiki.test.ts"
15599
+ test: "node --import tsx --test test/checkers.test.ts test/conformance.test.ts test/constants.test.ts test/context.test.ts test/document.test.ts test/dxf.test.ts test/e2e.test.ts test/gate.test.ts test/graphEvalRowsym.test.ts test/labels.test.ts test/mep.test.ts test/overlap.test.ts test/parity.test.ts test/proposals.test.ts test/raster.test.ts test/resources.test.ts test/safewrite.test.ts test/scalewarn.test.ts test/scope.test.ts test/session.test.ts test/staging.test.ts test/sweepguard.test.ts test/tools.test.ts test/transitions.test.ts test/twins.test.ts test/view.test.ts test/wiki.test.ts"
15551
15600
  },
15552
15601
  dependencies: {
15553
15602
  "@modelcontextprotocol/sdk": "^1.12.0",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opentakeoff-mcp",
3
- "version": "0.9.85",
3
+ "version": "0.9.86",
4
4
  "mcpName": "io.github.Kentucky-ai/opentakeoff",
5
5
  "type": "module",
6
6
  "description": "OpenTakeoff MCP server — drive the takeoff engine from your MCP client over stdio.",
@@ -20,7 +20,7 @@
20
20
  "check:wiki": "node scripts/check-wiki.mjs",
21
21
  "check:versions": "node ../scripts/check-version-consistency.mjs",
22
22
  "check:tool-count": "node --import tsx scripts/check-tool-count.mjs",
23
- "test": "node --import tsx --test test/checkers.test.ts test/conformance.test.ts test/constants.test.ts test/context.test.ts test/dxf.test.ts test/e2e.test.ts test/gate.test.ts test/graphEvalRowsym.test.ts test/labels.test.ts test/mep.test.ts test/overlap.test.ts test/parity.test.ts test/proposals.test.ts test/raster.test.ts test/resources.test.ts test/safewrite.test.ts test/scalewarn.test.ts test/scope.test.ts test/session.test.ts test/staging.test.ts test/sweepguard.test.ts test/tools.test.ts test/transitions.test.ts test/twins.test.ts test/view.test.ts test/wiki.test.ts"
23
+ "test": "node --import tsx --test test/checkers.test.ts test/conformance.test.ts test/constants.test.ts test/context.test.ts test/document.test.ts test/dxf.test.ts test/e2e.test.ts test/gate.test.ts test/graphEvalRowsym.test.ts test/labels.test.ts test/mep.test.ts test/overlap.test.ts test/parity.test.ts test/proposals.test.ts test/raster.test.ts test/resources.test.ts test/safewrite.test.ts test/scalewarn.test.ts test/scope.test.ts test/session.test.ts test/staging.test.ts test/sweepguard.test.ts test/tools.test.ts test/transitions.test.ts test/twins.test.ts test/view.test.ts test/wiki.test.ts"
24
24
  },
25
25
  "dependencies": {
26
26
  "@modelcontextprotocol/sdk": "^1.12.0",