Skip to content

fix: auto-start dev servers in tmux instead of blocking#344

Merged
affaan-m merged 3 commits intoaffaan-m:mainfrom
helbetica-com-ar:fix/dev-server-auto-tmux
Mar 7, 2026
Merged

fix: auto-start dev servers in tmux instead of blocking#344
affaan-m merged 3 commits intoaffaan-m:mainfrom
helbetica-com-ar:fix/dev-server-auto-tmux

Conversation

@helbetica-com-ar
Copy link
Copy Markdown
Contributor

@helbetica-com-ar helbetica-com-ar commented Mar 6, 2026

Summary

Replaces the blocking PreToolUse hook that used process.exit(2) with an auto-transform hook that wraps server startup commands in tmux sessions, allowing Claude Code to proceed without interruption.

What Changed

  • New Script: scripts/hooks/auto-tmux-dev.js - Transforms startup commands to run in detached tmux sessions
  • Updated: hooks/hooks.json - References new script instead of inline blocking node command

How It Works

When detecting startup commands (npm run dev, pnpm dev, yarn dev, bun run dev):

  1. Derives tmux session name from current directory basename
  2. Kills any existing session with that name (clean restart)
  3. Creates new detached tmux session with the startup command
  4. Echoes confirmation with log viewing instructions

Benefits

  • Non-blocking: Startup servers no longer interrupt Claude Code execution
  • Persistent: Sessions survive Claude Code restarts; logs remain accessible
  • Multi-project: Each project gets its own session; multiple projects can run simultaneously
  • Graceful fallback: If tmux unavailable, command still runs without blocking
  • No false positives: Uses word boundary regex; won't trigger on search commands

Testing

  • Verified hook script parses stdin/stdout correctly
  • Confirmed regex matches all server startup variants with word boundaries
  • Updated both source and cached plugin (v1.4.1) for immediate effect
  • Tested with various command patterns

Implementation Notes

Authored with Claude Code - hook implementation follows existing patterns from post-edit-format.js and other scripts in the hooks directory.


Summary by cubic

Replaces the blocking dev-server hook with a non-blocking transform that starts dev commands in a detached tmux session on macOS/Linux or a new cmd window on Windows. Keeps execution unblocked, names sessions after the project directory, and makes logs easy to view.

  • New Features

    • Adds scripts/hooks/auto-tmux-dev.js to detect npm/pnpm/yarn/bun dev commands and run them detached (tmux on Unix, cmd window on Windows).
    • Clean restart: kills any existing session with the same sanitized directory-based name before starting a new one.
    • Updates hooks/hooks.json to use the new script; falls back to the original command if tmux isn’t available.
  • Bug Fixes

    • Hardened script: sanitize session names, escape commands, fix variable scope, replace unused execFileSync with spawnSync, check tmux availability, and pass through invalid JSON unchanged.
    • Windows: fix quoting by doubling double quotes ("") in cmd.exe so quoted args (e.g., --filter="my-app") work correctly.

Written for commit 4170a29. Summary will update on new commits.

Summary by CodeRabbit

  • New Features

    • Dev servers on non-Windows platforms now auto-start inside tmux sessions.
    • Sessions are named from the project directory for easy identification.
    • Startup confirms session creation and shows how to view logs.
    • On Windows, dev servers open in a new terminal window with a descriptive title.
  • Refactoring

    • Replaced prior inline blocking behavior with an external hook to manage tmux-based dev server startup.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 6, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3ae9d2ae-31a6-4dad-8c52-e46aaba92271

📥 Commits

Reviewing files that changed from the base of the PR and between 41d6c32 and 4170a29.

📒 Files selected for processing (2)
  • hooks/hooks.json
  • scripts/hooks/auto-tmux-dev.js
🚧 Files skipped from review as they are similar to previous changes (2)
  • hooks/hooks.json
  • scripts/hooks/auto-tmux-dev.js

📝 Walkthrough

Walkthrough

Replaces inline dev-server blocking logic with an external Node.js PreToolUse hook that detects dev commands and auto-starts them in tmux (naming sessions from the current directory) or opens a new Windows cmd window; returns modified JSON tool_input.command or passes input through unchanged.

Changes

Cohort / File(s) Summary
Hook Configuration
hooks/hooks.json
Replaces inline blocking logic with a call to scripts/hooks/auto-tmux-dev.js; updates the hook description to indicate auto-starting dev servers in tmux with directory-based session names.
New Tmux Auto-Start Script
scripts/hooks/auto-tmux-dev.js
Adds Node.js hook that reads JSON from stdin (1 MB limit), detects dev commands (npm/pnpm/yarn/bun), and on non-Windows wraps the command to run in a tmux detached session (sanitized session name, kills existing session, creates new one, echoes log instructions). On Windows opens the dev server in a new cmd window; if input invalid or tmux missing, passes original command through.

