@bookedsolid/rea 0.10.3 → 0.12.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.
- package/.husky/pre-push +48 -162
- package/README.md +834 -552
- package/agents/codex-adversarial.md +5 -3
- package/commands/codex-review.md +3 -5
- package/dist/audit/append.d.ts +7 -32
- package/dist/audit/append.js +7 -35
- package/dist/cli/audit.d.ts +0 -31
- package/dist/cli/audit.js +5 -74
- package/dist/cli/doctor.d.ts +12 -0
- package/dist/cli/doctor.js +96 -17
- package/dist/cli/hook.d.ts +55 -0
- package/dist/cli/hook.js +138 -0
- package/dist/cli/index.js +5 -80
- package/dist/cli/init.js +1 -1
- package/dist/cli/install/gitignore.d.ts +2 -2
- package/dist/cli/install/gitignore.js +3 -3
- package/dist/cli/install/pre-push.d.ts +158 -272
- package/dist/cli/install/pre-push.js +491 -2633
- package/dist/cli/install/settings-merge.d.ts +17 -0
- package/dist/cli/install/settings-merge.js +48 -1
- package/dist/cli/upgrade.js +131 -3
- package/dist/config/tier-map.js +18 -25
- package/dist/hooks/push-gate/base.d.ts +104 -0
- package/dist/hooks/push-gate/base.js +198 -0
- package/dist/hooks/push-gate/codex-runner.d.ts +126 -0
- package/dist/hooks/push-gate/codex-runner.js +223 -0
- package/dist/hooks/push-gate/findings.d.ts +68 -0
- package/dist/hooks/push-gate/findings.js +142 -0
- package/dist/hooks/push-gate/halt.d.ts +28 -0
- package/dist/hooks/push-gate/halt.js +49 -0
- package/dist/hooks/push-gate/index.d.ts +98 -0
- package/dist/hooks/push-gate/index.js +416 -0
- package/dist/hooks/push-gate/policy.d.ts +55 -0
- package/dist/hooks/push-gate/policy.js +64 -0
- package/dist/hooks/push-gate/report.d.ts +89 -0
- package/dist/hooks/push-gate/report.js +140 -0
- package/dist/policy/loader.d.ts +15 -10
- package/dist/policy/loader.js +8 -6
- package/dist/policy/types.d.ts +73 -22
- package/package.json +1 -1
- package/scripts/tarball-smoke.sh +7 -2
- package/dist/cache/review-cache.d.ts +0 -115
- package/dist/cache/review-cache.js +0 -200
- package/dist/cli/cache.d.ts +0 -84
- package/dist/cli/cache.js +0 -150
- package/dist/hooks/review-gate/args.d.ts +0 -126
- package/dist/hooks/review-gate/args.js +0 -315
- package/dist/hooks/review-gate/audit.d.ts +0 -131
- package/dist/hooks/review-gate/audit.js +0 -181
- package/dist/hooks/review-gate/banner.d.ts +0 -97
- package/dist/hooks/review-gate/banner.js +0 -172
- package/dist/hooks/review-gate/base-resolve.d.ts +0 -155
- package/dist/hooks/review-gate/base-resolve.js +0 -247
- package/dist/hooks/review-gate/cache-key.d.ts +0 -55
- package/dist/hooks/review-gate/cache-key.js +0 -41
- package/dist/hooks/review-gate/cache.d.ts +0 -108
- package/dist/hooks/review-gate/cache.js +0 -120
- package/dist/hooks/review-gate/constants.d.ts +0 -26
- package/dist/hooks/review-gate/constants.js +0 -34
- package/dist/hooks/review-gate/diff.d.ts +0 -181
- package/dist/hooks/review-gate/diff.js +0 -232
- package/dist/hooks/review-gate/errors.d.ts +0 -72
- package/dist/hooks/review-gate/errors.js +0 -100
- package/dist/hooks/review-gate/hash.d.ts +0 -43
- package/dist/hooks/review-gate/hash.js +0 -46
- package/dist/hooks/review-gate/index.d.ts +0 -31
- package/dist/hooks/review-gate/index.js +0 -35
- package/dist/hooks/review-gate/metadata.d.ts +0 -98
- package/dist/hooks/review-gate/metadata.js +0 -158
- package/dist/hooks/review-gate/policy.d.ts +0 -55
- package/dist/hooks/review-gate/policy.js +0 -71
- package/dist/hooks/review-gate/protected-paths.d.ts +0 -46
- package/dist/hooks/review-gate/protected-paths.js +0 -76
- package/hooks/_lib/push-review-core.sh +0 -1250
- package/hooks/commit-review-gate.sh +0 -330
- package/hooks/push-review-gate-git.sh +0 -94
- package/hooks/push-review-gate.sh +0 -92
package/.husky/pre-push
CHANGED
|
@@ -1,180 +1,66 @@
|
|
|
1
1
|
#!/bin/sh
|
|
2
|
-
# rea:husky-pre-push-gate
|
|
3
|
-
# rea:gate-body-
|
|
4
|
-
# .husky/pre-push — rea governance gate for terminal-initiated pushes.
|
|
2
|
+
# rea:husky-pre-push-gate v3
|
|
3
|
+
# rea:gate-body-v3
|
|
5
4
|
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
# <local_ref> <local_sha> <remote_ref> <remote_sha>).
|
|
5
|
+
# Husky pre-push hook installed by `rea init` / `rea upgrade`. Do NOT
|
|
6
|
+
# edit by hand — the file is refreshed on every rea upgrade.
|
|
9
7
|
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
# 2. If the push touches a protected path AND policy.review.codex_required
|
|
13
|
-
# is not explicitly false, require a `codex.review` audit entry for the
|
|
14
|
-
# HEAD SHA (or REA_SKIP_CODEX_REVIEW env var for a one-off bypass).
|
|
15
|
-
#
|
|
16
|
-
# Escape hatch: REA_SKIP_CODEX_REVIEW=<reason> bypasses the protected-path
|
|
17
|
-
# check. The skip record is appended by `push-review-gate.sh` in the Claude
|
|
18
|
-
# Code path; for terminal pushes, export the variable AND append a skip
|
|
19
|
-
# record manually if you want it in the audit trail.
|
|
20
|
-
#
|
|
21
|
-
# Subshell-safety note: earlier versions piped `echo "$INPUT" | while read`,
|
|
22
|
-
# which ran the loop in a subshell — `exit 1` inside the loop aborted the
|
|
23
|
-
# subshell only, and the script then ran `exit 0` and allowed the push. We
|
|
24
|
-
# now feed the loop with a here-doc so it runs in the main shell, and we
|
|
25
|
-
# abort immediately (`exit 1`) on the first blocking refspec. The accumulator
|
|
26
|
-
# pattern (`block_push=1; continue`) was dropped so the text-level detector
|
|
27
|
-
# in `src/cli/install/pre-push.ts` can verify the miss-path is truly blocking
|
|
28
|
-
# without modeling loop-carried flags and post-loop exit blocks.
|
|
8
|
+
# Governance contract: HALT kill-switch check, then delegate to
|
|
9
|
+
# `rea hook push-gate`. See src/hooks/push-gate/index.ts.
|
|
29
10
|
|
|
30
11
|
set -eu
|
|
31
12
|
|
|
32
|
-
#
|
|
33
|
-
#
|
|
34
|
-
#
|
|
35
|
-
#
|
|
36
|
-
|
|
13
|
+
# REA push-gate (0.11.0+). The heavy lifting — git diff resolution, Codex
|
|
14
|
+
# invocation, verdict inference, audit write — lives in
|
|
15
|
+
# `src/hooks/push-gate/` and is invoked via `rea hook push-gate`.
|
|
16
|
+
# This stub only short-circuits on the kill-switch and resolves the rea
|
|
17
|
+
# binary (in priority: project node_modules/.bin/rea → dogfood dist →
|
|
18
|
+
# PATH → npx).
|
|
19
|
+
#
|
|
20
|
+
# The 0.10.x hooks assumed rea was on PATH. Consumers who bootstrap via
|
|
21
|
+
# `npx @bookedsolid/rea init` have no persistent global rea install, so
|
|
22
|
+
# the bare `exec rea` pattern fails with "rea: not found" on push. We
|
|
23
|
+
# resolve against the project-local node_modules/.bin first, then PATH,
|
|
24
|
+
# then fall back to npx so the gate runs in every documented setup.
|
|
37
25
|
|
|
38
26
|
REA_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
|
|
39
|
-
|
|
40
|
-
# Well-known empty-tree SHA: `git hash-object -t tree /dev/null`. Every git
|
|
41
|
-
# installation carries this object implicitly — using it as a merge-base
|
|
42
|
-
# baseline for initial pushes lets `git diff $EMPTY_TREE $local_sha` emit
|
|
43
|
-
# the complete change set against a truly-empty tree. The protected-path
|
|
44
|
-
# check then sees every file in the initial push, so a first push of
|
|
45
|
-
# protected-path changes to a fresh remote is still gated.
|
|
46
|
-
EMPTY_TREE='4b825dc642cb6eb9a060e54bf8d69288fbee4904'
|
|
47
|
-
|
|
48
27
|
if [ -f "${REA_ROOT}/.rea/HALT" ]; then
|
|
49
|
-
# POSIX `head` does not specify `-c`; use awk for the first line. HALT is
|
|
50
|
-
# a short reason string, so the first line is enough for display.
|
|
51
28
|
reason=$(awk 'NR==1 { print; exit }' "${REA_ROOT}/.rea/HALT" 2>/dev/null || printf 'unknown')
|
|
52
29
|
[ -z "${reason:-}" ] && reason='unknown'
|
|
53
30
|
printf 'REA HALT: %s\nAll push operations suspended. Run: rea unfreeze\n' "$reason" >&2
|
|
54
31
|
exit 1
|
|
55
32
|
fi
|
|
56
33
|
|
|
57
|
-
#
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
#
|
|
62
|
-
# `src/thirdparty/src/policy/loader.c` is not mistaken for a protected path.
|
|
63
|
-
# `^\.claude/hooks/` is included so someone editing the consumer install
|
|
64
|
-
# (which ships alongside rea itself) cannot sneak past the gate.
|
|
65
|
-
PROTECTED_RE='^src/gateway/middleware/|^hooks/|^\.claude/hooks/|^src/policy/|^\.github/workflows/'
|
|
66
|
-
AUDIT_LOG="${REA_ROOT}/.rea/audit.jsonl"
|
|
67
|
-
|
|
68
|
-
# G11.4 — honor review.codex_required. When explicitly false, skip the
|
|
69
|
-
# protected-path Codex audit requirement entirely (first-class no-Codex
|
|
70
|
-
# mode). Mirrors the logic in `.claude/hooks/push-review-gate.sh`.
|
|
34
|
+
# Dispatch the rea CLI as a positional-args list rather than a string
|
|
35
|
+
# (the 0.11.x `exec $REA_BIN ...` pattern broke when REA_ROOT contained
|
|
36
|
+
# whitespace because the unquoted $REA_BIN expansion underwent word
|
|
37
|
+
# splitting; `/Users/jane/My Projects/repo` produced four argv tokens
|
|
38
|
+
# instead of two). The `set --` form below preserves spaces verbatim.
|
|
71
39
|
#
|
|
72
|
-
#
|
|
73
|
-
#
|
|
74
|
-
#
|
|
75
|
-
CODEX_REQUIRED=true
|
|
76
|
-
READ_FIELD_JS="${REA_ROOT}/dist/scripts/read-policy-field.js"
|
|
77
|
-
if [ -f "$READ_FIELD_JS" ]; then
|
|
78
|
-
field_value=$(REA_ROOT="$REA_ROOT" node "$READ_FIELD_JS" review.codex_required 2>/dev/null || printf '')
|
|
79
|
-
if [ "$field_value" = "false" ]; then
|
|
80
|
-
CODEX_REQUIRED=false
|
|
81
|
-
fi
|
|
82
|
-
fi
|
|
83
|
-
|
|
84
|
-
# Here-doc feeds the loop without creating a subshell, so an `exit 1`
|
|
85
|
-
# inside the loop terminates the hook and blocks the push. A pipeline
|
|
86
|
-
# would run the loop in a subshell and `exit 1` inside it would only
|
|
87
|
-
# abort that subshell — NOT the push — which was a real governance
|
|
88
|
-
# defect in the pre-review version of this file.
|
|
89
|
-
while IFS=' ' read -r local_ref local_sha remote_ref remote_sha; do
|
|
90
|
-
[ -z "${local_sha:-}" ] && continue
|
|
91
|
-
# Branch deletion: local_sha is 40 zeros. Skip protected-path check.
|
|
92
|
-
case "$local_sha" in
|
|
93
|
-
0000000000000000000000000000000000000000) continue ;;
|
|
94
|
-
esac
|
|
40
|
+
# The pre-push stdin carries one line per refspec; `exec` inherits stdin
|
|
41
|
+
# unchanged. $@ on entry carries git's <remote-name> <remote-url>; we
|
|
42
|
+
# preserve those by appending "$@" inside each `set --` arm.
|
|
95
43
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
#
|
|
100
|
-
#
|
|
101
|
-
#
|
|
102
|
-
#
|
|
103
|
-
#
|
|
104
|
-
#
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
#
|
|
110
|
-
#
|
|
111
|
-
#
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
default_ref="refs/remotes/${REMOTE}/main"
|
|
118
|
-
elif git rev-parse --verify --quiet "refs/remotes/${REMOTE}/master" >/dev/null 2>&1; then
|
|
119
|
-
default_ref="refs/remotes/${REMOTE}/master"
|
|
120
|
-
else
|
|
121
|
-
default_ref=""
|
|
122
|
-
fi
|
|
123
|
-
fi
|
|
124
|
-
if [ -n "${default_ref:-}" ]; then
|
|
125
|
-
base=$(git merge-base "$default_ref" "$local_sha" 2>/dev/null || printf '')
|
|
126
|
-
else
|
|
127
|
-
# Bootstrap: no remote-tracking ref exists at all. Use the empty-tree
|
|
128
|
-
# baseline so the diff covers every file in the push. git diff accepts
|
|
129
|
-
# a tree SHA as the left-hand side.
|
|
130
|
-
base="$EMPTY_TREE"
|
|
131
|
-
fi
|
|
132
|
-
else
|
|
133
|
-
base=$(git merge-base "$remote_sha" "$local_sha" 2>/dev/null || printf '')
|
|
134
|
-
fi
|
|
135
|
-
# Fail CLOSED on empty merge-base when a remote ref DID resolve. The
|
|
136
|
-
# 0.4.0..0.6.2 behavior here was to `continue` — a silent bypass. A push
|
|
137
|
-
# whose history is unrelated to origin (or any transient git failure at
|
|
138
|
-
# merge-base resolution) would pass through without the protected-path
|
|
139
|
-
# check ever running. Refuse instead and force the operator to resolve it.
|
|
140
|
-
if [ -z "${base:-}" ]; then
|
|
141
|
-
printf 'PUSH BLOCKED: could not resolve merge-base between %s and %s (local_ref=%s remote_ref=%s).\n' \
|
|
142
|
-
"${remote_sha:-<new>}" "${local_sha:-<missing>}" "${local_ref:-<unknown>}" "${remote_ref:-<unknown>}" >&2
|
|
143
|
-
printf ' Run `git fetch %s` and retry. If the history is genuinely unrelated\n' "$REMOTE" >&2
|
|
144
|
-
printf ' to %s (e.g. grafted branch), resolve manually before pushing.\n' "$REMOTE" >&2
|
|
145
|
-
exit 1
|
|
146
|
-
fi
|
|
147
|
-
|
|
148
|
-
# Check if the diff touches protected paths.
|
|
149
|
-
if git diff --name-only "$base" "$local_sha" 2>/dev/null | grep -qE "$PROTECTED_RE"; then
|
|
150
|
-
if [ "$CODEX_REQUIRED" = "false" ]; then
|
|
151
|
-
# Policy opts out of the Codex gate. The downstream `.claude/hooks/`
|
|
152
|
-
# path already records telemetry; terminal pushes skip silently.
|
|
153
|
-
continue
|
|
154
|
-
fi
|
|
155
|
-
if [ -n "${REA_SKIP_CODEX_REVIEW:-}" ]; then
|
|
156
|
-
printf 'rea: REA_SKIP_CODEX_REVIEW set (%s) — skipping Codex review requirement for %s\n' \
|
|
157
|
-
"$REA_SKIP_CODEX_REVIEW" "$local_sha" >&2
|
|
158
|
-
continue
|
|
159
|
-
fi
|
|
160
|
-
if [ ! -f "$AUDIT_LOG" ]; then
|
|
161
|
-
printf 'PUSH BLOCKED: protected paths changed but no audit log found at %s\n' "$AUDIT_LOG" >&2
|
|
162
|
-
printf ' Run /codex-review on HEAD %s before pushing.\n' "$local_sha" >&2
|
|
163
|
-
exit 1
|
|
164
|
-
fi
|
|
165
|
-
# Require both (a) a `codex.review` tool_name and (b) the exact head_sha
|
|
166
|
-
# on the same JSONL line. The `codex.review` pattern ends with a closing
|
|
167
|
-
# quote, so `codex.review.skipped` never satisfies the gate. The first
|
|
168
|
-
# refspec that fails this check aborts the hook — no accumulator needed.
|
|
169
|
-
if ! grep -E '"tool_name":"codex\.review"' "$AUDIT_LOG" 2>/dev/null | \
|
|
170
|
-
grep -qF "\"head_sha\":\"$local_sha\""; then
|
|
171
|
-
printf 'PUSH BLOCKED: protected paths changed — /codex-review required for HEAD %s\n' "$local_sha" >&2
|
|
172
|
-
printf ' Run /codex-review, or set REA_SKIP_CODEX_REVIEW=<reason> to bypass.\n' >&2
|
|
173
|
-
exit 1
|
|
174
|
-
fi
|
|
175
|
-
fi
|
|
176
|
-
done <<HOOK_INPUT_EOF
|
|
177
|
-
$INPUT
|
|
178
|
-
HOOK_INPUT_EOF
|
|
44
|
+
if [ -x "${REA_ROOT}/node_modules/.bin/rea" ]; then
|
|
45
|
+
set -- "${REA_ROOT}/node_modules/.bin/rea" hook push-gate "$@"
|
|
46
|
+
elif [ -f "${REA_ROOT}/dist/cli/index.js" ] && [ -f "${REA_ROOT}/package.json" ] && grep -q '"name": *"@bookedsolid/rea"' "${REA_ROOT}/package.json" 2>/dev/null; then
|
|
47
|
+
# rea's own repo (dogfood) — the package is not installed under
|
|
48
|
+
# node_modules here because we ARE the package. The built CLI
|
|
49
|
+
# entry point lives at dist/cli/index.js; node runs it directly.
|
|
50
|
+
# Gate this branch on `package.json` declaring `@bookedsolid/rea` so a
|
|
51
|
+
# consumer repo that happens to ship its own `dist/cli/index.js` does
|
|
52
|
+
# not get this hook executing the consumer's unrelated build.
|
|
53
|
+
set -- node "${REA_ROOT}/dist/cli/index.js" hook push-gate "$@"
|
|
54
|
+
elif command -v rea >/dev/null 2>&1; then
|
|
55
|
+
set -- rea hook push-gate "$@"
|
|
56
|
+
elif command -v npx >/dev/null 2>&1; then
|
|
57
|
+
# Last resort: npx will resolve the package from npm or the cache.
|
|
58
|
+
# Pass `--no-install` so a rare cache-cold machine surfaces a clear
|
|
59
|
+
# error instead of silently downloading at push time.
|
|
60
|
+
set -- npx --no-install @bookedsolid/rea hook push-gate "$@"
|
|
61
|
+
else
|
|
62
|
+
printf 'rea: cannot locate the rea CLI. Install locally (`pnpm add -D @bookedsolid/rea`) or globally (`npm i -g @bookedsolid/rea`).\n' >&2
|
|
63
|
+
exit 2
|
|
64
|
+
fi
|
|
179
65
|
|
|
180
|
-
|
|
66
|
+
exec "$@"
|