@de-otio/epimethian-mcp 5.3.1 → 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.1"}`;
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.1"}`;
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 {
@@ -49580,7 +49579,10 @@ function stripAttributionFooter(body) {
49580
49579
  // Also strip bare (unmarked) attribution paragraphs — these appear
49581
49580
  // when an agent copies page content from get_page and passes it
49582
49581
  // back to update_page without removing the footer.
49583
- /<p[^>]*>[\s\S]*?<a\s[^>]*href="https:\/\/github\.com\/de-otio\/epimethian-mcp"[^>]*>(?:<em>)?Epimethian(?:<\/em>)?<\/a>[\s\S]*?<\/p>/gi,
49582
+ // Use (?:(?!<\/p>)[\s\S])*? instead of [\s\S]*? to prevent crossing
49583
+ // </p> boundaries — without this, the match spans from the first <p>
49584
+ // in the document to the attribution link, wiping the entire body.
49585
+ /<p[^>]*>(?:(?!<\/p>)[\s\S])*?<a\s[^>]*href="https:\/\/github\.com\/de-otio\/epimethian-mcp"[^>]*>(?:<em>)?Epimethian(?:<\/em>)?<\/a>(?:(?!<\/p>)[\s\S])*?<\/p>/gi,
49584
49586
  ""
49585
49587
  ).trimEnd();
49586
49588
  }
@@ -49760,9 +49762,11 @@ function sanitizeCommentBody(body) {
49760
49762
  }
49761
49763
  return stripped;
49762
49764
  }
