@catalyst-cloud/cli 0.13.0 → 0.14.0

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 (149) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/README.md +27 -30
  3. package/dist/args.js +299 -61
  4. package/dist/ask.js +30 -18
  5. package/dist/capabilities.js +7 -0
  6. package/dist/cli.js +125 -20
  7. package/dist/config.js +3 -1
  8. package/dist/env/draft.js +134 -0
  9. package/dist/env/legacy.js +54 -0
  10. package/dist/env/parse-env.js +50 -0
  11. package/dist/env/settings-toml.js +66 -0
  12. package/dist/env.js +126 -26
  13. package/dist/environment.js +1 -1
  14. package/dist/legacy.js +224 -0
  15. package/dist/onboard-consent.js +65 -0
  16. package/dist/onboard.js +836 -0
  17. package/dist/project.js +191 -0
  18. package/dist/ready.js +11 -1
  19. package/dist/release.js +1 -1
  20. package/dist/repo.js +297 -0
  21. package/dist/runtime-verb.js +1 -1
  22. package/dist/secret.js +25 -4
  23. package/dist/skill-shape.js +21 -13
  24. package/dist/skills.js +44 -4
  25. package/dist/transport.js +26 -1
  26. package/dist/var.js +118 -0
  27. package/package.json +6 -4
  28. package/skills/catalyst-github/SKILL.md +7 -7
  29. package/skills/catalyst-github/references/is-it-mergeable.md +8 -2
  30. package/skills/catalyst-github/references/what-a-pr-accumulates.md +3 -3
  31. package/skills/catalyst-github/scripts/is-it-mergeable.mjs +3 -3
  32. package/skills/catalyst-github/scripts/lib/cli.mjs +9 -7
  33. package/skills/catalyst-github/scripts/lib/credential.mjs +2 -2
  34. package/skills/catalyst-github/scripts/read-pr.mjs +2 -2
  35. package/skills/catalyst-linear/SKILL.md +17 -19
  36. package/skills/catalyst-linear/agents/openai.yaml +1 -1
  37. package/skills/catalyst-linear/agents/portability.yaml +1 -1
  38. package/skills/catalyst-linear/references/reading-a-ticket.md +9 -26
  39. package/skills/catalyst-linear/references/what-a-ticket-accumulates.md +2 -2
  40. package/skills/catalyst-linear/references/writing-to-linear.md +7 -9
  41. package/skills/catalyst-linear/scripts/comment.mjs +1 -1
  42. package/skills/catalyst-linear/scripts/create-ticket.mjs +1 -1
  43. package/skills/catalyst-linear/scripts/label.mjs +1 -1
  44. package/skills/catalyst-linear/scripts/lib/cli.mjs +9 -7
  45. package/skills/catalyst-linear/scripts/lib/credential.mjs +2 -2
  46. package/skills/catalyst-linear/scripts/move.mjs +1 -1
  47. package/skills/catalyst-linear/scripts/read-ticket.mjs +1 -1
  48. package/skills/catalyst-linear/scripts/search.mjs +1 -1
  49. package/skills/catalyst-onboard/SKILL.md +37 -57
  50. package/skills/catalyst-onboard/agents/openai.yaml +2 -2
  51. package/skills/catalyst-onboard/agents/portability.yaml +1 -1
  52. package/skills/catalyst-onboard/references/connecting-this-machine.md +35 -0
  53. package/skills/catalyst-onboard/references/declaring-a-repository.md +18 -21
  54. package/skills/catalyst-onboard/references/local-sync.md +19 -7
  55. package/skills/catalyst-onboard/references/reading-ready.md +79 -0
  56. package/skills/catalyst-onboard/references/replacing-a-credential.md +8 -33
  57. package/skills/catalyst-onboard/references/repository-agent-setup.md +23 -0
  58. package/skills/catalyst-onboard/references/skill-sources.md +13 -19
  59. package/skills/catalyst-onboard/references/the-one-path.md +18 -74
  60. package/skills/catalyst-onboard/references/what-a-phase-needs.md +21 -28
  61. package/skills/catalyst-onboard/references/what-the-browser-owns.md +20 -37
  62. package/skills/{catalyst-setup → catalyst-onboard}/scripts/check.mjs +10 -9
  63. package/skills/catalyst-onboard/scripts/lib/cli.mjs +6 -4
  64. package/skills/catalyst-onboard/scripts/lib/credential.mjs +2 -2
  65. package/skills/catalyst-onboard/scripts/lib/project-coverage.mjs +53 -0
  66. package/skills/catalyst-onboard/scripts/where-am-i.mjs +929 -184
  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 +6 -13
  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 +10 -19
  96. package/skills/what-this-repo-needs/references/what-each-group-means.md +14 -44
  97. package/skills/what-this-repo-needs/scripts/check.mjs +13 -14
  98. package/skills/what-this-repo-needs/scripts/inventory.mjs +2 -2
  99. package/skills/what-this-repo-needs/scripts/lib/cli.mjs +11 -9
  100. package/skills/what-this-repo-needs/scripts/lib/credential.mjs +2 -2
  101. package/skills/whats-happening/SKILL.md +20 -30
  102. package/skills/whats-happening/agents/openai.yaml +1 -1
  103. package/skills/whats-happening/references/coding-accounts.md +11 -0
  104. package/skills/whats-happening/references/routing-work.md +4 -33
  105. package/skills/whats-happening/references/settings-and-where-they-live.md +17 -0
  106. package/skills/whats-happening/references/stages-and-mapping.md +19 -0
  107. package/skills/whats-happening/references/status-reply.md +11 -31
  108. package/skills/whats-happening/references/the-ladder.md +20 -0
  109. package/skills/whats-happening/references/what-runs-next.md +13 -0
  110. package/skills/whats-happening/references/when-a-phase-fails.md +21 -0
  111. package/skills/whats-happening/references/why-is-it-stuck.md +38 -62
  112. package/skills/whats-happening/scripts/explain.mjs +1 -1
  113. package/skills/whats-happening/scripts/lib/cli.mjs +7 -5
  114. package/skills/whats-happening/scripts/lib/credential.mjs +2 -2
  115. package/skills/{how-catalyst-works → whats-happening}/scripts/show-my-map.mjs +18 -32
  116. package/skills/whats-happening/scripts/snapshot.mjs +10 -6
  117. package/dist/env/declaration-rules.js +0 -163
  118. package/skills/catalyst-onboard/references/choosing-a-coding-account.md +0 -52
  119. package/skills/catalyst-onboard/references/who-fixes-what.md +0 -64
  120. package/skills/catalyst-setup/SKILL.md +0 -36
  121. package/skills/catalyst-setup/agents/openai.yaml +0 -6
  122. package/skills/catalyst-setup/agents/portability.yaml +0 -4
  123. package/skills/catalyst-setup/references/what-each-check-means.md +0 -88
  124. package/skills/catalyst-setup/scripts/lib/cli.mjs +0 -103
  125. package/skills/catalyst-setup/scripts/lib/credential.mjs +0 -29
  126. package/skills/catalyst-setup/scripts/replica-status.mjs +0 -46
  127. package/skills/connect-me/SKILL.md +0 -63
  128. package/skills/connect-me/agents/openai.yaml +0 -6
  129. package/skills/connect-me/agents/portability.yaml +0 -5
  130. package/skills/connect-me/references/keeping-the-replica-running.md +0 -88
  131. package/skills/connect-me/scripts/lib/cli.mjs +0 -185
  132. package/skills/connect-me/scripts/lib/credential.mjs +0 -29
  133. package/skills/connect-me/scripts/verify-connection.mjs +0 -68
  134. package/skills/how-catalyst-works/SKILL.md +0 -45
  135. package/skills/how-catalyst-works/agents/openai.yaml +0 -6
  136. package/skills/how-catalyst-works/agents/portability.yaml +0 -4
  137. package/skills/how-catalyst-works/references/coding-accounts.md +0 -51
  138. package/skills/how-catalyst-works/references/settings-and-where-they-live.md +0 -20
  139. package/skills/how-catalyst-works/references/stages-and-mapping.md +0 -56
  140. package/skills/how-catalyst-works/references/the-ladder.md +0 -41
  141. package/skills/how-catalyst-works/references/what-catalyst-is.md +0 -30
  142. package/skills/how-catalyst-works/references/what-runs-next.md +0 -95
  143. package/skills/how-catalyst-works/references/when-a-phase-fails.md +0 -57
  144. package/skills/how-catalyst-works/scripts/explain-ticket.mjs +0 -41
  145. package/skills/how-catalyst-works/scripts/lib/cli.mjs +0 -164
  146. package/skills/how-catalyst-works/scripts/lib/credential.mjs +0 -29
  147. package/skills/how-catalyst-works/scripts/whats-running.mjs +0 -65
  148. package/skills/whats-happening/references/reading-the-board.md +0 -43
  149. package/skills/whats-happening/references/reprioritising.md +0 -37
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,65 +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: {},
318
+ project: {
319
+ team: {
320
+ value: true,
321
+ help: "the project's team key (default: the only mapped project)",
322
+ },
323
+ },
324
+ legacy: {
325
+ remove: {
326
+ value: false,
327
+ help: "remove what was found, after one question (or --yes)",
328
+ },
329
+ data: {
330
+ value: false,
331
+ help: "with --remove: delete the old runtime's data folders too",
332
+ },
333
+ yes: {
334
+ value: false,
335
+ help: "with --remove: answer the question yes (a run with no terminal only reports otherwise)",
336
+ },
337
+ },
338
+ repo: {
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
+ },
351
+ },
139
352
  connections: {
140
- 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
+ },
141
357
  },
