
Automating Voice Memos
- 13 installs
- 39 repo stars
- Updated January 14, 2026
- spillwavesolutions/automating-mac-apps-plugin
Helps with ai & agent building tasks during AI-assisted development.
About
automating-voice-memos is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted coding.
- automating-voice-memos
- AI & Agent Building
- AI-coding skill
Automating Voice Memos by the numbers
- 13 all-time installs (skills.sh)
- Ranked #11,399 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/spillwavesolutions/automating-mac-apps-plugin --skill automating-voice-memosAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 13 |
|---|---|
| repo stars | ★ 39 |
| Last updated | January 14, 2026 |
| Repository | spillwavesolutions/automating-mac-apps-plugin ↗ |
What it does
Helps with ai & agent building tasks during AI-assisted development.
Files
Automating Voice Memos (no dictionary, data+UI hybrid)
Relationship to the macOS automation skill
- This is a standalone skill for Voice Memos automation.
- For setup help, see the
automating-mac-appsskill for permissions (Full Disk Access, Accessibility) and the ObjC bridge basics. - Prerequisites: Basic JXA (JavaScript for Automation) knowledge; install via macOS System Preferences > Security & Privacy.
Core Framing
- Catalyst App: Voice Memos is an iOS app adapted for macOS without full macOS APIs, hence no AppleScript dictionary for automation.
- UI-first: Prefer UI scripting/keyboard shortcuts for exports to avoid touching the database/container.
- Data (optional): Use data-layer control only if needed (ObjC + sqlite3). This requires broader permissions.
- Permissions: Accessibility for UI automation; Full Disk Access only if you read/write the container/DB.
Workflow (default)
1) UI-first (no FDA): export via menu/shortcut to a folder you control. 2) Optional data path: resolve storage paths; query CloudRecordings.db (Apple epoch +978307200) only if required. 3) For UI actions (recording/export), drive the app with System Events (shortcuts preferred over clicks). 4) For editing, prefer external tools (ffmpeg) after export; avoid writing directly into the container unless you accept FDA.
Quickstart (UI-only export; no Full Disk Access)
- Open Voice Memos and select a recording.
- Export/share UI: the Share sheet can send audio to Notes/other apps (no direct File > Export).
- Transcript copy (UI-only, no FDA):
- Run:
osascript skills/automating-voice-memos/scripts/copy_transcript_to_file.applescript "/path/to/output.txt" - Defaults: Desktop/voice-memo-transcript.txt if no arg.
- Shows transcript (if available), selects all, copies, and writes to the target file via clipboard.
Permissions
- Accessibility: System Settings > Privacy & Security > Accessibility > Enable for System Events and your automation app (Terminal/Python/Script Editor).
- Full Disk Access: Only if you read/write the Voice Memos container/DB directly. UI-only exports do not require FDA.
- Verify: For UI-only, confirm you can open the Export menu and interact with the save dialog. For data access, verify
${home}/Library/Group Containers/group.com.apple.VoiceMemos.shared/.
Validation Checklist
After implementing Voice Memos automation:
- [ ] Verify Accessibility permissions granted for Terminal/Script Editor
- [ ] Confirm Voice Memos app opens and responds to UI scripting
- [ ] Test transcript copy script executes without errors
- [ ] Validate output file contains expected transcript text
- [ ] For data access: verify Full Disk Access and database path exists
Troubleshooting
- Permission denied: Verify Full Disk Access and Accessibility permissions.
- Database locked: Close Voice Memos app before querying.
- File not found: Check macOS version for correct storage path.
- UI automation failures: Ensure Voice Memos is focused and Accessibility is enabled.
When Not to Use
- For cross-platform audio recording (use ffmpeg or platform-agnostic tools)
- When you need programmatic audio capture (use AVFoundation directly)
- For iOS Voice Memos automation (no API available)
- When Full Disk Access cannot be granted
What to load
- Start with basics & prerequisites:
automating-voice-memos/references/voice-memos-basics.md(setup and core concepts). - UI automation:
automating-voice-memos/references/voice-memos-ui.md(shortcuts, AX scripting). - Data access (if needed):
automating-voice-memos/references/voice-memos-data.md(storage paths, epochs, database schema). - Recipes:
automating-voice-memos/references/voice-memos-recipes.md(exports, transcripts, recording control).
Voice Memos JXA Basics (no dictionary)
- Voice Memos is a Mac Catalyst app; no AppleScript dictionary. All automation is data-layer + UI scripting.
- Permissions:
- Full Disk Access to read/write the Group Container and CloudRecordings.db.
- Accessibility to drive UI/keystrokes.
- Storage paths:
- Sonoma/Sequoia:
~/Library/Group Containers/group.com.apple.VoiceMemos.shared/Recordings - Older (fallback):
~/Library/Application Support/com.apple.voicememos/Recordingsor~/Library/Containers/com.apple.VoiceMemos/Data/Library/Application Support/Recordings - Database:
CloudRecordings.db(Core Data SQLite) inside the Recordings folder. - Core Data epoch offset:
978307200seconds (Apple epoch). JS date =(zdate + 978307200)*1000. - Prefer data access first; use UI only for actions that must touch the app (record, transcript view).
Voice Memos Data & SQLite Access
Paths
- Primary (Sonoma/Sequoia):
~/Library/Group Containers/group.com.apple.VoiceMemos.shared/Recordings - Legacy fallback:
~/Library/Application Support/com.apple.voicememos/Recordings - DB:
CloudRecordings.dbinside the Recordings folder.
Resolve home with ObjC:
ObjC.import('Foundation');
const home = $.NSFileManager.defaultManager.homeDirectoryForCurrentUser.path.js;Core Data timestamps
- Apple epoch offset:
978307200seconds. - Convert:
new Date((zdate + 978307200) * 1000)
Key tables/columns (CloudRecordings.db)
ZCLOUDRECORDINGZPATH: filename (e.g.,20240101-120000.m4a)ZCUSTOMLABEL: user-visible titleZDATE: creation timestamp (Core Data epoch)ZDURATION: secondsZTRASHEDDATE: non-null => recently deletedZFOLDER: FK toZFOLDER.Z_PKZFOLDERZ_PK: primary keyZTITLE: folder nameZUUID: stable UUID
SQLite query (export-friendly)
const sql = `SELECT ZPATH, ZCUSTOMLABEL, ZDATE FROM ZCLOUDRECORDING
WHERE ZTRASHEDDATE IS NULL;`;
const cmd = `sqlite3 -separator "|" "${dbPath}" "${sql}"`;
const out = app.doShellScript(cmd);File copy with NSFileManager
ObjC.import('Foundation');
const fm = $.NSFileManager.defaultManager;
if (fm.fileExistsAtPath(src)) {
const ok = fm.copyItemAtPathToPathError(src, dest, null);
}Safe renaming
- Sanitize title:
title.replace(/[\/\\:]/g, "-") - Avoid collisions by appending counter if destination exists.
Voice Memos Recipes
Batch export (data-first)
ObjC.import('Foundation');
const app = Application.currentApplication();
app.includeStandardAdditions = true;
const fm = $.NSFileManager.defaultManager;
const home = fm.homeDirectoryForCurrentUser.path.js;
const root = home + "/Library/Group Containers/group.com.apple.VoiceMemos.shared/Recordings";
const dbPath = root + "/CloudRecordings.db";
const outDir = home + "/Desktop/Exported Voice Memos";
if (!fm.fileExistsAtPath(outDir)) fm.createDirectoryAtPathWithIntermediateDirectoriesAttributesError(outDir, true, null, null);
const sql = `SELECT ZPATH, ZCUSTOMLABEL, ZDATE FROM ZCLOUDRECORDING WHERE ZTRASHEDDATE IS NULL;`;
const cmd = `sqlite3 -separator "|" "${dbPath}" "${sql}"`;
const rows = app.doShellScript(cmd).split('\r');
rows.forEach(line => {
if (!line.trim()) return;
const [file, titleRaw, zdateRaw] = line.split('|');
const date = new Date((parseFloat(zdateRaw) + 978307200) * 1000);
const dateStr = date.toISOString().split('T');
const title = (titleRaw || "Untitled").replace(/[\/\\:]/g, "-");
let dest = `${outDir}/${dateStr} ${title}.m4a`;
let c = 1;
while (fm.fileExistsAtPath(dest)) dest = `${outDir}/${dateStr} ${title} ${c++}.m4a`;
const src = `${root}/${file}`;
if (fm.fileExistsAtPath(src)) fm.copyItemAtPathToPathError(src, dest, null);
});Start/stop recording via shortcuts
const se = Application("System Events");
const vm = Application("Voice Memos");
vm.activate();
delay(0.2);
se.keystroke("n", {using: "command down"}); // start
// ... wait as needed ...
se.keystroke(".", {using: "command down"}); // stopTranscript scrape (outline)
1) Activate Voice Memos, Cmd+F, type recording name, Enter. 2) Open transcript via View menu or transcript button. 3) Use a recursive UI search to find AXTextArea and read .value(). 4) Save text next to the exported audio. Example write:
const app = Application.currentApplication();
app.includeStandardAdditions = true;
const text = transcriptText; // result of AXTextArea.value()
const out = `${outDir}/${safeTitle}.txt`;
app.doShellScript(`printf %s ${app.doShellScript("python3 - <<'PY'\nimport sys,json\nprint(json.dumps(sys.stdin.read()))\nPY <<< " + JSON.stringify(text))} > ${out}`);External trim/enhance workflow
1) Export recording (data workflow above). 2) Run ffmpeg via doShellScript for silence removal or trimming. 3) Optional: re-import by simulating drag/drop or opening in Finder, then delete the original via DB/UI if needed.
Voice Memos UI Automation (System Events)
- No dictionary: rely on System Events + keyboard shortcuts (prefer shortcuts over clicks).
- Accessibility tree (Catalyst): expect deep
AXGroup/AXSplitGroupnesting; buttons may lack titles, use role/description.
Keyboard shortcuts (preferred)
- Start Recording:
Cmd+N - Stop/Done:
Cmd+.or Space - Play/Pause: Space
- Trim:
Cmd+T - Delete:
Deletekey (keyCode 51) - Enhance Audio:
Shift+Cmd+E - Skip Silence:
Shift+Cmd+X
Example keystroke:
const se = Application("System Events");
se.keystroke("n", {using: "command down"}); // start recordingRecursive finder (simplified pattern)
Use a stack/DFS to find elements by role/description when needed:
function findUIElement(root, criteria) {
const stack = [root];
while (stack.length) {
const el = stack.pop();
let ok = true;
for (const key in criteria) {
try {
if (el[key]()!== criteria[key]) { ok = false; break; }
} catch (e) { ok = false; break; }
}
if (ok) return el;
try {
el.uiElements().forEach(child => stack.push(child));
} catch (e) {}
}
return null;
}Transcription view (UI)
- Open the app frontmost, search via
Cmd+F, Enter, then use View > Transcript (if present) or locate a button with description like "Transcript". - Scrape text by finding a large
AXTextAreaafter the transcript pane is shown.
UI fragility
- Sidebar hidden changes group indices; avoid hardcoded indices.
- Always ensure Accessibility permission is granted before automating.
#!/usr/bin/osascript
(*
Copy the transcript of the currently selected Voice Memos recording to a file.
UI-only: avoids DB/files; requires Accessibility. Voice Memos must be open with a recording selected.
Usage:
osascript copy_transcript_to_file.applescript "/path/to/output.txt"
Defaults:
~/Desktop/voice-memo-transcript.txt
*)
on run argv
set targetFile to (POSIX path of (path to desktop)) & "voice-memo-transcript.txt"
if (count of argv) ≥ 1 then set targetFile to item 1 of argv
tell application "Voice Memos" to activate
delay 0.4
tell application "System Events"
if not (exists process "VoiceMemos") then error "Voice Memos process not found. Launch it first."
tell process "VoiceMemos"
set frontmost to true
-- Try to show transcript pane (button with transcript icon/label, may vary by macOS version).
try
if exists (first button of toolbar 1 of window 1 whose description contains "Transcript") then
click (first button of toolbar 1 of window 1 whose description contains "Transcript")
delay 0.2
end if
end try
-- Attempt to focus transcript area; element hierarchy varies by macOS version.
try
if exists scroll area 1 of window 1 then
tell scroll area 1 of window 1
if exists static text 1 then
click static text 1
end if
end tell
end if
end try
-- Select all + copy.
keystroke "a" using command down
delay 0.05
keystroke "c" using command down
end tell
end tell
set transcriptText to the clipboard
if transcriptText is missing value or transcriptText is "" then
error "No transcript text found in clipboard. Ensure transcript is visible and selected."
end if
set outFile to POSIX file targetFile as text
set fh to open for access outFile with write permission
try
set eof fh to 0
write transcriptText to fh
on error errMsg number errNum
close access fh
error errMsg number errNum
end try
close access fh
return "Transcript saved to " & targetFile
end run
#!/usr/bin/osascript
(*
UI-only export for the currently selected Voice Memos recording.
Avoids reading the database/files directly (no Full Disk Access required).
Requirements: Voice Memos open and a recording selected; Accessibility enabled for the runner (Terminal/Script Editor).
Usage (defaults to Desktop/voice-memo-export.m4a):
osascript export_selected_recording_via_ui.applescript
osascript export_selected_recording_via_ui.applescript "/path/to/dir" "my-recording"
*)
on run argv
set targetFolder to POSIX path of (path to desktop)
set baseName to "voice-memo-export"
if (count of argv) ≥ 1 then set targetFolder to item 1 of argv
if (count of argv) ≥ 2 then set baseName to item 2 of argv
tell application "Voice Memos" to activate
delay 0.3
tell application "System Events"
if not (exists process "VoiceMemos") then error "Voice Memos process not found. Launch it first."
tell process "VoiceMemos"
set frontmost to true
my openExportMenu(menu bar 1)
end tell
my fillSavePanel(targetFolder, baseName)
end tell
end run
on openExportMenu(mb)
tell application "System Events"
tell mb
tell menu bar item "File"
tell menu "File"
if exists menu item "Export…" then
click menu item "Export…"
else if exists menu item "Export..." then
click menu item "Export..."
else
set candidates to every menu item whose name begins with "Export"
if candidates is not {} then
click item 1 of candidates
else
keystroke "e" using {command down, shift down} -- fallback shortcut (may vary by macOS version)
end if
end if
end tell
end tell
end tell
end tell
end openExportMenu
on fillSavePanel(targetFolder, baseName)
tell application "System Events"
tell process "VoiceMemos"
set saveSheet to missing value
repeat 30 times
if exists sheet 1 of window 1 then
set saveSheet to sheet 1 of window 1
exit repeat
else if exists window 1 then
try
if (subrole of window 1 is "AXDialog") then
set saveSheet to window 1
exit repeat
end if
end try
end if
delay 0.2
end repeat
if saveSheet is missing value then error "Save dialog not found. Ensure a recording is selected."
if exists text field 1 of saveSheet then set value of text field 1 of saveSheet to baseName
keystroke "g" using {command down, shift down}
delay 0.2
try
tell sheet 1 of saveSheet
if exists text field 1 then
set value of text field 1 to targetFolder
keystroke return
end if
end tell
end try
delay 0.3
try
if exists button "Save" of saveSheet then
click button "Save" of saveSheet
else if exists button "Export" of saveSheet then
click button "Export" of saveSheet
end if
end try
end tell
end tell
end fillSavePanel
#!/usr/bin/env bash
# Voice Memos setup helper: tries to activate the app (best effort) and reports the data path.
# Notes:
# - Voice Memos has no AppleScript dictionary. Automation relies on UI scripting and filesystem access.
# - You likely need Accessibility (for UI scripting) and Full Disk Access (for database/files).
set -euo pipefail
echo "Attempting to launch Voice Memos (best effort)..."
# Try bundle ID first, then a few common paths (Catalyst name is often VoiceMemos.app without a space).
launch_attempted=0
if open -b com.apple.VoiceMemos 2>/dev/null; then
launch_attempted=1
else
possible_paths=(
"/System/Applications/VoiceMemos.app"
"/System/Applications/Voice Memos.app"
"/Applications/VoiceMemos.app"
"/Applications/Voice Memos.app"
)
for app_path in "${possible_paths[@]}"; do
if [[ -d "$app_path" ]]; then
echo "Found app at: $app_path"
if open "$app_path" 2>/dev/null || open -a "$app_path" 2>/dev/null; then
launch_attempted=1
break
else
echo "Launch attempt failed for $app_path"
fi
fi
done
fi
if [[ "$launch_attempted" -eq 0 ]]; then
echo "Warning: Could not launch Voice Memos via bundle ID or known paths; verify the app location."
fi
# Detect likely data paths across macOS versions.
paths=(
"$HOME/Library/Group Containers/group.com.apple.VoiceMemos.shared/Recordings"
"$HOME/Library/Application Support/com.apple.voicememos/Recordings"
"$HOME/Library/Containers/com.apple.VoiceMemos/Data/Library/Application Support/Recordings"
)
echo "Checking known Voice Memos data locations:"
found=0
for p in "${paths[@]}"; do
if [[ -d "$p" ]]; then
echo "✔ Found: $p"
found=1
else
echo "✖ Not present: $p"
fi
done
if [[ "$found" -eq 0 ]]; then
echo "No known data path found. Run Voice Memos once and re-run this script."
else
echo "If you need direct file/database access, grant Full Disk Access to Terminal/Python."
fi
echo "Reminder: Enable Accessibility for UI scripting (System Settings > Privacy & Security > Accessibility)."