Roomy / Documentation
v1.3.0 · macOS & Linux

roomy CLI reference

The same engine as the Mac app, for terminals, scripts and AI agents. One universal binary on macOS, one static binary on Linux. It never deletes anything without a plan file, prints versioned JSON, and refuses irreversible items unless told otherwise.

Last reviewed: October 27, 2026 · roomy --help and roomy <command> --help are authoritative.

Install

macOS (universal, Apple Silicon and Intel):

brew install tdermendjiev/roomy/roomy

Linux (x86_64 and arm64, one static binary, no runtime to install):

sudo dpkg -i roomy_1.3.0_amd64.deb                 # Debian, Ubuntu: installs /usr/bin/roomy, systemd units, /etc/roomy/policy.json
sudo rpm -i roomy-1.3.0-1.x86_64.rpm               # RHEL, Fedora
apk add --allow-untrusted roomy_1.3.0_x86_64.apk   # Alpine
tar xzf roomy-1.3.0-linux-amd64.tar.gz && sudo install -m 755 roomy /usr/bin/
docker run --rm ghcr.io/tdermendjiev/roomy version      # container image

Then check the environment:

roomy doctor

doctor reports whether du, git, docker and conda are available, whether the Trash and quarantine directory are usable, the volume capacity and the license in effect. Nothing is modified except creating the quarantine directory.

Package upgrades never overwrite /etc/roomy/policy.json. On Linux the packages also install the fleet systemd units and the hub service; both are inactive until enabled.

Commands

roomy scan [paths…] [--json] [--plan FILE] …    find reclaimable artifacts (never deletes)
roomy clean --plan FILE [--dry-run] [--yes] …    remove exactly what the plan lists
roomy purge [--older-than DAYS] [--trash]        empty expired quarantine (and Linux Trash)
roomy restore [PATH]                             list or restore quarantined items
roomy rules [--json] [--platform P]              show the rule catalog
roomy doctor                                     environment check
roomy fleet …                                    policy-driven, unattended runs   → Fleet
roomy hub …                                      central inventory and dashboard  → Hub
roomy version [--json]

The typical flow is three commands. The first produces data, the second writes a file, the third does exactly what the file says.

roomy scan --json                 # what could be reclaimed, as data
roomy scan --plan plan.json       # write a plan (safe tier by default)
roomy clean --plan plan.json      # remove exactly what the plan lists

Exit codes

CodeMeaning
0OK, including "nothing to do"
1Error, or clean refused to proceed without confirmation
3Finished, but some items failed to dispose
4Another fleet run holds the lock
5clean refused the plan because it contains items above the allowed safety tier
64Usage error (bad flag or value)

roomy scan

Finds artifacts under one or more roots (default: the home directory) and probes the fixed-path tool caches for the current platform. It never deletes.

roomy scan [<roots>…]
  --home DIR                 home to probe for tool caches (default: current user)
  --categories a b …         system categories: aiTools agentHost xcode globalCaches globalModules python homebrew docker
  --ecosystems a b …         ai npm py rust ruby go java swift dotnet general
  --exclude P …              paths or globs to skip
  --rules FILE …             extra rule catalogs merged over the built-in one
  --disable-rule ID …        switch rules off
  --no-system | --no-projects
  --json                     full ScanReport on stdout
  --plan FILE                write a CleanupPlan
  --plan-tier safe|caution|warning     highest tier admitted to the plan (default safe)
  --min-idle-days N          only plan items idle ≥ N days
  --require-known-idle       exclude items whose idle time is unknown
  --method trash|quarantine|delete     disposal recorded in the plan
  --top N                    table rows (default 40)
  -q, --quiet                no progress on stderr

The walk never enters tool-owned trees such as ~/.nvm, ~/.cargo, ~/.vscode-server or ~/Library, because a node_modules inside them is part of a tool's install, not a project artifact. The system probes cover the ones worth reclaiming. A skipped directory is still scanned when it is given explicitly as the root.

ScanReport JSON

--json prints a ScanReport with schemaVersion: 1. The shape is additive-only: fields are added, never renamed or removed.

{
  "schemaVersion": 1, "roomyVersion": "1.3.0", "catalogVersion": "2026.10.27",
  "generatedAt": "2026-10-27T00:00:00Z", "host": "agent-07", "platform": "linux",
  "scanRoot": "/home/agent", "home": "/home/agent", "durationSeconds": 1.2, "partial": false,
  "totalBytes": 1610612736,
  "bytesByTier": { "safe": 1500000000, "caution": 0, "warning": 1048576 },
  "bytesByCategory": { "project": 900000000, "aiTools": 600000000, "globalCaches": 110000000 },
  "bytesByEcosystem": { "py": 900000000, "ai": 600000000, "npm": 110000000 },
  "projectCount": 3,
  "volume": { "totalBytes": 1374000000000, "availableBytes": 1288000000000 },
  "items": [
    {
      "id": "9f1c…",                 // stable per path
      "path": "/home/agent/.cache/uv", "displayPath": "~/.cache/uv",
      "label": "uv cache", "bytes": 492000000,
      "tier": "safe", "ecosystem": "py", "category": "globalCaches",
      "group": "Global package caches", "source": "systemProbe", "ruleID": "uv-cache",
      "restoreHint": "uv cache clean",
      "lastActivity": "2026-10-20T10:00:00Z", "idleDays": 7, "activity": "recent"
    }
  ]
}

