What It Does

Answering “is this job loaded, has it ever actually run, and did it fail?” normally means launchctl print gui/501/com.example.thing and reading a hundred lines of output. Writing a new job means hand-authoring XML and knowing that Weekday counts Sunday as both 0 and 7.

Kairos shows every agent in ~/Library/LaunchAgents with its schedule in English — “18:30 on Monday, Wednesday, Friday”, not Minute => 30, Hour => 18 — along with how many times it has run, its last exit code, and its own output tailed from wherever it writes.

Load, unload, enable, disable, run now, edit, reveal, delete.

Four States, Not Two

Most tools show one on/off switch, which hides the difference between off and off for a reason you forgot months ago.

Kairos shows loaded, disabled, running and failed as distinct states. A job that has never once fired looks exactly like a healthy idle one until you see the count is zero.

The Trap It Exists to Surface

launchctl disable writes to a database that is separate from the plist, and survives deleting the file. A job can be disabled by a command you ran months ago, and nothing in ~/Library/LaunchAgents will tell you. You load it, launchd says nothing, and it never runs.

Kairos reads that database separately and shows Disabled as its own state, with the explanation attached.

Scheduling an App

The second creation mode, and the one no generic plist editor has. Pick an app, pick a launch time and a quit time; Kairos writes both jobs and presents them as one thing.

The launch and the quit have independent days, so a schedule can span them: launch Friday at 16:00, quit Monday at 09:00, and the app runs across the whole weekend.

An app schedule shows four states from two independent facts — whether it is due to run, and whether the app is running:

The app is…due to runnot due
runningRunningRunning, outside its schedule
not runningShould be running — it is notNext run Mon 09:00

Only one of those means anything is wrong. Telling you an app should be running is guesswork when the answer is available for the asking; telling you it should be and is not is the thing worth interrupting you for.

Keeping an App Running

Deliberately not launchd’s KeepAlive. That key on the launch job would watch open — which exits the instant the app is up — and relaunch it in a tight loop forever. “Keep this process alive” and “keep that application running” are different requests, and only the second is ever what anyone means.

Instead the launch job opens a window: a marker saying the app is due to be running. The quit job clears it. A small guard checks on an interval and restarts the app only if the marker is present and the app is not. No clock arithmetic, survives reboots, and multi-day schedules fall out for free.

Two Details It Gets Right on Your Behalf

The Dock Icon Tells the Time

While Kairos is running, its Dock icon is the real clock — redrawn on the minute, aligned to the minute boundary so it changes when the clock does rather than up to 59 seconds late.

Only while it is running: applicationIconImage belongs to the running application, so Finder and a quit app’s Dock tile still show the static icon. Apple’s own Clock behaves the same way, for the same reason.

Scope

User agents only. Not /Library/LaunchDaemons. Editing system daemons needs a privileged helper, which is a large security surface for a small utility — and more decisively, a LaunchDaemon cannot launch a GUI application into your login session at all, so for the thing this app exists to make easy, daemons are the wrong tool anyway.

Permissions

None required. Kairos reads and writes your own ~/Library/LaunchAgents and runs /bin/launchctl.

macOS raises a Login Items notification whenever a launch agent is added. That is the system telling you a background item appeared, and it will happen every time you create a schedule.

Updates

Auto-updates are handled by Sparkle. Kairos checks once a day in the background; use Check for Updates… to check on demand.

Installation

Two formats on every release — both signed and notarised, pick whichever suits:

Or install it with Homebrew: brew install --cask perpetualbeta/jorvik/kairos

Building from Source

Kairos uses Swift Package Manager. No Xcode project is required.

The build includes the shared release.mk from jorvik-release, which must be checked out beside the repository. macOS ships GNU Make 3.81, which cannot run these recipes, so the commands use gmake from Homebrew’s make.

  1. brew install make
  2. git clone https://github.com/PerpetualBeta/jorvik-release.git
  3. git clone https://github.com/PerpetualBeta/Kairos.git
  4. cd Kairos && gmake build
  5. open .build/Kairos.app

Requirements

macOS 14 (Sonoma) or later. Universal binary (Apple Silicon and Intel).