@catalyst-cloud/cli 0.14.5 → 0.14.7

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 (63) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/README.md +5 -3
  3. package/dist/args.js +51 -13
  4. package/dist/cli.js +58 -10
  5. package/dist/config.js +16 -3
  6. package/dist/daemon-authority-lifetime.js +171 -0
  7. package/dist/daemon-authority.js +216 -0
  8. package/dist/daemon-cache.js +348 -0
  9. package/dist/daemon-store.js +370 -0
  10. package/dist/daemon-supervisor.js +460 -0
  11. package/dist/env/onboard-draft-store.js +243 -0
  12. package/dist/env/onboard-draft.js +33 -0
  13. package/dist/env/selected-import-writer.js +163 -0
  14. package/dist/env/settings-authority.js +203 -0
  15. package/dist/events-cloud-tail.js +134 -0
  16. package/dist/events-cloud.js +500 -0
  17. package/dist/events.js +13 -4
  18. package/dist/legacy.js +145 -32
  19. package/dist/oauth.js +125 -33
  20. package/dist/onboard-accounts.js +259 -0
  21. package/dist/onboard-automation-management.js +15 -0
  22. package/dist/onboard-bootstrap.js +162 -0
  23. package/dist/onboard-capabilities.js +15 -7
  24. package/dist/onboard-capacity.js +226 -0
  25. package/dist/onboard-existing.js +106 -32
  26. package/dist/onboard-file-snapshot.js +45 -0
  27. package/dist/onboard-login-candidate.js +261 -0
  28. package/dist/onboard-projects.js +350 -0
  29. package/dist/onboard-repositories.js +147 -37
  30. package/dist/onboard-runtime.js +104 -9
  31. package/dist/onboard-settings-context.js +141 -0
  32. package/dist/onboard-settings-import.js +127 -0
  33. package/dist/onboard-settings.js +234 -0
  34. package/dist/onboard-ui.js +99 -11
  35. package/dist/onboard-workflow.js +316 -0
  36. package/dist/onboard.js +359 -27
  37. package/dist/owned-fetch.js +182 -0
  38. package/dist/query.js +77 -27
  39. package/dist/ready.js +143 -35
  40. package/dist/replica.js +11 -4
  41. package/dist/sdk.js +33 -0
  42. package/dist/watch.js +2 -2
  43. package/package.json +3 -2
  44. package/skills/catalyst-github/SKILL.md +2 -2
  45. package/skills/catalyst-github/references/is-it-mergeable.md +1 -1
  46. package/skills/catalyst-github/references/what-a-pr-accumulates.md +1 -1
  47. package/skills/catalyst-github/scripts/is-it-mergeable.mjs +210 -45
  48. package/skills/catalyst-github/scripts/read-pr.mjs +51 -15
  49. package/skills/catalyst-linear/SKILL.md +3 -3
  50. package/skills/catalyst-linear/references/reading-a-ticket.md +3 -3
  51. package/skills/catalyst-linear/scripts/read-ticket.mjs +37 -12
  52. package/skills/catalyst-onboard/SKILL.md +5 -4
  53. package/skills/catalyst-onboard/references/local-sync.md +3 -1
  54. package/skills/run-this-project/SKILL.md +1 -1
  55. package/skills/unstick/SKILL.md +1 -1
  56. package/skills/what-needs-me/SKILL.md +1 -1
  57. package/skills/what-this-repo-needs/SKILL.md +1 -1
  58. package/skills/whats-happening/SKILL.md +2 -2
  59. package/skills/whats-happening/references/status-reply.md +1 -1
  60. package/skills/whats-happening/scripts/snapshot.mjs +104 -26
  61. package/vendor/settings/index.d.ts +4 -0
  62. package/vendor/settings/index.js +2937 -0
  63. package/vendor/settings/provenance.json +148 -0
@@ -4,7 +4,7 @@ description: >-
4
4
  The human's decision inbox on Catalyst Cloud, and the one way an agent raises a decision on their behalf. Use when the person asks "what needs me?", "what am I blocking?", or when active work is gated on a choice only they can make. Lists open asks ranked by how much open work each one holds, files an ask through the cloud's ask route with the account's own template, and records the answer so the held work releases. Never answers as the human, never duplicates an open ask.
