@rt-tools/agent-kit 0.10.0 → 0.11.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.
Files changed (91) hide show
  1. package/assets/checks/check-state-next.mjs +194 -0
  2. package/assets/checks/check-states.mjs +142 -0
  3. package/assets/checks/check-turn-map.mjs +146 -0
  4. package/assets/commands/agent-kit-digest.md +6 -5
  5. package/assets/defaults/project.sh +58 -6
  6. package/assets/defaults/turn-map.md +46 -0
  7. package/assets/hooks/browser-device-id.sh +2 -0
  8. package/assets/hooks/browser-guard-device-id.sh +2 -0
  9. package/assets/hooks/browser-guard-no-asking.sh +2 -0
  10. package/assets/hooks/browser-guard-no-listing.sh +2 -0
  11. package/assets/hooks/browser-guard-no-other-drivers.sh +2 -0
  12. package/assets/hooks/browser-guard-require-select.sh +2 -0
  13. package/assets/hooks/claim-guard.sh +2 -0
  14. package/assets/hooks/commit-msg.sh +2 -0
  15. package/assets/hooks/conscience-guard.sh +2 -0
  16. package/assets/hooks/constitution-index.sh +2 -0
  17. package/assets/hooks/dev-server-guard.sh +2 -0
  18. package/assets/hooks/docs-guard.sh +2 -0
  19. package/assets/hooks/exam-guard.sh +2 -0
  20. package/assets/hooks/git-guard-delivery-signature.sh +2 -0
  21. package/assets/hooks/git-guard-delivery.sh +2 -0
  22. package/assets/hooks/git-guard-main.sh +2 -0
  23. package/assets/hooks/git-guard-push-tests.sh +2 -0
  24. package/assets/hooks/glossary-load.sh +2 -0
  25. package/assets/hooks/grill-gate.sh +2 -0
  26. package/assets/hooks/handoff-entry-guard.sh +2 -0
  27. package/assets/hooks/handoff-write.sh +103 -0
  28. package/assets/hooks/lint-after-edit.sh +2 -0
  29. package/assets/hooks/observe.sh +2 -0
  30. package/assets/hooks/postmortem-guard.sh +2 -0
  31. package/assets/hooks/proposal-guard.sh +2 -0
  32. package/assets/hooks/prose-style-guard.sh +2 -0
  33. package/assets/hooks/qa-dataid-guard.sh +2 -0
  34. package/assets/hooks/rerun-guard.sh +2 -0
  35. package/assets/hooks/reuse-first-guard.sh +2 -0
  36. package/assets/hooks/roles.sh +2 -0
  37. package/assets/hooks/skill-gate-layers.sh +2 -0
  38. package/assets/hooks/skill-gate-rearm.sh +2 -0
  39. package/assets/hooks/skill-gate.sh +2 -0
  40. package/assets/hooks/skill-loaded.sh +2 -0
  41. package/assets/hooks/sql-guard-parse.sh +2 -0
  42. package/assets/hooks/sql-guard-request.sh +2 -0
  43. package/assets/hooks/sql-guard-target.sh +2 -0
  44. package/assets/hooks/sql-guard-write.sh +2 -0
  45. package/assets/hooks/sql-guard.sh +2 -0
  46. package/assets/hooks/task-context-load.sh +2 -0
  47. package/assets/hooks/task-flow-guard.sh +2 -0
  48. package/assets/hooks/turn-entry-load.sh +62 -0
  49. package/assets/hooks/turn-exit-guard.sh +2 -0
  50. package/assets/hooks/utf8.sh +35 -0
  51. package/assets/hooks/waiting-turn-guard.sh +2 -0
  52. package/assets/hooks/window-fill-guard.sh +31 -1
  53. package/assets/laws/work-conduct.md +29 -0
  54. package/assets/patterns/cargo-triage-mark.md +119 -0
  55. package/assets/patterns/task-flow-close.md +21 -0
  56. package/assets/patterns/task-flow-handoff.md +18 -0
  57. package/assets/patterns/task-flow-resume.md +6 -0
  58. package/assets/patterns/task-flow-start.md +41 -0
  59. package/assets/patterns/turn-entry-map.md +81 -0
  60. package/assets/rules/cargo-triage.md +126 -0
  61. package/assets/rules/task-flow.md +14 -1
  62. package/assets/rules/turn-entry.md +93 -0
  63. package/bin/agent-kit.d.ts.map +1 -1
  64. package/bin/agent-kit.js +42 -1
  65. package/bin/agent-kit.js.map +1 -1
  66. package/lib/cargo-state.d.ts +62 -0
  67. package/lib/cargo-state.d.ts.map +1 -0
  68. package/lib/cargo-state.js +118 -0
  69. package/lib/cargo-state.js.map +1 -0
  70. package/lib/cargo.d.ts +42 -0
  71. package/lib/cargo.d.ts.map +1 -1
  72. package/lib/cargo.js +2 -0
  73. package/lib/cargo.js.map +1 -1
  74. package/lib/commands.d.ts.map +1 -1
  75. package/lib/commands.js +64 -4
  76. package/lib/commands.js.map +1 -1
  77. package/lib/observations.d.ts +35 -1
  78. package/lib/observations.d.ts.map +1 -1
  79. package/lib/observations.js +14 -2
  80. package/lib/observations.js.map +1 -1
  81. package/lib/ship.d.ts +3 -0
  82. package/lib/ship.d.ts.map +1 -1
  83. package/lib/ship.js +56 -0
  84. package/lib/ship.js.map +1 -1
  85. package/lib/thresholds.d.ts +49 -0
  86. package/lib/thresholds.d.ts.map +1 -0
  87. package/lib/thresholds.js +151 -0
  88. package/lib/thresholds.js.map +1 -0
  89. package/package.json +1 -1
  90. package/rt-tools-agent-kit-0.11.0.tgz +0 -0
  91. package/rt-tools-agent-kit-0.10.0.tgz +0 -0
