Problem
When the Happy daemon spawns a new Claude session (via mobile app or happy daemon), it does not read ~/.claude/settings.json to determine the user's configured permission mode. This causes every Bash/tool call to prompt for approval, even when the user has set "defaultMode": "bypassPermissions" in their Claude settings.
Additionally, the --settings flag passed to the Agent SDK binary points to a minimal Happy-internal hook file (~/.happy/tmp/hooks/session-hook-*.json) that only contains a SessionStart forwarder. This means:
- User-defined hooks (PreToolUse, PostToolUse, Stop, SessionEnd, etc.) from
~/.claude/settings.json are not loaded
- Plugins configured in settings.json are not passed through
- Permission allowlists from
settings.local.json are ignored
- Environment variables set in the
env block of settings.json are not injected
Root Cause
In dist/index-q9G4ktSK.mjs (and CJS equivalent), the daemon spawn code builds args without reading Claude's permission settings:
// Line ~5371 (new session spawn)
const args = [
agentCommand,
"--happy-starting-mode", "remote",
"--started-by", "daemon"
];
// No --permission-mode flag added
// Line ~5296 (tmux spawn)
const fullCommand = `node ... ${agent} --happy-starting-mode remote --started-by daemon`;
// No --permission-mode flag added
Meanwhile, readClaudeSettings() already exists and correctly reads ~/.claude/settings.json (used for includeCoAuthoredBy), but its output is never consulted for permission mode on daemon spawns. The options.permissionMode passthrough only happens for resume operations (line ~5514), not new spawns.
Expected Behavior
- Daemon-spawned sessions should inherit
permissions.defaultMode from ~/.claude/settings.json
- Ideally, the full settings merge chain (
settings.json + settings.local.json + project-level settings) should be respected, same as a direct claude CLI launch
- At minimum: if
defaultMode: "bypassPermissions" is set, daemon spawns should pass --permission-mode bypassPermissions
Workaround
Users can work around the permission issue by always launching with happy --yolo, but this doesn't help daemon-spawned sessions (the primary use case for the mobile app).
A local patch to readClaudeSettings() at the spawn sites works but is overwritten on npm update.
Environment
- Happy CLI: 1.1.8
- OS: Ubuntu 24.04 (Linux 6.17)
- Claude Agent SDK: bundled in happy node_modules
- Claude Code: 2.1.176
Problem
When the Happy daemon spawns a new Claude session (via mobile app or
happy daemon), it does not read~/.claude/settings.jsonto determine the user's configured permission mode. This causes every Bash/tool call to prompt for approval, even when the user has set"defaultMode": "bypassPermissions"in their Claude settings.Additionally, the
--settingsflag passed to the Agent SDK binary points to a minimal Happy-internal hook file (~/.happy/tmp/hooks/session-hook-*.json) that only contains a SessionStart forwarder. This means:~/.claude/settings.jsonare not loadedsettings.local.jsonare ignoredenvblock of settings.json are not injectedRoot Cause
In
dist/index-q9G4ktSK.mjs(and CJS equivalent), the daemon spawn code builds args without reading Claude's permission settings:Meanwhile,
readClaudeSettings()already exists and correctly reads~/.claude/settings.json(used forincludeCoAuthoredBy), but its output is never consulted for permission mode on daemon spawns. Theoptions.permissionModepassthrough only happens for resume operations (line ~5514), not new spawns.Expected Behavior
permissions.defaultModefrom~/.claude/settings.jsonsettings.json+settings.local.json+ project-level settings) should be respected, same as a directclaudeCLI launchdefaultMode: "bypassPermissions"is set, daemon spawns should pass--permission-mode bypassPermissionsWorkaround
Users can work around the permission issue by always launching with
happy --yolo, but this doesn't help daemon-spawned sessions (the primary use case for the mobile app).A local patch to
readClaudeSettings()at the spawn sites works but is overwritten onnpm update.Environment