mirror of
https://github.com/grub-basket/SP.git
synced 2026-07-22 07:46:27 +00:00
119 lines
5.3 KiB
Bash
Executable file
119 lines
5.3 KiB
Bash
Executable file
#!/bin/sh
|
|
# In-Obsidian "Claude is testing" banner + countdown, so the user gets a loud,
|
|
# in-app heads-up even when they're not watching the Claude chat.
|
|
#
|
|
# Why: automated Obsidian looks identical to a normal one. If the user pokes the
|
|
# UI mid-test, their clicks collide with the automation. The chat 🔴/✅ handshake
|
|
# only helps if they're looking at the chat — this pushes the warning into
|
|
# Obsidian itself, with a grace period to back off.
|
|
#
|
|
# Usage:
|
|
# scripts/obs-test-guard start [seconds] # show banner, wait N s (default 10), then "testing now"
|
|
# scripts/obs-test-guard ensure # INSTANT banner (no wait) — re-raise it before any
|
|
# # driving eval if you're unsure it's still up
|
|
# scripts/obs-test-guard end # clear banner, show transient "safe to use" notice
|
|
# scripts/obs-test-guard note 'message' # ad-hoc persistent banner with a custom message
|
|
#
|
|
# PROTOCOL: run `start` ONCE at the beginning of a testing session and `end`
|
|
# ONLY when you're completely done and handing control back. Keep the banner up
|
|
# across EVERY intervening eval/screenshot/reload — do NOT `end` then keep
|
|
# testing. If you already ended (or aren't sure), call `ensure` (instant, no
|
|
# countdown — the user was already warned) before the next driving eval.
|
|
#
|
|
# The banner Notice is persistent (duration 0) and tracked on
|
|
# window.__claudeTestBanner so `end` can dismiss it. `start` does the wait in the
|
|
# SHELL (not inside eval — obs eval has a ~1s ceiling).
|
|
|
|
DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
OBS="$DIR/obs"
|
|
|
|
show() {
|
|
# show <js-string-literal-message> — (re)uses the single tracked banner Notice.
|
|
"$OBS" eval code="(() => {
|
|
const msg = $1;
|
|
if (window.__claudeTestBanner && typeof window.__claudeTestBanner.setMessage === 'function') {
|
|
window.__claudeTestBanner.setMessage(msg);
|
|
} else {
|
|
window.__claudeTestBanner = new Notice(msg, 0);
|
|
}
|
|
return 'shown';
|
|
})()" >/dev/null 2>&1
|
|
}
|
|
|
|
# verify — confirm the CLI is talking to the THROWAWAY dev vault before any
|
|
# driving/destructive test, so a misrouted CLI (wrong window focused, multiple
|
|
# vaults open) can never run against a real vault. Two independent checks:
|
|
# 1. vault name === "Claude Dev Vault"
|
|
# 2. the sentinel note exists AND carries its unique token
|
|
# The sentinel makes this robust even if the vault is renamed again — a real
|
|
# vault would have to deliberately contain the token to be mistaken for this one.
|
|
# Prints "verify: OK" + exits 0 on match; LOUD error + exits 1 otherwise.
|
|
EXPECT_VAULT="Claude Dev Vault"
|
|
SENTINEL_TOKEN="claude-dev-vault-7f3a9c2e-do-not-delete"
|
|
verify() {
|
|
out="$("$OBS" eval code="(async () => {
|
|
const name = app.vault.getName();
|
|
let token = '';
|
|
try { token = await app.vault.adapter.read('CLAUDE-DEV-VAULT-SENTINEL.md'); } catch (e) {}
|
|
const ok = name === '$EXPECT_VAULT' && token.indexOf('$SENTINEL_TOKEN') !== -1;
|
|
return JSON.stringify({ ok, name, sentinel: token.indexOf('$SENTINEL_TOKEN') !== -1 });
|
|
})()" 2>/dev/null)"
|
|
case "$out" in
|
|
*'"ok":true'*) echo "verify: OK — $EXPECT_VAULT (name + sentinel confirmed)"; return 0 ;;
|
|
*)
|
|
echo "✋ GUARD ABORT: the running Obsidian is NOT the throwaway dev vault." >&2
|
|
echo " Expected \"$EXPECT_VAULT\" + sentinel note; got: ${out:-<no response from CLI>}" >&2
|
|
echo " Refusing to drive — focus the Claude Dev Vault window (or restore the sentinel) and retry." >&2
|
|
return 1 ;;
|
|
esac
|
|
}
|
|
|
|
case "$1" in
|
|
verify)
|
|
verify; exit $?
|
|
;;
|
|
start)
|
|
verify || exit 1
|
|
secs="${2:-5}"
|
|
show "'🤖 Claude is about to run an automated Stashpad test.\n✋ Please STOP interacting — starting in about ${secs} seconds…'"
|
|
i="$secs"
|
|
while [ "$i" -gt 0 ]; do
|
|
sleep 1
|
|
i=$((i - 1))
|
|
show "'🤖 Claude is testing Stashpad.\n✋ Please don\\'t touch — starting in ${i}s…'"
|
|
done
|
|
show "'🤖 Claude is TESTING Stashpad now.\n✋ Please don\\'t touch until you see the all-clear.'"
|
|
echo "guard: banner up, ${secs}s grace elapsed — safe to drive"
|
|
;;
|
|
ensure)
|
|
# Instant re-raise (no countdown) — for follow-up driving evals when the
|
|
# banner may have been ended. The user was already warned by the session's
|
|
# initial `start`, so no grace period is needed. Still re-verifies the vault
|
|
# (the user could have switched windows between evals).
|
|
verify || exit 1
|
|
show "'🤖 Claude is TESTING Stashpad now.\n✋ Please don\\'t touch until you see the all-clear.'"
|
|
echo "guard: banner ensured (instant)"
|
|
;;
|
|
end)
|
|
"$OBS" eval code="(() => {
|
|
if (window.__claudeTestBanner && typeof window.__claudeTestBanner.hide === 'function') {
|
|
try { window.__claudeTestBanner.hide(); } catch (e) {}
|
|
}
|
|
window.__claudeTestBanner = null;
|
|
new Notice('✅ Safe to use Stashpad again — Claude has finished testing.', 6000);
|
|
return 'cleared';
|
|
})()" >/dev/null 2>&1
|
|
echo "guard: banner cleared, all-clear shown"
|
|
;;
|
|
note)
|
|
[ -n "$2" ] || { echo "usage: obs-test-guard note 'message'" >&2; exit 1; }
|
|
# Escape single quotes in the user message for the JS string literal.
|
|
esc="$(printf '%s' "$2" | sed "s/'/\\\\'/g")"
|
|
show "'$esc'"
|
|
echo "guard: custom banner shown"
|
|
;;
|
|
*)
|
|
echo "usage: obs-test-guard {verify|start [seconds]|ensure|end|note 'message'}" >&2
|
|
exit 1
|
|
;;
|
|
esac
|