142
358
  release: {
143
- because: { value: true, help: "what changed since the ticket was held (required unless --dry-run)" },
144
- "retry-unchanged": { value: false, help: "release even though nothing the mirror can see changed (say what did in --because)" },
145
- "dry-run": { value: false, help: "show what a release would clear and refuse, and change nothing" },
146
- 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
+ },
147
375
  team: { value: true, help: "with --class: the team key" },
148
- 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
+ },
149
380
  },
150
381
  };
151
382
  export const VERB_USAGE = {
152
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]",
153
385
  install: "install [--skills-dir <dir>] [--force]",
154
386
  status: "status",
155
387
  notice: "notice",
156
388
  me: "me [--json]",
157
389
  contract: "contract [--refresh] [--path <a.b.c>] [--json]",
158
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]",
159
- 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>]',
160
392
  runtime: "runtime <status [--json]|install|path|uninstall>",
161
393
  events: "events <tail|wait-for|query|status [--probe] [--json]> [--type NAME] [--ticket CTC-N] [--after SEQUENCE] [--limit N] [--timeout SECONDS] [--directory PATH]",
162
394
  explain: "explain <ticket> [--history] [--json]",
@@ -170,11 +402,15 @@ export const VERB_USAGE = {
170
402
  accounts: "accounts [--json]",
171
403
  mcp: "mcp <add <name> --url URL <--auth none|--bearer SECRET_NAME|--header NAME=SECRET_NAME...>|list|remove <name>> [--json]",
172
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]",
173
- 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)",
174
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)",
175
- 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)",
176
409
  identity: "identity linear <status|options|set> [<linearUserId>] [--json]",
