A collection of small Python CLIs exposed as subcommands under a single entrypoint. One install, one venv, many tools.
Built for fun, occasionally useful, never useless.
Requires uv and Python >=3.12.
git clone <repo-url> simple-tools
cd simple-tools
uv syncSome tools have system-level dependencies (e.g. ffmpeg). Check the per-tool entry in docs/SPECS.md.
uv run simple-tools --help # list all tools
uv run simple-tools <tool> --help # help for a specific tool
uv run simple-tools <tool> [args...] # run a toolFor AI agents: every tool exposes its full flag surface via
--help. Runuv run simple-tools <tool> --helpbefore invoking a tool to discover its arguments, defaults, and behavior. The per-tool sections below mirror that output.
Artifacts (downloads, conversions, generated files) are written by default to output/<tool>/ at the repo root.
| Command | Description | Status |
|---|---|---|
yt-mp3 |
Download a YouTube video's audio as MP3 | available |
play-music |
Randomly play MP3s from a folder | available |
See docs/SPECS.md for full per-tool specs.
Download a YouTube video's audio as an MP3. Requires ffmpeg on PATH (e.g. brew install ffmpeg).
Usage
simple-tools yt-mp3 [OPTIONS] URL
Arguments
| Name | Required | Description |
|---|---|---|
URL |
yes | YouTube video URL to extract audio from. Playlists and channel URLs are not supported. |
Options
| Flag | Short | Type | Default | Description |
|---|---|---|---|---|
--output |
-o |
path | output/yt-mp3 |
Directory to write the MP3 into. Created if missing. Resolved relative to CWD. |
--bitrate |
-b |
int | 192 |
MP3 bitrate in kbps. Common values: 128 (compact), 192 (balanced), 320 (highest standard MP3 quality). |
--filename |
-f |
str | video title (sanitized) | Output filename without extension. The .mp3 extension is added automatically. Path separators are stripped. |
--debug |
flag | off | On failure, show the full Python traceback instead of a single-line error message. | |
--help |
flag | Show full help and exit. |
Behavior
- On success: prints the absolute path of the produced MP3 to stdout (one line). Progress goes to stderr.
- On failure: exits
1with a one-line stderr message (use--debugfor full traceback). Missing required argument exits2. - Refuses to overwrite an existing file at the target path.
Examples
# default: writes to ./output/yt-mp3/<sanitized-title>.mp3
uv run simple-tools yt-mp3 "https://www.youtube.com/watch?v=jNQXAC9IVRw"
# custom filename + bitrate + output dir
uv run simple-tools yt-mp3 \
"https://www.youtube.com/watch?v=jNQXAC9IVRw" \
--filename me-at-the-zoo \
--bitrate 320 \
--output ~/Music/clipsRandomly play MP3 files from a folder. Requires ffplay (bundled with ffmpeg; brew install ffmpeg).
Usage
simple-tools play-music [OPTIONS] FOLDER
Arguments
| Name | Required | Description |
|---|---|---|
FOLDER |
yes | Directory to scan for .mp3 files. Subdirectories are included by default. |
Options
| Flag | Short | Type | Default | Description |
|---|---|---|---|---|
--once |
flag | off | Play one shuffled pass and exit. Default loops forever. | |
--no-recursive |
flag | off | Scan only the top level of FOLDER. |
|
--visualize |
flag | off | Render an animated bar visualizer beside the playback timer. Bars are colored green→yellow→red by height; set NO_COLOR=1 to disable color. Cosmetic only — bars are time-driven, not synced to actual audio. No-op when stdout is not a TTY. |
|
--debug |
flag | off | On failure, show full Python traceback instead of a one-line error. | |
--help |
flag | Show full help and exit. |
Behavior
- Discovers
.mp3files (case-insensitive); empty or missing folder → exit1. - Shuffles and plays each track via
ffplay -nodisp -autoexit -loglevel quiet. Prints▶ <path> (mm:ss)to stdout per track (duration viaffprobe). - During playback (when stdout is a TTY), updates a live
[mm:ss/mm:ss]count-up timer on a single line. With--visualize, the bar animation joins that line. - Default mode loops, reshuffling between passes.
Ctrl-Cexits0cleanly. - Missing
ffplay→ exit1with hint tobrew install ffmpeg. Missingffprobeis non-fatal — the timer simply omits the total duration.
Examples
# loop forever, reshuffling between passes (Ctrl-C to stop)
uv run simple-tools play-music output/yt-mp3
# play one shuffled pass and exit
uv run simple-tools play-music ~/Music --once
# top-level only, no subfolder recursion
uv run simple-tools play-music ~/Music --no-recursive --once
# with animated bar visualizer (cosmetic; requires a TTY)
uv run simple-tools play-music output/yt-mp3 --visualizesimple-tools/
├── docs/ # SPECS + execution plans
├── output/ # tool artifacts (gitignored)
├── src/simple_tools/
│ ├── cli.py # root Typer app
│ └── tools/<tool_name>/ # one package per tool
└── tests/
uv run pytest tests/ -v # run all tests
uv run simple-tools <tool> ... # run a tool from the workspace venvProject conventions, slash commands, and contribution workflow are documented in AGENTS.md.
MIT — see LICENSE.