@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.
Files changed (77) hide show
  1. package/.husky/pre-push +48 -162
  2. package/README.md +834 -552
  3. package/agents/codex-adversarial.md +5 -3
  4. package/commands/codex-review.md +3 -5
  5. package/dist/audit/append.d.ts +7 -32
  6. package/dist/audit/append.js +7 -35
  7. package/dist/cli/audit.d.ts +0 -31
  8. package/dist/cli/audit.js +5 -74
  9. package/dist/cli/doctor.d.ts +12 -0
  10. package/dist/cli/doctor.js +96 -17
  11. package/dist/cli/hook.d.ts +55 -0
  12. package/dist/cli/hook.js +138 -0
  13. package/dist/cli/index.js +5 -80
  14. package/dist/cli/init.js +1 -1
  15. package/dist/cli/install/gitignore.d.ts +2 -2
  16. package/dist/cli/install/gitignore.js +3 -3
  17. package/dist/cli/install/pre-push.d.ts +158 -272
  18. package/dist/cli/install/pre-push.js +491 -2633
  19. package/dist/cli/install/settings-merge.d.ts +17 -0
  20. package/dist/cli/install/settings-merge.js +48 -1
  21. package/dist/cli/upgrade.js +131 -3
  22. package/dist/config/tier-map.js +18 -25
  23. package/dist/hooks/push-gate/base.d.ts +104 -0
  24. package/dist/hooks/push-gate/base.js +198 -0
  25. package/dist/hooks/push-gate/codex-runner.d.ts +126 -0
  26. package/dist/hooks/push-gate/codex-runner.js +223 -0
  27. package/dist/hooks/push-gate/findings.d.ts +68 -0
  28. package/dist/hooks/push-gate/findings.js +142 -0
  29. package/dist/hooks/push-gate/halt.d.ts +28 -0
  30. package/dist/hooks/push-gate/halt.js +49 -0
  31. package/dist/hooks/push-gate/index.d.ts +98 -0
  32. package/dist/hooks/push-gate/index.js +416 -0
  33. package/dist/hooks/push-gate/policy.d.ts +55 -0
  34. package/dist/hooks/push-gate/policy.js +64 -0
  35. package/dist/hooks/push-gate/report.d.ts +89 -0
  36. package/dist/hooks/push-gate/report.js +140 -0
  37. package/dist/policy/loader.d.ts +15 -10
  38. package/dist/policy/loader.js +8 -6
  39. package/dist/policy/types.d.ts +73 -22
  40. package/package.json +1 -1
  41. package/scripts/tarball-smoke.sh +7 -2
  42. package/dist/cache/review-cache.d.ts +0 -115
  43. package/dist/cache/review-cache.js +0 -200
  44. package/dist/cli/cache.d.ts +0 -84
  45. package/dist/cli/cache.js +0 -150
  46. package/dist/hooks/review-gate/args.d.ts +0 -126
  47. package/dist/hooks/review-gate/args.js +0 -315
  48. package/dist/hooks/review-gate/audit.d.ts +0 -131
  49. package/dist/hooks/review-gate/audit.js +0 -181
  50. package/dist/hooks/review-gate/banner.d.ts +0 -97
  51. package/dist/hooks/review-gate/banner.js +0 -172
  52. package/dist/hooks/review-gate/base-resolve.d.ts +0 -155
  53. package/dist/hooks/review-gate/base-resolve.js +0 -247
  54. package/dist/hooks/review-gate/cache-key.d.ts +0 -55
  55. package/dist/hooks/review-gate/cache-key.js +0 -41
  56. package/dist/hooks/review-gate/cache.d.ts +0 -108
  57. package/dist/hooks/review-gate/cache.js +0 -120
  58. package/dist/hooks/review-gate/constants.d.ts +0 -26
  59. package/dist/hooks/review-gate/constants.js +0 -34
  60. package/dist/hooks/review-gate/diff.d.ts +0 -181
  61. package/dist/hooks/review-gate/diff.js +0 -232
  62. package/dist/hooks/review-gate/errors.d.ts +0 -72
  63. package/dist/hooks/review-gate/errors.js +0 -100
  64. package/dist/hooks/review-gate/hash.d.ts +0 -43
  65. package/dist/hooks/review-gate/hash.js +0 -46
  66. package/dist/hooks/review-gate/index.d.ts +0 -31
  67. package/dist/hooks/review-gate/index.js +0 -35
  68. package/dist/hooks/review-gate/metadata.d.ts +0 -98
  69. package/dist/hooks/review-gate/metadata.js +0 -158
  70. package/dist/hooks/review-gate/policy.d.ts +0 -55
  71. package/dist/hooks/review-gate/policy.js +0 -71
  72. package/dist/hooks/review-gate/protected-paths.d.ts +0 -46
  73. package/dist/hooks/review-gate/protected-paths.js +0 -76
  74. package/hooks/_lib/push-review-core.sh +0 -1250
  75. package/hooks/commit-review-gate.sh +0 -330
  76. package/hooks/push-review-gate-git.sh +0 -94
  77. package/hooks/push-review-gate.sh +0 -92
