This repository was archived by the owner on Feb 14, 2026. It is now read-only.
fix: enable terminal title control via change_title MCP tool#161
Open
michelhelsdingen wants to merge 19 commits intoslopus:mainfrom
Open
fix: enable terminal title control via change_title MCP tool#161michelhelsdingen wants to merge 19 commits intoslopus:mainfrom
michelhelsdingen wants to merge 19 commits intoslopus:mainfrom
Conversation
…us#11) ## THE FIX Forward SIGTERM, SIGINT, SIGHUP to child Claude CLI process. This ensures child processes are killed when parent is terminated. ## ROOT CAUSE When switching between local/remote modes, the parent process was killed but child Claude CLI processes remained alive ("orphaned"). These orphaned processes continued to hold stdin, causing: - Duplicate/garbled characters when typing - "Competing processes" fighting for terminal input ## SOLUTION (from GitHub slopus/happy#430) ```javascript const forwardSignal = (signal) => { if (child.pid && !child.killed) { child.kill(signal); } }; process.on('SIGTERM', () => forwardSignal('SIGTERM')); process.on('SIGINT', () => forwardSignal('SIGINT')); process.on('SIGHUP', () => forwardSignal('SIGHUP')); ``` ## FILES CHANGED - scripts/claude_version_utils.cjs - binary launcher signal forwarding - src/claude/claudeLocal.ts - TypeScript launcher signal forwarding ## TESTED ✅ Fresh local mode - typing works ✅ Switch to remote mode - typing works ✅ Switch back to local mode - typing works (was broken) ✅ Multiple mode switches - stable ## IMPORTANT This is the ONLY fix needed. No stdin cleanup, no removeAllListeners(), no setRawMode changes required. Signal forwarding alone solves it. Closes slopus#11 References: slopus/happy#430, PR slopus#127 Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>
Re-apply terminal title feature (originally df7262c) that was lost when upstream main diverged. Sets iTerm2/Terminal.app window title via OSC escape sequences when change_title is invoked. - Add CLAUDE_CODE_DISABLE_TERMINAL_TITLE=1 to launcher to prevent Claude Code from overwriting the terminal title - Add setTerminalTitle() using OSC 0 escape sequence for direct window title control - change_title now sets both terminal title and Happy app title Tested: title updates work in iTerm2, remote→local mode switch still functions correctly (no stdin regression). Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>
- Add getUploadDir RPC handler for mobile image uploads - Support writeFile to session-scoped temp upload directory - Enhance path security with symlink resolution and multi-dir support - Pass sessionId to registerCommonHandlers for upload dir scoping - Remove CLAUDECODE env var before spawning Claude subprocess - Improve error logging in claudeRemoteLauncher (Error objects → message+stack) Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>
Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>
Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>
Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>
…ndlers Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>
Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>
Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>
setTerminalTitle() now checks process.stdout.isTTY before writing OSC escape sequences. In remote/daemon sessions stdout is a pipe (not TTY), causing EPIPE which triggered uncaughtException and killed the session. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Wire up real-time Live Activity updates via APNs HTTP/2 pushes. The CLI now receives the ActivityKit push token from the mobile app via RPC and sends status updates (running/waiting/permission) with coordinator task progress to the iOS widget. Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>
Sessions spawned with detached:true survive daemon restarts but were previously lost from the in-memory tracking Map. Now the daemon writes tracked sessions to ~/.happy/tracked-sessions.json on every Map mutation and recovers still-alive sessions on startup. Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>
- Fix socket message loss in sendClaudeSessionMessage with queue/retry - Fix permission mode leak between session switches - Add markCurrentFailed() to Coordinator for proper failure handling - Remove APNs Live Activity module (replaced by Expo push) - Add parseOptions() for extracting actionable push notification buttons - Enhanced auto-pilot completion notification with failure reporting Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>
…and-p0-fixes feat: session persistence, coordinator, and P0 fixes
Adds automatic session recap posting to the /v1/feed endpoint when Claude finishes a turn. The recap includes title, duration, cost, model, and turn count. Uses repeatKey for idempotent updates per session. - Add postFeedItem() to ApiClient - Track session title from mcp__happy__change_title calls - Extract model from result.modelUsage - Fire-and-forget in onResult (before killSession can terminate process) Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>
feat: session recaps in inbox feed
Three fixes for the tmux-based session spawning and local mode takeover: 1. tmux spawn: Fix flag ordering and env var escaping in spawnInTmux(). tmux stops parsing options at the first positional argument, so -P, -F, -t flags must come before the shell-command. Also remove shell escaping from -e values since Node.js spawn() bypasses the shell. 2. local mode crash: Remove CLAUDECODE and CLAUDE_CODE_ENTRYPOINT env vars before spawning the local Claude CLI. The SDK sets CLAUDE_CODE_ENTRYPOINT='sdk-ts' in process.env during remote mode, which persists after remote mode ends. When passed to the local CLI, Claude detects nested session and exits immediately (~263ms). 3. diagnostics: Add exit code/signal/runtime logging to claudeLocal, detect suspiciously fast exits (<2s) as failures, and implement retry logic (max 3 attempts) with error reporting to mobile app. Also adds token usage fields to session recap events. Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>
The isReplayPhase logic (added in 147553e) assumed the Claude Code SDK replays old messages when resuming a session. It does not — only new turn messages are streamed. This caused all messages to be suppressed and the first result to be skipped during local→remote handoff, leaving the Mac terminal stuck on "Starting new Claude session...". Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>
Track files read, files modified, bash commands, and tools used during remote sessions. This data is included in the session-recap push notification so the phone app can show a richer summary of what happened. Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
setTerminalTitle()function using OSC escape sequences to set iTerm2/Terminal.app window title whenchange_titleMCP tool is invokedCLAUDE_CODE_DISABLE_TERMINAL_TITLE=1env var to launcher to prevent Claude Code from overwriting the terminal titlechange_titlenow updates both the terminal window title (for desktop) and the Happy server (for mobile app)Context
The
change_titleMCP tool currently only sends the title to the Happy server for the mobile app. Desktop users (iTerm2, Terminal.app) don't see the title reflected in their terminal tabs/windows. This makes it hard to distinguish between multiple Happy sessions.This fix uses standard OSC 0 escape sequences (
\x1b]0;title\x07) which work with iTerm2, Terminal.app, and most modern terminal emulators.Test plan
change_titleis called🤖 Generated with Claude Code
via Happy