---
name: uninstall-skill
description: Removes a locally-installed skill from ~/.claude/skills/ (user scope) or .claude/skills/ (project scope). Deletes the skill folder and its .maxv-source.json sidecar. Refuses orphans (no sidecar) and maxv-* synthesized skills without explicit overrides. Called by cleanup-ephemerals at SessionEnd via --from-cleanup flag.
when_to_use: |
  Trigger phrases: "remova skill <name>", "uninstall skill <name>",
  "/maxvision-orchestration:uninstall-skill <name>". Called programmatically by
  cleanup-ephemerals with --from-cleanup to skip confirmation prompts.
disable-model-invocation: true
allowed-tools: Read Bash(test *) Bash(python *) Bash(cat *) Bash(test -d *) Bash(test -f *)
---

# Uninstall skill

Argument: `$ARGUMENTS` — `<skill-name> [--scope=user|project] [--keep-sidecar] [--force-orphan] [--from-cleanup]`

> **Hard rule:** this skill writes to disk (rm -rf semantics). Always confirms with the user before removing UNLESS invoked by `cleanup-ephemerals` (which passes `--from-cleanup`).

## Workflow

### 1. Parse arguments

Read `$ARGUMENTS`. The first positional token is the skill name; the rest are flags. Validate that the name matches `^[a-zA-Z][a-zA-Z0-9_-]*$` — reject anything else (path traversal guard).

If `$ARGUMENTS` is empty, abort with `"Missing skill name. Usage: uninstall-skill <name> [flags]"` exit 2.

### 2. Guardrails (pre-FS checks)

The Python helper enforces these — listed here for human readability:

- `maxv-*` prefix → **refused (exit 4)**. Synthesized skills use `synthesize-skill --resync` to refresh; uninstalling them here would leak the licensing+commit metadata of their composed sources.
- Skill folder absent → **exit 3** (not installed). Idempotent for re-runs.
- Sidecar (`.maxv-source.json`) absent AND no `--force-orphan` → **refused (exit 4)**. Orphans may be hand-installed; refusing default keeps user intent intact.

### 3. Confirmation gate

Unless `--from-cleanup` is in the args, prompt:

```
About to remove ~/.claude/skills/<name>.
  Sidecar: present | ABSENT (orphan)
  Keep sidecar: true|false
Proceed? [sim/skip]:
```

`sim`/`y`/`yes` proceeds. Anything else returns exit 0 with message "skipped by user".

### 4. Execute removal

Invoke the helper (delegates the strict argument parsing + atomic rmtree):

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

Exit code from the helper propagates to the caller. `--from-cleanup` makes the helper skip its internal `input()` prompt; you (the model) MUST NOT add your own prompt in that branch.

### 5. Report

Print one of:

- success: `removed <name> from <scope_root>` (+ archive path if `--keep-sidecar`)
- exit 3: `not installed: <name>`
- exit 4: `refused: <reason>` (orphan or maxv-*)
- exit 2: `invalid skill name: <name>`
- exit 1: `removal failed: <oserror>`

### 6. Side-effect awareness

This skill deletes a folder. It does **not** touch:

- `index.db` FTS5 catalog — the entry remains so re-install via `install-skill` works
- `ephemeral-registry.json` — `cleanup-ephemerals` rewrites it after batch removal
- `~/.claude/projects/<workspace>/maxv-orchestration.log` — only appends

If you removed an ephemeral skill manually (outside of SessionEnd flow), call
`cleanup-ephemerals --reconcile` afterwards to drop the orphan registry entry.

## Argument reference

| Flag | Default | Effect |
| --- | --- | --- |
| `--scope=user` | user | Resolves to `~/.claude/skills/<name>` |
| `--scope=project` | — | Resolves to `$(pwd)/.claude/skills/<name>` |
| `--keep-sidecar` | off | Archives `.maxv-source.json` to `<scope_root>/.uninstalled/<name>.maxv-source.json` before rm |
| `--force-orphan` | off | Removes a skill without a sidecar (use with care) |
| `--from-cleanup` | off | Skips interactive confirmation; intended for `cleanup-ephemerals` only |

## Exit codes

| Code | Meaning |
| --- | --- |
| 0 | success (or user skipped at confirmation) |
| 1 | generic error (rmtree failed) |
| 2 | bad arguments (invalid name, unknown scope) |
| 3 | not installed (skill folder absent) |
| 4 | refused (orphan without flag, maxv-* prefix) |