@@ -1,55 +0,0 @@
1
- /**
2
- * Cache-key computation. This module pins the contract between the push-
3
- * review gate and the review-cache (`rea cache check` / `rea cache set`).
4
- *
5
- * ## The 0.10.1 revert constraint (design §8)
6
- *
7
- * An earlier attempt at defect N changed the cache-key input in the bash
8
- * core and silently invalidated every consumer's existing cache. The
9
- * failure was: the bash resolver started using the refspec target ref in
10
- * the cache-key input rather than the merge-base-anchor SHA, so a
11
- * legitimate cache entry from before the change produced a different key
12
- * after the upgrade.
13
- *
14
- * The TS port makes the contract explicit:
15
- *
16
- * cache_key = sha256_hex( full_git_diff_output )
17
- *
18
- * where `full_git_diff_output` is the UTF-8 string returned by
19
- * `git diff <merge_base>..<source_sha>` with NO added framing. The key
20
- * is NOT a function of ref names, branch names, or target labels — those
21
- * are stored in the cache entry as context but do not participate in key
22
- * derivation.
23
- *
24
- * ## Fixture-backed compatibility test
25
- *
26
- * `__fixtures__/cache-keys.json` records six scenarios captured from the
27
- * 0.10.1 bash core (bare push, multi-refspec, force-push, deletion,
28
- * new-branch, cross-repo). `cache-key.test.ts` asserts byte-exact
29
- * `computeCacheKey(input) === expected` across all scenarios. Any phase
30
- * that changes this module without updating the fixture fails the suite.
31
- */
32
- import { type HexSha256 } from './hash.js';
33
- export interface CacheKeyInput {
34
- /** Full `git diff <merge_base>..<source_sha>` output. */
35
- diff: string;
36
- }
37
- /**
38
- * Compute the cache key for a push-review entry. Stable, deterministic,
39
- * pure. The key is the SHA-256 of the UTF-8 bytes of the diff string.
40
- *
41
- * @returns the 64-char lowercase hex digest.
42
- */
43
- export declare function computeCacheKey(input: CacheKeyInput): HexSha256;
44
- /**
45
- * Input shape for a cache-lookup call. The key itself is the diff digest
46
- * from `computeCacheKey`; branch + base are context fields that select
47
- * which entry within the key-bucket to return. The bash core and the
48
- * existing `src/cache/review-cache.ts` both key lookups on
49
- * `(sha, branch, base)`, and the TS port keeps that contract.
50
- */
51
- export interface CacheLookupContext {
52
- key: HexSha256;
53
- branch: string;
54
- base: string;
55
- }
@@ -1,41 +0,0 @@
1
- /**
2
- * Cache-key computation. This module pins the contract between the push-
3
- * review gate and the review-cache (`rea cache check` / `rea cache set`).
4
- *
5
- * ## The 0.10.1 revert constraint (design §8)
6
- *
7
- * An earlier attempt at defect N changed the cache-key input in the bash
8
- * core and silently invalidated every consumer's existing cache. The
9
- * failure was: the bash resolver started using the refspec target ref in
10
- * the cache-key input rather than the merge-base-anchor SHA, so a
11
- * legitimate cache entry from before the change produced a different key
12
- * after the upgrade.
13
- *
14
- * The TS port makes the contract explicit:
15
- *
16
- * cache_key = sha256_hex( full_git_diff_output )
17
- *
18
- * where `full_git_diff_output` is the UTF-8 string returned by
19
- * `git diff <merge_base>..<source_sha>` with NO added framing. The key
20
- * is NOT a function of ref names, branch names, or target labels — those
21
- * are stored in the cache entry as context but do not participate in key
22
- * derivation.
23
- *
24
- * ## Fixture-backed compatibility test
25
- *
26
- * `__fixtures__/cache-keys.json` records six scenarios captured from the
27
- * 0.10.1 bash core (bare push, multi-refspec, force-push, deletion,
28
- * new-branch, cross-repo). `cache-key.test.ts` asserts byte-exact
29
- * `computeCacheKey(input) === expected` across all scenarios. Any phase
30
- * that changes this module without updating the fixture fails the suite.
31
- */
32
- import { sha256Hex } from './hash.js';
33
- /**
34
- * Compute the cache key for a push-review entry. Stable, deterministic,
35
- * pure. The key is the SHA-256 of the UTF-8 bytes of the diff string.
36
- *
37
- * @returns the 64-char lowercase hex digest.
38
- */
39
- export function computeCacheKey(input) {
40
- return sha256Hex(input.diff);
41
- }
@@ -1,108 +0,0 @@
1
- /**
2
- * Review-cache adapter for the push-review gate.
3
- *
4
- * ## What this module owns
5
- *
6
- * 1. Compute the cache key from a diff. **Re-exports** `computeCacheKey`
7
- * from the Phase-1 `cache-key.ts` module — this is the same function,
8
- * not a reimplementation. The fixture suite (`cache.test.ts`) locks
9
- * that invariant via a byte-exact re-run against
10
- * `__fixtures__/cache-keys.json`. If the two modules ever diverge,
11
- * the 0.10.x → 0.11.0 cache contract is broken and every consumer's
12
- * on-disk cache becomes unreachable. See design §8.
13
- *
14
- * 2. Wrap `src/cache/review-cache.ts` so callers get a single entry
15
- * point — `checkReviewCache({ diff, branch, base })` — that hides
16
- * the TTL semantics, the `(sha, branch, base)` key tuple, and the
17
- * cache-lookup result shape.
18
- *
19
- * 3. Translate the cache lookup into the same three-way outcome the
20
- * bash core's §1203 `jq -e '.hit == true and .result == "pass"'`
21
- * check emitted:
22
- *
23
- * - `hit_pass` — cache hit AND result === 'pass'. Gate passes.
24
- * - `hit_fail` — cache hit AND result === 'fail'. Cached
25
- * negative verdict; gate blocks. (Bash §1197-1202
26
- * rejects this; we preserve.)
27
- * - `miss` — no hit / expired / empty file. Gate falls
28
- * through to the review-required banner.
29
- * - `query_error` — cache lookup threw. Bash §1180-1196 treated
30
- * this as a `{"hit":false,"reason":"query_error"}`
31
- * cached result; we carry the error body so the
32
- * caller can emit the CACHE CHECK FAILED banner
33
- * with the SANITIZED stderr (defect C0/C1 strip).
34
- *
35
- * ## Phase 2a scope
36
- *
37
- * This file exports pure functions over the already-TS
38
- * `review-cache.ts`. No subprocess `spawn`, no CLI fork. The bash
39
- * core's `rea cache check` subprocess hop is obviated entirely —
40
- * once Phase 3 swaps the shim we no longer fork/exec node for the
41
- * cache lookup at all.
42
- *
43
- * ## Phase 2b composition
44
- *
45
- * `runPushReviewGate` in `index.ts` calls `computeCacheKeyFromDiff` +
46
- * `checkReviewCache` sequentially; the latter returns a discriminated
47
- * outcome the gate branches on. No new behavior lands here — that's
48
- * the `codex-gate.ts` / composition step.
49
- */
50
- import type { HexSha256 } from './hash.js';
51
- /**
52
- * Compute the cache key for a diff. This is a thin re-export of Phase 1's
53
- * `computeCacheKey` — exposed on this module so callers can use a single
54
- * import when they need both the key AND the lookup.
55
- *
56
- * The function is UNCHANGED from Phase 1. The fixture suite in
57
- * `cache.test.ts` proves byte-exact parity against
58
- * `__fixtures__/cache-keys.json` for all six scenarios.
59
- */
60
- export declare function computeCacheKey(diff: string): HexSha256;
61
- /**
62
- * Discriminated outcome of a cache lookup. Matches the three-way state
63
- * the bash core's §1203 predicate surfaces, plus a `query_error` kind
64
- * for the §1180-1196 case.
65
- */
66
- export type CacheOutcome = {
67
- kind: 'hit_pass';
68
- key: HexSha256;
69
- recorded_at: string;
70
- } | {
71
- kind: 'hit_fail';
72
- key: HexSha256;
73
- recorded_at: string;
74
- reason?: string;
75
- } | {
76
- kind: 'miss';
77
- key: HexSha256;
78
- reason: 'no-entry' | 'expired' | 'empty-file';
79
- } | {
80
- kind: 'query_error';
81
- key: HexSha256;
82
- error: string;
83
- };
84
- /**
85
- * Input for `checkReviewCache`. `diff` is the full `git diff` body; the
86
- * cache key is derived from it via `computeCacheKey`. `branch` + `base`
87
- * select which entry in the key-bucket is returned (most-recent match).
88
- */
89
- export interface CheckReviewCacheInput {
90
- baseDir: string;
91
- diff: string;
92
- branch: string;
93
- base: string;
94
- /** Optional override for `Date.now()`-driven TTL expiration — test hook only. */
95
- nowMs?: number;
96
- /** Optional override for the TTL (seconds). Defaults to cache-module default. */
97
- maxAgeSeconds?: number;
98
- }
99
- /**
100
- * Perform a cache lookup and translate into the discriminated outcome.
101
- *
102
- * Does NOT throw on a lookup failure — every known error path routes
103
- * through the `query_error` variant so the caller's single `switch`
104
- * handles all four outcomes. This mirrors the bash core's
105
- * `CACHE_STDOUT || CACHE_EXIT != 0 → {"hit":false,...}` collapse at
106
- * §1180-1196.
107
- */
108
- export declare function checkReviewCache(input: CheckReviewCacheInput): Promise<CacheOutcome>;
@@ -1,120 +0,0 @@
1
- /**
2
- * Review-cache adapter for the push-review gate.
3
- *
4
- * ## What this module owns
5
- *
6
- * 1. Compute the cache key from a diff. **Re-exports** `computeCacheKey`
7
- * from the Phase-1 `cache-key.ts` module — this is the same function,
8
- * not a reimplementation. The fixture suite (`cache.test.ts`) locks
9
- * that invariant via a byte-exact re-run against
10
- * `__fixtures__/cache-keys.json`. If the two modules ever diverge,
11
- * the 0.10.x → 0.11.0 cache contract is broken and every consumer's
12
- * on-disk cache becomes unreachable. See design §8.
13
- *
14
- * 2. Wrap `src/cache/review-cache.ts` so callers get a single entry
15
- * point — `checkReviewCache({ diff, branch, base })` — that hides
16
- * the TTL semantics, the `(sha, branch, base)` key tuple, and the
17
- * cache-lookup result shape.
18
- *
19
- * 3. Translate the cache lookup into the same three-way outcome the
20
- * bash core's §1203 `jq -e '.hit == true and .result == "pass"'`
21
- * check emitted:
22
- *
23
- * - `hit_pass` — cache hit AND result === 'pass'. Gate passes.
24
- * - `hit_fail` — cache hit AND result === 'fail'. Cached
25
- * negative verdict; gate blocks. (Bash §1197-1202
26
- * rejects this; we preserve.)
27
- * - `miss` — no hit / expired / empty file. Gate falls
28
- * through to the review-required banner.
29
- * - `query_error` — cache lookup threw. Bash §1180-1196 treated
30
- * this as a `{"hit":false,"reason":"query_error"}`
31
- * cached result; we carry the error body so the
32
- * caller can emit the CACHE CHECK FAILED banner
33
- * with the SANITIZED stderr (defect C0/C1 strip).
34
- *
35
- * ## Phase 2a scope
36
- *
37
- * This file exports pure functions over the already-TS
38
- * `review-cache.ts`. No subprocess `spawn`, no CLI fork. The bash
39
- * core's `rea cache check` subprocess hop is obviated entirely —
40
- * once Phase 3 swaps the shim we no longer fork/exec node for the
41
- * cache lookup at all.
42
- *
43
- * ## Phase 2b composition
44
- *
45
- * `runPushReviewGate` in `index.ts` calls `computeCacheKeyFromDiff` +
46
- * `checkReviewCache` sequentially; the latter returns a discriminated
47
- * outcome the gate branches on. No new behavior lands here — that's
48
- * the `codex-gate.ts` / composition step.
49
- */
50
- import { lookup } from '../../cache/review-cache.js';
51
- import { computeCacheKey as computePhase1CacheKey } from './cache-key.js';
52
- /**
53
- * Compute the cache key for a diff. This is a thin re-export of Phase 1's
54
- * `computeCacheKey` — exposed on this module so callers can use a single
55
- * import when they need both the key AND the lookup.
56
- *
57
- * The function is UNCHANGED from Phase 1. The fixture suite in
58
- * `cache.test.ts` proves byte-exact parity against
59
- * `__fixtures__/cache-keys.json` for all six scenarios.
60
- */
61
- export function computeCacheKey(diff) {
62
- return computePhase1CacheKey({ diff });
63
- }
64
- /**
65
- * Perform a cache lookup and translate into the discriminated outcome.
66
- *
67
- * Does NOT throw on a lookup failure — every known error path routes
68
- * through the `query_error` variant so the caller's single `switch`
69
- * handles all four outcomes. This mirrors the bash core's
70
- * `CACHE_STDOUT || CACHE_EXIT != 0 → {"hit":false,...}` collapse at
71
- * §1180-1196.
72
- */
73
- export async function checkReviewCache(input) {
74
- const key = computeCacheKey(input.diff);
75
- let result;
76
- try {
77
- result = await lookup(input.baseDir, {
78
- sha: key,
79
- branch: input.branch,
80
- base: input.base,
81
- ...(input.nowMs !== undefined ? { nowMs: input.nowMs } : {}),
82
- ...(input.maxAgeSeconds !== undefined ? { maxAgeSeconds: input.maxAgeSeconds } : {}),
83
- });
84
- }
85
- catch (err) {
86
- const msg = err instanceof Error ? err.message : String(err);
87
- return { kind: 'query_error', key, error: msg };
88
- }
89
- if (!result.hit) {
90
- // `result.missReason` is always set when `hit` is false (see
91
- // review-cache.ts's CacheLookupResult contract). The fallback to
92
- // 'no-entry' is defensive for an ill-formed result we shouldn't see.
93
- return {
94
- kind: 'miss',
95
- key,
96
- reason: result.missReason ?? 'no-entry',
97
- };
98
- }
99
- // Hit. Branch on verdict — the bash core requires BOTH hit==true AND
100
- // result==pass. A hit with result==fail is a cached negative verdict
101
- // and must NOT unblock the push.
102
- const entry = result.entry;
103
- if (entry === undefined) {
104
- // Defensive: `hit: true` without an entry is an ill-formed result.
105
- return { kind: 'miss', key, reason: 'no-entry' };
106
- }
107
- if (entry.result === 'pass') {
108
- return { kind: 'hit_pass', key, recorded_at: entry.recorded_at };
109
- }
110
- // result === 'fail'
111
- const hitFail = {
112
- kind: 'hit_fail',
113
- key,
114
- recorded_at: entry.recorded_at,
115
- };
116
- if (entry.reason !== undefined && entry.reason.length > 0) {
117
- hitFail.reason = entry.reason;
118
- }
119
- return hitFail;
120
- }
@@ -1,26 +0,0 @@
1
- /**
2
- * Constants shared across the review-gate modules. Kept in one file so the
3
- * values that a reader cares about (the all-zeros SHA, the empty-tree SHA,
4
- * the protected-path set) are trivially grep-able.
5
- */
6
- /** The git-native "null" SHA — a deletion on the pre-push contract. */
7
- export declare const ZERO_SHA = "0000000000000000000000000000000000000000";
8
- /**
9
- * The canonical empty-tree SHA-1. Used as the merge-base baseline when a
10
- * new-branch push has no remote-tracking ref to anchor on — `git diff
11
- * <empty-tree>..<local_sha>` gives the full push content, and the gate
12
- * treats it as a diff against nothing (which is what it is, operationally).
13
- */
14
- export declare const EMPTY_TREE_SHA = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
15
- /**
16
- * The protected-path set. A push that touches any file under one of these
17
- * prefixes requires a matching `codex.review` audit record with verdict in
18
- * {pass, concerns} AND emission_source in {rea-cli, codex-cli}. See the
19
- * THREAT_MODEL for the full threat statement, and the design doc §9 for
20
- * the carry-forward from the bash core.
21
- *
22
- * Pattern format: leading/trailing slashes stripped; matched as directory
23
- * prefixes by `protected-paths.ts`. Order is not significant (a hit on any
24
- * entry is sufficient), but we keep the list sorted for grep-ability.
25
- */
26
- export declare const PROTECTED_PATH_PREFIXES: readonly string[];
@@ -1,34 +0,0 @@
1
- /**
2
- * Constants shared across the review-gate modules. Kept in one file so the
3
- * values that a reader cares about (the all-zeros SHA, the empty-tree SHA,
4
- * the protected-path set) are trivially grep-able.
5
- */
6
- /** The git-native "null" SHA — a deletion on the pre-push contract. */
7
- export const ZERO_SHA = '0000000000000000000000000000000000000000';
8
- /**
9
- * The canonical empty-tree SHA-1. Used as the merge-base baseline when a
10
- * new-branch push has no remote-tracking ref to anchor on — `git diff
11
- * <empty-tree>..<local_sha>` gives the full push content, and the gate
12
- * treats it as a diff against nothing (which is what it is, operationally).
13
- */
14
- export const EMPTY_TREE_SHA = '4b825dc642cb6eb9a060e54bf8d69288fbee4904';
15
- /**
16
- * The protected-path set. A push that touches any file under one of these
17
- * prefixes requires a matching `codex.review` audit record with verdict in
18
- * {pass, concerns} AND emission_source in {rea-cli, codex-cli}. See the
19
- * THREAT_MODEL for the full threat statement, and the design doc §9 for
20
- * the carry-forward from the bash core.
21
- *
22
- * Pattern format: leading/trailing slashes stripped; matched as directory
23
- * prefixes by `protected-paths.ts`. Order is not significant (a hit on any
24
- * entry is sufficient), but we keep the list sorted for grep-ability.
25
- */
26
- export const PROTECTED_PATH_PREFIXES = [
27
- '.claude/hooks/',
28
- '.github/workflows/',
29
- '.husky/',
30
- '.rea/',
31
- 'hooks/',
32
- 'src/gateway/middleware/',
33
- 'src/policy/',
34
- ];
@@ -1,181 +0,0 @@
1
- /**
2
- * Git-subprocess wrappers the review gate needs.
3
- *
4
- * ## Why a dedicated module
5
- *
6
- * The bash core spawns git via inline `$(cd "$REA_ROOT" && git ... 2>/dev/null)`
7
- * subshells. Each invocation carries a handful of concerns the TS port
8
- * separates cleanly:
9
- *
10
- * - Args passed as an ARRAY so the shell never interprets them
11
- * (push-review-ts-port design §9 security posture: no argument-injection
12
- * CVEs around refspec names like `main;rm -rf /`).
13
- * - `cwd` always set to the resolved repo root — the caller never has to
14
- * remember to `cd` first.
15
- * - stdout/stderr captured separately. Git's error text goes to stderr in
16
- * normal modes, and callers often want stderr for diagnostics while
17
- * still deciding based on exit code.
18
- * - A single shared timeout (10s) catches a hung `git` process. The bash
19
- * core had no timeout — an upstream `git` stuck on NFS could wedge the
20
- * whole push indefinitely.
21
- *
22
- * ## Mockability
23
- *
24
- * Every exported function takes a `GitRunner` as its first positional so
25
- * tests can stub the subprocess layer. The default runner spawns `git`;
26
- * tests supply a recording runner and assert over the command history. This
27
- * is how `base-resolve.test.ts` and `diff.test.ts` avoid needing a real
28
- * git repo.
29
- *
30
- * ## Defect carry-forwards
31
- *
32
- * - Two-dot `A..B` for diff inputs, NEVER three-dot `A...B`. The bash
33
- * core's comment on push-review-core.sh §1053-1060 covers this: three-dot
34
- * computes an implicit merge-base which FAILS when A is the empty-tree
35
- * SHA (a valid bootstrap anchor in `base-resolve.ts`). Two-dot accepts
36
- * any revision on the left.
37
- * - `git diff --name-status` output is tab-separated, one line per change;
38
- * `protected-paths.ts` owns the parse.
39
- * - `git cat-file -e <sha>^{commit}` is the "object is locally resolvable"
40
- * probe. Bash used a bare exit-code check; we preserve that.
41
- */
42
- /**
43
- * Result of a git invocation. `status` is the git exit code (0 on success).
44
- * Both `stdout` and `stderr` are trimmed of trailing newlines — the bash
45
- * core's callers all applied `$(...)` which already collapses trailing
46
- * whitespace, so TS callers expect the same contract.
47
- */
48
- export interface GitRunResult {
49
- status: number;
50
- stdout: string;
51
- stderr: string;
52
- }
53
- /**
54
- * Signature the git-subprocess layer must implement. Tests supply a stub
55
- * that records calls and returns canned results; production uses
56
- * {@link spawnGit}.
57
- */
58
- export type GitRunner = (args: readonly string[], cwd: string) => GitRunResult;
59
- /**
60
- * Default production git runner. Spawns `git` with the supplied args, cwd,
61
- * and a fixed timeout. `encoding: 'utf8'` so the returned strings are
62
- * decoded, matching the bash `$()` shape.
63
- *
64
- * Security: args is always an array; no shell string ever participates in
65
- * the invocation. Refspec names that happen to contain shell metacharacters
66
- * are inert.
67
- */
68
- export declare function spawnGit(args: readonly string[], cwd: string): GitRunResult;
69
- /**
70
- * Return the current branch name (empty string when detached or on failure).
71
- * Bash-core parity (push-review-core.sh §687): `git branch --show-current`.
72
- */
73
- export declare function currentBranch(runner: GitRunner, cwd: string): string;
74
- /**
75
- * Resolve `HEAD` to a commit SHA, or return the empty string when the repo
76
- * has no commits / the rev-parse fails. Bash-core parity
77
- * (push-review-core.sh §134 and §412): `git rev-parse HEAD`.
78
- */
79
- export declare function resolveHead(runner: GitRunner, cwd: string): string;
80
- /**
81
- * Resolve a ref (e.g. `feature/foo`) to a commit SHA via
82
- * `git rev-parse --verify <ref>^{commit}`, or return null on failure.
83
- * Bash-core parity (push-review-core.sh §187): the `^{commit}` suffix
84
- * forces resolution to the commit even for annotated-tag refs.
85
- */
86
- export declare function resolveRefToSha(runner: GitRunner, cwd: string, ref: string): string | null;
87
- /**
88
- * Return the short-name upstream for the current branch (e.g. `origin/main`)
89
- * or null if no upstream is set. Bash-core parity (push-review-core.sh §129
90
- * and §601): `git rev-parse --abbrev-ref --symbolic-full-name '@{upstream}'`.
91
- */
92
- export declare function resolveUpstream(runner: GitRunner, cwd: string): string | null;
93
- /**
94
- * Check whether a commit object is locally resolvable. Bash-core parity
95
- * (push-review-core.sh §743): `git cat-file -e <sha>^{commit}`. Returns
96
- * true on exit 0, false otherwise. Used before merge-base computation on
97
- * the non-new-branch path — if the remote ref isn't fetched, `git merge-
98
- * base` would silently return an unrelated base and the gate would diff
99
- * against the wrong anchor.
100
- */
101
- export declare function hasCommitLocally(runner: GitRunner, cwd: string, sha: string): boolean;
102
- /**
103
- * Compute the merge-base between two refs. Returns the SHA on success,
104
- * null on failure or empty output. Bash-core parity
105
- * (push-review-core.sh §756 and §860): `git merge-base <a> <b>`.
106
- */
107
- export declare function mergeBase(runner: GitRunner, cwd: string, a: string, b: string): string | null;
108
- /**
109
- * True iff `ref` is a resolvable rev (commit, tag, or branch). Bash-core
110
- * parity (push-review-core.sh §815 and §835): `git rev-parse --verify
111
- * --quiet <ref>` with stdout suppressed and stderr ignored.
112
- */
113
- export declare function refExists(runner: GitRunner, cwd: string, ref: string): boolean;
114
- /**
115
- * Read a git config value (e.g. `branch.<name>.base`). Returns the value
116
- * or the empty string when the config entry is absent or `git config` fails.
117
- * Bash-core parity (push-review-core.sh §808): `git config --get <key>`.
118
- */
119
- export declare function readGitConfig(runner: GitRunner, cwd: string, key: string): string;
120
- /**
121
- * Resolve `refs/remotes/<remote>/HEAD` to its symbolic target (e.g.
122
- * `refs/remotes/origin/main`). Returns null on failure. Bash-core parity
123
- * (push-review-core.sh §826): `git symbolic-ref refs/remotes/<remote>/HEAD`.
124
- */
125
- export declare function resolveRemoteDefaultRef(runner: GitRunner, cwd: string, remote: string): string | null;
126
- /**
127
- * Full `git diff <a>..<b>` output (two-dot per the §1053-1060 rationale).
128
- * Returns the diff body on success; throws via the typed error if git
129
- * exits non-zero so the caller can translate to the banner + exit 2.
130
- *
131
- * Empty diff → empty string, status 0 is a legitimate no-op push and the
132
- * caller routes to its "no reviewable diff" branch.
133
- */
134
- export interface DiffResult {
135
- /** The full `git diff` output (may be empty). */
136
- diff: string;
137
- /** git's exit code. */
138
- status: number;
139
- /** git's stderr for error-path diagnostics. */
140
- stderr: string;
141
- }
142
- export declare function fullDiff(runner: GitRunner, cwd: string, a: string, b: string): DiffResult;
143
- /**
144
- * `git diff --name-status <a>..<b>` output. One line per change, tab-
145
- * separated `<STATUS>\t<path1>[\t<path2>]`. Consumed by
146
- * `protected-paths.ts`. Returns stdout on success or null on error (the
147
- * caller emits a banner + exit 2; see bash core §904-914).
148
- */
149
- export interface NameStatusResult {
150
- /** Raw name-status output (may be empty on a zero-change diff). */
151
- output: string;
152
- /** git's exit code. */
153
- status: number;
154
- /** git's stderr for error-path diagnostics. */
155
- stderr: string;
156
- }
157
- export declare function diffNameStatus(runner: GitRunner, cwd: string, a: string, b: string): NameStatusResult;
158
- /**
159
- * Commit count between two refs. Bash-core parity
160
- * (push-review-core.sh §996): `git rev-list --count <a>..<b>`. Returns -1
161
- * on error so callers can distinguish "git failed" (exit 2) from "zero
162
- * commits" (legitimate, usually a same-ref push).
163
- */
164
- export declare function revListCount(runner: GitRunner, cwd: string, a: string, b: string): number;
165
- /**
166
- * Resolve the repository's common-dir (the path to `.git` or to a
167
- * shared worktree parent). Returns the absolute path or null when not in
168
- * a git repo. Bash-core parity (push-review-core.sh §272-273):
169
- * `git rev-parse --path-format=absolute --git-common-dir`.
170
- *
171
- * Used by the cross-repo guard in §1a to distinguish two checkouts of the
172
- * same repo (linked worktrees share a common-dir) from two unrelated
173
- * repos. Phase 2a ships the primitive; composition happens in Phase 2b.
174
- */
175
- export declare function gitCommonDir(runner: GitRunner, cwd: string): string | null;
176
- /**
177
- * Read git's user email + name fallback for skip-audit actor attribution.
178
- * Bash-core parity (push-review-core.sh §393-396 and §563-566): prefer
179
- * email; fall back to name if email is empty; empty string if both missing.
180
- */
181
- export declare function readGitActor(runner: GitRunner, cwd: string): string;