196 lines
8.2 KiB
Bash
Executable File
196 lines
8.2 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
# Hand a prompt to whichever coding agent this machine has chosen.
|
|
#
|
|
# Every rung of the escalation ladder ends here: a crash toast, a failed shell
|
|
# reload, a red health check, `panama diagnose`. They gather facts; this decides
|
|
# which binary runs them and puts it in a terminal you can watch and interrupt.
|
|
#
|
|
# Two settings decide everything, and both are read at press time rather than at
|
|
# start time, so choosing an agent in Settings takes effect on the next crash
|
|
# without restarting anything:
|
|
#
|
|
# preferredAgent none | claude | codex ("none" is the default: silence)
|
|
# agentAutoApprove true -> the agent starts in its own "don't stop to ask"
|
|
# mode; false -> its normal prompting mode, untouched.
|
|
#
|
|
# "none" exits 0 without a word. It is not an error to have no agent; it is the
|
|
# shipped state, and a rung that shouted about it would be a rung that gets
|
|
# turned off.
|
|
#
|
|
# panama-agent open the agent on the repo
|
|
# panama-agent --prompt "text" open it with something to work on
|
|
#
|
|
# Environment seams, for the contract and for a second checkout:
|
|
#
|
|
# PANAMA_PATH the repository; also the agent's working directory
|
|
# PANAMA_AGENT_SETTINGS the settings file to read (default: the real one)
|
|
#
|
|
# ---------------------------------------------------------------------------
|
|
# Adapted from Omarchy's bin/omarchy-agent (https://github.com/basecamp/omarchy)
|
|
#
|
|
# Copyright (c) David Heinemeier Hansson
|
|
#
|
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
# of this software and associated documentation files (the "Software"), to deal
|
|
# in the Software without restriction, including without limitation the rights
|
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
# copies of the Software, and to permit persons to whom the Software is
|
|
# furnished to do so, subject to the following conditions:
|
|
#
|
|
# The above copyright notice and this permission notice shall be included in all
|
|
# copies or substantial portions of the Software.
|
|
#
|
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
# SOFTWARE.
|
|
# ---------------------------------------------------------------------------
|
|
|
|
set -euo pipefail
|
|
|
|
PANAMA_PATH="${PANAMA_PATH:-$(cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")/.." && pwd)}"
|
|
SETTINGS="${PANAMA_AGENT_SETTINGS:-${XDG_CONFIG_HOME:-$HOME/.config}/panama/settings.json}"
|
|
|
|
# A fixed window class rather than the per-binary default, so one Hyprland rule
|
|
# can catch every agent window regardless of which agent is chosen.
|
|
readonly WINDOW_CLASS="panama-agent"
|
|
|
|
# THE INHERITED PATH IS NOT THE USER'S PATH. Every rung except `panama diagnose`
|
|
# reaches this script from the Quickshell shell, which systemd starts with
|
|
# neither PANAMA_PATH nor ~/.local/bin -- and ~/.local/bin is where both agents
|
|
# install themselves. Trusting PATH here meant a perfectly well installed agent
|
|
# reporting itself as missing, into the stderr of a detached process nobody will
|
|
# ever read: the whole ladder failing silently, which is the exact failure it
|
|
# exists to prevent.
|
|
#
|
|
# So the binary is resolved rather than named. PATH first, because a user who
|
|
# put an agent somewhere else meant it; then the XDG user bin directory, which
|
|
# is where the installers actually put them.
|
|
resolve_agent() {
|
|
local name="$1" found
|
|
found="$(command -v "$name" 2>/dev/null)" && { printf '%s' "$found"; return 0; }
|
|
[[ -x "$HOME/.local/bin/$name" ]] && { printf '%s' "$HOME/.local/bin/$name"; return 0; }
|
|
return 1
|
|
}
|
|
|
|
# The same repair, for the agent's own sake rather than this script's: an agent
|
|
# launched from a notification click would otherwise run every shell command it
|
|
# is asked to with a PATH unlike the one the user gets in a terminal. Applied
|
|
# just before the spawn rather than here, so resolve_agent above is answering
|
|
# the question the caller actually asked -- "can this be found from where I was
|
|
# started" -- instead of one this script has already fixed for itself.
|
|
repair_path() {
|
|
case ":$PATH:" in
|
|
*":$HOME/.local/bin:"*) ;;
|
|
*) PATH="$PATH:$HOME/.local/bin" ;;
|
|
esac
|
|
export PATH
|
|
}
|
|
|
|
usage() {
|
|
cat <<'EOF'
|
|
Usage: panama-agent [--prompt "text"]
|
|
|
|
Opens the agent named by preferredAgent in a terminal, in the Panama checkout.
|
|
With no agent chosen, exits silently: choose one on Settings > System > Agents.
|
|
EOF
|
|
}
|
|
|
|
prompt=""
|
|
while (($#)); do
|
|
case "$1" in
|
|
--prompt)
|
|
prompt="${2:?--prompt needs a value}"
|
|
shift 2
|
|
;;
|
|
-h | --help)
|
|
usage
|
|
exit 0
|
|
;;
|
|
*)
|
|
printf 'panama-agent: unexpected argument: %s\n' "$1" >&2
|
|
usage >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
# Same shape as panama-idle's reader: a missing file, a missing key and an
|
|
# explicit null all mean "the default", because all three describe a machine
|
|
# that has never been asked the question.
|
|
read_setting() {
|
|
local key="$1" fallback="$2"
|
|
[[ -r "$SETTINGS" ]] || { printf '%s' "$fallback"; return; }
|
|
command -v jq >/dev/null 2>&1 || { printf '%s' "$fallback"; return; }
|
|
jq -r --arg k "$key" --arg d "$fallback" \
|
|
'if has($k) and (.[$k] != null) then (.[$k] | tostring) else $d end' \
|
|
"$SETTINGS" 2>/dev/null || printf '%s' "$fallback"
|
|
}
|
|
|
|
agent="$(read_setting preferredAgent none)"
|
|
|
|
# The shipped state. Nothing to launch, nothing to say.
|
|
[[ -n "$agent" && "$agent" != "none" ]] || exit 0
|
|
|
|
auto_approve="$(read_setting agentAutoApprove true)"
|
|
|
|
case "$agent" in
|
|
claude | codex) ;;
|
|
*)
|
|
printf 'panama-agent: unsupported preferredAgent: %s\n' "$agent" >&2
|
|
printf 'Choose one on Settings > System > Agents.\n' >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
# Resolved to a path before argv is built, so kitty is never asked to repeat a
|
|
# PATH lookup this script has already done more carefully than kitty could.
|
|
if ! agent_bin="$(resolve_agent "$agent")"; then
|
|
printf 'panama-agent: %s is not installed.\n' "$agent" >&2
|
|
printf 'Looked on PATH and in %s.\n' "$HOME/.local/bin" >&2
|
|
printf 'Install it, or choose another agent on Settings > System > Agents.\n' >&2
|
|
exit 1
|
|
fi
|
|
|
|
# The per-agent launch table. VERIFIED against the installed binaries' --help on
|
|
# 2026-08-25 (claude 2.1.245, codex-cli 0.149.1) -- these flags are not the same
|
|
# from release to release, so re-run --help before changing them.
|
|
#
|
|
# claude --permission-mode auto "auto" is one of acceptEdits/auto/
|
|
# bypassPermissions/manual/dontAsk/plan
|
|
# codex --approve-for-me routes approvals through automatic review
|
|
# inside the workspace-write sandbox
|
|
#
|
|
# With agentAutoApprove off, no mode flag is passed at all: the agent's own
|
|
# configured default is a choice the user already made, and overriding it with
|
|
# an explicit "prompt me" would be this script having an opinion it was told not
|
|
# to have.
|
|
declare -a argv=("$agent_bin")
|
|
case "$agent" in
|
|
claude) [[ "$auto_approve" == "true" ]] && argv+=(--permission-mode auto) ;;
|
|
codex) [[ "$auto_approve" == "true" ]] && argv+=(--approve-for-me) ;;
|
|
esac
|
|
|
|
# One argv element, after the option terminator. Both CLIs take the prompt as a
|
|
# trailing positional, and `--` is what stops a prompt beginning with a dash --
|
|
# or one that happens to read like a subcommand -- from being parsed as flags.
|
|
[[ -n "$prompt" ]] && argv+=(-- "$prompt")
|
|
|
|
# The checkout, not $HOME: the skills the prompts point at, the repository the
|
|
# agent is being asked about, and .claude/settings.json's pre-approved read-only
|
|
# diagnostics all live here. An agent started anywhere else finds none of them.
|
|
cd "$PANAMA_PATH"
|
|
|
|
repair_path
|
|
|
|
# setsid so the agent outlives whatever spawned it -- a notification handler, a
|
|
# crash watcher, a terminal that is about to close.
|
|
exec setsid kitty \
|
|
--directory "$PANAMA_PATH" \
|
|
--class "$WINDOW_CLASS" \
|
|
-e "${argv[@]}"
|