xcode-disk-cleanup
Xcode Disk Cleanup
Section titled “Xcode Disk Cleanup”Safety contract
Section titled “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
Section titled “Workflow”1. Preflight
Section titled “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
.buildand.derived-datascanning. Do not crawl the whole home directory.
2. Run the read-only audit
Section titled “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
Section titled “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
Section titled “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
evidenceandreasonfields 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.mdfor 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 archivecandidates 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
Section titled “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.jsonSimulator device and runtime deletions are irreversible and require a second approval plus:
--confirm-irreversible I_APPROVED_IRREVERSIBLE_SIMULATOR_DELETIONA 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
Section titled “6. Verify”- Re-run the audit.
- Compare
dffree 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
Section titled “Xcode installer and application rules”- Search
.xip,.dmg, and.zipinstallers 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
kMDItemLastUsedDatereflects LaunchServices opens, not command-line use. Combine it withxcode-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
Section titled “Output quality checklist”- Audit completed without mutation
- Every item has an exact size and stable ID
- Candidate and actual recovered space are separate
- Archives/dSYMs are preserve-by-default
- Active processes and selected Xcode are protected
- User directly approved exact IDs or made an unambiguous category request after its fully enumerated frozen selection set was shown
- Irreversible operations received separate approval
- Post-cleanup audit confirms only approved items changed