@catalyst-cloud/cli 0.9.5 → 0.13.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 (147) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/README.md +28 -29
  3. package/dist/args.js +19 -1
  4. package/dist/ask.js +30 -18
  5. package/dist/capabilities.js +5 -0
  6. package/dist/cli.js +24 -6
  7. package/dist/env/declaration-rules.js +163 -0
  8. package/dist/env/group.js +51 -0
  9. package/dist/env/inventory.js +102 -0
  10. package/dist/env/render.js +39 -0
  11. package/dist/env/scan-env-example.js +62 -0
  12. package/dist/env/scan-source.js +43 -0
  13. package/dist/env/scan-workflow.js +123 -0
  14. package/dist/env/scan-wrangler.js +120 -0
  15. package/dist/env/types.js +1 -0
  16. package/dist/env/walk.js +87 -0
  17. package/dist/env.js +76 -0
  18. package/dist/environment.js +1 -1
  19. package/dist/legacy.js +224 -0
  20. package/dist/project.js +129 -0
  21. package/dist/release.js +1 -1
  22. package/dist/repo.js +297 -0
  23. package/dist/runtime-verb.js +1 -1
  24. package/dist/secret.js +1 -1
  25. package/dist/skill-shape.js +21 -13
  26. package/dist/skills.js +44 -4
  27. package/dist/transport.js +26 -1
  28. package/package.json +7 -6
  29. package/skills/catalyst-github/SKILL.md +7 -7
  30. package/skills/catalyst-github/references/is-it-mergeable.md +8 -2
  31. package/skills/catalyst-github/references/what-a-pr-accumulates.md +3 -3
  32. package/skills/catalyst-github/scripts/is-it-mergeable.mjs +3 -3
  33. package/skills/catalyst-github/scripts/lib/cli.mjs +9 -7
  34. package/skills/catalyst-github/scripts/lib/credential.mjs +2 -2
  35. package/skills/catalyst-github/scripts/read-pr.mjs +2 -2
  36. package/skills/catalyst-linear/SKILL.md +17 -19
  37. package/skills/catalyst-linear/agents/openai.yaml +1 -1
  38. package/skills/catalyst-linear/agents/portability.yaml +1 -1
  39. package/skills/catalyst-linear/references/reading-a-ticket.md +9 -26
  40. package/skills/catalyst-linear/references/what-a-ticket-accumulates.md +2 -2
  41. package/skills/catalyst-linear/references/writing-to-linear.md +7 -9
  42. package/skills/catalyst-linear/scripts/comment.mjs +1 -1
  43. package/skills/catalyst-linear/scripts/create-ticket.mjs +1 -1
  44. package/skills/catalyst-linear/scripts/label.mjs +1 -1
  45. package/skills/catalyst-linear/scripts/lib/cli.mjs +9 -7
  46. package/skills/catalyst-linear/scripts/lib/credential.mjs +2 -2
  47. package/skills/catalyst-linear/scripts/move.mjs +1 -1
  48. package/skills/catalyst-linear/scripts/read-ticket.mjs +1 -1
  49. package/skills/catalyst-linear/scripts/search.mjs +1 -1
  50. package/skills/catalyst-onboard/SKILL.md +37 -57
  51. package/skills/catalyst-onboard/agents/openai.yaml +2 -2
  52. package/skills/catalyst-onboard/agents/portability.yaml +1 -1
  53. package/skills/catalyst-onboard/references/connecting-this-machine.md +35 -0
  54. package/skills/catalyst-onboard/references/declaring-a-repository.md +7 -17
  55. package/skills/catalyst-onboard/references/local-sync.md +19 -7
  56. package/skills/catalyst-onboard/references/reading-ready.md +79 -0
  57. package/skills/catalyst-onboard/references/replacing-a-credential.md +8 -33
  58. package/skills/catalyst-onboard/references/repository-agent-setup.md +23 -0
  59. package/skills/catalyst-onboard/references/skill-sources.md +13 -19
  60. package/skills/catalyst-onboard/references/the-one-path.md +18 -74
  61. package/skills/catalyst-onboard/references/what-a-phase-needs.md +21 -28
  62. package/skills/catalyst-onboard/references/what-the-browser-owns.md +20 -37
  63. package/skills/{catalyst-setup → catalyst-onboard}/scripts/check.mjs +10 -9
  64. package/skills/catalyst-onboard/scripts/lib/cli.mjs +6 -4
  65. package/skills/catalyst-onboard/scripts/lib/credential.mjs +2 -2
  66. package/skills/catalyst-onboard/scripts/where-am-i.mjs +60 -4
  67. package/skills/run-this-project/SKILL.md +23 -25
  68. package/skills/run-this-project/agents/openai.yaml +1 -1
  69. package/skills/run-this-project/agents/portability.yaml +1 -1
  70. package/skills/run-this-project/references/making-work-ready.md +20 -37
  71. package/skills/run-this-project/references/reacting-to-events.md +25 -48
  72. package/skills/run-this-project/references/stalls-and-escalation.md +20 -42
  73. package/skills/run-this-project/scripts/lib/cli.mjs +7 -5
  74. package/skills/run-this-project/scripts/lib/credential.mjs +2 -2
  75. package/skills/run-this-project/scripts/scope-status.mjs +0 -0
  76. package/skills/run-this-project/scripts/watch-scope.mjs +1 -1
  77. package/skills/unstick/SKILL.md +12 -12
  78. package/skills/unstick/agents/openai.yaml +1 -1
  79. package/skills/unstick/agents/portability.yaml +1 -1
  80. package/skills/unstick/references/playbook.md +13 -13
  81. package/skills/unstick/scripts/lib/cli.mjs +6 -4
  82. package/skills/unstick/scripts/lib/credential.mjs +2 -2
  83. package/skills/unstick/scripts/unstick.mjs +1 -1
  84. package/skills/what-needs-me/SKILL.md +12 -14
  85. package/skills/what-needs-me/agents/openai.yaml +2 -2
  86. package/skills/what-needs-me/agents/portability.yaml +1 -1
  87. package/skills/what-needs-me/references/raising-a-decision.md +8 -16
  88. package/skills/what-needs-me/references/reading-the-inbox.md +9 -12
  89. package/skills/what-needs-me/references/settling-an-answer.md +6 -12
  90. package/skills/what-needs-me/scripts/inbox.mjs +14 -13
  91. package/skills/what-needs-me/scripts/lib/cli.mjs +6 -4
  92. package/skills/what-needs-me/scripts/lib/credential.mjs +2 -2
  93. package/skills/what-needs-me/scripts/raise.mjs +1 -1
  94. package/skills/what-needs-me/scripts/settle.mjs +1 -1
  95. package/skills/what-this-repo-needs/SKILL.md +31 -0
  96. package/skills/what-this-repo-needs/agents/openai.yaml +6 -0
  97. package/skills/what-this-repo-needs/agents/portability.yaml +4 -0
  98. package/skills/what-this-repo-needs/references/what-each-group-means.md +38 -0
  99. package/skills/what-this-repo-needs/scripts/check.mjs +64 -0
  100. package/skills/what-this-repo-needs/scripts/inventory.mjs +61 -0
  101. package/skills/{catalyst-setup → what-this-repo-needs}/scripts/lib/cli.mjs +35 -22
  102. package/skills/{catalyst-setup → what-this-repo-needs}/scripts/lib/credential.mjs +2 -2
  103. package/skills/whats-happening/SKILL.md +27 -21
  104. package/skills/whats-happening/agents/openai.yaml +1 -1
  105. package/skills/whats-happening/references/coding-accounts.md +24 -0
  106. package/skills/whats-happening/references/routing-work.md +11 -29
  107. package/skills/whats-happening/references/settings-and-where-they-live.md +20 -0
  108. package/skills/whats-happening/references/stages-and-mapping.md +25 -0
  109. package/skills/whats-happening/references/status-reply.md +21 -21
  110. package/skills/whats-happening/references/the-ladder.md +30 -0
  111. package/skills/whats-happening/references/what-runs-next.md +32 -0
  112. package/skills/whats-happening/references/when-a-phase-fails.md +27 -0
  113. package/skills/whats-happening/references/why-is-it-stuck.md +33 -40
  114. package/skills/whats-happening/scripts/explain.mjs +1 -1
  115. package/skills/whats-happening/scripts/lib/cli.mjs +7 -5
  116. package/skills/whats-happening/scripts/lib/credential.mjs +2 -2
  117. package/skills/{how-catalyst-works → whats-happening}/scripts/show-my-map.mjs +18 -32
  118. package/skills/whats-happening/scripts/snapshot.mjs +10 -6
  119. package/skills/catalyst-onboard/references/choosing-a-coding-account.md +0 -52
  120. package/skills/catalyst-onboard/references/who-fixes-what.md +0 -64
  121. package/skills/catalyst-setup/SKILL.md +0 -36
  122. package/skills/catalyst-setup/agents/openai.yaml +0 -6
  123. package/skills/catalyst-setup/agents/portability.yaml +0 -4
  124. package/skills/catalyst-setup/references/what-each-check-means.md +0 -88
  125. package/skills/catalyst-setup/scripts/replica-status.mjs +0 -46
  126. package/skills/connect-me/SKILL.md +0 -63
  127. package/skills/connect-me/agents/openai.yaml +0 -6
  128. package/skills/connect-me/agents/portability.yaml +0 -5
  129. package/skills/connect-me/references/keeping-the-replica-running.md +0 -88
  130. package/skills/connect-me/scripts/lib/cli.mjs +0 -185
  131. package/skills/connect-me/scripts/lib/credential.mjs +0 -29
  132. package/skills/connect-me/scripts/verify-connection.mjs +0 -68
  133. package/skills/how-catalyst-works/SKILL.md +0 -43
  134. package/skills/how-catalyst-works/agents/openai.yaml +0 -6
  135. package/skills/how-catalyst-works/agents/portability.yaml +0 -4
  136. package/skills/how-catalyst-works/references/coding-accounts.md +0 -51
  137. package/skills/how-catalyst-works/references/stages-and-mapping.md +0 -56
  138. package/skills/how-catalyst-works/references/the-ladder.md +0 -41
  139. package/skills/how-catalyst-works/references/what-catalyst-is.md +0 -30
  140. package/skills/how-catalyst-works/references/what-runs-next.md +0 -88
  141. package/skills/how-catalyst-works/references/when-a-phase-fails.md +0 -57
  142. package/skills/how-catalyst-works/scripts/explain-ticket.mjs +0 -41
  143. package/skills/how-catalyst-works/scripts/lib/cli.mjs +0 -164
  144. package/skills/how-catalyst-works/scripts/lib/credential.mjs +0 -29
  145. package/skills/how-catalyst-works/scripts/whats-running.mjs +0 -65
  146. package/skills/whats-happening/references/reading-the-board.md +0 -43
  147. package/skills/whats-happening/references/reprioritising.md +0 -37
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.13.1
4
+
5
+ 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.
6
+
7
+ The CLI adds project wip-limit get and set, repo agents-block and agent-setup, and legacy. These commands inspect or change a project's WIP limit, check or install repository agent guidance, and find old local runtime files. Legacy cleanup requires consent. The CLI now depends on SDK 0.13.1.
8
+
9
+ ## 0.13.0
10
+
11
+ Catalyst's released pieces now share one version line, 0.13: `@catalyst-cloud/cli` and the deprecated `@catalyst-cloud/catalyst-skills` name, these skills, `@catalyst-cloud/sdk`, the installer, and the schema, replicate and read-model libraries. A CLI on 0.13.x belongs with a cloud whose tenant contract names `releaseLine` 0.13. The CLI moves from 0.9 straight to 0.13 and runs on `@catalyst-cloud/sdk` 0.13. The tenant contract keeps its own version, which tracks the document's shape.
12
+
13
+ Ask your agent what a setting does and where it is set, and it names the screen, the route and the rule instead of guessing: connecting Linear, installing the GitHub App, registering a repository, the merge policy and its reviewers, secrets versus environment variables, and why routing has no write route yet all have a home in `how-catalyst-works`. `whats-happening` now maps every reason a ticket can be held to a next action and who takes it — the person with their own login, a tenant owner or admin in settings, an operator, or "no one acts" when it is genuinely just waiting — and three reasons that were missing from the stuck-state table (a live container holding a later phase, too many claims in an hour, and a repository at capacity) are documented for the first time. Connecting your own Linear account and being matched as a Linear identity are now explained as two different, sometimes disagreeing, facts, so "I connected Linear, why am I still not getting asks?" has a real answer. And the README says plainly what it still does not know: flow metrics — cycle time, throughput, how long a pull request has been open — are not computed yet, rather than counting something else and calling it that.
14
+
3
15
  ## 0.9.5