49763
- 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]+);/;
49764
49767
  function toStorageFormat(body) {
49765
- 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>`;
49766
49770
  }
49767
49771
  function extractHeadings(storageHtml) {
49768
49772
  const headingRe = /<h([1-6])[^>]*>(.*?)<\/h\1>/gi;
@@ -49943,7 +49947,8 @@ function toMarkdownView(storageHtml) {
49943
49947
  return markdown;
49944
49948
  }
49945
49949
  function looksLikeMarkdown(body) {
49946
- 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)) {
49947
49952
  return false;
49948
49953
  }
49949
49954
  const STRONG_MARKDOWN_SIGNALS = [
@@ -49970,7 +49975,11 @@ function looksLikeMarkdown(body) {
49970
49975
  /\*\*[^*]+\*\*/
49971
49976
  // inline bold **text**
49972
49977
  ];
49973
- 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);
49974
49983
  }
49975
49984
  async function formatPage(page, optionsOrIncludeBody) {
49976
49985
  const options2 = typeof optionsOrIncludeBody === "boolean" ? { includeBody: optionsOrIncludeBody } : optionsOrIncludeBody;
@@ -50294,9 +50303,9 @@ function structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, ne
50294
50303
  const hunks = [];
50295
50304
  let oldRangeStart = 0, newRangeStart = 0, curRange = [], oldLine = 1, newLine = 1;
50296
50305
  for (let i = 0; i < diff.length; i++) {
50297
- const current = diff[i], lines = current.lines || splitLines(current.value);
50298
- current.lines = lines;
50299
- 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) {
50300
50309
  if (!oldRangeStart) {
50301
50310
  const prev = diff[i - 1];
50302
50311
  oldRangeStart = oldLine;
@@ -50308,9 +50317,9 @@ function structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, ne
50308
50317
  }
50309
50318
  }
50310
50319
  for (const line of lines) {
50311
- curRange.push((current.added ? "+" : "-") + line);
50320
+ curRange.push((current2.added ? "+" : "-") + line);
50312
50321
  }
50313
- if (current.added) {
50322
+ if (current2.added) {
50314
50323
  newLine += lines.length;
50315
50324
  } else {
50316
50325
  oldLine += lines.length;
@@ -51226,19 +51235,19 @@ var EntityDecoder = class {
51226
51235
  */
51227
51236
  stateNamedEntity(str2, offset) {
51228
51237
  const { decodeTree } = this;
51229
- let current = decodeTree[this.treeIndex];
51230
- let valueLength = (current & BinTrieFlags.VALUE_LENGTH) >> 14;
51238
+ let current2 = decodeTree[this.treeIndex];
51239
+ let valueLength = (current2 & BinTrieFlags.VALUE_LENGTH) >> 14;
51231
51240
  for (; offset < str2.length; offset++, this.excess++) {
51232
51241
  const char = str2.charCodeAt(offset);
51233
- 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);
51234
51243
  if (this.treeIndex < 0) {
51235
51244
  return this.result === 0 || // If we are parsing an attribute
51236
51245
  this.decodeMode === DecodingMode.Attribute && // We shouldn't have consumed any characters after the entity,
51237
51246
  (valueLength === 0 || // And there should be no invalid characters.
51238
51247
  isEntityInAttributeInvalidEnd(char)) ? 0 : this.emitNotTerminatedNamedEntity();
51239
51248
  }
51240
- current = decodeTree[this.treeIndex];
51241
- valueLength = (current & BinTrieFlags.VALUE_LENGTH) >> 14;
51249
+ current2 = decodeTree[this.treeIndex];
51250
+ valueLength = (current2 & BinTrieFlags.VALUE_LENGTH) >> 14;
51242
51251
  if (valueLength !== 0) {
51243
51252
  if (char === CharCodes.SEMI) {
51244
51253
  return this.emitNamedEntityData(this.treeIndex, valueLength, this.consumed + this.excess);
@@ -51338,9 +51347,9 @@ function getDecoder(decodeTree) {
51338
51347
  return result;
51339
51348
  };
51340
51349
  }
51341
- function determineBranch(decodeTree, current, nodeIdx, char) {
51342
- const branchCount = (current & BinTrieFlags.BRANCH_LENGTH) >> 7;
51343
- 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;
51344
51353
  if (branchCount === 0) {
51345
51354
  return jumpOffset !== 0 && char === jumpOffset ? nodeIdx : -1;
51346
51355
  }
@@ -52765,15 +52774,15 @@ function escapedSplit(str2) {
52765
52774
  let ch = str2.charCodeAt(pos);
52766
52775
  let isEscaped = false;
52767
52776
  let lastPos = 0;
52768
- let current = "";
52777
+ let current2 = "";
52769
52778
  while (pos < max) {
52770
52779
  if (ch === 124) {
52771
52780
  if (!isEscaped) {
52772
- result.push(current + str2.substring(lastPos, pos));
52773
- current = "";
52781
+ result.push(current2 + str2.substring(lastPos, pos));
52782
+ current2 = "";
52774
52783
  lastPos = pos + 1;
52775
52784
  } else {
52776
- current += str2.substring(lastPos, pos - 1);
52785
+ current2 += str2.substring(lastPos, pos - 1);
52777
52786
  lastPos = pos;
52778
52787
  }
52779
52788
  }
@@ -52781,7 +52790,7 @@ function escapedSplit(str2) {
52781
52790
  pos++;
52782
52791
  ch = str2.charCodeAt(pos);
52783
52792
  }
52784
- result.push(current + str2.substring(lastPos));
52793
+ result.push(current2 + str2.substring(lastPos));
52785
52794
  return result;
52786
52795
  }
52787
52796
  function table(state, startLine, endLine, silent) {
@@ -56721,7 +56730,10 @@ function planUpdate(params) {
56721
56730
  } = params;
56722
56731
  if (replaceBody) {
56723
56732
  const newStorage2 = markdownToStorage(callerMarkdown, converterOptions);
56724
- 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 };
56725
56737
  }
56726
56738
  const { canonical, sidecar } = tokeniseStorage(currentStorage);
56727
56739
  const diff = diffTokens(canonical, callerMarkdown, sidecar);
@@ -56749,22 +56761,34 @@ function planUpdate(params) {
56749
56761
  }
56750
56762
 
56751
56763
  // src/server/converter/content-safety-guards.ts
56752
- 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;
56753
56767
  var SHRINKAGE_GUARD_MAX_RATIO = 0.5;
56754
56768
  var STRUCTURE_GUARD_MIN_OLD_HEADINGS = 3;
56755
56769
  var STRUCTURE_GUARD_MAX_RATIO = 0.5;
56756
- var EMPTY_BODY_MIN_OLD_LEN = 500;
56757
- var EMPTY_BODY_MIN_TEXT_LEN = 100;
56770
+ var EMPTY_BODY_MIN_OLD_LEN = 100;
56771
+ var EMPTY_BODY_MIN_TEXT_LEN = 3;
56758
56772
  var HEADING_RE = /<h[1-6][^>]*>/gi;
56759
56773
  function countHeadings(storage) {
56760
56774
  const cleaned = storage.replace(/<ac:plain-text-body>[\s\S]*?<\/ac:plain-text-body>/g, "").replace(/<!--[\s\S]*?-->/g, "");
56761
56775
  return (cleaned.match(HEADING_RE) || []).length;
56762
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
+ }
56763
56787
  function extractTextContent(storage) {
56764
- 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();
56765
56789
  }
56766
56790
  function enforceContentSafetyGuards(input) {
56767
- const { oldStorage, newStorage, confirmShrinkage, confirmStructureLoss } = input;
56791
+ const { oldStorage, newStorage, confirmShrinkage, confirmStructureLoss, confirmDeletions } = input;
56768
56792
  const oldLen = oldStorage.length;
56769
56793
  const newLen = newStorage.length;
56770
56794
  if (oldLen > SHRINKAGE_GUARD_MIN_OLD_LEN && newLen < oldLen * SHRINKAGE_GUARD_MAX_RATIO && !confirmShrinkage) {
@@ -56789,6 +56813,22 @@ function enforceContentSafetyGuards(input) {
56789
56813
  EMPTY_BODY_REJECTED
56790
56814
  );
56791
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
+ }
56792
56832
  }
56793
56833
 
56794
56834
  // src/server/converter/storage-to-md.ts
@@ -56819,8 +56859,12 @@ function storageToMarkdown(storage) {
56819
56859
  // src/server/mutation-log.ts
56820
56860
  var import_node_fs = require("node:fs");
56821
56861
  var import_node_path2 = require("node:path");
56862
+ var import_node_crypto2 = require("node:crypto");
56822
56863
  var MAX_LOG_AGE_MS = 30 * 24 * 60 * 60 * 1e3;
56823
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
+ }
56824
56868
  var logPath = null;
56825
56869
  var logFd = null;
56826
56870
  function sanitizeErrorMessage(err) {
@@ -56887,7 +56931,7 @@ function errorRecord(operation, pageId, err, extra) {
56887
56931
  var import_promises2 = require("node:fs/promises");
56888
56932
  var import_node_path3 = require("node:path");
56889
56933
  var import_node_os2 = require("node:os");
56890
- var import_node_crypto2 = require("node:crypto");
56934
+ var import_node_crypto3 = require("node:crypto");
56891
56935
  var import_node_child_process2 = require("node:child_process");
56892
56936
  var import_node_util = require("node:util");
56893
56937
  var execFileAsync = (0, import_node_util.promisify)(import_node_child_process2.execFile);
@@ -56905,11 +56949,11 @@ function parseSemVer(version2) {
56905
56949
  patch: parseInt(match2[3], 10)
56906
56950
  };
56907
56951
  }
56908
- function classifyUpdate(current, latest) {
56909
- if (latest.major > current.major) return "major";
56910
- 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)
56911
56955
  return "minor";
56912
- 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)
56913
56957
  return "patch";
56914
56958
  return null;
56915
56959
  }
@@ -56930,7 +56974,7 @@ async function writeCheckState(state) {
56930
56974
  const data = JSON.stringify(state, null, 2) + "\n";
56931
56975
  const tmpFile = (0, import_node_path3.join)(
56932
56976
  CONFIG_DIR2,
56933
- `.update-check.${(0, import_node_crypto2.randomBytes)(4).toString("hex")}.tmp`
56977
+ `.update-check.${(0, import_node_crypto3.randomBytes)(4).toString("hex")}.tmp`
56934
56978
  );
56935
56979
  await (0, import_promises2.writeFile)(tmpFile, data, { mode: 384 });
56936
56980
  await (0, import_promises2.rename)(tmpFile, UPDATE_CHECK_FILE);
@@ -56981,10 +57025,10 @@ async function checkForUpdates(currentVersion) {
56981
57025
  if (!latestStr) {
56982
57026
  return state?.pendingUpdate ?? null;
56983
57027
  }
56984
- const current = parseSemVer(currentVersion);
57028
+ const current2 = parseSemVer(currentVersion);
56985
57029
  const latest = parseSemVer(latestStr);
56986
- if (!current || !latest) return null;
56987
- const type = classifyUpdate(current, latest);
57030
+ if (!current2 || !latest) return null;
57031
+ const type = classifyUpdate(current2, latest);
56988
57032
  const newState = {
56989
57033
  lastCheck: (/* @__PURE__ */ new Date()).toISOString()
56990
57034
  };
@@ -57204,11 +57248,16 @@ function registerTools(server, config3) {
57204
57248
  throw new Error("Combined body exceeds 2MB limit");
57205
57249
  }
57206
57250
  const newBody = position === "prepend" ? contentStorage + sep + currentStorage : currentStorage + sep + contentStorage;
57251
+ enforceContentSafetyGuards({
57252
+ oldStorage: currentStorage,
57253
+ newStorage: newBody
57254
+ });
57207
57255
  const { page, newVersion } = await updatePage(page_id, {
57208
57256
  title: currentPage.title,
57209
57257
  body: newBody,
57210
57258
  version: version2,
57211
57259
  versionMessage: opts.versionMessage,
57260
+ previousBody: currentStorage,
57212
57261
  clientLabel: getClientLabel(server)
57213
57262
  });
57214
57263
  return { page, newVersion, oldLen: currentStorage.length, newLen: newBody.length };
@@ -57257,7 +57306,9 @@ function registerTools(server, config3) {
57257
57306
  operation: "create_page",
57258
57307
  pageId: page.id,
57259
57308
  newVersion: page.version?.number ?? 1,
57260
- newBodyLen: finalBody.length
57309
+ newBodyLen: finalBody.length,
57310
+ newBodyHash: bodyHash(finalBody),
57311
+ clientLabel: getClientLabel(server)
57261
57312
  });
57262
57313
  return toolResult(await formatPage(page, false) + echo);
57263
57314
  } catch (err) {
@@ -57420,7 +57471,11 @@ ${truncated}`);
57420
57471
  oldStorage: currentStorage,
