@catalyst-cloud/cli 0.13.1 → 0.14.1

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 (44) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/README.md +2 -2
  3. package/dist/args.js +291 -69
  4. package/dist/capabilities.js +2 -0
  5. package/dist/cli.js +109 -17
  6. package/dist/config.js +3 -1
  7. package/dist/env/draft.js +134 -0
  8. package/dist/env/legacy.js +54 -0
  9. package/dist/env/parse-env.js +50 -0
  10. package/dist/env/settings-toml.js +66 -0
  11. package/dist/env.js +126 -26
  12. package/dist/legacy.js +16 -17
  13. package/dist/onboard-consent.js +65 -0
  14. package/dist/onboard.js +836 -0
  15. package/dist/project.js +75 -13
  16. package/dist/ready.js +11 -1
  17. package/dist/secret.js +24 -3
  18. package/dist/var.js +118 -0
  19. package/package.json +3 -2
  20. package/skills/catalyst-github/SKILL.md +1 -1
  21. package/skills/catalyst-linear/SKILL.md +1 -1
  22. package/skills/catalyst-onboard/SKILL.md +1 -1
  23. package/skills/catalyst-onboard/references/declaring-a-repository.md +19 -12
  24. package/skills/catalyst-onboard/references/reading-ready.md +1 -1
  25. package/skills/catalyst-onboard/references/the-one-path.md +4 -4
  26. package/skills/catalyst-onboard/references/what-the-browser-owns.md +2 -2
  27. package/skills/catalyst-onboard/scripts/lib/project-coverage.mjs +53 -0
  28. package/skills/catalyst-onboard/scripts/where-am-i.mjs +885 -196
  29. package/skills/run-this-project/SKILL.md +1 -1
  30. package/skills/unstick/SKILL.md +1 -1
  31. package/skills/what-needs-me/SKILL.md +1 -1
  32. package/skills/what-this-repo-needs/SKILL.md +4 -4
  33. package/skills/what-this-repo-needs/scripts/check.mjs +12 -13
  34. package/skills/whats-happening/SKILL.md +19 -35
  35. package/skills/whats-happening/references/coding-accounts.md +6 -19
  36. package/skills/whats-happening/references/routing-work.md +4 -15
  37. package/skills/whats-happening/references/settings-and-where-they-live.md +15 -18
  38. package/skills/whats-happening/references/stages-and-mapping.md +12 -18
  39. package/skills/whats-happening/references/status-reply.md +11 -29
  40. package/skills/whats-happening/references/the-ladder.md +15 -25
  41. package/skills/whats-happening/references/what-runs-next.md +7 -26
  42. package/skills/whats-happening/references/when-a-phase-fails.md +16 -22
  43. package/skills/whats-happening/references/why-is-it-stuck.md +38 -48
  44. package/dist/env/declaration-rules.js +0 -163
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.14.1
4
+
5
+ Legacy cleanup keeps all shared Catalyst config and data folders, including with the historical --data flag. It no longer removes ~/.catalyst/bin wholesale or recursively deletes directories. The onboarding skill keeps data and no longer offers data deletion.
6
+
7
+ ## 0.14.0
8
+
9
+ The `catalyst onboard` command starts a resumable setup journal, migrates the installer receipt, and safely coordinates with the install script through an owner-token lock. It supports JSON plans, dry runs, step selection and resume validation. The first executable slice checks legacy local runtime files while preserving user data; provider and project steps remain staged for later releases.
10
+
3
11
  ## 0.13.1
4
12
 
5
13
  The Cloud pack now has eight skills. catalyst-onboard includes connection and setup, and whats-happening includes the guide to how Catalyst works. The guides use the catalyst command and call a tenant your cloud account. They explain asks, holds and the 48-hour fallback from the running code. Install and refresh remove retired Cloud-pack skills that carry the pack's provenance.
package/README.md CHANGED
@@ -159,13 +159,13 @@ The replica is a Node process, not a service: the supported path is the plain co
159
159
  | `catalyst-linear` | "Show me the ticket, the history, what Catalyst wrote on it." | Reads a ticket with its comments, relations, labels, linked pull requests and agent sessions inline, from the replica when fresh and the API otherwise, always naming the source; writes comments, card moves, labels and new tickets as the app actor; knows what a ticket accumulates as Catalyst works it. | [`SKILL.md`](skills/catalyst-linear/SKILL.md) |
160
160
  | `catalyst-github` | "Show me the PR, the checks, the review, the queue." | A ticket's pull request with its checks, reviews and review threads; whether it is mergeable under the repository's policy; what a PR accumulates as the ticket moves (the branch, the draft, the rewrite, the force-pushes, the labels, the queue). | [`SKILL.md`](skills/catalyst-github/SKILL.md) |
