@catalyst-cloud/cli 0.15.2 → 0.15.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.
Files changed (36) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/README.md +2 -2
  3. package/dist/cli.js +5 -17
  4. package/dist/oauth.js +1 -1
  5. package/dist/onboard-accounts.js +67 -171
  6. package/dist/onboard-checklist.js +309 -0
  7. package/dist/onboard-existing.js +1 -1
  8. package/dist/onboard-first-ticket.js +199 -0
  9. package/dist/onboard-headless.js +2 -2
  10. package/dist/onboard-housekeeping.js +93 -0
  11. package/dist/onboard-next.js +108 -9
  12. package/dist/onboard-ready.js +61 -0
  13. package/dist/onboard-runtime.js +60 -27
  14. package/dist/onboard-standalone-copy.js +73 -66
  15. package/dist/onboard-ui.js +162 -89
  16. package/dist/onboard-values.js +47 -0
  17. package/dist/onboard.js +49 -13
  18. package/dist/ready-copy.js +87 -0
  19. package/dist/ready.js +1 -57
  20. package/dist/setup-onboard-copy.js +237 -41
  21. package/dist/setup-render.js +73 -16
  22. package/dist/setup.js +6 -3
  23. package/package.json +1 -1
  24. package/skills/catalyst-github/SKILL.md +1 -1
  25. package/skills/catalyst-linear/SKILL.md +1 -1
  26. package/skills/catalyst-onboard/SKILL.md +3 -3
  27. package/skills/catalyst-onboard/references/replacing-a-credential.md +9 -15
  28. package/skills/catalyst-onboard/references/what-a-phase-needs.md +4 -11
  29. package/skills/catalyst-onboard/scripts/where-am-i.mjs +1 -1
  30. package/skills/run-this-project/SKILL.md +2 -2
  31. package/skills/unstick/SKILL.md +1 -1
  32. package/skills/what-needs-me/SKILL.md +1 -1
  33. package/skills/what-this-repo-needs/SKILL.md +1 -1
  34. package/skills/whats-happening/SKILL.md +2 -2
  35. package/skills/whats-happening/references/coding-accounts.md +1 -1
  36. package/skills/whats-happening/scripts/snapshot.mjs +1 -1
@@ -1,9 +1,10 @@
1
1
  import { COMPUTER_CHECKS, standalonePlan, standalonePlanNotes, pendingContinuation, } from "./onboard-standalone-copy.js";
2
2
  import { trackedPrompt } from "./setup-prompt-frame.js";
3
3
  import { createOnboardInterrupts } from "./onboard-interrupts.js";
4
- import { setupStepView, CONNECTION_REPAIR_REASONS, setupFinalScreen, setupBrowserInstruction, SETUP_NUMBERS, } from "./setup-onboard-copy.js";
4
+ import { setupStepView, CONNECTION_REPAIR_REASONS, setupFinalScreen, setupBrowserInstruction, setupCommandWords as say, setupPartHeading, setupPartProgress, setupTrackerRows, SETUP_PART_NUMBERS, SETUP_PART_OF, SETUP_PARTS, } from "./setup-onboard-copy.js";
5
5
  import { createOnboardProgress, } from "./onboard-progress.js";
6
6
  import { CREATE_TEAM_CHOICE, suggestTeamKey, teamKeyProblem, teamNameProblem, } from "./onboard-team-create.js";
7
+ import { FIRST_TICKET_SAMPLE, FIRST_TICKET_SKIP, } from "./onboard-first-ticket.js";
7
8
  import { ONBOARD_STEPS, ONBOARD_TITLES, stepSatisfied, onboardIdentityLines, onboardNextActions, onboardReadyForWork, onboardStepAction, } from "./onboard.js";
8
9
  import { onboardStepDetail, onboardReasonText } from "./onboard-next.js";
9
10
  export function shouldUseOnboardUi(args, stdoutTty) {
@@ -12,25 +13,6 @@ export function shouldUseOnboardUi(args, stdoutTty) {
12
13
  args.flags.yes !== true &&
13
14
  args.flags["dry-run"] !== true);
14
15
  }