57421
57472
  newStorage: finalStorage,
57422
57473
  confirmShrinkage: confirm_shrinkage,
57423
- 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
57424
57479
  });
57425
57480
  }
57426
57481
  const { page, newVersion } = await updatePage(page_id, {
@@ -57439,6 +57494,9 @@ ${truncated}`);
57439
57494
  newVersion,
57440
57495
  oldBodyLen: currentStorage.length,
57441
57496
  newBodyLen: finalStorage?.length ?? currentStorage.length,
57497
+ oldBodyHash: bodyHash(currentStorage),
57498
+ newBodyHash: finalStorage ? bodyHash(finalStorage) : void 0,
57499
+ clientLabel: getClientLabel(server),
57442
57500
  replaceBody: replace_body || void 0
57443
57501
  });
57444
57502
  const bodyReport = finalStorage !== void 0 ? `body: ${currentStorage.length}\u2192${finalStorage.length} chars` : `title only, body unchanged`;
@@ -57514,6 +57572,10 @@ ${truncated}`);
57514
57572
  `Section "${section}" not found. Use headings_only to see available sections.`
57515
57573
  );
57516
57574
  }
57575
+ enforceContentSafetyGuards({
57576
+ oldStorage: fullBody,
57577
+ newStorage: newFullBody
57578
+ });
57517
57579
  const { page: updated, newVersion } = await updatePage(page_id, {
57518
57580
  title: page.title,
57519
57581
  body: newFullBody,
@@ -57529,7 +57591,10 @@ ${truncated}`);
57529
57591
  oldVersion: version2,
57530
57592
  newVersion,
57531
57593
  oldBodyLen: fullBody.length,
57532
- newBodyLen: newFullBody.length
57594
+ newBodyLen: newFullBody.length,
57595
+ oldBodyHash: bodyHash(fullBody),
57596
+ newBodyHash: bodyHash(newFullBody),
57597
+ clientLabel: getClientLabel(server)
57533
57598
  });
57534
57599
  return toolResult(
57535
57600
  `Updated section "${section}" in: ${updated.title} (ID: ${updated.id}, version: ${newVersion})` + echo
@@ -57916,21 +57981,41 @@ ${truncated}`);
57916
57981
  ` <ac:parameter ac:name="contentVer">1</ac:parameter>`,
57917
57982
  `</ac:structured-macro>`
57918
57983
  ].join("\n");
