@catalyst-cloud/catalyst-skills 0.5.0 → 0.8.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 (127) hide show
  1. package/README.md +11 -178
  2. package/bin/catalyst-skills.js +3 -49
  3. package/bin/catalyst.js +3 -0
  4. package/package.json +8 -29
  5. package/CHANGELOG.md +0 -41
  6. package/LICENSE +0 -21
  7. package/dist/args.js +0 -261
  8. package/dist/ask.js +0 -158
  9. package/dist/browser.js +0 -20
  10. package/dist/cli.js +0 -356
  11. package/dist/config.js +0 -148
  12. package/dist/contract-types.js +0 -4
  13. package/dist/contract.js +0 -178
  14. package/dist/detach.js +0 -10
  15. package/dist/errors.js +0 -27
  16. package/dist/events.js +0 -106
  17. package/dist/execution.js +0 -369
  18. package/dist/oauth.js +0 -300
  19. package/dist/prompt.js +0 -35
  20. package/dist/query.js +0 -225
  21. package/dist/ready.js +0 -163
  22. package/dist/release.js +0 -142
  23. package/dist/replica.js +0 -325
  24. package/dist/sdk.js +0 -26
  25. package/dist/skill-shape.js +0 -179
  26. package/dist/skills.js +0 -78
  27. package/dist/transport.js +0 -205
  28. package/dist/ts-deps-loader.js +0 -68
  29. package/dist/watch/consumer.js +0 -141
  30. package/dist/watch/cursor-file.js +0 -62
  31. package/dist/watch.js +0 -175
  32. package/dist/write.js +0 -224
  33. package/skills/catalyst-github/SKILL.md +0 -35
  34. package/skills/catalyst-github/agents/openai.yaml +0 -6
  35. package/skills/catalyst-github/agents/portability.yaml +0 -4
  36. package/skills/catalyst-github/references/is-it-mergeable.md +0 -57
  37. package/skills/catalyst-github/references/what-a-pr-accumulates.md +0 -61
  38. package/skills/catalyst-github/scripts/is-it-mergeable.mjs +0 -124
  39. package/skills/catalyst-github/scripts/lib/cli.mjs +0 -103
  40. package/skills/catalyst-github/scripts/lib/credential.mjs +0 -29
  41. package/skills/catalyst-github/scripts/lib/pull.mjs +0 -82
  42. package/skills/catalyst-github/scripts/read-pr.mjs +0 -97
  43. package/skills/catalyst-linear/SKILL.md +0 -43
  44. package/skills/catalyst-linear/agents/openai.yaml +0 -6
  45. package/skills/catalyst-linear/agents/portability.yaml +0 -5
  46. package/skills/catalyst-linear/references/reading-a-ticket.md +0 -50
  47. package/skills/catalyst-linear/references/what-a-ticket-accumulates.md +0 -53
  48. package/skills/catalyst-linear/references/writing-to-linear.md +0 -43
  49. package/skills/catalyst-linear/scripts/comment.mjs +0 -59
  50. package/skills/catalyst-linear/scripts/create-ticket.mjs +0 -44
  51. package/skills/catalyst-linear/scripts/label.mjs +0 -48
  52. package/skills/catalyst-linear/scripts/lib/cli.mjs +0 -164
  53. package/skills/catalyst-linear/scripts/lib/credential.mjs +0 -29
  54. package/skills/catalyst-linear/scripts/move.mjs +0 -41
  55. package/skills/catalyst-linear/scripts/read-ticket.mjs +0 -93
  56. package/skills/catalyst-linear/scripts/search.mjs +0 -49
  57. package/skills/catalyst-setup/SKILL.md +0 -32
  58. package/skills/catalyst-setup/agents/openai.yaml +0 -6
  59. package/skills/catalyst-setup/agents/portability.yaml +0 -4
  60. package/skills/catalyst-setup/references/what-each-check-means.md +0 -66
  61. package/skills/catalyst-setup/scripts/check.mjs +0 -75
  62. package/skills/catalyst-setup/scripts/lib/cli.mjs +0 -103
  63. package/skills/catalyst-setup/scripts/lib/credential.mjs +0 -29
  64. package/skills/catalyst-setup/scripts/replica-status.mjs +0 -46
  65. package/skills/connect-me/SKILL.md +0 -63
  66. package/skills/connect-me/agents/openai.yaml +0 -6
  67. package/skills/connect-me/agents/portability.yaml +0 -5
  68. package/skills/connect-me/references/keeping-the-replica-running.md +0 -88
  69. package/skills/connect-me/scripts/lib/cli.mjs +0 -185
  70. package/skills/connect-me/scripts/lib/credential.mjs +0 -29
  71. package/skills/connect-me/scripts/verify-connection.mjs +0 -68
  72. package/skills/how-catalyst-works/SKILL.md +0 -42
  73. package/skills/how-catalyst-works/agents/openai.yaml +0 -6
  74. package/skills/how-catalyst-works/agents/portability.yaml +0 -4
  75. package/skills/how-catalyst-works/references/coding-accounts.md +0 -51
  76. package/skills/how-catalyst-works/references/stages-and-mapping.md +0 -54
  77. package/skills/how-catalyst-works/references/the-ladder.md +0 -41
  78. package/skills/how-catalyst-works/references/what-runs-next.md +0 -77
  79. package/skills/how-catalyst-works/references/when-a-phase-fails.md +0 -57
  80. package/skills/how-catalyst-works/scripts/explain-ticket.mjs +0 -41
  81. package/skills/how-catalyst-works/scripts/lib/cli.mjs +0 -164
  82. package/skills/how-catalyst-works/scripts/lib/credential.mjs +0 -29
  83. package/skills/how-catalyst-works/scripts/show-my-map.mjs +0 -94
  84. package/skills/how-catalyst-works/scripts/whats-running.mjs +0 -65
  85. package/skills/run-this-project/SKILL.md +0 -45
  86. package/skills/run-this-project/agents/openai.yaml +0 -6
  87. package/skills/run-this-project/agents/portability.yaml +0 -5
  88. package/skills/run-this-project/assets/stall-policy.json +0 -15
  89. package/skills/run-this-project/references/making-work-ready.md +0 -60
  90. package/skills/run-this-project/references/reacting-to-events.md +0 -76
  91. package/skills/run-this-project/references/stalls-and-escalation.md +0 -63
  92. package/skills/run-this-project/scripts/lib/cli.mjs +0 -185
  93. package/skills/run-this-project/scripts/lib/credential.mjs +0 -29
  94. package/skills/run-this-project/scripts/make-ready.mjs +0 -64
  95. package/skills/run-this-project/scripts/scope-status.mjs +0 -0
  96. package/skills/run-this-project/scripts/watch-scope.mjs +0 -61
  97. package/skills/unstick/SKILL.md +0 -41
  98. package/skills/unstick/agents/openai.yaml +0 -6
  99. package/skills/unstick/agents/portability.yaml +0 -5
  100. package/skills/unstick/references/playbook.md +0 -51
  101. package/skills/unstick/scripts/lib/cli.mjs +0 -135
  102. package/skills/unstick/scripts/lib/credential.mjs +0 -29
  103. package/skills/unstick/scripts/unstick.mjs +0 -57
  104. package/skills/what-needs-me/SKILL.md +0 -41
  105. package/skills/what-needs-me/agents/openai.yaml +0 -6
  106. package/skills/what-needs-me/agents/portability.yaml +0 -5
  107. package/skills/what-needs-me/references/raising-a-decision.md +0 -41
  108. package/skills/what-needs-me/references/reading-the-inbox.md +0 -38
  109. package/skills/what-needs-me/references/settling-an-answer.md +0 -37
  110. package/skills/what-needs-me/scripts/inbox.mjs +0 -56
  111. package/skills/what-needs-me/scripts/lib/cli.mjs +0 -135
  112. package/skills/what-needs-me/scripts/lib/credential.mjs +0 -29
  113. package/skills/what-needs-me/scripts/raise.mjs +0 -53
  114. package/skills/what-needs-me/scripts/settle.mjs +0 -73
  115. package/skills/whats-happening/SKILL.md +0 -43
  116. package/skills/whats-happening/agents/openai.yaml +0 -6
  117. package/skills/whats-happening/agents/portability.yaml +0 -4
  118. package/skills/whats-happening/assets/status-reply.json +0 -77
  119. package/skills/whats-happening/references/reading-the-board.md +0 -43
  120. package/skills/whats-happening/references/reprioritising.md +0 -37
  121. package/skills/whats-happening/references/routing-work.md +0 -36
  122. package/skills/whats-happening/references/status-reply.md +0 -34
  123. package/skills/whats-happening/references/why-is-it-stuck.md +0 -62
  124. package/skills/whats-happening/scripts/explain.mjs +0 -28
  125. package/skills/whats-happening/scripts/lib/cli.mjs +0 -135
  126. package/skills/whats-happening/scripts/lib/credential.mjs +0 -29
  127. package/skills/whats-happening/scripts/snapshot.mjs +0 -145
