@bookedsolid/rea 0.17.0 → 0.19.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.
@@ -58,13 +58,24 @@ fi
58
58
  source "$(dirname "$0")/_lib/cmd-segments.sh"
59
59
 
60
60
  # ── 6. Check if this is a relevant command ────────────────────────────────────
61
+ # 0.18.0 helix-020 / discord-ops Round 10 #2 fix (G4.A): use
62
+ # `any_segment_starts_with`, not `any_segment_matches`. The pre-fix
63
+ # matcher used the unanchored form, so a segment like
64
+ # gh pr edit --body "tracked: gh pr create earlier in the run"
65
+ # triggered IS_RELEVANT=1 because the substring `gh pr create` was
66
+ # anywhere in the segment. The downstream attribution check then
67
+ # scanned the body for the markdown-link / Co-Authored-By patterns,
68
+ # and ANY mention of those terms in the body's prose got blocked
69
+ # even though the actual command was a `gh pr edit` whose intent had
70
+ # nothing to do with structural attribution. The same anchoring fix
71
+ # `dangerous-bash-interceptor.sh` got in 0.16.3 F5 finally lands here.
61
72
  IS_RELEVANT=0
62
73
 
63
- if any_segment_matches "$CMD" 'gh[[:space:]]+pr[[:space:]]+(create|edit)'; then
74
+ if any_segment_starts_with "$CMD" 'gh[[:space:]]+pr[[:space:]]+(create|edit)'; then
64
75
  IS_RELEVANT=1
65
76
  fi
66
77
 
67
- if any_segment_matches "$CMD" 'git[[:space:]]+commit'; then
78
+ if any_segment_starts_with "$CMD" 'git[[:space:]]+commit'; then
68
79
  IS_RELEVANT=1
69
80
  fi
70
81
 
@@ -77,7 +88,21 @@ fi
77
88
  FOUND=0
78
89
 
79
90
  # Co-Authored-By with noreply@ email
80
- if any_segment_matches "$CMD" 'Co-Authored-By:.*noreply@'; then
91
+ # 0.18.0 helix-020 / discord-ops Round 10 #3 fix (G4.B): exclude
92
+ # GitHub's legitimate `<user>@users.noreply.github.com` collaborator
93
+ # footers from the noreply match. Pre-fix the regex `Co-Authored-By:.*noreply@`
94
+ # matched both AI-tool noreply addresses (anthropic.com, openai.com,
95
+ # github-copilot, etc.) AND GitHub's per-user noreply form, blocking
96
+ # legitimate human collaborator credits. The new regex requires
97
+ # `noreply@` to be followed by something that ISN'T `users.noreply.github.com`
98
+ # — covered via a negative-lookahead simulation: match `noreply@` then
99
+ # either end-of-line, whitespace, `>`, or a domain that does NOT begin
100
+ # with `users.noreply.github.com`. Posix ERE has no lookarounds, so we
101
+ # enumerate the allowed-prefix shapes explicitly. The "AI names" branch
102
+ # below catches Co-Authored-By with named tools regardless of the email
103
+ # domain, so dropping `users.noreply.github.com` from the noreply
104
+ # pattern only relaxes the check for human collaborators — never for AI.
105
+ if any_segment_matches "$CMD" 'Co-Authored-By:.*noreply@(anthropic\.com|openai\.com|github-copilot|github\.com|claude\.ai|chatgpt\.com|googlemail\.com|google\.com|cursor\.com|codeium\.com|tabnine\.com|amazon\.com|amazonaws\.com|amazon-q\.amazonaws\.com|cody\.dev|sourcegraph\.com)'; then
81
106
  FOUND=1
82
107
  fi
83
108
 
