Hunch predicts your next shell command from your own history. It learns which commands tend to follow which, then shows the most likely next one as inline ghost text — accept it with a keystroke, or keep typing.
Local, statistical, zero telemetry. No LLM, no cloud, no accounts, and it gets better the more you use it.
zsh only, for now. Hunch needs a per-keystroke prediction path plus a ghost-text primitive; zsh's ZLE is the only shell scripting layer that gives a plugin both. See Shell support for why bash, fish, and PowerShell aren't there yet.
Hunch anticipates your git flow and hydrates the suggestion from your own
history — accept it with Right/End, or type over the ghost if you want
something slightly different:
Hunch learned mkdir → cd in one directory, then transferred that habit to a
brand-new subdirectory it had never seen. hunch why shows exactly which
context matched:
Setting up is one command, and hunch doctor verifies the whole install:
# Install
go install github.com/DerekCorniello/hunch@latest
# Set up the zsh integration (appends the source line to .zshrc)
hunch init --auto
# Restart your shell, and you're done.
# Hunch learns from every command. Predictions appear as you type.
# Start using it - predictions appear as ghost text
git clone https://github.com/user/repo.git
# ghost text: cd repo press Right or End to accepthunch init offers to import your existing ~/.zsh_history so you don't
start from nothing, and prints a self-test accuracy number computed from that
same history right after:
Self-test on your history: top-1 24%, top-3 31% (vs. 9% always guessing your most frequent command).
Run 'hunch eval' any time to recheck this against your live history.
After a few commands, Hunch learns your workflows:
git clone REPO -> cd STR
cargo build -> cargo run
ssh STR -> ssh STR
If a suggestion looks wrong, hunch why explains the scoring behind it
instead of asking you to trust it (see hunch why).
No Go toolchain required. Download the binary for your platform from the
latest release,
make it executable, and put it on your PATH:
# Linux (x86_64) - substitute your platform below
curl -L -o hunch https://github.com/DerekCorniello/hunch/releases/latest/download/hunch-linux-amd64
chmod +x hunch
sudo mv hunch /usr/local/bin/Available builds: hunch-linux-amd64, hunch-linux-arm64,
hunch-darwin-amd64, hunch-darwin-arm64, hunch-windows-amd64.exe,
hunch-windows-arm64.exe.
On macOS, Gatekeeper will block the binary on first run because it is
unsigned. Clear the quarantine attribute with
xattr -d com.apple.quarantine /usr/local/bin/hunch.
To upgrade later, run hunch update. It downloads the current release for
your platform and replaces the running binary in place, so no Go toolchain
is needed.
go install github.com/DerekCorniello/hunch@latestThe binary is built at ~/go/bin/hunch (or wherever $GOBIN points). Make sure it's on your PATH.
Build with a version string:
go install -ldflags "-X github.com/DerekCorniello/hunch/cli.Version=v0.1.0" github.com/DerekCorniello/hunch@latestOr from a local clone:
go install -ldflags "-X github.com/DerekCorniello/hunch/cli.Version=$(git describe --tags --always)" .Hunch requires no external runtime dependencies. The Go binary is fully static (SQLite is handled by modernc.org/sqlite, a pure-Go port - no CGO needed).
# Set up zsh integration and append the source line to .zshrc
hunch init --auto
# Without --auto, just prints the source line to add yourself
hunch init
# Prints: source /path/to/hunch/integrations/zsh/hunch.zshThe integration:
- Auto-starts the daemon when sourced
- Shows inline ghost text as you type, via a persistent
servecoprocess and zsh'sPOSTDISPLAY- accept with Right/End, cycle candidates with Alt-n / Alt-p - Captures each command's exit code and working directory, feeding the location-affinity and outcome-weighting signals
- Sends recorded commands to the daemon asynchronously (non-blocking)
- Uses the
HUNCH_BINenvironment variable to locate thehunchbinary (default:hunch) - Silently degrades if the daemon is unavailable
- Composes with
zsh-autosuggestionsand similar plugins regardless of load order (see Shell integration conflicts)
Hunch is zsh-only right now. This isn't an arbitrary limitation - it's that inline ghost text (a suggestion appearing after your cursor as you type, which you accept with a keystroke or type over) needs a shell scripting layer that exposes two things: a per-keystroke hook, and a place to draw text the shell itself won't try to execute. Whether a shell offers that varies a lot:
| Shell | Can a plugin do this? | Why / why not |
|---|---|---|
| zsh | Yes - this is what hunch uses | ZLE exposes both a per-keystroke hook and POSTDISPLAY, a variable built for exactly this. zsh-autosuggestions uses the same mechanism. |
| bash | No, not without a large add-on | Bash's line editor (GNU Readline) has no inline-suggestion concept at all. The only way in is ble.sh, which replaces Readline entirely with a pure-bash reimplementation - a heavy, non-default dependency. |
| fish | Not from outside fish | Fish already ships its own built-in autosuggestions, better than either of the above out of the box - but it doesn't expose a supported way for a third-party tool to feed suggestions into that mechanism. |
| PowerShell | Not from a script | PSReadLine has ICommandPredictor, an official, well-designed extension point for exactly this (used by Microsoft's own predictors) - but it requires a compiled .NET module, not a .ps1 profile script. This is the one gap that's genuinely just unbuilt, not blocked by the shell. |
Given that, hunch focuses on zsh doing this well - inline suggestions, ranked
cycling, acceptance feedback, all backed by real regression coverage - rather
than spreading thin across four shells with three different, lesser
experiences. A PowerShell ICommandPredictor module is the most plausible
next platform if there's demand; bash and fish would need their own
ecosystems to open a door that isn't open today.
If you're on bash, fish, or PowerShell: hunch daemon, hunch client,
hunch stats, hunch eval, and hunch why all still work from the CLI, you
just won't get the shell-integrated ghost text.
| Variable | Field | Default |
|---|---|---|
HUNCH_BIN |
Binary path | hunch (from PATH) |
HUNCH_SOCKET |
Unix socket path | ~/.cache/hunch.sock |
HUNCH_DB_PATH |
SQLite database path | ~/.local/share/hunch.db |
HUNCH_DAEMON_BIN |
Daemon binary path | (same as hunch) |
HUNCH_HALF_LIFE_HOURS |
Decay half-life | 720 (30 days) |
HUNCH_MAX_IDLE_DAYS |
Hard cutoff: forget a transition untouched for this many days, regardless of count | 90 (0 disables) |
HUNCH_ALPHA |
Additive smoothing | 0.5 |
HUNCH_BETA |
CWD-affinity boost strength | 0.75 |
HUNCH_GAMMA |
Failure-rate suppression strength | 0.5 |
HUNCH_DELTA |
Prior-outcome boost strength | 0.5 |
HUNCH_EPSILON |
Confirmed-acceptance boost strength | 0.5 |
HUNCH_MIN_CONFIDENCE |
Score a generalized match must reach to be shown | 0.10 |
HUNCH_MIN_COUNT |
Times a command must have been seen before it is suggested | 2 |
HUNCH_EXTRA_PARENTS |
Extra parent commands (comma-separated) | (none) |
HUNCH_IGNORE |
Extra regexes for sensitive commands to never record (comma-separated) | (none) |
HUNCH_LOG_LEVEL |
Log level | info |
Each scoring strength (beta/gamma/delta/epsilon) is a soft,
multiplicative adjustment that is the identity when its signal is absent; set
any to 0 to disable that signal.
half_life_hours and max_idle_days control two different things and are
intentionally separate. half_life_hours governs how fast a still-alive
transition's ranking influence fades - a command you ran 100 times legitimately
keeps some pull longer than a one-off, and that's by design. max_idle_days is
a flat, evidence-independent backstop: once you haven't touched something in
that many days, it's forgotten outright, no matter how much of a habit it used
to be. Set max_idle_days = 0 to disable the hard cutoff and rely on
half-life decay alone.
min_count is how many times you must have run a command in a given context
before hunch will suggest it. The default of 2 means one-off commands are
never suggested: hunch predicts habits, and a habit is repeated by definition.
This matters more than it sounds, because a command run once in a context you
never revisit is the only candidate for that state, so it scores as the most
confident suggestion possible on the least evidence possible. Set it to 1 to
be suggested everything, including things you ran once by accident.
min_confidence controls how readily hunch generalizes. An exact-context match
is always shown. When there is no exact match, hunch widens the context (drop
the directory, then drop the oldest command) and shows the result only if it
scores at least this high. Lower it to see suggestions more often at the cost
of more wrong ones; set it to 1 to suppress generalized suggestions entirely
and only ever show exact-context matches. Sensitive commands matching a built-in or
HUNCH_IGNORE pattern are never recorded (neither the transition nor the raw
command), so secrets are not persisted or suggested back.
Hunch looks for config.toml in the XDG config directory:
| OS | Config path |
|---|---|
| Linux | ~/.config/hunch/config.toml |
| macOS | ~/.config/hunch/config.toml |
| Windows | %AppData%\hunch\config.toml |
socket = "/run/user/1000/hunch.sock"
db_path = "/var/lib/hunch/hunch.db"
half_life_hours = 720
max_idle_days = 90 # forget anything untouched this long, regardless of count
alpha = 0.5
beta = 0.75 # CWD-affinity boost
gamma = 0.5 # failure-rate suppression
delta = 0.5 # prior-outcome boost
epsilon = 0.5 # confirmed-acceptance boost
min_confidence = 0.10 # bar for suggestions from a generalized context
min_count = 2 # ignore commands you have only run once
accept_keys = ["right", "end"]
extra_parents = ["mycli", "teamtool"]
ignore = ['(?i)--api-token'] # extra sensitive-command patterns to never record
log_level = "info"Precedence (lowest to highest): built-in defaults -> config file -> env vars -> CLI flags.
Set up the zsh integration.
--auto Automatically append the source line to .zshrc
When run interactively, hunch init detects your ~/.zsh_history and offers to import
it to jump-start predictions, then prints a self-test accuracy summary computed
from that same history (see hunch eval below). In non-interactive contexts
(piped, scripted), the prompt is skipped.
Import ~/.zsh_history as training data for predictions.
--path <file> History file path (default: ~/.zsh_history)
--threads <N> Number of normalize worker threads (default: CPU count)
Processes history by parsing commands, normalizing them into templates, building state transitions, and importing into the daemon as a seed.
Safe to re-run: counts combine by maximum, so importing the same history twice leaves the graph unchanged rather than doubling it. Worth doing after an upgrade that changes how transitions are recorded, since the import backfills context that older data does not have.
Measure how well hunch predicts your own history. Replays your shell history in order, predicting each command using only the commands before it, which is how the daemon sees your session as you work.
--path <file> History file path (default: ~/.zsh_history)
--warmup <n> Commands to learn from before scoring begins (default: 50)
Reports top-1, top-3, and top-5 hit rates, how often any suggestion was offered, and a baseline: the hit rate you would get by always guessing your single most frequent command. The baseline is the number worth comparing against, since a shell history dominated by one command can make a weak model look strong.
Explain the scoring behind a prediction instead of asking you to trust it. Runs the same fallback ladder the daemon uses for a real suggestion (exact directory, then ancestor directory, then no directory, then shorter history), reports which level answered, and breaks down each candidate's score into its components: time-decay weight, CWD affinity, prior-outcome affinity, acceptance rate, and failure rate.
--state <prev1,prev2> Prior commands (default: last 2 from ~/.zsh_history)
--cwd <dir> Directory to explain (default: current directory)
--prior-outcome <s> success, failure, or empty
--limit <n> Max candidates to show (default: 5)
With no flags, hunch why explains "what would hunch suggest right now, in
this directory, given what I just ran" - so after seeing a suggestion you
don't understand, running it plain is usually enough:
$ hunch why
hunch why
context matched: exact directory
cwd: /home/you/project
state: git add PATH -> git commit FLAG STR
gates: a suggestion needs count >= 2 always; a fallback context also needs score >= 0.10
# SCORE COUNT DECAY CWD PRIOR ACCEPT FAIL NEXT
1 0.342 12 0.91 80% - - 5% git push
2 0.201 4 0.75 - - - - git status
- means that boost is either disabled or has no data for this candidate, so
it left the score unchanged rather than pulling it up or down.
Scoring is only half the picture: a template like cd PATH still has to be
filled in with an actual directory, and sometimes more than one is plausible
(you've cd'd into two different repos recently, say). When that hydration
is genuinely ambiguous, hunch why shows what was considered:
cd PATH:
1. cd hunch 100%
2. cd my-other-project 74%
The same ambiguity is what live cycling (Alt-n/Alt-p) pages through in the shell - if hunch is confident about the shape but unsure which directory, you're not stuck with its first guess.
Manage the background daemon process.
| Action | Description |
|---|---|
run |
Run daemon in foreground (useful for debugging) |
start |
Detach and run daemon in background |
stop |
Stop the running daemon |
status |
Check if daemon is running (exit 0 = running) |
Send an IPC operation to the running daemon.
| Op | Description |
|---|---|
record |
Record a command transition |
predict |
Get next-command predictions |
explain |
Get the scoring breakdown behind a prediction (JSON; see hunch why for a formatted view) |
reset |
Wipe all learned data |
export |
Export the transition graph as JSON |
normalize |
Normalize a raw command to its template |
stats |
Show daemon stats (size, half-life, alpha) |
config |
Show active daemon configuration |
import |
Import a seed JSON file |
--state <prev1,prev2> Previous 1-2 commands (comma-separated)
--next <command> The command that was run
--at <timestamp> ISO 8601 timestamp
--state <prev1,prev2> Previous 1-2 commands (comma-separated)
--prefix <text> Current buffer text for filtering
--limit <n> Max suggestions (default: 3)
--state <prev1,prev2> Previous 1-2 commands (comma-separated)
--cwd <dir> Current working directory
--prior-outcome <s> success, failure, or empty
--limit <n> Max candidates to break down (default: 5)
Same context flags as predict, but returns the full ExplainResponse JSON
(level, gating thresholds, per-candidate breakdown) instead of just the
ranked suggestions. hunch why is this same request with human-formatted
output.
Check hunch installation and daemon health. Verifies:
- Binary location and PATH
- Daemon status
- Database file
- Shell integration source line
Returns exit code 0 if all checks pass, non-zero otherwise.
Remove hunch from your system. Stops the daemon, removes all data files (database, socket, logs, integrations, config), and removes the source line from all shell rc files.
--yes, -y Skip confirmation prompt
Check for and install updates. Queries GitHub for the latest release and, if a
newer version exists, downloads the binary built for your platform, verifies it
against the release's SHA256SUMS, and replaces the running executable in
place. No Go toolchain is required. The daemon is restarted automatically
afterwards.
Verification fails closed: if the checksum file is missing or the digest does not match, the download is discarded and nothing is installed.
The new binary is downloaded next to the current one and moved into place, so
the directory holding hunch must be writable. If it is not (for example
/usr/local/bin owned by root), re-run with elevated privileges. Platforms
with no published binary are told to reinstall from source instead.
For convenience, these shortcuts wrap common hunch client operations:
| Command | Equivalent |
|---|---|
hunch stats |
hunch client stats |
hunch predict [flags] |
hunch client predict [flags] |
hunch reset |
hunch client reset |
Example:
hunch predict --state "git add,git commit" --limit 5shell -> integration (thin adapter) -> daemon (background service) -> core/ (logic)
|
SQLite (WAL)
- core/ - Pure logic.
normalize(two-phase: unwrap wrappers, classify tokens),graph(transition counts),predict(additive-smoothed exponential decay scoring). Deterministic and stateless. - daemon/ - Background service. Owns SQLite, receives IPC events, calls core to update and predict. One request per connection over a Unix socket.
- cli/ - Admin interface. Routes to init/daemon/client subcommands. Links the full daemon package.
- integrations/ - Shell-specific adapters. Minimal shims that shell out to
hunch client. No learning logic.
See AGENTS.md for the full architecture and design decisions.
| Platform | Status |
|---|---|
| Linux (x86_64, aarch64) | Full support |
| macOS (x86_64, arm64) | Supported |
| Windows (x86_64) | Daemon/CLI supported (Unix domain sockets); shell integration needs zsh (e.g. via WSL) |
| Other Unix (FreeBSD, etc.) | Supported (flock, XDG paths) |
The daemon, CLI, and hunch client commands build and run everywhere Go
cross-compiles. The shell integration itself is zsh, so on native Windows
(PowerShell/cmd) you get the CLI and daemon but not inline ghost text unless
you're running zsh under WSL. See Shell support.
On Windows, you may need to exclude %LocalAppData%\hunch\ from Windows Defender
real-time scanning to avoid lock contention with the SQLite database.
The daemon starts automatically the first time you open a terminal after boot
(your shell rc file runs hunch daemon start). It then stays alive as a
detached background process across terminal sessions. This is sufficient for
normal interactive use.
If you need the daemon running without an interactive shell (e.g. tmux sessions that auto-start, CI, SSH invocations), install a user service for your platform:
Linux (systemd):
mkdir -p ~/.config/systemd/user
cat > ~/.config/systemd/user/hunch-daemon.service << 'EOF'
[Unit]
Description=Hunch daemon
After=network.target
[Service]
Type=simple
ExecStart=%h/go/bin/hunch daemon run
Restart=on-failure
RestartSec=5
[Install]
WantedBy=default.target
EOF
systemctl --user enable --now hunch-daemonmacOS (launchd):
mkdir -p ~/Library/LaunchAgents
cat > ~/Library/LaunchAgents/com.user.hunch-daemon.plist << 'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.user.hunch-daemon</string>
<key>ProgramArguments</key>
<array>
<string>PATH/TO/hunch</string>
<string>daemon</string>
<string>run</string>
</array>
<key>KeepAlive</key>
<true/>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>
EOF
launchctl load ~/Library/LaunchAgents/com.user.hunch-daemon.plistWindows (Task Scheduler):
$action = New-ScheduledTaskAction -Execute "hunch.exe" -Argument "daemon run"
$trigger = New-ScheduledTaskTrigger -AtLogOn
Register-ScheduledTask -TaskName HunchDaemon -Action $action -Trigger $triggerHunch overlaps with tools you may already run. It is designed to sit alongside them rather than replace them.
| Tool | What it answers | Relationship to hunch |
|---|---|---|
zsh-autosuggestions / fish autosuggestions |
"What did I type that started this way?" | Prefix search over history. It needs you to start typing; hunch suggests before you type anything, based on what you just ran. They compose. |
atuin |
"What did I run before, and where?" | A searchable history database with sync. It is recall on demand; hunch is a prediction offered unprompted. Different moments. |
fzf history widget |
"Let me hunt through history interactively." | Explicit search you invoke. hunch never requires an invocation. |
thefuck |
"That command failed, what did I mean?" | Corrects the command you just ran. hunch proposes the next one. |
The distinguishing idea is that hunch models the sequence: it learns that
cargo build tends to be followed by cargo run, and conditions that on the
directory you are in and whether the last command succeeded. Prefix-matching
tools have no notion of what typically comes next.
Two honest caveats. Predictions are templates hydrated with a concrete
past command, so hunch suggests things you have run before, not novel commands.
And it needs history to be useful: run hunch import-history to start from
your existing history rather than from nothing.
Measure it on your own history with hunch eval rather than taking
any of this on faith.
- No AI/LLM - purely statistical learning
- No cloud sync or telemetry
- No distributed system
- No multi-user graph merging
- No complex shell grammar parsing
- No daemon-less mode (the daemon is required)
-
Check if the daemon is running:
hunch daemon status
-
If not running, start it:
hunch daemon start
-
Verify shell integration is loaded:
hunch doctor
-
Check that your rc file sources the hunch integration script.
-
Check for stale socket file:
ls -la ~/.cache/hunch.sock -
Remove it if the daemon isn't running:
rm ~/.cache/hunch.sock hunch daemon start -
Check the log file for errors:
tail -f ~/.local/share/hunch/hunch.log
The binary isn't on your PATH. Either:
# Install globally
go install github.com/DerekCorniello/hunch@latest
# Or add to PATH
export PATH="$PATH:$(go env GOPATH)/bin"- Hunch needs time to learn your patterns. Run
hunch import-historyto jump-start from your shell history. - Check the graph size:
hunch stats - Reset and start fresh:
hunch reset
Hunch registers its zle hooks through add-zle-hook-widget (zsh 5.3+), which
keeps a list of widgets per hook and runs all of them. It composes with
zsh-autosuggestions and similar plugins regardless of load order, and adopts
any widget a plugin bound directly. On zsh older than 5.3 it falls back to
chaining a single previous widget.
Sourcing hunch after other plugins is still recommended: when two plugins both want to draw ghost text, the one that runs last is the one you see.
- Exclude
%LocalAppData%\hunch\from Windows Defender real-time scanning to avoid SQLite lock contention. - Ensure you're using Windows 10 version 1803 or later for Unix domain socket support.
Run hunch doctor for a comprehensive health check, or check the logs at ~/.local/share/hunch/hunch.log.
MIT. See LICENSE.


