Xcode Disk Cleanup
Safety contract
- Treat every cleanup request as permission to audit, not permission to delete.
- Never mutate storage before presenting exact candidate IDs, paths or simulator
identifiers, measured sizes, risks, and regeneration costs.
- Treat a category-level request made after an audit (for example, “remove all
outdated documentation caches”) as approval of the matching, current frozen
selection set when it was already fully enumerated to the user. Execute in
that turn after revalidation; do not restate the set and ask again. No special
command or repeated confirmation is required. Broad requests made before an
audit, such as “clean Xcode” or “delete everything,” remain audit-only.
- Default ordinary files and directories to Trash. Explain that space is not
reclaimed until Trash is emptied, and request separate approval before doing so.
- Revalidate identity and running-process usage immediately before mutation.
- Never use wildcards, unbounded
rm -rf, sudo, SIP changes, or manual deletion
inside system-managed CoreSimulator and MobileAsset directories.
- Preserve archives and dSYMs unless the user proves the exact distributed build
is backed up elsewhere and separately approves deletion.
- Never delete
Package.resolved, signing assets, credentials, custom DocSets, the
active Xcode, a running simulator, or an Xcode that uniquely provides a required
SDK/toolchain.
- Report summed candidate sizes separately from actual APFS space recovered.
Read references/safety-model.md before applying cleanup.
Workflow
1. Preflight
- Confirm the host is macOS and the bundled script is available.
- Check for active Xcode, Simulator,
xcodebuild, Swift build, test, archive, and
package-resolution processes.
- Treat an open Xcode or Simulator application as context, not an automatic
cleanup blocker. Defer an affected cleanup when a build, test, archive, or
package-resolution process is active, an exact candidate is actively needed
by that operation, or a supported deletion API requires the app/device to be
stopped.
- Do not ask the user to quit apps merely because they appear in
ps. For
regenerable documentation caches, moving an old cache while Xcode is open may
temporarily remove local documentation until it reloads, but does not require
quitting Xcode or Simulator. State that cost instead.
- Determine optional source roots for project-local
.build and .derived-data
scanning. Do not crawl the whole home directory.
2. Run the read-only audit
python3 "${SKILL_DIR}/scripts/xcode_disk_cleanup.py" audit \
--output-dir .xcode-disk-cleanup-audit \
[--scan-root /path/to/projects]
Read both generated files:
.xcode-disk-cleanup-audit/report.md
.xcode-disk-cleanup-audit/audit.json
The script audits DerivedData, documentation caches, CocoaPods caches, simulator
devices and runtimes (including stale superseded beta runtimes), orphan runtime
volumes, simulator dyld caches, Xcode-managed components, Xcode installers,
installed Xcodes, archives (including never-distributed orphans), DeviceSupport
for every platform, diagnostic logs, XCTest device clones, legacy DocSets, and
explicitly requested project roots.
Shared compiler caches (ModuleCache.noindex, precompiled SDK modules) and the
global SwiftPM download cache are deliberately out of scope: they are always in
use on an active development machine, and clearing them trades real build-time
pain for little lasting space. Do not propose them.
3. Validate and enrich findings
Use the category router:
| Finding |
Reference |
DerivedData, module caches, project .build, CocoaPods |
references/generated-data.md |
| Simulator devices, runtimes, dyld caches, XCTest clones |
references/simulators.md |
| Archives, dSYMs, DeviceSupport, logs, DocSets |
references/release-artifacts.md |
| Xcode DMGs/XIPs and installed Xcodes |
references/xcode-installations.md |
| Confirmation and deletion behavior |
references/safety-model.md |
Do not mechanically accept a scanner classification. Verify uncertain ownership,
custom paths, backups, and toolchain requirements.
4. Present the proposal
Sort by recoverable GiB and show:
- Candidate ID
- Category and exact path/identifier
- Recoverable GiB
- Risk: regenerable, destructive, or preserve
- Evidence — a short "why this is stale" phrase (age in days, superseded-by,
missing workspace), not just a classification label
- What must be rebuilt, redownloaded, or permanently lost
- Proposed action
Keep the proposal scannable:
- Render folder candidates as clickable
file:// links so the user can inspect
them before approving. Show the candidate ID only where the user needs it to
approve, not as the item's display name.
- Carry the audit's
evidence and reason fields into your notes. The script
already explains why each item is stale ("Unchanged for 4 days", "Superseded
by 16.4.1 (325) archived 2026-07-21", "the same documentation stays available");
dropping that context turns an explainable proposal into a bare file list.
- Hide individual items below roughly 0.5 GiB; the full itemized list stays in
report.md for reference. Do not pad the proposal with a "small items" bucket —
it adds questions, not signal.
- Group unavailable simulators into a table per runtime.
- Give every category its own subheading with its total size, even small ones
like documentation caches or orphan archives. A category summarized in a
trailing paragraph under another category's table gets overlooked, and
overlooked items cannot be meaningfully approved.
- Trust the scanner's classification unless you have live contrary evidence.
In particular,
Orphan archive candidates already encode proof (no Organizer
distribution record plus a newer archive of the same app); do not demote them
back to preserve just because archives are preserve-by-default elsewhere.
Format each category like this example, adapting labels to the machine:
### Orphaned DerivedData — 15.5 GiB · regenerable
All four belong to workspaces that no longer exist.
| Folder | Size | Why stale |
|---|---:|---|
| [RocketSim-beqqrx…](file:///Users/me/Library/Developer/Xcode/DerivedData/RocketSim-beqqrximmvoaakbpvjshhqhuacyv) | 5.6 GiB | Workspace deleted; unchanged for 1 day |
| [RocketSim-fjapct…](file:///Users/me/Library/Developer/Xcode/DerivedData/RocketSim-fjapctdhvtzcaaabmtsyumkmcarf) | 4.0 GiB | Only package downloads, no build products; unchanged for 4 days |
Include:
- Total candidate GiB by risk
- Current free disk space
- A warning that APFS cloning and snapshots make candidate sizes non-additive
- A direct request for approval of exact IDs. When the user selects a category,
restate every selected ID, the summed size, risk, and proposed action in a
single frozen selection set. State once that a later plain-language removal
request approves the matching set. Do not ask the user to repeat IDs, use a
prescribed phrase, or reconfirm after an unchanged revalidation.
5. Expand category selections and apply only approved IDs
After a completed audit, a request such as “delete all stale DerivedData” or
“clean up all outdated documentation caches” may approve every currently audited
candidate in that category only when all of the following hold:
- Every selected item has already passed the category-specific validation and
has an exact stable ID, path or simulator identifier, size, risk, and action.
- The assistant prints the complete expanded ID list, summed size, and action
as a frozen selection set and explains that a plain-language category request
will approve it.
- The user then makes an unambiguous, affirmative request to remove that
category. This is the single approval and authorizes only the IDs in the
displayed set, not the category in general. Apply it immediately after
revalidation; never require a magic phrase or another user turn.
- Revalidation succeeds. A refreshed audit does not invalidate approval when
every approved candidate retains the same ID, identity, risk, and action.
Silently use the refreshed audit and proceed. Never add a new candidate.
- If an approved item changes, becomes active, or is reclassified, exclude and
report that item. Continue with unchanged approved items when safe; ask again
only if proceeding would materially change the approved scope or action.
Never infer category membership from a broad request before the audit, and never
include preserve items, archives/dSYMs, active assets, or simulator operations
unless their separate safeguards and approvals are met.
Invoke apply only after direct per-ID approval or a plain-language approval of a
frozen expanded ID set from the current audit:
python3 "${SKILL_DIR}/scripts/xcode_disk_cleanup.py" apply \
--audit-file .xcode-disk-cleanup-audit/audit.json \
--ids "<approved-id-1>" "<approved-id-2>" \
--confirm I_APPROVED_THE_SELECTED_ITEMS \
--output .xcode-disk-cleanup-audit/cleanup-result.json
Simulator device and runtime deletions are irreversible and require a second
approval plus:
--confirm-irreversible I_APPROVED_IRREVERSIBLE_SIMULATOR_DELETION
A stale runtime is only proposed when simctl reports it deletable, no installed
Xcode SDK resolves to its build, and a newer runtime supersedes it on the same
platform. This is the pattern behind leftover beta platforms in Xcode Settings →
Components after installing a newer Xcode beta.
Do not pass either confirmation phrase unless the corresponding user approval is
present in the conversation.
“Separate approval” means distinct, explicit intent for the irreversible
Simulator category, not necessarily a separate message. After exact Simulator
IDs and permanent data loss have been shown, a single request such as “remove
the DerivedData and unavailable simulators” contains both ordinary approval and
separate category-specific irreversible approval. Revalidate and execute both in
that turn. A generic “clean everything” does not provide irreversible approval.
6. Verify
- Re-run the audit.
- Compare
df free space before and after.
- Distinguish “moved to Trash” from immediately reclaimed space.
- Confirm protected and unapproved candidates remain.
- Report successes, failures, and deferred items.
Xcode installer and application rules
- Search
.xip, .dmg, and .zip installers through Spotlight and common
download folders.
- Suggest an installer only when its parsed version matches an installed Xcode;
otherwise report it as uncertain.
- An older Xcode may only be suggested, never automatically selected, when it is
not active/running and a newer same-channel installation exists.
- Spotlight
kMDItemLastUsedDate reflects LaunchServices opens, not command-line
use. Combine it with xcode-select, DEVELOPER_DIR, running processes,
.xcode-version, CI scripts, and unique SDK/toolchain evidence.
- Never infer that a stable Xcode supersedes a beta, or vice versa.
Output quality checklist
1---2name: xcode-disk-cleanup3description: Audit and safely clean Xcode-related developer storage on macOS. Use whenever a developer mentions low disk space, Xcode storage, DerivedData, simulator devices or runtimes, stale beta platforms in Xcode Settings, DeviceSupport, archives, dSYMs, CocoaPods caches, simulator dyld caches, project .build folders, downloaded Xcode DMGs/XIPs, duplicate Xcode installers, or old Xcode applications. Also use this skill proactively when you hit low storage during other work, such as a build, download, or install failing with an out-of-disk-space error. Always measure first, report recoverable GiB with evidence, and obtain explicit itemized approval before any mutation.4---56# Xcode Disk Cleanup78## Safety contract910- Treat every cleanup request as permission to audit, not permission to delete.11- Never mutate storage before presenting exact candidate IDs, paths or simulator12 identifiers, measured sizes, risks, and regeneration costs.13- Treat a category-level request made after an audit (for example, “remove all14 outdated documentation caches”) as approval of the matching, current frozen15 selection set when it was already fully enumerated to the user. Execute in16 that turn after revalidation; do not restate the set and ask again. No special17 command or repeated confirmation is required. Broad requests made before an18 audit, such as “clean Xcode” or “delete everything,” remain audit-only.19- Default ordinary files and directories to Trash. Explain that space is not20 reclaimed until Trash is emptied, and request separate approval before doing so.21- Revalidate identity and running-process usage immediately before mutation.22- Never use wildcards, unbounded `rm -rf`, `sudo`, SIP changes, or manual deletion23 inside system-managed CoreSimulator and MobileAsset directories.24- Preserve archives and dSYMs unless the user proves the exact distributed build25 is backed up elsewhere and separately approves deletion.26- Never delete `Package.resolved`, signing assets, credentials, custom DocSets, the27 active Xcode, a running simulator, or an Xcode that uniquely provides a required28 SDK/toolchain.29- Report summed candidate sizes separately from actual APFS space recovered.3031Read `references/safety-model.md` before applying cleanup.3233## Workflow3435### 1. Preflight36371. Confirm the host is macOS and the bundled script is available.382. Check for active Xcode, Simulator, `xcodebuild`, Swift build, test, archive, and39 package-resolution processes.403. Treat an open Xcode or Simulator application as context, not an automatic41 cleanup blocker. Defer an affected cleanup when a build, test, archive, or42 package-resolution process is active, an exact candidate is actively needed43 by that operation, or a supported deletion API requires the app/device to be44 stopped.454. Do not ask the user to quit apps merely because they appear in `ps`. For46 regenerable documentation caches, moving an old cache while Xcode is open may47 temporarily remove local documentation until it reloads, but does not require48 quitting Xcode or Simulator. State that cost instead.495. Determine optional source roots for project-local `.build` and `.derived-data`50 scanning. Do not crawl the whole home directory.5152### 2. Run the read-only audit5354```bash55python3 "${SKILL_DIR}/scripts/xcode_disk_cleanup.py" audit \56 --output-dir .xcode-disk-cleanup-audit \57 [--scan-root /path/to/projects]58```5960Read both generated files:6162- `.xcode-disk-cleanup-audit/report.md`63- `.xcode-disk-cleanup-audit/audit.json`6465The script audits DerivedData, documentation caches, CocoaPods caches, simulator66devices and runtimes (including stale superseded beta runtimes), orphan runtime67volumes, simulator dyld caches, Xcode-managed components, Xcode installers,68installed Xcodes, archives (including never-distributed orphans), DeviceSupport69for every platform, diagnostic logs, XCTest device clones, legacy DocSets, and70explicitly requested project roots.7172Shared compiler caches (`ModuleCache.noindex`, precompiled SDK modules) and the73global SwiftPM download cache are deliberately out of scope: they are always in74use on an active development machine, and clearing them trades real build-time75pain for little lasting space. Do not propose them.7677### 3. Validate and enrich findings7879Use the category router:8081| Finding | Reference |82|---|---|83| DerivedData, module caches, project `.build`, CocoaPods | `references/generated-data.md` |84| Simulator devices, runtimes, dyld caches, XCTest clones | `references/simulators.md` |85| Archives, dSYMs, DeviceSupport, logs, DocSets | `references/release-artifacts.md` |86| Xcode DMGs/XIPs and installed Xcodes | `references/xcode-installations.md` |87| Confirmation and deletion behavior | `references/safety-model.md` |8889Do not mechanically accept a scanner classification. Verify uncertain ownership,90custom paths, backups, and toolchain requirements.9192### 4. Present the proposal9394Sort by recoverable GiB and show:95961. Candidate ID972. Category and exact path/identifier983. Recoverable GiB994. Risk: regenerable, destructive, or preserve1005. Evidence — a short "why this is stale" phrase (age in days, superseded-by,101 missing workspace), not just a classification label1026. What must be rebuilt, redownloaded, or permanently lost1037. Proposed action104105Keep the proposal scannable:106107- Render folder candidates as clickable `file://` links so the user can inspect108 them before approving. Show the candidate ID only where the user needs it to109 approve, not as the item's display name.110- Carry the audit's `evidence` and `reason` fields into your notes. The script111 already explains *why* each item is stale ("Unchanged for 4 days", "Superseded112 by 16.4.1 (325) archived 2026-07-21", "the same documentation stays available");113 dropping that context turns an explainable proposal into a bare file list.114- Hide individual items below roughly 0.5 GiB; the full itemized list stays in115 `report.md` for reference. Do not pad the proposal with a "small items" bucket —116 it adds questions, not signal.117- Group unavailable simulators into a table per runtime.118- Give every category its own subheading with its total size, even small ones119 like documentation caches or orphan archives. A category summarized in a120 trailing paragraph under another category's table gets overlooked, and121 overlooked items cannot be meaningfully approved.122- Trust the scanner's classification unless you have live contrary evidence.123 In particular, `Orphan archive` candidates already encode proof (no Organizer124 distribution record plus a newer archive of the same app); do not demote them125 back to preserve just because archives are preserve-by-default elsewhere.126127Format each category like this example, adapting labels to the machine:128129```markdown130### Orphaned DerivedData — 15.5 GiB · regenerable131132All four belong to workspaces that no longer exist.133134| Folder | Size | Why stale |135|---|---:|---|136| [RocketSim-beqqrx…](file:///Users/me/Library/Developer/Xcode/DerivedData/RocketSim-beqqrximmvoaakbpvjshhqhuacyv) | 5.6 GiB | Workspace deleted; unchanged for 1 day |137| [RocketSim-fjapct…](file:///Users/me/Library/Developer/Xcode/DerivedData/RocketSim-fjapctdhvtzcaaabmtsyumkmcarf) | 4.0 GiB | Only package downloads, no build products; unchanged for 4 days |138```139140Include:141142- Total candidate GiB by risk143- Current free disk space144- A warning that APFS cloning and snapshots make candidate sizes non-additive145- A direct request for approval of exact IDs. When the user selects a category,146 restate every selected ID, the summed size, risk, and proposed action in a147 single frozen selection set. State once that a later plain-language removal148 request approves the matching set. Do not ask the user to repeat IDs, use a149 prescribed phrase, or reconfirm after an unchanged revalidation.150151### 5. Expand category selections and apply only approved IDs152153After a completed audit, a request such as “delete all stale DerivedData” or154“clean up all outdated documentation caches” may approve every currently audited155candidate in that category only when all of the following hold:1561571. Every selected item has already passed the category-specific validation and158 has an exact stable ID, path or simulator identifier, size, risk, and action.1592. The assistant prints the complete expanded ID list, summed size, and action160 as a frozen selection set and explains that a plain-language category request161 will approve it.1623. The user then makes an unambiguous, affirmative request to remove that163 category. This is the single approval and authorizes only the IDs in the164 displayed set, not the category in general. Apply it immediately after165 revalidation; never require a magic phrase or another user turn.1664. Revalidation succeeds. A refreshed audit does not invalidate approval when167 every approved candidate retains the same ID, identity, risk, and action.168 Silently use the refreshed audit and proceed. Never add a new candidate.1695. If an approved item changes, becomes active, or is reclassified, exclude and170 report that item. Continue with unchanged approved items when safe; ask again171 only if proceeding would materially change the approved scope or action.172173Never infer category membership from a broad request before the audit, and never174include preserve items, archives/dSYMs, active assets, or simulator operations175unless their separate safeguards and approvals are met.176177Invoke apply only after direct per-ID approval or a plain-language approval of a178frozen expanded ID set from the current audit:179180```bash181python3 "${SKILL_DIR}/scripts/xcode_disk_cleanup.py" apply \182 --audit-file .xcode-disk-cleanup-audit/audit.json \183 --ids "<approved-id-1>" "<approved-id-2>" \184 --confirm I_APPROVED_THE_SELECTED_ITEMS \185 --output .xcode-disk-cleanup-audit/cleanup-result.json186```187188Simulator device and runtime deletions are irreversible and require a second189approval plus:190191```text192--confirm-irreversible I_APPROVED_IRREVERSIBLE_SIMULATOR_DELETION193```194195A stale runtime is only proposed when `simctl` reports it deletable, no installed196Xcode SDK resolves to its build, and a newer runtime supersedes it on the same197platform. This is the pattern behind leftover beta platforms in Xcode Settings →198Components after installing a newer Xcode beta.199200Do not pass either confirmation phrase unless the corresponding user approval is201present in the conversation.202203“Separate approval” means distinct, explicit intent for the irreversible204Simulator category, not necessarily a separate message. After exact Simulator205IDs and permanent data loss have been shown, a single request such as “remove206the DerivedData and unavailable simulators” contains both ordinary approval and207separate category-specific irreversible approval. Revalidate and execute both in208that turn. A generic “clean everything” does not provide irreversible approval.209210### 6. Verify2112121. Re-run the audit.2132. Compare `df` free space before and after.2143. Distinguish “moved to Trash” from immediately reclaimed space.2154. Confirm protected and unapproved candidates remain.2165. Report successes, failures, and deferred items.217218## Xcode installer and application rules219220- Search `.xip`, `.dmg`, and `.zip` installers through Spotlight and common221 download folders.222- Suggest an installer only when its parsed version matches an installed Xcode;223 otherwise report it as uncertain.224- An older Xcode may only be suggested, never automatically selected, when it is225 not active/running and a newer same-channel installation exists.226- Spotlight `kMDItemLastUsedDate` reflects LaunchServices opens, not command-line227 use. Combine it with `xcode-select`, `DEVELOPER_DIR`, running processes,228 `.xcode-version`, CI scripts, and unique SDK/toolchain evidence.229- Never infer that a stable Xcode supersedes a beta, or vice versa.230231## Output quality checklist232233- [ ] Audit completed without mutation234- [ ] Every item has an exact size and stable ID235- [ ] Candidate and actual recovered space are separate236- [ ] Archives/dSYMs are preserve-by-default237- [ ] Active processes and selected Xcode are protected238- [ ] User directly approved exact IDs or made an unambiguous category request239 after its fully enumerated frozen selection set was shown240- [ ] Irreversible operations received separate approval241- [ ] Post-cleanup audit confirms only approved items changed