diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json new file mode 100644 index 0000000..6f786d4 --- /dev/null +++ b/.claude-plugin/marketplace.json @@ -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" + } + ] +} diff --git a/CLAUDE.md b/CLAUDE.md index ef83cef..193d311 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -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`) | diff --git a/README.md b/README.md index b83d149..77d9f47 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 6ebf5a9..2ccfb92 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -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)) @@ -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. diff --git a/docs/decisions/claude-code-plugin.md b/docs/decisions/claude-code-plugin.md new file mode 100644 index 0000000..84ae9ab --- /dev/null +++ b/docs/decisions/claude-code-plugin.md @@ -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. diff --git a/plugins/devclean/.claude-plugin/plugin.json b/plugins/devclean/.claude-plugin/plugin.json new file mode 100644 index 0000000..d4d6682 --- /dev/null +++ b/plugins/devclean/.claude-plugin/plugin.json @@ -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" +} diff --git a/plugins/devclean/skills/devclean/SKILL.md b/plugins/devclean/skills/devclean/SKILL.md new file mode 100644 index 0000000..3326126 --- /dev/null +++ b/plugins/devclean/skills/devclean/SKILL.md @@ -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: + +## 2. Scan + +```bash +devclean scan --json [--path ] [--eco ] [--status ] [--min-size 100MB] [-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 [--item ...] --dry-run --yes +devclean clean --item [--item ...] --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.