feat(hooks): role-hint.sh — подсказка роли по session_title; tests/run.sh
Refs #2 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MzkPQWjfA7RpqYWkjnqJwt
This commit is contained in:
parent
9dc8d9f70e
commit
b6c71b0d7a
17
hooks/role-hint.sh
Executable file
17
hooks/role-hint.sh
Executable file
@ -0,0 +1,17 @@
|
||||
#!/usr/bin/env bash
|
||||
# SessionStart hook (все источники): если заголовок сессии начинается с имени роли, напомнить вызвать /role.
|
||||
# Ничего не блокирует: любой сбой → тихий exit 0. Заголовок приходит в поле session_title (claude -n "<роль> …", /rename).
|
||||
set -uo pipefail
|
||||
input=$(cat 2>/dev/null || true)
|
||||
[ -n "$input" ] || exit 0
|
||||
title=$(printf '%s' "$input" | python3 -c 'import sys,json
|
||||
try: print((json.load(sys.stdin) or {}).get("session_title") or "")
|
||||
except Exception: print("")' 2>/dev/null) || exit 0
|
||||
[ -n "$title" ] || exit 0
|
||||
# первое слово: нижний регистр, без ведущих пробелов и префикса алиаса cc-; только латинские буквы.
|
||||
# LC_ALL=C — чтобы BSD tr/sed работали побайтово и не спотыкались о кириллицу в заголовке.
|
||||
first=$(printf '%s' "$title" | LC_ALL=C tr '[:upper:]' '[:lower:]' | LC_ALL=C sed -E 's/^[[:space:]]*(cc-)?([a-z]*).*$/\2/')
|
||||
case "$first" in
|
||||
dev|admin|analyst|qa) echo "[claude-harness] Роль сессии: $first. Вызови /role $first до первого ответа." ;;
|
||||
esac
|
||||
exit 0
|
||||
@ -29,6 +29,14 @@
|
||||
"command": "\"$HOME/.claude/hooks/harness-check.sh\""
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "\"$HOME/.claude/hooks/role-hint.sh\""
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
23
tests/run.sh
Executable file
23
tests/run.sh
Executable file
@ -0,0 +1,23 @@
|
||||
#!/usr/bin/env bash
|
||||
# Тесты обвязки: bash, без зависимостей, работают с источниками в репозитории.
|
||||
# Запуск: bash tests/run.sh [подстрока имени файла теста]
|
||||
set -uo pipefail
|
||||
HERE="$(cd "$(dirname "$0")" && pwd)"; ROOT="$(dirname "$HERE")"
|
||||
pass=0; fail=0
|
||||
ok(){ pass=$((pass+1)); printf ' ok %s\n' "$1"; }
|
||||
fail(){ fail=$((fail+1)); printf ' FAIL %s\n %s\n' "$1" "$2"; }
|
||||
assert_eq(){ [ "$2" = "$3" ] && ok "$1" || fail "$1" "получено: '$2', ожидалось: '$3'"; }
|
||||
assert_file(){ [ -e "$2" ] && ok "$1" || fail "$1" "нет файла $2"; }
|
||||
assert_grep(){ grep -qE -- "$2" "$3" 2>/dev/null && ok "$1" || fail "$1" "в $3 нет /$2/"; }
|
||||
assert_max_bytes(){ local n; n=$(wc -c < "$2" | tr -d ' '); [ "$n" -le "$3" ] && ok "$1 ($n Б)" || fail "$1" "$n Б > $3"; }
|
||||
assert_symlink(){ [ -L "$2" ] && [ -e "$2" ] && ok "$1" || fail "$1" "$2 не симлинк или битый"; }
|
||||
for t in "$HERE"/test-*.sh; do
|
||||
[ -n "${1:-}" ] && [[ "$(basename "$t")" != *"$1"* ]] && continue
|
||||
echo "## $(basename "$t")"
|
||||
TMP=$(mktemp -d); export TMP ROOT
|
||||
# shellcheck disable=SC1090
|
||||
. "$t"
|
||||
rm -rf "$TMP"
|
||||
done
|
||||
echo "passed $pass, failed $fail"
|
||||
[ "$fail" = 0 ]
|
||||
22
tests/test-role-hint.sh
Normal file
22
tests/test-role-hint.sh
Normal file
@ -0,0 +1,22 @@
|
||||
# role-hint.sh: подсказка роли по session_title, никогда не блокирует.
|
||||
H="$ROOT/hooks/role-hint.sh"
|
||||
assert_file "role-hint.sh существует" "$H"
|
||||
run(){ printf '%s' "$1" | bash "$H" 2>/dev/null; echo "rc=$?"; }
|
||||
assert_eq "admin: … → подсказка" "$(run '{"session_title":"admin: спринт 3","source":"startup"}')" \
|
||||
"[claude-harness] Роль сессии: admin. Вызови /role admin до первого ответа.
|
||||
rc=0"
|
||||
assert_eq "qa без двоеточия → подсказка" "$(run '{"session_title":"qa прогон Q1"}')" \
|
||||
"[claude-harness] Роль сессии: qa. Вызови /role qa до первого ответа.
|
||||
rc=0"
|
||||
assert_eq "регистр и пробелы в начале" "$(run '{"session_title":" Admin — Jira"}')" \
|
||||
"[claude-harness] Роль сессии: admin. Вызови /role admin до первого ответа.
|
||||
rc=0"
|
||||
assert_eq "префикс алиаса cc-" "$(run '{"session_title":"cc-analyst ТЗ v4"}')" \
|
||||
"[claude-harness] Роль сессии: analyst. Вызови /role analyst до первого ответа.
|
||||
rc=0"
|
||||
assert_eq "чужое слово → тишина" "$(run '{"session_title":"harness: роли"}')" "rc=0"
|
||||
assert_eq "слово с продолжением (devops) → тишина" "$(run '{"session_title":"devops"}')" "rc=0"
|
||||
assert_eq "без заголовка → тишина" "$(run '{"cwd":"/tmp","source":"resume"}')" "rc=0"
|
||||
assert_eq "пустой stdin → тишина, exit 0" "$(run '')" "rc=0"
|
||||
assert_eq "не JSON → тишина, exit 0" "$(run 'not json at all')" "rc=0"
|
||||
assert_grep "settings.hooks.json регистрирует role-hint" 'role-hint\.sh' "$ROOT/hooks/settings.hooks.json"
|
||||
Loading…
x
Reference in New Issue
Block a user