57919
- const current = await getPage(page_id, true);
57920
- 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 ?? "";
57921
57986
  const newBody = append ? `${existingBody}
57922
57987
  ${macro}` : macro;
57988
+ enforceContentSafetyGuards({
57989
+ oldStorage: existingBody,
57990
+ newStorage: newBody
57991
+ });
57923
57992
  const { page, newVersion } = await updatePage(page_id, {
57924
- title: current.title,
57993
+ title: current2.title,
57925
57994
  body: newBody,
57926
- version: current.version?.number ?? 0,
57995
+ version: current2.version?.number ?? 0,
57927
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),
57928
58010
  clientLabel: getClientLabel(server)
57929
58011
  });
57930
58012
  return toolResult(
57931
58013
  `Diagram "${filename}" added to page ${page.title} (ID: ${page.id}, version: ${newVersion})` + echo
57932
58014
  );
57933
58015
  } catch (err) {
58016
+ logMutation(errorRecord("update_page", page_id, err, {
58017
+ oldVersion: current.version?.number ?? 0
58018
+ }));
57934
58019
  return toolError(err);
57935
58020
  }
57936
58021
  }
@@ -58445,6 +58530,7 @@ ${result.diff}${truncNote}` + echo
58445
58530
  body: historical.rawBody,
58446
58531
  version: current_version,
58447
58532
  versionMessage: version_message ?? `Revert to version ${target_version}`,
58533
+ previousBody: currentStorage,
58448
58534
  clientLabel: getClientLabel(server)
58449
58535
  });
58450
58536
  logMutation({
@@ -58454,7 +58540,10 @@ ${result.diff}${truncNote}` + echo
58454
58540
  oldVersion: current_version,
