From devops-tools
Configure a MacBook as an always-on-AC desktop workstation with USB device resilience, battery longevity, and self-healing audio device.
How this skill is triggered — by the user, by Claude, or both
Slash command
/devops-tools:macbook-desktop-modeThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
A holistic configuration guide for running a MacBook as an always-on-AC desktop workstation. Solves two interconnected problems: USB devices (especially USB 1.1 audio) disappearing during sleep/wake cycles, and unnecessary battery degradation from constant charge cycling.
A holistic configuration guide for running a MacBook as an always-on-AC desktop workstation. Solves two interconnected problems: USB devices (especially USB 1.1 audio) disappearing during sleep/wake cycles, and unnecessary battery degradation from constant charge cycling.
Self-Evolving Skill: This skill improves through use. If instructions are wrong, parameters drifted, or a workaround was needed — fix this file immediately, don't defer. Only update for real, reproducible issues.
pmset for always-on-AC useuhubctl for software-controlled USB resetsBefore applying fixes, diagnose the specific failure mode. This framework was developed from empirical analysis of a MacBook Pro M3 Max with an Antlion USB Microphone (VID 0x2F96, PID 0x0200).
macOS performs frequent DarkWake (partial maintenance wake) cycles — typically every 15 minutes overnight. During DarkWake:
Diagnostic command — check sleep/wake history:
pmset -g log | grep -E "Sleep |Wake |DarkWake" | tail -30
USB 1.1 devices (12 Mbps, USBSpeed = 1) are the most fragile across sleep/wake:
iSerialNumber = 0), making re-identification after bus reset unreliableDiagnostic command — check device properties:
ioreg -r -c IOUSBHostDevice -l | grep -A 20 "DEVICE_NAME"
Key fields: USBSpeed (1=Full, 2=High, 3=Super), iSerialNumber (0 = no serial), IOPowerManagement.DriverPowerState.
Applications like Chrome hold direct USB user client handles (AppleUSBHostDeviceUserClient) for WebRTC/WebAudio. These stale handles prevent clean re-initialization after sleep.
Diagnostic command — check who has USB handles:
ioreg -r -c IOUSBHostDevice -l | grep -B5 "IOUserClientCreator"
On an always-AC Mac with no charge limit, the battery cycles between the ML-predicted level and actual charge. DarkWake cycles consume power, causing repeated charge/discharge micro-cycles.
Diagnostic command — check daily charge range:
ioreg -r -c AppleSmartBattery -l | grep -E "DailyMinSoc|DailyMaxSoc|Temperature|CycleCount"
Temperature is in centidegrees (divide by 100 for Celsius)DailyMinSoc/DailyMaxSoc show today's charge swing rangeSystem Settings → Battery → Charging Optimization → "Limit to 80%"
On Apple Silicon, once at the limit the Mac enters AC bypass mode — power flows directly from charger to system board. The battery sits electrically disconnected, eliminating both calendar aging and cycle aging.
For an always-on-AC desktop, system sleep creates more problems than it solves:
# Disable system sleep on AC only (battery settings unchanged)
sudo pmset -c sleep 0
# Verify
pmset -g custom | grep -A1 "AC Power" | grep sleep
What this preserves:
displaysleep=10 on AC)sleep=1 for travel)ttyskeepawake=1 still honoredWhat this eliminates:
Cost: ~5-8W idle draw on Apple Silicon. No fan spin. ~$5-8/year in electricity.
# Full power settings
pmset -g custom
# Battery health snapshot
ioreg -r -c AppleSmartBattery -l | grep -E "Temperature|CycleCount|DailyMinSoc|DailyMaxSoc|MaxCapacity|IsCharging"
# Active power assertions
pmset -g assertions
A powered USB hub creates a USB session boundary. The Mac's XHCI controller maintains a session with the hub (a robust USB 2.0+ device with serial number). The hub independently maintains sessions with connected devices. Sleep/wake only stresses the Mac↔Hub link.
Additionally, the hub enables software-controlled USB port reset via uhubctl — the equivalent of a physical unplug/replug without touching hardware.
Requirements:
Compatible chipsets (from the uhubctl project):
# Install
brew install uhubctl
# List compatible hubs (must have powered hub connected)
uhubctl
# Cycle all ports (2-second off period)
uhubctl -a cycle -d 2
# Cycle specific port on specific hub
uhubctl -a cycle -p 1 -l 1-1 -d 2
The reference implementation is a Swift daemon (AudioDeviceMonitorRunner.swift) deployed as a macOS launchd KeepAlive agent. It combines:
IORegisterForSystemPower ┌──────────┐
┌────────▶│ PRESENT │◀──────────────┐
│ └────┬─────┘ │
│ │ device gone │
│ ▼ │
│ ┌──────────────┐ │
│ │ DISAPPEARED │ │
│ │ (3s debounce)│ │
│ └──────┬───────┘ │
│ │ still missing │
│ ▼ │
│ ┌──────────────┐ found │
│ │ RECOVERING │───────────────┘
│ │ uhubctl cycle│
│ └──────┬───────┘
│ │ max attempts
│ ▼
│ ┌──────────────┐
│ │ DEAD │
└─────│ (Telegram) │
replug└──────────────┘
NSWorkspace — no AppKit dependency, works in headless launchd daemonsiokit_common_msg() C macros; values computed from sys_iokit (0xe0000000) | message_code.main; no locks neededThe daemon is a single-file Swift program compiled into a self-contained binary:
# Compile
swiftc -O -framework CoreAudio -framework IOKit \
-o /path/to/output/audio-device-monitor \
AudioDeviceMonitorRunner.swift
# Codesign for launchd (ad-hoc, no Apple Developer account needed)
codesign -s - -f -i com.yourorg.audio-device-monitor /path/to/output/audio-device-monitor
Deploy as a launchd agent (user-level, KeepAlive):
<?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.audio-device-monitor</string>
<key>ProgramArguments</key>
<array>
<string>/path/to/output/audio-device-monitor</string>
</array>
<key>KeepAlive</key>
<true/>
<key>RunAtLoad</key>
<true/>
<key>ProcessType</key>
<string>Background</string>
<key>EnvironmentVariables</key>
<dict>
<key>HOME</key>
<string>/Users/yourusername</string>
</dict>
<key>StandardOutPath</key>
<string>/path/to/logs/audio-device-monitor-stdout.log</string>
<key>StandardErrorPath</key>
<string>/path/to/logs/audio-device-monitor-stderr.log</string>
</dict>
</plist>
Load: launchctl load ~/Library/LaunchAgents/com.yourorg.audio-device-monitor.plist
Priority lists and guarded devices are defined as constants at the top of the Swift source:
inputPriorities / outputPriorities — ordered arrays, highest priority firstguardedDevices — array of GuardedDevice(name:, isInput:) for disappearance monitoringheartbeatInterval (60s), disappearDebounce (3s), maxRecoveryAttempts (3)loadCredentials())After changes, recompile and restart the launchd agent.
# Quick battery snapshot
ioreg -r -c AppleSmartBattery -l | grep -E "Temperature|CycleCount|DailyMinSoc|DailyMaxSoc|MaxCapacity"
# Full power state
pmset -g batt
system_profiler SPPowerDataType | grep -E "Cycle|Condition|Maximum|Charging"
With the 80% charge limit set and sleep=0 on AC:
DailyMinSoc and DailyMaxSoc should converge (no swing)Temperature should stay under 3500 (35°C) at steady stateCycleCount accumulation should slow dramatically# Current USB device tree
system_profiler SPUSBDataType
# IOKit details for a specific device
ioreg -r -c IOUSBHostDevice -l | grep -A 25 "DEVICE_NAME"
# Kernel USB assertions (shows which devices prevent sleep)
pmset -g assertions | grep USB
# Tail live logs (adjust path to your log location)
tail -50 /path/to/logs/audio-device-monitor-stderr.log
# Key log patterns to watch for:
# "Starting v2 — device guardian mode" → v2 features active
# "System woke — checking devices" → wake detection working
# "ALERT: '<device>' disappeared" → device dropout detected
# "recovered via uhubctl!" → automated recovery succeeded
# "recovery FAILED" → manual replug needed
| Anti-Pattern | Why It Fails | Correct Approach |
|---|---|---|
Changing powernap=0 to reduce DarkWake | Trades USB stability for missed iCloud sync, Find My, etc. | Set sleep=0 on AC — eliminates DarkWake entirely |
| Disabling Optimized Battery Charging | Allows battery to charge to 100% — worse for longevity | Set explicit 80% charge limit |
| Killing Chrome to release USB handles | Whack-a-mole; any WebRTC app can grab handles | Powered hub creates session boundary |
Polling system_profiler for USB status | Expensive subprocess, 2-3 second latency | CoreAudio property listeners are instant |
Using NSWorkspace.didWakeNotification | Requires AppKit/NSApplication — won't work in launchd daemon | IORegisterForSystemPower callback |
| Resetting USB via IOKit when device gone | Can't reset a device that's already dropped from IO registry | uhubctl power-cycles the physical port |
kokoro-tts:realtime-audio-architecture — Complementary skill covering audio playback patterns (PortAudio, GIL contention, jitter elimination, device hot-switching). This skill handles the system/USB layer; that one handles the application/playback layer.After this skill completes, reflect before closing the task:
Do NOT defer. The next invocation inherits whatever you leave behind.
npx claudepluginhub terrylica/cc-skills --plugin devops-toolsDiagnoses macOS workstation issues including kernel panics, failing drives, launchd startup audit, wake reasons, TCC denials, and APFS snapshot space.
Diagnoses and resolves macOS disk and memory issues including kernel panics, Jetsam events, and low disk space. Sets up automated alerting via LaunchAgent and uses Mole for safe cleanup.
macOS diagnose-and-fix command center. Wraps the macos-toolkit CLI suite (machealth, netwhiz, pstop, macdog, lanchr, macbroom, macctl, macfig, updater) behind one entrypoint — self-installs the suite on first use, runs a read-only baseline audit (security, launch agents, processes, network, disk, system health), and applies guarded fixes (firewall, stale daemons, cache cleanup) with per-action confirmation.