mklaunchd supports plugins via the same convention git uses — any
executable named mklaunchd-<command> found on PATH is treated as
a first-class subcommand. This document covers everything you need
to write, test, and distribute an mklaunchd plugin.
When mklaunchd receives a subcommand it does not recognise as a
built-in, it scans PATH for an executable named
mklaunchd-<command>. If found, it is invoked directly with all
remaining arguments passed through unchanged.
mklaunchd monitor io.uradical.myservice --interval 30
# mklaunchd finds mklaunchd-monitor on PATH
# executes: mklaunchd-monitor io.uradical.myservice --interval 30Plugins are completely independent binaries. They can be written in
any language — Swift, Go, Python, shell — as long as the result is
an executable on PATH.
mklaunchd passes the following environment variables to every plugin. These are stable across minor versions. Breaking changes to this contract constitute a major version bump.
| Variable | Description | Example |
|---|---|---|
MKLAUNCHD_UID |
Current user's uid | 501 |
MKLAUNCHD_DOMAIN |
Resolved launchd domain | gui/501 |
MKLAUNCHD_VERSION |
mklaunchd version | 0.1.0 |
MKLAUNCHD_PREFIX |
Plugin prefix string | mklaunchd- |
All existing environment variables from the parent process are also
inherited. PATH is unchanged.
Your binary must be named mklaunchd-<command> where <command> is
a single lowercase word or hyphenated phrase with no spaces:
mklaunchd-monitor correct
mklaunchd-log-rotate correct
mklaunchd-MyPlugin incorrect (uppercase)
mklaunchd-my plugin incorrect (space)
| Code | Meaning |
|---|---|
0 |
Success |
1 |
General failure |
64 |
Usage error (bad arguments) |
65 |
Data error (bad input) |
78 |
Configuration error |
Implement --help and print usage to stdout.
Implement --version and print your plugin's version to stdout.
If your plugin responds to --description with a single line of
text and exits 0, mklaunchd will display that line in
mklaunchd help alongside built-in commands. Keep descriptions
under 60 characters.
Where your plugin produces structured output, support --json to
emit it as JSON. This makes your plugin composable with jq and
consistent with mklaunchd's built-in commands.
Respect the NO_COLOR environment variable. If it is set, suppress
all ANSI colour codes in your output.
// Package.swift
let package = Package(
name: "mklaunchd-monitor",
platforms: [.macOS(.v13)],
dependencies: [
.package(url: "https://github.com/apple/swift-argument-parser", from: "1.3.0")
],
targets: [
.executableTarget(
name: "mklaunchd-monitor",
dependencies: [.product(name: "ArgumentParser", package: "swift-argument-parser")]
)
]
)// main.swift
import ArgumentParser
import Foundation
@main
struct Monitor: ParsableCommand {
static let configuration = CommandConfiguration(
commandName: "mklaunchd-monitor",
abstract: "Watch a launchd job and alert on state changes"
)
@Argument(help: "Job label to monitor")
var label: String
@Flag(name: .long, help: "Print description and exit")
var description: Bool = false
mutating func run() throws {
if description {
print("Watch a launchd job and alert on state changes")
return
}
let domain = ProcessInfo.processInfo.environment["MKLAUNCHD_DOMAIN"] ?? "gui/501"
print("Monitoring \(label) in \(domain)")
}
}swift build -c release
cp .build/release/mklaunchd-monitor /usr/local/bin/
mklaunchd monitor <label>package main
import (
"fmt"
"os"
)
func main() {
if len(os.Args) > 1 && os.Args[1] == "--description" {
fmt.Println("Watch a launchd job and alert on state changes")
os.Exit(0)
}
domain := os.Getenv("MKLAUNCHD_DOMAIN")
if domain == "" { domain = "gui/501" }
fmt.Printf("Monitoring in domain %s\n", domain)
}#!/bin/zsh
# mklaunchd-notify
case "${1:-}" in
--description) echo "Send a notification when a job changes state"; exit 0 ;;
--help) echo "Usage: mklaunchd notify <label>"; exit 0 ;;
--version) echo "mklaunchd-notify 0.1.0"; exit 0 ;;
esac
LABEL="${1:-}"
if [[ -z "$LABEL" ]]; then echo "Error: label required" >&2; exit 64; fi
osascript -e "display notification \"Job $LABEL changed state\" with title \"mklaunchd\""class MklaunchdMonitor < Formula
desc "Watch a launchd job and alert on state changes"
homepage "https://github.com/yourname/mklaunchd-monitor"
url "https://github.com/yourname/mklaunchd-monitor/archive/refs/tags/v0.1.0.tar.gz"
sha256 "..."
depends_on :macos
depends_on xcode: ["14.0", :build]
def install
system "swift", "build", "-c", "release", "--disable-sandbox"
bin.install ".build/release/mklaunchd-monitor"
end
test do
assert_match "Watch", shell_output("#{bin}/mklaunchd-monitor --description")
end
endbrew tap yourname/mklaunchd-monitor
brew install mklaunchd-monitor
mklaunchd monitor <label>mklaunchd-myplugin --description
mklaunchd-myplugin --help
mklaunchd-myplugin --version
mklaunchd help | grep myplugin
mklaunchd myplugin --help| Plugin | Command | Description |
|---|---|---|
| mklaunchd-monitor | mklaunchd monitor |
Watch jobs and alert on state changes |
Community plugins are listed in the mklaunchd wiki.
Open an issue at https://github.com/uradical/mklaunchd if you have questions about the plugin API or want to list your plugin in the wiki.