Commit Graph
3 Commits
Author SHA1 Message Date
ingjieyeandClaude Opus 5 07bf3854b7 README: state plainly that the Notes connector uses AppleScript
Confirmed by direct verification against Claude Desktop, so drop the hedge
about the implementation not being public and list the connector alongside
memo and the OpenClaw/Hermes skills.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-09 16:46:23 +08:00
ingjieyeandClaude Opus 5 cb81fb4605 README: frame this as read-first and detail the AppleScript trade-offs
The common case is looking things up, not writing, so say so up front and
explain why the existing tooling struggles there.

Names what the ecosystem actually does: memo shells out to osascript, the
OpenClaw and Hermes apple-notes skills wrap memo, and Claude's Notes connector
is a desktop-and-macOS-only extension. The connector's implementation is not
public, so this claims only that Apple Events is the sole supported route in
rather than asserting its internals.

Spells out the read-path costs, the first two verified from memo's source:

- Full-text search is impractical at ~0.325s per body, so memo matches titles
  only -- search_memo.py fuzzy-matches a {title: id} map and never touches
  bodies, while the OpenClaw skill still presents it as "fuzzy search".
- memo's 300s cache TTL can hide a note you just wrote, and neither the CLI
  nor the skill docs mention it.
- Plus needing Notes.app running, interactive Automation permission (so no
  cron/CI/SSH), HTML bodies requiring conversion, macOS-only, and one
  osascript round trip per note.

Also drops the duplicate write-path paragraph, which restated the existing
"Reading is all it does" section.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-09 16:44:04 +08:00
ingjieyeandClaude Opus 5 a8615b3c45 Read Apple Notes directly from its SQLite store
Splits the original single-file exporter into a package and adds a query
layer, so notes can be searched and read live instead of only exported.

Reading the Core Data store rather than driving Notes.app through AppleScript
is what makes full-text search practical. Measured on an 818-note store,
osascript needs ~0.325s per note body (`first note whose id is` is an
unindexed linear scan), so searching everything costs minutes; decoding every
gzipped protobuf body here takes 0.11s total. No cache, so results are always
current.

- store.py   read-only SQLite access, folder tree, attachments
- parser.py  gzip + protobuf decoding and Markdown rendering (moved verbatim)
- search.py  search, recency listing, single-note reads
- export.py  Markdown output and the incremental manifest

Three correctness details found while building this:

- Open with mode=ro, never immutable=1. The immutable flag makes SQLite skip
  the -wal file and silently serve a stale snapshot; the live store routinely
  carries megabytes of un-checkpointed WAL.
- Recently Deleted is ZFOLDERTYPE = 1, and notes in it keep
  ZMARKEDFORDELETION = 0, so the existing filter still surfaced them. Matching
  the folder type is also language-independent. Now excluded by default.
- Creation timestamps live in whichever of ZCREATIONDATE..3 is populated, so
  every candidate column is read.

Export no longer writes a CLAUDE.md into the output directory unless asked
with --write-claude-md.

tests/test_parity.py keeps the original suite byte-for-byte as a regression
check on the split; test_search.py covers the new query layer against a
synthetic store. 48 tests pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-09 16:38:00 +08:00