@rubytech/create-maxy-code 0.1.569 → 0.1.570
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/payload/platform/plugins/admin/PLUGIN.md +3 -3
- package/payload/platform/plugins/admin/hooks/__tests__/pdf-text-layer-inject.test.sh +177 -0
- package/payload/platform/plugins/admin/hooks/pdf-text-layer-inject.sh +137 -0
- package/payload/platform/plugins/admin/skills/a4-print-documents/SKILL.md +12 -1
- package/payload/platform/plugins/admin/skills/a4-print-documents/pdf-inspect.mjs +113 -0
- package/payload/platform/plugins/admin/skills/whats-new/SKILL.md +6 -0
- package/payload/platform/plugins/docs/references/admin-session.md +15 -0
- package/payload/platform/scripts/__tests__/mailbox-inject-registered.test.sh +15 -39
- package/payload/platform/scripts/__tests__/public-prompt-gate-registered.test.sh +22 -37
- package/payload/platform/scripts/__tests__/public-surface-registered.test.sh +8 -31
- package/payload/platform/scripts/setup-account.sh +7 -62
- package/payload/platform/services/claude-session-manager/dist/blocked-tool-census.d.ts +129 -0
- package/payload/platform/services/claude-session-manager/dist/blocked-tool-census.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/blocked-tool-census.js +344 -0
- package/payload/platform/services/claude-session-manager/dist/blocked-tool-census.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/index.js +20 -0
- package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
- package/payload/platform/templates/account-settings.json +6 -0
- package/payload/server/server.js +750 -656
- package/payload/platform/scripts/lib/__tests__/account-settings-askgate.test.sh +0 -144
- package/payload/platform/scripts/lib/__tests__/account-settings-mailbox-inject.test.sh +0 -166
- package/payload/platform/scripts/lib/__tests__/account-settings-pdf-conformance.test.sh +0 -151
- package/payload/platform/scripts/lib/__tests__/account-settings-public-surface.test.sh +0 -109
- package/payload/platform/scripts/lib/account-settings-askgate.sh +0 -98
- package/payload/platform/scripts/lib/account-settings-mailbox-inject.sh +0 -91
- package/payload/platform/scripts/lib/account-settings-pdf-conformance.sh +0 -98
- package/payload/platform/scripts/lib/account-settings-public-surface.sh +0 -109
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# Regression test for account-settings-askgate.sh (Task 1683).
|
|
3
|
-
#
|
|
4
|
-
# The lib retrofits the AskUserQuestion carrier gate onto existing accounts'
|
|
5
|
-
# .claude/settings.json on every install/upgrade. Task 1552 added the gate to
|
|
6
|
-
# provision-account-dir.sh, but that writer only runs at account-provision time
|
|
7
|
-
# and the upgrade path never re-provisions existing client sub-accounts — so an
|
|
8
|
-
# account provisioned before 1552 keeps a stale AskUserQuestion matcher with no
|
|
9
|
-
# carrier gate and re-wedges on a channel AskUserQuestion (live SiteDesk
|
|
10
|
-
# incident, session d39a22b0, account 2078cb54).
|
|
11
|
-
#
|
|
12
|
-
# Covers:
|
|
13
|
-
# 1. Stale matcher (investigate only) -> updated, carrier appended, idempotent on re-run
|
|
14
|
-
# 2. Correct matcher (both gates) -> already-set, file byte-identical
|
|
15
|
-
# 3. AskUserQuestion matcher absent -> updated, matcher added with both gates
|
|
16
|
-
# 4. No .hooks.PreToolUse -> no-hooks, file untouched
|
|
17
|
-
# 5. File absent -> absent
|
|
18
|
-
# 6. Malformed JSON -> rewrite-failed, file untouched
|
|
19
|
-
# 7. reconcile_all_accounts_askgate -> patches every account.json dir, skips non-account dirs
|
|
20
|
-
#
|
|
21
|
-
# All cases assert the [backfill-1683] status token and the post-state of the file.
|
|
22
|
-
|
|
23
|
-
set -u
|
|
24
|
-
|
|
25
|
-
LIB="$(cd "$(dirname "$0")/.." && pwd)/account-settings-askgate.sh"
|
|
26
|
-
if [ ! -f "$LIB" ]; then
|
|
27
|
-
echo "FAIL: $LIB not found" >&2
|
|
28
|
-
exit 1
|
|
29
|
-
fi
|
|
30
|
-
# shellcheck source=/dev/null
|
|
31
|
-
. "$LIB"
|
|
32
|
-
|
|
33
|
-
CARRIER='bash $PLATFORM_ROOT/plugins/admin/hooks/askuserquestion-channel-carrier-gate.sh'
|
|
34
|
-
INVEST='bash $PLATFORM_ROOT/plugins/admin/hooks/askuserquestion-investigate-gate.sh'
|
|
35
|
-
|
|
36
|
-
PASS=0
|
|
37
|
-
FAIL=0
|
|
38
|
-
TMP="$(mktemp -d)"
|
|
39
|
-
trap 'rm -rf "$TMP"' EXIT
|
|
40
|
-
|
|
41
|
-
ok() { PASS=$((PASS+1)); }
|
|
42
|
-
bad() { FAIL=$((FAIL+1)); echo "FAIL: $1" >&2; }
|
|
43
|
-
|
|
44
|
-
# settings.json with an AskUserQuestion matcher carrying the given command list.
|
|
45
|
-
# Args after $1 are command strings for the AskUserQuestion matcher.
|
|
46
|
-
write_settings() {
|
|
47
|
-
local file="$1"; shift
|
|
48
|
-
local cmds="" c
|
|
49
|
-
for c in "$@"; do
|
|
50
|
-
[ -n "$cmds" ] && cmds="$cmds,"
|
|
51
|
-
cmds="$cmds{\"type\":\"command\",\"command\":\"$c\"}"
|
|
52
|
-
done
|
|
53
|
-
mkdir -p "$(dirname "$file")"
|
|
54
|
-
cat > "$file" <<EOF
|
|
55
|
-
{
|
|
56
|
-
"permissions": { "defaultMode": "bypassPermissions", "allow": [], "deny": [] },
|
|
57
|
-
"hooks": {
|
|
58
|
-
"PreToolUse": [
|
|
59
|
-
{ "matcher": "Write", "hooks": [ { "type": "command", "command": "bash \$P/w.sh" } ] },
|
|
60
|
-
{ "matcher": "AskUserQuestion", "hooks": [ $cmds ] }
|
|
61
|
-
]
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
EOF
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
carrier_count() { grep -cF 'askuserquestion-channel-carrier-gate.sh' "$1" 2>/dev/null || true; }
|
|
68
|
-
askmatcher_present() {
|
|
69
|
-
jq -e '.hooks.PreToolUse | any(.[]; .matcher == "AskUserQuestion")' "$1" >/dev/null 2>&1
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
# --- 1. stale matcher -> updated, then idempotent ---------------------------
|
|
73
|
-
F="$TMP/stale/.claude/settings.json"
|
|
74
|
-
write_settings "$F" "$INVEST"
|
|
75
|
-
OUT="$(ensure_askgate_carrier "$F")"
|
|
76
|
-
case "$OUT" in *status=updated*) ok;; *) bad "1a stale should be updated, got: $OUT";; esac
|
|
77
|
-
[ "$(carrier_count "$F")" -eq 1 ] && ok || bad "1b carrier not appended (count=$(carrier_count "$F"))"
|
|
78
|
-
# investigate still present and first
|
|
79
|
-
if jq -e '.hooks.PreToolUse[] | select(.matcher=="AskUserQuestion") | .hooks[0].command | test("investigate-gate")' "$F" >/dev/null 2>&1; then ok; else bad "1c investigate no longer first"; fi
|
|
80
|
-
OUT2="$(ensure_askgate_carrier "$F")"
|
|
81
|
-
case "$OUT2" in *status=already-set*) ok;; *) bad "1d re-run should be already-set, got: $OUT2";; esac
|
|
82
|
-
[ "$(carrier_count "$F")" -eq 1 ] && ok || bad "1e carrier duplicated on re-run (count=$(carrier_count "$F"))"
|
|
83
|
-
|
|
84
|
-
# --- 2. correct matcher -> already-set, byte-identical ----------------------
|
|
85
|
-
F="$TMP/good/.claude/settings.json"
|
|
86
|
-
write_settings "$F" "$INVEST" "$CARRIER"
|
|
87
|
-
BEFORE="$(cat "$F")"
|
|
88
|
-
OUT="$(ensure_askgate_carrier "$F")"
|
|
89
|
-
case "$OUT" in *status=already-set*) ok;; *) bad "2a good should be already-set, got: $OUT";; esac
|
|
90
|
-
[ "$BEFORE" = "$(cat "$F")" ] && ok || bad "2b already-set file was rewritten"
|
|
91
|
-
|
|
92
|
-
# --- 3. AskUserQuestion matcher absent -> updated, matcher added -------------
|
|
93
|
-
F="$TMP/nomatcher/.claude/settings.json"
|
|
94
|
-
mkdir -p "$(dirname "$F")"
|
|
95
|
-
cat > "$F" <<'EOF'
|
|
96
|
-
{ "permissions": {}, "hooks": { "PreToolUse": [ { "matcher": "Write", "hooks": [] } ] } }
|
|
97
|
-
EOF
|
|
98
|
-
OUT="$(ensure_askgate_carrier "$F")"
|
|
99
|
-
case "$OUT" in *status=updated*) ok;; *) bad "3a nomatcher should be updated, got: $OUT";; esac
|
|
100
|
-
askmatcher_present "$F" && ok || bad "3b AskUserQuestion matcher not added"
|
|
101
|
-
[ "$(carrier_count "$F")" -eq 1 ] && ok || bad "3c carrier missing after add"
|
|
102
|
-
if jq -e '.hooks.PreToolUse[] | select(.matcher=="AskUserQuestion") | .hooks | length == 2' "$F" >/dev/null 2>&1; then ok; else bad "3d added matcher should carry both gates"; fi
|
|
103
|
-
|
|
104
|
-
# --- 4. no PreToolUse -> no-hooks, untouched --------------------------------
|
|
105
|
-
F="$TMP/nohooks/.claude/settings.json"
|
|
106
|
-
mkdir -p "$(dirname "$F")"
|
|
107
|
-
echo '{ "permissions": {}, "hooks": {} }' > "$F"
|
|
108
|
-
BEFORE="$(cat "$F")"
|
|
109
|
-
OUT="$(ensure_askgate_carrier "$F")"
|
|
110
|
-
case "$OUT" in *status=no-hooks*) ok;; *) bad "4a should be no-hooks, got: $OUT";; esac
|
|
111
|
-
[ "$BEFORE" = "$(cat "$F")" ] && ok || bad "4b no-hooks file was rewritten"
|
|
112
|
-
|
|
113
|
-
# --- 5. file absent -> absent ------------------------------------------------
|
|
114
|
-
OUT="$(ensure_askgate_carrier "$TMP/does-not-exist/.claude/settings.json")"
|
|
115
|
-
case "$OUT" in *status=absent*) ok;; *) bad "5 should be absent, got: $OUT";; esac
|
|
116
|
-
|
|
117
|
-
# --- 6. malformed JSON -> rewrite-failed, untouched -------------------------
|
|
118
|
-
F="$TMP/bad/.claude/settings.json"
|
|
119
|
-
mkdir -p "$(dirname "$F")"
|
|
120
|
-
printf '{ this is not json ' > "$F"
|
|
121
|
-
BEFORE="$(cat "$F")"
|
|
122
|
-
OUT="$(ensure_askgate_carrier "$F")"
|
|
123
|
-
case "$OUT" in *status=rewrite-failed*) ok;; *) bad "6a malformed should be rewrite-failed, got: $OUT";; esac
|
|
124
|
-
[ "$BEFORE" = "$(cat "$F")" ] && ok || bad "6b malformed file was mutated"
|
|
125
|
-
[ ! -e "$F.1683.tmp" ] && ok || bad "6c temp file left behind"
|
|
126
|
-
|
|
127
|
-
# --- 7. reconcile_all_accounts_askgate --------------------------------------
|
|
128
|
-
ROOT="$TMP/accts"
|
|
129
|
-
mkdir -p "$ROOT/acctA/.claude" "$ROOT/acctB/.claude" "$ROOT/notanaccount/.claude"
|
|
130
|
-
echo '{"role":"house"}' > "$ROOT/acctA/account.json"
|
|
131
|
-
echo '{"role":"client"}' > "$ROOT/acctB/account.json"
|
|
132
|
-
# notanaccount has no account.json -> must be skipped
|
|
133
|
-
write_settings "$ROOT/acctA/.claude/settings.json" "$INVEST"
|
|
134
|
-
write_settings "$ROOT/acctB/.claude/settings.json" "$INVEST" "$CARRIER"
|
|
135
|
-
write_settings "$ROOT/notanaccount/.claude/settings.json" "$INVEST"
|
|
136
|
-
RECOUT="$(reconcile_all_accounts_askgate "$ROOT")"
|
|
137
|
-
[ "$(carrier_count "$ROOT/acctA/.claude/settings.json")" -eq 1 ] && ok || bad "7a acctA not patched"
|
|
138
|
-
[ "$(carrier_count "$ROOT/acctB/.claude/settings.json")" -eq 1 ] && ok || bad "7b acctB disturbed"
|
|
139
|
-
[ "$(carrier_count "$ROOT/notanaccount/.claude/settings.json")" -eq 0 ] && ok || bad "7c non-account dir was patched"
|
|
140
|
-
case "$RECOUT" in *acctA*status=updated*) ok;; *) bad "7d reconcile did not log acctA updated";; esac
|
|
141
|
-
|
|
142
|
-
echo "----------------------------------------"
|
|
143
|
-
echo "PASS=$PASS FAIL=$FAIL"
|
|
144
|
-
[ "$FAIL" -eq 0 ]
|
|
@@ -1,166 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# Regression test for account-settings-mailbox-inject.sh (Task 2157).
|
|
3
|
-
#
|
|
4
|
-
# The lib retrofits the per-turn <mailboxes> injection hook onto existing
|
|
5
|
-
# accounts' .claude/settings.json on every install. provision-account-dir.sh
|
|
6
|
-
# writes the UserPromptSubmit array at provision time only, and setup-account.sh
|
|
7
|
-
# re-provisions the HOUSE account only, so a client sub-account created by the
|
|
8
|
-
# admin-core account-lifecycle tool never gets the hook without this reconcile.
|
|
9
|
-
#
|
|
10
|
-
# Covers:
|
|
11
|
-
# 1. Array with a sibling hook -> updated, command appended, sibling preserved
|
|
12
|
-
# 2. Re-run on the same file -> already-set, file byte-identical
|
|
13
|
-
# 3. Empty UserPromptSubmit array-> updated, whole entry created
|
|
14
|
-
# 4. Command already present -> already-set, no duplicate
|
|
15
|
-
# 5. No .hooks.UserPromptSubmit -> no-hooks, file untouched
|
|
16
|
-
# 6. File absent -> absent
|
|
17
|
-
# 7. Malformed JSON -> rewrite-failed, file untouched
|
|
18
|
-
# 8. settings.json is an array -> rewrite-failed or no-hooks, file untouched
|
|
19
|
-
# 9. reconcile_all_accounts_* -> patches every account.json dir, skips others
|
|
20
|
-
# 10. No temp file is ever left behind, on any path
|
|
21
|
-
#
|
|
22
|
-
# All cases assert the [backfill-2157] status token and the post-state of the file.
|
|
23
|
-
|
|
24
|
-
set -u
|
|
25
|
-
|
|
26
|
-
LIB="$(cd "$(dirname "$0")/.." && pwd)/account-settings-mailbox-inject.sh"
|
|
27
|
-
if [ ! -f "$LIB" ]; then
|
|
28
|
-
echo "FAIL: $LIB not found" >&2
|
|
29
|
-
exit 1
|
|
30
|
-
fi
|
|
31
|
-
# shellcheck source=/dev/null
|
|
32
|
-
. "$LIB"
|
|
33
|
-
|
|
34
|
-
CMD='bash $PLATFORM_ROOT/plugins/admin/hooks/mailbox-inject.sh'
|
|
35
|
-
SIB='bash $PLATFORM_ROOT/plugins/admin/hooks/datetime-inject.sh'
|
|
36
|
-
|
|
37
|
-
PASS=0
|
|
38
|
-
FAIL=0
|
|
39
|
-
TMP="$(mktemp -d)"
|
|
40
|
-
trap 'rm -rf "$TMP"' EXIT
|
|
41
|
-
|
|
42
|
-
ok() { echo "PASS: $1"; PASS=$((PASS+1)); }
|
|
43
|
-
bad() { echo "FAIL: $1" >&2; FAIL=$((FAIL+1)); }
|
|
44
|
-
|
|
45
|
-
# cmds <file> -> every command string in the UserPromptSubmit array, one per line
|
|
46
|
-
cmds() { jq -r '[.hooks.UserPromptSubmit[]?.hooks[]?.command] | .[]' "$1" 2>/dev/null; }
|
|
47
|
-
status_of() { printf '%s' "$1" | /usr/bin/sed 's/.*status=//'; }
|
|
48
|
-
|
|
49
|
-
# --- 1. array with a sibling hook -> updated, sibling preserved -------------
|
|
50
|
-
F="$TMP/a.json"
|
|
51
|
-
printf '%s' "{\"hooks\":{\"UserPromptSubmit\":[{\"hooks\":[{\"type\":\"command\",\"command\":\"$SIB\"}]}]}}" > "$F"
|
|
52
|
-
OUT=$(ensure_mailbox_inject_hook "$F")
|
|
53
|
-
if [ "$(status_of "$OUT")" = "updated" ] \
|
|
54
|
-
&& [ "$(cmds "$F" | /usr/bin/grep -cF "$CMD")" -eq 1 ] \
|
|
55
|
-
&& [ "$(cmds "$F" | /usr/bin/grep -cF "$SIB")" -eq 1 ]; then
|
|
56
|
-
ok "existing array -> updated, sibling hook preserved"
|
|
57
|
-
else
|
|
58
|
-
bad "case 1 (out=$OUT cmds=$(cmds "$F" | tr '\n' ' '))"
|
|
59
|
-
fi
|
|
60
|
-
|
|
61
|
-
# --- 2. re-run -> already-set, byte-identical -------------------------------
|
|
62
|
-
B=$(shasum "$F" | /usr/bin/cut -d' ' -f1)
|
|
63
|
-
OUT=$(ensure_mailbox_inject_hook "$F")
|
|
64
|
-
A=$(shasum "$F" | /usr/bin/cut -d' ' -f1)
|
|
65
|
-
if [ "$(status_of "$OUT")" = "already-set" ] && [ "$B" = "$A" ] \
|
|
66
|
-
&& [ "$(cmds "$F" | /usr/bin/grep -cF "$CMD")" -eq 1 ]; then
|
|
67
|
-
ok "re-run -> already-set, file byte-identical, no duplicate"
|
|
68
|
-
else
|
|
69
|
-
bad "case 2 (out=$OUT before=$B after=$A)"
|
|
70
|
-
fi
|
|
71
|
-
|
|
72
|
-
# --- 3. empty array -> updated, whole entry created -------------------------
|
|
73
|
-
F="$TMP/b.json"; printf '%s' '{"hooks":{"UserPromptSubmit":[]}}' > "$F"
|
|
74
|
-
OUT=$(ensure_mailbox_inject_hook "$F")
|
|
75
|
-
if [ "$(status_of "$OUT")" = "updated" ] && [ "$(cmds "$F" | /usr/bin/grep -cF "$CMD")" -eq 1 ]; then
|
|
76
|
-
ok "empty array -> updated, entry created"
|
|
77
|
-
else
|
|
78
|
-
bad "case 3 (out=$OUT cmds=$(cmds "$F" | tr '\n' ' '))"
|
|
79
|
-
fi
|
|
80
|
-
|
|
81
|
-
# --- 4. command already present in a later entry -> already-set, no dupe ----
|
|
82
|
-
F="$TMP/c.json"
|
|
83
|
-
printf '%s' "{\"hooks\":{\"UserPromptSubmit\":[{\"hooks\":[{\"type\":\"command\",\"command\":\"$SIB\"}]},{\"hooks\":[{\"type\":\"command\",\"command\":\"$CMD\"}]}]}}" > "$F"
|
|
84
|
-
OUT=$(ensure_mailbox_inject_hook "$F")
|
|
85
|
-
if [ "$(status_of "$OUT")" = "already-set" ] && [ "$(cmds "$F" | /usr/bin/grep -cF "$CMD")" -eq 1 ]; then
|
|
86
|
-
ok "command present in a later entry -> already-set, not duplicated"
|
|
87
|
-
else
|
|
88
|
-
bad "case 4 (out=$OUT cmds=$(cmds "$F" | tr '\n' ' '))"
|
|
89
|
-
fi
|
|
90
|
-
|
|
91
|
-
# --- 5. no UserPromptSubmit -> no-hooks, untouched --------------------------
|
|
92
|
-
F="$TMP/d.json"; printf '%s' '{"permissions":{}}' > "$F"
|
|
93
|
-
B=$(shasum "$F" | /usr/bin/cut -d' ' -f1); OUT=$(ensure_mailbox_inject_hook "$F"); A=$(shasum "$F" | /usr/bin/cut -d' ' -f1)
|
|
94
|
-
if [ "$(status_of "$OUT")" = "no-hooks" ] && [ "$B" = "$A" ]; then
|
|
95
|
-
ok "no UserPromptSubmit array -> no-hooks, file untouched"
|
|
96
|
-
else
|
|
97
|
-
bad "case 5 (out=$OUT)"
|
|
98
|
-
fi
|
|
99
|
-
|
|
100
|
-
# --- 6. file absent -> absent ----------------------------------------------
|
|
101
|
-
OUT=$(ensure_mailbox_inject_hook "$TMP/does-not-exist.json")
|
|
102
|
-
if [ "$(status_of "$OUT")" = "absent" ]; then
|
|
103
|
-
ok "file absent -> absent"
|
|
104
|
-
else
|
|
105
|
-
bad "case 6 (out=$OUT)"
|
|
106
|
-
fi
|
|
107
|
-
|
|
108
|
-
# --- 7. malformed JSON -> rewrite-failed, untouched -------------------------
|
|
109
|
-
F="$TMP/e.json"; printf 'not json at all' > "$F"
|
|
110
|
-
B=$(shasum "$F" | /usr/bin/cut -d' ' -f1); OUT=$(ensure_mailbox_inject_hook "$F"); A=$(shasum "$F" | /usr/bin/cut -d' ' -f1)
|
|
111
|
-
if [ "$(status_of "$OUT")" = "rewrite-failed" ] && [ "$B" = "$A" ]; then
|
|
112
|
-
ok "malformed JSON -> rewrite-failed, file untouched"
|
|
113
|
-
else
|
|
114
|
-
bad "case 7 (out=$OUT)"
|
|
115
|
-
fi
|
|
116
|
-
|
|
117
|
-
# --- 8. settings.json is a JSON array -> not patched, untouched -------------
|
|
118
|
-
F="$TMP/f.json"; printf '%s' '[]' > "$F"
|
|
119
|
-
B=$(shasum "$F" | /usr/bin/cut -d' ' -f1); OUT=$(ensure_mailbox_inject_hook "$F"); A=$(shasum "$F" | /usr/bin/cut -d' ' -f1)
|
|
120
|
-
S=$(status_of "$OUT")
|
|
121
|
-
if { [ "$S" = "no-hooks" ] || [ "$S" = "rewrite-failed" ]; } && [ "$B" = "$A" ]; then
|
|
122
|
-
ok "settings.json that is an array -> not patched, file untouched"
|
|
123
|
-
else
|
|
124
|
-
bad "case 8 (out=$OUT before=$B after=$A)"
|
|
125
|
-
fi
|
|
126
|
-
|
|
127
|
-
# --- 9. reconcile walks account dirs only ----------------------------------
|
|
128
|
-
R="$TMP/accts"; mkdir -p "$R/one/.claude" "$R/two/.claude" "$R/notanaccount/.claude"
|
|
129
|
-
printf '%s' '{}' > "$R/one/account.json"; printf '%s' '{}' > "$R/two/account.json"
|
|
130
|
-
for d in one two notanaccount; do
|
|
131
|
-
printf '%s' "{\"hooks\":{\"UserPromptSubmit\":[{\"hooks\":[{\"type\":\"command\",\"command\":\"$SIB\"}]}]}}" > "$R/$d/.claude/settings.json"
|
|
132
|
-
done
|
|
133
|
-
OUT=$(reconcile_all_accounts_mailbox_inject "$R")
|
|
134
|
-
if [ "$(printf '%s' "$OUT" | /usr/bin/grep -c 'status=updated')" -eq 2 ] \
|
|
135
|
-
&& [ "$(cmds "$R/one/.claude/settings.json" | /usr/bin/grep -cF "$CMD")" -eq 1 ] \
|
|
136
|
-
&& [ "$(cmds "$R/two/.claude/settings.json" | /usr/bin/grep -cF "$CMD")" -eq 1 ] \
|
|
137
|
-
&& [ "$(cmds "$R/notanaccount/.claude/settings.json" | /usr/bin/grep -cF "$CMD")" -eq 0 ]; then
|
|
138
|
-
ok "reconcile patches account.json dirs only"
|
|
139
|
-
else
|
|
140
|
-
bad "case 9 (out=$OUT)"
|
|
141
|
-
fi
|
|
142
|
-
|
|
143
|
-
# --- 10. no temp file left behind on any path ------------------------------
|
|
144
|
-
if [ "$(find "$TMP" -name '*.2157.tmp' | /usr/bin/wc -l | tr -d ' ')" -eq 0 ]; then
|
|
145
|
-
ok "no .2157.tmp file left behind on any path"
|
|
146
|
-
else
|
|
147
|
-
bad "case 10 (strays: $(find "$TMP" -name '*.2157.tmp'))"
|
|
148
|
-
fi
|
|
149
|
-
|
|
150
|
-
# --- 11. never returns non-zero, even on the failure paths -----------------
|
|
151
|
-
ensure_mailbox_inject_hook "$TMP/e.json" >/dev/null; r1=$?
|
|
152
|
-
ensure_mailbox_inject_hook "$TMP/nope.json" >/dev/null; r2=$?
|
|
153
|
-
reconcile_all_accounts_mailbox_inject "$TMP/no-such-dir" >/dev/null; r3=$?
|
|
154
|
-
if [ "$r1" -eq 0 ] && [ "$r2" -eq 0 ] && [ "$r3" -eq 0 ]; then
|
|
155
|
-
ok "every path returns 0, so a caller running set -e is never aborted"
|
|
156
|
-
else
|
|
157
|
-
bad "case 11 (malformed=$r1 absent=$r2 nodir=$r3)"
|
|
158
|
-
fi
|
|
159
|
-
|
|
160
|
-
echo "----"
|
|
161
|
-
echo "PASS=$PASS FAIL=$FAIL"
|
|
162
|
-
if [ $((PASS+FAIL)) -ne 11 ]; then
|
|
163
|
-
echo "FAIL: expected 11 cases, ran $((PASS+FAIL))" >&2
|
|
164
|
-
exit 1
|
|
165
|
-
fi
|
|
166
|
-
[ "$FAIL" -eq 0 ]
|
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# Regression test for account-settings-pdf-conformance.sh (Task 1929).
|
|
3
|
-
#
|
|
4
|
-
# The lib retrofits the quote-render PDF-conformance PostToolUse matcher onto
|
|
5
|
-
# existing accounts' .claude/settings.json on every install/upgrade. Task 1922
|
|
6
|
-
# added the matcher to provision-account-dir.sh, but that writer only runs at
|
|
7
|
-
# account-provision time and the upgrade path re-provisions only the house
|
|
8
|
-
# account -- so an account provisioned before 1922 keeps a settings.json without
|
|
9
|
-
# the browser-pdf-save PostToolUse matcher and never runs the PDF gate (live
|
|
10
|
-
# glsmith is such an account).
|
|
11
|
-
#
|
|
12
|
-
# Covers:
|
|
13
|
-
# 1. Stale matcher (browser-pdf-save present, conformance cmd absent) -> updated, appended, idempotent
|
|
14
|
-
# 2. Correct matcher (conformance cmd present) -> already-set, byte-identical
|
|
15
|
-
# 3. browser-pdf-save matcher absent (broad mcp__.* present) -> updated, matcher added, wildcard untouched
|
|
16
|
-
# 4. No .hooks.PostToolUse -> no-hooks, untouched
|
|
17
|
-
# 5. File absent -> absent
|
|
18
|
-
# 6. Malformed JSON -> rewrite-failed, untouched, no temp
|
|
19
|
-
# 7. reconcile_all_accounts_pdf_conformance -> patches every account.json dir, skips others
|
|
20
|
-
#
|
|
21
|
-
# All cases assert the [backfill-1929] status token and the post-state of the file.
|
|
22
|
-
|
|
23
|
-
set -u
|
|
24
|
-
|
|
25
|
-
LIB="$(cd "$(dirname "$0")/.." && pwd)/account-settings-pdf-conformance.sh"
|
|
26
|
-
if [ ! -f "$LIB" ]; then
|
|
27
|
-
echo "FAIL: $LIB not found" >&2
|
|
28
|
-
exit 1
|
|
29
|
-
fi
|
|
30
|
-
# shellcheck source=/dev/null
|
|
31
|
-
. "$LIB"
|
|
32
|
-
|
|
33
|
-
MATCHER='mcp__plugin_browser_browser__browser-pdf-save'
|
|
34
|
-
CONFORMANCE='bash $PLATFORM_ROOT/plugins/admin/hooks/quote-render-pdf-conformance.sh'
|
|
35
|
-
|
|
36
|
-
PASS=0
|
|
37
|
-
FAIL=0
|
|
38
|
-
TMP="$(mktemp -d)"
|
|
39
|
-
trap 'rm -rf "$TMP"' EXIT
|
|
40
|
-
|
|
41
|
-
ok() { PASS=$((PASS+1)); }
|
|
42
|
-
bad() { FAIL=$((FAIL+1)); echo "FAIL: $1" >&2; }
|
|
43
|
-
|
|
44
|
-
# settings.json with a PostToolUse browser-pdf-save matcher carrying the given
|
|
45
|
-
# command list. Args after $1 are command strings for that matcher.
|
|
46
|
-
write_settings() {
|
|
47
|
-
local file="$1"; shift
|
|
48
|
-
local cmds="" c
|
|
49
|
-
for c in "$@"; do
|
|
50
|
-
[ -n "$cmds" ] && cmds="$cmds,"
|
|
51
|
-
cmds="$cmds{\"type\":\"command\",\"command\":\"$c\"}"
|
|
52
|
-
done
|
|
53
|
-
mkdir -p "$(dirname "$file")"
|
|
54
|
-
cat > "$file" <<EOF
|
|
55
|
-
{
|
|
56
|
-
"permissions": { "defaultMode": "bypassPermissions", "allow": [], "deny": [] },
|
|
57
|
-
"hooks": {
|
|
58
|
-
"PostToolUse": [
|
|
59
|
-
{ "matcher": "Bash", "hooks": [ { "type": "command", "command": "bash \$P/b.sh" } ] },
|
|
60
|
-
{ "matcher": "$MATCHER", "hooks": [ $cmds ] },
|
|
61
|
-
{ "matcher": "mcp__.*", "hooks": [ { "type": "command", "command": "bash \$P/missing.sh" } ] }
|
|
62
|
-
]
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
EOF
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
conformance_count() { grep -cF 'quote-render-pdf-conformance.sh' "$1" 2>/dev/null || true; }
|
|
69
|
-
pdfmatcher_present() {
|
|
70
|
-
jq -e --arg m "$MATCHER" '.hooks.PostToolUse | any(.[]; .matcher == $m)' "$1" >/dev/null 2>&1
|
|
71
|
-
}
|
|
72
|
-
wildcard_present() {
|
|
73
|
-
jq -e '.hooks.PostToolUse | any(.[]; .matcher == "mcp__.*")' "$1" >/dev/null 2>&1
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
# --- 1. stale matcher -> updated, then idempotent ---------------------------
|
|
77
|
-
F="$TMP/stale/.claude/settings.json"
|
|
78
|
-
write_settings "$F"
|
|
79
|
-
OUT="$(ensure_pdf_conformance_matcher "$F")"
|
|
80
|
-
case "$OUT" in *status=updated*) ok;; *) bad "1a stale should be updated, got: $OUT";; esac
|
|
81
|
-
[ "$(conformance_count "$F")" -eq 1 ] && ok || bad "1b conformance not appended (count=$(conformance_count "$F"))"
|
|
82
|
-
OUT2="$(ensure_pdf_conformance_matcher "$F")"
|
|
83
|
-
case "$OUT2" in *status=already-set*) ok;; *) bad "1c re-run should be already-set, got: $OUT2";; esac
|
|
84
|
-
[ "$(conformance_count "$F")" -eq 1 ] && ok || bad "1d conformance duplicated on re-run (count=$(conformance_count "$F"))"
|
|
85
|
-
|
|
86
|
-
# --- 2. correct matcher -> already-set, byte-identical ----------------------
|
|
87
|
-
F="$TMP/good/.claude/settings.json"
|
|
88
|
-
write_settings "$F" "$CONFORMANCE"
|
|
89
|
-
BEFORE="$(cat "$F")"
|
|
90
|
-
OUT="$(ensure_pdf_conformance_matcher "$F")"
|
|
91
|
-
case "$OUT" in *status=already-set*) ok;; *) bad "2a good should be already-set, got: $OUT";; esac
|
|
92
|
-
[ "$BEFORE" = "$(cat "$F")" ] && ok || bad "2b already-set file was rewritten"
|
|
93
|
-
|
|
94
|
-
# --- 3. browser-pdf-save matcher absent (wildcard present) -> updated, added --
|
|
95
|
-
F="$TMP/nomatcher/.claude/settings.json"
|
|
96
|
-
mkdir -p "$(dirname "$F")"
|
|
97
|
-
cat > "$F" <<'EOF'
|
|
98
|
-
{ "permissions": {}, "hooks": { "PostToolUse": [
|
|
99
|
-
{ "matcher": "Bash", "hooks": [] },
|
|
100
|
-
{ "matcher": "mcp__.*", "hooks": [ { "type": "command", "command": "bash $P/missing.sh" } ] }
|
|
101
|
-
] } }
|
|
102
|
-
EOF
|
|
103
|
-
OUT="$(ensure_pdf_conformance_matcher "$F")"
|
|
104
|
-
case "$OUT" in *status=updated*) ok;; *) bad "3a nomatcher should be updated, got: $OUT";; esac
|
|
105
|
-
pdfmatcher_present "$F" && ok || bad "3b browser-pdf-save matcher not added"
|
|
106
|
-
[ "$(conformance_count "$F")" -eq 1 ] && ok || bad "3c conformance missing after add"
|
|
107
|
-
wildcard_present "$F" && ok || bad "3d wildcard matcher was disturbed"
|
|
108
|
-
# added matcher carries exactly the one conformance command
|
|
109
|
-
if jq -e --arg m "$MATCHER" '.hooks.PostToolUse[] | select(.matcher==$m) | .hooks | length == 1' "$F" >/dev/null 2>&1; then ok; else bad "3e added matcher should carry exactly one command"; fi
|
|
110
|
-
|
|
111
|
-
# --- 4. no PostToolUse -> no-hooks, untouched -------------------------------
|
|
112
|
-
F="$TMP/nohooks/.claude/settings.json"
|
|
113
|
-
mkdir -p "$(dirname "$F")"
|
|
114
|
-
echo '{ "permissions": {}, "hooks": {} }' > "$F"
|
|
115
|
-
BEFORE="$(cat "$F")"
|
|
116
|
-
OUT="$(ensure_pdf_conformance_matcher "$F")"
|
|
117
|
-
case "$OUT" in *status=no-hooks*) ok;; *) bad "4a should be no-hooks, got: $OUT";; esac
|
|
118
|
-
[ "$BEFORE" = "$(cat "$F")" ] && ok || bad "4b no-hooks file was rewritten"
|
|
119
|
-
|
|
120
|
-
# --- 5. file absent -> absent ------------------------------------------------
|
|
121
|
-
OUT="$(ensure_pdf_conformance_matcher "$TMP/does-not-exist/.claude/settings.json")"
|
|
122
|
-
case "$OUT" in *status=absent*) ok;; *) bad "5 should be absent, got: $OUT";; esac
|
|
123
|
-
|
|
124
|
-
# --- 6. malformed JSON -> rewrite-failed, untouched -------------------------
|
|
125
|
-
F="$TMP/bad/.claude/settings.json"
|
|
126
|
-
mkdir -p "$(dirname "$F")"
|
|
127
|
-
printf '{ this is not json ' > "$F"
|
|
128
|
-
BEFORE="$(cat "$F")"
|
|
129
|
-
OUT="$(ensure_pdf_conformance_matcher "$F")"
|
|
130
|
-
case "$OUT" in *status=rewrite-failed*) ok;; *) bad "6a malformed should be rewrite-failed, got: $OUT";; esac
|
|
131
|
-
[ "$BEFORE" = "$(cat "$F")" ] && ok || bad "6b malformed file was mutated"
|
|
132
|
-
[ ! -e "$F.1929.tmp" ] && ok || bad "6c temp file left behind"
|
|
133
|
-
|
|
134
|
-
# --- 7. reconcile_all_accounts_pdf_conformance ------------------------------
|
|
135
|
-
ROOT="$TMP/accts"
|
|
136
|
-
mkdir -p "$ROOT/acctA/.claude" "$ROOT/acctB/.claude" "$ROOT/notanaccount/.claude"
|
|
137
|
-
echo '{"role":"house"}' > "$ROOT/acctA/account.json"
|
|
138
|
-
echo '{"role":"client"}' > "$ROOT/acctB/account.json"
|
|
139
|
-
# notanaccount has no account.json -> must be skipped
|
|
140
|
-
write_settings "$ROOT/acctA/.claude/settings.json"
|
|
141
|
-
write_settings "$ROOT/acctB/.claude/settings.json" "$CONFORMANCE"
|
|
142
|
-
write_settings "$ROOT/notanaccount/.claude/settings.json"
|
|
143
|
-
RECOUT="$(reconcile_all_accounts_pdf_conformance "$ROOT")"
|
|
144
|
-
[ "$(conformance_count "$ROOT/acctA/.claude/settings.json")" -eq 1 ] && ok || bad "7a acctA not patched"
|
|
145
|
-
[ "$(conformance_count "$ROOT/acctB/.claude/settings.json")" -eq 1 ] && ok || bad "7b acctB disturbed"
|
|
146
|
-
[ "$(conformance_count "$ROOT/notanaccount/.claude/settings.json")" -eq 0 ] && ok || bad "7c non-account dir was patched"
|
|
147
|
-
case "$RECOUT" in *acctA*status=updated*) ok;; *) bad "7d reconcile did not log acctA updated";; esac
|
|
148
|
-
|
|
149
|
-
echo "----------------------------------------"
|
|
150
|
-
echo "PASS=$PASS FAIL=$FAIL"
|
|
151
|
-
[ "$FAIL" -eq 0 ]
|
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# Task 2339 — the backfill must reach an account provisioned before the hook
|
|
3
|
-
# existed, must be a byte-identical no-op on a second run, and must never damage
|
|
4
|
-
# a file it cannot patch.
|
|
5
|
-
set -uo pipefail
|
|
6
|
-
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
7
|
-
# shellcheck source=../account-settings-public-surface.sh
|
|
8
|
-
. "$DIR/../account-settings-public-surface.sh"
|
|
9
|
-
PASS=0; FAIL=0
|
|
10
|
-
ok() { echo "PASS: $1"; PASS=$((PASS+1)); }
|
|
11
|
-
bad() { echo "FAIL: $1" >&2; FAIL=$((FAIL+1)); }
|
|
12
|
-
CMD='bash $PLATFORM_ROOT/plugins/admin/hooks/public-tool-surface.sh'
|
|
13
|
-
|
|
14
|
-
# A pre-2339 settings file: all three arrays present, none carrying the command.
|
|
15
|
-
mkpre() {
|
|
16
|
-
local f="$1"
|
|
17
|
-
cat > "$f" <<'JSON'
|
|
18
|
-
{
|
|
19
|
-
"permissions": { "defaultMode": "bypassPermissions", "allow": [], "deny": [] },
|
|
20
|
-
"hooks": {
|
|
21
|
-
"PreToolUse": [
|
|
22
|
-
{ "matcher": "Bash", "hooks": [ { "type": "command", "command": "bash $PLATFORM_ROOT/plugins/admin/hooks/quote-engine-gate.sh" } ] },
|
|
23
|
-
{ "hooks": [ { "type": "command", "command": "bash $PLATFORM_ROOT/plugins/admin/hooks/archive-ingest-surface-gate.sh" } ] }
|
|
24
|
-
],
|
|
25
|
-
"UserPromptSubmit": [
|
|
26
|
-
{ "hooks": [ { "type": "command", "command": "bash $PLATFORM_ROOT/plugins/admin/hooks/datetime-inject.sh" } ] }
|
|
27
|
-
],
|
|
28
|
-
"Stop": [
|
|
29
|
-
{ "hooks": [ { "type": "command", "command": "bash $PLATFORM_ROOT/plugins/admin/hooks/prompt-optimiser-compliance.sh" } ] }
|
|
30
|
-
]
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
JSON
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
D=$(mktemp -d)
|
|
37
|
-
F="$D/settings.json"
|
|
38
|
-
mkpre "$F"
|
|
39
|
-
OUT=$(ensure_public_surface_hook "$F")
|
|
40
|
-
if [[ "$OUT" == *"status=updated"* ]]; then ok "a pre-2339 file is updated"; else bad "expected updated, got: $OUT"; fi
|
|
41
|
-
if [ "$(jq -r --arg c "$CMD" '[.hooks.PreToolUse[] | select(has("matcher") | not) | .hooks[] | select(.command == $c)] | length' "$F")" = "1" ]; then
|
|
42
|
-
ok "the command lands on the matcher-less PreToolUse entry"
|
|
43
|
-
else
|
|
44
|
-
bad "command not on the matcher-less PreToolUse entry"
|
|
45
|
-
fi
|
|
46
|
-
if [ "$(jq -r --arg c "$CMD" '[.hooks.PreToolUse[] | select(has("matcher")) | .hooks[] | select(.command == $c)] | length' "$F")" = "0" ]; then
|
|
47
|
-
ok "no matcher-scoped entry gained the command"
|
|
48
|
-
else
|
|
49
|
-
bad "a matcher-scoped entry gained the command"
|
|
50
|
-
fi
|
|
51
|
-
if [ "$(jq -r --arg c "$CMD" '[.hooks.Stop[].hooks[] | select(.command == $c)] | length' "$F")" = "1" ]; then
|
|
52
|
-
ok "the command lands on the Stop entry"
|
|
53
|
-
else
|
|
54
|
-
bad "command absent from Stop"
|
|
55
|
-
fi
|
|
56
|
-
if [ "$(jq -r --arg c "$CMD" '[.hooks.UserPromptSubmit[].hooks[] | select(.command == $c)] | length' "$F")" = "1" ]; then
|
|
57
|
-
ok "the command lands on the UserPromptSubmit entry"
|
|
58
|
-
else
|
|
59
|
-
bad "command absent from UserPromptSubmit"
|
|
60
|
-
fi
|
|
61
|
-
|
|
62
|
-
BEFORE=$(cat "$F")
|
|
63
|
-
OUT=$(ensure_public_surface_hook "$F")
|
|
64
|
-
if [[ "$OUT" == *"status=already-set"* ]]; then ok "a second run reports already-set"; else bad "expected already-set, got: $OUT"; fi
|
|
65
|
-
if [ "$BEFORE" = "$(cat "$F")" ]; then ok "a second run leaves the file byte-identical"; else bad "second run rewrote the file"; fi
|
|
66
|
-
|
|
67
|
-
# Missing file.
|
|
68
|
-
OUT=$(ensure_public_surface_hook "$D/nope.json")
|
|
69
|
-
if [[ "$OUT" == *"status=absent"* ]]; then ok "a missing file reports absent"; else bad "expected absent, got: $OUT"; fi
|
|
70
|
-
|
|
71
|
-
# Valid JSON with no hooks block.
|
|
72
|
-
echo '{"permissions":{}}' > "$D/nohooks.json"
|
|
73
|
-
OUT=$(ensure_public_surface_hook "$D/nohooks.json")
|
|
74
|
-
if [[ "$OUT" == *"status=no-hooks"* ]]; then ok "a hookless file reports no-hooks"; else bad "expected no-hooks, got: $OUT"; fi
|
|
75
|
-
|
|
76
|
-
# Malformed JSON is reported and left untouched.
|
|
77
|
-
printf '{ not json' > "$D/bad.json"
|
|
78
|
-
BAD_BEFORE=$(cat "$D/bad.json")
|
|
79
|
-
OUT=$(ensure_public_surface_hook "$D/bad.json")
|
|
80
|
-
if [[ "$OUT" == *"status=rewrite-failed"* ]]; then ok "a malformed file reports rewrite-failed"; else bad "expected rewrite-failed, got: $OUT"; fi
|
|
81
|
-
if [ "$BAD_BEFORE" = "$(cat "$D/bad.json")" ]; then ok "a malformed file is left untouched"; else bad "malformed file was modified"; fi
|
|
82
|
-
|
|
83
|
-
# An empty Stop array gains a whole entry.
|
|
84
|
-
mkpre "$D/emptystop.json"
|
|
85
|
-
jq '.hooks.Stop = []' "$D/emptystop.json" > "$D/es.tmp" && mv "$D/es.tmp" "$D/emptystop.json"
|
|
86
|
-
ensure_public_surface_hook "$D/emptystop.json" >/dev/null
|
|
87
|
-
if [ "$(jq -r --arg c "$CMD" '[.hooks.Stop[].hooks[] | select(.command == $c)] | length' "$D/emptystop.json")" = "1" ]; then
|
|
88
|
-
ok "an empty Stop array gains a whole entry"
|
|
89
|
-
else
|
|
90
|
-
bad "empty Stop array not handled"
|
|
91
|
-
fi
|
|
92
|
-
|
|
93
|
-
# A PreToolUse array with no matcher-less entry gains one.
|
|
94
|
-
mkpre "$D/nocatchall.json"
|
|
95
|
-
jq 'del(.hooks.PreToolUse[] | select(has("matcher") | not))' "$D/nocatchall.json" > "$D/nc.tmp" && mv "$D/nc.tmp" "$D/nocatchall.json"
|
|
96
|
-
ensure_public_surface_hook "$D/nocatchall.json" >/dev/null
|
|
97
|
-
if [ "$(jq -r --arg c "$CMD" '[.hooks.PreToolUse[] | select(has("matcher") | not) | .hooks[] | select(.command == $c)] | length' "$D/nocatchall.json")" = "1" ]; then
|
|
98
|
-
ok "a PreToolUse array with no matcher-less entry gains one"
|
|
99
|
-
else
|
|
100
|
-
bad "missing catch-all entry not handled"
|
|
101
|
-
fi
|
|
102
|
-
|
|
103
|
-
echo "----"
|
|
104
|
-
echo "PASS=$PASS FAIL=$FAIL"
|
|
105
|
-
if [ $((PASS+FAIL)) -ne 13 ]; then
|
|
106
|
-
echo "FAIL: expected 13 checks, ran $((PASS+FAIL))" >&2
|
|
107
|
-
exit 1
|
|
108
|
-
fi
|
|
109
|
-
[ "$FAIL" -eq 0 ]
|