#!/bin/zsh -l # ask-claude.sh — matrix-bridge headless "ask" wrapper. # # Invoked over SSH by the bot: ask-claude.sh # Runs `claude -p` one-shot in the repo and prints the answer to STDOUT, which the bot # captures over the SSH pipe and posts back into the Matrix room. Unlike launch-claude.sh / # gui-launch.sh (interactive, surfaced to the phone), this NEVER opens a GUI Terminal. # # Two seams it owns, both proven the hard way in Phase 0: # - LOGIN shell (-l): a non-login SSH shell loads neither ~/.zprofile nor ~/.zshrc, so # ~/.local/bin isn't on PATH and `claude` isn't found. Same reason as launch-claude.sh. # - Headless auth via CLAUDE_CODE_OAUTH_TOKEN (from `claude setup-token`, stored in ../.env): # a non-GUI SSH session can't reach the login Keychain, so plain `claude -p` reports # "Not logged in" (D11 / Approach A). We export the token to bypass the Keychain. set -e script_dir="${0:A:h}" # Pull just the token out of ../.env (don't `source` the whole file — other values, e.g. a # password, may not be shell-safe). Absent token => claude reports "Not logged in", reported # back to the room by the bot. env_file="$script_dir/../.env" if [[ -f "$env_file" ]]; then token_line="$(grep -E '^CLAUDE_CODE_OAUTH_TOKEN=' "$env_file" | head -1)" token="${token_line#*=}" token="${token#\"}" # strip one surrounding quote pair if present (KEY="value") token="${token%\"}" export CLAUDE_CODE_OAUTH_TOKEN="$token" fi repo_dir="$1" shift prompt="$*" if [[ -z "$repo_dir" || -z "$prompt" ]]; then print -u2 "usage: ask-claude.sh " exit 2 fi # Fail loud on a bad directory — never run Claude in the wrong place. cd "$repo_dir" || { print -u2 "ask-claude: no such repo dir: $repo_dir"; exit 1; } # < /dev/null: print mode reads stdin by default and otherwise stalls ~3s waiting for it. exec claude -p "$prompt" < /dev/null