@catalyst-cloud/cli 0.13.0 → 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 (137) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.md +26 -29
  3. package/dist/args.js +16 -0
  4. package/dist/ask.js +30 -18
  5. package/dist/capabilities.js +5 -0
  6. package/dist/cli.js +18 -5
  7. package/dist/environment.js +1 -1
  8. package/dist/legacy.js +224 -0
  9. package/dist/project.js +129 -0
  10. package/dist/release.js +1 -1
  11. package/dist/repo.js +297 -0
  12. package/dist/runtime-verb.js +1 -1
  13. package/dist/secret.js +1 -1
  14. package/dist/skill-shape.js +21 -13
  15. package/dist/skills.js +44 -4
  16. package/dist/transport.js +26 -1
  17. package/package.json +5 -4
  18. package/skills/catalyst-github/SKILL.md +7 -7
  19. package/skills/catalyst-github/references/is-it-mergeable.md +8 -2
  20. package/skills/catalyst-github/references/what-a-pr-accumulates.md +3 -3
  21. package/skills/catalyst-github/scripts/is-it-mergeable.mjs +3 -3
  22. package/skills/catalyst-github/scripts/lib/cli.mjs +9 -7
  23. package/skills/catalyst-github/scripts/lib/credential.mjs +2 -2
  24. package/skills/catalyst-github/scripts/read-pr.mjs +2 -2
  25. package/skills/catalyst-linear/SKILL.md +17 -19
  26. package/skills/catalyst-linear/agents/openai.yaml +1 -1
  27. package/skills/catalyst-linear/agents/portability.yaml +1 -1
  28. package/skills/catalyst-linear/references/reading-a-ticket.md +9 -26
  29. package/skills/catalyst-linear/references/what-a-ticket-accumulates.md +2 -2
  30. package/skills/catalyst-linear/references/writing-to-linear.md +7 -9
  31. package/skills/catalyst-linear/scripts/comment.mjs +1 -1
  32. package/skills/catalyst-linear/scripts/create-ticket.mjs +1 -1
  33. package/skills/catalyst-linear/scripts/label.mjs +1 -1
  34. package/skills/catalyst-linear/scripts/lib/cli.mjs +9 -7
  35. package/skills/catalyst-linear/scripts/lib/credential.mjs +2 -2
  36. package/skills/catalyst-linear/scripts/move.mjs +1 -1
  37. package/skills/catalyst-linear/scripts/read-ticket.mjs +1 -1
  38. package/skills/catalyst-linear/scripts/search.mjs +1 -1
  39. package/skills/catalyst-onboard/SKILL.md +37 -57
  40. package/skills/catalyst-onboard/agents/openai.yaml +2 -2
  41. package/skills/catalyst-onboard/agents/portability.yaml +1 -1
  42. package/skills/catalyst-onboard/references/connecting-this-machine.md +35 -0
  43. package/skills/catalyst-onboard/references/declaring-a-repository.md +7 -17
  44. package/skills/catalyst-onboard/references/local-sync.md +19 -7
  45. package/skills/catalyst-onboard/references/reading-ready.md +79 -0
  46. package/skills/catalyst-onboard/references/replacing-a-credential.md +8 -33
  47. package/skills/catalyst-onboard/references/repository-agent-setup.md +23 -0
  48. package/skills/catalyst-onboard/references/skill-sources.md +13 -19
  49. package/skills/catalyst-onboard/references/the-one-path.md +18 -74
  50. package/skills/catalyst-onboard/references/what-a-phase-needs.md +21 -28
  51. package/skills/catalyst-onboard/references/what-the-browser-owns.md +20 -37
  52. package/skills/{catalyst-setup → catalyst-onboard}/scripts/check.mjs +10 -9
  53. package/skills/catalyst-onboard/scripts/lib/cli.mjs +6 -4
  54. package/skills/catalyst-onboard/scripts/lib/credential.mjs +2 -2
  55. package/skills/catalyst-onboard/scripts/where-am-i.mjs +60 -4
  56. package/skills/run-this-project/SKILL.md +23 -25
  57. package/skills/run-this-project/agents/openai.yaml +1 -1
  58. package/skills/run-this-project/agents/portability.yaml +1 -1
  59. package/skills/run-this-project/references/making-work-ready.md +20 -37
  60. package/skills/run-this-project/references/reacting-to-events.md +25 -48
  61. package/skills/run-this-project/references/stalls-and-escalation.md +20 -42
  62. package/skills/run-this-project/scripts/lib/cli.mjs +7 -5
  63. package/skills/run-this-project/scripts/lib/credential.mjs +2 -2
  64. package/skills/run-this-project/scripts/scope-status.mjs +0 -0
  65. package/skills/run-this-project/scripts/watch-scope.mjs +1 -1
  66. package/skills/unstick/SKILL.md +12 -12
  67. package/skills/unstick/agents/openai.yaml +1 -1
  68. package/skills/unstick/agents/portability.yaml +1 -1
  69. package/skills/unstick/references/playbook.md +13 -13
  70. package/skills/unstick/scripts/lib/cli.mjs +6 -4
  71. package/skills/unstick/scripts/lib/credential.mjs +2 -2
  72. package/skills/unstick/scripts/unstick.mjs +1 -1
  73. package/skills/what-needs-me/SKILL.md +12 -14
  74. package/skills/what-needs-me/agents/openai.yaml +2 -2
  75. package/skills/what-needs-me/agents/portability.yaml +1 -1
  76. package/skills/what-needs-me/references/raising-a-decision.md +8 -16
  77. package/skills/what-needs-me/references/reading-the-inbox.md +6 -13
  78. package/skills/what-needs-me/references/settling-an-answer.md +6 -12
  79. package/skills/what-needs-me/scripts/inbox.mjs +14 -13
  80. package/skills/what-needs-me/scripts/lib/cli.mjs +6 -4
  81. package/skills/what-needs-me/scripts/lib/credential.mjs +2 -2
  82. package/skills/what-needs-me/scripts/raise.mjs +1 -1
  83. package/skills/what-needs-me/scripts/settle.mjs +1 -1
  84. package/skills/what-this-repo-needs/SKILL.md +10 -19
  85. package/skills/what-this-repo-needs/references/what-each-group-means.md +14 -44
  86. package/skills/what-this-repo-needs/scripts/check.mjs +2 -2
  87. package/skills/what-this-repo-needs/scripts/inventory.mjs +2 -2
  88. package/skills/what-this-repo-needs/scripts/lib/cli.mjs +11 -9
  89. package/skills/what-this-repo-needs/scripts/lib/credential.mjs +2 -2
  90. package/skills/whats-happening/SKILL.md +27 -21
  91. package/skills/whats-happening/agents/openai.yaml +1 -1
  92. package/skills/whats-happening/references/coding-accounts.md +24 -0
  93. package/skills/whats-happening/references/routing-work.md +11 -29
  94. package/skills/whats-happening/references/settings-and-where-they-live.md +20 -0
  95. package/skills/whats-happening/references/stages-and-mapping.md +25 -0
  96. package/skills/whats-happening/references/status-reply.md +21 -23
  97. package/skills/whats-happening/references/the-ladder.md +30 -0
  98. package/skills/whats-happening/references/what-runs-next.md +32 -0
  99. package/skills/whats-happening/references/when-a-phase-fails.md +27 -0
  100. package/skills/whats-happening/references/why-is-it-stuck.md +30 -44
  101. package/skills/whats-happening/scripts/explain.mjs +1 -1
  102. package/skills/whats-happening/scripts/lib/cli.mjs +7 -5
  103. package/skills/whats-happening/scripts/lib/credential.mjs +2 -2
  104. package/skills/{how-catalyst-works → whats-happening}/scripts/show-my-map.mjs +18 -32
  105. package/skills/whats-happening/scripts/snapshot.mjs +10 -6
  106. package/skills/catalyst-onboard/references/choosing-a-coding-account.md +0 -52
  107. package/skills/catalyst-onboard/references/who-fixes-what.md +0 -64
  108. package/skills/catalyst-setup/SKILL.md +0 -36
  109. package/skills/catalyst-setup/agents/openai.yaml +0 -6
  110. package/skills/catalyst-setup/agents/portability.yaml +0 -4
  111. package/skills/catalyst-setup/references/what-each-check-means.md +0 -88
  112. package/skills/catalyst-setup/scripts/lib/cli.mjs +0 -103
  113. package/skills/catalyst-setup/scripts/lib/credential.mjs +0 -29
  114. package/skills/catalyst-setup/scripts/replica-status.mjs +0 -46
  115. package/skills/connect-me/SKILL.md +0 -63
  116. package/skills/connect-me/agents/openai.yaml +0 -6
  117. package/skills/connect-me/agents/portability.yaml +0 -5
  118. package/skills/connect-me/references/keeping-the-replica-running.md +0 -88
  119. package/skills/connect-me/scripts/lib/cli.mjs +0 -185
  120. package/skills/connect-me/scripts/lib/credential.mjs +0 -29
  121. package/skills/connect-me/scripts/verify-connection.mjs +0 -68
  122. package/skills/how-catalyst-works/SKILL.md +0 -45
  123. package/skills/how-catalyst-works/agents/openai.yaml +0 -6
  124. package/skills/how-catalyst-works/agents/portability.yaml +0 -4
  125. package/skills/how-catalyst-works/references/coding-accounts.md +0 -51
  126. package/skills/how-catalyst-works/references/settings-and-where-they-live.md +0 -20
  127. package/skills/how-catalyst-works/references/stages-and-mapping.md +0 -56
  128. package/skills/how-catalyst-works/references/the-ladder.md +0 -41
  129. package/skills/how-catalyst-works/references/what-catalyst-is.md +0 -30
  130. package/skills/how-catalyst-works/references/what-runs-next.md +0 -95
  131. package/skills/how-catalyst-works/references/when-a-phase-fails.md +0 -57
  132. package/skills/how-catalyst-works/scripts/explain-ticket.mjs +0 -41
  133. package/skills/how-catalyst-works/scripts/lib/cli.mjs +0 -164
  134. package/skills/how-catalyst-works/scripts/lib/credential.mjs +0 -29
  135. package/skills/how-catalyst-works/scripts/whats-running.mjs +0 -65
  136. package/skills/whats-happening/references/reading-the-board.md +0 -43
  137. package/skills/whats-happening/references/reprioritising.md +0 -37