15
- export const STEP_NUMBERS = {
16
- signin: 4,
17
- housekeeping: 5,
18
- "linear.workspace": 6,
19
- "linear.personal": 7,
20
- "linear.team": 8,
21
- "linear.adopt": 9,
22
- "linear.automations": 10,
23
- "github.install": 11,
24
- "github.personal": 12,
25
- "github.repos": 13,
26
- projects: 13,
27
- accounts: 14,
28
- capacity: 15,
29
- runner: 15,
30
- settings: 16,
31
- values: 16,
32
- "first-ticket": 17,
33
- };
34
16
  export function createClackOnboardUi(prompts, streams, deps = {}) {
35
17
  const abort = new AbortController();
36
18
  const options = { ...streams, signal: abort.signal };
@@ -52,6 +34,15 @@ export function createClackOnboardUi(prompts, streams, deps = {}) {
52
34
  const renderer = deps.renderer;
53
35
  const interactive = deps.interactive !== false;
54
36
  let group;
37
+ // CTC-4680: the part whose steps are on screen, the journal and plan the tracker reads, and
38
+ // whether boundaries draw the tracker (not on a check-again pass).
39
+ let part;
40
+ // The part of the last step that ended in this run, shown or not: a boundary is a move past it.
41
+ let ranPart;
42
+ let journalSeen;
43
+ let planned = {};
44
+ let rechecking = false;
45
+ let shownPlan;
55
46
  let browserOpened = true;
56
47
  let questionOpen = false;
57
48
  let signinShown = false;
@@ -81,6 +72,41 @@ export function createClackOnboardUi(prompts, streams, deps = {}) {
81
72
  ].includes(id))) &&
82
73
  !(id === "signin" && signinShown));
83
74
  const title = (id) => setupStepView({ id, state: "running" }).title;
75
+ const number = (id) => SETUP_PART_NUMBERS[id] ?? 0;
76
+ /** A part's heading, then its plan rows, numbered from 1. */
77
+ const planPart = (r, p, journal) => {
78
+ r.heading(setupPartHeading(p, r.traits.unicode));
79
+ for (const row of standalonePlan(journal, planned.scope, planned.runner))
80
+ if (row.part === p)
81
+ r.plan(row.number, row.title, row.detail);
82
+ };
83
+ /** Starting a step in another part: its heading, and when this run has moved past a part, the
84
+ * tracker again with the new part's plan. After the install engine, part 1's checks run unshown,
85
+ * so setup opens on the part 1 boundary. */
86
+ const enterPart = (id) => {
87
+ if (!renderer)
88
+ return;
89
+ const next = SETUP_PART_OF[id];
90
+ if (next === part)
91
+ return;
92
+ const previous = ranPart;
93
+ part = next;
94
+ if (previous === undefined ||
95
+ next <= previous ||
96
+ rechecking ||
97
+ !journalSeen) {
98
+ renderer.heading(setupPartHeading(next, renderer.traits.unicode));
99
+ return;
100
+ }
101
+ const progress = setupPartProgress(journalSeen, planned.scope);
102
+ const left = progress[previous].total - progress[previous].done;
103
+ renderer.heading(left
104
+ ? `Part ${previous} has ${left === 1 ? "1 step" : `${left} steps`} left. Setup carries on with part ${next}.`
105
+ : `Part ${previous} done. ${SETUP_PARTS[previous].done}`);
106
+ renderer.tracker(setupTrackerRows(progress, { next }));
107
+ planPart(renderer, next, journalSeen);
108
+ renderer.blank();
109
+ };
84
110
  const stop = () => {
85
111
  if (active) {
86
112
  if (renderer)
@@ -103,14 +129,16 @@ export function createClackOnboardUi(prompts, streams, deps = {}) {
103
129
  }
104
130
  signinShown = true;
105
131
  if (reveal) {
106
- group = "This computer";
107
- renderer.heading(group);
108
- renderer.begin(4, title("signin"), "signing in…");
132
+ enterPart("signin");
133
+ renderer.begin(number("signin"), title("signin"), "signing in…");
109
134
  active = true;
110
135
  }
111
136
  }
112
137
  if (hidden(currentStep))
113
138
  return;
139
+ // The sign-in's own wait line: the frame already shows the countdown and how to stop.
140
+ if (renderer && /^Waiting for you to approve…/.test(text))
141
+ return;
114
142
  if (renderer &&
115
143
  currentStep === "linear.adopt" &&
116
144
  text.endsWith("already has every state and label"))
@@ -124,7 +152,7 @@ export function createClackOnboardUi(prompts, streams, deps = {}) {
124
152
  }
125
153
  if (currentStep === "capacity")
126
154
  return;
127
- renderer.detail(text);
155
+ renderer.detail(say(text));
128
156
  return;
129
157
  }