161
161
  | `unstick` | "Why is this parked? Unpark it. Get things flowing again." | Reads why a ticket is not running and every park or hold on it, decides whether the recorded cause is fixed, previews the release and releases it the right way from your own login — or one failure class across a team — and raises an ask only for what a person has to do. | [`SKILL.md`](skills/unstick/SKILL.md) |
162
- | `what-this-repo-needs` | "What env vars does this repo need? What does catalyst.env.json mean?" | Scans a repository offline — no login, no network — and lists the environment variable names it needs, grouped build/test, deploy-only and bindings, each with where it was found, what uses it, and where a local value would come from. Never reads or prints a value. Also validates a `catalyst.env.json` file with the same rules the cloud applies. | [`SKILL.md`](skills/what-this-repo-needs/SKILL.md) |
162
+ | `what-this-repo-needs` | "What env vars does this repo need? What belongs in its environment declaration?" | Scans a repository offline — no login, no network — and lists the environment variable names it needs, grouped build/test, deploy-only and bindings, each with where it was found, what uses it, and where a local value would come from. Never reads or prints a value. Also checks TOML syntax and the environment variable table in `.catalyst/catalyst.toml`. | [`SKILL.md`](skills/what-this-repo-needs/SKILL.md) |
163
163
 
164
164
  Some of these only read, and some write: a comment, a card move, an ask, a release. Your agent may pick any of them from what you ask. Picking a skill is not permission to write: each write goes through your own login, spends the daily write budget your account sets, and is recorded against your name, and the skills preview first where the product offers a preview (a release's dry run, a stage mapping's plan). Each skill's `agents/portability.yaml` says whether it writes. Every skill declares `allowed-tools` scoped to this package's own binary, so none of them needs a blanket shell grant.
165
165
 
166
166
  ## What a key cannot see yet
167
167
 
168
- Your personal key reads everything the skills need — tickets, pull requests, the eligibility explainer, the dispatch queue, fleet activity, per-ticket execution history (`catalyst explain --history <ticket>`: phase attempts, remediation rounds, park state) and coding-account status (`catalyst accounts`: provider, declared and observed state, window usage, walls, quarantine — never a credential; enrolling or pausing one is `<your cloud>/settings/coding-accounts`). It also releases a parked or held ticket once its cause is fixed: `catalyst release <ticket> --because <what changed>` (the `unstick` skill runs it), recorded against your name and shown in the ticket's history. And it declares what your containers need: `catalyst environment` reads the account-wide declaration, `environment propose --file <path> --approve` proposes and approves it in one compare-and-set, and the values behind the names stay in the cloud — reading needs any active seat, proposing and approving need an admin or owner one. An admin or owner can put a repository's values in from the terminal. `catalyst secret import .env --repo owner/name` stores every name in the file and lists the declared names that still have no value. `catalyst secret set NAME --repo owner/name --command 'op read op://Vault/item/field'` runs the command on your machine and stores its output; with no `--command` it reads the value from stdin, or asks for it without echoing. No value is ever printed, and the cloud's audit records the command, not its output. That is the account's own declaration — a repository's own `.catalyst/catalyst.toml`, which `catalyst-onboard` already reports on, is a separate thing. `catalyst env inventory` lists the environment variable names a repository needs — never a value — and `env check` validates a `catalyst.env.json` file offline; neither needs a login (the `what-this-repo-needs` skill walks through both). Two things it cannot do, and the skills say so by name rather than guess:
168
+ Your personal key reads everything the skills need — tickets, pull requests, the eligibility explainer, the dispatch queue, fleet activity, per-ticket execution history (`catalyst explain --history <ticket>`: phase attempts, remediation rounds, park state) and coding-account status (`catalyst accounts`: provider, declared and observed state, window usage, walls, quarantine — never a credential; enrolling or pausing one is `<your cloud>/settings/coding-accounts`). It also releases a parked or held ticket once its cause is fixed: `catalyst release <ticket> --because <what changed>` (the `unstick` skill runs it), recorded against your name and shown in the ticket's history. And it declares what your containers need: `catalyst environment` reads the account-wide declaration, `environment propose --file <path> --approve` proposes and approves it in one compare-and-set, and the values behind the names stay in the cloud — reading needs any active seat, proposing and approving need an admin or owner one. An admin or owner can put a repository's values in from the terminal. `catalyst secret import .env --repo owner/name` stores every name in the file and lists the declared names that still have no value. `catalyst secret set NAME --repo owner/name --command 'op read op://Vault/item/field'` runs the command on your machine and stores its output; with no `--command` it reads the value from stdin, or asks for it without echoing. No value is ever printed, and the cloud's audit records the command, not its output. That is the account's own declaration — a repository's own `.catalyst/catalyst.toml`, which `catalyst-onboard` already reports on, is a separate thing. `catalyst env inventory` lists the environment variable names a repository needs — never a value; `env check` checks the TOML syntax and environment variable table in `.catalyst/catalyst.toml` offline; `env migrate [catalyst.env.json]` prints a TOML environment table from a legacy declaration, with all names optional and values omitted. These commands need no login (the `what-this-repo-needs` skill walks through them). Two things it cannot do, and the skills say so by name rather than guess:
169
169
 
