Shoddy · Fettler

Fettler & Picker

A fettler kept a mill’s machines in working order. These two programs keep an AI assistant’s work in order: fettle for the files and pick for the SQL Server. Fewer calls, less context, and nothing left half done.

fettle: the files

Find, search, read and edit files, with answers a script or an assistant can hand straight back. Start it in a folder and it works. Guardrails and screenings are optional levels on top.

Start with the files →

pick: the SQL Server

SELECT and DESCRIBE, one checked statement at a time. There is no default database, no USE, and no system catalog. A parser refuses anything it cannot check. The same two optional levels sit on top.

Start with the SQL →

Why is an AI assistant in 2026 using command-line tools like it’s 1976?

Ask an assistant to rename a type across a project, then watch what it runs. It reaches for grep, sed and find, or for its own copies of them, one file at a time through a shell. Those tools are fifty years old, and they were written for a person at a terminal. They answer in text meant for eyes, they know nothing about the call that comes next, and each one costs the assistant a round trip and a block of its context, the text it can hold at one time.

Fettler gives the assistant the same jobs as verbs built for it. Each answer is shaped to be the next call’s argument, several things happen in one call, and a change lands whole or not at all.

The jobThe built-in tool, or the shellWhat went wrongfettle
Find filesGlob, or findResults swamped by bin and node_modules. A pattern that matches on one platform and not another. A list with no size or time, so a second call asks for them.find: a pattern that means the same on every platform, with each file’s size and time. Generated folders are skipped. It stops at 1,000 files and says so.
Search textGrep, or grepA pattern that runs away. A hit printed as text to be parsed again. A PDF or a spreadsheet skipped in silence, so the search reports nothing and looks complete. A brace pattern that matched nothing and said nothing.search: several patterns in one call, each under a time limit. Each hit is tree:path:line:column. It looks inside PDF, Word and Excel files, and a pattern that matched no file says so.
Read a fileRead, or catA whole file into context for three lines of it. A minified file as one enormous line. An encoding guessed wrong and written back wrong. An answer so large the client rejects it, which costs a turn and returns nothing.read: several paths in one call, a range or a tail, numbered lines, a hash of the content, and a size cap it states. The encoding is read from the file, never guessed.
Read a notebookNotebookEditA megabyte of image data pasted into the conversation, for a picture nobody can see.read returns the cells and measures an image output instead of pasting it. A notebook is JSON underneath, so edit changes it like any text file.
Create a fileWrite, or echo >A file overwritten without asking. A secret written into a checked-in file. A PowerShell here-string putting CRLF into an LF file.write, new, mkdir: replacing a file needs saying so. Refused if the text would add a password or key, or if the file says what Fettler or the assistant may do.
Change a fileEdit, or sedAn anchor that no longer matches exactly, so the edit is retried until it lands, sometimes in the wrong place. Quotes, backslashes and newlines escaped onto a command line. sed -i rewriting every line ending for a one-line change. A byte-order mark spliced into the middle of a file.edit: a batch of edits, anchored on text or on a line number, checked whole before a byte is written. Refused if the file changed since you read it, and a failed edit is named. Text can come from a file, so nothing is escaped. Line endings and encoding stay as they were.
Change many filesa sed -i loopA loop that fails at file 15 and leaves 14 changed. No way to see the change before it is made.replace: one substitution across a pattern of files, with a dry run first. All files change, or none do.
Move, copy, deletemv, cp, rmrm -r taking a folder nobody meant. A move that replaces its target without saying so. A path that climbs out of the project.move, copy, delete: a path that leaves the tree is refused. Replacing a target needs saying so. A recursive delete that would reach a protected folder is refused whole.
Unpack an archivetar, unzipA member named ../../etc/passwd landing outside the folder. A half-unpacked archive. A link member that escapes after every path check passed.extract: every member is checked first, and the archive is refused whole if any would land outside. Links are refused. The execute bit is carried.
Several stepsa shell pipelineQuoting that differs in every shell. A pipeline that fails halfway and leaves the state unknown. PowerShell 5.1 turning a native program’s error output into an error of its own, with $? false on exit 0.batch: one call, in order, stopping at the first step that fails and naming it. In JSON mode the whole answer is on standard output, failures included.
Run the buildBash, PowerShell, MonitorA command line composed from arguments, so a quote or a semicolon in a value changes what runs. A command that works on one machine and fails on the next.run: a task the project declares, by name, with no arguments and a timeout. Nothing is composed. There is no shell.
Read back a long outputBashOutput, TaskOutputBytes that reach the model without passing the tree boundary, the credential check or the screen.The client saves a long answer in its tool-results folder. read opens it there, read-only, through the same caps and checks as any other file.

The second column is the eleven built-in tools that guardrails deny: six file tools, three shells and two output readers. Each has a verb here that answers in a form the next call can use. And there is no current directory. Every path names its tree, so a call cannot land in the wrong folder.

One job, with the arithmetic

Renaming a C# type in this repository touches 200 places across 23 files. Here it is done with the built-in tools, and done with Fettler.

