Files
mesh-catalog/modules/claude-code/cmd/claude-code/hook_notice.go
T
jochen 02a8837348 Fix review round 1 of the wait list and its hooks (issues 1629, 1743, 1744)
A session id reaching the Stop and SessionStart hooks was only checked
for blank, so "../../x" or "a/b" could read or write outside the waits
directory; both hooks now hold every session id to waitSessionID before
building any path, and allow (saying why) rather than fail on a bad one.
claude_code_wait's list/watched/done/said held only add to that same
check, letting a malformed id through as an ordinary, always-empty key;
they all refuse it now.

The Stop hook's three-strikes cap could be bypassed forever if its
counter file could not be written on a refusal: it now allows instead,
saying so, so a wait can never hold a turn open indefinitely. Its advice
offered mesh_wait on a wait's event family unconditionally, ignoring
that the session's tool runner may not be granted it (ADR 0367 §2); it
now offers mesh_wait only "if this session's tool runner is granted it,
otherwise start a watcher", and drops the family entirely when a wait
has none.

Both hooks' fail-open and let-through notices were said only on stderr,
which Claude Code does not show the operator on an allowed (exit 0)
outcome; they now also print the same words as a "systemMessage" JSON
object on stdout, which Claude Code does show.
2026-10-21 18:23:42 +02:00

24 lines
850 B
Go

package main
// Shared by the plugin's hooks (novox/hq ADR 0489 §5): a notice on exit 0 — the stop hook failing open or
// letting a wait through, the session-start hook unable to use a session id — reaches the operator's screen,
// not only the log. Claude Code shows a hook's top-level "systemMessage" field to the person; stderr alone,
// on an allowed (exit 0) outcome, is never shown there.
import (
"encoding/json"
"fmt"
"io"
)
// writeSystemMessage prints message as the one JSON object Claude Code shows the operator, falling back to
// the plain words on the one error that cannot happen (a string always marshals).
func writeSystemMessage(w io.Writer, message string) {
raw, err := json.Marshal(map[string]string{"systemMessage": message})
if err != nil {
fmt.Fprintln(w, message)
return
}
fmt.Fprintln(w, string(raw))
}