58455
58541
  newVersion,
58456
58542
  oldBodyLen: currentStorage.length,
58457
- newBodyLen: historical.rawBody.length
58543
+ newBodyLen: historical.rawBody.length,
58544
+ oldBodyHash: bodyHash(currentStorage),
58545
+ newBodyHash: bodyHash(historical.rawBody),
58546
+ clientLabel: getClientLabel(server)
58458
58547
  });
58459
58548
  return toolResult(
58460
58549
  `Reverted: ${page.title} (ID: ${page.id}, v${target_version}\u2192v${newVersion}, body: ${currentStorage.length}\u2192${historical.rawBody.length} chars)` + echo
@@ -58537,7 +58626,7 @@ ${lines.join("\n")}${echo2}`
58537
58626
  inputSchema: {}
58538
58627
  },
58539
58628
  async () => {
58540
- let text2 = `epimethian-mcp v${"5.3.1"}`;
58629
+ let text2 = `epimethian-mcp v${"5.3.3"}`;
58541
58630
  try {
58542
58631
  const pending = await getPendingUpdate();
58543
58632
  if (pending) {
@@ -58567,7 +58656,7 @@ ${pending.type === "major" ? "Major" : "Minor"} update available: v${pending.cur
58567
58656
  const pending = await getPendingUpdate();
58568
58657
  if (!pending) {
58569
58658
  return toolResult(
58570
- `epimethian-mcp v${"5.3.1"} is already up to date.`
58659
+ `epimethian-mcp v${"5.3.3"} is already up to date.`
58571
58660
  );
58572
58661
  }
58573
58662
  const output = await performUpgrade(pending.latest);
@@ -58595,12 +58684,12 @@ async function main() {
58595
58684
  const serverName = config3.profile ? `confluence-${config3.profile}` : "confluence";
58596
58685
  const server = new McpServer({
58597
58686
  name: serverName,
58598
- version: "5.3.1"
58687
+ version: "5.3.3"
58599
58688
  });
58600
58689
  registerTools(server, config3);
58601
58690
  const transport = new StdioServerTransport();
58602
58691
  await server.connect(transport);
58603
- checkForUpdates("5.3.1").catch(() => {
58692
+ checkForUpdates("5.3.3").catch(() => {
58604
58693
  });
58605
58694
  }
58606
58695