apple-notes-mcp 2.8.28 → 2.8.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +57 -2
  2. package/build/index.js +758 -202
  3. package/package.json +1 -1
package/build/index.js CHANGED
@@ -24465,14 +24465,14 @@ var require_turndown_cjs = __commonJS({
24465
24465
  } else if (node.nodeType === 1) {
24466
24466
  replacement = replacementForNode.call(self, node);
24467
24467
  }
24468
- return join15(output, replacement);
24468
+ return join16(output, replacement);
24469
24469
  }, "");
24470
24470
  }
24471
24471
  function postProcess(output) {
24472
24472
  var self = this;
24473
24473
  this.rules.forEach(function(rule) {
24474
24474
  if (typeof rule.append === "function") {
24475
- output = join15(output, rule.append(self.options));
24475
+ output = join16(output, rule.append(self.options));
24476
24476
  }
24477
24477
  });
24478
24478
  return output.replace(/^[\t\r\n]+/, "").replace(/[\t\r\n\s]+$/, "");
@@ -24484,7 +24484,7 @@ var require_turndown_cjs = __commonJS({
24484
24484
  if (whitespace.leading || whitespace.trailing) content = content.trim();
24485
24485
  return whitespace.leading + rule.replacement(content, node, this.options) + whitespace.trailing;
24486
24486
  }
24487
- function join15(output, replacement) {
24487
+ function join16(output, replacement) {
24488
24488
  var s1 = trimTrailingNewlines(output);
24489
24489
  var s2 = trimLeadingNewlines(replacement);
24490
24490
  var nls = Math.max(output.length - s1.length, replacement.length - s2.length);
@@ -24777,8 +24777,8 @@ var ZodError = class _ZodError extends Error {
24777
24777
  constructor(issues) {
24778
24778
  super();
24779
24779
  this.issues = [];
24780
- this.addIssue = (sub3) => {
24781
- this.issues = [...this.issues, sub3];
24780
+ this.addIssue = (sub4) => {
24781
+ this.issues = [...this.issues, sub4];
24782
24782
  };
24783
24783
  this.addIssues = (subs = []) => {
24784
24784
  this.issues = [...this.issues, ...subs];
@@ -24845,13 +24845,13 @@ var ZodError = class _ZodError extends Error {
24845
24845
  flatten(mapper = (issue2) => issue2.message) {
24846
24846
  const fieldErrors = {};
24847
24847
  const formErrors = [];
24848
- for (const sub3 of this.issues) {
24849
- if (sub3.path.length > 0) {
24850
- const firstEl = sub3.path[0];
24848
+ for (const sub4 of this.issues) {
24849
+ if (sub4.path.length > 0) {
24850
+ const firstEl = sub4.path[0];
24851
24851
  fieldErrors[firstEl] = fieldErrors[firstEl] || [];
24852
- fieldErrors[firstEl].push(mapper(sub3));
24852
+ fieldErrors[firstEl].push(mapper(sub4));
24853
24853
  } else {
24854
- formErrors.push(mapper(sub3));
24854
+ formErrors.push(mapper(sub4));
24855
24855
  }
24856
24856
  }
24857
24857
  return { formErrors, fieldErrors };
@@ -29150,12 +29150,12 @@ var $ZodRealError = $constructor("$ZodError", initializer, { Parent: Error });
29150
29150
  function flattenError(error2, mapper = (issue2) => issue2.message) {
29151
29151
  const fieldErrors = {};
29152
29152
  const formErrors = [];
29153
- for (const sub3 of error2.issues) {
29154
- if (sub3.path.length > 0) {
29155
- fieldErrors[sub3.path[0]] = fieldErrors[sub3.path[0]] || [];
29156
- fieldErrors[sub3.path[0]].push(mapper(sub3));
29153
+ for (const sub4 of error2.issues) {
29154
+ if (sub4.path.length > 0) {
29155
+ fieldErrors[sub4.path[0]] = fieldErrors[sub4.path[0]] || [];
29156
+ fieldErrors[sub4.path[0]].push(mapper(sub4));
29157
29157
  } else {
29158
- formErrors.push(mapper(sub3));
29158
+ formErrors.push(mapper(sub4));
29159
29159
  }
29160
29160
  }
29161
29161
  return { formErrors, fieldErrors };
@@ -39275,7 +39275,7 @@ function decodeVarint(buf, offset) {
39275
39275
  }
39276
39276
  throw new Error(`Unexpected end of buffer reading varint at offset ${offset}`);
39277
39277
  }
39278
- function decodeMessage(buf) {
39278
+ function decodeMessage(buf, options = {}) {
39279
39279
  const fields = [];
39280
39280
  let offset = 0;
39281
39281
  while (offset < buf.length) {
@@ -39296,10 +39296,13 @@ function decodeMessage(buf) {
39296
39296
  const value = buf.slice(offset, offset + length);
39297
39297
  fields.push({ fieldNumber, wireType, value });
39298
39298
  offset += length;
39299
- } else if (wireType === 5) {
39300
- offset += 4;
39301
- } else if (wireType === 1) {
39302
- offset += 8;
39299
+ } else if (wireType === 5 || wireType === 1) {
39300
+ const width = wireType === 5 ? 4 : 8;
39301
+ if (offset + width > buf.length) break;
39302
+ if (options.keepFixed) {
39303
+ fields.push({ fieldNumber, wireType, value: buf.slice(offset, offset + width) });
39304
+ }
39305
+ offset += width;
39303
39306
  } else {
39304
39307
  break;
39305
39308
  }
@@ -39330,6 +39333,11 @@ function embeddedMessage(field) {
39330
39333
  if (!bytes) return void 0;
39331
39334
  return decodeMessage(bytes);
39332
39335
  }
39336
+ function fixed64Double(field) {
39337
+ if (!field || field.wireType !== 1 || !(field.value instanceof Uint8Array)) return void 0;
39338
+ if (field.value.length !== 8) return void 0;
39339
+ return new DataView(field.value.buffer, field.value.byteOffset, 8).getFloat64(0, true);
39340
+ }
39333
39341
  var ProtobufDecodeError = class extends Error {
39334
39342
  constructor(message) {
39335
39343
  super(message);
@@ -39901,12 +39909,555 @@ function assertLinkedWrite(rich, content, format, allowLinkChanges = false) {
39901
39909
  }
39902
39910
  }
39903
39911
 
39912
+ // src/utils/audioTranscripts.ts
39913
+ import { execFileSync as execFileSync4 } from "node:child_process";
39914
+ import { homedir as homedir3 } from "node:os";
39915
+ import { join as join3 } from "node:path";
39916
+ import { gunzipSync as gunzipSync3 } from "node:zlib";
39917
+ var NOTES_DB_PATH2 = join3(
39918
+ homedir3(),
39919
+ "Library/Group Containers/group.com.apple.notes/NoteStore.sqlite"
39920
+ );
39921
+ var DEFAULT_MAX_SEGMENTS = 2e3;
39922
+ var MAX_SEGMENTS_LIMIT = 2e4;
39923
+ var AudioTranscriptError = class extends Error {
39924
+ constructor(kind, message) {
39925
+ super(message);
39926
+ this.kind = kind;
39927
+ this.name = "AudioTranscriptError";
39928
+ }
39929
+ kind;
39930
+ };
39931
+ var decode = (bytes) => decodeMessage(bytes, { keepFixed: true });
39932
+ var sub = (f, n) => {
39933
+ const bytes = getField(f, n)?.value;
39934
+ return bytes instanceof Uint8Array ? decode(bytes) : void 0;
39935
+ };
39936
+ var need = (value, what) => {
39937
+ if (value === void 0) throw new Error(`Missing ${what}`);
39938
+ return value;
39939
+ };
39940
+ var many = (f, n) => getFields(f, n).map((field) => {
39941
+ if (!(field.value instanceof Uint8Array)) throw new Error("Invalid nested entry");
39942
+ return decode(field.value);
39943
+ });
39944
+ function parseAudioRecording(data) {
39945
+ const root = decode(data);
39946
+ const entries = many(root, 3);
39947
+ if (entries.length === 0) throw new Error("No mergeable objects");
39948
+ if (entries.length > 2e6) throw new Error("Recording too large");
39949
+ const keys = getFields(root, 4).map((f) => stringValue(f) ?? "");
39950
+ const types = getFields(root, 5).map((f) => stringValue(f) ?? "");
39951
+ const uuids = getFields(root, 6).map(
39952
+ (f) => f.value instanceof Uint8Array ? Buffer.from(f.value).toString("hex") : ""
39953
+ );
39954
+ const uuidSlot = /* @__PURE__ */ new Map();
39955
+ uuids.forEach((uuid2, i) => {
39956
+ if (uuid2 && !uuidSlot.has(uuid2)) uuidSlot.set(uuid2, i);
39957
+ });
39958
+ const entry = (index) => {
39959
+ const value = entries[index];
39960
+ if (!value) throw new Error("Invalid object reference");
39961
+ return value;
39962
+ };
39963
+ const customMap = (e) => {
39964
+ const map = sub(e, 13);
39965
+ if (!map) return void 0;
39966
+ const values = /* @__PURE__ */ new Map();
39967
+ for (const item of many(map, 3)) {
39968
+ const key = keys[need(varintValue(getField(item, 1)), "map key")];
39969
+ const value = sub(item, 2);
39970
+ if (key !== void 0 && value) values.set(key, value);
39971
+ }
39972
+ return { type: types[need(varintValue(getField(map, 1)), "map type")] ?? "", values };
39973
+ };
39974
+ const objectIndex = (id2) => id2 ? varintValue(getField(id2, 6)) : void 0;
39975
+ const registerTarget = (id2) => {
39976
+ const index = objectIndex(id2);
39977
+ if (index === void 0) return void 0;
39978
+ const register = sub(entry(index), 1);
39979
+ if (!register) return void 0;
39980
+ const target = objectIndex(sub(register, 2));
39981
+ return target === void 0 ? void 0 : entry(target);
39982
+ };
39983
+ const registerPrimitive = (id2) => {
39984
+ const target = registerTarget(id2);
39985
+ const map = target && customMap(target);
39986
+ if (!map) return void 0;
39987
+ const self = map.values.get("self");
39988
+ if (self) return stringValue(getField(self, 4));
39989
+ const double = map.values.get("doubleValue");
39990
+ if (double) return fixed64Double(getField(double, 3));
39991
+ const integer2 = map.values.get("integerValue");
39992
+ if (integer2) return varintValue(getField(integer2, 2));
39993
+ return void 0;
39994
+ };
39995
+ const registerNoteText = (id2) => {
39996
+ const target = registerTarget(id2);
39997
+ const note = target && sub(target, 10);
39998
+ const text = note && stringValue(getField(note, 2));
39999
+ return text?.replace(/\n+$/u, "") || void 0;
40000
+ };
40001
+ const recordings = entries.map((e) => customMap(e)).filter((m) => m?.type === "com.apple.notes.ICTTAudioRecording");
40002
+ if (recordings.length !== 1) throw new Error("Expected exactly one audio recording object");
40003
+ const recording = recordings[0];
40004
+ const fragments = [];
40005
+ const fragmentList = objectIndex(recording.values.get("fragments"));
40006
+ if (fragmentList !== void 0) {
40007
+ const list = need(sub(entry(fragmentList), 5), "fragment list");
40008
+ for (const item of many(list, 1)) {
40009
+ const fragment = customMap(entry(need(objectIndex(sub(item, 2)), "fragment reference")));
40010
+ if (fragment?.type !== "com.apple.notes.ICTTAudioRecording.Fragment")
40011
+ throw new Error("Unexpected fragment type");
40012
+ const identity = stringValue(getField(fragment.values.get("identity") ?? [], 4));
40013
+ const segments = [];
40014
+ const transcriptRef = objectIndex(fragment.values.get("transcript"));
40015
+ if (transcriptRef !== void 0) {
40016
+ const set = sub(entry(transcriptRef), 15);
40017
+ if (!set) throw new Error("Unsupported transcript container");
40018
+ const bySlot = /* @__PURE__ */ new Map();
40019
+ for (const element of many(sub(set, 2) ?? [], 1)) {
40020
+ const keyMap = customMap(entry(need(objectIndex(sub(element, 1)), "segment key")));
40021
+ const slot = varintValue(getField(keyMap?.values.get("UUIDIndex") ?? [], 2));
40022
+ const value = objectIndex(sub(element, 2));
40023
+ if (slot === void 0 || value === void 0) throw new Error("Invalid segment key");
40024
+ bySlot.set(slot, value);
40025
+ }
40026
+ const ordering = many(need(sub(set, 1), "transcript ordering"), 2).map((pair) => ({
40027
+ index: need(varintValue(getField(pair, 1)), "segment index"),
40028
+ uuid: getField(pair, 2)?.value
40029
+ })).sort((a, b) => a.index - b.index);
40030
+ for (const { uuid: uuid2 } of ordering) {
40031
+ if (!(uuid2 instanceof Uint8Array)) throw new Error("Invalid segment UUID");
40032
+ const slot = uuidSlot.get(Buffer.from(uuid2).toString("hex"));
40033
+ const segmentIndex = slot === void 0 ? void 0 : bySlot.get(slot);
40034
+ if (segmentIndex === void 0) throw new Error("Unresolved transcript segment");
40035
+ const segment = customMap(entry(segmentIndex));
40036
+ if (segment?.type !== "com.apple.notes.ICTTTranscriptSegment")
40037
+ throw new Error("Unexpected segment type");
40038
+ const text = registerPrimitive(segment.values.get("text"));
40039
+ if (typeof text !== "string") throw new Error("Segment without text");
40040
+ const start = registerPrimitive(segment.values.get("timestamp"));
40041
+ const duration3 = registerPrimitive(segment.values.get("duration"));
40042
+ const speaker = registerPrimitive(segment.values.get("speaker"));
40043
+ segments.push({
40044
+ text,
40045
+ ...typeof start === "number" ? { start } : {},
40046
+ ...typeof duration3 === "number" ? { duration: duration3 } : {},
40047
+ ...typeof speaker === "string" && speaker ? { speaker } : {}
40048
+ });
40049
+ }
40050
+ }
40051
+ fragments.push({ ...identity ? { identity } : {}, segments });
40052
+ }
40053
+ }
40054
+ const summary = registerNoteText(recording.values.get("summary"));
40055
+ const topLineSummary = registerNoteText(recording.values.get("topLineSummary"));
40056
+ return {
40057
+ fragments,
40058
+ ...summary ? { summary } : {},
40059
+ ...topLineSummary ? { topLineSummary } : {}
40060
+ };
40061
+ }
40062
+ function joinSegments(segments) {
40063
+ let out = "";
40064
+ for (const { text } of segments) {
40065
+ if (!text) continue;
40066
+ const glue = !out || /\s$/u.test(out) || /^\s/u.test(text) || /^[.,!?;:%)\]}…'’]/u.test(text) ? "" : " ";
40067
+ out += glue + text;
40068
+ }
40069
+ return out.trim();
40070
+ }
40071
+ function parseNoteId(noteId3) {
40072
+ const match = /^x-coredata:\/\/([0-9A-Fa-f-]+)\/ICNote\/p(\d+)$/.exec(noteId3);
40073
+ if (!match) {
40074
+ throw new AudioTranscriptError(
40075
+ "invalid_id",
40076
+ `Invalid note ID format: "${noteId3}". Expected format: x-coredata://UUID/ICNote/pNNN`
40077
+ );
40078
+ }
40079
+ return { store: match[1], pk: match[2] };
40080
+ }
40081
+ var AUDIO_UTI_SQL = "(a.ZTYPEUTI LIKE '%audio%' OR a.ZTYPEUTI IN ('public.mp3'))";
40082
+ function buildTranscriptSql(pk, available) {
40083
+ if (!/^\d+$/.test(pk)) throw new Error("Invalid primary key");
40084
+ const optional2 = (column, key, alias = "a") => available.has(column) ? `, '${key}', ${alias}.${column}` : "";
40085
+ const locked = available.has("ZISPASSWORDPROTECTED") ? "COALESCE(n.ZISPASSWORDPROTECTED, 0)" : "0";
40086
+ return [
40087
+ "BEGIN;",
40088
+ // Every statement yields exactly one row, so output lines stay positional.
40089
+ `SELECT json_object('found', (SELECT count(*) FROM ZICCLOUDSYNCINGOBJECT n WHERE n.Z_PK = ${pk}), 'locked', (SELECT ${locked} FROM ZICCLOUDSYNCINGOBJECT n WHERE n.Z_PK = ${pk}));`,
40090
+ `SELECT COALESCE((SELECT hex(ZDATA) FROM ZICNOTEDATA WHERE ZNOTE = ${pk} LIMIT 1), '');`,
40091
+ "SELECT json_group_array(json_object('pk', a.Z_PK, 'identifier', a.ZIDENTIFIER, 'uti', a.ZTYPEUTI, 'data', hex(a.ZMERGEABLEDATA1)" + optional2("ZDURATION", "duration") + optional2("ZNEEDSTRANSCRIPTION", "needsTranscription") + ", 'fragments', json((SELECT json_group_array(json_object('identifier', c.ZIDENTIFIER" + optional2("ZDURATION", "duration", "c") + `)) FROM ZICCLOUDSYNCINGOBJECT c WHERE c.ZPARENTATTACHMENT = a.Z_PK)))) FROM ZICCLOUDSYNCINGOBJECT a WHERE a.ZNOTE = ${pk} AND a.ZPARENTATTACHMENT IS NULL AND ${AUDIO_UTI_SQL};`,
40092
+ "COMMIT;"
40093
+ ].join("\n");
40094
+ }
40095
+ var REQUIRED_COLUMNS = [
40096
+ "ZMERGEABLEDATA1",
40097
+ "ZPARENTATTACHMENT",
40098
+ "ZTYPEUTI",
40099
+ "ZNOTE",
40100
+ "ZIDENTIFIER"
40101
+ ];
40102
+ function runSqlite(dbPath2, sql) {
40103
+ return execFileSync4("sqlite3", ["-readonly", dbPath2, sql], {
40104
+ encoding: "utf8",
40105
+ timeout: 1e4,
40106
+ maxBuffer: 256 * 1024 * 1024,
40107
+ stdio: ["pipe", "pipe", "pipe"]
40108
+ });
40109
+ }
40110
+ function bodyAttachmentOrder(gzipped) {
40111
+ const doc = decodeMessage(gunzipSync3(gzipped, { maxOutputLength: 64 * 1024 * 1024 }));
40112
+ const body = embeddedMessage(getField(embeddedMessage(getField(doc, 2)) ?? [], 3));
40113
+ if (!body) throw new Error("Unsupported Notes document structure");
40114
+ const ids = [];
40115
+ for (const run of getFields(body, 5)) {
40116
+ const attachment = embeddedMessage(getField(embeddedMessage(run) ?? [], 12));
40117
+ const id2 = attachment && stringValue(getField(attachment, 1));
40118
+ if (id2 && !ids.includes(id2)) ids.push(id2);
40119
+ }
40120
+ return ids;
40121
+ }
40122
+ function readAudioTranscripts(noteId3, options = {}) {
40123
+ const { store, pk } = parseNoteId(noteId3);
40124
+ const dbPath2 = options.dbPath ?? NOTES_DB_PATH2;
40125
+ const maxSegments = Math.min(
40126
+ Math.max(1, Math.floor(options.maxSegments ?? DEFAULT_MAX_SEGMENTS)),
40127
+ MAX_SEGMENTS_LIMIT
40128
+ );
40129
+ let lines;
40130
+ try {
40131
+ const available = new Set(
40132
+ runSqlite(dbPath2, "SELECT name FROM pragma_table_info('ZICCLOUDSYNCINGOBJECT');").split("\n").map((line) => line.trim()).filter(Boolean)
40133
+ );
40134
+ const missing = REQUIRED_COLUMNS.filter((c) => !available.has(c));
40135
+ if (missing.length)
40136
+ throw new AudioTranscriptError(
40137
+ "query_error",
40138
+ `This macOS version's Notes database lacks ${missing.join(", ")}; stored transcripts cannot be read.`
40139
+ );
40140
+ lines = runSqlite(dbPath2, buildTranscriptSql(pk, available)).split("\n");
40141
+ } catch (error2) {
40142
+ if (error2 instanceof AudioTranscriptError) throw error2;
40143
+ const message = error2 instanceof Error ? error2.message : String(error2);
40144
+ if (/authorization denied|unable to open database/i.test(message))
40145
+ throw new AudioTranscriptError(
40146
+ "no_fda",
40147
+ `Full Disk Access is required to read stored transcripts. Grant it to the app that launches this server, then fully quit and relaunch it. Setup guide: ${FULL_DISK_ACCESS_GUIDE_URL}`
40148
+ );
40149
+ throw new AudioTranscriptError("query_error", "Failed to read the Notes database.");
40150
+ }
40151
+ const [noteLine = "{}", bodyLine = "", rowsLine = "[]"] = lines;
40152
+ const note = JSON.parse(noteLine || "{}");
40153
+ if (!note.found)
40154
+ throw new AudioTranscriptError(
40155
+ "not_found",
40156
+ `No note found in the database for ID "${noteId3}".`
40157
+ );
40158
+ if (note.locked)
40159
+ throw new AudioTranscriptError(
40160
+ "locked",
40161
+ "This note is password-protected; its transcripts are encrypted and cannot be read."
40162
+ );
40163
+ const rows = JSON.parse(rowsLine || "[]");
40164
+ let order;
40165
+ try {
40166
+ if (/^[0-9A-F]+$/i.test(bodyLine.trim()))
40167
+ order = bodyAttachmentOrder(Buffer.from(bodyLine.trim(), "hex"));
40168
+ } catch {
40169
+ order = void 0;
40170
+ }
40171
+ const ordered = order ? order.flatMap((id2) => rows.filter((row) => row.identifier === id2)) : [...rows].sort((a, b) => a.pk - b.pk);
40172
+ const attachments = ordered.map((row) => describeRow(row, store, options, maxSegments));
40173
+ return { id: noteId3, attachments, bodyOrder: Boolean(order), truncated: false };
40174
+ }
40175
+ function describeRow(row, store, options, maxSegments) {
40176
+ const childDurations = (row.fragments ?? []).map((f) => f.duration).filter((d) => typeof d === "number" && d > 0);
40177
+ const durationSeconds = typeof row.duration === "number" && row.duration > 0 ? row.duration : childDurations.length ? childDurations.reduce((a, b) => a + b, 0) : void 0;
40178
+ const base = {
40179
+ attachmentId: `x-coredata://${store}/ICAttachment/p${row.pk}`,
40180
+ identifier: row.identifier,
40181
+ typeUti: row.uti,
40182
+ status: "none",
40183
+ ...durationSeconds !== void 0 ? { durationSeconds } : {},
40184
+ ...typeof row.needsTranscription === "number" ? { needsTranscription: row.needsTranscription === 1 } : {}
40185
+ };
40186
+ if (!row.data) return base;
40187
+ let recording;
40188
+ try {
40189
+ recording = parseAudioRecording(Buffer.from(row.data, "hex"));
40190
+ } catch (error2) {
40191
+ return {
40192
+ ...base,
40193
+ status: "undecodable",
40194
+ reason: error2 instanceof Error ? error2.message : String(error2)
40195
+ };
40196
+ }
40197
+ const multi = recording.fragments.length > 1;
40198
+ const all = recording.fragments.flatMap(
40199
+ (fragment, i) => fragment.segments.map((s) => multi ? { ...s, fragment: i } : s)
40200
+ );
40201
+ const text = recording.fragments.map((f) => joinSegments(f.segments)).filter(Boolean).join("\n\n");
40202
+ const speakers = [...new Set(all.flatMap((s) => s.speaker ? [s.speaker] : []))];
40203
+ return {
40204
+ ...base,
40205
+ status: all.length ? "ok" : "none",
40206
+ fragmentCount: recording.fragments.length,
40207
+ wordCount: all.length,
40208
+ ...text ? { text } : {},
40209
+ ...speakers.length ? { speakers } : {},
40210
+ ...recording.summary ? { summary: recording.summary } : {},
40211
+ ...recording.topLineSummary ? { topLineSummary: recording.topLineSummary } : {},
40212
+ ...options.includeSegments && all.length ? {
40213
+ segments: all.slice(0, maxSegments),
40214
+ ...all.length > maxSegments ? { segmentsTruncated: true } : {}
40215
+ } : {}
40216
+ };
40217
+ }
40218
+ function fitTranscriptsToBudget(result, maxBytes, measure) {
40219
+ if (measure(result) <= maxBytes) return result;
40220
+ let next = {
40221
+ ...result,
40222
+ truncated: true,
40223
+ attachments: result.attachments.map((a) => {
40224
+ if (!a.segments) return a;
40225
+ const rest = { ...a, segmentsTruncated: true };
40226
+ delete rest.segments;
40227
+ return rest;
40228
+ })
40229
+ };
40230
+ for (let share = 0.5; measure(next) > maxBytes && share > 1e-4; share /= 2) {
40231
+ next = {
40232
+ ...next,
40233
+ attachments: next.attachments.map((a) => {
40234
+ const original = result.attachments.find((o) => o.attachmentId === a.attachmentId);
40235
+ if (!original?.text) return a;
40236
+ const keep = Math.floor(original.text.length * share);
40237
+ return { ...a, text: original.text.slice(0, keep), textTruncated: true };
40238
+ })
40239
+ };
40240
+ }
40241
+ return next;
40242
+ }
40243
+ var clock = (seconds) => {
40244
+ const total = Math.round(seconds);
40245
+ const h = Math.floor(total / 3600);
40246
+ const m = Math.floor(total % 3600 / 60);
40247
+ const s = total % 60;
40248
+ const mm = h ? String(m).padStart(2, "0") : String(m);
40249
+ return `${h ? `${h}:` : ""}${mm}:${String(s).padStart(2, "0")}`;
40250
+ };
40251
+ function formatTranscriptsText(result) {
40252
+ const count = result.attachments.length;
40253
+ if (count === 0) return `No audio attachments found in note ${result.id}.`;
40254
+ const lines = [`${count} audio attachment${count === 1 ? "" : "s"} in note ${result.id}:`];
40255
+ result.attachments.forEach((a, i) => {
40256
+ const facts = [
40257
+ `status ${a.status}`,
40258
+ ...a.durationSeconds !== void 0 ? [clock(a.durationSeconds)] : [],
40259
+ ...a.wordCount !== void 0 ? [`${a.wordCount} words`] : [],
40260
+ ...a.fragmentCount && a.fragmentCount > 1 ? [`${a.fragmentCount} fragments`] : [],
40261
+ ...a.speakers?.length ? [`${a.speakers.length} speakers`] : [],
40262
+ ...a.reason ? [`reason: ${a.reason}`] : []
40263
+ ];
40264
+ lines.push("", `[${i + 1}] ${a.attachmentId} (${facts.join(", ")})`);
40265
+ if (a.summary) lines.push(`Summary: ${a.summary}`);
40266
+ if (a.text) lines.push(a.textTruncated ? `${a.text} [truncated]` : a.text);
40267
+ });
40268
+ if (result.truncated)
40269
+ lines.push(
40270
+ "",
40271
+ "Some segments or text were left out to stay under the response size limit (APPLE_NOTES_MCP_EXPORT_MAX_BYTES)."
40272
+ );
40273
+ return lines.join("\n");
40274
+ }
40275
+
40276
+ // src/utils/noteTables.ts
40277
+ import { gunzipSync as gunzipSync4 } from "node:zlib";
40278
+ var sub2 = (f, n) => {
40279
+ const value = embeddedMessage(getField(f, n));
40280
+ if (!value) throw new Error(`Missing table field ${n}`);
40281
+ return value;
40282
+ };
40283
+ var num = (f, n) => {
40284
+ const v = varintValue(getField(f, n));
40285
+ if (v === void 0) throw new Error(`Missing table index ${n}`);
40286
+ return v;
40287
+ };
40288
+ var many2 = (f, n) => getFields(f, n).map((v) => {
40289
+ const m = embeddedMessage(v);
40290
+ if (!m) throw new Error("Invalid table entry");
40291
+ return m;
40292
+ });
40293
+ var hex = (f) => {
40294
+ if (!(f?.value instanceof Uint8Array)) throw new Error("Missing table UUID");
40295
+ return Buffer.from(f.value).toString("hex");
40296
+ };
40297
+ function parseNoteTable(compressed) {
40298
+ const table = decodeTable(compressed, true);
40299
+ return { rows: table.rows, rowIds: table.rowIds, columnIds: table.columnIds };
40300
+ }
40301
+ function parseNoteTableCells(compressed) {
40302
+ return decodeTable(compressed, false);
40303
+ }
40304
+ function decodeTable(compressed, strict) {
40305
+ const root = decodeMessage(gunzipSync4(compressed, { maxOutputLength: 16 * 1024 * 1024 }));
40306
+ const data = sub2(sub2(root, 2), 3), entries = many2(data, 3);
40307
+ if (entries.length > 1e5) throw new Error("Table too large");
40308
+ const keys = getFields(data, 4).map(stringValue), types = getFields(data, 5).map(stringValue), uuids = getFields(data, 6).map(hex);
40309
+ const entry = (index) => {
40310
+ if (!entries[index]) throw new Error("Invalid table reference");
40311
+ return entries[index];
40312
+ };
40313
+ const uuidIndex = (index) => num(sub2(many2(sub2(entry(index), 13), 3)[0], 2), 2);
40314
+ const roots = entries.filter((e) => {
40315
+ const map = embeddedMessage(getField(e, 13));
40316
+ return map && types[num(map, 1)] === "com.apple.notes.ICTable";
40317
+ });
40318
+ if (roots.length !== 1) throw new Error("Ambiguous native table root");
40319
+ const refs = new Map(
40320
+ many2(sub2(roots[0], 13), 3).filter((m) => ["crRows", "crColumns", "cellColumns"].includes(keys[num(m, 1)] || "")).map((m) => [keys[num(m, 1)], num(sub2(m, 2), 6)])
40321
+ );
40322
+ const ordered = (key) => {
40323
+ const ref = refs.get(key);
40324
+ if (ref === void 0) throw new Error("Missing table dimension");
40325
+ const ordering = sub2(sub2(entry(ref), 16), 1), array2 = sub2(ordering, 1);
40326
+ const ids = many2(array2, 2).map((a) => hex(getField(a, 2)));
40327
+ const map = /* @__PURE__ */ new Map();
40328
+ ids.forEach((id2, i) => {
40329
+ const index = uuids.indexOf(id2);
40330
+ if (index < 0) throw new Error("Missing dimension UUID");
40331
+ map.set(index, i);
40332
+ });
40333
+ const aliases = many2(sub2(ordering, 2), 1).map((pair) => [
40334
+ uuidIndex(num(sub2(pair, 1), 6)),
40335
+ uuidIndex(num(sub2(pair, 2), 6))
40336
+ ]);
40337
+ for (let pass = 0; pass < aliases.length + 1; pass++) {
40338
+ let changed = false;
40339
+ for (const [key2, value] of aliases)
40340
+ if (map.has(key2) && !map.has(value)) {
40341
+ map.set(value, map.get(key2));
40342
+ changed = true;
40343
+ }
40344
+ if (!changed) break;
40345
+ }
40346
+ return { ids, map };
40347
+ };
40348
+ const rows = ordered("crRows"), columns = ordered("crColumns");
40349
+ if (!rows.ids.length || !columns.ids.length || rows.ids.length * columns.ids.length > 1e5)
40350
+ throw new Error("Unsupported table size");
40351
+ const values = rows.ids.map(() => columns.ids.map(() => ""));
40352
+ const incomplete = [];
40353
+ const cellRef = refs.get("cellColumns");
40354
+ if (cellRef === void 0) throw new Error("Missing table cells");
40355
+ for (const column of many2(sub2(entry(cellRef), 6), 1)) {
40356
+ const ci = columns.map.get(uuidIndex(num(sub2(column, 1), 6)));
40357
+ const cells = entry(num(sub2(column, 2), 6));
40358
+ for (const row of many2(sub2(cells, 6), 1)) {
40359
+ const ri = rows.map.get(uuidIndex(num(sub2(row, 1), 6)));
40360
+ if (ri === void 0 || ci === void 0) continue;
40361
+ const note = sub2(entry(num(sub2(row, 2), 6)), 10);
40362
+ const text = stringValue(getField(note, 2));
40363
+ if (text === void 0 || text.includes("\uFFFC")) {
40364
+ if (strict) throw new Error("Embedded or unsupported table cell");
40365
+ values[ri][ci] = null;
40366
+ incomplete.push({
40367
+ ri,
40368
+ ci,
40369
+ reason: text === void 0 ? "Cell text is missing" : "Cell contains an embedded object"
40370
+ });
40371
+ continue;
40372
+ }
40373
+ values[ri][ci] = text.replace(/\n$/u, "");
40374
+ }
40375
+ }
40376
+ const rtl = entries.some((e) => {
40377
+ const map = embeddedMessage(getField(e, 13));
40378
+ return map && many2(map, 3).some(
40379
+ (m) => stringValue(getField(sub2(m, 2), 4)) === "CRTableColumnDirectionRightToLeft"
40380
+ );
40381
+ });
40382
+ const width = columns.ids.length;
40383
+ if (rtl) {
40384
+ for (const row of values) row.reverse();
40385
+ columns.ids.reverse();
40386
+ }
40387
+ const incompleteCells = incomplete.map(({ ri, ci, reason }) => ({ row: ri, column: rtl ? width - 1 - ci : ci, reason })).sort((a, b) => a.row - b.row || a.column - b.column);
40388
+ return { rows: values, rowIds: rows.ids, columnIds: columns.ids, incompleteCells };
40389
+ }
40390
+
40391
+ // src/utils/tableMarkdown.ts
40392
+ var UNDECODED_CELL_MARKER = "[undecoded cell]";
40393
+ function escapeMarkdownTableCell(text) {
40394
+ return text.replace(/\\/g, "\\\\").replace(/\|/g, "\\|").replace(/\r\n|[\r\n\u2028\u2029]/g, "<br>");
40395
+ }
40396
+ function renderMarkdownTable(rows) {
40397
+ const width = rows.reduce((max, row) => Math.max(max, row.length), 0);
40398
+ if (rows.length === 0 || width === 0) return "";
40399
+ const line = (row) => "| " + Array.from({ length: width }, (_, i) => {
40400
+ const cell = row[i];
40401
+ return cell === null ? UNDECODED_CELL_MARKER : escapeMarkdownTableCell(cell ?? "");
40402
+ }).join(" | ") + " |";
40403
+ const [header, ...body] = rows;
40404
+ return [line(header), "| " + Array(width).fill("---").join(" | ") + " |", ...body.map(line)].join(
40405
+ "\n"
40406
+ );
40407
+ }
40408
+ function collectNoteTables(rich, noteId3) {
40409
+ const seen = /* @__PURE__ */ new Set();
40410
+ const ordered = (rich.objects || []).filter((object3) => object3.type.includes("table")).sort((a, b) => a.start - b.start).filter((object3) => {
40411
+ if (seen.has(object3.id)) return false;
40412
+ seen.add(object3.id);
40413
+ return true;
40414
+ });
40415
+ const tables = ordered.map((object3, i) => {
40416
+ const index = i + 1;
40417
+ const data = (rich.objectData || []).find((row) => row.id === object3.id);
40418
+ if (!data || !data.mergeable) {
40419
+ return { index, id: object3.id, complete: false, reason: "Native table data is unavailable" };
40420
+ }
40421
+ const attachmentId = noteId3.replace(/ICNote\/p\d+$/, `ICAttachment/p${data.pk}`);
40422
+ try {
40423
+ const table = parseNoteTableCells(Buffer.from(data.mergeable, "hex"));
40424
+ const complete = table.incompleteCells.length === 0;
40425
+ return {
40426
+ index,
40427
+ id: object3.id,
40428
+ attachmentId,
40429
+ complete,
40430
+ ...complete ? {} : { reason: `${table.incompleteCells.length} cell(s) could not be decoded` },
40431
+ rows: table.rows,
40432
+ rowIds: table.rowIds,
40433
+ columnIds: table.columnIds,
40434
+ rowCount: table.rowIds.length,
40435
+ columnCount: table.columnIds.length,
40436
+ incompleteCells: table.incompleteCells,
40437
+ markdown: renderMarkdownTable(table.rows)
40438
+ };
40439
+ } catch (error2) {
40440
+ return {
40441
+ index,
40442
+ id: object3.id,
40443
+ attachmentId,
40444
+ complete: false,
40445
+ reason: error2 instanceof Error ? error2.message : String(error2)
40446
+ };
40447
+ }
40448
+ });
40449
+ const markdown = tables.map(
40450
+ (table) => table.markdown !== void 0 ? table.markdown : `[table ${table.index} could not be decoded: ${table.reason}]`
40451
+ ).join("\n\n");
40452
+ return { tables, tableCellsComplete: tables.every((table) => table.complete), markdown };
40453
+ }
40454
+
39904
40455
  // src/utils/smartFolders.ts
39905
- import { execFileSync as execFileSync4 } from "child_process";
40456
+ import { execFileSync as execFileSync5 } from "child_process";
39906
40457
  import * as fs2 from "fs";
39907
40458
  import * as os2 from "os";
39908
40459
  import * as path2 from "path";
39909
- var NOTES_DB_PATH2 = path2.join(
40460
+ var NOTES_DB_PATH3 = path2.join(
39910
40461
  os2.homedir(),
39911
40462
  "Library/Group Containers/group.com.apple.notes/NoteStore.sqlite"
39912
40463
  );
@@ -40197,7 +40748,7 @@ WHERE Z_ENT = (SELECT Z_ENT FROM Z_PRIMARYKEY WHERE Z_NAME = 'ICHashtag')
40197
40748
  AND ZSTANDARDIZEDCONTENT IS NOT NULL
40198
40749
  AND COALESCE(ZMARKEDFORDELETION, 0) = 0;
40199
40750
  COMMIT;`;
40200
- var REQUIRED_COLUMNS = [
40751
+ var REQUIRED_COLUMNS2 = [
40201
40752
  "ZIDENTIFIER",
40202
40753
  "ZTITLE2",
40203
40754
  "ZFOLDERTYPE",
@@ -40251,21 +40802,21 @@ function buildSmartFolders(output) {
40251
40802
  (a, b) => (a.account ?? "").localeCompare(b.account ?? "") || (a.name ?? "").localeCompare(b.name ?? "") || a.id.localeCompare(b.id)
40252
40803
  );
40253
40804
  }
40254
- function runSqlite(dbPath2, sql) {
40255
- return execFileSync4("sqlite3", ["-readonly", dbPath2, sql], {
40805
+ function runSqlite2(dbPath2, sql) {
40806
+ return execFileSync5("sqlite3", ["-readonly", dbPath2, sql], {
40256
40807
  encoding: "utf8",
40257
40808
  timeout: 5e3,
40258
40809
  maxBuffer: 16 * 1024 * 1024,
40259
40810
  stdio: ["pipe", "pipe", "pipe"]
40260
40811
  });
40261
40812
  }
40262
- function readSmartFolders(dbPath2 = NOTES_DB_PATH2) {
40813
+ function readSmartFolders(dbPath2 = NOTES_DB_PATH3) {
40263
40814
  if (!fs2.existsSync(dbPath2)) return { folders: null, error: "no_fda", message: FDA_MESSAGE };
40264
40815
  try {
40265
40816
  const columns = new Set(
40266
- runSqlite(dbPath2, "SELECT name FROM pragma_table_info('ZICCLOUDSYNCINGOBJECT');").trim().split("\n")
40817
+ runSqlite2(dbPath2, "SELECT name FROM pragma_table_info('ZICCLOUDSYNCINGOBJECT');").trim().split("\n")
40267
40818
  );
40268
- const missing = REQUIRED_COLUMNS.filter((column) => !columns.has(column));
40819
+ const missing = REQUIRED_COLUMNS2.filter((column) => !columns.has(column));
40269
40820
  if (missing.length) {
40270
40821
  return {
40271
40822
  folders: null,
@@ -40273,7 +40824,7 @@ function readSmartFolders(dbPath2 = NOTES_DB_PATH2) {
40273
40824
  message: `This Notes database does not have the columns smart folders need (${missing.join(", ")}).`
40274
40825
  };
40275
40826
  }
40276
- return { folders: buildSmartFolders(runSqlite(dbPath2, SMART_FOLDERS_SQL)) };
40827
+ return { folders: buildSmartFolders(runSqlite2(dbPath2, SMART_FOLDERS_SQL)) };
40277
40828
  } catch (error2) {
40278
40829
  const message = error2 instanceof Error ? error2.message : String(error2);
40279
40830
  if (message.includes("authorization denied") || message.includes("unable to open database")) {
@@ -40295,11 +40846,11 @@ import {
40295
40846
  rmSync,
40296
40847
  statSync
40297
40848
  } from "fs";
40298
- import { dirname, isAbsolute, join as join4, relative, resolve, sep } from "path";
40299
- import { homedir as homedir4, tmpdir } from "os";
40849
+ import { dirname, isAbsolute, join as join5, relative, resolve, sep } from "path";
40850
+ import { homedir as homedir5, tmpdir } from "os";
40300
40851
  function allowedSaveRoots() {
40301
40852
  return [
40302
- resolve(homedir4()),
40853
+ resolve(homedir5()),
40303
40854
  resolve(tmpdir()),
40304
40855
  "/Volumes",
40305
40856
  "/private/var/folders",
@@ -40373,7 +40924,7 @@ function assertSafeSavePath(p, roots = allowedSaveRoots()) {
40373
40924
  if (suffix.split(sep).includes("..")) {
40374
40925
  throw new Error(`Refusing to write outside allowed locations (home, temp, /Volumes): "${abs}"`);
40375
40926
  }
40376
- const canonicalDest = suffix ? join4(canonicalAncestor, suffix) : canonicalAncestor;
40927
+ const canonicalDest = suffix ? join5(canonicalAncestor, suffix) : canonicalAncestor;
40377
40928
  const allowed = canonicalRoots(roots);
40378
40929
  if (!isWithinRoots(canonicalAncestor, allowed) || !isWithinRoots(canonicalDest, allowed)) {
40379
40930
  throw new Error(
@@ -40423,8 +40974,8 @@ function cleanupTempDir(dir) {
40423
40974
  // src/services/appleNotesManager.ts
40424
40975
  var import_turndown = __toESM(require_turndown_cjs(), 1);
40425
40976
  import { existsSync as existsSync4 } from "fs";
40426
- import { homedir as homedir5 } from "os";
40427
- import { join as join5 } from "path";
40977
+ import { homedir as homedir6 } from "os";
40978
+ import { join as join6 } from "path";
40428
40979
  var FIELD_SEP = "";
40429
40980
  var RECORD_SEP = "";
40430
40981
  var AS_FIELD_SEP = "(character id 31)";
@@ -40652,7 +41203,7 @@ function getNoteLinkFromDB(coreDataId2) {
40652
41203
  const match = coreDataId2.match(/\/p(\d+)$/);
40653
41204
  if (!match) return null;
40654
41205
  const pk = parseInt(match[1], 10);
40655
- const dbPath2 = join5(homedir5(), "Library/Group Containers/group.com.apple.notes/NoteStore.sqlite");
41206
+ const dbPath2 = join6(homedir6(), "Library/Group Containers/group.com.apple.notes/NoteStore.sqlite");
40656
41207
  if (!existsSync4(dbPath2)) return null;
40657
41208
  try {
40658
41209
  const { DatabaseSync } = __require("node:sqlite");
@@ -41094,6 +41645,21 @@ var AppleNotesManager = class {
41094
41645
  passwordProtected: parsed.passwordProtected
41095
41646
  };
41096
41647
  }
41648
+ /**
41649
+ * Reads every native table in one note, in body order, as JSON rows and
41650
+ * GitHub-flavored Markdown.
41651
+ *
41652
+ * Reads the NoteStore database read-only (Full Disk Access required); no
41653
+ * AppleScript is involved. Cells that cannot be decoded are returned as null
41654
+ * and flagged, never guessed.
41655
+ *
41656
+ * @param id - CoreData URL identifier for the note
41657
+ * @returns Tables in body order with a combined Markdown rendering
41658
+ * @throws Error when the note's rich data cannot be read (e.g. no Full Disk Access)
41659
+ */
41660
+ getNoteTablesById(id2) {
41661
+ return collectNoteTables(readRichNote(id2), id2);
41662
+ }
41097
41663
  /**
41098
41664
  * Retrieves detailed metadata for a note by title.
41099
41665
  *
@@ -42946,18 +43512,31 @@ var AppleNotesManager = class {
42946
43512
  }
42947
43513
  return markdown;
42948
43514
  }
43515
+ /**
43516
+ * Reads the transcripts Notes has stored for a note's top-level audio
43517
+ * recordings, one entry per attachment in body order. Read-only: queries the
43518
+ * NoteStore database with `sqlite3 -readonly` and needs Full Disk Access.
43519
+ *
43520
+ * @param id - CoreData URL identifier for the note
43521
+ * @param options - word-level segment inclusion and cap
43522
+ * @throws AudioTranscriptError for an invalid id, a missing or locked note,
43523
+ * missing Full Disk Access, or a database read failure
43524
+ */
43525
+ getAudioTranscripts(id2, options = {}) {
43526
+ return readAudioTranscripts(id2, options);
43527
+ }
42949
43528
  };
42950
43529
 
42951
43530
  // src/utils/syncDetection.ts
42952
- import { execFileSync as execFileSync5 } from "child_process";
43531
+ import { execFileSync as execFileSync6 } from "child_process";
42953
43532
  import * as fs3 from "fs";
42954
43533
  import * as path3 from "path";
42955
43534
  import * as os3 from "os";
42956
- var NOTES_DB_PATH3 = path3.join(
43535
+ var NOTES_DB_PATH4 = path3.join(
42957
43536
  os3.homedir(),
42958
43537
  "Library/Group Containers/group.com.apple.notes/NoteStore.sqlite"
42959
43538
  );
42960
- var WAL_PATH = `${NOTES_DB_PATH3}-wal`;
43539
+ var WAL_PATH = `${NOTES_DB_PATH4}-wal`;
42961
43540
  var RECENT_ACTIVITY_THRESHOLD_SECONDS = 5;
42962
43541
  var SYNC_STATUS_CACHE_TTL_MS = 2e3;
42963
43542
  var cachedSyncStatus = null;
@@ -42973,7 +43552,7 @@ function getSyncStatus(useCache = true) {
42973
43552
  recentActivity: false
42974
43553
  };
42975
43554
  try {
42976
- if (!fs3.existsSync(NOTES_DB_PATH3)) {
43555
+ if (!fs3.existsSync(NOTES_DB_PATH4)) {
42977
43556
  status.error = "Notes database not found";
42978
43557
  cachedSyncStatus = status;
42979
43558
  cacheTimestamp = Date.now();
@@ -42994,9 +43573,9 @@ function getSyncStatus(useCache = true) {
42994
43573
  WHERE object.ZCLOUDSTATE = state.Z_PK
42995
43574
  );
42996
43575
  `;
42997
- const result = execFileSync5(
43576
+ const result = execFileSync6(
42998
43577
  "sqlite3",
42999
- ["-readonly", NOTES_DB_PATH3, query.replace(/\n/g, " ")],
43578
+ ["-readonly", NOTES_DB_PATH4, query.replace(/\n/g, " ")],
43000
43579
  {
43001
43580
  encoding: "utf8",
43002
43581
  timeout: 5e3,
@@ -43053,11 +43632,11 @@ function withSyncAwarenessSync(operation, fn) {
43053
43632
  }
43054
43633
 
43055
43634
  // src/utils/noteMetadata.ts
43056
- import { execFileSync as execFileSync6 } from "child_process";
43635
+ import { execFileSync as execFileSync7 } from "child_process";
43057
43636
  import * as fs4 from "fs";
43058
43637
  import * as path4 from "path";
43059
43638
  import * as os4 from "os";
43060
- var NOTES_DB_PATH4 = path4.join(
43639
+ var NOTES_DB_PATH5 = path4.join(
43061
43640
  os4.homedir(),
43062
43641
  "Library/Group Containers/group.com.apple.notes/NoteStore.sqlite"
43063
43642
  );
@@ -43073,15 +43652,15 @@ var COLUMN_MAP = [
43073
43652
  { key: "widgetSnippet", column: "ZWIDGETSNIPPET", type: "text" },
43074
43653
  { key: "smartFolderQuery", column: "ZSMARTFOLDERQUERYJSON", type: "text" }
43075
43654
  ];
43076
- function runSqlite2(query) {
43077
- return execFileSync6("sqlite3", ["-readonly", NOTES_DB_PATH4, query], {
43655
+ function runSqlite3(query) {
43656
+ return execFileSync7("sqlite3", ["-readonly", NOTES_DB_PATH5, query], {
43078
43657
  encoding: "utf8",
43079
43658
  timeout: 5e3,
43080
43659
  stdio: ["pipe", "pipe", "pipe"]
43081
43660
  }).trim();
43082
43661
  }
43083
43662
  function presentColumns() {
43084
- const out = runSqlite2("PRAGMA table_info(ZICCLOUDSYNCINGOBJECT);");
43663
+ const out = runSqlite3("PRAGMA table_info(ZICCLOUDSYNCINGOBJECT);");
43085
43664
  const cols = /* @__PURE__ */ new Set();
43086
43665
  for (const line of out.split("\n")) {
43087
43666
  const name = line.split("|")[1];
@@ -43099,7 +43678,7 @@ function getNoteMetadata(noteId3) {
43099
43678
  };
43100
43679
  }
43101
43680
  const pk = pkMatch[1];
43102
- if (!fs4.existsSync(NOTES_DB_PATH4)) {
43681
+ if (!fs4.existsSync(NOTES_DB_PATH5)) {
43103
43682
  return { metadata: null, error: "no_fda", message: FDA_MESSAGE2 };
43104
43683
  }
43105
43684
  try {
@@ -43109,7 +43688,7 @@ function getNoteMetadata(noteId3) {
43109
43688
  return { metadata: {} };
43110
43689
  }
43111
43690
  const pairs = selected.map((c) => `'${c.key}', ${c.column}`).join(", ");
43112
- const row = runSqlite2(
43691
+ const row = runSqlite3(
43113
43692
  `SELECT json_object(${pairs}) FROM ZICCLOUDSYNCINGOBJECT WHERE Z_PK = ${pk};`
43114
43693
  );
43115
43694
  if (!row) {
@@ -43142,11 +43721,11 @@ function getNoteMetadata(noteId3) {
43142
43721
  }
43143
43722
 
43144
43723
  // src/utils/noteIdentifiers.ts
43145
- import { execFileSync as execFileSync7 } from "child_process";
43724
+ import { execFileSync as execFileSync8 } from "child_process";
43146
43725
  import * as fs5 from "fs";
43147
43726
  import * as os5 from "os";
43148
43727
  import * as path5 from "path";
43149
- var NOTES_DB_PATH5 = path5.join(
43728
+ var NOTES_DB_PATH6 = path5.join(
43150
43729
  os5.homedir(),
43151
43730
  "Library/Group Containers/group.com.apple.notes/NoteStore.sqlite"
43152
43731
  );
@@ -43224,7 +43803,7 @@ function runJsonQuery(sql, dbPath2) {
43224
43803
  if (!fs5.existsSync(dbPath2)) throw new IdentifierResolutionError("no_fda", NO_FDA_MESSAGE);
43225
43804
  let out;
43226
43805
  try {
43227
- out = execFileSync7("sqlite3", ["-readonly", dbPath2, sql], {
43806
+ out = execFileSync8("sqlite3", ["-readonly", dbPath2, sql], {
43228
43807
  encoding: "utf8",
43229
43808
  timeout: 5e3,
43230
43809
  stdio: ["pipe", "pipe", "pipe"]
@@ -43244,7 +43823,7 @@ function runJsonQuery(sql, dbPath2) {
43244
43823
  function coreDataId(store, entity2, pk) {
43245
43824
  return `x-coredata://${store}/${entity2}/p${pk}`;
43246
43825
  }
43247
- function resolveIdentifiers(values, entity2, dbPath2 = NOTES_DB_PATH5) {
43826
+ function resolveIdentifiers(values, entity2, dbPath2 = NOTES_DB_PATH6) {
43248
43827
  const resolved = /* @__PURE__ */ new Map();
43249
43828
  const keys = [];
43250
43829
  const uuids = [];
@@ -43290,7 +43869,7 @@ function resolveIdentifiers(values, entity2, dbPath2 = NOTES_DB_PATH5) {
43290
43869
  return resolved;
43291
43870
  }
43292
43871
  var COREDATA_PARTS = /^x-coredata:\/\/([0-9A-Fa-f-]+)\/(ICNote|ICFolder|ICAccount)\/p(\d{1,18})$/;
43293
- function lookupStableIdentifiers(ids, entity2, dbPath2 = NOTES_DB_PATH5) {
43872
+ function lookupStableIdentifiers(ids, entity2, dbPath2 = NOTES_DB_PATH6) {
43294
43873
  const result = /* @__PURE__ */ new Map();
43295
43874
  const wanted = /* @__PURE__ */ new Map();
43296
43875
  for (const id2 of ids) {
@@ -43326,7 +43905,7 @@ function lookupStableIdentifiers(ids, entity2, dbPath2 = NOTES_DB_PATH5) {
43326
43905
  }
43327
43906
  return result;
43328
43907
  }
43329
- function withStableIdentifiers(items, entity2, dbPath2 = NOTES_DB_PATH5) {
43908
+ function withStableIdentifiers(items, entity2, dbPath2 = NOTES_DB_PATH6) {
43330
43909
  const ids = items.map((item) => item.id).filter((id2) => typeof id2 === "string");
43331
43910
  if (ids.length === 0) return items;
43332
43911
  const found = lookupStableIdentifiers(ids, entity2, dbPath2);
@@ -43914,12 +44493,12 @@ function positiveTextTerms(node, negated = false) {
43914
44493
  }
43915
44494
 
43916
44495
  // src/utils/noteQueryStore.ts
43917
- import { execFileSync as execFileSync8 } from "child_process";
44496
+ import { execFileSync as execFileSync9 } from "child_process";
43918
44497
  import * as fs6 from "fs";
43919
44498
  import * as os6 from "os";
43920
44499
  import * as path6 from "path";
43921
- import { gunzipSync as gunzipSync3 } from "zlib";
43922
- var NOTES_DB_PATH6 = path6.join(
44500
+ import { gunzipSync as gunzipSync5 } from "zlib";
44501
+ var NOTES_DB_PATH7 = path6.join(
43923
44502
  os6.homedir(),
43924
44503
  "Library/Group Containers/group.com.apple.notes/NoteStore.sqlite"
43925
44504
  );
@@ -44007,13 +44586,13 @@ function countWords(text) {
44007
44586
  }
44008
44587
  return count;
44009
44588
  }
44010
- var REQUIRED_COLUMNS2 = ["Z_PK", "Z_ENT", "ZTITLE1", "ZFOLDER", "ZMODIFICATIONDATE1"];
44589
+ var REQUIRED_COLUMNS3 = ["Z_PK", "Z_ENT", "ZTITLE1", "ZFOLDER", "ZMODIFICATIONDATE1"];
44011
44590
  function col(available, alias, name) {
44012
44591
  return available.has(name) ? `${alias}.${name}` : "NULL";
44013
44592
  }
44014
44593
  var entity = (name) => `(SELECT Z_ENT FROM Z_PRIMARYKEY WHERE Z_NAME='${name}')`;
44015
44594
  function buildScanSql(available, options) {
44016
- const missing = REQUIRED_COLUMNS2.filter((c) => !available.has(c));
44595
+ const missing = REQUIRED_COLUMNS3.filter((c) => !available.has(c));
44017
44596
  if (missing.length) {
44018
44597
  throw new NoteQueryStoreError(
44019
44598
  `This macOS version's Notes database lacks columns query-notes needs (${missing.join(", ")}).`,
@@ -44054,8 +44633,8 @@ function buildScanSql(available, options) {
44054
44633
  "COMMIT;"
44055
44634
  ].join(" ");
44056
44635
  }
44057
- function runSqlite3(dbPath2, query) {
44058
- return execFileSync8("sqlite3", ["-readonly", dbPath2, query], {
44636
+ function runSqlite4(dbPath2, query) {
44637
+ return execFileSync9("sqlite3", ["-readonly", dbPath2, query], {
44059
44638
  encoding: "utf8",
44060
44639
  timeout: 3e4,
44061
44640
  maxBuffer: 512 * 1024 * 1024,
@@ -44064,7 +44643,7 @@ function runSqlite3(dbPath2, query) {
44064
44643
  }
44065
44644
  function presentColumns2(dbPath2) {
44066
44645
  const cols = /* @__PURE__ */ new Set();
44067
- for (const line of runSqlite3(dbPath2, "PRAGMA table_info(ZICCLOUDSYNCINGOBJECT);").split("\n")) {
44646
+ for (const line of runSqlite4(dbPath2, "PRAGMA table_info(ZICCLOUDSYNCINGOBJECT);").split("\n")) {
44068
44647
  const name = line.split("|")[1];
44069
44648
  if (name) cols.add(name);
44070
44649
  }
@@ -44119,12 +44698,12 @@ function queryNotes(expression, options = {}) {
44119
44698
  const includeDeleted = options.includeDeleted ?? false;
44120
44699
  const withBodies = needsContent(ast);
44121
44700
  const withTags = needsTags(ast);
44122
- const dbPath2 = options.dbPath ?? NOTES_DB_PATH6;
44701
+ const dbPath2 = options.dbPath ?? NOTES_DB_PATH7;
44123
44702
  if (!fs6.existsSync(dbPath2)) throw new NoteQueryStoreError(QUERY_FDA_MESSAGE, "no_fda");
44124
44703
  let output;
44125
44704
  try {
44126
44705
  const available = presentColumns2(dbPath2);
44127
- output = runSqlite3(
44706
+ output = runSqlite4(
44128
44707
  dbPath2,
44129
44708
  buildScanSql(available, { scanLimit, includeDeleted, withBodies, withTags })
44130
44709
  );
@@ -44169,7 +44748,7 @@ function queryNotes(expression, options = {}) {
44169
44748
  const locked = Boolean(row.locked);
44170
44749
  let decoded;
44171
44750
  let content;
44172
- const decode = () => {
44751
+ const decode2 = () => {
44173
44752
  if (decoded !== void 0) return decoded;
44174
44753
  decoded = null;
44175
44754
  if (locked) return decoded;
@@ -44180,7 +44759,7 @@ function queryNotes(expression, options = {}) {
44180
44759
  try {
44181
44760
  decoded = decodeNoteBody(
44182
44761
  new Uint8Array(
44183
- gunzipSync3(Buffer.from(row.data, "hex"), { maxOutputLength: 32 * 1024 * 1024 })
44762
+ gunzipSync5(Buffer.from(row.data, "hex"), { maxOutputLength: 32 * 1024 * 1024 })
44184
44763
  )
44185
44764
  );
44186
44765
  } catch {
@@ -44200,7 +44779,7 @@ function queryNotes(expression, options = {}) {
44200
44779
  modified: coreDataMs(row.modified),
44201
44780
  content: () => {
44202
44781
  if (content !== void 0) return content;
44203
- const body2 = decode();
44782
+ const body2 = decode2();
44204
44783
  if (!body2) return content = null;
44205
44784
  const text = body2.text;
44206
44785
  const firstBreak = text.indexOf("\n");
@@ -44225,7 +44804,7 @@ function queryNotes(expression, options = {}) {
44225
44804
  if (!evaluateNoteQuery(ast, note)) continue;
44226
44805
  matched++;
44227
44806
  if (hits.length >= limit) continue;
44228
- const body = locked ? null : decode();
44807
+ const body = locked ? null : decode2();
44229
44808
  hits.push({
44230
44809
  id: `x-coredata://${uuid2}/ICNote/p${row.pk}`,
44231
44810
  title: row.title ?? "",
@@ -44255,10 +44834,10 @@ function queryNotes(expression, options = {}) {
44255
44834
  import { spawnSync } from "child_process";
44256
44835
 
44257
44836
  // src/services/nativeTags.ts
44258
- import { execFileSync as execFileSync9 } from "node:child_process";
44837
+ import { execFileSync as execFileSync10 } from "node:child_process";
44259
44838
  import { mkdtempSync as mkdtempSync2, writeFileSync, rmSync as rmSync2 } from "node:fs";
44260
44839
  import { tmpdir as tmpdir2 } from "node:os";
44261
- import { join as join10 } from "node:path";
44840
+ import { join as join11 } from "node:path";
44262
44841
 
44263
44842
  // src/services/shortcutConsent.ts
44264
44843
  function shortcutConsentHint(shortcut) {
@@ -44334,7 +44913,7 @@ function addNativeTags(request, deps) {
44334
44913
  };
44335
44914
  }
44336
44915
  function listInstalledShortcuts() {
44337
- return execFileSync9("/usr/bin/shortcuts", ["list", "--show-identifiers"], {
44916
+ return execFileSync10("/usr/bin/shortcuts", ["list", "--show-identifiers"], {
44338
44917
  encoding: "utf8",
44339
44918
  timeout: 15e3,
44340
44919
  maxBuffer: 1024 * 1024,
@@ -44360,11 +44939,11 @@ function runNativeTagsShortcut(input) {
44360
44939
  const status = nativeTagsStatus();
44361
44940
  if (!status.installed)
44362
44941
  throw new Error(`Import the supplied ${status.shortcut}.shortcut in Shortcuts first`);
44363
- const directory = mkdtempSync2(join10(tmpdir2(), "apple-notes-native-tags-"));
44942
+ const directory = mkdtempSync2(join11(tmpdir2(), "apple-notes-native-tags-"));
44364
44943
  try {
44365
- const path7 = join10(directory, "request.json");
44944
+ const path7 = join11(directory, "request.json");
44366
44945
  writeFileSync(path7, JSON.stringify(input), { mode: 384 });
44367
- execFileSync9("/usr/bin/shortcuts", ["run", status.identifier, "--input-path", path7], {
44946
+ execFileSync10("/usr/bin/shortcuts", ["run", status.identifier, "--input-path", path7], {
44368
44947
  encoding: "utf8",
44369
44948
  timeout: 6e4,
44370
44949
  maxBuffer: 1024 * 1024,
@@ -44383,10 +44962,10 @@ function runNativeTagsShortcut(input) {
44383
44962
  }
44384
44963
 
44385
44964
  // src/services/backgroundNotes.ts
44386
- import { execFileSync as execFileSync10 } from "node:child_process";
44965
+ import { execFileSync as execFileSync11 } from "node:child_process";
44387
44966
  import { mkdtempSync as mkdtempSync3, writeFileSync as writeFileSync2, rmSync as rmSync3 } from "node:fs";
44388
44967
  import { tmpdir as tmpdir3 } from "node:os";
44389
- import { join as join11 } from "node:path";
44968
+ import { join as join12 } from "node:path";
44390
44969
 
44391
44970
  // src/utils/appendMarkdown.ts
44392
44971
  var escape2 = (s) => s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
@@ -44575,9 +45154,9 @@ function runBackgroundShortcut(input, status = backgroundStatus()) {
44575
45154
  throw new Error(
44576
45155
  `Install the supplied "${status.shortcut}" Shortcut once; Shortcuts must list it exactly once`
44577
45156
  );
44578
- const directory = mkdtempSync3(join11(tmpdir3(), "apple-notes-background-"));
45157
+ const directory = mkdtempSync3(join12(tmpdir3(), "apple-notes-background-"));
44579
45158
  try {
44580
- const file = join11(directory, "request.json");
45159
+ const file = join12(directory, "request.json");
44581
45160
  writeFileSync2(
44582
45161
  file,
44583
45162
  JSON.stringify({
@@ -44597,7 +45176,7 @@ function runBackgroundShortcut(input, status = backgroundStatus()) {
44597
45176
  { mode: 384 }
44598
45177
  );
44599
45178
  try {
44600
- execFileSync10("/usr/bin/shortcuts", ["run", status.identifier, "--input-path", file], {
45179
+ execFileSync11("/usr/bin/shortcuts", ["run", status.identifier, "--input-path", file], {
44601
45180
  encoding: "utf8",
44602
45181
  timeout: 6e4,
44603
45182
  maxBuffer: 1024 * 1024,
@@ -44889,7 +45468,7 @@ ${request.content}`
44889
45468
  }
44890
45469
 
44891
45470
  // src/services/capabilityMatrix.ts
44892
- import { execFileSync as execFileSync11 } from "node:child_process";
45471
+ import { execFileSync as execFileSync12 } from "node:child_process";
44893
45472
  import { release } from "node:os";
44894
45473
  var MACOS_SHORTCUTS_CLI = "12.0";
44895
45474
  var MACOS_MARKDOWN_IMPORT = "26.0";
@@ -45096,7 +45675,7 @@ function evaluateFeatures(env, features = FEATURES) {
45096
45675
  function readMacOSVersion() {
45097
45676
  if (process.platform !== "darwin") return null;
45098
45677
  try {
45099
- const out = execFileSync11("/usr/bin/sw_vers", ["-productVersion"], {
45678
+ const out = execFileSync12("/usr/bin/sw_vers", ["-productVersion"], {
45100
45679
  encoding: "utf8",
45101
45680
  timeout: 3e3,
45102
45681
  stdio: ["ignore", "pipe", "ignore"]
@@ -45250,12 +45829,12 @@ function formatDoctorReport(r) {
45250
45829
 
45251
45830
  // src/services/fileConfig.ts
45252
45831
  import { existsSync as existsSync9, readFileSync as readFileSync2 } from "fs";
45253
- import { join as join12 } from "path";
45254
- import { homedir as homedir10 } from "os";
45832
+ import { join as join13 } from "path";
45833
+ import { homedir as homedir11 } from "os";
45255
45834
  function fileConfigPath(env = process.env) {
45256
45835
  const override = env.APPLE_NOTES_MCP_CONFIG_FILE;
45257
45836
  if (override && override.trim()) return override.trim();
45258
- return join12(homedir10(), "Library", "Application Support", "apple-notes-mcp", "config.json");
45837
+ return join13(homedir11(), "Library", "Application Support", "apple-notes-mcp", "config.json");
45259
45838
  }
45260
45839
  function loadFileConfig(env = process.env, path7 = fileConfigPath(env)) {
45261
45840
  const applied = [];
@@ -45557,109 +46136,12 @@ function createShutdown(stream, exit, timeoutMs = SHUTDOWN_DRAIN_TIMEOUT_MS) {
45557
46136
  };
45558
46137
  }
45559
46138
 
45560
- // src/utils/noteTables.ts
45561
- import { gunzipSync as gunzipSync4 } from "node:zlib";
45562
- var sub = (f, n) => {
45563
- const value = embeddedMessage(getField(f, n));
45564
- if (!value) throw new Error(`Missing table field ${n}`);
45565
- return value;
45566
- };
45567
- var num = (f, n) => {
45568
- const v = varintValue(getField(f, n));
45569
- if (v === void 0) throw new Error(`Missing table index ${n}`);
45570
- return v;
45571
- };
45572
- var many = (f, n) => getFields(f, n).map((v) => {
45573
- const m = embeddedMessage(v);
45574
- if (!m) throw new Error("Invalid table entry");
45575
- return m;
45576
- });
45577
- var hex = (f) => {
45578
- if (!(f?.value instanceof Uint8Array)) throw new Error("Missing table UUID");
45579
- return Buffer.from(f.value).toString("hex");
45580
- };
45581
- function parseNoteTable(compressed) {
45582
- const root = decodeMessage(gunzipSync4(compressed, { maxOutputLength: 16 * 1024 * 1024 }));
45583
- const data = sub(sub(root, 2), 3), entries = many(data, 3);
45584
- if (entries.length > 1e5) throw new Error("Table too large");
45585
- const keys = getFields(data, 4).map(stringValue), types = getFields(data, 5).map(stringValue), uuids = getFields(data, 6).map(hex);
45586
- const entry = (index) => {
45587
- if (!entries[index]) throw new Error("Invalid table reference");
45588
- return entries[index];
45589
- };
45590
- const uuidIndex = (index) => num(sub(many(sub(entry(index), 13), 3)[0], 2), 2);
45591
- const roots = entries.filter((e) => {
45592
- const map = embeddedMessage(getField(e, 13));
45593
- return map && types[num(map, 1)] === "com.apple.notes.ICTable";
45594
- });
45595
- if (roots.length !== 1) throw new Error("Ambiguous native table root");
45596
- const refs = new Map(
45597
- many(sub(roots[0], 13), 3).filter((m) => ["crRows", "crColumns", "cellColumns"].includes(keys[num(m, 1)] || "")).map((m) => [keys[num(m, 1)], num(sub(m, 2), 6)])
45598
- );
45599
- const ordered = (key) => {
45600
- const ref = refs.get(key);
45601
- if (ref === void 0) throw new Error("Missing table dimension");
45602
- const ordering = sub(sub(entry(ref), 16), 1), array2 = sub(ordering, 1);
45603
- const ids = many(array2, 2).map((a) => hex(getField(a, 2)));
45604
- const map = /* @__PURE__ */ new Map();
45605
- ids.forEach((id2, i) => {
45606
- const index = uuids.indexOf(id2);
45607
- if (index < 0) throw new Error("Missing dimension UUID");
45608
- map.set(index, i);
45609
- });
45610
- const aliases = many(sub(ordering, 2), 1).map((pair) => [
45611
- uuidIndex(num(sub(pair, 1), 6)),
45612
- uuidIndex(num(sub(pair, 2), 6))
45613
- ]);
45614
- for (let pass = 0; pass < aliases.length + 1; pass++) {
45615
- let changed = false;
45616
- for (const [key2, value] of aliases)
45617
- if (map.has(key2) && !map.has(value)) {
45618
- map.set(value, map.get(key2));
45619
- changed = true;
45620
- }
45621
- if (!changed) break;
45622
- }
45623
- return { ids, map };
45624
- };
45625
- const rows = ordered("crRows"), columns = ordered("crColumns");
45626
- if (!rows.ids.length || !columns.ids.length || rows.ids.length * columns.ids.length > 1e5)
45627
- throw new Error("Unsupported table size");
45628
- const values = rows.ids.map(() => columns.ids.map(() => ""));
45629
- const cellRef = refs.get("cellColumns");
45630
- if (cellRef === void 0) throw new Error("Missing table cells");
45631
- for (const column of many(sub(entry(cellRef), 6), 1)) {
45632
- const ci = columns.map.get(uuidIndex(num(sub(column, 1), 6)));
45633
- const cells = entry(num(sub(column, 2), 6));
45634
- for (const row of many(sub(cells, 6), 1)) {
45635
- const ri = rows.map.get(uuidIndex(num(sub(row, 1), 6)));
45636
- if (ri === void 0 || ci === void 0) continue;
45637
- const note = sub(entry(num(sub(row, 2), 6)), 10);
45638
- const text = stringValue(getField(note, 2));
45639
- if (text === void 0 || text.includes("\uFFFC"))
45640
- throw new Error("Embedded or unsupported table cell");
45641
- values[ri][ci] = text.replace(/\n$/u, "");
45642
- }
45643
- }
45644
- const rtl = entries.some((e) => {
45645
- const map = embeddedMessage(getField(e, 13));
45646
- return map && many(map, 3).some(
45647
- (m) => stringValue(getField(sub(m, 2), 4)) === "CRTableColumnDirectionRightToLeft"
45648
- );
45649
- });
45650
- if (rtl) {
45651
- for (const row of values) row.reverse();
45652
- columns.ids.reverse();
45653
- }
45654
- return { rows: values, rowIds: rows.ids, columnIds: columns.ids };
45655
- }
45656
-
45657
46139
  // src/utils/noteBlocks.ts
45658
- import { execFileSync as execFileSync12 } from "node:child_process";
46140
+ import { execFileSync as execFileSync13 } from "node:child_process";
45659
46141
  import { existsSync as existsSync10 } from "node:fs";
45660
- import { homedir as homedir11 } from "node:os";
45661
- import { join as join13 } from "node:path";
45662
- import { gunzipSync as gunzipSync5 } from "node:zlib";
46142
+ import { homedir as homedir12 } from "node:os";
46143
+ import { join as join14 } from "node:path";
46144
+ import { gunzipSync as gunzipSync6 } from "node:zlib";
45663
46145
  var NoteBlocksError = class extends Error {
45664
46146
  code;
45665
46147
  constructor(code, message) {
@@ -45708,7 +46190,7 @@ var stringOf = (fields, n) => {
45708
46190
  const b = bytesOf(fields, n);
45709
46191
  return b ? utf8.decode(b) : void 0;
45710
46192
  };
45711
- var sub2 = (fields, n) => {
46193
+ var sub3 = (fields, n) => {
45712
46194
  const b = bytesOf(fields, n);
45713
46195
  return b ? decodeWireFields(b) : void 0;
45714
46196
  };
@@ -45741,7 +46223,7 @@ function decodeParagraph(fields, tally) {
45741
46223
  blockQuote: (varintOf(fields, 8) ?? 0) !== 0
45742
46224
  };
45743
46225
  if (attrs.styleType === -1) attrs.styleType = null;
45744
- const checklist = sub2(fields, 5);
46226
+ const checklist = sub3(fields, 5);
45745
46227
  const checklistId = checklist && bytesOf(checklist, 1);
45746
46228
  if (attrs.styleType === 103 && checklistId)
45747
46229
  attrs.checklist = { id: hex2(checklistId), done: varintOf(checklist, 2) === 1 };
@@ -45751,7 +46233,7 @@ function decodeParagraph(fields, tally) {
45751
46233
  }
45752
46234
  function decodeInline(fields) {
45753
46235
  const inline = {};
45754
- const font = sub2(fields, 3);
46236
+ const font = sub3(fields, 3);
45755
46237
  const hints = font ? varintOf(font, 3) ?? 0 : 0;
45756
46238
  const weight = varintOf(fields, 5) ?? 0;
45757
46239
  if (weight === 1 || weight === 3 || hints & 1) inline.bold = true;
@@ -45761,7 +46243,7 @@ function decodeInline(fields) {
45761
46243
  const baseline = varintOf(fields, 8) ?? 0;
45762
46244
  if (baseline > 0) inline.superscript = true;
45763
46245
  if (baseline < 0) inline.subscript = true;
45764
- const color = sub2(fields, 10);
46246
+ const color = sub3(fields, 10);
45765
46247
  const colorValue = color && colorOf(color);
45766
46248
  if (colorValue) inline.color = colorValue;
45767
46249
  const emphasis = varintOf(fields, 14);
@@ -45783,7 +46265,7 @@ function decodeInline(fields) {
45783
46265
  ...size !== void 0 && Number.isFinite(size) ? { size } : {}
45784
46266
  };
45785
46267
  }
45786
- const attachment = sub2(fields, 12);
46268
+ const attachment = sub3(fields, 12);
45787
46269
  const attachmentId = attachment && stringOf(attachment, 1);
45788
46270
  if (attachmentId)
45789
46271
  inline.attachment = { id: attachmentId, uti: stringOf(attachment, 2) || "unknown" };
@@ -45798,8 +46280,8 @@ function wrap(error2) {
45798
46280
  function decodeNoteBlocks(data) {
45799
46281
  let note;
45800
46282
  try {
45801
- const document = sub2(decodeWireFields(data), 2);
45802
- note = document && sub2(document, 3);
46283
+ const document = sub3(decodeWireFields(data), 2);
46284
+ note = document && sub3(document, 3);
45803
46285
  } catch (error2) {
45804
46286
  wrap(error2);
45805
46287
  }
@@ -45824,7 +46306,7 @@ function decodeNoteBlocks(data) {
45824
46306
  runs.push({
45825
46307
  start: position,
45826
46308
  length,
45827
- paragraph: decodeParagraph(sub2(fields, 2), paragraphTally),
46309
+ paragraph: decodeParagraph(sub3(fields, 2), paragraphTally),
45828
46310
  inline: decodeInline(fields)
45829
46311
  });
45830
46312
  position += length;
@@ -45947,7 +46429,7 @@ function summarize(blocks, attachments) {
45947
46429
  function decodeCompressedNoteBlocks(compressed) {
45948
46430
  let data;
45949
46431
  try {
45950
- data = gunzipSync5(compressed, { maxOutputLength: 32 * 1024 * 1024 });
46432
+ data = gunzipSync6(compressed, { maxOutputLength: 32 * 1024 * 1024 });
45951
46433
  } catch (error2) {
45952
46434
  throw new NoteBlocksError(
45953
46435
  "decompress-failed",
@@ -46001,11 +46483,11 @@ function pageNoteBlocks(doc, { offset = 0, limit = 500, maxBytes = blocksMaxResp
46001
46483
  undecodedFields: doc.undecodedFields
46002
46484
  };
46003
46485
  }
46004
- var NOTES_DB_PATH7 = join13(
46005
- homedir11(),
46486
+ var NOTES_DB_PATH8 = join14(
46487
+ homedir12(),
46006
46488
  "Library/Group Containers/group.com.apple.notes/NoteStore.sqlite"
46007
46489
  );
46008
- function readNoteBlocks(id2, { dbPath: dbPath2 = NOTES_DB_PATH7 } = {}) {
46490
+ function readNoteBlocks(id2, { dbPath: dbPath2 = NOTES_DB_PATH8 } = {}) {
46009
46491
  const pk = /^x-coredata:\/\/[0-9a-f-]+\/ICNote\/p([0-9]{1,18})$/i.exec(id2)?.[1];
46010
46492
  if (!pk)
46011
46493
  throw new NoteBlocksError(
@@ -46017,7 +46499,7 @@ function readNoteBlocks(id2, { dbPath: dbPath2 = NOTES_DB_PATH7 } = {}) {
46017
46499
  const sql = "SELECT json_object('exists', (SELECT count(*) FROM ZICCLOUDSYNCINGOBJECT WHERE Z_PK = @pk AND Z_ENT = (SELECT Z_ENT FROM Z_PRIMARYKEY WHERE Z_NAME = 'ICNote')), 'data', (SELECT hex(ZDATA) FROM ZICNOTEDATA WHERE ZNOTE = @pk), 'encrypted', (SELECT ZCRYPTOINITIALIZATIONVECTOR IS NOT NULL FROM ZICNOTEDATA WHERE ZNOTE = @pk));";
46018
46500
  let output;
46019
46501
  try {
46020
- output = execFileSync12(
46502
+ output = execFileSync13(
46021
46503
  "/usr/bin/sqlite3",
46022
46504
  ["-readonly", "-cmd", ".parameter init", "-cmd", `.parameter set @pk ${pk}`, dbPath2, sql],
46023
46505
  {
@@ -46178,7 +46660,7 @@ import {
46178
46660
  writeFileSync as writeFileSync3
46179
46661
  } from "node:fs";
46180
46662
  import { tmpdir as tmpdir4 } from "node:os";
46181
- import { basename, isAbsolute as isAbsolute2, join as join14 } from "node:path";
46663
+ import { basename, isAbsolute as isAbsolute2, join as join15 } from "node:path";
46182
46664
  var noteId = exactIdInput(
46183
46665
  "ICNote",
46184
46666
  /^x-coredata:\/\/[0-9a-f-]+\/ICNote\/p\d+$/i,
@@ -46292,8 +46774,8 @@ function registerDirectOperations(server2, manager) {
46292
46774
  if (before.hash !== expectedContentHash) throw new Error("Note revision changed");
46293
46775
  const bytes = localAttachment(path7);
46294
46776
  const beforeAttachments = manager.listAttachmentsById(id2);
46295
- const directory = mkdtempSync4(join14(tmpdir4(), "notes-attachment-add-"));
46296
- const temporaryFile = join14(directory, basename(path7));
46777
+ const directory = mkdtempSync4(join15(tmpdir4(), "notes-attachment-add-"));
46778
+ const temporaryFile = join15(directory, basename(path7));
46297
46779
  try {
46298
46780
  writeFileSync3(temporaryFile, bytes, { mode: 384 });
46299
46781
  if (readSnapshot(manager, id2).hash !== before.hash)
@@ -47675,6 +48157,42 @@ registerTool(
47675
48157
  });
47676
48158
  }, "Error reading native objects")
47677
48159
  );
48160
+ registerTool(
48161
+ "get-note-tables",
48162
+ {
48163
+ description: "Use when: reading the native tables in one exact note as data or Markdown.\nReturns: every table in body order as GitHub-flavored Markdown (first row as header) plus JSON rows with stable row/column ids, and tableCellsComplete.\nDo not use when: you need the whole note (get-note-markdown / get-note-content) or native object ranges and checklist ids (get-native-objects).\nSafety: read-only; reads the NoteStore database and requires Full Disk Access. A cell that cannot be decoded is null in rows, listed in incompleteCells, and marked [undecoded cell] in Markdown; it is never guessed. Cell text only: links and styling inside cells are not rendered.",
48164
+ inputSchema: { id: noteIdInput },
48165
+ outputSchema: {
48166
+ id: external_exports.string().optional(),
48167
+ tables: external_exports.array(external_exports.record(external_exports.unknown())).optional(),
48168
+ tableCount: external_exports.number().optional(),
48169
+ tableCellsComplete: external_exports.boolean().optional(),
48170
+ markdown: external_exports.string().optional()
48171
+ },
48172
+ annotations: { readOnlyHint: true }
48173
+ },
48174
+ withErrorHandling(({ id: id2 }) => {
48175
+ const { metadata, message } = getNoteMetadata(id2);
48176
+ if (!metadata) return errorResponse(message || `Failed to read note "${id2}"`);
48177
+ if (metadata.passwordProtected) {
48178
+ return errorResponse(
48179
+ `Note "${id2}" is password-protected; its tables are encrypted. Unlock it in Notes.app first.`
48180
+ );
48181
+ }
48182
+ const result = notesManager.getNoteTablesById(id2);
48183
+ const count = result.tables.length;
48184
+ const summary = count === 0 ? "This note has no native tables." : `${count} table(s)${result.tableCellsComplete ? "" : " (some content could not be decoded; see tables[].reason)"}:
48185
+
48186
+ ${result.markdown}`;
48187
+ return successResponse(summary, {
48188
+ id: id2,
48189
+ tables: result.tables,
48190
+ tableCount: count,
48191
+ tableCellsComplete: result.tableCellsComplete,
48192
+ markdown: result.markdown
48193
+ });
48194
+ }, "Error reading note tables")
48195
+ );
47678
48196
  registerTool(
47679
48197
  "get-note-blocks",
47680
48198
  {
@@ -48965,6 +49483,44 @@ ${summary}`,
48965
49483
  );
48966
49484
  }, "Error reading checklist state")
48967
49485
  );
49486
+ registerTool(
49487
+ "get-audio-transcripts",
49488
+ {
49489
+ description: "Use when: reading the transcript (and summary, if any) that Notes already computed for the audio recordings in one note, by id.\nReturns: one entry per top-level audio attachment in body order, with attachmentId, durationSeconds, status (ok, none when no transcript is stored, or undecodable with a reason), joined transcript text, wordCount, speakers and summary when stored, and optional word-level segments.\nDo not use when: you need the audio file itself (save-attachment) or the note body (get-note-content). This tool does not transcribe; it only reads what Notes stored.\nSafety: read-only; requires Full Disk Access and reads the NoteStore database. Password-protected notes are refused. Large responses drop segments first, then shorten text, to stay under APPLE_NOTES_MCP_EXPORT_MAX_BYTES.",
49490
+ inputSchema: {
49491
+ id: noteIdInput,
49492
+ includeSegments: external_exports.boolean().optional().describe(
49493
+ "Also return word-level segments (text, start and duration in seconds, speaker). Default false."
49494
+ ),
49495
+ maxSegments: external_exports.number().int().min(1).max(MAX_SEGMENTS_LIMIT).optional().describe(
49496
+ `Cap on segments returned per attachment when includeSegments is true (default ${DEFAULT_MAX_SEGMENTS}).`
49497
+ )
49498
+ },
49499
+ outputSchema: {
49500
+ id: external_exports.string().optional(),
49501
+ attachments: external_exports.array(external_exports.object({}).passthrough()).optional(),
49502
+ bodyOrder: external_exports.boolean().optional(),
49503
+ truncated: external_exports.boolean().optional()
49504
+ },
49505
+ annotations: { readOnlyHint: true }
49506
+ },
49507
+ withErrorHandling(({ id: id2, includeSegments, maxSegments }) => {
49508
+ let result;
49509
+ try {
49510
+ result = notesManager.getAudioTranscripts(id2, { includeSegments, maxSegments });
49511
+ } catch (error2) {
49512
+ if (error2 instanceof AudioTranscriptError) return errorResponse(error2.message);
49513
+ throw error2;
49514
+ }
49515
+ const toResponse = (r) => successResponse(formatTranscriptsText(r), { ...r });
49516
+ const fitted = fitTranscriptsToBudget(
49517
+ result,
49518
+ exportMaxResponseBytes(),
49519
+ (r) => Buffer.byteLength(JSON.stringify(toResponse(r)))
49520
+ );
49521
+ return toResponse(fitted);
49522
+ }, "Error reading audio transcripts")
49523
+ );
48968
49524
  registerTool(
48969
49525
  "get-note-metadata",
48970
49526
  {