@@ -1,28 +1,21 @@
1
1
  ---
2
2
  name: what-this-repo-needs
3
3
  description: >-
4
- What environment variable names does this repository need, and where does each one come from? Scans the repository offline (no login, no network) and lists the names in three groups — build/test, deploy-only, bindings — each with where it was found, what uses it, and where a local value would come from. Never reads or prints a value. Also validates a catalyst.env.json file the same way the cloud does. Use when someone asks "what does catalyst.env.json mean", "what env vars does this repo need", "why does the container need this", or before reviewing or writing a repository's environment declaration.
5
- allowed-tools: Bash(catalyst-skills:*) Bash(npx @catalyst-cloud/catalyst-skills:*)
4
+ What environment variable names does this repository need, and where does each one come from? Scans the repository offline (no login, no network) and lists the names in three groups (build/test, deploy-only, bindings), each with where it was found, what uses it, and where a local value would come from. Never reads or prints a value. Also validates a catalyst.env.json file the same way the cloud does. Use when someone asks "what does catalyst.env.json mean", "what env vars does this repo need", "why does the container need this", or before reviewing or writing a repository's environment declaration.
5
+ allowed-tools: Bash(catalyst:*) Bash(npx -p @catalyst-cloud/cli catalyst:*)
6
6
  ---
7
- <!-- vendored-from: @catalyst-cloud/catalyst-skills@0.13.0 — written in this repository for customer tenants -->
7
+ <!-- vendored-from: @catalyst-cloud/catalyst-skills@0.13.1 — written in this repository for customer accounts -->
8
8
 
9
9
  # What this repository needs
10
10
 
11
- Say this first, in your own words, before you scan: the fleet builds and tests this repository in a
12
- container that has only what is declared — nothing more, nothing guessed. The names below are what
13
- that container needs; none of them is a value, and this tool never reads or shows one.
11
+ Say this first, in your own words, before you scan: the fleet builds and tests this repository in a container that has only what is declared, nothing guessed. The names below are what that container needs; none of them is a value, and this tool never reads or shows one.
14
12
 
15
13
  ## Run first
16
14
 
17
- - `node scripts/inventory.mjs [path] --help` — scans the repository (default: here) and prints the
18
- names in three groups: build/test, deploy-only, bindings. Each name shows where it was found
19
- (file:line), what uses it, and where a local value would come from — a `.env` file, your shell, a
20
- CI secret, or a Cloudflare binding. `--json` gives the same data in machine form.
21
- - `node scripts/check.mjs <file> --help` — validates an existing `catalyst.env.json` offline, with
22
- the same rules the cloud applies, and never prints a value from it either.
15
+ - `node scripts/inventory.mjs [path] --help`: scans the repository (default: here) and prints the names in three groups: build/test, deploy-only, bindings. Each name shows where it was found (file:line), what uses it, and where a local value would come from: a `.env` file, your shell, a CI secret, or a Cloudflare binding. `--json` gives the same data in machine form.
16
+ - `node scripts/check.mjs <file> --help`: validates an existing `catalyst.env.json` offline with the cloud's rules, and never prints a value from it either.
23
17
 
24
- Then ask the person to review the grouped list: for each name, keep it, drop it, or move it to a
25
- different group. Keep your own reasoning short — the list is the point, not your commentary on it.
18
+ Then ask the person to review the grouped list: for each name, keep it, drop it, or move it to a different group. Keep your own reasoning short; the list is the point.
26
19
 
27
20
  ## Load on demand
28
21
 
@@ -32,9 +25,7 @@ different group. Keep your own reasoning short — the list is the point, not yo
32
25
 
33
26
  ## Rules
34
27
 
35
- - Report, never repair. This tool never writes `catalyst.env.json` and never proposes anything.
36
- - Never print a value. Not a `.env` value, not a secret, not a committed `wrangler.toml [vars]`
37
- value — only the name, where it was found, and where a local value would come from.
28
+ - Report only. This tool writes no `catalyst.env.json` and proposes nothing.
29
+ - Print names only: never a `.env` value, a secret, or a committed `wrangler.toml [vars]` value. Give the name, where it was found, and where a local value would come from.
38
30
  - A Cloudflare (or other platform) binding is not an environment value; keep the two apart.
39
- - This reads the repository only. It needs no login and makes no network call — unlike the
40
- account-scope `catalyst-skills environment` verb, which reads your tenant's own declaration.
31
+ - This reads the repository only, with no login and no network call. The account-scope `catalyst environment` verb reads your cloud account's own declaration.
@@ -1,68 +1,38 @@
1
1
  # What each group means
2
2
 
3
- `node scripts/inventory.mjs` sorts every environment variable name it finds into exactly one of
4
- three groups. This page explains what each one means and why a name lands where it does — it never
5
- restates a value, because the scan itself never reads one.
3
+ `node scripts/inventory.mjs` sorts every environment variable name it finds into exactly one of three groups. This page explains why a name lands where it does; the scan never reads a value.
6
4
 
