Skip to content

Persist the prompt file so claude --bg sessions can respawn - #9

Open
Iteratrix wants to merge 1 commit into
nklisch:mainfrom
Iteratrix:persistent-prompt-file
Open

Persist the prompt file so claude --bg sessions can respawn#9
Iteratrix wants to merge 1 commit into
nklisch:mainfrom
Iteratrix:persistent-prompt-file

Conversation

@Iteratrix

Copy link
Copy Markdown

Problem

claude-mode writes the assembled prompt to a mkdtemp directory, spawns claude --system-prompt-file <tmp>, waits for it to exit, then rmSyncs the directory. That assumes Claude Code reads the file exactly once, while claude-mode is still alive.

Claude Code's background sessions break that assumption. With claude-mode <preset> -- --bg ..., claude returns within a second and hands the session to the background supervisor, which outlives claude-mode and re-executes the worker with the original argv:

  • after the ~1 h idle stop, when you attach or reply again
  • on claude respawn <id> (and the automatic respawn after a Claude Code update)
  • on reboot recovery ("attach or reply to it and it restarts from where it left off")

Every one of those re-reads --system-prompt-file, which by then has been deleted. Reproduced on Claude Code 2.1.263: hiding the prompt file and running claude respawn gives

bg settled 9f690ee9 (crashed): exit 1 before init — Error: System prompt file not found: /home/ve/.local/state/claude-mode/prompts/cadb0be235118d4c.md

Restoring the file and respawning again brings the session back. So the file has to outlive the launcher.

(build-prompt.ts had the opposite problem: it never cleaned up, so every call leaked a /tmp/claude-mode-* directory. I had 77 of them on one machine.)

Change

  • writeTempPrompt becomes writePromptFile: the prompt goes to $XDG_STATE_HOME/claude-mode/prompts/<sha256[:16]>.md (default ~/.local/state/...) and is never deleted. Content addressing means reruns of the same mode in the same environment share one file, so the directory stays small (~16 KB per distinct prompt).
  • cli.ts drops the post-exit cleanup; build-prompt.ts uses the same path, which also fixes its leak.
  • createCliRunner gains an optional env overlay. Under bun, execSync does not see process.env mutations made in the test body, which the new e2e test needs.
  • README "How it works" and CLAUDE.md pipeline notes updated.

I chose the state dir over the cache dir because cache directories are conventionally safe to wipe, and a wiped prompt file turns into a crashed respawn hours or days later. Happy to switch to a different location or add pruning if you'd rather; I kept the change to the minimum that makes --bg work.

Verification

  • bunx tsc --noEmit clean; bun test 726 pass with the same 4 pre-existing model-fixture failures as main on my machine (my Claude settings select a different model than the fixtures expect).
  • End to end on a headless box with Claude Code 2.1.263: claude-mode tinker -- --bg -n cm-bgtest "..." starts, the worker's argv points at the state-dir file, the session answers, claude respawn succeeds, and the negative control above fails exactly as expected when the file is missing.

🤖 Generated with Claude Code

claude-mode wrote the assembled prompt to a mkdtemp directory and removed
it once claude exited. That assumed claude reads the file exactly once,
while claude-mode is still alive. Background sessions break that
assumption: `claude-mode <preset> -- --bg` returns immediately, and the
session is then hosted by Claude Code's supervisor, which re-executes the
worker with the original argv after idle stops, `claude respawn`, binary
updates, and reboot recovery. Every one of those re-reads
--system-prompt-file, by then already deleted.

Write the prompt to $XDG_STATE_HOME/claude-mode/prompts/<sha256[:16]>.md
(default ~/.local/state) and never delete it. Content addressing means
reruns of the same mode in the same environment share one file. This also
stops build-prompt.ts leaking one /tmp/claude-mode-* directory per call,
which it never cleaned up.

createCliRunner gains an optional env overlay: under bun, execSync does
not see process.env mutations made in the test body.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant