Skip to content

fix: tmux parsing/targeting + review feedback - #3

Merged
leeroybrun merged 2 commits into
mainfrom
slopus/pr/tmux-fix-2026-01-12
Jan 12, 2026
Merged

leeroybrun merged 2 commits into
mainfrom
slopus/pr/tmux-fix-2026-01-12

Conversation

@leeroybrun

@leeroybrun leeroybrun commented Jan 12, 2026 •

Copy link
Copy Markdown
Owner

Validation:

  • Verified TMUX/TMUX_PANE values inside an isolated tmux server (custom socket)

  • Verified kill-window syntax and new-session -t semantics via tmux manpage

Changes:

  • Correct TMUX env parsing (socket_path + server_pid) and prefer TMUX_PANE for pane id

  • Avoid invalid -t injection for commands that already specify a target; do not treat new-session as targetable

  • Fix kill-window invocation to use -t session:window

  • Align Machine.metadata typing with runtime (nullable)

  • Make socket send behavior consistent when disconnected; tighten misc scripts/tests

Issues reported by CodeRabbit in #1

Summary by CodeRabbit

  • Bug Fixes

    • Improved resume session detection to recognize additional flag variants.
    • Enhanced error handling for incomplete command invocations with clearer usage guidance.
    • Fixed machine metadata nullability handling.
  • Chores

    • Refactored internal signal handling for improved process management.
    • Updated environment initialization and path detection logic.
    • Enhanced test coverage and fixture updates for environment detection scenarios.

✏️ Tip: You can customize this high-level summary in your review settings.

Validation:

- Verified TMUX/TMUX_PANE values inside an isolated tmux server (custom socket)

- Verified kill-window syntax and new-session -t semantics via tmux manpage

Changes:

- Correct TMUX env parsing (socket_path + server_pid) and prefer TMUX_PANE for pane id

- Avoid invalid -t injection for commands that already specify a target; do not treat new-session as targetable

- Fix kill-window invocation to use -t session:window

- Align Machine.metadata typing with runtime (nullable)

- Make socket send behavior consistent when disconnected; tighten misc scripts/tests
@coderabbitai

coderabbitai Bot commented Jan 12, 2026 •

Copy link
Copy Markdown

Warning

Rate limit exceeded

@leeroybrun has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 11 minutes and 35 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between a929750 and 13db0d0.

📒 Files selected for processing (2)
  • src/api/apiSession.ts
  • src/claude/runClaude.ts
📝 Walkthrough

Walkthrough

This pull request refactors environment detection and signal handling across development utilities, test infrastructure, and core runner scripts. Key changes include streamlined environment variable initialization, refined platform detection logic, enhanced error handling for incomplete invocations, and significant restructuring of tmux environment parsing and command execution patterns.

Changes

