apple-notes-mcp 2.15.3 → 2.15.5

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/build/index.js +498 -94
  2. package/package.json +1 -1
package/build/index.js CHANGED
@@ -40498,6 +40498,87 @@ function styleValue(field) {
40498
40498
  }
40499
40499
  return Buffer.from(field.value).toString("hex");
40500
40500
  }
40501
+ function storedStyleSignature(data) {
40502
+ return JSON.stringify(
40503
+ decodeWireFields(data).filter((field) => field.fieldNumber !== 1).map((field) => [
40504
+ field.fieldNumber,
40505
+ field.wireType,
40506
+ field.bytes ? styleValue({
40507
+ fieldNumber: field.fieldNumber,
40508
+ wireType: field.wireType,
40509
+ value: field.bytes
40510
+ }) : field.varint.toString()
40511
+ ])
40512
+ );
40513
+ }
40514
+ function nativeRunSemantics(data) {
40515
+ const result = {
40516
+ complete: true,
40517
+ unknown: false,
40518
+ structuredParagraph: false,
40519
+ links: false,
40520
+ objects: []
40521
+ };
40522
+ const check2 = (fields, schema) => {
40523
+ const seen = /* @__PURE__ */ new Set();
40524
+ for (const field of fields) {
40525
+ if (!Object.hasOwn(schema, field.fieldNumber)) result.unknown = true;
40526
+ else if (seen.has(field.fieldNumber) || field.wireType !== schema[field.fieldNumber])
40527
+ result.complete = false;
40528
+ seen.add(field.fieldNumber);
40529
+ }
40530
+ };
40531
+ const nested = (field) => {
40532
+ if (field.wireType !== 2 || !field.bytes) {
40533
+ result.complete = false;
40534
+ return [];
40535
+ }
40536
+ return decodeWireFields(field.bytes);
40537
+ };
40538
+ try {
40539
+ const fields = decodeWireFields(data);
40540
+ check2(fields, { 1: 0, 2: 2, 3: 2, 5: 0, 6: 0, 7: 0, 8: 0, 9: 2, 10: 2, 12: 2, 14: 0 });
40541
+ if (fields.filter((f) => f.fieldNumber === 1).length !== 1) result.complete = false;
40542
+ result.links = fields.some((f) => f.fieldNumber === 9);
40543
+ for (const field of fields) {
40544
+ if (field.fieldNumber === 2) {
40545
+ const paragraph = nested(field);
40546
+ check2(paragraph, { 1: 0, 2: 0, 4: 0, 5: 2, 8: 0, 9: 2 });
40547
+ for (const style of paragraph.filter((f) => f.fieldNumber === 1)) {
40548
+ const type = style.varint === void 0 ? void 0 : Number(BigInt.asIntN(64, style.varint));
40549
+ if (type !== void 0 && type >= 100 && type <= 103) result.structuredParagraph = true;
40550
+ else if (type === void 0 || ![-1, 0, 1, 2, 3, 4].includes(type)) result.unknown = true;
40551
+ }
40552
+ for (const uuid2 of paragraph.filter((f) => f.fieldNumber === 9))
40553
+ if (uuid2.bytes?.length !== 16) result.complete = false;
40554
+ for (const todo of paragraph.filter((f) => f.fieldNumber === 5)) {
40555
+ result.structuredParagraph = true;
40556
+ const item = nested(todo);
40557
+ check2(item, { 1: 2, 2: 0 });
40558
+ const id2 = item.filter((f) => f.fieldNumber === 1);
40559
+ if (id2.length !== 1 || id2[0].bytes?.length !== 16) result.complete = false;
40560
+ }
40561
+ } else if (field.fieldNumber === 3) check2(nested(field), { 1: 2, 2: 5, 3: 0 });
40562
+ else if (field.fieldNumber === 10) check2(nested(field), { 1: 5, 2: 5, 3: 5, 4: 5 });
40563
+ else if (field.fieldNumber === 12) {
40564
+ const attachment = nested(field);
40565
+ check2(attachment, { 1: 2, 2: 2 });
40566
+ const ids = attachment.filter((f) => f.fieldNumber === 1), types = attachment.filter((f) => f.fieldNumber === 2);
40567
+ if (ids.length !== 1 || types.length !== 1 || !ids[0].bytes || !types[0].bytes) {
40568
+ result.complete = false;
40569
+ continue;
40570
+ }
40571
+ const utf82 = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true });
40572
+ const id2 = utf82.decode(ids[0].bytes), type = utf82.decode(types[0].bytes);
40573
+ if (!id2 || !type) result.complete = false;
40574
+ result.objects.push({ id: id2, type });
40575
+ }
40576
+ }
40577
+ } catch {
40578
+ result.complete = false;
40579
+ }
40580
+ return result;
40581
+ }
40501
40582
  function parseRichNote(data, nativeTags = [], options = {}) {
40502
40583
  const doc = decodeMessage(data);
40503
40584
  const wrapper = embeddedMessage(getField(doc, 2));
@@ -40526,9 +40607,8 @@ function parseRichNote(data, nativeTags = [], options = {}) {
40526
40607
  highlight: Boolean(varintValue(getField(fields, 14))),
40527
40608
  start: position,
40528
40609
  length,
40529
- signature: JSON.stringify(
40530
- fields.filter((f) => f.fieldNumber >= 2 && f.fieldNumber <= 12 || f.fieldNumber === 14).map((f) => [f.fieldNumber, styleValue(f)])
40531
- )
40610
+ signature: storedStyleSignature(run.value),
40611
+ nativeSemantics: nativeRunSemantics(run.value)
40532
40612
  });
