#!/usr/bin/env bash # Tarbuckle — the short way in. v2 §9: named invocation, mute, off. # # ! tarbuckle call him by name; he answers at length # ! tarbuckle what now ask him something # ! tarbuckle "opinion?" a QUESTION MARK must be quoted, or dropped # # ⚠ zsh expands `?`, `*` and `[` as globs before this script is ever reached, so an # unquoted `tarbuckle opinion?` dies at "no matches found" in the shell. Three ways # past it, in order of least fuss: drop the `?`, quote the phrase, or rely on the # `alias tarbuckle='noglob tarbuckle'` in shell/.zshrc — which only helps if the # calling shell is interactive, since that is the only kind that reads .zshrc. # ! tarbuckle mute silence the utterances; he stays in the room # ! tarbuckle off remove him entirely # ! tarbuckle on bring him back # ! tarbuckle status what he is doing, and when he might speak next # # ⚠ Run this YOURSELF. A fool relayed by the executor is the executor's paraphrase of # a fool. The `!` prefix in Claude Code runs it in the session so the output is his. set -euo pipefail S="$HOME/dotfiles/scripts" M="$HOME/.claude/state/tarbuckle-mute" mkdir -p "$(dirname "$M")" case "${1-}" in mute) echo mute > "$M"; echo "muted — he stays in the room and says nothing. Never a fault." ;; off) echo off > "$M"; echo "off — removed from the status line too." ;; on|unmute) rm -f "$M"; echo "back." ;; status) st=$(cat "$M" 2>/dev/null || echo "listening") echo "state: $st" if [ -f "$HOME/.claude/state/tarbuckle-last-tick" ]; then python3 - "$HOME/.claude/state/tarbuckle-last-tick" <<'PY' import sys, time t = int(open(sys.argv[1]).read()) print("next tick:", time.strftime('%H:%M', time.localtime(t + 20*60)), "(27% he speaks)") PY fi r="$HOME/.claude/state/tarbuckle-rejects.jsonl" [ -f "$r" ] && echo "silences: $(wc -l < "$r" | tr -d ' ') logged (net rejections + timeouts)" || true ;; --help|-h) sed -n '2,22p' "$0" | sed 's/^# \{0,1\}//' ;; *) exec python3 "$S/tarbuckle-invoke.py" "$@" ;; esac