squeeze vs head and tail

2026-11-09

The command finished, and the last lines said it was done. The failure had already printed, hundreds of lines earlier, and then the log went back to progress. I kept the start of that printout and the end, which is the usual cut, and both pieces missed no such table: users. The comparison I ran was squeeze vs head and tail. squeeze is one Python file you put in front of a command so the printout is shorter before a coding tool reads it. On that same log it kept the traceback, and the short version was 510 characters out of 10,839. It is not a model. A later log said the table did not exist, without the word error, and squeeze threw that line away too.

A coding tool that can run commands has to read what those commands print. That printout goes into the chat. The chat can only hold so much, so a long log pushes older notes out. The usual fix is a cut by position. The head is the start. The tail is the end. Keep one, or keep both, and drop the middle.

Checked November 2026 against the squeeze script and a head/tail cut of the same failing command log. Numbers below are from that one-run comparison.

squeeze vs head and tail comparison
Where the command error sits in the log

What does squeeze do to a long command log

Mind map of squeeze vs head and tail: middle error, budget, traceback, full log path

squeeze shortens a command’s printout in three steps, and it does not call a model.

You need Python 3 and the one file. There is no package to install and no key to paste. The file uses only the library that ships with Python. If an install wants to download a model, you have a different project that happens to use almost the same name.

How squeeze shrinks a long log

The first step strips the hidden codes that colour terminal text, and it collapses a progress line that paints over itself. A bar often jumps back to the start of the line and draws again. squeeze keeps the last paint, so loading... done survives and the earlier paints do not. On a small sample the colour codes were gone and the word ERROR was still there.

The second step looks for repeats. It hides every number, then asks whether the lines match. Three or more matches become three lines: the first, a count of the ones in between, and the last. processed file 1 through processed file 50 became the first, [... 48 similar lines], and the last, on a run I did with the budget left wide open. A line that only changes in the number is treated as the same line. That includes a byte count, a percent, or an id. The middle values are not kept. I also ran unique line 0 about 0 through unique line 29 about 841. Those lines differ in the words only by their numbers, so they collapsed the same way.

The third step runs only if the text is still longer than the budget. The budget is a character count, not a word count. The default is 8,000 characters. About 35 percent is reserved for the start, about 45 percent for the end, and the rest for lines in the cut middle that contain a failure word. It then writes the full original printout to a file and prints the path. If the collapsed text already fits, that file is not written. The middle of a repeated run is already gone, and there is no copy to open.

A line counts as a failure if it contains one of these, in any case: error, exception, traceback, fatal, failed, failure, panic, denied, not found, refused, timeout, warning, warn, assert, errno, exit code, a status in the 400s or 500s, or a bare 400-or-500 number. I checked the list against the file I ran. status 404 matches. status 200 does not. cache not found, using default matches, even when that is a normal fallback. no such table does not match. does not exist does not match. OperationalError matches, because the letters of the word error sit inside the class name. This is a text search. It does not know that a database refused a query.

Does head and tail drop the error in the middle

Yes. On a log of repeats, with the failure in the middle, both the head and the head-and-tail cut missed it.

I built the log myself. It was 330 lines and 10,839 characters. The start was a test header and forty collected item N ok lines. Then two hundred progress N/200 compiling lines. Then a traceback whose message was sqlite3.OperationalError: no such table: users. Then eighty retry N waiting lines, and a tidy ending: cleanup, a report file, done in 12.4s. The failure is not the last line. The last line looks like success.

I set the budget to 700 characters so a cut had to happen. Head means the first 700 characters. Head and tail means a share from the start and a share from the end, with a short note where the middle was. The article does not run a named coding product for this cut. I cut the text the way that method cuts it.

squeeze result on a repeated log
CutCharacters leftTraceback still thereno such table: users still there
Full log10,839yesyes
Head700nono
Head and tail582nono
squeeze510yesyes

squeeze did not win this one by fishing the middle for failure words. After the repeats collapsed, the traceback and the database line still fit inside 700 characters, so the whole short log was kept. The output was 510 characters. No full-copy file was written, which matches the rule above: the file is written only when the text is still over budget. Head and tail have no collapse step, so they spent the budget on collected item and retry lines and never reached the traceback.

This is the short log squeeze actually printed:

== run tests ==
platform linux
collected item 1 ok
[... 38 similar lines]
collected item 40 ok
progress 0/200 compiling module_0.py
[... 198 similar lines]
progress 199/200 compiling module_199.py
Traceback (most recent call last):
  File "app.py", line 88, in load_user
    return db.fetch(uid)
sqlite3.OperationalError: no such table: users
ERROR app.worker task failed: no such table: users
retry 0 waiting 50ms
[... 78 similar lines]
retry 79 waiting 50ms
cleanup temp dirs
wrote report.json
done in 12.4s

The File line survived here because nothing was cut. Collapse had already made the log fit. That is a different result from the next test, where a cut did run and the lines under the word Traceback disappeared.

That is the useful case for the repeated log. A command that prints the same line with a new number, and fails once in the middle, is exactly the log that a position cut handles badly.

When the failure is the last line

Both kept it. squeeze was only shorter.

I made 120 lines of ok N and ended with FATAL: disk full. The budget was 400 characters. The full log was 747 characters. Head and tail still showed disk full, in 341 characters. squeeze showed it in 53 characters, because the ok lines collapsed to a first line, a count, and the last ok, then the fatal line. If your command prints the reason at the end, the usual cut is already enough. squeeze is a smaller version of the same answer, not a different answer.

When every line is different

Collapse does nothing, and a line that does not match the word list is dropped.

