@windyroad/voice-tone 0.2.1 → 0.3.0
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.
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Shared session-announcement marker helpers (P095 / ADR-038).
|
|
3
|
+
#
|
|
4
|
+
# Used by UserPromptSubmit hooks to gate verbose MANDATORY instruction
|
|
5
|
+
# prose behind a once-per-session check. First prompt of a session emits
|
|
6
|
+
# the full block AND calls mark_announced; subsequent prompts see the
|
|
7
|
+
# marker via has_announced and emit only a terse reminder.
|
|
8
|
+
#
|
|
9
|
+
# Why no TTL or drift check (unlike review-gate.sh): announcement is
|
|
10
|
+
# bookkeeping for prose verbosity, not enforcement. PreToolUse gates
|
|
11
|
+
# still block unauthorised edits regardless of announcement state; the
|
|
12
|
+
# delegated agent re-reads policy when it runs. Extending the marker's
|
|
13
|
+
# lifetime across policy changes mid-session is safe — the gate, not
|
|
14
|
+
# the announcement, is load-bearing.
|
|
15
|
+
#
|
|
16
|
+
# Marker path convention: /tmp/${SYSTEM}-announced-${SESSION_ID}
|
|
17
|
+
# (mirrors the /tmp/${SYSTEM}-reviewed-${SESSION_ID} convention from
|
|
18
|
+
# style-guide/voice-tone/risk-scorer review-gate.sh; the -announced-
|
|
19
|
+
# suffix distinguishes announcement markers from clearance markers).
|
|
20
|
+
#
|
|
21
|
+
# Empty SESSION_ID fallback: has_announced returns 1 (not announced,
|
|
22
|
+
# full block emits) and mark_announced is a no-op (no file written).
|
|
23
|
+
# This covers manual hook invocation, test harnesses, and any rare
|
|
24
|
+
# case where Claude Code does not pass a session_id on stdin.
|
|
25
|
+
|
|
26
|
+
# Returns 0 if the hook for SYSTEM has already announced in SESSION_ID,
|
|
27
|
+
# 1 otherwise. Empty SESSION_ID => returns 1 (never announced).
|
|
28
|
+
#
|
|
29
|
+
# Usage: has_announced "architect" "$SESSION_ID"
|
|
30
|
+
has_announced() {
|
|
31
|
+
local SYSTEM="$1"
|
|
32
|
+
local SESSION_ID="$2"
|
|
33
|
+
[ -n "$SESSION_ID" ] || return 1
|
|
34
|
+
[ -f "/tmp/${SYSTEM}-announced-${SESSION_ID}" ]
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
# Writes the announcement marker for SYSTEM in SESSION_ID. Empty
|
|
38
|
+
# SESSION_ID => no-op. Safe to call more than once per session.
|
|
39
|
+
#
|
|
40
|
+
# Usage: mark_announced "architect" "$SESSION_ID"
|
|
41
|
+
mark_announced() {
|
|
42
|
+
local SYSTEM="$1"
|
|
43
|
+
local SESSION_ID="$2"
|
|
44
|
+
[ -n "$SESSION_ID" ] || return 0
|
|
45
|
+
: > "/tmp/${SYSTEM}-announced-${SESSION_ID}"
|
|
46
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
#!/usr/bin/env bats
|
|
2
|
+
|
|
3
|
+
# P095 / ADR-038: voice-tone-eval.sh UserPromptSubmit hook must emit
|
|
4
|
+
# the full MANDATORY block only on the first prompt of a session;
|
|
5
|
+
# subsequent prompts emit a terse reminder.
|
|
6
|
+
|
|
7
|
+
setup() {
|
|
8
|
+
REPO_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../../.." && pwd)"
|
|
9
|
+
HOOK="$REPO_ROOT/packages/voice-tone/hooks/voice-tone-eval.sh"
|
|
10
|
+
|
|
11
|
+
WORKDIR="$(mktemp -d)"
|
|
12
|
+
mkdir -p "$WORKDIR/docs"
|
|
13
|
+
: > "$WORKDIR/docs/VOICE-AND-TONE.md"
|
|
14
|
+
|
|
15
|
+
SID="vt-eval-test-$$-$RANDOM"
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
teardown() {
|
|
19
|
+
rm -f "/tmp/voice-tone-announced-${SID}"
|
|
20
|
+
rm -f "/tmp/voice-tone-announced-${SID}-alt"
|
|
21
|
+
rm -rf "$WORKDIR"
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
run_hook() {
|
|
25
|
+
local sid="$1"
|
|
26
|
+
(cd "$WORKDIR" && echo "{\"session_id\":\"$sid\"}" | bash "$HOOK")
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
@test "voice-tone-eval: first invocation emits the full MANDATORY block" {
|
|
30
|
+
run run_hook "$SID"
|
|
31
|
+
[ "$status" -eq 0 ]
|
|
32
|
+
[ "${#output}" -gt 400 ]
|
|
33
|
+
[[ "$output" == *"MANDATORY VOICE & TONE CHECK"* ]]
|
|
34
|
+
[[ "$output" == *"wr-voice-tone:agent"* ]]
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
@test "voice-tone-eval: first invocation writes the announcement marker" {
|
|
38
|
+
run run_hook "$SID"
|
|
39
|
+
[ -f "/tmp/voice-tone-announced-${SID}" ]
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
@test "voice-tone-eval: second invocation emits only a terse reminder" {
|
|
43
|
+
run_hook "$SID" >/dev/null
|
|
44
|
+
run run_hook "$SID"
|
|
45
|
+
[ "${#output}" -lt 250 ]
|
|
46
|
+
[[ "$output" == *"voice-and-tone"* ]] || [[ "$output" == *"VOICE"* ]] || [[ "$output" == *"voice"* ]]
|
|
47
|
+
[[ "$output" == *"wr-voice-tone:agent"* ]]
|
|
48
|
+
[[ "$output" != *"REQUIRED ACTIONS:"* ]]
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
@test "voice-tone-eval: terse reminder preserves the MANDATORY signal word" {
|
|
52
|
+
run_hook "$SID" >/dev/null
|
|
53
|
+
run run_hook "$SID"
|
|
54
|
+
[[ "$output" == *"MANDATORY"* ]] || [[ "$output" == *"REQUIRED"* ]] || [[ "$output" == *"NON-OPTIONAL"* ]]
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
@test "voice-tone-eval: terse reminder names the trigger artifact" {
|
|
58
|
+
run_hook "$SID" >/dev/null
|
|
59
|
+
run run_hook "$SID"
|
|
60
|
+
[[ "$output" == *"VOICE-AND-TONE.md"* ]]
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
@test "voice-tone-eval: different session_id re-emits the full block" {
|
|
64
|
+
run_hook "$SID" >/dev/null
|
|
65
|
+
local SID2="${SID}-alt"
|
|
66
|
+
run run_hook "$SID2"
|
|
67
|
+
[ "${#output}" -gt 400 ]
|
|
68
|
+
[[ "$output" == *"MANDATORY VOICE & TONE CHECK"* ]]
|
|
69
|
+
rm -f "/tmp/voice-tone-announced-${SID2}"
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
@test "voice-tone-eval: empty session_id emits the full block and writes no marker" {
|
|
73
|
+
run run_hook ""
|
|
74
|
+
[ "${#output}" -gt 400 ]
|
|
75
|
+
[[ "$output" == *"MANDATORY VOICE & TONE CHECK"* ]]
|
|
76
|
+
[ ! -f "/tmp/voice-tone-announced-" ]
|
|
77
|
+
}
|
package/hooks/voice-tone-eval.sh
CHANGED
|
@@ -1,10 +1,25 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
-
# Voice & Tone - UserPromptSubmit hook
|
|
2
|
+
# Voice & Tone - UserPromptSubmit hook (P095 / ADR-038)
|
|
3
3
|
# Detects VOICE-AND-TONE.md in the project and injects delegation instruction.
|
|
4
4
|
# If the file doesn't exist, instructs Claude to create it via the agent.
|
|
5
|
+
#
|
|
6
|
+
# Progressive disclosure (ADR-038): full MANDATORY block on first
|
|
7
|
+
# prompt; terse reminder on subsequent prompts in the same session.
|
|
8
|
+
|
|
9
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
10
|
+
# shellcheck source=lib/session-marker.sh
|
|
11
|
+
source "$SCRIPT_DIR/lib/session-marker.sh"
|
|
12
|
+
|
|
13
|
+
INPUT=$(cat)
|
|
14
|
+
SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // empty' 2>/dev/null || echo "")
|
|
5
15
|
|
|
6
16
|
if [ -f "docs/VOICE-AND-TONE.md" ]; then
|
|
7
|
-
|
|
17
|
+
if has_announced "voice-tone" "$SESSION_ID"; then
|
|
18
|
+
cat <<'HOOK_OUTPUT'
|
|
19
|
+
MANDATORY voice-and-tone gate active (docs/VOICE-AND-TONE.md present). Delegate to wr-voice-tone:agent before editing user-facing copy in .html, .jsx, .tsx, .vue, .svelte, .ejs, .hbs files. See turn-1 instructions for full scope.
|
|
20
|
+
HOOK_OUTPUT
|
|
21
|
+
else
|
|
22
|
+
cat <<'HOOK_OUTPUT'
|
|
8
23
|
INSTRUCTION: MANDATORY VOICE & TONE CHECK. YOU MUST FOLLOW THIS.
|
|
9
24
|
DETECTED: docs/VOICE-AND-TONE.md exists in this project.
|
|
10
25
|
|
|
@@ -22,6 +37,8 @@ REQUIRED ACTIONS:
|
|
|
22
37
|
SCOPE: User-facing files (.html, .jsx, .tsx, .vue, .svelte, .ejs, .hbs).
|
|
23
38
|
Does NOT apply to: .css files, .ts/.js backend files, config files.
|
|
24
39
|
HOOK_OUTPUT
|
|
40
|
+
mark_announced "voice-tone" "$SESSION_ID"
|
|
41
|
+
fi
|
|
25
42
|
else
|
|
26
43
|
# Check if this is a web project (has UI files)
|
|
27
44
|
if ls src/**/*.tsx src/**/*.jsx src/**/*.html 2>/dev/null | head -1 | grep -q .; then
|