automating-notes
npx machina-cli add skill SpillwaveSolutions/automating-mac-apps-plugin/automating-notes --openclawAutomating Apple Notes (JXA-first, AppleScript discovery)
Relationship to the macOS automation skill
- Standalone for Notes; reuse
automating-mac-appsfor permissions, shell, and Objective-C/UI scripting patterns. - PyXA Installation: To use PyXA examples in this skill, see the installation instructions in
automating-mac-appsskill (PyXA Installation section).
Core framing
- Notes uses an AEOM hierarchy: Application → Accounts → Folders → Notes (with nested folders).
- Load:
automating-notes/references/notes-basics.mdfor complete specifier reference.
Workflow (default)
- Resolve account/folder explicitly (iCloud vs On My Mac); validate existence and permissions.
- Ensure target path exists (create folders if needed); handle creation failures gracefully.
- Create notes with explicit
nameand HTMLbody; verify creation success. - Query/update with
.whosefilters; batch delete/move as needed; check counts before/after operations. - For checklists/attachments, use clipboard/Objective-C scripting if dictionary support is insufficient; test fallbacks.
- For meeting/people workflows, file notes under
meetings/<company>/<date>-<meeting-title>andpeople/<first>-<last>/...; validate final structure.
Quickstart (ensure path + create)
JXA (Legacy):
const Notes = Application("Notes");
// Ensure folder path exists, creating intermediate folders as needed
function ensurePath(acc, path) {
const parts = path.split("/").filter(Boolean);
let container = acc;
parts.forEach(seg => {
let f; try {
f = container.folders.byName(seg);
f.name(); // Verify access
} catch (e) {
// Folder doesn't exist, create it
f = Notes.Folder({ name: seg });
container.folders.push(f);
}
container = f;
});
return container;
}
try {
// Get iCloud account and ensure meeting folder exists
const acc = Notes.accounts.byName("iCloud");
const folder = ensurePath(acc, "meetings/Acme/2024-07-01-Review");
// Create new note in the folder
folder.notes.push(Notes.Note({
name: "Client Review",
body: "<h1>Client Review</h1><div>Agenda...</div>"
}));
console.log("Note created successfully");
} catch (e) {
console.error("Failed to create note: " + e.message);
}
PyXA (Recommended Modern Approach):
import PyXA
notes = PyXA.Notes()
def ensure_path(account, path):
"""Ensure folder path exists, creating intermediate folders as needed"""
parts = [p for p in path.split("/") if p] # Filter empty parts
container = account
for part in parts:
try:
# Try to find existing folder
folder = container.folders().by_name(part)
folder.name() # Verify access
except:
# Folder doesn't exist, create it
folder = notes.make("folder", {"name": part})
container.folders().push(folder)
container = folder
return container
try:
# Get iCloud account
account = notes.accounts().by_name("iCloud")
# Ensure meeting folder exists
folder = ensure_path(account, "meetings/Acme/2024-07-01-Review")
# Create new note in the folder
note = folder.notes().push({
"name": "Client Review",
"body": "<h1>Client Review</h1><div>Agenda...</div>"
})
print("Note created successfully")
except Exception as e:
print(f"Failed to create note: {e}")
PyObjC with Scripting Bridge:
from ScriptingBridge import SBApplication
notes = SBApplication.applicationWithBundleIdentifier_("com.apple.Notes")
def ensure_path(account, path):
"""Ensure folder path exists, creating intermediate folders as needed"""
parts = [p for p in path.split("/") if p]
container = account
for part in parts:
try:
folder = container.folders().objectWithName_(part)
folder.name() # Verify access
except:
# Create new folder
folder = notes.classForScriptingClass_("folder").alloc().init()
folder.setName_(part)
container.folders().addObject_(folder)
container = folder
return container
try:
# Get iCloud account
accounts = notes.accounts()
account = None
for acc in accounts:
if acc.name() == "iCloud":
account = acc
break
if account:
# Ensure meeting folder exists
folder = ensure_path(account, "meetings/Acme/2024-07-01-Review")
# Create new note
note = notes.classForScriptingClass_("note").alloc().init()
note.setName_("Client Review")
note.setBody_("<h1>Client Review</h1><div>Agenda...</div>")
folder.notes().addObject_(note)
print("Note created successfully")
else:
print("iCloud account not found")
except Exception as e:
print(f"Failed to create note: {e}")
Validation Checklist
- Account access works (iCloud vs On My Mac)
- Folder creation and path resolution succeeds
- Note creation with valid HTML body completes
- Note appears in Notes UI
-
.whosequeries return expected results - Error handling covers missing accounts/folders
HTML & fallbacks
- Allowed tags:
<h1>-<h3>,<b>,<i>,<u>,<ul>/<ol>/<li>,<div>/<p>/<br>,<a>. - Security: Always sanitize HTML input; avoid
<script>,<style>, or event handlers to prevent XSS in rendered notes. - Checklists/attachments: Objective-C/clipboard fallback (Cmd+Shift+L for checklist, paste image via NSPasteboard + System Events).
- Append helper: replace
</body>with extra HTML, or append when missing; validate HTML structure post-modification.
When Not to Use
- Cross-platform note taking (use Notion API, Obsidian, or Markdown files)
- iCloud sync operations requiring status feedback (limited API support)
- Non-macOS platforms
- Rich formatting beyond supported HTML tags
- Collaborative editing workflows (no multi-user support)
What to load
- Basics & specifiers:
automating-notes/references/notes-basics.md - Recipes (create/move/query/ensure path/checklists):
automating-notes/references/notes-recipes.md - Advanced (HTML body rules, attachments/UI, JSON import/export, ObjC bridge):
automating-notes/references/notes-advanced.md - Dictionary/type map:
automating-notes/references/notes-dictionary.md - PyXA API Reference (complete class/method docs):
automating-notes/references/notes-pyxa-api-reference.md
Source
git clone https://github.com/SpillwaveSolutions/automating-mac-apps-plugin/blob/main/plugins/automating-mac-apps-plugin/skills/automating-notes/SKILL.mdView on GitHub Overview
This skill automates Apple Notes using JXA-first scripting to create, organize, and query notes. It models the AEOM hierarchy (Applications → Accounts → Folders → Notes), supports HTML bodies, folder creation, and moving notes, with Objective-C/UI fallbacks for Notes.app on macOS. Use it to programmatically create notes and structure them for meetings, projects, and research.
How This Skill Works
The skill uses JXA (and PyXA as an optional modern approach) to drive the Notes app, resolving an account and folder path, creating or moving notes with explicit names and HTML bodies, and verifying success. When advanced needs arise (checklists or attachments), it falls back to clipboard methods or Objective-C/UI scripting to ensure reliability across macOS versions.
When to Use It
- You need to create notes programmatically from a script or automation.
- You want to automate repetitive Notes tasks like moving, renaming, or batch deleting notes.
- You manage notes within structured accounts/folders (e.g., iCloud vs On My Mac) and need explicit path resolution.
- You require HTML-formatted note bodies and reliable creation/verification.
- You have complex workflows (meetings/people) that benefit from folder hierarchies and fallback scripting.
Quick Start
- Step 1: Resolve account/folder (iCloud vs On My Mac) and ensure the target path exists.
- Step 2: Create a new note in the target folder with an explicit name and an HTML body.
- Step 3: Verify creation and, if needed, run a query/update or perform a batch move/delete with fallbacks.
Best Practices
- Resolve the target account and folder explicitly (iCloud vs On My Mac) before any operation.
- Ensure the entire target path exists; create intermediate folders if needed and handle creation failures gracefully.
- Create notes with explicit name and HTML body; verify creation success after the operation.
- Use .whose style queries for filtering and perform batch delete/move with pre/post checks.
- Provide robust fallbacks for checklists/attachments using clipboard or Objective-C/UI scripting and test across macOS versions.
Example Use Cases
- Create a new note under iCloud in meetings/Acme/2024-07-01-Review with an HTML body for Client Review.
- Use PyXA to implement ensure_path(account, path) that recursively creates folders, then adds a note with a HTML body.
- Query notes with .whose filters to locate and batch-move or delete notes in a specific folder.
- Fallback to Objective-C/UI scripting for checklist items or attachments when dictionary support is insufficient.
- Organize meeting notes under meetings/<company>/<date>-<meeting-title> and people/<first>-<last>/… to mirror calendar workflows.