7
5
  ## build/test
8
6
 
9
- Needed to install, build and run the tests in a container. This is the default group: a name lands
10
- here unless something specific pulls it into `deploy-only` or `bindings`. Examples: a name read by
11
- the repository's own source (`process.env.X`), a name documented in a `.env.example`-shaped file, a
12
- name a non-deploy workflow job uses.
7
+ Needed to install, build and run the tests in a container. This is the default group: a name lands here unless something specific pulls it into `deploy-only` or `bindings`. Examples: a name the source reads (`process.env.X`), a name in a `.env.example`-shaped file, a name a non-deploy workflow job uses.
13
8
 
14
- The scan does not read `package.json` scripts. A name that only a script's own shell line mentions
15
- and nothing else refers to is therefore not listed — add it by hand if the container needs it.
9
+ The scan does not read `package.json` scripts. A name only a script's shell line mentions is therefore not listed; add it by hand if the container needs it.
16
10
 
17
- A name referenced from **both** a deploy job and a non-deploy job (or build step) still lands here,
18
- not in `deploy-only` — a container that builds and tests the repository needs it too, so the
19
- stricter requirement wins.
11
+ A name referenced from **both** a deploy job and a non-deploy job (or build step) lands here, because a container that builds and tests the repository needs it too.
20
12
 
21
13
  ## deploy-only
22
14
 
23
- A CI secret used **only** by a job the scan classifies as deploying something: a workflow job that
24
- sets `environment:`, whose id looks like `deploy`, `publish`, `release` or `cd`, or that runs a
25
- recognised deploy command (`wrangler deploy`, `npm publish`, `gh release create`, and similar). If
26
- the same name is referenced anywhere outside a deploy job, it moves to `build/test` instead.
27
-
28
- Its local value comes from a CI secret — something a repository or organisation admin sets in the
29
- CI provider, not something a person types into a local `.env` file.
15
+ A CI secret used **only** by a job the scan classifies as deploying: a workflow job that sets `environment:`, whose id looks like `deploy`, `publish`, `release` or `cd`, or that runs a recognised deploy command (`wrangler deploy`, `npm publish`, `gh release create`, and similar). A reference anywhere outside a deploy job moves the name to `build/test`. Its local value is a CI secret a repository or organisation admin sets in the CI provider.
30
16
 
31
17
  ## bindings
32
18
 
33
- A Cloudflare (or other platform) binding: a name bound to a resource — a KV namespace, a D1
34
- database, a Durable Object, an R2 bucket, a queue — declared in `wrangler.toml`. A binding is not an
35
- environment value a process reads from `process.env`; it is wired up by the platform at deploy
36
- time, which is why this scan keeps the two apart even though both are "names the container needs".
37
-
38
- A `wrangler.toml` binding table names the binding through one attribute's value — `binding = "NAME"`
39
- for most tables, `name = "NAME"` for `durable_objects.bindings` — never through the table's own key.
19
+ A Cloudflare (or other platform) binding: a name bound to a resource (a KV namespace, a D1 database, a Durable Object, an R2 bucket, a queue) declared in `wrangler.toml`. The platform wires it up at deploy time, so it is not a `process.env` value, and the scan keeps the two apart. A binding table names the binding through one attribute's value (`binding = "NAME"` for most tables, `name = "NAME"` for `durable_objects.bindings`), never through the table's own key.
40
20
 
41
21
  ## wrangler.toml `[vars]`: build/test, not a binding, and nothing to declare
42
22
 
43
- A `[vars]` (or `[env.<name>.vars]`) table is different from every other wrangler table: its keys ARE
44
- the names, and their values are committed in `wrangler.toml` itself — checked into the repository,
45
- not a secret and not something a person supplies locally. So a `[vars]` name lands in `build/test`
46
- (it is a plain value a build or test step could read, not a resource binding), and the scan marks it
47
- `needsDeclaration: false`: there is nothing for a customer to declare, because the value is already
48
- sitting in the file everyone can already read.
23
+ A `[vars]` (or `[env.<name>.vars]`) table's keys ARE the names, and their values are committed in `wrangler.toml` itself, readable by everyone. So a `[vars]` name lands in `build/test` as a plain value a step could read, marked `needsDeclaration: false`: there is nothing to declare.
49
24
 
50
25
  ## Where a local value comes from
51
26
 
52
- For every name, "where the local value comes from" is one of:
27
+ For every name, one of:
53
28
 
54
- - **a `.env` file or your shell** — the ordinary case for `build/test`.
55
- - **a CI secret** — for `deploy-only`, and for any `build/test` name whose only non-committed source
56
- is a workflow's `secrets.X` reference.
57
- - **a Cloudflare binding** — for `bindings`; there is no local value to supply, the platform wires
58
- it up.
59
- - **value committed in wrangler.toml — nothing to declare** — for a `[vars]` entry.
29
+ - **a `.env` file or your shell:** the ordinary case for `build/test`.
30
+ - **a CI secret:** for `deploy-only`, and for a `build/test` name whose only non-committed source is a workflow's `secrets.X` reference.
31
+ - **a Cloudflare binding:** for `bindings`; the platform wires it, so there is no local value.
32
+ - `value committed in wrangler.toml — nothing to declare`: for a `[vars]` entry.
60
33
 
61
- None of these is the value itself — only where it would come from, so a person reviewing the list
62
- knows what to check without this tool ever showing them what is actually set.
34
+ Each says where a value would come from, never the value, so a reviewer knows what to check without seeing what is set.
63
35
 
64
36
  ## A `wrangler.jsonc` or `wrangler.json` file
65
37
 
66
- Modern Wrangler configs are often JSONC. This scan does not read them yet — `wrangler.toml` is the
67
- only Cloudflare config format it understands — so a repository configured that way gets a note
68
- saying so, rather than a silent, confidently wrong "no bindings found".
38
+ The scan reads only `wrangler.toml`, so a repository configured with JSONC gets a note saying so rather than a confident "no bindings found".
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  // check.mjs — validates a catalyst.env.json file offline, with the same rules this bundle vendors
3
3
  // from the cloud's own validator. Never reads or prints a value. Offline: no login, no network —
4
- // wraps `catalyst-skills env check`.
4
+ // wraps `catalyst env check`.
5
5
  import { runCliOffline } from "./lib/cli.mjs";
6
6
 
7
7
  const HELP = `Usage: node scripts/check.mjs <file> [--json]
@@ -30,7 +30,7 @@ if (res.stderr) process.stderr.write(res.stderr);
30
30
  await finish(res.code);
31
31
 
32
32
  /**
33
- * ⛔ NEVER `process.exit(code)` STRAIGHT AFTER A WRITE — the same rule `bin/catalyst-skills.js`
33
+ * ⛔ NEVER `process.exit(code)` STRAIGHT AFTER A WRITE — the same rule `bin/launch.js`
34
34
  * states in full, reintroduced here at validate attempt 29 (M-3). When this script's own stdout is a
35
35
  * PIPE — which is how an agent harness runs it — writes are asynchronous, so `process.exit` severs
36
36
  * whatever is still in flight AND STILL REPORTS THE ORIGINAL EXIT CODE. Measured: a `--json` body of
@@ -2,7 +2,7 @@
2
2
  // inventory.mjs — lists the environment variable NAMES this repository needs, grouped build/test,
3
3
  // deploy-only and bindings, each with where it was found, what uses it, and where a local value
4
4
  // would come from. Never reads or prints a value. Offline: no login, no network — wraps
5
- // `catalyst-skills env inventory`.
5
+ // `catalyst env inventory`.
6
6
  import { runCliOffline } from "./lib/cli.mjs";
7
7
 
8
8
  const HELP = `Usage: node scripts/inventory.mjs [path] [--json]