category is project for project-walk findings or the system category key. source is one of projectWalk, systemProbe, dockerCLI, condaCLI, pipCLI; the last three have synthetic paths such as docker:prune:dangling, conda:env:foo or pip:orphan:/path/.venv.

roomy clean

Executes a plan file and nothing else.

roomy clean --plan FILE
  --method M                 override the plan's method
  --allow-caution            admit review-tier items
  --allow-irreversible       admit irreversible items (session history, archives, volumes)
  --dry-run                  report, touch nothing
  -y, --yes                  skip confirmation (required when stdin is not a TTY)
  --json                     summary on stdout
  --audit FILE               audit log (default ~/.roomy/audit.jsonl)
  --quarantine-dir DIR       (quarantine) default ~/.roomy/quarantine or $ROOMY_QUARANTINE_DIR
  --retention-days N         recorded for purge (default 7)
  --actor NAME               recorded in the audit log, e.g. "claude-code"

Rules of engagement

  1. Only paths in the plan are touched. Items that no longer exist are skipped and reported.
  2. Tier is enforced at clean time, not plan time: a plan containing a warning item is refused with exit 5 unless --allow-irreversible is passed, even if the plan was written with --plan-tier warning.
  3. Without a TTY, --yes is mandatory. Agents should pass --yes only after inspecting the plan.
  4. Every receipt and failure is appended to the audit log, with the --actor name.

Disposal methods

MethodWhat happensUndo
trashFinder Trash on macOS, the freedesktop Trash on desktop Linux. Default on macOS.Put back from the Trash
quarantineRename into ~/.roomy/quarantine on the same volume with a manifest. Instant. Default on Linux.roomy restore until purged
deletePermanent removal.None

Docker, Conda and pip findings are always cleaned through their own tool (docker rmi, conda clean, pip uninstall), whatever the method.

CleanupPlan JSON

A plan is a small JSON file, schemaVersion: 1. roomy scan --plan writes one; any tool may write one by filtering a report's items and keeping the fields below.

{
  "schemaVersion": 1, "createdAt": "…", "host": "…", "method": "quarantine", "reason": "roomy scan --plan",
  "items": [
    { "id": "…", "path": "…", "label": "…", "bytes": 1, "tier": "safe",
      "category": "project", "source": "projectWalk", "ruleID": "node_modules" }
  ]
}

roomy clean validates existence and tier of every item at run time, so a stale or over-eager plan fails closed.

roomy purge and roomy restore

Quarantine keeps a <timestamp>-<name>.roomy.json manifest next to each item with the original path, size and time.

roomy restore                       # list what is in quarantine
roomy restore legacy-app/node_modules   # move one item back (path suffix)
roomy purge --older-than 7          # permanently remove expired items; this is when space is freed
roomy purge --trash                 # also empty the freedesktop Trash on Linux

Items on another volume are quarantined in <mount>/.roomy-quarantine so the move stays a rename.

roomy rules and roomy doctor

The rule catalog is data, embedded into every binary. roomy rules prints project rules and system rules for a platform; --json prints the whole catalog in the same schema accepted by --rules FILE and a policy's scan.extraRules.

roomy rules --platform linux
roomy rules --json > catalog.json
roomy scan --rules /etc/roomy/rules.d/our-agent.json      # merge a custom catalog
roomy scan --disable-rule cursor-state-backup             # switch one rule off

A custom catalog uses the same schema. Only id is required; a rule with an existing id replaces the built-in one, new ids are appended. That is how a fleet adds a new agent's cache directory without waiting for a release.

{
  "schemaVersion": 1,
  "systemRules": [
    {
      "id": "our-agent-cache",
      "category": "agentHost",           // aiTools agentHost xcode globalCaches globalModules python homebrew docker
      "label": "Our agent's scratch cache",
      "ecosystem": "ai",
      "tier": "safe",                    // safe | caution | warning
      "restoreHint": "Recreated on the next run",
      "activity": "newestChild",         // none | self | newestChild
      "paths": { "linux": ["$XDG_CACHE_HOME/our-agent"], "macos": ["Library/Caches/our-agent"] }
    }
  ]
}

Path specs are home-relative by default; ~/… or absolute paths, $VAR with XDG defaults, and globs in any component are allowed. Findings under 1 MB are dropped.

Safety tiers in the CLI

