feat: v0.8.2 clickable paths, alert sessions, ops guard and trail
build-deb / Lint, test, package (push) Successful in 2m45s
build-deb / Deploy to CT 126 (push) Successful in 5s
build-deb / Notify on failure (push) Skipped

- Terminal link provider: printed paths that are files of the session open
  in Files (at the line); the file list is preloaded so the first hover works.
- Alerts: "In a new session" opens alert-<name> and types the prompt once
  Claude is up.
- devdesk-guard PreToolUse(Bash) hook: permissionDecision "ask" for
  destructive commands (verified to prompt in auto mode); DEVDESK_GUARD and
  guard.local.
- State hook logs Bash commands and derives host tags (ssh targets, CT/VM
  ids); --list carries tags, --peek the last commands; UI shows both.

Closes #21
Closes #18
Closes #27

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-10 20:37:46 -04:00
co-authored by Claude Opus 5.5
parent a92d10ace3
commit f82c13612d
21 changed files with 389 additions and 23 deletions
+45
View File
@@ -740,3 +740,48 @@ JSONL
[ "$status" -ne 0 ]
[[ $output == *"answer that first"* ]]
}
@test "the guard asks before destructive commands and lets the rest through (#27)" {
guard() { jq -cn --arg c "$1" '{hook_event_name: "PreToolUse", tool_name: "Bash", tool_input: {command: $c}}' | "$REPO/libexec/devdesk-guard"; }
asks=("pct destroy 105" "ssh root@large1 'pct destroy 105 --purge'" "qm destroy 210" "zfs destroy rpool/data/vm-105-disk-0"
"zpool destroy tank" "ceph osd pool delete rbd rbd --yes-i-really-really-mean-it" "rbd rm vm-105-disk-0"
"rm -rf /" "rm -rf /etc" "sudo rm -r -f /var/" "dd if=/dev/zero of=/dev/sdb bs=1M" "mkfs.ext4 /dev/sdb1" "wipefs -a /dev/sdc"
"git push --force origin main" "git push -f" "psql -c 'drop table users'" "systemctl restart devdesk-tmux"
"ssh pve1 reboot" "systemctl poweroff" "cd /tmp && shutdown -h now")
for c in "${asks[@]}"; do
run guard "$c"
[ "$(jq -r '.hookSpecificOutput.permissionDecision' <<<"$output")" = ask ] || { echo "should ask: $c"; false; }
done
passes=("pct list" "pct exec 105 -- df -h" "zfs list" "rm -rf /tmp/build-cache" "rm -rf node_modules" "rm -rf ./dist"
"git push origin main" "dd if=a.img of=b.img" "echo reboot later" "systemctl restart devdesk" "ceph -s" "ls -la /")
for c in "${passes[@]}"; do
run guard "$c"
[ -z "$output" ] || { echo "should pass: $c -> $output"; false; }
done
# Local rules and the off switch
printf 'nuke-it\tis a local rule\n' >"$TMP/guard.local"
run env DEVDESK_GUARD_LOCAL="$TMP/guard.local" bash -c "jq -cn '{tool_input: {command: \"nuke-it now\"}}' | '$REPO/libexec/devdesk-guard'"
[[ $output == *"is a local rule"* ]]
run env DEVDESK_GUARD=0 bash -c "jq -cn '{tool_input: {command: \"pct destroy 1\"}}' | '$REPO/libexec/devdesk-guard'"
[ -z "$output" ]
}
@test "Bash commands are logged and tag the session with the hosts it works on (#27)" {
start_server
devdesk new alpha
pane=$(tmux -S "$DEVDESK_SOCKET" display -p -t =alpha:shell '#{pane_id}')
bash_cmd() { jq -cn --arg c "$1" '{hook_event_name: "PostToolUse", tool_name: "Bash", tool_input: {command: $c}}' |
TMUX="$DEVDESK_SOCKET,1,0" TMUX_PANE="$pane" "$REPO/libexec/devdesk-state-hook"; }
bash_cmd "ssh -o BatchMode=yes root@large1 'pct exec 167 -- systemctl status authelia'"
bash_cmd "ls -la"
bash_cmd "ssh pve2 qm status 210"
[ "$(wc -l <"$DEVDESK_STATE_DIR/commands/alpha.jsonl")" -eq 3 ]
run devdesk attach --list
[ "$(jq -r '.sessions[] | select(.name == "alpha") | .tags | join(",")' <<<"$output")" = "pve2,VM 210,large1,CT 167" ]
run devdesk attach --peek alpha
[ "$(jq -r '.commands | map(.cmd) | .[1]' <<<"$output")" = "ls -la" ]
devdesk rename alpha beta
[ -f "$DEVDESK_STATE_DIR/commands/beta.jsonl" ] && [ -f "$DEVDESK_STATE_DIR/tags/beta.json" ]
devdesk end beta
[ ! -e "$DEVDESK_STATE_DIR/commands/beta.jsonl" ] && [ ! -e "$DEVDESK_STATE_DIR/tags/beta.json" ]
}