stay is a tiny macOS CLI utility that keeps the user session active by posting real mouse movement events through Core Graphics.
It is designed as a transparent foreground process:
stayWhile the command is running, stay is active. Press Ctrl+C and it stops immediately.
stay is CLI-only. It does not install a .app, menu bar item, tray icon, daemon, launch agent, AppleScript, or any GUI component.
name: stay
type: cli-utility
platforms:
- macos
architectures:
- apple-silicon
- intel
language: go
install:
homebrew: brew install KarlinskyS/tap/stay
command: stay
purpose: prevent macOS user idle by posting real mouse movement events
input-method: Core Graphics mouse movement events
requires:
- macOS Accessibility permission
does-not-use:
- GUI app
- menu bar
- tray icon
- daemon
- launch agent
- AppleScript
- external GUI automation
runtime-model: long-running foreground terminal process until Ctrl+C, SIGINT, or SIGTERM
default-interval: 60s
default-distance: 1 display point
primary-use-cases:
- macOS idle prevention
- transparent CLI mouse mover
- foreground terminal alternative to GUI mouse mover appsSearch keywords: macOS idle prevention, mouse mover CLI, keep Mac awake by mouse movement, Core Graphics mouse event, Accessibility permission, Apple Silicon CLI, no GUI mouse mover, foreground terminal process.
If you are looking for a CLI alternative to a mouse jiggler, mouse mover, or GUI keep-awake app on macOS, stay is built for that use case. It keeps the behavior visible in a terminal, uses native Core Graphics mouse events, and avoids AppleScript or background agents.
Related search terms: macOS mouse jiggler CLI, mouse mover for Mac terminal, keep Mac session active, prevent macOS idle with mouse movement, no-GUI mouse mover, Apple Silicon mouse mover.
Some GUI mouse mover apps are hard to inspect, hard to debug, and awkward to run in controlled environments. stay keeps the behavior explicit:
- one terminal command starts it;
- terminal logs show every decision;
Ctrl+C,SIGINT, orSIGTERMstops it;- the cursor returns to its original position after every synthetic movement;
- there is no accumulated cursor drift.
The implementation is inspired by the general idea of periodic mouse movement tools, including automatic-mouse-mover, but no source code from that project is copied here.
- macOS.
- Apple Silicon or Intel Mac.
- Go 1.22+ if building from source.
- Accessibility permission for either:
- the terminal application that runs
stay; or - the installed
staybinary.
- the terminal application that runs
stay uses macOS ApplicationServices/Core Graphics APIs. It does not use AppleScript.
Recommended install method:
brew install KarlinskyS/tap/stayThis installs the stay command from the public Homebrew tap:
KarlinskyS/homebrew-tap
Then run:
stayYou do not need to install Go manually for the Homebrew flow. Homebrew builds stay from source and installs any build dependencies it needs.
Upgrade later:
brew upgrade KarlinskyS/tap/stayUninstall:
brew uninstall stayBuild and install into $HOME/.local/bin:
make build
make installMake sure $HOME/.local/bin is in your PATH:
export PATH="$HOME/.local/bin:$PATH"Install into /usr/local/bin instead:
sudo make install PREFIX=/usr/localstayDefault behavior:
- checks activity every
60s; - moves the cursor by
1display point; - skips synthetic movement if real mouse or keyboard activity happened during the interval;
- moves to a nearby valid on-screen point and then returns to the original cursor position.
Example output:
13:42:00 Stay started
Interval: 60s
Move distance: 1 display point(s)
Press Ctrl+C to stop
13:43:00 idle for 60s - cursor moved
13:44:00 user activity detected - skipped
13:45:00 idle for 60s - cursor moved
13:45:12 stopped
stay --interval 60s
stay --distance 1
stay --always
stay --verbose
stay --version
stay --help| Option | Description |
|---|---|
--interval |
Check interval. Accepts Go duration values such as 60s, 1m, 500ms. A bare integer is treated as seconds. |
--distance |
Cursor movement distance in macOS display points. Default: 1. |
--always |
Move every interval, even if user activity was detected. |
--verbose |
Print more detailed diagnostics. |
--version |
Print version and exit. |
--help |
Print usage and exit. |
Examples:
stay --interval 30s
stay --interval 5s --verbose
stay --distance 2
stay --alwaysOn first run, macOS may block synthetic input events until Accessibility permission is granted.
If permission is missing, stay exits with:
Stay requires Accessibility permission.
Open System Settings -> Privacy & Security -> Accessibility
and allow your terminal application or the stay binary.
To grant permission:
- Open
System Settings. - Go to
Privacy & Security. - Open
Accessibility. - Enable your terminal application, for example
Terminal,iTerm2,Ghostty,WezTerm, orAlacritty. - Run
stayagain.
If you run the installed binary directly and macOS shows the binary as the requesting process, allow stay itself.
Every interval, stay asks macOS how long it has been since the last input event.
If the user has been idle for at least the configured interval, stay:
- reads the current cursor position;
- reads the active display bounds;
- chooses a nearby valid point on any active display;
- posts a Core Graphics
kCGEventMouseMovedevent; - waits briefly;
- posts another mouse move event back to the original position.
This handles screen edges and multi-monitor layouts, including displays with negative coordinates.
Because synthetic movement itself resets the macOS idle timer, stay tracks its own last synthetic movement and distinguishes it from real user activity.
Run with a short interval while testing:
stay --interval 5s --verboseCheck macOS power assertions:
pmset -g assertionsAfter synthetic movement, macOS should report recent user activity, commonly through a UserIsActive assertion owned by WindowServer.
Useful checks:
stay --version
stay --help
make test
make buildThe install path is probably not in PATH.
Run directly:
$HOME/.local/bin/stayOr add it to your shell profile:
export PATH="$HOME/.local/bin:$PATH"Try the following:
- restart the terminal application;
- remove and re-add the terminal application in Accessibility settings;
- if launching
/usr/local/bin/stayor$HOME/.local/bin/staydirectly, allow the binary if macOS lists it separately; - run
stay --interval 5s --verboseand inspect the logs.
Remote desktop clients decide which local input events are forwarded into the remote session. stay can only generate local macOS mouse movement. The remote client must accept and forward that movement.
If installed into $HOME/.local/bin:
rm -f "$HOME/.local/bin/stay"If installed into /usr/local/bin:
sudo rm -f /usr/local/bin/stayYou can also remove the Accessibility permission from System Settings -> Privacy & Security -> Accessibility.
make test
make build
bin/stay --help
bin/stay --interval 5s --verboseProject layout:
.
├── cmd/stay/main.go
├── internal/activity
├── internal/cli
├── internal/mover
├── go.mod
├── Makefile
├── README.md
└── LICENSE
Manual smoke checklist:
staystarts a long-running foreground process.- Terminal output clearly shows that the process is active.
Ctrl+Cstops the process and logs shutdown.SIGTERMstops the process and logs shutdown.- Each actual synthetic movement is logged.
- Cursor returns to the original position after movement.
- Repeated movement does not accumulate cursor drift.
- Movement works on the primary monitor.
- Movement works on an additional monitor.
- Cursor near screen edges still moves to a valid on-screen point and returns.
- Missing Accessibility permission shows the documented error.
pmset -g assertionsor another system indicator sees user activity after synthetic movement.
Use stay only in ways that comply with your organization policies, contracts, and local rules. This tool is intentionally transparent so its behavior is easy to inspect and reason about.
MIT.