Sequence Diagram(s)

sequenceDiagram
  participant Tool as Tool Runner
  participant Hook as PreToolUse Hook (auto-tmux-dev.js)
  participant Shell as Shell/OS
  participant Tmux as tmux
  Tool->>Hook: send tool_input JSON via stdin
  Hook->>Hook: parse JSON, sanitize dir name, detect dev command
  alt Non-Windows & dev command detected
    Hook->>Shell: check tmux availability
    alt tmux available
      Hook->>Tmux: kill-session (if exists)
      Hook->>Tmux: create detached session running dev command
      Hook-->>Tool: return modified JSON with tmux-wrapped command and confirmation message
    else tmux missing
      Hook-->>Tool: return original JSON unchanged
    end
  else Windows & dev command detected
    Hook->>Shell: open new cmd window with descriptive title running dev command
    Hook-->>Tool: return modified JSON (Windows cmd invocation)
  else no dev command detected
    Hook-->>Tool: return original JSON unchanged
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

A rabbit taps the script with glee,
Spawns tmux nests named after a tree,
Commands slip in, then hop away,
Logs wait patient — come view the play,
Devs can sprint — hooray, hooray! 🐰✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix: auto-start dev servers in tmux instead of blocking' accurately summarizes the main change: replacing a blocking dev server hook with auto-starting servers in tmux sessions.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
scripts/hooks/auto-tmux-dev.js (2)

2-5: Documentation claims cross-platform but Windows is explicitly unsupported.

The header comment states "Cross-platform (Windows, macOS, Linux)" but Line 44 explicitly skips Windows (process.platform !== 'win32'). This is misleading.

Per coding guidelines for scripts/**/*.js, cross-platform compatibility should be ensured. Consider either implementing Windows support (e.g., using start /b or Windows Terminal tabs) or updating the documentation to accurately reflect that Windows falls back to running the original command unchanged.

