@getrefino/cli 0.1.0-rc.2 → 0.1.0-rc.3

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/README.md CHANGED
@@ -84,6 +84,43 @@ Then check the result:
84
84
  npx @getrefino/cli verify
85
85
  ```
86
86
 
87
+ ## Upgrading a site to a newer Refino
88
+
89
+ Run the command you installed with, from the newer version:
90
+
91
+ ```bash
92
+ npx @getrefino/cli@latest init --agent --yes --refino-site site_…
93
+ ```
94
+
95
+ Updating the `@getrefino/*` packages is only half of an upgrade. Refino also
96
+ generates runtime code into your repository (`refino/`, the `/edit` route), and
97
+ installing a package never touches a file already committed there — so a site
98
+ can be on the newest packages and still running an older release's integration.
99
+ `init` is what moves that half.
100
+
101
+ You never have to delete or move a generated file. `init` refreshes every one
102
+ it can prove you have not edited and reports it:
103
+
104
+ ```
105
+ Updated Refino-generated integration:
106
+ refino/refino-site.ts 0.1.0-rc.1 → 0.1.0-rc.3
107
+ refino/copy-editing.tsx 0.1.0-rc.1 → 0.1.0-rc.3
108
+ ```
109
+
110
+ A file **you** changed is never overwritten. It is listed as needing review,
111
+ Refino's current version is written beside it as `<file>.refino-new`, and
112
+ `init` exits non-zero rather than let a half-upgraded site look finished. Move
113
+ your changes onto that version, replace the original with it, delete the
114
+ `.refino-new` file, and run `init` again.
115
+
116
+ Refino knows which is which from `.refino/generated.json`, the record it writes
117
+ of the exact bytes it generated — commit it with the rest. Sites installed
118
+ before that file existed are recognised from the bytes of the older release, so
119
+ they upgrade the same way.
120
+
121
+ `refino verify` reports the same three states — current, refreshable, needs
122
+ review — without changing anything.
123
+
87
124
  ## If you are a coding agent
88
125
 
89
126
  1. Run `npx @getrefino/cli init --agent --refino-site <site id>` in the
@@ -98,6 +135,13 @@ npx @getrefino/cli verify
98
135
  3. Run `npx @getrefino/cli verify` and fix only what it reports. Repeat until
99
136
  it passes.
100
137
 
138
+ If you are **upgrading** an existing installation rather than creating one, run
139
+ the same `init` command from the newer version and read its output. A non-zero
140
+ exit means a generated file has local changes Refino would have had to discard:
141
+ resolve each one against the `.refino-new` file it left beside the original,
142
+ then run `init` again. Do not delete or move generated files to get past it,
143
+ and do not report the upgrade as done while `init` or `verify` still fails.
144
+
101
145
  ## Commands
102
146
 
103
147
  | Command | What it does |
package/dist/cli.js CHANGED
@@ -33,6 +33,23 @@ Install into a site, in this order
33
33
  Without --refino-site the same steps set up a self-hosted site, which holds its
34
34
  own password and repository token instead.
35
35
 
36
+ Upgrade an existing installation
37
+ Re-run exactly the command you installed with, from the new version:
38
+
39
+ npx @getrefino/cli@latest init --agent --yes --refino-site <site_id>
40
+
41
+ Upgrading the @getrefino/* packages is not enough on its own. Refino also
42
+ copies runtime code into the repository (refino/), and installing a package
43
+ does not touch a file already committed there, so both have to move. init
44
+ refreshes every generated file it can prove you have not edited, says which
45
+ ones it changed and from which version, and never touches one you have:
46
+ those are listed as needing review, Refino's current version is written
47
+ beside each as <file>.refino-new, and init exits non-zero so a half-upgraded
48
+ site is never reported as a finished one. You never have to delete or move a
49
+ generated file yourself.
50
+
51
+ refino verify reports the same three states without changing anything.
52
+
36
53
  Usage
37
54
  refino init [--refino-site <id> [--refino-url <origin>]] [--via <installer>] [--route <path>]... [--all] [--agent | --dry-run | --plan] [--yes]
38
55
  refino inspect [--json]
@@ -283,7 +300,25 @@ export async function run(argv) {
283
300
  console.log("");
284
301
  if (mode === "dry-run") {
285
302
  console.log(dim("Nothing was written. Run `refino init --agent` to write these files."));
286
- return 0;
303
+ if (!result.needsReview)
304
+ return 0;
305
+ // A dry run is also the read-only way to ask "would this upgrade
306
+ // cleanly?", so it answers with the same exit code the real run would.
307
+ console.log(red(`${result.generated.filter((state) => state.status === "review").length} generated file(s) have local changes and would not be updated; --agent would leave them alone and report them.`));
308
+ return 1;
309
+ }
310
+ // A generated file Refino left behind because it had been edited means
311
+ // this site is not running the integration this version generates. That
312
+ // is the whole failure this exit code exists for: init used to say
313
+ // "Next steps" over a half-upgraded site, and nothing downstream could
314
+ // tell. Say what is true, and fail.
315
+ if (result.needsReview) {
316
+ const count = result.generated.filter((state) => state.status === "review").length;
317
+ console.log(red(bold(`Refino did not finish: ${count} generated file(s) listed above were left as they are, and are not what Refino ${TOOL_VERSION} generates.`)));
318
+ console.log(red("This installation is NOT current. Resolve each one, then run this command again."));
319
+ console.log("");
320
+ console.log(dim(`Everything else was written; ${PLAN_FILE} and ${INSTRUCTIONS_FILE} are up to date.`));
321
+ return 1;
287
322
  }
288
323
  console.log(bold("Next steps"));
289
324
  if (result.installCommand)
package/dist/format.d.ts CHANGED
@@ -11,4 +11,11 @@ export declare function formatCandidates(candidates: readonly CopyCandidate[], o
11
11
  }): string;
12
12
  export declare function formatPlanSummary(plan: MigrationPlan): string;
13
13
  export declare function formatApply(result: ApplyResult): string;
14
+ /**
15
+ * The upgrade half of an init run: what Refino refreshed, and what it refused
16
+ * to touch. The second list is the important one -- it is the difference
17
+ * between "your installation is current" and "your installation is not", and
18
+ * it has to be impossible to skim past.
19
+ */
20
+ export declare function formatGeneratedUpgrade(result: ApplyResult): string;
14
21
  export declare function formatVerification(result: VerificationResult): string;