@@ -27,7 +27,7 @@ if (res.stderr) process.stderr.write(res.stderr);
27
27
  await finish(res.code);
28
28
 
29
29
  /**
30
- * ⛔ NEVER `process.exit(code)` STRAIGHT AFTER A WRITE — the same rule `bin/catalyst-skills.js`
30
+ * ⛔ NEVER `process.exit(code)` STRAIGHT AFTER A WRITE — the same rule `bin/launch.js`
31
31
  * states in full, reintroduced here at validate attempt 29 (M-3). When this script's own stdout is a
32
32
  * PIPE — which is how an agent harness runs it — writes are asynchronous, so `process.exit` severs
33
33
  * whatever is still in flight AND STILL REPORTS THE ORIGINAL EXIT CODE. Measured: a `--json` body of
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  // lib/cli.mjs — the one way a skill script reaches the Catalyst Cloud SDK and API: by spawning the
3
- // catalyst-skills CLI this bundle installed. It reads customer.json to find that CLI and nothing
3
+ // catalyst CLI this bundle installed. It reads customer.json to find that CLI and nothing
4
4
  // else; it never holds the key itself. This file is a library — run a sibling script with
5
5
  // --help for usage. Per-skill (skills install one directory at a time, so nothing shared outside the
6
6
  // skill would ever be installed) — see runCliOffline below for the one way this skill's copy differs.
@@ -10,7 +10,9 @@ import { join } from "node:path";
10
10
  import { fileURLToPath } from "node:url";
11
11
  import { CONNECT_COMMAND, hasCredential } from "./credential.mjs";
12
12
 
13
- export const PACKAGE = "@catalyst-cloud/catalyst-skills";
13
+ export const PACKAGE = "@catalyst-cloud/cli";
14
+ /** The package ships two commands, so npx needs the one to run named: `npx -p <package> catalyst`. */
15
+ export const NPX_ARGS = ["-p", PACKAGE, "catalyst"];
14
16
  export const CONNECT_HINT = `this machine is not connected to a tenant yet — run: ${CONNECT_COMMAND}`;
15
17
 
16
18
  /** ~/.config/catalyst-cloud/customer.json, honouring CATALYST_SKILLS_HOME before HOME. */
@@ -41,8 +43,8 @@ export function requireCustomerConfig() {
41
43
  }
42
44
 
43
45
  /**
44
- * Run one catalyst-skills verb and return {code, stdout, stderr}. Spawns the CLI whose path the
45
- * connect step recorded in customer.json; falls back to `npx @catalyst-cloud/catalyst-skills` when
46
+ * Run one catalyst verb and return {code, stdout, stderr}. Spawns the CLI whose path the
47
+ * connect step recorded in customer.json; falls back to `npx -p @catalyst-cloud/cli catalyst` when
46
48
  * no path is recorded or it no longer exists. Exits 2 when the machine is not connected.
47
49
  */