4
16
 
5
17
  The CLI calls itself `catalyst` everywhere: usage lines, every verb's `--help`, hints, remedies and errors. `catalyst-skills` still works as a deprecated alias. The npx form is `npx -p @catalyst-cloud/cli catalyst …`, because bare `npx @catalyst-cloud/cli` cannot pick a binary. A replica read on the `node:sqlite` fallback no longer prints a notice every time; `CATALYST_DEBUG=1` still shows it.
@@ -60,6 +72,22 @@ That one command is `catalyst-skills runtime install`. It downloads a pinned Nod
60
72
 
61
73
  ## 0.7.0
62
74
 
75
+ `catalyst-skills env inventory [path]` scans a repository offline — no login, no network — and lists
76
+ the environment variable NAMES it needs, never a value. Names are grouped build/test (needed to
77
+ install, build and run the tests), deploy-only (a CI secret only a deploy job uses) and bindings (a
78
+ Cloudflare or other platform binding, which is not an environment value). Each name shows where it
79
+ was found (file:line), what uses it, and where a local value would come from — a `.env` file, your
80
+ shell, a CI secret, or a Cloudflare binding. `--json` gives the same data in machine form. `env check
81
+ <file>` validates a `catalyst.env.json` offline, against this bundle's own dated, cited copy of the
82
+ cloud's validation rules (it does not compute the cloud's canonical hash — only a `valid` / `invalid`
83
+ verdict and the refusal reasons). The new `what-this-repo-needs` skill explains why a cloud container
84
+ needs these names before it scans, then asks you to review the list: keep, drop or move each one.
85
+
86
+ Reading a GitHub Actions workflow now goes through a real YAML parser (`yaml`, now a runtime
87
+ dependency of this package — it carries no transitive dependencies of its own) rather than a
88
+ hand-written line scanner, so a workflow that declares its `env:` in flow style (`{ A: 1, B: 2 }`) is
89
+ read correctly instead of silently missed.
90
+
63
91
  `explain` and `ready` now name a team that cannot start work even when the live read is not available. Your tenant's contract carries each team's dispatch gate, and this machine already keeps a copy of that contract on disk; until now only the live eligibility read could name the gate, so a network hiccup, or a cloud older than that read, left `explain` printing nothing at all. `explain` now leads with the gate and the fix from the cached contract when the live read is unavailable or sends no gate, and says which of the two it read; when both answer and disagree, the live read wins and the paragraph says the cached one disagreed. A refusal from the cloud — a credential that is not accepted, for instance — is still a refusal, never quietly replaced by a cached answer. `ready` now prints one dispatch-gate line per team: open teams read `ok`, and a team whose stages are not saved reads `FAIL` with the remedy your tenant sent and turns the verdict to NOT READY, because nothing in that team can start. A cloud that does not send the gate changes nothing.
