tts:voices

tts:voices is a command for Claude Code from chendrizzy/claude-tts. It costs 23 tokens per session (1,425 once invoked), scanned A, original, MIT.

A command that shows which speech voice is assigned to each project repository. A repository is a project folder tracked by Git, a version-control system.

In plain words
What is it for?
It helps inspect voice assignments for known repositories or paths and confirm which project is currently selected.
Why use it?
It makes the daemon’s automatic project-to-voice assignments visible, including the voice used by the current repository.

Command for Claude Code

Written for Claude Code: $ARGUMENTS substitution. Also seen: reads .claude/ paths.

Part of the claude-tts plugin — 1 skill, 8 commands, 5 hooks shipped together

Good fit It helps inspect voice assignments for known repositories or paths and confirm which project is currently selected.

Compare 6 commands from other repositories ↓
Install with agentmods
npx agentmods add commands/chendrizzy/claude-tts/voices
Install

Getting it into your agent

One page per mod, every tool's command on it. A separate URL per tool would split the same page into five that compete with each other.

Clone the repo
git clone --depth 1 https://github.com/chendrizzy/claude-tts

Made for: Claude Code.

Or install claude-tts, the plugin that ships this one along with the rest of its 1 skill, 8 commands, 5 hooks.

Wrote this? Show the measurements

A badge with what this costs and how it scanned, read live from this page, so it follows the numbers instead of freezing them. Markdown for a README, HTML for a documentation site or a project page.

agentmods badge for tts:voices

