From c384b929ea9b0608bf6d42d0c16ba18efcfbe1b1 Mon Sep 17 00:00:00 2001 From: jjwjr94 Date: Tue, 22 Sep 2026 11:06:40 -0400 Subject: [PATCH] Don't emit terminalSequence outside Claude Code `terminalSequence` is a Claude Code-specific hook output field (added in 2.1.141). Other harnesses run Claude Code-format hooks but reject unknown keys in hook stdout, so emitting it there turns every hook into a visible error rather than a notification. Codex is the concrete case: it maps hooks/hooks.json onto its own event names and registers all five of this plugin's hooks, but its hook output schema has no terminalSequence and denies unknown fields. Hooks run with no controlling tty, so emit_terminal_sequence() falls through to the JSON branch and the user gets hook returned invalid post-tool-use JSON output hook returned invalid user prompt submit JSON output on every single tool call and prompt submission. That JSON fallback fires only when CLAUDE_CODE_VERSION is unset. Claude Code always sets CLAUDE_CODE_VERSION (and CLAUDECODE), so inside Claude Code the fallback is unreachable; outside it the field is guaranteed invalid. Gate it on a Claude Code env marker so the guess is only made where it can pay off. Co-Authored-By: Claude Opus 5 (1M context) --- .claude-plugin/marketplace.json | 2 +- plugins/warp/.claude-plugin/plugin.json | 2 +- plugins/warp/scripts/emit-terminal-sequence.sh | 15 +++++++++++++++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index ab081b9..11b534f 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -10,7 +10,7 @@ "name": "warp", "description": "Native Warp notifications when Claude completes tasks or needs input", "source": "./plugins/warp", - "version": "2.2.0", + "version": "2.2.1", "category": "productivity", "tags": ["notifications", "terminal", "warp"] }, diff --git a/plugins/warp/.claude-plugin/plugin.json b/plugins/warp/.claude-plugin/plugin.json index 1e29217..15e2ce4 100644 --- a/plugins/warp/.claude-plugin/plugin.json +++ b/plugins/warp/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "warp", "description": "Warp terminal integration for Claude Code - native notifications, and more to come", - "version": "2.2.0", + "version": "2.2.1", "author": { "name": "Warp", "url": "https://warp.dev" diff --git a/plugins/warp/scripts/emit-terminal-sequence.sh b/plugins/warp/scripts/emit-terminal-sequence.sh index b6a83b9..9a46bc8 100644 --- a/plugins/warp/scripts/emit-terminal-sequence.sh +++ b/plugins/warp/scripts/emit-terminal-sequence.sh @@ -45,6 +45,17 @@ _parse_cc_version() { echo "$1" | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1 } +# Returns 0 if we are running inside Claude Code. +# +# The `terminalSequence` output field is Claude Code-specific. Other harnesses +# run Claude Code-format hooks (Codex, for one, maps hooks/hooks.json onto its +# own event names) but reject unknown keys in hook stdout, so emitting it there +# turns every hook into a user-visible error -- e.g. Codex prints +# "hook returned invalid post-tool-use JSON output" on every tool call. +_is_claude_code() { + [ -n "${CLAUDECODE:-}" ] || [ -n "${CLAUDE_CODE_VERSION:-}" ] +} + # Returns 0 if the running Claude Code version supports terminalSequence. _supports_terminal_sequence() { local raw="${CLAUDE_CODE_VERSION:-}" @@ -82,5 +93,9 @@ emit_terminal_sequence() { if printf '%s' "$seq" > /dev/tty 2>/dev/null; then return 0 fi + + # No tty and no version. Only gamble on terminalSequence when we can still + # tell we are inside Claude Code; every other harness rejects the field. + _is_claude_code || return 0 jq -nc --arg seq "$seq" '{terminalSequence: $seq}' }