48
50
  export function runCli(args, opts = {}) {
@@ -51,12 +53,12 @@ export function runCli(args, opts = {}) {
51
53
  }
52
54
 
53
55
  /**
54
- * Run `catalyst-skills env inventory` / `env check` without requiring a credential at all. Unlike
56
+ * Run `catalyst env inventory` / `env check` without requiring a credential at all. Unlike
55
57
  * every other verb this launcher can reach, `env inventory` and `env check` are LOCAL and OFFLINE —
56
58
  * they never call the cloud — so gating them on `requireCustomerConfig` would break the feature they
57
59
  * exist to provide. Still resolves the recorded `cliPath` when a customer.json happens to exist (so
58
60
  * an already-connected machine reuses the same installed CLI), and still falls back to
59
- * `npx @catalyst-cloud/catalyst-skills` otherwise.
61
+ * `npx -p @catalyst-cloud/cli catalyst` otherwise.
60
62
  */
61
63
  export function runCliOffline(args, opts = {}) {
62
64
  let cfg = null;
@@ -72,7 +74,7 @@ export function runCliOffline(args, opts = {}) {
72
74
  function spawnCli(cfg, args, opts) {
73
75
  const recorded = Boolean(cfg) && typeof cfg.cliPath === "string" && existsSync(cfg.cliPath);
74
76
  const command = recorded ? process.execPath : process.platform === "win32" ? "npx.cmd" : "npx";
75
- const argv = recorded ? [cfg.cliPath, ...args] : [PACKAGE, ...args];
77
+ const argv = recorded ? [cfg.cliPath, ...args] : [...NPX_ARGS, ...args];
76
78
  const res = spawnSync(command, argv, {
77
79
  encoding: "utf8",
78
80
  input: opts.stdin,
@@ -81,7 +83,7 @@ function spawnCli(cfg, args, opts) {
81
83
  shell: !recorded && process.platform === "win32",
82
84
  });
83
85
  if (res.error) {
84
- console.error(`could not run ${recorded ? cfg.cliPath : `npx ${PACKAGE}`}: ${res.error.message}`);
86
+ console.error(`could not run ${recorded ? cfg.cliPath : `npx -p ${PACKAGE} catalyst`}: ${res.error.message}`);
85
87
  process.exit(2);
86
88
  }
87
89
  return { code: res.status ?? 1, stdout: res.stdout ?? "", stderr: res.stderr ?? "" };
@@ -95,7 +97,7 @@ function spawnCli(cfg, args, opts) {
95
97
  export function runCliOrExit(args, opts = {}) {
96
98
  const res = runCli(args, opts);
97
99
  if (res.code === 0) return res;
98
- const why = res.stderr.trim() || res.stdout.trim() || `catalyst-skills ${args.join(" ")} exited ${res.code}`;
100
+ const why = res.stderr.trim() || res.stdout.trim() || `catalyst ${args.join(" ")} exited ${res.code}`;
99
101
  console.error(why);
100
102
  process.exit(res.code === 2 ? 2 : 1);
101
103
  }
@@ -6,12 +6,12 @@
6
6
  //
7
7
  // customer.json carries exactly one credential: a personal key (`key`), or the keyless login's
8
8
  // session (`auth`, the recommended rail). A script never reads either for its value: it spawns the
9
- // catalyst-skills CLI, which authenticates with whichever is present and refreshes a login's token
9
+ // catalyst CLI, which authenticates with whichever is present and refreshes a login's token
10
10
  // itself. A new credential kind lands here, once.
11
11
 
12
12
  /** The command that connects this machine, as every not-connected line names it. */
13
13
  export const CONNECT_COMMAND =
14
- "npx @catalyst-cloud/catalyst-skills login (or, with a personal key: CATALYST_CLOUD_TOKEN=<your personal key> npx @catalyst-cloud/catalyst-skills login)";
14
+ "npx -p @catalyst-cloud/cli catalyst login (or, with a personal key: CATALYST_CLOUD_TOKEN=<your personal key> npx -p @catalyst-cloud/cli catalyst login)";
15
15
 
16
16
  /** True when `cfg` (parsed customer.json) holds a usable credential of either kind. Never throws. */
17
17
  export function hasCredential(cfg) {
@@ -1,43 +1,49 @@
1
1
  ---
2
2
  name: whats-happening
3
3
  description: >-
4
- The desk for a Catalyst Cloud tenant. Use when the person asks "what's happening?", "where are we?", "why is that stuck?", "what closed?", "what's next?", or asks for something to be done rather than known. Reads the tenant contract, what is running and queued, the eligibility explainer and the open asks through the catalyst-skills CLI, and answers in one reply with ticket ids. Routes work to a project owner and decisions to what-needs-me. Never composes a URL, never polls, never answers as the human.
5
- allowed-tools: Bash(catalyst-skills:*) Bash(npx @catalyst-cloud/catalyst-skills:*)
4
+ The desk for a Catalyst Cloud account, and the facts behind it. Use when the person asks "what's happening?", "where are we?", "why is that stuck?", "what closed?", "what's next?", "how does this work?", "why did it do that?", "how does it prioritise?" or "what does this setting do, where is it set?", or asks for something to be done rather than known. Reads the contract, what is running and queued, the eligibility explainer, the coding accounts and the open asks through the catalyst CLI, and answers in one reply with ticket ids. Explains 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. Read-only: never composes a URL, never polls, never answers as the human.
5
+ allowed-tools: Bash(catalyst:*) Bash(npx -p @catalyst-cloud/cli catalyst:*)
6
6
  ---
7
- <!-- vendored-from: @catalyst-cloud/catalyst-skills@0.13.0 — written in this repository for customer tenants -->
7
+ <!-- vendored-from: @catalyst-cloud/catalyst-skills@0.13.1 — written in this repository for customer accounts -->
8
8
 
9
9
  # What's happening
10
10
 
11
- You are the one desk the person talks to about their Catalyst Cloud tenant. They ask four questions: where are we, why is that stuck, what closed, what is next. You answer each in one reply, from their tenant's data only, with a ticket identifier on every line. When they ask for something to be done, you route it to an owner; when only they can decide something, you raise it through `what-needs-me`.
11
+ You are the one desk the person talks to about their Catalyst Cloud account: where things stand, why something is stuck, what closed, what is next, and how Catalyst works. Answer each in one reply from their own account's data.
12
+
13
+ Every account-specific fact (stage names and ids, labels, the ladder keying, thresholds, merge policy) comes from the contract, `GET /api/v1/agent/contract`; the references hold only what is the same for every account.
12
14
 
13
15
  ## Run first
14
16
 
15
- Scripts are run, never read. Each prints `--help`; exit 2 means this machine is not connected (run the `connect-me` skill), exit 1 means the check itself failed.
17
+ Scripts are run, never read; each prints `--help`. Exit 2 means this machine is not connected (`catalyst-onboard`), exit 1 that the check itself failed.
16
18
 
17
- - `node scripts/snapshot.mjs --help` — one JSON document: the trimmed contract (teams, stage names by slot, thresholds, ladder), what is running, the queue, the open asks ranked by what they hold, and the replica verdict. Add `--board` for open tickets grouped by stage in the contract's slot order, `--team K` to narrow.
18
- - `node scripts/explain.mjs --help` — why nothing is offered for one ticket, in one paragraph.
19
+ - `node scripts/snapshot.mjs`: the trimmed contract, what is running, the queue, the ranked open asks, and the replica verdict. `--board` adds open tickets by stage, `--accounts` the coding accounts, `--team K` narrows.
20
+ - `node scripts/explain.mjs <ticket>`: why one ticket is or is not about to run. `--history` adds attempts, rounds, holds and past releases.
21
+ - `node scripts/show-my-map.mjs [--team K]`: this account's slot-to-stage map, labels, ladder and live thresholds.
19
22
 
20
- For a single ticket's comments, relations and linked PRs use the `catalyst-linear` skill (`catalyst-skills query issue <id>`); for a pull request's checks and threads, `catalyst-github`.
23
+ One ticket's comments and relations are the `catalyst-linear` skill; a pull request's checks and threads, `catalyst-github`; readiness ("am I set up?"), `catalyst-onboard`.
21
24
 
22
25
  ## Load on demand
23
26
 
24
27
  | when | read |
25
28
  | -- | -- |
26
- | writing any status answer | `references/status-reply.md` (schema: `assets/status-reply.json`) |
27
- | the board looks wrong, a column is long, or you must decide whether something is stuck | `references/reading-the-board.md` |
29
+ | writing any status answer, or deciding whether something is stuck | `references/status-reply.md` (schema: `assets/status-reply.json`) |
28
30
  | `explain` returned a reason and the person wants the next action | `references/why-is-it-stuck.md` |
29
- | "do this one first", "why is that not next", "stop that" | `references/reprioritising.md` |
31
+ | "what runs next?", "do this one first", the WIP limit, capacity, routing | `references/what-runs-next.md` |
32
+ | a phase failed, a card went to Remediate, a ticket is parked or on hold | `references/when-a-phase-fails.md` |
30
33
  | the person asks for work to be done, or a decision surfaces | `references/routing-work.md` |
31
-
32
- For depth beyond these, load the fact skills: `how-catalyst-works` (the ladder, slots and mapping, failure layers, queue order, coding accounts), `catalyst-linear` (what a ticket accumulates, reading and writing), `catalyst-github` (what a PR accumulates, mergeability), `catalyst-setup` (readiness).
34
+ | "what is Catalyst Cloud?", the phases, what each produces, when a ticket is Done | `references/the-ladder.md` |
35
+ | "which column is which, why does nothing dispatch, what is a slot?" | `references/stages-and-mapping.md` |
36
+ | "why is nothing running", walls, quarantine, which provider ran a phase | `references/coding-accounts.md` |
37
+ | "what does this setting do, where is it set, who can change it?" | `references/settings-and-where-they-live.md` |
33
38
 
34
39
  ## Rules
35
40
 
36
- - **Their tenant, as them.** Every read goes through the CLI, which holds the person's own key, the tenant and who they are. You never name, guess at, or try another tenant, and you never paste the key anywhere. "You" in your reply means the connected person: their assigned tickets, their asks.
37
- - **Tenant facts come from the contract, live.** Stage names, label names, team keys, thresholds and the ladder are in the snapshot's `tenant` block; read them there each time and never restate them from memory.
38
- - **One reply, ticket ids on every line, source named.** The reply opens with when the snapshot was taken and whether the replica or the API answered. A stale replica is stated, never hidden.
39
- - **Say what a key cannot see.** PR labels and reactions are not mirrored; the CLI prints the settings URL, and you repeat it instead of guessing. A park or hold is released from the person's own login once its cause is fixed: route that to the `unstick` skill (`catalyst-skills release`), never tell them it needs an operator. Coding-account status (`accounts`) and per-ticket execution history (`explain --history`) are readable — read them.
40
- - **No polling.** One snapshot per question. Waiting on a change is the project owner's job (`run-this-project` subscribes to the tenant stream); the desk never loops a read.
41
- - **You are not the owner.** You route work and make it visible; you do not dispatch, overrule a project owner, or run long work in this session.
42
- - **Never answer as the human.** A decision is an ask through `what-needs-me`, filed before anyone proceeds on its default; the answer is recorded there as the app actor.
43
- - **Cite an identifier only after a create call returned it.**
41
+ - **Their account, as them.** Every read goes through the CLI with the person's own login; "you" in a reply means the connected person.
42
+ - **Print, never recall.** A stage name, label, threshold or route in your answer comes from a script's output in this session. A settings answer names the screen, its route and the rule from `references/settings-and-where-they-live.md`.
43
+ - **One reply, ticket ids on every line, source named** (`references/status-reply.md`).
44
+ - **A reason is a row** in `references/why-is-it-stuck.md`: name what releases it and who can. Unknown is not absent; report it as inconclusive.
45
+ - **Parks and holds have their own release.** Once the cause is fixed, the person's own login releases a park, a no-change hold or a validate-budget hold through the `unstick` skill (`catalyst release`). A round-threshold hold and a review that will not converge refuse that release; `references/when-a-phase-fails.md` says what clears each.
46
+ - **Say what a key cannot see.** PR labels and reactions are not mirrored; repeat the settings URL the CLI prints. Coding accounts and execution history are readable, so read them.
47
+ - **System causes are one alert.** A provider outage, the runner image breaker or a paused repository is said once, for every ticket it holds.
48
+ - **One snapshot per question.** Waiting on a change is the project owner's job (`run-this-project` subscribes to the stream).
49
+ - **You route; the owner acts** (`references/routing-work.md`). The human answers for themselves: a decision is an ask through `what-needs-me`, filed before anyone proceeds on its default.
@@ -1,6 +1,6 @@
1
1
  interface:
2
2
  display_name: "What's happening"
3
- short_description: "Where your Catalyst work stands: in flight, blocked and on whom, closed, and what runs next"
3
+ short_description: "Where your Catalyst work stands and how Catalyst decides: in flight, blocked and on whom, closed, what runs next, and why"
4
4
  default_prompt: "Use $whats-happening to tell me where my work stands and why anything is stuck."
5
5
  policy:
6
6
  allow_implicit_invocation: true
@@ -0,0 +1,24 @@
1
+ # Coding accounts: slots, windows, walls
2
+
3
+ The person's own rows print with `node scripts/snapshot.mjs --accounts`, with no credential or email. An owner or admin enrols, pauses or removes one at `<their cloud>/settings/coding-accounts`.
4
+
5
+ ## Slots and providers
6
+
7
+ A slot is one enrolled credential that runs phases; identity is the credential, so one email may hold several. Claude, GLM and Qwen run under the Claude CLI, Codex under Codex, and GLM and Qwen also under OpenCode. The routing rows (`references/what-runs-next.md`), not the harness, name the model.
8
+
9
+ ## State
10
+
11
+ - **Declared:** `active` or `disabled`, set by the operator.
12
+ - **Observed:** `healthy`, `degraded` or `unknown`, set by the poller (every 5 minutes for an active slot, daily for a disabled one).
13
+ - **Quarantined:** system-set on a credential conflict or an authentication mismatch. Replace credential on the account's page clears it, and a workspace owner or admin can do that.
14
+
15
+ ## Windows and walls
16
+
17
+ Subscriptions meter usage over a **5-hour** and a **7-day** window. A **wall** is the limit a session can die at mid-run, so a slot whose remaining headroom cannot fit the phase's projected burn is not offered. A Claude slot serves several phases at once up to a cap; a Codex slot serves one, because its refresh tokens are single-use. Vendor status pages say nothing about the person's own windows.
18
+
19
+ ## "Why is nothing running?"
20
+
21
+ 1. `node scripts/explain.mjs <ticket>`: `routing_unavailable` naming a slot or provider, or `no_eligible_account_slot` in the routing block, points at accounts.
22
+ 2. `node scripts/snapshot.mjs --accounts`: an empty list means none is enrolled; a wall is the `walled` field, a quarantine `quarantined` with its reason. Read them; silence proves nothing.
23
+ 3. Report it as one fleet-level cause for every ticket it holds.
24
+
@@ -1,36 +1,18 @@
1
1
  # Routing work
2
2
 
3
- This reference restates an invariant: what this skill does when the person asks for something to be done rather than known, and where a decision goes. Nothing here is tenant-specific.
3
+ Your first sentence says which of three things the message is:
4
4
 
5
- ## Three kinds of request
5
+ 1. **A question** ("where is X, why is Y stuck, what closed?"): answer it in the one-reply shape (`references/status-reply.md`).
6
+ 2. **A request for work** ("get X done, own this until it closes"): turn it into a project with an owner, in one pass, as below.
7
+ 3. **A decision only they can make**: raise it through `what-needs-me` before anyone proceeds on the default. `what-needs-me` owns what is and is not an ask.
6
8
 
7
- A message from the person is one of three things, and you decide which in the first sentence of your reply:
9
+ A request containing a decision is both; do and say both.
8
10
 
9
- 1. **A question.** "Where is X? Why is Y stuck? What closed today?" Answer it in the one-reply shape (`references/status-reply.md`). No routing.
10
- 2. **A request for work.** "Get X done. Ship the Y change. Own this until it closes." Turn it into a project with an owner, in one pass, as below.
11
- 3. **A decision only they can make**, surfaced by you or by the cloud. That is never answered here; it goes to `what-needs-me`.
11
+ ## A request becomes a project with an owner
12
12
 
13
- A request that contains a decision is both: route the work and raise the decision, and say in the reply that you did both.
13
+ 1. **Find the existing home** with `catalyst query projects` and `catalyst query search <terms>`. A request that fits an open project is a ticket there, never a duplicate project.
14
+ 2. **Scaffold when there is none:** the outcome in a sentence, the first ticket or two (through `catalyst-linear`), and the owner. The person creates a project in Linear; name it for them.
15
+ 3. **Name the owner:** a `run-this-project` session for that project, or a person. Give the one command that starts the owner session; long-running work belongs there, not at the desk.
16
+ 4. **Say what you did:** the identifiers created, the owner, and the next thing the person will see. Cite an identifier only after the create call returned it; a guessed number is usually a real, unrelated ticket.
14
17
 
15
- ## A request becomes a project with an owner, in one pass
16
-
17
- You hold no authority over project owners. Your job is to make the work visible and hand it to a single-threaded owner, then get out of the way.
18
-
19
- 1. **Find the existing home first.** `catalyst-skills query projects` and `query search <terms>`. A request that fits an open project is a ticket in that project, not a new project. Duplicate projects split one goal's status across two places.
20
- 2. **Scaffold when there is no home.** State the outcome in one sentence, the first ticket or two, and who owns it. Tickets are created through the `catalyst-linear` skill (the app actor, the team key from the contract); a project itself is created by the person in Linear, so name what you want it called and ask them to create it if none fits.
21
- 3. **Name the owner.** The owner is a `run-this-project` session for that project, or a person. Say which, in the reply. Tell the person the one command that starts the owner session, and do not start long-running work inside this session: the desk answers questions; the owner reacts to events.
22
- 4. **Say what you did.** The reply ends with the identifiers created, the owner named, and the next thing the person will see.
23
-
24
- Cite an identifier only after the create call returned it. A guessed number is usually a real, unrelated ticket.
25
-
26
- ## What goes to what-needs-me
27
-
28
- Anything that gates active work on a choice only the human can make: a product call, a priority call between two things that cannot both go first, an approval, or an action only they can physically take (a click in settings, a credential). It is raised as an ask through the `what-needs-me` skill with the question, the options, the default that fires if they stay silent, and what it blocks, and it is raised **before** anyone proceeds on the default.
29
-
30
- Not an ask: brainstorming, a design back-and-forth, a question the person asked first, a retry-or-abandon call an owner can make, a provider outage (that is one status line, not a per-ticket question).
31
-
32
- ## Three rules that bind this skill
33
-
34
- - **Never answer as the human.** You do not pick an option on an open ask, close one, or post in their voice. When their answer arrives in chat, it is recorded on the ask through `what-needs-me` so the record is complete, and it is recorded as the app actor, never as them.
35
- - **Escalate inward, never outward.** An instrument reports to the project owner; the owner asks the desk; the desk asks the human, as an ask. A single stuck ticket is never a page to the human. A system-level failure (provider down, out of capacity, rate-limited) is one line in the status reply, not a question per ticket.
36
- - **One door.** If the person needs a second place to look after your reply, add the missing block to the reply next time rather than pointing them at a dashboard.
18
+ The human answers for themselves: pick no option on an open ask, close none, and post nothing in their voice. An answer they give in chat is recorded on the ask through `what-needs-me`, as the app actor. A system-level failure is one line in the status reply, not a question per ticket.
@@ -0,0 +1,20 @@
1
+ # Settings and where they live
2
+
3
+ Live values come from the contract or a script. Prefix every route with the person's own `<their cloud>`, never an invented host. Unless a row says otherwise, an owner or admin sets it.
4
+
5
+ | what | where | the rule |
6
+ | -- | -- | -- |
7
+ | **Connecting Linear** | `<their cloud>/settings/connections` | Needs an owner or admin browser session, which also decides the account. |
8
+ | **One workspace, one account** | `<their cloud>/settings/connections` | A Linear workspace binds to exactly one account, and the reverse; a second account for the same workspace is refused. Raise it as a decision, not a retry. |
9
+ | **Installing the GitHub App** | `<their cloud>/settings/connections`, after `<their cloud>/settings/profile` | The admin's personal GitHub grant comes first: a refusal naming a missing GitHub identity or someone else's installation means "grant your personal GitHub first". |
10
+ | **Uninstalling** | `<their cloud>/settings/connections` | Removing the App on GitHub self-heals the registry; unlinking in Catalyst uninstalls upstream. Neither needs support. |
11
+ | **One team, one repository** | `<their cloud>/settings/repositories` | A Linear team maps to at most one repository per account, and a GitHub repository to at most one, case-insensitively. |
12
+ | **Neither half can be re-pointed** | `<their cloud>/settings/repositories` | A repository is one team plus one GitHub repo; the only editable field is its name, so a monorepo across two teams cannot be modelled. |
13
+ | **An archived repository holds both keys** | `<their cloud>/settings/repositories` | Re-registering the same team-plus-repo pair is refused until the archived entry is restored. |
14
+ | **Merge policy** | `<their cloud>/settings/repositories/$repoId/merging` | The effective policy and its source are on the contract's `merge` block; this page changes it (`catalyst-github` only reads it). |
15
+ | **Reviewers** | `<their cloud>/settings/repositories/$repoId/code-reviews` | "Post review requests as" names one connected member. A strict merge policy with no reviewer never earns the merge label: configure one, or change the policy. |
16
+ | **The merge queue** | no settings page; the repository's own merge automation | The merge phase's one write is the ready label; the merge itself is whatever the repository's own setup does with it. This bundle cannot see which queue runs; say so, naming no vendor the person has not named. The GitHub repository's administrator owns it. |
17
+ | **The runner cap** | `<their cloud>/settings/repositories` | 20 running phases per repository by default, which an operator changes; the page only displays it, and a paused repository resolves to zero. See `references/what-runs-next.md`. |
18
+ | **Secrets and environment variables** | `<their cloud>/settings/secrets`, `<their cloud>/settings/environment`, and each repository's sections | Secrets are write-only; variables are readable. A name is one or the other, never both; a repository value wins over an account one; a variable may reference a vault secret by name, expanded only in the container. |
19
+ | **"My secrets are not in the container"** | `<their cloud>/settings/repositories` | When a repository was registered without its team, nothing reaches a runner, and the failure is silent: a phase's build cannot find its environment. Raise the fix, an admin or operator action, as an ask. |
20
+ | **Routing** | none: there is no settings page for this | Stage defaults are global and the per-account table is empty, so routing always resolves to the default provider. A Codex-first pipeline is not something to promise (`references/what-runs-next.md`). |
@@ -0,0 +1,25 @@
1
+ # Stages and mapping
2
+
3
+ A **slot** is Catalyst's name for a position on the board; a **stage** is the Linear workflow state a team has; the **mapping** binds each slot to one stage per team. Every team's live map is on the contract under `teams[].stages`; `node scripts/show-my-map.mjs [--team <key>]` prints it with the load-bearing slots starred.
4
+
5
+ The slots, in pipeline order: `dispatch`, `intake`, `research`, `plan`, `implement`, `remediate`, `verify`, `review`, `pr`, `done`, `canceled`.
6
+
7
+ ## Five slots are load-bearing
8
+
9
+ | slot | why it matters | allowed state types |
10
+ | -- | -- | -- |
11
+ | `dispatch` | moving a card here dispatches work; nothing is offered from any other column | `unstarted`, `backlog` |
12
+ | `intake` | where a never-seen ticket lands for the optional intake pass | `unstarted`, `backlog`, never Linear's triage type |
13
+ | `pr` | a card must sit here for `merge` to be offered | `started` |
14
+ | `done` | written by the merge webhook | `completed` |
15
+ | `canceled` | terminal; never work | `canceled` |
16
+
17
+ An absent or wrongly typed mapping on one of these stops the ladder silently (on the others it costs a readiness warning): until dispatch, pr, done and canceled each point at a live stage, Catalyst starts nothing in that team, and `explain` says so. `teams[].gitAutomation` is a stored consent for a feature not built yet; nothing reads it, so its value neither stops nor starts work.
18
+
19
+ `teams[].workflowMode` is **adopted** (Catalyst created the stages), **mapped** (a human chose each) or **mixed**. Mappings live at `<their cloud>/settings/linear-teams`; `.catalyst/catalyst.toml` has no stage section.
20
+
21
+ ## The state id is the authority
22
+
23
+ Only a stage's `stateId` is a lookup key, because a Linear import can keep every name while re-minting every id. Move cards by slot (`catalyst-linear` does) and let the CLI resolve the id. `stateStillExists: false` means the team needs re-mapping (`catalyst team map <KEY>`) before that slot can be written; the CLI refuses the move.
24
+
25
+ Backlog is not a slot: parking a card moves it to the team's backlog-type state, which the CLI resolves from the live workflow. Readiness over the mapping, and who fixes each check, is the `catalyst-onboard` skill.
@@ -1,36 +1,34 @@
1
1
  # The one-reply shape
2
2
 
3
- This reference restates an invariant of this skill: how a status answer is shaped. The facts inside it come live from `node scripts/snapshot.mjs` and `node scripts/explain.mjs`; the machine-readable schema is `assets/status-reply.json`.
3
+ One question gets one reply, complete enough that the person needs no second surface. Every line names a ticket identifier, and a fact you could not read is named as unreadable, with where it lives.
4
4
 
5
- ## The rule
5
+ Open with one clause: the snapshot's `takenAt` and its source (a fresh replica's cursor, or the API and why). Then the blocks, in order:
6
6
 
7
- The person asked one question and gets one reply. If they would need a second surface, a second message, or a follow-up question from you to know where things stand, the reply is wrong. Every line names a ticket identifier. Nothing in the reply is a guess: a fact you could not read is named as unreadable, with where it lives.
7
+ 1. **In flight.** From the snapshot's `running` block: identifier, the phase running, how long.
8
+ 2. **Blocked, and on whom.** The queue's excluded rows, with `explain` on the ones the person cares about. Every line carries the reason in the person's words and who releases it: the person, a role, the cloud itself, or a clock.
9
+ 3. **Waiting on the human.** `waitingOnHuman` in the snapshot, ranked by what each ask holds: identifier, the question, what it releases. `what-needs-me` owns the detail.
10
+ 4. **Closed.** What reached the done slot in the window asked about (`query issues` by the done-slot stage name, or the change feed); with no window read, "since my last reply".
11
+ 5. **Next.** The snapshot's `queue` block in the cloud's order, with each ticket's next phase.
12
+ 6. **Cannot see**, only when non-empty: PR labels and reactions (not mirrored), each with the URL the CLI printed. A flow number goes here by name: cycle time, throughput, or how long pull requests have been open are not computed by anything a key reads, so say they are not computed rather than counting ticket dates.
8
13
 
9
- ## The five blocks, in this order
10
-
11
- 1. **In flight.** Tickets a phase is running on right now, or that hold a live lease mid-ladder. Source: the snapshot's `running` block (fleet activity, the agent roster, lease attributions). One line per ticket: identifier, the phase running, how long it has run.
12
- 2. **Blocked, and on whom.** Tickets nothing is offered for. Source: the queue's excluded rows, then `explain` on each one the person cares about. Every line carries the reason in the person's words and who releases it: the human (an ask), a role, the cloud itself (a backoff, a park that self-releases), or a clock. "On whom" is never blank; if you cannot tell, say the reason the cloud gave and that the release is unknown to a key.
13
- 3. **Waiting on the human.** The open asks, ranked by what each one holds. Source: `waitingOnHuman` in the snapshot. Keep this to identifier, the question, and what it releases; the `what-needs-me` skill owns the detail and the settling.
14
- 4. **Closed.** What reached the done slot in the window the person asked about (or since your last reply). Source: `query issues` filtered by the team's done-slot stage name from the contract, or the change feed for a time window. When you did not read a window, say "since my last reply" and mean it.
15
- 5. **Next.** What the queue picks up next, in the cloud's order, with the phase each will run. Source: the snapshot's `queue` block. Do not reorder it to what you think should be next; the levers are in `references/reprioritising.md`.
14
+ ## Writing the lines
16
15
 
17
- A sixth block, **cannot see**, appears only when it is non-empty: the facts your key cannot read (PR labels and reactions, which are not mirrored; an operator-only park release), each with the URL the CLI printed.
16
+ - Identifier first, then the stage as the contract spells it, then one clause: `KEY-123 · <stage name> · implement running 14 min`.
17
+ - Age in human units from `updated_at` or the lease start, judged against the phase's natural duration.
18
+ - A reason is its translation from `references/why-is-it-stuck.md`. One ticket in flight and nothing blocked is a three-line reply.
18
19
 
19
- A flow number is named in that block by name, not left out: cycle time, throughput, or how long pull requests have been open are not computed by anything a key reads today. Say they are not computed rather than counting ticket dates or open pull requests and calling it that.
20
+ ## Reading the board
20
21
 
21
- ## The header line
22
+ `node scripts/snapshot.mjs --board` groups open tickets by stage in slot order; read it by slot, never by name. A long dispatch column with nothing in flight is a capacity or eligibility question: `explain` the first row. A card that has not moved is running, retrying in place, or in remediate, which is an interrupt to count separately, never progress. An ask-labelled ticket belongs in the waiting-on-human block; a local-lane ticket is in flight elsewhere. The ticket's comments (`catalyst-linear`) and `catalyst explain --history <ticket>` must agree.
22
23
 
23
- The reply opens with one clause that says when and from where: the snapshot's `takenAt` and its source line. When the replica was stale or absent, the reply says the numbers came from the API; when it was fresh, it says the cursor. This is not decoration. A stale source that goes unmentioned is the way a wrong status reply happens.
24
+ ## What counts as stuck
24
25
 
25
- ## Writing the lines
26
+ A ticket is **stuck** only when all three hold:
26
27
 
27
- - Identifier first, then the stage as the contract spells it for that team, then one clause. `KEY-123 · <stage name> · implement running 14 min`.
28
- - Age in human units (minutes, hours, days), from `updated_at` or the lease's start, never a raw timestamp.
29
- - A reason is the translation from `references/why-is-it-stuck.md`, not the cloud's snake_case token, unless the token is one the table does not know, in which case quote it as the cloud spelled it.
30
- - No adjectives about health. "Stuck" has a definition (`references/reading-the-board.md`); use it only when it applies.
28
+ 1. Nothing is offered for it: the queue excludes it, or `explain` gives a reason rather than a position.
29
+ 2. The reason does not release itself (`references/why-is-it-stuck.md` marks which do).
30
+ 3. No one is acting on the release: no open ask names it, and no human comment or push landed after the failure.
31
31
 
32
- ## What the reply never does
32
+ Everything else is **waiting**, and the reply says on what; calling a wait "stuck" sends the human to fix what the cloud is handling.
33
33
 
34
- - It never restates a stage name, threshold or label from memory. The snapshot's `tenant` block carries the live values; read them there each time.
35
- - It never answers an ask, proposes a default on the human's behalf inside the status reply, or moves anything. Routing a request is `references/routing-work.md`; a decision is the `what-needs-me` skill.
36
- - It never pads a short answer. When one ticket is in flight and nothing is blocked, the reply is three lines.
34
+ The reply answers no ask, proposes no default for the human, and moves nothing: requests go to `references/routing-work.md`, decisions to `what-needs-me`.
@@ -0,0 +1,30 @@
1
+ # What Catalyst is, and the ladder
2
+
3
+ ## In the person's terms
4
+
5
+ Use their repositories and tickets as examples. Moving a card into the team's dispatch column starts the work, and nothing else does. Each phase runs in a cloud container set up for the repository, on a coding account their workspace enrolled, and leaves a document and an outcome comment on the ticket, so the ticket is the record. Their laptop runs no coding sessions, and agents react to events rather than polling.
6
+
7
+ What changes in their day: they write each ticket for a reader who cannot see their chat (an outcome title, what done looks like, a priority, no open blocker). They leave working stages and Done to Catalyst. A draft pull request appears that a later phase marks ready, and a green one with no open threads merges through the queue unless a hold label keeps it out. A decision only they can make arrives as an ask in their own Linear, with options, a silent default and what it holds. A comment they post gets an eyes reaction and a threaded reply.
8
+
9
+ ## The phases
10
+
11
+ The contract's `ladder` block (`node scripts/show-my-map.mjs`) says whether intake is on and which keying applies.
12
+
13
+ | phase | produces | notes |
14
+ | -- | -- | -- |
15
+ | `intake` | nothing (a classification pass) | optional, for a new ticket, when `ladder.intakeEnabled` |
16
+ | `research` | `research.md` | offered from the dispatch slot |
17
+ | `plan` | `plan.md` | |
18
+ | `implement` | `implement.md` | creates the branch, named exactly the ticket identifier, and opens a draft pull request |
19
+ | `validate` | `validation.md` | success moves the card to the review stage (verify and review share it) |
20
+ | `pr` | `pr.md` | first line is the PR title, then the body; rebases, force-pushes, marks the PR ready |
21
+ | `remediate` | `remediation.json` | an interrupt that repairs a failed phase; never advances past the PR slot |
22
+ | `merge` | a receipt | applies the queue-ready label once its evidence gate passes |
23
+
24
+ ## What moves the card
25
+
26
+ The contract's `ladder.advance` maps each completed phase to a card move; its keying decides whether a stage names the phase just finished (trailing, the default) or the one ahead (leading). A failed phase, `remediate` and `merge` move nothing (`references/when-a-phase-fails.md`).
27
+
28
+ **Done is written by the merge.** GitHub's merged event moves the ticket to Done within seconds, with a sweep behind a dropped webhook, so a ticket not Done a minute after its PR merged is a finding, not a chore to hand-close. Done means merged; a separately deployed change is live only once deployed.
29
+
30
+ To place a ticket, its stage says which phase last finished (`node scripts/show-my-map.mjs --team <key>` translates it) and `explain` says what runs next.