Built-in file toolsFettler
Callsone search, then a read and an edit for each file: 47search, replace --dry-run, replace: 3
Context spent23 whole filesthe matching lines
If it fails at file 1514 files renamed and 9 not, and nothing says which half changednothing is written until all 23 files are checked
Scanning this repository’s 15 MB of text takes a few hundred milliseconds. So a call costs about a thousand times more than the work, and the number of calls is what Fettler cuts.

What the tools do

Batching and pipeliningread takes several paths. search takes several patterns. batch takes a whole sequence. Every answer is the next call’s argument: search gives the place, read gives a hash, and edit takes the hash back.
Paging and large answersA read stops at 2,000 lines and 40,000 characters and says how much it left out. A range reads the middle of a file, and tail reads the end of a log. A notebook’s image output is measured, not pasted. One file cannot fill the assistant’s context by accident.
Documents, not only sourceNotebooks come back as cells. Excel comes back as rows, with the formulas, and with dates as dates. Word comes back as paragraphs under their headings, PDF as text per page, images as facts, and archive members without unpacking. A search looks inside all of them and cites the page.
ReliabilityA batch is all or nothing. A file keeps its own encoding, line endings and execute bit. Results are ordered before any limit, so a cut answer is the same cut twice. A refusal says why in its first sentence. In JSON mode the whole answer is on standard output, failures included.
Writes it refusesA write that would add a password or an access key to a file. A write to any file that says what Fettler or the assistant may do.
The client’s own foldersThis session’s scratch folder, the tool-results and memory folders, and the working folder of a companion server such as sparky, all opened with nothing declared.
One file, two front endsOne self-contained file per operating system, with no .NET to install. A command line for scripts and an MCP server for assistants, and the two give the same answer byte for byte.

No WSL, no Git Bash, no Python

On Windows, Fettler needs no WSL, no Git Bash, no Python and no .NET. It is one file that runs natively on Windows, macOS and Linux, and its verbs are the same on each. The command an assistant runs from PowerShell is the command it runs from bash. Nothing else is installed to make the file tools work.

Three levels

Both programs do three kinds of work, called levels. The tools are always on. The other two turn on when the project’s configuration file has a section of that name, so a project that only wants the tools writes nothing.

The three levels. Tools is first and always on with no file. Guardrails is second and turns on when the file has a guardrails section. Screenings is third and turns on when the file has a screenings section. On the left, .fettler.json with one section per level.
The three levels. The file on the left, .fettler.json, has one section per level, and a section turns its level on. The tools section is optional and only adjusts settings. The tools level is on with no file at all.
LevelOn whenWhat it gives you
toolsalways, with no fileeverything above. For data: SELECT and DESCRIBE through the query gate
guardrailsthe file has a guardrails sectionthe folders or databases you name, what may be done in each, and nothing else existing
screeningsthe file has a screenings sectiona screen that refuses any answer that would disclose regulated data

Guardrails: the folders you name

With guardrails on, the world is a declared list. A tree is a folder you name, with a list of what may be done in it. Whatever you do not name does not exist, and a path outside the trees is refused in the same words as a file that was never there. The rules people arrive with are all sayable:

The rule you wantWith guardrails on
Look, don’t touch.A tree can be read-only. It is the default for every tree but the one that holds the file.
Work here, not there.Two trees. One grants writes, and the other keeps the read-only default.
May add and revise, never destroy.Grant create, update and rename, and withhold delete. Most tooling cannot say this at all.
Not for you.A folder granted nothing does not exist. It never appears in a search, and guessing its path answers the same as a path that was never there.
Run the build, and nothing else.Declare the task by name and grant execute in one tree. There is no shell, so there is nothing else to run.
All of that, but only under one folder.A scope: the same words, stated inside a tree.

A setup command then turns off the assistant’s built-in file tools and shell, so Fettler is the only route to a file. Picker does the same for databases, tables and columns, and a hidden column is missing from SELECT * before the server sees the query. Guardrails for the files and guardrails for the data draw the whole arrangement, line by line.

Screenings: refusing regulated data

The screen refuses any answer that would disclose regulated data, and it is off by default. You name the tree or the database that holds the records, and every answer leaving it is checked first. One match refuses the whole answer, with exit code 13. It never hands back a redacted copy, because a redacted copy looks safe and is not.

CategoryCatchesNeeds
identifierssocial security numbers, valid card numbers, phone numbers, email addresses, labelled record numbers and labelled dates of birthnothing installed. Six patterns run inside the program.
clinicalnames, dates and conditions in clinical texta model you install, run by burler, a second program
legalcontract and consumer-report texta model
scientificresearch and scientific dataa model

Until a model is installed, the last three categories screen nothing, and the tools say so in plain words. Screenings for the files and screenings for the rows cover the models, and one set of models on disk serves both programs.

Getting started

You wantfettlepick
What it is, and whyOverviewOverview
Working in five minutesQuick startQuick start
The operations, with no fileToolsTools
Declaring the boundary, drawnGuardrailsGuardrails
Refusing regulated data, and the modelsScreeningsScreenings
The per-machine setup, in fullInstallInstall
Every verb, refusal and exit codeReferenceReference
Both programs are written in C#, ship as one self-contained file per platform, and need nothing installed beside them. burler, the model host the screen’s second tier uses, is a separate download. Only people who switch that tier on need it, and it serves both programs.