@@ -8,6 +8,8 @@
8
8
  #
9
9
  # Файл с идентификатором в репозиторий не коммитится: у каждой машины он свой.
10
10
 
11
+ . "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
12
+
11
13
  if [ -n "${RT_BROWSER_DEVICE_ID:-}" ]; then
12
14
  printf '%s\n' "$RT_BROWSER_DEVICE_ID"
13
15
  exit 0
@@ -11,6 +11,8 @@
11
11
  #
12
12
  # ОТКАЗ В ПОЛЬЗУ РАБОТЫ: помощник не назвал профиль — пропуск.
13
13
 
14
+ . "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
15
+
14
16
  input="$(cat 2>/dev/null)"
15
17
 
16
18
  device_id="$("${CLAUDE_PROJECT_DIR:-.}/.claude/hooks/browser-device-id.sh" 2>/dev/null)"
@@ -14,6 +14,8 @@
14
14
  #
15
15
  # FAIL-OPEN when browser-device-id.sh prints nothing.
16
16
 
17
+ . "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
18
+
17
19
  input="$(cat 2>/dev/null)"
18
20
 
19
21
  device_id="$("${CLAUDE_PROJECT_DIR:-.}/.claude/hooks/browser-device-id.sh" 2>/dev/null)"
@@ -10,6 +10,8 @@
10
10
  # ОТКАЗ В ПОЛЬЗУ РАБОТЫ: помощник не назвал профиль — пропуск. Гард, который не может назвать
11
11
  # нужный профиль, ничего не предлагает взамен, и слепой отказ только заводил бы работу в тупик.
12
12
 
13
+ . "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
14
+
13
15
  cat >/dev/null 2>&1
14
16
 
15
17
  device_id="$("${CLAUDE_PROJECT_DIR:-.}/.claude/hooks/browser-device-id.sh" 2>/dev/null)"
@@ -13,6 +13,8 @@
13
13
  #
14
14
  # ОТКАЗ В ПОЛЬЗУ РАБОТЫ: помощник не назвал профиль — пропуск.
15
15
 
16
+ . "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
17
+
16
18
  input="$(cat 2>/dev/null)"
17
19
 
18
20
  device_id="$("${CLAUDE_PROJECT_DIR:-.}/.claude/hooks/browser-device-id.sh" 2>/dev/null)"
@@ -20,6 +20,8 @@
20
20
  #
21
21
  # ОТКАЗ В ПОЛЬЗУ РАБОТЫ: помощник не назвал профиль — пропуск.
22
22
 
23
+ . "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
24
+
23
25
  input="$(cat 2>/dev/null)"
24
26
 
25
27
  device_id="$("${CLAUDE_PROJECT_DIR:-.}/.claude/hooks/browser-device-id.sh" 2>/dev/null)"
@@ -25,6 +25,8 @@
25
25
  # ОТКАЗ В ПОЛЬЗУ РАБОТЫ: при любой ошибке, нехватке `jq`, отсутствии записи хода или пустом
26
26
  # тексте ход РАЗРЕШАЕТСЯ (exit 0). Сломанный гард не имеет права заклинить разговор.
27
27
 
28
+ . "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
29
+
28
30
  input="$(cat 2>/dev/null)"
