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
This commit is contained in:
MToan authored and GitHub committed 2026-07-07 11:54:23 +07:00
1 parent 09d8660f3b
commit afd0bd89e6
2 files changed
+33 -1

No files matched your search

+7 -1
View File
@@ -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
@@ -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 "<name>" 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)
}
}
}