Repository navigation
Guard
This page tells how the guard slice of the core plugin claudetto denies a Bash command that only prints a file.
A user reads it to learn why Claude gets a deny and what to do.
A contributor reads it before an edit of slices/guard/.
The Read tool sends a short stub when Claude reads the same range of an unchanged file again.
A Bash print of a file gets no stub, so the text enters the context again.
The guard slice denies such a print and names the Read call that does the same work.
The slice has one control, print deny.
Remove it when Claude Code gives the short stub for a Bash print too.
The slice adds one tool.call hook on the tool Bash.
The hook runs in the phase check, after the sembr rewrite of the phase rewrite.
So the hook checks the command that would run.
- The hook reads the command and splits it into parts.
- If each part only prints a file, the hook finds the file and the line range.
- The hook denies the call and names the
Readcall withfile_path,offset, andlimit.
If one part does other work, the call runs.
Examples of such a part are ls, a build, and a print that goes into a pipe.
The hook reads these forms as a print of one file:
| Command | Range in the deny |
|---|---|
cat F |
No range |
sed -n A,Bp F or sed -n Np F
|
offset A, limit B-A+1 |
head F, head -n N F, head -N F
|
offset 1, limit N (10 by default) |
tail -n +N F |
offset N |
tail F, tail -n N F, tail -N F
|
The last N lines (10 by default) |
A compound command is a print only when each part is a print.
The hook splits on ;, &&, ||, |, &, and new lines outside quotes.
A plain cd DIR is not work, and it sets the folder of the later parts.
A relative path uses the folder of the session or of the last cd.
The hook lets the call run in these cases:
- The command has a heredoc, an open quote, a substitution, a variable, a glob, or a redirect.
- A part prints into a pipe, or
cdhas an unknown folder. - The file does not exist, or is not a regular file.
- The file is a generated file.
These are files in
node_modules,.git,dist,build,target,coverage,.next,out, or__pycache__. Files that end in.log,.map,.lock,.min.js,.min.css,.tsbuildinfo,package-lock.json, orbun.lockandbun.lockbcount too. - The file is empty and the command uses
tailwith a count.
The hook counts the lines of a file with the engine file reader.
A file over READ_MAX_BYTES (4 MiB, in slices/guard/limits.ts) gets no line count.
The deny then names the file with no range.
The same holds for cat F.
Claude Code skips a hook that throws or times out, so the call would run.
The slice adds an error handler for this case.
If the hook fails and the command prints a file, the handler denies the call and gives the kind of the failure.
The reason tells Claude to use Read with an offset and a limit.
A command that does not print a file runs.
The guard slice denies a Bash command that only prints a file.
Claude gets a Read call to use instead.
This saves tokens, because Read does not send the same text twice.
claudetto
- Start
- Slices
- Add-Ons
- Reference
- Rationale
- Evidence
- Development
Claude Code