@windyroad/voice-tone 0.5.6 → 0.5.7-preview.576
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/agents/agent.md
CHANGED
|
@@ -15,11 +15,24 @@ You are the Voice and Tone Lead. You review proposed copy changes against the pr
|
|
|
15
15
|
|
|
16
16
|
## Your Role
|
|
17
17
|
|
|
18
|
-
1. Read `docs/VOICE-AND-TONE.md` in the project to load the current guide
|
|
18
|
+
1. Read `docs/VOICE-AND-TONE.md` in the project to load the current guide. **If the file does not exist, see "Missing Guide Handling" below** — return PASS-with-advisory and stop. Do not proceed to step 2 in that case.
|
|
19
19
|
2. Read the file(s) being edited to understand the existing copy and context
|
|
20
20
|
3. Review proposed changes against every section of the guide
|
|
21
21
|
4. Report: OK if compliant, or list specific violations with suggested fixes
|
|
22
22
|
|
|
23
|
+
## Missing Guide Handling (P200)
|
|
24
|
+
|
|
25
|
+
If `docs/VOICE-AND-TONE.md` does not exist in the project, the voice-tone gate is **inactive**. Return PASS with a one-line advisory — do NOT return FAIL on a blanket "guide is missing" basis. This mirrors the architect agent's graceful pattern ("If `docs/decisions/` itself does not exist, that is fine") and aligns with ADR-028's per-evaluator advisory-only fallback already implemented in `external-comms-gate.sh` (line 272 — "Advisory-only fallback when policy file is absent").
|
|
26
|
+
|
|
27
|
+
Sibling-consistent reasoning: the project has opted not to install a voice-tone guide; the agent cannot review against rules that do not exist. The protective surface for projects that DO adopt voice-tone is `voice-tone-enforce-edit.sh` — which still blocks edits when the policy is missing (a separate concern; this agent does not override that hook). The agent's job is to review against the guide, and when the guide is absent there is nothing to review against.
|
|
28
|
+
|
|
29
|
+
Output shape when the guide is absent:
|
|
30
|
+
|
|
31
|
+
> **Voice & Tone Review: PASS**
|
|
32
|
+
> voice-tone gate inactive — no `docs/VOICE-AND-TONE.md` present. Run `/wr-voice-tone:update-guide` to enable voice-tone reviews.
|
|
33
|
+
|
|
34
|
+
Then `printf 'PASS' > /tmp/voice-tone-verdict` and stop.
|
|
35
|
+
|
|
23
36
|
## What You Check
|
|
24
37
|
|
|
25
38
|
All review criteria come from `docs/VOICE-AND-TONE.md`. Read the guide first and apply its sections. Typical sections include:
|
package/agents/external-comms.md
CHANGED
|
@@ -24,7 +24,19 @@ The invoking skill (`/wr-voice-tone:assess-external-comms`) or the agent that hi
|
|
|
24
24
|
|
|
25
25
|
Read `docs/VOICE-AND-TONE.md` (project root) to get the authoritative voice profile. Typical sections include voice principles, tone by context, banned patterns, word list / terminology, and language/locale conventions.
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
## Missing Guide Handling (P200)
|
|
28
|
+
|
|
29
|
+
If `docs/VOICE-AND-TONE.md` is absent, return a PASS verdict with a one-line advisory — do NOT return FAIL on a blanket "guide is missing" basis. The canonical `external-comms-gate.sh` hook handles this case by permitting with an advisory message (line 272), and your verdict must agree with that gate posture so the marker hook does not blanket-deny a session whose project has opted out of voice-tone enforcement.
|
|
30
|
+
|
|
31
|
+
This aligns with ADR-028's per-evaluator advisory-only fallback ("if `docs/VOICE-AND-TONE.md` is absent, voice-tone review is advisory-only... its verdict file reads PASS unconditionally"). The fallback is sibling-consistent with the architect agent's graceful "no prior decisions are recorded" pattern.
|
|
32
|
+
|
|
33
|
+
Output shape when the guide is absent — a terse note in prose followed by the structured verdict block:
|
|
34
|
+
|
|
35
|
+
> voice-tone gate inactive — no `docs/VOICE-AND-TONE.md` present. Run `/wr-voice-tone:update-guide` to enable voice-tone reviews.
|
|
36
|
+
>
|
|
37
|
+
> EXTERNAL_COMMS_VOICE_TONE_VERDICT: PASS
|
|
38
|
+
|
|
39
|
+
Stop after emitting that block — do NOT proceed with draft review when there is no policy to review against.
|
|
28
40
|
|
|
29
41
|
## Review process
|
|
30
42
|
|
|
@@ -14,6 +14,14 @@
|
|
|
14
14
|
# - gh api .../comments (any REST surface accepting prose)
|
|
15
15
|
# - npm publish (README / package metadata to npm)
|
|
16
16
|
# - PreToolUse:Write|Edit on .changeset/*.md (P073 — gates author-time)
|
|
17
|
+
# - git commit -m / --message (incl. HEREDOC) (P082 Phase 1 — commit message
|
|
18
|
+
# body reaches every reader of git
|
|
19
|
+
# log, PR commits tab, release-page
|
|
20
|
+
# auto-notes, CHANGELOG. Editor
|
|
21
|
+
# flow is out of scope per P082 SC1
|
|
22
|
+
# — message is written to
|
|
23
|
+
# .git/COMMIT_EDITMSG AFTER
|
|
24
|
+
# PreToolUse, nothing to read.)
|
|
17
25
|
#
|
|
18
26
|
# Gate behaviour:
|
|
19
27
|
# 1. BYPASS_RISK_GATE=1 short-circuits the gate (consistent with git-push-gate.sh).
|
|
@@ -144,20 +152,59 @@ except Exception:
|
|
|
144
152
|
SURFACE="gh-api-comments"
|
|
145
153
|
elif echo "$COMMAND" | grep -qE '(^|;|&&|\|\|)\s*npm publish(\s|$)'; then
|
|
146
154
|
SURFACE="npm-publish"
|
|
155
|
+
elif echo "$COMMAND" | grep -qE '(^|;|&&|\|\|)\s*git commit(\s|$)'; then
|
|
156
|
+
# P082 Phase 1: gate `git commit -m / --message / HEREDOC` so commit
|
|
157
|
+
# message bodies are reviewed by the voice-tone + risk evaluators
|
|
158
|
+
# before they land in git log / PR commits tab / release notes /
|
|
159
|
+
# CHANGELOG. Editor flow (bare `git commit`) is out of scope per
|
|
160
|
+
# P082 SC1 — git writes .git/COMMIT_EDITMSG AFTER PreToolUse fires,
|
|
161
|
+
# so there's no body to extract at gate time. Skip silently when
|
|
162
|
+
# neither -m nor --message is present.
|
|
163
|
+
if echo "$COMMAND" | grep -qE '(\s|^)(-m|--message)(\s|=)'; then
|
|
164
|
+
SURFACE="git-commit-message"
|
|
165
|
+
else
|
|
166
|
+
exit 0
|
|
167
|
+
fi
|
|
147
168
|
else
|
|
148
169
|
exit 0
|
|
149
170
|
fi
|
|
150
171
|
|
|
151
|
-
# Best-effort body extraction
|
|
152
|
-
#
|
|
153
|
-
#
|
|
172
|
+
# Best-effort body extraction. Order matters — most-specific first.
|
|
173
|
+
#
|
|
174
|
+
# HEREDOC first: `git commit -m "$(cat <<'EOF'\n...\nEOF\n)"` is the
|
|
175
|
+
# AI-dominant form. Must precede --body "..." / -m "..." because
|
|
176
|
+
# those would otherwise match the literal `$(cat <<'EOF'...EOF)`
|
|
177
|
+
# text as the body, defeating the marker key match against the
|
|
178
|
+
# subagent's <draft> body.
|
|
179
|
+
# Then --body / --field for the gh + npm + security-advisories surfaces.
|
|
180
|
+
# Then -m / --message for git commit (single-line literal forms).
|
|
181
|
+
#
|
|
182
|
+
# When absent (npm publish, --body-file, editor flow already filtered),
|
|
183
|
+
# DRAFT="" is acceptable: the agent will be invoked with command
|
|
184
|
+
# context and read whatever body source the call uses.
|
|
154
185
|
DRAFT=$(printf '%s' "$COMMAND" | python3 -c "
|
|
155
186
|
import sys, re
|
|
156
187
|
cmd = sys.stdin.read()
|
|
157
|
-
#
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
188
|
+
# (pattern, flags) — first match wins.
|
|
189
|
+
patterns = [
|
|
190
|
+
# HEREDOC body — matches a here-doc with EOF delimiter (quoted or
|
|
191
|
+
# unquoted). The literal '<<' is written as the char-class pair
|
|
192
|
+
# [<][<] so bash's command-substitution parser does NOT mis-parse
|
|
193
|
+
# this regex as a real here-doc operator (P082 implementation note).
|
|
194
|
+
# DOTALL so the body can span newlines.
|
|
195
|
+
(r\"[<][<]\s*['\\\"]?EOF['\\\"]?\s*\n(.*?)\nEOF\", re.DOTALL),
|
|
196
|
+
# gh issue/pr + npm publish --body 'TEXT' / --body \"TEXT\" (existing).
|
|
197
|
+
(r\"--body[= ]'([^']*)'\", 0),
|
|
198
|
+
(r'--body[= ]\"([^\"]*)\"', 0),
|
|
199
|
+
# gh api --field summary='TEXT' / --field summary=\"TEXT\" (existing).
|
|
200
|
+
(r\"--field [a-zA-Z_]+='([^']*)'\", 0),
|
|
201
|
+
(r'--field [a-zA-Z_]+=\"([^\"]*)\"', 0),
|
|
202
|
+
# git commit -m / --message single-line literal forms (P082 Phase 1).
|
|
203
|
+
(r\"(?:-m|--message)[= ]'([^']*)'\", 0),
|
|
204
|
+
(r'(?:-m|--message)[= ]\"([^\"]*)\"', 0),
|
|
205
|
+
]
|
|
206
|
+
for pat, flags in patterns:
|
|
207
|
+
m = re.search(pat, cmd, flags)
|
|
161
208
|
if m:
|
|
162
209
|
print(m.group(1))
|
|
163
210
|
break
|
|
@@ -231,3 +231,94 @@ run_hook() {
|
|
|
231
231
|
[ "$status" -eq 0 ]
|
|
232
232
|
[ -z "$output" ]
|
|
233
233
|
}
|
|
234
|
+
|
|
235
|
+
# ---------------------------------------------------------------------------
|
|
236
|
+
# P082 Phase 1 — git commit message surface (voice-tone evaluator).
|
|
237
|
+
# Commit messages reach git log / PR commits tab / release notes / CHANGELOG;
|
|
238
|
+
# voice-tone evaluator gates the message body for AI-tells, hedging,
|
|
239
|
+
# em-dashes, banned-phrase drift before the commit lands. Editor flow
|
|
240
|
+
# (bare `git commit`) is out of scope per P082 SC1 — the message is
|
|
241
|
+
# written to .git/COMMIT_EDITMSG AFTER PreToolUse fires.
|
|
242
|
+
# ---------------------------------------------------------------------------
|
|
243
|
+
|
|
244
|
+
@test "P082: git commit -m with literal -m body denies and delegates to voice-tone evaluator" {
|
|
245
|
+
INPUT=$(build_bash_input "git commit -m \"I've implemented the feature\"")
|
|
246
|
+
run_hook "$INPUT"
|
|
247
|
+
[ "$status" -eq 0 ]
|
|
248
|
+
[[ "$output" == *"deny"* ]]
|
|
249
|
+
[[ "$output" == *"git-commit-message"* ]]
|
|
250
|
+
[[ "$output" == *"wr-voice-tone:external-comms"* ]]
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
@test "P082: git commit --message with literal body denies and delegates" {
|
|
254
|
+
INPUT=$(build_bash_input "git commit --message \"happy to help further with this fix\"")
|
|
255
|
+
run_hook "$INPUT"
|
|
256
|
+
[ "$status" -eq 0 ]
|
|
257
|
+
[[ "$output" == *"deny"* ]]
|
|
258
|
+
[[ "$output" == *"git-commit-message"* ]]
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
@test "P082: git commit --amend -m is intercepted (P082 SC2)" {
|
|
262
|
+
INPUT=$(build_bash_input "git commit --amend -m \"rewritten subject\"")
|
|
263
|
+
run_hook "$INPUT"
|
|
264
|
+
[ "$status" -eq 0 ]
|
|
265
|
+
[[ "$output" == *"deny"* ]]
|
|
266
|
+
[[ "$output" == *"git-commit-message"* ]]
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
@test "P082: git commit HEREDOC body is intercepted and the body becomes the marker key" {
|
|
270
|
+
# Build a HEREDOC-shaped command. The hook regex pulls the body BETWEEN
|
|
271
|
+
# the <<'EOF' opener and the closing EOF marker — the extracted DRAFT is
|
|
272
|
+
# the inner text, NOT the literal `$(cat <<'EOF' ... EOF)` wrapper.
|
|
273
|
+
BODY=$'feat(foo): add bar\n\nWe observed a build failure on Node 20.'
|
|
274
|
+
CMD=$'git commit -m "$(cat <<\'EOF\'\n'"$BODY"$'\nEOF\n)"'
|
|
275
|
+
INPUT=$(build_bash_input "$CMD")
|
|
276
|
+
run_hook "$INPUT"
|
|
277
|
+
[ "$status" -eq 0 ]
|
|
278
|
+
[[ "$output" == *"deny"* ]]
|
|
279
|
+
[[ "$output" == *"git-commit-message"* ]]
|
|
280
|
+
|
|
281
|
+
# Pre-place the per-evaluator marker keyed on the extracted HEREDOC body
|
|
282
|
+
# + the git-commit-message surface; the second run must permit silently.
|
|
283
|
+
SURFACE="git-commit-message"
|
|
284
|
+
KEY=$(printf '%s\n%s' "$BODY" "$SURFACE" | shasum -a 256 | cut -d' ' -f1)
|
|
285
|
+
touch "${RDIR}/external-comms-voice-tone-reviewed-${KEY}"
|
|
286
|
+
run_hook "$INPUT"
|
|
287
|
+
[ "$status" -eq 0 ]
|
|
288
|
+
[ -z "$output" ]
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
@test "P082: bare git commit (editor flow) is silently allowed per SC1" {
|
|
292
|
+
# No -m / --message → .git/COMMIT_EDITMSG doesn't exist at PreToolUse
|
|
293
|
+
# time. Phase 1 skip is pragmatic; the editor flow has user-eyeballs.
|
|
294
|
+
INPUT=$(build_bash_input "git commit")
|
|
295
|
+
run_hook "$INPUT"
|
|
296
|
+
[ "$status" -eq 0 ]
|
|
297
|
+
[ -z "$output" ]
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
@test "P082: git merge is silently allowed (not a git commit verb)" {
|
|
301
|
+
INPUT=$(build_bash_input "git merge --no-ff feature-branch")
|
|
302
|
+
run_hook "$INPUT"
|
|
303
|
+
[ "$status" -eq 0 ]
|
|
304
|
+
[ -z "$output" ]
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
@test "P082: BYPASS_RISK_GATE=1 short-circuits the git commit gate" {
|
|
308
|
+
INPUT=$(build_bash_input "git commit -m \"I've implemented the feature\"")
|
|
309
|
+
run bash -c "cd '$TEST_PROJECT_DIR' && BYPASS_RISK_GATE=1 printf '%s' \"\$1\" | BYPASS_RISK_GATE=1 '$HOOK'" _ "$INPUT"
|
|
310
|
+
[ "$status" -eq 0 ]
|
|
311
|
+
[ -z "$output" ]
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
@test "P082: per-evaluator marker keyed on (body, git-commit-message) permits the call" {
|
|
315
|
+
BODY="docs(retro): close iter 3 ask-hygiene trail"
|
|
316
|
+
SURFACE="git-commit-message"
|
|
317
|
+
KEY=$(printf '%s\n%s' "$BODY" "$SURFACE" | shasum -a 256 | cut -d' ' -f1)
|
|
318
|
+
touch "${RDIR}/external-comms-voice-tone-reviewed-${KEY}"
|
|
319
|
+
|
|
320
|
+
INPUT=$(build_bash_input "git commit -m \"$BODY\"")
|
|
321
|
+
run_hook "$INPUT"
|
|
322
|
+
[ "$status" -eq 0 ]
|
|
323
|
+
[ -z "$output" ]
|
|
324
|
+
}
|