Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"name": "devclean",
"description": "devclean plugin for Claude Code",
"owner": {
"name": "Youngsup Oh"
},
"plugins": [
{
"name": "devclean",
"source": "./plugins/devclean",
"description": "Find reclaimable disk space in developer environments and clean the artifacts you approve, using the devclean CLI"
}
]
}
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ go test ./internal/ -run TestGolden -update # regenerate golden files after
|---|---|---|
| `docs/SPEC.md` | behavior the user approved: commands, flags, JSON, safety levels, ecosystem catalog, config | behavior changes |
| `docs/ARCHITECTURE.md` | module boundaries, cross-module contracts, adding an ecosystem | a boundary or contract changes |
| `plugins/devclean/skills/devclean/SKILL.md` | the Claude Code skill that runs the binary for the user | commands, flags, JSON fields or safety gates change |
| `docs/decisions/` | one costly-to-reverse choice per file: decision, verification, rationale, rejected alternatives | such a choice is made or reversed |
| `CHANGELOG.md` | release sections written by release-please from PR titles | never by hand (`docs/decisions/release-please.md`) |

Expand Down
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,16 @@ go build -o devclean ./cmd/devclean
> A Homebrew formula is planned for a later release. Until then, use one of the
> options above.

## Claude Code plugin

Install the binary first, then the plugin (Claude Code 2.1.275 or later), and ask Claude to free disk space:

```text
/plugin install devclean --marketplace ohing504/devclean
```

The skill scans with `devclean scan --json`, proposes artifacts, and cleans only the paths you approve.

## Usage and documentation

