- name
- skill-refresh
- description
- Manage Claude Code resources - terminate orphaned processes and clean up ~/.claude/ directory
- allowed-tools
- Bash, AskUserQuestion
# Refresh Skill (Direct Execution)
Direct execution skill for managing Claude Code resources. Runs ten distinct passes across three
areas, each with its own gate and destructiveness -- see the Pass Inventory table below for the
complete, authoritative list:
1. **Process cleanup**: orphaned Claude processes, idle Lean LSP process trees, unreaped-child
(zombie) reporting, and MCP server fan-out reporting.
2. **Spec-directory cleanup**: orphaned postflight markers, stale task `.lock` dirs, stale
session-scoped orchestration files, and stale session registry entries.
3. **File cleanup**: stale `.backup` files and `~/.claude/` directory age-threshold cleanup.
This skill executes inline without spawning a subagent.
## Pass Inventory
The single canonical list of every pass this skill runs, each with its owning Step/section (a
thin pointer -- see that Step for the full behavior, not restated here), its gate, whether it is
destructive, and whether the hourly `claude-refresh.timer` cadence reaches it. Only
`claude-refresh.sh`'s four internal passes (rows 1-4) are reached by that cadence; the remaining
six are `/refresh`-only.
| # | Pass | Owning Step / Section | Gate | Destructive | Hourly cadence |
|---|------|------------------------|------|--------------|-----------------|
| 1 | Orphaned Claude processes | Step 2 / "Process Safety" | interactive-confirm (AskUserQuestion) / `--dry-run` preview / `--force` terminates immediately | Yes | Yes |
| 2 | Lean LSP process-tree reclamation | Step 2 / "Process Safety" | interactive-confirm (same combined prompt as row 1) / `--dry-run` preview / `--force` terminates immediately | Yes, but recoverable -- `lean-lsp-mcp` respawns a fresh tree automatically on next tool call | Yes |
| 3 | Zombie (unreaped-child) reporting | "Process Safety" | report-only-always (no `--force` branch exists) | No | Yes |
| 4 | MCP server fan-out reporting | "Process Safety" | report-only-always (never terminates or reconfigures) | No | Yes |
| 5 | Orphaned postflight markers | Step 3 | age-threshold-only (60 min), no interactive confirmation | Yes | No (`/refresh`-only) |
| 6 | Stale task `.lock` dirs | Step 4 | age-threshold-only (`TASK_LOCK_REAP_MIN`, default 120 min), no interactive confirmation | Yes | No (`/refresh`-only) |
| 7 | Stale session-scoped orchestration files | Step 4.5 | age-threshold-only (`ORCHESTRATOR_SESSION_REAP_MIN`, default 240 min), no interactive confirmation | Yes | No (`/refresh`-only) |
| 8 | Stale session registry entries | Step 4.6 | age-threshold-only (`SESSION_REGISTRY_REAP_MIN`, default 240 min), no interactive confirmation | Yes | No (`/refresh`-only) |
| 9 | Stale `.backup` files | Step 5 | `--dry-run` preview / unconditional delete otherwise (no age threshold, no confirmation) | Yes | No (`/refresh`-only) |
| 10 | `~/.claude/` directory cleanup | Steps 6-7 | interactive-confirm (age-threshold selection) / `--dry-run` preview / `--force` immediate (8h default) | Yes -- protected filenames and the 1-hour safety margin (see "Safety Measures" below) are exempted | No (`/refresh`-only) |
## Execution
### Step 1: Parse Arguments
Extract flags from command input:
- `--dry-run`: Preview mode
- `--force`: Skip confirmation, use 8-hour default
```bash
# Parse from command input
dry_run=false
force=false
if [[ "$*" == *"--dry-run"* ]]; then
dry_run=true
fi
if [[ "$*" == *"--force"* ]]; then
force=true
fi
```
### Step 2: Run Process Cleanup
Execute process cleanup script, forwarding `--dry-run` through when set (reusing the `dry_run`
boolean already parsed in Step 1 -- no new argument parsing). This one invocation covers BOTH the
Claude-process pass and the separately-gated Lean LSP process-tree pass (see "Process Safety"
below); the script always runs both and reports/terminates each independently:
```bash
process_output=$(.claude/scripts/claude-refresh.sh $( [ "$force" = true ] && echo "--force" ) $( [ "$dry_run" = true ] && echo "--dry-run" ))
```
Store process cleanup output for display.
**Interactive confirmation** (only when neither `force` nor `dry_run` was set at invocation --
`--force` already terminates immediately with no prompt per the existing contract, and
`--dry-run` never terminates regardless): closes a pre-existing gap where this step stored
output but never actually prompted, despite this skill's frontmatter declaring `AskUserQuestion`
and the script's own header comment already assuming a prompt exists ("skill will prompt with
AskUserQuestion and re-run with --force if confirmed"). Check `process_output` for candidates
from either pass by testing for the absence of BOTH "No orphaned processes found." and "No idle
Lean LSP process trees found." -- if either line is absent (that pass found something), prompt
once with a single combined confirmation covering both passes:
```json
{
"question": "Terminate the orphaned Claude processes and/or idle Lean LSP process trees found above?",
"header": "Process Cleanup",
"multiSelect": false,
"options": [
{
"label": "Yes, terminate",
"description": "Terminate every orphaned Claude process and idle Lean LSP process tree reported above"
},
{
"label": "No, skip",
"description": "Leave everything reported above running"
}
]
}
```
**The confirmation trigger above is scoped to exactly these two passes, deliberately.** The
script also runs a zombie (unreaped-child) reporting pass and an MCP server fan-out reporting
pass (see "Process Safety" below) as part of the SAME invocation, but neither offers a terminate
action to confirm -- both are report-only, with no `--force` branch of their own. The
absence-of-both-no-findings-lines check above MUST NOT be extended to key off either new pass's
own no-findings line ("No unreaped child processes found." / a server-table with no flagged
rows); doing so would prompt the user for a confirmation that has nothing to confirm.
If the user selects "Yes, terminate", re-run with `--force` and replace the stored output:
```bash
process_output=$(.claude/scripts/claude-refresh.sh --force)
```
If the user selects "No, skip" -- or if both "No orphaned processes found." and "No idle Lean
LSP process trees found." were already present in `process_output` -- skip this prompt entirely
and proceed to Step 3 with the already-stored `process_output`.
### Step 3: Clean Orphaned Postflight Markers
Clean any orphaned postflight coordination files from the specs directory. These files should normally be cleaned up by skills after postflight completes, but may be left behind if a process is interrupted.
**Destructiveness**: this pass deletes unconditionally past a 60-minute age threshold, with no
interactive confirmation, whenever `--dry-run` is not set. This is a different gate class from
the confirmation-gated process-termination passes above (Step 2): there is no "yes/no" prompt at
any age, only the age threshold itself. It is also `/refresh`-only -- the hourly
`claude-refresh.timer` cadence never reaches this step.
```bash
echo ""
echo "=== Cleaning Orphaned Postflight Markers ==="
echo ""
# Find orphaned postflight markers (older than 1 hour)
orphaned_pending=$(find specs -maxdepth 3 -name ".postflight-pending" -mmin +60 -type f 2>/dev/null)
orphaned_guard=$(find specs -maxdepth 3 -name ".postflight-loop-guard" -mmin +60 -type f 2>/dev/null)
# Also check for legacy global markers
legacy_pending=""
legacy_guard=""
if [ -f "specs/.postflight-pending" ]; then
legacy_pending="specs/.postflight-pending"
fi
if [ -f "specs/.postflight-loop-guard" ]; then
legacy_guard="specs/.postflight-loop-guard"
fi
if [ -n "$orphaned_pending" ] || [ -n "$orphaned_guard" ] || [ -n "$legacy_pending" ] || [ -n "$legacy_guard" ]; then
if [ "$dry_run" = true ]; then
echo "Would delete the following orphaned markers:"
[ -n "$orphaned_pending" ] && echo "$orphaned_pending"
[ -n "$orphaned_guard" ] && echo "$orphaned_guard"
[ -n "$legacy_pending" ] && echo "$legacy_pending"
[ -n "$legacy_guard" ] && echo "$legacy_guard"
else
# Delete orphaned task-scoped markers
find specs -maxdepth 3 -name ".postflight-pending" -mmin +60 -delete 2>/dev/null
find specs -maxdepth 3 -name ".postflight-loop-guard" -mmin +60 -delete 2>/dev/null
# Delete legacy global markers
rm -f specs/.postflight-pending 2>/dev/null
rm -f specs/.postflight-loop-guard 2>/dev/null
echo "Cleaned orphaned postflight markers."
fi
else
echo "No orphaned postflight markers found."
fi
```
### Step 4: Reap Stale Task Locks
Sweep `specs/` for stale task-number `.lock` directories (see `context/patterns/task-lock.md`'s
Reap Contract section for the full threshold reasoning) and report every one found. This is a
distinct cleanup target from Step 3's postflight markers, added alongside it rather than merged
into it. Reuses the `dry_run` boolean already parsed in Step 1 -- no new argument parsing.
```bash
echo ""
echo "=== Reaping Stale Task Locks ==="
echo ""
if [ "$dry_run" = true ]; then
.claude/scripts/task-lock.sh reap --dry-run
else
.claude/scripts/task-lock.sh reap
fi
```
The per-lock detail (task number, session id, operation, age) is produced by `task-lock.sh`
itself; echo its output verbatim rather than summarizing it away, matching the reap subcommand's
own per-item reporting contract.
### Step 4.5: Reap Stale Session-Scoped Orchestration Files
Sweep `specs/` for stale session-scoped `specs/.orchestrator-multi-state-{session_id}.json` and
`specs/.return-meta-multi-{session_id}.json` files (see
`context/standards/orchestrator-runtime-files.md`'s Class Table) and report every one found. This
is a distinct cleanup target from Step 4's task-lock reap — session-scoping the two repo-level
batch-orchestration singletons trades collision risk for unbounded litter if an abandoned batch's
file is never swept, so this step exists to bound that litter. Uses the `X.5` numbering
deliberately so Steps 5-7 below keep their existing numbers and no cross-reference to them in
`refresh.md` needs to change. Reuses the `dry_run` boolean already parsed in Step 1.
```bash
echo ""
echo "=== Reaping Stale Session-Scoped Orchestration Files ==="
echo ""
if [ "$dry_run" = true ]; then
.claude/scripts/reap-session-runtime-files.sh --dry-run
else
.claude/scripts/reap-session-runtime-files.sh
fi
```
The per-file detail (filename, embedded session id, age in minutes) is produced by
`reap-session-runtime-files.sh` itself; echo its output verbatim rather than summarizing it away,
matching Step 4's own "echo verbatim" instruction. This cleanup runs only on explicit `/refresh`
invocation, not on the hourly systemd cadence.
### Step 4.6: Reap Stale Session Registry Entries
Sweep `specs/.sessions/` for stale in-flight orchestration session registry entries (see
`context/patterns/task-lock.md`'s Session-Registry CLI section) and report every one found. This
is a distinct cleanup target from Step 4's task-lock reap and Step 4.5's session-scoped
orchestration-file reap — the session registry (`specs/.sessions/{session_id}.json`) is a
separate, additive mechanism produced by `task-lock.sh session-register`/`session-heartbeat`, not
one of the two files Step 4.5 sweeps. Uses the `X.6` numbering deliberately so Steps 5-7 keep
their existing numbers and no cross-reference to them in `refresh.md` needs to change. Reuses the
`dry_run` boolean already parsed in Step 1 — the same `--dry-run` passthrough branch structure as
Step 4.5.
```bash
echo ""
echo "=== Reaping Stale Session Registry Entries ==="
echo ""
if [ "$dry_run" = true ]; then
.claude/scripts/task-lock.sh session-reap --dry-run
else
.claude/scripts/task-lock.sh session-reap
fi
```
The per-entry detail (session id, command, task numbers, age, reap reason) is produced by
`task-lock.sh session-reap` itself; echo its output verbatim rather than summarizing it away,
matching Step 4 and Step 4.5's own "echo verbatim" instruction. This cleanup runs only on
explicit `/refresh` invocation, never on the hourly `claude-refresh.timer` cadence, which runs
process cleanup only and does not sweep `specs/`.
### Step 5: Clean Stale Backup Files
Scan for and remove any `.backup` files left over from the deprecated backup mechanism in `.claude/`:
```bash
echo ""
echo "=== Cleaning Stale Backup Files ==="
echo ""
# Find .backup files in .claude/ directory
backup_files=$(find .claude/ -name "*.backup" -type f 2>/dev/null)
if [ -n "$backup_files" ]; then
backup_count=$(echo "$backup_files" | wc -l)
if [ "$dry_run" = true ]; then
echo "Would delete $backup_count .backup file(s):"
echo "$backup_files"
else
echo "$backup_files" | xargs rm -f
echo "Deleted $backup_count stale .backup file(s)."
fi
else
echo "No stale .backup files found."
fi
```
### Step 6: Run Directory Survey
Show current directory status without cleaning yet:
```bash
.claude/scripts/claude-cleanup.sh
```
This displays:
- Current ~/.claude/ directory size
- Breakdown by directory
- Space that can be reclaimed
### Step 7: Execute Based on Mode
#### Dry-Run Mode
If `--dry-run` is set:
```bash
echo ""
echo "=== DRY RUN MODE ==="
echo "Showing 8-hour cleanup preview..."
echo ""
.claude/scripts/claude-cleanup.sh --dry-run --age 8
```
Exit after showing preview.
#### Force Mode
If `--force` is set:
```bash
echo ""
echo "=== EXECUTING CLEANUP (8-hour default) ==="
echo ""
.claude/scripts/claude-cleanup.sh --force --age 8
```
Show results and exit.
#### Interactive Mode (Default)
If neither flag is set:
1. Check if cleanup candidates exist (claude-cleanup.sh exits with code 1 if candidates found)
2. If no candidates, display message and exit:
```
No cleanup candidates found within default thresholds.
All files are either protected or recently modified.
```
3. If candidates exist, prompt user for age selection:
在 GitHub 查看