Skip to content
OpenSmartRoute
Skillv1.0.0

apple-notes-migration-deep-dive

Migrate notes between Apple Notes, Obsidian, Notion, and other platforms. Trigger: "apple notes migration".

by jeremylongshore(0) 0 installs
Free
Sign in to install

Free account. Installing gives you the manifest plus copy-paste snippets.

See reviews

About

Imported from jeremylongshore/tons-of-skills-marketplace (skills/.curated/apple-notes-migration-deep-dive/SKILL.md). Install upstream with npx skills add jeremylongshore/tons-of-skills-marketplace --skill apple-notes-migration-deep-dive. Copyright stays with the author (MIT).

Apple Notes Migration Deep Dive

Overview

Migrating 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.

Prerequisites

  • Written scope, migration owner, encrypted backup, rollback decision, and retention policy for both source and destination.
  • A synthetic pilot corpus that includes formatting and attachment edge cases but contains no production data.
  • A durable manifest keyed by source identifier and a target environment/folder that has been explicitly approved.

Instructions

  1. Run export, conversion, and import as separate, inspectable phases; do not stream unreviewed note bodies into a destination.
  2. Sanitize HTML/Markdown and filenames, encrypt intermediate artifacts, and keep content out of shell arguments and logs.
  3. Pilot a small batch, reconcile source and destination manifests, then obtain owner approval before each larger batch.
  4. Make imports idempotent and stop on a timeout, conflict, or attachment-fidelity gap; do not retry blindly.

Migration Paths

From To Method Attachments
Apple Notes Obsidian JXA export HTML → convert to Markdown → vault Manual via Shortcuts
Apple Notes Notion JXA export JSON → Notion API import Re-upload required
Obsidian Apple Notes Read .md → convert to HTML → JXA create Not supported via JXA
Evernote Apple Notes File > Import from Evernote (built-in) Preserved automatically
OneNote Apple Notes Export to .enex → Import from Evernote Partial preservation

Step 1: Pre-Migration Backup

#!/bin/bash
# Always back up before migration
BACKUP_DIR="$HOME/notes-backup-$(date +%Y%m%d-%H%M)"
mkdir -p "$BACKUP_DIR"
osascript -l JavaScript -e '
  const Notes = Application("Notes");
  const data = Notes.defaultAccount.notes().map(n => ({
    id: n.id(), title: n.name(), body: n.body(),
    folder: n.container().name(),
    created: n.creationDate().toISOString(),
    modified: n.modificationDate().toISOString(),
    attachments: n.attachments().length
  }));
  JSON.stringify(data, null, 2);
' > "$BACKUP_DIR/full-export.json"
echo "Backed up $(jq length "$BACKUP_DIR/full-export.json") notes to $BACKUP_DIR"

Step 2: Apple Notes to Obsidian

#!/bin/bash
VAULT_DIR="$HOME/obsidian-vault/Apple Notes Import"
mkdir -p "$VAULT_DIR"

osascript -l JavaScript -e '
  const Notes = Application("Notes");
  Notes.defaultAccount.notes().map(n => JSON.stringify({
    title: n.name(), body: n.body(),
    folder: n.container().name(),
    created: n.creationDate().toISOString(),
  })).join("\n===NOTESEP===\n");
' | while IFS= read -r line; do
  [ "$line" = "===NOTESEP===" ] && continue
  title=$(echo "$line" | jq -r '.title' 2>/dev/null) || continue
  body=$(echo "$line" | jq -r '.body' 2>/dev/null)
  folder=$(echo "$line" | jq -r '.folder' 2>/dev/null)
  created=$(echo "$line" | jq -r '.created' 2>/dev/null)

  # Convert Apple Notes HTML to Markdown
  md=$(echo "$body" | sed 's/<h1>/# /g; s/<\/h1>//g; s/<h2>/## /g; s/<\/h2>//g' \
    | sed 's/<li class="done">/- [x] /g; s/<li>/- /g; s/<\/li>//g' \
    | sed 's/<br[^>]*>/\n/g; s/<[^>]*>//g' | sed '/^$/N;/^\n$/d')

  safe_title=$(echo "$title" | tr '/:*?"<>|' '-' | head -c 80)
  mkdir -p "$VAULT_DIR/$folder"
  printf "---\ncreated: %s\nsource: apple-notes\n---\n\n%s\n" "$created" "$md" \
    > "$VAULT_DIR/$folder/$safe_title.md"
done
echo "Migration complete: $(find "$VAULT_DIR" -name '*.md' | wc -l) files in $VAULT_DIR"

Step 3: Obsidian to Apple Notes

