macos-plugin/skills/launchservices-health/SKILL.md
Diagnose macOS LaunchServices DB bloat and launchservicesd CPU spikes. Use when launchservicesd is pegged at high CPU or WindowServer XPC stalls are suspected.
npx skillsauth add laurigates/claude-plugins launchservices-healthInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
| Use this skill when... | Use something else when... |
|------------------------|----------------------------|
| launchservicesd is at sustained high CPU | General process forensics — use ps/top directly |
| GUI feels sluggish after weeks of uptime | The machine actually hung — see macos-incident-postmortem |
| Quantifying LS DB bloat (size, bundle count, claim count) | Checking app launch failures — use system_profiler SPApplicationsDataType |
| Deciding whether lsregister rebuild is warranted | First-time install setup — use configure-plugin |
This skill is macOS-only. The lsregister binary, launchservicesd daemon, and the LaunchServices database are Darwin-specific. Refuse to act if uname -s is not Darwin.
test "$(uname -s)" = "Darwin" || { echo "macos-plugin: not Darwin, refusing"; exit 1; }
LaunchServices is the macOS subsystem that maps documents and URL schemes to applications. State is held by launchservicesd and persisted to a binary database. Two failure modes matter:
lsregister -dump runs and the more memory launchservicesd holds.launchservicesd can spin at high CPU when a misbehaving process repeatedly registers/unregisters bundles. WindowServer makes synchronous XPC calls into launchservicesd for icon and document-handler lookups; a wedged daemon manifests as a frozen GUI.The diagnostic answer is always: measure first, rebuild only if warranted. Rebuild (lsregister -kill -r ...) takes minutes, makes every app's "open with" disappear briefly, and rebuilds the DB from scratch — only do it when the metrics justify it.
| Path | Purpose |
|------|---------|
| /System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister | The lsregister CLI — not on $PATH, must be invoked by absolute path |
| ~/Library/Application Support/com.apple.PersistentURLTranslator/ | Per-user persisted state |
| /var/folders/*/0/com.apple.LaunchServices* | LS cache directories (varies by macOS version) |
The reference script ls-stats produces a one-page health snapshot. Install at ~/.local/bin/ls-stats (mode 0755):
#!/usr/bin/env bash
# ls-stats: macOS LaunchServices DB + launchservicesd health snapshot
set -euo pipefail
[ "$(uname -s)" = "Darwin" ] || { echo "not Darwin" >&2; exit 1; }
LSREG=/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister
PID=$(pgrep -x launchservicesd) || { echo "launchservicesd not running" >&2; exit 1; }
DUMP=$(mktemp); trap 'rm -f "$DUMP"' EXIT
"$LSREG" -dump >"$DUMP" 2>/dev/null
echo "=== launchservicesd ==="
ps -o pid,rss,etime,command -p "$PID" | sed 's/^/ /'
echo
echo "=== LaunchServices DB ==="
wc -c <"$DUMP" | awk '{printf " size: %d bytes (%.1f MB)\n", $1, $1/1048576}'
printf " bundles: %d\n" "$(grep -c '^bundle id:' "$DUMP")"
printf " claims: %d\n" "$(grep -c '^claim ' "$DUMP")"
printf " records: %d\n" "$(grep -c '^----' "$DUMP")"
echo
printf "=== Diagnostic reports ===\n launchservicesd CPU events: %d\n" \
"$(ls /Library/Logs/DiagnosticReports/ 2>/dev/null | grep -c launchservicesd)"
echo
echo "=== Uptime ==="
uptime | awk '{print " " $0}'
The script prints four blocks: daemon process state (RSS, etime), DB size and counts, recent CPU diagnostic-report count, and current uptime. All numbers are absolute — interpret them against the baseline thresholds below.
| RSS (KB) | Meaning | |----------|---------| | < 50 000 (~50 MB) | Healthy | | 50 000 – 150 000 | Normal for active use | | 150 000 – 400 000 | Bloated; consider rebuild | | > 400 000 (~400 MB) | Pathological; rebuild warranted |
etime (elapsed time since launch)launchservicesd is launched once per boot and lives until reboot. etime close to uptime is normal. etime significantly less than uptime means the daemon was relaunched mid-session — usually after a crash; check Diagnostic Reports.
| Size | Meaning | |------|---------| | < 5 MB | Normal | | 5 – 20 MB | Healthy heavy-app workstation | | 20 – 50 MB | Bloated; rebuild improves things | | > 50 MB | Pathological |
There is no absolute threshold — these matter relative to the expected number of installed apps. Useful sanity check:
ls /Applications /Applications/Utilities ~/Applications 2>/dev/null | wc -l
If bundle id: count is 5x or more above the visible-.app count, the DB has stale records.
/Library/Logs/DiagnosticReports/launchservicesd-*.cpu_resource.diag files indicate the daemon hit a CPU threshold (typically 80% sustained for 90s). Each file is one event. Recent volume:
find /Library/Logs/DiagnosticReports -name 'launchservicesd*.cpu_resource.diag' -mtime -7 | wc -l
| Count (last 7 days) | Meaning | |---------------------|---------| | 0 | Normal | | 1 – 3 | Notable; monitor | | 4+ | Pattern; likely a misbehaving registrar — investigate |
Rebuild only when the snapshot shows multiple red flags. The rebuild takes 1–10 minutes depending on app count.
# Rebuild the LaunchServices database
/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister \
-kill -r -domain local -domain system -domain user
What the flags do:
| Flag | Effect |
|------|--------|
| -kill | Reset the existing database first |
| -r | Recursively scan for bundles to register |
| -domain local | /Applications |
| -domain system | /System/Applications, /System/Library |
| -domain user | ~/Applications, ~/Library/Application Support |
Side effects:
launchservicesd process is spawned; PID changesRe-run ls-stats to confirm RSS, DB size, and counts dropped.
ls-stats > /tmp/ls-before.txt
"$LSREG" -kill -r -domain local -domain system -domain user
ls-stats > /tmp/ls-after.txt
diff -u /tmp/ls-before.txt /tmp/ls-after.txt
Append a snapshot to a logbook on each session:
{ date -Iseconds; ls-stats; echo "---"; } >> ~/.local/state/launchservices.log
Plot or grep over time to see whether DB size is monotonically growing.
When launchservicesd is hot and you suspect a specific app:
log stream --predicate 'process == "launchservicesd"' --style syslog | head -200
Look for repeated bundle id or path strings — that's the offender.
Wine/CrossOver bottle creation registers hundreds of .exe-as-bundle entries. Count them:
"$LSREG" -dump 2>/dev/null | grep -c -i 'wineprefix\|bottle'
A 4-digit answer is the smoking gun.
[doc('Quantify LaunchServices DB and launchservicesd state (macOS)')]
[macos]
ls-stats:
@~/.local/bin/ls-stats
The [macos] attribute (just 1.34+) skips the recipe on non-Darwin. On older just versions, drop the attribute and let the script's uname -s check do the gating.
| Context | Command |
|---------|---------|
| One-line size | $LSREG -dump 2>/dev/null \| wc -c \| awk '{printf "%.1f MB\n", $1/1048576}' |
| RSS only | ps -o rss= -p "$(pgrep -x launchservicesd)" |
| Bundle count | $LSREG -dump 2>/dev/null \| grep -c '^bundle id:' |
| Recent CPU events | find /Library/Logs/DiagnosticReports -name 'launchservicesd*.cpu_resource.diag' -mtime -7 \| wc -l |
| Daemon uptime vs system uptime | ps -o etime= -p "$(pgrep -x launchservicesd)"; uptime |
| Path | Purpose |
|------|---------|
| /System/.../Support/lsregister | The CLI |
| /Library/Logs/DiagnosticReports/ | CPU and crash diag files |
| ~/Library/Application Support/com.apple.PersistentURLTranslator/ | Per-user state |
| Flag | Description |
|------|-------------|
| -dump | Print the entire DB to stdout |
| -kill -r -domain ... | Rebuild |
| -h | List all flags (undocumented elsewhere) |
| -f <bundle> | Force-register a single bundle |
| -u <bundle> | Unregister a bundle |
| Metric | Healthy | Bloated | Rebuild | |--------|---------|---------|---------| | DB size | < 5 MB | 20–50 MB | > 50 MB | | Daemon RSS | < 50 MB | 150–400 MB | > 400 MB | | Bundle count vs visible apps | 1–2x | 3–5x | > 5x | | CPU diag events / 7 days | 0 | 1–3 | 4+ |
| Symptom | Cause | Fix |
|---------|-------|-----|
| lsregister: command not found | Not on $PATH by design | Use the full /System/.../Support/lsregister path |
| lsregister -dump exits non-zero with stderr | Permissions or DB corruption | Capture stderr separately; rebuild may resolve it |
| pgrep -x launchservicesd returns nothing | Daemon crashed and not yet relaunched | Check Diagnostic Reports; force a relaunch with lsregister -kill -r ... |
| Rebuild appears to hang | Indexing many app bundles is genuinely slow | Wait — typical 1–10 min depending on app count |
| Sudden RSS jump after Homebrew upgrade | Many bundles re-registered at once | Expected; will settle within minutes |
macos-incident-postmortem — when LaunchServices CPU caused a GUI hang, parse Diagnostic Reports therekitty-session-persistence — recover terminal sessions lost when WindowServer wedged on an LS XPC calltools
Scaffold a new ComfyUI custom-node repo (pyproject, CI, release-please, vitest+pytest, JS extension skeleton) in the picker/gesture vein. Use when bootstrapping or init-ing a comfyui node pack.
tools
Orchestrate a ComfyUI node pack from idea to registry: scaffold, create + seed the repo, open the gitops adoption PR. Use when releasing or spinning up a new comfyui node pack.
testing
macOS EndpointSecurity/EDR high CPU & battery drain. Use when Kandji ESF / XProtect pegs a core; trace the exec storm via powermetrics + eslogger.
development
odiff pixel-by-pixel image diffing. Use when comparing screenshots, detecting visual regressions, diffing before/after PNGs, asserting golden images.