@catalyst-cloud/cli 0.8.0 → 0.9.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.
- package/CHANGELOG.md +12 -0
- package/README.md +27 -3
- package/dist/args.js +15 -1
- package/dist/cli.js +14 -2
- package/dist/connections.js +85 -0
- package/dist/event-status.js +66 -0
- package/dist/events.js +8 -2
- package/dist/identity.js +53 -0
- package/dist/mcp.js +101 -0
- package/dist/sdk.js +23 -0
- package/package.json +2 -2
- package/skills/catalyst-github/SKILL.md +1 -1
- package/skills/catalyst-linear/SKILL.md +1 -1
- package/skills/catalyst-onboard/SKILL.md +5 -2
- package/skills/catalyst-onboard/references/local-sync.md +15 -0
- package/skills/catalyst-onboard/references/skill-sources.md +5 -2
- package/skills/catalyst-onboard/references/the-one-path.md +17 -17
- package/skills/catalyst-onboard/references/what-the-browser-owns.md +2 -0
- package/skills/catalyst-onboard/references/who-fixes-what.md +2 -2
- package/skills/catalyst-onboard/scripts/local-sync.mjs +216 -0
- package/skills/catalyst-onboard/scripts/where-am-i.mjs +68 -8
- package/skills/catalyst-setup/SKILL.md +1 -1
- package/skills/connect-me/SKILL.md +1 -1
- package/skills/how-catalyst-works/SKILL.md +1 -1
- package/skills/run-this-project/SKILL.md +1 -1
- package/skills/unstick/SKILL.md +1 -1
- package/skills/what-needs-me/SKILL.md +1 -1
- package/skills/whats-happening/SKILL.md +1 -1
|
@@ -5,7 +5,7 @@ description: >-
|
|
|
5
5
|
disable-model-invocation: true
|
|
6
6
|
allowed-tools: Bash(catalyst-skills:*) Bash(npx @catalyst-cloud/catalyst-skills:*)
|
|
7
7
|
---
|
|
8
|
-
<!-- vendored-from: @catalyst-cloud/catalyst-skills@0.
|
|
8
|
+
<!-- vendored-from: @catalyst-cloud/catalyst-skills@0.9.0 — written in this repository for customer tenants -->
|
|
9
9
|
|
|
10
10
|
# Onboard me
|
|
11
11
|
|
|
@@ -20,6 +20,7 @@ Scripts are run, never read. Each prints `--help`.
|
|
|
20
20
|
- `node scripts/where-am-i.mjs` — every part, each with the instrument that read it, its verdict, and for anything unfinished who can fix it and the page it is on. Works before the machine is connected; that is one of the states it reports.
|
|
21
21
|
- `node scripts/where-am-i.mjs --next` — the same reading, reduced to the single next step.
|
|
22
22
|
- `node scripts/where-am-i.mjs --json` — the same document for you to branch on.
|
|
23
|
+
- `node scripts/local-sync.mjs` — optional local replica and event sync status. Run `node scripts/local-sync.mjs --start` only after the person chooses local sync; the script starts the supported writer and waits for a live heartbeat at the cloud cursor.
|
|
23
24
|
|
|
24
25
|
Start every session with it, and run it again after every step the person completes. It is the only thing that decides where you are.
|
|
25
26
|
|
|
@@ -28,6 +29,7 @@ Start every session with it, and run it again after every step the person comple
|
|
|
28
29
|
| when | read |
|
|
29
30
|
| -- | -- |
|
|
30
31
|
| walking the steps — what each one does, what to read back, when it is done | `references/the-one-path.md` |
|
|
32
|
+
| the person chooses an optional local event and replica cache | `references/local-sync.md` |
|
|
31
33
|
| anything reports not ready, or you are about to say who should fix something | `references/who-fixes-what.md` |
|
|
32
34
|
| the next step is a browser page, or a page said it worked and you have to confirm it | `references/what-the-browser-owns.md` |
|
|
33
35
|
| the person asks what Catalyst actually is, or how a ticket gets worked | the `how-catalyst-works` skill |
|
|
@@ -41,11 +43,12 @@ Start every session with it, and run it again after every step the person comple
|
|
|
41
43
|
## Rules
|
|
42
44
|
|
|
43
45
|
- **One step, then stop.** Say what you are about to do, do it, show the real output, say what it means and what comes next. Never queue several steps into one message, and never move on from a step you did not watch finish.
|
|
46
|
+
- **Local sync is opt-in.** API-backed skills work without it. Ask whether the person wants a local event and replica cache before running `local-sync.mjs --start`. A detached process starting is not evidence of freshness; only the script's verified current verdict is.
|
|
44
47
|
- **Report what you observed, not what you expected.** Print the lines the command actually produced. "That worked" without the output it produced is the single easiest thing to get wrong here, and a person who later finds it did not work stops trusting every other step you reported.
|
|
45
48
|
- **Each part by its own instrument.** Read the machine with the machine's instrument and the project with the project's, and label every finding with the part it belongs to. The script does this for you; keep it that way when you summarize.
|
|
46
49
|
- **Not ready is a question about who, not a reason to retry.** When something reports not ready, name which check, who can fix it, and where. If the owner is not the person in front of you, say so and stop — re-running a local command cannot move a check that belongs to a tenant owner, an admin, or a browser page.
|
|
47
50
|
- **Never invent a count or a list.** Every number and every name comes from what a command printed. If you want to tell them how many projects are mapped, read it off the script's output; do not carry one over from an earlier turn.
|
|
48
|
-
- **
|
|
51
|
+
- **Provider consent belongs to the person in a browser.** This includes approving login, the tenant's Linear connection and GitHub App install, and the member's personal Linear and GitHub connections. Start personal Linear consent after tenant Linear is connected; start personal GitHub consent only after the tenant GitHub App is installed and its repository is registered. Give the person the printed URL, then check `status`. Never claim a grant succeeded because a browser opened.
|
|
49
52
|
- **Some steps a key cannot do yet.** Listing every project, saving a stage mapping, adopting the workflow, registering a repository and approving one repository's environment are settings-page work today; a key-callable path for them is being built. Route those through the browser and say plainly that it is a gap, not the design. Never guess at a route for them.
|
|
50
53
|
- **The account-wide environment declaration is the exception, and the one setup write you can perform.** `catalyst-skills environment` reads it, proposes it and approves it. Use the verb; do not send them to a page for it.
|
|
51
54
|
- **Their tenant, as them.** Everything goes through the CLI and the person's own login. You never name another tenant, and you never ask for a key you could avoid — the keyless login needs nothing pasted.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Optional local events and replica
|
|
2
|
+
|
|
3
|
+
**For:** a person who wants an on-machine event file and searchable replica. The normal API-backed skills work without this, so ask before starting a background writer.
|
|
4
|
+
|
|
5
|
+
**You run:** `node scripts/local-sync.mjs` to show the current local state. Only if the person opts in, run `node scripts/local-sync.mjs --start`. The script uses `catalyst-skills replica start --detach`, whose writer also starts the local event cache. It waits up to 90 seconds; `--wait <seconds>` sets a bounded wait from 0 to 300.
|
|
6
|
+
|
|
7
|
+
**Read back:** `Local sync current` only when both the replica and event cache have live writer heartbeats and each cursor equals its own cloud head. A detached process starting, one fresh heartbeat alone, or replica freshness alone does not prove event freshness. `catalyst-skills replica status --probe --json` checks the replica cursor; `catalyst-skills events status --probe --json` checks the separate event cursor. Either command can report stale or unknown; unknown means the cloud comparison could not be proved, not that the cache is stale. Exit 0 means current, 1 stale, 2 unknown or not connected, and 3 absent. Do not tell the person local data is current unless the combined check says so.
|
|
8
|
+
|
|
9
|
+
**Owner:** you check and, with the person's opt-in, start. The local writer is optional. A reboot recovery service is separate work; if this machine restarts, recheck status before relying on local data.
|
|
10
|
+
|
|
11
|
+
## First ticket event
|
|
12
|
+
|
|
13
|
+
Ask before starting the local writer. Before the person moves a card, run `node scripts/local-sync.mjs --start` after they opt in and wait until it reports current. Then run `catalyst-skills events status --probe --json` and record its `cursor` as `<cursor-before-move>`. Move the card only after this baseline is captured. Once the card is moved, run `catalyst-skills events wait-for --ticket <ticket-identifier> --after <cursor-before-move> --timeout 300` with the identifier `explain` printed. Do not guess an event type. The explicit cursor lets `wait-for` find the event if it reached the local cache before the command began.
|
|
14
|
+
|
|
15
|
+
Exit 0 prints a matching event after the recorded baseline and proves it reached this machine's cache. Exit 1 means no matching cached event arrived within five minutes; recheck `node scripts/local-sync.mjs` and report stale or unknown evidence without inferring a cloud or webhook failure. Exit 3 means the event cache is absent; ask before starting it. Other errors are unknown. `explain` remains the API-backed source for why a ticket can or cannot run.
|
|
@@ -18,6 +18,9 @@ Install both packs on a workstation used for coding and tenant operations. Each
|
|
|
18
18
|
- The global lock is `$XDG_STATE_HOME/skills/.skill-lock.json` when `XDG_STATE_HOME` is set, else `~/.agents/.skill-lock.json`. A project uses its own `skills-lock.json`.
|
|
19
19
|
- The installer writes relative links. Resolve each with `readlink -f` before you compare paths.
|
|
20
20
|
|
|
21
|
+
|
|
22
|
+
The `npx skills add --all` command replaces existing same-named directories and links. Before a first install or refresh on an existing machine, read the lock above. Inspect every same-named agent destination. Proceed only when each destination is absent or a verified, unmodified copy of the intended pack or its symlink. A lock entry alone does not verify every destination. Keep independent, changed, or uncertain copies in place and resolve the conflict before adding either pack. Do not schedule raw add commands as an unattended refresh.
|
|
23
|
+
|
|
21
24
|
## Is each pack there
|
|
22
25
|
|
|
23
26
|
`catalyst-skills ready` checks the Cloud pack only. Check `catalyst-onboard/SKILL.md` (Cloud pack) and `research-codebase/SKILL.md` (development pack) in the intended directory. If both are there, have the person type `/catalyst-onboard` (`$catalyst-onboard` in Codex).
|
|
@@ -28,8 +31,8 @@ Inventory source and scope before removing anything:
|
|
|
28
31
|
|
|
29
32
|
1. Run `claude plugin list` and record whether `catalyst-dev@catalyst` is installed and at which scope.
|
|
30
33
|
2. Inspect the selected agent's home skill directory and the current project's skill directory separately. Read any `skills-lock.json` files and source/provenance markers. A folder name alone does not prove which repository supplied it. Check whether `.claude/skills` is a symlink to another skills directory before changing either path.
|
|
31
|
-
3. If the old plugin is active, remove only `catalyst-dev@catalyst`: `claude plugin uninstall catalyst-dev@catalyst --scope user --keep-data --yes`. Keep `catalyst-dev@catalyst-dev-skills` and `catalyst@catalyst-cloud`.
|
|
34
|
+
3. If the old plugin is active, remove only `catalyst-dev@catalyst`: `claude plugin uninstall catalyst-dev@catalyst --scope user --keep-data --yes`. Keep `catalyst-dev@catalyst-dev-skills` and `catalyst@catalyst-cloud`. For a copied skill whose lock source names the deprecated Catalyst runtime repository, use `npx skills remove <name> -g -y` only when every existing global agent path is that canonical copy or a symlink to it. Omit `-g` for a proven project install and inspect every same-named project agent path first. The command removes that name across agent directories. Leave independent or uncertain copies in place and report the conflict.
|
|
32
35
|
4. Install the replacement pack or packs in the intended scope with the commands above. For the Cloud pack, omit `-g` only when a project-scoped install is intended. Do not use a blanket `npx skills remove --all` during migration.
|
|
33
|
-
5. Read back the plugin list
|
|
36
|
+
5. Read back the plugin list, the lock for each changed scope, and the destination skill folders. Start a new agent session after changing Claude plugins.
|
|
34
37
|
|
|
35
38
|
Do not delete a Catalyst checkout, local project data, or a same-named skill whose source is not known. If the source or scope is ambiguous, stop and report what is unclear before removing anything.
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
# The one path
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Eleven steps, in this order. The order is the product's own: the tenant-side steps run Linear, then the project, then GitHub, then the repository, because each one is the cheapest place to catch the failure the next one would otherwise hide. Connect the tenant's Linear workspace before starting personal Linear consent (3a). Install the tenant's GitHub App and register its repository before starting personal GitHub consent (6a).
|
|
4
4
|
|
|
5
5
|
Walk them **one at a time**. Before each step say what you are about to do and why; after it, show what actually came back. `node scripts/where-am-i.mjs --next` decides which step you are on — never your memory of the last turn.
|
|
6
6
|
|
|
7
7
|
Each step below states: what it is for, what you run or hand over, **what you read back to prove it landed**, and **who owns it**.
|
|
8
8
|
|
|
9
|
-
---
|
|
10
9
|
|
|
11
10
|
## 0 — Where are we
|
|
12
11
|
|
|
@@ -18,8 +17,6 @@ Each step below states: what it is for, what you run or hand over, **what you re
|
|
|
18
17
|
|
|
19
18
|
**Owner:** you.
|
|
20
19
|
|
|
21
|
-
---
|
|
22
|
-
|
|
23
20
|
## 1 — Connect this machine
|
|
24
21
|
|
|
25
22
|
**For:** giving this machine a credential, so every later read is the person's own.
|
|
@@ -32,8 +29,6 @@ Each step below states: what it is for, what you run or hand over, **what you re
|
|
|
32
29
|
|
|
33
30
|
If it refuses, stop here and use the `connect-me` skill; it owns every failure mode of this step.
|
|
34
31
|
|
|
35
|
-
---
|
|
36
|
-
|
|
37
32
|
## 2 — Who you are
|
|
38
33
|
|
|
39
34
|
**For:** the person grain. A connected machine does not mean an active seat, and an active seat does not mean their Linear identity is matched.
|
|
@@ -42,9 +37,7 @@ If it refuses, stop here and use the `connect-me` skill; it owns every failure m
|
|
|
42
37
|
|
|
43
38
|
**Read back:** their label and role, and whether their Linear identity is matched.
|
|
44
39
|
|
|
45
|
-
**Owner:**
|
|
46
|
-
|
|
47
|
-
---
|
|
40
|
+
**Owner:** a personal Linear connection normally matches the person's Linear identity automatically. If it remains unmatched after personal consent, run `catalyst-skills identity linear status`, then `catalyst-skills identity linear options`. Let the member select their own listed identity and run `catalyst-skills identity linear set <linearUserId>`. The command reads the result back. An automatically resolved identity cannot be replaced; an existing claim conflict needs a tenant owner or admin. An inactive seat also needs an owner or admin. Say which finding the instrument reported.
|
|
48
41
|
|
|
49
42
|
## 3 — Connect Linear
|
|
50
43
|
|
|
@@ -56,7 +49,15 @@ If it refuses, stop here and use the `connect-me` skill; it owns every failure m
|
|
|
56
49
|
|
|
57
50
|
**Owner:** a tenant owner or admin, in a browser. **This is a browser step by construction** — it is an authorization grant, and no key can perform one.
|
|
58
51
|
|
|
59
|
-
|
|
52
|
+
## 3a — Connect your personal Linear account
|
|
53
|
+
|
|
54
|
+
**For:** giving this person's agent its own provider access. The tenant's Linear connection and GitHub App serve the tenant; they do not prove that this member has approved personal grants.
|
|
55
|
+
|
|
56
|
+
**You run:** `catalyst-skills connections personal linear start`. It gives a short-lived URL and tries to open it. The person approves in their own browser. If the browser does not open, give them the URL printed by the command. Never paste a provider token into a prompt.
|
|
57
|
+
|
|
58
|
+
**Read back:** after approval, run `catalyst-skills connections personal linear status`. `node scripts/where-am-i.mjs` also reads the personal grant statuses. A URL opening is not proof that a grant landed.
|
|
59
|
+
|
|
60
|
+
**Owner:** you start and check; the member approves in a browser. A usable personal Linear grant normally binds its viewer automatically. If identity remains unmatched, use `catalyst-skills identity linear options` for supported self-service recovery. Never choose a roster entry on the member's behalf. An already-resolved or already-claimed refusal needs an owner or admin to inspect the conflict.
|
|
60
61
|
|
|
61
62
|
## 4 — Pick one project, and map its stages
|
|
62
63
|
|
|
@@ -70,7 +71,6 @@ If it refuses, stop here and use the `connect-me` skill; it owns every failure m
|
|
|
70
71
|
|
|
71
72
|
⭐ **One project at a time is safe, and lead with this.** Mapping one project changes no other project's stages and moves no other project's tickets. Encourage a pilot: pick the project they care least about breaking.
|
|
72
73
|
|
|
73
|
-
---
|
|
74
74
|
|
|
75
75
|
## 5 — Install the GitHub App
|
|
76
76
|
|
|
@@ -82,7 +82,6 @@ If it refuses, stop here and use the `connect-me` skill; it owns every failure m
|
|
|
82
82
|
|
|
83
83
|
**Owner:** a tenant owner or admin, in a browser. **Browser by construction**, same reason as step 3.
|
|
84
84
|
|
|
85
|
-
---
|
|
86
85
|
|
|
87
86
|
## 6 — Register the repository
|
|
88
87
|
|
|
@@ -94,13 +93,16 @@ If it refuses, stop here and use the `connect-me` skill; it owns every failure m
|
|
|
94
93
|
|
|
95
94
|
**Owner:** a tenant owner or admin. ⛔ **Registering is settings-page work today**; a key-callable path is being built. ⛔ A repository registered without a project attached is the trap here: the call succeeds, the repository is listed, and nothing can ever dispatch into it. Make sure they attach the project in the same form, and say why.
|
|
96
95
|
|
|
97
|
-
|
|
96
|
+
|
|
97
|
+
## 6a — Connect your personal GitHub account
|
|
98
|
+
|
|
99
|
+
**For:** letting Catalyst act as you in GitHub. This grant is separate from the tenant's GitHub App installation. **You run:** `catalyst-skills connections personal github start`, only after the GitHub App is installed and the repository is registered. The browser flow is the same as 3a. **Read back:** `catalyst-skills connections personal github status`; a connected result confirms the personal grant. **Owner:** you start and check; the member approves in a browser.
|
|
98
100
|
|
|
99
101
|
## 7 — Declare what the containers need
|
|
100
102
|
|
|
101
103
|
**For:** the environment a phase runs in — the names of the variables and secrets the person's code needs. Names leave the machine; values are entered once, by them, in the app.
|
|
102
104
|
|
|
103
|
-
|
|
105
|
+
You can read, propose, and approve the account-wide declaration through the CLI if this member has an admin or owner seat. Personal connection initiation and status are also CLI actions; the provider approval remains in the browser.
|
|
104
106
|
|
|
105
107
|
**You run:** `catalyst-skills environment` first, to read what the tenant already declares — the current revision, whether it is approved, and which revision a phase's checkout actually carries. Those last two are different things more often than people expect: a proposal that nobody approved changes nothing.
|
|
106
108
|
|
|
@@ -120,7 +122,6 @@ Add `--approve` to approve exactly the revision that propose just returned, whic
|
|
|
120
122
|
|
|
121
123
|
If they do not know what their build needs yet, skip this step. It blocks nothing until a phase needs a secret.
|
|
122
124
|
|
|
123
|
-
---
|
|
124
125
|
|
|
125
126
|
## 8 — A coding account, and a host
|
|
126
127
|
|
|
@@ -136,13 +137,12 @@ If they do not know what their build needs yet, skip this step. It blocks nothin
|
|
|
136
137
|
|
|
137
138
|
**You run:** `catalyst-skills ready`. Its READY does not cover step 8; the script does. Read them the verdict and every failing line, each with its own fix and owner. If it says NOT READY, go to `references/who-fixes-what.md` before you touch anything — a project check failing is not something re-running anything on this machine can fix.
|
|
138
139
|
|
|
139
|
-
**Then:** have them move one card into the project's dispatch stage, and watch. `catalyst-skills explain <ticket>` says why it is or is not about to run.
|
|
140
|
+
**Then:** have them move one card into the project's dispatch stage, and watch. `catalyst-skills explain <ticket>` says why it is or is not about to run. If they opted into local sync, use the optional first-event check in `references/local-sync.md`.
|
|
140
141
|
|
|
141
142
|
**Read back:** what `explain` actually said. If it says the ticket cannot start, the reason it names is the answer — read it to them and use the `how-catalyst-works` skill for what the reason means, then `unstick` if something is holding it.
|
|
142
143
|
|
|
143
144
|
**Owner:** the card move is theirs. The verdict is the tenant's.
|
|
144
145
|
|
|
145
|
-
---
|
|
146
146
|
|
|
147
147
|
## When you are done
|
|
148
148
|
|
|
@@ -11,6 +11,8 @@ These will never be a command, on any release. Each is an authorization a person
|
|
|
11
11
|
| approving the login | the URL and short code that `catalyst-skills login` printed | "I have started the login. It printed this code and this URL — approve it in your browser, or on your phone, and tell me when it is done." |
|
|
12
12
|
| connecting Linear | `<their cloud>/settings/connections` | "Open this page and connect Linear. It will send you to Linear to authorize it and bring you back." |
|
|
13
13
|
| installing the GitHub App | `<their cloud>/settings/connections` | "Open the same page and install the GitHub App, granting it the repository you want worked." |
|
|
14
|
+
| connecting personal Linear | the URL printed by `catalyst-skills connections personal linear start` | "Approve your own Linear account in this browser. I will check the grant status afterward." |
|
|
15
|
+
| connecting personal GitHub, after the tenant GitHub App is installed and its repository is registered | the URL printed by `catalyst-skills connections personal github start` | "Approve your own GitHub account in this browser. I will check the grant status afterward." |
|
|
14
16
|
|
|
15
17
|
Say **by construction**, not "not supported yet". A person who thinks it is a missing feature will wait for it.
|
|
16
18
|
|
|
@@ -7,7 +7,7 @@ Setup is seven parts. Each has one instrument, and each instrument answers about
|
|
|
7
7
|
| part | instrument | what a pass proves | what it does **not** prove | who fixes a failure, and where |
|
|
8
8
|
| -- | -- | -- | -- | -- |
|
|
9
9
|
| **machine** | `catalyst-skills status`, and the non-team checks of `catalyst-skills ready` | Node is new enough, this machine holds a credential, the CLI is where the config says, the contract is cached, the skills are on disk | anything at all about the tenant | the person at this keyboard, here |
|
|
10
|
-
| **person** | `catalyst-skills me` | the credential resolves to this person,
|
|
10
|
+
| **person** | `catalyst-skills me`, and `catalyst-skills connections personal <linear|github> status` | the credential resolves to this person, shows their role and Linear identity match, and reports each personal grant's state | that their seat is active, or that they may change tenant settings | the member approves missing or expired personal grants in a browser; an owner or admin handles seat or identity conflicts in Settings → Members |
|
|
11
11
|
| **account** | `catalyst-skills contract --path account` | a resolved Linear workspace means the tenant's Linear grant landed | that the GitHub App is installed — the contract does not carry it | a tenant owner or admin, `<their cloud>/settings/connections` |
|
|
12
12
|
| **project** | `catalyst-skills contract --path teams`, and the `team:` checks of `ready` | for each project **that has been mapped**: its readiness verdict and each failing check by name | that this is every project they have — see below | a tenant owner or admin, `<their cloud>/settings/linear-teams` |
|
|
13
13
|
| **repository** | `catalyst-skills contract --path merge.repositories` | the repository is registered to the account | that it is active, that a project can dispatch into it, or that its environment is declared | a tenant owner or admin, `<their cloud>/settings/repositories` |
|
|
@@ -34,7 +34,7 @@ Setup is seven parts. Each has one instrument, and each instrument answers about
|
|
|
34
34
|
|
|
35
35
|
- **A machine check failed.** This is the person's, here, now. Each failing check carries its own `fix` line; read it and do it. A missing or partial skill set is `catalyst-skills install`; a stale contract is `catalyst-skills contract --refresh`; a missing CLI path is one more `catalyst-skills login`.
|
|
36
36
|
- **A project check failed.** ⛔ **Nothing you run on this machine can move it.** Name the check, name the project, and name the owner — the `who` field carries the tenant's own owners and admins. Point at `<their cloud>/settings/linear-teams`. Then stop. Re-running `ready` in a loop is the failure this section exists to prevent: it will keep saying NOT READY for a reason that lives somewhere else entirely.
|
|
37
|
-
- **A check is a note.** Notes never move the verdict. A stale or absent replica is optional;
|
|
37
|
+
- **A check is a note.** Notes never move the verdict. A stale or absent replica or event cache is optional; an unknown freshness probe means the cloud comparison could not be proved. The API-backed skills still work. Say whether the local cache is absent, stale or unknown, and ask before starting the optional writer.
|
|
38
38
|
|
|
39
39
|
## When to stop rather than continue
|
|
40
40
|
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Optional onboarding step. The replica command starts the event cache with the writer;
|
|
3
|
+
// only a live heartbeat at the cloud head proves that both were started successfully.
|
|
4
|
+
import { spawnSync } from "node:child_process";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { cliTarget } from "./lib/cli.mjs";
|
|
7
|
+
|
|
8
|
+
const STATUS_COMMAND = ["replica", "status", "--probe", "--json"];
|
|
9
|
+
const EVENTS_COMMAND = ["events", "status", "--probe", "--json"];
|
|
10
|
+
const START_COMMAND = ["replica", "start", "--detach"];
|
|
11
|
+
const RECOVERY_COMMAND = "catalyst-skills replica start --detach";
|
|
12
|
+
|
|
13
|
+
function defaultRun(args) {
|
|
14
|
+
const target = cliTarget();
|
|
15
|
+
const result = spawnSync(target.command, [...target.prefix, ...args], {
|
|
16
|
+
encoding: "utf8",
|
|
17
|
+
timeout: 30_000,
|
|
18
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
19
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
20
|
+
shell: !target.recorded && process.platform === "win32",
|
|
21
|
+
env: process.env,
|
|
22
|
+
});
|
|
23
|
+
return {
|
|
24
|
+
code: result.status ?? 1,
|
|
25
|
+
stdout: result.stdout ?? "",
|
|
26
|
+
stderr: result.stderr ?? "",
|
|
27
|
+
error: result.error?.message,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function assessLocalSync(status, events) {
|
|
32
|
+
if (!status || typeof status !== "object")
|
|
33
|
+
return { verdict: "unknown", current: false, reason: "replica status did not return a document" };
|
|
34
|
+
if (status.verdict === "not-configured")
|
|
35
|
+
return { verdict: "unknown", current: false, reason: "this machine is not connected" };
|
|
36
|
+
if (status.verdict === "absent")
|
|
37
|
+
return { verdict: "absent", current: false, reason: "local replica is absent" };
|
|
38
|
+
if (status.verdict === "unknown" || status.verdict === "unverified" || !["fresh", "stale"].includes(status.verdict))
|
|
39
|
+
return { verdict: "unknown", current: false, reason: `replica freshness is unknown: ${(status.reasons ?? []).join("; ")}` };
|
|
40
|
+
if (status.verdict !== "fresh")
|
|
41
|
+
return {
|
|
42
|
+
verdict: "stale",
|
|
43
|
+
current: false,
|
|
44
|
+
reason: `replica ${status.verdict ?? "unknown"}: ${(status.reasons ?? []).join("; ")}`,
|
|
45
|
+
};
|
|
46
|
+
if (status.writerAlive !== true)
|
|
47
|
+
return { verdict: "stale", current: false, reason: "replica writer heartbeat is absent or stale" };
|
|
48
|
+
if (!Number.isFinite(status.heartbeatAgeMs))
|
|
49
|
+
return { verdict: "unknown", current: false, reason: "replica heartbeat age was not reported" };
|
|
50
|
+
if (status.heartbeatAgeMs >= 15_000)
|
|
51
|
+
return { verdict: "stale", current: false, reason: `replica writer heartbeat is ${status.heartbeatAgeMs}ms old` };
|
|
52
|
+
if (!Number.isSafeInteger(status.cursor) || !Number.isSafeInteger(status.head) || !Number.isSafeInteger(status.lag))
|
|
53
|
+
return { verdict: "unknown", current: false, reason: "replica cursor or cloud head was not reported" };
|
|
54
|
+
if (status.lag !== 0 || status.cursor !== status.head) {
|
|
55
|
+
return {
|
|
56
|
+
verdict: "stale",
|
|
57
|
+
current: false,
|
|
58
|
+
reason: "cloud head and local cursor have not been proved equal",
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
if (!events || !["current", "stale", "absent"].includes(events.verdict)) {
|
|
62
|
+
return {
|
|
63
|
+
verdict: "unknown",
|
|
64
|
+
current: false,
|
|
65
|
+
reason: `event freshness is unknown: ${(events?.reasons ?? []).join("; ")}`,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
if (events.verdict === "absent") {
|
|
69
|
+
return { verdict: "absent", current: false, reason: "local event cache is absent" };
|
|
70
|
+
}
|
|
71
|
+
if (events.verdict === "stale")
|
|
72
|
+
return {
|
|
73
|
+
verdict: "stale",
|
|
74
|
+
current: false,
|
|
75
|
+
reason: `event cache is ${events?.verdict ?? "unverified"}: ${(events?.reasons ?? []).join("; ")}`,
|
|
76
|
+
};
|
|
77
|
+
if (events.writerAlive !== true)
|
|
78
|
+
return { verdict: "stale", current: false, reason: "event writer heartbeat is absent or stale" };
|
|
79
|
+
if (!Number.isFinite(events.heartbeatAgeMs))
|
|
80
|
+
return { verdict: "unknown", current: false, reason: "event writer heartbeat age was not reported" };
|
|
81
|
+
if (events.heartbeatAgeMs >= 15_000)
|
|
82
|
+
return { verdict: "stale", current: false, reason: `event writer heartbeat is ${events.heartbeatAgeMs}ms old` };
|
|
83
|
+
if (!Number.isSafeInteger(events.cursor) || !Number.isSafeInteger(events.head))
|
|
84
|
+
return { verdict: "unknown", current: false, reason: "event cursor or cloud head was not reported" };
|
|
85
|
+
if (events.cursor !== events.head)
|
|
86
|
+
return { verdict: "stale", current: false, reason: `event cache cursor ${events.cursor} differs from cloud head ${events.head}` };
|
|
87
|
+
return {
|
|
88
|
+
verdict: "current",
|
|
89
|
+
current: true,
|
|
90
|
+
reason: `replica cursor ${status.cursor} and event cursor ${events.cursor} each match their cloud head; both writer heartbeats are live`,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function readStatus(run) {
|
|
95
|
+
const replicaResult = run(STATUS_COMMAND);
|
|
96
|
+
let status, events;
|
|
97
|
+
try {
|
|
98
|
+
status = JSON.parse(replicaResult.stdout);
|
|
99
|
+
} catch {
|
|
100
|
+
return {
|
|
101
|
+
status: null,
|
|
102
|
+
events: null,
|
|
103
|
+
assessment: {
|
|
104
|
+
verdict: "unknown",
|
|
105
|
+
current: false,
|
|
106
|
+
reason: `replica status is unknown: ${replicaResult.error || replicaResult.stderr.trim() || "invalid JSON"}`,
|
|
107
|
+
},
|
|
108
|
+
recovery: replicaResult.code === 2 ? "catalyst-skills login" : "catalyst-skills replica status --probe --json",
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
const eventsResult = run(EVENTS_COMMAND);
|
|
112
|
+
try {
|
|
113
|
+
events = JSON.parse(eventsResult.stdout);
|
|
114
|
+
} catch {
|
|
115
|
+
return {
|
|
116
|
+
status,
|
|
117
|
+
events: null,
|
|
118
|
+
assessment: {
|
|
119
|
+
verdict: "unknown",
|
|
120
|
+
current: false,
|
|
121
|
+
reason: `event freshness is unknown: ${eventsResult.error || eventsResult.stderr.trim() || "invalid JSON"}`,
|
|
122
|
+
},
|
|
123
|
+
recovery: "catalyst-skills events status --probe --json",
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
return { status, events, assessment: assessLocalSync(status, events) };
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** A bounded check. Starting the optional writer requires an explicit opt-in. */
|
|
130
|
+
export async function runLocalSync({
|
|
131
|
+
start = false,
|
|
132
|
+
waitSeconds = 90,
|
|
133
|
+
run = defaultRun,
|
|
134
|
+
now = Date.now,
|
|
135
|
+
sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
|
|
136
|
+
} = {}) {
|
|
137
|
+
if (!Number.isInteger(waitSeconds) || waitSeconds < 0 || waitSeconds > 300)
|
|
138
|
+
throw new Error("--wait must be an integer from 0 to 300 seconds");
|
|
139
|
+
let reading = readStatus(run);
|
|
140
|
+
if (reading.assessment.current || !start)
|
|
141
|
+
return { ...reading, started: false, recovery: reading.recovery ?? RECOVERY_COMMAND };
|
|
142
|
+
if (!reading.status)
|
|
143
|
+
return { ...reading, started: false, recovery: reading.recovery ?? "catalyst-skills replica status --probe --json" };
|
|
144
|
+
if (
|
|
145
|
+
reading.status?.verdict === "not-configured" ||
|
|
146
|
+
reading.status?.dbPath == null
|
|
147
|
+
) {
|
|
148
|
+
return { ...reading, started: false, recovery: "catalyst-skills login" };
|
|
149
|
+
}
|
|
150
|
+
let started = false;
|
|
151
|
+
if (reading.status.writerAlive !== true && ["absent", "stale"].includes(reading.assessment.verdict)) {
|
|
152
|
+
const result = run(START_COMMAND);
|
|
153
|
+
if (result.code !== 0) {
|
|
154
|
+
return {
|
|
155
|
+
status: reading.status,
|
|
156
|
+
events: reading.events,
|
|
157
|
+
assessment: {
|
|
158
|
+
verdict: "unknown",
|
|
159
|
+
current: false,
|
|
160
|
+
reason: `writer start failed: ${result.error || result.stderr.trim() || result.stdout.trim()}`,
|
|
161
|
+
},
|
|
162
|
+
started: false,
|
|
163
|
+
recovery: RECOVERY_COMMAND,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
started = true;
|
|
167
|
+
}
|
|
168
|
+
const deadline = now() + waitSeconds * 1000;
|
|
169
|
+
do {
|
|
170
|
+
reading = readStatus(run);
|
|
171
|
+
if (reading.assessment.current || now() >= deadline) break;
|
|
172
|
+
await sleep(Math.min(3000, deadline - now()));
|
|
173
|
+
} while (true);
|
|
174
|
+
return {
|
|
175
|
+
...reading,
|
|
176
|
+
started,
|
|
177
|
+
recovery: reading.assessment.verdict === "unknown"
|
|
178
|
+
? "node scripts/local-sync.mjs --json"
|
|
179
|
+
: reading.status?.writerAlive
|
|
180
|
+
? "catalyst-skills replica stop && catalyst-skills replica start --detach"
|
|
181
|
+
: RECOVERY_COMMAND,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function parseArgs(argv) {
|
|
186
|
+
const opts = { start: false, waitSeconds: 90, json: false };
|
|
187
|
+
for (let i = 0; i < argv.length; i++) {
|
|
188
|
+
if (argv[i] === "--start") opts.start = true;
|
|
189
|
+
else if (argv[i] === "--json") opts.json = true;
|
|
190
|
+
else if (argv[i] === "--wait") opts.waitSeconds = Number(argv[++i]);
|
|
191
|
+
else if (argv[i] === "--help") {
|
|
192
|
+
console.log(
|
|
193
|
+
"Usage: node scripts/local-sync.mjs [--start] [--wait 0..300] [--json]\nWithout --start, this only checks status. Local sync is optional.",
|
|
194
|
+
);
|
|
195
|
+
process.exit(0);
|
|
196
|
+
} else throw new Error(`Unknown argument: ${argv[i]}`);
|
|
197
|
+
}
|
|
198
|
+
return opts;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]) {
|
|
202
|
+
try {
|
|
203
|
+
const opts = parseArgs(process.argv.slice(2));
|
|
204
|
+
const result = await runLocalSync(opts);
|
|
205
|
+
if (opts.json) console.log(JSON.stringify(result));
|
|
206
|
+
else {
|
|
207
|
+
const label = result.assessment.verdict === "current" ? "current" : result.assessment.verdict;
|
|
208
|
+
console.log(`Local sync ${label}: ${result.assessment.reason}`);
|
|
209
|
+
if (!result.assessment.current) console.log(`Next: ${result.recovery}`);
|
|
210
|
+
}
|
|
211
|
+
process.exitCode = result.assessment.verdict === "current" ? 0 : result.assessment.verdict === "stale" ? 1 : result.assessment.verdict === "absent" ? 3 : 2;
|
|
212
|
+
} catch (error) {
|
|
213
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
214
|
+
process.exitCode = 2;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
//
|
|
7
7
|
// Every count and every name below is read off what a verb printed. Nothing here is written down.
|
|
8
8
|
import { cliTarget, CONNECT_LINE, parseFlags, printHelp, runCli, tryJson, tryLoadConfig } from "./lib/cli.mjs";
|
|
9
|
+
import { runLocalSync } from "./local-sync.mjs";
|
|
9
10
|
|
|
10
11
|
const SPEC = {
|
|
11
12
|
next: { help: "print only the single next step" },
|
|
@@ -13,7 +14,8 @@ const SPEC = {
|
|
|
13
14
|
};
|
|
14
15
|
const NOTES = [
|
|
15
16
|
"Reads, in this order: `status` (machine), `ready --json` (machine checks and project checks, kept apart),",
|
|
16
|
-
"`
|
|
17
|
+
"`replica status --probe --json` and `events status --probe --json` (optional local freshness),",
|
|
18
|
+
"`me --json` and personal connection statuses (person), `contract --path …` for the account, the projects and the repositories,",
|
|
17
19
|
"`contract --path codingAccounts` (coding accounts; `accounts --json` on an older cloud), and each",
|
|
18
20
|
"project's hosts_current check with its fixedWhere (host).",
|
|
19
21
|
"Writes nothing and changes nothing. Runs before this machine is connected — that is one of the states it reports.",
|
|
@@ -31,6 +33,13 @@ if (positionals.length > 0) {
|
|
|
31
33
|
|
|
32
34
|
const via = cliTarget().via;
|
|
33
35
|
const parts = [];
|
|
36
|
+
const personalConnections = {};
|
|
37
|
+
let personalGrantIncomplete = false;
|
|
38
|
+
let personalLinearIncomplete = false;
|
|
39
|
+
let personalGithubIncomplete = false;
|
|
40
|
+
let personalNext = null;
|
|
41
|
+
let workspaceResolved = false;
|
|
42
|
+
let repositoryRegistered = false;
|
|
34
43
|
// `blocking` is false for a finding that is real and reportable but does not stop the next step —
|
|
35
44
|
// an unmatched Linear identity is the one that matters: it must be said, and it must not become the
|
|
36
45
|
// thing the person is told to go and do before they can map a project.
|
|
@@ -64,6 +73,7 @@ let machineVerdict = connected ? "ok" : "unfinished";
|
|
|
64
73
|
// whose id begins with "team:" belongs to a project and cannot be moved from this machine.
|
|
65
74
|
let projectChecks = [];
|
|
66
75
|
let machineFix = null;
|
|
76
|
+
let localSync;
|
|
67
77
|
if (connected) {
|
|
68
78
|
const ready = runCli(["ready", "--json"]);
|
|
69
79
|
const report = tryJson(ready.stdout);
|
|
@@ -81,6 +91,17 @@ if (connected) {
|
|
|
81
91
|
machineFix = failed.find((c) => typeof c.fix === "string")?.fix ?? null;
|
|
82
92
|
}
|
|
83
93
|
}
|
|
94
|
+
// Supplemental only: local caches are optional and do not change setup completion or --next.
|
|
95
|
+
localSync = await runLocalSync({ waitSeconds: 0 });
|
|
96
|
+
machineLines.push(
|
|
97
|
+
`note optional local sync ${localSync.assessment.verdict}: ${localSync.assessment.reason}; check with 'node scripts/local-sync.mjs', and start only with the person's opt-in via 'node scripts/local-sync.mjs --start'`,
|
|
98
|
+
);
|
|
99
|
+
} else {
|
|
100
|
+
localSync = {
|
|
101
|
+
assessment: { verdict: "unknown", current: false, reason: "connect this machine before local freshness can be checked" },
|
|
102
|
+
started: false,
|
|
103
|
+
recovery: "catalyst-skills login",
|
|
104
|
+
};
|
|
84
105
|
}
|
|
85
106
|
add(
|
|
86
107
|
"machine",
|
|
@@ -111,13 +132,35 @@ if (!connected) {
|
|
|
111
132
|
);
|
|
112
133
|
} else {
|
|
113
134
|
const matched = typeof user.linearUserId === "string" && user.linearUserId !== "";
|
|
135
|
+
const grantLines = [];
|
|
136
|
+
for (const provider of ["linear", "github"]) {
|
|
137
|
+
const read = runCli(["connections", "personal", provider, "status", "--json"]);
|
|
138
|
+
const result = tryJson(read.stdout);
|
|
139
|
+
const outcome = typeof result?.outcome === "string" ? result.outcome : "unreadable";
|
|
140
|
+
personalConnections[provider] = outcome;
|
|
141
|
+
if (outcome === "connected") {
|
|
142
|
+
grantLines.push(`personal ${provider}: connected`);
|
|
143
|
+
} else if (outcome === "absent" || outcome === "lapsed") {
|
|
144
|
+
personalGrantIncomplete = true;
|
|
145
|
+
if (provider === "linear") personalLinearIncomplete = true;
|
|
146
|
+
else personalGithubIncomplete = true;
|
|
147
|
+
grantLines.push(`personal ${provider}: ${outcome === "absent" ? "not connected" : "expired"}`);
|
|
148
|
+
if (provider === "linear") personalNext ??= "connect your personal linear account";
|
|
149
|
+
} else {
|
|
150
|
+
personalGrantIncomplete = true;
|
|
151
|
+
if (provider === "linear") personalLinearIncomplete = true;
|
|
152
|
+
else personalGithubIncomplete = true;
|
|
153
|
+
grantLines.push(`personal ${provider}: ${outcome === "unavailable" ? "temporarily unavailable; grant state unknown" : "could not be checked; update the catalyst-skills CLI or inspect its status output"}`);
|
|
154
|
+
if (provider === "linear") personalNext ??= "re-check your personal linear connection";
|
|
155
|
+
}
|
|
156
|
+
}
|
|
114
157
|
add(
|
|
115
158
|
"person",
|
|
116
|
-
"catalyst-skills me",
|
|
117
|
-
matched ? "ok" : "unfinished",
|
|
118
|
-
[`${user.label ?? "(unnamed)"} (${user.role ?? "role unknown"})`, matched ? "Linear identity matched" : "Linear identity NOT matched — asks assigned to you cannot be told apart from everyone else's. It blocks nothing below;
|
|
119
|
-
matched ? null : "a tenant owner or admin",
|
|
120
|
-
matched ? null : link("/settings/account"),
|
|
159
|
+
"catalyst-skills me, and catalyst-skills connections personal <provider> status --json",
|
|
160
|
+
matched && !personalGrantIncomplete ? "ok" : "unfinished",
|
|
161
|
+
[`${user.label ?? "(unnamed)"} (${user.role ?? "role unknown"})`, matched ? "Linear identity matched" : "Linear identity NOT matched — asks assigned to you cannot be told apart from everyone else's. It blocks nothing below. Run catalyst-skills identity linear options for self-service recovery; personal Linear consent normally binds its viewer automatically.", ...grantLines],
|
|
162
|
+
personalGrantIncomplete ? "you" : matched ? null : "a tenant owner or admin",
|
|
163
|
+
personalGrantIncomplete ? "catalyst-skills connections personal <provider> start or status" : matched ? null : link("/settings/account"),
|
|
121
164
|
false,
|
|
122
165
|
);
|
|
123
166
|
}
|
|
@@ -133,6 +176,7 @@ if (!connected) {
|
|
|
133
176
|
add("account", "catalyst-skills contract --path account", "unreadable", ["the account block could not be read — try: catalyst-skills contract --refresh"], null, null);
|
|
134
177
|
} else {
|
|
135
178
|
const workspace = typeof doc.linearWorkspaceSlug === "string" && doc.linearWorkspaceSlug !== "" ? doc.linearWorkspaceSlug : typeof doc.linearWorkspaceId === "string" && doc.linearWorkspaceId !== "" ? doc.linearWorkspaceId : null;
|
|
179
|
+
workspaceResolved = workspace !== null;
|
|
136
180
|
// The declaration is the one part of the account a key can also READ — and it is the one part a
|
|
137
181
|
// key can WRITE, so it is reported here rather than left to the settings page like the rest.
|
|
138
182
|
const envLines = [];
|
|
@@ -212,6 +256,7 @@ if (!connected) {
|
|
|
212
256
|
add("repositories", "catalyst-skills contract --path merge.repositories", "unreadable", ["the repository list could not be read — try: catalyst-skills contract --refresh"], null, null);
|
|
213
257
|
} else {
|
|
214
258
|
const lines = [`${rows.length} registered`, ...rows.map((r) => `${r.owner ?? "?"}/${r.name ?? "?"}`)];
|
|
259
|
+
repositoryRegistered = rows.length > 0;
|
|
215
260
|
lines.push("⛔ REGISTRATION only. This carries no status and no project attachment, so it never proves a repository can be dispatched into.");
|
|
216
261
|
add("repositories", "catalyst-skills contract --path merge.repositories", rows.length > 0 ? "ok" : "unfinished", lines, "a tenant owner or admin", link("/settings/repositories"));
|
|
217
262
|
}
|
|
@@ -309,13 +354,28 @@ const machineNext = connected
|
|
|
309
354
|
: "connect this machine";
|
|
310
355
|
const NEXT = {
|
|
311
356
|
machine: machineNext,
|
|
312
|
-
person:
|
|
357
|
+
person: personalLinearIncomplete
|
|
358
|
+
? personalNext
|
|
359
|
+
: personalGithubIncomplete && repositoryRegistered
|
|
360
|
+
? "connect your personal github account"
|
|
361
|
+
: personalGithubIncomplete
|
|
362
|
+
? "install the tenant GitHub App and register its repository before connecting your personal GitHub account"
|
|
363
|
+
: "get this person's seat and Linear identity sorted",
|
|
313
364
|
account: "connect Linear, and install the GitHub App",
|
|
314
365
|
projects: "pick ONE project and map its stages (or adopt the Catalyst workflow)",
|
|
315
366
|
repositories: "register the repository, attaching it to the project you mapped",
|
|
316
367
|
"coding accounts": accountsNext,
|
|
317
368
|
host: "connect a Catalyst host",
|
|
318
369
|
};
|
|
370
|
+
// A personal Linear grant cannot start until the tenant's Linear workspace exists. Once that account
|
|
371
|
+
// connection is present, a missing personal grant becomes the next member step before project setup.
|
|
372
|
+
const personPart = parts.find((p) => p.part === "person");
|
|
373
|
+
// A personal Linear grant is the next provider step once the tenant workspace exists. A personal
|
|
374
|
+
// GitHub grant is sequenced after the tenant GitHub App: successful repository registration is the
|
|
375
|
+
// onboarding path's existing proof that the App installation is usable.
|
|
376
|
+
if (personPart && personalGrantIncomplete && workspaceResolved) {
|
|
377
|
+
personPart.blocking = personalLinearIncomplete || (personalGithubIncomplete && repositoryRegistered);
|
|
378
|
+
}
|
|
319
379
|
const blocked = parts.filter((p) => p.verdict !== "ok" && p.blocking);
|
|
320
380
|
const stuck = blocked[0] ?? parts.find((p) => p.verdict !== "ok") ?? null;
|
|
321
381
|
const next =
|
|
@@ -325,7 +385,7 @@ const next =
|
|
|
325
385
|
const finished = parts.every((p) => p.verdict === "ok");
|
|
326
386
|
|
|
327
387
|
if (flags.json) {
|
|
328
|
-
console.log(JSON.stringify({ cli: via, connected, cloud, parts, next, finished }));
|
|
388
|
+
console.log(JSON.stringify({ cli: via, connected, cloud, personalConnections, parts, localSync, next, finished }));
|
|
329
389
|
} else if (flags.next) {
|
|
330
390
|
if (next === null) console.log("nothing left: every part is finished, a coding account is enrolled and the host check passes. Move one card into the project's dispatch stage.");
|
|
331
391
|
else console.log(`${next.part}: ${next.action}${next.blocking ? "" : " (does not block the steps below)"}${next.owner ? ` — who: ${next.owner}` : ""}${next.where ? ` — ${next.where.startsWith("http") ? "where" : "do"}: ${next.where}` : ""}`);
|
|
@@ -4,7 +4,7 @@ description: >-
|
|
|
4
4
|
Am I set up? Machine readiness (Node, the tenant connection, the cached contract, the CLI path, the skills, the SDK, the optional replica) plus tenant readiness from the contract's per-team checks, in one verdict: what passes, what is blocked, what is merely waiting, and who can click what. Use when someone asks "am I set up", "what is missing", "why does nothing happen", "is the replica running", or right after connecting a new machine. Reports; never repairs.
|
|
5
5
|
allowed-tools: Bash(catalyst-skills:*) Bash(npx @catalyst-cloud/catalyst-skills:*)
|
|
6
6
|
---
|
|
7
|
-
<!-- vendored-from: @catalyst-cloud/catalyst-skills@0.
|
|
7
|
+
<!-- vendored-from: @catalyst-cloud/catalyst-skills@0.9.0 — written in this repository for customer tenants -->
|
|
8
8
|
|
|
9
9
|
# Am I set up?
|
|
10
10
|
|
|
@@ -5,7 +5,7 @@ description: >-
|
|
|
5
5
|
disable-model-invocation: true
|
|
6
6
|
allowed-tools: Bash(catalyst-skills:*) Bash(npx @catalyst-cloud/catalyst-skills:*)
|
|
7
7
|
---
|
|
8
|
-
<!-- vendored-from: @catalyst-cloud/catalyst-skills@0.
|
|
8
|
+
<!-- vendored-from: @catalyst-cloud/catalyst-skills@0.9.0 — written in this repository for customer tenants -->
|
|
9
9
|
|
|
10
10
|
# Connect me
|
|
11
11
|
|