My personal collection of scripts for overhauling the Bash experience
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Yuki Otsuka d25cc941d4 Report a toolchain that arrived after the shell started
PATH is arranged while the shell starts, so installing elan into a
running session leaves ~/.elan/bin on disk and out of PATH, and doctor
simply did not list elan, with nothing to explain why. Remember every
directory a module asks for, including the ones that do not exist yet,
and let doctor name the ones that exist now but are not in this shell's
PATH, along with the two ways to pick them up.

The fzf stub matched with grep, whose own arguments then appeared in the
process list fkill was choosing from, so the kill test failed whenever
that pid had already gone. It matches in the shell now.
2026-09-18 00:10:34 +09:00
lib Report a toolchain that arrived after the shell started 2026-09-18 00:10:34 +09:00
modules Report a toolchain that arrived after the shell started 2026-09-18 00:10:34 +09:00
tests Report a toolchain that arrived after the shell started 2026-09-18 00:10:34 +09:00
.editorconfig Initial commit 2026-09-17 22:39:08 +09:00
.gitignore Initial commit 2026-09-17 22:39:08 +09:00
bash_prompt.bash Show a failing exit status in the prompt 2026-09-17 23:30:29 +09:00
bashrc Keep CDPATH from choosing where cd goes 2026-09-17 23:46:15 +09:00
env Keep CDPATH from choosing where cd goes 2026-09-17 23:46:15 +09:00
install.sh Keep CDPATH from choosing where cd goes 2026-09-17 23:46:15 +09:00
manage.sh Keep CDPATH from choosing where cd goes 2026-09-17 23:46:15 +09:00
README.en.md Report a toolchain that arrived after the shell started 2026-09-18 00:10:34 +09:00
README.md Report a toolchain that arrived after the shell started 2026-09-18 00:10:34 +09:00

bashra

日本語 | English

A collection of Bash settings and scripts. Source bashra/env from your shell configuration to load only the scripts enabled in that file. It covers the prompt, history, completion, directory movement, and file handling, along with Git, GitHub CLI, fzf, and the Lean 4, Haskell, Idris 2, Racket, and Chez Scheme toolchains. Every part can be switched on or off by itself.

Requirements

bashra is designed for Bash. Completion and fzf integration use common Linux installation paths. Git, GitHub CLI, fzf, bash-completion, direnv, and zoxide are optional; integrations activate when the corresponding tools are installed. The language toolchains (GHCup, Cabal, Stack, elan, Lake, pack, Idris 2, Racket, and Chez Scheme) are optional in the same way, and shortcuts and completion are configured only for the ones that are installed. The tests require Bash 4.4 or later and Git; the line editing checks use script, and are skipped without it.

Usage

Add this line to ~/.bashrc. Skip this step if it already loads bashra.

source "$HOME/.bashra/env"

Alternatively, run bash install.sh from this directory to back up your existing startup file and append the loader. Do not run the installer if you already have a handwritten loader. Keep this repository at the same location after installation. The repository's bashrc is a compatibility entry point that delegates to env. The installer refuses to edit symlinks. To edit the target file explicitly, use bash install.sh /path/to/bashrc.

Enable or disable scripts

Comment out a source line in env to disable a script; uncomment it to enable it. Scripts load in the order listed. Files are never loaded by directory discovery.

source "$BASHRA_ROOT/modules/10-shell.bash"
# source "$BASHRA_ROOT/bash_prompt.bash"

From a shell, the bashra command does the same work.

bashra list
bashra disable bash_prompt.bash
bashra enable bash_prompt.bash
bashra reload
bashra doctor
Command Action
bashra list Show every script and whether it is enabled
bashra enable SCRIPT / disable SCRIPT Switch a script, from the next Bash session
bashra reload Load the configuration again in the current shell
bashra doctor Report the load path, Bash version, script states, the tools that were found, and any PATH entry still missing
bashra root Print the directory bashra was loaded from
bashra help Show the usage message

PATH entries are added while the shell starts, so a tool installed afterwards is not visible to a shell that was already running. bashra doctor lists the directories a module asked for that exist but are not in PATH, and says what to do about them.

installed since this shell started, and not in its PATH:
  /home/yuotsuka/.elan/bin
Run bashra reload, or open a new shell, to use them here.