5
5
  allowed-tools: Bash(catalyst:*) Bash(npx -p @catalyst-cloud/cli catalyst:*)
6
6
  ---
7
- <!-- vendored-from: @catalyst-cloud/catalyst-skills@0.14.5 — written in this repository for customer accounts -->
7
+ <!-- vendored-from: @catalyst-cloud/catalyst-skills@0.14.7 — written in this repository for customer accounts -->
8
8
 
9
9
  # What needs me
10
10
 
@@ -4,7 +4,7 @@ description: >-
4
4
  What environment variable names does this repository need, and where does each one come from? Scans the repository offline (no login, no network) and lists the names in three groups (build/test, deploy-only, bindings), each with where it was found, what uses it, and where a local value would come from. Never reads a value or prints one. Also checks the environment variable table in `.catalyst/catalyst.toml` offline. Use when someone asks "what does catalyst.env.json mean", "what env vars does this repo need", "why does the container need this", or before reviewing or writing the declaration.
5
5
  allowed-tools: Bash(catalyst:*) Bash(npx -p @catalyst-cloud/cli catalyst:*)
6
6
  ---
7
- <!-- vendored-from: @catalyst-cloud/catalyst-skills@0.14.5 — written in this repository for customer accounts -->
7
+ <!-- vendored-from: @catalyst-cloud/catalyst-skills@0.14.7 — written in this repository for customer accounts -->
8
8
 
9
9
  # What this repository needs
10
10
 
@@ -4,13 +4,13 @@ description: >-
4
4
  Use for "what's happening?", "where are we?", "why is that stuck?", "what closed?", "what's next?", "how does this work?", "why did it do that?", "how does it prioritise?" and "what does this setting do, where is it set?" in Catalyst Cloud, or asks for something to be done rather than known. Reads the contract via catalyst CLI. Read-only: route work/decisions, never answer as the human.
5
5
  allowed-tools: Bash(catalyst:*) Bash(npx -p @catalyst-cloud/cli catalyst:*)
6
6
  ---
7
- <!-- vendored-from: @catalyst-cloud/catalyst-skills@0.14.5 — written in this repository for customer accounts -->
7
+ <!-- vendored-from: @catalyst-cloud/catalyst-skills@0.14.7 — written in this repository for customer accounts -->
8
8
 
9
9
  # What's happening
10
10
 
11
11
  Use one snapshot and the person's login. Run scripts, never read them. --help explains usage; exit 2 needs onboard, exit 1 means failure.
12
12
 
13
- - `node scripts/snapshot.mjs` prints contract, running work, queue, ranked asks, replica verdict. --board adds stages, --accounts coding accounts, --team K narrows.
13
+ - `node scripts/snapshot.mjs` reads contract, running work, queue and ranked asks from the cloud. --board adds stages, --accounts coding accounts, --team K narrows. --replica adds optional local diagnostics.
14
14
  - `node scripts/explain.mjs <ticket>` explains eligibility; --history adds attempts, rounds, holds and releases.
15
15
  - `node scripts/show-my-map.mjs [--team K]` prints stages, labels, ladder and thresholds.
16
16
 
@@ -1,6 +1,6 @@
1
1
  # Status
2
2
 
3
- Open with takenAt, source and replica cursor/API reason. Then:
3
+ Open with takenAt and the cloud source. Include a replica cursor only when optional local diagnostics were requested. Then:
4
4
 
5
5
  1. In flight: ticket, phase and elapsed time from running.
6
6
  2. Blocked: reason and release actor.
@@ -2,25 +2,48 @@
2
2
  // snapshot.mjs — one call that answers "where are we?": the account's contract (trimmed to what a
3
3
  // status reply needs), what is running, what is queued, what is waiting on a human, and whether the
4
4
  // local replica is fresh. Prints ONE JSON document on stdout and the source line on stderr.