package/dist/format.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { COMPARISON_SUFFIX } from "@getrefino/onboarding";
1
2
  const useColor = process.stdout.isTTY && !process.env.NO_COLOR;
2
3
  const paint = (code) => (text) => (useColor ? `[${code}m${text}` : text);
3
4
  export const bold = paint("1");
@@ -124,6 +125,49 @@ export function formatApply(result) {
124
125
  for (const item of result.skipped)
125
126
  rows.push(` ${dim("skip".padEnd(7))} ${item.path} ${dim(item.reason)}`);
126
127
  }
128
+ const upgrade = formatGeneratedUpgrade(result);
129
+ if (upgrade) {
130
+ rows.push("");
131
+ rows.push(upgrade);
132
+ }
133
+ return rows.join("\n");
134
+ }
135
+ function versionArrow(state) {
136
+ return state.from && state.from !== state.to ? `${state.from} → ${state.to}` : state.to;
137
+ }
138
+ /**
139
+ * The upgrade half of an init run: what Refino refreshed, and what it refused
140
+ * to touch. The second list is the important one -- it is the difference
141
+ * between "your installation is current" and "your installation is not", and
142
+ * it has to be impossible to skim past.
143
+ */
144
+ export function formatGeneratedUpgrade(result) {
145
+ const updated = result.generated.filter((state) => state.status === "update");
146
+ const review = result.generated.filter((state) => state.status === "review");
147
+ if (updated.length === 0 && review.length === 0)
148
+ return "";
149
+ const rows = [];
150
+ const width = updated.length > 0 ? Math.max(...updated.map((state) => state.path.length)) : 0;
151
+ if (updated.length > 0) {
152
+ rows.push(bold(result.dryRun ? "Refino-generated integration that would be updated:" : "Updated Refino-generated integration:"));
153
+ for (const state of updated)
154
+ rows.push(` ${state.path.padEnd(width)} ${cyan(versionArrow(state))}`);
155
+ }
156
+ if (review.length > 0) {
157
+ if (updated.length > 0)
158
+ rows.push("");
159
+ rows.push(red(bold("Needs manual review — NOT updated:")));
160
+ for (const state of review) {
161
+ // No version arrow here: the whole point is that this file's provenance
162
+ // could not be established, so printing one would be a claim.
163
+ rows.push(` ${yellow(state.path)}`);
164
+ rows.push(` ${dim("why:")} ${state.reason}`);
165
+ if (!result.dryRun && state.comparisonPath) {
166
+ rows.push(` ${dim("next:")} Compare it with ${state.comparisonPath} (Refino's ${state.to} version, written beside it just now).`);
167
+ rows.push(` Move your changes onto that version, replace ${state.path} with it, delete the ${COMPARISON_SUFFIX} file, then run init again.`);
168
+ }
169
+ }
170
+ }
127
171
  return rows.join("\n");
128
172
  }
129
173
  export function formatVerification(result) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getrefino/cli",
3
- "version": "0.1.0-rc.2",
3
+ "version": "0.1.0-rc.3",
4
4
  "description": "Refino installer. Run `npx @getrefino/cli init` in a website's repository to let its owner edit visible copy in the browser while Git remains the source of truth. Inspects the repo, writes an authoritative migration plan and instructions for a coding agent, then verifies the result.",
5
5
  "keywords": [
6
6
  "refino",
@@ -45,7 +45,7 @@
45
45
  "./package.json": "./package.json"
46
46
  },
47
47
  "dependencies": {
48
- "@getrefino/onboarding": "0.1.0-rc.2"
48
+ "@getrefino/onboarding": "0.1.0-rc.3"
49
49
  },
50
50
  "devDependencies": {
51
51
  "@types/node": "22.20.2",