170
170
  - Read pull-request labels or the reviewer's reaction. The mirror does not carry them; GitHub's own page does.
171
171
  - Compute a flow number — cycle time, throughput, or how long pull requests have been open. Nothing serves those yet, so say they are not computed rather than counting something else and calling it that.
package/dist/args.js CHANGED
@@ -4,23 +4,48 @@
4
4
  import { UsageError } from "./errors.js";
5
5
  const GLOBAL_FLAGS = {
6
6
  key: { value: true, help: "your personal key (or CATALYST_CLOUD_TOKEN)" },
7
- "base-url": { value: true, help: "Catalyst Cloud origin (or CATALYST_CLOUD_BASE_URL)" },
8
- "skills-dir": { value: true, help: "where the skills are copied (default ~/.claude/skills)" },
9
- force: { value: false, help: "replace a skill directory this package did not install" },
7
+ "base-url": {
8
+ value: true,
9
+ help: "Catalyst Cloud origin (or CATALYST_CLOUD_BASE_URL)",
10
+ },
11
+ "skills-dir": {
12
+ value: true,
13
+ help: "where the skills are copied (default ~/.claude/skills)",
14
+ },
15
+ force: {
16
+ value: false,
17
+ help: "replace a skill directory this package did not install",
18
+ },
10
19
  json: { value: false, help: "machine-readable output" },
11
20
  };
12
21
  /** Per-verb flag tables. A verb absent here accepts only the global flags. */