@@ -171,6 +171,23 @@ _extract_body_file_paths() {
171
171
  while (i <= n) {
172
172
  ch = substr(line, i, 1)
173
173
  if (mode == 0) {
174
+ # 0.18.0 helix-020 G3.B fix: in plain (unquoted) mode,
175
+ # `\X` (any character X) is the POSIX shell escape for
176
+ # the literal character X — most commonly a space in
177
+ # paths like `path\ with\ spaces.md`. Pre-fix the
178
+ # tokenizer treated the `\` as an ordinary character and
179
+ # truncated at the following space, dropping the rest of
180
+ # the path. We now consume the backslash and emit the
181
+ # following byte as a literal part of the current token.
182
+ # `\<eol>` (line-continuation) is left intact — emit the
183
+ # `\` and let the splitter flow into the next record on
184
+ # the assumption that the caller already joined the line.
185
+ if (ch == "\\" && i < n) {
186
+ nxt = substr(line, i + 1, 1)
187
+ tok = tok nxt
188
+ i += 2
189
+ continue
190
+ }
174
191
  if (ch == " " || ch == "\t") {
175
192
  if (tok != "") { emit_token(tok); tok = "" }
176
193
  i++; continue
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bookedsolid/rea",
3
- "version": "0.17.0",
3
+ "version": "0.19.0",
4
4
  "description": "Agentic governance layer for Claude Code — policy enforcement, hook-based safety gates, audit logging, and Codex-integrated adversarial review for AI-assisted projects",
5
5
  "license": "MIT",
6
6
  "author": "Booked Solid Technology <oss@bookedsolid.tech> (https://bookedsolid.tech)",
@@ -28,3 +28,11 @@ context_protection:
28
28
  - pnpm run test
29
29
  - pnpm run lint
30
30
  max_bash_output_lines: 100
31
+ # 0.18.1+ helixir #9: enable audit log rotation by default for
32
+ # bst-internal. Long sessions accumulate 100s of push_gate.reviewed
33
+ # entries; without rotation the audit file grows unbounded. The empty
34
+ # `rotation: {}` block opts in to the documented defaults — 50 MiB
35
+ # OR 30 days, whichever arrives first. Rotation marker preserves the
36
+ # hash chain across the boundary.
37
+ audit:
38
+ rotation: {}
@@ -116,13 +116,51 @@ try {
116
116
 
117
117
  if (manifestVersion === installedVersion) process.exit(0);
118
118
 
119
- // Package-manager-agnostic message. Any of `npx rea upgrade`,
119
+ // 0.18.1+ helixir #3: opt-in auto-upgrade. Pre-fix the drift was
120
+ // detected and a "run rea upgrade" nudge printed, but consumers had
121
+ // to run the upgrade by hand on every install. With
122
+ // `REA_AUTO_UPGRADE=1` (or `--yes` semantics inferred from a
123
+ // package.json field), the postinstall runs `rea upgrade --yes`
124
+ // for them. Defaults to PRINT-ONLY for back-compat — silent
125
+ // mutation of the consumer's `.claude/` / `.husky/` on every
126
+ // install would surprise existing users.
127
+ const autoUpgrade =
128
+ process.env.REA_AUTO_UPGRADE === '1' ||
129
+ process.env.REA_AUTO_UPGRADE === 'true';
130
+
131
+ if (autoUpgrade) {
132
+ // Best-effort: invoke `rea upgrade --yes`. Failures fall through to
133
+ // the print path so the consumer still sees the drift advisory.
134
+ try {
135
+ const reaCli = path.join(consumerRoot, 'node_modules', '.bin', 'rea');
136
+ if (fs.existsSync(reaCli)) {
137
+ const { spawnSync } = await import('node:child_process');
138
+ const res = spawnSync(reaCli, ['upgrade', '--yes'], {
139
+ cwd: consumerRoot,
140
+ stdio: 'inherit',
141
+ env: process.env,
142
+ });
143
+ if (res.status === 0) {
144
+ NOTE([
145
+ `@bookedsolid/rea: auto-upgraded from v${manifestVersion} to v${installedVersion}.`,
146
+ `(REA_AUTO_UPGRADE=1; set REA_AUTO_UPGRADE=0 to opt out.)`,
147
+ ]);
148
+ process.exit(0);
149
+ }
150
+ }
151
+ } catch {
152
+ // Fall through to the manual-nudge path below.
153
+ }
154
+ }
155
+
156
+ // Package-manager-agnostic nudge. Any of `npx rea upgrade`,
120
157
  // `pnpm exec rea upgrade`, or `yarn rea upgrade` works; recommending `npx`
121
158
  // covers the widest audience without privileging pnpm in error output.
122
159
  NOTE([
123
160
  `@bookedsolid/rea v${installedVersion} installed; manifest at v${manifestVersion}.`,
124
161
  `Run \`npx rea upgrade\` to sync .claude/, .husky/, and managed fragments.`,
125
162
  `(Or \`npx rea doctor --drift\` to preview without changes.)`,
163
+ `(Set \`REA_AUTO_UPGRADE=1\` to auto-run upgrade on future installs.)`,
126
164
  ]);
127
165
  } catch {
128
166
  // Any uncaught failure → silent success. Never break the consumer's install.