5
- import { mustRun, parseFlags, parseJson, printHelp, runCli } from "./lib/cli.mjs";
5
+ import {
6
+ mustRun,
7
+ parseFlags,
8
+ parseJson,
9
+ printHelp,
10
+ runCli,
11
+ } from "./lib/cli.mjs";
6
12
 
7
13
  const SPEC = {
8
14
  team: { value: true, help: "limit the queue and the board to this team key" },
9
- board: { value: false, help: "also include open tickets grouped by stage, in the contract's slot order" },
15
+ board: {
16
+ value: false,
17
+ help: "also include open tickets grouped by stage, in the contract's slot order",
18
+ },
10
19
  limit: { value: true, help: "board: max tickets to read (default 200)" },
11
- "full-contract": { value: false, help: "include the whole contract instead of the trimmed `tenant` block" },
12
- accounts: { value: false, help: "also include the coding accounts: provider, state, usage windows, walls, quarantine (never a credential)" },
20
+ "full-contract": {
21
+ value: false,
22
+ help: "include the whole contract instead of the trimmed `tenant` block",
23
+ },
24
+ accounts: {
25
+ value: false,
26
+ help: "also include the coding accounts: provider, state, usage windows, walls, quarantine (never a credential)",
27
+ },
28
+ replica: {
29
+ value: false,
30
+ help: "also read optional local replica diagnostics",
31
+ },
13
32
  };
14
33
 
15
34
  const { help, flags } = parseFlags(process.argv.slice(2), SPEC);
16
35
  if (help) {
17
- printHelp("node scripts/snapshot.mjs [--team K] [--board] [--accounts] [--limit N] [--full-contract] [--help]", SPEC, [
18
- "Runs, in order: catalyst contract, running, queue, ask list, replica status (query issues with --board,",
19
- "accounts with --accounts).",
20
- "Output: one JSON document {takenAt, source, tenant, running, queue, waitingOnHuman, board?, accounts?, errors?}.",
21
- "A section the cloud refused is reported under errors and the rest still prints; exit 1 in that case.",
22
- "Every account fact (stage names, thresholds, teams) comes from the contract in this output, never from prose.",
23
- ]);
36
+ printHelp(
37
+ "node scripts/snapshot.mjs [--team K] [--board] [--accounts] [--limit N] [--full-contract] [--help]",
38
+ SPEC,
39
+ [
40
+ "Reads catalyst contract, running, queue and ask list from the cloud (query issues with --board,",
41
+ "accounts with --accounts, optional local diagnostics with --replica).",
42
+ "Output: one JSON document {takenAt, source, tenant, running, queue, waitingOnHuman, board?, accounts?, errors?}.",
43
+ "A section the cloud refused is reported under errors and the rest still prints; exit 1 in that case.",
44
+ "Every account fact (stage names, thresholds, teams) comes from the contract in this output, never from prose.",
45
+ ],
46
+ );
24
47
  process.exit(0);
25
48
  }
26
49
 
@@ -28,26 +51,40 @@ const errors = {};
28
51
  const section = (name, args) => {
29
52
  const r = runCli(args);
30
53
  if (r.code !== 0) {
31
- errors[name] = (r.stderr || r.stdout || `exit ${r.code}`).trim().split("\n").at(-1);
54
+ errors[name] = (r.stderr || r.stdout || `exit ${r.code}`)
55
+ .trim()
56
+ .split("\n")
57
+ .at(-1);
32
58
  return null;
33
59
  }
34
60
  return parseJson(r.stdout, name);
35
61
  };
36
62
 
37
- const contract = parseJson(mustRun(["contract", "--json"], { quiet: true }).stdout, "contract");
63
+ const contract = parseJson(
64
+ mustRun(["contract", "--json"], { quiet: true }).stdout,
65
+ "contract",
66
+ );
38
67
  // `replica status` exits 0 fresh, 1 stale, 3 absent — all three are verdicts with JSON on stdout,
39
68
  // not failures; only a non-JSON answer is an error.
