- Ops Alerts → Ask Claude now calls `devdesk attach --investigate SESSION UID`. The server looks the alert up, writes the prompt and starts Claude in a new alert-<name> session with it as the first prompt, already sent. Prompts are stored in prompts/<session>.txt and consumed once by devdesk-claude-run. A session that is already running is just opened. - The prompt has the rule, query, summary, details and labels, and where it runs: the CT/VM id, the node it is on now, status and HA state, and `ssh <node> pct exec <id> --`. It reminds Claude that HA guests move (`devdesk guests <id>`), and gives aggregate rules a hint. - `devdesk guests`: Proxmox inventory over SSH (DEVDESK_PVE_NODES). pvesh gives the cluster view; each node gives lxc-info and guest-agent IPs. Cached in guests.json, keeping last-seen IPs for stopped guests; --alerts refreshes it in the background every 5 minutes. - Alerts carry uid, query, description and targets, matched by instance IP/host or by name; each card shows the target. - cmd_alerts moves to libexec/devdesk-ops.sh. Closes #39 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
123 lines
4.3 KiB
Bash
Executable File
123 lines
4.3 KiB
Bash
Executable File
#!/bin/bash
|
|
# Runs in each session's "claude" window. Resumes the conversation recorded for
|
|
# this session (see devdesk-session-hook), starts a fresh one if there is none,
|
|
# and offers a small menu whenever Claude exits so the window never just dies.
|
|
set -uo pipefail
|
|
|
|
# shellcheck source=libexec/devdesk-lib.sh
|
|
. "${DEVDESK_LIBEXEC:-/usr/lib/devdesk}/devdesk-lib.sh"
|
|
|
|
NAME=$(dd_current_session)
|
|
[ -n "$NAME" ] || dd_die "not running inside a DevDesk session"
|
|
|
|
SETTINGS="$DEVDESK_SHARE/claude-settings.json"
|
|
|
|
# Re-read /etc/devdesk/env before every Claude launch, so changes (Remote
|
|
# Control, CLAUDE_ARGS, …) apply on the next restart of Claude in a session.
|
|
reload_config() {
|
|
dd_load_env
|
|
CLAUDE_BIN=${CLAUDE_BIN:-claude}
|
|
read -r -a EXTRA <<<"${CLAUDE_ARGS:-}"
|
|
}
|
|
|
|
ORANGE=$'\033[38;2;255;107;0m'
|
|
CYAN=$'\033[38;2;0;212;255m'
|
|
MUTED=$'\033[38;2;62;96;122m'
|
|
RESET=$'\033[0m'
|
|
|
|
common_args() {
|
|
printf '%s\0' --settings "$SETTINGS"
|
|
if [ "${DEVDESK_REMOTE_CONTROL:-0}" = "1" ]; then
|
|
printf '%s\0' --remote-control "devdesk-$NAME"
|
|
fi
|
|
if [ ${#EXTRA[@]} -gt 0 ]; then
|
|
printf '%s\0' "${EXTRA[@]}"
|
|
fi
|
|
}
|
|
|
|
run_new() {
|
|
local args prompt=() file="$DEVDESK_STATE_DIR/prompts/$NAME.txt"
|
|
mapfile -d '' args < <(common_args)
|
|
# A first prompt left by `devdesk attach --investigate` (Ops Alerts → Ask
|
|
# Claude, devdesk#39): sent once, right after the session name so options
|
|
# in CLAUDE_ARGS can't swallow it.
|
|
if [ -s "$file" ]; then
|
|
prompt=("$(cat "$file")")
|
|
rm -f "$file"
|
|
fi
|
|
"$CLAUDE_BIN" -n "$NAME" "${prompt[@]}" "${args[@]}"
|
|
}
|
|
|
|
run_resume() {
|
|
local id args fork=()
|
|
id=$(dd_registry_get "$NAME" claude_session_id)
|
|
if [ -z "$id" ]; then
|
|
run_new
|
|
return
|
|
fi
|
|
# Start fresh only when the conversation's file is really gone. Any other
|
|
# failure (an early Ctrl+C, a network error) leaves the recorded id alone
|
|
# and ends at the menu, where ⏎ tries again (devdesk#12).
|
|
if [ -z "$(dd_convo_file "$id")" ]; then
|
|
printf "%sConversation %s is no longer in Claude's history — starting a new one.%s\n" "$MUTED" "$id" "$RESET"
|
|
sleep 1
|
|
run_new
|
|
return
|
|
fi
|
|
mapfile -d '' args < <(common_args)
|
|
# "Fork" from conversation search (devdesk#24): continue as a new
|
|
# conversation, once; the SessionStart hook then records the new id.
|
|
if [ "$(dd_registry_get "$NAME" fork)" = true ]; then
|
|
fork=(--fork-session)
|
|
# shellcheck disable=SC2016 # $n is a jq variable
|
|
dd_registry_update 'del(.sessions[$n].fork)' --arg n "$NAME" 2>/dev/null || true
|
|
fi
|
|
"$CLAUDE_BIN" --resume "$id" "${fork[@]}" "${args[@]}"
|
|
}
|
|
|
|
menu() {
|
|
printf '\n%s◈ Claude exited%s %s(session %s, code %s)%s\n' "$ORANGE" "$RESET" "$MUTED" "$NAME" "$1" "$RESET"
|
|
printf ' %s[⏎]%s resume %s[n]%s new conversation %s[s]%s shell %s[x]%s end session\n' \
|
|
"$CYAN" "$RESET" "$CYAN" "$RESET" "$CYAN" "$RESET" "$CYAN" "$RESET"
|
|
}
|
|
|
|
printf '\033]2;%s\033\134' "$NAME" # pane title (ST = ESC \\)
|
|
|
|
# While a DevDesk tab shows this session, this marker exists and Claude tells
|
|
# Remote Control a client is present, which holds back phone pushes (devdesk#26).
|
|
# Keyed by tmux's session id, which survives renames.
|
|
sid=$(dd_tmux display -p -t "${TMUX_PANE:-}" '#{session_id}' 2>/dev/null)
|
|
if [ -n "$sid" ]; then
|
|
export CLAUDE_CLIENT_PRESENCE_FILE="$DEVDESK_PRESENCE_DIR/${sid#\$}"
|
|
fi
|
|
mode=resume
|
|
while :; do
|
|
NAME=$(dd_current_session) # follow renames
|
|
reload_config
|
|
if [ "$mode" = new ]; then run_new; else run_resume; fi
|
|
rc=$?
|
|
NAME=$(dd_current_session)
|
|
while :; do
|
|
menu "$rc"
|
|
IFS= read -rsn1 key || exit 0
|
|
case $key in
|
|
"") mode=resume; break ;;
|
|
n | N) mode=new; break ;;
|
|
s | S)
|
|
printf '%sShell in %s — exit to return to this menu.%s\n' "$MUTED" "$PWD" "$RESET"
|
|
"${SHELL:-/bin/bash}" -l
|
|
;;
|
|
x | X)
|
|
printf 'End session %s? This stops Claude and forgets the session. [y/N] ' "$NAME"
|
|
IFS= read -rsn1 confirm || exit 0
|
|
echo
|
|
if [ "$confirm" = y ] || [ "$confirm" = Y ]; then
|
|
dd_registry_remove "$NAME"
|
|
tmux kill-session -t "=$NAME" 2>/dev/null
|
|
exit 0
|
|
fi
|
|
;;
|
|
esac
|
|
done
|
|
done
|