apple-notes-mcp 2.8.37 → 2.8.39

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 +54 -3
  2. package/build/index.js +925 -343
  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(path8) {
3272
- let input = path8;
3271
+ function removeDotSegments(path9) {
3272
+ let input = path9;
3273
3273
  const output = [];
3274
3274
  let nextSlash = -1;
3275
3275
  let len = 0;
@@ -3674,8 +3674,8 @@ var require_schemes = __commonJS({
3674
3674
  }
3675
3675
  if (wsComponent.resourceName) {
3676
3676
  const queryIndex = wsComponent.resourceName.indexOf("?");
3677
- const path8 = queryIndex === -1 ? wsComponent.resourceName : wsComponent.resourceName.slice(0, queryIndex);
3678
- wsComponent.path = path8 && path8 !== "/" ? path8 : void 0;
3677
+ const path9 = queryIndex === -1 ? wsComponent.resourceName : wsComponent.resourceName.slice(0, queryIndex);
3678
+ wsComponent.path = path9 && path9 !== "/" ? path9 : 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, fs8, exportName) {
7184
+ function addFormats(ajv, list, fs9, 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, fs8[f]);
7189
+ ajv.addFormat(f, fs9[f]);
7190
7190
  }
7191
7191
  module.exports = exports = formatsPlugin;
7192
7192
  Object.defineProperty(exports, "__esModule", { value: true });
@@ -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(path8) {
12242
- if (!path8) return path8;
12241
+ function remove_dot_segments(path9) {
12242
+ if (!path9) return path9;
12243
12243
  var output = "";
12244
- while (path8.length > 0) {
12245
- if (path8 === "." || path8 === "..") {
12246
- path8 = "";
12244
+ while (path9.length > 0) {
12245
+ if (path9 === "." || path9 === "..") {
12246
+ path9 = "";
12247
12247
  break;
12248
12248
  }
12249
- var twochars = path8.substring(0, 2);
12250
- var threechars = path8.substring(0, 3);
12251
- var fourchars = path8.substring(0, 4);
12249
+ var twochars = path9.substring(0, 2);
12250
+ var threechars = path9.substring(0, 3);
12251
+ var fourchars = path9.substring(0, 4);
12252
12252
  if (threechars === "../") {
12253
- path8 = path8.substring(3);
12253
+ path9 = path9.substring(3);
12254
12254
  } else if (twochars === "./") {
12255
- path8 = path8.substring(2);
12255
+ path9 = path9.substring(2);
12256
12256
  } else if (threechars === "/./") {
12257
- path8 = "/" + path8.substring(3);
12258
- } else if (twochars === "/." && path8.length === 2) {
12259
- path8 = "/";
12260
- } else if (fourchars === "/../" || threechars === "/.." && path8.length === 3) {
12261
- path8 = "/" + path8.substring(4);
12257
+ path9 = "/" + path9.substring(3);
12258
+ } else if (twochars === "/." && path9.length === 2) {
12259
+ path9 = "/";
12260
+ } else if (fourchars === "/../" || threechars === "/.." && path9.length === 3) {
12261
+ path9 = "/" + path9.substring(4);
12262
12262
  output = output.replace(/\/?[^\/]*$/, "");
12263
12263
  } else {
12264
- var segment = path8.match(/(\/?([^\/]*))/)[0];
12264
+ var segment = path9.match(/(\/?([^\/]*))/)[0];
12265
12265
  output += segment;
12266
- path8 = path8.substring(segment.length);
12266
+ path9 = path9.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 join21(output, replacement);
24468
+ return join23(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 = join21(output, rule.append(self.options));
24475
+ output = join23(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 join21(output, replacement) {
24487
+ function join23(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);
@@ -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: path8, errorMaps, issueData } = params;
24983
- const fullPath = [...path8, ...issueData.path || []];
24982
+ const { data, path: path9, errorMaps, issueData } = params;
24983
+ const fullPath = [...path9, ...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, path8, key) {
25099
+ constructor(parent, value, path9, key) {
25100
25100
  this._cachedPath = [];
25101
25101
  this.parent = parent;
25102
25102
  this.data = value;
25103
- this._path = path8;
25103
+ this._path = path9;
25104
25104
  this._key = key;
25105
25105
  }
25106
25106
  get path() {
@@ -28737,10 +28737,10 @@ function assignProp(target, prop, value) {
28737
28737
  configurable: true
28738
28738
  });
28739
28739
  }
28740
- function getElementAtPath(obj, path8) {
28741
- if (!path8)
28740
+ function getElementAtPath(obj, path9) {
28741
+ if (!path9)
28742
28742
  return obj;
28743
- return path8.reduce((acc, key) => acc?.[key], obj);
28743
+ return path9.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(path8, issues) {
29063
+ function prefixIssues(path9, issues) {
29064
29064
  return issues.map((iss) => {
29065
29065
  var _a;
29066
29066
  (_a = iss).path ?? (_a.path = []);
29067
- iss.path.unshift(path8);
29067
+ iss.path.unshift(path9);
29068
29068
  return iss;
29069
29069
  });
29070
29070
  }
@@ -32475,11 +32475,11 @@ function normalizeObjectSchema(schema) {
32475
32475
  }
32476
32476
  return void 0;
32477
32477
  }
32478
- function getDotPath(path8) {
32479
- if (path8.length === 0) {
32478
+ function getDotPath(path9) {
32479
+ if (path9.length === 0) {
32480
32480
  return "object root";
32481
32481
  }
32482
- return path8.reduce((acc, seg, index) => {
32482
+ return path9.reduce((acc, seg, index) => {
32483
32483
  if (index === 0) {
32484
32484
  return String(seg);
32485
32485
  }
@@ -39452,9 +39452,9 @@ function queryNoteData(noteId3) {
39452
39452
  return { hex: null, error: "invalid_id" };
39453
39453
  }
39454
39454
  const pk = pkMatch[1];
39455
- const query = `SELECT hex(nd.ZDATA) FROM ZICNOTEDATA nd JOIN ZICCLOUDSYNCINGOBJECT n ON nd.ZNOTE = n.Z_PK WHERE n.Z_PK = ${pk};`;
39455
+ const query2 = `SELECT hex(nd.ZDATA) FROM ZICNOTEDATA nd JOIN ZICCLOUDSYNCINGOBJECT n ON nd.ZNOTE = n.Z_PK WHERE n.Z_PK = ${pk};`;
39456
39456
  try {
39457
- const result = execFileSync2("sqlite3", ["-readonly", NOTES_DB_PATH, query], {
39457
+ const result = execFileSync2("sqlite3", ["-readonly", NOTES_DB_PATH, query2], {
39458
39458
  encoding: "utf8",
39459
39459
  timeout: 5e3,
39460
39460
  stdio: ["pipe", "pipe", "pipe"]
@@ -40713,7 +40713,7 @@ function decodeSmartFolderQuery(raw, context = {}) {
40713
40713
  inner = type.and.filter((clause) => !isDeletedWrapper(clause));
40714
40714
  }
40715
40715
  }
40716
- const query = inner.length === 1 ? inner[0] : { and: inner };
40716
+ const query2 = inner.length === 1 ? inner[0] : { and: inner };
40717
40717
  let match = "all";
40718
40718
  let filters;
40719
40719
  const only = inner.length === 1 && isClause(inner[0]) ? inner[0] : void 0;
@@ -40732,7 +40732,7 @@ function decodeSmartFolderQuery(raw, context = {}) {
40732
40732
  return {
40733
40733
  match,
40734
40734
  filters,
40735
- query,
40735
+ query: query2,
40736
40736
  ...includesRecentlyDeleted === void 0 ? {} : { includesRecentlyDeleted },
40737
40737
  fullyDecoded: decoder.fullyDecoded
40738
40738
  };
@@ -40854,11 +40854,55 @@ function readSmartFolders(dbPath2 = NOTES_DB_PATH3) {
40854
40854
  }
40855
40855
  }
40856
40856
 
40857
+ // src/utils/trashFolders.ts
40858
+ import { execFileSync as execFileSync6 } from "child_process";
40859
+ import * as fs3 from "fs";
40860
+ import * as os3 from "os";
40861
+ import * as path3 from "path";
40862
+ var NOTES_DB_PATH4 = path3.join(
40863
+ os3.homedir(),
40864
+ "Library/Group Containers/group.com.apple.notes/NoteStore.sqlite"
40865
+ );
40866
+ var RECENTLY_DELETED_FOLDER_NAME = "Recently Deleted";
40867
+ var CACHE_TTL_MS = 5 * 60 * 1e3;
40868
+ var cache = null;
40869
+ function query(dbPath2, sql) {
40870
+ return execFileSync6("sqlite3", ["-readonly", dbPath2, sql], {
40871
+ encoding: "utf8",
40872
+ timeout: 5e3,
40873
+ stdio: ["pipe", "pipe", "pipe"]
40874
+ });
40875
+ }
40876
+ function readTrashFolderIds(dbPath2 = NOTES_DB_PATH4) {
40877
+ if (cache && cache.dbPath === dbPath2 && Date.now() - cache.at < CACHE_TTL_MS) {
40878
+ return cache.ids;
40879
+ }
40880
+ try {
40881
+ if (!fs3.existsSync(dbPath2)) return [];
40882
+ const columns = new Set(
40883
+ query(dbPath2, "SELECT name FROM pragma_table_info('ZICCLOUDSYNCINGOBJECT');").split("\n").map((line) => line.trim()).filter(Boolean)
40884
+ );
40885
+ const predicates = [];
40886
+ if (columns.has("ZFOLDERTYPE")) predicates.push("COALESCE(ZFOLDERTYPE, 0) = 1");
40887
+ if (columns.has("ZIDENTIFIER"))
40888
+ predicates.push("COALESCE(ZIDENTIFIER, '') LIKE 'TrashFolder%'");
40889
+ if (!predicates.length) return [];
40890
+ const sql = `SELECT (SELECT Z_UUID FROM Z_METADATA LIMIT 1); SELECT Z_PK FROM ZICCLOUDSYNCINGOBJECT WHERE Z_ENT = (SELECT Z_ENT FROM Z_PRIMARYKEY WHERE Z_NAME = 'ICFolder') AND (${predicates.join(" OR ")}) ORDER BY Z_PK;`;
40891
+ const [uuid2, ...pks] = query(dbPath2, sql).split("\n").map((line) => line.trim()).filter(Boolean);
40892
+ if (!uuid2 || !/^[0-9A-F-]+$/i.test(uuid2)) return [];
40893
+ const ids = pks.filter((pk) => /^\d+$/.test(pk)).map((pk) => `x-coredata://${uuid2}/ICFolder/p${pk}`);
40894
+ cache = { dbPath: dbPath2, at: Date.now(), ids };
40895
+ return ids;
40896
+ } catch {
40897
+ return [];
40898
+ }
40899
+ }
40900
+
40857
40901
  // src/utils/attachmentFs.ts
40858
40902
  import {
40859
40903
  closeSync,
40860
40904
  constants,
40861
- existsSync as existsSync3,
40905
+ existsSync as existsSync4,
40862
40906
  fstatSync,
40863
40907
  lstatSync,
40864
40908
  mkdirSync,
@@ -40869,11 +40913,11 @@ import {
40869
40913
  rmSync,
40870
40914
  statSync
40871
40915
  } from "fs";
40872
- import { dirname, isAbsolute, join as join5, relative, resolve, sep } from "path";
40873
- import { homedir as homedir5, tmpdir } from "os";
40916
+ import { dirname, isAbsolute, join as join6, relative, resolve, sep } from "path";
40917
+ import { homedir as homedir6, tmpdir } from "os";
40874
40918
  function allowedSaveRoots() {
40875
40919
  return [
40876
- resolve(homedir5()),
40920
+ resolve(homedir6()),
40877
40921
  resolve(tmpdir()),
40878
40922
  "/Volumes",
40879
40923
  "/private/var/folders",
@@ -40881,8 +40925,8 @@ function allowedSaveRoots() {
40881
40925
  "/private/tmp"
40882
40926
  ];
40883
40927
  }
40884
- function canonicalize(path8) {
40885
- return realpathSync.native(path8);
40928
+ function canonicalize(path9) {
40929
+ return realpathSync.native(path9);
40886
40930
  }
40887
40931
  function isWithinRoots(candidate, roots) {
40888
40932
  return roots.some((root) => {
@@ -40904,9 +40948,9 @@ function canonicalRoots(roots) {
40904
40948
  }
40905
40949
  return canonical;
40906
40950
  }
40907
- function entryExists(path8) {
40951
+ function entryExists(path9) {
40908
40952
  try {
40909
- lstatSync(path8);
40953
+ lstatSync(path9);
40910
40954
  return true;
40911
40955
  } catch (e) {
40912
40956
  const code = e.code;
@@ -40947,7 +40991,7 @@ function assertSafeSavePath(p, roots = allowedSaveRoots()) {
40947
40991
  if (suffix.split(sep).includes("..")) {
40948
40992
  throw new Error(`Refusing to write outside allowed locations (home, temp, /Volumes): "${abs}"`);
40949
40993
  }
40950
- const canonicalDest = suffix ? join5(canonicalAncestor, suffix) : canonicalAncestor;
40994
+ const canonicalDest = suffix ? join6(canonicalAncestor, suffix) : canonicalAncestor;
40951
40995
  const allowed = canonicalRoots(roots);
40952
40996
  if (!isWithinRoots(canonicalAncestor, allowed) || !isWithinRoots(canonicalDest, allowed)) {
40953
40997
  throw new Error(
@@ -41031,13 +41075,13 @@ function readAllowedTextFile(p, maxBytes, roots = allowedSaveRoots()) {
41031
41075
  }
41032
41076
  function cleanupTempDir(dir) {
41033
41077
  try {
41034
- if (existsSync3(dir)) rmSync(dir, { recursive: true, force: true });
41078
+ if (existsSync4(dir)) rmSync(dir, { recursive: true, force: true });
41035
41079
  } catch {
41036
41080
  }
41037
41081
  }
41038
41082
 
41039
41083
  // src/utils/paperAttachments.ts
41040
- import { execFileSync as execFileSync7 } from "node:child_process";
41084
+ import { execFileSync as execFileSync8 } from "node:child_process";
41041
41085
  import {
41042
41086
  closeSync as closeSync3,
41043
41087
  constants as constants3,
@@ -41050,10 +41094,10 @@ import {
41050
41094
  unlinkSync as unlinkSync2,
41051
41095
  writeSync as writeSync2
41052
41096
  } from "node:fs";
41053
- import { basename as basename2, dirname as dirname3, extname as extname2, join as join7 } from "node:path";
41097
+ import { basename as basename2, dirname as dirname3, extname as extname2, join as join8 } from "node:path";
41054
41098
 
41055
41099
  // src/utils/attachmentAssets.ts
41056
- import { execFileSync as execFileSync6 } from "node:child_process";
41100
+ import { execFileSync as execFileSync7 } from "node:child_process";
41057
41101
  import {
41058
41102
  closeSync as closeSync2,
41059
41103
  constants as constants2,
@@ -41067,11 +41111,11 @@ import {
41067
41111
  unlinkSync,
41068
41112
  writeSync
41069
41113
  } from "node:fs";
41070
- import { homedir as homedir6 } from "node:os";
41071
- import { basename, dirname as dirname2, extname, isAbsolute as isAbsolute2, join as join6, relative as relative2, resolve as resolve2, sep as sep2 } from "node:path";
41114
+ import { homedir as homedir7 } from "node:os";
41115
+ import { basename, dirname as dirname2, extname, isAbsolute as isAbsolute2, join as join7, relative as relative2, resolve as resolve2, sep as sep2 } from "node:path";
41072
41116
  import { gunzipSync as gunzipSync5 } from "node:zlib";
41073
- var NOTES_CONTAINER_DIR = join6(
41074
- homedir6(),
41117
+ var NOTES_CONTAINER_DIR = join7(
41118
+ homedir7(),
41075
41119
  "Library/Group Containers/group.com.apple.notes"
41076
41120
  );
41077
41121
  var AttachmentStoreError = class extends Error {
@@ -41176,7 +41220,7 @@ function buildAttachmentRowsSql(notePk, columns) {
41176
41220
  }
41177
41221
  function runSqlite3(dbPath2, sql) {
41178
41222
  try {
41179
- return execFileSync6("/usr/bin/sqlite3", ["-readonly", dbPath2, sql], {
41223
+ return execFileSync7("/usr/bin/sqlite3", ["-readonly", dbPath2, sql], {
41180
41224
  encoding: "utf8",
41181
41225
  timeout: 1e4,
41182
41226
  maxBuffer: 128 * 1024 * 1024,
@@ -41244,7 +41288,7 @@ function attachmentOrderFromNoteData(hex3) {
41244
41288
  return null;
41245
41289
  }
41246
41290
  }
41247
- function readNoteAttachmentRows(noteId3, dbPath2 = join6(NOTES_CONTAINER_DIR, "NoteStore.sqlite")) {
41291
+ function readNoteAttachmentRows(noteId3, dbPath2 = join7(NOTES_CONTAINER_DIR, "NoteStore.sqlite")) {
41248
41292
  const { pk } = parseNoteId2(noteId3);
41249
41293
  const columnList = runSqlite3(
41250
41294
  dbPath2,
@@ -41274,24 +41318,24 @@ function safeComponent(value) {
41274
41318
  if (value === "." || value === ".." || value.includes("/") || value.includes("\0")) return null;
41275
41319
  return value;
41276
41320
  }
41277
- function realInside(path8, rootReal) {
41321
+ function realInside(path9, rootReal) {
41278
41322
  try {
41279
- const real = realpathSync2.native(path8);
41323
+ const real = realpathSync2.native(path9);
41280
41324
  return real === rootReal || real.startsWith(rootReal + sep2) ? real : null;
41281
41325
  } catch {
41282
41326
  return null;
41283
41327
  }
41284
41328
  }
41285
- function isRegularFile(path8) {
41329
+ function isRegularFile(path9) {
41286
41330
  try {
41287
- return lstatSync2(path8).isFile();
41331
+ return lstatSync2(path9).isFile();
41288
41332
  } catch {
41289
41333
  return false;
41290
41334
  }
41291
41335
  }
41292
- function isDirectory(path8) {
41336
+ function isDirectory(path9) {
41293
41337
  try {
41294
- return lstatSync2(path8).isDirectory();
41338
+ return lstatSync2(path9).isDirectory();
41295
41339
  } catch {
41296
41340
  return false;
41297
41341
  }
@@ -41305,7 +41349,7 @@ function boundedEntries(dir, limit) {
41305
41349
  }
41306
41350
  }
41307
41351
  function resolveAccountDir(containerDir, accountIdentifier) {
41308
- const accountsDir = join6(containerDir, "Accounts");
41352
+ const accountsDir = join7(containerDir, "Accounts");
41309
41353
  let accountsReal;
41310
41354
  try {
41311
41355
  accountsReal = realpathSync2.native(accountsDir);
@@ -41314,14 +41358,14 @@ function resolveAccountDir(containerDir, accountIdentifier) {
41314
41358
  }
41315
41359
  const account = safeComponent(accountIdentifier);
41316
41360
  if (account) {
41317
- const dir = realInside(join6(accountsReal, account), accountsReal);
41361
+ const dir = realInside(join7(accountsReal, account), accountsReal);
41318
41362
  if (dir && isDirectory(dir)) return dir;
41319
41363
  }
41320
41364
  const entries2 = (boundedEntries(accountsReal, 64) ?? []).filter(
41321
- (e) => isDirectory(join6(accountsReal, e))
41365
+ (e) => isDirectory(join7(accountsReal, e))
41322
41366
  );
41323
41367
  if (entries2.length !== 1) return null;
41324
- return realInside(join6(accountsReal, entries2[0]), accountsReal);
41368
+ return realInside(join7(accountsReal, entries2[0]), accountsReal);
41325
41369
  }
41326
41370
  function generationRank(name) {
41327
41371
  const m = /^(\d+)_/.exec(name);
@@ -41329,23 +41373,23 @@ function generationRank(name) {
41329
41373
  }
41330
41374
  function generationDirs(base, accountDir) {
41331
41375
  const entries2 = boundedEntries(base, MAX_GENERATION_DIRS) ?? [];
41332
- return entries2.map((e) => realInside(join6(base, e), accountDir)).filter((p) => p !== null && isDirectory(p)).sort((a, b) => generationRank(basename(b)) - generationRank(basename(a)));
41376
+ return entries2.map((e) => realInside(join7(base, e), accountDir)).filter((p) => p !== null && isDirectory(p)).sort((a, b) => generationRank(basename(b)) - generationRank(basename(a)));
41333
41377
  }
41334
41378
  function fallbackFiles(accountDir, rootName, identifier, generation, names) {
41335
- const base = join6(accountDir, rootName, identifier);
41379
+ const base = join7(accountDir, rootName, identifier);
41336
41380
  const found = [];
41337
41381
  const add = (candidate) => {
41338
41382
  const real = realInside(candidate, accountDir);
41339
41383
  if (real && isRegularFile(real) && !found.includes(real)) found.push(real);
41340
41384
  };
41341
41385
  const gen = safeComponent(generation);
41342
- if (gen) for (const name of names) add(join6(base, gen, name));
41386
+ if (gen) for (const name of names) add(join7(base, gen, name));
41343
41387
  if (isDirectory(base)) {
41344
41388
  for (const dir of generationDirs(base, accountDir))
41345
- for (const name of names) add(join6(dir, name));
41346
- for (const name of names) add(join6(base, name));
41389
+ for (const name of names) add(join7(dir, name));
41390
+ for (const name of names) add(join7(base, name));
41347
41391
  }
41348
- for (const name of names) add(join6(accountDir, rootName, `${identifier}${extname(name)}`));
41392
+ for (const name of names) add(join7(accountDir, rootName, `${identifier}${extname(name)}`));
41349
41393
  return found;
41350
41394
  }
41351
41395
  function previewPixelArea(name) {
@@ -41357,7 +41401,7 @@ function previewFileInBundle(bundle, accountDir) {
41357
41401
  const direct = [];
41358
41402
  const nested = [];
41359
41403
  for (const entry of boundedEntries(bundle, MAX_BUNDLE_ENTRIES) ?? []) {
41360
- const child = realInside(join6(bundle, entry), accountDir);
41404
+ const child = realInside(join7(bundle, entry), accountDir);
41361
41405
  if (!child) continue;
41362
41406
  if (isRegularFile(child)) direct.push(child);
41363
41407
  else if (isDirectory(child)) nested.push(child);
@@ -41365,14 +41409,14 @@ function previewFileInBundle(bundle, accountDir) {
41365
41409
  nested.sort((a, b) => generationRank(basename(b)) - generationRank(basename(a)));
41366
41410
  const pick2 = (files) => files.find((f) => basename(f) === "Preview.png") ?? files.find((f) => PREVIEW_IMAGE_SUFFIXES.has(extname(f).toLowerCase())) ?? null;
41367
41411
  for (const dir of nested) {
41368
- const files = (boundedEntries(dir, MAX_BUNDLE_ENTRIES) ?? []).map((e) => realInside(join6(dir, e), accountDir)).filter((p) => p !== null && isRegularFile(p));
41412
+ const files = (boundedEntries(dir, MAX_BUNDLE_ENTRIES) ?? []).map((e) => realInside(join7(dir, e), accountDir)).filter((p) => p !== null && isRegularFile(p));
41369
41413
  const chosen = pick2(files);
41370
41414
  if (chosen) return chosen;
41371
41415
  }
41372
41416
  return pick2(direct);
41373
41417
  }
41374
41418
  function listPreviewEntries(accountDir) {
41375
- return boundedEntries(join6(accountDir, "Previews"), MAX_PREVIEW_DIR_ENTRIES) ?? [];
41419
+ return boundedEntries(join7(accountDir, "Previews"), MAX_PREVIEW_DIR_ENTRIES) ?? [];
41376
41420
  }
41377
41421
  function previewPaths(accountDir, identifier, entries2) {
41378
41422
  const id2 = safeComponent(identifier);
@@ -41384,7 +41428,7 @@ function previewPaths(accountDir, identifier, entries2) {
41384
41428
  }).sort((a, b) => previewPixelArea(b) - previewPixelArea(a) || a.localeCompare(b));
41385
41429
  const files = [];
41386
41430
  for (const name of candidates) {
41387
- const real = realInside(join6(accountDir, "Previews", name), accountDir);
41431
+ const real = realInside(join7(accountDir, "Previews", name), accountDir);
41388
41432
  if (!real) continue;
41389
41433
  const file = isRegularFile(real) ? real : isDirectory(real) ? previewFileInBundle(real, accountDir) : null;
41390
41434
  if (file && !files.includes(file)) files.push(file);
@@ -41401,13 +41445,13 @@ function assetPathsFor(accountDir, row) {
41401
41445
  const mediaName = safeComponent(row.mediaFilename);
41402
41446
  const mediaGen = safeComponent(row.mediaGeneration);
41403
41447
  if (mediaId && mediaName) {
41404
- if (mediaGen) add(join6(accountDir, "Media", mediaId, mediaGen, mediaName));
41405
- add(join6(accountDir, "Media", mediaId, mediaName));
41448
+ if (mediaGen) add(join7(accountDir, "Media", mediaId, mediaGen, mediaName));
41449
+ add(join7(accountDir, "Media", mediaId, mediaName));
41406
41450
  }
41407
41451
  const id2 = safeComponent(row.identifier);
41408
41452
  if (!id2) return found;
41409
41453
  const ownName = safeComponent(row.filename);
41410
- if (ownName) add(join6(accountDir, "Media", id2, ownName));
41454
+ if (ownName) add(join7(accountDir, "Media", id2, ownName));
41411
41455
  for (const file of fallbackFiles(accountDir, "FallbackImages", id2, row.fallbackImageGeneration, [
41412
41456
  "FallbackImage.png",
41413
41457
  "FallbackImage.jpg"
@@ -41498,10 +41542,10 @@ function canonicalTail(p) {
41498
41542
  const tail = [];
41499
41543
  for (; ; ) {
41500
41544
  try {
41501
- return join6(realpathSync2.native(current), ...tail);
41545
+ return join7(realpathSync2.native(current), ...tail);
41502
41546
  } catch {
41503
41547
  const parent = dirname2(current);
41504
- if (parent === current) return join6(current, ...tail);
41548
+ if (parent === current) return join7(current, ...tail);
41505
41549
  tail.unshift(basename(current));
41506
41550
  current = parent;
41507
41551
  }
@@ -41583,7 +41627,7 @@ function exportOneAttachment(record2, dir, source) {
41583
41627
  if (!source) return base;
41584
41628
  const name = exportFileName(record2, source.path, source.kind);
41585
41629
  for (let attempt = 1; attempt <= MAX_COLLISION_SUFFIX; attempt++) {
41586
- const dest = join6(dir, collisionName(name, attempt));
41630
+ const dest = join7(dir, collisionName(name, attempt));
41587
41631
  try {
41588
41632
  assertSafeSavePath(dest);
41589
41633
  copyFileExclusive(source.path, dest);
@@ -41671,10 +41715,10 @@ function readImageInfoFd(fd) {
41671
41715
  const read = readSync2(fd, buf, 0, buf.length, 0);
41672
41716
  return parseImageHeader(buf.subarray(0, read));
41673
41717
  }
41674
- function readImageInfo(path8) {
41718
+ function readImageInfo(path9) {
41675
41719
  let fd;
41676
41720
  try {
41677
- fd = openSync3(path8, constants3.O_RDONLY | constants3.O_NOFOLLOW);
41721
+ fd = openSync3(path9, constants3.O_RDONLY | constants3.O_NOFOLLOW);
41678
41722
  } catch {
41679
41723
  return null;
41680
41724
  }
@@ -41710,7 +41754,7 @@ function buildDrawingRowsSql(notePk, columns) {
41710
41754
  }
41711
41755
  function runSqlite4(dbPath2, sql) {
41712
41756
  try {
41713
- return execFileSync7("/usr/bin/sqlite3", ["-readonly", dbPath2, sql], {
41757
+ return execFileSync8("/usr/bin/sqlite3", ["-readonly", dbPath2, sql], {
41714
41758
  encoding: "utf8",
41715
41759
  timeout: 1e4,
41716
41760
  maxBuffer: 16 * 1024 * 1024,
@@ -41750,7 +41794,7 @@ function parseDrawingRows(json2) {
41750
41794
  }
41751
41795
  return rows.sort((a, b) => a.pk - b.pk);
41752
41796
  }
41753
- function readDrawingRows(noteId3, dbPath2 = join7(NOTES_CONTAINER_DIR, "NoteStore.sqlite")) {
41797
+ function readDrawingRows(noteId3, dbPath2 = join8(NOTES_CONTAINER_DIR, "NoteStore.sqlite")) {
41754
41798
  const { pk } = parseNoteId2(noteId3);
41755
41799
  const columns = new Set(
41756
41800
  runSqlite4(
@@ -41774,9 +41818,9 @@ function readDrawingRows(noteId3, dbPath2 = join7(NOTES_CONTAINER_DIR, "NoteStor
41774
41818
  throw new AttachmentStoreError("Drawing rows could not be parsed", "query_error");
41775
41819
  }
41776
41820
  }
41777
- function kindOf(path8) {
41821
+ function kindOf(path9) {
41778
41822
  try {
41779
- const st = lstatSync3(path8);
41823
+ const st = lstatSync3(path9);
41780
41824
  return st.isFile() ? "file" : st.isDirectory() ? "dir" : null;
41781
41825
  } catch {
41782
41826
  return null;
@@ -41794,16 +41838,16 @@ var byGenerationDesc = (a, b) => generationRank(basename2(b)) - generationRank(b
41794
41838
  function findFallbackImage(accountDir, identifier, generation) {
41795
41839
  const id2 = safeComponent(identifier);
41796
41840
  if (!id2) return null;
41797
- const base = join7(accountDir, "FallbackImages", id2);
41841
+ const base = join8(accountDir, "FallbackImages", id2);
41798
41842
  const names = ["FallbackImage.png", "FallbackImage.jpg"];
41799
41843
  const candidates = [];
41800
41844
  const gen = safeComponent(generation);
41801
- if (gen) for (const n of names) candidates.push(join7(base, gen, n));
41802
- const gens = entries(base, MAX_DIR_ENTRIES).map((e) => join7(base, e)).sort(byGenerationDesc);
41803
- for (const g of gens) for (const n of names) candidates.push(join7(g, n));
41804
- for (const n of names) candidates.push(join7(base, n));
41805
- candidates.push(join7(accountDir, "FallbackImages", `${id2}.png`));
41806
- candidates.push(join7(accountDir, "FallbackImages", `${id2}.jpg`));
41845
+ if (gen) for (const n of names) candidates.push(join8(base, gen, n));
41846
+ const gens = entries(base, MAX_DIR_ENTRIES).map((e) => join8(base, e)).sort(byGenerationDesc);
41847
+ for (const g of gens) for (const n of names) candidates.push(join8(g, n));
41848
+ for (const n of names) candidates.push(join8(base, n));
41849
+ candidates.push(join8(accountDir, "FallbackImages", `${id2}.png`));
41850
+ candidates.push(join8(accountDir, "FallbackImages", `${id2}.jpg`));
41807
41851
  for (const c of candidates) {
41808
41852
  const real = realInside(c, accountDir);
41809
41853
  if (real && kindOf(real) === "file") return real;
@@ -41813,21 +41857,21 @@ function findFallbackImage(accountDir, identifier, generation) {
41813
41857
  function findLargestPreview(accountDir, identifier) {
41814
41858
  const id2 = safeComponent(identifier);
41815
41859
  if (!id2) return null;
41816
- const dir = join7(accountDir, "Previews");
41860
+ const dir = join8(accountDir, "Previews");
41817
41861
  const prefix = `${id2}-`.toLowerCase();
41818
41862
  const names = entries(dir, MAX_PREVIEW_DIR_ENTRIES2).filter((n) => n.toLowerCase().startsWith(prefix)).filter((n) => {
41819
41863
  const ext = extname2(n).toLowerCase();
41820
41864
  return !ext || IMAGE_SUFFIXES.has(ext) || /^\.\d+$/.test(ext);
41821
41865
  }).sort((a, b) => previewPixelArea(b) - previewPixelArea(a) || a.localeCompare(b));
41822
41866
  for (const name of names) {
41823
- const real = realInside(join7(dir, name), accountDir);
41867
+ const real = realInside(join8(dir, name), accountDir);
41824
41868
  if (!real) continue;
41825
41869
  const kind = kindOf(real);
41826
41870
  if (kind === "file") return real;
41827
41871
  if (kind !== "dir") continue;
41828
- const gens = entries(real, MAX_DIR_ENTRIES).map((e) => realInside(join7(real, e), accountDir)).filter((p) => p !== null && kindOf(p) === "dir").sort(byGenerationDesc);
41872
+ const gens = entries(real, MAX_DIR_ENTRIES).map((e) => realInside(join8(real, e), accountDir)).filter((p) => p !== null && kindOf(p) === "dir").sort(byGenerationDesc);
41829
41873
  for (const g of [...gens, real]) {
41830
- const file = realInside(join7(g, "Preview.png"), accountDir);
41874
+ const file = realInside(join8(g, "Preview.png"), accountDir);
41831
41875
  if (file && kindOf(file) === "file") return file;
41832
41876
  }
41833
41877
  }
@@ -41840,15 +41884,15 @@ function describeDrawings(rows, containerDir = NOTES_CONTAINER_DIR) {
41840
41884
  const id2 = safeComponent(row.identifier);
41841
41885
  const fallbackImagePath = accountDir ? findFallbackImage(accountDir, row.identifier, row.fallbackImageGeneration) : null;
41842
41886
  const previewPath = accountDir ? findLargestPreview(accountDir, row.identifier) : null;
41843
- const bundle = accountDir && id2 && kind === "paper" ? realInside(join7(accountDir, "Paper", "Bundles", `${id2}.bundle`), accountDir) : null;
41887
+ const bundle = accountDir && id2 && kind === "paper" ? realInside(join8(accountDir, "Paper", "Bundles", `${id2}.bundle`), accountDir) : null;
41844
41888
  let raster = null;
41845
- for (const [path8, source] of [
41889
+ for (const [path9, source] of [
41846
41890
  [fallbackImagePath, "fallback"],
41847
41891
  [previewPath, "preview"]
41848
41892
  ]) {
41849
- const info = path8 ? readImageInfo(path8) : null;
41850
- if (path8 && info) {
41851
- raster = { path: path8, source, ...info };
41893
+ const info = path9 ? readImageInfo(path9) : null;
41894
+ if (path9 && info) {
41895
+ raster = { path: path9, source, ...info };
41852
41896
  break;
41853
41897
  }
41854
41898
  }
@@ -41865,10 +41909,10 @@ function describeDrawings(rows, containerDir = NOTES_CONTAINER_DIR) {
41865
41909
  };
41866
41910
  });
41867
41911
  }
41868
- function verifyWrittenImage(fd, path8, expected) {
41912
+ function verifyWrittenImage(fd, path9, expected) {
41869
41913
  const check2 = readImageInfoFd(fd);
41870
41914
  if (!check2 || check2.format !== expected.format || check2.width !== expected.width || check2.height !== expected.height) {
41871
- unlinkSync2(path8);
41915
+ unlinkSync2(path9);
41872
41916
  throw new Error("The exported image failed validation and was removed.");
41873
41917
  }
41874
41918
  return check2;
@@ -41966,13 +42010,34 @@ function exportDrawingRaster(drawing, savePath, containerDir = NOTES_CONTAINER_D
41966
42010
 
41967
42011
  // src/services/appleNotesManager.ts
41968
42012
  var import_turndown = __toESM(require_turndown_cjs(), 1);
41969
- import { existsSync as existsSync4 } from "fs";
41970
- import { homedir as homedir7 } from "os";
41971
- import { join as join8 } from "path";
42013
+ import { existsSync as existsSync5 } from "fs";
42014
+ import { homedir as homedir8 } from "os";
42015
+ import { join as join9 } from "path";
41972
42016
  var FIELD_SEP = "";
41973
42017
  var RECORD_SEP = "";
41974
42018
  var AS_FIELD_SEP = "(character id 31)";
41975
42019
  var AS_RECORD_SEP = "(character id 30)";
42020
+ var GROUP_SEP = "";
42021
+ var AS_GROUP_SEP = "(character id 29)";
42022
+ var FOLDER_ID_PATTERN = /^x-coredata:\/\/[0-9A-F-]+\/ICFolder\/p\d+$/i;
42023
+ function trashFolderIdList() {
42024
+ const ids = readTrashFolderIds().filter((id2) => FOLDER_ID_PATTERN.test(id2));
42025
+ return `{${ids.map((id2) => `"${id2}"`).join(", ")}}`;
42026
+ }
42027
+ function buildTrashNoteIdsCollector() {
42028
+ return `
42029
+ set __trashNoteIds to {}
42030
+ repeat with __trashFolderId in ${trashFolderIdList()}
42031
+ try
42032
+ set __trashNoteIds to __trashNoteIds & (id of notes of folder id (contents of __trashFolderId))
42033
+ end try
42034
+ end repeat
42035
+ try
42036
+ repeat with __trashFolder in (every folder whose name is "${RECENTLY_DELETED_FOLDER_NAME}")
42037
+ set __trashNoteIds to __trashNoteIds & (id of notes of __trashFolder)
42038
+ end repeat
42039
+ end try`;
42040
+ }
41976
42041
  var DEFAULT_EXPORT_PAGE_SIZE = 50;
41977
42042
  var DEFAULT_EXPORT_MAX_RESPONSE_BYTES = 8 * 1024 * 1024;
41978
42043
  function exportMaxResponseBytes(env = process.env) {
@@ -42196,8 +42261,8 @@ function getNoteLinkFromDB(coreDataId2) {
42196
42261
  const match = coreDataId2.match(/\/p(\d+)$/);
42197
42262
  if (!match) return null;
42198
42263
  const pk = parseInt(match[1], 10);
42199
- const dbPath2 = join8(homedir7(), "Library/Group Containers/group.com.apple.notes/NoteStore.sqlite");
42200
- if (!existsSync4(dbPath2)) return null;
42264
+ const dbPath2 = join9(homedir8(), "Library/Group Containers/group.com.apple.notes/NoteStore.sqlite");
42265
+ if (!existsSync5(dbPath2)) return null;
42201
42266
  try {
42202
42267
  const { DatabaseSync } = __require("node:sqlite");
42203
42268
  const db = new DatabaseSync(dbPath2, { readOnly: true });
@@ -42402,9 +42467,9 @@ var AppleNotesManager = class {
42402
42467
  * const topResults = manager.searchNotes("project", false, undefined, undefined, undefined, 10);
42403
42468
  * ```
42404
42469
  */
42405
- searchNotes(query, searchContent = false, account, folder, modifiedSince, limit) {
42470
+ searchNotes(query2, searchContent = false, account, folder, modifiedSince, limit) {
42406
42471
  const targetAccount = this.resolveAccount(account);
42407
- const safeQuery = escapePlainStringForAppleScript(query);
42472
+ const safeQuery = escapePlainStringForAppleScript(query2);
42408
42473
  const safeLimit = limit !== void 0 && limit > 0 ? Math.floor(limit) : void 0;
42409
42474
  const whereParts = [];
42410
42475
  if (searchContent) {
@@ -42462,7 +42527,7 @@ var AppleNotesManager = class {
42462
42527
  const script = buildAccountScopedScript({ account: targetAccount }, searchCommand);
42463
42528
  const result = executeAppleScript(script);
42464
42529
  if (!result.success) {
42465
- throw new Error(`Failed to search notes for "${query}": ${result.error ?? "unknown error"}`);
42530
+ throw new Error(`Failed to search notes for "${query2}": ${result.error ?? "unknown error"}`);
42466
42531
  }
42467
42532
  if (!result.output.trim()) {
42468
42533
  return [];
@@ -42762,6 +42827,16 @@ var AppleNotesManager = class {
42762
42827
  try
42763
42828
  set originalFolder to container of noteRef
42764
42829
  end try
42830
+ if originalFolder is not missing value then
42831
+ set __inTrash to false
42832
+ try
42833
+ if ${trashFolderIdList()} contains (id of originalFolder) then set __inTrash to true
42834
+ end try
42835
+ try
42836
+ if (name of originalFolder) is "${RECENTLY_DELETED_FOLDER_NAME}" then set __inTrash to true
42837
+ end try
42838
+ if __inTrash then return "SAFETY_IN_RECENTLY_DELETED"
42839
+ end if
42765
42840
  set currentBody to body of noteRef
42766
42841
  considering case
42767
42842
  if currentBody is not "${safeExpectedBody}" and currentBody is not "${safeExpectedBody}" & linefeed then return "SAFETY_CONFLICT"
@@ -42781,6 +42856,7 @@ var AppleNotesManager = class {
42781
42856
  }
42782
42857
  const status = result.output.trim();
42783
42858
  if (status === "SAFETY_CONFLICT") return { status: "conflict" };
42859
+ if (status === "SAFETY_IN_RECENTLY_DELETED") return { status: "in-recently-deleted" };
42784
42860
  if (status === "SAFETY_NOT_DELETED") return { status: "not-deleted" };
42785
42861
  return status === "SAFETY_DELETED" ? { status: "deleted" } : { status: "failed" };
42786
42862
  }
@@ -42806,14 +42882,20 @@ var AppleNotesManager = class {
42806
42882
  * dateSetup: a date filter must scan every note's date). The script then
42807
42883
  * returns the total note count as a leading record so the caller can
42808
42884
  * detect a dedup shortfall and fall back to a full fetch.
42885
+ * @param trashCheck - Also return the ids of notes in Recently Deleted,
42886
+ * after a group separator, so the caller can exclude or flag them (#207).
42887
+ * Only valid inside an account-scoped script.
42809
42888
  */
42810
42889
  buildBulkListCommand(opts) {
42811
- const { folderRef, dateSetup, sliceLimit } = opts;
42890
+ const { folderRef, dateSetup, sliceLimit, trashCheck } = opts;
42812
42891
  const fullSource = folderRef ? `notes of ${folderRef}` : "notes";
42892
+ const trashSetup = trashCheck ? `${buildTrashNoteIdsCollector()}
42893
+ ` : "";
42894
+ const trashSuffix = trashCheck ? ` & ${AS_GROUP_SEP} & (__trashNoteIds as text)` : "";
42813
42895
  const countGuard = (listVar) => `if (count of ${listVar}) is not (count of noteNames) then error "${BULK_LIST_MUTATION_ERROR}"`;
42814
42896
  if (sliceLimit !== void 0) {
42815
42897
  const slicedSource = folderRef ? `(notes 1 thru fetchCount of ${folderRef})` : `(notes 1 thru fetchCount)`;
42816
- return `
42898
+ return `${trashSetup}
42817
42899
  set totalCount to count of ${fullSource}
42818
42900
  set fetchCount to ${sliceLimit}
42819
42901
  if fetchCount > totalCount then set fetchCount to totalCount
@@ -42840,7 +42922,7 @@ var AppleNotesManager = class {
42840
42922
  end repeat
42841
42923
  end if
42842
42924
  set AppleScript's text item delimiters to ${AS_RECORD_SEP}
42843
- return (totalCount as text) & ${AS_RECORD_SEP} & (resultList as text)
42925
+ return (totalCount as text) & ${AS_RECORD_SEP} & (resultList as text)${trashSuffix}
42844
42926
  `;
42845
42927
  }
42846
42928
  const dateFetch = dateSetup ? `set noteDates to modification date of ${fullSource}
@@ -42851,7 +42933,7 @@ var AppleNotesManager = class {
42851
42933
  ` : "";
42852
42934
  const dateGuardClose = dateSetup ? `
42853
42935
  end if` : "";
42854
- return `
42936
+ return `${trashSetup}
42855
42937
  ${dateSetup ?? ""}set noteNames to name of ${fullSource}
42856
42938
  set noteIds to id of ${fullSource}
42857
42939
  ${dateFetch}${countGuard("noteIds")}
@@ -42860,7 +42942,7 @@ var AppleNotesManager = class {
42860
42942
  ${dateGuardOpen}set end of resultList to (item i of noteNames) & ${AS_FIELD_SEP} & (item i of noteIds)${dateGuardClose}
42861
42943
  end repeat
42862
42944
  set AppleScript's text item delimiters to ${AS_RECORD_SEP}
42863
- return resultList as text
42945
+ return (resultList as text)${trashSuffix}
42864
42946
  `;
42865
42947
  }
42866
42948
  /**
@@ -42922,19 +43004,49 @@ var AppleNotesManager = class {
42922
43004
  * after dedup so duplicates never count against it.
42923
43005
  */
42924
43006
  parseBulkListOutput(output, safeLimit) {
42925
- if (!output.trim()) return [];
42926
- const seenIds = /* @__PURE__ */ new Set();
43007
+ return this.parseBulkListRecords(output, safeLimit).refs;
43008
+ }
43009
+ /**
43010
+ * Splits a trash-checked listing (see buildBulkListCommand's trashCheck)
43011
+ * into the listing records and the set of note ids in Recently Deleted.
43012
+ * Output without the group separator carries no trash information (null).
43013
+ */
43014
+ splitTrashIds(output) {
43015
+ const idx = output.lastIndexOf(GROUP_SEP);
43016
+ if (idx === -1) return { records: output, trashIds: null };
43017
+ const trashIds = new Set(
43018
+ output.slice(idx + 1).split(RECORD_SEP).map((id2) => id2.trim()).filter(Boolean)
43019
+ );
43020
+ return { records: output.slice(0, idx), trashIds };
43021
+ }
43022
+ /**
43023
+ * Parses bulk listing records, deduplicated by id. Notes whose id is in
43024
+ * `trash.ids` are dropped (and counted) unless `trash.include`, in which
43025
+ * case they are kept and flagged `inRecentlyDeleted`. The limit applies
43026
+ * after both dedup and trash filtering.
43027
+ */
43028
+ parseBulkListRecords(output, safeLimit, trash) {
42927
43029
  const refs = [];
43030
+ let excludedRecentlyDeleted = 0;
43031
+ if (!output.trim()) return { refs, excludedRecentlyDeleted };
43032
+ const seenIds = /* @__PURE__ */ new Set();
42928
43033
  for (const item of output.split(RECORD_SEP)) {
42929
43034
  const [title, id2] = item.split(FIELD_SEP);
42930
43035
  if (!title?.trim()) continue;
42931
43036
  const noteId3 = id2?.trim() || generateFallbackId();
42932
43037
  if (seenIds.has(noteId3)) continue;
42933
43038
  seenIds.add(noteId3);
42934
- refs.push({ title: title.trim(), id: noteId3 });
43039
+ const trashed = trash?.ids?.has(noteId3) ?? false;
43040
+ if (trashed && !trash?.include) {
43041
+ excludedRecentlyDeleted++;
43042
+ continue;
43043
+ }
43044
+ refs.push(
43045
+ trashed ? { title: title.trim(), id: noteId3, inRecentlyDeleted: true } : { title: title.trim(), id: noteId3 }
43046
+ );
42935
43047
  if (safeLimit !== void 0 && refs.length >= safeLimit) break;
42936
43048
  }
42937
- return refs;
43049
+ return { refs, excludedRecentlyDeleted };
42938
43050
  }
42939
43051
  /**
42940
43052
  * Core listing path shared by `listNotes()` and `listNoteRefs()`: lists
@@ -42942,7 +43054,7 @@ var AppleNotesManager = class {
42942
43054
  * returning (title, id) pairs. Kept private because both public callers
42943
43055
  * need to apply their own return shape.
42944
43056
  */
42945
- listNotesCore(account, folder, modifiedSince, limit) {
43057
+ listNotesCore(account, folder, modifiedSince, limit, includeRecentlyDeleted = false) {
42946
43058
  const targetAccount = this.resolveAccount(account);
42947
43059
  const safeLimit = limit !== void 0 && limit > 0 ? Math.floor(limit) : void 0;
42948
43060
  const folderRef = folder ? buildFolderReference(folder) : void 0;
@@ -42956,30 +43068,38 @@ var AppleNotesManager = class {
42956
43068
  if (safeLimit !== void 0 && !dateSetup) {
42957
43069
  const script2 = buildAccountScopedScript(
42958
43070
  { account: targetAccount },
42959
- this.buildBulkListCommand({ folderRef, sliceLimit: safeLimit })
43071
+ this.buildBulkListCommand({ folderRef, sliceLimit: safeLimit, trashCheck: true })
42960
43072
  );
42961
43073
  const result2 = executeAppleScript(script2);
42962
43074
  if (!result2.success) {
42963
43075
  throw new Error(`Failed to list notes: ${result2.error ?? "unknown error"}`);
42964
43076
  }
42965
- const sepIdx = result2.output.indexOf(RECORD_SEP);
42966
- const header = sepIdx === -1 ? result2.output : result2.output.slice(0, sepIdx);
43077
+ const { records: body, trashIds: trashIds2 } = this.splitTrashIds(result2.output);
43078
+ const sepIdx = body.indexOf(RECORD_SEP);
43079
+ const header = sepIdx === -1 ? body : body.slice(0, sepIdx);
42967
43080
  const totalCount = Number.parseInt(header.trim(), 10);
42968
- const records = sepIdx === -1 ? "" : result2.output.slice(sepIdx + 1);
42969
- const refs = this.parseBulkListOutput(records, safeLimit);
42970
- if (!Number.isNaN(totalCount) && (refs.length >= safeLimit || totalCount <= safeLimit)) {
42971
- return refs;
43081
+ const records2 = sepIdx === -1 ? "" : body.slice(sepIdx + 1);
43082
+ const parsed = this.parseBulkListRecords(records2, safeLimit, {
43083
+ ids: trashIds2,
43084
+ include: includeRecentlyDeleted
43085
+ });
43086
+ if (!Number.isNaN(totalCount) && (parsed.refs.length >= safeLimit || totalCount <= safeLimit)) {
43087
+ return parsed;
42972
43088
  }
42973
43089
  }
42974
43090
  const script = buildAccountScopedScript(
42975
43091
  { account: targetAccount },
42976
- this.buildBulkListCommand({ folderRef, dateSetup })
43092
+ this.buildBulkListCommand({ folderRef, dateSetup, trashCheck: true })
42977
43093
  );
42978
43094
  const result = executeAppleScript(script);
42979
43095
  if (!result.success) {
42980
43096
  throw new Error(`Failed to list notes: ${result.error ?? "unknown error"}`);
42981
43097
  }
42982
- return this.parseBulkListOutput(result.output, safeLimit);
43098
+ const { records, trashIds } = this.splitTrashIds(result.output);
43099
+ return this.parseBulkListRecords(records, safeLimit, {
43100
+ ids: trashIds,
43101
+ include: includeRecentlyDeleted
43102
+ });
42983
43103
  }
42984
43104
  /**
42985
43105
  * Lists all notes in an account, optionally filtered by folder, date, and limit.
@@ -42991,7 +43111,7 @@ var AppleNotesManager = class {
42991
43111
  * @returns Array of note titles
42992
43112
  */
42993
43113
  listNotes(account, folder, modifiedSince, limit) {
42994
- return this.listNotesCore(account, folder, modifiedSince, limit).map((ref) => ref.title);
43114
+ return this.listNotesCore(account, folder, modifiedSince, limit).refs.map((ref) => ref.title);
42995
43115
  }
42996
43116
  /**
42997
43117
  * Lists all notes in an account, optionally filtered by folder, date, and
@@ -43002,10 +43122,23 @@ var AppleNotesManager = class {
43002
43122
  * specifier resolves ambiguously to the same one note every time — see
43003
43123
  * the fix in exportNotesAsJson for the failure mode this avoids).
43004
43124
  *
43125
+ * Notes in Recently Deleted are excluded (#207); use
43126
+ * `listNoteRefsDetailed()` to include and flag them.
43127
+ *
43005
43128
  * @returns Array of { title, id } pairs, deduplicated by id
43006
43129
  */
43007
43130
  listNoteRefs(account, folder, modifiedSince, limit) {
43008
- return this.listNotesCore(account, folder, modifiedSince, limit);
43131
+ return this.listNotesCore(account, folder, modifiedSince, limit).refs;
43132
+ }
43133
+ /**
43134
+ * Same as `listNoteRefs()`, plus control over notes in Recently Deleted and
43135
+ * a count of how many were skipped. Notes.app's account-wide `notes`
43136
+ * includes Recently Deleted, so they are excluded unless
43137
+ * `includeRecentlyDeleted`, in which case each is flagged
43138
+ * `inRecentlyDeleted: true`. (#207)
43139
+ */
43140
+ listNoteRefsDetailed(account, folder, modifiedSince, limit, includeRecentlyDeleted = false) {
43141
+ return this.listNotesCore(account, folder, modifiedSince, limit, includeRecentlyDeleted);
43009
43142
  }
43010
43143
  /**
43011
43144
  * Lists all shared (collaborative) notes across all accounts.
@@ -44075,7 +44208,7 @@ var AppleNotesManager = class {
44075
44208
  if (parts[0] !== "OK") {
44076
44209
  return { success: false, error: parts[1]?.trim() || "attachment not found" };
44077
44210
  }
44078
- if (!existsSync4(abs) || fileSize(abs) === 0) {
44211
+ if (!existsSync5(abs) || fileSize(abs) === 0) {
44079
44212
  return { success: false, error: `Notes reported success but no file was written to ${abs}` };
44080
44213
  }
44081
44214
  return {
@@ -44580,15 +44713,15 @@ var AppleNotesManager = class {
44580
44713
  };
44581
44714
 
44582
44715
  // src/utils/syncDetection.ts
44583
- import { execFileSync as execFileSync8 } from "child_process";
44584
- import * as fs3 from "fs";
44585
- import * as path3 from "path";
44586
- import * as os3 from "os";
44587
- var NOTES_DB_PATH4 = path3.join(
44588
- os3.homedir(),
44716
+ import { execFileSync as execFileSync9 } from "child_process";
44717
+ import * as fs4 from "fs";
44718
+ import * as path4 from "path";
44719
+ import * as os4 from "os";
44720
+ var NOTES_DB_PATH5 = path4.join(
44721
+ os4.homedir(),
44589
44722
  "Library/Group Containers/group.com.apple.notes/NoteStore.sqlite"
44590
44723
  );
44591
- var WAL_PATH = `${NOTES_DB_PATH4}-wal`;
44724
+ var WAL_PATH = `${NOTES_DB_PATH5}-wal`;
44592
44725
  var RECENT_ACTIVITY_THRESHOLD_SECONDS = 5;
44593
44726
  var SYNC_STATUS_CACHE_TTL_MS = 2e3;
44594
44727
  var cachedSyncStatus = null;
@@ -44604,19 +44737,19 @@ function getSyncStatus(useCache = true) {
44604
44737
  recentActivity: false
44605
44738
  };
44606
44739
  try {
44607
- if (!fs3.existsSync(NOTES_DB_PATH4)) {
44740
+ if (!fs4.existsSync(NOTES_DB_PATH5)) {
44608
44741
  status.error = "Notes database not found";
44609
44742
  cachedSyncStatus = status;
44610
44743
  cacheTimestamp = Date.now();
44611
44744
  return status;
44612
44745
  }
44613
- if (fs3.existsSync(WAL_PATH)) {
44614
- const walStats = fs3.statSync(WAL_PATH);
44746
+ if (fs4.existsSync(WAL_PATH)) {
44747
+ const walStats = fs4.statSync(WAL_PATH);
44615
44748
  const secondsAgo = (Date.now() - walStats.mtimeMs) / 1e3;
44616
44749
  status.secondsSinceLastChange = Math.round(secondsAgo);
44617
44750
  status.recentActivity = secondsAgo < RECENT_ACTIVITY_THRESHOLD_SECONDS;
44618
44751
  }
44619
- const query = `
44752
+ const query2 = `
44620
44753
  SELECT COUNT(*) FROM ZICCLOUDSTATE state
44621
44754
  WHERE state.ZCURRENTLOCALVERSION > state.ZLATESTVERSIONSYNCEDTOCLOUD
44622
44755
  AND state.ZLATESTVERSIONSYNCEDTOCLOUD IS NOT NULL
@@ -44625,9 +44758,9 @@ function getSyncStatus(useCache = true) {
44625
44758
  WHERE object.ZCLOUDSTATE = state.Z_PK
44626
44759
  );
44627
44760
  `;
44628
- const result = execFileSync8(
44761
+ const result = execFileSync9(
44629
44762
  "sqlite3",
44630
- ["-readonly", NOTES_DB_PATH4, query.replace(/\n/g, " ")],
44763
+ ["-readonly", NOTES_DB_PATH5, query2.replace(/\n/g, " ")],
44631
44764
  {
44632
44765
  encoding: "utf8",
44633
44766
  timeout: 5e3,
@@ -44684,12 +44817,12 @@ function withSyncAwarenessSync(operation, fn) {
44684
44817
  }
44685
44818
 
44686
44819
  // src/utils/noteMetadata.ts
44687
- import { execFileSync as execFileSync9 } from "child_process";
44688
- import * as fs4 from "fs";
44689
- import * as path4 from "path";
44690
- import * as os4 from "os";
44691
- var NOTES_DB_PATH5 = path4.join(
44692
- os4.homedir(),
44820
+ import { execFileSync as execFileSync10 } from "child_process";
44821
+ import * as fs5 from "fs";
44822
+ import * as path5 from "path";
44823
+ import * as os5 from "os";
44824
+ var NOTES_DB_PATH6 = path5.join(
44825
+ os5.homedir(),
44693
44826
  "Library/Group Containers/group.com.apple.notes/NoteStore.sqlite"
44694
44827
  );
44695
44828
  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.`;
@@ -44704,8 +44837,8 @@ var COLUMN_MAP = [
44704
44837
  { key: "widgetSnippet", column: "ZWIDGETSNIPPET", type: "text" },
44705
44838
  { key: "smartFolderQuery", column: "ZSMARTFOLDERQUERYJSON", type: "text" }
44706
44839
  ];
44707
- function runSqlite5(query) {
44708
- return execFileSync9("sqlite3", ["-readonly", NOTES_DB_PATH5, query], {
44840
+ function runSqlite5(query2) {
44841
+ return execFileSync10("sqlite3", ["-readonly", NOTES_DB_PATH6, query2], {
44709
44842
  encoding: "utf8",
44710
44843
  timeout: 5e3,
44711
44844
  stdio: ["pipe", "pipe", "pipe"]
@@ -44730,7 +44863,7 @@ function getNoteMetadata(noteId3) {
44730
44863
  };
44731
44864
  }
44732
44865
  const pk = pkMatch[1];
44733
- if (!fs4.existsSync(NOTES_DB_PATH5)) {
44866
+ if (!fs5.existsSync(NOTES_DB_PATH6)) {
44734
44867
  return { metadata: null, error: "no_fda", message: FDA_MESSAGE2 };
44735
44868
  }
44736
44869
  try {
@@ -44776,12 +44909,12 @@ function getNoteMetadata(noteId3) {
44776
44909
  import { gunzipSync as gunzipSync6 } from "zlib";
44777
44910
 
44778
44911
  // src/utils/noteStoreSql.ts
44779
- import { execFileSync as execFileSync10 } from "child_process";
44780
- import * as fs5 from "fs";
44781
- import * as os5 from "os";
44782
- import * as path5 from "path";
44783
- var NOTES_DB_PATH6 = path5.join(
44784
- os5.homedir(),
44912
+ import { execFileSync as execFileSync11 } from "child_process";
44913
+ import * as fs6 from "fs";
44914
+ import * as os6 from "os";
44915
+ import * as path6 from "path";
44916
+ var NOTES_DB_PATH7 = path6.join(
44917
+ os6.homedir(),
44785
44918
  "Library/Group Containers/group.com.apple.notes/NoteStore.sqlite"
44786
44919
  );
44787
44920
  var CORE_DATA_EPOCH_MS = Date.UTC(2001, 0, 1);
@@ -44819,7 +44952,7 @@ function renderValue(value) {
44819
44952
  return `ieee754_from_blob(x'${doubleToHex(double)}')`;
44820
44953
  }
44821
44954
  function runReadOnlySql(dbPath2, sql, params = {}) {
44822
- if (!fs5.existsSync(dbPath2)) throw new NoteStoreError(STORE_FDA_MESSAGE, "no_fda");
44955
+ if (!fs6.existsSync(dbPath2)) throw new NoteStoreError(STORE_FDA_MESSAGE, "no_fda");
44823
44956
  const args = ["-readonly"];
44824
44957
  const names = Object.keys(params);
44825
44958
  if (names.length) args.push("-cmd", ".parameter init");
@@ -44831,7 +44964,7 @@ function runReadOnlySql(dbPath2, sql, params = {}) {
44831
44964
  }
44832
44965
  args.push(dbPath2, sql);
44833
44966
  try {
44834
- return execFileSync10("sqlite3", args, {
44967
+ return execFileSync11("sqlite3", args, {
44835
44968
  encoding: "utf8",
44836
44969
  timeout: 3e4,
44837
44970
  maxBuffer: 512 * 1024 * 1024,
@@ -45024,7 +45157,7 @@ function buildSpecialNotesSql(columns, kind, scoped) {
45024
45157
  ].join(" ");
45025
45158
  }
45026
45159
  function listSpecialNotes(options) {
45027
- const dbPath2 = options.dbPath ?? NOTES_DB_PATH6;
45160
+ const dbPath2 = options.dbPath ?? NOTES_DB_PATH7;
45028
45161
  const limit = Math.min(
45029
45162
  Math.max(1, Math.trunc(options.limit ?? SPECIAL_LIMIT.DEFAULT)),
45030
45163
  SPECIAL_LIMIT.MAX
@@ -45115,7 +45248,7 @@ function referencedObjects(hex3) {
45115
45248
  }
45116
45249
  }
45117
45250
  function nativeTagInventory(options = {}) {
45118
- const dbPath2 = options.dbPath ?? NOTES_DB_PATH6;
45251
+ const dbPath2 = options.dbPath ?? NOTES_DB_PATH7;
45119
45252
  const columns = readColumns(dbPath2);
45120
45253
  const context = readStoreContext(dbPath2, columns);
45121
45254
  const scope2 = options.account ? resolveAccountName(context.accounts, options.account) : void 0;
@@ -45177,12 +45310,12 @@ function assembleInventory(rows, accounts, scope2) {
45177
45310
  }
45178
45311
 
45179
45312
  // src/utils/noteIdentifiers.ts
45180
- import { execFileSync as execFileSync11 } from "child_process";
45181
- import * as fs6 from "fs";
45182
- import * as os6 from "os";
45183
- import * as path6 from "path";
45184
- var NOTES_DB_PATH7 = path6.join(
45185
- os6.homedir(),
45313
+ import { execFileSync as execFileSync12 } from "child_process";
45314
+ import * as fs7 from "fs";
45315
+ import * as os7 from "os";
45316
+ import * as path7 from "path";
45317
+ var NOTES_DB_PATH8 = path7.join(
45318
+ os7.homedir(),
45186
45319
  "Library/Group Containers/group.com.apple.notes/NoteStore.sqlite"
45187
45320
  );
45188
45321
  var UUID_PATTERN = /^[0-9A-Fa-f]{8}(?:-[0-9A-Fa-f]{4}){3}-[0-9A-Fa-f]{12}$/;
@@ -45256,10 +45389,10 @@ function buildLookupSql(entity3, keys) {
45256
45389
  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(entity3)} AND o.Z_PK IN (${inList})));`;
45257
45390
  }
45258
45391
  function runJsonQuery(sql, dbPath2) {
45259
- if (!fs6.existsSync(dbPath2)) throw new IdentifierResolutionError("no_fda", NO_FDA_MESSAGE);
45392
+ if (!fs7.existsSync(dbPath2)) throw new IdentifierResolutionError("no_fda", NO_FDA_MESSAGE);
45260
45393
  let out;
45261
45394
  try {
45262
- out = execFileSync11("sqlite3", ["-readonly", dbPath2, sql], {
45395
+ out = execFileSync12("sqlite3", ["-readonly", dbPath2, sql], {
45263
45396
  encoding: "utf8",
45264
45397
  timeout: 5e3,
45265
45398
  stdio: ["pipe", "pipe", "pipe"]
@@ -45279,7 +45412,7 @@ function runJsonQuery(sql, dbPath2) {
45279
45412
  function coreDataId(store, entity3, pk) {
45280
45413
  return `x-coredata://${store}/${entity3}/p${pk}`;
45281
45414
  }
45282
- function resolveIdentifiers(values, entity3, dbPath2 = NOTES_DB_PATH7) {
45415
+ function resolveIdentifiers(values, entity3, dbPath2 = NOTES_DB_PATH8) {
45283
45416
  const resolved = /* @__PURE__ */ new Map();
45284
45417
  const keys = [];
45285
45418
  const uuids = [];
@@ -45325,7 +45458,7 @@ function resolveIdentifiers(values, entity3, dbPath2 = NOTES_DB_PATH7) {
45325
45458
  return resolved;
45326
45459
  }
45327
45460
  var COREDATA_PARTS = /^x-coredata:\/\/([0-9A-Fa-f-]+)\/(ICNote|ICFolder|ICAccount)\/p(\d{1,18})$/;
45328
- function lookupStableIdentifiers(ids, entity3, dbPath2 = NOTES_DB_PATH7) {
45461
+ function lookupStableIdentifiers(ids, entity3, dbPath2 = NOTES_DB_PATH8) {
45329
45462
  const result = /* @__PURE__ */ new Map();
45330
45463
  const wanted = /* @__PURE__ */ new Map();
45331
45464
  for (const id2 of ids) {
@@ -45337,14 +45470,14 @@ function lookupStableIdentifiers(ids, entity3, dbPath2 = NOTES_DB_PATH7) {
45337
45470
  wanted.set(pk, list);
45338
45471
  }
45339
45472
  if (wanted.size === 0) return result;
45340
- let query;
45473
+ let query2;
45341
45474
  try {
45342
- query = runJsonQuery(buildLookupSql(entity3, [...wanted.keys()]), dbPath2);
45475
+ query2 = runJsonQuery(buildLookupSql(entity3, [...wanted.keys()]), dbPath2);
45343
45476
  } catch {
45344
45477
  return result;
45345
45478
  }
45346
- const store = query.store?.toUpperCase();
45347
- for (const row of query.rows) {
45479
+ const store = query2.store?.toUpperCase();
45480
+ for (const row of query2.rows) {
45348
45481
  const fields = {};
45349
45482
  for (const key of [
45350
45483
  "identifier",
@@ -45361,7 +45494,7 @@ function lookupStableIdentifiers(ids, entity3, dbPath2 = NOTES_DB_PATH7) {
45361
45494
  }
45362
45495
  return result;
45363
45496
  }
45364
- function withStableIdentifiers(items, entity3, dbPath2 = NOTES_DB_PATH7) {
45497
+ function withStableIdentifiers(items, entity3, dbPath2 = NOTES_DB_PATH8) {
45365
45498
  const ids = items.map((item) => item.id).filter((id2) => typeof id2 === "string");
45366
45499
  if (ids.length === 0) return items;
45367
45500
  const found = lookupStableIdentifiers(ids, entity3, dbPath2);
@@ -45949,13 +46082,13 @@ function positiveTextTerms(node, negated = false) {
45949
46082
  }
45950
46083
 
45951
46084
  // src/utils/noteQueryStore.ts
45952
- import { execFileSync as execFileSync12 } from "child_process";
45953
- import * as fs7 from "fs";
45954
- import * as os7 from "os";
45955
- import * as path7 from "path";
46085
+ import { execFileSync as execFileSync13 } from "child_process";
46086
+ import * as fs8 from "fs";
46087
+ import * as os8 from "os";
46088
+ import * as path8 from "path";
45956
46089
  import { gunzipSync as gunzipSync7 } from "zlib";
45957
- var NOTES_DB_PATH8 = path7.join(
45958
- os7.homedir(),
46090
+ var NOTES_DB_PATH9 = path8.join(
46091
+ os8.homedir(),
45959
46092
  "Library/Group Containers/group.com.apple.notes/NoteStore.sqlite"
45960
46093
  );
45961
46094
  var QUERY_SCAN = { DEFAULT: 500, MAX: 5e3 };
@@ -46089,8 +46222,8 @@ function buildScanSql(available, options) {
46089
46222
  "COMMIT;"
46090
46223
  ].join(" ");
46091
46224
  }
46092
- function runSqlite6(dbPath2, query) {
46093
- return execFileSync12("sqlite3", ["-readonly", dbPath2, query], {
46225
+ function runSqlite6(dbPath2, query2) {
46226
+ return execFileSync13("sqlite3", ["-readonly", dbPath2, query2], {
46094
46227
  encoding: "utf8",
46095
46228
  timeout: 3e4,
46096
46229
  maxBuffer: 512 * 1024 * 1024,
@@ -46117,12 +46250,12 @@ function resolveFolders(rows) {
46117
46250
  seen.add(pk);
46118
46251
  const parent = row.parent !== null ? resolve5(row.parent, seen) : void 0;
46119
46252
  const name = row.name ?? "";
46120
- const path8 = parent ? `${parent.path}/${escapeSegment(name)}` : escapeSegment(name);
46253
+ const path9 = parent ? `${parent.path}/${escapeSegment(name)}` : escapeSegment(name);
46121
46254
  const plainPath = parent ? `${parent.plainPath}/${name}` : name;
46122
46255
  const info = {
46123
- path: path8,
46256
+ path: path9,
46124
46257
  plainPath,
46125
- keys: [...new Set([name, plainPath, path8].map(normalizeForMatch))],
46258
+ keys: [...new Set([name, plainPath, path9].map(normalizeForMatch))],
46126
46259
  accountPk: row.owner ?? parent?.accountPk ?? null,
46127
46260
  shared: Boolean(row.shared) || Boolean(parent?.shared)
46128
46261
  };
@@ -46154,8 +46287,8 @@ function queryNotes(expression, options = {}) {
46154
46287
  const includeDeleted = options.includeDeleted ?? false;
46155
46288
  const withBodies = needsContent(ast);
46156
46289
  const withTags = needsTags(ast);
46157
- const dbPath2 = options.dbPath ?? NOTES_DB_PATH8;
46158
- if (!fs7.existsSync(dbPath2)) throw new NoteQueryStoreError(QUERY_FDA_MESSAGE, "no_fda");
46290
+ const dbPath2 = options.dbPath ?? NOTES_DB_PATH9;
46291
+ if (!fs8.existsSync(dbPath2)) throw new NoteQueryStoreError(QUERY_FDA_MESSAGE, "no_fda");
46159
46292
  let output;
46160
46293
  try {
46161
46294
  const available = presentColumns2(dbPath2);
@@ -46290,10 +46423,10 @@ function queryNotes(expression, options = {}) {
46290
46423
  import { spawnSync } from "child_process";
46291
46424
 
46292
46425
  // src/services/nativeTags.ts
46293
- import { execFileSync as execFileSync13 } from "node:child_process";
46426
+ import { execFileSync as execFileSync14 } from "node:child_process";
46294
46427
  import { mkdtempSync as mkdtempSync2, writeFileSync, rmSync as rmSync2 } from "node:fs";
46295
46428
  import { tmpdir as tmpdir2 } from "node:os";
46296
- import { join as join14 } from "node:path";
46429
+ import { join as join15 } from "node:path";
46297
46430
 
46298
46431
  // src/services/shortcutConsent.ts
46299
46432
  function shortcutConsentHint(shortcut) {
@@ -46369,7 +46502,7 @@ function addNativeTags(request, deps) {
46369
46502
  };
46370
46503
  }
46371
46504
  function listInstalledShortcuts() {
46372
- return execFileSync13("/usr/bin/shortcuts", ["list", "--show-identifiers"], {
46505
+ return execFileSync14("/usr/bin/shortcuts", ["list", "--show-identifiers"], {
46373
46506
  encoding: "utf8",
46374
46507
  timeout: 15e3,
46375
46508
  maxBuffer: 1024 * 1024,
@@ -46395,11 +46528,11 @@ function runNativeTagsShortcut(input) {
46395
46528
  const status = nativeTagsStatus();
46396
46529
  if (!status.installed)
46397
46530
  throw new Error(`Import the supplied ${status.shortcut}.shortcut in Shortcuts first`);
46398
- const directory = mkdtempSync2(join14(tmpdir2(), "apple-notes-native-tags-"));
46531
+ const directory = mkdtempSync2(join15(tmpdir2(), "apple-notes-native-tags-"));
46399
46532
  try {
46400
- const path8 = join14(directory, "request.json");
46401
- writeFileSync(path8, JSON.stringify(input), { mode: 384 });
46402
- execFileSync13("/usr/bin/shortcuts", ["run", status.identifier, "--input-path", path8], {
46533
+ const path9 = join15(directory, "request.json");
46534
+ writeFileSync(path9, JSON.stringify(input), { mode: 384 });
46535
+ execFileSync14("/usr/bin/shortcuts", ["run", status.identifier, "--input-path", path9], {
46403
46536
  encoding: "utf8",
46404
46537
  timeout: 6e4,
46405
46538
  maxBuffer: 1024 * 1024,
@@ -46418,10 +46551,10 @@ function runNativeTagsShortcut(input) {
46418
46551
  }
46419
46552
 
46420
46553
  // src/services/backgroundNotes.ts
46421
- import { execFileSync as execFileSync14 } from "node:child_process";
46554
+ import { execFileSync as execFileSync15 } from "node:child_process";
46422
46555
  import { mkdtempSync as mkdtempSync3, writeFileSync as writeFileSync2, rmSync as rmSync3 } from "node:fs";
46423
46556
  import { tmpdir as tmpdir3 } from "node:os";
46424
- import { join as join15 } from "node:path";
46557
+ import { join as join16 } from "node:path";
46425
46558
 
46426
46559
  // src/utils/appendMarkdown.ts
46427
46560
  var escape2 = (s) => s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
@@ -46746,9 +46879,9 @@ function runBackgroundShortcut(input, status = backgroundStatus()) {
46746
46879
  throw new Error(
46747
46880
  `Install the supplied "${status.shortcut}" Shortcut once; Shortcuts must list it exactly once`
46748
46881
  );
46749
- const directory = mkdtempSync3(join15(tmpdir3(), "apple-notes-background-"));
46882
+ const directory = mkdtempSync3(join16(tmpdir3(), "apple-notes-background-"));
46750
46883
  try {
46751
- const file = join15(directory, "request.json");
46884
+ const file = join16(directory, "request.json");
46752
46885
  writeFileSync2(
46753
46886
  file,
46754
46887
  JSON.stringify({
@@ -46768,7 +46901,7 @@ function runBackgroundShortcut(input, status = backgroundStatus()) {
46768
46901
  { mode: 384 }
46769
46902
  );
46770
46903
  try {
46771
- execFileSync14("/usr/bin/shortcuts", ["run", status.identifier, "--input-path", file], {
46904
+ execFileSync15("/usr/bin/shortcuts", ["run", status.identifier, "--input-path", file], {
46772
46905
  encoding: "utf8",
46773
46906
  timeout: callTimeoutMs() ?? 6e4,
46774
46907
  maxBuffer: 1024 * 1024,
@@ -47003,7 +47136,7 @@ function createMarkdownNote(manager, request, run = runBackgroundShortcut) {
47003
47136
  throw new Error(
47004
47137
  `Install the supplied "${status.shortcut}" Shortcut once; Shortcuts must list it exactly once`
47005
47138
  );
47006
- const segments = (path8) => JSON.stringify(splitFolderPath(path8).map((part) => part.toLocaleLowerCase()));
47139
+ const segments = (path9) => JSON.stringify(splitFolderPath(path9).map((part) => part.toLocaleLowerCase()));
47007
47140
  if (request.folder) {
47008
47141
  const wanted = segments(request.folder);
47009
47142
  if (!manager.listAccounts().some(
@@ -47093,7 +47226,7 @@ ${request.content}`
47093
47226
  }
47094
47227
 
47095
47228
  // src/services/capabilityMatrix.ts
47096
- import { execFileSync as execFileSync15 } from "node:child_process";
47229
+ import { execFileSync as execFileSync16 } from "node:child_process";
47097
47230
  import { release } from "node:os";
47098
47231
  var MACOS_SHORTCUTS_CLI = "12.0";
47099
47232
  var MACOS_MARKDOWN_IMPORT = "26.0";
@@ -47300,7 +47433,7 @@ function evaluateFeatures(env, features = FEATURES) {
47300
47433
  function readMacOSVersion() {
47301
47434
  if (process.platform !== "darwin") return null;
47302
47435
  try {
47303
- const out = execFileSync15("/usr/bin/sw_vers", ["-productVersion"], {
47436
+ const out = execFileSync16("/usr/bin/sw_vers", ["-productVersion"], {
47304
47437
  encoding: "utf8",
47305
47438
  timeout: 3e3,
47306
47439
  stdio: ["ignore", "pipe", "ignore"]
@@ -47333,9 +47466,9 @@ function getCapabilityMatrix(probe = probeCapabilityEnvironment) {
47333
47466
  return evaluateFeatures(probe());
47334
47467
  }
47335
47468
  function formatCapabilityMatrix(matrix) {
47336
- const os8 = matrix.runtimeOS;
47469
+ const os9 = matrix.runtimeOS;
47337
47470
  const lines = [
47338
- `Feature matrix (macOS ${os8.macOSVersion ?? "unknown"}, Darwin ${os8.darwinRelease}):`
47471
+ `Feature matrix (macOS ${os9.macOSVersion ?? "unknown"}, Darwin ${os9.darwinRelease}):`
47339
47472
  ];
47340
47473
  for (const [name, status] of Object.entries(matrix.features)) {
47341
47474
  lines.push(
@@ -47453,19 +47586,19 @@ function formatDoctorReport(r) {
47453
47586
  }
47454
47587
 
47455
47588
  // src/services/fileConfig.ts
47456
- import { existsSync as existsSync10, readFileSync as readFileSync2 } from "fs";
47457
- import { join as join16 } from "path";
47458
- import { homedir as homedir13 } from "os";
47589
+ import { existsSync as existsSync11, readFileSync as readFileSync2 } from "fs";
47590
+ import { join as join17 } from "path";
47591
+ import { homedir as homedir14 } from "os";
47459
47592
  function fileConfigPath(env = process.env) {
47460
47593
  const override = env.APPLE_NOTES_MCP_CONFIG_FILE;
47461
47594
  if (override && override.trim()) return override.trim();
47462
- return join16(homedir13(), "Library", "Application Support", "apple-notes-mcp", "config.json");
47595
+ return join17(homedir14(), "Library", "Application Support", "apple-notes-mcp", "config.json");
47463
47596
  }
47464
- function loadFileConfig(env = process.env, path8 = fileConfigPath(env)) {
47597
+ function loadFileConfig(env = process.env, path9 = fileConfigPath(env)) {
47465
47598
  const applied = [];
47466
47599
  try {
47467
- if (!existsSync10(path8)) return applied;
47468
- const parsed = JSON.parse(readFileSync2(path8, "utf8"));
47600
+ if (!existsSync11(path9)) return applied;
47601
+ const parsed = JSON.parse(readFileSync2(path9, "utf8"));
47469
47602
  if (!parsed || typeof parsed !== "object") return applied;
47470
47603
  for (const [k, v] of Object.entries(parsed)) {
47471
47604
  if (typeof v !== "string") continue;
@@ -47475,7 +47608,7 @@ function loadFileConfig(env = process.env, path8 = fileConfigPath(env)) {
47475
47608
  }
47476
47609
  }
47477
47610
  } catch (e) {
47478
- console.error(`Failed to load apple-notes-mcp config file ${path8}: ${String(e)}`);
47611
+ console.error(`Failed to load apple-notes-mcp config file ${path9}: ${String(e)}`);
47479
47612
  }
47480
47613
  return applied;
47481
47614
  }
@@ -47762,10 +47895,10 @@ function createShutdown(stream, exit, timeoutMs = SHUTDOWN_DRAIN_TIMEOUT_MS) {
47762
47895
  }
47763
47896
 
47764
47897
  // src/utils/noteBlocks.ts
47765
- import { execFileSync as execFileSync16 } from "node:child_process";
47766
- import { existsSync as existsSync11 } from "node:fs";
47767
- import { homedir as homedir14 } from "node:os";
47768
- import { join as join17 } from "node:path";
47898
+ import { execFileSync as execFileSync17 } from "node:child_process";
47899
+ import { existsSync as existsSync12 } from "node:fs";
47900
+ import { homedir as homedir15 } from "node:os";
47901
+ import { join as join18 } from "node:path";
47769
47902
  import { gunzipSync as gunzipSync8 } from "node:zlib";
47770
47903
  var NoteBlocksError = class extends Error {
47771
47904
  code;
@@ -48108,23 +48241,23 @@ function pageNoteBlocks(doc, { offset = 0, limit = 500, maxBytes = blocksMaxResp
48108
48241
  undecodedFields: doc.undecodedFields
48109
48242
  };
48110
48243
  }
48111
- var NOTES_DB_PATH9 = join17(
48112
- homedir14(),
48244
+ var NOTES_DB_PATH10 = join18(
48245
+ homedir15(),
48113
48246
  "Library/Group Containers/group.com.apple.notes/NoteStore.sqlite"
48114
48247
  );
48115
- function readNoteBlocks(id2, { dbPath: dbPath2 = NOTES_DB_PATH9 } = {}) {
48248
+ function readNoteBlocks(id2, { dbPath: dbPath2 = NOTES_DB_PATH10 } = {}) {
48116
48249
  const pk = /^x-coredata:\/\/[0-9a-f-]+\/ICNote\/p([0-9]{1,18})$/i.exec(id2)?.[1];
48117
48250
  if (!pk)
48118
48251
  throw new NoteBlocksError(
48119
48252
  "invalid-id",
48120
48253
  `Invalid note ID: expected x-coredata://<store>/ICNote/p<number>`
48121
48254
  );
48122
- if (!existsSync11(dbPath2))
48255
+ if (!existsSync12(dbPath2))
48123
48256
  throw new NoteBlocksError("no-full-disk-access", "The Notes database is not readable");
48124
48257
  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));";
48125
48258
  let output;
48126
48259
  try {
48127
- output = execFileSync16(
48260
+ output = execFileSync17(
48128
48261
  "/usr/bin/sqlite3",
48129
48262
  ["-readonly", "-cmd", ".parameter init", "-cmd", `.parameter set @pk ${pk}`, dbPath2, sql],
48130
48263
  {
@@ -48274,7 +48407,7 @@ function insertLink(request, deps) {
48274
48407
 
48275
48408
  // src/services/notesExport.ts
48276
48409
  import { mkdirSync as mkdirSync5 } from "node:fs";
48277
- import { dirname as dirname4 } from "node:path";
48410
+ import { basename as basename4, dirname as dirname4, extname as extname4, join as join21 } from "node:path";
48278
48411
 
48279
48412
  // src/utils/exportAssets.ts
48280
48413
  import {
@@ -48289,10 +48422,11 @@ import {
48289
48422
  statSync as statSync3,
48290
48423
  writeSync as writeSync3
48291
48424
  } from "node:fs";
48292
- import { homedir as homedir15 } from "node:os";
48293
- import { basename as basename3, extname as extname3, join as join18, relative as relative3, resolve as resolve3, sep as sep3 } from "node:path";
48294
- var NOTES_CONTAINER = join18(homedir15(), "Library/Group Containers/group.com.apple.notes");
48425
+ import { homedir as homedir16 } from "node:os";
48426
+ import { basename as basename3, extname as extname3, join as join19, relative as relative3, resolve as resolve3, sep as sep3 } from "node:path";
48427
+ var NOTES_CONTAINER = join19(homedir16(), "Library/Group Containers/group.com.apple.notes");
48295
48428
  var MAX_EMBED_BYTES = 10 * 1024 * 1024;
48429
+ var MAX_EMBED_TOTAL_BYTES = 256 * 1024 * 1024;
48296
48430
  var MAX_DIRECTORY_ENTRIES = 2e4;
48297
48431
  var MAX_BUNDLE_ENTRIES2 = 64;
48298
48432
  var IMAGE_EXTENSIONS = /* @__PURE__ */ new Set([".png", ".jpg", ".jpeg", ".heic", ".gif", ".tiff", ".webp"]);
@@ -48317,10 +48451,10 @@ function listDirectory(dir, limit) {
48317
48451
  return [];
48318
48452
  }
48319
48453
  }
48320
- function isFile(path8) {
48454
+ function isFile(path9) {
48321
48455
  let fd;
48322
48456
  try {
48323
- fd = openSync4(path8, constants4.O_RDONLY | constants4.O_NOFOLLOW);
48457
+ fd = openSync4(path9, constants4.O_RDONLY | constants4.O_NOFOLLOW);
48324
48458
  return fstatSync4(fd).isFile();
48325
48459
  } catch {
48326
48460
  return false;
@@ -48328,9 +48462,9 @@ function isFile(path8) {
48328
48462
  if (fd !== void 0) closeSync4(fd);
48329
48463
  }
48330
48464
  }
48331
- function isDirectory2(path8) {
48465
+ function isDirectory2(path9) {
48332
48466
  try {
48333
- return statSync3(path8).isDirectory();
48467
+ return statSync3(path9).isDirectory();
48334
48468
  } catch {
48335
48469
  return false;
48336
48470
  }
@@ -48346,11 +48480,11 @@ var AssetLocator = class {
48346
48480
  constructor(container = NOTES_CONTAINER) {
48347
48481
  let accountsRoot = "";
48348
48482
  try {
48349
- accountsRoot = realpathSync3.native(join18(container, "Accounts"));
48483
+ accountsRoot = realpathSync3.native(join19(container, "Accounts"));
48350
48484
  } catch {
48351
48485
  }
48352
48486
  this.accounts = accountsRoot ? listDirectory(accountsRoot, 1e3).flatMap((name) => {
48353
- const dir = safeComponent2(name) && confine(join18(accountsRoot, name), accountsRoot);
48487
+ const dir = safeComponent2(name) && confine(join19(accountsRoot, name), accountsRoot);
48354
48488
  return dir && isDirectory2(dir) ? [dir] : [];
48355
48489
  }) : [];
48356
48490
  }
@@ -48410,18 +48544,18 @@ var AssetLocator = class {
48410
48544
  if (!mediaId || !filename) return void 0;
48411
48545
  const generation = safeComponent2(source.mediaGeneration);
48412
48546
  const candidates = [
48413
- ...generation ? [join18(account, "Media", mediaId, generation, filename)] : [],
48414
- join18(account, "Media", mediaId, filename)
48547
+ ...generation ? [join19(account, "Media", mediaId, generation, filename)] : [],
48548
+ join19(account, "Media", mediaId, filename)
48415
48549
  ];
48416
48550
  for (const candidate of candidates) {
48417
- const path8 = confine(candidate, account);
48418
- if (path8 && isFile(path8)) return { path: path8, name: filename, role: "original" };
48551
+ const path9 = confine(candidate, account);
48552
+ if (path9 && isFile(path9)) return { path: path9, name: filename, role: "original" };
48419
48553
  }
48420
48554
  return void 0;
48421
48555
  }
48422
48556
  /** <dir>/<id>/<generation>/<name>, <dir>/<id>/<name>, then any generation. */
48423
48557
  fallback(account, dir, id2, generation, names) {
48424
- const base = join18(account, dir, id2);
48558
+ const base = join19(account, dir, id2);
48425
48559
  const gen = safeComponent2(generation);
48426
48560
  const generations = [
48427
48561
  ...gen ? [gen] : [],
@@ -48430,8 +48564,8 @@ var AssetLocator = class {
48430
48564
  ];
48431
48565
  for (const g of generations)
48432
48566
  for (const name of names) {
48433
- const path8 = confine(g ? join18(base, g, name) : join18(base, name), account);
48434
- if (path8 && isFile(path8)) return path8;
48567
+ const path9 = confine(g ? join19(base, g, name) : join19(base, name), account);
48568
+ if (path9 && isFile(path9)) return path9;
48435
48569
  }
48436
48570
  return void 0;
48437
48571
  }
@@ -48440,7 +48574,7 @@ var AssetLocator = class {
48440
48574
  let index = this.previewIndex.get(account);
48441
48575
  if (!index) {
48442
48576
  index = /* @__PURE__ */ new Map();
48443
- for (const name of listDirectory(join18(account, "Previews"), MAX_DIRECTORY_ENTRIES)) {
48577
+ for (const name of listDirectory(join19(account, "Previews"), MAX_DIRECTORY_ENTRIES)) {
48444
48578
  const key = name.slice(0, 36).toUpperCase();
48445
48579
  const entries3 = index.get(key) ?? [];
48446
48580
  entries3.push(name);
@@ -48453,7 +48587,7 @@ var AssetLocator = class {
48453
48587
  return !ext || IMAGE_EXTENSIONS.has(ext) || /^\.\d+$/.test(ext);
48454
48588
  }).sort((a, b) => previewArea(b) - previewArea(a) || a.localeCompare(b));
48455
48589
  for (const name of entries2) {
48456
- const entry = confine(join18(account, "Previews", name), account);
48590
+ const entry = confine(join19(account, "Previews", name), account);
48457
48591
  if (!entry) continue;
48458
48592
  const file = isFile(entry) ? entry : this.bundleImage(entry, account);
48459
48593
  if (file) return { path: file, name: "preview", role: "preview" };
@@ -48464,16 +48598,16 @@ var AssetLocator = class {
48464
48598
  bundleImage(bundle, account) {
48465
48599
  const found = [];
48466
48600
  for (const child of listDirectory(bundle, MAX_BUNDLE_ENTRIES2)) {
48467
- const path8 = confine(join18(bundle, child), account);
48468
- if (!path8) continue;
48469
- if (isFile(path8)) found.push(path8);
48601
+ const path9 = confine(join19(bundle, child), account);
48602
+ if (!path9) continue;
48603
+ if (isFile(path9)) found.push(path9);
48470
48604
  else
48471
- for (const grandchild of listDirectory(path8, MAX_BUNDLE_ENTRIES2)) {
48472
- const inner = confine(join18(path8, grandchild), account);
48605
+ for (const grandchild of listDirectory(path9, MAX_BUNDLE_ENTRIES2)) {
48606
+ const inner = confine(join19(path9, grandchild), account);
48473
48607
  if (inner && isFile(inner)) found.push(inner);
48474
48608
  }
48475
48609
  }
48476
- return found.find((path8) => basename3(path8) === "Preview.png") ?? found.find((path8) => IMAGE_EXTENSIONS.has(extname3(path8).toLowerCase()));
48610
+ return found.find((path9) => basename3(path9) === "Preview.png") ?? found.find((path9) => IMAGE_EXTENSIONS.has(extname3(path9).toLowerCase()));
48477
48611
  }
48478
48612
  };
48479
48613
  function sniffMime(head, name) {
@@ -48521,8 +48655,8 @@ function safeAssetName(name, mime) {
48521
48655
  if (clean.length > 120) clean = clean.slice(0, 120 - ext.length) + ext;
48522
48656
  return clean;
48523
48657
  }
48524
- function encodePathUrl(path8) {
48525
- return path8.split("/").map(encodeURIComponent).join("/");
48658
+ function encodePathUrl(path9) {
48659
+ return path9.split("/").map(encodeURIComponent).join("/");
48526
48660
  }
48527
48661
  function canonicalForm(abs) {
48528
48662
  let current = abs;
@@ -48530,18 +48664,18 @@ function canonicalForm(abs) {
48530
48664
  for (; ; ) {
48531
48665
  try {
48532
48666
  const real = realpathSync3.native(current);
48533
- return rest ? join18(real, rest) : real;
48667
+ return rest ? join19(real, rest) : real;
48534
48668
  } catch {
48535
48669
  const parent = resolve3(current, "..");
48536
48670
  if (parent === current) return abs;
48537
- rest = rest ? join18(basename3(current), rest) : basename3(current);
48671
+ rest = rest ? join19(basename3(current), rest) : basename3(current);
48538
48672
  current = parent;
48539
48673
  }
48540
48674
  }
48541
48675
  }
48542
48676
  function assertExportPath(p, container = NOTES_CONTAINER) {
48543
48677
  const abs = assertSafeSavePath(p);
48544
- const within = (path8, root) => path8 === root || path8.startsWith(root + sep3);
48678
+ const within = (path9, root) => path9 === root || path9.startsWith(root + sep3);
48545
48679
  const roots = [resolve3(container), canonicalForm(resolve3(container))];
48546
48680
  const forms = [abs, canonicalForm(abs)];
48547
48681
  if (forms.some((form) => roots.some((root) => within(form, root))))
@@ -48551,16 +48685,16 @@ function assertExportPath(p, container = NOTES_CONTAINER) {
48551
48685
  var CREATE_FLAGS = constants4.O_WRONLY | constants4.O_CREAT | constants4.O_EXCL | constants4.O_NOFOLLOW;
48552
48686
  var OutputExistsError = class extends Error {
48553
48687
  code = "output_exists";
48554
- constructor(path8) {
48555
- super(`Output file already exists: ${path8}. Choose a new path or remove the old file first.`);
48688
+ constructor(path9) {
48689
+ super(`Output file already exists: ${path9}. Choose a new path or remove the old file first.`);
48556
48690
  this.name = "OutputExistsError";
48557
48691
  }
48558
48692
  };
48559
- function openCreateOnly(path8) {
48693
+ function openCreateOnly(path9) {
48560
48694
  try {
48561
- return openSync4(path8, CREATE_FLAGS, 420);
48695
+ return openSync4(path9, CREATE_FLAGS, 420);
48562
48696
  } catch (error2) {
48563
- if (error2.code === "EEXIST") throw new OutputExistsError(path8);
48697
+ if (error2.code === "EEXIST") throw new OutputExistsError(path9);
48564
48698
  throw error2;
48565
48699
  }
48566
48700
  }
@@ -48574,8 +48708,8 @@ function writeAllAndClose(fd, content) {
48574
48708
  closeSync4(fd);
48575
48709
  }
48576
48710
  }
48577
- function openSource(path8) {
48578
- const fd = openSync4(path8, constants4.O_RDONLY | constants4.O_NOFOLLOW);
48711
+ function openSource(path9) {
48712
+ const fd = openSync4(path9, constants4.O_RDONLY | constants4.O_NOFOLLOW);
48579
48713
  const stat = fstatSync4(fd);
48580
48714
  if (!stat.isFile()) {
48581
48715
  closeSync4(fd);
@@ -48620,7 +48754,7 @@ var SidecarWriter = class {
48620
48754
  let target;
48621
48755
  let out;
48622
48756
  for (let n = 1; n <= 1e3 && out === void 0; n++) {
48623
- target = join18(this.dir, n === 1 ? name : `${stem}-${n}${ext}`);
48757
+ target = join19(this.dir, n === 1 ? name : `${stem}-${n}${ext}`);
48624
48758
  try {
48625
48759
  out = openSync4(target, CREATE_FLAGS, 420);
48626
48760
  } catch (error2) {
@@ -48652,6 +48786,49 @@ var SidecarWriter = class {
48652
48786
  }
48653
48787
  }
48654
48788
  };
48789
+ var DataUrlWriter = class {
48790
+ constructor(maxBytes = MAX_EMBED_BYTES, totalBytes = MAX_EMBED_TOTAL_BYTES) {
48791
+ this.maxBytes = maxBytes;
48792
+ this.totalBytes = totalBytes;
48793
+ }
48794
+ maxBytes;
48795
+ totalBytes;
48796
+ placed = /* @__PURE__ */ new Map();
48797
+ embeddedBytes = 0;
48798
+ count = 0;
48799
+ place(asset) {
48800
+ const done = this.placed.get(asset.path);
48801
+ if (done) return done;
48802
+ let source;
48803
+ try {
48804
+ source = openSource(asset.path);
48805
+ } catch {
48806
+ return { error: "unreadable" };
48807
+ }
48808
+ try {
48809
+ if (source.size > this.maxBytes || this.embeddedBytes + source.size > this.totalBytes)
48810
+ return { error: "too-large" };
48811
+ const data = Buffer.alloc(source.size);
48812
+ let read = 0;
48813
+ while (read < source.size) {
48814
+ const n = readSync3(source.fd, data, read, source.size - read, read);
48815
+ if (n <= 0) break;
48816
+ read += n;
48817
+ }
48818
+ const mime = sniffMime(data.subarray(0, 16), asset.name);
48819
+ const result = {
48820
+ url: `data:${mime};base64,${data.subarray(0, read).toString("base64")}`,
48821
+ mime
48822
+ };
48823
+ this.count++;
48824
+ this.embeddedBytes += read;
48825
+ this.placed.set(asset.path, result);
48826
+ return result;
48827
+ } finally {
48828
+ closeSync4(source.fd);
48829
+ }
48830
+ }
48831
+ };
48655
48832
 
48656
48833
  // src/utils/exportRender.ts
48657
48834
  var emptyStats = () => ({
@@ -48818,6 +48995,289 @@ function unreferencedAttachments(note) {
48818
48995
  );
48819
48996
  }
48820
48997
 
48998
+ // src/utils/htmlExport.ts
48999
+ function escapeHtml(text2) {
49000
+ return text2.replace(
49001
+ /[&<>"']/g,
49002
+ (char) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[char]
49003
+ );
49004
+ }
49005
+ var HIGHLIGHT_CLASSES = /* @__PURE__ */ new Set(["purple", "pink", "orange", "mint", "blue"]);
49006
+ var ALIGN = /* @__PURE__ */ new Set(["center", "right", "justify"]);
49007
+ var REASONS = {
49008
+ "too-large": " (too large to embed; export with embedAssets false)",
49009
+ unreadable: " (unreadable)",
49010
+ undecodable: " (could not be decoded)"
49011
+ };
49012
+ function wrapText(text2, fmt) {
49013
+ let out = escapeHtml(text2).replace(/\n/g, "<br>");
49014
+ if (fmt.subscript) out = `<sub>${out}</sub>`;
49015
+ if (fmt.superscript) out = `<sup>${out}</sup>`;
49016
+ if (fmt.highlight)
49017
+ out = `<mark class="hl-${HIGHLIGHT_CLASSES.has(fmt.highlight) ? fmt.highlight : "other"}">${out}</mark>`;
49018
+ if (fmt.strikethrough) out = `<s>${out}</s>`;
49019
+ if (fmt.underline) out = `<u>${out}</u>`;
49020
+ if (fmt.italic) out = `<em>${out}</em>`;
49021
+ if (fmt.bold) out = `<strong>${out}</strong>`;
49022
+ if (fmt.color && /^#[0-9A-F]{6}(?:[0-9A-F]{2})?$/i.test(fmt.color))
49023
+ out = `<span style="color:${fmt.color}">${out}</span>`;
49024
+ return out;
49025
+ }
49026
+ var bracket = (label, name) => escapeHtml(`[${name ? `${label}: ${name}` : label}]`);
49027
+ function domainOf(url) {
49028
+ try {
49029
+ return new URL(url).hostname;
49030
+ } catch {
49031
+ return url;
49032
+ }
49033
+ }
49034
+ function isHtmlBlockPlan(plan) {
49035
+ switch (plan.type) {
49036
+ case "table":
49037
+ case "divider":
49038
+ case "gallery":
49039
+ case "card":
49040
+ return true;
49041
+ case "asset":
49042
+ return plan.display !== "link" || !!plan.previewUrl;
49043
+ default:
49044
+ return false;
49045
+ }
49046
+ }
49047
+ function inlinePlanHtml(plan) {
49048
+ switch (plan.type) {
49049
+ case "inline":
49050
+ return plan.link ? `<a href="${escapeHtml(plan.link)}">${escapeHtml(plan.text)}</a>` : escapeHtml(plan.text);
49051
+ case "placeholder":
49052
+ return `<span class="attachment-placeholder">${bracket(plan.label, plan.name)}</span>`;
49053
+ case "unavailable":
49054
+ return `<span class="attachment-unavailable" role="note">${bracket(
49055
+ `${plan.label} unavailable${REASONS[plan.reason] ?? ""}`,
49056
+ plan.name
49057
+ )}</span>`;
49058
+ case "asset":
49059
+ return `<a class="attachment attachment-file" href="${escapeHtml(plan.url)}">${escapeHtml(
49060
+ plan.name ?? plan.label
49061
+ )}</a>`;
49062
+ default:
49063
+ return blockPlanHtml(plan);
49064
+ }
49065
+ }
49066
+ function blockPlanHtml(plan) {
49067
+ switch (plan.type) {
49068
+ case "table":
49069
+ return tableHtml(plan.rows);
49070
+ case "divider":
49071
+ return "<hr>";
49072
+ case "gallery":
49073
+ return `<div class="attachment-gallery">${plan.items.map((item) => isHtmlBlockPlan(item) ? blockPlanHtml(item) : inlinePlanHtml(item)).join("")}</div>`;
49074
+ case "card": {
49075
+ const image = plan.previewUrl ? `<img src="${escapeHtml(plan.previewUrl)}" alt="" loading="lazy">` : "";
49076
+ const text2 = `<span class="link-card-text"><span class="link-card-title">${escapeHtml(plan.title)}</span><span class="link-card-domain">${escapeHtml(domainOf(plan.displayUrl))}</span></span>`;
49077
+ return plan.url ? `<a class="link-card" href="${escapeHtml(plan.url)}">${image}${text2}</a>` : `<div class="link-card">${image}${text2}</div>`;
49078
+ }
49079
+ case "asset": {
49080
+ const name = escapeHtml(plan.name ?? plan.label);
49081
+ const src = escapeHtml(plan.url);
49082
+ const caption = plan.name ? `<figcaption>${name}</figcaption>` : "";
49083
+ if (plan.display === "image")
49084
+ return `<figure class="attachment attachment-image"><img src="${src}" alt="${name}" loading="lazy"></figure>`;
49085
+ if (plan.display === "audio" || plan.display === "video")
49086
+ return `<figure class="attachment attachment-${plan.display}"><${plan.display} controls preload="metadata" src="${src}"></${plan.display}>${caption}</figure>`;
49087
+ return `<figure class="attachment attachment-document"><a href="${src}"><img src="${escapeHtml(plan.previewUrl)}" alt="${name}" loading="lazy"></a><figcaption><a href="${src}">${name}</a></figcaption></figure>`;
49088
+ }
49089
+ default:
49090
+ return `<p>${inlinePlanHtml(plan)}</p>`;
49091
+ }
49092
+ }
49093
+ function tableHtml(rows) {
49094
+ const width = Math.max(1, ...rows.map((row) => row.length));
49095
+ const cells = (row, tag) => Array.from(
49096
+ { length: width },
49097
+ (_, i) => `<${tag}>${escapeHtml(row[i] ?? "").replace(/\r?\n/g, "<br>")}</${tag}>`
49098
+ ).join("");
49099
+ const [head = [], ...body] = rows;
49100
+ return `<table><thead><tr>${cells(head, "th")}</tr></thead><tbody>${body.map((row) => `<tr>${cells(row, "td")}</tr>`).join("")}</tbody></table>`;
49101
+ }
49102
+ function inlineHtml(pieces) {
49103
+ let out = "";
49104
+ for (let i = 0; i < pieces.length; ) {
49105
+ const piece = pieces[i];
49106
+ if (piece.type === "attachment") {
49107
+ out += inlinePlanHtml(piece.plan);
49108
+ i++;
49109
+ continue;
49110
+ }
49111
+ const link = piece.fmt.link;
49112
+ let inner = "";
49113
+ let j = i;
49114
+ for (; j < pieces.length; j++) {
49115
+ const next = pieces[j];
49116
+ if (next.type !== "text" || next.fmt.link !== link) break;
49117
+ inner += wrapText(next.text, next.fmt);
49118
+ }
49119
+ out += link ? `<a href="${escapeHtml(link)}">${inner}</a>` : inner;
49120
+ i = j;
49121
+ }
49122
+ return out;
49123
+ }
49124
+ function blockParts(pieces) {
49125
+ const parts = [];
49126
+ let segment = [];
49127
+ const flush = () => {
49128
+ const html = inlineHtml(segment).trim();
49129
+ if (html) parts.push({ inline: html });
49130
+ segment = [];
49131
+ };
49132
+ for (const piece of pieces) {
49133
+ if (piece.type === "attachment" && isHtmlBlockPlan(piece.plan)) {
49134
+ flush();
49135
+ parts.push({ block: blockPlanHtml(piece.plan) });
49136
+ } else segment.push(piece);
49137
+ }
49138
+ flush();
49139
+ return parts;
49140
+ }
49141
+ var LIST_TAGS = {
49142
+ bulleted: "ul",
49143
+ dashed: "ul",
49144
+ checklist: "ul",
49145
+ numbered: "ol"
49146
+ };
49147
+ var alignAttr = (block) => ALIGN.has(block.alignment) ? ` style="text-align:${block.alignment}"` : "";
49148
+ function renderNoteHtml(note, ctx) {
49149
+ const plan = (id2) => planAttachment(note.attachments.get(id2), ctx);
49150
+ const titleIndex = titleBlockIndex(note);
49151
+ const out = [];
49152
+ const lists = [];
49153
+ let quote = false;
49154
+ let code;
49155
+ const closeLists = (depth = 0) => {
49156
+ while (lists.length > depth) {
49157
+ const list = lists.pop();
49158
+ out.push(`${list.open ? "</li>" : ""}</${list.tag}>`);
49159
+ }
49160
+ };
49161
+ const flushCode = () => {
49162
+ if (code) out.push(`<pre><code>${escapeHtml(code.join("\n"))}</code></pre>`);
49163
+ code = void 0;
49164
+ };
49165
+ const setQuote = (on) => {
49166
+ if (on === quote) return;
49167
+ flushCode();
49168
+ closeLists();
49169
+ out.push(on ? "<blockquote>" : "</blockquote>");
49170
+ quote = on;
49171
+ };
49172
+ if (titleIndex === -1 && note.title.trim()) out.push(`<h1>${escapeHtml(note.title.trim())}</h1>`);
49173
+ for (const block of note.doc.blocks) {
49174
+ setQuote(block.blockQuote);
49175
+ if (block.style === "monospaced") {
49176
+ closeLists();
49177
+ (code ??= []).push(block.text.replace(//g, ""));
49178
+ continue;
49179
+ }
49180
+ flushCode();
49181
+ const listTag = LIST_TAGS[block.style];
49182
+ if (!listTag) closeLists();
49183
+ if (!block.text.trim()) continue;
49184
+ const parts = blockParts(blockPieces(block, plan));
49185
+ if (!parts.length) continue;
49186
+ if (listTag) {
49187
+ const level = Math.min(block.indent, 20);
49188
+ const cls = block.style === "checklist" || block.style === "dashed" ? block.style : "";
49189
+ closeLists(level + 1);
49190
+ const top = lists[level];
49191
+ if (top && (top.tag !== listTag || top.cls !== cls)) closeLists(level);
49192
+ if (lists[level]?.open) out.push("</li>");
49193
+ while (lists.length < level) {
49194
+ out.push(`<${listTag}><li class="nest">`);
49195
+ lists.push({ tag: listTag, cls: "", open: true });
49196
+ }
49197
+ if (lists.length === level) {
49198
+ out.push(`<${listTag}${cls ? ` class="${cls}"` : ""}>`);
49199
+ lists.push({ tag: listTag, cls, open: false });
49200
+ }
49201
+ lists[level].open = true;
49202
+ const box = block.style === "checklist" ? `<input type="checkbox" disabled${block.checklist?.done ? " checked" : ""}> ` : "";
49203
+ const done = block.style === "checklist" && block.checklist?.done ? ` class="done"` : "";
49204
+ const body = parts.map((part) => "inline" in part ? part.inline : part.block).join("");
49205
+ out.push(`<li${done}${alignAttr(block)}>${box}${body}`);
49206
+ continue;
49207
+ }
49208
+ const tag = block.index === titleIndex || block.style === "title" ? "h1" : block.style === "heading" ? "h2" : block.style === "subheading" ? "h3" : "p";
49209
+ for (const part of parts)
49210
+ out.push(
49211
+ "inline" in part ? `<${tag}${alignAttr(block)}>${part.inline}</${tag}>` : part.block
49212
+ );
49213
+ }
49214
+ flushCode();
49215
+ closeLists();
49216
+ setQuote(false);
49217
+ for (const attachment of unreferencedAttachments(note)) {
49218
+ ctx.stats.unreferenced++;
49219
+ out.push(blockPlanHtml(planAttachment(attachment, ctx)));
49220
+ }
49221
+ return `<article class="note">
49222
+ ${out.filter(Boolean).join("\n")}
49223
+ </article>`;
49224
+ }
49225
+ var CSS = `
49226
+ :root { color-scheme: light dark; --fg: #1d1d1f; --bg: #fff; --muted: #6e6e73; --line: #d2d2d7; --card: #f5f5f7; }
49227
+ @media (prefers-color-scheme: dark) { :root { --fg: #f5f5f7; --bg: #1d1d1f; --muted: #a1a1a6; --line: #424245; --card: #2c2c2e; } }
49228
+ body { margin: 0; background: var(--bg); color: var(--fg); font: 16px/1.5 -apple-system, BlinkMacSystemFont, "Helvetica Neue", sans-serif; }
49229
+ main { max-width: 46rem; margin: 0 auto; padding: 2rem 1rem; }
49230
+ h1, h2, h3 { line-height: 1.25; }
49231
+ blockquote { margin: 1rem 0; padding-left: 1rem; border-left: 3px solid var(--line); color: var(--muted); }
49232
+ pre { background: var(--card); padding: .75rem 1rem; overflow-x: auto; border-radius: 6px; }
49233
+ code { font: 14px/1.4 ui-monospace, Menlo, monospace; }
49234
+ ul.dashed { list-style-type: "\u2013 "; }
49235
+ ul.checklist { list-style: none; padding-left: 1.25rem; }
49236
+ ul.checklist li.done { color: var(--muted); text-decoration: line-through; }
49237
+ li.nest { list-style: none; }
49238
+ table { border-collapse: collapse; margin: 1rem 0; }
49239
+ th, td { border: 1px solid var(--line); padding: .35rem .6rem; text-align: left; vertical-align: top; }
49240
+ th { background: var(--card); }
49241
+ figure { margin: 1rem 0; }
49242
+ figure img, figure video { max-width: 100%; height: auto; border-radius: 6px; }
49243
+ figcaption { color: var(--muted); font-size: .875rem; }
49244
+ .attachment-gallery { display: flex; flex-wrap: wrap; gap: .5rem; }
49245
+ .attachment-gallery figure { margin: 0; flex: 1 1 12rem; }
49246
+ .link-card { display: flex; gap: .75rem; align-items: center; margin: 1rem 0; padding: .5rem; border: 1px solid var(--line); border-radius: 8px; background: var(--card); color: inherit; text-decoration: none; }
49247
+ .link-card img { width: 4rem; height: 4rem; object-fit: cover; border-radius: 4px; }
49248
+ .link-card-text { display: flex; flex-direction: column; min-width: 0; }
49249
+ .link-card-title { font-weight: 600; }
49250
+ .link-card-domain { color: var(--muted); font-size: .875rem; }
49251
+ .attachment-unavailable, .attachment-placeholder { display: inline-block; padding: 0 .35rem; border: 1px dashed var(--muted); border-radius: 4px; color: var(--muted); font-size: .875rem; }
49252
+ mark { border-radius: 2px; padding: 0 .1em; }
49253
+ mark.hl-purple { background: #e5d4ff; } mark.hl-pink { background: #ffd1e3; } mark.hl-orange { background: #ffe0b8; }
49254
+ mark.hl-mint { background: #c8f2de; } mark.hl-blue { background: #cfe3ff; } mark.hl-other { background: #fff3a8; }
49255
+ hr.note-separator { margin: 3rem 0; border: 0; border-top: 2px solid var(--line); }
49256
+ `.trim();
49257
+ function renderNotesHtml(notes, ctx, { title }) {
49258
+ const body = notes.map((note) => renderNoteHtml(note, ctx)).join('\n<hr class="note-separator">\n');
49259
+ return [
49260
+ "<!DOCTYPE html>",
49261
+ "<html>",
49262
+ "<head>",
49263
+ '<meta charset="utf-8">',
49264
+ '<meta name="viewport" content="width=device-width, initial-scale=1">',
49265
+ '<meta name="generator" content="apple-notes-mcp">',
49266
+ `<title>${escapeHtml(title)}</title>`,
49267
+ `<style>
49268
+ ${CSS}
49269
+ </style>`,
49270
+ "</head>",
49271
+ "<body>",
49272
+ "<main>",
49273
+ body,
49274
+ "</main>",
49275
+ "</body>",
49276
+ "</html>",
49277
+ ""
49278
+ ].join("\n");
49279
+ }
49280
+
48821
49281
  // src/utils/markdownExport.ts
48822
49282
  var NOTE_SEPARATOR = "\n\n---\n\n";
48823
49283
  function escapeMarkdown(text2) {
@@ -48866,7 +49326,7 @@ function layered(pieces, depth = 0) {
48866
49326
  }
48867
49327
  return out;
48868
49328
  }
48869
- var bracket = (label, name) => `\\[${escapeMarkdown(name ? `${label}: ${name}` : label)}\\]`;
49329
+ var bracket2 = (label, name) => `\\[${escapeMarkdown(name ? `${label}: ${name}` : label)}\\]`;
48870
49330
  function planMarkdown(plan) {
48871
49331
  switch (plan.type) {
48872
49332
  case "inline":
@@ -48876,9 +49336,9 @@ function planMarkdown(plan) {
48876
49336
  case "table":
48877
49337
  return tableMarkdown(plan.rows);
48878
49338
  case "placeholder":
48879
- return bracket(plan.label, plan.name);
49339
+ return bracket2(plan.label, plan.name);
48880
49340
  case "unavailable":
48881
- return bracket(`${plan.label} unavailable`, plan.name);
49341
+ return bracket2(`${plan.label} unavailable`, plan.name);
48882
49342
  case "asset": {
48883
49343
  const text2 = escapeMarkdown(plan.name ?? plan.label);
48884
49344
  if (plan.display === "image") return `![${text2}](${linkDestination(plan.url)})`;
@@ -49049,11 +49509,11 @@ function renderNotesMarkdown(notes, ctx, { wrap: wrap2 = 0 } = {}) {
49049
49509
  }
49050
49510
 
49051
49511
  // src/utils/noteExportData.ts
49052
- import { execFileSync as execFileSync17 } from "node:child_process";
49053
- import { homedir as homedir16 } from "node:os";
49054
- import { join as join19 } from "node:path";
49055
- var NOTES_DB_PATH10 = join19(
49056
- homedir16(),
49512
+ import { execFileSync as execFileSync18 } from "node:child_process";
49513
+ import { homedir as homedir17 } from "node:os";
49514
+ import { join as join20 } from "node:path";
49515
+ var NOTES_DB_PATH11 = join20(
49516
+ homedir17(),
49057
49517
  "Library/Group Containers/group.com.apple.notes/NoteStore.sqlite"
49058
49518
  );
49059
49519
  var IMAGE_UTIS2 = /* @__PURE__ */ new Set([
@@ -49117,7 +49577,7 @@ var ATTACHMENT_COLUMNS = [
49117
49577
  var columnCache = /* @__PURE__ */ new Map();
49118
49578
  function sqlite(dbPath2, args) {
49119
49579
  try {
49120
- return execFileSync17(
49580
+ return execFileSync18(
49121
49581
  "/usr/bin/sqlite3",
49122
49582
  ["-readonly", ...args.slice(0, -1), dbPath2, args.at(-1)],
49123
49583
  {
@@ -49134,7 +49594,7 @@ function sqlite(dbPath2, args) {
49134
49594
  throw new NoteBlocksError("query-failed", "Failed to query the Notes database");
49135
49595
  }
49136
49596
  }
49137
- function objectColumns(dbPath2 = NOTES_DB_PATH10) {
49597
+ function objectColumns(dbPath2 = NOTES_DB_PATH11) {
49138
49598
  let columns = columnCache.get(dbPath2);
49139
49599
  if (!columns) {
49140
49600
  columns = new Set(
@@ -49192,7 +49652,7 @@ function buildAttachments(rows) {
49192
49652
  }
49193
49653
  return { byId: new Map(all.map((a) => [a.id, a])), ordered };
49194
49654
  }
49195
- function readExportNote(id2, { dbPath: dbPath2 = NOTES_DB_PATH10 } = {}) {
49655
+ function readExportNote(id2, { dbPath: dbPath2 = NOTES_DB_PATH11 } = {}) {
49196
49656
  const doc = readNoteBlocks(id2, { dbPath: dbPath2 });
49197
49657
  const pk = /\/ICNote\/p([0-9]{1,18})$/.exec(id2)[1];
49198
49658
  const output = sqlite(dbPath2, [
@@ -49231,9 +49691,9 @@ var NotesExportError = class extends Error {
49231
49691
  }
49232
49692
  code;
49233
49693
  };
49234
- function validPath(path8, what) {
49694
+ function validPath(path9, what) {
49235
49695
  try {
49236
- return assertExportPath(path8);
49696
+ return assertExportPath(path9);
49237
49697
  } catch (error2) {
49238
49698
  throw new NotesExportError(
49239
49699
  "invalid-path",
@@ -49269,6 +49729,24 @@ function loadNotes(ids, single, read) {
49269
49729
  }
49270
49730
  return { notes, skipped };
49271
49731
  }
49732
+ function openOutput(output) {
49733
+ mkdirSync5(dirname4(output), { recursive: true });
49734
+ try {
49735
+ return openCreateOnly(output);
49736
+ } catch (error2) {
49737
+ if (error2 instanceof OutputExistsError)
49738
+ throw new NotesExportError("output_exists", error2.message);
49739
+ throw error2;
49740
+ }
49741
+ }
49742
+ function renderInto(fd, render) {
49743
+ try {
49744
+ return render();
49745
+ } catch (error2) {
49746
+ if (fd !== void 0) writeAllAndClose(fd, "");
49747
+ throw error2;
49748
+ }
49749
+ }
49272
49750
  function exportNotesMarkdown(request, deps) {
49273
49751
  const output = request.outputPath ? validPath(request.outputPath, "outputPath") : void 0;
49274
49752
  const assetsDir = request.assetsDir ? validPath(request.assetsDir, "assetsDir") : void 0;
@@ -49276,29 +49754,16 @@ function exportNotesMarkdown(request, deps) {
49276
49754
  throw new NotesExportError("invalid-path", "outputPath and assetsDir must differ.");
49277
49755
  const ids = selectNotes(request, deps);
49278
49756
  const { notes, skipped } = loadNotes(ids, !!request.id, deps.readNote);
49279
- let fd;
49280
- if (output) {
49281
- mkdirSync5(dirname4(output), { recursive: true });
49282
- try {
49283
- fd = openCreateOnly(output);
49284
- } catch (error2) {
49285
- if (error2 instanceof OutputExistsError)
49286
- throw new NotesExportError("output_exists", error2.message);
49287
- throw error2;
49288
- }
49289
- }
49757
+ const fd = output ? openOutput(output) : void 0;
49290
49758
  const writer = assetsDir ? new SidecarWriter(assetsDir, output ? dirname4(output) : void 0) : void 0;
49291
49759
  const ctx = {
49292
49760
  stats: emptyStats(),
49293
49761
  ...writer ? { writer, locator: deps.locator ?? new AssetLocator() } : {}
49294
49762
  };
49295
- let markdown;
49296
- try {
49297
- markdown = renderNotesMarkdown(notes, ctx, { wrap: request.wrap ?? 0 });
49298
- } catch (error2) {
49299
- if (fd !== void 0) writeAllAndClose(fd, "");
49300
- throw error2;
49301
- }
49763
+ const markdown = renderInto(
49764
+ fd,
49765
+ () => renderNotesMarkdown(notes, ctx, { wrap: request.wrap ?? 0 })
49766
+ );
49302
49767
  const bytes = Buffer.byteLength(markdown);
49303
49768
  const receipt = {
49304
49769
  format: "markdown",
@@ -49319,6 +49784,47 @@ function exportNotesMarkdown(request, deps) {
49319
49784
  );
49320
49785
  return { ...receipt, markdown };
49321
49786
  }
49787
+ function defaultSidecarDir(output) {
49788
+ return join21(dirname4(output), `${basename4(output, extname4(output))}.assets`);
49789
+ }
49790
+ function exportNotesHtml(request, deps) {
49791
+ if (!request.outputPath)
49792
+ throw new NotesExportError(
49793
+ "invalid-request",
49794
+ "outputPath is required for HTML export; the document is written to a file."
49795
+ );
49796
+ const embed = request.embedAssets ?? !request.assetsDir;
49797
+ if (embed && request.assetsDir)
49798
+ throw new NotesExportError(
49799
+ "invalid-request",
49800
+ "assetsDir applies only with embedAssets false (sidecar assets)."
49801
+ );
49802
+ const output = validPath(request.outputPath, "outputPath");
49803
+ const assetsDir = embed ? void 0 : validPath(request.assetsDir ?? defaultSidecarDir(output), "assetsDir");
49804
+ if (assetsDir === output)
49805
+ throw new NotesExportError("invalid-path", "outputPath and assetsDir must differ.");
49806
+ const ids = selectNotes(request, deps);
49807
+ const { notes, skipped } = loadNotes(ids, !!request.id, deps.readNote);
49808
+ const fd = openOutput(output);
49809
+ const writer = assetsDir ? new SidecarWriter(assetsDir, dirname4(output)) : new DataUrlWriter();
49810
+ const ctx = {
49811
+ stats: emptyStats(),
49812
+ writer,
49813
+ locator: deps.locator ?? new AssetLocator()
49814
+ };
49815
+ const title = request.id ? notes[0]?.title || "Note" : request.folder;
49816
+ const html = renderInto(fd, () => renderNotesHtml(notes, ctx, { title }));
49817
+ const bytes = writeAllAndClose(fd, html);
49818
+ return {
49819
+ format: "html",
49820
+ count: notes.length,
49821
+ bytes,
49822
+ output,
49823
+ stats: ctx.stats,
49824
+ skipped,
49825
+ ...assetsDir ? { assets: { dir: assetsDir, files: writer.count } } : { embedded: writer.count }
49826
+ };
49827
+ }
49322
49828
 
49323
49829
  // src/tools/directOperations.ts
49324
49830
  import { createHash as createHash2 } from "node:crypto";
@@ -49333,7 +49839,7 @@ import {
49333
49839
  writeFileSync as writeFileSync3
49334
49840
  } from "node:fs";
49335
49841
  import { tmpdir as tmpdir4 } from "node:os";
49336
- import { basename as basename4, extname as extname4, isAbsolute as isAbsolute3, join as join20 } from "node:path";
49842
+ import { basename as basename5, extname as extname5, isAbsolute as isAbsolute3, join as join22 } from "node:path";
49337
49843
  var noteId = exactIdInput(
49338
49844
  "ICNote",
49339
49845
  /^x-coredata:\/\/[0-9a-f-]+\/ICNote\/p\d+$/i,
@@ -49376,9 +49882,9 @@ function assertExistingContentPreserved(before, after) {
49376
49882
  throw new Error("Existing native object content or presentation changed");
49377
49883
  }
49378
49884
  }
49379
- function localAttachment(path8) {
49380
- if (!isAbsolute3(path8)) throw new Error("An absolute local file path is required");
49381
- const descriptor = openSync5(path8, constants5.O_RDONLY | constants5.O_NOFOLLOW);
49885
+ function localAttachment(path9) {
49886
+ if (!isAbsolute3(path9)) throw new Error("An absolute local file path is required");
49887
+ const descriptor = openSync5(path9, constants5.O_RDONLY | constants5.O_NOFOLLOW);
49382
49888
  try {
49383
49889
  const stat = fstatSync5(descriptor);
49384
49890
  if (!stat.isFile() || stat.size === 0 || stat.size > 64 * 1024 * 1024)
@@ -49500,8 +50006,8 @@ function registerDirectOperations(server2, manager) {
49500
50006
  }
49501
50007
  );
49502
50008
  }
49503
- function attachmentName(path8, filename) {
49504
- const source = basename4(path8);
50009
+ function attachmentName(path9, filename) {
50010
+ const source = basename5(path9);
49505
50011
  if (filename === void 0) return source;
49506
50012
  if (Buffer.byteLength(filename, "utf8") > 255)
49507
50013
  throw new Error("filename must be at most 255 bytes");
@@ -49509,21 +50015,21 @@ function attachmentName(path8, filename) {
49509
50015
  throw new Error(
49510
50016
  "filename must be one path component with no slash, colon, backslash, control character, leading dot, or surrounding spaces"
49511
50017
  );
49512
- if (extname4(filename).toLowerCase() !== extname4(source).toLowerCase())
50018
+ if (extname5(filename).toLowerCase() !== extname5(source).toLowerCase())
49513
50019
  throw new Error(
49514
- `filename must keep the source file's extension (${extname4(source) || "none"})`
50020
+ `filename must keep the source file's extension (${extname5(source) || "none"})`
49515
50021
  );
49516
50022
  return filename;
49517
50023
  }
49518
50024
  function attachFile(manager, args) {
49519
- const { id: id2, expectedContentHash, path: path8 } = args;
49520
- const name = attachmentName(path8, args.filename);
50025
+ const { id: id2, expectedContentHash, path: path9 } = args;
50026
+ const name = attachmentName(path9, args.filename);
49521
50027
  const before = readSnapshot(manager, id2);
49522
50028
  if (before.hash !== expectedContentHash) throw new Error("Note revision changed");
49523
- const bytes = localAttachment(path8);
50029
+ const bytes = localAttachment(path9);
49524
50030
  const beforeAttachments = manager.listAttachmentsById(id2);
49525
- const directory = mkdtempSync4(join20(tmpdir4(), "notes-attachment-add-"));
49526
- const temporaryFile = join20(directory, name);
50031
+ const directory = mkdtempSync4(join22(tmpdir4(), "notes-attachment-add-"));
50032
+ const temporaryFile = join22(directory, name);
49527
50033
  try {
49528
50034
  writeFileSync3(temporaryFile, bytes, { mode: 384 });
49529
50035
  if (readSnapshot(manager, id2).hash !== before.hash) throw new Error("Note revision changed");
@@ -50081,7 +50587,7 @@ function registerNativeOperations(server2, manager) {
50081
50587
 
50082
50588
  // src/setupShortcuts.ts
50083
50589
  import { spawnSync as spawnSync2 } from "node:child_process";
50084
- import { existsSync as existsSync12 } from "node:fs";
50590
+ import { existsSync as existsSync13 } from "node:fs";
50085
50591
  import { release as release2 } from "node:os";
50086
50592
  import { dirname as dirname5, resolve as resolve4 } from "node:path";
50087
50593
  import { fileURLToPath } from "node:url";
@@ -50101,16 +50607,16 @@ var shortcutFiles = [
50101
50607
  ];
50102
50608
  function setupShortcuts(checkOnly, dependencies = {}) {
50103
50609
  const status = dependencies.status || nativeTagsStatus;
50104
- const exists = dependencies.exists || existsSync12;
50105
- const open = dependencies.open || ((path8) => {
50106
- const result = spawnSync2("/usr/bin/open", [path8], { encoding: "utf8" });
50610
+ const exists = dependencies.exists || existsSync13;
50611
+ const open = dependencies.open || ((path9) => {
50612
+ const result = spawnSync2("/usr/bin/open", [path9], { encoding: "utf8" });
50107
50613
  return result.status === 0 ? { ok: true } : { ok: false, error: result.stderr || result.error?.message || "open failed" };
50108
50614
  });
50109
50615
  const baseDirectory = dependencies.baseDirectory || resolve4(dirname5(fileURLToPath(import.meta.url)), "../shortcuts");
50110
50616
  const osRelease = (dependencies.osRelease || release2)();
50111
50617
  const darwinMajor = Number.parseInt(osRelease.split(".")[0], 10);
50112
50618
  const items = shortcutFiles.map(({ name, file, optional: optional2 }) => {
50113
- const path8 = resolve4(baseDirectory, file);
50619
+ const path9 = resolve4(baseDirectory, file);
50114
50620
  let installed = false;
50115
50621
  let identifier;
50116
50622
  let error2;
@@ -50126,9 +50632,9 @@ function setupShortcuts(checkOnly, dependencies = {}) {
50126
50632
  if (!installed && !checkOnly) {
50127
50633
  if (optional2 && !(darwinMajor >= MARKDOWN_MIN_DARWIN_MAJOR))
50128
50634
  skipped = `requires macOS 26 or later (this Mac reports Darwin ${osRelease})`;
50129
- else if (!exists(path8)) error2 = `Packaged Shortcut is missing: ${path8}`;
50635
+ else if (!exists(path9)) error2 = `Packaged Shortcut is missing: ${path9}`;
50130
50636
  else {
50131
- const result = open(path8);
50637
+ const result = open(path9);
50132
50638
  opened = result.ok;
50133
50639
  if (!result.ok) error2 = result.error || `Could not open ${file}`;
50134
50640
  }
@@ -50137,7 +50643,7 @@ function setupShortcuts(checkOnly, dependencies = {}) {
50137
50643
  name,
50138
50644
  installed,
50139
50645
  identifier,
50140
- file: path8,
50646
+ file: path9,
50141
50647
  opened,
50142
50648
  ...optional2 ? { optional: optional2 } : {},
50143
50649
  ...skipped ? { skipped } : {},
@@ -50267,6 +50773,9 @@ function readExactNoteSnapshot(id2) {
50267
50773
  return { note, body, rich, contentHash: richContentHash(body, rich) };
50268
50774
  }
50269
50775
  var NOT_DELETED_MESSAGE = "Notes.app accepted the delete, but the note is still in its original folder, so it was not moved to Recently Deleted. Nothing was deleted; read the note again before retrying.";
50776
+ function inRecentlyDeletedMessage(title) {
50777
+ return `Note "${title}" is already in Recently Deleted, where deleting it would remove it permanently. Nothing was deleted. Remove it from Recently Deleted in Notes.app if that is intended.`;
50778
+ }
50270
50779
  function revisionConflictMessage(title) {
50271
50780
  return `Note "${title}" changed after it was read. Read it again and review the newer version before retrying.`;
50272
50781
  }
@@ -50445,7 +50954,7 @@ registerTool(
50445
50954
  count: external_exports.number().optional()
50446
50955
  }
50447
50956
  },
50448
- withErrorHandling(({ query, searchContent = false, account, folder, modifiedSince, limit }) => {
50957
+ withErrorHandling(({ query: query2, searchContent = false, account, folder, modifiedSince, limit }) => {
50449
50958
  const effectiveLimit = resolveSearchLimit(limit);
50450
50959
  const limitWasDefault = limit === void 0;
50451
50960
  const {
@@ -50454,7 +50963,7 @@ registerTool(
50454
50963
  syncInterference
50455
50964
  } = withSyncAwarenessSync(
50456
50965
  "search-notes",
50457
- () => notesManager.searchNotes(query, searchContent, account, folder, modifiedSince, effectiveLimit)
50966
+ () => notesManager.searchNotes(query2, searchContent, account, folder, modifiedSince, effectiveLimit)
50458
50967
  );
50459
50968
  const searchType = searchContent ? "content" : "titles";
50460
50969
  const folderInfo = folder ? ` in folder "${folder}"` : "";
@@ -50477,7 +50986,7 @@ ${syncWarnings.join(" ")}` : "";
50477
50986
  if (notes.length === 0) {
50478
50987
  const scopeHint = describeSearchScope(searchContent, notes.length);
50479
50988
  return successResponse(
50480
- `No notes found matching "${query}" in ${searchType}${folderInfo}${dateInfo}${scopeHint}${syncNote}`,
50989
+ `No notes found matching "${query2}" in ${searchType}${folderInfo}${dateInfo}${scopeHint}${syncNote}`,
50481
50990
  { notes: [], count: 0 }
50482
50991
  );
50483
50992
  }
@@ -50529,10 +51038,10 @@ registerTool(
50529
51038
  },
50530
51039
  annotations: { readOnlyHint: true }
50531
51040
  },
50532
- withErrorHandling(({ query, limit, scanLimit, includeDeleted }) => {
51041
+ withErrorHandling(({ query: query2, limit, scanLimit, includeDeleted }) => {
50533
51042
  let result;
50534
51043
  try {
50535
- result = queryNotes(query, { limit, scanLimit, includeDeleted });
51044
+ result = queryNotes(query2, { limit, scanLimit, includeDeleted });
50536
51045
  } catch (error2) {
50537
51046
  if (error2 instanceof NoteQueryError || error2 instanceof NoteQueryStoreError) {
50538
51047
  return errorResponse(
@@ -51443,7 +51952,7 @@ registerTool(
51443
51952
  registerTool(
51444
51953
  "delete-note",
51445
51954
  {
51446
- description: "Use when: moving one exact note to Recently Deleted after reading and reviewing it.\nReturns: confirmation with the exact id.\nDo not use when: you only have a title or the note changed since review.\nSafety: requires id and expectedContentHash from get-note-content. The body comparison and delete happen in one AppleScript, so a newer edit is preserved.",
51955
+ description: "Use when: moving one exact note to Recently Deleted after reading and reviewing it.\nReturns: confirmation with the exact id.\nDo not use when: you only have a title or the note changed since review.\nSafety: requires id and expectedContentHash from get-note-content. The body comparison and delete happen in one AppleScript, so a newer edit is preserved. Refuses a note already in Recently Deleted, where a delete would be permanent.",
51447
51956
  inputSchema: {
51448
51957
  id: noteIdInput,
51449
51958
  expectedContentHash: expectedContentHashInput,
@@ -51467,6 +51976,9 @@ registerTool(
51467
51976
  if (result.status === "conflict") {
51468
51977
  return errorResponse(revisionConflictMessage(snapshot.note.title));
51469
51978
  }
51979
+ if (result.status === "in-recently-deleted") {
51980
+ return errorResponse(inRecentlyDeletedMessage(snapshot.note.title));
51981
+ }
51470
51982
  if (result.status === "not-deleted") {
51471
51983
  return errorResponse(NOT_DELETED_MESSAGE);
51472
51984
  }
@@ -51535,36 +52047,47 @@ registerTool(
51535
52047
  registerTool(
51536
52048
  "list-notes",
51537
52049
  {
51538
- description: "Use when: enumerating notes in an account or folder; supports modifiedSince and limit for large collections.\nReturns: each note's title and id (ids are safe to use for follow-up reads/edits even when titles are duplicated \u2014 see search-notes for keyword-based lookup instead).\nDo not use when: you need content (get-note-content).\nNote: warns if iCloud sync is active and results may be partial.",
52050
+ description: "Use when: enumerating notes in an account or folder; supports modifiedSince and limit for large collections.\nReturns: each note's title and id (ids are safe to use for follow-up reads/edits even when titles are duplicated \u2014 see search-notes for keyword-based lookup instead).\nDo not use when: you need content (get-note-content).\nNote: excludes notes in Recently Deleted unless includeRecentlyDeleted is true, which flags them inRecentlyDeleted. Warns if iCloud sync is active and results may be partial.",
51539
52051
  inputSchema: {
51540
52052
  account: external_exports.string().max(MAX.ACCOUNT).optional().describe("Account to list notes from"),
51541
52053
  folder: external_exports.string().max(MAX.FOLDER).optional().describe("Filter to specific folder"),
51542
52054
  modifiedSince: external_exports.string().max(64).optional().describe(
51543
52055
  "ISO 8601 date string to filter notes modified on or after this date (e.g., '2025-01-01'). Useful for listing only recent notes in large collections."
51544
52056
  ),
51545
- limit: external_exports.number().int().positive().optional().describe("Maximum number of notes to return")
52057
+ limit: external_exports.number().int().positive().optional().describe("Maximum number of notes to return"),
52058
+ includeRecentlyDeleted: external_exports.boolean().optional().describe(
52059
+ "Also list notes in Recently Deleted, each flagged inRecentlyDeleted (default false)"
52060
+ )
51546
52061
  },
51547
52062
  outputSchema: {
51548
52063
  notes: external_exports.array(
51549
52064
  external_exports.object({
51550
52065
  title: external_exports.string(),
51551
52066
  id: external_exports.string(),
52067
+ inRecentlyDeleted: external_exports.boolean().optional(),
51552
52068
  identifier: external_exports.string().optional(),
51553
52069
  folderIdentifier: external_exports.string().optional(),
51554
52070
  accountIdentifier: external_exports.string().optional()
51555
52071
  })
51556
52072
  ).optional(),
51557
- count: external_exports.number().optional()
52073
+ count: external_exports.number().optional(),
52074
+ excludedRecentlyDeleted: external_exports.number().optional()
51558
52075
  }
51559
52076
  },
51560
- withErrorHandling(({ account, folder, modifiedSince, limit }) => {
52077
+ withErrorHandling(({ account, folder, modifiedSince, limit, includeRecentlyDeleted }) => {
51561
52078
  const {
51562
- result: notes,
52079
+ result: { refs: notes, excludedRecentlyDeleted },
51563
52080
  syncBefore,
51564
52081
  syncInterference
51565
52082
  } = withSyncAwarenessSync(
51566
52083
  "list-notes",
51567
- () => notesManager.listNoteRefs(account, folder, modifiedSince, limit)
52084
+ () => notesManager.listNoteRefsDetailed(
52085
+ account,
52086
+ folder,
52087
+ modifiedSince,
52088
+ limit,
52089
+ includeRecentlyDeleted ?? false
52090
+ )
51568
52091
  );
51569
52092
  const location = folder ? ` in folder "${folder}"` : "";
51570
52093
  const acct = account ? ` (${account})` : "";
@@ -51580,17 +52103,22 @@ registerTool(
51580
52103
  const syncNote = syncWarnings.length > 0 ? `
51581
52104
 
51582
52105
  ${syncWarnings.join(" ")}` : "";
52106
+ const trashNote = excludedRecentlyDeleted > 0 ? `
52107
+
52108
+ Skipped ${excludedRecentlyDeleted} note(s) in Recently Deleted; pass includeRecentlyDeleted: true to list them.` : "";
52109
+ const trashData = excludedRecentlyDeleted > 0 ? { excludedRecentlyDeleted } : {};
51583
52110
  if (notes.length === 0) {
51584
- return successResponse(`No notes found${location}${acct}${dateInfo}${syncNote}`, {
52111
+ return successResponse(`No notes found${location}${acct}${dateInfo}${trashNote}${syncNote}`, {
51585
52112
  notes: [],
51586
- count: 0
52113
+ count: 0,
52114
+ ...trashData
51587
52115
  });
51588
52116
  }
51589
- const noteList = notes.map((n) => ` - ${n.title} [id: ${n.id}]`).join("\n");
52117
+ const noteList = notes.map((n) => ` - ${n.title}${n.inRecentlyDeleted ? " [RECENTLY DELETED]" : ""} [id: ${n.id}]`).join("\n");
51590
52118
  return successResponse(
51591
52119
  `Found ${notes.length} notes${location}${acct}${dateInfo}${limitInfo}:
51592
- ${noteList}${syncNote}`,
51593
- { notes: withStableIdentifiers(notes, "ICNote"), count: notes.length }
52120
+ ${noteList}${trashNote}${syncNote}`,
52121
+ { notes: withStableIdentifiers(notes, "ICNote"), count: notes.length, ...trashData }
51594
52122
  );
51595
52123
  }, "Error listing notes")
51596
52124
  );
@@ -52096,7 +52624,7 @@ ${attachmentList}`,
52096
52624
  registerTool(
52097
52625
  "batch-delete-notes",
52098
52626
  {
52099
- description: "Use when: moving several reviewed notes to Recently Deleted.\nReturns: per-note success or conflict.\nDo not use when: deleting a single note.\nSafety: every entry requires an exact id and the content hash from get-note-content. Any note changed since review is preserved and reported as a conflict.",
52627
+ description: "Use when: moving several reviewed notes to Recently Deleted.\nReturns: per-note success or conflict.\nDo not use when: deleting a single note.\nSafety: every entry requires an exact id and the content hash from get-note-content. Any note changed since review is preserved and reported as a conflict. A note already in Recently Deleted is refused, since deleting it there would be permanent.",
52100
52628
  inputSchema: {
52101
52629
  notes: external_exports.array(
52102
52630
  external_exports.object({
@@ -52127,6 +52655,9 @@ registerTool(
52127
52655
  if (result.status === "conflict") {
52128
52656
  return { id: id2, success: false, error: revisionConflictMessage(snapshot.note.title) };
52129
52657
  }
52658
+ if (result.status === "in-recently-deleted") {
52659
+ return { id: id2, success: false, error: inRecentlyDeletedMessage(snapshot.note.title) };
52660
+ }
52130
52661
  if (result.status === "not-deleted")
52131
52662
  return { id: id2, success: false, error: NOT_DELETED_MESSAGE };
52132
52663
  return { id: id2, success: false, error: "Delete result uncertain; inspect this exact ID" };
@@ -52584,6 +53115,57 @@ registerTool(
52584
53115
  return successResponse(receipt.markdown ?? "", { ...receipt });
52585
53116
  }, "Error exporting Markdown")
52586
53117
  );
53118
+ registerTool(
53119
+ "export-notes-html",
53120
+ {
53121
+ description: "Use when: exporting one note (by exact id) or a folder's notes as one standalone HTML file rendered from the decoded note body, with semantic tables and images, drawings, scans, audio, files and link cards in body order.\nReturns: a receipt {format, count, bytes, output} plus embedded or sidecar asset counts, attachment counts and skipped notes. The HTML itself is never returned inline.\nDo not use when: you want Markdown (export-notes-markdown) or a restorable backup (export-notes-json). A folder document is a presentation format, not something to import back.\nSafety: read-only against Notes; requires Full Disk Access. outputPath is required and create-only ([output_exists] if it exists). Assets are embedded as data URLs (each up to 10 MiB) unless embedAssets is false, which copies them to a sidecar directory (assetsDir, default <output stem>.assets) with relative URLs and never replaces existing files. No file: URLs or Notes library paths are written; missing assets show a visible unavailable marker.",
53122
+ inputSchema: {
53123
+ id: noteIdInput.optional(),
53124
+ folder: external_exports.string().min(1).max(MAX.FOLDER).optional().describe("Folder path to export instead of one note (nested paths use '/')"),
53125
+ account: external_exports.string().max(MAX.ACCOUNT).optional().describe("Account holding the folder (defaults to Notes.app's default account)"),
53126
+ limit: external_exports.number().int().min(1).max(MAX_FOLDER_EXPORT_LIMIT).optional().describe(
53127
+ `Maximum notes read from the folder (default 100, max ${MAX_FOLDER_EXPORT_LIMIT})`
53128
+ ),
53129
+ outputPath: external_exports.string().min(1).max(MAX.SAVE_PATH).describe(
53130
+ "HTML file to create (absolute; under home, a temp dir, or /Volumes; never inside the Notes library)"
53131
+ ),
53132
+ embedAssets: external_exports.boolean().optional().describe("Embed assets as data URLs (default true). False writes a sidecar directory"),
53133
+ assetsDir: exportPathInput(
53134
+ "Sidecar directory when embedAssets is false (default <stem>.assets)"
53135
+ )
53136
+ },
53137
+ outputSchema: {
53138
+ format: external_exports.string().optional(),
53139
+ count: external_exports.number().optional(),
53140
+ bytes: external_exports.number().optional(),
53141
+ output: external_exports.string().optional(),
53142
+ assets: external_exports.object({ dir: external_exports.string(), files: external_exports.number() }).optional(),
53143
+ embedded: external_exports.number().optional(),
53144
+ stats: exportStatsSchema.optional(),
53145
+ skipped: external_exports.array(external_exports.object({ id: external_exports.string(), code: external_exports.string() })).optional()
53146
+ },
53147
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false }
53148
+ },
53149
+ withErrorHandling((request) => {
53150
+ let receipt;
53151
+ try {
53152
+ receipt = exportNotesHtml(request, {
53153
+ listNoteRefs: (account, folder, since, limit) => notesManager.listNoteRefs(account, folder, since, limit),
53154
+ maxInlineBytes: 0
53155
+ });
53156
+ } catch (error2) {
53157
+ if (!(error2 instanceof NotesExportError || error2 instanceof NoteBlocksError)) throw error2;
53158
+ const hint = error2.code === "no-full-disk-access" ? ` Grant Full Disk Access to the app that launches this server: ${FULL_DISK_ACCESS_GUIDE_URL}` : "";
53159
+ return errorResponse(`Error exporting HTML [${error2.code}]: ${error2.message}${hint}`);
53160
+ }
53161
+ const assets = receipt.assets ? `; copied ${receipt.assets.files} asset file(s) to ${receipt.assets.dir}` : `; embedded ${receipt.embedded ?? 0} asset(s)`;
53162
+ const skipped = receipt.skipped.length ? `; skipped ${receipt.skipped.length}` : "";
53163
+ return successResponse(
53164
+ `Wrote ${receipt.count} note(s) as HTML (${receipt.bytes} bytes) to ${receipt.output}${assets}${skipped}.`,
53165
+ { ...receipt }
53166
+ );
53167
+ }, "Error exporting HTML")
53168
+ );
52587
53169
  registerTool(
52588
53170
  "get-checklist-state",
52589
53171
  {