- [Spec](docs/SPEC.md) — commands and flags, safety levels, supported ecosystems, configuration
Expand Down
6 changes: 5 additions & 1 deletion docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Module boundaries and the contracts several modules keep together. User-visible
Scan → Classify → Filter/Sort → Output/Clean
```

devclean is a monolithic Go CLI binary: every ecosystem scanner is built in, with no external plugins.
devclean is a monolithic Go CLI binary: every ecosystem scanner is built in, with no external scanner plugins.

1. **Scan**: Registry batches all project (walk-based) scanners into a single filesystem pass, then runs stat scanners (fixed paths) sequentially. Real-time progress via spinner.
2. **Classify**: activity status + gitignore-aware protection ([SPEC](SPEC.md#classification))
Expand Down Expand Up @@ -107,6 +107,10 @@ Implemented commands: [SPEC](SPEC.md#vendor-cleanups). Natural future fits: Dock

Classification forks git per repo (`git rev-parse` to resolve roots, `git status` + `git log` for protection and last-commit time, `git check-ignore` per dirty root). These run across a bounded worker pool (`min(NumCPU, 8)`) — root resolution over distinct project dirs, then git info over distinct roots, then check-ignore over dirty roots — instead of serially. As with sizing, the win is overlapping the forks, not replacing them; results are applied serially afterwards so output stays deterministic regardless of scheduling.

## Claude Code plugin

`plugins/devclean/` is a Claude Code plugin holding one skill; the repository root's `.claude-plugin/marketplace.json` lists it ([decision](decisions/claude-code-plugin.md)). The skill drives the installed binary through `scan --json` and `clean --item … --yes`, so it depends on the flags, JSON fields and safety gates in SPEC.

## Adding an ecosystem

Pick the scanner family by where the artifacts live.
Expand Down
22 changes: 22 additions & 0 deletions docs/decisions/claude-code-plugin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Ship the agent skill as a Claude Code plugin from this repository

## Decision

The repository root is a Claude Code marketplace (`.claude-plugin/marketplace.json`) listing one plugin, `plugins/devclean/`, which holds one skill. The skill runs the `devclean` binary on the user's PATH; the plugin ships no binary.

- The plugin sets no `version`, so installs track the repository's commits instead of a second version number next to release-please's.
- The skill measures with `scan --json` and deletes with `clean --item … --yes`; size accounting and the safety gates (`protected` never deleted, `caution` only with `--include-caution`) stay in the binary.
- The human interface (table, interactive selector) stays; removing it is a separate decision after the skill has been used.

## Rationale

- Skills under a repository's `.claude/skills/` load only in sessions working in that repository; devclean is run from any directory, and Claude Code's docs direct skills used across repositories to a plugin.
- Keeping the plugin in this repository lets a PR that changes a flag or JSON field change the skill in the same diff.
- An agent rebuilding sizes with `du` or deleting with `rm` gets APFS clones, hard links and sparse files wrong without an error, and skips the safety gates; the binary already handles both.

## Rejected alternatives

- **Skill in `~/.claude/skills/`** — loads everywhere for one user, but changes separately from the CLI it describes, and others cannot install it.
- **Skill only, no CLI** (find/du/rm recipes in the skill) — the size and classification bugs fixed in the scanner would come back as silent errors on each run.
- **Binary bundled in the plugin** — needs per-OS builds inside the plugin and duplicates the GoReleaser release.
- **Plugin `version` bumped by release-please** — would tie skill updates to releases, but adds a second version file to keep in sync for a skill that is plain text.
10 changes: 10 additions & 0 deletions plugins/devclean/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"name": "devclean",
"description": "Find reclaimable disk space in developer environments and clean the artifacts you approve, using the devclean CLI",
"author": {
"name": "Youngsup Oh"
},
"homepage": "https://github.com/ohing504/devclean",
"repository": "https://github.com/ohing504/devclean",
"license": "MIT"
}
80 changes: 80 additions & 0 deletions plugins/devclean/skills/devclean/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
---
name: devclean
description: Find reclaimable disk space in a developer machine (node_modules, build outputs, Xcode DerivedData and simulators, package manager caches, Docker, LLM model stores) and delete only the artifacts the user approves, by running the devclean CLI. Use for "disk is full", "free up space", "what is taking space in ~/workspace", "clean old node_modules", "clean Xcode caches", "devclean".
---

# devclean

Use devclean for every size and every delete; decide only which artifacts to propose. Never measure with `du` or delete with `rm`: devclean's `size` is the space a delete frees (aware of APFS clones, hard links and sparse files), and its `clean` moves to Trash and refuses `protected` artifacts.

## 1. Check the binary

Run `devclean --version`. If it is not found, stop and give the user the install command; do not install it yourself:

```bash
go install github.com/ohing504/devclean/cmd/devclean@latest
```

Pre-built binaries: <https://github.com/ohing504/devclean/releases/latest>

## 2. Scan

```bash
devclean scan --json [--path <dir>] [--eco <eco,...>] [--status <status>] [--min-size 100MB] [-n <N>]
```

- `--path` defaults to `~`. Narrow it to what the user asked about.
- The JSON can be large (one object per artifact). Pass `--min-size` and `-n`, and summarize it with `jq` or a short script instead of reading it whole.
- `devclean list` prints the ecosystems, categories, statuses and safety levels; `devclean scan --help` prints every flag.

Fields to use per artifact:

- `path`, `project_root`, `ecosystem`, `category`
- `size` — bytes freed by deleting the artifact. `total_size` counts shared blocks once, so it can be less than the sum of `size`.
- `activity` — `active` < 7 days, `recent` 7–30 days, `stale` 30–90 days, `dormant` 90+ days since the last change
- `safety`, `reason`, `recommendation`

Safety levels:

- `safe` — regenerated by the ecosystem's own tooling.
- `caution` — deletable, but loses something slow or impossible to regenerate, or affects other projects. Say what is lost (`reason`) before proposing it.
- `protected` — devclean never deletes it. Do not propose it or work around it.

## 3. Propose and get approval

- **Show**: artifacts grouped by project, with size, activity and safety, ordered by `size` descending.
- **Propose first**: `safe` artifacts in `stale`/`dormant` projects. Propose `active` projects only when the user asks.
- **Wait**: the user approves a list of paths in chat before any `clean` run.
- **Group approval**: approving a group ("all dormant node_modules") approves the paths listed for it, not paths found later.

## 4. Clean the approved artifacts

Name every approved artifact with `--item` (its `path` field), and preview first:

```bash
devclean clean --item <path> [--item <path> ...] --dry-run --yes
devclean clean --item <path> [--item <path> ...] --yes
```

Scope:

- Always pass `--item`. `clean --yes` without it deletes every `safe` artifact in the scanned scope.
- Pass the same `--path` and `--eco` as the scan. `clean` scans again before deleting; without them it rescans `~` (slow), and an `--item` outside the scope matches nothing.
- Always pass `--yes`. When the user wants to pick interactively, give them the `devclean clean` command to run in their terminal.

Flags that need the user's approval:

- `--include-caution` — only for `caution` paths the user approved by name.
- `--force` — permanent delete instead of Trash; only when the user asks for it.
- `--vendor-cleanup` — also runs tool cleanup commands (`npm cache clean --force`, `pnpm store prune`, `xcrun simctl delete unavailable`, …) for the targeted ecosystems; only when the user approves those commands.

Failures:

- Exit status 1 before deletion lists each `--item` that matched nothing or would be skipped, with the reason. Report it; do not retry with broader flags.

## 5. Report

- Freed size, and whether the artifacts went to Trash or were deleted permanently, from the `clean` output.
- Artifacts that failed, with their errors.
- Every `recommendation` on the cleaned artifacts (for example, run `pnpm store prune` after deleting pnpm `node_modules`).
- For Trash moves: the space is freed when the Trash is emptied.
Loading