Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,11 @@
"source": "./plugins/claudeception/",
"description": "Continuous-learning meta-skill for both hosts:"
},
{
"name": "cmux-node-options-tmpdir-guard",
"source": "./plugins/cmux-node-options-tmpdir-guard/",
"description": "Keep cmux's Claude NODE_OPTIONS restore guard alive when it lives under $TMPDIR. Use when: (1) a long-lived cmux/Claude session suddenly has every node/claude invocation fail with a missing --require target, (2) NODE_OPTIONS points at a /var/folders/.../T/cmux-claude-node-options/restore-node-options.cjs path on a cmux build that predates manaflow-ai/cmux#3699, (3) reboot-surviving cmux sessions break after about three days idle. Root cause: macOS reaps $TMPDIR files untouched for about three days, so the required guard file vanishes while NODE_OPTIONS still references it. Covers the persistent-HOME-copy stopgap, the LaunchAgent that recreates and mtime-refreshes the $TMPDIR file daily, why a LaunchAgent resolves the same per-user temp base, and when to retire it."
},
{
"name": "cmux-autoresume-after-reboot",
"source": "./plugins/cmux-autoresume-after-reboot/",
Expand Down
5 changes: 5 additions & 0 deletions .codex-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,11 @@
"source": "./plugins/claudeception/",
"description": "Continuous-learning meta-skill for both hosts:"
},
{
"name": "cmux-node-options-tmpdir-guard",
"source": "./plugins/cmux-node-options-tmpdir-guard/",
"description": "Keep cmux's Claude NODE_OPTIONS restore guard alive when it lives under $TMPDIR. Use when: (1) a long-lived cmux/Claude session suddenly has every node/claude invocation fail with a missing --require target, (2) NODE_OPTIONS points at a /var/folders/.../T/cmux-claude-node-options/restore-node-options.cjs path on a cmux build that predates manaflow-ai/cmux#3699, (3) reboot-surviving cmux sessions break after about three days idle. Root cause: macOS reaps $TMPDIR files untouched for about three days, so the required guard file vanishes while NODE_OPTIONS still references it. Covers the persistent-HOME-copy stopgap, the LaunchAgent that recreates and mtime-refreshes the $TMPDIR file daily, why a LaunchAgent resolves the same per-user temp base, and when to retire it."
},
{
"name": "cmux-autoresume-after-reboot",
"source": "./plugins/cmux-autoresume-after-reboot/",
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ environments and older Codex versions.
| [agent-traffic-log](./skills/agent-traffic-log/SKILL.md) | skill | Claude, Codex | Append-only JSONL log of agent-to-agent traffic + a live pane; lock-free concurrent appends, and `xs status` derives who is blocked from the events |
| [terraform-noninteractive-prod-apply](./skills/terraform-noninteractive-prod-apply/SKILL.md) | skill | Claude, Codex | Non-interactive terraform **prod** apply when `apply.sh` has no `-auto-approve`: use `plan -out` + `apply <file>`, not `echo yes \|`, because a prompt-piped apply approves a plan terraform **recomputes** rather than the one you reviewed |
| [cmux-config-silent-drop-triage](./skills/cmux-config-silent-drop-triage/SKILL.md) | skill | Claude, Codex | A cmux.json entry passes `config doctor` but never appears: doctor is syntax-only, so bisect the backups and read the `[CmuxConfig]` diagnostics and enum values off the binary's `strings` |
| [cmux-node-options-tmpdir-guard](./skills/cmux-node-options-tmpdir-guard/SKILL.md) | skill | Claude, Codex | Keep cmux's Claude `NODE_OPTIONS` restore guard alive when it lives under `$TMPDIR`: macOS reaps files there after ~3 days untouched, so a long-lived session's `--require` target vanishes and every `node`/`claude` invocation in it dies. Persistent `$HOME` copy plus a daily LaunchAgent that recreates and mtime-refreshes the file |
| [alb-per-rule-traffic-attribution](./skills/alb-per-rule-traffic-attribution/SKILL.md) | skill | Claude, Codex | ALB publishes no per-listener-rule CloudWatch metric; access logs answer it, but `matched_rule_priority` is a position that renumbers when a rule is inserted - attribute by `request_url` + `user_agent` |
| [cmux-claude-codex-cross-runtime-messaging](./skills/cmux-claude-codex-cross-runtime-messaging/SKILL.md) | skill | Claude, Codex | Claude Code <-> Codex CLI agents as messaging peers in cmux: `cmux send` transport, self-named tabs, both sides in the traffic log, where the three transcripts live |
| [`alb-per-rule-traffic-attribution` plugin](./plugins/alb-per-rule-traffic-attribution/) | plugin | Claude, Codex | Single-skill plugin: alb-per-rule-traffic-attribution |
Expand Down Expand Up @@ -191,6 +192,7 @@ environments and older Codex versions.
| [`cloudwatch-per-host-stat-single-host-vs-fleet` plugin](./plugins/cloudwatch-per-host-stat-single-host-vs-fleet/) | plugin | Claude, Codex | Single-skill plugin: cloudwatch-per-host-stat-single-host-vs-fleet |
| [`cmux-autoresume-after-reboot` plugin](./plugins/cmux-autoresume-after-reboot/) | plugin | Claude, Codex | Single-skill plugin: cmux-autoresume-after-reboot |
| [`cmux-config-silent-drop-triage` plugin](./plugins/cmux-config-silent-drop-triage/) | plugin | Claude, Codex | Single-skill plugin: cmux-config-silent-drop-triage |
| [`cmux-node-options-tmpdir-guard` plugin](./plugins/cmux-node-options-tmpdir-guard/) | plugin | Claude, Codex | Single-skill plugin: cmux-node-options-tmpdir-guard |
| [`cmux-cross-session-visibility` plugin](./plugins/cmux-cross-session-visibility/) | plugin | Claude, Codex | Single-skill plugin: cmux-cross-session-visibility |
| [`cmux-session-self-identity` plugin](./plugins/cmux-session-self-identity/) | plugin | Claude, Codex | Single-skill plugin: cmux-session-self-identity |
| [`git-worktree-add-relative-path-nests-inside-repo` plugin](./plugins/git-worktree-add-relative-path-nests-inside-repo/) | plugin | Claude, Codex | Single-skill plugin: git-worktree-add-relative-path-nests-inside-repo |
Expand Down
23 changes: 23 additions & 0 deletions catalog.json
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,15 @@
],
"summary": "Make AI agents show as watchable cmux tabs; Claude needs the claude-teams wrapper, Codex tabs via codex-teams/hooks."
},
{
"name": "cmux-node-options-tmpdir-guard",
"path": "skills/cmux-node-options-tmpdir-guard/SKILL.md",
"hosts": [
"claude",
"codex"
],
"summary": "Keep cmux's Claude NODE_OPTIONS restore guard alive when it lives under $TMPDIR: macOS reaps $TMPDIR files untouched for about three days, so a long-lived session's --require target vanishes and every node/claude invocation in it dies. Covers the persistent-HOME-copy stopgap, the daily LaunchAgent that recreates and mtime-refreshes the file, why a LaunchAgent resolves the same per-user temp base, and when to retire it."
},
{
"name": "cmux-autoresume-after-reboot",
"path": "skills/cmux-autoresume-after-reboot/SKILL.md",
Expand Down Expand Up @@ -1127,6 +1136,7 @@
"cloudwatch-metric-filter-dimensions-default-value-exclusive",
"cloudwatch-per-host-stat-single-host-vs-fleet",
"cmux-agent-tabs",
"cmux-node-options-tmpdir-guard",
"cmux-autoresume-after-reboot",
"cmux-claude-codex-cross-runtime-messaging",
"cmux-config-silent-drop-triage",
Expand Down Expand Up @@ -2213,6 +2223,19 @@
],
"summary": "Single-skill plugin: claudeception."
},
{
"name": "cmux-node-options-tmpdir-guard",
"claude_manifest": "plugins/cmux-node-options-tmpdir-guard/.claude-plugin/plugin.json",
"codex_manifest": "plugins/cmux-node-options-tmpdir-guard/.codex-plugin/plugin.json",
"hosts": [
"claude",
"codex"
],
"skills": [
"cmux-node-options-tmpdir-guard"
],
"summary": "Single-skill plugin: cmux-node-options-tmpdir-guard."
},
{
"name": "cmux-autoresume-after-reboot",
"claude_manifest": "plugins/cmux-autoresume-after-reboot/.claude-plugin/plugin.json",
Expand Down
12 changes: 12 additions & 0 deletions plugins/cmux-node-options-tmpdir-guard/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"name": "cmux-node-options-tmpdir-guard",
"version": "1.0.0",
"description": "Keep cmux's Claude NODE_OPTIONS restore guard alive when it lives under $TMPDIR. Use when: (1) a long-lived cmux/Claude session suddenly has every node/claude invocation fail with a missing --require target, (2) NODE_OPTIONS points at a /var/folders/.../T/cmux-claude-node-options/restore-node-options.cjs path on a cmux build that predates manaflow-ai/cmux#3699, (3) reboot-surviving cmux sessions break after about three days idle. Root cause: macOS reaps $TMPDIR files untouched for about three days, so the required guard file vanishes while NODE_OPTIONS still references it. Covers the persistent-HOME-copy stopgap, the LaunchAgent that recreates and mtime-refreshes the $TMPDIR file daily, why a LaunchAgent resolves the same per-user temp base, and when to retire it.",
"author": {
"name": "voitta-ai"
},
"homepage": "https://github.com/voitta-ai/skillz",
"repository": "https://github.com/voitta-ai/skillz",
"license": "MIT",
"skills": "./skills/"
}
19 changes: 19 additions & 0 deletions plugins/cmux-node-options-tmpdir-guard/.codex-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"name": "cmux-node-options-tmpdir-guard",
"version": "1.0.0",
"description": "Keep cmux's Claude NODE_OPTIONS restore guard alive when it lives under $TMPDIR. Use when: (1) a long-lived cmux/Claude session suddenly has every node/claude invocation fail with a missing --require target, (2) NODE_OPTIONS points at a /var/folders/.../T/cmux-claude-node-options/restore-node-options.cjs path on a cmux build that predates manaflow-ai/cmux#3699, (3) reboot-surviving cmux sessions break after about three days idle. Root cause: macOS reaps $TMPDIR files untouched for about three days, so the required guard file vanishes while NODE_OPTIONS still references it. Covers the persistent-HOME-copy stopgap, the LaunchAgent that recreates and mtime-refreshes the $TMPDIR file daily, why a LaunchAgent resolves the same per-user temp base, and when to retire it.",
"author": "voitta-ai",
"homepage": "https://github.com/voitta-ai/skillz",
"repository": "https://github.com/voitta-ai/skillz",
"license": "MIT",
"keywords": [
"claude",
"codex",
"skills",
"cmux",
"macos",
"launchd",
"nodejs"
],
"skills": "./skills/"
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "launchd-env-sync-session-leak",
"description": "Diagnose and undo a login-environment -> launchd sync that was run from inside a multiplexer pane or an AI-agent session, so PROMPT_COMMAND, CMUX_*, CLAUDECODE/CLAUDE_CODE_*,...",
"version": "1.0.0",
"version": "1.2.0",
"author": {
"name": "voitta-ai"
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "launchd-env-sync-session-leak",
"version": "1.0.0",
"version": "1.2.0",
"description": "Diagnose and undo a login-environment -> launchd sync that was run from inside a multiplexer pane or an AI-agent session, so PROMPT_COMMAND, CMUX_*, CLAUDECODE/CLAUDE_CODE_*,...",
"author": "voitta-ai",
"homepage": "https://github.com/voitta-ai/skillz",
Expand Down
2 changes: 1 addition & 1 deletion plugins/skillz/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "skillz",
"description": "Skillz bundle: every skill in the catalog except those whose own plugin ships hooks (see catalog.json).",
"version": "1.129.0",
"version": "1.130.0",
"author": {
"name": "voitta-ai"
},
Expand Down
2 changes: 1 addition & 1 deletion plugins/skillz/.codex-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "skillz",
"version": "1.129.0",
"version": "1.130.0",
"description": "Skillz bundle: every skill in the catalog except those whose own plugin ships hooks (see catalog.json).",
"author": "voitta-ai",
"homepage": "https://github.com/voitta-ai/skillz",
Expand Down
1 change: 1 addition & 0 deletions plugins/skillz/skills-codex/cmux-node-options-tmpdir-guard
121 changes: 121 additions & 0 deletions skills/cmux-node-options-tmpdir-guard/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
---
name: cmux-node-options-tmpdir-guard
description: |
Keep cmux's Claude NODE_OPTIONS restore guard alive when it lives under $TMPDIR.
Use when: (1) a long-lived cmux/Claude session suddenly has every `node`/`claude`
invocation fail with a missing `--require` target, (2) NODE_OPTIONS points at
/var/folders/.../T/cmux-claude-node-options/restore-node-options.cjs on a cmux
build that predates manaflow-ai/cmux#3699, (3) reboot-surviving cmux sessions
break after ~3 days idle. Root cause: macOS reaps $TMPDIR files untouched ~3
days; the required guard file vanishes while NODE_OPTIONS still references it.
Stopgap: persistent HOME copy + a LaunchAgent that recreates and mtime-refreshes
the $TMPDIR file daily. Retire once on a build that writes the guard under $HOME.
author: Claude Code
version: 1.0.0
date: 2026-07-25
source: https://github.com/voitta-ai/skillz
source_file: skills/cmux-node-options-tmpdir-guard/SKILL.md
---

# cmux NODE_OPTIONS $TMPDIR guard survival

> **Canonical source.** This skill lives in the repo at
> https://github.com/voitta-ai/skillz (file:
> `skills/cmux-node-options-tmpdir-guard/SKILL.md`).