Cohort / File(s) Summary
Development Environment Setup
bin/happy-dev.mjs
Moved HAPPY_HOME_DIR and HAPPY_VARIANT initialization to unconditional top-level, removing duplicate assignments from conditional branches. Environment variables now established before control flow evaluation.
Platform Detection & Version Utilities
scripts/claude_version_utils.cjs, scripts/claude_version_utils.test.ts
Windows npm detection for Claude Code now requires additional path segments (@anthropic-ai, claude-code) and excludes .claude-code- paths, narrowing npm-source classification. Test expectation updated to reflect stricter detection.
Script Runtime Guards & Discovery
scripts/env-wrapper.cjs, scripts/ripgrep_launcher.cjs, scripts/test-continue-fix.sh
Added command argument validation in env-wrapper (prints usage and exits on missing command). Enabled stdout capture in ripgrep discovery via stdio: ['ignore', 'pipe', 'ignore']. Added directory existence check in test-continue-fix using existsSync.
API Communication & Type Safety
src/api/apiSession.ts, src/api/types.ts
Introduced private canSend() helper to centralize socket connectivity checks across multiple message paths. Made Machine.metadata field nullable (`MachineMetadata
Claude Session Management
src/claude/claudeLocal.ts, src/claude/runClaude.ts
Extended resume flag recognition to treat -r as resume indicator in initial calculation. Added AbortController with SIGINT/SIGTERM handlers in runClaude for coordinated cancellation; removed duplicate hasResumeFlag declaration affecting offline session logic.
Tmux Environment Detection
src/utils/tmux.ts, src/utils/tmux.test.ts
Major refactor of tmux environment interface: replaced session/window fields with socket_path and server_pid; updated detectTmuxEnvironment() to parse three components and require all to be valid; removed automatic targeting for new-session, kill-session, list-windows, list-panes; refactored spawnInTmux() and executeTmuxCommand() signatures and implementation. Test expectations updated to validate new output shape.
Documentation & Test Cleanup
docs/bug-fix-plan-2025-01-15-athundt.md, src/api/api.test.ts
Removed unused on: vi.fn() mock property from claudeLocal test spawn setup. Consolidated console spy initialization in api test to avoid duplication.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • Sync upstream main 12-01-2016 #1: Modifies bin/happy-dev.mjs environment variable initialization for HAPPY_HOME_DIR and HAPPY_VARIANT, directly overlapping with this PR's refactoring of the same environment setup logic.

Poem

🐰 A hopper's delight, the changes take flight,
With tmux paths cleaned and sockets held tight,
Signals now answered with AbortController's grace,
And guards on each send keep the sessions in place. ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix: tmux parsing/targeting + review feedback' directly addresses the main changes: tmux environment parsing corrections, command targeting fixes, and incorporation of review feedback across multiple files.

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


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.

@gemini-code-assist

Copy link
Copy Markdown

Summary of Changes

Hello @leeroybrun, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request refines the core functionality related to tmux integration, making its environment parsing and command execution more accurate and robust. It also includes several quality-of-life improvements across various scripts and API components, such as ensuring consistent socket communication, enhancing error handling, and aligning type definitions with actual runtime states. These changes collectively aim to improve the stability and reliability of the application, addressing specific feedback and potential edge cases.

Highlights

  • TMUX Parsing and Targeting Improvements: The logic for parsing the TMUX environment variable has been significantly refactored to correctly extract socket_path, server_pid, and pane information, with a preference for TMUX_PANE for the pane ID. This also includes stricter validation for malformed TMUX values. Additionally, tmux command targeting has been refined to avoid injecting -t if an explicit target is already present and new-session is no longer treated as targetable in the same way, aligning with tmux's behavior where new-session -t targets a group.
  • Consistent Socket Message Sending: A new private method canSend was introduced in ApiSessionClient to centralize and ensure that messages are only attempted to be sent over the socket when it is actively connected, preventing unnecessary logging and potential issues when disconnected.
  • Enhanced Error Handling and Robustness: Several scripts and utilities received minor fixes to improve robustness, including moving environment variable setup earlier in bin/happy-dev.mjs, refining Windows NPM detection in scripts/claude_version_utils.cjs, adding a missing command argument check in scripts/env-wrapper.cjs, and implementing signal handling for claudeLocal execution in src/claude/runClaude.ts for proper termination.
  • Type Alignment and Test Updates: The Machine.metadata type in src/api/types.ts was updated to be nullable (MachineMetadata | null) to reflect runtime behavior. Corresponding test files (src/utils/tmux.test.ts, scripts/claude_version_utils.test.ts, src/api/api.test.ts) were updated to match the new logic and expected outcomes.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a number of valuable fixes and improvements. The corrections to tmux environment variable parsing, command targeting, and kill-window invocation are particularly great, making the tmux integration much more robust. The refactoring in apiSession.ts to centralize the socket connection check is a good step towards cleaner code, and the added argument validation in scripts improves their reliability.

I've included a few suggestions in src/api/apiSession.ts to slightly improve efficiency by performing the socket connection check before encrypting message payloads. This avoids unnecessary work when the socket is disconnected.

Overall, this is a solid set of changes that addresses several important issues.

Comment thread src/api/apiSession.ts Outdated
Comment on lines +246 to +249
const encrypted = encodeBase64(encrypt(this.encryptionKey, this.encryptionVariant, content));

// Check if socket is connected before sending
if (!this.socket.connected) {
logger.debug('[API] Socket not connected, cannot send message. Message will be lost:', { type: body.type });
// TODO: Consider implementing message queue or HTTP fallback for reliability
}
if (!this.canSend('Codex message', { type: body?.type })) return;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

For efficiency, it's better to check if the socket is connected before performing the encryption. This avoids unnecessary computation if the message can't be sent anyway.

Suggested change
const encrypted = encodeBase64(encrypt(this.encryptionKey, this.encryptionVariant, content));
// Check if socket is connected before sending
if (!this.socket.connected) {
logger.debug('[API] Socket not connected, cannot send message. Message will be lost:', { type: body.type });
// TODO: Consider implementing message queue or HTTP fallback for reliability
}
if (!this.canSend('Codex message', { type: body?.type })) return;
// Check if socket is connected before sending
if (!this.canSend('Codex message', { type: body?.type })) return;
const encrypted = encodeBase64(encrypt(this.encryptionKey, this.encryptionVariant, content));

Comment thread src/api/apiSession.ts Outdated
Comment on lines +278 to +281
const encrypted = encodeBase64(encrypt(this.encryptionKey, this.encryptionVariant, content));

// Check if socket is connected before sending
if (!this.canSend(`${agentType} message`, { agentType, type: body?.type })) return;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

For efficiency, it's better to check if the socket is connected before performing the encryption. This avoids unnecessary computation if the message can't be sent anyway.

Suggested change
const encrypted = encodeBase64(encrypt(this.encryptionKey, this.encryptionVariant, content));
// Check if socket is connected before sending
if (!this.canSend(`${agentType} message`, { agentType, type: body?.type })) return;
// Check if socket is connected before sending
if (!this.canSend(`${agentType} message`, { agentType, type: body?.type })) return;
const encrypted = encodeBase64(encrypt(this.encryptionKey, this.encryptionVariant, content));

Comment thread src/api/apiSession.ts Outdated
Comment on lines +306 to +309
const encrypted = encodeBase64(encrypt(this.encryptionKey, this.encryptionVariant, content));

// Check if socket is connected before sending
if (!this.canSend('session event', { eventType: event.type })) return;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

For efficiency, it's better to check if the socket is connected before performing the encryption. This avoids unnecessary computation if the message can't be sent anyway.

Suggested change
const encrypted = encodeBase64(encrypt(this.encryptionKey, this.encryptionVariant, content));
// Check if socket is connected before sending
if (!this.canSend('session event', { eventType: event.type })) return;
// Check if socket is connected before sending
if (!this.canSend('session event', { eventType: event.type })) return;
const encrypted = encodeBase64(encrypt(this.encryptionKey, this.encryptionVariant, content));

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @src/claude/runClaude.ts:
- Around line 132-150: The abortOnSignal handler and abortController are
declared inside the try block so abortOnSignal is out of scope in the finally
block; move the declarations for abortController and abortOnSignal to the
surrounding scope (declare abortController and define abortOnSignal before the
try) so that the finally block can call process.removeListener('SIGINT',
abortOnSignal) and process.removeListener('SIGTERM', abortOnSignal) and still
pass abortController.signal into claudeLocal; keep the same calls to
process.once and the existing claudeLocal invocation.
🧹 Nitpick comments (5)
scripts/claude_version_utils.test.ts (1)

35-38: Update test name to reflect the new behavior.

The expectation change is correct, but the test name "should detect npm with different scoped packages" is now misleading. The test verifies that non-Claude-Code npm packages are not classified as 'npm' source.

✏️ Suggested rename
-      it('should detect npm with different scoped packages', () => {
+      it('should NOT detect npm for non-Claude-Code scoped packages', () => {
         const result = detectSourceFromPath('C:/Users/test/AppData/Roaming/npm/node_modules/@babel/core/cli.js');
         expect(result).toBe('PATH');
       });
src/utils/tmux.ts (1)

95-107: Consider removing unused interface fields.

TmuxSessionInfo still declares env_session and env_window (lines 103-104), but getSessionInfo no longer populates them since TmuxEnvironment no longer includes session/window. These fields will always be undefined.

♻️ Suggested cleanup
 export interface TmuxSessionInfo {
     target_session: string;
     session: string;
     window: string;
     pane: string;
     socket_path?: string;
     tmux_active: boolean;
     current_session?: string;
-    env_session?: string;
-    env_window?: string;
     env_pane?: string;
     available_sessions: string[];
 }

Also applies to: 695-702

src/api/apiSession.ts (3)

246-254: Consider moving the guard before encryption for consistency.

The canSend check at Line 249 is placed after encryption at Line 246. This differs from sendClaudeSessionMessage where the guard precedes encryption. Moving the guard before encryption avoids unnecessary crypto work when disconnected.

♻️ Suggested reordering
     sendCodexMessage(body: any) {
+        // Check if socket is connected before sending
+        if (!this.canSend('Codex message', { type: body?.type })) return;
+
         let content = {
             role: 'agent',
             content: {
                 type: 'codex',
                 data: body  // This wraps the entire Claude message
             },
             meta: {
                 sentFrom: 'cli'
             }
         };
         const encrypted = encodeBase64(encrypt(this.encryptionKey, this.encryptionVariant, content));
-        
-        // Check if socket is connected before sending
-        if (!this.canSend('Codex message', { type: body?.type })) return;
-        
+
         this.socket.emit('message', {
             sid: this.sessionId,
             message: encrypted
         });
     }

276-286: Same inconsistency: guard placed after encryption.

For consistency with sendClaudeSessionMessage, consider moving the canSend check (Lines 280-281) before the encryption at Line 278.

♻️ Suggested reordering
     sendAgentMessage(agentType: 'gemini' | 'codex' | 'claude' | 'opencode', body: any) {
+        // Check if socket is connected before sending
+        if (!this.canSend(`${agentType} message`, { agentType, type: body?.type })) return;
+
         let content = {
             role: 'agent',
             content: {
                 type: agentType,
                 data: body
             },
             meta: {
                 sentFrom: 'cli'
             }
         };
         
         logger.debug(`[SOCKET] Sending ${agentType} message:`, { type: body.type, hasMessage: !!body.message });
         
         const encrypted = encodeBase64(encrypt(this.encryptionKey, this.encryptionVariant, content));
-
-        // Check if socket is connected before sending
-        if (!this.canSend(`${agentType} message`, { agentType, type: body?.type })) return;
-
+        
         this.socket.emit('message', {
             sid: this.sessionId,
             message: encrypted
         });
     }

306-314: Same inconsistency: guard placed after encryption.

For consistency, consider moving the canSend check (Lines 308-309) before the encryption at Line 306.

♻️ Suggested reordering
     sendSessionEvent(event: {
         type: 'switch', mode: 'local' | 'remote'
     } | {
         type: 'message', message: string
     } | {
         type: 'permission-mode-changed', mode: 'default' | 'acceptEdits' | 'bypassPermissions' | 'plan'
     } | {
         type: 'ready'
     }, id?: string) {
+        // Check if socket is connected before sending
+        if (!this.canSend('session event', { eventType: event.type })) return;
+
         let content = {
             role: 'agent',
             content: {
                 id: id ?? randomUUID(),
                 type: 'event',
                 data: event
             }
         };
         const encrypted = encodeBase64(encrypt(this.encryptionKey, this.encryptionVariant, content));
-
-        // Check if socket is connected before sending
-        if (!this.canSend('session event', { eventType: event.type })) return;
-
+        
         this.socket.emit('message', {
             sid: this.sessionId,
             message: encrypted
         });
     }
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bdf6e62 and a929750.

📒 Files selected for processing (14)
  • bin/happy-dev.mjs
  • docs/bug-fix-plan-2025-01-15-athundt.md
  • scripts/claude_version_utils.cjs
  • scripts/claude_version_utils.test.ts
  • scripts/env-wrapper.cjs
  • scripts/ripgrep_launcher.cjs
  • scripts/test-continue-fix.sh
  • src/api/api.test.ts
  • src/api/apiSession.ts
  • src/api/types.ts
  • src/claude/claudeLocal.ts
  • src/claude/runClaude.ts
  • src/utils/tmux.test.ts
  • src/utils/tmux.ts
💤 Files with no reviewable changes (1)
  • docs/bug-fix-plan-2025-01-15-athundt.md
🧰 Additional context used
📓 Path-based instructions (8)
**/*.{js,ts,tsx,jsx,py,java,cs,go,rb,php}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{js,ts,tsx,jsx,py,java,cs,go,rb,php}: Use descriptive variable and function names that clearly convey intent
Write clear, concise comments explaining the 'why' behind complex logic
Keep functions small and focused on a single responsibility
Use consistent code formatting and adhere to the project's style guide

Files:

  • src/claude/runClaude.ts
  • scripts/claude_version_utils.test.ts
  • src/api/types.ts
  • src/utils/tmux.test.ts
  • src/api/api.test.ts
  • src/api/apiSession.ts
  • src/utils/tmux.ts
  • src/claude/claudeLocal.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: No untyped code - use strict typing in TypeScript with explicit parameter and return types
Use comprehensive JSDoc comments - each file includes header comments explaining responsibilities
ALL imports must be at the top of the file - NEVER import modules mid-code
Use named exports preferred, with occasional default exports for main functions
Avoid creating stupid small functions, getters, or setters - minimize unnecessary function decomposition
Avoid excessive use of if statements - especially if you can avoid control flow changes with a better design

Files:

  • src/claude/runClaude.ts
  • scripts/claude_version_utils.test.ts
  • src/api/types.ts
  • src/utils/tmux.test.ts
  • src/api/api.test.ts
  • src/api/apiSession.ts
  • src/utils/tmux.ts
  • src/claude/claudeLocal.ts
src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

src/**/*.{ts,tsx}: Use @/ alias for src imports, e.g., import { logger } from '@/ui/logger'
Use graceful error handling with proper error messages, try-catch blocks with specific error logging, and abort controllers for cancellable operations
All debugging must be through file logs to avoid disturbing Claude sessions - console output only for user-facing messages
Handle large JSON objects with truncation in logs to avoid excessive output
Minimize use of classes - prefer functional approaches in TypeScript

Files:

  • src/claude/runClaude.ts
  • src/api/types.ts
  • src/utils/tmux.test.ts
  • src/api/api.test.ts
  • src/api/apiSession.ts
  • src/utils/tmux.ts
  • src/claude/claudeLocal.ts
src/**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Use .ts file extensions for TypeScript files

Files:

  • src/claude/runClaude.ts
  • src/api/types.ts
  • src/utils/tmux.test.ts
  • src/api/api.test.ts
  • src/api/apiSession.ts
  • src/utils/tmux.ts
  • src/claude/claudeLocal.ts
**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.test.{ts,tsx}: Write unit tests using Vitest with no mocking - tests should make real API calls
Colocate test files with source files using .test.ts naming convention
Use descriptive test names and proper async handling in tests

Files:

  • scripts/claude_version_utils.test.ts
  • src/utils/tmux.test.ts
  • src/api/api.test.ts
src/api/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

src/api/**/*.{ts,tsx}: Implement end-to-end encryption for all communications using TweetNaCl
Use Socket.IO for real-time messaging in API sessions
Implement optimistic concurrency control for state updates in sessions
Implement session isolation through unique session IDs

Files:

  • src/api/types.ts
  • src/api/api.test.ts
  • src/api/apiSession.ts
src/api/types.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use Zod schemas for type-safe API communication in types files

Files:

  • src/api/types.ts
src/api/apiSession.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Register RPC handlers for remote procedure calls in session clients

Files:

  • src/api/apiSession.ts
🧠 Learnings (19)
📚 Learning: 2026-01-12T11:37:46.296Z
Learnt from: CR
Repo: leeroybrun/happy-cli PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-12T11:37:46.296Z
Learning: Applies to src/claude/watcher.{ts,tsx} : Implement file system watcher for Claude session files in interactive mode

Applied to files:

  • src/claude/runClaude.ts
  • scripts/claude_version_utils.test.ts
  • scripts/claude_version_utils.cjs
  • src/claude/claudeLocal.ts
  • scripts/test-continue-fix.sh
📚 Learning: 2026-01-12T11:37:46.296Z
Learnt from: CR
Repo: leeroybrun/happy-cli PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-12T11:37:46.296Z
Learning: Applies to src/**/*.{ts,tsx} : Use graceful error handling with proper error messages, try-catch blocks with specific error logging, and abort controllers for cancellable operations

Applied to files:

  • src/claude/runClaude.ts
📚 Learning: 2026-01-12T11:38:16.567Z
Learnt from: CR
Repo: leeroybrun/happy-cli PR: 0
File: src/daemon/CLAUDE.md:0-0
Timestamp: 2026-01-12T11:38:16.567Z
Learning: Applies to src/daemon/**/daemon/**/*.{ts,tsx} : On daemon shutdown, resolve the shutdown promise when OS signals (SIGINT/SIGTERM) are received, HTTP /stop endpoint is called, RPC requestShutdown is invoked, or uncaught exceptions occur

Applied to files:

  • src/claude/runClaude.ts
📚 Learning: 2026-01-12T11:38:16.567Z
Learnt from: CR
Repo: leeroybrun/happy-cli PR: 0
File: src/daemon/CLAUDE.md:0-0
Timestamp: 2026-01-12T11:38:16.567Z
Learning: Applies to src/daemon/**/daemon/**/*.{ts,tsx} : In stopDaemon() from controlClient.ts, read daemon.state.json and attempt graceful shutdown via HTTP POST to /stop endpoint, falling back to process.kill(pid, 'SIGKILL') if HTTP fails

Applied to files:

  • src/claude/runClaude.ts
📚 Learning: 2026-01-12T11:38:16.567Z
Learnt from: CR
Repo: leeroybrun/happy-cli PR: 0
File: src/daemon/CLAUDE.md:0-0
Timestamp: 2026-01-12T11:38:16.567Z
Learning: Applies to src/daemon/**/daemon/**/*.{ts,tsx} : Ensure caffeinate process is started by the daemon (not individual sessions) and tracked in daemon.state.json to prevent it from becoming a runaway process

Applied to files:

  • src/claude/runClaude.ts
📚 Learning: 2026-01-12T11:37:46.296Z
Learnt from: CR
Repo: leeroybrun/happy-cli PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-12T11:37:46.296Z
Learning: Applies to **/*.test.{ts,tsx} : Use descriptive test names and proper async handling in tests

Applied to files:

  • scripts/claude_version_utils.test.ts
  • src/utils/tmux.test.ts
  • scripts/test-continue-fix.sh
📚 Learning: 2026-01-12T11:38:16.567Z
Learnt from: CR
Repo: leeroybrun/happy-cli PR: 0
File: src/daemon/CLAUDE.md:0-0
Timestamp: 2026-01-12T11:38:16.567Z
Learning: Applies to src/daemon/**/daemon/**/*.{ts,tsx} : Implement machine metadata structure containing host, platform, happyCliVersion, homeDir, and happyHomeDir as static machine information that rarely changes

Applied to files:

  • src/api/types.ts
📚 Learning: 2026-01-12T11:38:16.567Z
Learnt from: CR
Repo: leeroybrun/happy-cli PR: 0
File: src/daemon/CLAUDE.md:0-0
Timestamp: 2026-01-12T11:38:16.567Z
Learning: Applies to src/daemon/**/daemon/**/*.{ts,tsx} : Emit 'machine-update-metadata' WebSocket events when machine metadata changes (rare updates like version changes), including expectedVersion for version mismatch handling

Applied to files:

  • src/api/types.ts
📚 Learning: 2026-01-12T11:38:16.567Z
Learnt from: CR
Repo: leeroybrun/happy-cli PR: 0
File: src/daemon/CLAUDE.md:0-0
Timestamp: 2026-01-12T11:38:16.567Z
Learning: Applies to src/daemon/**/daemon/**/*.{ts,tsx} : On daemon startup, POST to /v1/machines REST endpoint with encrypted metadata and daemonState, sending machineId, and receive metadataVersion and daemonStateVersion from server response

Applied to files:

  • src/api/types.ts
📚 Learning: 2026-01-12T11:38:16.567Z
Learnt from: CR
Repo: leeroybrun/happy-cli PR: 0
File: src/daemon/CLAUDE.md:0-0
Timestamp: 2026-01-12T11:38:16.567Z
Learning: Applies to src/daemon/**/daemon/**/*.{ts,tsx} : Encrypt all daemon state and metadata separately with TweetNaCl before transmitting via WebSocket; use base64 encoding for encrypted payloads

Applied to files:

  • src/api/types.ts
  • src/api/apiSession.ts
📚 Learning: 2026-01-12T11:38:16.567Z
Learnt from: CR
Repo: leeroybrun/happy-cli PR: 0
File: src/daemon/CLAUDE.md:0-0
Timestamp: 2026-01-12T11:38:16.567Z
Learning: Applies to src/daemon/**/daemon/**/*.{ts,tsx} : Implement daemon state structure containing status ('running' | 'shutting-down' | 'offline'), pid, httpPort, startedAt, shutdownRequestedAt, and shutdownSource as dynamic daemon runtime state

Applied to files:

  • src/api/types.ts
📚 Learning: 2026-01-12T11:38:16.567Z
Learnt from: CR
Repo: leeroybrun/happy-cli PR: 0
File: src/daemon/CLAUDE.md:0-0
Timestamp: 2026-01-12T11:38:16.567Z
Learning: Applies to src/daemon/**/src/index.ts : In CLI startup phase, check if machine ID exists in settings; if not, create ID locally only without creating machine on server (daemon's responsibility on startup)

Applied to files:

  • src/api/types.ts
📚 Learning: 2026-01-12T11:38:16.567Z
Learnt from: CR
Repo: leeroybrun/happy-cli PR: 0
File: src/daemon/CLAUDE.md:0-0
Timestamp: 2026-01-12T11:38:16.567Z
Learning: Applies to src/daemon/**/daemon/**/*.{ts,tsx} : Emit 'machine-update-state' WebSocket events whenever daemon status changes, including expectedVersion for optimistic locking and handle version-mismatch responses by syncing to current server version

Applied to files:

  • src/api/types.ts
  • src/api/apiSession.ts
📚 Learning: 2026-01-12T11:37:46.296Z
Learnt from: CR
Repo: leeroybrun/happy-cli PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-12T11:37:46.296Z
Learning: Applies to **/*.test.{ts,tsx} : Write unit tests using Vitest with no mocking - tests should make real API calls

Applied to files:

  • src/api/api.test.ts
📚 Learning: 2026-01-12T11:37:46.296Z
Learnt from: CR
Repo: leeroybrun/happy-cli PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-12T11:37:46.296Z
Learning: Applies to src/api/**/*.{ts,tsx} : Use Socket.IO for real-time messaging in API sessions

Applied to files:

  • src/api/apiSession.ts
📚 Learning: 2026-01-12T11:37:46.296Z
Learnt from: CR
Repo: leeroybrun/happy-cli PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-12T11:37:46.296Z
Learning: Applies to src/api/**/*.{ts,tsx} : Implement session isolation through unique session IDs

Applied to files:

  • src/api/apiSession.ts
📚 Learning: 2026-01-12T11:37:46.296Z
Learnt from: CR
Repo: leeroybrun/happy-cli PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-12T11:37:46.296Z
Learning: Applies to src/api/**/*.{ts,tsx} : Implement optimistic concurrency control for state updates in sessions

Applied to files:

  • src/api/apiSession.ts
📚 Learning: 2026-01-12T11:38:16.567Z
Learnt from: CR
Repo: leeroybrun/happy-cli PR: 0
File: src/daemon/CLAUDE.md:0-0
Timestamp: 2026-01-12T11:38:16.567Z
Learning: Applies to src/daemon/**/doctor/**/*.{ts,tsx} : In 'happy doctor' command, use 'ps aux | grep' to find all Happy processes and categorize by command args: daemon, daemon-spawned, user-session, doctor; match production patterns 'happy.mjs', 'happy-coder', 'dist/index.mjs' and development pattern 'tsx.*src/index.ts'

Applied to files:

  • bin/happy-dev.mjs
📚 Learning: 2026-01-12T11:38:16.567Z
Learnt from: CR
Repo: leeroybrun/happy-cli PR: 0
File: src/daemon/CLAUDE.md:0-0
Timestamp: 2026-01-12T11:38:16.567Z
Learning: Applies to src/daemon/**/daemon/**/*.{ts,tsx} : When daemon receives RPC 'spawn-happy-session' from backend, create directory if needed, spawn detached Happy process with flags '--happy-starting-mode remote --started-by daemon', add to pidToTrackedSession map, and set up 10-second awaiter for session webhook

Applied to files:

  • bin/happy-dev.mjs
🧬 Code graph analysis (3)
src/claude/runClaude.ts (1)
src/claude/claudeLocal.ts (1)
  • claudeLocal (17-328)
scripts/env-wrapper.cjs (1)
scripts/claude_version_utils.cjs (1)
  • command (60-60)
src/api/apiSession.ts (2)
src/ui/logger.ts (1)
  • logger (235-235)
scripts/session_hook_forwarder.cjs (1)
  • body (26-26)
🪛 GitHub Actions: Smoke Test
src/claude/runClaude.ts

[error] 149-149: Cannot find name 'abortOnSignal'. Did you mean 'AbortSignal'? (during 'yarn build' / tsc --noEmit)

🪛 GitHub Actions: TypeScript typecheck
src/claude/runClaude.ts

[error] 149-149: Cannot find name 'abortOnSignal'. Did you mean 'AbortSignal'? (Command: yarn typecheck; tsc --noEmit)

🪛 GitHub Check: smoke-test-linux (20)
src/claude/runClaude.ts

[failure] 150-150:
Cannot find name 'abortOnSignal'. Did you mean 'AbortSignal'?


[failure] 149-149:
Cannot find name 'abortOnSignal'. Did you mean 'AbortSignal'?

🪛 GitHub Check: smoke-test-windows (20)
src/claude/runClaude.ts

[failure] 150-150:
Cannot find name 'abortOnSignal'. Did you mean 'AbortSignal'?


[failure] 149-149:
Cannot find name 'abortOnSignal'. Did you mean 'AbortSignal'?

🪛 GitHub Check: typecheck
src/claude/runClaude.ts

[failure] 150-150:
Cannot find name 'abortOnSignal'. Did you mean 'AbortSignal'?


[failure] 149-149:
Cannot find name 'abortOnSignal'. Did you mean 'AbortSignal'?

🔇 Additional comments (19)
scripts/claude_version_utils.cjs (1)

134-136: LGTM! Stricter Windows npm detection prevents false positives.

The added path segment requirements (@anthropic-ai, claude-code, and exclusion of .claude-code-) correctly narrow the npm source classification to only match actual Claude Code installations, avoiding false positive detection of other npm packages like @babel/core.

scripts/ripgrep_launcher.cjs (1)

41-54: LGTM! Correct stdio configuration for capturing command output.

The change to stdio: ['ignore', 'pipe', 'ignore'] properly configures execFileSync to capture stdout while ignoring stdin and stderr. Combined with encoding: 'utf8', this ensures the which/where output is returned as a string for parsing.

src/claude/claudeLocal.ts (1)

37-37: LGTM!

Good consistency fix - the -r short flag is now recognized in hasResumeFlag, aligning with the extractFlag call at line 90 which already handles both --resume and -r.

scripts/test-continue-fix.sh (1)

17-17: LGTM!

Good defensive addition. The guard prevents a confusing ENOENT error from readdirSync by failing early with a clear, actionable error message when the project directory doesn't exist.

Also applies to: 25-28

scripts/env-wrapper.cjs (1)

54-61: LGTM!

Good guard to enforce the required command argument. This prevents proceeding with an undefined command, which would otherwise cause confusing failures downstream when spawning the subprocess.

bin/happy-dev.mjs (1)

12-14: LGTM!

Moving environment variable initialization to the top-level ensures HAPPY_HOME_DIR and HAPPY_VARIANT are consistently set before the re-exec vs direct-import branching. This eliminates duplication and guarantees the subprocess inherits the correct environment via env: process.env on line 27.

src/utils/tmux.test.ts (4)

288-312: LGTM!

The test helper correctly manages TMUX_PANE lifecycle: captures original state, conditionally sets or deletes the variable, and properly restores it in the finally block. This ensures tests don't leak environment state.


322-332: LGTM!

Expected fields correctly updated to socket_path and server_pid to match the new TmuxEnvironment interface. The tests validate the corrected parsing logic that extracts these values from the TMUX environment variable format.

Also applies to: 350-361


334-348: LGTM!

Good coverage for malformed TMUX values. Returning null for non-numeric server_pid is the correct behavior since the second component must be a valid PID.


412-422: LGTM!

This test validates the critical fix: TMUX_PANE takes precedence over the third TMUX component when present. The expected pane value '%0' (from TMUX_PANE) correctly differs from '0' (the TMUX third component).

src/utils/tmux.ts (5)

79-86: LGTM!

The updated TmuxEnvironment interface correctly reflects the TMUX environment variable format: socket_path,server_pid,pane_id. Using number for server_pid is appropriate since it's a process ID.


348-350: LGTM!

Correct removal of new-session from auto-targeting. The comment clarifies that new-session -t targets a session group, not a session/window target—different semantics that would cause incorrect behavior if auto-targeted.


379-397: LGTM!

The rewritten parsing logic correctly:

  1. Requires exactly 3+ comma-separated components
  2. Validates server_pid is numeric via regex before parsing
  3. Prefers TMUX_PANE when set, falling back to the third TMUX component
  4. Returns null on any validation failure instead of partial/incorrect data

This aligns with the PR objective of correcting TMUX environment parsing.


444-450: LGTM!

Good fix to prevent double -t injection. The hasExplicitTarget check ensures auto-targeting is skipped when the command already specifies a target, addressing the reported issue of invalid target injection.


884-885: LGTM!

The killWindow fix correctly delegates to executeTmuxCommand with separate session and window parameters. This ensures the proper -t session:window syntax is built, fixing the kill-window invocation issue mentioned in the PR objectives.

src/api/api.test.ts (1)

221-238: LGTM!

The spy initialization is now consistent with the pattern used in other tests in this file. The test correctly validates that non-connection errors are re-thrown with proper wrapping and that the offline warning is not shown.

src/api/apiSession.ts (2)

30-36: Good centralization of the socket connectivity guard.

The helper provides consistent logging and a clean abstraction for connection checks. Based on learnings, this aligns with the requirement to use Socket.IO for real-time messaging in API sessions.


203-212: LGTM - Guard is correctly placed before encryption.

The canSend check at Line 206 is placed after logging but before the encryption operation at Line 208, avoiding unnecessary encryption work when the socket is disconnected.

src/api/types.ts (1)

217-225: Type alignment with runtime behavior is correct.

Making metadata nullable aligns with daemonState (already nullable on Line 223) and reflects actual runtime scenarios where metadata may not yet be populated. Verification confirms consumers in apiMachine.ts properly handle the null case through handler functions. Zod schemas are implemented as required.

Comment thread src/claude/runClaude.ts Resolved
- Fix abortOnSignal scoping in offline mode so finally cleanup compiles

- Avoid unnecessary encryption/UUID work when socket is disconnected

(Gemini/CodeRabbit feedback on PR #3)
@leeroybrun
leeroybrun merged commit 5852584 into main Jan 12, 2026
6 checks passed
leeroybrun added a commit that referenced this pull request Jan 12, 2026
- Remove unused env_session/env_window fields from TmuxSessionInfo

- Rename misleading claude_version_utils npm test

(CodeRabbit/Gemini nitpicks on PR #3)
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