#!/bin/bash
# Import Markdown files into Apple Notes
VAULT_DIR="${1:-$HOME/obsidian-vault}"
COUNT=0
find "$VAULT_DIR" -name '*.md' -type f | while read -r md_file; do
  title=$(head -20 "$md_file" | grep -m1 '^# ' | sed 's/^# //')
  [ -z "$title" ] && title=$(basename "$md_file" .md)
  # Convert Markdown to Apple Notes HTML
  body=$(cat "$md_file" | sed 's/^# \(.*\)/<h1>\1<\/h1>/; s/^## \(.*\)/<h2>\1<\/h2>/' \
    | sed 's/\*\*\([^*]*\)\*\*/<b>\1<\/b>/g; s/\*\([^*]*\)\*/<i>\1<\/i>/g' \
    | sed 's/$/<br>/g' | tr -d '\n')
  osascript -l JavaScript -e "
    const Notes = Application('Notes');
    const note = Notes.Note({name: '$title', body: '$body'});
    Notes.defaultAccount.folders[0].notes.push(note);
  "
  COUNT=$((COUNT + 1))
  sleep 1  # Throttle for iCloud sync
done
echo "Imported $COUNT notes"

Error Handling

Issue Cause Solution
Notes missing after import iCloud sync delay Wait 5-10 minutes; check on another device
HTML formatting garbled Unsupported HTML tags in source Pre-clean HTML; strip to Apple Notes subset only
Special characters in title Shell escaping issues with JXA Use JSON encoding; pipe through jq
Attachments not migrated JXA cannot write binary attachments Use Shortcuts "Add Attachment to Note" action
Duplicate notes after re-run No dedup in import script Track imported note IDs in a local manifest file

Output

A migration produces a protected source receipt, conversion exception report, destination manifest, reconciliation result, and rollback decision. The routine receipt uses opaque identifiers and counts; note titles, bodies, and attachment names remain in controlled artifacts only.

Examples

Migrate a five-note synthetic pilot into a dedicated test folder, compare every manifest key and expected formatting exception, then delete the pilot destination under the test policy. For a production migration, move in bounded approved batches and pause immediately if the reconciliation count or attachment handling differs from the signed plan.

Resources

Next Steps

For data format details and HTML conversion, see apple-notes-data-handling. For macOS version compatibility during migration, see apple-notes-upgrade-migration.

Use it

Copy one of these into your project. Installing also returns the manifest and these snippets.

yaml
targets:
  - https://api.opensmartroute.ai/api/v1/registry/jeremylongshore-tons-of-skills-marketplace-apple-notes-m-876eb5/manifest   # or paste the manifest below

Manifest

An Open Capability Manifest: the router reads it to know what this does, what it costs and when to pick it.

jeremylongshore-tons-of-skills-marketplace-apple-notes-m-876eb5.ocm.jsonjson
{
  "ocm": "1",
  "id": "jeremylongshore-tons-of-skills-marketplace-apple-notes-m-876eb5",
  "kind": "skill",
  "name": "apple-notes-migration-deep-dive",
  "description": "Migrate notes between Apple Notes, Obsidian, Notion, and other platforms. Trigger: \"apple notes migration\".",
  "publisher": "jeremylongshore",
  "version": "1.0.0",
  "capabilities": {
    "domains": [
      "general"
    ],
    "tags": [
      "skill-md",
      "saas",
      "macos",
      "apple-notes",
      "automation",
      "skills-sh"
    ],
    "languages": [
      "en"
    ]
  },
  "quality_prior": 0.6,
  "examples": [
    "Migrate notes between Apple Notes, Obsidian, Notion, and other platforms. Trigger: \"apple notes migration\"."
  ],
  "primary": false,
  "metadata": {
    "source": {
      "provider": "skills.sh",
      "repository": "https://github.com/jeremylongshore/tons-of-skills-marketplace",
      "path": "skills/.curated/apple-notes-migration-deep-dive/SKILL.md",
      "ref": "HEAD",
      "url": "https://github.com/jeremylongshore/tons-of-skills-marketplace/blob/HEAD/skills/.curated/apple-notes-migration-deep-dive/SKILL.md",
      "key": "jeremylongshore/tons-of-skills-marketplace/skills/.curated/apple-notes-migration-deep-dive/SKILL.md"
    },
    "compatibility": "Designed for Claude Code",
    "allowed_tools": [
      "Read,",
      "Write,",
      "Edit,",
      "Bash(osascript:*),",
      "Grep"
    ],
    "license": "MIT"
  },
  "instructions": "# Apple Notes Migration Deep Dive\n\n## Overview\n\nMigrating 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.\n\n## Prerequisites\n\n- Written scope, migration owner, encryp",
  "cost": {
    "context_tokens": 1642
  }
}

Fetch it by URL: GET /api/v1/registry/jeremylongshore-tons-of-skills-marketplace-apple-notes-m-876eb5/manifest?version=1.0.0

Reviews

Star ratings from people who tried it. One review per account; edit yours any time.

No reviews yet. Install it, try it, and be the first to rate it.