## Problem

cmux launches Claude Code with a `--require` shim to restore the user's original
`NODE_OPTIONS` inside the agent:

```
NODE_OPTIONS=--require=$TMPDIR/cmux-claude-node-options/restore-node-options.cjs --max-old-space-size=4096
```

That required `.cjs` lives under `$TMPDIR` (`/var/folders/.../T`). macOS reaps
files there that go untouched for ~3 days. A cmux/Claude session that outlives that
window (exactly what a reboot-surviving setup encourages) loses the file while
`NODE_OPTIONS` still points at it, so **every** subsequent `node`/`claude`
invocation in the session dies on a missing `--require` target.

Upstream fix: [manaflow-ai/cmux#3699](https://github.com/manaflow-ai/cmux/pull/3699)
(closes #3463) writes the guard under `$HOME/.claude/cmux` instead. Builds predating
it still use `$TMPDIR`.

## Context / Trigger Conditions

- `echo $NODE_OPTIONS` shows a `--require=/var/folders/.../T/cmux-claude-node-options/...` path.
- `~/.claude/cmux/` does not exist (fix #3699 not present).
- Node/Claude subprocesses fail referencing a missing `restore-node-options.cjs`.
- Applies to macOS + a cmux build older than the one that lands #3699.

## Solution

The guard content is static and session-independent, so one persistent copy under
`$HOME` covers every session. A LaunchAgent recreates the `$TMPDIR` file and
refreshes its mtime daily — well inside the ~3-day reap window.

1. Persistent copy (source of truth):

```bash
mkdir -p ~/.claude/cmux/cmux-claude-node-options
cp "$TMPDIR/cmux-claude-node-options/restore-node-options.cjs" \
~/.claude/cmux/cmux-claude-node-options/restore-node-options.cjs
```

2. Refresh script `~/.claude/cmux/refresh-node-options-guard.sh` (chmod +x):

```bash
#!/usr/bin/env bash
set -euo pipefail
src="$HOME/.claude/cmux/cmux-claude-node-options/restore-node-options.cjs"
tmpbase="$(getconf DARWIN_USER_TEMP_DIR 2>/dev/null || echo "${TMPDIR:-/tmp}")"
dst_dir="${tmpbase%/}/cmux-claude-node-options"
dst="$dst_dir/restore-node-options.cjs"
[ -f "$src" ] || exit 0
mkdir -p "$dst_dir"
cp -f "$src" "$dst" # cp refreshes mtime, resetting the reaper clock
```

3. LaunchAgent `~/Library/LaunchAgents/<label>.plist` (RunAtLoad + `StartInterval`
86400), `ProgramArguments` = `/bin/bash <script>`. Load with the modern API:

```bash
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/<label>.plist
launchctl kickstart -k gui/$(id -u)/<label>
```

## Why it works (key subtlety)

A LaunchAgent runs in the **same per-user launchd context** as the GUI app, so
`getconf DARWIN_USER_TEMP_DIR` inside it returns the **same** `/var/folders/.../T`
base cmux used — verified equal to `$TMPDIR`. That is what lets the agent target
the right file without hardcoding the (per-boot) path. `cp` updating mtime is what
resets the reaper's idle clock; a bare `touch` of an existing file would also work
but `cp` additionally self-heals a partially-deleted dir.

## Verification

```bash
rm -f "$TMPDIR/cmux-claude-node-options/restore-node-options.cjs"
bash ~/.claude/cmux/refresh-node-options-guard.sh
ls "$TMPDIR/cmux-claude-node-options/" # file back, content identical, mtime=now
launchctl print gui/$(id -u)/<label> | grep 'last exit' # last exit code = 0
```

## Notes

- Stopgap only. Once on a build with #3699, the guard lives under `$HOME`; the
LaunchAgent becomes a harmless no-op and can be removed:
`launchctl bootout gui/$(id -u)/<label>`.
- `StartInterval` 86400 (daily) is well inside the ~3-day reap window; do not
stretch it near 3 days.
- Do not use deprecated `launchctl load -w`; use `bootstrap`/`kickstart`/`bootout`.

## References

- [manaflow-ai/cmux#3699](https://github.com/manaflow-ai/cmux/pull/3699) (closes #3463)
- `launchd-env-sync-session-leak` - the same class of failure from the other
direction: a stale value synced *into* launchd rather than a guard file
reaped out from under one.
4 changes: 3 additions & 1 deletion skills/launchd-env-sync-session-leak/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ description: |
services), the purge, the reload order that actually clears a service, and
the `bootout` race that leaves it DOWN.
author: Claude Code
version: 1.1.0
version: 1.2.0
date: 2026-08-28
source: https://github.com/voitta-ai/skillz
source_file: skills/launchd-env-sync-session-leak/SKILL.md
Expand Down Expand Up @@ -164,3 +164,5 @@ faithfully re-publish the leaked names on its next run. Close the gate first.

- `cmux-autoresume-after-reboot` - a stray `CMUX_DISABLE_SESSION_RESTORE`
synced into launchd is the same mechanism with a different victim.
- `cmux-node-options-tmpdir-guard` - why the `NODE_OPTIONS` `--require` target
lives in `$TMPDIR` and what keeps it alive.
Loading