README.md
[![agentmods](https://agentmods.dev/badge/commands/chendrizzy/claude-tts/voices.svg)](https://agentmods.dev/commands/chendrizzy/claude-tts/voices)
Your own site
<a href="https://agentmods.dev/commands/chendrizzy/claude-tts/voices"><img src="https://agentmods.dev/badge/commands/chendrizzy/claude-tts/voices.svg" alt="Measured on agentmods" height="20"></a>
Per session 23 Only the description is in the session, so the agent can decide to use it. The body loads when it is invoked.
When invoked 1,425 The whole file, excluding the scripts and references it only reads on demand.
Security scan A 0 findings. A grade says what 26 rules found in the file — not that it is safe.
Origin original No closer match found in the catalogue.
Token cost

What it costs to keep this loaded

Counted locally with the o200k_base tokenizer, which is exact for GPT models; Claude uses its own tokenizer and its counts differ. Treat this as one consistent yardstick across the catalogue rather than a bill. Prices are per million input tokens.

ModelPer sessionOnce invoked
Fable 5.1 $0.00023 $0.01425
Opus 5 $0.00012 $0.00713
Sonnet 5 $0.00005 $0.00285
Haiku 4.5 $0.00002 $0.00143

Measured 7d ago against content hash c1b2c39886b7, method: parsed. Prices are Anthropic first-party input rates as of 2026-09-08, from the pricing page.

Security

Grade A, and why

tts:voices scanned grade A with 0 findings against 26 rules in 11 categories — prompt injection, anti-refusal, data exfiltration, privilege escalation, supply chain, agent snooping, system-prompt leakage, SSRF and excessive agency — measured 7d ago.

A static scan of the body, not an audit. Every finding is printed with the line that produced it so you can judge whether it matters here. A mod is markdown that instructs an agent; that is exactly why what it instructs is worth reading.

Nothing flagged

None of the 26 patterns this scan looks for appear in this file: no shell pipes, no recursive deletes, no credential paths, no hidden text, no instruction-override or anti-refusal phrasing, no agent-config snooping. That is not a guarantee, it is the absence of the things that are checkable.

commands/voices.md · 160 lines

How it starts

The opening of the file, as written. The whole thing — 160 lines — stays where its author put it; the contents beside it link to each section on GitHub.

/tts:voices — project → voice index

Per-project voices are audibly distinct but otherwise invisible. This prints the daemon's deterministic project → voice map so you can see, at a glance, which repo speaks in which voice. The current repo is highlighted with .

The mapping is the daemon's own: a project is its REPO ROOT (nearest .git ancestor), and the voice is pool[ sha256(basename(root)) % len(pool) ] — the exact logic in daemon/content_router.py (_project_root_project_label_resolve_project_voice). The pool and enable flags are read from your config — $CLAUDE_TTS_CONFIG, else ~/.config/claude-tts/config.json (honoring $XDG_CONFIG_HOME). Restart the daemon to apply config changes.

"Known/active repos" = repos the daemon has actually spoken in (distinct cwds in ~/.claude/logs/tts/spoken/*.jsonl), plus the current repo, plus any repo paths you pass as arguments.

Run:

ARGS="$ARGUMENTS"
python3 - "$PWD" $ARGS <<'PY'
# Reproduces daemon/content_router.py's project->voice mapping EXACTLY. Kept
# inline so the command stays self-contained as an installed plugin. Pinned to
# the daemon by tests/test_project_voice_parity.py (imports the real functions).
import sys, os, json, hashlib, glob

CWD = sys.argv[1] if len(sys.argv) > 1 else os.getcwd()
EXTRA = [a for a in sys.argv[2:] if a.strip()]

# --- config: pool + enable flags ----------------------------------------------
# Mirrors daemon/paths.py config_path() precedence: $CLAUDE_TTS_CONFIG, then
# $XDG_CONFIG_HOME/claude-tts/config.json (else ~/.config/claude-tts/config.json).
# The trailing entries are legacy source-tree layouts, kept last as fallbacks.
def load_config():
    cands = []
    if os.environ.get("CLAUDE_TTS_CONFIG"):
        cands.append(os.environ["CLAUDE_TTS_CONFIG"])
    xdg = os.environ.get("XDG_CONFIG_HOME") or os.path.expanduser("~/.config")
    cands.append(os.path.join(xdg, "claude-tts", "config.json"))
    cands.append(os.path.expanduser("~/.claude/tts/config/tts_user_config.json"))
    cands.append(os.path.join(CWD, "config", "tts_user_config.json"))
    for c in cands:
        try:
            with open(c, encoding="utf-8") as fh:
                return json.load(fh) or {}
        except Exception:
            continue
    return {}

cfg = load_config()
pv = cfg.get("project_voices", {}) if isinstance(cfg, dict) else {}
# Daemon defaults: enabled False, pool [] (content_router.py ctor).
pv_enabled = bool(pv.get("enabled", False))
pool = list(pv.get("pool", []) or [])
default_voice = ((cfg.get("voice", {}) or {}).get("name")) or "(engine default)"
# Per-project voice assignment is independent of output_context; output_context
# only controls verbal project/context phrasing.
active = pv_enabled and bool(pool)

# --- the daemon's mapping (root -> label -> voice) ----------------------------
def project_root(cwd):
    if not isinstance(cwd, str) or not cwd.strip():
        return ""
    cwd = cwd.strip()
    root = cwd
    try:
        d = os.path.abspath(cwd)
        while True:
            if os.path.exists(os.path.join(d, ".git")):
                root = d
                break
            parent = os.path.dirname(d)
            if parent == d:
                break
            d = parent
    except Exception:
        root = cwd
    return root

def project_label(cwd):
    if not isinstance(cwd, str):
        return ""
    c = cwd.strip()
    if not c:
        return ""
    proj = c.rstrip("/").rsplit("/", 1)[-1]
    if not proj or proj in ("", "/", ".", "tmp"):
        return ""
    return proj

def voice_for(label):
    if not pool or not label:
        return None
    idx = int(hashlib.sha256(label.encode("utf-8")).hexdigest(), 16) % len(pool)
    return pool[idx]

# --- gather known/active repo roots -------------------------------------------
def spoken_cwds():
    out = set()
    for f in glob.glob(os.path.expanduser("~/.claude/logs/tts/spoken/*.jsonl")):
        try:
            with open(f, encoding="utf-8", errors="ignore") as fh:
                for ln in fh:
                    ln = ln.strip()
                    if not ln:
                        continue
                    try:
                        r = json.loads(ln)
                    except Exception:
                        continue
                    c = r.get("cwd")
                    if c:
                        out.add(c)
        except OSError:
            pass
    return out

cur_root = project_root(CWD)
roots = {}  # root -> label
for c in list(spoken_cwds()) + EXTRA + [CWD]:
    r = project_root(c)
    lbl = project_label(r)
    if lbl:
        roots[r] = lbl

# --- render -------------------------------------------------------------------
print("# /tts:voices — project → voice")
if not active:
    why = []
    if not pv_enabled:
        why.append("project_voices.enabled=false")
    if not pool:
        why.append("empty pool")
    print(f"per-project voices are OFF ({', '.join(why) or 'disabled'}); "
          f"the daemon speaks everything in the default voice: {default_voice}")
    print("(showing the mapping that WOULD apply if enabled)")
print(f"pool ({len(pool)}): {', '.join(pool) if pool else '(none)'}")
print(f"default voice: {default_voice}")
print()

if not roots:
    print("(no known repos yet — pass a repo path, or speak something first)")
    raise SystemExit

width = max((len(l) for l in roots.values()), default=7)
for root in sorted(roots, key=lambda r: roots[r].lower()):
    lbl = roots[root]
    voice = voice_for(lbl) or "-"
    mark = "▶" if root == cur_root else " "
    print(f"{mark} {lbl:<{width}}  ·  {voice:<10}  {root}")
PY

Read the full file on GitHub · 160 lines

Changes

What this file has done since we first saw it

Hashed on every crawl. A supply-chain change to an agent config is a question of when, not whether, so the history is kept rather than the latest state alone.

  1. 7d ago First seen · 160 lines · 23 tokens per session scan A c1b2c39886b7

Subscribe to this mod's changes

tts:voices is a command published in the GitHub repository chendrizzy/claude-tts (1 stars, last pushed 21d ago), licensed MIT. It adds 23 tokens to every session and 1,425 once invoked, about $0.0001 per session on Opus 5. A static security scan graded it A with 0 findings. No closer match exists in the catalogue, so it is treated as the original; first seen 2026-08-31.

Related

Other commands, from other repositories

wp-cinematic-scene

Author, replace, or regenerate a single cinematic scene. Updates the matching cinematicscenes repeater row (eyebrow, headline, body, CTA, videos, poster) and — if the scene needs a non-default layout — emits a per-scene template fragment override at template-parts/cinematic/scene- .php. Cinematic equivalent of…

yojahny55/claude-wp-builder · 0 tokens

wp-cinematic-seed

Seed (or re-seed) all cinematic scenes from a manifest. Idempotent — skips scenes whose sceneid already has content unless --force. Sideloads sample videos from the kit if no Media Library attachments are pinned.

yojahny55/claude-wp-builder · 56 tokens

wp-cinematic-encode

Encode source videos for cinematic scroll-scrub (all-keyframe MP4) and mobile autoplay-loop (9:16 portrait crop). Thin wrapper over the kit's scripts/encode-keyframe.sh and scripts/encode-mobile-portrait.sh. Drops encoded files into /assets/videos/ and updates the matching ACF row if --scene=N is provided.

yojahny55/claude-wp-builder · 84 tokens

make-creative

Design and produce any fixed-canvas creative — poster, flyer, brochure, business card, social post/ad, story, thumbnail, event banner/signage, infographic, or email — correctly spec'd and on-brand.

ca-who-codes/ultimate.UIUX.design.skills · 43 tokens

make-carousel

Design and produce a social media carousel (Instagram / LinkedIn / TikTok) — hook, narrative slides, CTA — with a consistent template and rendered slides.

ca-who-codes/ultimate.UIUX.design.skills · 32 tokens

make-deck

Design and produce a presentation/deck (pitch, sales, conference, internal) using the Design Pro knowledge base — structure, slide design, and a producible output.

ca-who-codes/ultimate.UIUX.design.skills · 34 tokens