64
92
 
65
93
  `catalyst-skills ready` now tells you when the skills on this machine, or the CLI itself, are behind the newest published release — it names the version you have, the version that is out, and the one command that updates each, as a note that never changes the verdict. Every skill file now records the bundle version it was vendored at, so a copy installed months ago is visible as such instead of only by diffing files. The check reads the npm registry's `dist-tags.latest`, capped at 1500ms and cached for six hours, and never blocks: an unreachable registry is one note that says so, and `--offline` (or `CATALYST_SKILLS_OFFLINE=1`) skips the lookup entirely.
package/README.md CHANGED
@@ -12,7 +12,7 @@ curl -fsSL https://staging.catalystcloud.dev/install.sh | sh
12
12
 
13
13
  The installer puts the CLI and both skill packs on this machine and connects it with one browser approval. It ends by printing the next step. Then open a new session of your coding agent in any directory and type `/catalyst-onboard` (`$catalyst-onboard` in Codex). The onboarding skill reads where this machine stands and walks you through the rest one step at a time. It asks before it writes anything to your machine. Everything else on this page is the reference it follows.
14
14
 
15
- This repository supplies skills for setting up and operating a Catalyst Cloud tenant. A coding workstation also uses [`coalesce-labs/catalyst-dev-skills`](https://github.com/coalesce-labs/catalyst-dev-skills) for research, planning, implementation, review, and shipping.
15
+ This repository supplies skills for setting up and operating your Catalyst Cloud account. A coding workstation also uses [`coalesce-labs/catalyst-dev-skills`](https://github.com/coalesce-labs/catalyst-dev-skills) for research, planning, implementation, review, and shipping.
16
16
 
17
17
  Or by hand. One command, for every coding agent on the machine:
18
18
 
@@ -64,7 +64,7 @@ npx skills@latest add coalesce-labs/catalyst-cloud-skills
64
64
  Without `--all` the installer asks which skills to take and which agents to install them on.
65
65
  </details>
66
66
 
67
- ### Then connect to your tenant
67
+ ### Then connect to your cloud account
68
68
 
69
69
  The skills call one CLI, and the CLI holds your credential. Install it once and connect this machine — the keyless way logs you in as yourself, with nothing to mint or paste:
70
70
 
@@ -84,46 +84,46 @@ CATALYST_CLOUD_TOKEN=<your-personal-key> catalyst login
84
84
 
85
85
  `--key <your-personal-key>` is the third form, for a script.
86
86
 
87
- After a tenant owner connects the tenant's Linear workspace, connect your personal Linear account. Connect your personal GitHub account only after the tenant's GitHub App is installed and its repository is registered. These personal grants are separate from the tenant's Linear connection and GitHub App installation. Your agent can start each connection and check whether it landed, but you approve each one in your browser:
87
+ After a workspace owner connects your cloud account's Linear workspace, connect your personal Linear account. Connect your personal GitHub account only after the workspace's GitHub App is installed and its repository is registered. These personal grants are separate from the workspace's Linear connection and GitHub App installation. Your agent can start each connection and check whether it landed, but you approve each one in your browser:
88
88
 
89
89
  ```sh
90
90
  catalyst connections personal linear start
91
91
  catalyst connections personal linear status
92
- # First install the tenant's GitHub App and register its repository in Settings.
92
+ # First install the workspace's GitHub App and register its repository in Settings.
93
93
  catalyst connections personal github start
94
94
  catalyst connections personal github status
95
95
  ```
96
96
 
97
97
  Each `start` prints a short-lived URL and attempts to open your browser. On a remote machine, open the printed URL on your own device. `status --json` gives the same result to an agent; `start --wait 60` waits up to one minute for approval. A connected result is the proof that the grant landed. If a provider reports unavailable, retry status later rather than starting a second approval.
98
98
 
99
- The `catalyst-onboard` skill checks both grants before taking you through the rest of tenant setup and the first ticket.
99
+ The `catalyst-onboard` skill checks both grants before taking you through the rest of setup and the first ticket.
100
100
 
101
101
  The command is `catalyst`. `catalyst-skills` still works as a deprecated alias, and `@catalyst-cloud/catalyst-skills` is the deprecated package name that forwards to this one.
102
102
 
103
103
  ## What this is
104
104
 
105
- A set of skills that let your coding agent run your own Catalyst Cloud tenant (https://staging.catalystcloud.dev) from your seat: what is happening, what needs you, and what to do about it. They read your tenant through the Catalyst Cloud SDK, write to it through the tenant's agent proxy, and never compose a URL or run a tool of their own; every read, write and subscription is a `catalyst` verb with `--help`. Every skill is plain Markdown under `skills/<name>/SKILL.md` in this repository, and your own login — a keyless device-code session, or a personal key — is the only credential. Your agent acts as you: the asks it raises and the comments it posts carry your name, and "what needs me" means you.
105
+ A set of skills that let your coding agent run your own Catalyst Cloud account (https://staging.catalystcloud.dev) from your seat: what is happening, what needs you, and what to do about it. They read your account through the Catalyst Cloud SDK, write to it through the account's agent proxy, and never compose a URL or run a tool of their own; every read, write and subscription is a `catalyst` verb with `--help`. Every skill is plain Markdown under `skills/<name>/SKILL.md` in this repository, and your own login — a keyless device-code session, or a personal key — is the only credential. Your agent acts as you: the asks it raises and the comments it posts carry your name, and "what needs me" means you.
106
106
 
107
107
  ## What connecting does
108
108
 
109
- `login` does three things: it calls `GET /api/v1/me` with your credential, which discovers your tenant and who you are from the credential alone; it writes `~/.config/catalyst-cloud/customer.json` with file mode `0600`, holding your credential (a keyless login session, or a personal key), who you are (id, label, role, your Linear user id) and the absolute path of the CLI so every skill script can spawn the same binary; and it fetches the tenant contract (`GET /api/v1/agent/contract`) and caches it at `~/.config/catalyst-cloud/contract.json` with its ETag, so stage names and ids, label ids, the ask template, thresholds and the route table come from your tenant, live, and no skill restates them.
109
+ `login` does three things: it calls `GET /api/v1/me` with your credential, which discovers your cloud account and who you are from the credential alone; it writes `~/.config/catalyst-cloud/customer.json` with file mode `0600`, holding your credential (a keyless login session, or a personal key), who you are (id, label, role, your Linear user id) and the absolute path of the CLI so every skill script can spawn the same binary; and it fetches your account's contract (`GET /api/v1/agent/contract`) and caches it at `~/.config/catalyst-cloud/contract.json` with its ETag, so stage names and ids, label ids, the ask template, thresholds and the route table come from your account, live, and no skill restates them.
110
110
 
111
111
  A keyless session's access token is short-lived and rotates on its own: every request refreshes it within a minute of expiry and rewrites the config atomically, so you stay connected for months without logging in again. You only log in again if the session is revoked or you have been away long enough to lapse — then one clear line tells you to run `login`.
112
112
 
113
- `login` does not install skills; the install command above did that. Re-running `login` after a key rotation, or to switch rails, rewrites the config. `catalyst status` prints which tenant this machine is connected to and as whom, `catalyst ready` prints one READY or NOT READY verdict with the fix for each failure and who can apply it, and `catalyst --version` prints the package version and its pinned tenant contract range.
113
+ `login` does not install skills; the install command above did that. Re-running `login` after a key rotation, or to switch rails, rewrites the config. `catalyst status` prints which cloud account this machine is connected to and as whom, `catalyst ready` prints one READY or NOT READY verdict with the fix for each failure and who can apply it, and `catalyst --version` prints the package version and its pinned contract range.
114
114
 
115
- The setup skill Catalyst seeds into your repository ends by pointing at this same command. That served skill lives in the Catalyst Cloud application, not here; this is the only connect step a customer runs. The tenant's **account key** (Settings → Account keys, admin-minted) is a different credential — for a host or daemon that runs unattended — and is not what a person connects their own agent with: it would strip your name from everything your agent writes, and `login` says so if you use one.
115
+ The setup skill Catalyst seeds into your repository ends by pointing at this same command. That served skill lives in the Catalyst Cloud application, not here; this is the only connect step a customer runs. Your cloud account's **account key** (Settings → Account keys, admin-minted) is a different credential — for a host or daemon that runs unattended — and is not what a person connects their own agent with: it would strip your name from everything your agent writes, and `login` says so if you use one.
116
116
 
117
117
  ## Requirements
118
118
 
119
119
  - Node 22.15 or newer (Node 26 works), or bun 1.4 or newer. The bundle uses Node's built-in SQLite module (and, on bun, bun's own `node:sqlite`) for the optional local replica, so there is no native dependency to build; if `better-sqlite3` resolves on the machine it is used instead. `catalyst ready` names the exact reason when the runtime is too old, and `catalyst runtime install` installs a pinned Node under this CLI's own cache — without touching your machine's default Node — if you would rather not upgrade it.
120
- - Your personal key, minted by you at Settings → API keys. The key is the only tenant selector: you never type a tenant or account id. If your Linear identity is not matched yet, `login` says so; an admin matches it in Settings → Members, and until then "what needs me" shows everyone's asks.
120
+ - Your personal key, minted by you at Settings → API keys. The key is the only account selector: you never type an account id. If your Linear identity is not matched yet, `login` says so; you match it yourself with `catalyst identity linear set` (an identity someone else already claims needs an admin at Settings → Members), and until then "what needs me" shows everyone's asks.
121
121
  - An agent that discovers skills. Claude Code loads the plugin; Codex, Cursor, OpenCode and the rest read the `skills/<name>/SKILL.md` files the `npx skills` installer writes.
122
122
  - Bun is optional, only if you prefer `bunx` over `npx` — bun 1.4 or newer, which is when `node:sqlite` arrives; older bun cannot run this CLI at all.
123
123
 
124
124
  ## First use
125
125
 
126
- Open a new session and ask about your own tenant. The skills read the config `login` wrote. For example:
126
+ Open a new session and ask about your own cloud account. The skills read the config `login` wrote. For example:
127
127
 
128
128
  - What's happening? Where are we, why is that stuck, what closed, what's next?
129
129
  - What needs me?
@@ -140,45 +140,44 @@ Nothing, by default. After `login`, every read, write, ask and explanation goes
140
140
  | --- | --- | --- | --- |
141
141
  | none | every read, write, ask and explain | `customer.json` and `contract.json` | the default after login |
142
142
  | `catalyst replica start` | local SQL, cheap repeated reads, and local event `tail`, `wait-for`, and `query` | one SQLite file plus a bounded event cache under `$XDG_STATE_HOME/catalyst/events/` (fallback `~/.local/state/catalyst/events/`) | one long-running process; foreground by default, `--detach` writes a pidfile beside the database and returns; `login --start-replica` does the same at the end of login |
143
- | `catalyst watch` | a project owner reacting to its scope | one cursor file (`~/.config/catalyst-cloud/watch-cursor.json`) stamped with the tenant | lives inside the session that armed it; exits with it |
143
+ | `catalyst watch` | a project owner reacting to its scope | one cursor file (`~/.config/catalyst-cloud/watch-cursor.json`) stamped with the account | lives inside the session that armed it; exits with it |
144
144
 
145
145
  The check every skill runs first is `catalyst replica status`, which needs no network: is the pidfile's process alive, is the writer-lock heartbeat younger than the staleness threshold, and is the cursor non-empty. It exits `0` for fresh, `1` for present but stale, `2` for not connected, `3` for absent, and prints one line either way (`--json` for scripts). A fresh replica is used; anything else falls back to the API and the skill says so in its answer. A skill never refuses to work because the replica is down and never silently reads a stale one. `replica status --probe` compares the local cursor against the cloud's head for the honest "how far behind" number; that is the only form that touches the network. `catalyst replica stop` stops a detached writer.
146
146
 
147
- `catalyst events status --probe` checks the event cache's own cursor and compares it with the cloud head; `replica status --probe` checks the separate entity-replica cursor. A fresh replica does not prove event freshness. `events tail` follows new cached events, `events wait-for --ticket ... --timeout ...` performs a bounded wait, and `events query` reads retained history. Those three commands only read the cache; `events status --probe` makes the cloud comparison. The replica process is the single writer and reports an event-sync failure without terminating a healthy entity replica. The event cache keeps closed daily segments for at most seven days or 256 MiB per tenant and reports an explicit gap when a requested sequence has retired.
147
+ `catalyst events status --probe` checks the event cache's own cursor and compares it with the cloud head; `replica status --probe` checks the separate entity-replica cursor. A fresh replica does not prove event freshness. `events tail` follows new cached events, `events wait-for --ticket ... --timeout ...` performs a bounded wait, and `events query` reads retained history. Those three commands only read the cache; `events status --probe` makes the cloud comparison. The replica process is the single writer and reports an event-sync failure without terminating a healthy entity replica. The event cache keeps closed daily segments for at most seven days or 256 MiB per account and reports an explicit gap when a requested sequence has retired.
148
148
 
149
- The replica is a Node process, not a service: the supported path is the plain command, and `skills/connect-me/references/keeping-the-replica-running.md` gives launchd and systemd examples for people who want the writer to survive a reboot.
149
+ The replica is a Node process, not a service: the supported path is the plain command, and `skills/catalyst-onboard/references/local-sync.md` gives launchd and systemd examples for people who want the writer to survive a reboot.
150
150
 
151
151
  ## The skills
152
152
 
153
153
  | skill | what the person says | what it does | file |
154
154
  | --- | --- | --- | --- |
155
- | `catalyst-onboard` | "Set me up. Onboard me. I just signed up — what do I do first?" | Walks you from nothing to your first ticket running, one step at a time: connect this machine, connect the tenant and your personal provider accounts, map one project, register one repository, then watch a card move. Reads each part of setup with the instrument that owns it and says who can fix anything unfinished and where. Hands over browser consent and settings steps without claiming they happened until a status check confirms them. | [`SKILL.md`](skills/catalyst-onboard/SKILL.md) |
156
- | `whats-happening` | "What's happening? Where are we? Why is that stuck? What's next?" | The desk for a tenant: reads the contract, what is running and queued, the eligibility explainer and the open asks, and answers in one reply with ticket ids. Routes work to a project owner and decisions to `what-needs-me`. | [`SKILL.md`](skills/whats-happening/SKILL.md) |
157
- | `what-needs-me` | "What needs me? What am I blocking?" | The human's decision inbox, ranked by what each answer releases, and the one way an agent raises a decision on their behalf: files an ask through the cloud's ask route with the tenant's own template and records the answer so the held work releases. | [`SKILL.md`](skills/what-needs-me/SKILL.md) |
158
- | `run-this-project` | "Run this project for me. Own it until it closes." | Single-threaded owner of one project: subscribes to the tenant stream for its scope, reacts to each change in the same turn, makes tickets ready and moves them to dispatch, parks what should stop, chases stalls, escalates inward, and keeps one status summary current. Never polls. | [`SKILL.md`](skills/run-this-project/SKILL.md) |
159
- | `catalyst-setup` | "Am I set up? What is missing?" | Machine readiness plus tenant readiness from the contract's per-team checks, in one verdict: what passes, what is blocked, what is merely waiting, and who can click what. Reports; never repairs. | [`SKILL.md`](skills/catalyst-setup/SKILL.md) |
155
+ | `catalyst-onboard` | "Set me up. I just signed up. Am I set up? What is missing? Log me in." | Walks you from nothing to your first ticket running, one step at a time: connect this machine, connect the workspace and your personal provider accounts, map one project, register one repository, then watch a card move. Afterwards it answers "am I set up?" with one READY or NOT READY verdict and who can fix each failure, logs a machine in again, and checks the optional local replica. Hands over browser consent and settings steps without claiming they happened until a status check confirms them. | [`SKILL.md`](skills/catalyst-onboard/SKILL.md) |
156
+ | `whats-happening` | "What's happening? Why is that stuck? How does this work? How does it prioritise?" | The desk for your account: reads the contract, what is running and queued, the eligibility explainer, the coding accounts and the open asks, and answers in one reply with ticket ids. Carries the facts behind the answers: the ladder, the stage map, failures, parks and holds, the queue order and every reason a ticket is excluded. Routes work to a project owner and decisions to `what-needs-me`. | [`SKILL.md`](skills/whats-happening/SKILL.md) |
157
+ | `what-needs-me` | "What needs me? What am I blocking?" | The human's decision inbox, ranked by how much open work each ask holds, and the one way an agent raises a decision on their behalf: files an ask through the cloud's ask route with the account's own template and records the answer so the held work releases. | [`SKILL.md`](skills/what-needs-me/SKILL.md) |
158
+ | `run-this-project` | "Run this project for me. Own it until it closes." | Single-threaded owner of one project: subscribes to the account's event stream for its scope, reacts to each change in the same turn, makes tickets ready and moves them to dispatch, parks what should stop, chases stalls, escalates inward, and keeps one status summary current. Never polls. | [`SKILL.md`](skills/run-this-project/SKILL.md) |
160
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) |
161
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) |
162
- | `how-catalyst-works` | "How does this work? Why did it do that? How does it prioritise?" | The execution model as references loaded on demand: the eight-phase ladder, the eleven board slots and this team's live stage map, what happens when a phase fails, how the queue is ordered and routed, every exclusion reason, and the coding-account model. Scripts explain one ticket's eligibility in plain English. | [`SKILL.md`](skills/how-catalyst-works/SKILL.md) |
163
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) |
164
- | `connect-me` | "Connect this machine to my tenant." | Connects the machine to the tenant with your own personal key, caches the tenant contract, verifies, and offers to start the replica. | [`SKILL.md`](skills/connect-me/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) |
165
163
 
