diff --git a/CLI/cmux.swift b/CLI/cmux.swift index f6f0d760..3108185a 100644 --- a/CLI/cmux.swift +++ b/CLI/cmux.swift @@ -1305,7 +1305,16 @@ struct CMUXCLI { } case "clear-notifications": - let response = try sendV1Command("clear_notifications", client: client) + var socketCmd = "clear_notifications" + if let wsFlag = optionValue(commandArgs, name: "--workspace") { + let wsId = try resolveWorkspaceId(wsFlag, client: client) + socketCmd += " --tab=\(wsId)" + } else if windowId == nil, + let envWs = ProcessInfo.processInfo.environment["CMUX_WORKSPACE_ID"], + let wsId = try? resolveWorkspaceId(envWs, client: client) { + socketCmd += " --tab=\(wsId)" + } + let response = try sendV1Command(socketCmd, client: client) print(response) case "claude-hook": @@ -4441,7 +4450,7 @@ struct CMUXCLI { """ case "claude-hook": return """ - Usage: cmux claude-hook [flags] + Usage: cmux claude-hook [flags] Hook for Claude Code integration. Reads JSON from stdin. @@ -4452,6 +4461,7 @@ struct CMUXCLI { idle Alias for stop notification Forward a Claude notification notify Alias for notification + prompt-submit Clear notification and set Running on user prompt Flags: --workspace Target workspace (default: $CMUX_WORKSPACE_ID) @@ -5700,6 +5710,24 @@ struct CMUXCLI { print("OK") } + case "prompt-submit": + telemetry.breadcrumb("claude-hook.prompt-submit") + var workspaceId = fallbackWorkspaceId + if let sessionId = parsedInput.sessionId, + let mapped = try? sessionStore.lookup(sessionId: sessionId), + let mappedWorkspace = try? resolveWorkspaceIdForClaudeHook(mapped.workspaceId, client: client) { + workspaceId = mappedWorkspace + } + _ = try sendV1Command("clear_notifications --tab=\(workspaceId)", client: client) + try setClaudeStatus( + client: client, + workspaceId: workspaceId, + value: "Running", + icon: "bolt.fill", + color: "#4C8DFF" + ) + print("OK") + case "notification", "notify": telemetry.breadcrumb("claude-hook.notification") let summary = summarizeClaudeHookNotification(rawInput: rawInput) diff --git a/Resources/bin/claude b/Resources/bin/claude index d722b9c7..2205fe3c 100755 --- a/Resources/bin/claude +++ b/Resources/bin/claude @@ -50,7 +50,7 @@ done # Build hooks settings JSON. # Claude Code merges --settings additively with the user's own settings.json. -HOOKS_JSON='{"hooks":{"SessionStart":[{"matcher":"","hooks":[{"type":"command","command":"cmux claude-hook session-start","timeout":10}]}],"Stop":[{"matcher":"","hooks":[{"type":"command","command":"cmux claude-hook stop","timeout":10}]}],"Notification":[{"matcher":"","hooks":[{"type":"command","command":"cmux claude-hook notification","timeout":10}]}]}}' +HOOKS_JSON='{"hooks":{"SessionStart":[{"matcher":"","hooks":[{"type":"command","command":"cmux claude-hook session-start","timeout":10}]}],"Stop":[{"matcher":"","hooks":[{"type":"command","command":"cmux claude-hook stop","timeout":10}]}],"Notification":[{"matcher":"","hooks":[{"type":"command","command":"cmux claude-hook notification","timeout":10}]}],"UserPromptSubmit":[{"matcher":"","hooks":[{"type":"command","command":"cmux claude-hook prompt-submit","timeout":10}]}]}}' if [[ "$SKIP_SESSION_ID" == true ]]; then exec "$REAL_CLAUDE" --settings "$HOOKS_JSON" "$@" diff --git a/Sources/TerminalController.swift b/Sources/TerminalController.swift index aaf34f86..99ca19f6 100644 --- a/Sources/TerminalController.swift +++ b/Sources/TerminalController.swift @@ -878,7 +878,7 @@ class TerminalController { return listNotifications() case "clear_notifications": - return clearNotifications() + return clearNotifications(args) case "set_app_focus": return setAppFocusOverride(args) @@ -8790,7 +8790,7 @@ class TerminalController { notify_surface - Notify a specific surface notify_target - Notify by workspace+surface list_notifications - List all notifications - clear_notifications - Clear all notifications + clear_notifications [--tab=X] - Clear notifications (all or per-tab) set_app_focus - Override app focus state simulate_app_active - Trigger app active handler set_status [--icon=X] [--color=#hex] [--url=X] [--priority=N] [--format=plain|markdown] [--tab=X] - Set a status entry @@ -10124,9 +10124,30 @@ class TerminalController { return result.isEmpty ? "No notifications" : result } - private func clearNotifications() -> String { + private func clearNotifications(_ args: String) -> String { + let trimmed = args.trimmingCharacters(in: .whitespacesAndNewlines) + if trimmed.isEmpty { + DispatchQueue.main.sync { + TerminalNotificationStore.shared.clearAll() + } + return "OK" + } + let parsed = parseOptions(trimmed) + guard let tabOption = parsed.options["tab"], + !tabOption.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else { + return "ERROR: Usage: clear_notifications [--tab=X]" + } + var tabId: UUID? DispatchQueue.main.sync { - TerminalNotificationStore.shared.clearAll() + if let tab = resolveTabForReport(trimmed) { + tabId = tab.id + } + } + guard let tabId else { + return "ERROR: Tab not found" + } + DispatchQueue.main.sync { + TerminalNotificationStore.shared.clearNotifications(forTabId: tabId) } return "OK" }