29
31
  [ -z "$input" ] && exit 0
30
32
  command -v jq >/dev/null 2>&1 || exit 0
@@ -15,6 +15,8 @@
15
15
  # ОТКАЗ В ПОЛЬЗУ РАБОТЫ: проверяльщика нет (свежий клон без установленных зависимостей) —
16
16
  # пропуск. Формат тогда не проверяется, и это лучше, чем заклинивший коммит.
17
17
 
18
+ . "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
19
+
18
20
  msg_file="$1"
19
21
  [ -z "$msg_file" ] && exit 0
20
22
 
@@ -19,6 +19,8 @@
19
19
  # ОТКАЗ В ПОЛЬЗУ РАБОТЫ: нет `jq`, нет записи хода, роль молчит или отвечает не по форме — ход
20
20
  # разрешается. Сломанная совесть не имеет права заклинить разговор.
21
21
 
22
+ . "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
23
+
22
24
  input="$(cat 2>/dev/null)"
23
25
  [ -z "$input" ] && exit 0
24
26
  command -v jq >/dev/null 2>&1 || exit 0
@@ -12,6 +12,8 @@
12
12
  # ОТКАЗ В ПОЛЬЗУ РАБОТЫ: любая ошибка начинает сессию без добавленного контекста (exit 0).
13
13
  # Сломанный вход не имеет права остановить сессию.
14
14
 
15
+ . "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
16
+
15
17
  dir="${CLAUDE_PROJECT_DIR:-.}/docs/constitution"
16
18
  [ -d "$dir" ] || exit 0
17
19
 
@@ -14,6 +14,8 @@
14
14
  # Где именно подняты приложения, знает профиль проекта: .claude/rt-kit/project.sh, переменная
15
15
  # RT_STANDS. Нет профиля — текст отказа остаётся общим, сам гард работает.
16
16
 
17
+ . "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
18
+
17
19
  input="$(cat 2>/dev/null)"
18
20
 
19
21
  tool="$(printf '%s' "$input" | jq -r '.tool_name // empty' 2>/dev/null)"
@@ -26,6 +26,8 @@
26
26
  # ОТКАЗ В ПОЛЬЗУ РАБОТЫ: не репозиторий, нет разборщика, битый ввод, пустой список файлов —
27
27
  # пропуск.
28
28
 
29
+ . "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
30
+
29
31
  input="$(cat 2>/dev/null)"
30
32
  [ -z "$input" ] && exit 0
31
33
  command -v jq >/dev/null 2>&1 || exit 0
@@ -18,6 +18,8 @@
18
18
  # FAIL-OPEN: нет jq, нет записи хода, чужой инструмент → пропуск. Сломанный гард не должен
19
19
  # мешать работать.
20
20
 
21
+ . "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
22
+
21
23
  input="$(cat 2>/dev/null)"
22
24
  [ -z "$input" ] && exit 0
23
25
  command -v jq >/dev/null 2>&1 || exit 0
@@ -10,6 +10,8 @@
10
10
  # Вынесен он отсюда потому, что гард дорос до предела длины: предметов в нём пять, и подпись
11
11
  # среди них самая отдельная — своя точка проверки, свои условия молчания, свой отказ.
12
12
 