The second log was 55 unique lines and 1,494 characters. Each line was apply alpha change on shard with a different word, so hiding the numbers did not make them match. I buried psql: relation "users" does not exist in the middle. The budget was 500 characters.

Head missed it. Head and tail missed it, in 482 characters. squeeze missed it too, in 457 characters. The note said 40 lines were cut, and it wrote the full printout to a file. There was no note about error lines kept, because nothing in the middle matched the list.

I then added Traceback (most recent call last): and File "migrate.py", line 12, in up just above that database sentence, and ran the same budget again. The raw log was 1,565 characters. Head and tail still had neither the traceback nor the database sentence. squeeze kept one line, Traceback (most recent call last):, and dropped the file line and does not exist. The short log was 550 characters, and the full printout was on disk.

squeeze keeps the word not the reason

So the short text shows that a traceback happened and does not show the file or the reason. The reason is in the saved file, but only if something reads that file. The short text alone is not the failure.

Warnings can fill the slice before the real failure

Yes. squeeze walks the middle from the top and stops when the failure slice is full.

The slice is what is left after the start and the end take their share, about a fifth of the budget. At a budget of 420 characters, that is not many lines. I put twelve warning: skipped optional ... plugin lines above Traceback and RuntimeError: disk quota exceeded on /data, with unique lines on both sides. The raw log was 1,512 characters. squeeze kept two warnings, the alpha one and the bravo one, and dropped the traceback and the disk quota line. Head and tail missed them too. The warnings were not lies. They were also not the failure, and they arrived first, so they used up the slice.

If your log warns about everything and fails once near the end of the middle, squeeze can show you the warnings and hide the crash. Raise the budget, or open the saved file. Do not trust the short text as a complete list of what went wrong.

How do I run squeeze on one command

Put the file in front of the command, and the exit code of the command is the exit code you get back. The file is SolarNomads/squeeze.

git clone https://github.com/SolarNomads/squeeze.git
cd squeeze
python3 squeeze.py --budget 4000 -- pytest -q

Or pipe a printout you already have. 2>&1 joins the error stream into the normal stream so both go through the file. Without that, an error printed on the error stream never enters squeeze.

pytest -q 2>&1 | python3 squeeze.py --budget 4000

I wrapped a tiny command that printed three lines and exited with code 3. squeeze printed the lines, then [exit 3], and the process exit code was 3. The pipe form does not do that. A pipe’s exit code is the exit code of the last program, which is squeeze, and squeeze exits 0 after a successful shrink. If you need the original failure code, use the -- form, not the pipe.

The full printout, when a cut happens, goes to ~/.cache/squeeze unless you pass --keep. I passed an empty --keep and no cache directory was created. Pass a folder if you want the copies somewhere else. That file is the original printout, so it can contain paths, passwords, and keys the command printed. Do not point --keep at a shared folder.

squeeze vs head and tail

Use head and tail when the reason is the last thing the command prints. Use squeeze when the printout repeats and the failure can sit in the middle, and open the saved file when the short text only shows a warning or the bare word traceback.

Head and tailsqueeze
What it keepsStart and endStart, end, and lines with a failure word, after repeats collapse
Repeated progress linesKept until the budget runs outCollapsed to first, a count, and last
Failure only at the endKeptKept, usually shorter
does not exist in the middleDroppedDropped
The word Traceback, with the reason on the next lineBoth dropped in my cutThe word kept, the reason dropped
Warnings before the crashCan miss the crashCan keep the warnings and miss the crash
Full originalGone, unless something else saved itSaved only when the result is still over budget
Calls a modelNoNo

This is a different job from a tool that rewrites specific commands so they print less in the first place. That kind of shrink can be larger, and it can also drop lines you needed. The article does not run one on these logs. The measured version of that tradeoff is RTK and Caveman on a real coding bill.

It is also a different job from the text a coding tool sends before you type a word. A shorter log does not remove that. That overhead is the subject of MiniMax Code and Claude Code before the first prompt. Shrinking one printout is not the same as shrinking the whole chat either, which is the choice in context mode versus compact. If the chat is simply getting full, the number to watch is the status line. And if the waste is the tool opening the wrong files, a shorter log will not find the right function. That search is jevgrep.

I would pipe squeeze in front of a noisy test or a long compile, with --keep pointed at a folder I can open. I would not treat the short text as proof the command succeeded. The last line of a collapsed log can still say done.

Does squeeze keep the traceback

It keeps the line that contains the word, and it drops the lines under it unless those lines match the list too.

On the repeated log the whole traceback fit inside the budget, so the file name and the database line survived with it. On the unique log only Traceback (most recent call last): survived, and does not exist did not. Open the file path squeeze prints when you need the reason.

Common questions about squeeze vs head and tail

questions people ask before they pipe a log through it

does squeeze call a model

No. It is one Python file and the standard library. Nothing is sent out. If the install step downloads a model, that is a different tool with a similar name.

where does the full log go

Into the folder you pass with --keep, or into ~/.cache/squeeze if you leave the default. No file is written when the shortened text already fits the budget. Pass --keep with an empty value to turn the copy off. The copy is the original printout, so treat the folder like a log directory.

will it keep a database line that says the table does not exist

Not by itself. I ran does not exist and no such table through the matcher. Neither matched. A class name that contains the word error, such as OperationalError, does match. The sentence under a traceback is kept only when that sentence matches too.

what budget should i start with

8,000 characters is the default, and it is enough that many short commands are not cut at all. I used 400 to 700 so the cut would actually happen and I could see what was lost. If the short text shows a warning and not the crash, the budget was too small for that log, or the warnings came first. Raise it, then open the saved file if the reason is still missing.

Leave a comment