166
- Some of these only read; the ones that write anything — or that drive you through writes, as `catalyst-onboard` does — are marked so an agent cannot invoke them on its own, and you ask for them by name. Each skill's own `SKILL.md` says which it is, in its frontmatter; no count lives in this sentence, because a count in a sentence goes wrong the first time the table above gains a row. Every skill declares `allowed-tools` scoped to this package's own binary, so none of them needs a blanket shell grant.
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.
167
165
 
168
166
  ## What a key cannot see yet
169
167
 
170
- 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 tenant-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-setup` already reports on, is a separate thing. One thing 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 — 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:
171
169
 
172
170
  - Read pull-request labels or the reviewer's reaction. The mirror does not carry them; GitHub's own page does.
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.
173
172
 
174
173
  ## Versions and origins
175
174
 
176
- The package pins the tenant contract range `1.x || 2.x`, recorded in `package.json` under `catalystCloud.tenantContractRange`, and reports it in `--version`, `status` and `login`. A tenant whose contract version falls outside that range is refused with one line naming both versions; update the bundle. Every skill carries a `vendored-from:` line naming this package as its origin, and that line now also carries the version it was vendored at; all of them are written in this repository for customer tenants.
175
+ The package pins the contract range `1.x || 2.x`, recorded in `package.json` under `catalystCloud.tenantContractRange`, and reports it in `--version`, `status` and `login`. An account whose contract version falls outside that range is refused with one line naming both versions; update the bundle. Every skill carries a `vendored-from:` line naming this package as its origin, and that line now also carries the version it was vendored at; all of them are written in this repository for customer accounts.
177
176
 
178
177
  ## What it writes on your machine
179
178
 
180
179
  - `~/.config/catalyst-cloud/customer.json`, written with mode `0600`, holding your personal key, who you are, and the CLI path.
181
- - `~/.config/catalyst-cloud/contract.json`, the cached tenant contract.
180
+ - `~/.config/catalyst-cloud/contract.json`, your account's cached contract.
182
181
  - `~/.config/catalyst-cloud/published.json`, the cached answer to "what is the newest release" — no credential in it.
183
182
  - Only if you start them: `~/.config/catalyst-cloud/replica.db` with its `.pid`, `.writer.lock` and `.writer.state` sidecars, `$XDG_STATE_HOME/catalyst/events/<tenant>/backbone/` (or the home-directory fallback) with bounded daily event segments, and `~/.config/catalyst-cloud/watch-cursor.json`.
184
183
 
@@ -198,7 +197,7 @@ A `customer.json` written by an older bundle is still read unchanged; it gains t
198
197
 
199
198
  ## Uninstalling
200
199
 
201
- Remove the skills the way you installed them: `/plugin uninstall catalyst@catalyst-cloud` in Claude Code, or delete the skill directories (`catalyst-github`, `catalyst-linear`, `catalyst-onboard`, `catalyst-setup`, `connect-me`, `how-catalyst-works`, `run-this-project`, `unstick`, `what-needs-me`, `whats-happening`) from wherever `npx skills add` wrote them. Then remove what the CLI wrote:
200
+ Remove the skills the way you installed them: `/plugin uninstall catalyst@catalyst-cloud` in Claude Code, or delete the skill directories (`catalyst-github`, `catalyst-linear`, `catalyst-onboard`, `run-this-project`, `unstick`, `what-needs-me`, `what-this-repo-needs`, `whats-happening`) from wherever `npx skills add` wrote them. Skills an earlier release shipped (`catalyst-setup`, `connect-me`, `how-catalyst-works`) go the same way; the CLI removes its own stamped copies of them when it installs or refreshes skills. Then remove what the CLI wrote:
202
201
 
203
202
  ```sh
