Files
devdesk/libexec/devdesk-claude-run
jaredandClaude Opus 5.5 20134c1e58
build-deb / Lint, test, package (push) Successful in 4m59s
build-deb / Deploy to CT 126 (push) Successful in 14s
build-deb / Notify on failure (push) Skipped
feat: v0.12.0 Settings window, per-session Remote Control, delete conversations
- Settings (⋯ → Settings…, palette): theme, terminal text size, interface
  size, notifications, Remote Control & privacy. The ⋯ menu shrinks.
- Remote Control per session: registry choice > Settings default >
  DEVDESK_REMOTE_CONTROL. Switching an idle session restarts its Claude
  on the same conversation; busy sessions change at the next start.
  rc_active comes from the running process; RC tag on sessions.
- --delete-convo: remove a conversation's local transcript and subagent
  files; refused while in use. Delete… in Search past conversations.
Closes #61

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WHRLCCdXYm41Qx5kNhehaP
2026-11-03 21:23:29 -05:00

133 lines
4.8 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"
# Per session since devdesk#61 (Settings → Remote Control).
if [ "$(dd_rc_wanted "$NAME")" = 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)
# Settings changed Remote Control: Claude was stopped to restart on the same
# conversation with the new setting (devdesk#61), no menu.
restart="$DEVDESK_STATE_DIR/restart/$NAME"
if [ -e "$restart" ]; then
rm -f "$restart"
printf '\n%s◈ Restarting Claude with Remote Control %s…%s\n' "$MUTED" "$([ "$(dd_rc_wanted "$NAME")" = 1 ] && echo on || echo off)" "$RESET"
mode=resume
continue
fi
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