apple-notes-mcp 2.8.26 → 2.8.28

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 +73 -1
  2. package/build/index.js +1184 -206
  3. package/package.json +1 -1
package/build/index.js CHANGED
@@ -3268,8 +3268,8 @@ var require_utils = __commonJS({
3268
3268
  }
3269
3269
  return ind;
3270
3270
  }
3271
- function removeDotSegments(path6) {
3272
- let input = path6;
3271
+ function removeDotSegments(path7) {
3272
+ let input = path7;
3273
3273
  const output = [];
3274
3274
  let nextSlash = -1;
3275
3275
  let len = 0;
@@ -3358,9 +3358,9 @@ var require_utils = __commonJS({
3358
3358
  let output = "";
3359
3359
  for (let i = 0; i < input.length; i++) {
3360
3360
  if (input[i] === "%" && i + 2 < input.length) {
3361
- const hex2 = input.slice(i + 1, i + 3);
3362
- if (isHexPair(hex2)) {
3363
- const normalizedHex = hex2.toUpperCase();
3361
+ const hex3 = input.slice(i + 1, i + 3);
3362
+ if (isHexPair(hex3)) {
3363
+ const normalizedHex = hex3.toUpperCase();
3364
3364
  const decoded = String.fromCharCode(parseInt(normalizedHex, 16));
3365
3365
  if (decodeUnreserved && isUnreserved(decoded)) {
3366
3366
  output += decoded;
@@ -3380,9 +3380,9 @@ var require_utils = __commonJS({
3380
3380
  for (let i = 0; i < input.length; i++) {
3381
3381
  const ch = input[i];
3382
3382
  if (ch === "%" && i + 2 < input.length) {
3383
- const hex2 = input.slice(i + 1, i + 3);
3384
- if (isHexPair(hex2)) {
3385
- const normalizedHex = hex2.toUpperCase();
3383
+ const hex3 = input.slice(i + 1, i + 3);
3384
+ if (isHexPair(hex3)) {
3385
+ const normalizedHex = hex3.toUpperCase();
3386
3386
  const decoded = String.fromCharCode(parseInt(normalizedHex, 16));
3387
3387
  if (decoded !== "." && isUnreserved(decoded)) {
3388
3388
  output += decoded;
@@ -3422,9 +3422,9 @@ var require_utils = __commonJS({
3422
3422
  for (let i = 0; i < input.length; i++) {
3423
3423
  const ch = input[i];
3424
3424
  if (ch === "%" && i + 2 < input.length) {
3425
- const hex2 = input.slice(i + 1, i + 3);
3426
- if (isHexPair(hex2)) {
3427
- output += "%" + hex2.toUpperCase();
3425
+ const hex3 = input.slice(i + 1, i + 3);
3426
+ if (isHexPair(hex3)) {
3427
+ output += "%" + hex3.toUpperCase();
3428
3428
  i += 2;
3429
3429
  continue;
3430
3430
  }
@@ -3460,9 +3460,9 @@ var require_utils = __commonJS({
3460
3460
  for (let i = 0; i < input.length; i++) {
3461
3461
  const ch = input[i];
3462
3462
  if (ch === "%" && i + 2 < input.length) {
3463
- const hex2 = input.slice(i + 1, i + 3);
3464
- if (isHexPair(hex2)) {
3465
- output += "%" + hex2.toUpperCase();
3463
+ const hex3 = input.slice(i + 1, i + 3);
3464
+ if (isHexPair(hex3)) {
3465
+ output += "%" + hex3.toUpperCase();
3466
3466
  i += 2;
3467
3467
  continue;
3468
3468
  }
@@ -3507,9 +3507,9 @@ var require_utils = __commonJS({
3507
3507
  for (let i = 0; i < input.length; i++) {
3508
3508
  const ch = input[i];
3509
3509
  if (ch === "%" && i + 2 < input.length) {
3510
- const hex2 = input.slice(i + 1, i + 3);
3511
- if (isHexPair(hex2)) {
3512
- const normalizedHex = hex2.toUpperCase();
3510
+ const hex3 = input.slice(i + 1, i + 3);
3511
+ if (isHexPair(hex3)) {
3512
+ const normalizedHex = hex3.toUpperCase();
3513
3513
  const decoded = String.fromCharCode(parseInt(normalizedHex, 16));
3514
3514
  if (isUnreserved(decoded)) {
3515
3515
  output += decoded;
@@ -3547,9 +3547,9 @@ var require_utils = __commonJS({
3547
3547
  let output = "";
3548
3548
  for (let i = 0; i < input.length; i++) {
3549
3549
  if (input[i] === "%" && i + 2 < input.length) {
3550
- const hex2 = input.slice(i + 1, i + 3);
3551
- if (isHexPair(hex2)) {
3552
- output += "%" + hex2.toUpperCase();
3550
+ const hex3 = input.slice(i + 1, i + 3);
3551
+ if (isHexPair(hex3)) {
3552
+ output += "%" + hex3.toUpperCase();
3553
3553
  i += 2;
3554
3554
  continue;
3555
3555
  }
@@ -3674,8 +3674,8 @@ var require_schemes = __commonJS({
3674
3674
  }
3675
3675
  if (wsComponent.resourceName) {
3676
3676
  const queryIndex = wsComponent.resourceName.indexOf("?");
3677
- const path6 = queryIndex === -1 ? wsComponent.resourceName : wsComponent.resourceName.slice(0, queryIndex);
3678
- wsComponent.path = path6 && path6 !== "/" ? path6 : void 0;
3677
+ const path7 = queryIndex === -1 ? wsComponent.resourceName : wsComponent.resourceName.slice(0, queryIndex);
3678
+ wsComponent.path = path7 && path7 !== "/" ? path7 : void 0;
3679
3679
  wsComponent.query = queryIndex === -1 ? void 0 : wsComponent.resourceName.slice(queryIndex + 1);
3680
3680
  wsComponent.resourceName = void 0;
3681
3681
  }
@@ -7181,12 +7181,12 @@ var require_dist = __commonJS({
7181
7181
  throw new Error(`Unknown format "${name}"`);
7182
7182
  return f;
7183
7183
  };
7184
- function addFormats(ajv, list, fs6, exportName) {
7184
+ function addFormats(ajv, list, fs7, exportName) {
7185
7185
  var _a;
7186
7186
  var _b;
7187
7187
  (_a = (_b = ajv.opts.code).formats) !== null && _a !== void 0 ? _a : _b.formats = (0, codegen_1._)`require("ajv-formats/dist/formats").${exportName}`;
7188
7188
  for (const f of list)
7189
- ajv.addFormat(f, fs6[f]);
7189
+ ajv.addFormat(f, fs7[f]);
7190
7190
  }
7191
7191
  module.exports = exports = formatsPlugin;
7192
7192
  Object.defineProperty(exports, "__esModule", { value: true });
@@ -8768,25 +8768,25 @@ var require_ContainerNode = __commonJS({
8768
8768
  return this._firstChild;
8769
8769
  } },
8770
8770
  lastChild: { get: function() {
8771
- var kids = this._childNodes, first;
8771
+ var kids = this._childNodes, first2;
8772
8772
  if (kids) {
8773
8773
  return kids.length === 0 ? null : kids[kids.length - 1];
8774
8774
  }
8775
- first = this._firstChild;
8776
- if (first === null) {
8775
+ first2 = this._firstChild;
8776
+ if (first2 === null) {
8777
8777
  return null;
8778
8778
  }
8779
- return first._previousSibling;
8779
+ return first2._previousSibling;
8780
8780
  } },
8781
8781
  _ensureChildNodes: { value: function() {
8782
8782
  if (this._childNodes) {
8783
8783
  return;
8784
8784
  }
8785
- var first = this._firstChild, kid = first, childNodes = this._childNodes = new NodeList();
8786
- if (first) do {
8785
+ var first2 = this._firstChild, kid = first2, childNodes = this._childNodes = new NodeList();
8786
+ if (first2) do {
8787
8787
  childNodes.push(kid);
8788
8788
  kid = kid._nextSibling;
8789
- } while (kid !== first);
8789
+ } while (kid !== first2);
8790
8790
  this._firstChild = null;
8791
8791
  } },
8792
8792
  // Remove all of this node's children. This is a minor
@@ -10257,22 +10257,22 @@ var require_Element = __commonJS({
10257
10257
  }
10258
10258
  },
10259
10259
  _insertAdjacent: { value: function _insertAdjacent(position, node) {
10260
- var first = false;
10260
+ var first2 = false;
10261
10261
  switch (position) {
10262
10262
  case "beforebegin":
10263
- first = true;
10263
+ first2 = true;
10264
10264
  /* falls through */
10265
10265
  case "afterend":
10266
10266
  var parent = this.parentNode;
10267
10267
  if (parent === null) {
10268
10268
  return null;
10269
10269
  }
10270
- return parent.insertBefore(node, first ? this : this.nextSibling);
10270
+ return parent.insertBefore(node, first2 ? this : this.nextSibling);
10271
10271
  case "afterbegin":
10272
- first = true;
10272
+ first2 = true;
10273
10273
  /* falls through */
10274
10274
  case "beforeend":
10275
- return this.insertBefore(node, first ? this.firstChild : null);
10275
+ return this.insertBefore(node, first2 ? this.firstChild : null);
10276
10276
  default:
10277
10277
  return utils.SyntaxError();
10278
10278
  }
@@ -12238,32 +12238,32 @@ var require_URL = __commonJS({
12238
12238
  else
12239
12239
  return basepath.substring(0, lastslash + 1) + refpath;
12240
12240
  }
12241
- function remove_dot_segments(path6) {
12242
- if (!path6) return path6;
12241
+ function remove_dot_segments(path7) {
12242
+ if (!path7) return path7;
12243
12243
  var output = "";
12244
- while (path6.length > 0) {
12245
- if (path6 === "." || path6 === "..") {
12246
- path6 = "";
12244
+ while (path7.length > 0) {
12245
+ if (path7 === "." || path7 === "..") {
12246
+ path7 = "";
12247
12247
  break;
12248
12248
  }
12249
- var twochars = path6.substring(0, 2);
12250
- var threechars = path6.substring(0, 3);
12251
- var fourchars = path6.substring(0, 4);
12249
+ var twochars = path7.substring(0, 2);
12250
+ var threechars = path7.substring(0, 3);
12251
+ var fourchars = path7.substring(0, 4);
12252
12252
  if (threechars === "../") {
12253
- path6 = path6.substring(3);
12253
+ path7 = path7.substring(3);
12254
12254
  } else if (twochars === "./") {
12255
- path6 = path6.substring(2);
12255
+ path7 = path7.substring(2);
12256
12256
  } else if (threechars === "/./") {
12257
- path6 = "/" + path6.substring(3);
12258
- } else if (twochars === "/." && path6.length === 2) {
12259
- path6 = "/";
12260
- } else if (fourchars === "/../" || threechars === "/.." && path6.length === 3) {
12261
- path6 = "/" + path6.substring(4);
12257
+ path7 = "/" + path7.substring(3);
12258
+ } else if (twochars === "/." && path7.length === 2) {
12259
+ path7 = "/";
12260
+ } else if (fourchars === "/../" || threechars === "/.." && path7.length === 3) {
12261
+ path7 = "/" + path7.substring(4);
12262
12262
  output = output.replace(/\/?[^\/]*$/, "");
12263
12263
  } else {
12264
- var segment = path6.match(/(\/?([^\/]*))/)[0];
12264
+ var segment = path7.match(/(\/?([^\/]*))/)[0];
12265
12265
  output += segment;
12266
- path6 = path6.substring(segment.length);
12266
+ path7 = path7.substring(segment.length);
12267
12267
  }
12268
12268
  }
12269
12269
  return output;
@@ -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 join13(output, replacement);
24468
+ return join15(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 = join13(output, rule.append(self.options));
24475
+ output = join15(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 join13(output, replacement) {
24487
+ function join15(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);
@@ -24675,9 +24675,9 @@ var util;
24675
24675
  })(util || (util = {}));
24676
24676
  var objectUtil;
24677
24677
  (function(objectUtil2) {
24678
- objectUtil2.mergeShapes = (first, second) => {
24678
+ objectUtil2.mergeShapes = (first2, second) => {
24679
24679
  return {
24680
- ...first,
24680
+ ...first2,
24681
24681
  ...second
24682
24682
  // second overwrites first
24683
24683
  };
@@ -24777,8 +24777,8 @@ var ZodError = class _ZodError extends Error {
24777
24777
  constructor(issues) {
24778
24778
  super();
24779
24779
  this.issues = [];
24780
- this.addIssue = (sub2) => {
24781
- this.issues = [...this.issues, sub2];
24780
+ this.addIssue = (sub3) => {
24781
+ this.issues = [...this.issues, sub3];
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 sub2 of this.issues) {
24849
- if (sub2.path.length > 0) {
24850
- const firstEl = sub2.path[0];
24848
+ for (const sub3 of this.issues) {
24849
+ if (sub3.path.length > 0) {
24850
+ const firstEl = sub3.path[0];
24851
24851
  fieldErrors[firstEl] = fieldErrors[firstEl] || [];
24852
- fieldErrors[firstEl].push(mapper(sub2));
24852
+ fieldErrors[firstEl].push(mapper(sub3));
24853
24853
  } else {
24854
- formErrors.push(mapper(sub2));
24854
+ formErrors.push(mapper(sub3));
24855
24855
  }
24856
24856
  }
24857
24857
  return { formErrors, fieldErrors };
@@ -24979,8 +24979,8 @@ function getErrorMap() {
24979
24979
 
24980
24980
  // node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/parseUtil.js
24981
24981
  var makeIssue = (params) => {
24982
- const { data, path: path6, errorMaps, issueData } = params;
24983
- const fullPath = [...path6, ...issueData.path || []];
24982
+ const { data, path: path7, errorMaps, issueData } = params;
24983
+ const fullPath = [...path7, ...issueData.path || []];
24984
24984
  const fullIssue = {
24985
24985
  ...issueData,
24986
24986
  path: fullPath
@@ -25096,11 +25096,11 @@ var errorUtil;
25096
25096
 
25097
25097
  // node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/types.js
25098
25098
  var ParseInputLazyPath = class {
25099
- constructor(parent, value, path6, key) {
25099
+ constructor(parent, value, path7, key) {
25100
25100
  this._cachedPath = [];
25101
25101
  this.parent = parent;
25102
25102
  this.data = value;
25103
- this._path = path6;
25103
+ this._path = path7;
25104
25104
  this._key = key;
25105
25105
  }
25106
25106
  get path() {
@@ -27599,10 +27599,10 @@ var ZodRecord = class _ZodRecord extends ZodType {
27599
27599
  get element() {
27600
27600
  return this._def.valueType;
27601
27601
  }
27602
- static create(first, second, third) {
27602
+ static create(first2, second, third) {
27603
27603
  if (second instanceof ZodType) {
27604
27604
  return new _ZodRecord({
27605
- keyType: first,
27605
+ keyType: first2,
27606
27606
  valueType: second,
27607
27607
  typeName: ZodFirstPartyTypeKind.ZodRecord,
27608
27608
  ...processCreateParams(third)
@@ -27610,7 +27610,7 @@ var ZodRecord = class _ZodRecord extends ZodType {
27610
27610
  }
27611
27611
  return new _ZodRecord({
27612
27612
  keyType: ZodString.create(),
27613
- valueType: first,
27613
+ valueType: first2,
27614
27614
  typeName: ZodFirstPartyTypeKind.ZodRecord,
27615
27615
  ...processCreateParams(second)
27616
27616
  });
@@ -28737,10 +28737,10 @@ function assignProp(target, prop, value) {
28737
28737
  configurable: true
28738
28738
  });
28739
28739
  }
28740
- function getElementAtPath(obj, path6) {
28741
- if (!path6)
28740
+ function getElementAtPath(obj, path7) {
28741
+ if (!path7)
28742
28742
  return obj;
28743
- return path6.reduce((acc, key) => acc?.[key], obj);
28743
+ return path7.reduce((acc, key) => acc?.[key], obj);
28744
28744
  }
28745
28745
  function promiseAllObject(promisesObj) {
28746
28746
  const keys = Object.keys(promisesObj);
@@ -29060,11 +29060,11 @@ function aborted(x, startIndex = 0) {
29060
29060
  }
29061
29061
  return false;
29062
29062
  }
29063
- function prefixIssues(path6, issues) {
29063
+ function prefixIssues(path7, issues) {
29064
29064
  return issues.map((iss) => {
29065
29065
  var _a;
29066
29066
  (_a = iss).path ?? (_a.path = []);
29067
- iss.path.unshift(path6);
29067
+ iss.path.unshift(path7);
29068
29068
  return iss;
29069
29069
  });
29070
29070
  }
@@ -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 sub2 of error2.issues) {
29154
- if (sub2.path.length > 0) {
29155
- fieldErrors[sub2.path[0]] = fieldErrors[sub2.path[0]] || [];
29156
- fieldErrors[sub2.path[0]].push(mapper(sub2));
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));
29157
29157
  } else {
29158
- formErrors.push(mapper(sub2));
29158
+ formErrors.push(mapper(sub3));
29159
29159
  }
29160
29160
  }
29161
29161
  return { formErrors, fieldErrors };
@@ -32475,11 +32475,11 @@ function normalizeObjectSchema(schema) {
32475
32475
  }
32476
32476
  return void 0;
32477
32477
  }
32478
- function getDotPath(path6) {
32479
- if (path6.length === 0) {
32478
+ function getDotPath(path7) {
32479
+ if (path7.length === 0) {
32480
32480
  return "object root";
32481
32481
  }
32482
- return path6.reduce((acc, seg, index) => {
32482
+ return path7.reduce((acc, seg, index) => {
32483
32483
  if (index === 0) {
32484
32484
  return String(seg);
32485
32485
  }
@@ -39330,6 +39330,68 @@ function embeddedMessage(field) {
39330
39330
  if (!bytes) return void 0;
39331
39331
  return decodeMessage(bytes);
39332
39332
  }
39333
+ var ProtobufDecodeError = class extends Error {
39334
+ constructor(message) {
39335
+ super(message);
39336
+ this.name = "ProtobufDecodeError";
39337
+ }
39338
+ };
39339
+ function decodeVarint64(buf, offset) {
39340
+ let result = 0n;
39341
+ let shift = 0n;
39342
+ let pos = offset;
39343
+ while (pos < buf.length) {
39344
+ const byte = buf[pos++];
39345
+ result |= BigInt(byte & 127) << shift;
39346
+ if ((byte & 128) === 0) return [BigInt.asUintN(64, result), pos];
39347
+ shift += 7n;
39348
+ if (shift >= 70n) throw new ProtobufDecodeError(`Varint too long at offset ${offset}`);
39349
+ }
39350
+ throw new ProtobufDecodeError(`Unexpected end of buffer reading varint at offset ${offset}`);
39351
+ }
39352
+ function decodeWireFields(buf) {
39353
+ const fields = [];
39354
+ let offset = 0;
39355
+ while (offset < buf.length) {
39356
+ const [tag, afterTag] = decodeVarint64(buf, offset);
39357
+ offset = afterTag;
39358
+ const fieldNumber = Number(tag >> 3n);
39359
+ const wireType = Number(tag & 7n);
39360
+ if (fieldNumber === 0 || fieldNumber > 536870911)
39361
+ throw new ProtobufDecodeError("Invalid field number");
39362
+ if (wireType === 0) {
39363
+ const [value, next] = decodeVarint64(buf, offset);
39364
+ offset = next;
39365
+ fields.push({ fieldNumber, wireType, varint: value });
39366
+ continue;
39367
+ }
39368
+ let length;
39369
+ if (wireType === 1) length = 8;
39370
+ else if (wireType === 5) length = 4;
39371
+ else if (wireType === 2) {
39372
+ const [value, next] = decodeVarint64(buf, offset);
39373
+ if (value > BigInt(buf.length)) throw new ProtobufDecodeError("Truncated field");
39374
+ length = Number(value);
39375
+ offset = next;
39376
+ } else throw new ProtobufDecodeError(`Unsupported wire type ${wireType}`);
39377
+ if (offset + length > buf.length) throw new ProtobufDecodeError("Truncated field");
39378
+ fields.push({
39379
+ fieldNumber,
39380
+ wireType,
39381
+ bytes: buf.subarray(offset, offset + length)
39382
+ });
39383
+ offset += length;
39384
+ }
39385
+ return fields;
39386
+ }
39387
+ function signedVarint(value) {
39388
+ return Number(BigInt.asIntN(64, value));
39389
+ }
39390
+ function fixed32Float(field) {
39391
+ if (!field || field.wireType !== 5 || !field.bytes || field.bytes.length !== 4) return void 0;
39392
+ const bytes = field.bytes;
39393
+ return new DataView(bytes.buffer, bytes.byteOffset, 4).getFloat32(0, true);
39394
+ }
39333
39395
 
39334
39396
  // src/utils/checklistRuns.ts
39335
39397
  function checklistRunLineStart(text, position, length) {
@@ -39373,9 +39435,9 @@ function queryNoteData(noteId3) {
39373
39435
  timeout: 5e3,
39374
39436
  stdio: ["pipe", "pipe", "pipe"]
39375
39437
  });
39376
- const hex2 = result.trim();
39377
- if (!hex2) return { hex: null };
39378
- return { hex: hex2 };
39438
+ const hex3 = result.trim();
39439
+ if (!hex3) return { hex: null };
39440
+ return { hex: hex3 };
39379
39441
  } catch (error2) {
39380
39442
  const message = error2 instanceof Error ? error2.message : String(error2);
39381
39443
  console.error(`Failed to query NoteStore database: ${message}`);
@@ -39385,10 +39447,10 @@ function queryNoteData(noteId3) {
39385
39447
  return { hex: null };
39386
39448
  }
39387
39449
  }
39388
- function hexToBytes(hex2) {
39389
- const bytes = new Uint8Array(hex2.length / 2);
39390
- for (let i = 0; i < hex2.length; i += 2) {
39391
- bytes[i / 2] = parseInt(hex2.substring(i, i + 2), 16);
39450
+ function hexToBytes(hex3) {
39451
+ const bytes = new Uint8Array(hex3.length / 2);
39452
+ for (let i = 0; i < hex3.length; i += 2) {
39453
+ bytes[i / 2] = parseInt(hex3.substring(i, i + 2), 16);
39392
39454
  }
39393
39455
  return bytes;
39394
39456
  }
@@ -39839,9 +39901,392 @@ function assertLinkedWrite(rich, content, format, allowLinkChanges = false) {
39839
39901
  }
39840
39902
  }
39841
39903
 
39904
+ // src/utils/smartFolders.ts
39905
+ import { execFileSync as execFileSync4 } from "child_process";
39906
+ import * as fs2 from "fs";
39907
+ import * as os2 from "os";
39908
+ import * as path2 from "path";
39909
+ var NOTES_DB_PATH2 = path2.join(
39910
+ os2.homedir(),
39911
+ "Library/Group Containers/group.com.apple.notes/NoteStore.sqlite"
39912
+ );
39913
+ var FDA_MESSAGE = `Full Disk Access is required to read smart folders. In System Settings > Privacy & Security > Full Disk Access, grant access to the app that launches this server (Claude Desktop / Terminal / iTerm2), then fully quit and relaunch it. Setup guide: ${FULL_DISK_ACCESS_GUIDE_URL} \u2014 run the doctor tool to verify.`;
39914
+ var COCOA_EPOCH_OFFSET = 978307200;
39915
+ var MAX_DEPTH = 32;
39916
+ var RELATIVE_RANGES = {
39917
+ 0: "today",
39918
+ 1: "yesterday",
39919
+ 2: "in the last 7 days",
39920
+ 3: "in the last 30 days",
39921
+ 4: "in the last 3 months",
39922
+ 5: "in the last 12 months"
39923
+ };
39924
+ var CUSTOM_UNITS = {
39925
+ 0: "hours",
39926
+ 1: "days",
39927
+ 2: "weeks",
39928
+ 3: "months",
39929
+ 4: "years"
39930
+ };
39931
+ var ATTACHMENT_SECTIONS = {
39932
+ 1: "Photos & Videos",
39933
+ 2: "Drawings",
39934
+ 3: "Maps",
39935
+ 4: "Websites",
39936
+ 5: "Audio",
39937
+ 6: "Documents",
39938
+ 7: "Scans"
39939
+ };
39940
+ var isClause = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
39941
+ var isDeletedWrapper = (value) => isClause(value) && Object.keys(value).length === 1 && typeof value.deleted === "boolean";
39942
+ function cocoaDate(value) {
39943
+ if (typeof value !== "number" || !Number.isFinite(value)) return null;
39944
+ const date3 = new Date((value + COCOA_EPOCH_OFFSET) * 1e3);
39945
+ return Number.isNaN(date3.getTime()) ? null : date3.toISOString();
39946
+ }
39947
+ var Decoder = class {
39948
+ constructor(context) {
39949
+ this.context = context;
39950
+ }
39951
+ context;
39952
+ fullyDecoded = true;
39953
+ unknown(key, value, excluded) {
39954
+ this.fullyDecoded = false;
39955
+ return {
39956
+ type: "unknown",
39957
+ ...key === null ? {} : { key },
39958
+ value,
39959
+ ...excluded ? { excluded: true } : {},
39960
+ description: "Unrecognized clause (see value)"
39961
+ };
39962
+ }
39963
+ /** Decode a list of sibling clauses, dropping inner `deleted` wrappers. */
39964
+ list(clauses, excluded, depth) {
39965
+ if (!Array.isArray(clauses)) return [this.clause(clauses, excluded, depth)];
39966
+ return clauses.filter((clause) => !isDeletedWrapper(clause)).map((clause) => this.clause(clause, excluded, depth));
39967
+ }
39968
+ group(match, clauses, excluded, depth) {
39969
+ const items = Array.isArray(clauses) ? clauses.filter((clause) => !isDeletedWrapper(clause)) : [clauses];
39970
+ if (items.length === 1) return this.clause(items[0], excluded, depth + 1);
39971
+ const filters = this.list(items, false, depth + 1);
39972
+ const joiner = match === "all" ? "all of" : "any of";
39973
+ return {
39974
+ type: "group",
39975
+ match,
39976
+ ...excluded ? { excluded: true } : {},
39977
+ filters,
39978
+ description: `${excluded ? "not " : ""}${joiner}: ${filters.map((f) => f.description).join("; ")}`
39979
+ };
39980
+ }
39981
+ /** Mark a positively described leaf as an Exclude rule. */
39982
+ negate(filter) {
39983
+ const text = filter.description;
39984
+ const negated = text.startsWith("has ") ? text.replace(/^has /, "does not have ") : text.startsWith("is ") ? text.replace(/^is /, "is not ") : text.startsWith("mentions ") ? text.replace(/^mentions /, "does not mention ") : `not ${text}`;
39985
+ return { ...filter, excluded: true, description: negated };
39986
+ }
39987
+ clause(clause, excluded, depth) {
39988
+ if (depth > MAX_DEPTH || !isClause(clause)) return this.unknown(null, clause, excluded);
39989
+ const keys = Object.keys(clause);
39990
+ if (keys.length !== 1) {
39991
+ return this.group(
39992
+ "all",
39993
+ keys.map((key2) => ({ [key2]: clause[key2] })),
39994
+ excluded,
39995
+ depth
39996
+ );
39997
+ }
39998
+ const [key] = keys;
39999
+ const value = clause[key];
40000
+ if (key === "and" || key === "or") {
40001
+ if (!Array.isArray(value)) return this.unknown(key, value, excluded);
40002
+ return this.group(key === "and" ? "all" : "any", value, excluded, depth);
40003
+ }
40004
+ if (key === "not") return this.clause(value, !excluded, depth + 1);
40005
+ const leaf = this.leaf(key, value, excluded);
40006
+ if (!leaf) return this.unknown(key, value, excluded);
40007
+ return excluded && !leaf.excluded ? this.negate(leaf) : leaf;
40008
+ }
40009
+ /**
40010
+ * Decode one leaf clause. Boolean flags describe their own negation (an
40011
+ * excluded `pinned: true` reads "is not pinned"); other leaves are described
40012
+ * positively and negated by the caller.
40013
+ */
40014
+ leaf(key, value, excluded) {
40015
+ const flag = (description, negative) => typeof value === "boolean" ? {
40016
+ type: key,
40017
+ value,
40018
+ ...excluded ? { excluded: true } : {},
40019
+ description: value !== excluded ? description : negative
40020
+ } : null;
40021
+ switch (key) {
40022
+ case "checklist":
40023
+ return flag("has a checklist", "has no checklist");
40024
+ case "checklistInProgress":
40025
+ return flag("has an unfinished checklist", "has no unfinished checklist");
40026
+ case "checklistCompleted":
40027
+ return flag("has a completed checklist", "has no completed checklist");
40028
+ case "attachment":
40029
+ return flag("has attachments", "has no attachments");
40030
+ case "pinned":
40031
+ return flag("is pinned", "is not pinned");
40032
+ case "systemPaper":
40033
+ return flag("is a Quick Note", "is not a Quick Note");
40034
+ case "passwordProtected":
40035
+ return flag("is locked", "is not locked");
40036
+ case "shared":
40037
+ return flag("is shared", "is not shared");
40038
+ case "mention":
40039
+ return flag("mentions anyone", "mentions no one");
40040
+ case "tagged":
40041
+ return flag("has any tag", "has no tags");
40042
+ case "attachmentSection": {
40043
+ if (typeof value !== "number" || !ATTACHMENT_SECTIONS[value]) return null;
40044
+ return {
40045
+ type: key,
40046
+ value,
40047
+ name: ATTACHMENT_SECTIONS[value],
40048
+ description: `has ${ATTACHMENT_SECTIONS[value]} attachments`
40049
+ };
40050
+ }
40051
+ case "tag": {
40052
+ if (typeof value !== "string") return null;
40053
+ const names = this.context.tags?.[value] ?? [];
40054
+ const name = names.length === 1 ? names[0] : void 0;
40055
+ return {
40056
+ type: key,
40057
+ value,
40058
+ ...name ? { name } : {},
40059
+ description: `has tag #${name ?? value}`
40060
+ };
40061
+ }
40062
+ case "folder": {
40063
+ if (typeof value !== "string") return null;
40064
+ const folder = this.context.folders?.[value];
40065
+ return {
40066
+ type: key,
40067
+ value,
40068
+ ...folder ? { folderId: folder.id } : {},
40069
+ ...folder?.title ? { name: folder.title } : {},
40070
+ description: folder?.title ? `is in folder "${folder.title}"` : `is in folder ${value}${folder ? "" : " (not found)"}`
40071
+ };
40072
+ }
40073
+ case "sharedParticipant":
40074
+ case "mentionParticipant":
40075
+ return typeof value === "string" ? {
40076
+ type: key,
40077
+ value,
40078
+ description: key === "sharedParticipant" ? `is shared with participant ${value}` : `mentions participant ${value}`
40079
+ } : null;
40080
+ case "creationDateRelativeRange":
40081
+ case "modificationDateRelativeRange": {
40082
+ if (!isClause(value) || typeof value.type !== "number") return null;
40083
+ const verb = key.startsWith("creation") ? "created" : "edited";
40084
+ if (value.type === 6) {
40085
+ const amount = value.customAmount;
40086
+ const unit = typeof value.customUnit === "number" ? CUSTOM_UNITS[value.customUnit] : null;
40087
+ if (typeof amount !== "number" || !unit) return null;
40088
+ return {
40089
+ type: key,
40090
+ value,
40091
+ description: `${verb} in the last ${amount} ${unit}`
40092
+ };
40093
+ }
40094
+ const range = RELATIVE_RANGES[value.type];
40095
+ return range ? { type: key, value, description: `${verb} ${range}` } : null;
40096
+ }
40097
+ case "creationDateRange":
40098
+ case "modificationDateRange": {
40099
+ if (!isClause(value)) return null;
40100
+ const from = cocoaDate(value.fromDate);
40101
+ const to = cocoaDate(value.toDate);
40102
+ if (!from || !to) return null;
40103
+ const verb = key.startsWith("creation") ? "created" : "edited";
40104
+ return {
40105
+ type: key,
40106
+ value,
40107
+ from,
40108
+ to,
40109
+ description: `${verb} between ${from} and ${to}`
40110
+ };
40111
+ }
40112
+ default:
40113
+ return null;
40114
+ }
40115
+ }
40116
+ };
40117
+ function decodeSmartFolderQuery(raw, context = {}) {
40118
+ if (raw === null || raw.trim() === "") {
40119
+ return { match: null, filters: [], query: null, fullyDecoded: false };
40120
+ }
40121
+ let parsed;
40122
+ try {
40123
+ parsed = JSON.parse(raw);
40124
+ } catch {
40125
+ return { match: null, filters: [], query: raw, fullyDecoded: false };
40126
+ }
40127
+ const decoder = new Decoder(context);
40128
+ const type = isClause(parsed) ? parsed.type : void 0;
40129
+ if (!isClause(type)) {
40130
+ return {
40131
+ match: null,
40132
+ filters: [decoder.unknown(null, parsed, false)],
40133
+ query: parsed,
40134
+ fullyDecoded: false
40135
+ };
40136
+ }
40137
+ let includesRecentlyDeleted;
40138
+ let inner = [type];
40139
+ if (Object.keys(type).length === 1 && Array.isArray(type.and)) {
40140
+ const wrapper = type.and.find(isDeletedWrapper);
40141
+ if (wrapper) {
40142
+ includesRecentlyDeleted = wrapper.deleted;
40143
+ inner = type.and.filter((clause) => !isDeletedWrapper(clause));
40144
+ }
40145
+ }
40146
+ const query = inner.length === 1 ? inner[0] : { and: inner };
40147
+ let match = "all";
40148
+ let filters;
40149
+ const only = inner.length === 1 && isClause(inner[0]) ? inner[0] : void 0;
40150
+ const onlyKey = only && Object.keys(only).length === 1 ? Object.keys(only)[0] : void 0;
40151
+ if (only && onlyKey === "and" && Array.isArray(only.and)) {
40152
+ filters = decoder.list(only.and, false, 1);
40153
+ } else if (only && onlyKey === "or" && Array.isArray(only.or)) {
40154
+ match = "any";
40155
+ filters = decoder.list(only.or, false, 1);
40156
+ } else if (only && onlyKey === "not" && isClause(only.not) && Object.keys(only.not).length === 1 && Array.isArray(only.not.or)) {
40157
+ match = "none";
40158
+ filters = decoder.list(only.not.or, false, 1);
40159
+ } else {
40160
+ filters = decoder.list(inner, false, 1);
40161
+ }
40162
+ return {
40163
+ match,
40164
+ filters,
40165
+ query,
40166
+ ...includesRecentlyDeleted === void 0 ? {} : { includesRecentlyDeleted },
40167
+ fullyDecoded: decoder.fullyDecoded
40168
+ };
40169
+ }
40170
+ var SMART_FOLDERS_SQL = `BEGIN;
40171
+ SELECT Z_UUID FROM Z_METADATA;
40172
+ SELECT json_group_array(json_object(
40173
+ 'pk', f.Z_PK,
40174
+ 'identifier', f.ZIDENTIFIER,
40175
+ 'title', f.ZTITLE2,
40176
+ 'folderType', f.ZFOLDERTYPE,
40177
+ 'query', f.ZSMARTFOLDERQUERYJSON,
40178
+ 'parentPk', p.Z_PK,
40179
+ 'parentIdentifier', p.ZIDENTIFIER,
40180
+ 'parentTitle', p.ZTITLE2,
40181
+ 'accountPk', a.Z_PK,
40182
+ 'accountIdentifier', a.ZIDENTIFIER,
40183
+ 'accountName', a.ZNAME))
40184
+ FROM ZICCLOUDSYNCINGOBJECT f
40185
+ LEFT JOIN ZICCLOUDSYNCINGOBJECT p ON p.Z_PK = f.ZPARENT
40186
+ LEFT JOIN ZICCLOUDSYNCINGOBJECT a ON a.Z_PK = f.ZOWNER
40187
+ WHERE f.Z_ENT = (SELECT Z_ENT FROM Z_PRIMARYKEY WHERE Z_NAME = 'ICFolder')
40188
+ AND (f.ZFOLDERTYPE = 2 OR f.ZSMARTFOLDERQUERYJSON IS NOT NULL)
40189
+ AND COALESCE(f.ZMARKEDFORDELETION, 0) = 0;
40190
+ SELECT json_group_object(ZIDENTIFIER, json_object('pk', Z_PK, 'title', ZTITLE2))
40191
+ FROM ZICCLOUDSYNCINGOBJECT
40192
+ WHERE Z_ENT = (SELECT Z_ENT FROM Z_PRIMARYKEY WHERE Z_NAME = 'ICFolder')
40193
+ AND ZIDENTIFIER IS NOT NULL;
40194
+ SELECT json_group_array(json_object('standardized', ZSTANDARDIZEDCONTENT, 'display', ZDISPLAYTEXT))
40195
+ FROM ZICCLOUDSYNCINGOBJECT
40196
+ WHERE Z_ENT = (SELECT Z_ENT FROM Z_PRIMARYKEY WHERE Z_NAME = 'ICHashtag')
40197
+ AND ZSTANDARDIZEDCONTENT IS NOT NULL
40198
+ AND COALESCE(ZMARKEDFORDELETION, 0) = 0;
40199
+ COMMIT;`;
40200
+ var REQUIRED_COLUMNS = [
40201
+ "ZIDENTIFIER",
40202
+ "ZTITLE2",
40203
+ "ZFOLDERTYPE",
40204
+ "ZSMARTFOLDERQUERYJSON",
40205
+ "ZPARENT",
40206
+ "ZOWNER",
40207
+ "ZNAME",
40208
+ "ZMARKEDFORDELETION",
40209
+ "ZSTANDARDIZEDCONTENT",
40210
+ "ZDISPLAYTEXT"
40211
+ ];
40212
+ function buildSmartFolders(output) {
40213
+ const lines = output.trim().split("\n");
40214
+ if (lines.length < 4) throw new Error("Unexpected smart folder query output");
40215
+ const [storeUuid, rowsJson, foldersJson, tagsJson] = lines;
40216
+ if (!/^[0-9A-F-]+$/i.test(storeUuid)) throw new Error("Unexpected Notes store identifier");
40217
+ const coreDataId2 = (entity2, pk) => `x-coredata://${storeUuid}/${entity2}/p${pk}`;
40218
+ const rows = JSON.parse(rowsJson);
40219
+ const folderRows = JSON.parse(foldersJson || "{}");
40220
+ const tagRows = JSON.parse(tagsJson || "[]");
40221
+ const folders = {};
40222
+ for (const [identifier, row] of Object.entries(folderRows)) {
40223
+ folders[identifier] = { id: coreDataId2("ICFolder", row.pk), title: row.title };
40224
+ }
40225
+ const tags = {};
40226
+ for (const tag of tagRows) {
40227
+ if (!tag.display) continue;
40228
+ const names = tags[tag.standardized] ||= [];
40229
+ if (!names.includes(tag.display)) names.push(tag.display);
40230
+ }
40231
+ return rows.map((row) => {
40232
+ const decoded = decodeSmartFolderQuery(row.query, { folders, tags });
40233
+ return {
40234
+ id: coreDataId2("ICFolder", row.pk),
40235
+ identifier: row.identifier,
40236
+ name: row.title,
40237
+ account: row.accountName,
40238
+ accountId: row.accountPk === null ? null : coreDataId2("ICAccount", row.accountPk),
40239
+ accountIdentifier: row.accountIdentifier,
40240
+ parent: row.parentTitle,
40241
+ parentId: row.parentPk === null ? null : coreDataId2("ICFolder", row.parentPk),
40242
+ parentIdentifier: row.parentIdentifier,
40243
+ match: decoded.match,
40244
+ filters: decoded.filters,
40245
+ ...decoded.includesRecentlyDeleted === void 0 ? {} : { includesRecentlyDeleted: decoded.includesRecentlyDeleted },
40246
+ fullyDecoded: decoded.fullyDecoded,
40247
+ query: decoded.query,
40248
+ rawQuery: row.query
40249
+ };
40250
+ }).sort(
40251
+ (a, b) => (a.account ?? "").localeCompare(b.account ?? "") || (a.name ?? "").localeCompare(b.name ?? "") || a.id.localeCompare(b.id)
40252
+ );
40253
+ }
40254
+ function runSqlite(dbPath2, sql) {
40255
+ return execFileSync4("sqlite3", ["-readonly", dbPath2, sql], {
40256
+ encoding: "utf8",
40257
+ timeout: 5e3,
40258
+ maxBuffer: 16 * 1024 * 1024,
40259
+ stdio: ["pipe", "pipe", "pipe"]
40260
+ });
40261
+ }
40262
+ function readSmartFolders(dbPath2 = NOTES_DB_PATH2) {
40263
+ if (!fs2.existsSync(dbPath2)) return { folders: null, error: "no_fda", message: FDA_MESSAGE };
40264
+ try {
40265
+ const columns = new Set(
40266
+ runSqlite(dbPath2, "SELECT name FROM pragma_table_info('ZICCLOUDSYNCINGOBJECT');").trim().split("\n")
40267
+ );
40268
+ const missing = REQUIRED_COLUMNS.filter((column) => !columns.has(column));
40269
+ if (missing.length) {
40270
+ return {
40271
+ folders: null,
40272
+ error: "unsupported_schema",
40273
+ message: `This Notes database does not have the columns smart folders need (${missing.join(", ")}).`
40274
+ };
40275
+ }
40276
+ return { folders: buildSmartFolders(runSqlite(dbPath2, SMART_FOLDERS_SQL)) };
40277
+ } catch (error2) {
40278
+ const message = error2 instanceof Error ? error2.message : String(error2);
40279
+ if (message.includes("authorization denied") || message.includes("unable to open database")) {
40280
+ return { folders: null, error: "no_fda", message: FDA_MESSAGE };
40281
+ }
40282
+ console.error(`Failed to read smart folders: ${message}`);
40283
+ return { folders: null, error: "query_error", message: "Failed to read smart folders." };
40284
+ }
40285
+ }
40286
+
39842
40287
  // src/utils/attachmentFs.ts
39843
40288
  import {
39844
- existsSync as existsSync2,
40289
+ existsSync as existsSync3,
39845
40290
  lstatSync,
39846
40291
  mkdirSync,
39847
40292
  mkdtempSync,
@@ -39850,11 +40295,11 @@ import {
39850
40295
  rmSync,
39851
40296
  statSync
39852
40297
  } from "fs";
39853
- import { dirname, isAbsolute, join as join3, relative, resolve, sep } from "path";
39854
- import { homedir as homedir3, tmpdir } from "os";
40298
+ import { dirname, isAbsolute, join as join4, relative, resolve, sep } from "path";
40299
+ import { homedir as homedir4, tmpdir } from "os";
39855
40300
  function allowedSaveRoots() {
39856
40301
  return [
39857
- resolve(homedir3()),
40302
+ resolve(homedir4()),
39858
40303
  resolve(tmpdir()),
39859
40304
  "/Volumes",
39860
40305
  "/private/var/folders",
@@ -39862,8 +40307,8 @@ function allowedSaveRoots() {
39862
40307
  "/private/tmp"
39863
40308
  ];
39864
40309
  }
39865
- function canonicalize(path6) {
39866
- return realpathSync.native(path6);
40310
+ function canonicalize(path7) {
40311
+ return realpathSync.native(path7);
39867
40312
  }
39868
40313
  function isWithinRoots(candidate, roots) {
39869
40314
  return roots.some((root) => {
@@ -39885,9 +40330,9 @@ function canonicalRoots(roots) {
39885
40330
  }
39886
40331
  return canonical;
39887
40332
  }
39888
- function entryExists(path6) {
40333
+ function entryExists(path7) {
39889
40334
  try {
39890
- lstatSync(path6);
40335
+ lstatSync(path7);
39891
40336
  return true;
39892
40337
  } catch (e) {
39893
40338
  const code = e.code;
@@ -39928,7 +40373,7 @@ function assertSafeSavePath(p, roots = allowedSaveRoots()) {
39928
40373
  if (suffix.split(sep).includes("..")) {
39929
40374
  throw new Error(`Refusing to write outside allowed locations (home, temp, /Volumes): "${abs}"`);
39930
40375
  }
39931
- const canonicalDest = suffix ? join3(canonicalAncestor, suffix) : canonicalAncestor;
40376
+ const canonicalDest = suffix ? join4(canonicalAncestor, suffix) : canonicalAncestor;
39932
40377
  const allowed = canonicalRoots(roots);
39933
40378
  if (!isWithinRoots(canonicalAncestor, allowed) || !isWithinRoots(canonicalDest, allowed)) {
39934
40379
  throw new Error(
@@ -39970,16 +40415,16 @@ function makeTempDir() {
39970
40415
  }
39971
40416
  function cleanupTempDir(dir) {
39972
40417
  try {
39973
- if (existsSync2(dir)) rmSync(dir, { recursive: true, force: true });
40418
+ if (existsSync3(dir)) rmSync(dir, { recursive: true, force: true });
39974
40419
  } catch {
39975
40420
  }
39976
40421
  }
39977
40422
 
39978
40423
  // src/services/appleNotesManager.ts
39979
40424
  var import_turndown = __toESM(require_turndown_cjs(), 1);
39980
- import { existsSync as existsSync3 } from "fs";
39981
- import { homedir as homedir4 } from "os";
39982
- import { join as join4 } from "path";
40425
+ import { existsSync as existsSync4 } from "fs";
40426
+ import { homedir as homedir5 } from "os";
40427
+ import { join as join5 } from "path";
39983
40428
  var FIELD_SEP = "";
39984
40429
  var RECORD_SEP = "";
39985
40430
  var AS_FIELD_SEP = "(character id 31)";
@@ -40207,8 +40652,8 @@ function getNoteLinkFromDB(coreDataId2) {
40207
40652
  const match = coreDataId2.match(/\/p(\d+)$/);
40208
40653
  if (!match) return null;
40209
40654
  const pk = parseInt(match[1], 10);
40210
- const dbPath2 = join4(homedir4(), "Library/Group Containers/group.com.apple.notes/NoteStore.sqlite");
40211
- if (!existsSync3(dbPath2)) return null;
40655
+ const dbPath2 = join5(homedir5(), "Library/Group Containers/group.com.apple.notes/NoteStore.sqlite");
40656
+ if (!existsSync4(dbPath2)) return null;
40212
40657
  try {
40213
40658
  const { DatabaseSync } = __require("node:sqlite");
40214
40659
  const db = new DatabaseSync(dbPath2, { readOnly: true });
@@ -40849,6 +41294,58 @@ var AppleNotesManager = class {
40849
41294
  return resultList as text
40850
41295
  `;
40851
41296
  }
41297
+ /**
41298
+ * Lists every smart folder with its decoded query, read-only.
41299
+ *
41300
+ * Folder metadata and queries come from the NoteStore database (Full Disk
41301
+ * Access). With `includeMatchingNotes`, Notes.app itself is asked which notes
41302
+ * each smart folder currently shows, so membership reflects Notes' own
41303
+ * evaluation of the query rather than a reimplementation of it.
41304
+ *
41305
+ * @param options.includeMatchingNotes - Also list the notes each folder shows
41306
+ * @param options.limit - Maximum matching notes per folder (default 50)
41307
+ * @throws Error when the database cannot be read (message says why)
41308
+ */
41309
+ listSmartFolders(options = {}) {
41310
+ const result = readSmartFolders();
41311
+ if (!result.folders) throw new Error(result.message || "Failed to read smart folders.");
41312
+ if (!options.includeMatchingNotes) return result.folders;
41313
+ const limit = options.limit ?? 50;
41314
+ return result.folders.map((folder) => {
41315
+ try {
41316
+ const { total, notes } = this.listSmartFolderNoteRefs(folder.id, limit);
41317
+ return { ...folder, matchingNoteCount: total, matchingNotes: notes };
41318
+ } catch (error2) {
41319
+ return {
41320
+ ...folder,
41321
+ matchingNotesError: error2 instanceof Error ? error2.message : String(error2)
41322
+ };
41323
+ }
41324
+ });
41325
+ }
41326
+ /**
41327
+ * Asks Notes.app for the notes one smart folder currently shows.
41328
+ *
41329
+ * @returns The folder's total note count and up to `limit` (title, id) pairs
41330
+ */
41331
+ listSmartFolderNoteRefs(folderId2, limit) {
41332
+ if (!/^x-coredata:\/\/[0-9a-f-]+\/ICFolder\/p\d+$/i.test(folderId2))
41333
+ throw new Error("An exact folder ID is required");
41334
+ const safeLimit = Math.max(1, Math.floor(limit));
41335
+ const script = buildAppLevelScript(
41336
+ this.buildBulkListCommand({ folderRef: `folder id "${folderId2}"`, sliceLimit: safeLimit })
41337
+ );
41338
+ const result = executeAppleScript(script);
41339
+ if (!result.success) {
41340
+ throw new Error(`Failed to list smart folder notes: ${result.error ?? "unknown error"}`);
41341
+ }
41342
+ const sepIdx = result.output.indexOf(RECORD_SEP);
41343
+ const header = sepIdx === -1 ? result.output : result.output.slice(0, sepIdx);
41344
+ const total = Number.parseInt(header.trim(), 10);
41345
+ if (Number.isNaN(total)) throw new Error("Unexpected smart folder listing output");
41346
+ const records = sepIdx === -1 ? "" : result.output.slice(sepIdx + 1);
41347
+ return { total, notes: this.parseBulkListOutput(records, safeLimit) };
41348
+ }
40852
41349
  /**
40853
41350
  * Parses bulk listing output into deduplicated (title, id) pairs.
40854
41351
  *
@@ -42009,7 +42506,7 @@ var AppleNotesManager = class {
42009
42506
  if (parts[0] !== "OK") {
42010
42507
  return { success: false, error: parts[1]?.trim() || "attachment not found" };
42011
42508
  }
42012
- if (!existsSync3(abs) || fileSize(abs) === 0) {
42509
+ if (!existsSync4(abs) || fileSize(abs) === 0) {
42013
42510
  return { success: false, error: `Notes reported success but no file was written to ${abs}` };
42014
42511
  }
42015
42512
  return {
@@ -42452,15 +42949,15 @@ var AppleNotesManager = class {
42452
42949
  };
42453
42950
 
42454
42951
  // src/utils/syncDetection.ts
42455
- import { execFileSync as execFileSync4 } from "child_process";
42456
- import * as fs2 from "fs";
42457
- import * as path2 from "path";
42458
- import * as os2 from "os";
42459
- var NOTES_DB_PATH2 = path2.join(
42460
- os2.homedir(),
42952
+ import { execFileSync as execFileSync5 } from "child_process";
42953
+ import * as fs3 from "fs";
42954
+ import * as path3 from "path";
42955
+ import * as os3 from "os";
42956
+ var NOTES_DB_PATH3 = path3.join(
42957
+ os3.homedir(),
42461
42958
  "Library/Group Containers/group.com.apple.notes/NoteStore.sqlite"
42462
42959
  );
42463
- var WAL_PATH = `${NOTES_DB_PATH2}-wal`;
42960
+ var WAL_PATH = `${NOTES_DB_PATH3}-wal`;
42464
42961
  var RECENT_ACTIVITY_THRESHOLD_SECONDS = 5;
42465
42962
  var SYNC_STATUS_CACHE_TTL_MS = 2e3;
42466
42963
  var cachedSyncStatus = null;
@@ -42476,14 +42973,14 @@ function getSyncStatus(useCache = true) {
42476
42973
  recentActivity: false
42477
42974
  };
42478
42975
  try {
42479
- if (!fs2.existsSync(NOTES_DB_PATH2)) {
42976
+ if (!fs3.existsSync(NOTES_DB_PATH3)) {
42480
42977
  status.error = "Notes database not found";
42481
42978
  cachedSyncStatus = status;
42482
42979
  cacheTimestamp = Date.now();
42483
42980
  return status;
42484
42981
  }
42485
- if (fs2.existsSync(WAL_PATH)) {
42486
- const walStats = fs2.statSync(WAL_PATH);
42982
+ if (fs3.existsSync(WAL_PATH)) {
42983
+ const walStats = fs3.statSync(WAL_PATH);
42487
42984
  const secondsAgo = (Date.now() - walStats.mtimeMs) / 1e3;
42488
42985
  status.secondsSinceLastChange = Math.round(secondsAgo);
42489
42986
  status.recentActivity = secondsAgo < RECENT_ACTIVITY_THRESHOLD_SECONDS;
@@ -42497,9 +42994,9 @@ function getSyncStatus(useCache = true) {
42497
42994
  WHERE object.ZCLOUDSTATE = state.Z_PK
42498
42995
  );
42499
42996
  `;
42500
- const result = execFileSync4(
42997
+ const result = execFileSync5(
42501
42998
  "sqlite3",
42502
- ["-readonly", NOTES_DB_PATH2, query.replace(/\n/g, " ")],
42999
+ ["-readonly", NOTES_DB_PATH3, query.replace(/\n/g, " ")],
42503
43000
  {
42504
43001
  encoding: "utf8",
42505
43002
  timeout: 5e3,
@@ -42556,15 +43053,15 @@ function withSyncAwarenessSync(operation, fn) {
42556
43053
  }
42557
43054
 
42558
43055
  // src/utils/noteMetadata.ts
42559
- import { execFileSync as execFileSync5 } from "child_process";
42560
- import * as fs3 from "fs";
42561
- import * as path3 from "path";
42562
- import * as os3 from "os";
42563
- var NOTES_DB_PATH3 = path3.join(
42564
- os3.homedir(),
43056
+ import { execFileSync as execFileSync6 } from "child_process";
43057
+ import * as fs4 from "fs";
43058
+ import * as path4 from "path";
43059
+ import * as os4 from "os";
43060
+ var NOTES_DB_PATH4 = path4.join(
43061
+ os4.homedir(),
42565
43062
  "Library/Group Containers/group.com.apple.notes/NoteStore.sqlite"
42566
43063
  );
42567
- var FDA_MESSAGE = `Full Disk Access is required to read note metadata. In System Settings > Privacy & Security > Full Disk Access, grant access to the app that launches this server (Claude Desktop / Terminal / iTerm2), then fully quit and relaunch it. Setup guide: ${FULL_DISK_ACCESS_GUIDE_URL} \u2014 run the doctor tool to verify.`;
43064
+ var FDA_MESSAGE2 = `Full Disk Access is required to read note metadata. In System Settings > Privacy & Security > Full Disk Access, grant access to the app that launches this server (Claude Desktop / Terminal / iTerm2), then fully quit and relaunch it. Setup guide: ${FULL_DISK_ACCESS_GUIDE_URL} \u2014 run the doctor tool to verify.`;
42568
43065
  var COLUMN_MAP = [
42569
43066
  { key: "pinned", column: "ZISPINNED", type: "bool" },
42570
43067
  { key: "hasChecklist", column: "ZHASCHECKLIST", type: "bool" },
@@ -42576,15 +43073,15 @@ var COLUMN_MAP = [
42576
43073
  { key: "widgetSnippet", column: "ZWIDGETSNIPPET", type: "text" },
42577
43074
  { key: "smartFolderQuery", column: "ZSMARTFOLDERQUERYJSON", type: "text" }
42578
43075
  ];
42579
- function runSqlite(query) {
42580
- return execFileSync5("sqlite3", ["-readonly", NOTES_DB_PATH3, query], {
43076
+ function runSqlite2(query) {
43077
+ return execFileSync6("sqlite3", ["-readonly", NOTES_DB_PATH4, query], {
42581
43078
  encoding: "utf8",
42582
43079
  timeout: 5e3,
42583
43080
  stdio: ["pipe", "pipe", "pipe"]
42584
43081
  }).trim();
42585
43082
  }
42586
43083
  function presentColumns() {
42587
- const out = runSqlite("PRAGMA table_info(ZICCLOUDSYNCINGOBJECT);");
43084
+ const out = runSqlite2("PRAGMA table_info(ZICCLOUDSYNCINGOBJECT);");
42588
43085
  const cols = /* @__PURE__ */ new Set();
42589
43086
  for (const line of out.split("\n")) {
42590
43087
  const name = line.split("|")[1];
@@ -42602,8 +43099,8 @@ function getNoteMetadata(noteId3) {
42602
43099
  };
42603
43100
  }
42604
43101
  const pk = pkMatch[1];
42605
- if (!fs3.existsSync(NOTES_DB_PATH3)) {
42606
- return { metadata: null, error: "no_fda", message: FDA_MESSAGE };
43102
+ if (!fs4.existsSync(NOTES_DB_PATH4)) {
43103
+ return { metadata: null, error: "no_fda", message: FDA_MESSAGE2 };
42607
43104
  }
42608
43105
  try {
42609
43106
  const available = presentColumns();
@@ -42612,7 +43109,7 @@ function getNoteMetadata(noteId3) {
42612
43109
  return { metadata: {} };
42613
43110
  }
42614
43111
  const pairs = selected.map((c) => `'${c.key}', ${c.column}`).join(", ");
42615
- const row = runSqlite(
43112
+ const row = runSqlite2(
42616
43113
  `SELECT json_object(${pairs}) FROM ZICCLOUDSYNCINGOBJECT WHERE Z_PK = ${pk};`
42617
43114
  );
42618
43115
  if (!row) {
@@ -42637,7 +43134,7 @@ function getNoteMetadata(noteId3) {
42637
43134
  } catch (error2) {
42638
43135
  const message = error2 instanceof Error ? error2.message : String(error2);
42639
43136
  if (message.includes("authorization denied") || message.includes("unable to open database")) {
42640
- return { metadata: null, error: "no_fda", message: FDA_MESSAGE };
43137
+ return { metadata: null, error: "no_fda", message: FDA_MESSAGE2 };
42641
43138
  }
42642
43139
  console.error(`Failed to read note metadata: ${message}`);
42643
43140
  return { metadata: null, error: "query_error", message: "Failed to read note metadata." };
@@ -42645,12 +43142,12 @@ function getNoteMetadata(noteId3) {
42645
43142
  }
42646
43143
 
42647
43144
  // src/utils/noteIdentifiers.ts
42648
- import { execFileSync as execFileSync6 } from "child_process";
42649
- import * as fs4 from "fs";
42650
- import * as os4 from "os";
42651
- import * as path4 from "path";
42652
- var NOTES_DB_PATH4 = path4.join(
42653
- os4.homedir(),
43145
+ import { execFileSync as execFileSync7 } from "child_process";
43146
+ import * as fs5 from "fs";
43147
+ import * as os5 from "os";
43148
+ import * as path5 from "path";
43149
+ var NOTES_DB_PATH5 = path5.join(
43150
+ os5.homedir(),
42654
43151
  "Library/Group Containers/group.com.apple.notes/NoteStore.sqlite"
42655
43152
  );
42656
43153
  var UUID_PATTERN = /^[0-9A-Fa-f]{8}(?:-[0-9A-Fa-f]{4}){3}-[0-9A-Fa-f]{12}$/;
@@ -42724,10 +43221,10 @@ function buildLookupSql(entity2, keys) {
42724
43221
  return `SELECT json_object('store', (SELECT Z_UUID FROM Z_METADATA LIMIT 1), 'rows', (SELECT json_group_array(json_object(${fields})) FROM ZICCLOUDSYNCINGOBJECT o ${joins}WHERE o.Z_ENT = ${entityClause(entity2)} AND o.Z_PK IN (${inList})));`;
42725
43222
  }
42726
43223
  function runJsonQuery(sql, dbPath2) {
42727
- if (!fs4.existsSync(dbPath2)) throw new IdentifierResolutionError("no_fda", NO_FDA_MESSAGE);
43224
+ if (!fs5.existsSync(dbPath2)) throw new IdentifierResolutionError("no_fda", NO_FDA_MESSAGE);
42728
43225
  let out;
42729
43226
  try {
42730
- out = execFileSync6("sqlite3", ["-readonly", dbPath2, sql], {
43227
+ out = execFileSync7("sqlite3", ["-readonly", dbPath2, sql], {
42731
43228
  encoding: "utf8",
42732
43229
  timeout: 5e3,
42733
43230
  stdio: ["pipe", "pipe", "pipe"]
@@ -42747,7 +43244,7 @@ function runJsonQuery(sql, dbPath2) {
42747
43244
  function coreDataId(store, entity2, pk) {
42748
43245
  return `x-coredata://${store}/${entity2}/p${pk}`;
42749
43246
  }
42750
- function resolveIdentifiers(values, entity2, dbPath2 = NOTES_DB_PATH4) {
43247
+ function resolveIdentifiers(values, entity2, dbPath2 = NOTES_DB_PATH5) {
42751
43248
  const resolved = /* @__PURE__ */ new Map();
42752
43249
  const keys = [];
42753
43250
  const uuids = [];
@@ -42793,7 +43290,7 @@ function resolveIdentifiers(values, entity2, dbPath2 = NOTES_DB_PATH4) {
42793
43290
  return resolved;
42794
43291
  }
42795
43292
  var COREDATA_PARTS = /^x-coredata:\/\/([0-9A-Fa-f-]+)\/(ICNote|ICFolder|ICAccount)\/p(\d{1,18})$/;
42796
- function lookupStableIdentifiers(ids, entity2, dbPath2 = NOTES_DB_PATH4) {
43293
+ function lookupStableIdentifiers(ids, entity2, dbPath2 = NOTES_DB_PATH5) {
42797
43294
  const result = /* @__PURE__ */ new Map();
42798
43295
  const wanted = /* @__PURE__ */ new Map();
42799
43296
  for (const id2 of ids) {
@@ -42829,7 +43326,7 @@ function lookupStableIdentifiers(ids, entity2, dbPath2 = NOTES_DB_PATH4) {
42829
43326
  }
42830
43327
  return result;
42831
43328
  }
42832
- function withStableIdentifiers(items, entity2, dbPath2 = NOTES_DB_PATH4) {
43329
+ function withStableIdentifiers(items, entity2, dbPath2 = NOTES_DB_PATH5) {
42833
43330
  const ids = items.map((item) => item.id).filter((id2) => typeof id2 === "string");
42834
43331
  if (ids.length === 0) return items;
42835
43332
  const found = lookupStableIdentifiers(ids, entity2, dbPath2);
@@ -42926,7 +43423,7 @@ function decodeHtmlEntities(text) {
42926
43423
  }
42927
43424
  return String.fromCodePoint(codePoint);
42928
43425
  };
42929
- return text.replace(/&#x([0-9a-f]+);?/gi, (match, hex2) => decodeCodePoint(match, hex2, 16)).replace(/&#([0-9]+);?/g, (match, decimal) => decodeCodePoint(match, decimal, 10)).replace(/&nbsp(?:;|(?![0-9a-z]))/gi, " ").replace(/&quot(?:;|(?![0-9a-z]))/gi, '"').replace(/&apos(?:;|(?![0-9a-z]))/gi, "'").replace(/&lt(?:;|(?![0-9a-z]))/gi, "<").replace(/&gt(?:;|(?![0-9a-z]))/gi, ">").replace(/&amp(?:;|(?![0-9a-z]))/gi, "&");
43426
+ return text.replace(/&#x([0-9a-f]+);?/gi, (match, hex3) => decodeCodePoint(match, hex3, 16)).replace(/&#([0-9]+);?/g, (match, decimal) => decodeCodePoint(match, decimal, 10)).replace(/&nbsp(?:;|(?![0-9a-z]))/gi, " ").replace(/&quot(?:;|(?![0-9a-z]))/gi, '"').replace(/&apos(?:;|(?![0-9a-z]))/gi, "'").replace(/&lt(?:;|(?![0-9a-z]))/gi, "<").replace(/&gt(?:;|(?![0-9a-z]))/gi, ">").replace(/&amp(?:;|(?![0-9a-z]))/gi, "&");
42930
43427
  }
42931
43428
  function firstVisibleHtmlLine(html) {
42932
43429
  let text = html;
@@ -43417,13 +43914,13 @@ function positiveTextTerms(node, negated = false) {
43417
43914
  }
43418
43915
 
43419
43916
  // src/utils/noteQueryStore.ts
43420
- import { execFileSync as execFileSync7 } from "child_process";
43421
- import * as fs5 from "fs";
43422
- import * as os5 from "os";
43423
- import * as path5 from "path";
43917
+ import { execFileSync as execFileSync8 } from "child_process";
43918
+ import * as fs6 from "fs";
43919
+ import * as os6 from "os";
43920
+ import * as path6 from "path";
43424
43921
  import { gunzipSync as gunzipSync3 } from "zlib";
43425
- var NOTES_DB_PATH5 = path5.join(
43426
- os5.homedir(),
43922
+ var NOTES_DB_PATH6 = path6.join(
43923
+ os6.homedir(),
43427
43924
  "Library/Group Containers/group.com.apple.notes/NoteStore.sqlite"
43428
43925
  );
43429
43926
  var QUERY_SCAN = { DEFAULT: 500, MAX: 5e3 };
@@ -43510,13 +44007,13 @@ function countWords(text) {
43510
44007
  }
43511
44008
  return count;
43512
44009
  }
43513
- var REQUIRED_COLUMNS = ["Z_PK", "Z_ENT", "ZTITLE1", "ZFOLDER", "ZMODIFICATIONDATE1"];
44010
+ var REQUIRED_COLUMNS2 = ["Z_PK", "Z_ENT", "ZTITLE1", "ZFOLDER", "ZMODIFICATIONDATE1"];
43514
44011
  function col(available, alias, name) {
43515
44012
  return available.has(name) ? `${alias}.${name}` : "NULL";
43516
44013
  }
43517
44014
  var entity = (name) => `(SELECT Z_ENT FROM Z_PRIMARYKEY WHERE Z_NAME='${name}')`;
43518
44015
  function buildScanSql(available, options) {
43519
- const missing = REQUIRED_COLUMNS.filter((c) => !available.has(c));
44016
+ const missing = REQUIRED_COLUMNS2.filter((c) => !available.has(c));
43520
44017
  if (missing.length) {
43521
44018
  throw new NoteQueryStoreError(
43522
44019
  `This macOS version's Notes database lacks columns query-notes needs (${missing.join(", ")}).`,
@@ -43557,8 +44054,8 @@ function buildScanSql(available, options) {
43557
44054
  "COMMIT;"
43558
44055
  ].join(" ");
43559
44056
  }
43560
- function runSqlite2(dbPath2, query) {
43561
- return execFileSync7("sqlite3", ["-readonly", dbPath2, query], {
44057
+ function runSqlite3(dbPath2, query) {
44058
+ return execFileSync8("sqlite3", ["-readonly", dbPath2, query], {
43562
44059
  encoding: "utf8",
43563
44060
  timeout: 3e4,
43564
44061
  maxBuffer: 512 * 1024 * 1024,
@@ -43567,7 +44064,7 @@ function runSqlite2(dbPath2, query) {
43567
44064
  }
43568
44065
  function presentColumns2(dbPath2) {
43569
44066
  const cols = /* @__PURE__ */ new Set();
43570
- for (const line of runSqlite2(dbPath2, "PRAGMA table_info(ZICCLOUDSYNCINGOBJECT);").split("\n")) {
44067
+ for (const line of runSqlite3(dbPath2, "PRAGMA table_info(ZICCLOUDSYNCINGOBJECT);").split("\n")) {
43571
44068
  const name = line.split("|")[1];
43572
44069
  if (name) cols.add(name);
43573
44070
  }
@@ -43585,12 +44082,12 @@ function resolveFolders(rows) {
43585
44082
  seen.add(pk);
43586
44083
  const parent = row.parent !== null ? resolve3(row.parent, seen) : void 0;
43587
44084
  const name = row.name ?? "";
43588
- const path6 = parent ? `${parent.path}/${escapeSegment(name)}` : escapeSegment(name);
44085
+ const path7 = parent ? `${parent.path}/${escapeSegment(name)}` : escapeSegment(name);
43589
44086
  const plainPath = parent ? `${parent.plainPath}/${name}` : name;
43590
44087
  const info = {
43591
- path: path6,
44088
+ path: path7,
43592
44089
  plainPath,
43593
- keys: [...new Set([name, plainPath, path6].map(normalizeForMatch))],
44090
+ keys: [...new Set([name, plainPath, path7].map(normalizeForMatch))],
43594
44091
  accountPk: row.owner ?? parent?.accountPk ?? null,
43595
44092
  shared: Boolean(row.shared) || Boolean(parent?.shared)
43596
44093
  };
@@ -43622,12 +44119,12 @@ function queryNotes(expression, options = {}) {
43622
44119
  const includeDeleted = options.includeDeleted ?? false;
43623
44120
  const withBodies = needsContent(ast);
43624
44121
  const withTags = needsTags(ast);
43625
- const dbPath2 = options.dbPath ?? NOTES_DB_PATH5;
43626
- if (!fs5.existsSync(dbPath2)) throw new NoteQueryStoreError(QUERY_FDA_MESSAGE, "no_fda");
44122
+ const dbPath2 = options.dbPath ?? NOTES_DB_PATH6;
44123
+ if (!fs6.existsSync(dbPath2)) throw new NoteQueryStoreError(QUERY_FDA_MESSAGE, "no_fda");
43627
44124
  let output;
43628
44125
  try {
43629
44126
  const available = presentColumns2(dbPath2);
43630
- output = runSqlite2(
44127
+ output = runSqlite3(
43631
44128
  dbPath2,
43632
44129
  buildScanSql(available, { scanLimit, includeDeleted, withBodies, withTags })
43633
44130
  );
@@ -43758,10 +44255,10 @@ function queryNotes(expression, options = {}) {
43758
44255
  import { spawnSync } from "child_process";
43759
44256
 
43760
44257
  // src/services/nativeTags.ts
43761
- import { execFileSync as execFileSync8 } from "node:child_process";
44258
+ import { execFileSync as execFileSync9 } from "node:child_process";
43762
44259
  import { mkdtempSync as mkdtempSync2, writeFileSync, rmSync as rmSync2 } from "node:fs";
43763
44260
  import { tmpdir as tmpdir2 } from "node:os";
43764
- import { join as join9 } from "node:path";
44261
+ import { join as join10 } from "node:path";
43765
44262
 
43766
44263
  // src/services/shortcutConsent.ts
43767
44264
  function shortcutConsentHint(shortcut) {
@@ -43837,7 +44334,7 @@ function addNativeTags(request, deps) {
43837
44334
  };
43838
44335
  }
43839
44336
  function listInstalledShortcuts() {
43840
- return execFileSync8("/usr/bin/shortcuts", ["list", "--show-identifiers"], {
44337
+ return execFileSync9("/usr/bin/shortcuts", ["list", "--show-identifiers"], {
43841
44338
  encoding: "utf8",
43842
44339
  timeout: 15e3,
43843
44340
  maxBuffer: 1024 * 1024,
@@ -43863,11 +44360,11 @@ function runNativeTagsShortcut(input) {
43863
44360
  const status = nativeTagsStatus();
43864
44361
  if (!status.installed)
43865
44362
  throw new Error(`Import the supplied ${status.shortcut}.shortcut in Shortcuts first`);
43866
- const directory = mkdtempSync2(join9(tmpdir2(), "apple-notes-native-tags-"));
44363
+ const directory = mkdtempSync2(join10(tmpdir2(), "apple-notes-native-tags-"));
43867
44364
  try {
43868
- const path6 = join9(directory, "request.json");
43869
- writeFileSync(path6, JSON.stringify(input), { mode: 384 });
43870
- execFileSync8("/usr/bin/shortcuts", ["run", status.identifier, "--input-path", path6], {
44365
+ const path7 = join10(directory, "request.json");
44366
+ writeFileSync(path7, JSON.stringify(input), { mode: 384 });
44367
+ execFileSync9("/usr/bin/shortcuts", ["run", status.identifier, "--input-path", path7], {
43871
44368
  encoding: "utf8",
43872
44369
  timeout: 6e4,
43873
44370
  maxBuffer: 1024 * 1024,
@@ -43886,10 +44383,10 @@ function runNativeTagsShortcut(input) {
43886
44383
  }
43887
44384
 
43888
44385
  // src/services/backgroundNotes.ts
43889
- import { execFileSync as execFileSync9 } from "node:child_process";
44386
+ import { execFileSync as execFileSync10 } from "node:child_process";
43890
44387
  import { mkdtempSync as mkdtempSync3, writeFileSync as writeFileSync2, rmSync as rmSync3 } from "node:fs";
43891
44388
  import { tmpdir as tmpdir3 } from "node:os";
43892
- import { join as join10 } from "node:path";
44389
+ import { join as join11 } from "node:path";
43893
44390
 
43894
44391
  // src/utils/appendMarkdown.ts
43895
44392
  var escape2 = (s) => s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
@@ -43954,10 +44451,10 @@ var LEGACY_ENTITIES = {
43954
44451
  function comparableVisibleText(html) {
43955
44452
  return html.replace(/<br\s*\/?\s*>/gi, " ").replace(/<[^>]*>/g, (tag) => INLINE_TAG.test(tag) ? "" : " ").replace(
43956
44453
  /&(?:(nbsp|quot|lt|gt|amp);?|apos;|#(\d+);|#x([0-9a-f]+);)/gi,
43957
- (_match, legacy, dec, hex2) => {
44454
+ (_match, legacy, dec, hex3) => {
43958
44455
  if (legacy) return LEGACY_ENTITIES[legacy.toLowerCase()];
43959
44456
  if (dec) return String.fromCodePoint(Number(dec));
43960
- if (hex2) return String.fromCodePoint(Number.parseInt(hex2, 16));
44457
+ if (hex3) return String.fromCodePoint(Number.parseInt(hex3, 16));
43961
44458
  return "'";
43962
44459
  }
43963
44460
  ).replace(/\s+/g, " ").trim();
@@ -44078,9 +44575,9 @@ function runBackgroundShortcut(input, status = backgroundStatus()) {
44078
44575
  throw new Error(
44079
44576
  `Install the supplied "${status.shortcut}" Shortcut once; Shortcuts must list it exactly once`
44080
44577
  );
44081
- const directory = mkdtempSync3(join10(tmpdir3(), "apple-notes-background-"));
44578
+ const directory = mkdtempSync3(join11(tmpdir3(), "apple-notes-background-"));
44082
44579
  try {
44083
- const file = join10(directory, "request.json");
44580
+ const file = join11(directory, "request.json");
44084
44581
  writeFileSync2(
44085
44582
  file,
44086
44583
  JSON.stringify({
@@ -44100,7 +44597,7 @@ function runBackgroundShortcut(input, status = backgroundStatus()) {
44100
44597
  { mode: 384 }
44101
44598
  );
44102
44599
  try {
44103
- execFileSync9("/usr/bin/shortcuts", ["run", status.identifier, "--input-path", file], {
44600
+ execFileSync10("/usr/bin/shortcuts", ["run", status.identifier, "--input-path", file], {
44104
44601
  encoding: "utf8",
44105
44602
  timeout: 6e4,
44106
44603
  maxBuffer: 1024 * 1024,
@@ -44303,7 +44800,7 @@ function createMarkdownNote(manager, request, run = runBackgroundShortcut) {
44303
44800
  throw new Error(
44304
44801
  `Install the supplied "${status.shortcut}" Shortcut once; Shortcuts must list it exactly once`
44305
44802
  );
44306
- const segments = (path6) => JSON.stringify(splitFolderPath(path6).map((part) => part.toLocaleLowerCase()));
44803
+ const segments = (path7) => JSON.stringify(splitFolderPath(path7).map((part) => part.toLocaleLowerCase()));
44307
44804
  if (request.folder) {
44308
44805
  const wanted = segments(request.folder);
44309
44806
  if (!manager.listAccounts().some(
@@ -44392,7 +44889,7 @@ ${request.content}`
44392
44889
  }
44393
44890
 
44394
44891
  // src/services/capabilityMatrix.ts
44395
- import { execFileSync as execFileSync10 } from "node:child_process";
44892
+ import { execFileSync as execFileSync11 } from "node:child_process";
44396
44893
  import { release } from "node:os";
44397
44894
  var MACOS_SHORTCUTS_CLI = "12.0";
44398
44895
  var MACOS_MARKDOWN_IMPORT = "26.0";
@@ -44599,7 +45096,7 @@ function evaluateFeatures(env, features = FEATURES) {
44599
45096
  function readMacOSVersion() {
44600
45097
  if (process.platform !== "darwin") return null;
44601
45098
  try {
44602
- const out = execFileSync10("/usr/bin/sw_vers", ["-productVersion"], {
45099
+ const out = execFileSync11("/usr/bin/sw_vers", ["-productVersion"], {
44603
45100
  encoding: "utf8",
44604
45101
  timeout: 3e3,
44605
45102
  stdio: ["ignore", "pipe", "ignore"]
@@ -44632,9 +45129,9 @@ function getCapabilityMatrix(probe = probeCapabilityEnvironment) {
44632
45129
  return evaluateFeatures(probe());
44633
45130
  }
44634
45131
  function formatCapabilityMatrix(matrix) {
44635
- const os6 = matrix.runtimeOS;
45132
+ const os7 = matrix.runtimeOS;
44636
45133
  const lines = [
44637
- `Feature matrix (macOS ${os6.macOSVersion ?? "unknown"}, Darwin ${os6.darwinRelease}):`
45134
+ `Feature matrix (macOS ${os7.macOSVersion ?? "unknown"}, Darwin ${os7.darwinRelease}):`
44638
45135
  ];
44639
45136
  for (const [name, status] of Object.entries(matrix.features)) {
44640
45137
  lines.push(
@@ -44752,19 +45249,19 @@ function formatDoctorReport(r) {
44752
45249
  }
44753
45250
 
44754
45251
  // src/services/fileConfig.ts
44755
- import { existsSync as existsSync8, readFileSync as readFileSync2 } from "fs";
44756
- import { join as join11 } from "path";
44757
- import { homedir as homedir9 } from "os";
45252
+ import { existsSync as existsSync9, readFileSync as readFileSync2 } from "fs";
45253
+ import { join as join12 } from "path";
45254
+ import { homedir as homedir10 } from "os";
44758
45255
  function fileConfigPath(env = process.env) {
44759
45256
  const override = env.APPLE_NOTES_MCP_CONFIG_FILE;
44760
45257
  if (override && override.trim()) return override.trim();
44761
- return join11(homedir9(), "Library", "Application Support", "apple-notes-mcp", "config.json");
45258
+ return join12(homedir10(), "Library", "Application Support", "apple-notes-mcp", "config.json");
44762
45259
  }
44763
- function loadFileConfig(env = process.env, path6 = fileConfigPath(env)) {
45260
+ function loadFileConfig(env = process.env, path7 = fileConfigPath(env)) {
44764
45261
  const applied = [];
44765
45262
  try {
44766
- if (!existsSync8(path6)) return applied;
44767
- const parsed = JSON.parse(readFileSync2(path6, "utf8"));
45263
+ if (!existsSync9(path7)) return applied;
45264
+ const parsed = JSON.parse(readFileSync2(path7, "utf8"));
44768
45265
  if (!parsed || typeof parsed !== "object") return applied;
44769
45266
  for (const [k, v] of Object.entries(parsed)) {
44770
45267
  if (typeof v !== "string") continue;
@@ -44774,7 +45271,7 @@ function loadFileConfig(env = process.env, path6 = fileConfigPath(env)) {
44774
45271
  }
44775
45272
  }
44776
45273
  } catch (e) {
44777
- console.error(`Failed to load apple-notes-mcp config file ${path6}: ${String(e)}`);
45274
+ console.error(`Failed to load apple-notes-mcp config file ${path7}: ${String(e)}`);
44778
45275
  }
44779
45276
  return applied;
44780
45277
  }
@@ -45157,6 +45654,402 @@ function parseNoteTable(compressed) {
45157
45654
  return { rows: values, rowIds: rows.ids, columnIds: columns.ids };
45158
45655
  }
45159
45656
 
45657
+ // src/utils/noteBlocks.ts
45658
+ import { execFileSync as execFileSync12 } from "node:child_process";
45659
+ 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";
45663
+ var NoteBlocksError = class extends Error {
45664
+ code;
45665
+ constructor(code, message) {
45666
+ super(message);
45667
+ this.name = "NoteBlocksError";
45668
+ this.code = code;
45669
+ }
45670
+ };
45671
+ var STYLE_NAMES = {
45672
+ 0: "title",
45673
+ 1: "heading",
45674
+ 2: "subheading",
45675
+ 4: "monospaced",
45676
+ 100: "bulleted",
45677
+ 101: "dashed",
45678
+ 102: "numbered",
45679
+ 103: "checklist"
45680
+ };
45681
+ var ALIGNMENTS = {
45682
+ 0: "left",
45683
+ 1: "center",
45684
+ 2: "right",
45685
+ 3: "justify"
45686
+ };
45687
+ var HIGHLIGHTS = {
45688
+ 1: "purple",
45689
+ 2: "pink",
45690
+ 3: "orange",
45691
+ 4: "mint",
45692
+ 5: "blue"
45693
+ };
45694
+ var KNOWN_RUN_FIELDS = /* @__PURE__ */ new Set([1, 2, 3, 5, 6, 7, 8, 9, 10, 12, 14]);
45695
+ var KNOWN_PARAGRAPH_FIELDS = /* @__PURE__ */ new Set([1, 2, 4, 5, 8, 9]);
45696
+ var isSafeLink = (url) => /^(?:https?:\/\/|notes:\/\/|applenotes:|mailto:)/i.test(url) && !Array.from(url).some((char) => char.charCodeAt(0) < 32);
45697
+ var utf8 = new TextDecoder();
45698
+ var first = (fields, n) => fields.find((f) => f.fieldNumber === n);
45699
+ var varintOf = (fields, n) => {
45700
+ const f = first(fields, n);
45701
+ return f?.wireType === 0 && f.varint !== void 0 ? signedVarint(f.varint) : void 0;
45702
+ };
45703
+ var bytesOf = (fields, n) => {
45704
+ const f = first(fields, n);
45705
+ return f?.wireType === 2 ? f.bytes : void 0;
45706
+ };
45707
+ var stringOf = (fields, n) => {
45708
+ const b = bytesOf(fields, n);
45709
+ return b ? utf8.decode(b) : void 0;
45710
+ };
45711
+ var sub2 = (fields, n) => {
45712
+ const b = bytesOf(fields, n);
45713
+ return b ? decodeWireFields(b) : void 0;
45714
+ };
45715
+ var hex2 = (bytes) => Buffer.from(bytes).toString("hex");
45716
+ var uuidString = (bytes) => {
45717
+ const h = hex2(bytes).toUpperCase();
45718
+ return `${h.slice(0, 8)}-${h.slice(8, 12)}-${h.slice(12, 16)}-${h.slice(16, 20)}-${h.slice(20)}`;
45719
+ };
45720
+ var channel = (value) => Math.max(0, Math.min(255, Math.round((Number.isFinite(value) ? value : 0) * 255))).toString(16).toUpperCase().padStart(2, "0");
45721
+ function colorOf(fields) {
45722
+ const [r, g, b, a] = [1, 2, 3, 4].map((n) => fixed32Float(first(fields, n)));
45723
+ if (r === void 0 && g === void 0 && b === void 0) return void 0;
45724
+ const rgb = `#${channel(r)}${channel(g)}${channel(b)}`;
45725
+ return a === void 0 || a >= 0.999 ? rgb : `${rgb}${channel(a)}`;
45726
+ }
45727
+ var DEFAULT_PARAGRAPH = {
45728
+ styleType: null,
45729
+ alignmentValue: 0,
45730
+ indent: 0,
45731
+ blockQuote: false
45732
+ };
45733
+ function decodeParagraph(fields, tally) {
45734
+ if (!fields) return DEFAULT_PARAGRAPH;
45735
+ for (const n of new Set(fields.map((f) => f.fieldNumber)))
45736
+ if (!KNOWN_PARAGRAPH_FIELDS.has(n)) tally.set(n, (tally.get(n) || 0) + 1);
45737
+ const attrs = {
45738
+ styleType: varintOf(fields, 1) ?? null,
45739
+ alignmentValue: varintOf(fields, 2) ?? 0,
45740
+ indent: Math.max(0, varintOf(fields, 4) ?? 0),
45741
+ blockQuote: (varintOf(fields, 8) ?? 0) !== 0
45742
+ };
45743
+ if (attrs.styleType === -1) attrs.styleType = null;
45744
+ const checklist = sub2(fields, 5);
45745
+ const checklistId = checklist && bytesOf(checklist, 1);
45746
+ if (attrs.styleType === 103 && checklistId)
45747
+ attrs.checklist = { id: hex2(checklistId), done: varintOf(checklist, 2) === 1 };
45748
+ const uuid2 = bytesOf(fields, 9);
45749
+ if (uuid2?.length === 16) attrs.paragraphUuid = uuidString(uuid2);
45750
+ return attrs;
45751
+ }
45752
+ function decodeInline(fields) {
45753
+ const inline = {};
45754
+ const font = sub2(fields, 3);
45755
+ const hints = font ? varintOf(font, 3) ?? 0 : 0;
45756
+ const weight = varintOf(fields, 5) ?? 0;
45757
+ if (weight === 1 || weight === 3 || hints & 1) inline.bold = true;
45758
+ if (weight === 2 || weight === 3 || hints & 2) inline.italic = true;
45759
+ if (varintOf(fields, 6)) inline.underline = true;
45760
+ if (varintOf(fields, 7)) inline.strikethrough = true;
45761
+ const baseline = varintOf(fields, 8) ?? 0;
45762
+ if (baseline > 0) inline.superscript = true;
45763
+ if (baseline < 0) inline.subscript = true;
45764
+ const color = sub2(fields, 10);
45765
+ const colorValue = color && colorOf(color);
45766
+ if (colorValue) inline.color = colorValue;
45767
+ const emphasis = varintOf(fields, 14);
45768
+ if (emphasis !== void 0 && emphasis !== 0) {
45769
+ inline.highlight = HIGHLIGHTS[emphasis] ?? "unknown";
45770
+ if (inline.highlight === "unknown") inline.highlightValue = emphasis;
45771
+ }
45772
+ const link = stringOf(fields, 9);
45773
+ if (link) {
45774
+ inline.link = link;
45775
+ inline.linkSafe = isSafeLink(link);
45776
+ }
45777
+ if (font) {
45778
+ const name = stringOf(font, 1);
45779
+ const size = fixed32Float(first(font, 2));
45780
+ if (name || size !== void 0)
45781
+ inline.font = {
45782
+ ...name ? { name } : {},
45783
+ ...size !== void 0 && Number.isFinite(size) ? { size } : {}
45784
+ };
45785
+ }
45786
+ const attachment = sub2(fields, 12);
45787
+ const attachmentId = attachment && stringOf(attachment, 1);
45788
+ if (attachmentId)
45789
+ inline.attachment = { id: attachmentId, uti: stringOf(attachment, 2) || "unknown" };
45790
+ return inline;
45791
+ }
45792
+ function wrap(error2) {
45793
+ if (error2 instanceof NoteBlocksError) throw error2;
45794
+ if (error2 instanceof ProtobufDecodeError)
45795
+ throw new NoteBlocksError("malformed-protobuf", error2.message);
45796
+ throw error2;
45797
+ }
45798
+ function decodeNoteBlocks(data) {
45799
+ let note;
45800
+ try {
45801
+ const document = sub2(decodeWireFields(data), 2);
45802
+ note = document && sub2(document, 3);
45803
+ } catch (error2) {
45804
+ wrap(error2);
45805
+ }
45806
+ const textBytes = note && bytesOf(note, 2);
45807
+ if (!note || !textBytes)
45808
+ throw new NoteBlocksError("unsupported-structure", "Unsupported Notes document structure");
45809
+ const text = utf8.decode(textBytes);
45810
+ const runTally = /* @__PURE__ */ new Map();
45811
+ const paragraphTally = /* @__PURE__ */ new Map();
45812
+ const runs = [];
45813
+ let position = 0;
45814
+ try {
45815
+ for (const field of note.filter((f) => f.fieldNumber === 5)) {
45816
+ if (field.wireType !== 2 || !field.bytes)
45817
+ throw new NoteBlocksError("invalid-runs", "Invalid Notes attribute run");
45818
+ const fields = decodeWireFields(field.bytes);
45819
+ const length = varintOf(fields, 1);
45820
+ if (length === void 0 || length < 0 || position + length > text.length)
45821
+ throw new NoteBlocksError("invalid-runs", "Invalid Notes run length");
45822
+ for (const n of new Set(fields.map((f) => f.fieldNumber)))
45823
+ if (!KNOWN_RUN_FIELDS.has(n)) runTally.set(n, (runTally.get(n) || 0) + 1);
45824
+ runs.push({
45825
+ start: position,
45826
+ length,
45827
+ paragraph: decodeParagraph(sub2(fields, 2), paragraphTally),
45828
+ inline: decodeInline(fields)
45829
+ });
45830
+ position += length;
45831
+ }
45832
+ } catch (error2) {
45833
+ wrap(error2);
45834
+ }
45835
+ if (position !== text.length)
45836
+ throw new NoteBlocksError("invalid-runs", "Incomplete Notes attribute runs");
45837
+ const blocks = [];
45838
+ const attachments = [];
45839
+ let runIndex = 0;
45840
+ let paragraphStart = 0;
45841
+ while (paragraphStart < text.length) {
45842
+ const newline = text.indexOf("\n", paragraphStart);
45843
+ const end = newline === -1 ? text.length : newline;
45844
+ while (runIndex < runs.length - 1 && runs[runIndex].start + runs[runIndex].length <= paragraphStart)
45845
+ runIndex++;
45846
+ const attrs = runs[runIndex]?.paragraph ?? DEFAULT_PARAGRAPH;
45847
+ const style = attrs.styleType === null ? "body" : STYLE_NAMES[attrs.styleType] ?? "unknown";
45848
+ const alignment = ALIGNMENTS[attrs.alignmentValue] ?? "unknown";
45849
+ const block = {
45850
+ index: blocks.length,
45851
+ start: paragraphStart,
45852
+ length: end - paragraphStart,
45853
+ text: text.slice(paragraphStart, end),
45854
+ style,
45855
+ styleType: attrs.styleType,
45856
+ indent: attrs.indent,
45857
+ alignment,
45858
+ ...alignment === "unknown" ? { alignmentValue: attrs.alignmentValue } : {},
45859
+ blockQuote: attrs.blockQuote,
45860
+ ...attrs.checklist ? { checklist: attrs.checklist } : {},
45861
+ ...attrs.paragraphUuid ? { paragraphUuid: attrs.paragraphUuid } : {},
45862
+ runs: [],
45863
+ attachments: []
45864
+ };
45865
+ for (let i = runIndex; i < runs.length && runs[i].start < end; i++) {
45866
+ const run = runs[i];
45867
+ const start = Math.max(run.start, paragraphStart);
45868
+ const stop = Math.min(run.start + run.length, end);
45869
+ if (stop <= start) continue;
45870
+ block.runs.push({
45871
+ start,
45872
+ length: stop - start,
45873
+ text: text.slice(start, stop),
45874
+ ...run.inline
45875
+ });
45876
+ if (run.inline.attachment)
45877
+ for (let at = text.indexOf("\uFFFC", start); at !== -1 && at < stop; at = text.indexOf("\uFFFC", at + 1)) {
45878
+ const marker = { ...run.inline.attachment, start: at, blockIndex: block.index };
45879
+ block.attachments.push(marker);
45880
+ attachments.push(marker);
45881
+ }
45882
+ }
45883
+ blocks.push(block);
45884
+ paragraphStart = end + 1;
45885
+ }
45886
+ return {
45887
+ text,
45888
+ textLength: text.length,
45889
+ blocks,
45890
+ attachments,
45891
+ undecodedFields: {
45892
+ attributeRun: Object.fromEntries([...runTally].sort((a, b) => a[0] - b[0])),
45893
+ paragraphStyle: Object.fromEntries([...paragraphTally].sort((a, b) => a[0] - b[0]))
45894
+ },
45895
+ summary: summarize(blocks, attachments)
45896
+ };
45897
+ }
45898
+ function summarize(blocks, attachments) {
45899
+ const summary = {
45900
+ blocks: blocks.length,
45901
+ styles: {},
45902
+ alignments: {},
45903
+ blockQuotes: 0,
45904
+ indented: 0,
45905
+ checklist: { total: 0, done: 0 },
45906
+ inline: {
45907
+ bold: 0,
45908
+ italic: 0,
45909
+ underline: 0,
45910
+ strikethrough: 0,
45911
+ superscript: 0,
45912
+ subscript: 0,
45913
+ color: 0,
45914
+ highlight: 0,
45915
+ link: 0,
45916
+ unsafeLink: 0
45917
+ },
45918
+ attachments: attachments.length
45919
+ };
45920
+ for (const block of blocks) {
45921
+ summary.styles[block.style] = (summary.styles[block.style] || 0) + 1;
45922
+ summary.alignments[block.alignment] = (summary.alignments[block.alignment] || 0) + 1;
45923
+ if (block.blockQuote) summary.blockQuotes++;
45924
+ if (block.indent > 0) summary.indented++;
45925
+ if (block.checklist) {
45926
+ summary.checklist.total++;
45927
+ if (block.checklist.done) summary.checklist.done++;
45928
+ }
45929
+ for (const run of block.runs) {
45930
+ for (const key of [
45931
+ "bold",
45932
+ "italic",
45933
+ "underline",
45934
+ "strikethrough",
45935
+ "superscript",
45936
+ "subscript",
45937
+ "color",
45938
+ "highlight",
45939
+ "link"
45940
+ ])
45941
+ if (run[key]) summary.inline[key]++;
45942
+ if (run.link && !run.linkSafe) summary.inline.unsafeLink++;
45943
+ }
45944
+ }
45945
+ return summary;
45946
+ }
45947
+ function decodeCompressedNoteBlocks(compressed) {
45948
+ let data;
45949
+ try {
45950
+ data = gunzipSync5(compressed, { maxOutputLength: 32 * 1024 * 1024 });
45951
+ } catch (error2) {
45952
+ throw new NoteBlocksError(
45953
+ "decompress-failed",
45954
+ `Failed to decompress note data: ${error2 instanceof Error ? error2.message : String(error2)}`
45955
+ );
45956
+ }
45957
+ return decodeNoteBlocks(data);
45958
+ }
45959
+ var DEFAULT_BLOCKS_MAX_BYTES = 4 * 1024 * 1024;
45960
+ function blocksMaxResponseBytes(env = process.env) {
45961
+ const raw = env.APPLE_NOTES_MCP_BLOCKS_MAX_BYTES;
45962
+ if (raw !== void 0) {
45963
+ const n = Number(raw);
45964
+ if (Number.isFinite(n) && n > 0) return Math.floor(n);
45965
+ }
45966
+ return DEFAULT_BLOCKS_MAX_BYTES;
45967
+ }
45968
+ function pageNoteBlocks(doc, { offset = 0, limit = 500, maxBytes = blocksMaxResponseBytes() } = {}) {
45969
+ const total = doc.blocks.length;
45970
+ const start = Math.min(Math.max(0, offset), total);
45971
+ const blocks = [];
45972
+ let bytes = 0;
45973
+ for (let i = start; i < total && blocks.length < limit; i++) {
45974
+ let block = doc.blocks[i];
45975
+ let size = Buffer.byteLength(JSON.stringify(block));
45976
+ if (bytes + size > maxBytes) {
45977
+ if (blocks.length) break;
45978
+ block = {
45979
+ ...block,
45980
+ text: "",
45981
+ textOmitted: true,
45982
+ runs: block.runs.map((run) => ({ ...run, text: "" }))
45983
+ };
45984
+ size = Buffer.byteLength(JSON.stringify(block));
45985
+ }
45986
+ blocks.push(block);
45987
+ bytes += size;
45988
+ }
45989
+ const next = start + blocks.length;
45990
+ return {
45991
+ textLength: doc.textLength,
45992
+ blocks,
45993
+ page: {
45994
+ offset: start,
45995
+ returned: blocks.length,
45996
+ total,
45997
+ hasMore: next < total,
45998
+ ...next < total ? { nextOffset: next } : {}
45999
+ },
46000
+ summary: doc.summary,
46001
+ undecodedFields: doc.undecodedFields
46002
+ };
46003
+ }
46004
+ var NOTES_DB_PATH7 = join13(
46005
+ homedir11(),
46006
+ "Library/Group Containers/group.com.apple.notes/NoteStore.sqlite"
46007
+ );
46008
+ function readNoteBlocks(id2, { dbPath: dbPath2 = NOTES_DB_PATH7 } = {}) {
46009
+ const pk = /^x-coredata:\/\/[0-9a-f-]+\/ICNote\/p([0-9]{1,18})$/i.exec(id2)?.[1];
46010
+ if (!pk)
46011
+ throw new NoteBlocksError(
46012
+ "invalid-id",
46013
+ `Invalid note ID: expected x-coredata://<store>/ICNote/p<number>`
46014
+ );
46015
+ if (!existsSync10(dbPath2))
46016
+ throw new NoteBlocksError("no-full-disk-access", "The Notes database is not readable");
46017
+ 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
+ let output;
46019
+ try {
46020
+ output = execFileSync12(
46021
+ "/usr/bin/sqlite3",
46022
+ ["-readonly", "-cmd", ".parameter init", "-cmd", `.parameter set @pk ${pk}`, dbPath2, sql],
46023
+ {
46024
+ encoding: "utf8",
46025
+ timeout: 5e3,
46026
+ maxBuffer: 64 * 1024 * 1024,
46027
+ stdio: ["pipe", "pipe", "pipe"]
46028
+ }
46029
+ ).trim();
46030
+ } catch (error2) {
46031
+ const message = error2 instanceof Error ? error2.message : String(error2);
46032
+ if (/authorization denied|unable to open database/i.test(message))
46033
+ throw new NoteBlocksError("no-full-disk-access", "The Notes database is not readable");
46034
+ throw new NoteBlocksError("query-failed", "Failed to query the Notes database");
46035
+ }
46036
+ let row;
46037
+ try {
46038
+ row = JSON.parse(output);
46039
+ } catch {
46040
+ throw new NoteBlocksError("query-failed", "Unexpected Notes database response");
46041
+ }
46042
+ if (!row.exists) throw new NoteBlocksError("not-found", `No note found for ID "${id2}"`);
46043
+ if (row.encrypted)
46044
+ throw new NoteBlocksError(
46045
+ "encrypted",
46046
+ "This note is password-protected; its body is encrypted and cannot be decoded"
46047
+ );
46048
+ if (!row.data || !/^[0-9a-f]+$/i.test(row.data))
46049
+ throw new NoteBlocksError("no-body", "No body data is stored for this note");
46050
+ return decodeCompressedNoteBlocks(Buffer.from(row.data, "hex"));
46051
+ }
46052
+
45160
46053
  // src/utils/linkInsert.ts
45161
46054
  var MAX_LINK_URL_LENGTH = 4096;
45162
46055
  var MAX_LINK_LABEL_LENGTH = 2e3;
@@ -45285,7 +46178,7 @@ import {
45285
46178
  writeFileSync as writeFileSync3
45286
46179
  } from "node:fs";
45287
46180
  import { tmpdir as tmpdir4 } from "node:os";
45288
- import { basename, isAbsolute as isAbsolute2, join as join12 } from "node:path";
46181
+ import { basename, isAbsolute as isAbsolute2, join as join14 } from "node:path";
45289
46182
  var noteId = exactIdInput(
45290
46183
  "ICNote",
45291
46184
  /^x-coredata:\/\/[0-9a-f-]+\/ICNote\/p\d+$/i,
@@ -45328,9 +46221,9 @@ function assertExistingContentPreserved(before, after) {
45328
46221
  throw new Error("Existing native object content or presentation changed");
45329
46222
  }
45330
46223
  }
45331
- function localAttachment(path6) {
45332
- if (!isAbsolute2(path6)) throw new Error("An absolute local file path is required");
45333
- const descriptor = openSync(path6, constants.O_RDONLY | constants.O_NOFOLLOW);
46224
+ function localAttachment(path7) {
46225
+ if (!isAbsolute2(path7)) throw new Error("An absolute local file path is required");
46226
+ const descriptor = openSync(path7, constants.O_RDONLY | constants.O_NOFOLLOW);
45334
46227
  try {
45335
46228
  const stat = fstatSync(descriptor);
45336
46229
  if (!stat.isFile() || stat.size === 0 || stat.size > 64 * 1024 * 1024)
@@ -45394,13 +46287,13 @@ function registerDirectOperations(server2, manager) {
45394
46287
  "add-attachment",
45395
46288
  "Use when: adding one local file to an exact note without replacing its body.\nReturns: the new attachment id, byte count, and post-write content hash after exact byte verification.\nDo not use when: reading or exporting an existing attachment.\nSafety: requires a fresh rich revision, copies at most 64 MiB through a private temporary file, never retries insertion, and verifies existing content plus fetched bytes.",
45396
46289
  { id: noteId, expectedContentHash: revision, path: external_exports.string().min(1).max(4096) },
45397
- ({ id: id2, expectedContentHash, path: path6 }) => {
46290
+ ({ id: id2, expectedContentHash, path: path7 }) => {
45398
46291
  const before = readSnapshot(manager, id2);
45399
46292
  if (before.hash !== expectedContentHash) throw new Error("Note revision changed");
45400
- const bytes = localAttachment(path6);
46293
+ const bytes = localAttachment(path7);
45401
46294
  const beforeAttachments = manager.listAttachmentsById(id2);
45402
- const directory = mkdtempSync4(join12(tmpdir4(), "notes-attachment-add-"));
45403
- const temporaryFile = join12(directory, basename(path6));
46295
+ const directory = mkdtempSync4(join14(tmpdir4(), "notes-attachment-add-"));
46296
+ const temporaryFile = join14(directory, basename(path7));
45404
46297
  try {
45405
46298
  writeFileSync3(temporaryFile, bytes, { mode: 384 });
45406
46299
  if (readSnapshot(manager, id2).hash !== before.hash)
@@ -45946,7 +46839,7 @@ function registerNativeOperations(server2, manager) {
45946
46839
 
45947
46840
  // src/setupShortcuts.ts
45948
46841
  import { spawnSync as spawnSync2 } from "node:child_process";
45949
- import { existsSync as existsSync9 } from "node:fs";
46842
+ import { existsSync as existsSync11 } from "node:fs";
45950
46843
  import { release as release2 } from "node:os";
45951
46844
  import { dirname as dirname2, resolve as resolve2 } from "node:path";
45952
46845
  import { fileURLToPath } from "node:url";
@@ -45966,16 +46859,16 @@ var shortcutFiles = [
45966
46859
  ];
45967
46860
  function setupShortcuts(checkOnly, dependencies = {}) {
45968
46861
  const status = dependencies.status || nativeTagsStatus;
45969
- const exists = dependencies.exists || existsSync9;
45970
- const open = dependencies.open || ((path6) => {
45971
- const result = spawnSync2("/usr/bin/open", [path6], { encoding: "utf8" });
46862
+ const exists = dependencies.exists || existsSync11;
46863
+ const open = dependencies.open || ((path7) => {
46864
+ const result = spawnSync2("/usr/bin/open", [path7], { encoding: "utf8" });
45972
46865
  return result.status === 0 ? { ok: true } : { ok: false, error: result.stderr || result.error?.message || "open failed" };
45973
46866
  });
45974
46867
  const baseDirectory = dependencies.baseDirectory || resolve2(dirname2(fileURLToPath(import.meta.url)), "../shortcuts");
45975
46868
  const osRelease = (dependencies.osRelease || release2)();
45976
46869
  const darwinMajor = Number.parseInt(osRelease.split(".")[0], 10);
45977
46870
  const items = shortcutFiles.map(({ name, file, optional: optional2 }) => {
45978
- const path6 = resolve2(baseDirectory, file);
46871
+ const path7 = resolve2(baseDirectory, file);
45979
46872
  let installed = false;
45980
46873
  let identifier;
45981
46874
  let error2;
@@ -45991,9 +46884,9 @@ function setupShortcuts(checkOnly, dependencies = {}) {
45991
46884
  if (!installed && !checkOnly) {
45992
46885
  if (optional2 && !(darwinMajor >= MARKDOWN_MIN_DARWIN_MAJOR))
45993
46886
  skipped = `requires macOS 26 or later (this Mac reports Darwin ${osRelease})`;
45994
- else if (!exists(path6)) error2 = `Packaged Shortcut is missing: ${path6}`;
46887
+ else if (!exists(path7)) error2 = `Packaged Shortcut is missing: ${path7}`;
45995
46888
  else {
45996
- const result = open(path6);
46889
+ const result = open(path7);
45997
46890
  opened = result.ok;
45998
46891
  if (!result.ok) error2 = result.error || `Could not open ${file}`;
45999
46892
  }
@@ -46002,7 +46895,7 @@ function setupShortcuts(checkOnly, dependencies = {}) {
46002
46895
  name,
46003
46896
  installed,
46004
46897
  identifier,
46005
- file: path6,
46898
+ file: path7,
46006
46899
  opened,
46007
46900
  ...optional2 ? { optional: optional2 } : {},
46008
46901
  ...skipped ? { skipped } : {},
@@ -46782,6 +47675,42 @@ registerTool(
46782
47675
  });
46783
47676
  }, "Error reading native objects")
46784
47677
  );
47678
+ registerTool(
47679
+ "get-note-blocks",
47680
+ {
47681
+ description: "Use when: you need a note's structure, not just its text: paragraph styles (title, heading, subheading, body, monospaced, bulleted/dashed/numbered list, checklist with done state), indent, alignment, block quote, inline formatting (bold, italic, underline, strikethrough, superscript, subscript, color, highlight, links) and attachment positions, by exact id.\nReturns: one page of blocks in body order (default 500, stopped early under APPLE_NOTES_MCP_BLOCKS_MAX_BYTES, default 4 MB), whole-note summary counts, undecodedFields, and page info; while page.hasMore is true, call again with offset set to page.nextOffset. Offsets and lengths count UTF-16 code units.\nDo not use when: you want the editable HTML body (get-note-content) or Markdown (get-note-markdown).\nSafety: read-only; decodes the NoteStore database directly and requires Full Disk Access. Password-protected notes are refused. Link URLs are returned as stored; linkSafe is false for schemes other than http(s), notes, applenotes and mailto.",
47682
+ inputSchema: {
47683
+ id: noteIdInput,
47684
+ offset: external_exports.number().int().min(0).optional().describe("Index of the first block to return (default 0); use page.nextOffset"),
47685
+ limit: external_exports.number().int().min(1).max(5e3).optional().describe("Maximum blocks to return (default 500, max 5000)")
47686
+ },
47687
+ outputSchema: {
47688
+ id: external_exports.string().optional(),
47689
+ textLength: external_exports.number().optional(),
47690
+ blocks: external_exports.array(external_exports.record(external_exports.unknown())).optional(),
47691
+ page: external_exports.record(external_exports.unknown()).optional(),
47692
+ summary: external_exports.record(external_exports.unknown()).optional(),
47693
+ undecodedFields: external_exports.record(external_exports.unknown()).optional()
47694
+ },
47695
+ annotations: { readOnlyHint: true }
47696
+ },
47697
+ withErrorHandling(({ id: id2, offset, limit }) => {
47698
+ let page;
47699
+ try {
47700
+ page = pageNoteBlocks(readNoteBlocks(id2), { offset, limit });
47701
+ } catch (error2) {
47702
+ if (!(error2 instanceof NoteBlocksError)) throw error2;
47703
+ const hint = error2.code === "no-full-disk-access" ? ` Grant Full Disk Access to the app that launches this server: ${FULL_DISK_ACCESS_GUIDE_URL}` : "";
47704
+ return errorResponse(`Error reading note blocks [${error2.code}]: ${error2.message}${hint}`);
47705
+ }
47706
+ const { summary } = page;
47707
+ const styles = Object.entries(summary.styles).map(([style, count]) => `${style} ${count}`).join(", ");
47708
+ return successResponse(
47709
+ `Decoded ${summary.blocks} blocks (${styles || "none"}); returned ${page.page.returned} from offset ${page.page.offset}` + (page.page.hasMore ? `; more available at offset ${page.page.nextOffset}` : "") + ".",
47710
+ { id: id2, ...page }
47711
+ );
47712
+ }, "Error reading note blocks")
47713
+ );
46785
47714
  registerTool(
46786
47715
  "list-native-tags",
46787
47716
  {
@@ -47346,6 +48275,55 @@ ${folderList}${syncNote}`,
47346
48275
  );
47347
48276
  }, "Error listing folders")
47348
48277
  );
48278
+ registerTool(
48279
+ "list-smart-folders",
48280
+ {
48281
+ description: `Use when: listing Smart Folders and the rules that define them.
48282
+ Returns: each smart folder's name, ids, account, and parent, its rules decoded as match ("all"/"any"/"none") plus filters (each with a readable description), the stored query with the outer deleted wrapper removed, and the raw stored query JSON. With includeMatchingNotes, also the notes Notes.app currently shows in each folder.
48283
+ Do not use when: listing ordinary folders (list-folders) or searching notes (search-notes).
48284
+ Safety: read-only; reads the NoteStore database and requires Full Disk Access. includeMatchingNotes asks Notes.app (Automation permission) for each folder's current contents rather than re-evaluating the rules. Unrecognized rules are kept as "unknown" filters and set fullyDecoded false.`,
48285
+ inputSchema: {
48286
+ includeMatchingNotes: external_exports.boolean().optional().describe(
48287
+ "Also list the notes Notes.app currently shows in each smart folder (default false)"
48288
+ ),
48289
+ limit: external_exports.number().int().min(1).max(500).optional().describe("Maximum matching notes returned per folder (default 50; count is always total)")
48290
+ },
48291
+ outputSchema: {
48292
+ smartFolders: external_exports.array(external_exports.object({}).passthrough()).optional(),
48293
+ count: external_exports.number().optional(),
48294
+ fullyDecoded: external_exports.boolean().optional()
48295
+ },
48296
+ annotations: { readOnlyHint: true }
48297
+ },
48298
+ withErrorHandling(({ includeMatchingNotes = false, limit }) => {
48299
+ const folders = notesManager.listSmartFolders({ includeMatchingNotes, limit });
48300
+ const fullyDecoded = folders.every((folder) => folder.fullyDecoded);
48301
+ if (folders.length === 0) {
48302
+ return successResponse("No smart folders found.", {
48303
+ smartFolders: [],
48304
+ count: 0,
48305
+ fullyDecoded
48306
+ });
48307
+ }
48308
+ const lines = folders.map((folder) => {
48309
+ const joiner = folder.match === "any" ? " OR " : " AND ";
48310
+ const rules = folder.filters.map((filter) => filter.description).join(joiner);
48311
+ const prefix = folder.match === "none" ? "none of: " : "";
48312
+ const where = [folder.account, folder.parent].filter(Boolean).join(" / ");
48313
+ const matches = folder.matchingNoteCount !== void 0 ? ` [${folder.matchingNoteCount} note(s)]` : folder.matchingNotesError ? ` [notes unavailable: ${folder.matchingNotesError}]` : "";
48314
+ return ` - ${folder.name ?? "(untitled)"}${where ? ` (${where})` : ""}: ${prefix}${rules || "(no rules)"}${matches}`;
48315
+ });
48316
+ return successResponse(
48317
+ `Found ${folders.length} smart folder(s):
48318
+ ${lines.join("\n")}${fullyDecoded ? "" : '\n\nSome rules were not recognized; see filters of type "unknown".'}`,
48319
+ {
48320
+ smartFolders: folders,
48321
+ count: folders.length,
48322
+ fullyDecoded
48323
+ }
48324
+ );
48325
+ }, "Error listing smart folders")
48326
+ );
47349
48327
  registerTool(
47350
48328
  "create-folder",
47351
48329
  {