feat: session status at a glance, file & image drop, live Ops Alerts (0.4.0)
build-deb / Lint, test, package (push) Successful in 1m51s
build-deb / Deploy to CT 126 (push) Successful in 3s
build-deb / Notify on failure (push) Skipped

- devdesk-state-hook (Claude UserPromptSubmit/PostToolUse/Notification/Stop/
  SessionStart/SessionEnd) records whether each session is working, waiting on
  you or idle; --list carries it. The sidebar shows it, the header shows the
  current session's state, and a pulsing "needs you" chip jumps to sessions
  waiting for input. Session refresh no longer depends on the sidebar being open.
- `devdesk attach --upload SESSION FILE BYTES` receives a file as base64
  terminal input (raw mode, exact length, size-checked, sanitised name) into
  /var/lib/devdesk/uploads/<session>/. The UI uploads dropped or pasted files and
  pastes the path into Claude, which attaches images itself.
- `devdesk alerts` / `--alerts`: firing and pending Grafana alert rules with
  severity, instances, summary and a link. The panel's "Ask Claude" types an
  investigation prompt into the current session.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-07 19:26:06 -04:00
co-authored by Claude Opus 5.5
parent 531b640ae9
commit 03e4a49e78
14 changed files with 625 additions and 44 deletions
+49
View File
@@ -246,6 +246,55 @@ claude_args() {
[ "$output" = "$DEVDESK_PROJECTS_ROOT/alpha/ops" ]
}
@test "state hook tracks working, waiting and idle per session" {
start_server
devdesk new alpha
pane=$(tmux -S "$DEVDESK_SOCKET" display -p -t =alpha:shell '#{pane_id}')
hook() { TMUX="$DEVDESK_SOCKET,1,0" TMUX_PANE="$pane" "$REPO/libexec/devdesk-state-hook"; }
state() { jq -r '.sessions[] | select(.name=="alpha") | .status.state // "none"' <<<"$(devdesk attach --list)"; }
echo '{"hook_event_name":"UserPromptSubmit","prompt":"hi"}' | hook
[ "$(state)" = working ]
echo '{"hook_event_name":"Notification","message":"Claude needs your permission to use Bash"}' | hook
[ "$(state)" = waiting ]
[ "$(jq -r '.message' "$DEVDESK_STATE_DIR/state/alpha.json")" = "Claude needs your permission to use Bash" ]
echo '{"hook_event_name":"PostToolUse","tool_name":"Bash"}' | hook
[ "$(state)" = working ]
echo '{"hook_event_name":"Stop"}' | hook
[ "$(state)" = idle ]
echo '{"hook_event_name":"PreCompact"}' | hook
[ "$(state)" = idle ]
devdesk rename alpha beta
[ -f "$DEVDESK_STATE_DIR/state/beta.json" ] && [ ! -e "$DEVDESK_STATE_DIR/state/alpha.json" ]
devdesk end beta
[ ! -e "$DEVDESK_STATE_DIR/state/beta.json" ]
}
@test "alerts reports unconfigured and unreachable Grafana cleanly" {
run env DEVDESK_GRAFANA_URL= devdesk alerts
[ "$status" -eq 0 ]
[ "$(jq -r '.configured' <<<"$output")" = false ]
run env DEVDESK_GRAFANA_URL=http://127.0.0.1:9 devdesk attach --alerts
[ "$status" -eq 0 ]
[ "$(jq -r '.configured' <<<"$output")" = true ]
[ "$(jq -r '.error | length > 0' <<<"$output")" = true ]
}
@test "upload stores the exact bytes under a sanitised name" {
head -c 50001 /dev/urandom >"$TMP/in.bin"
run bash -c "base64 -w0 '$TMP/in.bin' | devdesk attach --upload alpha '../../etc/My Shot (1).png' 50001"
[ "$status" -eq 0 ]
path=$(tail -n1 <<<"$output" | jq -r .path)
[[ $path == "$DEVDESK_STATE_DIR/uploads/alpha/"*-My_Shot__1_.png ]]
cmp "$TMP/in.bin" "$path"
run bash -c "printf short | devdesk attach --upload alpha x.bin 100"
[ "$status" -ne 0 ]
[[ $output == *"upload incomplete"* ]]
run devdesk attach --upload alpha x.bin 999999999999
[ "$status" -ne 0 ]
run devdesk attach --upload ../x x.bin 10
[ "$status" -ne 0 ]
}
@test "web actions run through attach and answer with JSON" {
start_server
run devdesk attach --new web1 alpha