Cloud backup and restore for Backblaze B2. Backs up directories with git-aware handling, executable permission tracking, and nightly runs via /etc/backup.d.
Requires uv to be installed. Credentials are read from /etc/backup (configurable).
cb [OPTIONS] <COMMAND>
| Option | Description |
|---|---|
-q, --quiet |
Warnings and errors only |
--dry-run |
Print b2 commands without executing them |
--config-file <FILE> |
Config filename (default: /etc/backup) |
store — Backup to bucketcb store <PATH> <BUCKET> [--dir <DIR>]
Uploads a local directory to a Backblaze B2 bucket. Git directories are handled specially: only .git/config is included. Directories containing a .no-backup file are skipped. Executable permissions are tracked and restored on download.
| Argument | Description |
|---|---|
PATH |
Local directory to back up |
BUCKET |
Destination bucket name (format: name:) |
-d, --dir <DIR> |
Subdirectory within the bucket |
restore — Restore from bucketcb restore <BUCKET> <PATH> [--chown <OWNER>:<GROUP>]
Downloads a Backblaze B2 bucket to a local directory, creating it if needed.
| Argument | Description |
|---|---|
BUCKET |
Source bucket name (format: name:) |
PATH |
Destination directory |
-c, --chown <OWNER:GROUP> |
Recursively set owner and group after restore |
run — Run nightly backupcb run [--path <PATH>] [--timestamp-file <FILE>]
Runs all .sh scripts in the backup directory in sorted order. The backup directory must have mode 700. Scripts are executed with /bin/sh.
| Option | Description |
|---|---|
-p, --path <PATH> |
Path to backup scripts (default: /etc/backup.d) |
-t, --timestamp-file <FILE> |
Write completion timestamp to this file; uploads to meta bucket if configured |
Credentials are stored in the config file (default /etc/backup). The BACKUP_QUIET environment variable suppresses output when set.
Manages scheduled macOS launch agents from ~/Library/LaunchAgents. Wraps launchctl to create, list, reload, and delete agents, and provides a run subcommand that captures output and emails it on completion (like traditional cron). macOS only.
cronpad [OPTIONS] <COMMAND>
| Option | Description |
|---|---|
-q, --quiet |
Warnings and errors only |
list — List scheduled agentscronpad list
Lists all StartCalendarInterval launch agents in ~/Library/LaunchAgents, showing their name, command, day of week, and scheduled time. Alias: ls.
new — Create a new agentcronpad new [AGENT]
Interactive wizard that creates a new scheduled launch agent. Prompts for service ID, command, day of week, hour, and minute, then writes the plist and loads it immediately. Alias: add.
| Argument | Description |
|---|---|
AGENT |
Initial service ID (optional; prompted if omitted) |
The au.glob. prefix is added automatically if the name contains no dot. The generated plist calls cronpad run <command> so that output is captured and emailed automatically.
delete — Delete an agentcronpad delete <AGENT>
Unloads and removes the agent's plist from ~/Library/LaunchAgents. Aliases: del, rm, unload.
| Argument | Description |
|---|---|
AGENT |
Agent name, plist filename, or full service ID |
reload — Load or reload an agentcronpad reload <AGENT>
Unloads (ignoring errors) and reloads an agent's plist. Use this after editing a plist by hand. Alias: load.
| Argument | Description |
|---|---|
AGENT |
Agent name, plist filename, or full service ID |
edit — Edit an agent's plist and reloadcronpad edit <AGENT>
Opens the agent's plist file in $EDITOR, then automatically reloads the agent once the editor exits. Equivalent to manually editing the plist and running cronpad reload.
| Argument | Description |
|---|---|
AGENT |
Agent name, plist filename, or full service ID |
start — Start an agent immediatelycronpad start <AGENT>
Triggers the agent to run once immediately via launchctl kickstart, without waiting for its next scheduled time.
| Argument | Description |
|---|---|
AGENT |
Agent name, plist filename, or full service ID |
run — Execute a command (used by scheduled agents)cronpad run <COMMAND>...
Runs a command with a standard PATH set, captures its combined stdout and stderr, and emails the output to the current user if the command produces any. The subject line follows the traditional cron format (Cron <host>: <command>), or Cron <host> FAILED: <command> on non-zero exit. The email is sent from "cronpad@<host>" <user@host>.
This subcommand is embedded in every plist created by cronpad new — it is not normally called directly.
| Argument | Description |
|---|---|
COMMAND |
Command to execute |
launchctl and ~/Library/LaunchAgents.au.glob.backup), the plist filename (e.g. au.glob.backup.plist), or the full service ID (e.g. gui/501/au.glob.backup).Plays an audio notification immediately or after executing a command. Uses a success sound (ding) when the exit code is zero, or an error sound otherwise. Preserves the wrapped command's exit code for pipeline use.
ding [COMMAND...]
If no command is given, plays the success sound immediately.
# play success sound immediately
ding
# run a command and play success or error sound based on exit code
ding make build
# chain with other commands (exit code is preserved)
ding rsync -av src/ dst/ && echo "done"
ding spawns a background process to play the sound so the exit code is returned immediately.Wrapper for running commands in Docker containers, simplifying cron execution. Handles volume mounts, container naming, cleanup, and optional host networking.
docker-run [OPTIONS] <IMAGE> <COMMAND>...
| Argument | Description |
|---|---|
IMAGE |
Docker image to use |
COMMAND |
Command to run inside the container |
| Option | Description |
|---|---|
-m, --mount <HOST:CONTAINER> |
Bind mount a host path into the container (repeatable) |
--hostname <HOSTNAME> |
Set container hostname |
--user <USER> |
Set user inside the container |
--random <SECONDS> |
Sleep a random duration (0 to SECONDS) before running |
--host-network |
Use host network stack |
--name <NAME> |
Container name (default: IMAGE.COMMAND) |
--run-in-container |
Execute in an already-running container with the same name, if present |
-q, --quiet |
Warnings and errors only |
# run a command in a container, mounting /data
docker-run --mount /data:/data myimage mycommand --arg
# exec into a running container if it exists, otherwise start a new one
docker-run --run-in-container myimage mycommand
# randomise start time for cron use
docker-run --random 300 myimage backup.sh
--rm (removed on exit).--tty --interactive is added automatically.IMAGE.COMMAND_BASENAME.Dotfiles manager that tracks configuration files in a git repository (~/.dotfiles) and creates symlinks from your home directory to the tracked files.
dot [OPTIONS] <COMMAND>
| Option | Description |
|---|---|
-q, --quiet |
Warnings and errors only |
add — Track a filedot add <HOME_FILE> [DOT_FILENAME]
Moves a file from your home directory into ~/.dotfiles, records it in the config, and creates a symlink back.
| Argument | Description |
|---|---|
HOME_FILE |
File to start tracking (must be under ~) |
DOT_FILENAME |
Override the name/path inside ~/.dotfiles |
diff — Show pending changesdot diff
Shows uncommitted changes to tracked dotfiles.
init — Initialise repositorydot init <REPOSITORY>
Creates ~/.dotfiles, initialises a git repo, and pushes to GitHub.
| Argument | Description |
|---|---|
REPOSITORY |
GitHub repo as username/repo or a git SSH URL |
list — List tracked filesdot list
Lists all tracked dotfiles and their symlink targets. Orphaned symlinks pointing into ~/.dotfiles are highlighted. Aliases: ls, st.
push — Push changes to GitHubdot push
Stages all changes, generates a commit message (using Claude if available), and pushes to origin.
remove — Stop tracking a filedot remove <DOT_FILE>
Removes a file from ~/.dotfiles management, restoring it as a regular file in ~. Accepts paths relative to ~, ~/.dotfiles, or the file's actual path. Aliases: rm, del.
| Argument | Description |
|---|---|
DOT_FILE |
File to remove from tracking |
update — Pull and re-linkdot update [--no-pull]
Pulls from git and ensures all symlinks from ~ to ~/.dotfiles are correct. Aliases: up, pull.
| Option | Description |
|---|---|
-n, --no-pull |
Skip the git pull step |
~/.dotfiles/dot.properties.FFmpeg and image wrapper with sensible defaults, progress tracking, and a filter DSL for querying media by attributes. Handles video/audio transcoding and JPEG conversion, optimisation, and resizing.
Requires ffmpeg and ffprobe to be installed.
ff [OPTIONS] <COMMAND> <FILES>...
| Option | Description |
|---|---|
-f, --filter <EXPR> |
Filter expression (see Filter DSL below) |
-q, --quiet |
Warnings and errors only |
--filter-help |
Print filter DSL syntax reference |
info — Show metadataff info [--recurse] [--print0] [--precise] [--metadata] <FILES>...
Prints a summary of each media file: dimensions, size, duration, fps, and codecs.
| Option | Description |
|---|---|
-r, --recurse |
Recurse into subdirectories |
-0, --print0 |
Print filenames only, separated by NUL (for use with xargs -0) |
-p, --precise |
Show precise values instead of rounded |
-m, --metadata |
Show embedded metadata tags |
hash — Generate perceptual or human-readable hashff hash [--rename] [<FILE>]
Computes a DCT-based perceptual hash of an image or video file, displayed as a human-readable word sequence. If no file is given, generates a random hash.
| Option | Description |
|---|---|
-r, --rename |
Rename the file to its hash value |
mp4 — Convert to MP4ff mp4 [--replace] [--bitrate <RATE>] <FILES>...
Converts video files to H265/AAC MP4. Skips files already in MP4 format.
| Option | Description |
|---|---|
-r, --replace |
Replace existing output |
-b, --bitrate <RATE> |
Maximum bit-rate (e.g. 2M) |
mp3 — Convert to MP3ff mp3 [--replace] <FILES>...
Converts audio files to MP3. Skips files already in MP3 format.
| Option | Description |
|---|---|
-r, --replace |
Replace existing output |
recode — Recode in placeff recode [--replace] [--replace-if-smaller] [--h264] [--bitrate <RATE>] <FILES>...
Re-encodes MP4 files (H265 by default) or optimises JPEG files in place.
| Option | Description |
|---|---|
-r, --replace |
Replace existing output |
-s, --replace-if-smaller |
Replace only if the result is smaller (MP4) |
--h264 |
Encode as H264 instead of H265 |
-b, --bitrate <RATE> |
Maximum bit-rate (e.g. 2M) |
crop — Crop video or imageff crop [--size <W:H:X:Y>] [--letterbox <PX>] [--pillarbox <PX>] [--preview] [--replace] <FILES>...
Crops video or JPEG images to a specified region. Without --size, auto-detects black bars. For JPEG images the crop is applied directly; --preview is only applicable to video.
| Option | Description |
|---|---|
-s, --size <W:H:X:Y> |
Explicit crop dimensions (width:height:x:y) |
--letterbox <PX> |
Remove top/bottom black bars of given pixel height |
--pillarbox <PX> |
Remove left/right black bars of given pixel width |
-p, --preview |
Generate a JPEG preview of the crop region (video only) |
-r, --replace |
Replace existing output |
fix — Fix file extension and issuesff fix [--replace] <FILES>...
Detects the true file format and corrects the extension. Also fixes filenames starting with - and corrects JPEG display aspect ratio (DAR) issues.
| Option | Description |
|---|---|
-r, --replace |
Replace existing output |
flip — Flip videoff flip <FILES>...
Flips video horizontally or vertically using stream copy (no re-encode).
jpeg — Convert to JPEGff jpeg [--replace] <FILES>...
Converts images to JPEG. Skips files already in JPEG format.
| Option | Description |
|---|---|
-r, --replace |
Replace existing output (removing original) |
join — Join videosff join --output <FILE> [--replace] <FILES>...
Concatenates multiple MP4 files into one. All inputs must have the same dimensions.
| Option | Description |
|---|---|
-o, --output <FILE> |
Output filename (required) |
-r, --replace |
Replace existing output |
levels — Auto-adjust colour levelsff levels [--strength <F>] [--no-colour-correction] [--replace] <FILES>...
Auto-adjusts colour cast and contrast for MP4 video or JPEG images. Applies grey-edge colour cast correction followed by auto-levels normalisation.
| Option | Description |
|---|---|
--strength <F> |
Normalisation strength, 0.0 to 1.0 (default 0.7) |
--no-colour-correction |
Disable automatic colour cast correction |
-r, --replace |
Replace existing output |
mono — Stereo to monoff mono [--replace] <FILES>...
Converts stereo audio to mono by mixing channels.
| Option | Description |
|---|---|
-r, --replace |
Replace existing output |
normalise — Normalise audio levelsff normalise [--check] [--replace] <FILES>...
Normalises MP3 audio loudness to EBU R128 targets (−13 LUFS integrated, −1.5 dBTP peak).
| Option | Description |
|---|---|
-c, --check |
Report current levels without modifying |
-r, --replace |
Replace existing output |
organise — Normalise and organise mediaff organise [--max-height <H>] [--optimise] [<FILES>...]
Normalises media files in bulk: fixes file extensions, converts images to JPEG, converts video to H265/AAC MP4, downscales tall media, optimises JPEGs, and renames each file to its perceptual hash. Defaults to the current directory if no files are given.
| Option | Description |
|---|---|
-m, --max-height <H> |
Downscale media taller than this height (default 1920) |
-o, --optimise |
Force recoding/optimising even if already processed |
ratio — Set display aspect ratioff ratio [--replace] <FILES>...
Corrects the display aspect ratio (DAR) metadata without re-encoding.
| Option | Description |
|---|---|
-r, --replace |
Replace existing output |
resize — Resize video or imageff resize [--width <W>] [--height <H>] [--replace] [--smaller] <FILES>...
Resizes MP4 video or JPEG images. One of --width or --height is required; the other is calculated to preserve aspect ratio.
| Option | Description |
|---|---|
-w, --width <W> |
Output width (must be divisible by 2) |
-h, --height <H> |
Output height (must be divisible by 2) |
-r, --replace |
Replace existing output |
-s, --smaller |
Replace only if the result is smaller |
rotate — Rotate videoff rotate <ANGLE> [--replace] <FILES>...
Sets the display rotation metadata for MP4 files (stream copy, no re-encode).
| Argument | Description |
|---|---|
ANGLE |
Rotation angle: 0, 90, 180, or 270 |
| Option | Description |
|---|---|
-r, --replace |
Replace existing output |
silence — Remove audioff silence [--replace] <FILES>...
Removes the audio track from video files (stream copy).
| Option | Description |
|---|---|
-r, --replace |
Replace existing output |
split — Split MP4ff split [--at <TIMESTAMPS>] [--chapters] [--replace] <FILES>...
Splits MP4 files at specified timestamps or by chapter markers.
| Option | Description |
|---|---|
--at <TIMESTAMPS> |
Comma-separated split points (seconds or hh:mm:ss) |
--chapters |
Split at chapter boundaries |
-r, --replace |
Replace existing output |
subs — Embed SRT subtitlesff subs <hard|soft> [--replace] <FILES>...
Embeds an .srt subtitle file (same name as the video) into the MP4.
| Argument | Description |
|---|---|
hard |
Render subtitles into the video (burned in) |
soft |
Embed as a selectable subtitle stream |
| Option | Description |
|---|---|
-r, --replace |
Replace existing output |
trim — Trim start or endff trim [--start <TIME>] [--end <TIME>] [--duration <TIME>] [--intro] [--outro] [--threshold <F>] [--quick] [--replace] <FILES>...
Trims video from a start time, to an end time or duration. --intro and --outro auto-detect and remove mostly-black sections at the start or end.
| Option | Description |
|---|---|
--start <TIME> |
Start time offset (seconds or hh:mm:ss) |
--end <TIME> |
End time (seconds or hh:mm:ss) |
--duration <TIME> |
Duration to keep |
--intro |
Auto-detect and remove black intro |
--outro |
Auto-detect and remove black outro |
--threshold <F> |
Black detection threshold 0.0–1.0 |
--quick |
Trim to nearest keyframe (fast but imprecise) |
-r, --replace |
Replace existing output |
trim-silence — Trim silence from MP3ff trim-silence [--replace] <FILES>...
Removes silence from the start and end of MP3 files.
| Option | Description |
|---|---|
-r, --replace |
Replace existing output |
Use -f / --filter to select files by attributes before processing.
| Field | Operators | Example |
|---|---|---|
bit_rate |
< <= = != >= > |
bit_rate > 5000 |
fps |
< <= = != >= > |
fps >= 25 |
duration |
< <= = != >= > |
duration < 5m |
size |
< <= = != >= > |
size > 100m |
width, height |
< <= = != >= > |
width >= 1920 |
ext |
= != in not in |
ext in mp4,mkv |
video_codec |
= != in not in |
video_codec = h264 |
audio_codec |
= != in not in |
audio_codec in aac,mp3 |
has_video, no_video, has_audio, no_audio, has_dar, no_dar, is_image, no_image, has_chapters, no_chapters
| Expression | Description |
|---|---|
tag:<name> |
File has a tag with the given name |
tag:* |
File has any metadata tag |
tag:<name> = value |
Named tag equals value (case-insensitive) |
tag:<name> != value |
Named tag does not equal value |
tag:<name> ~ value |
Named tag contains value (case-insensitive) |
tag:<name> !~ value |
Named tag does not contain value |
tag:* = value |
Any tag equals value |
tag:* ~ value |
Any tag contains value |
Tag names and values are case-insensitive. Values containing spaces must be quoted: tag:comment ~ "i like cheese".
and, or, not, parentheses for grouping.
5m, 1h30m, 01:30:00500m, 1g, 100kh264,h265test, "i like cheese" (quote values containing spaces)ff info *.mp4 -f 'fps >= 25 and no_audio'
ff mp4 videos/ -f 'video_codec in h264,hevc and duration > 1m'
ff info * -f 'is_image and size > 1m'
ff recode . -f '(video_codec = h264 or video_codec = vp9) and width >= 1920'
ff info * -f 'tag:comment ~ cheese'
ff info * -f 'tag:* ~ "i like cheese"'
ff info * -f 'not tag:comment'
All keywords, field names, tag names, and string values are case-insensitive. Field and flag names accept underscores or hyphens interchangeably (e.g. bit_rate and bit-rate are equivalent).
Run ff --filter-help for the full reference.
.part temporary file and renamed atomically on success.Searches YouTube for high-quality music videos, appending hd remastered to the query automatically. Returns up to 15 results with colour-coded titles.
find-hq-music <QUERY>...
| Argument | Description |
|---|---|
QUERY |
Search terms (joined with spaces) |
Each result is printed as a YouTube URL followed by the channel name and title. Titles are colour-coded:
Requires a YouTube Data API v3 key in ~/.config/glob-util/youtube.properties:
api-key=YOUR_KEY_HERE
find-hq-music li kwan point zero
Analyses staged or unstaged Git changes and generates a commit message using Claude AI. Presents the proposed message for interactive review before committing.
git-auto-commit [OPTIONS]
| Option | Description |
|---|---|
--cli |
Force use of the Claude CLI (even if an API key is configured) |
--api |
Force use of the Claude API |
-n, --no-verify |
Bypass pre-commit and commit-msg hooks |
-p, --push |
Push after committing |
--debug-prompt |
Print the prompt sent to Claude |
--debug-request |
Print the full JSON request sent to Claude |
--debug-response |
Print the full JSON response from Claude |
After generating a message, the following options are presented:
| Key | Action |
|---|---|
y |
Accept and commit |
n |
Abort |
r |
Reroll (regenerate with a smarter model) |
s / l |
Switch between single-line and multi-line format |
e |
Edit the message inline |
d |
Show the full diff |
f |
Show all changed files (when list is truncated) |
p |
Add extra context for the next generation |
Uses the Claude CLI by default. To use the API directly, set an API key in ~/.config/glob-util/claude.properties:
api-key=YOUR_KEY_HERE
Lists unpushed commits in the current repository, showing short SHAs and commit titles.
git-outgoing-list
No options. Discovers the repository from the current directory. macOS only.
Each unpushed commit is printed as a short SHA (9 characters, yellow) followed by the commit title.
a1b2c3d4e fix typo in README
f6e5d4c3b add new feature
Shows full diffs for all unpushed commits in chronological order, piped through less.
git-outgoing-review
No options. Discovers the repository from the current directory. macOS only.
git show --pretty=medium --color for each commit.less --raw-control-chars to preserve colour.less early) is handled gracefully.Lists open GitHub issues for the current repository with colour-coded output. Automatically discovers the repository from the current directory's git remote.
git-todo [OPTIONS]
| Option | Description |
|---|---|
-n, --new |
Open the new issue page in a browser |
Each issue is displayed as:
<url> #<number> [label] title
Requires a GitHub API token in ~/.config/glob-util/github.properties:
api-token=YOUR_TOKEN_HERE
macOS only.
Optionally, an s.glob.au API token for URL shortening in ~/.config/glob-util/s.glob.au.properties:
api-token=YOUR_TOKEN_HERE
Generates the GitHub URL for the current repository or the current commit. Automatically discovers the repository from the git remote; optionally opens in a browser.
git-url [OPTIONS]
| Option | Description |
|---|---|
-o, --open |
Open the URL in the default browser |
-c, --commit |
Output the URL for the current HEAD commit |
-q, --quiet |
Warnings and errors only |
macOS only.
# print repository URL
git-url
# print URL for current commit
git-url --commit
# open repository in browser
git-url --open
# open current commit in browser
git-url --commit --open
Manages vendored code from external git repositories, tracked in .gitvendor. Supports adding, checking for updates, diffing against upstream, and updating in place.
git-vendor [OPTIONS] <COMMAND>
| Option | Description |
|---|---|
--repo-path <PATH> |
Path to repository (overrides current directory) |
-q, --quiet |
Warnings and errors only |
add — Add a vendored repositorygit-vendor add <URL> <DST_PATH> [--path <SRC_PATH>] [--verbose]
Clones a repository and copies it (or a subdirectory) into the destination path.
| Argument | Description |
|---|---|
URL |
Git repository URL |
DST_PATH |
Local destination path |
-p, --path <SRC_PATH> |
Subdirectory within the source repo to vendor |
-v, --verbose |
Verbose output |
check — Check for updatesgit-vendor check
Reports which vendored repositories are out of date compared to their upstream.
diff — Show upstream diffgit-vendor diff [--no-pull] [--path <PATH>]
Shows the diff between the local vendored copy and the current upstream.
| Option | Description |
|---|---|
-n, --no-pull |
Skip pulling from source |
-p, --path <PATH> |
Check only the specified local path |
list — List vendored repositoriesgit-vendor list
Lists all vendored repositories with their local paths, source repos, and status. Alias: ls.
remove — Remove a vendored repositorygit-vendor remove <DST_PATH>
Removes a vendored repository from the local tree and from .gitvendor.
update — Update vendored repositoriesgit-vendor update [--force] [--path <PATH>] [--no-pull]
Pulls upstream changes and syncs them into the local vendored paths.
| Option | Description |
|---|---|
-f, --force |
Overwrite local modifications |
-p, --path <PATH> |
Update only the specified local path |
-n, --no-pull |
Skip pulling from source |
.git-vendored file is written in each vendored directory recording the source URL and commit.--force is used.Network connectivity checker with DNS resolution and ICMP ping or TCP port testing. Supports wait modes to poll until a host comes online or goes offline.
is-alive [OPTIONS] <HOSTNAME> [PORT]
| Argument | Description |
|---|---|
HOSTNAME |
Hostname or IP address to check |
PORT |
TCP port to test (uses ICMP ping if omitted) |
| Option | Description |
|---|---|
-n, --nameserver <NS> |
DNS nameserver to use (default: 1.1.1.1) |
-s, --system |
Use the system nameserver instead |
-r, --no-ping |
Resolve only, skip connectivity test |
-p, --no-resolve |
Skip DNS resolution, use hostname/IP directly |
-w, --wait |
Poll until the host responds |
--wait-offline |
Poll until the host stops responding |
-d, --wait-delay <SECS> |
Seconds between polls in wait mode (default: 5) |
-t, --times |
Show timestamps on each check |
-q, --quiet |
Warnings and errors only |
# check if host responds to ICMP ping
is-alive example.com
# check if TCP port 443 is open
is-alive example.com 443
# wait until host comes online, checking every 10 seconds
is-alive --wait --wait-delay 10 example.com
# wait until host goes offline
is-alive --wait-offline example.com
# resolve hostname only
is-alive --no-ping example.com
# use system DNS resolver
is-alive --system example.com
HOSTNAME trigger a reverse DNS lookup; the resolved hostname is displayed but the IP is used for connectivity testing.Sends Slack notifications when a Monit service changes status. Designed for cron use — silently skips if offline or Monit is not running.
monit-notify [OPTIONS] <URL>
| Argument | Description |
|---|---|
URL |
Slack incoming webhook URL (https://hooks.slack.com/...) |
| Option | Description |
|---|---|
--quiet |
Warnings and errors only |
--test |
Send a test notification and exit |
On each run, monit-notify queries the Monit HTTP API (port 2812) for current service statuses and compares them against the previous state stored in ~/.monit-notify. Any services whose status has changed since the last run trigger a Slack notification.
Notification format:
[hostname] service-name: status
*/5 * * * * monit-notify https://hooks.slack.com/...MP3 utilities.
Displays MP3 metadata (tags, duration, bitrate, sample rate) for files or directories. Supports recursion, summary statistics, and compact or detailed output.
Cleans MP3 filenames and synchronises ID3 tags from the filename. Normalises whitespace, dashes, and capitalisation; removes (Original Mix); converts year format to [YYYY] brackets. Sets title, artist, album, author, and year tags from the filename, and strips all non-core tags.
mp3 info [OPTIONS] <FILES>...
| Argument | Description |
|---|---|
FILES |
MP3 files or directories to inspect |
| Option | Description |
|---|---|
-r, --recurse |
Recurse into subdirectories |
-l, --long |
Long format: show all tags, bitrate, and sample rate |
-s, --summary |
Show a summary line after all files |
-o, --only-summary |
Show summary only, suppress per-file output |
Default (one line per file):
filename: 3:45 8.5m
Long (-l):
filename.mp3
Duration: 3:45
Size: 8.5m
Bit Rate: 320,000bps
Sample Rate: 44,100Hz
Artist: ...
Title: ...
...
Summary (-s or -o):
Total: 12 files 45:30 102m
.) are skipped.mp3 fix [OPTIONS] <FILES>...
| Argument | Description |
|---|---|
FILES |
MP3 files or directories to process |
| Option | Description |
|---|---|
-r, --recurse |
Recurse into subdirectories |
-y, --require-year |
Require a year in the filename; prompts interactively if missing |
-a, --album <ALBUM> |
Set album tag: a literal string, a directory path, or an MP3 file to copy the album tag from |
-f, --force |
Ignore filename validation errors and process the file anyway |
fix expects filenames in one of these forms:
Artist - Title [YYYY].mp3
Artist - Title.mp3
Tags are derived entirely from the filename stem. The artist field is also written to the author (lyricist) tag.
Before writing tags, fix renames the file if the stem needs cleaning:
-(Original Mix) suffix(YYYY) year suffixes to [YYYY] bracket format[YYYY] bracketTitle, Artist, Album, Author (lyricist), and YearFilenames are checked for disallowed patterns before processing:
feat. / featuringpres. / presents- separator (e.g. Artist - Title - Remix)Artist - Title format)Use -f / --force to skip validation and process the file regardless.
Encodes file modification times into filenames, or decodes them back. Useful for preserving original mtimes when files must be transferred through systems that don't preserve timestamps.
mtime-filename [OPTIONS] <FILES>...
| Argument | Description |
|---|---|
FILES |
Files or directories to process |
| Option | Description |
|---|---|
-e, --encode |
Encode only (skip files that are already encoded) |
-d, --decode |
Decode only (skip files that are not encoded) |
Without --encode or --decode, both operations are applied as appropriate: un-encoded files are encoded, and encoded files are decoded.
Encoded filenames have the form:
mtime(UNIX_TIMESTAMP)original-filename.ext
For example:
mtime(1700000000)photo.jpg
Encode: reads the file's mtime, prepends mtime(UNIX_TIMESTAMP) to the filename, and renames the file.
Decode: strips the mtime(...) prefix, sets the file's mtime to the encoded timestamp, and renames the file back to its original name.
Files are not overwritten if the target name already exists.
Installs and manages tools in /opt from PyPI, NPM, crates.io, or GitHub. Creates isolated environments (virtualenvs, Cargo builds), and manages /usr/local/bin symlinks.
opt [OPTIONS] <COMMAND>
| Option | Description |
|---|---|
-q, --quiet |
Warnings and errors only |
add — Install a toolopt add <SOURCE> [--name <NAME>] [--python-version <VER>]
Installs a tool from a package registry or GitHub. The source prefix determines the repository:
| Prefix | Repository | Example |
|---|---|---|
py: |
PyPI | py:black |
node: / npm: |
NPM | node:prettier |
rust: |
crates.io | rust:ripgrep |
| GitHub URL | GitHub releases | https://github.com/user/repo |
| (no prefix) | auto-detected | black |
If exactly one binary is installed, a symlink is created in /usr/local/bin automatically. Alias: install.
| Option | Description |
|---|---|
-n, --name <NAME> |
Override the tool name |
--python-version <VER> |
Python version to use for the virtualenv |
delete — Remove a toolopt delete <NAME>
Removes the tool directory and any /usr/local/bin symlinks. Aliases: del, uninstall, remove, rm.
link — Create symlinksopt link <SCRIPTS>...
Creates /usr/local/bin symlinks for the specified scripts within a tool. Alias: ln.
unlink — Remove symlinksopt unlink <SCRIPTS>...
Removes /usr/local/bin symlinks for the specified scripts.
list — List installed toolsopt list [--short] [--long]
Lists all tools managed by opt.
| Option | Description |
|---|---|
-s, --short |
Names only |
-l, --long |
Include repo, version, last-updated, and provided binaries |
update — Update toolsopt update [<NAME>]
Updates all tools (or a specific tool) to their latest versions. Aliases: up, fix.
/opt must be owned by the current user./opt/opt.ini.Plays an internet radio stream (Icecast/Shoutcast) or local MP3 files in the terminal with a live spectrum visualiser. Searches a station list by name, accepts a direct stream URL, or plays a local MP3 file or directory. Displays track titles, supports volume and pause, and reconnects automatically on disconnect.
radio [OPTIONS] [QUERY]...
radio --update
radio --control <COMMAND>
| Argument | Description |
|---|---|
QUERY |
Station name, direct stream URL, or path to an MP3 file or directory |
--volume <N> |
Initial volume, 0–100 (default: 100) |
--update |
Refresh the station list from remote sources |
--list |
List all stations |
--control <COMMAND> |
Send a command to the running instance |
Valid control commands: pause, resume, quit, status
Status responses: stopped, connecting, playing, paused
| Key | Action |
|---|---|
Space |
Pause / resume |
Play/Pause media key |
Pause / resume |
n |
Next track (file mode only) |
- |
Volume down |
= |
Volume up |
r |
Reconnect |
q / Esc |
Quit |
Ctrl-C |
Quit |
# search for a station by name
radio progressive
# multi-word search
radio liquid drum and bass
# play a direct stream URL
radio https://example.com/synthwave
# play a single MP3 file
radio ~/music/song.mp3
# play all MP3s in a directory (shuffled)
radio ~/music/albums/kraftwerk
# start at 50% volume
radio --volume 50 progressive
# refresh the station list
radio --update
# signal the first running instance
radio --control pause
radio --control resume
radio --control quit
radio --control status
Station lists are loaded from ~/.config/glob-util/radio.toml. The file must contain an [Audio Addict] section with a listen_key for DI.FM and Radio Tunes access. Custom stations can be added as additional sections:
["Audio Addict"]
listen_key = "your-listen-key"
["My Station"]
url = "https://example.com/stream"
description = "An example station"
keywords = "example test" # space delimited
.mp3 file or directory, it plays locally.n advances to the next track.--update; if no cache exists, an update runs automatically.radio instance creates $TMPDIR/radio.sock; later instances keep playing without a control socket.Runs a command within a macOS sandbox using layered profiles to restrict filesystem and network access, primarily for isolating AI agents such as Claude and Codex.
sandbox [OPTIONS] <COMMAND>...
| Argument | Description |
|---|---|
COMMAND |
Command to run |
| Option | Description |
|---|---|
-p, --profile <P> |
Custom profile to append (see Profiles below) |
Profiles are macOS sandbox policy files (.sb) stored in ~/.config/glob-util/sandbox/. Three layers are applied in order:
BASE.sb is overwritten on every run and exists only as a reference — do not edit it. It defines the core policy:
/usr, /bin, /opt, etc.)./tmp, and standard devices.Ancestors of the current working directory are granted read access so processes can traverse the path.
default.sb is created in ~/.config/glob-util/sandbox/ if it does not already exist, and is applied after BASE on every run. Edit it to add permissions that should always be available. The shipped default grants read/write access to common tool data directories:
~/.gitconfig, ~/.config/git~/.config/gh~/.claude, ~/.cache/claude~/.codex~/.cargo, ~/.rustup~/.cache/uv, ~/.local/share/uvA named profile can be appended after the default using --profile. The profile file must exist at ~/.config/glob-util/sandbox/<NAME>.sb.
sandbox --profile my-profile claude --dangerously-skip-permissions .
Use custom profiles to grant additional permissions for a specific agent or task without widening the default policy.
# run claude-code with default policy
sandbox claude --dangerously-skip-permissions .
# run codex with an additional profile granting extra access
sandbox --profile codex-extra codex
# run an arbitrary command in the sandbox
sandbox python3 script.py
sandbox-exec.BASE.sb is overwritten on every invocation — any edits will be lost.Generates shortened URLs using the s.glob.au URL shortening service.
shorten-url <URL>
| Argument | Description |
|---|---|
URL |
URL to shorten |
An API token is required in ~/.config/glob-util/s.glob.au.properties:
api-token=YOUR_TOKEN_HERE
shorten-url https://example.com/some/very/long/path
# outputs: https://s.glob.au/abc123
Sleeps for a random duration between 0 and the specified maximum, then executes a command. Useful for staggering cron tasks across multiple hosts to avoid thundering-herd problems. Preserves the command's exit code.
sleep-random <SECONDS> <COMMAND>...
| Argument | Description |
|---|---|
SECONDS |
Maximum number of seconds to sleep |
COMMAND |
Command to run after sleeping |
# sleep 0–300 seconds, then run backup
sleep-random 300 /usr/local/bin/backup.sh
# in crontab (stagger a nightly job across multiple machines)
0 2 * * * sleep-random 3600 /usr/local/bin/nightly.sh
[0, SECONDS].COMMAND is returned as the exit code of sleep-random.Finds image and video files and plays them in mpv as a slideshow. Supports shuffle/sort modes, filename filtering, and configurable image duration.
Requires mpv to be installed. macOS only.
slideshow [OPTIONS] [ARGS]...
Arguments can be file paths, directory paths, or plain words treated as filename filters.
| Option | Description |
|---|---|
-r, --recursive |
Scan directories recursively |
ARGS |
Files, directories, or filename filter terms |
-x, --exclude <PATH> |
Exclude path(s) from results (repeatable) |
-o, --or |
Match files containing any filter term (default: all terms must match) |
| Option | Description |
|---|---|
-i, --images |
Show only images (gif, png, jpeg, jpg, webp) |
-v, --videos |
Show only videos (mp4, mkv, avi, etc.) |
| Option | Description |
|---|---|
--shuffle |
Random order (default) |
-N, --name |
Alphabetical by filename |
-n, --newest |
Newest files first (by modification time) |
-S, --size |
Largest files first |
-R, --reverse |
Reverse the sort order |
| Option | Description |
|---|---|
-l, --loop |
Loop each video |
-m, --mute |
Start muted |
-w, --windowed |
Play in a window instead of fullscreen |
-d, --delay <SECS> |
Seconds to display each image |
-q, --quiet |
Warnings and errors only |
--list |
Print filenames to stdout instead of launching mpv |
# shuffle all media in current directory
slideshow
# show images from a directory, newest first
slideshow -i -n ~/Photos
# filter by filename (all terms must match)
slideshow holiday beach
# filter by filename (any term matches)
slideshow -o holiday beach
# recursive scan, largest videos first
slideshow -r -v -S ~/Videos
# list matching files without launching mpv
slideshow --list holiday
/) are treated as filename filters.Manages SMB/CIFS network share mounts with stored credentials for quick remounting after reboot or disconnection. Credentials are stored in the macOS keychain. macOS only.
smb-mounter [OPTIONS] <COMMAND>
| Option | Description |
|---|---|
-q, --quiet |
Warnings and errors only |
add — Add and mount a sharesmb-mounter add <MOUNT_PATH> <HOSTNAME> <SHARE> [--username <USER>]
Registers a new SMB share and mounts it immediately. Prompts for a password and stores it in the keychain.
| Argument | Description |
|---|---|
MOUNT_PATH |
Local directory to mount the share on (must exist) |
HOSTNAME |
Remote server hostname |
SHARE |
Share name on the remote server |
-u, --username <USER> |
Username (defaults to current user) |
list — List configured mountssmb-mounter list
Lists all configured mounts and their current state. Alias: ls.
mount — Mount sharessmb-mounter mount [MOUNT_PATH]
Mounts all configured shares (or a specific one). Already-mounted shares are skipped.
| Argument | Description |
|---|---|
MOUNT_PATH |
Mount only this path (optional) |
unmount — Unmount a sharesmb-mounter unmount <MOUNT_PATH>
Unmounts the share at the given path.
remove — Remove a configured mountsmb-mounter remove <MOUNT_PATH>
Removes a mount from the configuration. Aliases: rm, del.
noatime,nodev,nosuid,nobrowse.add, the password prompt is retried automatically.~/.config/smb-mounter/config.toml.Waits for files or glob patterns to exist (or not exist), polling every second. Exits successfully when the condition is met, or fails on timeout.
wait-exists [OPTIONS] <FILES>...
| Argument | Description |
|---|---|
FILES |
File paths or glob patterns to wait for |
| Option | Description |
|---|---|
-n, --not |
Wait for all files to be deleted rather than created |
-t, --timeout <SECS> |
Fail after this many seconds (must be > 0) |
-v, --verbose |
Print "waiting..." every 10 seconds |
# wait for a file to appear
wait-exists /tmp/lock
# wait for a glob pattern to match
wait-exists /var/run/*.pid
# wait for a file to be deleted, with a 60-second timeout
wait-exists --not --timeout 60 /tmp/lock
# wait with verbose output
wait-exists --verbose /tmp/done
--timeout, waits indefinitely.yt-dlp wrapper with sensible defaults for video downloads. Converts to MP4 and embeds English subtitles by default, accepts bare YouTube video IDs, and skips files that already exist.
Requires uv to be installed. yt-dlp is installed and updated automatically. aria2c is optional and enables threaded downloads with --threaded.
youtube-dl [OPTIONS] [URL|ID]...
URLs and bare 11-character YouTube video IDs are accepted. All other arguments are passed through to yt-dlp.
The following are applied to every download:
--recode-video mp4)--write-sub --sub-lang en --embed-subs)%(title)s.%(ext)s)| Site | Default |
|---|---|
| YouTube, youtu.be | 1080p, unless --format, -f, --extract-audio, or -x is given |
| Vimeo | 1080p, unless --format, -f, --extract-audio, or -x is given |
| SoundCloud | MP3 |
YouTube URLs have time_continue and t query string parameters stripped automatically.
| Option | Description |
|---|---|
--mp3 |
Download and convert to MP3 |
--480, --480p |
Request 480p video |
--720, --720p |
Request 720p video |
--1080, --1080p |
Request 1080p video |
--no-mp4, --nomp4 |
Retain original format; don't convert to MP4 |
--no-subs, --nosubs |
Don't download and embed subtitles |
--threaded, --aria2, --aria2c |
Download multi-threaded using aria2 |
--expand-playlist, --playlist-urls |
Expand playlist into individual URLs |
--test |
Print full yt-dlp command without executing |
# download a YouTube video by URL
youtube-dl https://www.youtube.com/watch?v=dQw4w9WgXcQ
# download by bare video ID
youtube-dl dQw4w9WgXcQ
# download as MP3
youtube-dl --mp3 https://www.youtube.com/watch?v=dQw4w9WgXcQ
# download at 720p instead of the default 1080p
youtube-dl --720p https://www.youtube.com/watch?v=dQw4w9WgXcQ
# threaded download using aria2c
youtube-dl --threaded https://www.youtube.com/watch?v=dQw4w9WgXcQ
# preview the full yt-dlp command without running it
youtube-dl --test https://www.youtube.com/watch?v=dQw4w9WgXcQ
# expand a playlist into individual URLs
youtube-dl --expand-playlist https://www.youtube.com/playlist?list=PLxxx
uv and updated automatically once per day.ff info after a successful download if ff is installed.--update or -U to force an immediate update of yt-dlp.