40
69
  const replica = (() => {
70
+ if (!flags.replica)
71
+ return { verdict: "not-selected", cursor: null, heartbeatAgeMs: null };
41
72
  const r = runCli(["replica", "status", "--json"]);
42
73
  try {
43
74
  return JSON.parse(r.stdout.trim());
44
75
  } catch {
45
- errors.replica = (r.stderr || r.stdout || `exit ${r.code}`).trim().split("\n").at(-1);
76
+ errors.replica = (r.stderr || r.stdout || `exit ${r.code}`)
77
+ .trim()
78
+ .split("\n")
79
+ .at(-1);
46
80
  return { verdict: "unknown", cursor: null, heartbeatAgeMs: null };
47
81
  }
48
82
  })();
49
83
  const running = section("running", ["running", "--json"]);
50
- const queue = section("queue", flags.team ? ["queue", "--team", flags.team, "--json"] : ["queue", "--json"]);
84
+ const queue = section(
85
+ "queue",
86
+ flags.team ? ["queue", "--team", flags.team, "--json"] : ["queue", "--json"],
87
+ );
51
88
  const asks = section("waitingOnHuman", ["ask", "list", "--json"]);
52
89
 
53
90
  const out = {
@@ -58,7 +95,9 @@ const out = {
58
95
  replicaCursor: replica.cursor ?? null,
59
96
  replicaHeartbeatAgeMs: replica.heartbeatAgeMs ?? null,
60
97
  },
61
- tenant: flags["full-contract"] ? contract : trimContract(contract, flags.team),
98
+ tenant: flags["full-contract"]
99
+ ? contract
100
+ : trimContract(contract, flags.team),
62
101
  running,
63
102
  queue,
64
103
  waitingOnHuman: asks,
@@ -67,14 +106,23 @@ const out = {
67
106
  if (flags.accounts) out.accounts = section("accounts", ["accounts", "--json"]);
68
107
 
69
108
  if (flags.board) {
70
- const args = ["query", "issues", "--limit", String(flags.limit ?? 200), "--json"];
109
+ const args = [
110
+ "query",
111
+ "issues",
112
+ "--source",
113
+ "api",
114
+ "--limit",
115
+ String(flags.limit ?? 200),
116
+ "--json",
117
+ ];
71
118
  if (flags.team) args.push("--team", flags.team);
72
119
  const r = runCli(args);
73
120
  const sourceLine = (r.stderr.match(/^source: .*$/m) ?? [null])[0];
74
121
  // A board read is a window (`--limit` stays the default), and a window the cloud cut short must
75
122
  // say so rather than reading as the whole team.
76
123
  const truncatedLine = (r.stderr.match(/^truncated at .*$/m) ?? [null])[0];
77
- if (r.code !== 0) errors.board = (r.stderr || `exit ${r.code}`).trim().split("\n").at(-1);
124
+ if (r.code !== 0)
125
+ errors.board = (r.stderr || `exit ${r.code}`).trim().split("\n").at(-1);
78
126
  else {
79
127
  const rows = parseJson(r.stdout, "issues");
80
128
  out.board = groupByStage(rows, contract, flags.team);
@@ -84,18 +132,27 @@ if (flags.board) {
84
132
  }
85
133
 
86
134
  if (Object.keys(errors).length) out.errors = errors;
87
- process.stderr.write(`source: api for running/queue/asks; replica ${replica.verdict}${replica.cursor != null ? ` (cursor ${replica.cursor})` : ""}${out.source.board ? `; board from ${out.source.board}` : ""}\n`);
135
+ process.stderr.write(
136
+ `source: api for running/queue/asks; replica ${replica.verdict}${replica.cursor != null ? ` (cursor ${replica.cursor})` : ""}${out.source.board ? `; board from ${out.source.board}` : ""}\n`,
137
+ );
88
138
  process.stdout.write(JSON.stringify(out, null, 2) + "\n");
89
139
  process.exit(Object.keys(errors).length ? 1 : 0);
90
140
 
91
141
  /** The contract facts a status reply needs, and nothing a reply should not paste. */
92
142
  function trimContract(doc, teamKey) {
93
- const teams = (doc.teams ?? []).filter((t) => !teamKey || String(t.key ?? "").toUpperCase() === teamKey.toUpperCase());
143
+ const teams = (doc.teams ?? []).filter(
144
+ (t) =>
145
+ !teamKey || String(t.key ?? "").toUpperCase() === teamKey.toUpperCase(),
146
+ );
94
147
  return {
95
148
  contractVersion: doc.contractVersion,
96
149
  account: { slug: doc.account?.slug, name: doc.account?.name },
97
150
  slots: doc.slots,
98
- ladder: { phases: doc.ladder?.phases, intakeEnabled: doc.ladder?.intakeEnabled, keying: doc.ladder?.keying },
151
+ ladder: {
152
+ phases: doc.ladder?.phases,
153
+ intakeEnabled: doc.ladder?.intakeEnabled,
154
+ keying: doc.ladder?.keying,
155
+ },
99
156
  thresholds: doc.thresholds,
100
157
  mergeDefaultPolicy: doc.merge?.defaultPolicy,
101
158
  teams: teams.map((t) => ({
@@ -106,7 +163,14 @@ function trimContract(doc, teamKey) {
106
163
  stages: Object.fromEntries(
107
164
  (doc.slots ?? Object.keys(t.stages ?? {}))
108
165
  .filter((slot) => t.stages?.[slot])
109
- .map((slot) => [slot, { name: t.stages[slot].name, type: t.stages[slot].type, stateStillExists: t.stages[slot].stateStillExists }]),
166
+ .map((slot) => [
167
+ slot,
168
+ {
169
+ name: t.stages[slot].name,
170
+ type: t.stages[slot].type,
171
+ stateStillExists: t.stages[slot].stateStillExists,
172
+ },
173
+ ]),
110
174
  ),
111
175
  labels: {
112
176
  ask: (t.labels?.ask ?? []).map((l) => l.name),
@@ -124,11 +188,19 @@ function groupByStage(rows, doc, teamKey) {
124
188
  const seen = new Set();
125
189
  const terminal = new Set();
126
190
  for (const t of doc.teams ?? []) {
127
- if (teamKey && String(t.key ?? "").toUpperCase() !== teamKey.toUpperCase()) continue;
191
+ if (teamKey && String(t.key ?? "").toUpperCase() !== teamKey.toUpperCase())
192
+ continue;
128
193
  for (const slot of doc.slots ?? []) {
129
194
  const s = t.stages?.[slot];
130
195
  if (!s?.name) continue;
131
- if (slot === "done" || slot === "canceled" || ["completed", "canceled", "cancelled"].includes(String(s.type ?? "").toLowerCase())) terminal.add(s.name);
196
+ if (
197
+ slot === "done" ||
198
+ slot === "canceled" ||
199
+ ["completed", "canceled", "cancelled"].includes(
200
+ String(s.type ?? "").toLowerCase(),
201
+ )
202
+ )
203
+ terminal.add(s.name);
132
204
  else if (!seen.has(s.name)) {
133
205
  seen.add(s.name);
134
206
  order.push(s.name);
@@ -148,6 +220,12 @@ function groupByStage(rows, doc, teamKey) {
148
220
  projectId: r.project_id ?? null,
149
221
  });
150
222
  }
151
- const extra = Object.keys(byStage).filter((s) => !seen.has(s)).sort();
152
- return { stageOrder: [...order, ...extra], byStage, terminalStagesDropped: [...terminal] };
223
+ const extra = Object.keys(byStage)
224
+ .filter((s) => !seen.has(s))
225
+ .sort();
226
+ return {
227
+ stageOrder: [...order, ...extra],
228
+ byStage,
229
+ terminalStagesDropped: [...terminal],
230
+ };
153
231
  }
@@ -0,0 +1,4 @@
1
+ export type FullSettingsValidation =
2
+ | { ok: false; errors: string[] }
3
+ | { ok: true; errors: []; linearTeam: string; variableNames: string[]; secretNames: string[] };
4
+ export declare function validateFullSettings(text: string): FullSettingsValidation;