Plugin skill command block (```!) executes but file writes don't persist to filesystem
Summary
Plugin skill command blocks (` `! ) execute and produce stdout, but filesystem side effects (file creation via mkdir, cat >`, etc.) do not persist to the project directory. This breaks plugins that depend on command blocks creating state files.
Environment
- Claude Code: 2.1.52 and 2.1.69 (confirmed on both)
- OS: Ubuntu 22.04, Linux 6.8.0-101-generic x86_64
- Node: v20.19.5
- Plugin:
ralph-loop@claude-plugins-official(cache version55b58ec6e564) - Sandbox: Not explicitly enabled in any settings file
Reproduction Steps
- Have the
ralph-loopplugin installed and enabled - Run
/ralph-loop "/review last commit" - The skill
ralph-loop.mdcontains a command block:
````
!`
"${CLAUDE_PLUGIN_ROOT}/scripts/setup-ralph-loop.sh" $ARGUMENTS
```
- The setup script (
setup-ralph-loop.sh) runsmkdir -p .claudeand writes.claude/ralph-loop.local.mdvia heredoc - The script's stdout appears in the conversation β including the full activation message with iteration count, max iterations, prompt text, etc.
- But
.claude/ralph-loop.local.mddoes not exist on disk
Expected Behavior
The setup script's file writes should persist to the project's working directory, creating .claude/ralph-loop.local.md. The plugin's Stop hook reads this file to determine whether to block session exit and feed the prompt back β without it, the loop never engages.
Actual Behavior
- Script stdout: captured and displayed in conversation (works)
- Script file writes (
mkdir -p .claude && cat > .claude/ralph-loop.local.md <<EOF ...): silently lost (doesn't persist)
This means:
- The
Stophook checks for.claude/ralph-loop.local.md, finds nothing, and allows exit - The ralph loop never activates despite the setup script reporting success
Evidence
The conversation displays this output from the script (confirming execution with correct arguments):
π Ralph loop activated in this session!
Iteration: 1
Max iterations: unlimited
Completion promise: none (runs forever)
The stop hook is now active. When you try to exit, the SAME PROMPT will be
fed back to you. You'll see your previous work in files, creating a
self-referential loop where you iteratively improve on the same task.
To monitor: head -10 .claude/ralph-loop.local.md
β οΈ WARNING: This loop cannot be stopped manually! It will run infinitely
unless you set --max-iterations or --completion-promise.
π
/review last commit
But verifying on disk:
$ ls -la .claude/ralph-loop.local.md
# File not found
$ ls -la .claude/
# Only settings.json, settings.local.json, plan.md exist
Impact
Any plugin that uses ` `! ` command blocks to create state files for hooks to read is broken. The ralph-loop plugin is the primary affected plugin β its core loop mechanism depends on the setup script persisting a state file that the Stop hook reads.
Related Issues
- #18044 β
$ARGUMENTSnot populated (different issue; arguments ARE populated here based on stdout) - #4462, #13890 β Subagent file writes not persisting (similar symptom, different mechanism β those are Task tool agents, this is skill command block execution)
- #28189 β Sandbox/bubblewrap creating stub files in
.claude/(related sandbox filesystem behavior on Linux)
This issue has 3 comments on GitHub. Read the full discussion on GitHub β