mirror of
https://github.com/garrytan/gstack.git
synced 2026-09-09 14:38:59 +02:00
fix: refuse the second registration, and say what leaves the machine
Two things the first cut got wrong.
Memorable's own installer registers the same UserPromptSubmit hook, under its
own name and outside gstack's table. `memorable start`, `memorable setup` and
`memorable install-hooks` all do it, and that is the documented way to install
the CLI, so on most machines it is already there before gstack is asked.
Registering ours beside it ran the same command twice on every prompt: context
injected twice, and the session captured twice against the user's own
extraction allowance. `enable` now looks for it and refuses, naming the entry
and the file it lives in; `status` says who registered it rather than reporting
none. Matched on the command rather than on a tag, for the reason the hook
table already gives: Claude Code rewrites settings and private tags do not
survive it.
The removal instruction says to delete the entry by hand because Memorable has
no command that removes its own hook. `uninstall-hooks` is not a command in
0.5.18; it answers "unknown command".
The README said "Memorable, not gstack, owns the captured data and any network
access", which answers the question by pointing away from it. It now carries a
per-command table of exactly what leaves the machine, in the shape the adopted
gbrain section uses, and it is explicit that the hook makes no network call of
its own, that every row is the third-party CLI acting under its own consent,
and that `gstack-egress` will therefore not show any of it. Under it, the split
between what gstack pin-tests (the gating and the wiring) and what is
Memorable's claim (storage, sending, and what disable and forget erase).
The CHANGELOG entry is removed. This file has never carried an [Unreleased]
heading; every entry is a version and a date, written at release. The text is
in the pull request for whoever cuts the next one.
Three tests added: enable refuses and touches neither consent nor settings when
Memorable already holds the hook, status names that registration, and a foreign
UserPromptSubmit hook is not mistaken for Memorable's.
(cherry picked from commit e0899afa8c)
This commit is contained in:
committed by
Garry Tan
parent
645a870c97
commit
01c5ff2791
+52
-1
@@ -5,6 +5,7 @@ set -u
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
SETTINGS_HOOK="$SCRIPT_DIR/gstack-settings-hook"
|
||||
SETTINGS_FILE="${GSTACK_SETTINGS_FILE:-${CLAUDE_CONFIG_DIR:-$HOME/.claude}/settings.json}"
|
||||
MEMORABLE_HOOK="$ROOT_DIR/hosts/claude/hooks/memorable-user-prompt-hook"
|
||||
HOOK_SOURCE="gstack-memorable"
|
||||
|
||||
@@ -41,6 +42,36 @@ require_memorable() {
|
||||
[ -n "$MEMORABLE_CLI" ] || return 1
|
||||
}
|
||||
|
||||
# Memorable's own installer registers the SAME UserPromptSubmit hook, under
|
||||
# its own name and outside gstack's table. `memorable start`, `memorable setup`
|
||||
# and `memorable install-hooks` all do it, and that is the documented way to
|
||||
# install the CLI — so on most machines it is already there before gstack is
|
||||
# asked. Registering ours beside it runs the same command twice on every
|
||||
# prompt: context injected twice, and the session captured twice against the
|
||||
# user's own extraction allowance.
|
||||
#
|
||||
# Matched on the command, not on a tag, for the same reason the hook table in
|
||||
# gstack-settings-hook matches on command: Claude Code rewrites settings and
|
||||
# private tags do not survive it.
|
||||
memorable_own_hook() {
|
||||
[ -f "$SETTINGS_FILE" ] || return 1
|
||||
GSTACK_SETTINGS_PATH="$SETTINGS_FILE" bun -e '
|
||||
const fs = require("fs");
|
||||
let s = {};
|
||||
try { s = JSON.parse(fs.readFileSync(process.env.GSTACK_SETTINGS_PATH, "utf8")); } catch { process.exit(1); }
|
||||
const groups = (s.hooks && s.hooks.UserPromptSubmit) || [];
|
||||
const ours = process.env.GSTACK_MEMORABLE_HOOK || "";
|
||||
for (const g of groups) {
|
||||
for (const h of (g.hooks || [])) {
|
||||
const c = String(h.command || "");
|
||||
if (c === ours || c.includes("memorable-user-prompt-hook")) continue;
|
||||
if (/memorable/i.test(c) && /hook\s+user-prompt/.test(c)) { console.log(c); process.exit(0); }
|
||||
}
|
||||
}
|
||||
process.exit(1);
|
||||
' 2>/dev/null
|
||||
}
|
||||
|
||||
hook_present() {
|
||||
[ -x "$SETTINGS_HOOK" ] || return 1
|
||||
if "$SETTINGS_HOOK" list-sources 2>/dev/null |
|
||||
@@ -67,6 +98,7 @@ hook_present() {
|
||||
}
|
||||
|
||||
enable_memorable() {
|
||||
local existing
|
||||
require_memorable || return 1
|
||||
[ -x "$SETTINGS_HOOK" ] || {
|
||||
echo "gstack-memorable: missing hook manager: $SETTINGS_HOOK" >&2
|
||||
@@ -77,6 +109,21 @@ enable_memorable() {
|
||||
return 1
|
||||
}
|
||||
|
||||
existing="$(GSTACK_MEMORABLE_HOOK="$MEMORABLE_HOOK" memorable_own_hook)" && {
|
||||
cat >&2 <<EOF
|
||||
gstack-memorable: Memorable already registers this hook itself:
|
||||
$existing
|
||||
|
||||
Registering gstack's as well would run it twice on every prompt: injected
|
||||
twice, and the session captured twice against your extraction allowance.
|
||||
|
||||
Keep the one you have, or hand it to gstack: delete that entry from
|
||||
$SETTINGS_FILE
|
||||
and run this again. Memorable has no command to remove its own hook.
|
||||
EOF
|
||||
return 1
|
||||
}
|
||||
|
||||
"$MEMORABLE_CLI" enable || return $?
|
||||
"$SETTINGS_HOOK" ensure-event \
|
||||
--event UserPromptSubmit \
|
||||
@@ -104,6 +151,7 @@ disable_memorable() {
|
||||
}
|
||||
|
||||
status_memorable() {
|
||||
local existing
|
||||
if MEMORABLE_CLI="$(resolve_memorable 2>/dev/null)" && [ -n "$MEMORABLE_CLI" ]; then
|
||||
printf 'Memorable CLI: available (%s)\n' "$MEMORABLE_CLI"
|
||||
else
|
||||
@@ -111,7 +159,10 @@ status_memorable() {
|
||||
fi
|
||||
|
||||
if hook_present; then
|
||||
echo "Claude UserPromptSubmit hook: registered"
|
||||
echo "Claude UserPromptSubmit hook: registered by gstack"
|
||||
elif existing="$(GSTACK_MEMORABLE_HOOK="$MEMORABLE_HOOK" memorable_own_hook)"; then
|
||||
printf 'Claude UserPromptSubmit hook: registered by Memorable itself (%s)\n' "$existing"
|
||||
echo " gstack is not managing it; 'gstack-memorable enable' would double it."
|
||||
else
|
||||
echo "Claude UserPromptSubmit hook: not registered"
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user