Claude Code skill pack for Apple Notes (24 skills)
Installation
Open Claude Code and run this command:
/plugin install apple-notes-pack@claude-code-plugins-plus
Use --global to install for all projects, or --project for current project only.
Skills (24)
Run Apple Notes automation in CI on macOS runners.
Apple Notes CI Integration
Overview
Apple Notes automation is macOS-only because it depends on the Apple Events subsystem and Notes.app. CI pipelines must use GitHub Actions macOS runners (macos-latest or macos-14). However, macOS CI runners have restricted TCC (Transparency, Consent, and Control) permissions, which means direct Notes.app automation via osascript will fail in CI. The standard pattern is to run unit tests against a mock JXA client in CI, and reserve real Notes.app integration tests for local macOS machines or self-hosted runners with pre-granted automation permissions.
GitHub Actions Workflow
# .github/workflows/notes-ci.yml
name: Notes Automation CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
unit-tests:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: "20", cache: "npm" }
- run: npm ci
- name: Verify macOS version
run: sw_vers
- name: Lint JXA scripts
run: |
# Validate JavaScript syntax in all .jxa files
for f in scripts/*.jxa; do
node --check "$f" 2>/dev/null || echo "WARN: $f is osascript-only"
done
- name: Unit tests (mocked Notes client)
run: npm test
- name: Validate JXA templates
run: |
# Ensure osascript can parse (but not execute) JXA scripts
for f in scripts/*.jxa; do
osascript -l JavaScript -e "$(cat "$f")" 2>&1 | grep -v "Not authorized" || true
done
Mock Client for CI
// tests/mocks/notes-client.mock.ts
export class MockAppleNotesClient {
private notes: Array<{ id: string; title: string; body: string; folder: string }> = [];
createNote(title: string, body: string, folder = "Notes"): string {
const id = `mock-note-${Date.now()}-${Math.random().toString(36).slice(2)}`;
this.notes.push({ id, title, body, folder });
return id;
}
listNotes() { return [...this.notes]; }
getNote(id: string) { return this.notes.find(n => n.id === id) || null; }
searchNotes(q: string) { return this.notes.filter(n => n.title.includes(q) || n.body.includes(q)); }
deleteNote(id: string) { this.notes = this.notes.filter(n => n.id !== id); }
getFolders() { return [...new Set(this.notes.map(n => n.folder))]; }
}
Self-Hosted Runner with TCC Pre-Approval
# On a self-hosted macOS runner, pre-grant automation permissions:
# 1. Open System Settings > Privacy & Security > Automation
# 2. Grant your CI user's terminal access to Notes.app
# 3. Verify with:
osascript -l JavaScript -e 'Application("Notes").defaultAccount.noDiagnose and fix common Apple Notes automation errors.
Apple Notes Common Errors
Overview
Apple Notes automation errors fall into three categories: TCC permission denials from macOS security, AppleEvent communication failures between your script and Notes.app, and iCloud sync issues that cause data inconsistency. Unlike REST APIs that return HTTP status codes, Apple Events use negative OSStatus codes. This guide covers every error you are likely to encounter when automating Notes via JXA or osascript, with tested fixes for each.
Error Reference
| Error | Code | Root Cause | Fix |
|---|---|---|---|
| Not authorized to send Apple events | -1743 | TCC denied automation permission | System Settings > Privacy > Automation > enable your app |
| AppleEvent timed out | -1712 | Notes.app busy, hung, or not running | Application("Notes").activate(); increase timeout with delay |
| Can't get application "Notes" | -2700 | Notes.app not installed or renamed | Verify with mdfind "kMDItemCFBundleIdentifier == com.apple.Notes" |
| Can't get folder | -1728 | Folder name mismatch (case-sensitive) | List folders first: Notes.defaultAccount.folders().map(f => f.name()) |
| Connection is invalid | -609 | Notes.app crashed mid-operation | killall Notes; sleep 2; open -a Notes; sleep 3 |
| User canceled | -128 | Security dialog dismissed or timed out | Re-run and click Allow; or pre-grant via MDM profile |
| Can't make Note | -10000 | Invalid HTML in note body | Validate HTML; strip unsupported tags before creating |
| Application isn't running | -600 | App quit between calls | Wrap in retry with Application("Notes").activate() first |
Diagnostic Script
#!/bin/bash
echo "=== Apple Notes Diagnostics ==="
echo -n "macOS version: "; sw_vers -productVersion
echo -n "Notes.app running: "; pgrep -x Notes > /dev/null && echo "Yes" || echo "No"
echo -n "Notes.app path: "; mdfind "kMDItemCFBundleIdentifier == com.apple.Notes" 2>/dev/null | head -1
echo -n "Note count: "
osascript -l JavaScript -e 'Application("Notes").defaultAccount.notes.length' 2>/dev/null || echo "ERROR — check TCC"
echo -n "Folder count: "
osascript -l JavaScript -e 'Application("Notes").defaultAccount.folders.length' 2>/dev/null || echo "ERROR"
echo -n "Accounts: "
osascript -l JavaScript -e 'Application("Notes").accounts().map(a => a.name()).join(", &Build automated note management workflows with Apple Notes JXA scripts.
Apple Notes Core Workflow A — Note Management Automation
Overview
Primary workflow: automate Apple Notes management with batch creation, template-based note generation, folder organization, and content sync from external sources (Markdown files, RSS, calendar events).
Instructions
Step 1: Batch Note Creator from Markdown Files
#!/bin/bash
# scripts/markdown-to-notes.sh — Import Markdown files as Apple Notes
FOLDER_NAME="${1:-Imported}"
for md_file in *.md; do
[ -f "$md_file" ] || continue
title=$(head -1 "$md_file" | sed 's/^#\s*//')
# Convert Markdown to basic HTML
body=$(cat "$md_file" | sed 's/^# /<h1>/;s/$/<\/h1>/' | sed 's/^## /<h2>/;s/$/<\/h2>/' | sed 's/^- /<li>/;s/$/<\/li>/' | sed 's/^$/<br>/')
osascript -l JavaScript -e "
const Notes = Application('Notes');
const account = Notes.defaultAccount;
let folder = account.folders().find(f => f.name() === '$FOLDER_NAME');
if (!folder) {
folder = Notes.Folder({ name: '$FOLDER_NAME' });
account.folders.push(folder);
}
const note = Notes.Note({ name: '$title', body: \`$body\` });
folder.notes.push(note);
'Created: $title';
"
echo "Imported: $md_file → $title"
done
Step 2: Note Template Engine (JXA)
// scripts/note-template.js — Run with: osascript -l JavaScript scripts/note-template.js
const Notes = Application('Notes');
const TEMPLATES = {
meeting: (data) => `
<h1>${data.title || 'Meeting Notes'}</h1>
<p><strong>Date:</strong> ${new Date().toLocaleDateString()}</p>
<p><strong>Attendees:</strong> ${data.attendees || 'TBD'}</p>
<h2>Agenda</h2><ul><li></li></ul>
<h2>Action Items</h2><ul><li></li></ul>
<h2>Notes</h2><p></p>
`,
daily: (data) => `
<h1>Daily Log — ${new Date().toLocaleDateString()}</h1>
<h2>Tasks</h2><ul><li></li></ul>
<h2>Accomplishments</h2><ul><li></li></ul>
<h2>Blockers</h2><ul><li></li></ul>
`,
project: (data) => `
<h1>${data.title || 'Project'}</h1>
<p><strong>Status:</strong> ${data.status || 'Active'}</p>
<h2>Overview</h2><p></p>
<h2>Requirements</h2><ul><li></li></ul>
<h2>Timeline</h2><ul><li></li></ul>
`,
};
function createFromTemplate(templateName, data, folderName) {
const template = Export and convert Apple Notes to Markdown, JSON, HTML, and SQLite.
Apple Notes Core Workflow B — Export & Conversion
Overview
Export Apple Notes to portable formats: Markdown, JSON, HTML files, and SQLite databases. Apple Notes stores content as HTML internally — these workflows convert it to developer-friendly formats.
Instructions
Step 1: Export All Notes to JSON
osascript -l JavaScript -e '
const Notes = Application("Notes");
const allNotes = Notes.defaultAccount.notes();
const exported = allNotes.map(n => ({
id: n.id(),
title: n.name(),
body: n.body(),
folder: n.container().name(),
created: n.creationDate().toISOString(),
modified: n.modificationDate().toISOString(),
}));
JSON.stringify(exported, null, 2);
' > apple-notes-export.json
echo "Exported $(jq length apple-notes-export.json) notes to apple-notes-export.json"
Step 2: Export Notes as Markdown Files
#!/bin/bash
# scripts/notes-to-markdown.sh
OUTPUT_DIR="${1:-./notes-export}"
mkdir -p "$OUTPUT_DIR"
osascript -l JavaScript -e '
const Notes = Application("Notes");
const notes = Notes.defaultAccount.notes();
notes.map(n => JSON.stringify({
title: n.name(),
body: n.body(),
folder: n.container().name(),
})).join("\n---SEPARATOR---\n");
' | while IFS= read -r line; do
if [ "$line" = "---SEPARATOR---" ]; then continue; fi
title=$(echo "$line" | jq -r '.title' 2>/dev/null)
body=$(echo "$line" | jq -r '.body' 2>/dev/null)
folder=$(echo "$line" | jq -r '.folder' 2>/dev/null)
# Convert HTML to basic Markdown
md_body=$(echo "$body" | sed 's/<h1>/# /g; s/<\/h1>//g; s/<h2>/## /g; s/<\/h2>//g; s/<p>//g; s/<\/p>/\n/g; s/<br>/\n/g; s/<li>/- /g; s/<\/li>//g; s/<[^>]*>//g')
safe_title=$(echo "$title" | tr '/:' '-' | head -c 100)
mkdir -p "$OUTPUT_DIR/$folder"
echo -e "# $title\n\n$md_body" > "$OUTPUT_DIR/$folder/$safe_title.md"
done
echo "Export complete: $OUTPUT_DIR"
Step 3: Export to SQLite Database
# Using apple-notes-to-sqlite (pip install apple-notes-to-sqlite)
pip install apple-notes-to-sqlite
apple-notes-to-sqlite export notes.db
# Or build your own with JXA + sqlite3
osascript -l JavaScript -e '
const Notes = Application("Notes");
const notes = Notes.defaultAccount.notes();
const rows = notes.map(n =>
`INSERT INTO notes (title, body, folder, created) VALUES (${JSON.stringify(n.name())}, ${JSON.stringify(n.body())}, ${JSON.stringify(n.container().name())}, ${JSON.stringify(n.creationDate().toISOString())});`
).join("\n");
rows;
' > /tmp/notes-inserts.sql
Apple Notes cost optimization — it is free, focus on iCloud storage management.
Apple Notes Cost Tuning
Overview
Apple Notes itself is free with every Apple ID. The only real cost is iCloud storage, which is shared across Photos, iCloud Drive, Mail, Notes, and device backups. For automation workflows, the main cost drivers are large embedded attachments (images, PDFs, scans) that inflate iCloud usage, and the "On My Mac" account that uses local disk instead. Understanding what consumes storage lets you keep notes within the free 5 GB tier or choose the right iCloud+ plan for your organization.
iCloud Storage Tiers
| Plan | Storage | Price/mo | Approx Notes Capacity |
|---|---|---|---|
| Free | 5 GB | $0 | ~50,000 text-only notes |
| iCloud+ 50 GB | 50 GB | $0.99 | Unlimited text; moderate attachments |
| iCloud+ 200 GB | 200 GB | $2.99 | Shared with Family Sharing |
| iCloud+ 2 TB | 2 TB | $9.99 | Enterprise/heavy media |
| iCloud+ 6 TB | 6 TB | $29.99 | Large teams with shared albums + notes |
| iCloud+ 12 TB | 12 TB | $59.99 | Maximum tier |
Storage Audit Script
#!/bin/bash
# Audit Apple Notes storage consumption
echo "=== iCloud Storage Overview ==="
# Total iCloud usage (approximate from system)
df -h ~/Library/Mobile\ Documents/ 2>/dev/null | tail -1
echo ""
echo "=== Notes Content Audit ==="
osascript -l JavaScript -e '
const Notes = Application("Notes");
const accounts = Notes.accounts();
let report = [];
accounts.forEach(a => {
const notes = a.notes();
let totalChars = 0;
let withAttachments = 0;
notes.forEach(n => {
totalChars += n.body().length;
if (n.attachments().length > 0) withAttachments++;
});
report.push(a.name() + ": " + notes.length + " notes, ~" +
Math.round(totalChars / 1024) + "KB text, " +
withAttachments + " with attachments");
});
report.join("\n");
'
Optimization Strategies
Move Large Notes to "On My Mac"
// Move attachment-heavy notes to local account (no iCloud cost)
const Notes = Application("Notes");
const localAccount = Notes.accounts().find(a => a.name() === "On My Mac");
const icloud = Notes.defaultAccount;
// Find notes with large bodies (likely have embedded images)
const largeNotes = icloud.notes().filter(n => n.body().length > 100000);
largeNotes.forEach(n => {
console.log(`Large note: ${n.name()} (${Math.round(n.body().length / 1024)}KB)`);
});
// Manual move: drag notes to "On My Mac" in Notes.app sidebar
Archive Old Notes to Markdown
Handle Apple Notes data formats: HTML body, attachments, and rich content.
Apple Notes Data Handling
Overview
Apple Notes stores note content as a restricted subset of HTML internally. The body() property in JXA returns this HTML, which includes -, , , , , and Apple-specific classes for checklists and tables. Attachments (images, PDFs, sketches, scans) are embedded as or object references but cannot be directly extracted via JXA — they require the attachments() property. Understanding these data formats is essential for building reliable import, export, and backup pipelines.
Note Body HTML Format
<!-- Apple Notes uses a subset of HTML wrapped in <div> blocks -->
<div><h1>Title</h1></div>
<div><br></div>
<div>Paragraph text here.</div>
<div><b>Bold text</b> and <i>italic text</i></div>
<div><br></div>
<div><ul><li>List item 1</li><li>List item 2</li></ul></div>
<!-- Checklists use Apple's custom class -->
<div><ul class="com-apple-note-checklist">
<li class="done">Completed item</li>
<li>Incomplete item</li>
</ul></div>
<!-- Tables (macOS Ventura+) use standard HTML tables -->
<div><table><tr><td>Cell 1</td><td>Cell 2</td></tr></table></div>
<!-- Tags (macOS Sonoma+) are stored as hashtags in body text -->
<div>#project #important</div>
Export All Notes to JSON
#!/bin/bash
# Full export with metadata — useful for backups and migration
osascript -l JavaScript -e '
const Notes = Application("Notes");
const results = Notes.defaultAccount.notes().map(n => ({
id: n.id(),
title: n.name(),
body: n.body(),
plaintext: n.plaintext(),
folder: n.container().name(),
created: n.creationDate().toISOString(),
modified: n.modificationDate().toISOString(),
attachmentCount: n.attachments().length,
}));
JSON.stringify(results, null, 2);
' > "$HOME/notes-export-$(date +%Y%m%d).json"
HTML to Markdown Converter
// src/data/html-to-markdown.ts
function notesHtmlToMarkdown(html: string): string {
return html
.replace(/<h1>(.*?)<\/h1>/g, "# $1")
.replace(/<h2>(.*?)<\/h2>/g, "## $1")
.replace(/<h3>(.*?)<\/h3>/g, "### $1")
.replace(/<b>(.*?)<\/b>/g, "**$1**")
.replace(/<strong>(.*?)<\/strong>/g, "**$1**")
.replace(/<i>(.*?)<\/i>/g, "*$1*")
.replace(/<e
Collect Apple Notes automation debug evidence for troubleshooting.
Apple Notes Debug Bundle
Overview
This debug bundle collects diagnostic information from Apple Notes automation integrations
for troubleshooting AppleScript and JXA (JavaScript for Automation) workflows. It captures
macOS version compatibility, Notes.app account configuration, folder and note counts,
TCC (Transparency, Consent, and Control) permission status, and Shortcuts automation
entitlements. The resulting tarball helps diagnose permission denials, sandbox restrictions,
iCloud sync failures, and scripting bridge errors that commonly block Notes automation.
Prerequisites
- macOS 12+ with Notes.app configured
osascript,taravailable (built into macOS)- Terminal granted Automation permission for Notes.app in System Preferences > Privacy & Security
Debug Collection Script
#!/bin/bash
set -euo pipefail
BUNDLE="debug-apple-notes-$(date +%Y%m%d-%H%M%S)"
mkdir -p "$BUNDLE"
# Environment check
echo "=== Environment ===" > "$BUNDLE/environment.txt"
echo "macOS: $(sw_vers -productVersion 2>/dev/null || echo 'not macOS')" >> "$BUNDLE/environment.txt"
echo "Notes.app running: $(pgrep -x Notes > /dev/null && echo Yes || echo No)" >> "$BUNDLE/environment.txt"
echo "Shell: $SHELL ($TERM)" >> "$BUNDLE/environment.txt"
echo "Timestamp: $(date -u)" >> "$BUNDLE/environment.txt"
# Automation permissions (TCC database)
echo "=== TCC Permissions ===" > "$BUNDLE/tcc-status.txt"
sqlite3 ~/Library/Application\ Support/com.apple.TCC/TCC.db \
"SELECT client, auth_value, auth_reason FROM access WHERE service='kTCCServiceAppleEvents'" \
>> "$BUNDLE/tcc-status.txt" 2>/dev/null || echo "Cannot read TCC database (SIP may block)" >> "$BUNDLE/tcc-status.txt"
# Account enumeration via JXA
echo "=== Accounts ===" > "$BUNDLE/accounts.txt"
osascript -l JavaScript -e '
const app = Application("Notes");
const accts = app.accounts();
accts.forEach(a => {
const notes = a.notes().length;
const folders = a.folders().length;
ObjC.import("stdlib"); // ensure stdio
$.system(`echo "${a.name()}: ${notes} notes, ${folders} folders" >> /dev/stdout`);
});
' >> "$BUNDLE/accounts.txt" 2>&1 || echo "JXA account query failed" >> "$BUNDLE/accounts.txt"
# Note count and folder structure
echo "=== Folder Structure ===" > "$BUNDLE/folders.txt"
osascript -l JavaScript -e '
const app = Application("Notes");
app.defaultAccount.folders().forEach(f => {
$.system(`echo " ${f.name()}: ${f.noDeploy Apple Notes automation as a local macOS service.
Apple Notes Deploy Integration
Overview
Apple Notes automation runs exclusively on macOS — there is no cloud deployment path because Notes.app depends on the local Apple Events subsystem and TCC permissions. Deployment means packaging your JXA/osascript automation as a persistent local service. The three deployment models are: launchd agents for scheduled/recurring tasks, Automator workflows for user-triggered actions, and Apple Shortcuts for cross-app automation. Each has different permission requirements and lifecycle management.
launchd Agent (Recommended for Background Tasks)
<!-- ~/Library/LaunchAgents/com.yourorg.notes-automation.plist -->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.yourorg.notes-automation</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/node</string>
<string>/Users/you/scripts/notes-sync.js</string>
</array>
<key>StartInterval</key>
<integer>3600</integer>
<key>StandardOutPath</key>
<string>/tmp/notes-automation.log</string>
<key>StandardErrorPath</key>
<string>/tmp/notes-automation-error.log</string>
<key>EnvironmentVariables</key>
<dict>
<key>PATH</key>
<string>/usr/local/bin:/usr/bin:/bin</string>
</dict>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>
# Deploy and manage the launchd agent
cp com.yourorg.notes-automation.plist ~/Library/LaunchAgents/
launchctl load ~/Library/LaunchAgents/com.yourorg.notes-automation.plist
launchctl list | grep notes-automation
# View logs
tail -f /tmp/notes-automation.log
# Unload for updates
launchctl unload ~/Library/LaunchAgents/com.yourorg.notes-automation.plist
Shortcuts Deployment (User-Triggered)
# Create a Shortcut that runs your JXA script
# 1. Open Shortcuts.app > New Shortcut
# 2. Add "Run Shell Script" action with:
osascript -l JavaScript /Users/you/scripts/notes-export.js
# Trigger shortcuts from CLI or other automations:
shortcuts run "Export Notes to Markdown"
shortcuts run "Daily Note Creator" --input-type text --input "$(date +%Y-%m-%d)"
# List available shortcuts
shortcuts list | grep -i note
Installer Script
#!/bin/bash
# scripts/install.sh — Deploy Notes automation to a macOS machine
set -euo pipefail
INSTALL_DIR="$HOME/.notes-automation"
PLISImplement access control for multi-user Apple Notes automation.
Apple Notes Enterprise RBAC
Overview
Apple Notes has no built-in role-based access control (RBAC). In enterprise environments with Managed Apple IDs via Apple Business Manager, administrators control Notes access through MDM (Mobile Device Management) profiles. For multi-user automation scenarios, implement access control at the automation layer using account separation, folder-based permissions, and shared folder restrictions. iCloud Shared Notes (macOS Ventura+) provide basic collaboration, but fine-grained permissions (read-only vs edit) must be enforced in your wrapper code.
Account-Based Access Control
// Apple Notes supports multiple accounts (iCloud, Gmail, On My Mac)
// Use account separation as the primary access boundary
const Notes = Application("Notes");
function getAccountByName(name) {
const account = Notes.accounts().find(a => a.name() === name);
if (!account) throw new Error(`Account not found: ${name}`);
return account;
}
// Audit all accounts and their folder structures
function auditAccounts() {
return Notes.accounts().map(a => ({
name: a.name(),
folders: a.folders().map(f => f.name()),
noteCount: a.notes().length,
}));
}
// Restrict automation to a specific account only
const ALLOWED_ACCOUNT = "iCloud";
function safeGetNotes() {
const account = getAccountByName(ALLOWED_ACCOUNT);
return account.notes();
}
Folder-Based Permission Model
// src/rbac/permissions.ts
interface FolderPermission {
folder: string;
allowedRoles: string[];
operations: ("read" | "write" | "delete")[];
}
const FOLDER_PERMISSIONS: FolderPermission[] = [
{ folder: "Public", allowedRoles: ["viewer", "editor", "admin"], operations: ["read"] },
{ folder: "Team", allowedRoles: ["editor", "admin"], operations: ["read", "write"] },
{ folder: "Sensitive", allowedRoles: ["admin"], operations: ["read", "write", "delete"] },
];
function checkPermission(role: string, folder: string, op: "read" | "write" | "delete"): boolean {
const perm = FOLDER_PERMISSIONS.find(p => p.folder === folder);
if (!perm) return false;
return perm.allowedRoles.includes(role) && perm.operations.includes(op);
}
MDM-Based Enforcement
# Apple Business Manager + MDM profiles can:
# 1. Disable Notes.app entirely on managed devices
# 2. Restrict iCloud Notes sync (force "On My Mac" only)
# 3. Enforce Managed Apple IDs (separate from personal)
# Check if device is MDM-managed
profiles status -type enrollment 2>/dev/null
# Check Notes restrictions via MDM profile
profiles list -verCreate, read, and list Apple Notes using JXA and AppleScript.
Apple Notes Hello World
Overview
Create, read, search, and delete Apple Notes using JXA (JavaScript for Automation) via osascript. All examples work from the command line on macOS.
Prerequisites
- Completed
apple-notes-install-auth(permissions granted) - macOS with Notes.app
Instructions
Step 1: Create a Note
# JXA: Create a note in the default folder
osascript -l JavaScript -e '
const Notes = Application("Notes");
const defaultFolder = Notes.defaultAccount.folders[0];
const newNote = Notes.Note({
name: "Hello from Automation",
body: "<h1>Hello World</h1><p>This note was created via JXA at " + new Date().toISOString() + "</p>"
});
defaultFolder.notes.push(newNote);
newNote.id();
'
# AppleScript equivalent:
osascript -e '
tell application "Notes"
tell account "iCloud"
make new note at folder "Notes" with properties {name:"Hello AppleScript", body:"<p>Created via AppleScript</p>"}
end tell
end tell
'
Step 2: List All Notes
# List notes with title and creation date
osascript -l JavaScript -e '
const Notes = Application("Notes");
const notes = Notes.defaultAccount.notes();
notes.slice(0, 10).map(n =>
`${n.name()} | Created: ${n.creationDate().toISOString().split("T")[0]}`
).join("\n");
'
Step 3: Read a Note's Content
# Read note body (returns HTML)
osascript -l JavaScript -e '
const Notes = Application("Notes");
const notes = Notes.defaultAccount.notes();
const target = notes.find(n => n.name() === "Hello from Automation");
if (target) {
`Title: ${target.name()}\nBody: ${target.body()}\nModified: ${target.modificationDate()}`;
} else {
"Note not found";
}
'
Step 4: Search Notes
# Search by keyword in note name
osascript -l JavaScript -e '
const Notes = Application("Notes");
const query = "Hello";
const results = Notes.defaultAccount.notes().filter(n =>
n.name().toLowerCase().includes(query.toLowerCase())
);
results.map(n => n.name()).join("\n") || "No results";
'
Step 5: Create Note in Specific Folder
# Create a folder and add a note to it
osascript -l JavaScript -e '
const Notes = Application("Notes");
const account = Notes.defaultAccount;
// Create folder if it does not exist
let folder = account.folders().find(f => f.name() === "Automation");
if (!folder) {
folder = Notes.Folder({ name: "Automation" });
Incident response runbook for Apple Notes automation failures.
Apple Notes Incident Runbook
Overview
This runbook covers the most common Apple Notes automation failures and their resolution procedures. Unlike cloud SaaS incidents that involve API endpoints and status pages, Apple Notes incidents are local to the macOS machine: app crashes, TCC permission revocations, iCloud sync failures, and database corruption. Each incident section follows a detect-diagnose-fix-verify structure. Keep this runbook accessible on any machine running Notes automation.
Severity Levels
| Severity | Description | Example | Response Time |
|---|---|---|---|
| P1 | All automation blocked | TCC permissions revoked, Notes.app won't launch | Immediate |
| P2 | Data inconsistency | iCloud sync stuck, notes missing | Within 1 hour |
| P3 | Degraded performance | Slow operations, intermittent timeouts | Within 4 hours |
| P4 | Cosmetic/minor | Log warnings, non-critical script errors | Next business day |
Incident 1: Notes.app Crash During Automation
# DETECT: Check if Notes is running
pgrep -x Notes > /dev/null && echo "Notes: running" || echo "Notes: NOT RUNNING"
# DIAGNOSE: Check crash logs
ls -lt ~/Library/Logs/DiagnosticReports/Notes* 2>/dev/null | head -3
# FIX: Restart Notes with stabilization delay
killall Notes 2>/dev/null
sleep 3
open -a Notes
sleep 5 # Wait for full launch and iCloud handshake
# VERIFY: Confirm access is restored
osascript -l JavaScript -e 'Application("Notes").defaultAccount.notes.length'
Incident 2: iCloud Sync Stuck
# DETECT: Compare note count with expected (from last known good)
CURRENT=$(osascript -l JavaScript -e 'Application("Notes").defaultAccount.notes.length' 2>/dev/null)
echo "Current note count: ${CURRENT:-ERROR}"
# DIAGNOSE: Check iCloud daemons
ps aux | grep -E "(bird|cloudd|nsurlsessiond)" | grep -v grep
# Check sync status
brctl status com.apple.Notes 2>/dev/null || echo "brctl unavailable"
log show --predicate 'subsystem == "com.apple.notes"' --last 5m 2>/dev/null | tail -20
# FIX: Restart iCloud sync daemons
killall bird 2>/dev/null; killall cloudd 2>/dev/null
sleep 10 # Allow daemons to restart and reconnect
# VERIFY: Check note count is increasing / stable
sleep 30
NEW_COUNT=$(osascript -l JavaScript -e 'Application("Notes").defaultAccount.notes.length' 2>/dev/null)
echo "Note count after sync restart: ${NEW_COUNT:-ERROR}"
Incident 3: TCC Permissions Revoked
# DETECT: Test Apple Events access
osascript -l JavaScript -e 'ApplicatioSet up macOS automation access for Apple Notes via AppleScript, JXA, and Shortcuts.
Apple Notes Install & Auth
Overview
Apple Notes has no REST API. Automation uses macOS scripting technologies: AppleScript, JavaScript for Automation (JXA), Shortcuts, and the osascript command-line tool. No SDK to install — but you need macOS accessibility permissions.
Prerequisites
- macOS 13+ (Ventura or later recommended)
- Terminal app or iTerm2
- System Preferences > Privacy & Security > Automation permissions
Instructions
Step 1: Grant Automation Permissions
# macOS requires explicit permission for scripts to control Notes.app
# The first time you run an osascript command targeting Notes, macOS will prompt.
# You can also pre-grant in: System Preferences > Privacy & Security > Automation
# Test basic Notes access (will trigger permission prompt)
osascript -e 'tell application "Notes" to get name of every note in default account'
Step 2: Verify JXA (JavaScript for Automation) Access
# JXA is the modern alternative to AppleScript
# Run JavaScript via osascript with -l JavaScript flag
osascript -l JavaScript -e '
const Notes = Application("Notes");
Notes.includeStandardAdditions = true;
const noteCount = Notes.defaultAccount.notes.length;
`Apple Notes accessible: ${noteCount} notes found`;
'
Step 3: Create a Wrapper Script
#!/bin/bash
# scripts/notes-cli.sh — Wrapper for common Apple Notes operations
case "$1" in
count)
osascript -l JavaScript -e '
const Notes = Application("Notes");
Notes.defaultAccount.notes.length;
'
;;
list)
osascript -l JavaScript -e '
const Notes = Application("Notes");
const notes = Notes.defaultAccount.notes();
notes.slice(0, 20).map(n => `${n.id()} | ${n.name()}`).join("\n");
'
;;
folders)
osascript -l JavaScript -e '
const Notes = Application("Notes");
Notes.defaultAccount.folders().map(f => f.name()).join("\n");
'
;;
*)
echo "Usage: notes-cli.sh {count|list|folders}"
;;
esac
Step 4: Verify Shortcuts Integration
# Apple Shortcuts can also interact with Notes
# Check available shortcuts
shortcuts list | grep -i note
# Run a shortcut that creates a note
shortcuts run "Create Note" --input-path /dev/stdin <<< "Test content"
Automation Technologies
| Technology | Language | Best For | Docs | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| AppleScript | AppleScript | Simple operations | Apple Scripting Guide | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| JXA | JavaScript | Complex logic, JSON handling | A
Set up local development workflow for Apple Notes automation with JXA hot reload.
ReadWriteEditBash(osascript:*)Grep
Apple Notes Local Dev LoopOverviewIterative development workflow for Apple Notes JXA scripts with file watching and test helpers. InstructionsStep 1: Project Setup
Step 2: JXA Runner with Hot Reload
Step 3: Test Helper
Step 4: Dev Scripts
Output
ResourcesMigrate notes between Apple Notes, Obsidian, Notion, and other platforms.
ReadWriteEditBash(osascript:*)Grep
Apple Notes Migration Deep DiveOverviewMigrating to or from Apple Notes requires understanding that Notes stores content as proprietary HTML with no REST API for bulk operations. All automation goes through JXA/osascript on a local Mac. This guide covers the four most common migration paths with production-tested scripts. Key challenges include: HTML-to-Markdown conversion fidelity, attachment extraction limitations (JXA cannot export binary attachment data directly), and iCloud sync delays that affect timing of bulk imports. Migration Paths
Step 1: Pre-Migration Backup
Step 2: Apple Notes to ObsidianConfigure Apple Notes automation for multiple accounts and environments.
ReadWriteEditBash(osascript:*)Grep
Apple Notes Multi-Environment SetupOverviewApple Notes supports multiple accounts simultaneously: iCloud (default), Gmail/Yahoo/AOL via IMAP, Exchange, and the local "On My Mac" account. Each account has isolated folders and notes, making accounts the natural boundary for environment separation. Use this to separate personal vs work notes, production vs development data, or synced vs local-only content. The "On My Mac" account is especially useful for development and testing because it never syncs to iCloud, so experiments stay local. Account Discovery
Environment-Based Configuration
Account-Scoped OperationsMonitor Apple Notes automation health and performance metrics.
ReadWriteEditBash(osascript:*)Grep
Apple Notes ObservabilityOverviewApple Notes has no built-in metrics API or health endpoint. Observability must be built from the outside: polling note counts and folder states via JXA, monitoring iCloud sync daemon health, tracking osascript response latency, and watching system logs for Notes-related errors. This guide sets up a lightweight monitoring stack using bash scripts, structured JSON logs, and macOS notifications for alerting. For persistent monitoring, deploy the health check as a launchd agent that runs on a schedule. Health Check Script
Metrics Dashboard (CLI)Optimize Apple Notes automation performance for large note collections.
ReadWriteEditBash(osascript:*)Grep
Apple Notes Performance TuningOverviewApple Notes automation performance degrades linearly with note count because JXA loads all note objects into memory when you access a collection. A vault with 10,000+ notes can take 30+ seconds for a simple list operation. The primary bottleneck is the Apple Events bridge between your script and Notes.app — every property access (name, body, date) is a separate IPC call. This guide covers caching strategies, incremental sync, batch optimization, and architectural patterns to keep automation responsive at scale. Performance Benchmarks
Strategy 1: Minimize Property Access
Strategy 2: Local SQLite CacheProduction checklist for Apple Notes automation deployments.
ReadWriteEditBash(osascript:*)Grep
Apple Notes Production ChecklistOverviewBefore deploying Apple Notes automation to a production macOS machine, validate every dependency: TCC permissions, iCloud sync health, Notes.app availability, error handling robustness, and data security. Unlike cloud services where deployment is a push, Apple Notes automation requires physical or remote access to a Mac with a logged-in user session. This checklist ensures nothing is missed before going live. Pre-Deployment ChecklistPermissions and Access
Application Configuration
Data and Sync
Reliability
Compatibility
Validation ScriptHandle Apple Notes automation rate limits and iCloud sync throttling.
ReadWriteEditBash(osascript:*)Grep
Apple Notes Rate LimitsOverviewApple Notes has no formal API rate limits like cloud services do. However, there are practical throughput limits imposed by three systems: the Apple Events IPC bridge (osascript to Notes.app), the iCloud sync daemon ( Practical Rate Limits
Throttled Operation Queue
<Reference architecture for Apple Notes automation systems.
ReadWriteEditBash(osascript:*)Grep
Apple Notes Reference ArchitectureOverviewApple 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 System Architecture
Project Structure
Component DesignApply production-ready patterns for Apple Notes JXA/AppleScript automation.
ReadWriteEditBash(osascript:*)Grep
Apple Notes SDK PatternsOverviewProduction patterns for Apple Notes automation: JXA wrapper class, error handling, batch operations, and cross-account support. InstructionsStep 1: JXA Client Wrapper (Node.js)
Step 2: Batch Operations with ThrottlingApply security best practices for Apple Notes automation scripts.
ReadWriteEditBash(osascript:*)Grep
Apple Notes Security BasicsOverviewApple Notes security involves three layers: macOS TCC (Transparency, Consent, and Control) which gates which apps can send Apple Events to Notes.app, the macOS sandbox that prevents direct database access, and iCloud encryption that protects notes in transit and at rest. For automation scripts, the primary security concerns are: preventing unauthorized Apple Events access, securing exported note data, avoiding credential leakage in scripts, and understanding the difference between standard and end-to-end encrypted (locked) notes. Security Checklist
TCC Permission Management
Safe Data Export Pattern
Locked Notes and EncryptionMigrate Apple Notes automation scripts between macOS versions.
ReadWriteEditBash(osascript:*)Grep
Apple Notes Upgrade & MigrationOverviewEach macOS major release can change Apple Notes capabilities, JXA API behavior, and the underlying NoteStore database schema. Automation scripts that work on Ventura may fail on Sonoma due to new properties, changed Apple Events handling, or TCC permission resets. This guide covers version-specific changes, pre-upgrade backup procedures, post-upgrade validation, and a compatibility matrix for JXA features across macOS versions. macOS Version Compatibility Matrix
Pre-Upgrade Backup
Post-Upgrade ValidationMonitor Apple Notes changes using file system events and Shortcuts triggers.
ReadWriteEditBash(osascript:*)Grep
Apple Notes Webhooks & EventsOverviewApple Notes has no webhook, pub/sub, or event streaming API. To detect changes, you must build your own event system using one of three approaches: (1) JXA polling that compares note snapshots at intervals, (2) file system events (FSEvents) on the NoteStore.sqlite database file for near-real-time change detection, or (3) Apple Shortcuts automations that trigger scripts when specific conditions are met. Each approach has different latency, reliability, and resource consumption tradeoffs. Approach 1: JXA Polling (Recommended)
Approach 2: FSEvents on Notes Database
Approach 3: Shortcuts AutomReady to use apple-notes-pack?Related Plugins000-jeremy-content-consistency-validatorRead-only validator that generates comprehensive discrepancy reports comparing messaging consistency across ANY HTML-based website (WordPress, Hugo, Next.js, React, Vue, static HTML, etc.), GitHub repositories, and local documentation. Detects mixed messaging without making changes. 002-jeremy-yaml-master-agentIntelligent YAML validation, generation, and transformation agent with schema inference, linting, and format conversion capabilities 003-jeremy-vertex-ai-media-masterComprehensive Google Vertex AI multimodal mastery for Jeremy - video processing (6+ hours), audio generation, image creation with Gemini 2.0/2.5 and Imagen 4. Marketing campaign automation, content generation, and media asset production. 004-jeremy-google-cloud-agent-sdkGoogle Cloud Agent Development Kit (ADK) and Agent Starter Pack mastery - build containerized multi-agent systems with production-ready templates, deploy to Cloud Run/GKE/Agent Engine, RAG agents, ReAct agents, and multi-agent orchestration. agent-context-managerAutomatically detects and loads AGENTS.md files to provide agent-specific instructions ai-commit-genAI-powered commit message generator - analyzes your git diff and creates conventional commit messages instantly
Tags
apple-notesapple notessaassdkintegration
|