@orkestrel/scaffold 0.0.16 → 0.0.17

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
@@ -1,7 +1,7 @@
1
1
  # @orkestrel/scaffold
2
2
 
3
- Blueprint-to-plan package scaffolding, auditing, dependency guide/version
4
- pulling, and fleet-wide shared-file upkeep for the `@orkestrel` line.
3
+ Blueprint-to-plan package scaffolding, auditing, dependency guide/version pulling,
4
+ complete guide mirroring, and fleet-wide shared-file upkeep for the `@orkestrel` line.
5
5
 
6
6
  ## Install
7
7
 
@@ -27,6 +27,12 @@ flag from the guided flow works standalone:
27
27
 
28
28
  ```sh
29
29
  npx scaffold new mypackage --src core --app core,browser,server --apply
30
+
31
+ # refresh every published Orkestrel package guide in the current target
32
+ npx scaffold mirror --apply --yes
33
+
34
+ # the same command from this checkout, after npm run build
35
+ node ./dist/bin/scaffold.js mirror --apply --yes
30
36
  ```
31
37
 
32
38
  In scripts, every verb is dry-run by default and fully non-interactive —
@@ -48,6 +54,7 @@ Node's default roots. `NODE_EXTRA_CA_CERTS` adds custom PEMs in either case.
48
54
  ```sh
49
55
  scaffold new [name] [--src <list>] [--app <list>] [--deps <list>] [--apply] [--yes] [--json]
50
56
  scaffold pull [--apply] [--yes] [--json]
57
+ scaffold mirror [--apply] [--yes] [--json]
51
58
  scaffold audit [--live] [--json]
52
59
  scaffold repair [--prune] [--apply] [--yes] [--json]
53
60
  scaffold fleet [--apply] [--yes] [--json]
@@ -70,6 +77,9 @@ error.
70
77
  its plan from your `package.json` and stays clean over the addition.
71
78
  - **`pull`** — fetches the latest vendored dependency guides and registry versions
72
79
  for an existing package and reports drift.
80
+ - **`mirror`** — discovers the exact published `@orkestrel/*` package set from npm and refreshes
81
+ every package's GitHub guide in deterministic name order. It never fetches registry versions or
82
+ overwrites the target package's own guide, and it applies nothing when any guide fetch fails.
73
83
  - **`audit`** — a conformance report over the artifacts the plan actually gates: the
74
84
  shared host-origin files (presence, or exact bytes once hydrated) AND the generated
75
85
  configs/manifest (exact UTF-8 bytes); reports drift as data, findings and all; exits nonzero
@@ -114,7 +124,7 @@ compiler.destroy()
114
124
 
115
125
  `@orkestrel/scaffold/server` carries the impure API — `createMaterializer`
116
126
  (writes a `Plan` to disk) and `createSync` (the only part of the system that
117
- touches the network, fetching dependency guides and registry versions).
127
+ touches the network, fetching dependency guides, fleet guide mirrors, and registry versions).
118
128
 
119
129
  The built host uses an exact `{ entries, roots }` manifest. Staging preflights
120
130
  containment and portable file-tree collisions, builds in a temporary sibling,
@@ -15,6 +15,7 @@ import { parseArgs } from "node:util";
15
15
  var KNOWN_VERBS = Object.freeze([
16
16
  "new",
17
17
  "pull",
18
+ "mirror",
18
19
  "audit",
19
20
  "repair",
20
21
  "fleet",
@@ -92,6 +93,7 @@ var EXIT_CODES = Object.freeze([
92
93
  var VERB_SUMMARY = Object.freeze({
93
94
  new: "scaffold a workspace into ./<name>",
94
95
  pull: "refresh vendored guides/versions, report drift",
96
+ mirror: "refresh every published Orkestrel package guide",
95
97
  audit: "whole-plan conformance report",
96
98
  repair: "restore host-owned files, plus generated canon with --generated",
97
99
  fleet: "audit/repair every workspace under the cwd's immediate children",
@@ -101,6 +103,7 @@ var VERB_SUMMARY = Object.freeze({
101
103
  var VERB_FLAGS = Object.freeze({
102
104
  new: "--src a,b --app a,b --deps x,y --apply --yes --target <path> --from <path>",
103
105
  pull: "--target . --deps x,y --apply --yes --strict",
106
+ mirror: "--target . --apply --yes --strict",
104
107
  audit: "--target . --live --generated --from <path> --groups a,b",
105
108
  repair: "--target . --generated --apply --yes --prune --from <path>",
106
109
  fleet: "--generated --apply --yes --prune --from <path>",
@@ -124,6 +127,12 @@ var VERB_FLAG_HELP = Object.freeze({
124
127
  ["--yes", "skip the confirmation question"],
125
128
  ["--strict", "fail (exit 1) on any drift, even non-fatal"]
126
129
  ],
130
+ mirror: [
131
+ ["--target .", "directory whose guide mirror is refreshed (default: current directory)"],
132
+ ["--apply", "write the refreshed guides (default is a dry run)"],
133
+ ["--yes", "skip the confirmation question"],
134
+ ["--strict", "fail immediately when an upstream guide cannot be fetched"]
135
+ ],
127
136
  audit: [
128
137
  ["--target .", "directory to audit (default: current directory)"],
129
138
  ["--live", "also check upstream freshness over the network"],
@@ -158,6 +167,7 @@ var VERB_FLAG_HELP = Object.freeze({
158
167
  var VERB_DRY_RUN_NOTE = Object.freeze({
159
168
  new: "dry run by default — add --apply to write the files, --yes to skip the question",
160
169
  pull: "dry run by default — add --apply to write the refreshed files, --yes to skip the question",
170
+ mirror: "dry run by default — add --apply to write every published package guide, --yes to skip the question",
161
171
  audit: "read-only — audit never writes; pass --live to also check upstream freshness",
162
172
  repair: "dry run by default — add --apply to write, --yes to skip the question",
163
173
  fleet: "dry run by default — add --apply to write across every package, --yes to skip the question",
@@ -167,6 +177,7 @@ var VERB_DRY_RUN_NOTE = Object.freeze({
167
177
  var VERB_EXAMPLE = Object.freeze({
168
178
  new: "example: scaffold new widget --src core,server --app core,browser --apply",
169
179
  pull: "example: scaffold pull --apply",
180
+ mirror: "example: scaffold mirror --apply --yes",
170
181
  audit: "example: scaffold audit --live",
171
182
  repair: "example: scaffold repair --apply",
172
183
  fleet: "example: scaffold fleet --apply --yes",
@@ -355,8 +366,8 @@ function repairSuccess(result, removed) {
355
366
  const written = result.written.length + result.copied.length;
356
367
  return `${ACTION_LABEL.written} ${written}, ${ACTION_LABEL.skipped} ${result.skipped.length}, ${ACTION_LABEL.removed} ${removed.length}`;
357
368
  }
358
- /** Render pull freshness as table rows. */
359
- function pullRows(report) {
369
+ /** Render synchronization freshness as table rows. */
370
+ function syncRows(report) {
360
371
  const guides = report.guides.map((guide) => [
361
372
  guide.name,
362
373
  "guide",
@@ -369,27 +380,28 @@ function pullRows(report) {
369
380
  ]);
370
381
  return [...guides, ...versions];
371
382
  }
372
- /** Create pull's terminal table. */
373
- function pullTable(report) {
383
+ /** Create a synchronization terminal table. */
384
+ function syncTable(report) {
374
385
  return {
375
386
  columns: [
376
387
  { label: "Name" },
377
388
  { label: "Kind" },
378
389
  { label: "Freshness" }
379
390
  ],
380
- rows: pullRows(report)
391
+ rows: syncRows(report)
381
392
  };
382
393
  }
383
- /** Render cause notes from non-current pull entries. */
384
- function pullCauseNotes(report) {
394
+ /** Render cause notes from non-current synchronization entries. */
395
+ function syncCauseNotes(report) {
385
396
  return [...report.guides, ...report.versions].filter((entry) => entry.note !== void 0).map((entry) => ` ${entry.name}: ${FRESHNESS_LABEL[entry.freshness] ?? entry.freshness} — ${entry.note}`);
386
397
  }
387
- /** Render pull's tally. */
388
- function pullVerdict(report) {
389
- return `pull: ${countPart(report.guides.length + report.versions.length, "entry")} — ${countPart(report.failed, "failed")}`;
398
+ /** Render a command-specific synchronization tally. */
399
+ function syncVerdict(report, action) {
400
+ const count = report.guides.length + report.versions.length;
401
+ return `${action}: ${String(count)} ${count === 1 ? "entry" : "entries"} — ${String(report.failed)} failed`;
390
402
  }
391
- /** Render pull's success tally. */
392
- function pullSuccess(count) {
403
+ /** Render a synchronization success tally. */
404
+ function syncSuccess(count) {
393
405
  return `wrote ${countPart(count, "guide")}`;
394
406
  }
395
407
  /** Render one fleet repository outcome. */
@@ -1117,30 +1129,52 @@ var CLI = class {
1117
1129
  } catch (error) {
1118
1130
  this.#error(error, json);
1119
1131
  }
1120
- if (!json) {
1121
- this.#reporter.table(pullTable(report));
1122
- for (const line of pullCauseNotes(report)) this.#reporter.line(line);
1123
- this.#reporter.line(pullVerdict(report));
1132
+ await this.#refresh(sync, report, target, values, json, "pull", true);
1133
+ } finally {
1134
+ sync.destroy();
1135
+ }
1136
+ }
1137
+ /** `scaffold mirror` — refresh every published Orkestrel package guide. */
1138
+ async #mirror(values, json) {
1139
+ const target = this.#contain(values.target ?? ".", json);
1140
+ const sync = createSync(values.strict === void 0 ? this.#sync : {
1141
+ ...this.#sync,
1142
+ strict: values.strict
1143
+ });
1144
+ try {
1145
+ let report;
1146
+ try {
1147
+ report = await sync.mirror(target);
1148
+ } catch (error) {
1149
+ this.#error(error, json);
1124
1150
  }
1125
- const toWrite = [...report.guides, ...report.versions].filter((entry) => entry.freshness !== "current").length;
1126
- const terminal = createTerminal();
1127
- const proceed = toWrite > 0 ? await this.#apply(terminal, applyConfirmMessage(toWrite), values, json) : false;
1128
- if (proceed) {
1129
- const spinner = this.#spinner("writing mirrors", json);
1130
- spinner?.start();
1131
- try {
1132
- const written = await sync.write(report, target);
1133
- if (json) this.#write(report);
1134
- else this.#succeed(spinner, json, pullSuccess(written.length));
1135
- } catch (error) {
1136
- this.#reject(spinner, json, error);
1137
- }
1138
- } else if (json) this.#write(report);
1139
- process.exitCode = report.clean ? 0 : proceed ? 0 : 1;
1151
+ await this.#refresh(sync, report, target, values, json, "mirror", report.failed === 0);
1140
1152
  } finally {
1141
1153
  sync.destroy();
1142
1154
  }
1143
1155
  }
1156
+ async #refresh(sync, report, target, values, json, action, writable) {
1157
+ if (!json) {
1158
+ this.#reporter.table(syncTable(report));
1159
+ for (const line of syncCauseNotes(report)) this.#reporter.line(line);
1160
+ this.#reporter.line(syncVerdict(report, action));
1161
+ }
1162
+ const toWrite = [...report.guides, ...report.versions].filter((entry) => entry.freshness !== "current").length;
1163
+ const terminal = createTerminal();
1164
+ const proceed = writable && toWrite > 0 ? await this.#apply(terminal, applyConfirmMessage(toWrite), values, json) : false;
1165
+ if (proceed) {
1166
+ const spinner = this.#spinner("writing mirrors", json);
1167
+ spinner?.start();
1168
+ try {
1169
+ const written = await sync.write(report, target);
1170
+ if (json) this.#write(report);
1171
+ else this.#succeed(spinner, json, syncSuccess(written.length));
1172
+ } catch (error) {
1173
+ this.#reject(spinner, json, error);
1174
+ }
1175
+ } else if (json) this.#write(report);
1176
+ process.exitCode = report.clean ? 0 : proceed ? 0 : 1;
1177
+ }
1144
1178
  /** `scaffold audit` — whole-plan conformance report; offers a repair handoff on drift. */
1145
1179
  async #audit(values, json) {
1146
1180
  const target = this.#contain(values.target ?? ".", json);
@@ -1602,6 +1636,7 @@ var CLI = class {
1602
1636
  }
1603
1637
  if (!isVerb(command)) this.#usage(didYouMean(command), json);
1604
1638
  if (command !== "catalog" && values.from !== void 0 && values.from.length > 1) this.#usage(`--from may be provided only once for '${command}'`, json);
1639
+ if (command === "mirror" && values.deps !== void 0) this.#usage("--deps is not supported for 'mirror'; use 'pull --deps'", json);
1605
1640
  if (values.help) {
1606
1641
  process.stdout.write(`${verbHelp(command)}\n`);
1607
1642
  process.exitCode = 0;
@@ -1609,6 +1644,7 @@ var CLI = class {
1609
1644
  }
1610
1645
  if (command === "new") return this.#new(values, argument, json);
1611
1646
  if (command === "pull") return this.#pull(values, json);
1647
+ if (command === "mirror") return this.#mirror(values, json);
1612
1648
  if (command === "audit") return this.#audit(values, json);
1613
1649
  if (command === "repair") return this.#repair(values, json);
1614
1650
  if (command === "fleet") return this.#fleet(values, json);