Stacks
Memory management for AI coding agents on one Mac.
Every agent wants its own simulator and its own dev server, and nobody stops them. Here is what we use so a 16 GB Mac stops swapping.
Start here
This sits on top of the agents stack
In short
- A stock iOS simulator starts about 170 background services that an app test does not need, such as Siri, widgets, Spotlight and iCloud sync.
- simslim from MobAI turns those services off per simulator; on our iOS 27 simulator it cut memory from 3.48 GB to 473 MB, while keeping push, StoreKit, universal links and the photo picker.
- Dev servers started by agents are the other half of the problem: on our Mac a forgotten API watcher had reached 10 GB and two Vite servers for the same site were still running.
- A small watchdog that launchd runs every 15 minutes restarts the servers we rely on when they grow past a limit, and stops servers nobody has connected to for a day.
- A rule in ~/.claude/rules tells every agent to reuse a running server and stop the ones it started.
What is in the Memory stack?
- Lighter simulatorsCLI
simslim
by MobAI
Turns off the background services an iOS, tvOS, watchOS or visionOS simulator does not need for development. It changes only the simulator you name, keeps working across reboots, and checks afterwards that the features you kept still work.
How we use it: We keep only what Kidtime uses: push, StoreKit, universal links, the photo and document pickers, Sign in with Apple and CallKit. A second simulator for a second agent now fits.
bashbrew install mobai-app/tap/simslim simslim on <udid> --except store,web,photos
How do the parts work together?
- Measure. Swap and memory footprint per process, not RSS: RSS hides what is swapped out.
- Slim the simulators. The agent reads your app’s native dependencies and keeps only the simslim categories they need, then checks them with
simslim doctor. - Clean up dev servers. Duplicates and servers nobody connects to get stopped, after you say yes.
- Install the watchdog. It restarts the servers you name when they stay over a memory limit, and stops the rest after a day with no client. It is tested on throwaway servers first.
- Add the rule. Agents reuse a running server and stop what they started.
Paste the prompt below into Claude Code and the agent does all five on your Mac. It asks before it stops anything or installs anything that runs in the background.
Set up memory management on this Mac so it can run several AI coding agents, iOS simulators and dev servers at once without swapping. Do the work yourself. Ask me before you stop any process, uninstall anything, or install something that runs in the background. Do not commit to any repository. Goal - iOS simulators run slim: only the background services my apps need. - Dev servers that nobody uses get stopped, and the ones I rely on get restarted when they grow too big. - Agents stop leaving servers behind. - I see the memory numbers before and after. 1. Measure - Read swap (sysctl vm.swapusage) and the biggest processes by memory footprint, not RSS: RSS misses swapped and compressed pages. top -l 1 -o mem shows footprint. - Group them: simulators, dev servers (Metro, Vite, Next, webpack, nest start --watch, nodemon, tsx watch), browsers, Docker, agent sessions and their MCP servers, everything else. - For each dev server: its folder (lsof -d cwd), its ports, its age, whether any client is connected to it, and whether another server of the same kind runs in the same folder. - Show me the table, largest first, before you change anything. 2. Slim the iOS simulators with simslim (github.com/MobAI-App/simslim, MIT, no network calls) - Install: brew install mobai-app/tap/simslim. Read simslim profiles and simslim doctor --list. - Find what my apps need. Read each app's native dependencies (package.json for React Native and Expo, Package.swift or the Podfile for native) and map them to simslim categories: push notifications and StoreKit or in-app purchases need store; universal links and web sign-in sheets need web; image pickers and the camera roll need photos; the document picker needs search; Sign in with Apple needs icloud; CallKit needs messaging; speech recognition needs com.apple.corespeechd; MapKit needs com.apple.MapKit.SnapshotService; the App Tracking Transparency prompt needs com.apple.ap.adprivacyd. - Write the result as a profile file, ~/.config/simslim/<name>.json, with "except" for whole categories and "keep" for single daemons. Show it to me with one line per kept item saying which dependency needs it. - Measure each booted simulator first with simslim measure <udid>. - Slim it with simslim on <udid> --no-reboot --profile <file>. This keeps the running apps and agents working. Then simslim doctor <udid> --requires <the features you kept> must pass, and simslim verify must match. - After I agree to a reboot, shut it down and boot it (simslim shutdown, simslim boot), verify again, and measure again. - A simulator I add later, or one that is erased, starts stock. Run simslim on with the same profile on it. 3. Clean up dev servers - List leftovers: a duplicate of a server in the same folder, or a server with no client connection that is days old. - Ask me which ones to stop. Stop each process tree (its npm or pnpm wrapper too), then check that the port is free. - For a server I want kept but that is large, find out how it was started (its parent process, the package.json script, the Node version from .nvmrc). Restart it detached with nohup, with its log in ~/Library/Logs/<project>/<server>.log, so it no longer depends on a terminal tab or an agent session. 4. A watchdog, so this stays fixed Write a small script, ~/.local/bin/devwatch, run by a launchd agent every 15 minutes (~/Library/LaunchAgents/<reverse-dns>.devwatch.plist, StartInterval 900). Use /usr/bin/python3 or plain shell, with no dependencies, and keep it under one second per run. Settings go in ~/.config/devwatch/config.json, state in ~/.local/state/devwatch/state.json, and a line per check in ~/Library/Logs/devwatch.log. - Find dev servers by command line and keep only those whose folder is under my code folder (ask me which). Treat a process tree as one server: walk up through npm, npx, pnpm, yarn and sh -c wrappers, and down through the children. - Memory: sum phys_footprint for the tree (proc_pid_rusage, flavor 2, via ctypes; not ps RSS). - In use: an established TCP connection whose local port is one of the tree's listening ports. A connected simulator or an open browser tab counts. - Managed servers: the ones I name, each with its folder, a command pattern, its port, a memory limit, its start command and its log. Over the limit at two checks in a row: stop the tree, start it again with the Node version from .nvmrc, wait for the port to listen, and notify me. - Everything else under the folder: stop it after 24 hours with no client. Stop a duplicate (same kind, same folder) after 30 minutes with no client, and always keep one, preferring the one with a client. - Notify me (osascript display notification) when swap stays above a limit I choose, or when a server you cannot restart grows past 2 GB. Notify once per problem, not every run. - A --dry-run flag prints what it would do and changes nothing. Before you install it: - Test every action on throwaway servers in a temporary folder: a tiny Node HTTP server started twice in one folder (the duplicate stop), one with an idle client socket held open (it must be kept), and a fake managed server with a 1 MB limit (the restart). Use a test config, state and log so my real servers are never touched. Then delete the folder. - Run --dry-run against my real servers and show me the output. Then bootstrap the launch agent and show me the first real check in the log. 5. A rule for agents Add ~/.claude/rules/dev-servers.md (Claude Code loads every file in ~/.claude/rules in every project; for another agent, put the same text in its global instructions): - Reuse before you start: check the port and running processes first. - Never start a second server for the same project, on any port. - Stop every server you started before you finish, and say which. - Ask before stopping a server you did not start. - If a server vanished, read ~/Library/Logs/devwatch.log before starting it again. Report - Before and after: swap used, each simulator's footprint, each dev server's footprint. - What you stopped, restarted and installed, with the command that undoes each one: simslim off <udid> for a simulator, launchctl bootout gui/$(id -u)/<label> and deleting the plist for the watchdog.
Frequently asked questions
- Is simslim free?
Yes. It is open source under the MIT license, it makes no network calls, and it needs no account. MobAI built it to run more simulators for its own agents.
- What stops working on a slim simulator?
Whatever you turn off. Turn off the store category and push and StoreKit stop; turn off web and universal links stop. That is why the prompt reads your app first and keeps what it uses.
simslim enable <udid> <category>turns one back on without a reboot.- Does it change my Mac?
No. simslim writes its settings into each simulator’s own launchd database.
simslim off <udid>puts that simulator back to stock.- Will the watchdog kill a server I am using?
It stops a server only when nothing has connected to it for 24 hours, or when it is a duplicate with no client for 30 minutes. An open browser tab or a connected simulator counts as a client. The servers you name are restarted, never stopped.
- Does this help on Android?
The watchdog and the rule do. simslim is for Apple simulators only, so an Android emulator still costs its full memory.
None of these makers pay us to list them. Setup commands checked on .
We set this up on your team’s codebase, with the rules that make the agent use it well.
Talk to us about your team