@younndai/lyt 0.9.4 → 0.9.5

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 (47) hide show
  1. package/dist/automator-bodies/arc-builder.js +0 -18
  2. package/dist/automator-bodies/arc-builder.js.map +1 -1
  3. package/dist/automator-bodies/index.js +0 -18
  4. package/dist/automator-bodies/index.js.map +1 -1
  5. package/dist/automator-bodies/lane-builder.js +0 -19
  6. package/dist/automator-bodies/lane-builder.js.map +1 -1
  7. package/dist/automator-bodies/metadata-filler.js +0 -49
  8. package/dist/automator-bodies/metadata-filler.js.map +1 -1
  9. package/dist/automator-run.js +0 -50
  10. package/dist/automator-run.js.map +1 -1
  11. package/dist/bench/graded-corpus.js +0 -25
  12. package/dist/bench/graded-corpus.js.map +1 -1
  13. package/dist/bench/invariant-corpus.js +0 -33
  14. package/dist/bench/invariant-corpus.js.map +1 -1
  15. package/dist/bench/ir-metrics.js +0 -17
  16. package/dist/bench/ir-metrics.js.map +1 -1
  17. package/dist/bench/latency-bench.js +0 -23
  18. package/dist/bench/latency-bench.js.map +1 -1
  19. package/dist/bench/latency-corpus.js +2 -20
  20. package/dist/bench/latency-corpus.js.map +1 -1
  21. package/dist/bench/pod-harness.js +0 -48
  22. package/dist/bench/pod-harness.js.map +1 -1
  23. package/dist/bench/run-bench.js +0 -23
  24. package/dist/bench/run-bench.js.map +1 -1
  25. package/dist/cli-automator-run.js +0 -31
  26. package/dist/cli-automator-run.js.map +1 -1
  27. package/dist/cli.js +0 -109
  28. package/dist/cli.js.map +1 -1
  29. package/dist/commands/bench.js +0 -27
  30. package/dist/commands/bench.js.map +1 -1
  31. package/dist/commands/capture.js +0 -83
  32. package/dist/commands/capture.js.map +1 -1
  33. package/dist/commands/init.js +1 -233
  34. package/dist/commands/init.js.map +1 -1
  35. package/dist/commands/primer.js +0 -44
  36. package/dist/commands/primer.js.map +1 -1
  37. package/dist/commands/reindex.js +0 -33
  38. package/dist/commands/reindex.js.map +1 -1
  39. package/dist/commands/search.js +0 -71
  40. package/dist/commands/search.js.map +1 -1
  41. package/dist/flows/heal.js +0 -46
  42. package/dist/flows/heal.js.map +1 -1
  43. package/dist/flows/init-bootstrap.js +0 -208
  44. package/dist/flows/init-bootstrap.js.map +1 -1
  45. package/dist/index.js +0 -18
  46. package/dist/index.js.map +1 -1
  47. package/package.json +7 -7
@@ -1,35 +1,3 @@
1
- /*
2
- * Copyright 2026 MARLINK TRADING SRL (YounndAI)
3
- *
4
- * Licensed under the Apache License, Version 2.0 (the "License");
5
- * you may not use this file except in compliance with the License.
6
- * You may obtain a copy of the License at
7
- *
8
- * http://www.apache.org/licenses/LICENSE-2.0
9
- *
10
- * Unless required by applicable law or agreed to in writing, software
11
- * distributed under the License is distributed on an "AS IS" BASIS,
12
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
- * See the License for the specific language governing permissions and
14
- * limitations under the License.
15
- */
16
- // v1.B.4 — `lyt init [--auto | --custom | --discover] [--json]`.
17
- //
18
- // Top-level meta-CLI verb per OD-1 default + master-plan §v1.B.4:543.
19
- // Composes the v1.B.4 initBootstrapFlow with mutually-exclusive flag
20
- // validation + structured-error contract + readline/promises three-prompt
21
- // walkthrough under --custom.
22
- //
23
- // Source: brief 2026-05-31-v1-b-4-lyt-init-bootstrap.md "What's to ship"
24
- // Commit 1 + federation-design §5:228-234 (custom-init prompts) +
25
- // commands/move.ts (closest CLI shape — readline/promises + mutually-
26
- // exclusive flag validation + structured-error contract).
27
- //
28
- // Error contract (OD-9 + brief acceptance):
29
- // --auto + --custom together → exit 2 + flag-conflict
30
- // --custom under non-TTY (incl. --json) → exit 3 + custom-requires-tty
31
- // re-init with ALL-failed integrity → exit 1 (matches v1.B.2 OD-6)
32
- // otherwise → exit 0
33
1
  import { Command } from "commander";
