8ad1cd8465
A message starting with `?` in a mapped room runs `claude -p` one-shot in that repo on the Mac and posts the full answer back into the room — Matrix as a request/response interface, not just a trigger. Non-`?` messages keep launching interactive sessions as before. New scripts/ask-claude.sh is a login-shell wrapper (so ~/.zprofile puts claude on PATH) that exports CLAUDE_CODE_OAUTH_TOKEN from the Mac's .env and runs `claude -p "$prompt" < /dev/null`, printing the answer to stdout. The bot adds a `?`-dispatch with run_ask/ask: SSH stdout captured, 300s timeout, fail-loud, output chunked under Matrix's event cap (no truncation). Headless claude -p needs the long-lived token because a non-GUI SSH session can't reach the login Keychain (reports "Not logged in") — the deliberate Approach A that the interactive GUI-Terminal path (D11) avoided. Token is kept Mac-side only; the Spark never runs claude. Sovereignty unchanged: claude -p uses the subscription, no frontier API touches message payloads. Proven live on the Spark; fresh-eyes reviewed before commit.
46 lines
1.9 KiB
Bash
Executable File
46 lines
1.9 KiB
Bash
Executable File
#!/bin/zsh -l
|
|
# ask-claude.sh — matrix-bridge headless "ask" wrapper.
|
|
#
|
|
# Invoked over SSH by the bot: ask-claude.sh <repo_dir> <prompt...>
|
|
# 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 <repo_dir> <prompt>"
|
|
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
|