From afd0bd89e69c1a8fe9ce0c36e9dcf0d4dc908275 Mon Sep 17 00:00:00 2001 From: MToan <58007568+taminhtoan2601@users.noreply.github.com> Date: Tue, 7 Jul 2026 06:54:23 +0200 Subject: [PATCH] fix(claude-cli): drop TodoRead/NotebookRead from always-blocked deny rules (#1376) Current Claude CLI releases (>= 2.x) no longer register TodoRead and NotebookRead. Passing them via --disallowedTools makes the CLI print 'Permission deny rule "..." matches no known tool' on every invocation, including background episodic summarization, burying real errors. Keep TodoWrite/NotebookEdit blocked (still registered, no GoClaw equivalent). Add a regression test guarding both directions. Fixes #1374 --- internal/providers/claude_cli_session.go | 8 +++++- internal/providers/claude_cli_session_test.go | 26 +++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/internal/providers/claude_cli_session.go b/internal/providers/claude_cli_session.go index 8759d82e..7b6f7e2c 100644 --- a/internal/providers/claude_cli_session.go +++ b/internal/providers/claude_cli_session.go @@ -31,8 +31,14 @@ var cliNativeToolGoclawEquivalent = map[string]string{ // cliNativeToolsAlwaysBlocked lists Claude CLI native tools with no GoClaw // policy equivalent. They are always disallowed so agent tool policy cannot // be bypassed through them. +// +// TodoRead and NotebookRead were removed here: current Claude CLI releases +// (>= 2.x) no longer register them, and a deny rule naming an unknown tool +// makes the CLI print "Permission deny rule ... matches no known tool" on +// every invocation. Their write-side counterparts TodoWrite/NotebookEdit +// still exist in the CLI and remain blocked. var cliNativeToolsAlwaysBlocked = []string{ - "Glob", "Grep", "TodoRead", "TodoWrite", "NotebookRead", "NotebookEdit", + "Glob", "Grep", "TodoWrite", "NotebookEdit", } // disallowedCLITools computes the --disallowedTools value for the Claude CLI diff --git a/internal/providers/claude_cli_session_test.go b/internal/providers/claude_cli_session_test.go index e4af199b..2c5361a9 100644 --- a/internal/providers/claude_cli_session_test.go +++ b/internal/providers/claude_cli_session_test.go @@ -123,3 +123,29 @@ func TestBuildStreamJSONInput_NoText(t *testing.T) { t.Errorf("content blocks = %d, want 1 (image only)", len(msg.Message.Content)) } } + +// TestDisallowedCLITools_NoStaleToolNames guards against deny rules that name +// tools the current Claude CLI no longer registers. A stale name makes the CLI +// print `Permission deny rule "" matches no known tool` on every +// invocation (including background episodic summarization), which buries real +// errors. TodoRead/NotebookRead were removed from the CLI in 2.x; their +// write-side counterparts must stay blocked. +func TestDisallowedCLITools_NoStaleToolNames(t *testing.T) { + blocked := disallowedCLITools(nil) // nil = fail closed: everything blocked + + got := make(map[string]bool, len(blocked)) + for _, name := range blocked { + got[name] = true + } + + for _, stale := range []string{"TodoRead", "NotebookRead"} { + if got[stale] { + t.Errorf("disallowedCLITools includes %q, which current Claude CLI no longer registers (causes 'matches no known tool' warnings)", stale) + } + } + for _, required := range []string{"TodoWrite", "NotebookEdit", "Glob", "Grep"} { + if !got[required] { + t.Errorf("disallowedCLITools missing %q — native tool without GoClaw equivalent must stay blocked", required) + } + } +}