130
158
  stop();
@@ -136,9 +164,9 @@ export function createClackOnboardUi(prompts, streams, deps = {}) {
136
164
  if (hidden(currentStep))
137
165
  return;
138
166
  if (renderer && currentStep)
139
- renderer.begin(SETUP_NUMBERS[currentStep] ?? 0, title(currentStep), text === ONBOARD_TITLES[currentStep] ? "checking…" : text);
167
+ renderer.begin(number(currentStep), title(currentStep), text === ONBOARD_TITLES[currentStep] ? "checking…" : text);
140
168
  else if (renderer)
141
- renderer.line(text);
169
+ renderer.line(say(text));
142
170
  else
143
171
  spin.start(text);
144
172
  active = true;
@@ -147,7 +175,7 @@ export function createClackOnboardUi(prompts, streams, deps = {}) {
147
175
  const question = () => {
148
176
  if (renderer && currentStep && !questionOpen) {
149
177
  stop();
150
- renderer.begin(SETUP_NUMBERS[currentStep] ?? 0, title(currentStep), "", "ask");
178
+ renderer.begin(number(currentStep), title(currentStep), "", "ask");
151
179
  active = true;
152
180
  questionOpen = true;
153
181
  }
@@ -155,7 +183,7 @@ export function createClackOnboardUi(prompts, streams, deps = {}) {
155
183
  const select = async (opts, ownFrame = false) => {
156
184
  if (!renderer)
157
185
  return prompts.select(opts);
158
- const detached = ownFrame || !currentStep;
186
+ const detached = ownFrame || (!currentStep && !questionOpen);
159
187
  if (detached) {
160
188
  stop();
161
189
  renderer.begin(0, "Your choice", "", "ask");
@@ -202,14 +230,15 @@ export function createClackOnboardUi(prompts, streams, deps = {}) {
202
230
  renderer.heading(screen.heading);
203
231
  if (screen.paused)
204
232
  renderer.line("Your progress is saved.");
233
+ renderer.tracker(setupTrackerRows(setupPartProgress(journal, planned.scope), "end"));
205
234
  if (screen.readinessCause)
206
- renderer.line(screen.readinessCause);
235
+ renderer.line(say(screen.readinessCause));
207
236
  if (screen.actions.length) {
208
237
  renderer.line(`${screen.actions.length} ${screen.actions.length === 1 ? "thing needs" : "things need"} you:`);
209
- screen.actions.forEach((action, i) => renderer.line(`${i + 1}. ${action.text}`));
238
+ screen.actions.forEach((action, i) => renderer.item(`${i + 1}. `, say(action.text)));
210
239
  if (screen.more)
211
240
  renderer.line(`and ${screen.more} more after these`);
212
- const continuation = pendingContinuation(journal);
241
+ const continuation = pendingContinuation(journal, new Set(screen.actions.map((action) => action.id)));
213
242
  if (continuation)
214
243
  renderer.line(continuation);
215
244
  }
@@ -227,6 +256,8 @@ export function createClackOnboardUi(prompts, streams, deps = {}) {
227
256
  interactive,
228
257
  handlesSignals: true,
229
258
  plan(journal, identity, planOptions) {
259
+ planned = { scope: planOptions?.scope, runner: planOptions?.runner };
260
+ journalSeen = journal;
230
261
  if (deps.consentGiven)
231
262
  return;
232
263
  machineScope = (planOptions?.scope ?? ONBOARD_STEPS).filter((id) => COMPUTER_CHECKS.some((check) => check === id));
@@ -235,32 +266,46 @@ export function createClackOnboardUi(prompts, streams, deps = {}) {
235
266
  stop();
236
267
  if (!introduced) {
237
268
  if (renderer)
238
- renderer.intro("Catalyst setup");
269
+ renderer.brand("setup", deps.version);
239
270
  else
240
271
  prompts.intro("Catalyst setup", { output: streams.output });
241
272
  introduced = true;
242
273
  }
243
274
  if (identity !== undefined)
244
275
  for (const line of onboardIdentityLines(identity))
245
- message(line);
276
+ if (renderer)
277
+ renderer.line(line);
278
+ else
279
+ message(line);
280
+ // After sign-in the plan is asked about again. When it is the plan already on screen, the
281
+ // person sees who they are now and the question, not the header, tracker and plan twice.
282
+ const planKey = JSON.stringify([
283
+ planned.scope,
284
+ planned.runner,
285
+ planOptions?.localSync ?? journal.localSync === true,
286
+ ]);
287
+ if (renderer && planKey === shownPlan)
288
+ return;
289
+ shownPlan = planKey;
246
290
  if (renderer) {
247
- renderer.heading("The plan");
248
- let planGroup;
249
- for (const row of standalonePlan(journal, planOptions?.scope, planOptions?.runner)) {
250
- if (row.group !== planGroup) {
251
- renderer.blank();
252
- renderer.line(renderer.bold(row.group));
253
- planGroup = row.group;
254
- }
255
- renderer.plan(row.number, row.title, row.detail);
256
- }
291
+ // CTC-4680: what setup does and the three parts first, then only the first part's steps.
292
+ // Each later part lists its own steps when it starts.
293
+ const first = standalonePlan(journal, planned.scope, planned.runner)[0]?.part ?? 1;
294
+ renderer.blank();
295
+ renderer.line("Setup gets Catalyst working on your team's Linear tickets. It has three parts.");
296
+ renderer.tracker(setupTrackerRows(setupPartProgress(journal, planned.scope), {
297
+ plan: first,
298
+ }));
299
+ planPart(renderer, first, journal);
300
+ // Its steps follow under this heading; the first one does not print it again.
301
+ part = first;
257
302
  renderer.line(standalonePlanNotes(planOptions?.localSync ?? journal.localSync === true));
258
303
  return;
259
304
  }
260
305
  const steps = new Map(journal.steps.map((step) => [step.id, step]));
261
306
  message(ONBOARD_STEPS.map((id) => `· ${ONBOARD_TITLES[id]}${steps.get(id)?.state === "done" ? " (recheck)" : ""}`).join("\n"));
262
307
  message("Use cloud reads by default. Local sync is optional for SQL or sustained local reads.");
263
- message("Setup may check one stored Claude account using a one-token provider request. This may use Claude quota. It does not refresh Codex credentials.");
308
+ message("Setup reads your AI accounts without sending them a request.");
264
309
  },
265
310
  async confirmPlan(localSync, signin = "unavailable") {
266
311
  if (deps.consentGiven || !interactive)
@@ -482,6 +527,36 @@ export function createClackOnboardUi(prompts, streams, deps = {}) {
482
527
  }
483
528
  return typeof answer === "string" ? answer : null;
484
529
  },
530
+ async chooseFirstTicket(tickets, { teamKey, stage }) {
531
+ stop();
532
+ if (abort.signal.aborted)
533
+ return null;
534
+ const answer = await select({
535
+ ...options,
536
+ message: `Which ticket should Catalyst start first? It moves to ${stage} in ${teamKey}.`,
537
+ initialValue: tickets[0]?.identifier ?? FIRST_TICKET_SAMPLE,
538
+ options: [
539
+ ...tickets.map((ticket) => ({
540
+ value: ticket.identifier,
541
+ label: `${ticket.identifier} ${ticket.title}`,
542
+ ...(ticket.estimate !== null
543
+ ? { hint: `estimate ${ticket.estimate}` }
544
+ : {}),
545
+ })),
546
+ {
547
+ value: FIRST_TICKET_SAMPLE,
548
+ label: "Create a sample ticket",
549
+ hint: "documents how to run the tests; changes only CONTRIBUTING.md",
550
+ },
551
+ { value: FIRST_TICKET_SKIP, label: "Skip for now" },
552
+ ],
553
+ });
554
+ if (prompts.isCancel(answer)) {
555
+ abort.abort();
556
+ return null;
557
+ }
558
+ return typeof answer === "string" ? answer : null;
559
+ },
485
560
  async chooseRunner() {
486
561
  stop();
487
562
  if (abort.signal.aborted)
@@ -511,15 +586,12 @@ export function createClackOnboardUi(prompts, streams, deps = {}) {
511
586
  async retryTimedOut(id) {
512
587
  if (abort.signal.aborted)
513
588
  return false;
514
- if (renderer && active) {
515
- // The wait frame is still live: its status and a last line say why it ended.
516
- renderer.update("the link timed out");
517
- renderer.detail("The link timed out.");
518
- }
519
- else if (renderer) {
520
- renderer.begin(SETUP_NUMBERS[id] ?? 0, title(id), "the link timed out");
521
- renderer.detail("The link timed out.");
589
+ if (renderer) {
590
+ // The question draws under this row, so the reason stays on screen while it is asked.
591
+ stop();
592
+ renderer.begin(number(id), title(id), "the link timed out", "ask");
522
593
  active = true;
594
+ questionOpen = true;
523
595
  }
524
596
  else {
525
597
  stop();
@@ -548,7 +620,7 @@ export function createClackOnboardUi(prompts, streams, deps = {}) {
548
620
  if (state === "done")
549
621
  stagedSigninApproved = true;
550
622
  if (currentStep === "signin" && signinShown)
551
- renderer.step(state === "done" ? "done" : state === "failed" ? "fail" : "act", 4, title("signin"), state === "done"
623
+ renderer.step(state === "done" ? "done" : state === "failed" ? "fail" : "act", number("signin"), title("signin"), state === "done"
552
624
  ? "approved"
553
625
  : state === "failed"
554
626
  ? "could not verify this account"
@@ -560,11 +632,11 @@ export function createClackOnboardUi(prompts, streams, deps = {}) {
560
632
  signinShown = false;
561
633
  if (state !== "done") {
562
634
  if (cause)
563
- renderer.line(cause.charAt(0).toUpperCase() + cause.slice(1));
635
+ renderer.line(say(cause.charAt(0).toUpperCase() + cause.slice(1)));
564
636
  renderer.heading(abort.signal.aborted ? "Setup paused" : "Setup is not ready yet");
565
637
  if (!cause?.includes("Your saved connection was not changed."))
566
638
  renderer.line("Your saved connection was not changed.");
567
- renderer.outro("Next: run catalyst onboard to sign in again.");
639
+ renderer.outro("Next: run catalyst setup to sign in again.");
568
640
  }
569
641
  return true;
570
642
  },
@@ -582,29 +654,17 @@ export function createClackOnboardUi(prompts, streams, deps = {}) {
582
654
  if (!machineScope.length)
583
655
  return;
584
656
  if (machineChecks.size === 0 && !machineBlocked) {
585
- renderer.heading("This computer");
586
- group = "This computer";
587
- renderer.begin(1, "This computer", "checking…");
657
+ enterPart(id);
658
+ renderer.begin(number(id), "This computer", "checking…");
588
659
  active = true;
589
660
  }
590
661
  return;
591
662
  }
592
663
  if (hidden(id))
593
664
  return;
594
- if (id === "projects" || id === "values")
665
+ if (id === "projects")
595
666
  return;
596
- const next = !deps.consentGiven &&
597
- ["signin", "daemon", "housekeeping", "ready"].includes(id)
598
- ? "This computer"
599
- : id.startsWith("linear.")
600
- ? "Linear"
601
- : id.startsWith("github.")
602
- ? "GitHub"
603
- : "Work";
604
- if (next !== group) {
605
- renderer.heading(next);
606
- group = next;
607
- }
667
+ enterPart(id);
608
668
  start(ONBOARD_TITLES[id]);
609
669
  return;
610
670
  }
@@ -626,6 +686,9 @@ export function createClackOnboardUi(prompts, streams, deps = {}) {
626
686
  start(ONBOARD_TITLES[id]);
627
687
  },
628
688
  stepEnd(step, journal) {
689
+ if (journal)
690
+ journalSeen = journal;
691
+ ranPart = SETUP_PART_OF[step.id];
629
692
  if (renderer) {
630
693
  if (standaloneComputer(step.id)) {
631
694
  machineChecks.set(step.id, step);
@@ -656,20 +719,18 @@ export function createClackOnboardUi(prompts, streams, deps = {}) {
656
719
  if ((step.id === "github.repos" || step.id === "settings") &&
657
720
  step.state === "done")
658
721
  return;
659
- if ((step.id === "projects" || step.id === "values") &&
660
- journal?.steps.find((s) => s.id === (step.id === "projects" ? "github.repos" : "settings"))?.state !== "done")
722
+ if (step.id === "projects" &&
723
+ journal?.steps.find((s) => s.id === "github.repos")?.state !== "done")
661
724
  return;
662
725
  const view = setupStepView(step, journal);
663
- if (hidden(step.id)) {
664
- renderer.heading("This computer");
665
- group = "This computer";
666
- }
726
+ if (hidden(step.id))
727
+ enterPart(step.id);
667
728
  renderer.step(view.mark, view.number, view.title, view.outcome);
668
729
  const granted = deps.verbose ? onboardStepDetail(step) : undefined;
669
730
  if (granted)
670
731
  renderer.detail(granted);
671
732
  if (deps.verbose && CONNECTION_REPAIR_REASONS.has(step.reason ?? ""))
672
- renderer.detail(onboardReasonText(step, { baseUrl: deps.baseUrl?.(), journal }));
733
+ renderer.detail(say(onboardReasonText(step, { baseUrl: deps.baseUrl?.(), journal })));
673
734
  if (view.mark === "fail")
674
735
  renderer.detail(deps.logPath?.()
675
736
  ? "Check the full log for the cause, then try this step again."
@@ -697,6 +758,16 @@ export function createClackOnboardUi(prompts, streams, deps = {}) {
697
758
  prompts.log.message(granted, { output: streams.output });
698
759
  },
699
760
  message,
761
+ note(text) {
762
+ if (hidden(currentStep))
763
+ return;
764
+ if (renderer)
765
+ renderer.detail(text);
766
+ else {
767
+ stop();
768
+ prompts.log.message(text, { output: streams.output });
769
+ }
770
+ },
700
771
  async checkAgain(journal) {
701
772
  if (!renderer ||
702
773
  !interactive ||
@@ -725,7 +796,8 @@ export function createClackOnboardUi(prompts, streams, deps = {}) {
725
796
  machineScope = COMPUTER_CHECKS.filter((id) => !stepSatisfied(journal.steps.find((step) => step.id === id)));
726
797
  machineChecks.clear();
727
798
  machineBlocked = false;
728
- group = undefined;
799
+ part = undefined;
800
+ rechecking = true;
729
801
  renderer.heading("Checking again");
730
802
  return true;
731
803
  },
@@ -741,8 +813,8 @@ export function createClackOnboardUi(prompts, streams, deps = {}) {
741
813
  renderer.line(`${ONBOARD_TITLES[only]} finished. Onboarding still has other steps.`);
742
814
  else
743
815
  for (const action of onboardNextActions(journal, deps.baseUrl?.(), only))
744
- renderer.line(action);
745
- renderer.outro("Next: run catalyst onboard");
816
+ renderer.line(say(action));
817
+ renderer.outro("Next: run catalyst setup");
746
818
  return;
747
819
  }
748
820
  if (renderer) {
@@ -755,7 +827,7 @@ export function createClackOnboardUi(prompts, streams, deps = {}) {
755
827
  renderer.line("Your progress is saved.");
756
828
  }
757
829
  summaryShown = false;
758
- renderer.outro(screen.next);
830
+ renderer.outro(say(screen.next));
759
831
  return;
760
832
  }
761
833
  stop();
@@ -779,10 +851,12 @@ export function createClackOnboardUi(prompts, streams, deps = {}) {
779
851
  text = `${ONBOARD_TITLES[only]} finished. Onboarding still has other steps.\nresume: catalyst onboard`;
780
852
  prompts.outro(text, { output: streams.output });
781
853
  },
782
- async wait(text, run) {
783
- const browser = currentStep &&
784
- renderer &&
785
- setupBrowserInstruction(currentStep, deps.baseUrl?.(), currentStep !== "accounts" && browserOpened);
854
+ async wait(text, run, page) {
855
+ const browser = currentStep && renderer && page
856
+ ? page
857
+ : currentStep &&
858
+ renderer &&
859
+ setupBrowserInstruction(currentStep, deps.baseUrl?.(), currentStep !== "accounts" && browserOpened);
786
860
  const signinWait = Boolean(renderer && currentStep === "signin" && signinShown);
787
861
  const seconds = signinWait
788
862
  ? Math.ceil((deps.signinTimeoutMs ?? 600000) / 1000)
@@ -794,11 +868,9 @@ export function createClackOnboardUi(prompts, streams, deps = {}) {
794
868
  };
795
869
  if (renderer && browser && currentStep) {
796
870
  stop();
797
- renderer.begin(SETUP_NUMBERS[currentStep] ?? 0, title(currentStep), renderer.traits.unicode
871
+ renderer.begin(number(currentStep), title(currentStep), renderer.traits.unicode
798
872
  ? status()
799
873
  : "waiting for you, up to 10 minutes");
800
- if (browser.preparation)
801
- renderer.detail(browser.preparation);
802
874
  renderer.action(browser.instruction);
803
875
  if (browser.url)
804
876
  renderer.detail(renderer.link(browser.url));
@@ -881,6 +953,7 @@ export function createClackOnboardUi(prompts, streams, deps = {}) {
881
953
  delete ui.chooseFirstRepository;
882
954
  delete ui.reviewSettings;
883
955
  delete ui.chooseRunner;
956
+ delete ui.chooseFirstTicket;
884
957
  delete ui.retryTimedOut;
885
958
  delete ui.confirmWorkflowAdoption;
886
959
  }
@@ -0,0 +1,47 @@
1
+ import { loadConfig } from "./config.js";
2
+ import { loadContract } from "./contract.js";
3
+ import { selectedOnboardTeam } from "./onboard-existing.js";
4
+ import { valuesFacts } from "./ready-copy.js";
5
+ const waiting = (reason, evidence) => ({
6
+ state: "waiting",
7
+ reason,
8
+ ...(evidence ? { evidence } : {}),
9
+ });
10
+ /** The selected team's `required_values` readiness check decides this step. It passes when the
11
+ * selected repositories declare no required value, or every one has a value. Nothing here reads,
12
+ * imports or prints a value: a missing one is named, with the page where it is set. A stale, absent
13
+ * or unknown check waits, so the step never claims values it could not see. */
14
+ export function onboardValuesAdapter() {
15
+ return {
16
+ check: async (ctx, journal, signal) => {
17
+ const cfg = loadConfig(ctx.home);
18
+ const teamId = selectedOnboardTeam(journal);
19
+ if (!cfg || !teamId)
20
+ return waiting("required_values_unverified");
21
+ let loaded;
22
+ try {
23
+ loaded = await loadContract(ctx, cfg, { refresh: true, signal });
24
+ }
25
+ catch {
26
+ return waiting(signal?.aborted ? "interrupted" : "required_values_unverified");
27
+ }
28
+ if (loaded.source === "cache")
29
+ return waiting("required_values_unverified");
30
+ const readiness = loaded.doc.teams.find((team) => team.id === teamId)?.readiness;
31
+ const fresh = readiness?.checkedAt != null &&
32
+ readiness.expiresAt != null &&
33
+ readiness.expiresAt > ctx.now().getTime();
34
+ const check = fresh
35
+ ? readiness.checks.find((row) => row.id === "required_values")
36
+ : undefined;
37
+ if (check?.state === "pass")
38
+ return { state: "done", evidence: { team: teamId } };
39
+ if (check?.state !== "fail")
40
+ return waiting("required_values_unverified");
41
+ return waiting("required_values_missing", {
42
+ team: teamId,
43
+ requiredValues: JSON.stringify(valuesFacts(check)),
44
+ });
45
+ },
46
+ };
47
+ }