13
22
  export const FLAG_TABLES = {
14
23
  login: {
15
- "start-replica": { value: false, help: "run `replica start --detach` after connecting" },
24
+ "start-replica": {
25
+ value: false,
26
+ help: "run `replica start --detach` after connecting",
27
+ },
16
28
  },
17
29
  mcp: {
18
30
  url: { value: true, help: "add: the upstream HTTPS endpoint" },
19
31
  auth: { value: true, help: "add: none for an unauthenticated upstream" },
20
- bearer: { value: true, help: "add: vault secret NAME for a bearer token, never its value" },
21
- header: { value: true, repeat: true, help: "add: HEADER_NAME=VAULT_SECRET_NAME (repeatable)" },
32
+ bearer: {
33
+ value: true,
34
+ help: "add: vault secret NAME for a bearer token, never its value",
35
+ },
36
+ header: {
37
+ value: true,
38
+ repeat: true,
39
+ help: "add: HEADER_NAME=VAULT_SECRET_NAME (repeatable)",
40
+ },
22
41
  },
23
42
  install: {},
43
+ onboard: {
44
+ "resume-from": { value: true, help: "resume at a verified onboarding step" },
45
+ only: { value: true, help: "run only the named onboarding step" },
46
+ yes: { value: false, help: "accept the plan's default choices" },
47
+ "dry-run": { value: false, help: "print the plan without taking the lock or changing files" },
48
+ },
24
49
  status: {},
25
50
  notice: {},
26
51
  me: {},
@@ -32,63 +57,148 @@ export const FLAG_TABLES = {
32
57
  team: { value: true, help: "team key filter" },
33
58
  project: { value: true, help: "project id filter" },
34
59
  state: { value: true, help: "state name filter" },
35
- limit: { value: true, help: "max rows (default 50); one page only unless --all" },
60
+ limit: {
61
+ value: true,
62
+ help: "max rows (default 50); one page only unless --all",
63
+ },
36
64
  since: { value: true, help: "changes: the cursor to read after" },
37
- source: { value: true, help: "replica | api (default: replica when fresh, else api)" },
65
+ source: {
66
+ value: true,
67
+ help: "replica | api (default: replica when fresh, else api)",
68
+ },
38
69
  ticket: { value: true, help: "pulls: only PRs linked to this ticket" },
39
- all: { value: false, help: "issues/pulls: follow the cloud's page cursor to the end of the scope" },
70
+ all: {
71
+ value: false,
72
+ help: "issues/pulls: follow the cloud's page cursor to the end of the scope",
73
+ },
40
74
  },
41
75
  replica: {
42
- detach: { value: false, help: "start: run the writer in the background and write a pidfile" },
76
+ detach: {
77
+ value: false,
78
+ help: "start: run the writer in the background and write a pidfile",
79
+ },
43
80
  db: { value: true, help: "replica file path (default from customer.json)" },
44
- probe: { value: false, help: "status: also fetch the cloud head and print the lag" },
45
- "stale-ms": { value: true, help: "status: heartbeat age that counts as stale (default 15000)" },
81
+ probe: {
82
+ value: false,
83
+ help: "status: also fetch the cloud head and print the lag",
84
+ },
85
+ "stale-ms": {
86
+ value: true,
87
+ help: "status: heartbeat age that counts as stale (default 15000)",
88
+ },
46
89
  },
47
90
  events: {
48
- probe: { value: false, help: "status: compare the local event cursor with the cloud event head" },
91
+ probe: {
92
+ value: false,
93
+ help: "status: compare the local event cursor with the cloud event head",
94
+ },
49
95
  type: { value: true, help: "exact event type" },
50
- ticket: { value: true, help: "ticket identifier found in the event payload" },
51
- after: { value: true, help: "event sequence to read after (tail/wait default to local head)" },
96
+ ticket: {
97
+ value: true,
98
+ help: "ticket identifier found in the event payload",
99
+ },
100
+ after: {
101
+ value: true,
102
+ help: "event sequence to read after (tail/wait default to local head)",
103
+ },
52
104
  limit: { value: true, help: "query: maximum matching events (default 50)" },
53
- timeout: { value: true, help: "wait-for: bounded wait in seconds (default 300)" },
54
- directory: { value: true, help: "event cache directory (default: SDK XDG path)" },
105
+ timeout: {
106
+ value: true,
107
+ help: "wait-for: bounded wait in seconds (default 300)",
108
+ },
109
+ directory: {
110
+ value: true,
111
+ help: "event cache directory (default: SDK XDG path)",
112
+ },
55
113
  },
56
114
  explain: {
57
- history: { value: false, help: "per-ticket execution history instead of the eligibility reason" },
115
+ history: {
116
+ value: false,
117
+ help: "per-ticket execution history instead of the eligibility reason",
118
+ },
58
119
  },
59
120
  history: {},
60
121
  running: {
61
- ticket: { value: true, help: "with --phase: also the lease attributions for this (ticket, phase)" },
62
- phase: { value: true, help: "with --ticket: the phase whose lease attributions to read" },
122
+ ticket: {
123
+ value: true,
124
+ help: "with --phase: also the lease attributions for this (ticket, phase)",
125
+ },
126
+ phase: {
127
+ value: true,
128
+ help: "with --ticket: the phase whose lease attributions to read",
129
+ },
63
130
  },
64
131
  queue: {
65
- team: { value: true, help: "team key (default: every team the tenant contract names)" },
132
+ team: {
133
+ value: true,
134
+ help: "team key (default: every team the tenant contract names)",
135
+ },
66
136
  },
67
137
  watch: {
68
138
  team: { value: true, help: "scope: a team key" },
69
- ticket: { value: true, repeat: true, help: "scope: a ticket identifier (repeatable)" },
139
+ ticket: {
140
+ value: true,
141
+ repeat: true,
142
+ help: "scope: a ticket identifier (repeatable)",
143
+ },
70
144
  project: { value: true, help: "scope: a project id" },
71
- exec: { value: true, help: "run this shell command per frame with the frame on stdin" },
72
- "cursor-file": { value: true, help: "cursor file path (default ~/.config/catalyst-cloud/watch-cursor.json)" },
145
+ exec: {
146
+ value: true,
147
+ help: "run this shell command per frame with the frame on stdin",
148
+ },
149
+ "cursor-file": {
150
+ value: true,
151
+ help: "cursor file path (default ~/.config/catalyst-cloud/watch-cursor.json)",
152
+ },
73
153
  from: { value: true, help: "cursor | head (default cursor)" },
74
154
  },
75
155
  write: {
76
156
  body: { value: true, help: "comment: the body text" },
77
- stdin: { value: false, help: "comment: read the body from stdin; create: read the description from stdin" },
157
+ stdin: {
158
+ value: false,
159
+ help: "comment: read the body from stdin; create: read the description from stdin",
160
+ },
78
161
  parent: { value: true, help: "comment: reply under this comment id" },
79
- bookkeeping: { value: false, help: "comment: prefix the contract's bookkeeping marker" },
80
- "as-user": { value: false, help: "post with the personal identity instead of the app actor" },
162
+ bookkeeping: {
163
+ value: false,
164
+ help: "comment: prefix the contract's bookkeeping marker",
165
+ },
166
+ "as-user": {
167
+ value: false,
168
+ help: "post with the personal identity instead of the app actor",
169
+ },
81
170
  slot: { value: true, help: "state: the workflow slot to move to" },
82
171
  "state-id": { value: true, help: "state: an explicit Linear state id" },
83
- "state-type": { value: true, help: "state: the first state of this type on the ticket's team (e.g. backlog)" },
84
- add: { value: true, repeat: true, help: "label: label name or id to add (repeatable)" },
85
- remove: { value: true, repeat: true, help: "label: label name or id to remove (repeatable)" },
172
+ "state-type": {
173
+ value: true,
174
+ help: "state: the first state of this type on the ticket's team (e.g. backlog)",
175
+ },
176
+ add: {
177
+ value: true,
178
+ repeat: true,
179
+ help: "label: label name or id to add (repeatable)",
180
+ },
181
+ remove: {
182
+ value: true,
183
+ repeat: true,
184
+ help: "label: label name or id to remove (repeatable)",
185
+ },
86
186
  team: { value: true, help: "create: team key" },
87
187
  title: { value: true, help: "create/attachment/session: title" },
88
- description: { value: true, help: "create: the ticket description (markdown)" },
89
- label: { value: true, repeat: true, help: "create: label name or id (repeatable)" },
188
+ description: {
189
+ value: true,
190
+ help: "create: the ticket description (markdown)",
191
+ },
192
+ label: {
193
+ value: true,
194
+ repeat: true,
195
+ help: "create: label name or id (repeatable)",
196
+ },
90
197
  priority: { value: true, help: "create: Linear priority 0-4" },
91
- comment: { value: true, help: "reaction: react to this comment id instead of the ticket" },
198
+ comment: {
199
+ value: true,
200
+ help: "reaction: react to this comment id instead of the ticket",
201
+ },
92
202
  emoji: { value: true, help: "reaction: the emoji" },
93
203
  url: { value: true, help: "attachment/session: the URL" },
94
204
  "plan-file": { value: true, help: "session: a JSON file holding the plan" },
@@ -98,78 +208,187 @@ export const FLAG_TABLES = {
98
208
  team: { value: true, help: "raise: team key" },
99
209
  title: { value: true, help: "raise: the question" },
100
210
  context: { value: true, help: "raise: context paragraph" },
101
- option: { value: true, repeat: true, help: "raise: an option (repeatable)" },
211
+ option: {
212
+ value: true,
213
+ repeat: true,
214
+ help: "raise: an option (repeatable)",
215
+ },
102
216
  default: { value: true, help: "raise: the default if silent" },
103
- blocks: { value: true, repeat: true, help: "raise: a ticket this decision blocks (repeatable)" },
104
- "nothing-to-block": { value: false, help: "raise: declare that nothing is blocked" },
105
- anyone: { value: false, help: "list: every open ask in the tenant, not only the ones assigned to you" },
217
+ blocks: {
218
+ value: true,
219
+ repeat: true,
220
+ help: "raise: a ticket this decision blocks (repeatable)",
221
+ },
222
+ "nothing-to-block": {
223
+ value: false,
224
+ help: "raise: declare that nothing is blocked",
225
+ },
226
+ anyone: {
227
+ value: false,
228
+ help: "list: every open ask in the tenant, not only the ones assigned to you",
229
+ },
106
230
  "ask-key": { value: true, help: "raise: idempotency key" },
107
231
  answer: { value: true, help: "accept: the answering comment id" },
108
232
  role: { value: true, help: "accept: the role recording the answer" },
109
233
  },
110
234
  ready: {
111
- offline: { value: false, help: "skip the published-release check (no network)" },
235
+ offline: {
236
+ value: false,
237
+ help: "skip the published-release check (no network)",
238
+ },
112
239
  },
113
240
  accounts: {},
114
- env: {},
241
+ env: {
242
+ root: { value: true, help: "draft: repository directory (default: current directory)" },
243
+ write: { value: false, help: "draft: write a new .catalyst/catalyst.toml; refuse to overwrite" },
244
+ diff: { value: false, help: "draft: show the generated TOML as an additions-only diff" },
245
+ },
246
+ var: {
247
+ repo: { value: true, help: "repository scope (owner/name); omit for tenant scope" },
248
+ names: { value: true, repeat: true, help: "import: only send these names (repeatable or comma-separated)" },
249
+ },
115
250
  team: {
116
251
  all: { value: false, help: "check every team, one request at a time" },
117
- stage: { value: true, repeat: true, help: "map a role to a live Linear state name (role=StateName)" },
118
- choice: { value: true, repeat: true, help: "migrate a source state to a destination (sourceId=destinationId)" },
119
- yes: { value: false, help: "apply the plan you have reviewed with the person" },
120
- "plan-hash": { value: true, help: "apply only the exact preview hash the person reviewed" },
121
- undo: { value: false, help: "adopt: preview or archive stages a prior adoption created" },
122
- retire: { value: false, help: "migrate: separately preview or retire emptied source stages" },
252
+ stage: {
253
+ value: true,
254
+ repeat: true,
255
+ help: "map a role to a live Linear state name (role=StateName)",
256
+ },
257
+ choice: {
258
+ value: true,
259
+ repeat: true,
260
+ help: "migrate a source state to a destination (sourceId=destinationId)",
261
+ },
262
+ yes: {
263
+ value: false,
264
+ help: "apply the plan you have reviewed with the person",
265
+ },
266
+ "plan-hash": {
267
+ value: true,
268
+ help: "apply only the exact preview hash the person reviewed",
269
+ },
270
+ undo: {
271
+ value: false,
272
+ help: "adopt: preview or archive stages a prior adoption created",
273
+ },
274
+ retire: {
275
+ value: false,
276
+ help: "migrate: separately preview or retire emptied source stages",
277
+ },
123
278
  },
124
279
  environment: {
125
280
  file: { value: true, help: "propose: a JSON file holding the declaration" },
126
281
  stdin: { value: false, help: "propose: read the declaration from stdin" },
127
- "expect-revision": { value: true, help: "propose: refuse unless the stored declaration is still at this revision" },
128
- approve: { value: false, help: "propose: approve exactly the revision the propose returned" },
129
- revision: { value: true, help: "approve: the revision to approve (with --hash; default is whatever read returns)" },
130
- hash: { value: true, help: "approve: the canonical hash to approve (with --revision)" },
282
+ "expect-revision": {
283
+ value: true,
284
+ help: "propose: refuse unless the stored declaration is still at this revision",
285
+ },
286
+ approve: {
287
+ value: false,
288
+ help: "propose: approve exactly the revision the propose returned",
289
+ },
290
+ revision: {
291
+ value: true,
292
+ help: "approve: the revision to approve (with --hash; default is whatever read returns)",
293
+ },
294
+ hash: {
295
+ value: true,
296
+ help: "approve: the canonical hash to approve (with --revision)",
297
+ },
131
298
  },
132
299
  secret: {
133
300
  repo: { value: true, help: "the repository, as owner/name (required)" },
134
- command: { value: true, help: "set: run this command on this machine and store its output (e.g. 'op read op://Vault/item/field'); the command text is audited, so never put a value in it" },
135
- rotate: { value: true, repeat: true, help: "import: replace this name if it is already set (repeatable)" },
301
+ command: {
302
+ value: true,
303
+ help: "set: run this command on this machine and store its output (e.g. 'op read op://Vault/item/field'); the command text is audited, so never put a value in it",
304
+ },
305
+ rotate: {
306
+ value: true,
307
+ repeat: true,
308
+ help: "import: replace this name if it is already set (repeatable)",
309
+ },
310
+ names: {
311
+ value: true,
312
+ repeat: true,
313
+ help: "import: only send these names from the local file (repeatable or comma-separated)",
314
+ },
136
315
  },
137
316
  identity: {},
138
317
  capabilities: {},
139
318
  project: {
140
- team: { value: true, help: "the project's team key (default: the only mapped project)" },
319
+ team: {
320
+ value: true,
321
+ help: "the project's team key (default: the only mapped project)",
322
+ },
141
323
  },
142
324
  legacy: {
143
- remove: { value: false, help: "remove what was found, after one question (or --yes)" },
144
- data: { value: false, help: "with --remove: delete the old runtime's data folders too" },
145
- yes: { value: false, help: "with --remove: answer the question yes (a run with no terminal only reports otherwise)" },
325
+ remove: {
326
+ value: false,
327
+ help: "remove what was found, after one question (or --yes)",
328
+ },
329
+ data: {
330
+ value: false,
331
+ help: "historical flag; shared data folders are always kept",
332
+ },
333
+ yes: {
334
+ value: false,
335
+ help: "with --remove: answer the question yes (a run with no terminal only reports otherwise)",
336
+ },
146
337
  },
147
338
  repo: {
148
- write: { value: false, help: "agents-block: write or update the block in AGENTS.md (working tree only)" },
149
- apply: { value: false, help: "agent-setup: perform the portable-layout plan in the working tree" },
150
- "with-check": { value: false, help: "agent-setup: also write scripts/agents-md-check.mjs, a CI check for the layout" },
339
+ write: {
340
+ value: false,
341
+ help: "agents-block: write or update the block in AGENTS.md (working tree only)",
342
+ },
343
+ apply: {
344
+ value: false,
345
+ help: "agent-setup: perform the portable-layout plan in the working tree",
346
+ },
347
+ "with-check": {
348
+ value: false,
349
+ help: "agent-setup: also write scripts/agents-md-check.mjs, a CI check for the layout",
350
+ },
151
351
  },
152
352
  connections: {
153
- wait: { value: true, help: "start: wait up to this many seconds for browser approval (0-600)" },
353
+ wait: {
354
+ value: true,
355
+ help: "start: wait up to this many seconds for browser approval (0-600)",
356
+ },
154
357
  },
155
358
  release: {
156
- because: { value: true, help: "what changed since the ticket was held (required unless --dry-run)" },
157
- "retry-unchanged": { value: false, help: "release even though nothing the mirror can see changed (say what did in --because)" },
158
- "dry-run": { value: false, help: "show what a release would clear and refuse, and change nothing" },
159
- class: { value: true, help: "release every ticket on --team parked under this failure class" },
359
+ because: {
360
+ value: true,
361
+ help: "what changed since the ticket was held (required unless --dry-run)",
362
+ },
363
+ "retry-unchanged": {
364
+ value: false,
365
+ help: "release even though nothing the mirror can see changed (say what did in --because)",
366
+ },
367
+ "dry-run": {
368
+ value: false,
369
+ help: "show what a release would clear and refuse, and change nothing",
370
+ },
371
+ class: {
372
+ value: true,
373
+ help: "release every ticket on --team parked under this failure class",
374
+ },
160
375
  team: { value: true, help: "with --class: the team key" },
161
- limit: { value: true, help: "with --class: at most this many tickets (the cloud caps it at 25)" },
376
+ limit: {
377
+ value: true,
378
+ help: "with --class: at most this many tickets (the cloud caps it at 25)",
379
+ },
162
380
  },
163
381
  };
164
382
  export const VERB_USAGE = {
165
383
  login: "login [--base-url <url>] [--start-replica] (keyless; or --key <personal-key> / CATALYST_CLOUD_TOKEN)",
384
+ onboard: "onboard [--resume-from <step>] [--only <step>] [--yes] [--dry-run] [--json]",
166
385
  install: "install [--skills-dir <dir>] [--force]",
167
386
  status: "status",
168
387
  notice: "notice",
169
388
  me: "me [--json]",
170
389
  contract: "contract [--refresh] [--path <a.b.c>] [--json]",
171
390
  query: "query <issues|issue <id>|pulls|pull <id>|projects|cycles|search <terms>|changes --since <cursor|head>> [--team K] [--project P] [--state S] [--limit N] [--all] [--source replica|api] [--json]",
172
- replica: "replica <start [--detach]|stop|status [--probe] [--json]|sql \"<select>\"|schema [table]> [--db <path>]",
391
+ replica: 'replica <start [--detach]|stop|status [--probe] [--json]|sql "<select>"|schema [table]> [--db <path>]',
173
392
  runtime: "runtime <status [--json]|install|path|uninstall>",
174
393
  events: "events <tail|wait-for|query|status [--probe] [--json]> [--type NAME] [--ticket CTC-N] [--after SEQUENCE] [--limit N] [--timeout SECONDS] [--directory PATH]",
175
394
  explain: "explain <ticket> [--history] [--json]",
@@ -183,12 +402,13 @@ export const VERB_USAGE = {
183
402
  accounts: "accounts [--json]",
184
403
  mcp: "mcp <add <name> --url URL <--auth none|--bearer SECRET_NAME|--header NAME=SECRET_NAME...>|list|remove <name>> [--json]",
185
404
  team: "team <list|check <KEY>|check --all|map <KEY> [--stage role=StateName]... [--yes --plan-hash H]|adopt <KEY> [--undo] [--yes --plan-hash H]|migrate <KEY> [--choice sourceId=destinationId]... [--retire] [--yes --plan-hash H]|checklist <KEY>> [--json]",
186
- env: "env <inventory [path] | check <file>> [--json] (THIS repository, offline — no login, no network)",
405
+ env: "env <inventory [path] | check <file> | migrate [catalyst.env.json] | draft [--root DIR] [--diff] [--write]> [--json] (THIS repository, offline — no login, no network)",
187
406
  environment: "environment [read] [--json] | environment propose --file <path>|--stdin [--expect-revision N] [--approve] [--json] | environment approve [--revision N --hash H] [--json] (your ACCOUNT's declaration; needs login)",
188
- secret: "secret set <NAME> --repo <owner/name> [--command '<cmd>'] [--json] (value from --command, stdin, or a hidden prompt) | secret import <file> --repo <owner/name> [--rotate NAME]... [--json]",
407
+ secret: "secret set <NAME> --repo <owner/name> [--command '<cmd>'] [--json] (value from --command, stdin, or a hidden prompt) | secret import <file> --repo <owner/name> [--names NAME[,NAME...]] [--rotate NAME]... [--json]",
408
+ var: "var <set NAME|import <file>> [--repo <owner/name>] [--names NAME[,NAME...]] [--json] (plain environment variables; set reads stdin or a hidden prompt)",
189
409
  identity: "identity linear <status|options|set> [<linearUserId>] [--json]",
190
410
  capabilities: "capabilities [--json]",
191
- project: "project wip-limit <get|set <n>|set default> [--team K] [--json]",
411
+ project: "project <list [--json] | wip-limit <get|set <n>|set default> [--team K] [--json]>",
192
412
  repo: "repo <agents-block <path> [--write]|agent-setup <path> [--apply] [--with-check]> [--json]",
193
413
  legacy: "legacy [--remove [--data] [--yes]] [--json]",
194
414
  connections: "connections personal <linear|github> <start|status> [--wait <seconds>] [--json]",
@@ -233,7 +453,9 @@ export function parseArgs(argv) {
233
453
  }
234
454
  const spec = table[name];
235
455
  if (!spec) {
236
- throw new UsageError(out.command ? `unknown option for ${out.command}: --${name}` : `unknown option: --${name}`);
456
+ throw new UsageError(out.command
457
+ ? `unknown option for ${out.command}: --${name}`
458
+ : `unknown option: --${name}`);
237
459
  }
238
460
  if (spec.value) {
239
461
  const v = inlineValue ?? requireValue(argv, ++i, `--${name}`);
@@ -5,6 +5,7 @@ const agent = (method, name) => ({ method, path: `/api/v1/agent/${name}` });
5
5
  * the person, the workspace, the project, the repository, then work. A new verb is a new row here
6
6
  * in the same change, or `catalyst capabilities` lies to every guide that reads it. */
7
7
  export const CAPABILITIES = [
8
+ { verb: "onboard", does: "run and resume Catalyst setup with a saved step record", needs: "member", routes: [], since: "0.14.0", bootstrapHandoff: true },
8
9
  { verb: "status", does: "say whether this machine is connected, as whom, and to which cloud", needs: "member", routes: [], since: "0.1.0" },
9
10
  { verb: "me", does: "read the person's seat, role and Linear identity", needs: "member", routes: [], since: "0.1.0" },
10
11
  { verb: "ready", does: "read the machine and the workspace's per-project readiness in one verdict", needs: "member", routes: [], since: "0.1.0" },
@@ -28,6 +29,7 @@ export const CAPABILITIES = [
28
29
  { verb: "environment propose", does: "propose the workspace-wide environment declaration (names only)", needs: "admin", routes: [agent("POST", "account-environment/propose")], since: "0.8.0" },
29
30
  { verb: "environment approve", does: "approve the workspace-wide environment declaration", needs: "admin", routes: [agent("POST", "account-environment/approve")], since: "0.8.0" },
30
31
  { verb: "secret set|import", does: "enter a repository secret's value from this terminal, never echoed", needs: "admin", routes: [], since: "0.8.0" },
32
+ { verb: "var set|import", does: "enter plain environment variable values from this terminal, never echoed", needs: "admin", routes: [], since: "0.13.2" },
31
33
  { verb: "accounts", does: "read the workspace's coding accounts and which can take work", needs: "member", routes: [], since: "0.5.0" },
32
34
  { verb: "explain|running|queue|history", does: "read why a ticket runs or waits, what runs now, and what is queued", needs: "member", routes: [], since: "0.1.0" },
33
35
  { verb: "write comment|state|label|create|reaction|attachment|session", does: "act on a ticket as the person, through the agent proxy", needs: "member", routes: [agent("POST", "issue-comment"), agent("POST", "issue-state"), agent("POST", "issue-label"), agent("POST", "issue-create")], since: "0.1.0" },