Chisel Agent Skill
You have access to a Chisel MCP server. Chisel exposes precision filesystem tools designed to minimise token cost and maximise safety. The cardinal rule: never read a full file when you only need part of it, and never write a full file when you only need to change part of it.
⚠️
shell_exechas no shell interpreter. Pipe (|),&&,||,$(), and all shell metacharacters are passed as literals to the process — they are never interpreted. Eachshell_execcall runs exactly one command. Compose multi-step logic across separate calls.
Tool Selection
Need to find something in a file or directory?
└─ shell_exec (grep / find / sed / awk / stat / ls)
Need to edit an existing file?
├─ Small targeted change (< whole file) → patch_apply
│ └─ patch_apply fails twice on a small file? → write_file (see below)
└─ Overwrite entirely / file is tiny → write_file
Need to add content to the end of a file?
└─ append
Need to create a directory?
└─ create_directory
Need to rename or relocate a file?
└─ move_file
Reading — Never Fetch What You Don't Need
Always start with the minimum slice required. Only escalate if the first call does not give enough context.
| Goal | Call |
|---|---|
| Find which lines contain a symbol | shell_exec grep ["-n", "symbol", "/root/file"] |
| Multi-file symbol search | shell_exec grep ["-rn", "symbol", "/root"] |
| List all headers in a Markdown file | shell_exec grep ["-n", "^#", "/root/file.md"] |
| Extract one section by header | shell_exec sed ["-n", "/^## Section/,/^## /p", "/root/file.md"] |
| Read lines 40–60 of a large file | shell_exec sed ["-n", "40,60p", "/root/file"] |
| Show every line with its number (small files) | shell_exec cat ["-n", "/root/file"] |
| Count occurrences | shell_exec grep ["-c", "pattern", "/root/file"] |
| Get file size / line count | shell_exec wc ["-l", "/root/file"] |
| List directory contents | shell_exec ls ["-la", "/root/dir"] |
| Find files by name | shell_exec find ["/root", "-name", "*.rs"] |
| Inspect file type / metadata | shell_exec stat ["/root/file"] |
| Tail a log | shell_exec tail ["-n", "50", "/root/app.log"] |
Avoid calling cat on a file longer than ~50 lines. Use sed -n 'M,Np' to window in, or cat -n on short files to get line numbers before writing a patch.
Editing — Prefer Diffs Over Full Rewrites
patch_apply — primary edit tool
Send only the changed lines. The file is written atomically; a hunk mismatch returns PatchFailed and leaves the original untouched.
--- a/src/main.rs
+++ b/src/main.rs
@@ -12,7 +12,7 @@
fn main() {
- let port = 3000;
+ let port = 8080;
start_server(port);
}
Rules for a valid patch:
- Context lines (no prefix) must match the file exactly, including whitespace.
@@ -L,N +L,N @@line numbers must be exact for the current file state — both the start line and the count. A wrong line number in the header producesPatchFailedeven if the content lines are correct. Always rungrep -norcat -nfirst to confirm the real line numbers before writing the hunk header.- To create a new file use
--- /dev/nullas the source header; the file must not exist. - You can include multiple
@@hunks in a single call — all are applied atomically.
When PatchFailed is returned:
- Re-read the exact lines with
grep -norsed -n 'L-3,L+3p'. - Regenerate the patch from the actual content — both context lines and hunk header.
- If
PatchFailedhappens twice on a file under ~100 lines: stop debugging the hunk. Usecat -nto read the whole file, correct it in full, and usewrite_fileinstead.
write_file — full overwrite
Use when:
- Creating a new file with substantial initial content.
- The change touches so much of the file that a diff would be larger than the file itself.
- The file is short (< ~30 lines) and a full rewrite is cleaner.
patch_applyhas failed twice on a short file —write_fileis the safe recovery path.
append
Use for log entries, appending lines to a config, or adding to an incrementally-built file. The file must already exist.
In-place Transformation with shell_exec sed -i
For bulk replacements (rename a symbol, fix a repeated pattern), sed mutates the file directly — no diff needed.
⚠️
sed -ihas different signatures on macOS (BSD sed) vs Linux (GNU sed). Use exactly the forms below — do not improvise alternatives.
Platform Argument array Linux (GNU sed) ["-i", "s/Old/New/g", "/abs/path/file"]macOS (BSD sed) ["-i", "", "s/Old/New/g", "/abs/path/file"]On macOS the empty string
""is the backup-suffix argument (meaning: no backup file). Chisel passes it through to BSD sed correctly as a separate argument — do not omit it, do not replace it with-i.bak, and do not combine it with-iinto a single arg like"-i''". Any deviation produces a cryptic BSD sed parse error (command a expects \…) and wastes turns.If you are unsure which platform you're on, use
shell_exec stat ["/proc/version"](Linux) orshell_exec uname ["-s"]to check. Alternatively, preferpatch_applyorwrite_filefor single-file edits — they are platform-independent.
# Linux
shell_exec sed ["-i", "s/OldName/NewName/g", "/root/src/lib.rs"]
# macOS — the "" is mandatory and is passed correctly; do not second-guess this
shell_exec sed ["-i", "", "s/OldName/NewName/g", "/root/src/lib.rs"]
A successful in-place edit returns exit_code: 0 with empty stdout. Non-zero exit or any stderr output means the expression or path is wrong — fix those, do not change the -i form.
Workflow Patterns
Locate → Read slice → Patch
The standard low-token edit loop:
grep -n "target_symbol" /root/file— find the line number.sed -n "L-5,L+10p" /root/file— read a tight window around it.- Verify the
@@ -L,N +L,N @@header matches exactly. patch_apply— send the diff against that exact content.
Short files only: use cat -n /root/file in step 1 to see every line numbered at once — faster than a grep/sed pair when the file is < 50 lines.
Explore unfamiliar codebase
ls -la /root— top-level layout.find /root -name "*.rs" -not -path "*/target/*"— enumerate source files.grep -rn "struct MyStruct" /root/src— locate the definition.sed -n "L,L+30p" /root/src/file.rs— read the struct body.
Relative paths are auto-anchored to root.
.means the root itself,subdir/filemeansroot/subdir/file. Absolute paths also work. Traversal (../escape) is always blocked.
Create a new file with boilerplate
Use write_file. Once it exists, all subsequent edits go through patch_apply.
Rename a symbol across many files
Preferred — one call via find -exec … {} +:
find -exec cmd {} + batches all matched paths into a single command invocation. No shell, no loop, one shell_exec call regardless of how many files match.
# macOS (BSD sed)
shell_exec find ["/root/src", "-name", "*.rs", "-exec", "sed", "-i", "", "s/OldName/NewName/g", "{}", "+"]
# Linux (GNU sed)
shell_exec find ["/root/src", "-name", "*.rs", "-exec", "sed", "-i", "s/OldName/NewName/g", "{}", "+"]
The {} placeholder and + terminator are passed literally to find — they are not shell metacharacters and require no escaping.
Fallback — one call per file (only if find -exec is unavailable or the pattern needs per-file logic):
grep -rl "OldName" /root/src— get the file list.- For each file:
- Linux:
sed ["-i", "s/OldName/NewName/g", "/root/src/file.rs"] - macOS:
sed ["-i", "", "s/OldName/NewName/g", "/root/src/file.rs"]
- Linux:
Scaffold a directory tree
create_directory /root/src/handlers
write_file /root/src/handlers/mod.rs "..."
write_file /root/src/handlers/auth.rs "..."
Error Handling
| Error | Meaning | Fix |
|---|---|---|
PatchFailed | Context lines or hunk header don't match current file | Re-read with grep -n / cat -n, fix both header line numbers and context, regenerate patch. If it fails twice on a short file, use write_file. |
OutsideRoot | Path escapes configured root | Use only paths under the root the server was started with |
NotFound | File doesn't exist | Use write_file to create, or check path with find |
ReadOnly | Server in read-only mode | Writes are disabled; only reads and shell_exec work |
CommandNotAllowed | Command not in whitelist | Use only: grep sed awk find cat head tail wc sort uniq cut tr diff file stat ls du |
What Not To Do
| Anti-pattern | Why | Do instead |
|---|---|---|
cat /root/large_file.rs | Returns thousands of tokens you mostly won't use | grep -n to locate, sed -n to slice; cat -n only on files < 50 lines |
write_file for a 3-line change in a 500-line file | Uploads entire file, risks hallucination corrupting unrelated lines | patch_apply with a targeted hunk |
Shell pipelines in a single shell_exec (cmd1 | cmd2) | No shell interpreter — |, &&, $() are literals, not operators | Make separate shell_exec calls; compose in your logic |
find / -name "*.env" | Path validated against root; will be blocked or return nothing useful | Always scope find to /root/... |
| Sending a patch with approximate context lines or wrong line numbers | PatchFailed — both context and @@ -L,N @@ must be exact | Run grep -n or cat -n first, then diff against the real content |
Running one sed call per file for a bulk replacement | N tool calls when 1 will do | find ["/root", "-name", "*.ext", "-exec", "sed", "-i", "", "s/x/y/g", "{}", "+"] — batches all files into one sed invocation |
sed -i "s/x/y/" file on macOS without "" | BSD sed requires an explicit suffix arg — omitting it silently misparses arguments | macOS: ["-i", "", "s/x/y/g", "/abs/file"]; Linux: ["-i", "s/x/y/g", "/abs/file"] |
Using -i.bak or -i'' instead of ["-i", ""] | Chisel passes the empty string through correctly — no workaround needed | Stick to ["-i", ""] two-arg form; do not invent alternatives |
Retrying with a different -i form after a BSD sed parse error | The error is the expression or path, not the -i "" form | Fix the sed expression or path; keep ["-i", ""] as-is |
Continuing to retry patch_apply after two failures on a small file | Wastes turns; context drift compounds the problem | cat -n the file, use write_file to rewrite it correctly |