34
2
  import { createInterface } from "node:readline/promises";
35
3
  import { getHandleFromIdentity, materializePodLocal, readIdentityCache, reconcilePublishFlow, ReadlinePromptHandler, renderNextSteps, renderPodCard, runWizard, startSpinner, validateMeshName, } from "@younndai/lyt-vault";
@@ -45,11 +13,6 @@ export function buildLytInitCommand() {
45
13
  .option("--dry-run", "Only valid with --wizard. Walks all 12 wizard phases without spawn invocations or filesystem writes.")
46
14
  .option("--json", "Emit deterministic Lock 0.3 JSON")
47
15
  .action(async (opts) => {
48
- // v1.G.13 Gap 1 — no-flag fresh-state wizard auto-route. When the
49
- // handler runs `lyt init` with NO mode flags AND the registry is
50
- // empty (first-init), enter the wizard. Re-init state falls through
51
- // to the existing --auto default. Non-TTY first-init errors out per
52
- // the ratified default.
53
16
  const noMode = opts.auto !== true &&
54
17
  opts.custom !== true &&
55
18
  opts.discover !== true &&
@@ -62,7 +25,6 @@ export function buildLytInitCommand() {
62
25
  isFresh = await probeFreshState();
63
26
  }
64
27
  catch (err) {
65
- // Probe failure (registry unreachable) — surface and exit.
66
28
  const msg = err instanceof Error ? err.message : String(err);
67
29
  emitError(false, { error: "first-init-probe-failed", message: msg });
68
30
  process.exitCode = 1;
@@ -77,16 +39,9 @@ export function buildLytInitCommand() {
77
39
  process.exitCode = 3;
78
40
  return;
79
41
  }
80
- // TTY + fresh → route through the existing --wizard branch by
81
- // flipping the flag. Single code path for both explicit
82
- // `--wizard` and the no-flag first-init auto-route.
83
42
  opts.wizard = true;
84
43
  }
85
- // Otherwise (re-init state) fall through to existing --auto default.
86
44
  }
87
- // v1.G.4 — --wizard takes a dedicated branch; mutually exclusive
88
- // with the existing auto/custom/discover/json modes (the wizard
89
- // composes mesh+vault+federation init itself).
90
45
  if (opts.wizard === true) {
91
46
  if (opts.auto === true ||
92
47
  opts.custom === true ||
@@ -107,18 +62,12 @@ export function buildLytInitCommand() {
107
62
  process.exitCode = 3;
108
63
  return;
109
64
  }
110
- // Under --dry-run, use a non-interactive default handler so the
111
- // wizard can be smoke-tested without stdin. Real interactive runs
112
- // use the readline-backed handler.
113
65
  const handler = opts.dryRun === true ? makeDryRunDefaultsHandler() : new ReadlinePromptHandler();
114
66
  try {
115
67
  const result = await runWizard({
116
68
  promptHandler: handler,
117
69
  dryRun: opts.dryRun === true,
118
70
  });
119
- // F3 (console-DX): scannable setup-summary block — a header/footer rule
120
- // + status glyphs (✓ done · ⊘ skipped · ✗ failed) replacing the flat
121
- // `[ok] Phase N (name): msg` lines that had no visible start/end.
122
71
  const summaryLines = [
123
72
  "",
124
73
  "── Setup summary ──────────────────────────────────",
@@ -129,7 +78,6 @@ export function buildLytInitCommand() {
129
78
  }),
130
79
  "───────────────────────────────────────────────────",
131
80
  ];
132
- // eslint-disable-next-line no-console
133
81
  console.log(summaryLines.join("\n"));
134
82
  if (result.status !== "completed") {
135
83
  process.exitCode = 1;
@@ -145,8 +93,6 @@ export function buildLytInitCommand() {
145
93
  }
146
94
  return;
147
95
  }
148
- // --dry-run is only valid with --wizard (the wizard branch returned
149
- // above; reaching here means --dry-run was passed without --wizard).
150
96
  if (opts.dryRun === true) {
151
97
  emitError(opts.json === true, {
152
98
  error: "flag-conflict",
@@ -155,7 +101,6 @@ export function buildLytInitCommand() {
155
101
  process.exitCode = 2;
156
102
  return;
157
103
  }
158
- // Flag conflict: --auto + --custom.
159
104
  if (opts.auto === true && opts.custom === true) {
160
105
  emitError(opts.json === true, {
161
106
  error: "flag-conflict",
@@ -164,7 +109,6 @@ export function buildLytInitCommand() {
164
109
  process.exitCode = 2;
165
110
  return;
166
111
  }
167
- // Flag conflict: --discover with prompts is nonsensical (read-only).
168
112
  if (opts.discover === true && opts.custom === true) {
169
113
  emitError(opts.json === true, {
170
114
  error: "flag-conflict",
@@ -173,7 +117,6 @@ export function buildLytInitCommand() {
173
117
  process.exitCode = 2;
174
118
  return;
175
119
  }
176
- // --custom requires a TTY (incl. JSON mode where prompts make no sense).
177
120
  if (opts.custom === true && (process.stdin.isTTY !== true || opts.json === true)) {
178
121
  emitError(opts.json === true, {
179
122
  error: "custom-requires-tty",
@@ -183,8 +126,6 @@ export function buildLytInitCommand() {
183
126
  return;
184
127
  }
185
128
  const mode = opts.discover === true ? "discover" : opts.custom === true ? "custom" : "auto";
186
- // --custom three-prompt walkthrough (per OD-6 default + federation-
187
- // design §5:228-234; main vault name SKIPPED per naming-convention).
188
129
  let customOverrides;
189
130
  if (mode === "custom") {
190
131
  try {
@@ -200,46 +141,17 @@ export function buildLytInitCommand() {
200
141
  return;
201
142
  }
202
143
  }
203
- // v1.GP F7-followup — phase-spanning init spinner. Drive a persistent
204
- // spinner across the WHOLE bootstrap so the surrounding sync work (mesh
205
- // forge, vault scaffold, libSQL writes, git init, pod.yon write)
206
- // no longer runs with a dead/frozen indicator. Active only for the
207
- // human-output FRESH/auto path: --json stays escape-code-free + the
208
- // discovery/re-init branches are fast read-only paths with no silent
209
- // gap to cover. The spinner's onPhase re-labels + yields to the event
210
- // loop at each boundary; per-op gh/git spinners deep in the flow defer
211
- // to it (single-spinner invariant in util/spinner.ts). Cursor restored
212
- // on stop() AND on throw via the finally below.
213
144
  const useSpinner = opts.json !== true && mode !== "discover";
214
145
  const spinner = useSpinner ? startSpinner() : undefined;
215
146
  const flowArgs = {
216
147
  mode,
217
- // W1.2 / OD-4 — heal on every `lyt init` bootstrap. The flow gates
218
- // this to the fresh + re-init branches (discovery stays read-only),
219
- // so a single `lyt init` re-aligns skills + agent manual + patterns.
220
- // Wired ONLY at the command layer so flow/integration unit tests
221
- // (which call initBootstrapFlow directly without `heal`) never write
222
- // to the real ~/.claude / ~/.codex / ~/.agents.
223
148
  heal: () => healPod(),
224
- // Brief B (B.1) — materialize each vault into a publishable LOCAL state
225
- // (git + initial commit + remote URL) and commit pod.yon, with push +
226
- // gh-create HELD (push: false). Outward publish is the consented sync
227
- // engine's job (B.2), triggered by the staged-HIL prompt (B.3). Wired
228
- // ONLY here so flow/integration tests stay hermetic (no git subprocesses
229
- // on temp vault dirs).
230
- // D34 (OD-LOCALFIRST) — in a local-first context (no gh / provisional
231
- // identity), hold the remote too (setRemote:false): the provisional
232
- // handle must never land in a vault `origin` URL. Connect re-materializes
233
- // with the real handle + setRemote:true.
234
149
  materializePublish: (db) => materializePodLocal(db, { push: false, setRemote: !isLocalFirstContext() }),
235
150
  ...(customOverrides !== undefined ? { customOverrides } : {}),
236
151
  ...(spinner !== undefined
237
152
  ? {
238
153
  onPhase: async (op, label) => {
239
154
  spinner.phase(op, label);
240
- // Yield so the render interval fires AT the boundary — the
241
- // label + elapsed visibly advance even though frames can't
242
- // animate inside a single blocking sync call.
243
155
  await new Promise((r) => setImmediate(r));
244
156
  },
245
157
  }
@@ -247,50 +159,34 @@ export function buildLytInitCommand() {
247
159
  };
248
160
  try {
249
161
  const result = await initBootstrapFlow(flowArgs);
250
- // Stop the spanning spinner BEFORE printing the result/card so its
251
- // teardown (clear-line + show-cursor) doesn't clobber the output.
252
162
  spinner?.stop();
253
163
  if (opts.json === true) {
254
164
  emitJsonResult(result);
255
165
  }
256
166
  else {
257
167
  emitHumanResult(result);
258
- // W1.2 — surface the heal summary (skills/manual/patterns realign).
259
168
  if (result.heal !== undefined) {
260
- // eslint-disable-next-line no-console
261
169
  console.log(summarizeHeal(result.heal));
262
170
  }
263
- // WS2 — render the pod card at the end of `lyt init --auto` too
264
- // (previously wizard-only). Fresh branch only; the lyt-pod-map line
265
- // is omitted because --auto does not generate a pod-map vault.
266
171
  if (result.branch === "fresh") {
267
172
  emitAutoPodCard(result);
268
173
  }
269
174
  else if (result.branch === "adopt" && result.adopt !== undefined) {
270
175
  emitAdoptPodCard(result);
271
176
  }
272
- // Brief B (B.3) — staged-HIL publish prompt. After the honest
273
- // (staged) card, ASK whether to publish now (default-Yes per OD-B2).
274
- // On yes → the consented sync engine pushes pod + vaults. Outward
275
- // effect ONLY behind this explicit consent.
276
177
  await maybePromptAndPublish(result);
277
178
  }
278
- // Re-init with ALL-failed integrity → exit 1 (matches v1.B.2 OD-6
279
- // skip-and-warn precedent + brief OD-4 default).
280
179
  if (result.branch === "re-init" &&
281
180
  result.integrityIssues !== undefined &&
282
181
  result.integrityIssues.length > 0 &&
283
182
  result.integrityIssues.every((i) => i.status !== "ok")) {
284
183
  process.exitCode = 1;
285
184
  }
286
- // a review finding — adopt attempted but the clone failed: clean non-zero exit (the
287
- // actionable error was already rendered by emitHumanResult/emitJsonResult).
288
185
  if (result.branch === "adopt" && result.adoptError !== undefined) {
289
186
  process.exitCode = 1;
290
187
  }
291
188
  }
292
189
  catch (err) {
293
- // Restore the cursor + clear the spinner line before the error path.
294
190
  spinner?.stop();
295
191
  const message = err instanceof Error ? err.message : String(err);
296
192
  emitError(opts.json === true, {
@@ -304,7 +200,6 @@ export function buildLytInitCommand() {
304
200
  async function runCustomPrompts() {
305
201
  const rl = createInterface({ input: process.stdin, output: process.stdout });
306
202
  try {
307
- // 1. Mesh name (default 'personal'; validateMeshName-gated).
308
203
  let meshName = "personal";
309
204
  while (true) {
310
205
  const ans = (await rl.question("Mesh name [personal]: ")).trim();
@@ -316,14 +211,9 @@ async function runCustomPrompts() {
316
211
  }
317
212
  catch (err) {
318
213
  const msg = err instanceof Error ? err.message : String(err);
319
- // eslint-disable-next-line no-console
320
214
  console.log(` ! ${msg}`);
321
- // loop until valid OR user submits empty (defaults to 'personal').
322
215
  }
323
216
  }
324
- // 2. Push target (default authenticated GH handle; accept handle or
325
- // org:name; not structurally validated here — passed through to mesh
326
- // init which validates).
327
217
  let pushDefault;
328
218
  try {
329
219
  pushDefault = getHandleFromIdentity();
@@ -336,11 +226,7 @@ async function runCustomPrompts() {
336
226
  : "Push target (handle or org:name) []: ";
337
227
  const pushAns = (await rl.question(pushPrompt)).trim();
338
228
  const pushTarget = pushAns.length === 0 ? pushDefault : pushAns;
339
- // Main vault name SKIPPED per OD-6 + naming-convention §The main vault
340
- // is locked. Surface the lock as an informational line.
341
- // eslint-disable-next-line no-console
342
229
  console.log("Main vault name: 'main' (locked; cannot be changed)");
343
- // 3. Starter content (default y).
344
230
  const starterAns = (await rl.question("Include starter content? [Y/n]: ")).trim().toLowerCase();
345
231
  const starterFigment = starterAns === "" || starterAns === "y" || starterAns === "yes";
346
232
  return { meshName, pushTarget, starterFigment };
@@ -349,17 +235,10 @@ async function runCustomPrompts() {
349
235
  rl.close();
350
236
  }
351
237
  }
352
- // a review finding (release review) — the honest adopt "expected" set: clone FAILURES only.
353
- // Benign skips (tombstoned / already-registered) are excluded so a clean adopt of
354
- // a pod that lists tombstoned vaults never reads as a partial restore. Shared by
355
- // the human + --json emit (one classification, no drift) and unit-tested directly.
356
238
  export function adoptCloneFailures(manifestSkipped) {
357
239
  return manifestSkipped.filter((s) => s.reason !== "tombstoned" && s.reason !== "already-registered");
358
240
  }
359
- // Exported for the SC5 emit-shape test (Phase-D a review finding — the rendered adopt
360
- // `--json` contract is pinned directly, not just the pure classifier).
361
241
  export function emitJsonResult(res) {
362
- // Lock 0.3 stable-key-ordered output (discriminated-union per `branch`).
363
242
  const stable = {
364
243
  branch: res.branch,
365
244
  durationMs: res.durationMs,
@@ -396,9 +275,6 @@ export function emitJsonResult(res) {
396
275
  alreadyInRegistry: r.alreadyInRegistry,
397
276
  }));
398
277
  }
399
- // ADOPT branch (V-A-11). Stable-keyed; `vaultsExpectedFromManifest` is the a review finding
400
- // honest denominator (excludes tombstoned/already-registered), `skipped` lists
401
- // only real clone failures, `partialRestore` is the SC8 honesty flag.
402
278
  if (res.adopt !== undefined) {
403
279
  const a = res.adopt;
404
280
  const failures = adoptCloneFailures(a.manifestSkipped);
@@ -412,17 +288,11 @@ export function emitJsonResult(res) {
412
288
  firstVaultCreated: a.firstVaultCreated,
413
289
  partialRestore: failures.length > 0,
414
290
  skipped: failures.map((f) => ({ vaultName: f.vaultName, reason: f.reason })),
415
- // (reconciledVaultPaths is carried once, at the top level — the cross-branch
416
- // contract fresh/re-init also use; Phase-D a review finding dropped the nested copy.)
417
291
  };
418
292
  }
419
293
  if (res.adoptError !== undefined) {
420
294
  stable["adoptError"] = { reason: res.adoptError.reason };
421
295
  }
422
- // W1.2 release review fix-pass (R1-Minor) — the heal runs its filesystem
423
- // side-effects under `--json` too; surface its outcome so an automation
424
- // consumer (e.g. the deferred self-updater) can observe collision/divergent
425
- // notes the handler is meant to see (D30.3/D30.4).
426
296
  if (res.heal !== undefined) {
427
297
  stable["heal"] = {
428
298
  runtimes: res.heal.runtimes,
@@ -438,9 +308,6 @@ export function emitJsonResult(res) {
438
308
  if (res.reconciledVaultPaths !== undefined) {
439
309
  stable["reconciledVaultPaths"] = res.reconciledVaultPaths;
440
310
  }
441
- // Brief B (B.3) — surface the staged-vs-published posture. Under --json the
442
- // run is non-interactive (no publish prompt), so state is always "staged":
443
- // init materializes locally; publishing is the consented `lyt sync` step.
444
311
  if (res.publish !== undefined && !res.publish.skipped) {
445
312
  stable["publish"] = {
446
313
  state: "staged",
@@ -448,32 +315,22 @@ export function emitJsonResult(res) {
448
315
  podCommitted: res.publish.podCommitted,
449
316
  };
450
317
  }
451
- // eslint-disable-next-line no-console
452
318
  console.log(JSON.stringify(stable, null, 2));
453
319
  }
454
320
  function emitHumanResult(res) {
455
321
  if (res.branch === "fresh") {
456
- // eslint-disable-next-line no-console
457
322
  console.log(`Forged mesh '${res.meshAssignment?.meshName}' + scaffolded main vault.`);
458
323
  if (res.federation !== undefined) {
459
- // WS3 / D25 — bridge pod ↔ federation on first surface in --auto output.
460
- // Brief C (F3) — honest staged-HIL text: the pod CONTAINER repo was
461
- // CREATED on GitHub (remoteCreated) per D31 §4 two-tier consent; CONTENT
462
- // is staged (unpushed) until `lyt sync`. "local-only" was misleading and
463
- // pointed at the retired `lyt federation rebuild --push` verb.
464
324
  const fed = res.federation;
465
325
  const podPosture = fed.remoteCreated
466
326
  ? "created on GitHub · content staged (unpushed) — run `lyt sync` to publish"
467
327
  : fed.branch === "adopted"
468
328
  ? "on GitHub (adopted) · content staged — run `lyt sync` to publish"
469
329
  : `${fed.branch} — run \`lyt sync\` to publish`;
470
- // eslint-disable-next-line no-console
471
330
  console.log(` pod (federation): ${fed.remoteFullName} — the identity layer behind your pod (${podPosture})`);
472
- // eslint-disable-next-line no-console
473
331
  console.log(` path: ${res.federation.localPath}`);
474
332
  }
475
333
  else {
476
- // eslint-disable-next-line no-console
477
334
  console.warn(" pod (federation): skipped (no authenticated handle resolvable)");
478
335
  }
479
336
  return;
@@ -482,27 +339,17 @@ function emitHumanResult(res) {
482
339
  const issues = res.integrityIssues ?? [];
483
340
  const ok = issues.filter((i) => i.status === "ok").length;
484
341
  const failed = issues.filter((i) => i.status !== "ok");
485
- // eslint-disable-next-line no-console
486
342
  console.log(`Re-init: ${issues.length} vault${issues.length === 1 ? "" : "s"} checked; ${ok} ok, ${failed.length} with issues.`);
487
343
  for (const f of failed) {
488
- // eslint-disable-next-line no-console
489
344
  console.warn(` ! ${f.vaultName} [${f.status}] ${f.error ?? ""}`);
490
345
  }
491
346
  if (failed.length === 0) {
492
- // eslint-disable-next-line no-console
493
347
  console.log(" integrity OK.");
494
348
  }
495
349
  return;
496
350
  }
497
351
  if (res.branch === "adopt") {
498
- // a review finding — adopt failed (pod/vault clone threw). Render an AI-actionable error;
499
- // the main flow sets a clean non-zero exit. Local state is left re-runnable.
500
352
  if (res.adoptError !== undefined) {
501
- // Phase-D a review finding — do NOT over-promise "registry empty / nothing scaffolded":
502
- // that holds for the common pod-clone throw but not a rare post-recovery
503
- // fault that leaves a partial registry. Point at `lyt doctor` for the
504
- // half-set-up case instead of asserting an unconditional clean state.
505
- // eslint-disable-next-line no-console
506
353
  console.error(`Couldn't finish adopting your pod — ${res.adoptError.reason}\n` +
507
354
  ` This is usually a network drop or a GitHub-credentials issue on a private repo.\n` +
508
355
  ` • Check your login: gh auth status\n` +
@@ -513,49 +360,28 @@ function emitHumanResult(res) {
513
360
  const a = res.adopt;
514
361
  if (a === undefined)
515
362
  return;
516
- // a review finding — honest "expected" denominator EXCLUDES benign skips; only real clone
517
- // failures count, so a clean adopt of a pod with tombstoned vaults never reads
518
- // as a partial restore (shared classifier with the --json emit).
519
363
  const failures = adoptCloneFailures(a.manifestSkipped);
520
364
  const expected = a.vaultsRecoveredFromManifest + failures.length;
521
- // eslint-disable-next-line no-console
522
365
  console.log(`Adopted pod ${a.podHandle}/lyt-pod — restored ${a.vaultsRecoveredFromManifest}/${expected} vault(s); re-indexed ${a.reconciledVaultPaths.length}.`);
523
366
  if (a.firstVaultCreated) {
524
- // eslint-disable-next-line no-console
525
367
  console.log(` pod had no vaults to restore — scaffolded ${a.primaryMeshName ?? "personal"}/main.`);
526
368
  }
527
369
  if (failures.length > 0) {
528
- // MF4/SC8 — partial restore is LOUD (a 3-of-5 adopt is never reported as success).
529
- // eslint-disable-next-line no-console
530
370
  console.warn(` ! partial restore: ${failures.length} vault(s) did not clone:`);
531
371
  for (const f of failures) {
532
- // eslint-disable-next-line no-console
533
372
  console.warn(` - ${f.vaultName}: ${f.reason}`);
534
373
  }
535
- // eslint-disable-next-line no-console
536
374
  console.warn(" Re-run `lyt init --auto` to retry the missing vault(s).");
537
375
  }
538
376
  return;
539
377
  }
540
- // discovery
541
378
  const repos = res.discoveredRepos ?? [];
542
- // eslint-disable-next-line no-console
543
379
  console.log(`Discovery: ${repos.length} lyt-* repo${repos.length === 1 ? "" : "s"} found.`);
544
380
  for (const r of repos) {
545
381
  const marker = r.alreadyInRegistry ? "[in registry]" : "[NEW]";
546
- // eslint-disable-next-line no-console
547
382
  console.log(` ${marker} ${r.fullName} (${r.kind})`);
548
383
  }
549
384
  }
550
- // WS2 — render the end-of-init pod card on `lyt init --auto` (FRESH branch).
551
- // Mirrors the wizard's emitPodCard sourcing but for the bootstrap result:
552
- // pod repo full name + local path come from the federation chokepoint
553
- // (res.federation), the mesh + main-vault row from res.meshAssignment. The
554
- // lyt-pod-map line is OMITTED because `--auto` does not generate a pod-map
555
- // vault (only the wizard's P11 does) — PodCardData simply leaves
556
- // podMapVaultPath unset so renderPodCard skips that block. Best-effort: a
557
- // missing federation (no handle) skips the card (the warn line already
558
- // printed by emitHumanResult covers that case).
559
385
  function emitAutoPodCard(res) {
560
386
  const fed = res.federation;
561
387
  const mesh = res.meshAssignment;
@@ -572,28 +398,12 @@ function emitAutoPodCard(res) {
572
398
  podRepoFullName: fed.remoteFullName,
573
399
  podLocalPath: fed.localPath,
574
400
  hyperlinksEnabled: process.stdout.isTTY === true,
575
- // Brief B (B.3) — init materializes LOCALLY (push held); the card is honest
576
- // that the pod is staged, not published, and points at `lyt sync`. The HIL
577
- // publish prompt (maybePromptAndPublish) runs after the card.
578
- // D34 (OD-LOCALFIRST) — a no-gh / provisional pod is "local-only" (NOT
579
- // connected), a stronger honesty than "staged" (which implies gh is wired).
580
401
  publishState: isLocalFirstContext() ? "local-only" : "staged",
581
402
  };
582
- // eslint-disable-next-line no-console
583
403
  console.log(renderPodCard(data));
584
- // Brief C (F4) — `--auto` materializes locally (publishState "staged"), so the
585
- // Next-steps lead with `lyt sync` whenever the pod isn't yet published.
586
- // eslint-disable-next-line no-console
587
404
  console.log(renderNextSteps({ unpublished: data.publishState !== "published" }));
588
- // eslint-disable-next-line no-console
589
405
  console.log("");
590
406
  }
591
- // V-A-11 — pod card for the ADOPT branch (a fresh machine that cloned an existing
592
- // pod). Built from res.adopt (the federation/meshAssignment fields stay unset on
593
- // adopt). publishState "staged": the pod + vaults came FROM GitHub, but the local
594
- // registration/recovery commits are unpushed (noPush) → Next-steps nudge `lyt sync`
595
- // to push them. A null primaryVaultPath (no vault recovered AND scaffold failed)
596
- // skips the card — the human/json line already carried the outcome.
597
407
  function emitAdoptPodCard(res) {
598
408
  const a = res.adopt;
599
409
  if (a === undefined || a.primaryVaultPath === null) {
@@ -612,34 +422,20 @@ function emitAdoptPodCard(res) {
612
422
  hyperlinksEnabled: process.stdout.isTTY === true,
613
423
  publishState: "staged",
614
424
  };
615
- // eslint-disable-next-line no-console
616
425
  console.log(renderPodCard(data));
617
- // eslint-disable-next-line no-console
618
426
  console.log(renderNextSteps({ unpublished: true }));
619
- // eslint-disable-next-line no-console
620
427
  console.log("");
621
428
  }
622
- // Brief B (B.3) — the staged-HIL publish prompt. Runs after the honest card on
623
- // the fresh + re-init human paths. Default-Yes ([Y/n]) per OD-B2: publishing is
624
- // the expected end-state and the prompt itself is the explicit consent (no
625
- // surprise push). On yes → the B.2 reconcile engine (push=true) does the outward
626
- // gh-create + push, resumable via the outbox. Non-interactive (no TTY) leaves
627
- // the pod staged + prints the honest `lyt sync` nudge (never blocks on stdin).
628
429
  async function maybePromptAndPublish(res) {
629
430
  const pub = res.publish;
630
431
  if (pub === undefined || pub.skipped)
631
- return; // no pod / nothing materialized
632
- // D34 (OD-LOCALFIRST) — a local-first (no-gh / provisional) pod has no gh to
633
- // publish to; the publish prompt would fail. Connect is `lyt sync`'s job (the
634
- // self-heal). Nudge there instead of prompting to publish.
432
+ return;
635
433
  if (isLocalFirstContext()) {
636
- // eslint-disable-next-line no-console
637
434
  console.log("\nYour pod is local-only (not connected to GitHub). Run `lyt sync` to connect + back it up.");
638
435
  return;
639
436
  }
640
437
  const vaultCount = pub.vaults.filter((v) => !v.skipped).length;
641
438
  if (process.stdin.isTTY !== true) {
642
- // eslint-disable-next-line no-console
643
439
  console.log("\nYour pod is staged locally (not published). Run `lyt sync` to publish it to GitHub.");
644
440
  return;
645
441
  }
@@ -649,45 +445,34 @@ async function maybePromptAndPublish(res) {
649
445
  const ans = (await rl.question(`\nPublish your pod to GitHub now? (pushes your pod + ${vaultCount} vault repo(s)) [Y/n]: `))
650
446
  .trim()
651
447
  .toLowerCase();
652
- // OD-B2 default-Yes: empty (Enter) or y/yes → publish.
653
448
  yes = ans === "" || ans === "y" || ans === "yes";
654
449
  }
655
450
  finally {
656
451
  rl.close();
657
452
  }
658
453
  if (!yes) {
659
- // eslint-disable-next-line no-console
660
454
  console.log("Staged. Run `lyt sync` when you're ready to publish to GitHub.");
661
455
  return;
662
456
  }
663
- // eslint-disable-next-line no-console
664
457
  console.log("Publishing your pod to GitHub…");
665
458
  const result = await reconcilePublishFlow({ push: true });
666
459
  if (result.skipped) {
667
- // eslint-disable-next-line no-console
668
460
  console.log(`Publish skipped — ${result.reason ?? "no pod"}.`);
669
461
  return;
670
462
  }
671
463
  const pushed = result.vaultOutcomes.filter((o) => o.pushed).length;
672
464
  if (result.ok) {
673
- // eslint-disable-next-line no-console
674
465
  console.log(`✓ Published to GitHub — ${pushed} vault repo(s) + your pod.`);
675
466
  }
676
467
  else {
677
- // eslint-disable-next-line no-console
678
468
  console.log(`⚠ Partial publish — ${pushed} vault(s) pushed; ${result.outboxRemaining} op(s) pending. Re-run \`lyt sync\` to finish (resumable, no data lost).`);
679
469
  for (const o of result.vaultOutcomes) {
680
470
  if (o.status === "conflict" || o.status === "failed") {
681
- // eslint-disable-next-line no-console
682
471
  console.log(` ${o.status}: ${o.vaultName} — ${o.message}`);
683
472
  }
684
473
  }
685
474
  }
686
475
  }
687
- // Non-interactive defaults handler used by `lyt init --wizard --dry-run`
688
- // so the smoke test (and CI sanity invocations) never block on stdin.
689
- // Returns: ask → defaultValue (or "" if absent); confirm → defaultValue
690
- // (or true); select → first option. Matches the runWizard contract.
691
476
  function makeDryRunDefaultsHandler() {
692
477
  return {
693
478
  async ask(_question, defaultValue) {
@@ -701,25 +486,10 @@ function makeDryRunDefaultsHandler() {
701
486
  },
702
487
  };
703
488
  }
704
- // D34 (OD-LOCALFIRST) — true when init should stay LOCAL: no gh handle resolves
705
- // (gh absent/unauthed) OR the cached identity is provisional (a local pod). In
706
- // both cases the materialize pass holds vault remotes (setRemote:false) so the
707
- // provisional handle never reaches a remote URL — connect wires the real one.
708
- //
709
- // release review fix-pass: read the cache DIRECTLY (no getIdentity / TTL /
710
- // gh-refresh). A provisional cache → local-first regardless of its age (connect,
711
- // not a silent init refresh, is where it reconciles); a gh-cli cache →
712
- // connected. Only when there is NO cache do we probe gh (the genuine
713
- // connected-fresh-vs-no-gh decision). Mirrors init-bootstrap's local-first
714
- // trigger so the command + flow agree.
715
489
  function isLocalFirstContext() {
716
- // MF1 — the provisional-cache determination is the shared resolveLocalFirst
717
- // predicate (kills the triplication: router + doFreshBranch + here). When a
718
- // cache exists its verdict is authoritative; only a NO-cache machine probes gh.
719
490
  const cached = readIdentityCache();
720
491
  if (cached !== null)
721
492
  return resolveLocalFirst(cached);
722
- // No cache → local-first iff no gh handle resolves (gh absent/unauthed).
723
493
  try {
724
494
  getHandleFromIdentity();
725
495
  return false;
@@ -730,11 +500,9 @@ function isLocalFirstContext() {
730
500
  }
731
501
  function emitError(json, body) {
732
502
  if (json) {
733
- // eslint-disable-next-line no-console
734
503
  console.error(JSON.stringify(body, null, 2));
735
504
  }
736
505
  else {
737
- // eslint-disable-next-line no-console
738
506
  console.error(`lyt init: ${String(body["message"] ?? body["error"])}`);
739
507
  }
740
508
  }