@@ -1,94 +0,0 @@
1
- #!/usr/bin/env node
2
- // show-my-map.mjs — this tenant's stage map, ladder and thresholds, straight from the contract:
3
- // per team, every slot with the Linear stage it maps to, its type, whether that state still exists
4
- // and how the mapping was chosen; then the ladder and the live failure thresholds.
5
- import { exitOnFailure, parseFlags, parseJson, relayStderr, runCli, wantsHelp } from "./lib/cli.mjs";
6
-
7
- const HELP = `Usage: node scripts/show-my-map.mjs [--team <key>] [--json]
8
-
9
- Prints the tenant contract's stage map, ladder and thresholds. Nothing here is guessed: every value is
10
- read from the contract Catalyst Cloud serves for your tenant. Wraps: catalyst-skills contract --path.
11
-
12
- --team <key> only this team
13
- --json one JSON document: { slots, teams, ladder, thresholds }
14
- --help this text
15
-
16
- A slot marked * is load-bearing: dispatch, intake, pr, done and canceled must be mapped for the
17
- ladder to move at all; the others are informational. "(unmapped)" means the team has no stage for
18
- that slot; "(state gone)" means the mapped Linear state no longer exists and needs re-mapping in
19
- settings.
20
-
21
- Exit 0, 1 on a usage error or an unknown team, 2 when this machine is not connected to a tenant or
22
- the contract could not be read (the line says which).`;
23
-
24
- const LOAD_BEARING = new Set(["dispatch", "intake", "pr", "done", "canceled"]);
25
-
26
- const argv = process.argv.slice(2);
27
- if (wantsHelp(argv)) {
28
- console.log(HELP);
29
- process.exit(0);
30
- }
31
- const { flags } = parseFlags(argv, { bool: ["json"], value: ["team"] });
32
-
33
- function contractPath(path) {
34
- const r = runCli(["contract", "--path", path, "--json"]);
35
- exitOnFailure(r);
36
- const v = parseJson(r.stdout);
37
- if (v === null || v === undefined) {
38
- relayStderr(r);
39
- console.error(`contract --path ${path} returned nothing readable`);
40
- process.exit(1);
41
- }
42
- return { value: v, stderr: r.stderr };
43
- }
44
-
45
- const slotsRead = contractPath("slots");
46
- if (slotsRead.stderr.trim()) console.error(slotsRead.stderr.trimEnd());
47
- const slots = slotsRead.value;
48
- let teams = contractPath("teams").value;
49
- const ladder = contractPath("ladder").value;
50
- const thresholds = contractPath("thresholds").value;
51
-
52
- if (flags.team) {
53
- const want = flags.team.toUpperCase();
54
- teams = teams.filter((t) => String(t.key ?? "").toUpperCase() === want);
55
- if (teams.length === 0) {
56
- console.error(`no team with key ${flags.team} on this tenant's contract`);
57
- process.exit(1);
58
- }
59
- }
60
-
61
- if (flags.json) {
62
- console.log(JSON.stringify({ slots, teams, ladder, thresholds }));
63
- process.exit(0);
64
- }
65
-
66
- const pad = (s, n) => String(s).padEnd(n);
67
- for (const team of teams) {
68
- console.log(`== team ${team.key ?? "(no key)"} — ${team.name ?? team.id} — workflow ${team.workflowMode}, git automation ${team.gitAutomation}, readiness ${team.readiness?.status ?? "unknown"}`);
69
- console.log(` ${pad("slot", 12)}${pad("stage", 22)}${pad("type", 12)}${pad("exists", 8)}source`);
70
- for (const slot of slots) {
71
- const s = team.stages?.[slot];
72
- const mark = LOAD_BEARING.has(slot) ? "*" : " ";
73
- if (!s) {
74
- console.log(` ${mark}${pad(slot, 12)}(unmapped)`);
75
- continue;
76
- }
77
- const name = s.stateStillExists ? (s.name ?? "(unnamed)") : "(state gone)";
78
- console.log(` ${mark}${pad(slot, 12)}${pad(name, 22)}${pad(s.type ?? "-", 12)}${pad(s.stateStillExists ? "yes" : "NO", 8)}${s.source}`);
79
- }
80
- const labels = team.labels ?? {};
81
- const describe = (list) => (list ?? []).map((l) => `${l.name}${l.preferredId ? "" : " (absent)"}`).join(", ") || "none";
82
- console.log(` labels: ask ${describe(labels.ask)}; hold ${describe(labels.hold)}; release ${describe(labels.release)}`);
83
- }
84
-
85
- console.log("== ladder");
86
- console.log(` phases: ${(ladder.phases ?? []).join(" → ")}`);
87
- console.log(` keying: ${ladder.keying} (${ladder.keyingScope}); intake ${ladder.intakeEnabled ? "enabled" : "off"}`);
88
-
89
- console.log("== thresholds (live, fleet-wide)");
90
- const minutes = (ms) => `${Math.round(ms / 60000)} min`;
91
- console.log(` park after ${thresholds.parkAfterConsecutiveFailures} consecutive failures`);
92
- console.log(` remediate round cap ${thresholds.remediateRoundCap} (escalated budget ${thresholds.remediateEscalatedRoundBudget}, rewind budget ${thresholds.remediateRewindBudget})`);
93
- console.log(` retry backoff ${(thresholds.retryBackoffMs ?? []).map(minutes).join(", ")}`);
94
- console.log(` write budget ${thresholds.hostDailyWriteBudget} per host per day`);
@@ -1,65 +0,0 @@
1
- #!/usr/bin/env node
2
- // whats-running.mjs — what Catalyst is executing right now (fleet activity, the agent roster, lease
3
- // attributions), optionally the dispatch queue and the coding-account line.
4
- import { exitOnFailure, parseFlags, parseJson, relayStderr, runCli, wantsHelp } from "./lib/cli.mjs";
5
-
6
- const HELP = `Usage: node scripts/whats-running.mjs [--queue] [--accounts] [--team <key>] [--json]
7
-
8
- Prints what is executing on your tenant. Wraps: catalyst-skills running, queue, accounts.
9
-
10
- --queue also print the dispatch queue (what runs next, in order)
11
- --team <key> with --queue: one team's queue
12
- --accounts also print the coding-account line (provider, state, windows — never a credential)
13
- --json one JSON document: { running, queue?, accounts? }
14
- --help this text
15
-
16
- Exit 0, 1 on a usage error, 2 when this machine is not connected to a tenant or the cloud refused
17
- a read (the line says which).`;
18
-
19
- const argv = process.argv.slice(2);
20
- if (wantsHelp(argv)) {
21
- console.log(HELP);
22
- process.exit(0);
23
- }
24
- const { flags } = parseFlags(argv, { bool: ["queue", "accounts", "json"], value: ["team"] });
25
-
26
- const out = {};
27
- const running = runCli(["running", "--json"]);
28
- exitOnFailure(running);
29
- relayStderr(running);
30
- out.running = parseJson(running.stdout) ?? running.stdout.trimEnd();
31
-
32
- if (flags.queue) {
33
- const args = ["queue", "--json"];
34
- if (flags.team) args.push("--team", flags.team);
35
- const queue = runCli(args);
36
- exitOnFailure(queue);
37
- relayStderr(queue);
38
- out.queue = parseJson(queue.stdout) ?? queue.stdout.trimEnd();
39
- }
40
-
41
- if (flags.accounts) {
42
- const accounts = runCli(["accounts"]);
43
- exitOnFailure(accounts);
44
- relayStderr(accounts);
45
- out.accounts = accounts.stdout.trimEnd();
46
- }
47
-
48
- if (flags.json) {
49
- console.log(JSON.stringify(out));
50
- process.exit(0);
51
- }
52
-
53
- const section = (title, value) => {
54
- console.log(`== ${title}`);
55
- console.log(typeof value === "string" ? value : JSON.stringify(value, null, 2));
56
- };
57
- if (out.running && typeof out.running === "object") {
58
- section("fleet activity", out.running.fleetActivity ?? out.running);
59
- if (out.running.agentRoster !== undefined) section("agent roster", out.running.agentRoster);
60
- if (out.running.leaseAttributions !== undefined) section("lease attributions", out.running.leaseAttributions);
61
- } else {
62
- section("running", out.running);
63
- }
64
- if (out.queue !== undefined) section("dispatch queue", out.queue);
65
- if (out.accounts !== undefined) section("coding accounts", out.accounts);
@@ -1,45 +0,0 @@
1
- ---
2
- name: run-this-project
3
- description: >-
4
- Own one Catalyst Cloud project end to end until it closes. Use when the person says "run this project for me", "own this until it ships", "keep this moving", or hands you a project id or a set of tickets to drive. Subscribes to the tenant stream for the scope through the catalyst-skills CLI, reacts to each change in the same turn, makes tickets ready and moves them to dispatch, parks what should stop, chases stalls, escalates inward, and keeps one status summary current. Writes to Linear as the app actor; never polls.
5
- disable-model-invocation: true
6
- allowed-tools: Bash(catalyst-skills:*) Bash(npx @catalyst-cloud/catalyst-skills:*)
7
- ---
8
- <!-- vendored-from: @catalyst-cloud/catalyst-skills — written in this repository for customer tenants -->
9
-
10
- # Run this project
11
-
12
- You are the steward: the single-threaded owner of ONE project or ticket set, from now until it closes. The person says "run this project for me" and hands you a scope. Catalyst does the phases; you make work ready and visible, react to what happens, unblock, and keep one status summary the person can read. You hold no authority over other stewards and you are not the desk; `whats-happening` is the desk.
13
-
14
- ## Run first
15
-
16
- Scripts are run, never read. Each prints its own `--help`.
17
-
18
- 1. `node scripts/scope-status.mjs --project <id>` (or `--team <key>`): tickets by stage in the contract's order, what is running and queued in scope, and stalls against the local policy. Run it once to take the scope, and again after any resync.
19
- 2. `node scripts/watch-scope.mjs --project <id>`: the subscription. In Claude Code, arm a monitor on it and react to each printed line in the same turn. In a harness with no monitor, add `--exec <command>` so a reaction still runs per frame.
20
- 3. `node scripts/make-ready.mjs <ticket>` to dispatch; `--park` to stop; `--note <why>` to record it.
21
- 4. `catalyst-skills explain <ticket>` whenever a ticket is not moving: one paragraph naming the reason and what releases it.
22
-
23
- Every script exits 2 when this machine is not connected (run `catalyst-skills login`), 1 when its own check fails.
24
-
25
- ## Load on demand
26
-
27
- | when | read |
28
- | -- | -- |
29
- | arming the watch, deciding what a frame means, a reaction failed, a resync line printed | `references/reacting-to-events.md` |
30
- | dispatching or parking a ticket, judging whether a phase actually ran, writing a ticket for Catalyst | `references/making-work-ready.md` |
31
- | something is not moving, tuning the stall policy, deciding whether a human needs to hear about it | `references/stalls-and-escalation.md` |
32
- | the exclusion vocabulary in depth, the ladder, this team's stage map | the `how-catalyst-works` skill |
33
- | raising or settling a decision | the `what-needs-me` skill |
34
- | a PR's checks, review and merge legs | the `catalyst-github` skill |
35
-
36
- ## Rules
37
-
38
- - **React, never poll.** A loop that re-reads the API or the replica is a defect. The stream and the cursor file are the mechanism; a reaction that throws leaves the cursor so the frame is offered again.
39
- - **Dispatch is a card move.** The cloud takes work from the dispatch column and writes every later stage itself. Your two moves are into dispatch and into the backlog; never hand-move a card into a ladder stage.
40
- - **Evidence a phase ran is the outcome comment, the attached document and the agent session,** not the clock and not the card's column.
41
- - **Tenant facts come from the contract, live.** Never restate a stage name, label id, team id, threshold or template in prose; run `catalyst-skills contract --path <a.b.c>` when you need one.
42
- - **Escalate inward.** Instrument, then you, then the desk, then the human as an ask filed through `what-needs-me` with what it blocks. Decide the technical calls yourself; take the sane default and record it; a fleet or provider condition is one note, never one ask per ticket.
43
- - **Reply where the message arrived,** in-thread, tagged, as the app actor. Never post as the human, never answer someone else's ask. Bookkeeping records take the contract's marker (`catalyst-skills write comment --bookkeeping`).
44
- - **One status summary,** kept current after every reaction: in flight, blocked and on whom, closed, next, each line with a ticket id. Where a human decision is pending, the line carries the ask's id.
45
- - **Say what a key cannot see.** PR labels and reactions are not mirrored; the CLI says so by name and points at settings. A park or hold is released from the person's own login once its cause is fixed, through the `unstick` skill (`catalyst-skills release`). Execution history (`explain --history`) and coding-account status (`accounts`) ARE readable — read them rather than reconstructing them from comments.
@@ -1,6 +0,0 @@
1
- interface:
2
- display_name: "Run this project"
3
- short_description: "Own one Catalyst project end to end: react to its events, make tickets ready, chase stalls, escalate inward"
4
- default_prompt: "Use $run-this-project to run project <id> for me until it closes."
5
- policy:
6
- allow_implicit_invocation: false
@@ -1,5 +0,0 @@
1
- identity: { pack: catalyst-cloud-skills, skill: run-this-project }
2
- effects: [external-write]
3
- mutating: true
4
- invocation: explicit
5
- exposure: [catalog]
@@ -1,15 +0,0 @@
1
- {
2
- "$comment": "Stall thresholds in minutes, per workflow slot, for scope-status.mjs. This is local policy a human sets once, not a cloud value: copy this file to ~/.config/catalyst-cloud/stall-policy.json and edit it there. A ticket counts as stalled when it has sat in a slot longer than the threshold with no update and nothing running or leased on it. 'dispatch' is how long a card may wait in the dispatch column before you ask why nothing picked it up; 'pr' is how long a card may wait for merge evidence; 'default' covers any slot not named.",
3
- "minutes": {
4
- "dispatch": 60,
5
- "intake": 60,
6
- "research": 90,
7
- "plan": 90,
8
- "implement": 120,
9
- "remediate": 120,
10
- "verify": 90,
11
- "review": 90,
12
- "pr": 240,
13
- "default": 120
14
- }
15
- }
@@ -1,60 +0,0 @@
1
- # Making work ready
2
-
3
- This reference restates invariants of how Catalyst takes work. The tenant's own values (which Linear state is the dispatch column, which state is the backlog, the ask and release label ids, the round cap and the park threshold) are read live from `catalyst-skills contract`; the scripts here never name a stage.
4
-
5
- ## The steward's two moves
6
-
7
- Catalyst does not take work by being asked. It takes work by finding a card in the team's dispatch column and offering that card's next phase to a runner. So the steward's dispatch verb is a card move, and its stop verb is a card move:
8
-
9
- ```sh
10
- node scripts/make-ready.mjs ENG-41 # move into the dispatch column; the cloud offers the next phase
11
- node scripts/make-ready.mjs ENG-41 --park # move into the team's backlog-type state; nothing further is offered
12
- node scripts/make-ready.mjs ENG-41 --park --note "waiting on the vendor's API change"
13
- ```
14
-
15
- Dispatch resolves the dispatch slot on the ticket's team from the contract. Parking resolves the team's first backlog-type state from its live workflow states, because the backlog is deliberately not one of the eleven slots. Those are the only two state moves you make. Every other stage move on a ticket in the ladder is the cloud's, written when a phase completes; moving a card forward by hand does not run a phase, it only confuses the advance table, and moving it backwards by hand does not undo one.
16
-
17
- ## What a dispatchable ticket carries
18
-
19
- Before the move, the ticket needs, in the record itself:
20
-
21
- - A title that states the outcome. A phase agent reads the ticket, not your chat.
22
- - A description that says what done looks like: the behaviour, the constraints, the files or surfaces if you know them, acceptance criteria a validate phase can check.
23
- - The right team. The team key is the ticket prefix, and the team's stage map and labels are what the cloud will use.
24
- - A priority. Queue order inside a team is priority first, then creation time, then identifier, so an unset priority sorts last.
25
- - No live blocking relation. A ticket with an open blocker is excluded as `blocked` until the blocker closes.
26
- - No ask on it. A ticket that carries the ask label, or whose own text reads as a decision request (an ask-shaped title, lettered options, a "default if silent" line), is excluded as a question rather than work. If a real ticket trips the shape detector, a human applies the release label named in the contract's vocabulary; you can also rewrite the text so it reads as work.
27
- - Declared scope when the team enforces it: a ticket whose declared files overlap a ticket already in flight is held as `scope_overlap` for the implement phase.
28
-
29
- A ticket does not need a branch, a PR, or any artifact to be dispatched: the ladder creates those. A ticket that has never entered the ladder starts at intake when the tenant enables it, otherwise at research.
30
-
31
- ## After the move
32
-
33
- `make-ready.mjs` asks the eligibility explainer as soon as the move lands and prints the verdict. Read it as follows:
34
-
35
- - `offered` or `eligible` with a queue position: done; a runner will pick it up in the next dispatch pass, ordered by priority, then age, then identifier, with tickets already mid-ladder ahead of fresh ones.
36
- - An ordering that is stale or never published: the cloud re-derives the team's queue within a pass of the move; ask again in a minute with `catalyst-skills explain <ticket>`.
37
- - Any other exclusion reason: the paragraph names it and what releases it. The reasons and what unblocks each are in `how-catalyst-works` and in `whats-happening`'s "why is it stuck" reference.
38
-
39
- ## Evidence a phase ran
40
-
41
- Do not infer progress from time passing. A phase leaves three kinds of evidence on the ticket, and a card move alone is not one of them:
42
-
43
- 1. **The outcome comment.** The cloud posts a card per phase: a completed phase names the phase, the attempt, and its artifact; a failed phase names the phase, the attempt, and the failure class, and may carry a park or hold block. A remediate round posts its own attempt card with the round number and the class it is repairing. These arrive as comment frames on the watch.
44
- 2. **The attachment and document.** Each artifact-bearing phase (research, plan, implement, validate, pr, remediate) is projected to a Linear document titled with the ticket, the phase, the attempt and the date, attached to the ticket, with a short link comment. Research and plan on a ticket with a project also appear as a project link.
45
- 3. **The agent session.** The ticket's agent session carries the ladder as its plan, with the phases before the current one completed, the current one in progress, and the rest pending; its activities are the phase start, the gate, artifacts written, the PR opened, and the report.
46
-
47
- The card's stage is the fourth, weakest signal: a completed phase moves the card to the stage the advance table names, a failed phase writes no stage at all, and Done is written only when the pull request actually merges, by the merge webhook, never by a phase. A card sitting in a stage tells you which phase last finished, not whether the next one is running; `scripts/scope-status.mjs` shows running and leased phases beside the stage for exactly that reason.
48
-
49
- ## Parking, and what it does not do
50
-
51
- Parking is the lever that stops the cloud offering more rounds on a ticket: moved out of the dispatch column and the ladder's stages, the ticket is excluded at the next offer. It does not kill a phase that is already running under a lease; that container finishes its phase, posts its outcome, and the next offer finds the card parked. If you park a ticket the cloud has itself parked (three consecutive failures, or the remediate round cap), record why in a bookkeeping note. Releasing a cloud park is not a card move: once its cause is fixed, the `unstick` skill releases it with `catalyst-skills release <ticket>`, and only a refusal that names a person's action becomes an ask.
52
-
53
- Un-parking is the same dispatch move again. The counted attempts and rounds do not reset when a card comes back; the contract's thresholds say how many remain.
54
-
55
- ## What you never do to make work ready
56
-
57
- - Never move a card into a research, plan, implement, validate, PR, done or canceled stage by hand to "skip ahead". The cloud reads those stages as the record of what ran.
58
- - Never remove an ask label or apply the release label yourself; a human decides whether a ticket is a question.
59
- - Never dispatch two tickets that touch the same files at once; serialise them or let the second one wait as `scope_overlap`.
60
- - Never dispatch a ticket you have not read in full.
@@ -1,76 +0,0 @@
1
- # Reacting to events
2
-
3
- This reference restates invariants of the Catalyst Cloud stream and the `watch` verb. Nothing here varies per tenant; the tenant facts a reaction needs (stage ids, label ids, the ask team) come from `catalyst-skills contract` at the moment you need them.
4
-
5
- ## The mechanism
6
-
7
- A steward never polls. It subscribes once to the tenant stream through the SDK's live client and reacts to each change as it arrives. The verb is `catalyst-skills watch`, and this skill's `scripts/watch-scope.mjs` is that verb with the scope checked up front:
8
-
9
- ```sh
10
- node scripts/watch-scope.mjs --project <id> # every ticket in the project
11
- node scripts/watch-scope.mjs --team <key> # every ticket on the team
12
- node scripts/watch-scope.mjs --ticket ENG-41 --ticket ENG-42
13
- ```
14
-
15
- The stream itself is tenant-wide; scope is filtered on this machine from each frame's entity and row, and a row that only carries a ticket reference (a comment, a session, a pull request) is resolved to its project through the ticket's own record, cached for the life of the watch. The cost of an ignored frame is one JSON parse, so a wide scope is fine.
16
-
17
- **In Claude Code**, arm a monitor on the command above. Each line it prints is one applied change inside the scope, delivered into your session as an event. React to it in the same turn: read what changed, decide, act, and only then let the turn end.
18
-
19
- **In Codex, OpenCode, or any harness without a monitor**, pass `--exec`:
20
-
21
- ```sh
22
- node scripts/watch-scope.mjs --project <id> --exec 'node my-reaction.mjs'
23
- ```
24
-
25
- The command runs once per frame with the frame on stdin. A non-zero exit is a failed reaction (see the cursor rule). Nothing polls in either shape.
26
-
27
- ## The frame
28
-
29
- One JSON object per line, exactly what the SDK delivers:
30
-
31
- ```json
32
- {"type":"change","accountId":"<tenant>","seq":4182,"entity":"comments","entityId":"<id>","op":"upsert","row":{...}}
33
- ```
34
-
35
- `seq` is the tenant-wide position. `entity` is one of the mirror's feed tables. `op` is `upsert` or `delete`. `row` is the mirrored row when the op is an upsert; a delete carries the id only. A frame for another tenant is refused by the CLI and never printed.
36
-
37
- ## Entities that matter to a scope
38
-
39
- | entity | what a change means | first reaction |
40
- | -- | -- | -- |
41
- | `issues` | a card moved, was retitled, re-prioritised, assigned, or created | if it moved into a stage the cloud owns, note the advance; if it moved out of the ladder by hand, ask why before touching it |
42
- | `comments` | someone or something wrote on a ticket | a human comment in your scope is answered in-thread by you, tagged; a cloud outcome card (phase complete, phase failed, remediate attempt, board health, merge wait) is your execution signal, read it |
43
- | `issue_labels`, `labels` | a label was added or removed | an ask label means a question, not work; a hold label means the cloud wants a human; the release label means a human cleared a false positive |
44
- | `relations` | a blocks relation appeared or went away | a new blocker on your ticket is a dependency to chase; a blocker closing may make a card dispatchable again |
45
- | `pull_requests`, `pr_events`, `pushes` | the branch and PR state changed | a PR going ready-for-review or merged is a milestone; a force-push after a resolved review thread is worth a look |
46
- | `check_runs`, `check_suites`, `commit_statuses` | CI moved | red on a required check after the queue label wakes an automatic remediation; you watch, you do not re-run it |
47
- | `reviews`, `pr_review_threads`, `pr_review_comments` | the reviewer spoke | unresolved threads block the merge; a clean pass is a reaction or a terse comment, not a review object |
48
- | `agent_sessions`, `agent_activities` | a phase started, wrote an artifact, opened a PR, reported | the plan on the session is the ladder itself; the current phase is the one in progress |
49
- | `fleet_activity`, `fleet_host_liveness` | a runner picked up or dropped a phase | a phase running is not a stall, whatever the clock says |
50
- | `fleet_anomalies` | the cloud raised a fleet-level alert | one alert covers every ticket it touches; never escalate it per ticket |
51
- | `workflow_states`, `team_workflow_mapping` | the tenant's stage map changed | refresh the contract (`catalyst-skills contract --refresh`) before the next state move |
52
- | `projects`, `cycles`, `initiatives` | your scope's container changed | update the status summary |
53
-
54
- Entities not listed still arrive when they are in scope; ignore what you do not need.
55
-
56
- ## The same-turn rule
57
-
58
- A frame is reacted to in the turn it arrives, not batched for a later pass. The reaction is whatever the table above says plus anything the situation obviously needs, and it ends with the one status summary for the scope being current. If a reaction needs a human decision, file the ask through `what-needs-me` before proceeding on the default, in that same turn.
59
-
60
- ## The cursor rule
61
-
62
- The cursor file (`~/.config/catalyst-cloud/watch-cursor.json`, stamped with the tenant) advances only after the reaction returns. A reaction that throws, or an `--exec` command that exits non-zero, leaves the cursor at the last good frame; the CLI closes the socket and the SDK's reconnect replays from that cursor, so the failed frame is offered again. Delivery is therefore at-least-once: make reactions safe to repeat (check before you write, prefer idempotent writes such as a label add over a fresh comment).
63
-
64
- Two consequences. First, a crash mid-reaction replays, never loses. Second, a reaction that keeps failing keeps the watch pinned on one frame; fix the reaction rather than skipping the frame, or restart with `--from head` and re-read the scope with `scripts/scope-status.mjs` to catch up on what you missed.
65
-
66
- ## What to do on resync
67
-
68
- When the tenant tells the stream it can no longer replay from your cursor, the CLI prints one line on standard error, `[watch] resync: cursor moved to head <n>`, and continues from the head. No rows are copied. Anything that happened between your old cursor and the head is not replayed, so a resync is your cue to run `scripts/scope-status.mjs` once and reconcile the summary against the live state.
69
-
70
- ## Starting a watch on an existing project
71
-
72
- Start with `--from head` when you are picking up a project that has been running without a steward; the backlog of old frames is not worth replaying, and `scope-status.mjs` gives you the present state in one call. Start from the saved cursor (the default) when you are resuming your own watch after a break, so nothing that happened in between is lost.
73
-
74
- ## The honest limit
75
-
76
- The stream carries the mirror's entity changes. It does not carry the relay ledger's phase failures, parks, remediate rounds or backoff timers as first-class frames. Between frames you learn those from the outcome comments the cloud posts on the ticket, which do arrive as `comments` frames: a phase-failed card names the phase, the attempt and the failure class; a remediate-attempt card names the round; a board-health comment names a stall the cloud itself noticed. For the current verdict on any ticket, ask the explainer: `catalyst-skills explain <ticket>` turns the exclusion reason and the last failure into one paragraph. `catalyst-skills explain --history <ticket>` prints the per-phase attempt ledger, the remediation rounds against the cap, and any park with what releases it.
@@ -1,63 +0,0 @@
1
- # Stalls and escalation
2
-
3
- The stall thresholds here are **local policy**, set once by the human who owns the tenant and read by `scripts/scope-status.mjs`. They are not cloud values and the cloud does not enforce them; the cloud's own enforcement numbers (retry backoff, the park threshold, the remediate round cap, the write budget) are on the contract and printed by `how-catalyst-works`. The chase order and the escalation rule below are invariants.
4
-
5
- ## The policy file
6
-
7
- `assets/stall-policy.json` ships the defaults: minutes a ticket may sit in each slot with no update and nothing running or leased on it before it counts as stalled. To change them, copy that file to `~/.config/catalyst-cloud/stall-policy.json` and edit it there; the script prefers the local copy and prints which file it used. Pass `--stall-policy <file>` to use another.
8
-
9
- Two slots deserve thought when tuning. The dispatch slot's threshold is how long a card may wait to be picked up before you ask why nothing took it; on a busy tenant with a full queue that is capacity, not a stall, and the answer is to wait or re-prioritise. The PR slot's threshold is how long a card may wait for merge evidence; reviews and CI take real time, so set it generously.
10
-
11
- ## What counts as stuck
12
-
13
- A ticket is stuck when all of these hold at once: it is in an active slot (not done, not canceled), nothing is running or leased on it, its last update is older than the slot's threshold, and the eligibility explainer does not name a reason that is simply waiting out a clock. `scope-status.mjs` checks the first three and hands you the fourth as a command to run.
14
-
15
- A running phase is never a stall, however long it has run; the cloud's own timeout will fail it and the outcome card will say so. A ticket in retry backoff is waiting out a rung of two, five or fifteen minutes and is not a stall either.
16
-
17
- ## The chase order
18
-
19
- Run `catalyst-skills explain <ticket>` first, and let its reason pick the row:
20
-
21
- | the explainer says | what it means | your move |
22
- | -- | -- | -- |
23
- | offered, with a position | the queue has it; a runner is the bottleneck | wait; if every ticket waits, it is capacity, see below |
24
- | lease held, intake lease held | a container has it | not a stall; wait for the outcome card |
25
- | retry backoff | a pre-branch or infrastructure failure is retrying in place | wait out the rung; three in a row parks it, and then it is a real stall |
26
- | blocked | a live blocks relation | chase the blocker: is it dispatchable, is it an ask nobody answered, is it done but still open |
27
- | ask ticket, ask shape suspected | the ticket is a question | route it to `what-needs-me`; if it is really work, a human applies the release label |
28
- | not at dispatch stage | somebody moved the card, or it never entered | read the card's history; re-dispatch with `make-ready.mjs` if it should run |
29
- | phase parked, cooling down, remediate parked | the cloud parked it after repeated failure or the round cap | read the last outcome card for the class; once its cause is fixed, the `unstick` skill releases it (`catalyst-skills release <ticket> --because <what changed>`); if the fix is a decision, file an ask |
30
- | no change hold | a remediate round changed nothing | a human comment on the ticket, or a new push to the branch, releases it; say what should change |
31
- | validate class spent, stale failure episode | the repair budget for this failure is used, or the ladder moved on | read the outcome cards; usually a decision about the approach, so an ask |
32
- | waiting on | a merge-gate failure with no automatic repair | read the merge-wait comment and the PR's three legs through `catalyst-github` |
33
- | branch missing, branch gone, no branch to remediate | the branch the phase needs does not exist | a hand-deleted branch is a human question; a never-created one means implement has not run, so check the earlier phases |
34
- | environment check required, running, failed, expired, hash mismatch | the repository's environment gate | a repository setting; the tenant admin resolves it in settings, one ask for the repository, not per ticket |
35
- | scope overlap | another in-flight ticket owns the files | wait for it, or re-order by priority |
36
- | routing unavailable, no eligible slot, runner image breaker, repo paused | a fleet or provider condition | one fleet note, never per ticket; see below |
37
- | pr merged, pipeline complete, ticket terminal | it is finished | close the loop in the summary; if the card is not Done a minute after the merge, that is a finding, not a chore |
38
-
39
- Where the explainer names a reason not in this table, it prints the raw reason; read it as spelled and consult `how-catalyst-works`.
40
-
41
- ## Capacity and fleet conditions are one note, not many asks
42
-
43
- When several tickets in scope are offered and nothing picks them up, or the explainer names a routing, slot, provider or image condition, the cause is shared: coding-account headroom, a provider outage, a paused repository, a poisoned runner image. Write one line in the status summary naming the condition and the tickets it holds. Do not file an ask per ticket. `catalyst-skills accounts` reads coding-account status (state, usage windows, walls, quarantine); enrolling, pausing or removing an account is the settings page, not you.
44
-
45
- ## Escalate inward, never outward
46
-
47
- The order is instrument, then you (the steward), then the desk (`whats-happening`), then the human, and the human only ever sees an ask. An instrument or a script that pages a human directly is a defect. So is a stall report that reaches the human as a bare label, a board row, or a chat aside with no ask behind it.
48
-
49
- Before anything reaches the human, answer three questions:
50
-
51
- 1. **Can I decide this myself?** Which approach, retry or abandon, rebase or re-cut, re-order two tickets: yours. Decide, record the decision in a bookkeeping note on the ticket, and move on.
52
- 2. **Does this need to block at all?** If a sane default exists, take it, record it, and file the ask anyway so the human can overrule; the work does not wait.
53
- 3. **Who else can move this?** Another steward, the desk, a repository owner. Pulling in a peer is preferred over pulling in the human.
54
-
55
- Only a genuine product, priority or approval decision, or an action only a human can physically take (close a person's own pull request, read a review that will not converge, resolve a repository setting, apply the release label, answer a merge policy question), survives to become an ask.
56
-
57
- ## When a stall becomes an ask
58
-
59
- File it through `what-needs-me`, never by hand and never as the human. The ask carries the question as its title, the options, the default that fires if silent, and what it blocks, with the stalled ticket named in the blocks list so the human's inbox ranks it by what it holds. Search for an existing ask about the same decision first and attach the new ticket to it rather than filing a second one; two asks for one decision split its urgency and sink both. Once filed, proceed on the default and say so in the ticket.
60
-
61
- ## Reply where the message arrived
62
-
63
- A human comment inside your scope is answered by you, in that thread, tagged, as the app actor. A question only a human can decide becomes an ask; you do not answer it, and you never post as the human. A bookkeeping record (a state move, a decision you took, a chain summary) carries the contract's bookkeeping marker as a prefix so it wakes nothing; the `--bookkeeping` flag on `catalyst-skills write comment` adds it.
@@ -1,185 +0,0 @@
1
- #!/usr/bin/env node
2
- // lib/cli.mjs — the one way a skill script reaches Catalyst Cloud: by spawning the catalyst-skills
3
- // CLI. The CLI holds the SDK and the key; this file holds neither. It reads
4
- // ~/.config/catalyst-cloud/customer.json (under CATALYST_SKILLS_HOME when set, else HOME) for the
5
- // CLI path that login recorded and falls back to `npx @catalyst-cloud/catalyst-skills`.
6
- //
7
- // Exit codes every script built on this file shares: 2 = this machine is not connected, 1 = the
8
- // script's own check failed, 0 = fine.
9
- import { spawn } from "node:child_process";
10
- import { existsSync, readFileSync } from "node:fs";
11
- import { join } from "node:path";
12
- import { fileURLToPath } from "node:url";
13
- import { CONNECT_COMMAND, hasCredential } from "./credential.mjs";
14
-
15
- export const PACKAGE_NAME = "@catalyst-cloud/catalyst-skills";
16
- export const NOT_CONFIGURED_EXIT = 2;
17
- export const CHECK_FAILED_EXIT = 1;
18
-
19
- export function homeDir() {
20
- return process.env.CATALYST_SKILLS_HOME ?? process.env.HOME ?? process.env.USERPROFILE ?? "/";
21
- }
22
-
23
- export function configDir() {
24
- return join(homeDir(), ".config", "catalyst-cloud");
25
- }
26
-
27
- export function configPath() {
28
- return join(configDir(), "customer.json");
29
- }
30
-
31
- /** The stored config, or null when the file is absent or unreadable. Never throws. */
32
- export function loadCustomerConfig() {
33
- const path = configPath();
34
- if (!existsSync(path)) return null;
35
- try {
36
- const parsed = JSON.parse(readFileSync(path, "utf8"));
37
- if (!hasCredential(parsed) || typeof parsed.account !== "string") return null;
38
- return parsed;
39
- } catch {
40
- return null;
41
- }
42
- }
43
-
44
- /** Print the one not-connected line and exit 2. */
45
- export function requireConfigured() {
46
- const cfg = loadCustomerConfig();
47
- if (cfg) return cfg;
48
- console.error(`not connected: ${configPath()} is missing or unreadable — run: ${CONNECT_COMMAND}`);
49
- process.exit(NOT_CONFIGURED_EXIT);
50
- }
51
-
52
- /** The command and argument prefix that reaches the CLI on this machine. */
53
- export function cliCommand(cfg = loadCustomerConfig()) {
54
- if (cfg && typeof cfg.cliPath === "string" && existsSync(cfg.cliPath)) {
55
- return { command: process.execPath, prefix: [cfg.cliPath], via: `node ${cfg.cliPath}` };
56
- }
57
- const npx = process.platform === "win32" ? "npx.cmd" : "npx";
58
- return { command: npx, prefix: [PACKAGE_NAME], via: `npx ${PACKAGE_NAME}` };
59
- }
60
-
61
- /**
62
- * Run one CLI verb and capture its output. Resolves `{code, stdout, stderr, notConfigured}`;
63
- * `notConfigured` is true when the CLI itself said the machine is not connected.
64
- */
65
- export function runCli(args, { stdin } = {}) {
66
- const { command, prefix } = cliCommand();
67
- return new Promise((resolve, reject) => {
68
- const child = spawn(command, [...prefix, ...args], {
69
- stdio: [stdin === undefined ? "ignore" : "pipe", "pipe", "pipe"],
70
- env: process.env,
71
- shell: process.platform === "win32",
72
- });
73
- let stdout = "";
74
- let stderr = "";
75
- child.stdout.on("data", (d) => (stdout += String(d)));
76
- child.stderr.on("data", (d) => (stderr += String(d)));
77
- child.on("error", reject);
78
- child.on("close", (code) => {
79
- resolve({ code: code ?? 1, stdout, stderr, notConfigured: /not (joined|connected)/i.test(stderr) || /not (joined|connected)/i.test(stdout) });
80
- });
81
- if (stdin !== undefined) child.stdin.end(stdin);
82
- });
83
- }
84
-
85
- /**
86
- * Run one CLI verb with the terminal attached, so a long-running verb such as `watch` streams
87
- * straight through. Resolves with the exit code; SIGINT and SIGTERM are forwarded to the child.
88
- */
89
- export function execCli(args) {
90
- const { command, prefix } = cliCommand();
91
- return new Promise((resolve, reject) => {
92
- const child = spawn(command, [...prefix, ...args], { stdio: "inherit", env: process.env, shell: process.platform === "win32" });
93
- const forward = (sig) => () => {
94
- try {
95
- child.kill(sig);
96
- } catch {
97
- // already gone
98
- }
99
- };
100
- const onInt = forward("SIGINT");
101
- const onTerm = forward("SIGTERM");
102
- process.on("SIGINT", onInt);
103
- process.on("SIGTERM", onTerm);
104
- child.on("error", reject);
105
- child.on("close", (code) => {
106
- process.off("SIGINT", onInt);
107
- process.off("SIGTERM", onTerm);
108
- resolve(code ?? 1);
109
- });
110
- });
111
- }
112
-
113
- /** Parse the CLI's --json output; a parse failure names the first line of what came back. */
114
- export function parseJson(stdout) {
115
- try {
116
- return JSON.parse(stdout);
117
- } catch {
118
- throw new Error(`the CLI did not answer with JSON: ${stdout.split("\n")[0] ?? "(empty)"}`);
119
- }
120
- }
121
-
122
- /** Exit 2 with the CLI's own not-connected line when a call reports it; otherwise return the result. */
123
- export function guard(result) {
124
- if (result.notConfigured) {
125
- process.stderr.write(result.stderr || result.stdout);
126
- process.exit(NOT_CONFIGURED_EXIT);
127
- }
128
- return result;
129
- }
130
-
131
- /** A tiny flag parser: `--name value`, `--name=value`, `--flag`, repeatable names collected as arrays. */
132
- export function parseFlags(argv, { values = [], repeat = [], booleans = [] } = {}) {
133
- const flags = {};
134
- const positionals = [];
135
- for (let i = 0; i < argv.length; i++) {
136
- const a = argv[i];
137
- if (a === "--") {
138
- positionals.push(...argv.slice(i + 1));
139
- break;
140
- }
141
- if (!a.startsWith("--")) {
142
- positionals.push(a);
143
- continue;
144
- }
145
- let name = a.slice(2);
146
- let value;
147
- const eq = name.indexOf("=");
148
- if (eq !== -1) {
149
- value = name.slice(eq + 1);
150
- name = name.slice(0, eq);
151
- }
152
- if (booleans.includes(name)) {
153
- flags[name] = true;
154
- continue;
155
- }
156
- if (!values.includes(name) && !repeat.includes(name)) {
157
- console.error(`unknown option: --${name} (try --help)`);
158
- process.exit(CHECK_FAILED_EXIT);
159
- }
160
- if (value === undefined) {
161
- value = argv[++i];
162
- if (value === undefined) {
163
- console.error(`--${name} needs a value`);
164
- process.exit(CHECK_FAILED_EXIT);
165
- }
166
- }
167
- if (repeat.includes(name)) flags[name] = [...(flags[name] ?? []), value];
168
- else flags[name] = value;
169
- }
170
- return { flags, positionals };
171
- }
172
-
173
- export function wantsHelp(argv) {
174
- return argv.includes("--help") || argv.includes("-h");
175
- }
176
-
177
- const HELP = `lib/cli.mjs — shared helper; not a command.
178
-
179
- Resolves the catalyst-skills CLI (the path login recorded in ${configPath()}, else npx ${PACKAGE_NAME})
180
- and runs one verb for the script that imports it. Run any sibling script with --help instead.`;
181
-
182
- if (process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]) {
183
- console.log(HELP);
184
- process.exit(0);
185
- }