📝 Update documentation to reflect actual behavior
 /**
  * Auto-Tmux Dev Hook - Start dev servers in tmux automatically
  *
- * Cross-platform (Windows, macOS, Linux)
+ * macOS/Linux: Runs dev servers in tmux sessions (non-blocking)
+ * Windows: Falls back to running the original command (blocking)
  *

As per coding guidelines: "Ensure cross-platform compatibility for Windows, macOS, and Linux via Node.js scripts".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/hooks/auto-tmux-dev.js` around lines 2 - 5, The header comment in
auto-tmux-dev.js incorrectly claims "Cross-platform (Windows, macOS, Linux)"
while the script explicitly skips Windows via the process.platform !== 'win32'
check; either implement Windows handling (e.g., add a Windows branch using a
suitable launcher like start /b or Windows Terminal commands within the existing
main routine that configures tmux/sessions) or update the top-of-file comment to
accurately state that Windows is not supported and that the script will fall
back to running the original command unchanged; locate the platform conditional
using process.platform and adjust the file header string or add a
Windows-specific code path in the same module to satisfy the guideline.

21-22: Unused import: execFileSync is never used.

The execFileSync import from child_process is not used anywhere in this script.

🧹 Remove unused import
 const path = require('path');
-const { execFileSync } = require('child_process');
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/hooks/auto-tmux-dev.js` around lines 21 - 22, Remove the unused
import by deleting execFileSync from the child_process require in
scripts/hooks/auto-tmux-dev.js; locate the top-level require that currently
reads const { execFileSync } = require('child_process') (or a combined require
with path) and remove the execFileSync symbol so only needed imports (e.g.,
path) remain.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@scripts/hooks/auto-tmux-dev.js`:
- Around line 44-55: The current concatenation of sessionName and cmd into
transformedCmd creates a shell-injection risk; update the code so you do NOT
interpolate raw sessionName/cmd into a single shell string. Either (preferred)
stop building transformedCmd and invoke tmux via a non-shell API (e.g., use
child_process.spawn/execFile and call tmux with arguments: kill-session -t
sessionName, new-session -d -s sessionName and pass the dev command as a safe
argument) or (if you must produce a shell string) apply proper
shell-quoting/escaping to both sessionName and cmd before composing
transformedCmd (escape single quotes inside cmd and wrap it in single quotes,
and escape any characters in sessionName used in double quotes); then assign the
safe result back to input.tool_input.command. Ensure you change the code around
the sessionName, transformedCmd, and input.tool_input.command sites to use the
safe approach.
- Around line 57-62: The catch block around JSON.parse(data) swallows the error
and leaves input undefined so process.stdout.write(JSON.stringify(input)) emits
"undefined"; instead, in the catch for JSON.parse(data) (the block that
surrounds JSON.parse(data) and sets input), write the original data string to
stdout (use process.stdout.write(data)) and exit, preserving the raw input for
downstream hooks; update the catch associated with JSON.parse(data) and replace
the existing process.stdout.write(JSON.stringify(input)) path when parsing
failed so the original data is passed through.

---

Nitpick comments:
In `@scripts/hooks/auto-tmux-dev.js`:
- Around line 2-5: The header comment in auto-tmux-dev.js incorrectly claims
"Cross-platform (Windows, macOS, Linux)" while the script explicitly skips
Windows via the process.platform !== 'win32' check; either implement Windows
handling (e.g., add a Windows branch using a suitable launcher like start /b or
Windows Terminal commands within the existing main routine that configures
tmux/sessions) or update the top-of-file comment to accurately state that
Windows is not supported and that the script will fall back to running the
original command unchanged; locate the platform conditional using
process.platform and adjust the file header string or add a Windows-specific
code path in the same module to satisfy the guideline.
- Around line 21-22: Remove the unused import by deleting execFileSync from the
child_process require in scripts/hooks/auto-tmux-dev.js; locate the top-level
require that currently reads const { execFileSync } = require('child_process')
(or a combined require with path) and remove the execFileSync symbol so only
needed imports (e.g., path) remain.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 08d45b9a-0c14-4657-954f-b445782b2f93

📥 Commits

Reviewing files that changed from the base of the PR and between 03b3e0d and 0ceb963.

📒 Files selected for processing (2)
  • hooks/hooks.json
  • scripts/hooks/auto-tmux-dev.js

Comment thread scripts/hooks/auto-tmux-dev.js Outdated
Comment thread scripts/hooks/auto-tmux-dev.js
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 2 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="scripts/hooks/auto-tmux-dev.js">

<violation number="1" location="scripts/hooks/auto-tmux-dev.js:53">
P1: The tmux wrapper command is brittle: it has no tmux-availability fallback and does not escape single quotes in `cmd`, so valid dev commands can fail to start.</violation>

<violation number="2" location="scripts/hooks/auto-tmux-dev.js:61">
P1: `input` is out of scope when writing stdout, causing a runtime `ReferenceError` and breaking the hook.</violation>
</file>

Since this is your first cubic review, here's how it works:

  • cubic automatically reviews your code and comments on bugs and improvements
  • Teach cubic by replying to its comments. cubic learns from your replies and gets better over time
  • Add one-off context when rerunning by tagging @cubic-dev-ai with guidance or docs links (including llms.txt)
  • Ask questions if you need clarification on any suggestion

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread scripts/hooks/auto-tmux-dev.js Outdated
Comment thread scripts/hooks/auto-tmux-dev.js Outdated
Copy link
Copy Markdown
Owner

@affaan-m affaan-m left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review: this PR has merge conflicts. Please rebase or resolve.

Copy link
Copy Markdown
Owner

@affaan-m affaan-m left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review: this PR has merge conflicts. Please rebase or resolve.

Copy link
Copy Markdown
Owner

@affaan-m affaan-m left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review: this PR has merge conflicts. Please rebase or resolve.

Copy link
Copy Markdown
Owner

@affaan-m affaan-m left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review: this PR has merge conflicts. Please rebase or resolve.

Copy link
Copy Markdown
Owner

@affaan-m affaan-m left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review: this PR has merge conflicts. Please rebase or resolve.

Copy link
Copy Markdown
Owner

@affaan-m affaan-m left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review: this PR has merge conflicts. Please rebase or resolve.

Copy link
Copy Markdown
Owner

@affaan-m affaan-m left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review: this PR has merge conflicts. Please rebase or resolve.

Copy link
Copy Markdown
Owner

@affaan-m affaan-m left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review: this PR has merge conflicts. Please rebase or resolve.

Copy link
Copy Markdown
Owner

@affaan-m affaan-m left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review: this PR has merge conflicts. Please rebase or resolve.

Copy link
Copy Markdown
Owner

@affaan-m affaan-m left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review: this PR has merge conflicts. Please rebase or resolve.

helbetica-com-ar added a commit to helbetica-com-ar/everything-claude-code that referenced this pull request Mar 7, 2026
Critical fixes:
- Fix variable scope: declare 'input' before try block, not inside
- Fix shell injection: sanitize sessionName and escape cmd for shell
- Replace unused execFileSync import with spawnSync

Improvements:
- Add real Windows support using cmd /k window launcher
- Add tmux availability check with graceful fallback
- Update header comment to accurately describe platform support

Test coverage:
- Valid JSON input: transforms command for respective platform
- Invalid JSON: passes through raw data unchanged
- Unsupported tools: gracefully falls back to original command
- Shell metacharacters: sanitized in sessionName, escaped in cmd
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 1 file (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="scripts/hooks/auto-tmux-dev.js">

<violation number="1" location="scripts/hooks/auto-tmux-dev.js:62">
P2: Wrong escape sequence for cmd.exe: `\"` is not a valid double-quote escape in cmd.exe. Use `^"` (cmd.exe's general escape character) or `""` (doubling) instead.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread scripts/hooks/auto-tmux-dev.js Outdated
Copy link
Copy Markdown
Owner

@affaan-m affaan-m left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review: this PR has merge conflicts. Please rebase or resolve.

Copy link
Copy Markdown
Owner

@affaan-m affaan-m left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review: this PR has merge conflicts. Please rebase or resolve.

Replace blocking PreToolUse hook that used process.exit(2) with an auto-transform hook that:
- Detects development server commands
- Wraps them in tmux with directory-based session names
- Runs server detached so Claude Code is not blocked
- Provides confirmation message with log viewing instructions

Benefits:
- Development servers no longer block Claude Code execution
- Each project gets its own tmux session (allows multiple projects)
- Logs remain accessible via 'tmux capture-pane -t <session>'
- Non-blocking: if tmux unavailable, command still runs (graceful fallback)

Implementation:
- Created scripts/hooks/auto-tmux-dev.js with transform logic
- Updated hooks.json to reference the script instead of inline node command
- Applied same fix to cached plugin version (1.4.1) for immediate effect
Critical fixes:
- Fix variable scope: declare 'input' before try block, not inside
- Fix shell injection: sanitize sessionName and escape cmd for shell
- Replace unused execFileSync import with spawnSync

Improvements:
- Add real Windows support using cmd /k window launcher
- Add tmux availability check with graceful fallback
- Update header comment to accurately describe platform support

Test coverage:
- Valid JSON input: transforms command for respective platform
- Invalid JSON: passes through raw data unchanged
- Unsupported tools: gracefully falls back to original command
- Shell metacharacters: sanitized in sessionName, escaped in cmd
Use double-quote doubling ('""') instead of backslash-escape ('\\\") for cmd.exe syntax.
Backslash escaping is Unix convention and not recognized by cmd.exe. This fixes quoted
arguments in dev server commands on Windows (e.g., 'npm run dev --filter="my-app"').
@helbetica-com-ar helbetica-com-ar force-pushed the fix/dev-server-auto-tmux branch from 41d6c32 to 4170a29 Compare March 7, 2026 06:14
@affaan-m affaan-m merged commit 7bed751 into affaan-m:main Mar 7, 2026
2 checks passed
affaan-m added a commit that referenced this pull request Mar 8, 2026
PR #344 replaced the blocking dev-server hook with auto-tmux-dev.js
which transforms commands into tmux sessions (exit 0) instead of
blocking them (exit 2). Updated 2 tests to match the new behavior.
This was referenced Mar 10, 2026
Closed
floatingman pushed a commit to floatingman/everything-claude-code that referenced this pull request Mar 22, 2026
* fix: auto-start development servers in tmux instead of blocking

Replace blocking PreToolUse hook that used process.exit(2) with an auto-transform hook that:
- Detects development server commands
- Wraps them in tmux with directory-based session names
- Runs server detached so Claude Code is not blocked
- Provides confirmation message with log viewing instructions

Benefits:
- Development servers no longer block Claude Code execution
- Each project gets its own tmux session (allows multiple projects)
- Logs remain accessible via 'tmux capture-pane -t <session>'
- Non-blocking: if tmux unavailable, command still runs (graceful fallback)

Implementation:
- Created scripts/hooks/auto-tmux-dev.js with transform logic
- Updated hooks.json to reference the script instead of inline node command
- Applied same fix to cached plugin version (1.4.1) for immediate effect

* fix: resolve PR affaan-m#344 code review issues in auto-tmux-dev.js

Critical fixes:
- Fix variable scope: declare 'input' before try block, not inside
- Fix shell injection: sanitize sessionName and escape cmd for shell
- Replace unused execFileSync import with spawnSync

Improvements:
- Add real Windows support using cmd /k window launcher
- Add tmux availability check with graceful fallback
- Update header comment to accurately describe platform support

Test coverage:
- Valid JSON input: transforms command for respective platform
- Invalid JSON: passes through raw data unchanged
- Unsupported tools: gracefully falls back to original command
- Shell metacharacters: sanitized in sessionName, escaped in cmd

* fix: correct cmd.exe escape sequence for double quotes on Windows

Use double-quote doubling ('""') instead of backslash-escape ('\\\") for cmd.exe syntax.
Backslash escaping is Unix convention and not recognized by cmd.exe. This fixes quoted
arguments in dev server commands on Windows (e.g., 'npm run dev --filter="my-app"').
floatingman pushed a commit to floatingman/everything-claude-code that referenced this pull request Mar 22, 2026
PR affaan-m#344 replaced the blocking dev-server hook with auto-tmux-dev.js
which transforms commands into tmux sessions (exit 0) instead of
blocking them (exit 2). Updated 2 tests to match the new behavior.
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.

2 participants