177
410
  capabilities: "capabilities [--json]",
411
+ project: "project <list [--json] | wip-limit <get|set <n>|set default> [--team K] [--json]>",
412
+ repo: "repo <agents-block <path> [--write]|agent-setup <path> [--apply] [--with-check]> [--json]",
413
+ legacy: "legacy [--remove [--data] [--yes]] [--json]",
178
414
  connections: "connections personal <linear|github> <start|status> [--wait <seconds>] [--json]",
179
415
  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]",
180
416
  };
@@ -217,7 +453,9 @@ export function parseArgs(argv) {
217
453
  }
218
454
  const spec = table[name];
219
455
  if (!spec) {
220
- 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}`);
221
459
  }
222
460
  if (spec.value) {
223
461
  const v = inlineValue ?? requireValue(argv, ++i, `--${name}`);
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
  }
@@ -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" },
@@ -19,10 +20,16 @@ export const CAPABILITIES = [
19
20
  { 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
21
  { 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
22
  { 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" },
23
+ { 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" },
24
+ { 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" },
25
+ { 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" },
26
+ { 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" },
27
+ { 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
28
  { verb: "environment read", does: "read the workspace-wide environment declaration", needs: "member", routes: [agent("GET", "account-environment")], since: "0.8.0" },
23
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" },
24
30
  { verb: "environment approve", does: "approve the workspace-wide environment declaration", needs: "admin", routes: [agent("POST", "account-environment/approve")], since: "0.8.0" },
25
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" },
26
33
  { verb: "accounts", does: "read the workspace's coding accounts and which can take work", needs: "member", routes: [], since: "0.5.0" },
27
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" },
28
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" },