@bookedsolid/rea 0.40.0 → 0.42.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/MIGRATING.md +139 -0
- package/README.md +153 -36
- package/dist/cli/audit-summary.d.ts +160 -0
- package/dist/cli/audit-summary.js +535 -0
- package/dist/cli/doctor.d.ts +44 -4
- package/dist/cli/doctor.js +141 -37
- package/dist/cli/index.js +33 -0
- package/dist/cli/install/gitignore.d.ts +23 -1
- package/dist/cli/install/gitignore.js +33 -6
- package/dist/cli/install/unified-diff.d.ts +78 -0
- package/dist/cli/install/unified-diff.js +270 -0
- package/dist/cli/upgrade-check.d.ts +187 -0
- package/dist/cli/upgrade-check.js +685 -0
- package/dist/cli/upgrade.js +42 -0
- package/package.json +1 -1
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal LCS-based unified-diff renderer (0.41.0).
|
|
3
|
+
*
|
|
4
|
+
* Used by `rea upgrade --check` to emit a per-file preview of what the
|
|
5
|
+
* upgrade flow WOULD change. We deliberately ship our own implementation
|
|
6
|
+
* rather than pulling in the `diff` npm package — REA's dependency
|
|
7
|
+
* footprint is small and load-bearing, and the upgrade-check preview is
|
|
8
|
+
* the only consumer.
|
|
9
|
+
*
|
|
10
|
+
* Output shape mirrors `diff -u`:
|
|
11
|
+
*
|
|
12
|
+
* --- a/path/to/file
|
|
13
|
+
* +++ b/path/to/file
|
|
14
|
+
* @@ -1,3 +1,4 @@
|
|
15
|
+
* context line
|
|
16
|
+
* -removed line
|
|
17
|
+
* +added line
|
|
18
|
+
* context line
|
|
19
|
+
*
|
|
20
|
+
* Hunks are constructed greedily — adjacent changed regions within
|
|
21
|
+
* `contextLines` of each other are merged into a single hunk so a
|
|
22
|
+
* reviewer reading the output doesn't have to mentally stitch tiny
|
|
23
|
+
* back-to-back hunks together.
|
|
24
|
+
*
|
|
25
|
+
* Performance: classic O(n*m) LCS with two parallel `Uint32Array`s for
|
|
26
|
+
* the DP table. Files in the upgrade-check path are bounded at
|
|
27
|
+
* `DIFF_SIZE_CAP_BYTES` (256 KiB) by callers, so even the worst-case
|
|
28
|
+
* shape (256 KiB of single-character lines) stays inside the addressable
|
|
29
|
+
* range of `Uint32Array` indices (~4 GiB worth of cells). The caller is
|
|
30
|
+
* responsible for refusing to diff files larger than the cap; this
|
|
31
|
+
* module trusts its inputs.
|
|
32
|
+
*
|
|
33
|
+
* Newline handling: we split on `\n` only. A file with `\r\n` line
|
|
34
|
+
* endings will surface as one big changed block if compared against a
|
|
35
|
+
* `\n`-ending canonical, which is the right behavior — line endings
|
|
36
|
+
* differing IS a real difference. We do not normalize.
|
|
37
|
+
*/
|
|
38
|
+
const DEFAULT_CONTEXT_LINES = 3;
|
|
39
|
+
/**
|
|
40
|
+
* Hard cap on the DP-table cell count. The O(m*n) LCS table is the
|
|
41
|
+
* dominant memory cost; with a 4-byte `Uint32Array` cell, this works
|
|
42
|
+
* out to ~16 MiB of allocation at the cap. We deliberately key off
|
|
43
|
+
* cell COUNT, not file bytes — codex round-1 P1 flagged that the
|
|
44
|
+
* 256 KiB byte cap callers enforce can still produce pathological
|
|
45
|
+
* matrices when files are mostly single-character lines (200 KiB of
|
|
46
|
+
* one-char lines = 200K lines = 40 GiB of cells).
|
|
47
|
+
*
|
|
48
|
+
* Exported so callers can detect the "too large to diff" verdict
|
|
49
|
+
* structurally instead of grepping the returned string.
|
|
50
|
+
*/
|
|
51
|
+
export const MAX_LCS_CELLS = 4_000_000;
|
|
52
|
+
/**
|
|
53
|
+
* Sentinel returned in place of a real diff when the line counts
|
|
54
|
+
* would blow past `MAX_LCS_CELLS`. Wrapped in a recognizable comment
|
|
55
|
+
* shape so consumers grepping the diff body see a clear notice.
|
|
56
|
+
*/
|
|
57
|
+
export const DIFF_TOO_LARGE_NOTICE = '# rea: diff suppressed — file pair too large for the LCS matrix budget\n';
|
|
58
|
+
/**
|
|
59
|
+
* Compute the LCS table for two line arrays. Cell (i, j) is the length
|
|
60
|
+
* of the longest common subsequence of `a[0..i)` and `b[0..j)`.
|
|
61
|
+
*
|
|
62
|
+
* Returns a row-major `Uint32Array` of size `(a.length + 1) * (b.length + 1)`.
|
|
63
|
+
*/
|
|
64
|
+
function lcsTable(a, b) {
|
|
65
|
+
const m = a.length;
|
|
66
|
+
const n = b.length;
|
|
67
|
+
const rowStride = n + 1;
|
|
68
|
+
const table = new Uint32Array((m + 1) * rowStride);
|
|
69
|
+
for (let i = 1; i <= m; i += 1) {
|
|
70
|
+
const rowBase = i * rowStride;
|
|
71
|
+
const prevRowBase = (i - 1) * rowStride;
|
|
72
|
+
for (let j = 1; j <= n; j += 1) {
|
|
73
|
+
if (a[i - 1] === b[j - 1]) {
|
|
74
|
+
table[rowBase + j] = table[prevRowBase + (j - 1)] + 1;
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
const up = table[prevRowBase + j];
|
|
78
|
+
const left = table[rowBase + (j - 1)];
|
|
79
|
+
table[rowBase + j] = up >= left ? up : left;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return table;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Walk the LCS table backwards to produce the ordered diff op sequence.
|
|
87
|
+
*/
|
|
88
|
+
function backtrackOps(a, b, table) {
|
|
89
|
+
const ops = [];
|
|
90
|
+
const rowStride = b.length + 1;
|
|
91
|
+
let i = a.length;
|
|
92
|
+
let j = b.length;
|
|
93
|
+
while (i > 0 || j > 0) {
|
|
94
|
+
if (i > 0 && j > 0 && a[i - 1] === b[j - 1]) {
|
|
95
|
+
ops.push({ kind: 'context', text: a[i - 1] });
|
|
96
|
+
i -= 1;
|
|
97
|
+
j -= 1;
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
const up = i > 0 ? table[(i - 1) * rowStride + j] : -1;
|
|
101
|
+
const left = j > 0 ? table[i * rowStride + (j - 1)] : -1;
|
|
102
|
+
if (j > 0 && (i === 0 || left >= up)) {
|
|
103
|
+
ops.push({ kind: 'add', text: b[j - 1] });
|
|
104
|
+
j -= 1;
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
ops.push({ kind: 'remove', text: a[i - 1] });
|
|
108
|
+
i -= 1;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
ops.reverse();
|
|
112
|
+
return ops;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Group ops into hunks. A hunk covers a contiguous changed region plus
|
|
116
|
+
* `contextLines` of context on each side. Two adjacent changed regions
|
|
117
|
+
* separated by fewer than `2 * contextLines` context lines are merged
|
|
118
|
+
* into a single hunk (the shared context belongs to both).
|
|
119
|
+
*/
|
|
120
|
+
function buildHunks(ops, contextLines) {
|
|
121
|
+
// First pass: collect indices of every change op.
|
|
122
|
+
const changedIndices = [];
|
|
123
|
+
for (let i = 0; i < ops.length; i += 1) {
|
|
124
|
+
if (ops[i].kind !== 'context')
|
|
125
|
+
changedIndices.push(i);
|
|
126
|
+
}
|
|
127
|
+
if (changedIndices.length === 0)
|
|
128
|
+
return [];
|
|
129
|
+
const windows = [];
|
|
130
|
+
for (const idx of changedIndices) {
|
|
131
|
+
const winStart = Math.max(0, idx - contextLines);
|
|
132
|
+
const winEnd = Math.min(ops.length, idx + 1 + contextLines);
|
|
133
|
+
const last = windows.length > 0 ? windows[windows.length - 1] : null;
|
|
134
|
+
if (last !== null && winStart <= last.end) {
|
|
135
|
+
last.end = Math.max(last.end, winEnd);
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
windows.push({ start: winStart, end: winEnd });
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
// Convert windows to hunks with correct old/new line numbers.
|
|
142
|
+
// Track 1-based line cursors as we walk the full ops array.
|
|
143
|
+
const hunks = [];
|
|
144
|
+
let oldLine = 1;
|
|
145
|
+
let newLine = 1;
|
|
146
|
+
let opIdx = 0;
|
|
147
|
+
for (const win of windows) {
|
|
148
|
+
while (opIdx < win.start) {
|
|
149
|
+
const op = ops[opIdx];
|
|
150
|
+
if (op.kind === 'context') {
|
|
151
|
+
oldLine += 1;
|
|
152
|
+
newLine += 1;
|
|
153
|
+
}
|
|
154
|
+
else if (op.kind === 'remove') {
|
|
155
|
+
oldLine += 1;
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
newLine += 1;
|
|
159
|
+
}
|
|
160
|
+
opIdx += 1;
|
|
161
|
+
}
|
|
162
|
+
const hunkOldStart = oldLine;
|
|
163
|
+
const hunkNewStart = newLine;
|
|
164
|
+
let hunkOldLines = 0;
|
|
165
|
+
let hunkNewLines = 0;
|
|
166
|
+
const hunkOps = [];
|
|
167
|
+
while (opIdx < win.end) {
|
|
168
|
+
const op = ops[opIdx];
|
|
169
|
+
hunkOps.push(op);
|
|
170
|
+
if (op.kind === 'context') {
|
|
171
|
+
hunkOldLines += 1;
|
|
172
|
+
hunkNewLines += 1;
|
|
173
|
+
oldLine += 1;
|
|
174
|
+
newLine += 1;
|
|
175
|
+
}
|
|
176
|
+
else if (op.kind === 'remove') {
|
|
177
|
+
hunkOldLines += 1;
|
|
178
|
+
oldLine += 1;
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
hunkNewLines += 1;
|
|
182
|
+
newLine += 1;
|
|
183
|
+
}
|
|
184
|
+
opIdx += 1;
|
|
185
|
+
}
|
|
186
|
+
hunks.push({
|
|
187
|
+
oldStart: hunkOldStart,
|
|
188
|
+
oldLines: hunkOldLines,
|
|
189
|
+
newStart: hunkNewStart,
|
|
190
|
+
newLines: hunkNewLines,
|
|
191
|
+
ops: hunkOps,
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
return hunks;
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Render hunks in unified-diff format. Header lines name the old and new
|
|
198
|
+
* paths via `--- a/<oldPath>` / `+++ b/<newPath>`, the same convention
|
|
199
|
+
* `diff -u` uses.
|
|
200
|
+
*
|
|
201
|
+
* When both files are identical, returns an empty string — the caller
|
|
202
|
+
* decides whether to emit a "no changes" notice.
|
|
203
|
+
*/
|
|
204
|
+
function renderHunks(oldPath, newPath, hunks) {
|
|
205
|
+
if (hunks.length === 0)
|
|
206
|
+
return '';
|
|
207
|
+
const lines = [];
|
|
208
|
+
lines.push(`--- a/${oldPath}`);
|
|
209
|
+
lines.push(`+++ b/${newPath}`);
|
|
210
|
+
for (const hunk of hunks) {
|
|
211
|
+
// Unified-diff convention: a hunk that contains zero lines on one
|
|
212
|
+
// side renders `0,0` for that side's count. Empty single-line hunks
|
|
213
|
+
// render `N` without a comma (`@@ -5 +5,2 @@`). We always emit the
|
|
214
|
+
// comma form for simplicity — `diff -u` accepts it.
|
|
215
|
+
lines.push(`@@ -${String(hunk.oldStart)},${String(hunk.oldLines)} +${String(hunk.newStart)},${String(hunk.newLines)} @@`);
|
|
216
|
+
for (const op of hunk.ops) {
|
|
217
|
+
if (op.kind === 'context')
|
|
218
|
+
lines.push(` ${op.text}`);
|
|
219
|
+
else if (op.kind === 'add')
|
|
220
|
+
lines.push(`+${op.text}`);
|
|
221
|
+
else
|
|
222
|
+
lines.push(`-${op.text}`);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return lines.join('\n') + '\n';
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Compute a unified diff between two text blobs.
|
|
229
|
+
*
|
|
230
|
+
* Returns the empty string when the two inputs are byte-identical. The
|
|
231
|
+
* caller decides whether to wrap that in a "no changes" notice.
|
|
232
|
+
*
|
|
233
|
+
* Splits on `\n` and drops a single trailing `\n` if present so the
|
|
234
|
+
* final line is not phantom-blank. A file that genuinely ends without
|
|
235
|
+
* a newline will appear identical to one that ends with a single
|
|
236
|
+
* newline — REA's canonical files all end with `\n`, so this is fine
|
|
237
|
+
* for our use case. Callers that need strict-EOL fidelity should
|
|
238
|
+
* normalize upstream.
|
|
239
|
+
*/
|
|
240
|
+
export function diffUnified(oldText, newText, options = {}) {
|
|
241
|
+
if (oldText === newText)
|
|
242
|
+
return '';
|
|
243
|
+
const oldPath = options.oldPath ?? 'file';
|
|
244
|
+
const newPath = options.newPath ?? oldPath;
|
|
245
|
+
const contextLines = options.contextLines ?? DEFAULT_CONTEXT_LINES;
|
|
246
|
+
// Drop one trailing newline so split() doesn't produce a phantom empty
|
|
247
|
+
// line at the end. We compare the trailing-stripped forms; the diff
|
|
248
|
+
// header doesn't track EOL state because callers don't.
|
|
249
|
+
const oldNorm = oldText.endsWith('\n') ? oldText.slice(0, -1) : oldText;
|
|
250
|
+
const newNorm = newText.endsWith('\n') ? newText.slice(0, -1) : newText;
|
|
251
|
+
// Empty file → empty array of lines.
|
|
252
|
+
const oldLines = oldNorm.length === 0 ? [] : oldNorm.split('\n');
|
|
253
|
+
const newLines = newNorm.length === 0 ? [] : newNorm.split('\n');
|
|
254
|
+
// Codex round-1 P1: guard against pathological line-count blowups
|
|
255
|
+
// BEFORE allocating the DP table. Cell count grows as
|
|
256
|
+
// (m+1)*(n+1) — a 200 KiB file of one-character lines is well
|
|
257
|
+
// inside any reasonable byte cap but would allocate gigabytes of
|
|
258
|
+
// Uint32 cells. Return a sentinel comment the caller can detect
|
|
259
|
+
// and surface as "too large to render" instead of OOMing.
|
|
260
|
+
const cellCount = (oldLines.length + 1) * (newLines.length + 1);
|
|
261
|
+
if (cellCount > MAX_LCS_CELLS) {
|
|
262
|
+
return (`--- a/${oldPath}\n` +
|
|
263
|
+
`+++ b/${newPath}\n` +
|
|
264
|
+
DIFF_TOO_LARGE_NOTICE);
|
|
265
|
+
}
|
|
266
|
+
const table = lcsTable(oldLines, newLines);
|
|
267
|
+
const ops = backtrackOps(oldLines, newLines, table);
|
|
268
|
+
const hunks = buildHunks(ops, contextLines);
|
|
269
|
+
return renderHunks(oldPath, newPath, hunks);
|
|
270
|
+
}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `rea upgrade --check` — 0.41.0 consumer-UX dry-run preview.
|
|
3
|
+
*
|
|
4
|
+
* Today `rea upgrade` rewrites consumer files immediately (gated by
|
|
5
|
+
* interactive prompts and `--dry-run` for a coarse preview). There has
|
|
6
|
+
* been no way to PREVIEW what would change, file-by-file, with the
|
|
7
|
+
* actual textual delta, before running the real upgrade. `--check`
|
|
8
|
+
* fills that gap.
|
|
9
|
+
*
|
|
10
|
+
* # Contract
|
|
11
|
+
*
|
|
12
|
+
* - Reads the same canonical file set as `rea upgrade`, classifies it
|
|
13
|
+
* against the installed manifest, and emits a summary table of
|
|
14
|
+
* counts (created / modified / unchanged / removed-upstream) plus a
|
|
15
|
+
* unified diff per modified file.
|
|
16
|
+
* - Never writes to disk. The classification phase is pure; nothing
|
|
17
|
+
* downstream of `computeUpgradePlan` mutates the filesystem.
|
|
18
|
+
* - Exits 0 regardless of what would change — `--check` is a preview,
|
|
19
|
+
* not an enforcement gate. Consumers wiring `rea upgrade --check`
|
|
20
|
+
* into CI gate on diff-present via the JSON output (`--json`).
|
|
21
|
+
* - Distinct from `--dry-run`: `--dry-run` runs the FULL interactive
|
|
22
|
+
* upgrade flow with writes suppressed (prompts still fire, output
|
|
23
|
+
* still streams in classification order). `--check` is purely
|
|
24
|
+
* structured, non-interactive, and emits the unified diffs that
|
|
25
|
+
* `--dry-run` does not.
|
|
26
|
+
*
|
|
27
|
+
* # JSON output
|
|
28
|
+
*
|
|
29
|
+
* `--json` emits a single document with shape:
|
|
30
|
+
*
|
|
31
|
+
* {
|
|
32
|
+
* "schema_version": 1,
|
|
33
|
+
* "rea_version": "0.41.0",
|
|
34
|
+
* "target_root": "/abs/path/to/repo",
|
|
35
|
+
* "bootstrap": false,
|
|
36
|
+
* "counts": { "created": 1, "modified": 3, "unchanged": 47,
|
|
37
|
+
* "removed_upstream": 0 },
|
|
38
|
+
* "files": [
|
|
39
|
+
* { "path": "hooks/foo.sh", "action": "modified",
|
|
40
|
+
* "old_sha": "…", "new_sha": "…", "diff": "--- …\n+++ …\n…" },
|
|
41
|
+
* …
|
|
42
|
+
* ]
|
|
43
|
+
* }
|
|
44
|
+
*
|
|
45
|
+
* `diff` is included for `created` (showing full content as additions),
|
|
46
|
+
* `modified` (true unified diff), and `removed_upstream` (showing the
|
|
47
|
+
* full content as removals). It is omitted for `unchanged`.
|
|
48
|
+
*
|
|
49
|
+
* # Why not extend `--dry-run`?
|
|
50
|
+
*
|
|
51
|
+
* `--dry-run` already serves a different purpose: rehearse the full
|
|
52
|
+
* upgrade flow in interactive mode. Bolting structured output onto it
|
|
53
|
+
* would either change its existing output shape (breaking pipelines)
|
|
54
|
+
* or fork it into two output paths anyway. A new flag is cleaner.
|
|
55
|
+
*/
|
|
56
|
+
import { type CanonicalFile } from './install/canonical.js';
|
|
57
|
+
/** Hard cap on the diff input size. Mirrors `DIFF_SIZE_CAP_BYTES` in
|
|
58
|
+
* upgrade.ts so the two preview surfaces agree on the "too big to
|
|
59
|
+
* render" threshold. */
|
|
60
|
+
export declare const CHECK_DIFF_SIZE_CAP_BYTES: number;
|
|
61
|
+
/**
|
|
62
|
+
* Stable schema marker for the JSON document. Bumped when the shape
|
|
63
|
+
* changes in a non-additive way.
|
|
64
|
+
*/
|
|
65
|
+
export declare const UPGRADE_CHECK_SCHEMA_VERSION = 1;
|
|
66
|
+
/**
|
|
67
|
+
* Tokens for hook commands that older rea versions registered into
|
|
68
|
+
* `.claude/settings.json` and that the upgrade flow PRUNES on every
|
|
69
|
+
* run. Mirrors `STALE_HOOK_COMMAND_TOKENS` in `upgrade.ts` — kept in
|
|
70
|
+
* sync because both modules want to anticipate the same prune set.
|
|
71
|
+
*
|
|
72
|
+
* Re-exported by upgrade.ts so the actual write path uses the same
|
|
73
|
+
* list.
|
|
74
|
+
*/
|
|
75
|
+
export declare const UPGRADE_CHECK_STALE_HOOK_TOKENS: readonly string[];
|
|
76
|
+
export type UpgradeCheckAction = 'created' | 'modified' | 'unchanged' | 'removed_upstream';
|
|
77
|
+
export interface UpgradeCheckFile {
|
|
78
|
+
/** Repo-relative path of the file (POSIX-normalized). */
|
|
79
|
+
path: string;
|
|
80
|
+
action: UpgradeCheckAction;
|
|
81
|
+
/** Synthetic entries (settings.json subset hash, CLAUDE.md fragment,
|
|
82
|
+
* .gitignore managed block) are flagged so the renderer can label
|
|
83
|
+
* them in the table. */
|
|
84
|
+
synthetic?: 'settings' | 'claude-md' | 'gitignore';
|
|
85
|
+
/** SHA-256 of the on-disk content at preview time, when present. */
|
|
86
|
+
old_sha?: string;
|
|
87
|
+
/** SHA-256 of the would-be-installed content. */
|
|
88
|
+
new_sha?: string;
|
|
89
|
+
/** Unified diff body. Empty string for `unchanged`. May be omitted
|
|
90
|
+
* when the file exceeds `CHECK_DIFF_SIZE_CAP_BYTES` — `diff_truncated`
|
|
91
|
+
* is then `true`. */
|
|
92
|
+
diff?: string;
|
|
93
|
+
/** Set when the diff was suppressed for size. Operators inspect the
|
|
94
|
+
* files manually in this case. */
|
|
95
|
+
diff_truncated?: boolean;
|
|
96
|
+
/** Free-form notice the renderer should display alongside the file
|
|
97
|
+
* row (e.g. "removed-upstream — kept by default unless --force"). */
|
|
98
|
+
note?: string;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* 0.42.0 charter item 2 — surface the same settings-schema validation
|
|
102
|
+
* the real upgrade flow runs. `runUpgrade` calls `validateSettings`
|
|
103
|
+
* on the merged result and refuses the write (throws) when it fails;
|
|
104
|
+
* pre-0.42.0 `rea upgrade --check` never invoked that check, so a
|
|
105
|
+
* preview could promise a write that the real upgrade would refuse.
|
|
106
|
+
*
|
|
107
|
+
* - `parsed: true` — schema validation succeeded; `errors` is empty.
|
|
108
|
+
* The real upgrade WOULD write the merged settings on demand.
|
|
109
|
+
* - `parsed: false` — schema validation failed. `errors` carries the
|
|
110
|
+
* same zod-issue strings `runUpgrade` would surface in its throw
|
|
111
|
+
* message. The real upgrade would refuse and leave settings.json
|
|
112
|
+
* untouched.
|
|
113
|
+
*/
|
|
114
|
+
export interface UpgradeCheckSettingsValidation {
|
|
115
|
+
parsed: boolean;
|
|
116
|
+
errors: string[];
|
|
117
|
+
}
|
|
118
|
+
export interface UpgradeCheckPlan {
|
|
119
|
+
schema_version: typeof UPGRADE_CHECK_SCHEMA_VERSION;
|
|
120
|
+
rea_version: string;
|
|
121
|
+
target_root: string;
|
|
122
|
+
/** `true` when no install-manifest exists; the consumer is on a
|
|
123
|
+
* pre-G12 install and the first real upgrade will record SHAs
|
|
124
|
+
* for whatever is on disk. */
|
|
125
|
+
bootstrap: boolean;
|
|
126
|
+
counts: {
|
|
127
|
+
created: number;
|
|
128
|
+
modified: number;
|
|
129
|
+
unchanged: number;
|
|
130
|
+
removed_upstream: number;
|
|
131
|
+
};
|
|
132
|
+
files: UpgradeCheckFile[];
|
|
133
|
+
/** 0.42.0 — schema-validation outcome on the merged settings.json
|
|
134
|
+
* the real `rea upgrade` would write. `null` when the synthetic
|
|
135
|
+
* settings classification did not produce a merged result (should
|
|
136
|
+
* not happen in practice; defensive). */
|
|
137
|
+
settings_validation: UpgradeCheckSettingsValidation | null;
|
|
138
|
+
/**
|
|
139
|
+
* 0.42.0 codex round 3 P2 (2026-05-16) — top-level "preview = real"
|
|
140
|
+
* verdict. `true` when `rea upgrade` would actually start mutating
|
|
141
|
+
* the install; `false` when the new pre-flight (settings-validation)
|
|
142
|
+
* gate would refuse the upgrade before any file is written.
|
|
143
|
+
*
|
|
144
|
+
* Why this matters: `counts` + `files` still describe what WOULD be
|
|
145
|
+
* written if validation passed (operators want the diff so they can
|
|
146
|
+
* fix the underlying invalid setting and see the upgrade preview in
|
|
147
|
+
* one shot). But CI and automation consuming the JSON need a single
|
|
148
|
+
* unambiguous signal that the real upgrade will write nothing in
|
|
149
|
+
* the current state. Use `would_apply` as that signal; treat
|
|
150
|
+
* `files[]` + `counts` as conditional on `would_apply === true`.
|
|
151
|
+
*/
|
|
152
|
+
would_apply: boolean;
|
|
153
|
+
}
|
|
154
|
+
export interface ComputeUpgradeCheckOptions {
|
|
155
|
+
/** Defaults to `process.cwd()`. */
|
|
156
|
+
baseDir?: string;
|
|
157
|
+
/** Tests can stub the canonical file enumeration; production reads
|
|
158
|
+
* from `PKG_ROOT`. */
|
|
159
|
+
canonicalFiles?: CanonicalFile[];
|
|
160
|
+
/** When `false`, skips the unified-diff computation (counts + paths
|
|
161
|
+
* only). Default `true`. Useful for very large repos previewed in
|
|
162
|
+
* CI where the diffs are not consumed. */
|
|
163
|
+
includeDiffs?: boolean;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Compute the full upgrade-check plan. Pure (filesystem reads only —
|
|
167
|
+
* no writes). All synthetic entries (CLAUDE.md fragment, settings
|
|
168
|
+
* subset hash, .gitignore managed block) are included alongside the
|
|
169
|
+
* canonical-file classifications.
|
|
170
|
+
*/
|
|
171
|
+
export declare function computeUpgradeCheck(options?: ComputeUpgradeCheckOptions): Promise<UpgradeCheckPlan>;
|
|
172
|
+
/**
|
|
173
|
+
* Render the plan as a human-readable summary block. Designed for
|
|
174
|
+
* terminal consumption — counts table on top, then a per-file section
|
|
175
|
+
* with the diff body indented.
|
|
176
|
+
*/
|
|
177
|
+
export declare function renderUpgradeCheck(plan: UpgradeCheckPlan): string;
|
|
178
|
+
export interface RunUpgradeCheckOptions {
|
|
179
|
+
json?: boolean;
|
|
180
|
+
/** Strip `diff` bodies from output (counts + paths only). */
|
|
181
|
+
noDiff?: boolean;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Commander entrypoint for `rea upgrade --check`. Always exits 0 —
|
|
185
|
+
* `--check` is a preview, not a gate.
|
|
186
|
+
*/
|
|
187
|
+
export declare function runUpgradeCheck(options?: RunUpgradeCheckOptions): Promise<void>;
|