The Bleep

The maintained successor to The Fuck.
Type the command wrong. Type bleep. Run the right one.
Get it
curl -fsSL https://raw.githubusercontent.com/stamparm/thebleep/master/install.sh | sh
That picks up whichever of uv, pipx or pip you already have, and prints
the one line to add to your shell's startup file. Prefer to do it yourself:
uv tool install thebleep # or: pipx install thebleep
thebleep --alias-loader >> ~/.bashrc
Open a new shell, and the next time you mistype something, type bleep.
The long version, including the muscle memory you already
have:
thebleep --alias-loader fuck >> ~/.bashrc
Why not just The Fuck
Because the idea deserves better than its last release. The Fuck 3.32 is from January 2022: it cannot start on Python 3.12 or newer, over three hundred issues are open on it, and a good number of its rules quietly stopped matching when the tools they correct changed what they print. The Bleep is the same tool, maintained — and several times quicker about it.
Same machine, same Python 3.11, 30 runs each, medians:
% of The Fuck's time The Fuck The Bleep faster
Open a shell ██▌░░░░░░░░░░░░░░░ 206 ms 29 ms 7.1×
Correct a mistyped command ████▌░░░░░░░░░░░░░ 240 ms 60 ms 4.0×
Correct inside a git repository ████░░░░░░░░░░░░░░ 248 ms 54 ms 4.6×
Correct when nothing matches ███▉░░░░░░░░░░░░░░ 333 ms 72 ms 4.6×
Correct a slow command * ████████████▍░░░░░ 816 ms 562 ms 1.5×
Correct after 1 MB of output ▋░░░░░░░░░░░░░░░░░ 3.25 s 114 ms 28.4×
* that command sleeps for half a second. Both tools have to sit through it to read what it printed, so this row is mostly the sleep.
Opening a shell is worth a second look: that row is eval "$(thebleep --alias)"
in your rc, which starts a Python interpreter every time. Use the
loader instead and opening a shell defines
a shell function and runs no Python at all — the 29 ms goes, and what is left
is too small to measure honestly against the noise in shell startup.
Those are Linux numbers, on the machine named in the result file. Windows and PowerShell are exercised in CI on every push; On Windows is about what makes a correction expensive there and what was done about it.
The harness is bench/, the run these numbers come from is
bench/results/final.json, and the block above is
written from that file by bench/chart.py. Reproduce it, and
read where the time went.
The rest of the reasons:
- Python 3.9 through 3.14, tested on Linux, macOS and Windows on every one of them — and Bash, Zsh, Fish, tcsh and PowerShell as before, with Nushell added. Supported everything.
- 30 items from The Fuck's backlog are fixed here, issues and pull
requests, four of them command injections — plus the rules that had rotted
against current
git,npm,docker,cargo,brew,gem,az,gradleandterraform. What's fixed. - It asks before running your previous command a second time. Reading what your command printed used to mean running it again, side effects and all. Safe by default.
- Press tab to edit the correction instead of running it. The suggestion lands in your own command line, with the cursor at the end of it, and nothing runs until you press return. Edit before you run.
- Press
?to be told why. Which rule made the suggestion, what it saw, and whether accepting it does anything besides run the command. Why am I being told this. thebleep --doctoranswers the questions a bug report usually starts with, in one screen you can paste anywhere. Diagnostics.- Nothing to relearn. The same rules and settings, and the same
fuckalias if you want it; seven rules are deliberately less eager, all in the direction of doing only what they say. Coming from The Fuck.
The Bleep is based on the original codebase by Vladimir Iakovlev and its contributors; their work and history remain fully credited.
Contents
- Safe by default
- Edit before you run
- Why am I being told this
- thebleep --doctor
- Coming from The Fuck
- What's fixed
- Supported everything
- Installation
- Updating
- Uninstall
- How it works
- Creating your own rules
- Settings
- Third-party packages with rules
- Experimental instant mode
- Performance
- Developing
- License
Safe by default
The Bleep asks before running a correction. In a non-interactive environment
(pipe, subprocess or CI), it does not silently apply the first suggestion;
use --yes when you explicitly want automatic application.
Reading the previous command
To suggest a fix, The Bleep needs to know what your command printed — and a shell keeps no record of that. The only way to find out is to run the command again, which means anything it changed changes twice:
$ deploy production
deploy: missing --confirm
$ bleep
deploy production has to run again to be read, and anything it changes will
change twice. Run it? [y/N]
So it asks first. It skips asking in two cases: the program is not there to be
found, so nothing runs either time (gti status), or the program is one of a
short list that only ever read, whatever they are asked to do (ls, cat,
grep). That second one is a judgement about the name, and a name is not a
proof about the program a PATH lookup will find — what makes it a reasonable
one is that the same program under the same name ran a moment ago, when you
typed it. It is deliberately not a list of dangerous commands: such a list
only declares the ones nobody thought of to be safe.
Where nobody can be asked — a pipe, a subprocess, CI — the answer is no, and the correction is attempted from the command alone.
Two ways to stop being asked:
- Record the output as it happens. Experimental instant mode reads what scrolled past instead of running anything again, so the question never comes up. This is the better answer if your shell supports it.
confirm_replay = Falsein your settings, or--yesfor a single run, which restores The Fuck's behaviour of running the previous command again without asking.
Edit before you run
A suggestion is often ninety-five percent of what you wanted. Press tab instead of enter and it is handed to you in your own command line to finish:
$ git chekout featuer
git: 'chekout' is not a git command. See 'git --help'.
$ bleep
git checkout feature [enter/↑/↓/tab=edit/?/ctrl+c/esc]
tab, and the next thing you see is your own prompt, with the cursor after the last character:
$ git checkout feature█
From there it is an ordinary command line: edit it, or press return to run it,
or ctrl+c to throw it away. Nothing has run yet. bleep --edit (or
-e) makes that the behaviour of enter too, and edit = True in
your settings makes it permanent — a mode where The Bleep never runs anything,
it only writes your next command for you.
The arrow keys still walk the other suggestions, so you can pick the one worth editing before you edit it.
Which shells
The correction goes into the line editor through whatever the shell offers for
exactly that. There is no fallback for the shells that offer nothing: the trick
that would work everywhere is TIOCSTI, which pushes characters into another
process's terminal as though they had been typed. Modern Linux can refuse it
outright, and does by default — for good reasons that apply here too.
| Shell | How | What you get |
|---|---|---|
| Zsh | print -z |
your next prompt, already filled in |
| Fish | commandline --replace |
your next prompt, already filled in |
| Nushell ≥ 0.87 | commandline edit --replace |
every correction, always |
| Bash ≥ 4.0 | read -e -i |
a readline prompt, already filled in |
| PowerShell | PSConsoleReadLine::AddToHistory |
press ↑ to bring it up |
| Bash 3.2 (macOS system bash) | — | not offered |
| tcsh | — | not offered |
Bash is the one that is close rather than exact. It has no way to write the
next prompt's buffer, so what you get is readline itself — your keymap, your
history, your editing keys — on a line that already holds the correction, and
the prompt is your own PS1. PowerShell's editing API belongs to a key handler
and does nothing when called from a function, so there the correction becomes
the newest history entry and one ↑ brings it up.
Where editing is not available, tab is not offered and does nothing;
--edit says so and runs nothing rather than falling back to running the
command. The prompt tells you which case you are in: if it says tab=edit, it
works.
Editing does not fire a rule's side effect and does not touch your history. Both belong to a command that ran, and an edited one has not — your shell records whatever you finally submit, which is the command you actually chose.
Nushell
Nushell is the shell where this is not an option but the whole design, so it is worth saying plainly what happens: a correction always goes to your command line, and you press return to run it.
> gti status
Error: nu::shell::external_command
× External command failed
> bleep
git status [enter/↑/↓/ctrl+c/esc]
> git status█
That is not a shortcoming worked around. Nushell has no eval, deliberately —
it parses a script all the way through before running any of it, which is where
most of what it can tell you about a pipeline comes from, and code that appears
at run time cannot be parsed that way. nu -c '...' is not a substitute: it
starts a second Nushell, so a corrected cd, mkdir -p x; cd x or $env
assignment would happen to a process that immediately exits, and a correction
that silently does nothing is worse than no correction. Writing it into your
command line runs it in the session you are actually in.
Two smaller differences follow from the same place. and/or in Nushell are
boolean operators and not command separators, so a chained correction is written
try { git pull; git push } — try stops at the command that failed, which is
what && means. And the broken command is not removed from your history, since
Nushell has no way to delete an entry; the corrected one is recorded normally
when you submit it.
Nushell 0.87 or newer, which is where commandline edit arrived.
Why am I being told this
A correction is a command you are about to run, and "because a program said so" is a thin reason to run anything. Press ? at the prompt:
$ git chekout featuer
$ bleep
git checkout feature [enter/↑/↓/tab=edit/?/ctrl+c/esc]
rule git_not_command (bundled)
matched git, and output containing "is not a git command. See 'git --help'."
read what your command printed
git checkout feature [enter/↑/↓/tab=edit/?/ctrl+c/esc]
Two more lines appear when they apply: side effect, when accepting the
suggestion does something besides run the command, and runs as, when the
correction begins with sudo or doas. Having asked once, the arrow keys
explain each suggestion as you walk them. bleep --explain starts that way, and
explain = True in your settings makes it permanent.
Everything there is a fact about the rule rather than a description of it: its
name, which of the three places its file came from, whether it declares that it
needs your command's output, whether it has a side effect — and then the two
that carry most of the meaning, the app it says it is about and the text it
requires in the output, both read out of the rule's own match by the same
extraction that decides which rules to load at all. Where several messages would
have satisfied the rule, the one quoted is the one that is actually in your
output.
Nothing reads a rule's body and tries to say in English what it means, and no
rule had to be given a hand-written description for this to work — so a rule of
your own, or one from a package, explains itself exactly as well as a bundled
one does. A rule that works its condition out in a way this cannot read says so:
matched a condition this rule works out for itself.
Back to Contents
thebleep --doctor
When something is not working, it is nearly always one of a dozen things, and
every one of them is a fact about the machine rather than about the code — the
alias is in a file this shell does not read, thebleep on PATH is an older
copy in another virtualenv, the settings file has a typo so every setting in it
was dropped, ~/.config/thefuck was never copied over. --doctor checks all of
them at once:
$ thebleep --doctor
The Bleep 4.0.0
Python 3.12.3 (/usr/bin/python3)
Platform Linux 6.8.0 (x86_64)
Shell ZSH 5.9 (from TB_SHELL)
Integration alias loader in ~/.zshrc
Executable ~/.local/bin/thebleep
On PATH yes
Config ~/.config/thebleep/settings.py (2 set: priority, rules)
Rules 174 bundled, 3 of your own
Rule pack ~/.cache/thebleep/rules-3-cb0d0d0a.pack (174 rules cached)
- Replayless capture available, not switched on
See --enable-experimental-instant-mode.
Editing supported by this shell (tab at the prompt)
Everything looks good.
! marks something worth fixing and the advice sits under it; - is worth
knowing. The exit status is non-zero when there is a !, so it is usable in a
script.
It is safe to paste. A diagnostic ends up in an issue, so it says that a
setting is set and not what it is set to, that an alias is defined and not what
it expands to, which rules exist and not what is in them. Nothing is read out of
the environment except the handful of names The Bleep itself defines, and
those are reported as set or unset. Paths have your home directory folded back
to ~, so your username does not travel either.
It changes nothing. No config directory is created, no settings file is written, no rule pack is built — a report that has to alter the machine before it can describe it is describing a different machine.
Back to Contents
Coming from The Fuck
Nothing is relearned. The rules, the settings and the flags are the ones you already know; the names changed and the config moved.
pip uninstall thefuck # optional, they coexist happily
cp -r ~/.config/thefuck ~/.config/thebleep # settings.py and your own rules
Then swap the line in your startup file. Keeping the word you are used to is one argument:
thebleep --alias-loader fuck >> ~/.bashrc # and delete the thefuck line
What to know:
THEFUCK_*environment variables areTHEBLEEP_*. The names after the prefix are unchanged.- Config is
$XDG_CONFIG_HOME/thebleep/settings.py, and your own rules go in$XDG_CONFIG_HOME/thebleep/rules. The settings themselves are the same, so the file copies straight over. - A rule of your own that imports
thefuck.utilswantsthebleep.utils. That is the whole of the port. - A rule package of your own is
thebleep_contrib_*rather thanthefuck_contrib_*. - The Bleep asks before running your previous command a second time.
confirm_replay = Falsein your settings restores what you are used to, and Reading the previous command explains why you might not want to.
Seven rules behave differently on purpose, all in the same direction — what you agree to is what runs:
dirty_untaranddirty_unzipsuggest extracting into a directory of their own, and no longer delete the files that were already unpacked. They could not tell an extracted file from one of yours under the same name, and their containment check was a string prefix that../walks straight out of.ssh_known_hostsshows you thessh-keygen -Rit wants to run, in front of your command. It used to hand back your own command and remove the offending line behind it, so a man-in-the-middle warning disappeared with nothing to read.rm_diradds-r, not-rf.-ris enough to remove a directory;-falso silences the prompt for a write-protected file.pip_installno longer falls back tosudo pip install.python_module_erroris off by default. An import name is not a distribution name —import yamlwants PyYAML — so the package it suggests installing is a guess, and a mistyped import makes itpip install <typo>. Ask for it withrules = ['DEFAULT_RULES', 'python_module_error'].quotation_marksonly fires when your command genuinely does not parse and swapping the quotes makes it parse. It used to fire whenever both kinds of quote appeared and rewrite them, sogit commit -m "it's fine"becamegit commit -m "it"s fine".
Back to Contents
What's fixed
Every commit that fixes a reported problem names the issue it fixes, so this is
git log --grep 'nvbn/thefuck#' rather than a claim in a README. Thirty
upstream backlog items so far — half of them issues and half of them pull
requests nobody merged — every one of them linked below, and the rest found by
running the tools.
It starts on current Python. distutils was removed in 3.12 and The Fuck
imports it, so it cannot run there at all; pkg_resources and imp were going
the same way. All three are gone, Python 2 support went with them, and the
suite runs on 3.9 through 3.14 on Linux, macOS and Windows.
#1499
#1610
#1552
#1479
#873
Four ways a command could be turned into a different command. A correction
is text a shell then evaluates, and much of that text is copied out of somewhere
you do not control — a tool's error message, a repository's branches, a package
file's scripts — where shell syntax is perfectly legal: git will make you a
branch called feature;rm -rf ~. Unquoted were the names the *_no_command
rules read out of another command's output; the paths and names read out of the
failed command's own output, ssh's known_hosts line and a branch from
origin/HEAD among them; the URL handed to open; and the sudo rule, which
re-quoted your whole script and gave it to sh -c as root. All four are quoted
now, and tests/test_injection.py runs each
suggestion through a real shell and checks what the program actually received.
#1531
#1606
It asks before running your command again. To correct a command you have to
know what it printed, and a shell keeps no record, so the command is run a
second time — deploy, git push, rm, whatever it was, before you have
agreed to anything. It asks first now, except where there is nothing to run or
the program only ever reads.
#1126
The alias breaking because of something you pasted. The shell handed us your recent history in an environment variable, and the kernel will not pass a program a variable larger than 128K. One pasted command that size and the alias failed with "Argument list too long" — for that correction and for every one afterwards, until the entry fell out of the history window. It asks the shell for a smaller window instead. #798
Rules that had quietly stopped matching. A rule that looks for a string in
a tool's output stops working the day that tool rewords it, silently, and
nothing in a test suite of fixtures notices. These were found by mistyping
commands at the installed binaries and reading what came back: npm 7+,
cargo 1.73+, docker 25+, git (main rather than master, and repository
ownership), brew 4 (five of its seven rules), gem 3.2+, az, gradle 8 and
terraform 1.x.
#1320
#1172
#1341
#1313
#1376
Crashes, and the places it did not work at all. An unreadable process tree,
a process that exits while being killed, no terminal attached, a closed pipe,
set -u, an empty alias, Fish's history moving to the XDG data directory, a
command on Windows whose file is not spelled the way you typed it, and your
environment being printed into debug output.
#1600
#1509
#1026
#1040
#1562
#1539
#1355
#1551
#1258
#1209
#1296
#995
#1506
And the test suite itself. Three of the thirty are about the tests rather
than the tool: mock became unittest.mock, a memoized helper leaked
between test cases, and usefixtures was applied to a fixture, where it does
nothing.
#1344
#1523
#1550
And it is quicker, which has a section of its own.
Back to Contents
Supported everything
| Python | 3.9, 3.10, 3.11, 3.12, 3.13, 3.14 |
| Systems | Linux, macOS, Windows — every Python on every one of them, on every push |
| Shells | Bash, Zsh, Fish, Nushell, tcsh, PowerShell |
| Rules | 174 of them, for git, docker, npm, yarn, pip, apt, dnf, zypper, pacman, brew, cargo, go, gradle, maven, terraform, aws, az, systemctl and the rest |
Bash, Zsh, Fish, Nushell and tcsh are exercised end to end, in containers, driving a real terminal: the tests type a wrong command into the shell, type the alias, and check what the shell then runs. PowerShell gets the same treatment on Windows in CI, in Windows PowerShell 5.1 as well as 7, because the two do not agree about command chaining. The Python suite covers all six.
Back to Contents
Installation
The one-liner picks up whichever of uv, pipx or pip you already have,
never asks for sudo, and never edits a file of yours:
curl -fsSL https://raw.githubusercontent.com/stamparm/thebleep/master/install.sh | sh
Read it first if you like — that is the same file as
install.sh in this repository, and sh install.sh --dry-run
prints what it would run without running it.
Or do it by hand, in whichever way you install command line tools:
uv tool install thebleep # https://docs.astral.sh/uv/
pipx install thebleep # https://pipx.pypa.io/
pip install --user thebleep # if your distribution lets pip write there
The first two put The Bleep in an environment of its own, which is what you
want for a tool rather than a library: nothing you pip install later can break
it. On Debian, Ubuntu and Fedora, pip install --user is refused outright
(PEP 668) — use uv or pipx there.
The alias, and why it costs nothing
Append the loader to your .bashrc, .zshrc or other startup script, once:
thebleep --alias-loader >> ~/.bashrc # or ~/.zshrc, etc.
That writes a few lines of shell that define the alias the first time you use
it, and nothing before — so opening a shell costs nothing at all. It is static:
it does not need regenerating when The Bleep is upgraded, because all it does is
call thebleep --alias on first use.
bleep() {
eval "$(TB_SHELL=bash thebleep --alias bleep)";
bleep "$@";
}
Any name you like, including the one your fingers already know:
thebleep --alias-loader BLEEP >> ~/.bashrc # for Mondays
thebleep --alias-loader fuck >> ~/.bashrc
Paying at startup instead
eval $(thebleep --alias) in your startup file does the same job by starting a
Python interpreter every time you open a shell, which is the 29 ms in the table
above. Use it if you prefer it, and for the experimental instant mode, which has
to set your prompt up front.
Your shell
--alias-loader writes the right thing for the shell you run it from, so the
only difference between shells is the file it goes in:
| Shell | |
|---|---|
| Bash | thebleep --alias-loader >> ~/.bashrc |
| Zsh | thebleep --alias-loader >> ~/.zshrc |
| Fish | thebleep --alias-loader >> ~/.config/fish/config.fish |
| tcsh | thebleep --alias-loader >> ~/.cshrc |
| Nushell | thebleep --alias-loader >> ~/.config/nushell/config.nu |
| PowerShell | thebleep --alias-loader >> $profile |
The few things worth knowing per shell:
- Bash. A login shell reads
~/.bash_profileand not~/.bashrc, which is how macOS's Terminal starts one. If the alias is not there in a new window, that is why;thebleep --alias-loader >> ~/.bash_profileas well, or source one from the other. - Zsh.
~/.zshrc, and that is all. If you use a framework that rewrites it, put the line in~/.zshrc.localor wherever it tells you to. - Fish.
~/.config/fish/config.fish. Fish is asked for your aliases and functions by runningfish -ic, so an alias defined only for interactive use is still found; the answer is cached againstconfig.fish, so it is looked up again when you change it. - tcsh.
~/.tcshrcif you have one,~/.cshrcotherwise. - Nushell.
$XDG_CONFIG_HOME/nushell/config.nuif that is set — on every platform, which is the order Nushell itself reads them in — otherwise~/.config/nushell/config.nu,%APPDATA%\nushellon Windows or~/Library/Application Support/nushellon macOS.thebleep --doctortells you which one it found. Here--alias-loaderwrites the alias itself rather than a stub that fetches it, because Nushell has noevalto define a command from a string — which costs nothing, since what your shell then reads at startup is a dozen lines of Nushell rather than a Python interpreter. Nushell 0.87 or newer, forcommandline edit. What a correction does there. - PowerShell.
$profilemay not exist yet:New-Item -Force -Path $profilefirst. If PowerShell refuses to run the profile, that is the execution policy rather than us:Set-ExecutionPolicy -Scope CurrentUser RemoteSigned. Both Windows PowerShell 5.1 and PowerShell 7 work; a chained correction is written asfirst; if ($?) { second }, because&&needs 7. - Anything else gets a generic alias that reads your last command with
fc -ln -1. Rules that need to know which shell you are in will not; setTB_SHELLyourself if it guesses wrong.
Without an alias to tell it, The Bleep works out which shell it is in by
walking up the process tree — which is right almost always, and wrong in the
places where the process above it is not the shell: a container, an IDE's
integrated terminal, a wrapper script, distrobox. --shell says so outright:
thebleep --shell fish --alias-loader >> ~/.config/fish/config.fish
thebleep --shell bash git brnch # correct as though bash had asked
It takes any of bash, csh, fish, nu, powershell, pwsh, tcsh,
zsh, and
an unknown name is an error rather than a silent fallback. Naming the shell also
skips the walk up the process tree, so it is the cheaper way round as well as
the certain one.
Changes are only available in a new shell session. To make changes immediately
available, run source ~/.bashrc (or your shell config file like .zshrc).
To run fixed commands without confirmation, use --yes (or -y for short):
bleep --yes
To fix commands recursively until succeeding, use the -r option:
bleep -r
Back to Contents
Updating
However you installed it:
uv tool upgrade thebleep
pipx upgrade thebleep
pip install --user --upgrade thebleep
Or run the one-liner again, which upgrades in place. The alias line in your
startup file never needs regenerating — all it does is call
thebleep --alias the first time you use it.
Uninstall
Reverse the two steps: delete the thebleep line from your shell's startup
file, then remove the package with uv tool uninstall thebleep,
pipx uninstall thebleep or pip uninstall thebleep.
How it works
The Bleep attempts to match the previous command with a rule. If a match is found, a new command is created using the matched rule and executed.
Commands with something in front of them
The interesting command is not always the first word:
$ sudo -u www-data git chekout main
$ bleep
sudo -u www-data git checkout main
sudo, doas, env FOO=bar, command, builtin, nice, nohup, setsid
and stdbuf are peeled off — nested, in any combination — the command
underneath is corrected by every rule as though you had typed it on its own,
and the wrapper comes back in front of the suggestion exactly as you wrote it.
That is one model applied to every rule, in place of the sudo-only decorator
that 26 of them had to ask for individually — which is still there, and still
works, for rules outside this repository.
It fails towards leaving your command alone. A wrapper that is not transparent
is not peeled: sudo -i and sudo -s run a shell, sudo -e opens an editor,
sudo -l lists privileges and command -v prints a path, so none of those is
the command underneath in a hat. Neither is an option it does not recognise —
that option might take a value, and mistaking a value for the command is worse
than not offering a correction. Nor is a script with shell syntax in it, where
the first word is not the only command anyway, nor a wrapper whose words would
have to be re-quoted to be handed back. time, strace and valgrind are
transparent and still not peeled, because the output being corrected from is
partly theirs: time git stauts prints git's error and time's report, and a
rule that picks a name out of a command's output would offer one of time's
lines as a branch to check out.
The following rules are enabled by default:
adb_unknown_command— fixes misspelled commands likeadb logcta;ag_literal— adds-Qtoagwhen suggested;aws_cli— fixes misspelled commands likeaws dynamdb scan;az_cli— fixes misspelled commands likeaz providers;cargo— runscargo buildinstead ofcargo;cargo_no_command— fixes wrong commands likecargo buid;cat_dir— replacescatwithlswhen you try tocata directory;cd_correction— spellchecks and corrects failed cd commands;cd_cs— changescstocd;cd_mkdir— creates directories before cd'ing into them;cd_parent— changescd..tocd ..;chmod_x— adds execution bit;choco_install— appends common suffixes for chocolatey packages;composer_not_command— fixes composer command name;conda_mistype— fixes conda commands;cp_create_destination— creates a new directory when you attempt tocpormvto a non-existent onecp_omitting_directory— adds-awhen youcpdirectory;cpp11— adds missing-std=c++11tog++orclang++;dirty_untar— suggests re-extracting atar xthat unpacked into the current directory into a directory of its own (it does not delete what was already unpacked — nothing in the archive says which of those files you already had);dirty_unzip— the same forunzip;django_south_ghost— adds--delete-ghost-migrationsto failed because ghosts django south migration;django_south_merge— adds--mergeto inconsistent django south migration;docker_daemon_not_running— starts Docker withsystemctlwhen its daemon is not listening;docker_login— executes adocker loginand repeats the previous command;docker_not_command— fixes wrong docker commands likedocker tags;docker_image_being_used_by_container— removes the container that is using the image before removing the image;dry— fixes repetitions likegit git push;fab_command_not_found— fixes misspelled fabric commands;fix_alt_space— replaces Alt+Space with Space character;fix_file— opens a file with an error in your$EDITOR;gem_unknown_command— fixes wronggemcommands;git_add— fixes "pathspec 'foo' did not match any file(s) known to git.";git_add_force— adds--forcetogit add <pathspec>...when paths are .gitignore'd;git_bisect_usage— fixesgit bisect strt,git bisect goood,git bisect rset, etc. when bisecting;git_branch_delete— changesgit branch -dtogit branch -D;git_branch_delete_checked_out— when you try to delete the branch you are on, checks out the default branch first and then deletes it: whateverorigin/HEADpoints at, ormainormasterif there is no remote to ask;git_branch_exists— offersgit branch -d foo,git branch -D fooorgit checkout foowhen creating a branch that already exists;git_branch_list— catchesgit branch listin place ofgit branchand removes created branch;git_branch_0flag— fixes commands such asgit branch 0vandgit branch 0rremoving the created branch;git_checkout— fixes branch name or creates new branch;git_clone_git_clone— replacesgit clone git clone ...withgit clone ...git_clone_missing— addsgit cloneto URLs that appear to link to a git repository.git_commit_add— offersgit commit -a ...orgit commit -p ...after previous commit if it failed because nothing was staged;git_commit_amend— offersgit commit --amendafter previous commit;git_commit_reset— offersgit reset HEAD~after previous commit;git_diff_no_index— adds--no-indexto previousgit diffon untracked files;git_diff_staged— adds--stagedto previousgit diffwith unexpected output;git_dubious_ownership— adds the repository tosafe.directorywhen git refuses to touch it because somebody else owns it;git_fix_stash— fixesgit stashcommands (misspelled subcommand and missingsave);git_flag_after_filename— fixesfatal: bad flag '...' after filenamegit_help_aliased— fixesgit help <alias>commands replacing with the aliased command;git_hook_bypass— adds--no-verifyflag previous togit am,git commit, orgit pushcommand;git_lfs_mistype— fixes mistypedgit lfs <command>commands;git_main_master— fixes incorrect branch name betweenmainandmastergit_merge— adds remote to branch names;git_merge_unrelated— adds--allow-unrelated-historieswhen requiredgit_not_command— fixes wrong git commands likegit brnch;git_pull— sets upstream before executing previousgit pull;git_pull_clone— clones instead of pulling when the repo does not exist;git_pull_uncommitted_changes— stashes changes before pulling and pops them afterwards;git_push— adds--set-upstream origin $branchto previous failedgit push;git_push_different_branch_names— fixes pushes when local branch name does not match remote branch name;git_push_pull— runsgit pullwhenpushwas rejected;git_push_without_commits— creates an initial commit if you forget and onlygit add ., when setting up a new project;git_rebase_no_changes— runsgit rebase --skipinstead ofgit rebase --continuewhen there are no changes;git_remote_delete— replacesgit remote delete remote_namewithgit remote remove remote_name;git_rm_local_modifications— adds-for--cachedwhen you try torma locally modified file;git_rm_recursive— adds-rwhen you try torma directory;git_rm_staged— adds-for--cachedwhen you try torma file with staged changesgit_rebase_merge_dir— offersgit rebase (--continue | --abort | --skip)or removing the.git/rebase-mergedir when a rebase is in progress;git_remote_seturl_add— runsgit remote addwhengit remote set_urlon nonexistent remote;git_stash— stashes your local modifications before rebasing or switching branch;git_stash_pop— adds your local modifications before popping stash, then resets;git_tag_force— adds--forcetogit tag <tagname>when the tag already exists;git_two_dashes— adds a missing dash to commands likegit commit -amendorgit rebase -continue;go_run— appends.goextension when compiling/running Go programs;go_unknown_command— fixes wronggocommands, for examplego bulid;gradle_no_task— fixes not found or ambiguousgradletask;gradle_wrapper— replacesgradlewith./gradlew;grep_arguments_order— fixesgreparguments order for situations likegrep -lir . test;grep_recursive— adds-rwhen you try togrepdirectory;grunt_task_not_found— fixes misspelledgruntcommands;gulp_not_task— fixes misspelledgulptasks;has_exists_script— prepends./when script/binary exists;heroku_multiple_apps— adds--app <app>toherokucommands likeheroku pg;heroku_not_command— fixes wrongherokucommands likeheroku log;history— tries to replace command with the most similar command from history;hostscli— tries to fixhostscliusage;ifconfig_device_not_found— fixes wrong device names likewlan0towlp2s0;java— removes.javaextension when running Java programs;javac— appends missing.javawhen compiling Java files;lein_not_task— fixes wrongleintasks likelein rpl;long_form_help— changes-hto--helpwhen the short form version is not supportedln_no_hard_link— catches hard link creation on directories, suggest symbolic link;ln_s_order— fixesln -sarguments order;ls_all— adds-Atolswhen output is empty;ls_lah— adds-lahtols;man— changes manual section;man_no_space— fixes man commands without spaces, for examplemandiff;mercurial— fixes wronghgcommands;missing_space_before_subcommand— fixes command with missing space likenpminstall;mkdir_p— adds-pwhen you try to create a directory without a parent;mvn_no_command— addsclean packagetomvn;mvn_unknown_lifecycle_phase— fixes misspelled life cycle phases withmvn;npm_missing_script— fixesnpmcustom script name innpm run-script <script>;npm_run_script— adds missingrun-scriptfor customnpmscripts;npm_wrong_command— fixes wrong npm commands likenpm urgrade;no_command— fixes wrong console commands, for examplevom/vim;no_such_file— creates missing directories withmvandcpcommands;omnienv_no_such_command— fixes wrong commands forgoenv,nodenv,pyenvandrbenv(eg.:pyenv isntallorgoenv list);open— either prependshttp://to address passed toopenor creates a new file or directory and passes it toopen;pip_install— adds--userwhenpip installfailed for want of permission. It does not offersudo pip install; where--useris not enough,pip_externally_managedbelow has the answer;pip_externally_managed— offerspipxor a virtual environment when pip refuses to install into the system Python (PEP 668);pip_unknown_command— fixes wrongpipcommands, for examplepip instatl/pip install;php_s— replaces-sby-Swhen trying to run a local php server;ping_url— pings the host in a URL you pasted, not the URL;port_already_in_use— kills process that bound port;prove_recursively— adds-rwhen called with directory;python_command— prependspythonwhen you try to run non-executable/without./python script;python_execute— appends missing.pywhen executing Python files;quotation_marks— fixes uneven usage of'and"when containing args';path_from_history— replaces not found path with a similar absolute path from history;rails_migrations_pending— runs pending migrations;react_native_command_unrecognized— fixes unrecognizedreact-nativecommands;remove_shell_prompt_literal— removes leading shell prompt symbol$, common when copying commands from documentations;remove_trailing_cedilla— removes trailing cedillasç, a common typo for European keyboard layouts;rm_dir— adds-rwhen you try to remove a directory;scm_correction— corrects wrong scm likehg logtogit log;sed_unterminated_s— adds missing '/' tosed'sscommands;sl_ls— changessltols;ssh_known_hosts— on a host key warning, suggests thessh-keygen -Rthat ssh itself recommends, in front of your command, so you can see which key it drops before agreeing;sudo— prependssudoto the previous command if it failed because of permissions;sudo_command_from_user_path— runs commands from users$PATHwithsudo;switch_lang— switches command from your local layout to en;systemctl— correctly orders parameters of confusingsystemctl;terraform_init— runsterraform initbefore plan or apply;terraform_no_command— fixes unrecognizedterraformcommands;test.py— runspytestinstead oftest.py;touch— creates missing directories before "touching";tsuru_login— runstsuru loginif not authenticated or session expired;tsuru_not_command— fixes wrongtsurucommands liketsuru shell;tmux— fixestmuxcommands;unknown_command— fixes hadoop hdfs-style "unknown command", for example adds missing '-' to the command onhdfs dfs ls;unsudo— removessudofrom previous command if a process refuses to run on superuser privilege.vagrant_up— starts up the vagrant instance;whois— fixeswhoiscommand;workon_doesnt_exists— fixesvirtualenvwrapperenv name os suggests to create new.wrong_hyphen_before_subcommand— removes an improperly placed hyphen (apt-install->apt install,git-log->git log, etc.)yarn_alias— fixes aliasedyarncommands likeyarn ls;yarn_command_not_found— fixes misspelledyarncommands;yarn_command_replaced— fixes replacedyarncommands;yarn_help— makes it easier to openyarndocumentation;
Back to Contents
The following rules are enabled by default on specific platforms only:
apt_get— installs app from apt if it not installed (requirespython-commandnotfound/python3-commandnotfound);apt_get_search— changes trying to search usingapt-getwith searching usingapt-cache;apt_invalid_operation— fixes invalidaptandapt-getcalls, likeapt-get isntall vim;apt_list_upgradable— helps you runapt list --upgradableafterapt update;apt_upgrade— helps you runapt upgradeafterapt list --upgradable;brew_cask_dependency— installs cask dependencies;brew_install— fixes formula name forbrew install;brew_reinstall— turnsbrew install <formula>intobrew reinstall <formula>;brew_link— adds--overwrite --dry-runif linking fails;brew_uninstall— adds--forcetobrew uninstallif multiple versions were installed;brew_unknown_command— fixes wrong brew commands, for examplebrew docto/brew doctor;brew_update_formula— turnsbrew update <formula>intobrew upgrade <formula>;dnf_no_such_command— fixes mistyped DNF commands;nixos_cmd_not_found— installs apps on NixOS;pacman— installs app withpacmanif it is not installed (usesparu,yay,pikauroryaourtif available, in that order);pacman_invalid_option— replaces lowercasepacmanoptions with uppercase.pacman_not_found— fixes package name withpacman,paru,yay,pikauroryaourt.yum_invalid_operation— fixes invalidyumcalls, likeyum isntall vim;zypper_no_such_command— fixes mistypedzyppercommands and their abbreviations on openSUSE and SLE, likezypper isntall vimorzypper dpu.
The following commands are bundled with The Bleep, but are not enabled by default:
git_push_force— adds--force-with-leaseto agit push(may conflict withgit_push_pull);python_module_error— installs the package a missing import needs. An import name is not a distribution name (import yamlwants PyYAML,cv2wants opencv-python), and a mistyped import makes the suggestionpip install <typo>, so this is not on by default;rm_root— adds--no-preserve-roottorm -rf /command.
Back to Contents
Creating your own rules
To add your own rule, create a file named your-rule-name.py
in ~/.config/thebleep/rules. The rule file must contain two functions:
match(command: Command) -> bool
get_new_command(command: Command) -> str | list[str]
Rules can also contain the optional variables enabled_by_default,
requires_output and priority.
Command has three attributes: script, output and script_parts.
Your rule should not change Command.
Rules api changed in 3.0: To access a rule's settings, import it with
from thebleep.conf import settings
settings is a special object assembled from ~/.config/thebleep/settings.py,
and values from env (see more below).
A whole rule, for a kubectl that wants --namespace and did not get one:
import re
# Read out of the rule by the loader, so a `kubectl` rule is never even
# compiled for your `git push`. Worth declaring; see How it works.
from thebleep.utils import for_app
@for_app('kubectl')
def match(command):
return 'the server doesn\'t have a resource type' in command.output
def get_new_command(command):
return re.sub(r'^kubectl ', 'kubectl --namespace default ', command.script)
# All optional, and these are the defaults.
enabled_by_default = True
requires_output = True # do not even try me without the command's output
priority = 1000 # lower is matched first
That is the whole interface. A rule reads a command and returns a string — or a list of strings, to offer several — and the one you accept is the one that runs.
side_effect, and why to think twice
A rule may also define:
side_effect(old_command: Command, fixed_command: str) -> None
which runs after you accept the correction, and only then — pressing tab to edit does not fire it, because nothing has run. It is supported and is not going away, and third-party rules that use it keep working.
It is still the wrong tool nine times out of ten. Whatever it does happens
outside the command you were shown and agreed to, so the thing you approved is
not the thing that happened — which is exactly how dirty_untar came to delete
files and ssh_known_hosts came to drop a host key behind a warning you never
read. Both are now rules that say what they do in the command itself, and both
are better rules for it. Prefer shell.and_('the thing you want first', command.script): it is visible, it is refusable, and it appears in your history
like anything else you ran.
More examples of rules, utility functions for rules, app/os-specific helpers.
Back to Contents
Settings
Several The Bleep parameters can be changed in the file $XDG_CONFIG_HOME/thebleep/settings.py
($XDG_CONFIG_HOME defaults to ~/.config):
rules— list of enabled rules, by defaultthebleep.const.DEFAULT_RULES;exclude_rules— list of disabled rules, by default[];require_confirmation— requires confirmation before running new command, by defaultTrue; when there's no terminal attached (a pipe, a subprocess or CI) confirmation is impossible, so the suggestion is only printed and nothing is run — pass--yesto apply it;confirm_replay— asks before running your previous command a second time to read what it printed, by defaultTrue; see Reading the previous command;wait_command— the max amount of time in seconds for getting previous command output;no_colors— disable colored output;priority— dict with rules priorities, rule with lowerprioritywill be matched first;debug— enables debug output, by defaultFalse;history_limit— the numeric value of how many history commands will be scanned, like2000;alter_history— push fixed command to history, by defaultTrue;wait_slow_command— max amount of time in seconds for getting previous command output if it inslow_commandslist;slow_commands— list of slow commands;num_close_matches— the maximum number of close matches to suggest, by default3;excluded_search_path_prefixes— path prefixes to ignore when searching for commands, by default[];instant_mode— read what scrolled past instead of running your command again, by defaultFalse; see Experimental instant mode;repeat— if the corrected command fails too, correct that as well, by defaultFalse;--repeatdoes it for one run;edit— hand the correction to your command line to edit instead of running it, by defaultFalse;--editdoes it for one run, and tab does it for one suggestion; see Edit before you run;explain— say which rule made each suggestion and what it matched, by defaultFalse;--explaindoes it for one run, and ? does it at the prompt; see Why am I being told this;env— environment variables to set for your previous command when it is run again to read its output, by default{'LC_ALL': 'C', 'LANG': 'C'}, which is there so that rules can look for English error messages. Git also getsGIT_TRACE=1, so thatgit stcan be resolved to whatever alias it stands for; nothing else does.
An example of settings.py:
rules = ['sudo', 'no_command']
exclude_rules = ['git_push']
require_confirmation = True
confirm_replay = True
wait_command = 10
no_colors = False
priority = {'sudo': 100, 'no_command': 9999}
debug = False
history_limit = 9999
wait_slow_command = 20
slow_commands = ['react-native', 'gradle']
num_close_matches = 5
instant_mode = False
repeat = False
edit = False
explain = False
env = {'LC_ALL': 'C', 'LANG': 'C'}
Or via environment variables:
THEBLEEP_RULES— list of enabled rules, likeDEFAULT_RULES:rm_rootorsudo:no_command;THEBLEEP_EXCLUDE_RULES— list of disabled rules, likegit_pull:git_push;THEBLEEP_REQUIRE_CONFIRMATION— require confirmation before running new command,true/false;THEBLEEP_CONFIRM_REPLAY— ask before running your previous command again to read its output,true/false;THEBLEEP_WAIT_COMMAND— the max amount of time in seconds for getting previous command output;THEBLEEP_NO_COLORS— disable colored output,true/false;THEBLEEP_PRIORITY— priority of the rules, likeno_command=9999:apt_get=100, rule with lowerprioritywill be matched first;THEBLEEP_DEBUG— enables debug output,true/false;THEBLEEP_HISTORY_LIMIT— how many history commands will be scanned, like2000;THEBLEEP_ALTER_HISTORY— push fixed command to historytrue/false;THEBLEEP_WAIT_SLOW_COMMAND— the max amount of time in seconds for getting previous command output if it inslow_commandslist;THEBLEEP_SLOW_COMMANDS— list of slow commands, likelein:gradle;THEBLEEP_NUM_CLOSE_MATCHES— the maximum number of close matches to suggest, like5.THEBLEEP_REPEAT— if the corrected command fails too, correct that as well,true/false.THEBLEEP_EDIT— hand the correction to your command line to edit instead of running it,true/false.THEBLEEP_EXPLAIN— say which rule made each suggestion and what it matched,true/false.THEBLEEP_INSTANT_MODE— read what scrolled past instead of running your command again,true/false; see Experimental instant mode.THEBLEEP_EXCLUDED_SEARCH_PATH_PREFIXES— path prefixes to ignore when searching for commands, by default[].
For example:
export THEBLEEP_RULES='sudo:no_command'
export THEBLEEP_EXCLUDE_RULES='git_pull:git_push'
export THEBLEEP_REQUIRE_CONFIRMATION='true'
export THEBLEEP_WAIT_COMMAND=10
export THEBLEEP_NO_COLORS='false'
export THEBLEEP_PRIORITY='no_command=9999:apt_get=100'
export THEBLEEP_HISTORY_LIMIT='2000'
export THEBLEEP_NUM_CLOSE_MATCHES='5'
Back to Contents
Third-party packages with rules
If you'd like to make a specific set of non-public rules, but would still like
to share them with others, create a package named thebleep_contrib_* with
the following structure:
thebleep_contrib_foo
thebleep_contrib_foo
rules
__init__.py
*third-party rules*
__init__.py
*third-party-utils*
setup.py
The Bleep will find rules located in the rules module.
Back to Contents
Experimental instant mode
Correcting a command means knowing what it printed, which normally means running it again — the reason The Bleep asks first. Instant mode takes the other way out: it records your session with script as it happens and reads the log, so the previous command never runs twice and the question never comes up. It is the better answer where it works, and it is also the faster one.
Currently, instant mode only supports bash and zsh. zsh's autocorrect function also needs to be disabled in order for thebleep to work properly.
To enable instant mode, add --enable-experimental-instant-mode
to the alias initialization in .bashrc, .bash_profile or .zshrc.
For example:
eval $(thebleep --alias --enable-experimental-instant-mode)
What it does, and where it stops
It is called experimental because it is, and it is worth being specific about which parts. This is what a real terminal was driven through, on bash 5.2 and zsh 5.9:
| A correction with no rerun and no question | works |
| After ctrl+c, or a window resize | works |
| Unicode in the output | works |
| Megabytes of output, wrapping the recording several times | works |
| Megabytes of Unicode output, wrapping mid-character | works |
Output that was never text at all (a cat of a binary) |
works |
After a full-screen program (less, vim, top) |
does not correct |
| After a shell started inside the shell | does not correct |
The two Unicode rows are one row in the tests and were two bugs until this
release: the recording is a ring, so reading the last megabyte of it begins at
whatever byte is a megabyte back, and that is inside a character as often as the
output has multibyte characters in it. Decoding it raised, and the traceback came
out of the middle of a correction.
tests/output_readers/test_read_log.py
holds every offset into that seam.
The last two rows are the same limitation. What is recorded is the raw terminal
stream, and where one command's output ends is worked out by looking for a mark
that instant mode puts in your PS1. A program that takes over the screen moves
the cursor wherever it likes and the marks stop lining up with what is on it; a
nested shell writes a second set of them. For the same reason it needs your
PS1 to still contain that mark, so a prompt framework that rebuilds PS1
after the alias is set up — powerlevel10k, starship, some oh-my-zsh themes —
switches instant mode off, with a warning saying so.
Where it does not work it does not go wrong: the mark is missing, or the command has scrolled out of the recording, and instant mode is simply not in play for that correction — which takes the ordinary route and asks before it runs anything again. Falling back gains nothing: the question is the same question, and the same short list of programs that only ever read is what skips it.
What is fixed rather than documented:
- The recording is yours alone. It used to be created world-readable in
/tmp— a megabyte of everything that had scrolled past, which is the contents of every file you read, every token a command printed, and every password typed at a prompt that echoes. It is mode 0600 now, in$XDG_RUNTIME_DIRwhere there is one, created withO_EXCLandO_NOFOLLOWso a name somebody else got to first is refused rather than opened. - It goes when the session goes. Closing the terminal used to leave the
recording, the logger and the shell inside it running for the rest of the
login session. The logger removes its own recording on the way out however it
leaves, and the shell that started it has a
trapas a backstop forSIGKILL. - No more holes in it. When the recording filled up, the chunk that overflowed was dropped rather than written after the room was made, so a busy session lost up to a kilobyte of output every time it wrapped.
- The terminal is put back. A shell that exited normally used to leave your terminal in raw mode.
Back to Contents
Performance
The numbers are at the top, and they are meant to be
checked rather than believed. Same machine, same Python, 30 runs each, medians,
measured with the harness in bench/; the run they come from
is committed as bench/results/final.json, and the
chart at the top is written from that file by
bench/chart.py, so the two cannot drift apart.
The shell startup row is the eager alias, eval "$(thebleep --alias)", which
starts an interpreter every time you open a shell. The loader is the row that is
not in the table, because there is nothing to time: it is five lines of shell
that define a function, and the interpreter starts the first time you use the
alias instead. Timing it against a shell with nothing configured at all comes out
inside the run-to-run spread of shell startup, which is the honest answer rather
than a number.
Reproduce it yourself:
./bench/setup_subjects.sh python3.11 # builds both, from their own packages
BENCH_CPU=2,3 ./bench/bench.py --runs 30 \
--subject fuck=bench/.venvs/fuck-3.11/bin/thefuck \
--subject bleep=bench/.venvs/bleep-3.11/bin/thebleep
Python 3.11 is used for the comparison because The Fuck cannot start on 3.12
or newer — it imports distutils, which is no longer in the standard library.
On this machine the interpreter itself costs 9 ms before either app runs a line,
so that is the floor both are measured against. The environment block in the
result file records the commit it was measured at, the kernel, the CPU and the
harness's interpreter, so git show is what says which source those numbers
belong to.
Where the time went:
- Rules are compiled once, not on every command. The compiled rules live in a cache keyed by the interpreter and the rule files' timestamps.
- Most rules are never loaded. A rule that declares
@for_app('git', ...), or whose match needs a particular string in the output, cannot match yourbrew install— and that is readable from the rule's syntax tree without running it. A typical command now reaches about a fifth of the 174 rules, and one for a tool with many rules of its own —git— under a quarter, instead of all of them. Rules that don't say what they are about are always loaded, so this makes corrections faster, never fewer.tests/test_performance.pyfails if dispatch goes broad again. - Startup imports almost nothing, and so does a correction.
pyte,psutil,argparse,pprintand the five shells you are not using never arrive at all;ast,pickle,socket,uuid,tempfile,shutil,subprocess,difflibandctypesarrive only on the paths that use them — roughly half of what a correction used to open.tests/test_performance.pynames every module that has to stay out and holds the total to a budget, measured on whatever machine it is running on: the absolute count depends on the interpreter and on how the package was installed, so it is a test rather than a number here. This matters most on Windows, where every module is a file a virus scanner reads before the interpreter may map it. - The failed command's output is read while it runs. It used to be read after the command exited, which deadlocks as soon as the output fills the pipe buffer: anything printing more than about 64KB waited out the full timeout and then produced nothing to correct from. That is the 28.4× row above, and it is a correctness fix as much as a speed one.
- Nothing is scanned twice. The list of everything on your
$PATHis remembered until a directory on it changes.
If a cache ever gets in your way, thebleep --clear-cache removes them all,
and THEBLEEP_NO_RULE_PACK=true turns the rule cache off entirely.
On Windows
The Fuck has been called slow on Windows for years, and the reason is not either tool's own logic. Windows charges for opening files, and a Python module is a file the interpreter has to find and then open — with a virus scanner reading it first. So the work was to open fewer of them, which is the module list above, and it is the change that matters most here.
There are no numbers in this section, and that is deliberate. The Linux table at
the top comes from a committed result file with the machine, the kernel, the CPU,
the interpreter and the source commit recorded in it, produced by a harness in
this repository that anybody can run. Nothing equivalent exists for Windows: the
GitHub runner is Windows Server rather than a desktop with Defender in its
default configuration, and a figure measured once on somebody's laptop with no
artifact behind it is marketing rather than evidence. If you would like the same
table for Windows, bench/bench.py runs there —
bench/README.md says how — and a recorded run would be a
welcome pull request.
What is checked on Windows, on every push: the whole test suite on Python 3.9
through 3.14, and the correction loop end to end in real Windows PowerShell 5.1
and PowerShell 7, because the two do not agree about command chaining. The
import budget in tests/test_performance.py is
enforced there as well as everywhere else, which is what stops the thing that
made it slow from coming back.
Two costs are left, and neither belongs to either tool: an interpreter takes several times longer to start on Windows than on Linux, and the failed command still has to be run a second time to see what it printed. Both tools pay both.
Back to Contents
Developing
See CONTRIBUTING.md
License MIT
Project License can be found here.
Back to Contents
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file thebleep-4.0.2.tar.gz.
File metadata
- Download URL: thebleep-4.0.2.tar.gz
- Upload date:
- Size: 270.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
83b8c34e5635a84a68b9385a34dc827436e9755c94ebd8cc8b7dd3ca604e03a9
|
|
| MD5 |
5b2f7d1302ba7caafc35607bb4468754
|
|
| BLAKE2b-256 |
5f2f09f60aac77693e03fedf145e14fcd768d9faf601a705f991132aae9f356a
|
Provenance
The following attestation bundles were made for thebleep-4.0.2.tar.gz:
Publisher:
release.yml on stamparm/thebleep
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
thebleep-4.0.2.tar.gz -
Subject digest:
83b8c34e5635a84a68b9385a34dc827436e9755c94ebd8cc8b7dd3ca604e03a9 - Sigstore transparency entry: 2517981744
- Sigstore integration time:
-
Permalink:
stamparm/thebleep@0fd13cf1b3e5f8b99e819b1703fb7efdef8cc89b -
Branch / Tag:
refs/tags/4.0.2 - Owner: https://github.com/stamparm
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@0fd13cf1b3e5f8b99e819b1703fb7efdef8cc89b -
Trigger Event:
push
-
Statement type:
File details
Details for the file thebleep-4.0.2-py3-none-any.whl.
File metadata
- Download URL: thebleep-4.0.2-py3-none-any.whl
- Upload date:
- Size: 231.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d001b43cf3507fd73d29e21a75b8cad30e48ad10c743df65d4134c54cf76ba4d
|
|
| MD5 |
6216616997c4722603599fe7b4cea3cb
|
|
| BLAKE2b-256 |
a6f1c052d4b695d29b841c9152a1a9628e9339a79762a0f8715376e5fde1d590
|
Provenance
The following attestation bundles were made for thebleep-4.0.2-py3-none-any.whl:
Publisher:
release.yml on stamparm/thebleep
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
thebleep-4.0.2-py3-none-any.whl -
Subject digest:
d001b43cf3507fd73d29e21a75b8cad30e48ad10c743df65d4134c54cf76ba4d - Sigstore transparency entry: 2517981949
- Sigstore integration time:
-
Permalink:
stamparm/thebleep@0fd13cf1b3e5f8b99e819b1703fb7efdef8cc89b -
Branch / Tag:
refs/tags/4.0.2 - Owner: https://github.com/stamparm
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@0fd13cf1b3e5f8b99e819b1703fb7efdef8cc89b -
Trigger Event:
push
-
Statement type: