Claude-code-plugins-plus-skills apple-notes-reference-architecture
install
source · Clone the upstream repo
git clone https://github.com/jeremylongshore/claude-code-plugins-plus-skills
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/jeremylongshore/claude-code-plugins-plus-skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/plugins/saas-packs/apple-notes-pack/skills/apple-notes-reference-architecture" ~/.claude/skills/jeremylongshore-claude-code-plugins-plus-skills-apple-notes-reference-architectu && rm -rf "$T"
manifest:
plugins/saas-packs/apple-notes-pack/skills/apple-notes-reference-architecture/SKILL.mdsource content
Apple Notes Reference Architecture
Overview
Apple Notes automation systems are fundamentally different from cloud SaaS integrations. There is no REST API, no server-side SDK, and no webhook infrastructure. Everything runs locally on macOS through the Apple Events IPC bridge. This reference architecture defines the standard layered approach: a Node.js application layer that calls JXA scripts via
osascript, a local SQLite cache for fast queries, a change detection poller for event-driven workflows, and optional Shortcuts integration for cross-app automation.
System Architecture
┌─────────────────────────────────────────────────────┐ │ macOS Machine │ │ │ │ ┌──────────┐ ┌───────────┐ ┌────────────────┐ │ │ │ Your App │──▶│ osascript │──▶│ Notes.app │ │ │ │ (Node.js)│ │ (JXA) │ │ (local DB) │ │ │ └────┬─────┘ └───────────┘ └───────┬────────┘ │ │ │ │ │ │ ┌────▼─────┐ ┌───────────┐ ┌───────▼────────┐ │ │ │ SQLite │ │ Shortcuts │ │ iCloud Sync │ │ │ │ Cache │ │ Automations│ │ (bird/cloudd) │ │ │ └──────────┘ └───────────┘ └────────────────┘ │ │ │ │ │ │ ┌────▼─────┐ ┌────────▼───────┐ │ │ │ Poller / │ │ Other Apple │ │ │ │ FSEvents │ │ Devices │ │ │ └──────────┘ └────────────────┘ │ └─────────────────────────────────────────────────────┘
Project Structure
apple-notes-automation/ ├── src/ │ ├── notes-client.ts # JXA wrapper class (osascript calls) │ ├── cache.ts # SQLite cache layer │ ├── templates/ # Note templates (HTML fragments) │ ├── export/ # Export to MD/JSON/SQLite/CSV │ ├── events/ # Change detection via polling │ └── server.ts # Optional: local HTTP API for remote access ├── scripts/ │ ├── notes-cli.sh # CLI wrapper for common operations │ ├── health-check.sh # Monitoring and alerting │ ├── export-all.sh # Full backup export │ └── install.sh # launchd deployment installer ├── tests/ │ ├── mocks/ # Mock JXA client for CI (non-macOS) │ └── unit/ # Unit tests (vitest) ├── config/ │ ├── environments.json # Account/folder per environment │ └── launchd.plist # Service definition template └── package.json
Component Design
// src/notes-client.ts — Core abstraction over osascript import { execSync } from "child_process"; export class NotesClient { private account: string; constructor(account = "iCloud") { this.account = account; } private exec(jxa: string): string { return execSync(`osascript -l JavaScript -e '${jxa.replace(/'/g, "'\\''")}'`, { encoding: "utf8", timeout: 30000 }).trim(); } count(): number { return parseInt(this.exec(`Application("Notes").accounts().find(a => a.name() === "${this.account}").notes.length`)); } list(): Array<{ id: string; title: string; modified: string }> { return JSON.parse(this.exec(` JSON.stringify(Application("Notes").accounts().find(a => a.name() === "${this.account}") .notes().map(n => ({id: n.id(), title: n.name(), modified: n.modificationDate().toISOString()}))) `)); } create(title: string, body: string, folder = "Notes"): string { return this.exec(` const Notes = Application("Notes"); const acct = Notes.accounts().find(a => a.name() === "${this.account}"); const f = acct.folders().find(f => f.name() === "${folder}") || acct.folders[0]; const n = Notes.Note({name: "${title}", body: "${body}"}); f.notes.push(n); n.id(); `); } }
Key Constraints
| Constraint | Impact | Workaround |
|---|---|---|
| macOS only | No Linux/Windows servers | Run on Mac; export data for cross-platform consumption |
| No REST API | Cannot access remotely | Optional: expose local HTTP server; lock down to localhost |
| iCloud sync lag | Writes may take 5-30s to appear on other devices | Poll with delay; verify on target device |
| No webhooks | Cannot receive push notifications | Poll for changes every 60s; watch FSEvents on Notes DB |
| HTML-only body | No native Markdown support | Convert HTML to/from Markdown in export/import layer |
| No attachment export via JXA | Binary data inaccessible from scripting | Use Shortcuts for attachment extraction |
Error Handling
| Issue | Cause | Solution |
|---|---|---|
| Architecture requires macOS server | No cloud-native option | Dedicate a Mac mini as automation server; use Tailscale for remote access |
| Local HTTP API exposed to network | Security risk if not locked down | Bind to 127.0.0.1 only; use SSH tunnel for remote access |
| Cache out of sync with Notes | Polling interval too long | Reduce poll interval; use FSEvents on NoteStore.sqlite for faster detection |
| Template HTML rejected by Notes | Invalid HTML tags | Test templates with a canary note before bulk creation |
Resources
Next Steps
For deploying this architecture as a service, see
apple-notes-deploy-integration. For monitoring the running system, see apple-notes-observability.