TierMeaningscan --planclean
safeFully regenerableDefaultDefault
cautionRegenerable at a cost, or mixed with user data--plan-tier caution--allow-caution
warningNot regenerable: session history, archives, volumes--plan-tier warning--allow-irreversible

Linux notes

Trash vs quarantine

Desktop Linux has the freedesktop Trash and Roomy supports it (--method trash). On a headless host nothing ever empties the Trash, so space is never reclaimed while the user believes it was. Roomy's default on Linux is therefore quarantine: items are renamed into ~/.roomy/quarantine with a manifest, and roomy purge or a fleet run with purgeOnRun frees them after the retention period. The agentHost category also reports an un-emptied Trash as a finding, because on agent hosts that is often gigabytes.

Which user

Artifacts belong to the user who created them. Run Roomy as that user. To scan a different home as root, pass --home on the CLI or set scan.home in a policy. Roomy respects XDG_CACHE_HOME, XDG_DATA_HOME, HF_HOME, UV_CACHE_DIR, GOCACHE, TMPDIR and OPENCLAW_STATE_DIR when resolving catalog paths.

Containers

The image runs roomy as its entrypoint. Mount the home to clean and the policy:

docker run --rm -v /home/agent:/home/agent -v /etc/roomy:/etc/roomy:ro \
  ghcr.io/tdermendjiev/roomy scan --home /home/agent --json

docker run -d --name roomy -p 9477:9477 -v /home/agent:/home/agent -v /etc/roomy:/etc/roomy:ro \
  ghcr.io/tdermendjiev/roomy fleet serve --listen 0.0.0.0:9477

Set scan.home to the mounted path. Mount /var/run/docker.sock to enable the Docker category inside the container.

What differs from macOS

CapabilitymacOSLinux
Project walk, all project rules✓✓
AI tools, agent host, package caches, Docker, Conda, pyenv, global modules✓✓
Xcode, simulators, Homebrew✓— (nothing to find)
TrashFinderfreedesktop
Quarantine✓✓ (default)
Fleet and Hub✓✓

For AI agents

An agent that hits ENOSPC mid-task, or is asked to free space, needs a tool that is structured, dry-run first, and incapable of deleting anything outside an explicit plan. That is the contract:

  1. Scan produces data. roomy scan --json never deletes. Every item has a stable id, a tier, a category, bytes, idleDays and a restoreHint.
  2. Plan is a file. Either roomy scan --plan plan.json (safe tier by default) or a plan the agent writes by filtering the report's items.
  3. Clean executes the plan and nothing else. Review-tier items need --allow-caution; irreversible items need --allow-irreversible. An agent should not pass the latter without a human in the loop.
  4. Disposal is recoverable. Trash on macOS, quarantine on Linux. roomy restore undoes.
  5. Everything is audited. Every removal is appended to ~/.roomy/audit.jsonl with the --actor name. Pass your tool name.

Recommended sequence

roomy doctor --json                                   # optional: is roomy usable here?
roomy scan --json --quiet > /tmp/roomy-report.json    # inspect bytesByTier, items
roomy scan --plan /tmp/roomy-plan.json --min-idle-days 7 --quiet
roomy clean --plan /tmp/roomy-plan.json --dry-run --json       # show the human (or your log) what will happen
roomy clean --plan /tmp/roomy-plan.json --yes --json --actor "claude-code"

Read bytesByTier.safe first: if it is already enough, stop there. Only escalate to caution with explicit user approval, and never automate warning.

What the categories mean

  • project — artifacts inside repositories (node_modules, .venv, target/). Safe. Regenerate with the install command in restoreHint.
  • aiTools — the agents' own storage. Models and caches are safe; session history is irreversible (Claude Code projects, Codex sessions, Cline tasks, Cursor's state.vscdb).
  • agentHost — leftovers on servers: OpenClaw build captures, crash dumps, Claude Code scratch dirs, stale Cursor agent builds, un-emptied Trash.
  • globalCaches, python, docker, globalModules, xcode, homebrew — tool caches outside repositories.

A Claude Code skill and an AGENTS.md snippet with the sequence above ship in the repository. There is no MCP server yet; the CLI with --json is the integration surface.

Environment variables

VariableEffect
ROOMY_POLICYDefault policy path for roomy fleet
ROOMY_HUB_URL, ROOMY_HUB_TOKENDefaults for roomy hub status/hosts/…, and the agent token for a host when the policy has none
ROOMY_HUB_DATA, ROOMY_HUB_AGENT_TOKEN, ROOMY_HUB_ADMIN_TOKENDefaults for roomy hub serve
ROOMY_LICENSELicense token (alternative to a key file)
ROOMY_QUARANTINE_DIRDefault quarantine directory
ROOMY_HOSTNAMEHost label in reports, audit and metrics
XDG_DATA_HOMEWhere the freedesktop Trash lives on Linux

Home · Mac app · Fleet · Hub · Release notes · Privacy · EULA · Source on GitHub