---
name: cleanup-ephemerals
description: Sweeps ~/.claude/skills/*/.maxv-source.json for ephemeral=true entries at SessionEnd. Deletes those below promote_threshold (default 5 uses); promotes those at/above threshold by rewriting ephemeral=false. Invoked automatically by the SessionEnd hook unless reason=resume. Backward compat — v1.0.0 manifests with no ephemeral field are treated as permanent.
when_to_use: |
  Triggered automatically by the SessionEnd hook (see hooks/maxv-orchestration-hooks.sh).
  Direct invocation by user: "/maxvision-orchestration:cleanup-ephemerals" or with
  "--dry-run" to preview, "--force-all" to delete every ephemeral regardless of count.
disable-model-invocation: true
allowed-tools: Read Bash(test *) Bash(python *) Bash(cat *) Bash(test -d *) Bash(test -f *) Bash(find *) Skill(uninstall-skill)
---

# Cleanup ephemerals

Argument: `$ARGUMENTS` — `[--scope=user|project] [--dry-run] [--force-all]`

> **When called by the SessionEnd hook**, no confirmation is shown — the script
> just runs and appends a JSON report to the session log. When called by the
> user, the helper's `--dry-run` flag lets you preview safely first.

## Workflow

### 1. Pre-flight

Verify `${CLAUDE_PLUGIN_ROOT}/scripts/cleanup_ephemerals.py` exists. If it does
not, abort with exit 1 — the plugin install is corrupt.

### 2. Run the sweep

```bash
set -euo pipefail
python "${CLAUDE_PLUGIN_ROOT}/scripts/cleanup_ephemerals.py" $ARGUMENTS
```

The helper walks the skills root (default `~/.claude/skills/`) and classifies
each child directory by reading its `.maxv-source.json`:

| Sidecar state | use_count vs threshold | Decision |
| --- | --- | --- |
| missing | — | SKIP — `skipped_no_sidecar` (orphan; cannot decide) |
| present, name starts `maxv-` | — | SKIP — `skipped_maxv` (synthesized — own re-synthesis flow) |
| `$schema_version` = `1.0.0` (no `ephemeral` field) | — | KEEP — `kept_permanent` (backward compat) |
| `ephemeral: false` | — | KEEP — `kept_permanent` |
| `ephemeral: true` AND `use_count >= promote_threshold` | promote | rewrite `ephemeral=false`, set `promoted_at` → `promoted` |
| `ephemeral: true` AND `use_count < promote_threshold` | delete | invoke `Skill(uninstall-skill --from-cleanup)` → `deleted` |
| any with `--force-all` AND `ephemeral: true` | force delete | always delete (ignore threshold) → `deleted` |

### 3. Output report

The helper prints a JSON document like:

```json
{
  "scanned": ["frontend-design", "shadcn-ui", "trailofbits"],
  "would_delete": [],
  "would_promote": [],
  "deleted": ["trailofbits"],
  "promoted": ["shadcn-ui"],
  "kept_permanent": ["frontend-design"],
  "skipped_no_sidecar": [],
  "skipped_maxv": [],
  "errors": []
}
```

`would_delete` and `would_promote` are populated only on `--dry-run` — the
real-run keys (`deleted`, `promoted`) are populated on the actual sweep.

### 4. Append to session log

After the helper exits 0, append the report to
`~/.claude/cache/maxv-orchestration/session-cleanup.log` for cross-session audit.
The SessionEnd hook script does this via background redirect — when called by
the user directly, copy/paste the report to your terminal if you want a record.

## Promotion semantics

A skill is promoted when its `use_count` (in `.maxv-source.json`) reaches
`promote_threshold`. The promotion is a single atomic field flip:

- `ephemeral: true → false`
- `promoted_at: null → <ISO-8601 UTC>`

After promotion the skill stays put across SessionEnd cleanups forever. The
user can still remove it manually with
`/maxvision-orchestration:uninstall-skill <name>` (no `--force-orphan` needed
because the sidecar remains).

## Error handling

Per-skill errors do not abort the sweep — they're logged in `errors[]` and
the next skill is processed. A fatal error (FS unreadable, sidecar JSON
malformed across the board) returns exit 1 from the helper.

## Flag reference

| Flag | Default | Effect |
| --- | --- | --- |
| `--scope=user` | user | `~/.claude/skills` |
| `--scope=project` | — | `$(pwd)/.claude/skills` |
| `--dry-run` | off | Print plan, no deletion/promotion |
| `--force-all` | off | Delete EVERY ephemeral skill regardless of use_count (does NOT touch permanent ones) |

## Exit codes

| Code | Meaning |
| --- | --- |
| 0 | sweep completed (per-skill errors in JSON report) |
| 1 | fatal (helper missing, scope_root unreadable) |
| 2 | bad arguments |
