- 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. Performs two operations:
1. **Process cleanup**: Identify and terminate orphaned Claude Code processes
2. **Directory cleanup**: Clean up accumulated files in ~/.claude/
This skill executes inline without spawning a subagent.
## 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):
```bash
.claude/scripts/claude-refresh.sh $( [ "$force" = true ] && echo "--force" ) $( [ "$dry_run" = true ] && echo "--dry-run" )
```
Store process cleanup output for display.
### 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.
```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:
```json
{
"question": "Select cleanup age threshold:",
"header": "Age Threshold",
"multiSelect": false,
"options": [
{
"label": "8 hours (default)",
"description": "Remove files older than 8 hours - aggressive cleanup"
},
{
"label": "2 days",
"description": "Remove files older than 2 days - conservative cleanup"
},
{
"label": "Clean slate",
"description": "Remove everything except safety margin (1 hour)"
}
]
}
```
4. Map user selection to age parameter:
- "8 hours (default)" → `--age 8`
- "2 days" → `--age 48`
- "Clean slate" → `--age 0`
5. Execute cleanup with selected age:
```bash
case "$selection" in
"8 hours (default)")
.claude/scripts/claude-cleanup.sh --force --age 8
;;
"2 days")
.claude/scripts/claude-cleanup.sh --force --age 48
;;
"Clean slate")
.claude/scripts/claude-cleanup.sh --force --age 0
;;
esac
```
6. Display cleanup results
---
## Example Execution Flows
### Interactive Flow
```bash
# User runs: /refresh
# Output:
Claude Code Refresh
===================
No orphaned processes found.
All 3 Claude processes are active sessions.
---
Claude Code Directory Cleanup
=============================
Target: ~/.claude/
Current total size: 7.3 GB
Scanning directories...
Directory Total Cleanable Files
---------- ------- ---------- -----
projects/ 7.0 GB 6.5 GB 980
debug/ 151.0 MB 140.0 MB 650
...
TOTAL 7.3 GB 6.7 GB 5577
Space that can be reclaimed: 6.7 GB
# Prompt appears:
[Age Threshold]
Select cleanup age threshold:
1. 8 hours (default) - Remove files older than 8 hours
2. 2 days - Remove files older than 2 days
3. Clean slate - Remove everything except safety margin
# User selects option 1
# Cleanup executes:
Cleanup Complete
================
Deleted: 5577 files
Failed: 0 files
Space reclaimed: 6.7 GB
New total size: 600.0 MB
```
### Dry-Run Flow
```bash
# User runs: /refresh --dry-run
# Process cleanup half, forwarding --dry-run through to claude-refresh.sh:
Claude Code Refresh
===================
[DRY RUN] Preview only -- no processes will be terminated.
Found 2 orphaned processes using 1.2 MB:
...
Total memory that can be reclaimed: 1.2 MB
# Directory cleanup half -- shows survey, then:
=== DRY RUN MODE ===
Showing 8-hour cleanup preview...
Would delete: 5577 files
Would reclaim: 6.7 GB
Dry Run Summary
===============
No changes made.
```
### Force Flow
```bash
# User runs: /refresh --force
# Shows survey, then immediately:
=== EXECUTING CLEANUP (8-hour default) ===
Cleanup Complete
================
Deleted: 5577 files
Space reclaimed: 6.7 GB
```
---
## Safety Measures
### Protected Files (Never Deleted)
- `sessions-index.json` (in each project directory)
- `settings.json`
- `.credentials.json`
- `history.jsonl`
### Safety Margin
Files modified within the last hour are **never deleted**, regardless of age threshold.
### Process Safety
`claude-refresh.sh` identifies orphans from a single atomic process snapshot, applying four
independently-testable predicates rather than an argv-substring match or an ancestor-only
process-tree walk (an ancestor walk cannot reach a sibling, such as the invoking session's own
sleep inhibitor, and re-querying a transient PID from an earlier snapshot is a race -- see the
script's own header comment for the full rationale):
- **Executable-identity match**: a candidate must match a narrow allow-list on its executable
name (`comm`), never on a substring anywhere in its argv. This is what keeps a system daemon
that merely mentions "claude" in one of its own arguments from ever being considered a
candidate at all.
- **System-slice cgroup exclusion**: a candidate under `/system.slice/` is never selected,
regardless of anything else.
- **Invoking-UID ownership**: a candidate not owned by the invoking user's UID is excluded.
عرض على GitHub