@de-otio/epimethian-mcp 5.3.2 → 5.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -18331,14 +18331,14 @@ var require_HTMLParser = __commonJS({
18331
18331
  }
18332
18332
  var insertToken = htmlparser.insertToken = function insertToken2(t, value, arg3, arg4) {
18333
18333
  flushText();
18334
- var current = stack.top;
18335
- if (!current || current.namespaceURI === NAMESPACE.HTML) {
18334
+ var current2 = stack.top;
18335
+ if (!current2 || current2.namespaceURI === NAMESPACE.HTML) {
18336
18336
  parser(t, value, arg3, arg4);
18337
18337
  } else {
18338
18338
  if (t !== TAG && t !== TEXT) {
18339
18339
  insertForeignToken(t, value, arg3, arg4);
18340
18340
  } else {
18341
- if (isMathmlTextIntegrationPoint(current) && (t === TEXT || t === TAG && value !== "mglyph" && value !== "malignmark") || t === TAG && value === "svg" && current.namespaceURI === NAMESPACE.MATHML && current.localName === "annotation-xml" || isHTMLIntegrationPoint(current)) {
18341
+ if (isMathmlTextIntegrationPoint(current2) && (t === TEXT || t === TAG && value !== "mglyph" && value !== "malignmark") || t === TAG && value === "svg" && current2.namespaceURI === NAMESPACE.MATHML && current2.localName === "annotation-xml" || isHTMLIntegrationPoint(current2)) {
18342
18342
  text_integration_mode = true;
18343
18343
  parser(t, value, arg3, arg4);
18344
18344
  text_integration_mode = false;
@@ -22938,7 +22938,7 @@ var require_HTMLParser = __commonJS({
22938
22938
  }
22939
22939
  return false;
22940
22940
  }
22941
- var current;
22941
+ var current2;
22942
22942
  switch (t) {
22943
22943
  case 1:
22944
22944
  if (frameset_ok && NONWSNONNUL.test(value))
@@ -23007,29 +23007,29 @@ var require_HTMLParser = __commonJS({
23007
23007
  }
23008
23008
  do {
23009
23009
  stack.pop();
23010
- current = stack.top;
23011
- } while (current.namespaceURI !== NAMESPACE.HTML && !isMathmlTextIntegrationPoint(current) && !isHTMLIntegrationPoint(current));
23010
+ current2 = stack.top;
23011
+ } while (current2.namespaceURI !== NAMESPACE.HTML && !isMathmlTextIntegrationPoint(current2) && !isHTMLIntegrationPoint(current2));
23012
23012
  insertToken(t, value, arg3, arg4);
23013
23013
  return;
23014
23014
  }
23015
- current = stack.elements.length === 1 && fragment ? fragmentContext : stack.top;
23016
- if (current.namespaceURI === NAMESPACE.MATHML) {
23015
+ current2 = stack.elements.length === 1 && fragment ? fragmentContext : stack.top;
23016
+ if (current2.namespaceURI === NAMESPACE.MATHML) {
23017
23017
  adjustMathMLAttributes(arg3);
23018
- } else if (current.namespaceURI === NAMESPACE.SVG) {
23018
+ } else if (current2.namespaceURI === NAMESPACE.SVG) {
23019
23019
  value = adjustSVGTagName(value);
23020
23020
  adjustSVGAttributes(arg3);
23021
23021
  }
23022
23022
  adjustForeignAttributes(arg3);
23023
- insertForeignElement(value, arg3, current.namespaceURI);
23023
+ insertForeignElement(value, arg3, current2.namespaceURI);
23024
23024
  if (arg4) {
23025
- if (value === "script" && current.namespaceURI === NAMESPACE.SVG) {
23025
+ if (value === "script" && current2.namespaceURI === NAMESPACE.SVG) {
23026
23026
  }
23027
23027
  stack.pop();
23028
23028
  }
23029
23029
  return;
23030
23030
  case 3:
23031
- current = stack.top;
23032
- if (value === "script" && current.namespaceURI === NAMESPACE.SVG && current.localName === "script") {
23031
+ current2 = stack.top;
23032
+ if (value === "script" && current2.namespaceURI === NAMESPACE.SVG && current2.localName === "script") {
23033
23033
  stack.pop();
23034
23034
  } else {
23035
23035
  var i = stack.elements.length - 1;
@@ -23226,8 +23226,8 @@ var require_Location = __commonJS({
23226
23226
  }
23227
23227
  },
23228
23228
  assign: { value: function(url) {
23229
- var current = new URL2(this._href);
23230
- var newurl = current.resolve(url);
23229
+ var current2 = new URL2(this._href);
23230
+ var newurl = current2.resolve(url);
23231
23231
  this._href = newurl;
23232
23232
  } },
23233
23233
  replace: { value: function(url) {
@@ -23832,11 +23832,11 @@ var require_turndown_cjs = __commonJS({
23832
23832
  node.parentNode.removeChild(node);
23833
23833
  return next2;
23834
23834
  }
23835
- function next(prev, current, isPre) {
23836
- if (prev && prev.parentNode === current || isPre(current)) {
23837
- return current.nextSibling || current.parentNode;
23835
+ function next(prev, current2, isPre) {
23836
+ if (prev && prev.parentNode === current2 || isPre(current2)) {
23837
+ return current2.nextSibling || current2.parentNode;
23838
23838
  }
23839
- return current.firstChild || current.nextSibling || current.parentNode;
23839
+ return current2.firstChild || current2.nextSibling || current2.parentNode;
23840
23840
  }
23841
23841
  var root = typeof window !== "undefined" ? window : {};
23842
23842
  function canParseHTMLNatively() {
@@ -25710,19 +25710,19 @@ var require_decode = __commonJS({
25710
25710
  };
25711
25711
  EntityDecoder3.prototype.stateNamedEntity = function(str2, offset) {
25712
25712
  var decodeTree = this.decodeTree;
25713
- var current = decodeTree[this.treeIndex];
25714
- var valueLength = (current & BinTrieFlags2.VALUE_LENGTH) >> 14;
25713
+ var current2 = decodeTree[this.treeIndex];
25714
+ var valueLength = (current2 & BinTrieFlags2.VALUE_LENGTH) >> 14;
25715
25715
  for (; offset < str2.length; offset++, this.excess++) {
25716
25716
  var char = str2.charCodeAt(offset);
25717
- this.treeIndex = determineBranch2(decodeTree, current, this.treeIndex + Math.max(1, valueLength), char);
25717
+ this.treeIndex = determineBranch2(decodeTree, current2, this.treeIndex + Math.max(1, valueLength), char);
25718
25718
  if (this.treeIndex < 0) {
25719
25719
  return this.result === 0 || // If we are parsing an attribute
25720
25720
  this.decodeMode === DecodingMode2.Attribute && // We shouldn't have consumed any characters after the entity,
25721
25721
  (valueLength === 0 || // And there should be no invalid characters.
25722
25722
  isEntityInAttributeInvalidEnd2(char)) ? 0 : this.emitNotTerminatedNamedEntity();
25723
25723
  }
25724
- current = decodeTree[this.treeIndex];
25725
- valueLength = (current & BinTrieFlags2.VALUE_LENGTH) >> 14;
25724
+ current2 = decodeTree[this.treeIndex];
25725
+ valueLength = (current2 & BinTrieFlags2.VALUE_LENGTH) >> 14;
25726
25726
  if (valueLength !== 0) {
25727
25727
  if (char === CharCodes2.SEMI) {
25728
25728
  return this.emitNamedEntityData(this.treeIndex, valueLength, this.consumed + this.excess);
@@ -25806,9 +25806,9 @@ var require_decode = __commonJS({
25806
25806
  return result;
25807
25807
  };
25808
25808
  }
25809
- function determineBranch2(decodeTree, current, nodeIdx, char) {
25810
- var branchCount = (current & BinTrieFlags2.BRANCH_LENGTH) >> 7;
25811
- var jumpOffset = current & BinTrieFlags2.JUMP_TABLE;
25809
+ function determineBranch2(decodeTree, current2, nodeIdx, char) {
25810
+ var branchCount = (current2 & BinTrieFlags2.BRANCH_LENGTH) >> 7;
25811
+ var jumpOffset = current2 & BinTrieFlags2.JUMP_TABLE;
25812
25812
  if (branchCount === 0) {
25813
25813
  return jumpOffset !== 0 && char === jumpOffset ? nodeIdx : -1;
25814
25814
  }
@@ -26923,15 +26923,15 @@ var require_helpers = __commonJS({
26923
26923
  if (nodeA === nodeB) {
26924
26924
  return 0;
26925
26925
  }
26926
- var current = (0, domhandler_1.hasChildren)(nodeA) ? nodeA : nodeA.parent;
26927
- while (current) {
26928
- aParents.unshift(current);
26929
- current = current.parent;
26926
+ var current2 = (0, domhandler_1.hasChildren)(nodeA) ? nodeA : nodeA.parent;
26927
+ while (current2) {
26928
+ aParents.unshift(current2);
26929
+ current2 = current2.parent;
26930
26930
  }
26931
- current = (0, domhandler_1.hasChildren)(nodeB) ? nodeB : nodeB.parent;
26932
- while (current) {
26933
- bParents.unshift(current);
26934
- current = current.parent;
26931
+ current2 = (0, domhandler_1.hasChildren)(nodeB) ? nodeB : nodeB.parent;
26932
+ while (current2) {
26933
+ bParents.unshift(current2);
26934
+ current2 = current2.parent;
26935
26935
  }
26936
26936
  var maxIdx = Math.min(aParents.length, bParents.length);
26937
26937
  var idx = 0;
@@ -28667,9 +28667,9 @@ var require_general = __commonJS({
28667
28667
  case css_what_1.SelectorType.Descendant: {
28668
28668
  if (options2.cacheResults === false || typeof WeakSet === "undefined") {
28669
28669
  return function descendant(elem) {
28670
- var current = elem;
28671
- while (current = getElementParent(current, adapter)) {
28672
- if (next(current)) {
28670
+ var current2 = elem;
28671
+ while (current2 = getElementParent(current2, adapter)) {
28672
+ if (next(current2)) {
28673
28673
  return true;
28674
28674
  }
28675
28675
  }
@@ -28678,13 +28678,13 @@ var require_general = __commonJS({
28678
28678
  }
28679
28679
  var isFalseCache_1 = /* @__PURE__ */ new WeakSet();
28680
28680
  return function cachedDescendant(elem) {
28681
- var current = elem;
28682
- while (current = getElementParent(current, adapter)) {
28683
- if (!isFalseCache_1.has(current)) {
28684
- if (adapter.isTag(current) && next(current)) {
28681
+ var current2 = elem;
28682
+ while (current2 = getElementParent(current2, adapter)) {
28683
+ if (!isFalseCache_1.has(current2)) {
28684
+ if (adapter.isTag(current2) && next(current2)) {
28685
28685
  return true;
28686
28686
  }
28687
- isFalseCache_1.add(current);
28687
+ isFalseCache_1.add(current2);
28688
28688
  }
28689
28689
  }
28690
28690
  return false;
@@ -28692,11 +28692,11 @@ var require_general = __commonJS({
28692
28692
  }
28693
28693
  case "_flexibleDescendant": {
28694
28694
  return function flexibleDescendant(elem) {
28695
- var current = elem;
28695
+ var current2 = elem;
28696
28696
  do {
28697
- if (next(current))
28697
+ if (next(current2))
28698
28698
  return true;
28699
- } while (current = getElementParent(current, adapter));
28699
+ } while (current2 = getElementParent(current2, adapter));
28700
28700
  return false;
28701
28701
  };
28702
28702
  }
@@ -49324,7 +49324,7 @@ async function getPage(pageId, includeBody) {
49324
49324
  async function createPage(spaceId, title, body, parentId, clientLabel) {
49325
49325
  const cfg = await getConfig();
49326
49326
  const pageBody = stripAttributionFooter(toStorageFormat(body));
49327
- const epimethianTag = `Epimethian v${"5.3.2"}`;
49327
+ const epimethianTag = `Epimethian v${"5.3.3"}`;
49328
49328
  const versionMsg = cfg.attribution && clientLabel ? `Created by ${clientLabel} (via ${epimethianTag})` : `Created by ${epimethianTag}`;
49329
49329
  const payload = {
49330
49330
  title,
@@ -49349,7 +49349,7 @@ async function createPage(spaceId, title, body, parentId, clientLabel) {
49349
49349
  async function updatePage(pageId, opts) {
49350
49350
  const cfg = await getConfig();
49351
49351
  const newVersion = opts.version + 1;
49352
- const epimethianTag = `Epimethian v${"5.3.2"}`;
49352
+ const epimethianTag = `Epimethian v${"5.3.3"}`;
49353
49353
  const effectiveClient = cfg.attribution ? opts.clientLabel : void 0;
49354
49354
  let versionMessage;
49355
49355
  if (opts.versionMessage && effectiveClient)
@@ -49360,14 +49360,14 @@ async function updatePage(pageId, opts) {
49360
49360
  versionMessage = `Updated by ${effectiveClient} (via ${epimethianTag})`;
49361
49361
  else
49362
49362
  versionMessage = `Updated by ${epimethianTag}`;
49363
+ const pageBody = opts.body ? stripAttributionFooter(toStorageFormat(opts.body)) : void 0;
49363
49364
  const payload = {
49364
49365
  id: pageId,
49365
49366
  status: "current",
49366
49367
  title: opts.title,
49367
49368
  version: { number: newVersion, message: versionMessage }
49368
49369
  };
49369
- if (opts.body) {
49370
- const pageBody = stripAttributionFooter(toStorageFormat(opts.body));
49370
+ if (pageBody !== void 0) {
49371
49371
  payload.body = {
49372
49372
  representation: "storage",
49373
49373
  value: pageBody
@@ -49386,8 +49386,7 @@ async function updatePage(pageId, opts) {
49386
49386
  throw err;
49387
49387
  }
49388
49388
  const page = PageSchema.parse(raw);
49389
- if (opts.body) {
49390
- const pageBody = stripAttributionFooter(toStorageFormat(opts.body));
49389
+ if (pageBody !== void 0) {
49391
49390
  pageCache.set(pageId, newVersion, pageBody);
49392
49391
  }
49393
49392
  try {
@@ -49763,9 +49762,11 @@ function sanitizeCommentBody(body) {
49763
49762
  }
49764
49763
  return stripped;
49765
49764
  }
49766
- var HTML_TAG_RE = /<[a-z][a-z0-9]*(?::[a-z][a-z0-9-]*)?[\s>\/]/i;
49765
+ var HTML_TAG_RE = /<\/?[a-z][a-z0-9]*(?::[a-z][a-z0-9-]*)?[\s>\/]/i;
49766
+ var HTML_ENTITY_RE = /&(?:[a-zA-Z]+|#x?[0-9a-fA-F]+);/;
49767
49767
  function toStorageFormat(body) {
49768
- return HTML_TAG_RE.test(body) ? body : `<p>${body}</p>`;
49768
+ if (HTML_TAG_RE.test(body) || HTML_ENTITY_RE.test(body)) return body;
49769
+ return `<p>${body}</p>`;
49769
49770
  }
49770
49771
  function extractHeadings(storageHtml) {
49771
49772
  const headingRe = /<h([1-6])[^>]*>(.*?)<\/h\1>/gi;
@@ -49946,7 +49947,8 @@ function toMarkdownView(storageHtml) {
49946
49947
  return markdown;
49947
49948
  }
49948
49949
  function looksLikeMarkdown(body) {
49949
- if (/<ac:/i.test(body) || /<ri:/i.test(body)) {
49950
+ const withoutCodeBlocks = body.replace(/^(`{3,})[^\n]*\n[\s\S]*?^\1\s*$/gm, "");
49951
+ if (/<ac:/i.test(withoutCodeBlocks) || /<ri:/i.test(withoutCodeBlocks)) {
49950
49952
  return false;
49951
49953
  }
49952
49954
  const STRONG_MARKDOWN_SIGNALS = [
@@ -49973,7 +49975,11 @@ function looksLikeMarkdown(body) {
49973
49975
  /\*\*[^*]+\*\*/
49974
49976
  // inline bold **text**
49975
49977
  ];
49976
- return STRONG_MARKDOWN_SIGNALS.some((re) => re.test(body));
49978
+ if (STRONG_MARKDOWN_SIGNALS.some((re) => re.test(body))) {
49979
+ return true;
49980
+ }
49981
+ const trimmed = body.trimStart();
49982
+ return !/^<[a-zA-Z]/i.test(trimmed);
49977
49983
  }
49978
49984
  async function formatPage(page, optionsOrIncludeBody) {
49979
49985
  const options2 = typeof optionsOrIncludeBody === "boolean" ? { includeBody: optionsOrIncludeBody } : optionsOrIncludeBody;
@@ -50297,9 +50303,9 @@ function structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, ne
50297
50303
  const hunks = [];
50298
50304
  let oldRangeStart = 0, newRangeStart = 0, curRange = [], oldLine = 1, newLine = 1;
50299
50305
  for (let i = 0; i < diff.length; i++) {
50300
- const current = diff[i], lines = current.lines || splitLines(current.value);
50301
- current.lines = lines;
50302
- if (current.added || current.removed) {
50306
+ const current2 = diff[i], lines = current2.lines || splitLines(current2.value);
50307
+ current2.lines = lines;
50308
+ if (current2.added || current2.removed) {
50303
50309
  if (!oldRangeStart) {
50304
50310
  const prev = diff[i - 1];
50305
50311
  oldRangeStart = oldLine;
@@ -50311,9 +50317,9 @@ function structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, ne
50311
50317
  }
50312
50318
  }
50313
50319
  for (const line of lines) {
50314
- curRange.push((current.added ? "+" : "-") + line);
50320
+ curRange.push((current2.added ? "+" : "-") + line);
50315
50321
  }
50316
- if (current.added) {
50322
+ if (current2.added) {
50317
50323
  newLine += lines.length;
50318
50324
  } else {
50319
50325
  oldLine += lines.length;
@@ -51229,19 +51235,19 @@ var EntityDecoder = class {
51229
51235
  */
51230
51236
  stateNamedEntity(str2, offset) {
51231
51237
  const { decodeTree } = this;
51232
- let current = decodeTree[this.treeIndex];
51233
- let valueLength = (current & BinTrieFlags.VALUE_LENGTH) >> 14;
51238
+ let current2 = decodeTree[this.treeIndex];
51239
+ let valueLength = (current2 & BinTrieFlags.VALUE_LENGTH) >> 14;
51234
51240
  for (; offset < str2.length; offset++, this.excess++) {
51235
51241
  const char = str2.charCodeAt(offset);
51236
- this.treeIndex = determineBranch(decodeTree, current, this.treeIndex + Math.max(1, valueLength), char);
51242
+ this.treeIndex = determineBranch(decodeTree, current2, this.treeIndex + Math.max(1, valueLength), char);
51237
51243
  if (this.treeIndex < 0) {
51238
51244
  return this.result === 0 || // If we are parsing an attribute
51239
51245
  this.decodeMode === DecodingMode.Attribute && // We shouldn't have consumed any characters after the entity,
51240
51246
  (valueLength === 0 || // And there should be no invalid characters.
51241
51247
  isEntityInAttributeInvalidEnd(char)) ? 0 : this.emitNotTerminatedNamedEntity();
51242
51248
  }
51243
- current = decodeTree[this.treeIndex];
51244
- valueLength = (current & BinTrieFlags.VALUE_LENGTH) >> 14;
51249
+ current2 = decodeTree[this.treeIndex];
51250
+ valueLength = (current2 & BinTrieFlags.VALUE_LENGTH) >> 14;
51245
51251
  if (valueLength !== 0) {
51246
51252
  if (char === CharCodes.SEMI) {
51247
51253
  return this.emitNamedEntityData(this.treeIndex, valueLength, this.consumed + this.excess);
@@ -51341,9 +51347,9 @@ function getDecoder(decodeTree) {
51341
51347
  return result;
51342
51348
  };
51343
51349
  }
51344
- function determineBranch(decodeTree, current, nodeIdx, char) {
51345
- const branchCount = (current & BinTrieFlags.BRANCH_LENGTH) >> 7;
51346
- const jumpOffset = current & BinTrieFlags.JUMP_TABLE;
51350
+ function determineBranch(decodeTree, current2, nodeIdx, char) {
51351
+ const branchCount = (current2 & BinTrieFlags.BRANCH_LENGTH) >> 7;
51352
+ const jumpOffset = current2 & BinTrieFlags.JUMP_TABLE;
51347
51353
  if (branchCount === 0) {
51348
51354
  return jumpOffset !== 0 && char === jumpOffset ? nodeIdx : -1;
51349
51355
  }
@@ -52768,15 +52774,15 @@ function escapedSplit(str2) {
52768
52774
  let ch = str2.charCodeAt(pos);
52769
52775
  let isEscaped = false;
52770
52776
  let lastPos = 0;
52771
- let current = "";
52777
+ let current2 = "";
52772
52778
  while (pos < max) {
52773
52779
  if (ch === 124) {
52774
52780
  if (!isEscaped) {
52775
- result.push(current + str2.substring(lastPos, pos));
52776
- current = "";
52781
+ result.push(current2 + str2.substring(lastPos, pos));
52782
+ current2 = "";
52777
52783
  lastPos = pos + 1;
52778
52784
  } else {
52779
- current += str2.substring(lastPos, pos - 1);
52785
+ current2 += str2.substring(lastPos, pos - 1);
52780
52786
  lastPos = pos;
52781
52787
  }
52782
52788
  }
@@ -52784,7 +52790,7 @@ function escapedSplit(str2) {
52784
52790
  pos++;
52785
52791
  ch = str2.charCodeAt(pos);
52786
52792
  }
52787
- result.push(current + str2.substring(lastPos));
52793
+ result.push(current2 + str2.substring(lastPos));
52788
52794
  return result;
52789
52795
  }
52790
52796
  function table(state, startLine, endLine, silent) {
@@ -56724,7 +56730,10 @@ function planUpdate(params) {
56724
56730
  } = params;
56725
56731
  if (replaceBody) {
56726
56732
  const newStorage2 = markdownToStorage(callerMarkdown, converterOptions);
56727
- return { newStorage: newStorage2, deletedTokens: [] };
56733
+ const { sidecar: sidecar2 } = tokeniseStorage(currentStorage);
56734
+ const droppedCount = Object.keys(sidecar2).length;
56735
+ const versionMessage2 = droppedCount > 0 ? `Wholesale rewrite (replace_body): dropped ${droppedCount} preserved element(s)` : void 0;
56736
+ return { newStorage: newStorage2, deletedTokens: [], versionMessage: versionMessage2 };
56728
56737
  }
56729
56738
  const { canonical, sidecar } = tokeniseStorage(currentStorage);
56730
56739
  const diff = diffTokens(canonical, callerMarkdown, sidecar);
@@ -56752,22 +56761,34 @@ function planUpdate(params) {
56752
56761
  }
56753
56762
 
56754
56763
  // src/server/converter/content-safety-guards.ts
56755
- var SHRINKAGE_GUARD_MIN_OLD_LEN = 500;
56764
+ var MACRO_LOSS_NOT_CONFIRMED = "MACRO_LOSS_NOT_CONFIRMED";
56765
+ var TABLE_LOSS_NOT_CONFIRMED = "TABLE_LOSS_NOT_CONFIRMED";
56766
+ var SHRINKAGE_GUARD_MIN_OLD_LEN = 200;
56756
56767
  var SHRINKAGE_GUARD_MAX_RATIO = 0.5;
56757
56768
  var STRUCTURE_GUARD_MIN_OLD_HEADINGS = 3;
56758
56769
  var STRUCTURE_GUARD_MAX_RATIO = 0.5;
56759
- var EMPTY_BODY_MIN_OLD_LEN = 500;
56760
- var EMPTY_BODY_MIN_TEXT_LEN = 100;
56770
+ var EMPTY_BODY_MIN_OLD_LEN = 100;
56771
+ var EMPTY_BODY_MIN_TEXT_LEN = 3;
56761
56772
  var HEADING_RE = /<h[1-6][^>]*>/gi;
56762
56773
  function countHeadings(storage) {
56763
56774
  const cleaned = storage.replace(/<ac:plain-text-body>[\s\S]*?<\/ac:plain-text-body>/g, "").replace(/<!--[\s\S]*?-->/g, "");
56764
56775
  return (cleaned.match(HEADING_RE) || []).length;
56765
56776
  }
56777
+ var STRUCTURED_MACRO_RE = /<ac:structured-macro[\s>]/gi;
56778
+ function countMacros(storage) {
56779
+ const cleaned = storage.replace(/<ac:plain-text-body>[\s\S]*?<\/ac:plain-text-body>/g, "").replace(/<!--[\s\S]*?-->/g, "");
56780
+ return (cleaned.match(STRUCTURED_MACRO_RE) || []).length;
56781
+ }
56782
+ var TABLE_RE = /<table[\s>]/gi;
56783
+ function countTables(storage) {
56784
+ const cleaned = storage.replace(/<ac:plain-text-body>[\s\S]*?<\/ac:plain-text-body>/g, "").replace(/<!--[\s\S]*?-->/g, "");
56785
+ return (cleaned.match(TABLE_RE) || []).length;
56786
+ }
56766
56787
  function extractTextContent(storage) {
56767
- return storage.replace(/<!--[\s\S]*?-->/g, "").replace(/<[^>]*>/g, "").replace(/&[a-zA-Z]+;/g, " ").replace(/&#x?[0-9a-fA-F]+;/g, " ").trim();
56788
+ return storage.replace(/<!--[\s\S]*?-->/g, "").replace(/<[^>]*>/g, "").replace(/&nbsp;/gi, " ").replace(/&[a-zA-Z]+;/g, "_").replace(/&#x?[0-9a-fA-F]+;/g, "_").trim();
56768
56789
  }
56769
56790
  function enforceContentSafetyGuards(input) {
56770
- const { oldStorage, newStorage, confirmShrinkage, confirmStructureLoss } = input;
56791
+ const { oldStorage, newStorage, confirmShrinkage, confirmStructureLoss, confirmDeletions } = input;
56771
56792
  const oldLen = oldStorage.length;
56772
56793
  const newLen = newStorage.length;
56773
56794
  if (oldLen > SHRINKAGE_GUARD_MIN_OLD_LEN && newLen < oldLen * SHRINKAGE_GUARD_MAX_RATIO && !confirmShrinkage) {
@@ -56792,6 +56813,22 @@ function enforceContentSafetyGuards(input) {
56792
56813
  EMPTY_BODY_REJECTED
56793
56814
  );
56794
56815
  }
56816
+ const oldMacros = countMacros(oldStorage);
56817
+ const newMacros = countMacros(newStorage);
56818
+ if (oldMacros > 0 && newMacros === 0 && !confirmShrinkage && !confirmDeletions) {
56819
+ throw new ConverterError(
56820
+ `All ${oldMacros} Confluence macro(s) would be removed from the page. This may indicate accidental content loss (e.g. a lossy markdown round-trip). Re-submit with confirm_shrinkage: true if this is intentional.`,
56821
+ MACRO_LOSS_NOT_CONFIRMED
56822
+ );
56823
+ }
56824
+ const oldTables = countTables(oldStorage);
56825
+ const newTables = countTables(newStorage);
56826
+ if (oldTables > 0 && newTables === 0 && !confirmStructureLoss && !confirmDeletions) {
56827
+ throw new ConverterError(
56828
+ `All ${oldTables} table(s) would be removed from the page. This may indicate accidental content loss. Re-submit with confirm_structure_loss: true if this is intentional.`,
56829
+ TABLE_LOSS_NOT_CONFIRMED
56830
+ );
56831
+ }
56795
56832
  }
56796
56833
 
56797
56834
  // src/server/converter/storage-to-md.ts
@@ -56822,8 +56859,12 @@ function storageToMarkdown(storage) {
56822
56859
  // src/server/mutation-log.ts
56823
56860
  var import_node_fs = require("node:fs");
56824
56861
  var import_node_path2 = require("node:path");
56862
+ var import_node_crypto2 = require("node:crypto");
56825
56863
  var MAX_LOG_AGE_MS = 30 * 24 * 60 * 60 * 1e3;
56826
56864
  var MAX_ERROR_LEN = 200;
56865
+ function bodyHash(body) {
56866
+ return (0, import_node_crypto2.createHash)("sha256").update(body).digest("hex").slice(0, 16);
56867
+ }
56827
56868
  var logPath = null;
56828
56869
  var logFd = null;
56829
56870
  function sanitizeErrorMessage(err) {
@@ -56890,7 +56931,7 @@ function errorRecord(operation, pageId, err, extra) {
56890
56931
  var import_promises2 = require("node:fs/promises");
56891
56932
  var import_node_path3 = require("node:path");
56892
56933
  var import_node_os2 = require("node:os");
56893
- var import_node_crypto2 = require("node:crypto");
56934
+ var import_node_crypto3 = require("node:crypto");
56894
56935
  var import_node_child_process2 = require("node:child_process");
56895
56936
  var import_node_util = require("node:util");
56896
56937
  var execFileAsync = (0, import_node_util.promisify)(import_node_child_process2.execFile);
@@ -56908,11 +56949,11 @@ function parseSemVer(version2) {
56908
56949
  patch: parseInt(match2[3], 10)
56909
56950
  };
56910
56951
  }
56911
- function classifyUpdate(current, latest) {
56912
- if (latest.major > current.major) return "major";
56913
- if (latest.major === current.major && latest.minor > current.minor)
56952
+ function classifyUpdate(current2, latest) {
56953
+ if (latest.major > current2.major) return "major";
56954
+ if (latest.major === current2.major && latest.minor > current2.minor)
56914
56955
  return "minor";
56915
- if (latest.major === current.major && latest.minor === current.minor && latest.patch > current.patch)
56956
+ if (latest.major === current2.major && latest.minor === current2.minor && latest.patch > current2.patch)
56916
56957
  return "patch";
56917
56958
  return null;
56918
56959
  }
@@ -56933,7 +56974,7 @@ async function writeCheckState(state) {
56933
56974
  const data = JSON.stringify(state, null, 2) + "\n";
56934
56975
  const tmpFile = (0, import_node_path3.join)(
56935
56976
  CONFIG_DIR2,
56936
- `.update-check.${(0, import_node_crypto2.randomBytes)(4).toString("hex")}.tmp`
56977
+ `.update-check.${(0, import_node_crypto3.randomBytes)(4).toString("hex")}.tmp`
56937
56978
  );
56938
56979
  await (0, import_promises2.writeFile)(tmpFile, data, { mode: 384 });
56939
56980
  await (0, import_promises2.rename)(tmpFile, UPDATE_CHECK_FILE);
@@ -56984,10 +57025,10 @@ async function checkForUpdates(currentVersion) {
56984
57025
  if (!latestStr) {
56985
57026
  return state?.pendingUpdate ?? null;
56986
57027
  }
56987
- const current = parseSemVer(currentVersion);
57028
+ const current2 = parseSemVer(currentVersion);
56988
57029
  const latest = parseSemVer(latestStr);
56989
- if (!current || !latest) return null;
56990
- const type = classifyUpdate(current, latest);
57030
+ if (!current2 || !latest) return null;
57031
+ const type = classifyUpdate(current2, latest);
56991
57032
  const newState = {
56992
57033
  lastCheck: (/* @__PURE__ */ new Date()).toISOString()
56993
57034
  };
@@ -57207,11 +57248,16 @@ function registerTools(server, config3) {
57207
57248
  throw new Error("Combined body exceeds 2MB limit");
57208
57249
  }
57209
57250
  const newBody = position === "prepend" ? contentStorage + sep + currentStorage : currentStorage + sep + contentStorage;
57251
+ enforceContentSafetyGuards({
57252
+ oldStorage: currentStorage,
57253
+ newStorage: newBody
57254
+ });
57210
57255
  const { page, newVersion } = await updatePage(page_id, {
57211
57256
  title: currentPage.title,
57212
57257
  body: newBody,
57213
57258
  version: version2,
57214
57259
  versionMessage: opts.versionMessage,
57260
+ previousBody: currentStorage,
57215
57261
  clientLabel: getClientLabel(server)
57216
57262
  });
57217
57263
  return { page, newVersion, oldLen: currentStorage.length, newLen: newBody.length };
@@ -57260,7 +57306,9 @@ function registerTools(server, config3) {
57260
57306
  operation: "create_page",
57261
57307
  pageId: page.id,
57262
57308
  newVersion: page.version?.number ?? 1,
57263
- newBodyLen: finalBody.length
57309
+ newBodyLen: finalBody.length,
57310
+ newBodyHash: bodyHash(finalBody),
57311
+ clientLabel: getClientLabel(server)
57264
57312
  });
57265
57313
  return toolResult(await formatPage(page, false) + echo);
57266
57314
  } catch (err) {
@@ -57423,7 +57471,11 @@ ${truncated}`);
57423
57471
  oldStorage: currentStorage,
57424
57472
  newStorage: finalStorage,
57425
57473
  confirmShrinkage: confirm_shrinkage,
57426
- confirmStructureLoss: confirm_structure_loss
57474
+ confirmStructureLoss: confirm_structure_loss,
57475
+ // confirm_deletions and replace_body both indicate the caller
57476
+ // has acknowledged macro/element removal — bypass the macro
57477
+ // and table loss guards, but NOT the shrinkage/structure guards.
57478
+ confirmDeletions: confirm_deletions || replace_body
57427
57479
  });
57428
57480
  }
57429
57481
  const { page, newVersion } = await updatePage(page_id, {
@@ -57442,6 +57494,9 @@ ${truncated}`);
57442
57494
  newVersion,
57443
57495
  oldBodyLen: currentStorage.length,
57444
57496
  newBodyLen: finalStorage?.length ?? currentStorage.length,
57497
+ oldBodyHash: bodyHash(currentStorage),
57498
+ newBodyHash: finalStorage ? bodyHash(finalStorage) : void 0,
57499
+ clientLabel: getClientLabel(server),
57445
57500
  replaceBody: replace_body || void 0
57446
57501
  });
57447
57502
  const bodyReport = finalStorage !== void 0 ? `body: ${currentStorage.length}\u2192${finalStorage.length} chars` : `title only, body unchanged`;
@@ -57517,6 +57572,10 @@ ${truncated}`);
57517
57572
  `Section "${section}" not found. Use headings_only to see available sections.`
57518
57573
  );
57519
57574
  }
57575
+ enforceContentSafetyGuards({
57576
+ oldStorage: fullBody,
57577
+ newStorage: newFullBody
57578
+ });
57520
57579
  const { page: updated, newVersion } = await updatePage(page_id, {
57521
57580
  title: page.title,
57522
57581
  body: newFullBody,
@@ -57532,7 +57591,10 @@ ${truncated}`);
57532
57591
  oldVersion: version2,
57533
57592
  newVersion,
57534
57593
  oldBodyLen: fullBody.length,
57535
- newBodyLen: newFullBody.length
57594
+ newBodyLen: newFullBody.length,
57595
+ oldBodyHash: bodyHash(fullBody),
57596
+ newBodyHash: bodyHash(newFullBody),
57597
+ clientLabel: getClientLabel(server)
57536
57598
  });
57537
57599
  return toolResult(
57538
57600
  `Updated section "${section}" in: ${updated.title} (ID: ${updated.id}, version: ${newVersion})` + echo
@@ -57919,21 +57981,41 @@ ${truncated}`);
57919
57981
  ` <ac:parameter ac:name="contentVer">1</ac:parameter>`,
57920
57982
  `</ac:structured-macro>`
57921
57983
  ].join("\n");
57922
- const current = await getPage(page_id, true);
57923
- const existingBody = current.body?.storage?.value ?? current.body?.value ?? "";
57984
+ const current2 = await getPage(page_id, true);
57985
+ const existingBody = current2.body?.storage?.value ?? current2.body?.value ?? "";
57924
57986
  const newBody = append ? `${existingBody}
57925
57987
  ${macro}` : macro;
57988
+ enforceContentSafetyGuards({
57989
+ oldStorage: existingBody,
57990
+ newStorage: newBody
57991
+ });
57926
57992
  const { page, newVersion } = await updatePage(page_id, {
57927
- title: current.title,
57993
+ title: current2.title,
57928
57994
  body: newBody,
57929
- version: current.version?.number ?? 0,
57995
+ version: current2.version?.number ?? 0,
57930
57996
  versionMessage: `Added diagram: ${filename}`,
57997
+ previousBody: existingBody,
57998
+ clientLabel: getClientLabel(server)
57999
+ });
58000
+ logMutation({
58001
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
58002
+ operation: "update_page",
58003
+ pageId: page_id,
58004
+ oldVersion: current2.version?.number ?? 0,
58005
+ newVersion,
58006
+ oldBodyLen: existingBody.length,
58007
+ newBodyLen: newBody.length,
58008
+ oldBodyHash: bodyHash(existingBody),
58009
+ newBodyHash: bodyHash(newBody),
57931
58010
  clientLabel: getClientLabel(server)
57932
58011
  });
57933
58012
  return toolResult(
57934
58013
  `Diagram "${filename}" added to page ${page.title} (ID: ${page.id}, version: ${newVersion})` + echo
57935
58014
  );
57936
58015
  } catch (err) {
58016
+ logMutation(errorRecord("update_page", page_id, err, {
58017
+ oldVersion: current.version?.number ?? 0
58018
+ }));
57937
58019
  return toolError(err);
57938
58020
  }
57939
58021
  }
@@ -58448,6 +58530,7 @@ ${result.diff}${truncNote}` + echo
58448
58530
  body: historical.rawBody,
58449
58531
  version: current_version,
58450
58532
  versionMessage: version_message ?? `Revert to version ${target_version}`,
58533
+ previousBody: currentStorage,
58451
58534
  clientLabel: getClientLabel(server)
58452
58535
  });
58453
58536
  logMutation({
@@ -58457,7 +58540,10 @@ ${result.diff}${truncNote}` + echo
58457
58540
  oldVersion: current_version,
58458
58541
  newVersion,
58459
58542
  oldBodyLen: currentStorage.length,
58460
- newBodyLen: historical.rawBody.length
58543
+ newBodyLen: historical.rawBody.length,
58544
+ oldBodyHash: bodyHash(currentStorage),
58545
+ newBodyHash: bodyHash(historical.rawBody),
58546
+ clientLabel: getClientLabel(server)
58461
58547
  });
58462
58548
  return toolResult(
58463
58549
  `Reverted: ${page.title} (ID: ${page.id}, v${target_version}\u2192v${newVersion}, body: ${currentStorage.length}\u2192${historical.rawBody.length} chars)` + echo
@@ -58540,7 +58626,7 @@ ${lines.join("\n")}${echo2}`
58540
58626
  inputSchema: {}
58541
58627
  },
58542
58628
  async () => {
58543
- let text2 = `epimethian-mcp v${"5.3.2"}`;
58629
+ let text2 = `epimethian-mcp v${"5.3.3"}`;
58544
58630
  try {
58545
58631
  const pending = await getPendingUpdate();
58546
58632
  if (pending) {
@@ -58570,7 +58656,7 @@ ${pending.type === "major" ? "Major" : "Minor"} update available: v${pending.cur
58570
58656
  const pending = await getPendingUpdate();
58571
58657
  if (!pending) {
58572
58658
  return toolResult(
58573
- `epimethian-mcp v${"5.3.2"} is already up to date.`
58659
+ `epimethian-mcp v${"5.3.3"} is already up to date.`
58574
58660
  );
58575
58661
  }
58576
58662
  const output = await performUpgrade(pending.latest);
@@ -58598,12 +58684,12 @@ async function main() {
58598
58684
  const serverName = config3.profile ? `confluence-${config3.profile}` : "confluence";
58599
58685
  const server = new McpServer({
58600
58686
  name: serverName,
58601
- version: "5.3.2"
58687
+ version: "5.3.3"
58602
58688
  });
58603
58689
  registerTools(server, config3);
58604
58690
  const transport = new StdioServerTransport();
58605
58691
  await server.connect(transport);
58606
- checkForUpdates("5.3.2").catch(() => {
58692
+ checkForUpdates("5.3.3").catch(() => {
58607
58693
  });
58608
58694
  }
58609
58695