204
203
  catalyst replica stop
@@ -211,10 +210,10 @@ npm uninstall -g @catalyst-cloud/cli
211
210
  ## If login fails
212
211
 
213
212
  - `catalyst: GET /me failed (401): credential not accepted — mint a personal key at Settings → API keys and log in again` — the key is stale, mistyped or revoked. Mint a new one, then run `login` again.
214
- - `catalyst: GET /me failed (403): account-not-operational` — the tenant is suspended. This is an admin conversation on the tenant, not a local fix.
213
+ - `catalyst: GET /me failed (403): account-not-operational` — your cloud account is suspended. This is a conversation with your account's admin, not a local fix.
215
214
  - `catalyst: could not reach <url>: <detail>` — the machine cannot reach the cloud. The URL is named in the message; check `CATALYST_CLOUD_BASE_URL` or `--base-url`.
216
- - A line naming two contract versions after `Connected to` — the tenant serves a contract outside this bundle's `1.x || 2.x` range. The config is written; update the bundle before using the other skills.
217
- - `[catalyst] GET /api/v1/agent/contract refused (403): this cloud is older than the bundle …` on stderr — the cloud has not yet deployed personal-key access to the contract. Update the cloud, or connect with the tenant's account key until it has.
215
+ - A line naming two contract versions after `Connected to` — your account serves a contract outside this bundle's `1.x || 2.x` range. The config is written; update the bundle before using the other skills.
216
+ - `[catalyst] GET /api/v1/agent/contract refused (403): this cloud is older than the bundle …` on stderr — the cloud has not yet deployed personal-key access to the contract. Update the cloud, or connect with your account key until it has.
218
217
 