Tab completion offers the commands, and the switchable script paths after enable and disable. bashra doctor runs none of the tools; it reports only whether an executable is in PATH. bashra reload re-reads the configuration in the current shell, but a script switched off does not take back the aliases and functions it has already defined.

The management script edits the same file:

bash ~/.bashra/manage.sh list
bash ~/.bashra/manage.sh disable bash_prompt.bash
bash ~/.bashra/manage.sh enable bash_prompt.bash
bash ~/.bashra/manage.sh disable modules/20-integrations.bash

Managed declarations must use a standalone source "$BASHRA_ROOT/relative/path" line. Files under lib/ are a prerequisite of the modules, so they are listed as required and refused as a switch. Leading whitespace and a comment marker are supported. The manager changes only the matching declaration and preserves other settings. Unknown script names and duplicate declarations are rejected. To add a script, create its file and add a declaration in this format to env. All bundled scripts are currently enabled.

Changes apply to new Bash sessions. Disabling a script does not undo aliases, functions, or PATH changes in an existing shell. Interactive settings load once per session.

Scripts

Path Purpose
lib/core.bash Helpers shared by the modules (required, not switchable)
modules/00-path.bash Append ~/.local/bin to PATH without duplicates
modules/01-haskell.bash PATH, shortcuts, and completion for GHCup, Cabal, and Stack
modules/02-lean.bash PATH, shortcuts, and completion for elan and Lake
modules/03-idris.bash PATH, shortcuts, and completion for pack and Idris 2
modules/04-racket.bash PATH, shortcuts, and completion for Racket and raco
modules/05-chez.bash Chez Scheme detection, library path, and shortcuts
modules/10-shell.bash Aliases and mkcd
modules/11-options.bash Shell options such as autocd and globstar
modules/12-history.bash History size and append, multiline entries, timestamps, and expansion review
modules/13-navigation.bash Moving to an ancestor directory or a project root, and the directory stack
modules/14-files.bash Archive extraction, backups, scratch directories, and file search
modules/15-readline.bash Completion display, key bindings, and prefix history search
modules/16-editor.bash Defaults for EDITOR and VISUAL, plus e
modules/17-output.bash Pager defaults, colored output, and man rendering
modules/20-integrations.bash Optional bash-completion, fzf, direnv, and zoxide integration
modules/22-fzf.bash fzf pickers for directories, files, processes, and Git
modules/30-git.bash Git shortcuts, completion, and repository root navigation
modules/35-gh.bash GitHub CLI shortcuts for PRs, issues, and Actions
bash_prompt.bash Username, hostname, working directory, and exit status prompt
modules/40-git-prompt.bash Git branch and working tree status in the prompt
modules/80-bashra.bash The bashra command: list, switch, reload, and report
modules/90-local.bash Load ~/.alias and ~/.config/bashra/local.bash

PATH settings also apply to noninteractive shells that source env. Subsequent settings apply only to interactive shells. Use ll and la to list files, .. and ... to move up directories, and mkcd DIRECTORY to create and enter a directory. fzf integration loads both its key bindings and its ** completion, falling back to the output of fzf --bash when neither file is installed. When the last command failed, its exit status appears before the prompt character on the second line, and nothing appears when it succeeded. Bash restores that status around PROMPT_COMMAND, so a hook installed by another tool does not hide it, and the substitutions expanded earlier in the prompt, such as the Git information, return that status unchanged.

yuki@host ~/src/mathlib [main *]
1 $

Set NO_COLOR=1 or TERM=dumb to disable prompt colors. bashra itself changes neither PROMPT_COMMAND nor the DEBUG trap. When direnv or zoxide is installed, its own initialization runs, and those tools install a PROMPT_COMMAND hook of their own.

PATH settings live in one file per tool, and the logic they share is collected in lib/core.bash.

Everyday use

bashra prioritizes everyday shell comfort, with separate controls for the prompt, history, and completion.

  • Type a prefix such as git, then use the up and down arrows to find commands beginning with the text before the cursor. With an empty line, the keys navigate all history. This works in Emacs mode and Vi insertion mode.
  • Tab completion ignores case and displays candidates when a match is ambiguous. Pressing Tab on an empty line does not list every command in PATH.
  • Candidates are marked by file type and colored the way ls colors them when the terminal can show it. A list of more than 200 candidates is confirmed first.
  • Ctrl with the left and right arrows moves by words. Pasted text is inserted as it is, so a newline inside it does not run the line.
  • Multiline commands retain their line breaks in a single history entry, and history displays timestamps. History is appended on shell exit; sessions do not share new entries immediately.
  • A history expansion such as !! or !ssh is placed on the command line instead of running, so it can be read before Enter. One that fails is left there to be corrected.

