@catalyst-cloud/cli 0.15.1 → 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.
- package/CHANGELOG.md +15 -0
- package/README.md +13 -4
- package/dist/args.js +1 -1
- package/dist/capabilities.js +3 -1
- package/dist/cli.js +25 -30
- package/dist/consent-browser.js +3 -1
- package/dist/oauth.js +1 -1
- package/dist/onboard-accounts.js +67 -171
- package/dist/onboard-checklist.js +309 -0
- package/dist/onboard-existing.js +1 -1
- package/dist/onboard-first-ticket.js +199 -0
- package/dist/onboard-github.js +13 -1
- package/dist/onboard-headless.js +6 -4
- package/dist/onboard-housekeeping.js +93 -0
- package/dist/onboard-next.js +124 -13
- package/dist/onboard-ready.js +61 -0
- package/dist/onboard-runtime.js +117 -26
- package/dist/onboard-standalone-copy.js +73 -66
- package/dist/onboard-ui.js +189 -81
- package/dist/onboard-values.js +47 -0
- package/dist/onboard.js +59 -18
- package/dist/ready-copy.js +325 -0
- package/dist/ready.js +35 -98
- package/dist/setup-onboard-copy.js +262 -41
- package/dist/setup-render.js +73 -14
- package/dist/setup.js +6 -3
- package/package.json +2 -2
- package/skills/catalyst-github/SKILL.md +1 -1
- package/skills/catalyst-linear/SKILL.md +1 -1
- package/skills/catalyst-onboard/SKILL.md +3 -3
- package/skills/catalyst-onboard/references/replacing-a-credential.md +9 -15
- package/skills/catalyst-onboard/references/what-a-phase-needs.md +4 -11
- package/skills/catalyst-onboard/scripts/where-am-i.mjs +3 -2
- package/skills/run-this-project/SKILL.md +2 -2
- package/skills/unstick/SKILL.md +1 -1
- package/skills/what-needs-me/SKILL.md +1 -1
- package/skills/what-this-repo-needs/SKILL.md +1 -1
- package/skills/whats-happening/SKILL.md +2 -2
- package/skills/whats-happening/references/coding-accounts.md +1 -1
- package/skills/whats-happening/scripts/snapshot.mjs +1 -1
package/dist/ready.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// is a note, never a failure; the HTTP SDK loading is a check for cloud reads.
|
|
4
4
|
import { accessSync, constants, existsSync, readFileSync, realpathSync, statSync, } from "node:fs";
|
|
5
5
|
import { basename, delimiter, dirname, join } from "node:path";
|
|
6
|
-
import { LEGACY_PACKAGE_NAME, PACKAGE_NAME, defaultSkillsDirFor, loadConfig, normalizeBaseUrl, readManifest, upgradeCommand, } from "./config.js";
|
|
6
|
+
import { DEFAULT_BASE_URL, LEGACY_PACKAGE_NAME, PACKAGE_NAME, defaultSkillsDirFor, loadConfig, normalizeBaseUrl, readManifest, upgradeCommand, } from "./config.js";
|
|
7
7
|
import { contractVersionInRange, readContractCache } from "./contract.js";
|
|
8
8
|
import { CliError } from "./errors.js";
|
|
9
9
|
import { latestPublishedVersion } from "./published.js";
|
|
@@ -15,6 +15,7 @@ import { FIRST_STAMPED_VERSION } from "./skill-shape.js";
|
|
|
15
15
|
import { installedBundleVersion } from "./skills.js";
|
|
16
16
|
import { onboardingReadyReport, observeCloudOnboarding, } from "./onboard-ready.js";
|
|
17
17
|
import { selectedOnboardTeam } from "./onboard-existing.js";
|
|
18
|
+
import { dispatchGateCopy, teamCheckCopy, teamPage, valuesFix, whoFixes } from "./ready-copy.js";
|
|
18
19
|
import { onboardStatePath, readOnboardJournal } from "./onboard.js";
|
|
19
20
|
export { semverOlder };
|
|
20
21
|
/** `ready`'s own wording for the replica. It never recommends adopting the replica — until the
|
|
@@ -40,7 +41,7 @@ function readyReplicaLine(s) {
|
|
|
40
41
|
case "not-configured":
|
|
41
42
|
return "replica: not configured — run login first";
|
|
42
43
|
case "absent":
|
|
43
|
-
return `replica: absent at ${s.dbPath} — optional, and off by default for large
|
|
44
|
+
return `replica: absent at ${s.dbPath} — optional, and off by default for large workspaces while the snapshot path is being made safe; every read works through the API`;
|
|
44
45
|
case "fresh":
|
|
45
46
|
return `replica: fresh at ${s.dbPath} (cursor ${s.cursor}, heartbeat ${s.heartbeatAgeMs}ms ago${s.lag !== undefined ? `, ${s.lag} behind head ${s.head}` : ""})`;
|
|
46
47
|
case "stale":
|
|
@@ -111,81 +112,6 @@ export function catalystCommandCheck(env, platform = process.platform) {
|
|
|
111
112
|
"Keep using catalyst-skills on this machine, or put the npm global bin directory ahead of that one on PATH.",
|
|
112
113
|
};
|
|
113
114
|
}
|
|
114
|
-
function nameList(v) {
|
|
115
|
-
return Array.isArray(v)
|
|
116
|
-
? v.filter((n) => typeof n === "string" && n.length > 0)
|
|
117
|
-
: [];
|
|
118
|
-
}
|
|
119
|
-
/** Entries whose name and references are both present; anything malformed is dropped, not guessed. */
|
|
120
|
-
function unresolvedList(v) {
|
|
121
|
-
if (!Array.isArray(v))
|
|
122
|
-
return [];
|
|
123
|
-
return v.flatMap((u) => {
|
|
124
|
-
if (typeof u !== "object" || u === null)
|
|
125
|
-
return [];
|
|
126
|
-
const { name, references } = u;
|
|
127
|
-
const refs = nameList(references);
|
|
128
|
-
return typeof name === "string" && name.length > 0 && refs.length > 0
|
|
129
|
-
? [{ name, references: refs }]
|
|
130
|
-
: [];
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
function unresolvedLine(u) {
|
|
134
|
-
const which = u.references.length === 1 ? "which has" : "which have";
|
|
135
|
-
return `${u.name} references ${u.references.join(", ")}, ${which} no value; the checkout refuses it before work starts`;
|
|
136
|
-
}
|
|
137
|
-
/** A team check's fix line. A check that carries `names` (CTC-3561: `required_values`, contract
|
|
138
|
-
* 1.24.0) names them and where to set them. CTC-3606: it also names each `unresolved` variable and
|
|
139
|
-
* the reference with no value, and each other repository's missing names from `repos[]`. `unresolved`
|
|
140
|
-
* is a subset of `names` (the variable exists; its reference does not), so those are not told to be
|
|
141
|
-
* "set". Every field is optional so an older cloud still works. Everything printed is a declared
|
|
142
|
-
* identifier or a repository name, never a value, and nothing here reads a value. Any other check
|
|
143
|
-
* keeps the generic line. */
|
|
144
|
-
function teamCheckFix(label, c) {
|
|
145
|
-
const automationRules = {
|
|
146
|
-
linear_automation_pr_open: "On PR open",
|
|
147
|
-
linear_automation_pr_review: "On PR review request or activity",
|
|
148
|
-
linear_automation_pr_ready: "On PR ready for merge",
|
|
149
|
-
linear_automation_pr_merge: "On PR merge",
|
|
150
|
-
};
|
|
151
|
-
const rule = automationRules[c.id];
|
|
152
|
-
if (rule) {
|
|
153
|
-
return `in Linear, open Settings → Teams → ${label} → Workflow → Workflows & automations → Pull request and commit automations and set ${rule} to No action, including branch-specific overrides; then run catalyst team check ${label}`;
|
|
154
|
-
}
|
|
155
|
-
const unresolved = unresolvedList(c.unresolved);
|
|
156
|
-
const unresolvedNames = new Set(unresolved.map((u) => u.name));
|
|
157
|
-
const names = nameList(c.names).filter((n) => !unresolvedNames.has(n));
|
|
158
|
-
const parts = [];
|
|
159
|
-
if (names.length > 0) {
|
|
160
|
-
parts.push(`set ${names.join(", ")} on the repository's Environment page under Settings → Your projects → the project → Repositories → the repository ` +
|
|
161
|
-
`(team ${label}; ${names.length === 1 ? "it has" : "they have"} no value at repository or account scope)`);
|
|
162
|
-
}
|
|
163
|
-
for (const u of unresolved)
|
|
164
|
-
parts.push(unresolvedLine(u));
|
|
165
|
-
for (const note of Array.isArray(c.repos) ? c.repos : []) {
|
|
166
|
-
if (typeof note !== "object" ||
|
|
167
|
-
note === null ||
|
|
168
|
-
typeof note.repo !== "string" ||
|
|
169
|
-
note.repo.length === 0)
|
|
170
|
-
continue;
|
|
171
|
-
const repoUnresolved = unresolvedList(note.unresolved);
|
|
172
|
-
const skip = new Set(repoUnresolved.map((u) => u.name));
|
|
173
|
-
const missing = nameList(note.names).filter((n) => !skip.has(n));
|
|
174
|
-
if (missing.length > 0)
|
|
175
|
-
parts.push(`${note.repo} is missing ${missing.join(", ")}`);
|
|
176
|
-
for (const u of repoUnresolved)
|
|
177
|
-
parts.push(`in ${note.repo}, ${unresolvedLine(u)}`);
|
|
178
|
-
}
|
|
179
|
-
if (parts.length === 0)
|
|
180
|
-
return `open settings for team ${label} and resolve ${c.id}`;
|
|
181
|
-
return parts.join(". ");
|
|
182
|
-
}
|
|
183
|
-
function whoCanAnswer(doc) {
|
|
184
|
-
const roles = doc.humans.map((h) => `${h.role} ${h.linearUserId}`);
|
|
185
|
-
return roles.length
|
|
186
|
-
? roles.join(", ")
|
|
187
|
-
: "a tenant owner or admin (none resolved on the contract)";
|
|
188
|
-
}
|
|
189
115
|
export async function readyReport(ctx, deps) {
|
|
190
116
|
const checks = [];
|
|
191
117
|
const facts = deps.runtime ?? detectRuntime();
|
|
@@ -265,7 +191,7 @@ export async function readyReport(ctx, deps) {
|
|
|
265
191
|
id: "bundle",
|
|
266
192
|
ok: false,
|
|
267
193
|
note: true,
|
|
268
|
-
line: `bundle: ${installed} installed is older than
|
|
194
|
+
line: `bundle: ${installed} installed is older than your workspace's minimum ${minVersion} — upgrade: ${upgradeCommand()}`,
|
|
269
195
|
});
|
|
270
196
|
}
|
|
271
197
|
}
|
|
@@ -394,7 +320,7 @@ export async function readyReport(ctx, deps) {
|
|
|
394
320
|
});
|
|
395
321
|
if (cache) {
|
|
396
322
|
const doc = cache.doc;
|
|
397
|
-
const
|
|
323
|
+
const base = (cfg?.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
398
324
|
for (const team of doc.teams) {
|
|
399
325
|
const label = team.key ?? team.id;
|
|
400
326
|
// The team's dispatch gate, straight off the cached contract: an older cloud omits it and this
|
|
@@ -404,28 +330,36 @@ export async function readyReport(ctx, deps) {
|
|
|
404
330
|
// checked still reports its gate, which is exactly the team most likely to be unmapped.
|
|
405
331
|
const dg = team.dispatchGate;
|
|
406
332
|
if (dg && typeof dg.status === "string") {
|
|
407
|
-
|
|
408
|
-
|
|
333
|
+
// CTC-4680: the cloud's own remedy text names an API route and says "tenant", so the words
|
|
334
|
+
// come from ready-copy; the status and slots stay readable in the check id's JSON neighbours.
|
|
335
|
+
const copy = dispatchGateCopy(base, label, dg.status, dg.missingSlots ?? []);
|
|
336
|
+
checks.push(dg.status === "stages_syncing"
|
|
409
337
|
? {
|
|
410
|
-
id: `team:${label}:dispatchGate`,
|
|
411
|
-
ok: true,
|
|
412
|
-
line: `team ${label}: dispatch gate open`,
|
|
413
|
-
}
|
|
414
|
-
: {
|
|
415
338
|
id: `team:${label}:dispatchGate`,
|
|
416
339
|
ok: false,
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
340
|
+
note: true,
|
|
341
|
+
line: copy.line,
|
|
342
|
+
}
|
|
343
|
+
: dg.status === "open"
|
|
344
|
+
? {
|
|
345
|
+
id: `team:${label}:dispatchGate`,
|
|
346
|
+
ok: true,
|
|
347
|
+
line: copy.line,
|
|
348
|
+
}
|
|
349
|
+
: {
|
|
350
|
+
id: `team:${label}:dispatchGate`,
|
|
351
|
+
ok: false,
|
|
352
|
+
line: copy.line,
|
|
353
|
+
...(copy.fix ? { fix: copy.fix } : {}),
|
|
354
|
+
who: whoFixes("mapping_total"),
|
|
355
|
+
});
|
|
422
356
|
}
|
|
423
357
|
if (team.readiness.status === "unchecked") {
|
|
424
358
|
checks.push({
|
|
425
359
|
id: `team:${label}`,
|
|
426
360
|
ok: true,
|
|
427
361
|
note: true,
|
|
428
|
-
line: `team ${label}:
|
|
362
|
+
line: `team ${label}: Catalyst hasn't checked this team yet`,
|
|
429
363
|
});
|
|
430
364
|
continue;
|
|
431
365
|
}
|
|
@@ -441,13 +375,14 @@ export async function readyReport(ctx, deps) {
|
|
|
441
375
|
for (const c of bad) {
|
|
442
376
|
const meta = doc.readinessChecks.find((r) => r.id === c.id);
|
|
443
377
|
const needsAnswer = meta?.needsAnswer ?? true;
|
|
378
|
+
const copy = teamCheckCopy(base, label, c.id, c.state === "fail" ? "fail" : "unknown", valuesFix(base, c));
|
|
444
379
|
checks.push({
|
|
445
380
|
id: `team:${label}:${c.id}`,
|
|
446
381
|
ok: !needsAnswer && c.state !== "fail",
|
|
447
382
|
note: !needsAnswer,
|
|
448
|
-
line:
|
|
449
|
-
fix:
|
|
450
|
-
who: needsAnswer ?
|
|
383
|
+
line: copy.line,
|
|
384
|
+
...(copy.fix ? { fix: copy.fix } : {}),
|
|
385
|
+
who: needsAnswer ? whoFixes(c.id) : "nobody yet; it is informational",
|
|
451
386
|
});
|
|
452
387
|
}
|
|
453
388
|
if (bad.length === 0 && team.readiness.status !== "ready") {
|
|
@@ -455,9 +390,11 @@ export async function readyReport(ctx, deps) {
|
|
|
455
390
|
id: `team:${label}`,
|
|
456
391
|
ok: team.readiness.status !== "blocked",
|
|
457
392
|
note: team.readiness.status === "degraded",
|
|
458
|
-
line:
|
|
459
|
-
|
|
460
|
-
|
|
393
|
+
line: team.readiness.status === "degraded"
|
|
394
|
+
? `team ${label}: works, with some optional settings still missing`
|
|
395
|
+
: `team ${label}: not ready for work yet`,
|
|
396
|
+
fix: `open ${teamPage(base, label)}`,
|
|
397
|
+
who: whoFixes("mapping_total"),
|
|
461
398
|
});
|
|
462
399
|
}
|
|
463
400
|
}
|