@catalyst-cloud/catalyst-skills 0.3.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.md +28 -15
  3. package/dist/args.js +19 -1
  4. package/dist/ask.js +1 -1
  5. package/dist/browser.js +20 -0
  6. package/dist/cli.js +56 -22
  7. package/dist/config.js +48 -6
  8. package/dist/contract.js +1 -1
  9. package/dist/events.js +106 -0
  10. package/dist/execution.js +55 -9
  11. package/dist/oauth.js +300 -0
  12. package/dist/query.js +1 -1
  13. package/dist/ready.js +1 -1
  14. package/dist/release.js +142 -0
  15. package/dist/replica.js +10 -3
  16. package/dist/{http.js → transport.js} +21 -10
  17. package/dist/watch/consumer.js +2 -2
  18. package/dist/watch.js +4 -2
  19. package/dist/write.js +1 -1
  20. package/package.json +5 -4
  21. package/skills/catalyst-github/scripts/lib/cli.mjs +3 -2
  22. package/skills/catalyst-github/scripts/lib/credential.mjs +29 -0
  23. package/skills/catalyst-linear/scripts/lib/cli.mjs +4 -3
  24. package/skills/catalyst-linear/scripts/lib/credential.mjs +29 -0
  25. package/skills/catalyst-setup/SKILL.md +1 -1
  26. package/skills/catalyst-setup/references/what-each-check-means.md +5 -1
  27. package/skills/catalyst-setup/scripts/lib/cli.mjs +3 -2
  28. package/skills/catalyst-setup/scripts/lib/credential.mjs +29 -0
  29. package/skills/catalyst-setup/scripts/replica-status.mjs +1 -1
  30. package/skills/connect-me/SKILL.md +16 -8
  31. package/skills/connect-me/scripts/lib/cli.mjs +3 -2
  32. package/skills/connect-me/scripts/lib/credential.mjs +29 -0
  33. package/skills/how-catalyst-works/references/stages-and-mapping.md +1 -1
  34. package/skills/how-catalyst-works/references/what-runs-next.md +1 -1
  35. package/skills/how-catalyst-works/references/when-a-phase-fails.md +4 -4
  36. package/skills/how-catalyst-works/scripts/lib/cli.mjs +4 -3
  37. package/skills/how-catalyst-works/scripts/lib/credential.mjs +29 -0
  38. package/skills/run-this-project/SKILL.md +1 -1
  39. package/skills/run-this-project/references/making-work-ready.md +1 -1
  40. package/skills/run-this-project/references/stalls-and-escalation.md +2 -2
  41. package/skills/run-this-project/scripts/lib/cli.mjs +3 -2
  42. package/skills/run-this-project/scripts/lib/credential.mjs +29 -0
  43. package/skills/unstick/SKILL.md +41 -0
  44. package/skills/unstick/agents/openai.yaml +6 -0
  45. package/skills/unstick/agents/portability.yaml +5 -0
  46. package/skills/unstick/references/playbook.md +51 -0
  47. package/skills/unstick/scripts/lib/cli.mjs +135 -0
  48. package/skills/unstick/scripts/lib/credential.mjs +29 -0
  49. package/skills/unstick/scripts/unstick.mjs +57 -0
  50. package/skills/what-needs-me/scripts/lib/cli.mjs +3 -2
  51. package/skills/what-needs-me/scripts/lib/credential.mjs +29 -0
  52. package/skills/whats-happening/SKILL.md +1 -1
  53. package/skills/whats-happening/references/why-is-it-stuck.md +9 -4
  54. package/skills/whats-happening/scripts/lib/cli.mjs +3 -2
  55. package/skills/whats-happening/scripts/lib/credential.mjs +29 -0
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env node
2
+ // lib/credential.mjs — is this machine connected? The ONE place a skill script decides it, vendored
3
+ // byte-identical into every skill's scripts/lib/ from skill-lib/credential.mjs at the package root
4
+ // (`npm run skill-lib:sync`; a test fails on any drift). Skills install one directory at a time, so
5
+ // each carries its own copy. This file is a library — run a sibling script with --help for usage.
6
+ //
7
+ // customer.json carries exactly one credential: a personal key (`key`), or the keyless login's
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
10
+ // itself. A new credential kind lands here, once.
11
+
12
+ /** The command that connects this machine, as every not-connected line names it. */
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)";
15
+
16
+ /** True when `cfg` (parsed customer.json) holds a usable credential of either kind. Never throws. */
17
+ export function hasCredential(cfg) {
18
+ if (cfg === null || typeof cfg !== "object") return false;
19
+ const key = cfg["key"];
20
+ if (typeof key === "string" && key !== "") return true;
21
+ const login = cfg["auth"];
22
+ return (
23
+ login !== null &&
24
+ typeof login === "object" &&
25
+ login["kind"] === "oauth" &&
26
+ typeof login["refreshToken"] === "string" &&
27
+ login["refreshToken"] !== ""
28
+ );
29
+ }
@@ -36,7 +36,7 @@ For depth beyond these, load the fact skills: `how-catalyst-works` (the ladder,
36
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
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
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, and a park is released only by an operator; the CLI prints the settings URL, and you repeat it instead of guessing. Coding-account status (`accounts`) and per-ticket execution history (`explain --history`) are readable — read them.
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
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
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
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.
@@ -34,13 +34,18 @@ This reference restates an invariant: the vocabulary the cloud's eligibility exp
34
34
  | `repo_paused` | an operator paused the repository | the operator resumes it |
35
35
  | `scope_overlap` | its declared file scope intersects a ticket already in flight | wait for the other ticket, or the human decides which goes first |
36
36
 
37
- ## Reasons that need an operator, and what a key cannot do
37
+ ## Reasons a release clears once the cause is fixed
38
+
39
+ These do not release themselves, and nobody but the person needs to act: once the recorded cause is fixed, the person's own login releases them with `catalyst-skills release <ticket>`. The desk does not release; route the ticket to the `unstick` skill, which reads the history, previews the release and runs it, or names what a person must do first.
38
40
 
39
41
  | reason | what it means | note |
40
42
  | -- | -- | -- |
41
- | `cooling_down` (parked after repeated failures, or the round cap was spent) | the phase is parked and does not release itself | an operator unparks it; your key has no unpark verb, so the reply names the park (from `explain --history`) and the settings page |
43
+ | `phase_parked` / `cooling_down` (parked after repeated failures, or the round cap was spent) | the phase is parked and does not release itself | `unstick`; `explain --history` names the park and the failure class it recorded |
42
44
  | `remediate_parked` | the repair phase itself is parked, so the failing phase has nowhere to be repaired | as above |
43
- | `validate_class_spent` | this validate failure already spent its one repair round in this episode | as above; a human comment may release the hold |
45
+ | `validate_class_spent` | this validate failure already spent its one repair round in this episode | a push or a comment saying what to change releases it on its own; otherwise `unstick` |
46
+ | `human_owned_pr` | a person's own pull request holds the ticket | that person closes or merges it; no release clears it |
47
+ | `review_not_converging` | review and repair kept finding new problems without converging | a person reads the findings and comments on the ticket to resume; raise an ask for that read |
48
+ | `round_threshold` | the ticket spent its lifetime repair budget | a person answers the ask the cloud raised, or pushes a fix |
44
49
  | `no_branch_to_remediate` / `branch_missing` / `branch_gone` | a branch-dependent phase has no branch to clone | the cloud releases missing-branch parks on its own budget; a deleted branch needs a human to decide |
45
50
  | `stale_failure_episode` | the ladder advanced after the recorded failure | informational; the round is dropped |
46
51
 
@@ -50,7 +55,7 @@ This reference restates an invariant: the vocabulary the cloud's eligibility exp
50
55
 
51
56
  ## When the cloud could not judge
52
57
 
53
- A status of `unknown` with a reason such as `ordering_never_published`, `ordering_stale`, `workflow_mapping_unknown`, `ticket_unknown`, `dependency_snapshot_unknown`, `blocker_unknown`, `label_snapshot_unknown`, `prior_artifact_unknown`, `scope_unknown`, `scope_occupancy_unknown` or `branch_snapshot_unknown` means the evaluator failed closed rather than guessing. Most clear on the cloud's next pass. If one persists for a team, the readiness checks (`catalyst-setup`) are the next read.
58
+ A status of `unknown` with a reason such as `ordering_never_published`, `ordering_stale`, `workflow_mapping_unknown`, `ticket_unknown`, `dependency_snapshot_unknown`, `blocker_unknown`, `label_snapshot_unknown`, `prior_artifact_unknown`, `scope_unknown`, `scope_occupancy_unknown` or `branch_snapshot_unknown` means the evaluator failed closed rather than guessing. Most clear on the cloud's next pass. `workflow_mapping_unknown`, and `ordering_never_published` beside it, does not clear by itself when the team has no saved stage mapping: `explain` then names the missing stages, and a tenant owner or admin maps the team in Settings → Linear teams. If any other one persists for a team, the readiness checks (`catalyst-setup`) are the next read.
54
59
 
55
60
  ## The one advisory
56
61
 
@@ -6,6 +6,7 @@
6
6
  import { spawnSync } from "node:child_process";
7
7
  import { existsSync, readFileSync } from "node:fs";
8
8
  import { join } from "node:path";
9
+ import { CONNECT_COMMAND, hasCredential } from "./credential.mjs";
9
10
 
10
11
  export const PACKAGE = "@catalyst-cloud/catalyst-skills";
11
12
  export const NOT_CONFIGURED_EXIT = 2;
@@ -21,7 +22,7 @@ export function loadConfig() {
21
22
  if (!existsSync(path)) notConfigured(`no config at ${path}`);
22
23
  try {
23
24
  const cfg = JSON.parse(readFileSync(path, "utf8"));
24
- if (typeof cfg !== "object" || cfg === null || typeof cfg.key !== "string") notConfigured(`config at ${path} has no key`);
25
+ if (!hasCredential(cfg)) notConfigured(`config at ${path} has no key or login`);
25
26
  return cfg;
26
27
  } catch (err) {
27
28
  if (err && err.exitCode === NOT_CONFIGURED_EXIT) throw err;
@@ -30,7 +31,7 @@ export function loadConfig() {
30
31
  }
31
32
 
32
33
  function notConfigured(why) {
33
- process.stderr.write(`not connected (${why}) — run: CATALYST_CLOUD_TOKEN=<your personal key> npx ${PACKAGE} login\n`);
34
+ process.stderr.write(`not connected (${why}) — run: ${CONNECT_COMMAND}\n`);
34
35
  process.exit(NOT_CONFIGURED_EXIT);
35
36
  }
36
37
 
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env node
2
+ // lib/credential.mjs — is this machine connected? The ONE place a skill script decides it, vendored
3
+ // byte-identical into every skill's scripts/lib/ from skill-lib/credential.mjs at the package root
4
+ // (`npm run skill-lib:sync`; a test fails on any drift). Skills install one directory at a time, so
5
+ // each carries its own copy. This file is a library — run a sibling script with --help for usage.
6
+ //
7
+ // customer.json carries exactly one credential: a personal key (`key`), or the keyless login's
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
10
+ // itself. A new credential kind lands here, once.
11
+
12
+ /** The command that connects this machine, as every not-connected line names it. */
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)";
15
+
16
+ /** True when `cfg` (parsed customer.json) holds a usable credential of either kind. Never throws. */
17
+ export function hasCredential(cfg) {
18
+ if (cfg === null || typeof cfg !== "object") return false;
19
+ const key = cfg["key"];
20
+ if (typeof key === "string" && key !== "") return true;
21
+ const login = cfg["auth"];
22
+ return (
23
+ login !== null &&
24
+ typeof login === "object" &&
25
+ login["kind"] === "oauth" &&
26
+ typeof login["refreshToken"] === "string" &&
27
+ login["refreshToken"] !== ""
28
+ );
29
+ }