The following Bash options are enabled. Each one is a Bash feature and can be turned off individually with shopt -u.

Option Behavior
autocd A bare directory name changes to it, echoing the command it runs
cdspell / dirspell Correct small spelling mistakes in cd and in completion
globstar Expand recursive patterns such as **/*.lean
extglob Accept extended patterns such as !(*.o)
checkjobs Warn on the first exit while jobs are still running
checkhash Look up a remembered command again when its path has changed
checkwinsize Update LINES and COLUMNS after the terminal is resized

History limits are set to HISTSIZE=10000 and HISTFILESIZE=20000. Bash assigns those two itself before any startup file runs, so a conditional default would never apply; choose other limits in ~/.config/bashra/local.bash, which loads last.

HISTCONTROL and HISTTIMEFORMAT are left to whatever they already are, because Bash does not set them. Set HISTTIMEFORMAT='' to hide timestamps. You can also override key bindings in that file.

bash ~/.bashra/manage.sh disable modules/11-options.bash
bash ~/.bashra/manage.sh disable modules/12-history.bash
bash ~/.bashra/manage.sh disable modules/15-readline.bash

Directory movement

Command Action
up Move to the parent directory
up 3 Move three levels up
up src Move to the nearest ancestor directory named src
proot Move to the project root, the nearest ancestor holding a marker
d dirs -v, the directory stack
pd / po pushd / popd

The markers proot looks for are the file names and globs in BASHRA_PROJECT_MARKERS. It starts with .git, and each language module adds the build files of its own ecosystem. Add your own from ~/.config/bashra/local.bash with bashra_project_marker flake.nix. To move to the Git root specifically, use groot.

Tab completion on up offers the names of the ancestor directories, quoting the ones that contain spaces. An unknown name or an extra argument reports an error and leaves the working directory unchanged.

bash ~/.bashra/manage.sh disable modules/13-navigation.bash

Editor

When EDITOR is unset, the first of nvim, vim, vi, and nano that is installed is used, and VISUAL follows it when it is unset too. e FILE opens $EDITOR, expanded when the alias runs, so a later change to EDITOR applies. A value set earlier, for example in ~/.config/bashra/local.bash, is kept.

bash ~/.bashra/manage.sh disable modules/16-editor.bash

Output and pager

PAGER and LESS are filled in only when unset. The LESS default is -F -R -i: quit when the output fits one screen, keep color escapes, and search without regard to case. grep, diff, ls, and ip gain --color=auto, which prints escapes to a terminal only, so piped output stays plain. man renders through less, so headings and option names are colored there too.

Nothing is colored when NO_COLOR is set or TERM is dumb, and LESS_TERMCAP_* values you set yourself are kept.

bash ~/.bashra/manage.sh disable modules/17-output.bash

File helpers

Command Action
extract ARCHIVE... Unpack by file name extension, keeping the archive
bak FILE... Copy to FILE.bak-TIMESTAMP with the original attributes
cdtmp Create a scratch directory, move into it, and print its path
ff PATTERN [DIR...] List paths whose name contains PATTERN, ignoring case

extract handles tar, zip, 7z, rar, gz, bz2, xz, and zst, and names the missing command before unpacking when one is not installed. bak refuses to overwrite an existing copy. ff is a plain find wrapper, so hidden directories are searched too.

bash ~/.bashra/manage.sh disable modules/14-files.bash

Language toolchains

PATH entries and project markers apply to non-interactive shells too, so a build started from an editor or a script uses the same tools as the prompt. Shortcuts and completion are configured only in an interactive shell, and only for the tools that are installed. Nothing is defined for a tool that is absent, no directory is added to PATH unless it exists, and an alias, function, or command that already has the name keeps it.

Module Toolchain
modules/01-haskell.bash GHCup, Cabal, and Stack
modules/02-lean.bash elan and Lake, for Lean 4
modules/03-idris.bash pack and Idris 2
modules/04-racket.bash Racket and raco
modules/05-chez.bash Chez Scheme

Haskell

~/.ghcup/bin and ~/.cabal/bin are prepended to PATH in that order.

Command Action
cb / cr / ct / crepl cabal build / run / test / repl
sb / sr / st / srepl stack build / run / test / repl

Completion for cabal uses cabal --list-options for the subcommand being typed, and falls back to file names when there is nothing to offer. Completion for stack and ghcup is generated the first time you press Tab. The markers *.cabal, cabal.project, stack.yaml, and package.yaml are registered for proot.

bash ~/.bashra/manage.sh disable modules/01-haskell.bash

Lean 4

~/.elan/bin is prepended to PATH. lean and lake are elan shims, so the version in use still comes from the project's lean-toolchain.

Command Action
lk lake
lkb / lkt / lkc lake build / test / clean
lke NAME lake exe NAME
lku lake update
lkcache lake exe cache get, fetching Mathlib's compiled oleans

Completion for elan is generated the first time you press Tab. lakefile.lean, lakefile.toml, and lean-toolchain are registered as proot markers, so one command returns from a directory such as Mathlib/Algebra/Group to the root.

bash ~/.bashra/manage.sh disable modules/02-lean.bash

Idris 2

~/.pack/bin and ~/.idris2/bin are prepended to PATH in that order.

Command Action
idr idris2
pk pack
pkb / pkt / pkr pack build / test / repl
pki / pkd pack install / install-deps
pku pack update-db, refreshing the package collection

Completion for pack and idris2 is generated on the first Tab when the installed build can print a completion script; otherwise file name completion stays in place. pack.toml and *.ipkg are registered as proot markers.

bash ~/.bashra/manage.sh disable modules/03-idris.bash

Racket

~/racket/bin, where the official installer puts a home installation, and the launchers raco pkg install writes to ~/.local/share/racket/VERSION/bin and ~/.racket/VERSION/bin are added to PATH. The home installation is searched first, and among versioned directories the newest one is.

Command Action
rkt racket
rktrl racket -il readline, a REPL with line editing
rr raco
rrm / rrt raco make / test
rrp / rrd / rre raco pkg / docs / exe

Completion for racket and raco is loaded from the shell-completion package when the installation includes it. info.rkt is registered as a proot marker.

bash ~/.bashra/manage.sh disable modules/04-racket.bash

Chez Scheme

The executable has a different name on each system, so chezscheme, chez-scheme, and chez are tried in that order, and scheme after them. Because other implementations answer to scheme as well, that name is accepted only next to scheme-script or petite, which Chez installs alongside it.

Command Action
chez The Chez Scheme executable that was found
chezs FILE Run a script with --script, without the banner or the REPL

When ~/.local/lib/chez exists and CHEZSCHEMELIBDIRS is unset, it becomes ~/.local/lib/chez:.. Chez searches only the working directory by default, so . stays in the list next to your own library directory. Expression editing and history are Chez's own, stored in ~/.chezscheme_history.

bash ~/.bashra/manage.sh disable modules/05-chez.bash

fzf pickers

These activate when fzf is installed. Cancelling a selection runs nothing and leaves the shell as it was.

Command Action
fcd [DIR] Choose a directory and move into it, skipping .git, .lake, dist-newstyle, and .stack-work
fe Choose files and open them in $EDITOR; Tab selects several
fkill [SIGNAL] Choose processes and signal them, TERM by default
fgb Choose a local branch and git switch to it
fgl [git log arguments] Choose commits and print their hashes

fgl writes the chosen hashes to standard output, so they can be passed on, as in git show $(fgl).

When FZF_DEFAULT_OPTS is unset it becomes --height=40% --layout=reverse --border --info=inline. With fd installed, FZF_DEFAULT_COMMAND and FZF_ALT_C_COMMAND use it. Previews use bat when it is installed and head otherwise; BASHRA_FZF_PREVIEW overrides the command.

bash ~/.bashra/manage.sh disable modules/22-fzf.bash

Git integration

Git integration activates when Git is installed and does not change global Git settings.

Command Action
g Run git, for example g fetch
gs Show the branch and a short status summary
gd / gds Show unstaged / staged changes
gl Show the latest 20 commits as a graph
ga Run git add, for example ga file.txt
gc Run git commit, for example gc -m "message"
gsw Run git switch, for example gsw -c feature
gb Run git branch
gsh Run git show
grs Run git restore, for example grs file.txt
gf / gpl / gp Run git fetch / pull / push
groot Move to the current worktree root

Shortcuts reuse Git completion definitions when available. The prompt displays repository status, for example [main >2 <1 + * ?].

Marker Meaning
>N / <N N commits ahead of / behind the locally recorded upstream
+ Staged changes
* Unstaged changes
? Untracked files
! Conflicts
merge / rebase / rebase/am / cherry-pick Operation in progress
detached:hash Detached HEAD

The prompt supports repositories without an initial commit and linked worktrees. It displays nothing outside a worktree and makes no network requests. Upstream comparisons use locally available information from the last fetch. Since the prompt runs git status each time, you can disable the display for large repositories:

bash ~/.bashra/manage.sh disable modules/40-git-prompt.bash
bash ~/.bashra/manage.sh disable modules/30-git.bash

The Git prompt module loads after the base prompt. It also works with custom prompts, but assigning PS1 afterward replaces the Git display as well.

GitHub CLI integration

This integration activates when gh is installed. Authenticate initially with gh auth login. bashra uses existing authentication and repository settings without changing them.

Command Action
gh-pr / gh-mine List PRs / PRs you authored
gh-pv [number] View a PR; defaults to the current branch
gh-pd [number] Show a PR diff
gh-ps Show the status of PRs relevant to you
gh-co [number] Check out a PR; select interactively if omitted
gh-checks [number] Show PR checks
gh-i / gh-iv number List issues / view an issue
gh-r / gh-rv [ID] / gh-rw [ID] List / view / watch Actions runs
gh-ci List Actions runs for the current branch
gh-browse Open the repository in a browser

Commands preserve the underlying gh arguments and exit codes. In detached HEAD, gh-ci filters by the current commit instead of the branch.

gh-pr --search 'label:"needs review"'
gh-pv 42 --repo owner/repo
gh-co 42
gh-checks --watch
gh-ci --workflow 'Build and Test' --limit 5
gh-rv 123456 --log-failed

The module loads Bash completion for gh itself, generated the first time you press Tab so that starting a shell never waits for gh; it does not register completion for the shortcuts. Shell startup and prompt rendering do not access the GitHub API. Network access and authentication are needed when running GitHub operations. To disable the module, run the following and open a new Bash session:

bash ~/.bashra/manage.sh disable modules/35-gh.bash

See the GitHub CLI manual for command details.

Extending

Personal settings belong in ~/.config/bashra/local.bash, which loads after the bundled scripts and can therefore override any alias or variable. To add a script, create the file and add a source "$BASHRA_ROOT/modules/NAME.bash" line to env.

Modules can use these helpers. All of them are defined in lib/core.bash, and every name starts with bashra_, so none of them shadows a command of yours.

Helper Behavior
bashra_have COMMAND Report whether a command exists, without running it
bashra_path_prepend DIR... Prepend the directories that exist, in the order given
bashra_path_append DIR... Append them the same way
bashra_first_file PATH... Print the first readable path; the caller sources it
bashra_alias NAME VALUE Define an alias only if the name is free
bashra_project_marker NAME... Register a file name or glob for proot, once each
bashra_lazy_completion CMD GENERATOR Run GENERATOR on the first Tab and install what it prints

A directory already in PATH is left where it is, and one that does not exist is not added.

Variable Contents
BASHRA_ROOT The directory bashra was loaded from
BASHRA_LOADED Records that the interactive settings are already loaded
BASHRA_PROJECT_MARKERS The markers proot looks for
BASHRA_PATH_REQUESTED The directories the modules asked to put on PATH
BASHRA_FZF_PREVIEW The preview command used by the fzf pickers
BASHRA_COMPLETION_GENERATORS The completions waiting for their first Tab
# ~/.config/bashra/local.bash
bashra_project_marker flake.nix dune-project
bashra_path_prepend "$HOME/opt/bin"
HISTSIZE=50000
alias ll='ls -alFh'

Uninstall

Remove the source line from your shell configuration and open a new Bash session. If you used the installer, remove the block from # >>> bashra >>> through # <<< bashra <<<.

Development and testing

Source files use UTF-8, LF line endings, and four spaces for indentation. Basic formatting settings are provided in .editorconfig. Run the following from the repository root:

bash tests/run.bash

To run a single suite:

bash tests/core.bash
bash tests/smoke.bash
bash tests/git.bash
bash tests/gh.bash

GitHub CLI tests use a stub and do not access or modify GitHub.