219
218
  ## License
220
219
 
package/dist/args.js CHANGED
@@ -111,6 +111,7 @@ export const FLAG_TABLES = {
111
111
  offline: { value: false, help: "skip the published-release check (no network)" },
112
112
  },
113
113
  accounts: {},
114
+ env: {},
114
115
  team: {
115
116
  all: { value: false, help: "check every team, one request at a time" },
116
117
  stage: { value: true, repeat: true, help: "map a role to a live Linear state name (role=StateName)" },
@@ -135,6 +136,19 @@ export const FLAG_TABLES = {
135
136
  },
136
137
  identity: {},
137
138
  capabilities: {},
139
+ project: {
140
+ team: { value: true, help: "the project's team key (default: the only mapped project)" },
141
+ },
142
+ 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)" },
146
+ },
147
+ 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" },
151
+ },
138
152
  connections: {
139
153
  wait: { value: true, help: "start: wait up to this many seconds for browser approval (0-600)" },
140
154
  },
@@ -169,10 +183,14 @@ export const VERB_USAGE = {
169
183
  accounts: "accounts [--json]",
170
184
  mcp: "mcp <add <name> --url URL <--auth none|--bearer SECRET_NAME|--header NAME=SECRET_NAME...>|list|remove <name>> [--json]",
171
185
  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]",
172
- environment: "environment [read] [--json] | environment propose --file <path>|--stdin [--expect-revision N] [--approve] [--json] | environment approve [--revision N --hash H] [--json]",
186
+ env: "env <inventory [path] | check <file>> [--json] (THIS repository, offline — no login, no network)",
187
+ 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)",
173
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]",
174
189
  identity: "identity linear <status|options|set> [<linearUserId>] [--json]",
175
190
  capabilities: "capabilities [--json]",
191
+ project: "project wip-limit <get|set <n>|set default> [--team K] [--json]",
192
+ repo: "repo <agents-block <path> [--write]|agent-setup <path> [--apply] [--with-check]> [--json]",
193
+ legacy: "legacy [--remove [--data] [--yes]] [--json]",
176
194
  connections: "connections personal <linear|github> <start|status> [--wait <seconds>] [--json]",
177
195
  release: "release <ticket> --because <what changed> [--retry-unchanged] [--dry-run] [--json] | release --class <failure-class> --team <K> --because <what changed> [--retry-unchanged] [--dry-run] [--limit N] [--json]",
178
196
  };
package/dist/ask.js CHANGED
@@ -86,8 +86,9 @@ export function inboxScope(cfg, anyone) {
86
86
  return { kind: "unmatched", label: cfg.user.label };
87
87
  return { kind: "mine", linearUserId: cfg.user.linearUserId, label: cfg.user.label };
88
88
  }