40533
40613
  if (/[^\s\ufffc]/u.test(text2.slice(position, position + length))) {
40534
40614
  const baseline = varintValue(getField(fields, 8)) ?? 0;
@@ -40596,7 +40676,7 @@ function parseRichNote(data, nativeTags = [], options = {}) {
40596
40676
  function readRichNote(id2, options = {}) {
40597
40677
  const pk = /^x-coredata:\/\/[0-9a-f-]+\/ICNote\/p([0-9]+)$/i.exec(id2)?.[1];
40598
40678
  if (!pk) throw new Error("Invalid exact note ID");
40599
- const sql = `BEGIN; SELECT hex(ZDATA) FROM ZICNOTEDATA WHERE ZNOTE=${pk}; SELECT json_group_object(ZIDENTIFIER,ZALTTEXT) FROM ZICCLOUDSYNCINGOBJECT WHERE ZNOTE1=${pk} AND ZTYPEUTI1='com.apple.notes.inlinetextattachment.hashtag'; SELECT json_group_array(json_object('id',ZIDENTIFIER,'pk',Z_PK,'type',COALESCE(ZTYPEUTI1,ZTYPEUTI),'mergeable',hex(COALESCE(ZMERGEABLEDATA1,ZMERGEABLEDATA)),'view',ZATTACHMENTVIEWTYPE)) FROM ZICCLOUDSYNCINGOBJECT WHERE ZNOTE1=${pk} OR ZNOTE=${pk}; COMMIT;`;
40679
+ const sql = `BEGIN; SELECT hex(ZDATA) FROM ZICNOTEDATA WHERE ZNOTE=${pk}; SELECT json_group_object(ZIDENTIFIER,ZALTTEXT) FROM ZICCLOUDSYNCINGOBJECT WHERE ZNOTE1=${pk} AND ZTYPEUTI1='com.apple.notes.inlinetextattachment.hashtag'; SELECT json_group_array(json_object('id',ZIDENTIFIER,'pk',Z_PK,'type',COALESCE(ZTYPEUTI1,ZTYPEUTI),'mergeable',hex(COALESCE(ZMERGEABLEDATA1,ZMERGEABLEDATA)),'view',ZATTACHMENTVIEWTYPE,'altText',ZALTTEXT)) FROM ZICCLOUDSYNCINGOBJECT WHERE ZNOTE1=${pk} OR ZNOTE=${pk}; COMMIT;`;
40600
40680
  const rows = execFileSync4("/usr/bin/sqlite3", ["-readonly", dbPath, sql], {
40601
40681
  encoding: "utf8",
40602
40682
  timeout: 5e3,
@@ -40615,14 +40695,21 @@ function readRichNote(id2, options = {}) {
40615
40695
  const tagMap = tags;
40616
40696
  const objectData = JSON.parse(rows[2] || "[]");
40617
40697
  if (!Array.isArray(objectData) || objectData.some(
40618
- (row) => !row || typeof row.id !== "string" || !Number.isInteger(row.pk) || typeof row.mergeable !== "string" || !/^[0-9a-f]*$/i.test(row.mergeable)
40698
+ (row) => !row || typeof row.id !== "string" || !Number.isInteger(row.pk) || typeof row.mergeable !== "string" || !/^[0-9a-f]*$/i.test(row.mergeable) || row.altText !== void 0 && row.altText !== null && typeof row.altText !== "string"
40619
40699
  ))
40620
40700
  throw new Error("Invalid native object metadata");
40621
40701
  rich.objectData = uniqueById(
40622
40702
  objectData.filter((row) => rich.nativeObjectIds.includes(row.id)).sort((a, b) => a.id.localeCompare(b.id))
40623
40703
  );
40624
- rich.revision = createHash("sha256").update(rich.revision).update(JSON.stringify(rich.objectData)).digest("hex");
40625
- rich.nativeTagObjectIds = {};
40704
+ const selectedRows = new Map(rich.objectData.map((row) => [row.id, row]));
40705
+ rich.nativeObjectDataComplete = objectData.every((candidate) => {
40706
+ const row = selectedRows.get(candidate.id);
40707
+ return !row || ["pk", "type", "mergeable", "view", "altText"].every(
40708
+ (key) => candidate[key] === row[key]
40709
+ );
40710
+ });
40711
+ rich.revision = createHash("sha256").update(rich.revision).update(JSON.stringify(rich.objectData)).update(JSON.stringify(rich.nativeObjectDataComplete)).digest("hex");
40712
+ rich.nativeTagObjectIds = /* @__PURE__ */ Object.create(null);
40626
40713
  for (const id3 of rich.nativeObjectIds)
40627
40714
  if (tagMap[id3]) {
40628
40715
  const tag = tagMap[id3].replace(/^#/, "");
@@ -48626,6 +48713,188 @@ import { mkdtempSync as mkdtempSync3, writeFileSync as writeFileSync2, rmSync as
48626
48713
  import { tmpdir as tmpdir3 } from "node:os";
48627
48714
  import { join as join17 } from "node:path";
48628
48715
 
48716
+ // src/utils/richContentPreservation.ts
48717
+ var TAG_OBJECT_TYPE = "com.apple.notes.inlinetextattachment.hashtag";
48718
+ function requirePreservationMetadata(rich) {
48719
+ const unavailable = (message = "Native object, checklist or formatting preservation metadata is unavailable") => {
48720
+ throw new Error(message);
48721
+ };
48722
+ const { objects, objectData, checklistItems, styleRuns, nativeTagObjectIds } = rich;
48723
+ if (!objects || !objectData || !checklistItems || !styleRuns || !nativeTagObjectIds || rich.nativeObjectDataComplete !== true)
48724
+ return unavailable();
48725
+ const ids = new Set(rich.nativeObjectIds);
48726
+ if (ids.size !== rich.nativeObjectIds.length || objects.length !== ids.size || objectData.length !== ids.size || new Set(objects.map((o) => o.id)).size !== ids.size || new Set(objectData.map((o) => o.id)).size !== ids.size || rich.hasNativeObjects !== Boolean(ids.size) || rich.hasChecklist !== Boolean(checklistItems.length))
48727
+ return unavailable();
48728
+ let objectEnd = 0;
48729
+ for (const object3 of objects) {
48730
+ const data = objectData.find((row) => row.id === object3.id);
48731
+ if (!ids.has(object3.id) || !Number.isInteger(object3.start) || !Number.isInteger(object3.length) || object3.start < objectEnd || object3.length < 1 || object3.start + object3.length > rich.text.length || !data || data.type !== object3.type || !Number.isInteger(data.pk) || !/^[0-9a-f]*$/iu.test(data.mergeable) || data.view !== null && !Number.isInteger(data.view) || !Object.hasOwn(data, "altText") || data.altText !== null && typeof data.altText !== "string")
48732
+ return unavailable();
48733
+ objectEnd = object3.start + object3.length;
48734
+ }
48735
+ const mappedIds = /* @__PURE__ */ new Set();
48736
+ if (new Set(rich.nativeTags).size !== rich.nativeTags.length || Object.keys(nativeTagObjectIds).length !== rich.nativeTags.length)
48737
+ return unavailable();
48738
+ for (const [tag, tagIds] of Object.entries(nativeTagObjectIds)) {
48739
+ if (!rich.nativeTags.includes(tag) || !tagIds.length) return unavailable();
48740
+ for (const id2 of tagIds) {
48741
+ if (mappedIds.has(id2) || !objects.some((o) => o.id === id2 && o.type === TAG_OBJECT_TYPE) || objectData.find((o) => o.id === id2)?.altText?.replace(/^#/, "") !== tag)
48742
+ return unavailable();
48743
+ mappedIds.add(id2);
48744
+ }
48745
+ }
48746
+ if (objects.some((o) => o.type === TAG_OBJECT_TYPE && !mappedIds.has(o.id))) return unavailable();
48747
+ if (new Set(checklistItems.map((item) => item.id)).size !== checklistItems.length)
48748
+ return unavailable();
48749
+ for (const item of checklistItems) {
48750
+ const end = item.start + item.text.length;
48751
+ if (!item.id || !Number.isInteger(item.start) || item.start < 0 || end > rich.text.length || rich.text.slice(item.start, end) !== item.text || end < rich.text.length && rich.text[end] !== "\n")
48752
+ return unavailable();
48753
+ }
48754
+ let position = 0;
48755
+ const firstReferences = /* @__PURE__ */ new Map();
48756
+ for (const run of styleRuns) {
48757
+ if (run.start !== position || !Number.isInteger(run.length) || run.length < 0 || typeof run.signature !== "string" || run.nativeSemantics?.complete !== true || run.length === 0 && (run.nativeSemantics.unknown || run.nativeSemantics.structuredParagraph || run.nativeSemantics.links || run.nativeSemantics.objects.length > 0))
48758
+ return unavailable();
48759
+ const references = run.nativeSemantics.objects;
48760
+ if (references.length > 1 || rich.text.slice(run.start, run.start + run.length).includes("\uFFFC") && references.length !== 1)
48761
+ return unavailable("Native object placement preservation metadata is unavailable");
48762
+ for (const reference of references) {
48763
+ if (!objectData.some((data) => data.id === reference.id && data.type === reference.type))
48764
+ return unavailable("Native object placement preservation metadata is unavailable");
48765
+ if (!firstReferences.has(reference.id))
48766
+ firstReferences.set(reference.id, {
48767
+ start: run.start,
48768
+ length: run.length,
48769
+ type: reference.type
48770
+ });
48771
+ }
48772
+ position += run.length;
48773
+ }
48774
+ if (position !== rich.text.length) return unavailable();
48775
+ if (objects.some((object3) => {
48776
+ const reference = firstReferences.get(object3.id);
48777
+ return !reference || reference.start !== object3.start || reference.length !== object3.length || reference.type !== object3.type;
48778
+ }))
48779
+ return unavailable("Native object placement preservation metadata is unavailable");
48780
+ for (const link of rich.links)
48781
+ if (!Number.isInteger(link.start) || !Number.isInteger(link.length) || link.start < 0 || link.length < 1 || link.start + link.length > rich.text.length || rich.text.slice(link.start, link.start + link.length) !== link.text)
48782
+ return unavailable();
48783
+ }
48784
+ function assertZeroLengthFormatting(before, after, spans) {
48785
+ const oldEmpty = before.styleRuns.filter((run) => run.length === 0).map((run) => {
48786
+ const span = spans.find(
48787
+ (span2) => run.start >= span2.before && run.start <= span2.before + span2.length
48788
+ );
48789
+ if (!span) throw new Error("Zero-length formatting falls inside the requested removal");
48790
+ return { ...run, start: span.after + run.start - span.before };
48791
+ });
48792
+ const newEmpty = after.styleRuns.filter(
48793
+ (run) => run.length === 0 && spans.some((span) => run.start >= span.after && run.start <= span.after + span.length)
48794
+ );
48795
+ if (JSON.stringify(oldEmpty) !== JSON.stringify(newEmpty))
48796
+ throw new Error("Existing zero-length formatting metadata changed");
48797
+ }
48798
+ function retainedSpans(before, after, change) {
48799
+ if (change.kind !== "remove-tag") {
48800
+ if (change.kind === "append" ? !after.text.startsWith(before.text) : after.text !== before.text)
48801
+ throw new Error("Existing note text or whitespace was not preserved");
48802
+ return {
48803
+ spans: [{ before: 0, after: 0, length: before.text.length }],
48804
+ removed: /* @__PURE__ */ new Set()
48805
+ };
48806
+ }
48807
+ const removed = new Set(before.nativeTagObjectIds[change.tag]);
48808
+ if (!removed.size || after.nativeTags.includes(change.tag))
48809
+ throw new Error("Native tag removal was not verified");
48810
+ const spans = [];
48811
+ let position = 0, output = "";
48812
+ for (const object3 of before.objects.filter((object4) => removed.has(object4.id))) {
48813
+ const text2 = before.text.slice(object3.start, object3.start + object3.length);
48814
+ if (text2 !== "\uFFFC" && text2 !== `#${change.tag}`)
48815
+ throw new Error("Native tag removal range is unsupported");
48816
+ const prefix = before.text.slice(position, object3.start);
48817
+ spans.push({ before: position, after: output.length, length: prefix.length });
48818
+ output += prefix;
48819
+ position = object3.start + object3.length;
48820
+ }
48821
+ spans.push({ before: position, after: output.length, length: before.text.length - position });
48822
+ output += before.text.slice(position);
48823
+ if (after.text !== output)
48824
+ throw new Error("Existing note text or whitespace was not preserved around the removed tag");
48825
+ return { spans, removed };
48826
+ }
48827
+ function assertRetainedRichContent(before, after, change = { kind: "unchanged" }) {
48828
+ requirePreservationMetadata(before);
48829
+ requirePreservationMetadata(after);
48830
+ const { spans, removed } = retainedSpans(before, after, change);
48831
+ const rangeStart = (start, length) => {
48832
+ const span = spans.find(
48833
+ (span2) => start >= span2.before && start + length <= span2.before + span2.length
48834
+ );
48835
+ return span && span.after + start - span.before;
48836
+ };
48837
+ const retainedIds = before.nativeObjectIds.filter((id2) => !removed.has(id2));
48838
+ if (retainedIds.some((id2) => !after.nativeObjectIds.includes(id2)) || [...removed].some((id2) => after.nativeObjectIds.includes(id2)) || change.kind !== "append" && after.nativeObjectIds.length !== retainedIds.length || change.kind === "append" && after.objects.some(
48839
+ (object3) => !retainedIds.includes(object3.id) && object3.start < before.text.length
48840
+ ))
48841
+ throw new Error("Existing native object identity or placement changed");
48842
+ for (const object3 of before.objects.filter((object4) => !removed.has(object4.id))) {
48843
+ const actual = after.objects.find((current) => current.id === object3.id);
48844
+ if (actual.type !== object3.type || actual.length !== object3.length || actual.start !== rangeStart(object3.start, object3.length))
48845
+ throw new Error("Existing native object range changed");
48846
+ const oldData = before.objectData.find((row) => row.id === object3.id), newData = after.objectData.find((row) => row.id === object3.id);
48847
+ if (oldData.pk !== newData.pk || oldData.type !== newData.type || oldData.mergeable !== newData.mergeable || oldData.view !== newData.view || oldData.altText !== newData.altText)
48848
+ throw new Error("Existing native object payload changed");
48849
+ }
48850
+ const tags = before.nativeTags.filter(
48851
+ (tag) => change.kind !== "remove-tag" || tag !== change.tag
48852
+ );
48853
+ if (tags.some(
48854
+ (tag) => JSON.stringify(before.nativeTagObjectIds[tag]) !== JSON.stringify(after.nativeTagObjectIds[tag])
48855
+ ) || change.kind !== "append" && after.nativeTags.length !== tags.length)
48856
+ throw new Error("Existing native tag identities changed");
48857
+ const links = change.kind === "append" ? after.links.filter((link) => link.start < before.text.length) : after.links;
48858
+ if (links.length !== before.links.length || before.links.some((link, index) => {
48859
+ const actual = links[index];
48860
+ return actual.text !== link.text || actual.url !== link.url || actual.length !== link.length || actual.start !== rangeStart(link.start, link.length);
48861
+ }))
48862
+ throw new Error("Existing links or their ranges were not preserved");
48863
+ const items = change.kind === "append" ? after.checklistItems.filter((item) => item.start < before.text.length) : after.checklistItems;
48864
+ if (items.length !== before.checklistItems.length || before.checklistItems.some((item, index) => {
48865
+ const actual = items[index];
48866
+ const retained = spans.flatMap((span) => {
48867
+ const start2 = Math.max(item.start, span.before), end = Math.min(item.start + item.text.length, span.before + span.length);
48868
+ return start2 < end ? [before.text.slice(start2, end)] : [];
48869
+ }).join("");
48870
+ const startSpan = spans.find(
48871
+ (span) => item.start >= span.before && item.start <= span.before + span.length
48872
+ );
48873
+ const start = startSpan && startSpan.after + item.start - startSpan.before;
48874
+ return actual.id !== item.id || actual.text !== retained || actual.done !== item.done || actual.start !== start;
48875
+ }))
48876
+ throw new Error("Existing checklist item identity, state or range changed");
48877
+ for (const span of spans) {
48878
+ let offset = 0, oldIndex = 0, newIndex = 0;
48879
+ while (offset < span.length) {
48880
+ const oldPosition = span.before + offset, newPosition = span.after + offset;
48881
+ while (before.styleRuns[oldIndex].start + before.styleRuns[oldIndex].length <= oldPosition)
48882
+ oldIndex++;
48883
+ while (after.styleRuns[newIndex].start + after.styleRuns[newIndex].length <= newPosition)
48884
+ newIndex++;
48885
+ const oldRun = before.styleRuns[oldIndex], newRun = after.styleRuns[newIndex];
48886
+ if (oldRun.signature !== newRun.signature || oldRun.paragraphStyle !== newRun.paragraphStyle || oldRun.blockQuote !== newRun.blockQuote || oldRun.highlight !== newRun.highlight)
48887
+ throw new Error("Existing rich formatting changed");
48888
+ offset += Math.min(
48889
+ span.length - offset,
48890
+ oldRun.start + oldRun.length - oldPosition,
48891
+ newRun.start + newRun.length - newPosition
48892
+ );
48893
+ }
48894
+ }
48895
+ assertZeroLengthFormatting(before, after, spans);
48896
+ }
48897
+
48629
48898
  // src/services/shortcutConsent.ts
48630
48899
  function shortcutConsentHint(shortcut) {
48631
48900
  const target = shortcut ? `"${shortcut}"` : "the bridge Shortcut";
@@ -48649,6 +48918,144 @@ function normalizeNativeTags(tags) {
48649
48918
  )
48650
48919
  ];
48651
48920
  }
48921
+ var TAG_OBJECT_TYPE2 = "com.apple.notes.inlinetextattachment.hashtag";
48922
+ function plainNativeRange(rich, start, length) {
48923
+ return rich.styleRuns.every((run) => {
48924
+ if (run.start >= start + length || run.start + run.length <= start) return true;
48925
+ const metadata = run.nativeSemantics;
48926
+ return metadata.complete && !metadata.unknown && !metadata.structuredParagraph && !metadata.links && !metadata.objects.length;
48927
+ });
48928
+ }
48929
+ function preservedTextSpans(before, after, missing) {
48930
+ const oldIds = new Set(before.nativeObjectIds);
48931
+ const added = after.objects.filter((o) => !oldIds.has(o.id));
48932
+ const tagsById = new Map(
48933
+ Object.entries(after.nativeTagObjectIds).flatMap(
48934
+ ([tag, ids]) => ids.map((id2) => [id2, tag])
48935
+ )
48936
+ );
48937
+ if (!added.length || added.some((o) => o.type !== TAG_OBJECT_TYPE2 || !missing.includes(tagsById.get(o.id))) || missing.some((tag) => !added.some((o) => tagsById.get(o.id) === tag)))
48938
+ return void 0;
48939
+ const spans = [];
48940
+ let oldPosition = 0, newPosition = 0, appended = false;
48941
+ for (const object3 of added) {
48942
+ const prefix = after.text.slice(newPosition, object3.start);
48943
+ const remaining2 = before.text.slice(oldPosition);
48944
+ const length = Math.min(prefix.length, remaining2.length);
48945
+ if (prefix.slice(0, length) !== remaining2.slice(0, length) || prefix.length > length && !/^[ \n]*$/u.test(prefix.slice(length)))
48946
+ return void 0;
48947
+ if (length) spans.push({ before: oldPosition, after: newPosition, length });
48948
+ oldPosition += length;
48949
+ const literal2 = `#${tagsById.get(object3.id)}`;
48950
+ const rendered = after.text.slice(object3.start, object3.start + object3.length);
48951
+ if (rendered !== "\uFFFC" && rendered !== literal2) return void 0;
48952
+ const previous = [...before.text.slice(Math.max(0, oldPosition - 2), oldPosition)].at(-1) || "";
48953
+ const next = String.fromCodePoint(before.text.codePointAt(oldPosition + literal2.length) ?? 0);
48954
+ if (oldPosition === before.text.length) appended = true;
48955
+ else if (before.text.startsWith(literal2, oldPosition) && !/[\p{L}\p{M}\p{N}_#-]/u.test(previous) && !/[\p{L}\p{M}\p{N}_-]/u.test(next) && plainNativeRange(before, oldPosition, literal2.length))
48956
+ oldPosition += literal2.length;
48957
+ else return void 0;
48958
+ newPosition = object3.start + object3.length;
48959
+ }
48960
+ const remaining = before.text.slice(oldPosition);
48961
+ const suffix = after.text.slice(newPosition);
48962
+ if (suffix !== remaining && !(appended && !remaining && /^[ \n]*$/u.test(suffix)))
48963
+ return void 0;
48964
+ if (remaining.length)
48965
+ spans.push({ before: oldPosition, after: newPosition, length: remaining.length });
48966
+ return spans;
48967
+ }
48968
+ function preservesAddedSemantics(before, after, spans) {
48969
+ const oldIds = new Set(before.nativeObjectIds);
48970
+ const added = after.objects.filter((o) => !oldIds.has(o.id));
48971
+ const changed = [];
48972
+ let end = 0;
48973
+ for (const span of spans) {
48974
+ if (end < span.after) changed.push({ start: end, end: span.after });
48975
+ end = span.after + span.length;
48976
+ }
48977
+ if (end < after.text.length) changed.push({ start: end, end: after.text.length });
48978
+ for (const run of after.styleRuns) {
48979
+ if (!changed.some((range) => run.start < range.end && run.start + run.length > range.start))
48980
+ continue;
48981
+ const metadata = run.nativeSemantics;
48982
+ if (!metadata.complete || metadata.unknown || metadata.structuredParagraph || metadata.links)
48983
+ return false;
48984
+ const object3 = added.find((o) => run.start === o.start && run.length === o.length);
48985
+ if (object3) {
48986
+ if (metadata.objects.length !== 1 || metadata.objects[0].id !== object3.id || metadata.objects[0].type !== object3.type)
48987
+ return false;
48988
+ } else if (metadata.objects.length || added.some((o) => run.start < o.start + o.length && run.start + run.length > o.start))
48989
+ return false;
48990
+ }
48991
+ return true;
48992
+ }
48993
+ function preservesNativeContent(before, after, spans) {
48994
+ try {
48995
+ assertZeroLengthFormatting(before, after, spans);
48996
+ } catch {
48997
+ return false;
48998
+ }
48999
+ const rangeStart = (start, length) => {
49000
+ const span = spans.find((s) => start >= s.before && start + length <= s.before + s.length);
49001
+ return span && span.after + start - span.before;
49002
+ };
49003
+ for (const object3 of before.objects) {
49004
+ const actual = after.objects.find((o) => o.id === object3.id);
49005
+ if (!actual || actual.type !== object3.type || actual.length !== object3.length || actual.start !== rangeStart(object3.start, object3.length))
49006
+ return false;
49007
+ }
49008
+ for (const object3 of before.objectData) {
49009
+ const actual = after.objectData.find((o) => o.id === object3.id);
49010
+ if (!actual || actual.pk !== object3.pk || actual.type !== object3.type || actual.mergeable !== object3.mergeable || actual.view !== object3.view || actual.altText !== object3.altText)
49011
+ return false;
49012
+ }
49013
+ for (const tag of before.nativeTags)
49014
+ if (JSON.stringify(before.nativeTagObjectIds[tag]) !== JSON.stringify(after.nativeTagObjectIds[tag]))
49015
+ return false;
49016
+ if (before.checklistItems.length !== after.checklistItems.length) return false;
49017
+ for (const [index, item] of before.checklistItems.entries()) {
49018
+ const actual = after.checklistItems[index];
49019
+ if (actual.id !== item.id || actual.text !== item.text || actual.done !== item.done || actual.start !== rangeStart(item.start, item.text.length))
49020
+ return false;
49021
+ }
49022
+ if (before.links.length !== after.links.length) return false;
49023
+ for (const [index, link] of before.links.entries()) {
49024
+ const actual = after.links[index];
49025
+ if (actual.text !== link.text || actual.url !== link.url || actual.length !== link.length || actual.start !== rangeStart(link.start, link.length))
49026
+ return false;
49027
+ }
49028
+ let oldIndex = 0, newIndex = 0;
49029
+ for (const span of spans) {
49030
+ let offset = 0;
49031
+ while (offset < span.length) {
49032
+ const oldPosition = span.before + offset, newPosition = span.after + offset;
49033
+ while (before.styleRuns[oldIndex].start + before.styleRuns[oldIndex].length <= oldPosition)
49034
+ oldIndex++;
49035
+ while (after.styleRuns[newIndex].start + after.styleRuns[newIndex].length <= newPosition)
49036
+ newIndex++;
49037
+ const oldRun = before.styleRuns[oldIndex], newRun = after.styleRuns[newIndex];
49038
+ if (oldRun.signature !== newRun.signature || oldRun.paragraphStyle !== newRun.paragraphStyle || oldRun.blockQuote !== newRun.blockQuote || oldRun.highlight !== newRun.highlight)
49039
+ return false;
49040
+ offset += Math.min(
49041
+ span.length - offset,
49042
+ oldRun.start + oldRun.length - oldPosition,
49043
+ newRun.start + newRun.length - newPosition
49044
+ );
49045
+ }
49046
+ }
49047
+ return true;
49048
+ }
49049
+ function nativeTagAdditionPreserved(before, after, tags) {
49050
+ requirePreservationMetadata(before);
49051
+ requirePreservationMetadata(after);
49052
+ const missing = tags.filter((tag) => !before.nativeTags.includes(tag));
49053
+ const allTags = [.../* @__PURE__ */ new Set([...before.nativeTags, ...tags])];
49054
+ const spans = preservedTextSpans(before, after, missing);
49055
+ return after.nativeTags.length === allTags.length && allTags.every((tag) => after.nativeTags.includes(tag)) && before.nativeObjectIds.every((id2) => after.nativeObjectIds.includes(id2)) && before.hasChecklist === after.hasChecklist && Boolean(
49056
+ spans && preservesAddedSemantics(before, after, spans) && preservesNativeContent(before, after, spans)
49057
+ );
49058
+ }
48652
49059
  function addNativeTags(request, deps) {
48653
49060
  if (!/^x-coredata:\/\/[0-9a-f-]+\/ICNote\/p\d+$/i.test(request.id))
48654
49061
  throw new Error("An exact CoreData note ID is required");
@@ -48663,32 +49070,35 @@ function addNativeTags(request, deps) {
48663
49070
  const missing = tags.filter((tag) => !before.rich.nativeTags.includes(tag));
48664
49071
  if (!missing.length)
48665
49072
  return { nativeTags: before.rich.nativeTags, added: [], contentHash: before.contentHash };
49073
+ requirePreservationMetadata(before.rich);
48666
49074
  const candidates = deps.candidates(request.title, request.scopeText);
48667
49075
  if (candidates.length !== 1 || candidates[0] !== request.id)
48668
49076
  throw new Error(
48669
49077
  "Shortcuts selection is ambiguous or points to a different note; nothing was changed"
48670
49078
  );
48671
- if (deps.read(request.id).contentHash !== request.expectedContentHash)
49079
+ const current = deps.read(request.id);
49080
+ if (current.contentHash !== request.expectedContentHash)
48672
49081
  throw new Error("Note revision changed during preflight; nothing was changed");
49082
+ requirePreservationMetadata(current.rich);
48673
49083
  let transportWarning;
48674
49084
  try {
48675
49085
  deps.run({ title: request.title, scopeText: request.scopeText, tags: missing });
48676
49086
  } catch (error2) {
48677
49087
  transportWarning = error2 instanceof Error ? error2.message : "Shortcuts completion was uncertain";
48678
49088
  }
48679
- const after = deps.read(request.id);
48680
- const allTags = [.../* @__PURE__ */ new Set([...before.rich.nativeTags, ...tags])];
48681
- const textWithoutTags = (text2) => {
48682
- for (const tag of [...allTags].sort((a, b) => b.length - a.length)) {
48683
- const escaped = tag.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
48684
- text2 = text2.replace(new RegExp(`#${escaped}(?![\\p{L}\\p{N}_-])`, "gu"), "");
48685
- }
48686
- return text2.replace(/[\s\ufffc]/gu, "");
48687
- };
48688
- if (after.title !== before.title || allTags.some((tag) => !after.rich.nativeTags.includes(tag)) || textWithoutTags(before.rich.text) !== textWithoutTags(after.rich.text) || linkSignature(before.rich.links) !== linkSignature(after.rich.links) || before.rich.nativeObjectIds.some((id2) => !after.rich.nativeObjectIds.includes(id2)) || before.rich.hasChecklist !== after.rich.hasChecklist) {
48689
- throw new Error(
48690
- transportWarning ? `Shortcuts did not complete cleanly, and exact-ID tags/text/links readback was not verified. Read the note before any retry. ${transportWarning}` : "Shortcuts ran, but exact-ID tags/text/links readback was not verified. Read the note before any retry"
48691
- );
49089
+ const verificationFailure = (detail) => new CodedError(
49090
+ (transportWarning ? "Shortcuts did not complete cleanly, and exact-ID tags/text/links/native-content readback was not verified." : "Shortcuts ran, but exact-ID tags/text/links/native-content readback was not verified.") + " Read the note before any retry." + (detail ? ` ${detail}` : "") + (transportWarning ? ` ${transportWarning}` : ""),
49091
+ { code: "verification_failed", indeterminate: true }
49092
+ );
49093
+ let after;
49094
+ try {
49095
+ after = deps.read(request.id);
49096
+ requirePreservationMetadata(after.rich);
49097
+ } catch (error2) {
49098
+ throw verificationFailure(error2 instanceof Error ? error2.message : String(error2));
49099
+ }
49100
+ if (after.title !== before.title || !nativeTagAdditionPreserved(before.rich, after.rich, tags)) {
49101
+ throw verificationFailure();
48692
49102
  }
48693
49103
  return {
48694
49104
  nativeTags: after.rich.nativeTags,
@@ -49127,53 +49537,25 @@ function backgroundDependencies(manager) {
49127
49537
  function assertPreserved(before, after, options = {}) {
49128
49538
  if (before.id !== after.id || before.title !== after.title)
49129
49539
  throw new Error("Note identity changed");
49130
- const tidy = (s) => s.replace(/\r\n/g, "\n").replace(/[\s\ufffc]+$/gu, "");
49131
- let oldText = tidy(before.rich.text), newText = tidy(after.rich.text);
49132
- if (options.tagChange) {
49133
- const escaped = options.tagChange.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
49134
- const strip = (s) => s.replace(new RegExp(`#${escaped}(?![\\p{L}\\p{N}_-])`, "gu"), "").replace(/[\s\ufffc]/gu, "");
49135
- oldText = strip(oldText);
49136
- newText = strip(newText);
49137
- }
49138
- if (options.append ? !newText.startsWith(oldText) : newText !== oldText)
49139
- throw new Error("Existing note text was not preserved");
49140
- const links = options.append ? after.rich.links.slice(0, before.rich.links.length) : after.rich.links;
49141
- if (linkSignature(before.rich.links) !== linkSignature(links))
49142
- throw new Error("Existing links were not preserved");
49143
- if (!options.tagChange && before.rich.nativeObjectIds.some((id2) => !after.rich.nativeObjectIds.includes(id2)))
49144
- throw new Error("Existing native object was lost");
49145
- if (JSON.stringify(before.checklist) !== JSON.stringify(
49146
- options.append ? after.checklist.slice(0, before.checklist.length) : after.checklist
49147
- ))
49148
- throw new Error("Existing checklist items changed");
49149
- for (const item of before.rich.checklistItems || []) {
49150
- const actual = after.rich.checklistItems?.find((current) => current.id === item.id);
49151
- if (!actual || actual.text !== item.text || actual.done !== item.done)
49152
- throw new Error("Existing checklist item identity or state changed");
49153
- }
49154
- if (before.rich.nativeTags.filter((t) => t !== options.tagChange).some((t) => !after.rich.nativeTags.includes(t)))
49155
- throw new Error("Existing native tag was lost");
49156
- const allowedRemoved = options.tagChange ? before.rich.nativeTagObjectIds?.[options.tagChange] || [] : [];
49157
- for (const object3 of before.rich.objectData || []) {
49158
- if (allowedRemoved.includes(object3.id)) continue;
49159
- const actual = after.rich.objectData?.find((o) => o.id === object3.id);
49160
- if (!actual || actual.mergeable !== object3.mergeable || actual.view !== object3.view)
49161
- throw new Error("Existing native object content or presentation changed");
49162
- }
49163
- if (!options.tagChange && before.rich.styleRuns && after.rich.styleRuns) {
49164
- const end = before.rich.text.trimEnd().length;
49165
- for (const oldRun of before.rich.styleRuns)
49166
- for (const newRun of after.rich.styleRuns) {
49167
- const overlapStart = Math.max(oldRun.start, newRun.start);
49168
- const overlapEnd = Math.min(
49169
- end,
49170
- oldRun.start + oldRun.length,
49171
- newRun.start + newRun.length
49172
- );
49173
- if (overlapStart < overlapEnd && /[^\s\ufffc]/u.test(before.rich.text.slice(overlapStart, overlapEnd)) && oldRun.signature !== newRun.signature)
49174
- throw new Error("Existing rich formatting changed");
49175
- }
49540
+ if (options.tagChange && !options.removeTag) {
49541
+ if (!nativeTagAdditionPreserved(before.rich, after.rich, [options.tagChange]))
49542
+ throw new Error("Native tag addition or retained content was not verified");
49543
+ } else {
49544
+ try {
49545
+ assertRetainedRichContent(
49546
+ before.rich,
49547
+ after.rich,
49548
+ options.tagChange ? { kind: "remove-tag", tag: options.tagChange } : options.append ? { kind: "append" } : { kind: "unchanged" }
49549
+ );
49550
+ } catch (error2) {
49551
+ if (error2 instanceof Error && error2.message === "Existing note text or whitespace was not preserved")
49552
+ throw new Error("Existing note text was not preserved", { cause: error2 });
49553
+ throw error2;
49554
+ }
49176
49555
  }
49556
+ for (const snapshot of [before, after])
49557
+ if (JSON.stringify(snapshot.checklist) !== JSON.stringify(snapshot.rich.checklistItems.map(({ text: text2, done }) => ({ text: text2, done }))))
49558
+ throw new Error("Native checklist readers disagree about item text or state");
49177
49559
  }
49178
49560
  function describeTransportFailure(error2) {
49179
49561
  const detail = error2;
@@ -49187,12 +49569,37 @@ function textBelowTitle(text2, title) {
49187
49569
  if (firstLine.trim() !== title.trim()) return text2;
49188
49570
  return lineEnd < 0 ? "" : text2.slice(lineEnd + 1);
49189
49571
  }
49572
+ function assertChecklistEndAppendAvailable(rich) {
49573
+ requirePreservationMetadata(rich);
49574
+ const items = [...rich.checklistItems].sort((a, b) => a.start - b.start);
49575
+ if (!items.length) return;
49576
+ const last = items.at(-1);
49577
+ if (items.some(
49578
+ (item, index) => item.start > 0 && rich.text[item.start - 1] !== "\n" || index > 0 && item.start !== items[index - 1].start + items[index - 1].text.length + 1
49579
+ ) || !/^\n?$/u.test(rich.text.slice(last.start + last.text.length)))
49580
+ throw new CodedError(
49581
+ "Checklist end-append requires one existing checklist group at the end of the note. Notes may insert into an earlier group when text or native objects follow it, or when groups are separate. Nothing changed",
49582
+ { code: "validation_error", committed: false, indeterminate: false }
49583
+ );
49584
+ }
49585
+ function requireBackgroundPreflightMetadata(rich) {
49586
+ try {
49587
+ requirePreservationMetadata(rich);
49588
+ } catch (error2) {
49589
+ throw new CodedError(error2 instanceof Error ? error2.message : String(error2), {
49590
+ code: "validation_error",
49591
+ committed: false,
49592
+ indeterminate: false
49593
+ });
49594
+ }
49595
+ }
49190
49596
  function mutateBackground(request, operation, data, verify, deps) {
49191
49597
  if (request.scopeText.length < 12 || request.scopeText.length > 500 || /[\r\n\0]/u.test(request.scopeText))
49192
49598
  throw new Error("Use a distinctive existing single-line scope of 12\u2013500 characters");
49193
49599
  const before = deps.read(request.id);
49194
49600
  if (before.hash !== request.expectedContentHash)
49195
49601
  throw new Error("Note revision changed; read it again");
49602
+ requireBackgroundPreflightMetadata(before.rich);
49196
49603
  if (!before.rich.text.includes(request.scopeText))
49197
49604
  throw new Error("Scope is absent from exact note");
49198
49605
  if (!textBelowTitle(before.rich.text, before.title).includes(request.scopeText))
@@ -49203,8 +49610,10 @@ function mutateBackground(request, operation, data, verify, deps) {
49203
49610
  const candidates = deps.candidates(before.title, request.scopeText);
49204
49611
  if (candidates.length !== 1 || candidates[0] !== request.id)
49205
49612
  throw new Error("Ambiguous note selection; nothing changed");
49206
- if (deps.read(request.id).hash !== before.hash)
49207
- throw new Error("Note revision changed during preflight");
49613
+ const current = deps.read(request.id);
49614
+ if (current.hash !== before.hash) throw new Error("Note revision changed during preflight");
49615
+ requireBackgroundPreflightMetadata(current.rich);
49616
+ if (operation === "create-checklist-item") assertChecklistEndAppendAvailable(current.rich);
49208
49617
  let transportUncertain = false;
49209
49618
  let transportMessage = "";
49210
49619
  let transportError;
@@ -49308,7 +49717,7 @@ function setNativeTag(manager, request) {
49308
49717
  request.present ? "add-tag" : "remove-tag",
49309
49718
  { tag },
49310
49719
  (before, after) => {
49311
- assertPreserved(before, after, { tagChange: tag });
49720
+ assertPreserved(before, after, { tagChange: tag, removeTag: !request.present });
49312
49721
  if (after.rich.nativeTags.includes(tag) !== request.present)
49313
49722
  throw new Error("Native tag state not verified");
49314
49723
  const removed = before.rich.nativeObjectIds.filter(
@@ -55247,30 +55656,13 @@ function readSnapshot(manager, id2) {
55247
55656
  const rich = readRichNote(id2);
55248
55657
  if (enriched.revision !== rich.revision)
55249
55658
  throw new Error("Native metadata changed during read; read the note again");
55659
+ requirePreservationMetadata(rich);
55250
55660
  return { id: id2, title: note.title, body, rich, hash: richContentHash(body, enriched) };
55251
55661
  }
55252
55662
  function assertExistingContentPreserved(before, after) {
55253
55663
  if (before.id !== after.id || before.title !== after.title)
55254
55664
  throw new Error("Note identity changed");
55255
- const tidy = (text2) => text2.replace(/\r\n/g, "\n").replace(/[\s\ufffc]+$/gu, "");
55256
- if (!tidy(after.rich.text).startsWith(tidy(before.rich.text)))
55257
- throw new Error("Existing note text was not preserved");
55258
- if (linkSignature(before.rich.links) !== linkSignature(after.rich.links.slice(0, before.rich.links.length)))
55259
- throw new Error("Existing links were not preserved");
55260
- if (before.rich.nativeObjectIds.some((id2) => !after.rich.nativeObjectIds.includes(id2)))
55261
- throw new Error("Existing native object was lost");
55262
- if (before.rich.nativeTags.some((tag) => !after.rich.nativeTags.includes(tag)))
55263
- throw new Error("Existing native tag was lost");
55264
- for (const item of before.rich.checklistItems || []) {
55265
- const actual = after.rich.checklistItems?.find((current) => current.id === item.id);
55266
- if (!actual || actual.text !== item.text || actual.done !== item.done)
55267
- throw new Error("Existing checklist item identity or state changed");
55268
- }
55269
- for (const object3 of before.rich.objectData || []) {
55270
- const actual = after.rich.objectData?.find((current) => current.id === object3.id);
55271
- if (!actual || actual.mergeable !== object3.mergeable || actual.view !== object3.view)
55272
- throw new Error("Existing native object content or presentation changed");
55273
- }
55665
+ assertRetainedRichContent(before.rich, after.rich, { kind: "append" });
55274
55666
  }
55275
55667
  function localAttachment(path10) {
55276
55668
  if (!isAbsolute4(path10)) throw new Error("An absolute local file path is required");
@@ -55647,8 +56039,19 @@ function attachFile(manager, args, checked, progress = { insertionStarted: false
55647
56039
  transportWarning: "Transport was uncertain; exact bytes and prior content were verified"
55648
56040
  } : {}
55649
56041
  };
56042
+ } catch (error2) {
56043
+ if (progress.insertionStarted)
56044
+ throw new CodedError(
56045
+ `Attachment outcome uncertain; read the exact note before any retry: ${error2 instanceof Error ? error2.message : String(error2)}`,
56046
+ { code: "verification_failed", indeterminate: true }
56047
+ );
56048
+ throw error2;
55650
56049
  } finally {
55651
- rmSync7(directory, { recursive: true, force: true });
56050
+ try {
56051
+ rmSync7(directory, { recursive: true, force: true });
56052
+ } catch {
56053
+ console.error("Could not remove private temporary attachment files");
56054
+ }
55652
56055
  }
55653
56056
  }
55654
56057
 
@@ -58520,7 +58923,7 @@ function registerSvgAnalysis(server2) {
58520
58923
  // src/tools/nativeTagsBridge.ts
58521
58924
  var noteId2 = exactIdInput(
58522
58925
  "ICNote",
58523
- /^x-coredata:\/\/[0-9a-f-]+\/ICNote\/p\d+$/i,
58926
+ /^x-coredata:\/\/[0-9a-fA-F-]+\/ICNote\/p\d+$/i,
58524
58927
  NOTE_ID_MESSAGE
58525
58928
  );
58526
58929
  var revision2 = external_exports.string().regex(/^sha256:[a-f0-9]{64}$/);
@@ -58554,8 +58957,9 @@ function registerNativeTagsBridge(server2, manager) {
58554
58957
  if (!body) throw new Error("Note content is unavailable");
58555
58958
  const enriched = enrichNoteRead(id2, body);
58556
58959
  const rich = readRichNote(id2);
58557
- if (!enriched.complete || enriched.revision !== rich.revision)
58960
+ if (enriched.revision !== rich.revision)
58558
58961
  throw new Error("Native metadata changed during read; read the note again");
58962
+ restoreNoteLinks(body, rich);
58559
58963
  return {
58560
58964
  contentHash: richContentHash(body, enriched),
58561
58965
  title: note.title,
@@ -59731,7 +60135,7 @@ function registerNativeOperations(server2, manager) {
59731
60135
  );
59732
60136
  tool(
59733
60137
  "create-checklist-item",
59734
- "Use when: appending one real unchecked Notes checklist item.\nReturns: the created checklist identity after exact-note readback.\nDo not use when: plain text is sufficient or more than one item is requested.\nSafety: preserves existing text, links, native objects, checklist identities, and formatting.",
60138
+ "Use when: appending one real unchecked Notes checklist item.\nReturns: the created checklist identity after exact-note readback.\nDo not use when: plain text is sufficient or more than one item is requested.\nSafety: preserves existing text, links, native objects, checklist identities, and formatting. If a checklist already exists, requires one contiguous group at the end of the note; otherwise refuses before running the bridge.",
59735
60139
  {
59736
60140
  ...common,
59737
60141
  text: external_exports.string().min(1).max(1e4).refine((s) => !/[\r\n\0]/u.test(s), "One line per checklist item")
@@ -59755,7 +60159,7 @@ function registerNativeOperations(server2, manager) {
59755
60159
  );
59756
60160
  tool(
59757
60161
  "create-checklist-items",
59758
- "Use when: appending several real unchecked Notes checklist items to one note, in order.\nReturns: each landed item's native identity and text, the final order check, and the new revision; on a stop, which items landed, the item whose outcome is uncertain, and the items not attempted.\nDo not use when: one item is enough (create-checklist-item) or plain text is sufficient.\nSafety: runs the verified single-item bridge once per item, chaining each verified revision into the next; checks after every item that exactly one new unchecked item with that text and a new identity appeared and that earlier items kept theirs, and stops at the first uncertain result without retrying. Each item takes a few seconds, so a full batch can run about a minute; if the call times out on the client side, read the note before retrying and send only items that are not present.",
60162
+ "Use when: appending several real unchecked Notes checklist items to one note, in order.\nReturns: each landed item's native identity and text, the final order check, and the new revision; on a stop, which items landed, the item whose outcome is uncertain, and the items not attempted.\nDo not use when: one item is enough (create-checklist-item) or plain text is sufficient.\nSafety: runs the verified single-item bridge once per item, chaining each verified revision into the next; requires any existing checklist to be one contiguous group at the end of the note before dispatch. Checks after every item that exactly one new unchecked item with that text and a new identity appeared and that earlier items kept theirs, and stops at the first uncertain result without retrying. Each item takes a few seconds, so a full batch can run about a minute; if the call times out on the client side, read the note before retrying and send only items that are not present.",
59759
60163
  {
59760
60164
  ...common,
59761
60165
  items: external_exports.array(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apple-notes-mcp",
3
- "version": "2.15.3",
3
+ "version": "2.15.5",
4
4
  "description": "MCP server for Apple Notes - read, search, create, edit, organize, and export notes on macOS via Claude and other AI assistants",
5
5
  "type": "module",
6
6
  "main": "build/index.js",