From claude-tts
Use for first-run setup of the claude-tts plugin — bootstraps the engine, LLM backend, OS service, and config on a fresh machine. Invoked by /tts:setup.
How this skill is triggered — by the user, by Claude, or both
Slash command
/claude-tts:tts-setupThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
First-run setup for the claude-tts plugin, invoked by `/tts:setup`. It is
First-run setup for the claude-tts plugin, invoked by /tts:setup. It is
idempotent and re-runnable; /tts:doctor re-checks health anytime.
Fail-stop contract: run the steps in order. After each step, verify it
succeeded. If a step cannot complete, report which step failed and the
single most likely remediation, then STOP — do not continue past a failed
step. Never hardcode absolute personal paths; the config dir is
~/.config/claude-tts and the socket is
${CLAUDE_TTS_SOCKET:-${XDG_RUNTIME_DIR:-/tmp}/claude-tts.sock}.
Let ROOT = the plugin/daemon root (${CLAUDE_PLUGIN_ROOT} when invoked as a
plugin). Run all Python through the project's uv venv: uv run python ....
Run uname -s (Darwin/Linux) and uname -m (arm64/x86_64). Record both. On
Darwin+arm64 the machine is Apple Silicon (Kokoro is available). Background
service install is automated on both platforms (launchd on macOS, systemd --user on Linux); Step 6 falls back to a manual launch only when no systemd
user session is present.
If command -v uv fails, install it: curl -LsSf https://astral.sh/uv/install.sh | sh.
Then create the env and install deps from ROOT: uv sync --extra edge (this
project is a uv-managed application — its dependencies live in pyproject.toml
and the default edge-tts engine is the edge extra; there is no
requirements.txt). Verify uv run python -c "import edge_tts" succeeds.
Remediation on failure: ensure network access and that ~/.local/bin is on PATH.
Default: edge-tts (cross-platform, no ML deps). If Apple Silicon, offer Kokoro
(uv run python -c "import mlx_audio" to check; the MLX model pulls on first use).
If a Voicebox app is reachable at its configured URL, offer voicebox. Record
the chosen engine and a default voice_name (en-US-AvaNeural for edge-tts).
If no ML engine is available and uv sync --extra edge cannot install edge-tts
(e.g. offline), choose say (macOS) / espeak (Linux) — the zero-dependency
system engine. It always produces audio on a bare machine (using the OS
default voice; voice_name is ignored for this engine).
Default: local Ollama with model qwen2.5-coder:1.5b. Offer to pull it:
ollama pull qwen2.5-coder:1.5b (skip if ollama list already shows it).
Alternatively, an OpenAI-compatible endpoint (collect base_url + model;
read the API key from an environment variable the user names — never store the
raw key in shell history). Or none (deterministic mode). Record backend
∈ ollama/openai/null.
Run the calibration gate for the chosen backend:
uv run python scripts/calibrate.py --backend <backend> [--model <m>] [--base-url <url>] [--api-key-env <ENVVAR>] --json
It prints {"mode": "smart"|"deterministic", ...}. If mode == "smart", keep
the chosen backend. If mode == "deterministic" (model unreachable or below the
precision/recall bar), warn the user that the LLM under-performed its own
deterministic floor and set backend = null for the config — TTS still works,
just rule-based. The null backend skips scoring and is deterministic by
definition.
Compute the launcher: PYTHON = ROOT/.venv/bin/python, program_args = [PYTHON, "-m", "daemon.tts_daemon"], env = {"PYTHONUNBUFFERED": "1", "PYTHONPATH": ROOT, "PATH": <current PATH>}. (PYTHONPATH=ROOT lets
-m daemon.tts_daemon resolve the package regardless of the service's working
directory — required for systemd --user, harmless for launchd.)
macOS — install and start via the platform seam:
uv run python -c "from daemon.platforms import make_platform; make_platform().install_service(program_args=[...], env={...})".
This writes ~/Library/LaunchAgents/com.claude-tts.daemon.plist and
launchctl bootstraps it. Linux — install and start via the same platform seam:
uv run python -c "from daemon.platforms import make_platform; make_platform().install_service(program_args=[...], env={...})".
This renders ~/.config/systemd/user/claude-tts.service, then runs
systemctl --user daemon-reload, systemctl --user enable --now claude-tts.service,
and loginctl enable-linger (so the daemon survives logout/reboot). If systemctl
is absent (no systemd user session), the seam raises a clear message — fall back to
starting the daemon manually (uv run python -m daemon.tts_daemon &).
Verify the socket binds at
${CLAUDE_TTS_SOCKET:-${XDG_RUNTIME_DIR:-/tmp}/claude-tts.sock}.
Remediation on failure: check /tmp/claude-tts-daemon.err.log.
Write ~/.config/claude-tts/config.json (read any API key from the env var you
named in Step 4 — never inline the raw key):
uv run python -c "import os; from daemon.config_io import render_config, write_config; write_config(render_config(engine='<engine>', voice_name='<voice>', backend='<backend>', model='<model>', base_url='<url>', api_key=os.environ.get('<KEY_ENV>', '')))".
Then run the /tts:doctor checks (disk, daemon+socket, deps, backend
reachability) and play one test utterance to confirm audio. Report the overall
verdict. If doctor reports any WARN, surface it with its remediation.
Creates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.
npx claudepluginhub chendrizzy/claude-tts --plugin claude-tts