Producer tags for developers. Every
git pushdrops yours.
Music producers have been stamping their work since the early 2000s. Developers
push thousands of commits and leave no mark. signum fixes that.
? Your producer tag phrase: › Alexey on the repo, foenem
? Style: › metro / khaled / pi'erre
? Voice: › [your OS voices]
Generating your tag... ████████████████ done
Installing git hook... ████████████████ done
✓ Signum installed. Every push drops your tag.
Same phrase, same voice, one per style — demo/metro.mp3 ·
demo/khaled.mp3 · demo/pierre.mp3
A rotating set, one phrase per style — demo/tag-1-made-it.mp3 ·
demo/tag-2-repos-up.mp3 ·
demo/tag-3-not-nice.mp3
pnpm dlx signum initOne command. No brew, no choco, no manual ffmpeg — the right binary downloads itself.
| Command | What it does |
|---|---|
signum init |
Setup wizard. Generates your tag, installs the git hook. |
signum play |
Preview your current tag. |
signum config |
Change your phrase, style, or voice. Regenerates the tag. |
signum uninstall |
Removes the hook and everything in ~/.signum. |
metro · khaled · pi'erre — each is an ffmpeg filter chain plus its own 808.
The 808s aren't sampled from anywhere; they're synthesized at build time from a
pitch-enveloped saturated sine (pnpm gen:808), so the whole thing stays
license-clean and free forever.
- Your phrase goes through OS-native TTS —
sayon macOS, SAPI on Windows. - ffmpeg applies the style's filter chain (tempo, echo, lowpass, gain).
- The style's 808 is mixed underneath at a 3:1 voice-to-808 weighting.
- The result lands at
~/.signum/tag.wav. - A global
pre-pushhook plays it in the background on every push.
The hook never blocks your push — playback is backgrounded with its stdin closed, so git's ref list still reaches the real hook.
Setting core.hooksPath globally normally makes git ignore every repo's
.git/hooks entirely — silently killing husky, lefthook, and hand-written
hooks machine-wide. Signum installs a forwarding shim for every client-side
hook, so your repo-local hooks keep running exactly as before, including their
exit codes. signum uninstall restores whatever core.hooksPath was set to
beforehand.
If you push mostly through Claude Code, you can skip the git hook entirely — no
global core.hooksPath, no forwarding shims, nothing touching your git config.
Drop this in ~/.claude/settings.json and put one or more .wav files in
~/.signum/tags/; it picks one at random per push:
{
"hooks": {
"PostToolUse": [{
"matcher": "Bash",
"hooks": [{
"type": "command",
"async": true,
"command": "jq -r '.tool_input.command // \"\"' | grep -qE '(^|[;&|]|&&)[[:space:]]*git([[:space:]]+-[^[:space:]]+([[:space:]]+[^-[:space:]][^[:space:]]*)?)*[[:space:]]+push([[:space:]]|$)' && { t=$(ls \"$HOME\"/.signum/tags/*.wav 2>/dev/null | sort -R | head -1); [ -n \"$t\" ] && afplay \"$t\" 2>/dev/null; }; true"
}]
}]
}
}The trade-off: it only fires on pushes Claude Code runs. A push you type in your own terminal stays silent — that one needs the git hook.
Two things worth knowing if you adapt it: sort -R is used instead of a shell
array because Claude hooks may run under zsh, whose arrays are 1-indexed (an
array pick silently plays nothing a third of the time). And the regex
deliberately tolerates git -C <path> push while rejecting
git commit -m "fix push bug".
Node ≥ 18. macOS (verified) or Windows via Git Bash (implemented, not yet hardware-tested).
pnpm install
pnpm test # vitest
pnpm typecheck # tsc --noEmit
pnpm build # dist/index.js + dist/signum-{mac,win}.js
pnpm gen:808 # re-synthesize assets/808-*.mp3Both bundles are plain CJS with no runtime deps beyond @ffmpeg-installer/ffmpeg,
so they run directly under node or bun:
node dist/signum-mac.js init
bun dist/signum-mac.js init