89
- /** Rank open asks by the priority-weighted count of open tickets each one blocks. Priority 1 (urgent)
90
- * weighs 4, 4 (low) weighs 1, 0 (none) weighs 1. */
89
+ /** Rank open asks the way the cloud's own Waiting-on-me feed does: the most open tickets blocked
90
+ * first, then the oldest ask first, then the identifier. `score` is the count of open tickets the
91
+ * ask blocks. Priority is not a weight here, and age only breaks a tie. */
91
92
  export function rankAsks(issues, doc, openState) {
92
93
  const askIds = new Set();
93
94
  for (const team of doc.teams)
@@ -103,11 +104,18 @@ export function rankAsks(issues, doc, openState) {
103
104
  for (const i of issues)
104
105
  if (typeof i.identifier === "string")
105
106
  byIdentifier.set(i.identifier, i);
106
- const weight = (issue) => {
107
- const p = typeof issue?.priority === "number" ? issue.priority : 0;
108
- return p >= 1 && p <= 4 ? 5 - p : 1;
107
+ // The cloud's age key: created, else updated; an ask with neither sorts after every dated one.
108
+ const ageKey = (issue) => {
109
+ for (const k of ["created_at", "updated_at"]) {
110
+ const v = issue[k];
111
+ if (typeof v === "number" && Number.isFinite(v))
112
+ return v;
113
+ if (typeof v === "string" && Number.isFinite(Date.parse(v)))
114
+ return Date.parse(v);
115
+ }
116
+ return Number.POSITIVE_INFINITY;
109
117
  };
110
- const out = [];
118
+ const ranked = [];
111
119
  for (const issue of issues) {
112
120
  if (!isAsk(issue) || !openState(issue))
113
121
  continue;
@@ -119,16 +127,20 @@ export function rankAsks(issues, doc, openState) {
119
127
  const t = byIdentifier.get(id);
120
128
  return !t || openState(t);
121
129
  });
122
- out.push({
123
- identifier: String(issue.identifier),
124
- title: String(issue.title ?? ""),
125
- state: String(issue.state ?? ""),
126
- blocks: blocked,
127
- score: blocked.reduce((sum, id) => sum + weight(byIdentifier.get(id)), 0),
128
- assigneeId: typeof issue.assignee_id === "string" && issue.assignee_id !== "" ? issue.assignee_id : null,
130
+ ranked.push({
131
+ age: ageKey(issue),
132
+ ask: {
133
+ identifier: String(issue.identifier),
134
+ title: String(issue.title ?? ""),
135
+ state: String(issue.state ?? ""),
136
+ blocks: blocked,
137
+ score: blocked.length,
138
+ assigneeId: typeof issue.assignee_id === "string" && issue.assignee_id !== "" ? issue.assignee_id : null,
139
+ },
129
140
  });
130
141
  }
131
- return out.sort((a, b) => b.score - a.score || a.identifier.localeCompare(b.identifier));
142
+ ranked.sort((a, b) => b.ask.score - a.ask.score || (a.age === b.age ? 0 : a.age < b.age ? -1 : 1) || a.ask.identifier.localeCompare(b.ask.identifier));
143
+ return ranked.map((r) => r.ask);
132
144
  }
133
145
  async function list(args, ctx, doc, api, cfg) {
134
146
  // ⛔ THE INBOX IS THE WHOLE SCOPE, NOT A PAGE. The mirror caps every page at 500 rows regardless of
@@ -146,16 +158,16 @@ async function list(args, ctx, doc, api, cfg) {
146
158
  return 0;
147
159
  }
148
160
  if (scope.kind === "unmatched") {
149
- ctx.stderr(`[catalyst] your Linear identity is not matched yet (an admin matches it in Settings → Members), so this is every open ask, not only yours`);
161
+ ctx.stderr(`[catalyst] your Linear identity is not matched yet (match it with: catalyst identity linear options, then catalyst identity linear set <linearUserId>), so this is every open ask, not only yours`);
150
162
  }
151
163
  else if (scope.kind === "no-person") {
152
- ctx.stderr(`[catalyst] connected with the tenant's account key, which names no person — this is every open ask; log in with your personal key to see only yours`);
164
+ ctx.stderr(`[catalyst] connected with your cloud account's shared key, which names no person — this is every open ask; log in with your personal key to see only yours`);
153
165
  }
154
166
  if (ranked.length === 0) {
155
- ctx.stdout(scope.kind === "mine" ? `no open asks assigned to ${scope.label} (${all.length} open in the tenant — add --anyone to see them)` : "no open asks");
167
+ ctx.stdout(scope.kind === "mine" ? `no open asks assigned to ${scope.label} (${all.length} open in your cloud account — add --anyone to see them)` : "no open asks");
156
168
  return 0;
157
169
  }
158
170
  for (const a of ranked)
159
- ctx.stdout(`${a.identifier} holds ${a.blocks.length} ticket${a.blocks.length === 1 ? "" : "s"} (weight ${a.score})${a.blocks.length ? `: ${a.blocks.join(", ")}` : ""} ${a.title}`);
171
+ ctx.stdout(`${a.identifier} holds ${a.blocks.length} ticket${a.blocks.length === 1 ? "" : "s"}${a.blocks.length ? `: ${a.blocks.join(", ")}` : ""} ${a.title}`);
160
172
  return 0;
161
173
  }
@@ -19,6 +19,11 @@ export const CAPABILITIES = [
19
19
  { verb: "team adopt", does: "create Catalyst's stages in a Linear team, from a preview the person approves", needs: "admin", routes: [agent("POST", "team-workflow/adopt")], since: "0.9.5" },
20
20
  { verb: "team migrate", does: "move tickets between stages, from a preview the person approves", needs: "admin", routes: [agent("POST", "team-workflow/migrate")], since: "0.9.5" },
21
21
  { verb: "team checklist", does: "print what adopting the workflow would do to a team", needs: "admin", routes: [agent("GET", "team-workflow")], since: "0.9.5" },
22
+ { verb: "project wip-limit get", does: "read a project's new-start WIP limit, where it comes from, and its work in progress now", needs: "member", routes: [agent("GET", "team-wip-limit")], since: "0.13.1" },
23
+ { verb: "project wip-limit set", does: "set a project's new-start WIP limit (0 to 9999, or back to the workspace default)", needs: "admin", routes: [agent("POST", "team-wip-limit")], since: "0.13.1" },
24
+ { verb: "repo agents-block", does: "add or refresh the one Catalyst block in a checkout's AGENTS.md, in the working tree, for the person to commit and open as a pull request", needs: "member", routes: [], since: "0.13.1" },
25
+ { verb: "repo agent-setup", does: "read a checkout's agent setup (AGENTS.md, CLAUDE.md, .agents and .claude skills and rules) and, on request, make it portable in the working tree", needs: "member", routes: [], since: "0.13.1" },
26
+ { verb: "legacy", does: "find leftovers of the old local Catalyst runtime on this machine and, on a yes, remove them through their own tools", needs: "member", routes: [], since: "0.13.1" },
22
27
  { verb: "environment read", does: "read the workspace-wide environment declaration", needs: "member", routes: [agent("GET", "account-environment")], since: "0.8.0" },
23
28
  { verb: "environment propose", does: "propose the workspace-wide environment declaration (names only)", needs: "admin", routes: [agent("POST", "account-environment/propose")], since: "0.8.0" },
24
29
  { verb: "environment approve", does: "approve the workspace-wide environment declaration", needs: "admin", routes: [agent("POST", "account-environment/approve")], since: "0.8.0" },
package/dist/cli.js CHANGED
@@ -1,4 +1,4 @@
1
- // cli.ts — the `catalyst-skills` dispatcher (npm: @catalyst-cloud/catalyst-skills). Verbs live in
1
+ // cli.ts — the `catalyst` dispatcher (npm: @catalyst-cloud/cli). Verbs live in
2
2
  // their own modules; this file keeps every export the 0.1 tests and the bin import.
3
3
  import { existsSync } from "node:fs";
4
4
  import { parseArgs, positionals, verbHelp } from "./args.js";
@@ -23,9 +23,13 @@ import { cmdAsk } from "./ask.js";
23
23
  import { cmdMcp } from "./mcp.js";
24
24
  import { cmdRelease } from "./release.js";
25
25
  import { cmdEnvironment } from "./environment.js";
26
+ import { cmdEnv } from "./env.js";
26
27
  import { cmdSecret } from "./secret.js";
27
28
  import { cmdTeam } from "./team.js";
28
29
  import { cmdCapabilities } from "./capabilities.js";
30
+ import { cmdProject } from "./project.js";
31
+ import { cmdRepo } from "./repo.js";
32
+ import { cmdLegacy } from "./legacy.js";
29
33
  import { cmdIdentity } from "./identity.js";
30
34
  import { cmdConnections } from "./connections.js";
31
35
  export { CONFIG_MODE, DEFAULT_BASE_URL, FIRST_STAMPED_VERSION, LEGACY_PACKAGE_NAME, PACKAGE_NAME, PROVENANCE_MARKER, CliError, MeError, UsageError, configPathFor, contractPathFor, defaultCtx, defaultReplicaDbFor, defaultSkillsDirFor, fetchMe, formatMode, installedBundleVersion, installSkills, loadConfig, modernCliPath, normalizeBaseUrl, parseArgs, parseChangelogEntry, parseProvenanceVersion, readChangelog, readManifest, resolveSkillsDir, saveConfig, skillsSourceDir, updateNoticeLine, writeConfig, };
@@ -33,12 +37,10 @@ export const CUSTOMER_SKILLS = [
33
37
  "catalyst-github",
34
38
  "catalyst-linear",
35
39
  "catalyst-onboard",
36
- "catalyst-setup",
37
- "connect-me",
38
- "how-catalyst-works",
39
40
  "run-this-project",
40
41
  "unstick",
41
42
  "what-needs-me",
43
+ "what-this-repo-needs",
42
44
  "whats-happening",
43
45
  ];
44
46
  /** `login` is the verb; `join` is the 0.1 name, kept as a deprecated alias for one minor version. */
@@ -67,10 +69,14 @@ export function usageText() {
67
69
  " catalyst write <comment|state|label|create|reaction|attachment|session> ...",
68
70
  " catalyst ask <raise|accept|list> ...",
69
71
  " catalyst release <ticket> --because <what changed> [--retry-unchanged] [--dry-run] | release --class <c> --team <K> ...",
70
- " catalyst environment [read] | environment propose --file <path>|--stdin [--approve] | environment approve",
72
+ " catalyst environment [read] | environment propose --file <path>|--stdin [--approve] | environment approve (your account; needs login)",
73
+ " catalyst env <inventory [path] | check <file>> (this repository, offline — no login, no network)",
71
74
  " catalyst secret set <NAME> --repo <owner/name> [--command '<cmd>'] | secret import <file> --repo <owner/name>",
72
75
  " catalyst team <list|check|map|adopt|migrate|checklist> ...",
73
76
  " catalyst capabilities [--json] (what this CLI can do, the role each verb needs, and whether this cloud serves it)",
77
+ " catalyst project wip-limit <get|set <n>|set default> [--team K] (a project's new-start WIP limit; members read, owners and admins set)",
78
+ " catalyst repo <agents-block <path> [--write]|agent-setup <path> [--apply] [--with-check]> (a checkout's AGENTS.md block and portable agent layout; working tree only)",
79
+ " catalyst legacy [--remove [--data] [--yes]] (leftovers of the old local Catalyst runtime: list them; remove them only on a yes)",
74
80
  " catalyst identity linear <status|options|set> [<linearUserId>] [--json]",
75
81
  " catalyst connections personal <linear|github> <start|status> [--wait <seconds>] [--json]",
76
82
  "",
@@ -165,6 +171,8 @@ function maybePrintUpdateNotice(args, ctx) {
165
171
  }
166
172
  if (refreshed.installed.length > 0)
167
173
  say(`[catalyst] refreshed ${refreshed.installed.join(", ")} at ${skillsDir} to ${manifest.version}`);
174
+ if (refreshed.removed.length > 0)
175
+ say(`[catalyst] removed retired skills ${refreshed.removed.join(", ")} from ${skillsDir}; their work moved into the skills this bundle ships`);
168
176
  for (const s of refreshed.skipped) {
169
177
  say(`[catalyst] left "${s.name}" alone: ${skillsDir}/${s.name} was not installed by this package (catalyst install --force to replace)`);
170
178
  }
@@ -245,12 +253,20 @@ export async function main(argv, ctx = defaultCtx(), deps = {}) {
245
253
  return await cmdRelease(args, ctx);
246
254
  case "environment":
247
255
  return await cmdEnvironment(args, ctx, deps.environment ?? {});
256
+ case "env":
257
+ return await cmdEnv(args, ctx, deps.env ?? {});
248
258
  case "secret":
249
259
  return await cmdSecret(args, ctx, deps.secret ?? {});
250
260
  case "team":
251
261
  return await cmdTeam(args, ctx, deps.team);
252
262
  case "capabilities":
253
263
  return await cmdCapabilities(args, ctx);
264
+ case "project":
265
+ return await cmdProject(args, ctx);
266
+ case "repo":
267
+ return await cmdRepo(args, ctx);
268
+ case "legacy":
269
+ return await cmdLegacy(args, ctx, deps.legacy ?? {});
254
270
  case "identity":
255
271
  return await cmdIdentity(args, ctx, deps.identity ?? {});
256
272
  case "connections":
@@ -278,7 +294,7 @@ export async function main(argv, ctx = defaultCtx(), deps = {}) {
278
294
  throw err;
279
295
  }
280
296
  }
281
- const VERB_HELP_KNOWN = Object.fromEntries(["login", "join", "install", "status", "notice", "me", "contract", "query", "replica", "runtime", "events", "explain", "running", "queue", "watch", "write", "ask", "ready", "accounts", "release", "secret", "environment", "connections", "identity", "mcp", "team", "capabilities"].map((v) => [v, true]));
297
+ const VERB_HELP_KNOWN = Object.fromEntries(["login", "join", "install", "status", "notice", "me", "contract", "query", "replica", "runtime", "events", "explain", "running", "queue", "watch", "write", "ask", "ready", "accounts", "release", "secret", "env", "environment", "connections", "identity", "mcp", "team", "capabilities", "project", "repo", "legacy"].map((v) => [v, true]));
282
298
  async function cmdLogin(args, ctx, deps) {
283
299
  const manifest = readManifest();
284
300
  const key = (args.key ?? ctx.env.CATALYST_CLOUD_TOKEN ?? "").trim();
@@ -371,6 +387,8 @@ function cmdInstall(args, ctx) {
371
387
  for (const s of result.skipped) {
372
388
  ctx.stdout(`Skipped "${s.name}": ${skillsDir}/${s.name} exists and was not installed by this package (use --force to replace)`);
373
389
  }
390
+ if (result.removed.length > 0)
391
+ ctx.stdout(`Removed retired skills from ${skillsDir}: ${result.removed.join(", ")}`);
374
392
  return 0;
375
393
  }
376
394
  function cmdStatus(ctx) {