13
+ . "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
14
+
13
15
  rt_delivery_signature() {
14
16
  # --- подпись машинного коммита ------------------------------------------------------------
15
17
  #
@@ -37,6 +37,8 @@
37
37
  #
38
38
  # ОТКАЗ В ПОЛЬЗУ РАБОТЫ: не репозиторий, нет разборщика, битый ввод, нет профиля — пропуск.
39
39
 
40
+ . "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
41
+
40
42
  input="$(cat 2>/dev/null)"
41
43
  [ -z "$input" ] && exit 0
42
44
  command -v jq >/dev/null 2>&1 || exit 0
@@ -13,6 +13,8 @@
13
13
  # ОТКАЗ В ПОЛЬЗУ РАБОТЫ: не репозиторий, нет гита, открепившийся HEAD, битый ввод — пропуск.
14
14
  # Сломанный гард не должен мешать работать.
15
15
 
16
+ . "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
17
+
16
18
  input="$(cat 2>/dev/null)"
17
19
  [ -z "$input" ] && exit 0
18
20
 
@@ -25,6 +25,8 @@
25
25
  #
26
26
  # ОТКАЗ В ПОЛЬЗУ РАБОТЫ: не репозиторий, битый ввод, нет профиля — пропуск.
27
27
 
28
+ . "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
29
+
28
30
  input="$(cat 2>/dev/null)"
29
31
  [ -z "$input" ] && exit 0
30
32
 
@@ -9,6 +9,8 @@
9
9
  #
10
10
  # FAIL-OPEN: нет файла или нет `jq` — выходим молча. Сессия важнее словаря.
11
11
 
12
+ . "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
13
+
12
14
  GLOSSARY="${CLAUDE_PROJECT_DIR:-.}/docs/GLOSSARY.md"
13
15
  [ -f "$GLOSSARY" ] || exit 0
14
16
  command -v jq >/dev/null 2>&1 || exit 0
@@ -22,6 +22,8 @@
22
22
  # ОТКАЗ В ПОЛЬЗУ РАБОТЫ: при любой ошибке, отсутствии записи хода и повторном заходе ход
23
23
  # РАЗРЕШАЕТСЯ (exit 0). Сломанный гард не имеет права заклинить разговор.
24
24
 
25
+ . "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
26
+
25
27
  input="$(cat 2>/dev/null)"
26
28
  [ -z "$input" ] && exit 0
27
29
 
@@ -14,6 +14,8 @@
14
14
  #
15
15
  # FAIL-OPEN: нет `jq`, нет записи хода, передачи в реплике нет → пропуск.
16
16
 
17
+ . "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
18
+
17
19
  input="$(cat 2>/dev/null)"
18
20
  [ -z "$input" ] && exit 0
19
21
  command -v jq >/dev/null 2>&1 || exit 0
@@ -0,0 +1,103 @@
1
+ #!/usr/bin/env bash
2
+ # rt-hook: PreCompact .*
3
+ # Требует: hooks/profile-check.sh
4
+ # Передача захода пишется перед сжатием контекста, а не рукой исполнителя.
5
+ #
6
+ # Заход кончается двумя способами: исполнитель доводит работу до точки и пишет передачу сам —
7
+ # либо контекст переполняется, и сжатие приходит от инструмента. Второй случай раньше терял
8
+ # передачу целиком: писать её в этот момент уже некому, а после сжатия пересказывать нечего.
9
+ # Ночью, когда напомнить некому, так кончается каждый заход.
10
+ #
11
+ # Хук собирает передачу из того, что лежит на диске: ход работы даёт состояние и следующий шаг,
12
+ # дерево — ветку, незакоммиченное и коммиты сверх главной. Ничего от себя он не добавляет:
13
+ # передача пересказывает записанное, а не заменяет его.
14
+ #
15
+ # Написанное руками не затирается молча: файл один, и последняя запись побеждает. Исполнитель,
16
+ # закрывающий заход по правилу, пишет поверх — его передача полнее, потому что он знает то,
17
+ # чего на диске нет.
18
+ #
19
+ # FAIL-OPEN: нет разборщика, пустой ввод, не репозиторий, нет каталога передачи — хук выходит
20
+ # нулём и молчит. Сжатие он не отбивает никогда: остановленное сжатие оставит заход без места.
21
+
22
+ . "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
23
+
24
+ input="$(cat 2>/dev/null)"
25
+ [ -z "$input" ] && exit 0
26
+
27
+ command -v jq >/dev/null 2>&1 || exit 0
28
+
29
+ rt_hooks_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
30
+ for profile in "$rt_hooks_dir/../rt-kit/defaults/project.sh" "$rt_hooks_dir/../defaults/project.sh" \
31
+ "${CLAUDE_PROJECT_DIR:-.}/.claude/rt-kit/defaults/project.sh" "${CLAUDE_PROJECT_DIR:-.}/.claude/rt-kit/project.sh"; do
32
+ # shellcheck disable=SC1090
33
+ [ -f "$profile" ] && . "$profile" 2>/dev/null
34
+ done
35
+
36
+ workdir="$(printf '%s' "$input" | jq -r '.cwd // empty' 2>/dev/null)"
37
+ [ -z "$workdir" ] && workdir="${CLAUDE_PROJECT_DIR:-.}"
38
+ cd "$workdir" 2>/dev/null || exit 0
39
+ git rev-parse --is-inside-work-tree >/dev/null 2>&1 || exit 0
40
+
41
+ branch="$(git branch --show-current 2>/dev/null)"
42
+ [ -z "$branch" ] && exit 0
43
+
44
+ root="$(git rev-parse --show-toplevel 2>/dev/null)"
45
+ [ -z "$root" ] && exit 0
46
+
47
+ handoff_dir="${RT_HANDOFF_DIR:-.claude/handoff}"
48
+ tasks_dir="${RT_TASKS_DIR:-docs/tasks}"
49
+
50
+ mkdir -p "$root/$handoff_dir" 2>/dev/null || exit 0
51
+ [ -d "$root/$handoff_dir" ] || exit 0
52
+
53
+ # Ручное сжатие от автоматического отличается одним: при ручном исполнитель у клавиатуры и
54
+ # может дописать передачу сам. Пишется она в обоих случаях — заход, сжатый руками, теряет
55
+ # контекст ровно так же.
56
+ trigger="$(printf '%s' "$input" | jq -r '.trigger // "auto"' 2>/dev/null)"
57
+ [ -z "$trigger" ] && trigger='auto'
58
+
59
+ progress="$root/$tasks_dir/$branch/progress.md"
60
+
61
+ # Строка раздела «Где стоим» по её названию. Пусто — работа идёт вне папки задачи, и выдумывать
62
+ # за неё состояние нельзя: в передаче тогда стоит то, что известно дереву.
63
+ line_of() {
64
+ [ -f "$progress" ] || return 0
65
+ sed -n "s/^[[:space:]]*[-*][[:space:]]*\*\*$1:\*\*[[:space:]]*\(.*\)/\1/p" "$progress" 2>/dev/null | head -1
66
+ }
67
+
68
+ state="$(line_of 'Состояние')"
69
+ stage="$(line_of 'Этап')"
70
+ next_step="$(line_of 'Следующий шаг')"
71
+ pull="$(line_of 'PR')"
72
+
73
+ uncommitted="$(git status --short 2>/dev/null | head -20)"
74
+ [ -z "$uncommitted" ] && uncommitted='нет'
75
+
76
+ ahead="$(git log --oneline origin/main..HEAD 2>/dev/null | head -20)"
77
+ [ -z "$ahead" ] && ahead='нет коммитов сверх главной'
78
+
79
+ target="$root/$handoff_dir/$branch.md"
80
+
81
+ {
82
+ printf '# Передача захода — сжатие контекста (%s)\n\n' "$trigger"
83
+ printf '**Рабочее дерево:** %s\n' "$root"
84
+ printf '**Ветка:** %s\n\n' "$branch"
85
+
86
+ if [ -n "$state" ]; then
87
+ printf '## Где стоим\n\n'
88
+ printf -- '- **Состояние:** %s\n' "$state"
89
+ [ -n "$stage" ] && printf -- '- **Этап:** %s\n' "$stage"
90
+ [ -n "$next_step" ] && printf -- '- **Следующий шаг:** %s\n' "$next_step"
91
+ [ -n "$pull" ] && printf -- '- **PR:** %s\n' "$pull"
92
+ printf '\nХод работы целиком — `%s/%s/progress.md`; замысел рядом с ним.\n\n' "$tasks_dir" "$branch"
93
+ else
94
+ printf '## Где стоим\n\nПапки задачи у этой ветки нет: состояние работы взять неоткуда.\n\n'
95
+ fi
96
+
97
+ printf '## Незакоммиченное\n\n```\n%s\n```\n\n' "$uncommitted"
98
+ printf '## Коммиты сверх главной\n\n```\n%s\n```\n\n' "$ahead"
99
+ printf 'Написана хуком перед сжатием контекста. Всё, что здесь стоит, проверяется деревом:\n'
100
+ printf 'передача пересказывает записанное и описывает минуту, когда её собрали.\n'
101
+ } > "$target" 2>/dev/null
102
+
103
+ exit 0
@@ -29,6 +29,8 @@
29
29
 
30
30
  # Сколько файлов линтуется за один перенос. Переезд либы трогает десятки файлов, и прогон по
31
31
  # каждому превратил бы хук в минутную паузу; на нарушение границы хватает первых.
32
+ . "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
33
+
32
34
  MAX_FILES=12
33
35
 
34
36
  input="$(cat 2>/dev/null)"
@@ -19,6 +19,8 @@
19
19
 
20
20
  # Значение, годное к записи. Путь не выносится ни при каких обстоятельствах, остальное чистится
21
21
  # до имени и обрезается: длинное значение в наблюдении не значит ничего, кроме утечки.
22
+ . "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
23
+
22
24
  rt_observe_clean() {
23
25
  case "$1" in
24
26
  */*) return 0 ;;
@@ -21,6 +21,8 @@
21
21
  # ОТКАЗ В ПОЛЬЗУ РАБОТЫ: при любой ошибке, отсутствии записи хода и повторном заходе ход
22
22
  # РАЗРЕШАЕТСЯ (exit 0). Сломанный гард не имеет права заклинить разговор.
23
23
 
24
+ . "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
25
+
24
26
  input="$(cat 2>/dev/null)"
25
27
  [ -z "$input" ] && exit 0
26
28
 
@@ -20,6 +20,8 @@
20
20
  # ОТКАЗ В ПОЛЬЗУ РАБОТЫ: при любой ошибке, нехватке `jq`, отсутствии записи хода и повторном
21
21
  # заходе ход РАЗРЕШАЕТСЯ (exit 0). Сломанный гард не имеет права заклинить разговор.
22
22
 
23
+ . "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
24
+
23
25
  input="$(cat 2>/dev/null)"
24
26
  [ -z "$input" ] && exit 0
25
27
 
@@ -12,6 +12,8 @@
12
12
  #
13
13
  # FAIL-OPEN: нет узла, нет проверки, чужой инструмент, не `.md` → пропуск.
14
14
 
15
+ . "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
16
+
15
17
  input="$(cat 2>/dev/null)"
16
18
  [ -z "$input" ] && exit 0
17
19
  command -v jq >/dev/null 2>&1 || exit 0
@@ -25,6 +25,8 @@
25
25
  #
26
26
  # ОТКАЗ В ПОЛЬЗУ РАБОТЫ: нет разборщика, битый ввод, чужой инструмент — пропуск.
27
27
 
28
+ . "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
29
+
28
30
  input="$(cat 2>/dev/null)"
29
31
  [ -z "$input" ] && exit 0
30
32
  command -v jq >/dev/null 2>&1 || exit 0
@@ -20,6 +20,8 @@
20
20
  # похож на перезапуск — пропуск. Гард без номера судить не берётся: перезапуск последнего
21
21
  # упавшего прогона зовут и без него, а угадывать, о каком задании речь, значит отбивать наугад.
22
22
 
23
+ . "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
24
+
23
25
  input="$(cat 2>/dev/null)"
24
26
  [ -z "$input" ] && exit 0
25
27
  command -v jq >/dev/null 2>&1 || exit 0
@@ -32,6 +32,8 @@
32
32
  #
33
33
  # ОТКАЗ В ПОЛЬЗУ РАБОТЫ: нет разборщика, битый ввод, чужой инструмент — пропуск.
34
34
 
35
+ . "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
36
+
35
37
  input="$(cat 2>/dev/null)"
36
38
  [ -z "$input" ] && exit 0
37
39
  command -v jq >/dev/null 2>&1 || exit 0
@@ -19,6 +19,8 @@
19
19
  # Выключена ли роль. Успех — выключена.
20
20
  #
21
21
  # rt_role_off strict-teacher && exit 0
22
+ . "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
23
+
22
24
  rt_role_off() {
23
25
  local role="$1" config
24
26
  [ -n "$role" ] || return 1
@@ -20,6 +20,8 @@
20
20
 
21
21
  # Уже названное вторым разом не требуется: отказ, перечисляющий одно правило дважды, читается
22
22
  # как два разных требования.
23
+ . "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
24
+
23
25
  rt_layer_add() {
24
26
  case " $req " in
25
27
  *" $1 "*) return 0 ;;
@@ -12,6 +12,8 @@
12
12
  # ОТКАЗ В ПОЛЬЗУ РАБОТЫ: любая ошибка пропускает. Хук удаляет временный файл и ничего не
13
13
  # отбивает.
14
14
 
15
+ . "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
16
+
15
17
  input="$(cat 2>/dev/null)"
16
18
  [ -z "$input" ] && exit 0
17
19
 
@@ -23,6 +23,8 @@
23
23
  # ОТКАЗ В ПОЛЬЗУ РАБОТЫ: любая ошибка и любой неопознанный путь пропускают правку (exit 0).
24
24
  # Сломанный гейт не имеет права остановить работу совсем.
25
25
 
26
+ . "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
27
+
26
28
  input="$(cat 2>/dev/null)"
27
29
  [ -z "$input" ] && exit 0
28
30
 
@@ -7,6 +7,8 @@
7
7
  #
8
8
  # Хук только наблюдает: он всегда пропускает и ничего не отбивает.
9
9
 
10
+ . "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
11
+
10
12
  input="$(cat 2>/dev/null)"
11
13
  [ -z "$input" ] && exit 0
12
14
 
@@ -13,6 +13,8 @@
13
13
  # команда становилась одним сегментом, доказательство чтения из первой строки покрывало
14
14
  # неразобранный вызов из второй, и запись на боевую базу проходила молча — тот же обход,
15
15
  # что чинили для `&&`, только набранный с новой строки.
16
+ . "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
17
+
16
18
  normalize() {
17
19
  perl -0ne '
18
20
  # Продолжение длинной команды обратным слешем — это одна строка, а не две: без
@@ -10,6 +10,8 @@
10
10
 
11
11
  # Три вида ввода: запрос через подключение редактора, команда оболочки и та же команда,
12
12
  # завёрнутая в универсальный исполнитель. Инструмент, не названный здесь, гарда не касается.
13
+ . "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
14
+
13
15
  sql_read_request() {
14
16
  sql=""
15
17
  context=""
@@ -18,6 +18,8 @@
18
18
  # подключения, и по одному SQL отличить прод от локальной копии невозможно. Поэтому
19
19
  # подключения опознаются в лицо. Список сверяется вызовом list_database_connections;
20
20
  # добавили новое — допишите сюда, иначе оно попадёт в «неизвестные» ниже.
21
+ . "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
22
+
21
23
  sql_resolve_target() {
22
24
  is_prod=""
23
25
  PROD_CONNECTIONS="${RT_PROD_CONNECTIONS:-}"
@@ -7,6 +7,8 @@
7
7
 
8
8
  # Глаголы ищутся в сегментах ВЫЗОВА, а не по всей строке: иначе слово из шаблона поиска в
9
9
  # соседнем звене цепочки объявляло записью читающую команду.
10
+ . "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
11
+
10
12
  sql_detect_write() {
11
13
  is_write=""
12
14
  write_scope="$segments"
@@ -40,6 +40,8 @@
40
40
  # пропускаются целиком — иначе слова «drop» и «update» в описании коммита читались бы как
41
41
  # запрос. Доставки запроса внутри такой команды не бывает, так что цена послабления нулевая.
42
42
 
43
+ . "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
44
+
43
45
  input="$(cat 2>/dev/null)"
44
46
  [ -z "$input" ] && exit 0
45
47
  command -v jq >/dev/null 2>&1 || exit 0
@@ -14,6 +14,8 @@
14
14
  # FAIL-OPEN: нет `jq`, не git-репозиторий, нет папки задачи — выходим молча. Сессия важнее
15
15
  # контекста.
16
16
 
17
+ . "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
18
+
17
19
  ROOT="${CLAUDE_PROJECT_DIR:-.}"
18
20
  command -v jq >/dev/null 2>&1 || exit 0
19
21
  cd "$ROOT" 2>/dev/null || exit 0
@@ -25,6 +25,8 @@
25
25
  # FAIL-OPEN: нет jq, не git-репозиторий, битый ввод, чужой инструмент → пропуск. Сломанный
26
26
  # гард не должен мешать работать.
27
27
 
28
+ . "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
29
+
28
30
  input="$(cat 2>/dev/null)"
29
31
  [ -z "$input" ] && exit 0
30
32
  command -v jq >/dev/null 2>&1 || exit 0
@@ -0,0 +1,62 @@
1
+ #!/usr/bin/env bash
2
+ # rt-hook: SessionStart startup|resume|compact|clear
3
+ # Требует: hooks/handoff-write.sh
4
+ # SessionStart: передача прошлого захода и карта хода уезжают в контекст на каждом запуске.
5
+ #
6
+ # Передачу пишет хук перед сжатием — и на этом её путь кончался: читать её не умел никто, и
7
+ # в новый заход её клал человек. Написанная и не прочитанная, она равна ненаписанной.
8
+ #
9
+ # Второе, чего заход после сжатия не знает, — как здесь ведут работу. Перечень состояний с
10
+ # обязательными действиями лежит в правиле, а правило после сжатия не загружено: заход первым
11
+ # движением идёт читать его целиком, то есть тратит на восстановление порядка ту часть окна,
12
+ # ради которой сжатие и случилось. Карта отвечает на этот вопрос до того, как он задан.
13
+ #
14
+ # Подаётся это на всех четырёх запусках, а не только после сжатия: заход после обрыва и заход
15
+ # после очистки начинают с того же пустого места, и разница между ними исполнителю не видна.
16
+ #
17
+ # FAIL-OPEN: не git-репозиторий, нет ветки, нет каталога передач, нет карты, нечитаемый файл —
18
+ # хук выходит нулём и молчит о той части, которой нет. Запуск он не отбивает никогда: сессия
19
+ # важнее контекста.
20
+
21
+ . "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
22
+
23
+ ROOT="${CLAUDE_PROJECT_DIR:-.}"
24
+ cd "$ROOT" 2>/dev/null || exit 0
25
+ git rev-parse --is-inside-work-tree >/dev/null 2>&1 || exit 0
26
+
27
+ branch="$(git branch --show-current 2>/dev/null)"
28
+
29
+ # Профиль дерева: сперва умолчание пакета, поверх него — надстройка проекта, если она есть.
30
+ rt_hooks_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
31
+ for profile in "$rt_hooks_dir/../rt-kit/defaults/project.sh" "$rt_hooks_dir/../defaults/project.sh" \
32
+ "$ROOT/.claude/rt-kit/defaults/project.sh" "$ROOT/.claude/rt-kit/project.sh"; do
33
+ # shellcheck disable=SC1090
34
+ [ -f "$profile" ] && . "$profile" 2>/dev/null
35
+ done
36
+
37
+ handoff_dir="${RT_HANDOFF_DIR:-.claude/handoff}"
38
+ map="$rt_hooks_dir/../rt-kit/defaults/turn-map.md"
39
+ [ -f "$map" ] || map="$ROOT/.claude/rt-kit/defaults/turn-map.md"
40
+
41
+ # Передача берётся по имени текущей ветки. Чужая, поданная как своя, описывает работу, которой
42
+ # в этом дереве нет, — поэтому имя файла собирается из ветки, а не выбирается из каталога.
43
+ handoff=''
44
+ [ -n "$branch" ] && handoff="$ROOT/$handoff_dir/$branch.md"
45
+
46
+ if [ -n "$handoff" ] && [ -r "$handoff" ]; then
47
+ printf 'ПЕРЕДАЧА ПРОШЛОГО ЗАХОДА — `%s/%s.md`, ниже целиком.\n\n' "$handoff_dir" "$branch"
48
+ printf 'Она написана прошлым заходом и описывает минуту, когда её собрали. Всё, что в ней\n'
49
+ printf 'стоит о дереве, проверяется деревом: команда этого хода главнее записанной вчера.\n\n'
50
+ cat "$handoff" 2>/dev/null
51
+ printf '\n\n'
52
+ fi
53
+
54
+ if [ -r "$map" ]; then
55
+ printf 'КАРТА ХОДА — состояния работы и то, чем ход кончается. Ниже целиком.\n\n'
56
+ printf 'Это выжимка правила ведения работы, а не оно само: правило объясняет, карта\n'
57
+ printf 'называет. Своё состояние заход читает в разделе «Где стоим» хода работы.\n\n'
58
+ cat "$map" 2>/dev/null
59
+ printf '\n'
60
+ fi
61
+
62
+ exit 0
@@ -29,6 +29,8 @@
29
29
  # или строки состояния ход РАЗРЕШАЕТСЯ (exit 0). Сломанный страж не имеет права заклинить
30
30
  # разговор.
31
31
 
32
+ . "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
33
+
32
34
  input="$(cat 2>/dev/null)"
33
35
  [ -z "$input" ] && exit 0
34
36
  command -v jq >/dev/null 2>&1 || exit 0
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env bash
2
+ # Локаль исполнения гарда. Подключается первой строкой тела: `. "<каталог гардов>/utf8.sh"`.
3
+ #
4
+ # Образцы гардов написаны словами того языка, на котором говорит дерево, и совпадение с ними
5
+ # зависит от локали процесса. В локали C складывание регистра работает только для латиницы:
6
+ # «В дереве этого нет» под образцом `(в дереве|здесь)…` не находится вовсе, гард выходит нулём
7
+ # и пропускает ход, который обязан был вернуть. Счёт `{0,30}` там же считает байты, а не буквы,
8
+ # и буква вне латиницы весит два.
9
+ #
10
+ # Заметить это на своей машине нечем: терминал разработчика идёт в UTF-8, а служба, запускающая
11
+ # ту же работу, наследует пустую локаль. Три сценария так и падали в конвейере, проходя на
12
+ # машине, где их писали.
13
+ #
14
+ # FAIL-OPEN: локали UTF-8 в системе не нашлось — ничего не объявляется, и гард работает как
15
+ # прежде. Отобрать у него работу из-за отсутствия локали хуже, чем оставить прежнее поведение.
16
+
17
+ rt_use_utf8_locale() {
18
+ case "${LC_ALL:-${LC_CTYPE:-${LANG:-}}}" in
19
+ *UTF-8* | *utf-8* | *UTF8* | *utf8*) return 0 ;;
20
+ esac
21
+
22
+ local available candidate
23
+ available="$(locale -a 2>/dev/null)" || return 0
24
+
25
+ for candidate in C.UTF-8 en_US.UTF-8 ru_RU.UTF-8; do
26
+ if printf '%s\n' "$available" | grep -qx -- "$candidate"; then
27
+ export LC_ALL="$candidate"
28
+ return 0
29
+ fi
30
+ done
31
+
32
+ return 0
33
+ }
34
+
35
+ rt_use_utf8_locale