Commits
The NSFW mode added complexity (two message tables, an init function,
a Current global, a config field) for what was essentially a 1-place
message lookup. Collapsed the locales package to a single file with
a plain map and Msg() helper, dropped the nsfw config field, and
updated docs accordingly.
- Add --restart flag to CHANGELOG.md and readme.md
- Update comparison links for new version
Adds a new --restart flag that runs commands in an infinite loop,
restarting them 5 seconds after any non-zero exit code until they succeed.
Perfect for keeping services up indefinitely.
Features:
- --restart <command> for infinite restart-on-failure
- 5 second delay between restart attempts
- Mutually exclusive with --retry (pick your stamina)
- Full shell completion support
Implement a locale system that allows switching between SFW (default) and
NSFW message variants via config. All user-facing strings (~60 keys) are
now centralized in the locales package, making future i18n possible.
- Add internal/locales package with Messages type and Msg() function
- Extract all strings to sfw.go (current messages) and nsfw.go (explicit)
- Add nsfw bool config option to toggle between modes
- Update main.go to use locales.Msg() for all output
- Shell completions remain professional in both modes
- Use proper date format (January 20, 2026)
- Break long lines under 80 chars
- Remove .PP after .SH and .SS macros
The man page now gently (and repeatedly) suggests that --help and
shell integration are superior alternatives. Features thick innuendos,
playful commentary on user choices, and a "Final Thoughts" section
for those who made it to the end.
Full troff-formatted man page with all commands, options, examples,
and configuration. Pipe to man for formatted viewing.
- Prune removes all completed jobs (any exit code), not just successful ones
- When all jobs are pruned, the ID counter resets to 1 for a fresh start
- Auto-prune also updated to remove all completed jobs older than threshold
- Unknown flag rejection (--unknown, -x)
- Kill by specific ID and JSON output
- Retry with --id, --delay, and JSON output
- List --running filter
Prevents accidentally running things like `bj --typo` as a background job.
Snapshot-heavy testing philosophy: simpler tests that are harder for AI
to subtly break. Coverage badge updates on main via GitHub wiki.
- Add retry, kill, gc, list filters, shell integration to usage
- Add .github/bj.toml as documented example config (all commented out)
- Link to example config from readme
- Convention over configuration: only change what you need
Full transparency: this whole thing was vibe coded with Claude Opus 4.5.
- Convert static output tests to use golden files in testdata/
- Add -update flag to regenerate snapshots: go test -update ./...
- Add help text snapshots for all commands
- Add shell completion and init script snapshots
- Document snapshot workflow in readme Contributing section
Inspired by @giacomocavalieri's advocacy for snapshot testing
- 31 tests covering all commands and edge cases
- Test harness with isolated temp config dirs (BJ_CONFIG_DIR env var)
- Static output tests for help, completion, init
- Job lifecycle tests with real process spawning
- GC tests using manually crafted orphaned jobs in JSON
- Error case coverage for invalid flags/args
Also fixes --kill not marking job as completed (now sets exit_code -15)
- Add --ids flag for shell completions (no more jq shelling out)
- Fix --prune help text to reflect that logs are now deleted
- Warn when --delay used without --retry
- Add 5s grace period to --gc to avoid false positives on fresh jobs
Users shouldn't have to type anything awkward or worry about
their shell history looking suspicious.
Keep CLI intuitive with standard terminology. The innuendo-laden
messages already provide plenty of personality.
Shell startup is the perfect time for housekeeping. Now bj tidies up
orphaned jobs and expired history every time a new shell is opened.
Job IDs start at 1, so bj now rejects zero and negative values
with a friendly reminder of its expectations.
Control how long bj waits between attempts. Default is 1 second.
Use --delay=0 for no waiting, or higher values to give things time to cool down.
Filter your job history by status. See only what's actively going,
review the ruined ones, or check what finished successfully.
Also accepts --ruined as an alias for --failed.
When bj or the system crashes, jobs may be left in a "running" state forever.
--gc detects these ruined jobs (process gone but job still marked running)
and marks them as failed so they can be retried or cleaned up.
bj can now pull out of any running job with --kill. Stores PID when
spawning jobs and sends SIGTERM to the process group for clean termination.
--retry can now wrap any command for persistent attempts, not just
replay failed jobs. Also adds --id flag for targeting specific jobs.
- Remove redundant mutex from tracker (flock provides cross-process safety)
- Add job ID to log filenames to avoid collision during rapid sessions
- Prune now cleans up log files too, leaving no trace behind
- Consistent error wrapping throughout tracker
bj now supports edging - teasing the command over and over until it
finally succeeds:
- --retry: keep trying until success (no release until it's done)
- --retry=N: give up after N attempts (denial after the limit)
- Retries the most recent failure if no job ID given
bj won't stop until you're satisfied, or until you tell it to.
Add CHANGELOG.md to track what bj has been up to.
Link it from the readme so everyone can see bj's history.
MPL 2.0 keeps it file-level frisky - you can touch individual files and
share the love back, while still letting bj slip into larger proprietary
arrangements without commitment issues.
This repo sets the tone for future projects. When something feels right,
you stick with it.
- bj --list --help shows detailed help for --list
- Works for all commands: --list, --logs, --prune, --completion, --init
- --help flag works in any position
- Add --completion for fish/zsh tab completions
- Add --init for prompt integration (shows running job count)
- --init auto-installs completions on first run (smart write, only if changed)
- Fish setup is now just: bj --init fish | source
- Add --json flag that works with all commands
- Replace -l with --list (less confusing)
- Show relative timestamps ("5 mins ago") instead of absolute dates
Replace tabwriter with manual column width calculation to prevent
color codes from affecting padding. Also set auto_prune_hours default
to 24 and update readme with prune feature.
Add --prune to clear all done jobs manually. Add auto_prune_hours toml
option to automatically prune done jobs older than N hours on startup.
- Close log file handle after spawning child process (was leaking fd)
- Add flock-based file locking for cross-process safety on jobs.json
- Return ErrJobNotFound instead of silent nil in Complete()
- Add job history pruning (max 100 completed jobs)
- Quote selfPath in wrapper command to handle special chars
- Copy loop variable before returning pointer in Get()
Explain lightweight design: no daemon, shell handles work,
bj exits immediately after spawning.
- Dim gray for completed jobs (done or error)
- Red status cell for failed jobs
- bj <cmd>: run command in background
- bj -l/--list: show jobs table with status, duration, command
- bj --logs [id]: open log in configured viewer (latest if no id)
Spawn background shell process with Setsid for true detachment.
Redirect stdout/stderr to log file, update tracker on completion.
Track job metadata: id, cmd, pwd, start_time, end_time, exit_code, log_file.
Provides list/get operations sorted by start time.
Load/create ~/.config/bj/bj.toml with log_dir and viewer fields.
Uses BurntSushi/toml for parsing.
Adds a new --restart flag that runs commands in an infinite loop,
restarting them 5 seconds after any non-zero exit code until they succeed.
Perfect for keeping services up indefinitely.
Features:
- --restart <command> for infinite restart-on-failure
- 5 second delay between restart attempts
- Mutually exclusive with --retry (pick your stamina)
- Full shell completion support
Implement a locale system that allows switching between SFW (default) and
NSFW message variants via config. All user-facing strings (~60 keys) are
now centralized in the locales package, making future i18n possible.
- Add internal/locales package with Messages type and Msg() function
- Extract all strings to sfw.go (current messages) and nsfw.go (explicit)
- Add nsfw bool config option to toggle between modes
- Update main.go to use locales.Msg() for all output
- Shell completions remain professional in both modes
- Convert static output tests to use golden files in testdata/
- Add -update flag to regenerate snapshots: go test -update ./...
- Add help text snapshots for all commands
- Add shell completion and init script snapshots
- Document snapshot workflow in readme Contributing section
Inspired by @giacomocavalieri's advocacy for snapshot testing
- 31 tests covering all commands and edge cases
- Test harness with isolated temp config dirs (BJ_CONFIG_DIR env var)
- Static output tests for help, completion, init
- Job lifecycle tests with real process spawning
- GC tests using manually crafted orphaned jobs in JSON
- Error case coverage for invalid flags/args
Also fixes --kill not marking job as completed (now sets exit_code -15)
bj now supports edging - teasing the command over and over until it
finally succeeds:
- --retry: keep trying until success (no release until it's done)
- --retry=N: give up after N attempts (denial after the limit)
- Retries the most recent failure if no job ID given
bj won't stop until you're satisfied, or until you tell it to.
- Close log file handle after spawning child process (was leaking fd)
- Add flock-based file locking for cross-process safety on jobs.json
- Return ErrJobNotFound instead of silent nil in Complete()
- Add job history pruning (max 100 completed jobs)
- Quote selfPath in wrapper command to handle special chars
- Copy loop variable before returning pointer in Get()