@catalyst-cloud/cli 0.16.34 → 0.16.35
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 +6 -0
- package/bin/postinstall.js +13 -0
- package/dist/agent-repoint.js +241 -0
- package/dist/runner-command.js +5 -3
- package/dist/runner-update.js +14 -0
- package/package.json +2 -1
- package/skills/catalyst-github/SKILL.md +1 -1
- package/skills/catalyst-linear/SKILL.md +1 -1
- package/skills/catalyst-onboard/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/what-this-repo-needs/SKILL.md +1 -1
- package/skills/whats-happening/SKILL.md +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.16.35
|
|
4
|
+
|
|
5
|
+
On a self-hosted machine set up before 0.16.33, the runner update agent kept running the CLI it was installed with, and that CLI could not update itself. The daily job installed newer versions into its own folder, but the agent never ran them. Installing this release globally (`npm install -g @catalyst-cloud/cli`) now points that agent at the newly installed copy. It only ever moves the agent to a newer catalyst, and keeps the agent's runner folder and PATH. It saves the previous agent file beside it, as `<file>.before-0.16.35`, so you can roll back. On a Mac it waits for an update already in progress to finish. An agent file you cannot change is left alone. Each outcome is written to the runner's `update.log`, and a failed attempt puts the agent back exactly as it was. The install itself never fails because of this. To turn it off, set `CATALYST_NO_AGENT_REPOINT=1`.
|
|
6
|
+
|
|
7
|
+
`catalyst runner update --check` now says when the last update failed or was rolled back, with its time, its reason and the release it was offered, between the image result and the component report. The JSON output carries the same as `lastProblem`.
|
|
8
|
+
|
|
3
9
|
## 0.16.34
|
|
4
10
|
|
|
5
11
|
`catalyst runner update` now tells Catalyst how its previous run went when it reads the approved release. A self-hosted machine that keeps waiting, refusing or rolling back an update shows up without anyone reading its local log. It sends a short report: the outcome, its reason code, the release it runs, the release that run was offered, when it ran and the CLI version. `--check` sends nothing, and nothing beyond that fixed set of words leaves the machine. When a self-hosted Mac cannot re-check its private storage during an update, the update log now names the reason instead of a generic failure.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// npm's postinstall for @catalyst-cloud/cli (CTC-6975): on a global install, point an older runner
|
|
3
|
+
// update agent at this copy (src/agent-repoint.ts). A local or workspace install does nothing, and
|
|
4
|
+
// nothing here ever fails the install. CATALYST_NO_AGENT_REPOINT=1 turns it off.
|
|
5
|
+
if (process.env.npm_config_global === "true" && process.env.CATALYST_NO_AGENT_REPOINT !== "1") {
|
|
6
|
+
try {
|
|
7
|
+
const { postinstallRepoint } = await import("../dist/agent-repoint.js");
|
|
8
|
+
const line = postinstallRepoint();
|
|
9
|
+
if (line) console.error(line);
|
|
10
|
+
} catch (err) {
|
|
11
|
+
console.error(`catalyst: could not check the runner update agent (${err instanceof Error ? err.message : String(err)}). Run: catalyst runner auto-update on`);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
// agent-repoint.ts — CTC-6975. A machine set up before 0.16.33 runs its runner update agent from a CLI
|
|
2
|
+
// that can never move itself: those CLIs read no approved component release on a self-hosted machine,
|
|
3
|
+
// so the agent's own copy stays where it was installed, while the daily job installs the newest CLI
|
|
4
|
+
// into its own global prefix. npm runs this from that install (bin/postinstall.js) and points an older
|
|
5
|
+
// agent at the copy updates do move.
|
|
6
|
+
//
|
|
7
|
+
// It only ever moves the agent forward, touches only this CLI's own agent, keeps the agent's runner
|
|
8
|
+
// directory and PATH, and never fails the install. The agent file it replaces is kept beside it, named
|
|
9
|
+
// `<file>.before-<version>`, which neither launchd nor systemd loads: that copy is the rollback.
|
|
10
|
+
// Removing the stale CLI copy is the installer's job.
|
|
11
|
+
import { accessSync, appendFileSync, constants, copyFileSync, existsSync, readFileSync, realpathSync, unlinkSync } from "node:fs";
|
|
12
|
+
import { dirname, join } from "node:path";
|
|
13
|
+
import { fileURLToPath } from "node:url";
|
|
14
|
+
import { compareVersions, exactVersion } from "./approved-release.js";
|
|
15
|
+
import { LEGACY_PACKAGE_NAME, readManifest } from "./config.js";
|
|
16
|
+
import { AUTO_UPDATE_LABEL, defaultRun, enableAutoUpdate, launchAgentPath, scheduledPath, systemdUnitPaths, writeFileAtomic, } from "./runner-auto-update.js";
|
|
17
|
+
import { CLAUDE_PATH_ENV, resolveClaude } from "./update-plugin.js";
|
|
18
|
+
/** Long enough for an in-place update (under a minute on a Mac measured 2026-10-24), well inside the
|
|
19
|
+
* installer's 600-second budget for the whole npm install. */
|
|
20
|
+
export const BUSY_WAIT_MS = 240_000;
|
|
21
|
+
const BUSY_POLL_MS = 5_000;
|
|
22
|
+
const blockingSleep = (ms) => Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
23
|
+
const unxml = (value) => value.replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/&/g, "&");
|
|
24
|
+
/** The agent's command, from a plist this CLI wrote (launchAgentPlist). Anything else is unrecognized. */
|
|
25
|
+
function plistProgram(text) {
|
|
26
|
+
const array = /<key>ProgramArguments<\/key>\s*<array>([\s\S]*?)<\/array>/.exec(text)?.[1];
|
|
27
|
+
if (array === undefined)
|
|
28
|
+
return null;
|
|
29
|
+
const args = [...array.matchAll(/<string>([\s\S]*?)<\/string>/g)].map((m) => unxml(m[1] ?? ""));
|
|
30
|
+
const env = (key) => {
|
|
31
|
+
const m = new RegExp(`<key>${key}</key>\\s*<string>([\\s\\S]*?)</string>`).exec(text);
|
|
32
|
+
return m ? unxml(m[1] ?? "") : null;
|
|
33
|
+
};
|
|
34
|
+
return programOf(args, env("PATH"), env(CLAUDE_PATH_ENV));
|
|
35
|
+
}
|
|
36
|
+
/** systemd's quoted words, as systemdUnits writes them. */
|
|
37
|
+
function unitWords(line) {
|
|
38
|
+
return [...line.matchAll(/"((?:[^"\\]|\\.)*)"/g)].map((m) => (m[1] ?? "").replace(/\\(.)/g, "$1").replace(/%%/g, "%"));
|
|
39
|
+
}
|
|
40
|
+
function unitProgram(text) {
|
|
41
|
+
const lines = text.split("\n");
|
|
42
|
+
const exec = lines.find((l) => l.startsWith("ExecStart="));
|
|
43
|
+
if (exec === undefined)
|
|
44
|
+
return null;
|
|
45
|
+
const envs = lines.filter((l) => l.startsWith("Environment=")).flatMap((l) => unitWords(l));
|
|
46
|
+
const env = (key) => envs.find((e) => e.startsWith(`${key}=`))?.slice(key.length + 1) ?? null;
|
|
47
|
+
return programOf(unitWords(exec), env("PATH"), env(CLAUDE_PATH_ENV));
|
|
48
|
+
}
|
|
49
|
+
function programOf(args, path, claude) {
|
|
50
|
+
const [cli, ...rest] = args;
|
|
51
|
+
if (cli === undefined || !cli.startsWith("/") || rest.length !== 5)
|
|
52
|
+
return null;
|
|
53
|
+
const [a, b, c, d, dir] = rest;
|
|
54
|
+
if (a !== "runner" || b !== "update" || c !== "--auto" || d !== "--dir" || dir === undefined || !dir.startsWith("/"))
|
|
55
|
+
return null;
|
|
56
|
+
return { cli, dir, path, claude };
|
|
57
|
+
}
|
|
58
|
+
const real = (path) => {
|
|
59
|
+
try {
|
|
60
|
+
return realpathSync(path);
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
const writable = (path) => {
|
|
67
|
+
try {
|
|
68
|
+
accessSync(path, constants.W_OK);
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
/** The version of the catalyst package a command belongs to, or null when it is not one. */
|
|
76
|
+
export function cliVersionAt(command) {
|
|
77
|
+
const resolved = real(command);
|
|
78
|
+
if (resolved === null)
|
|
79
|
+
return null;
|
|
80
|
+
let dir = dirname(resolved);
|
|
81
|
+
for (let i = 0; i < 4; i++) {
|
|
82
|
+
const manifest = join(dir, "package.json");
|
|
83
|
+
if (existsSync(manifest)) {
|
|
84
|
+
try {
|
|
85
|
+
const pkg = JSON.parse(readFileSync(manifest, "utf8"));
|
|
86
|
+
if (pkg.name === "@catalyst-cloud/cli" || pkg.name === LEGACY_PACKAGE_NAME)
|
|
87
|
+
return exactVersion(pkg.version);
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
dir = dirname(dir);
|
|
95
|
+
}
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
/** Whether the macOS agent is idle, waiting up to the budget for a running update to finish. */
|
|
99
|
+
function waitIdle(deps) {
|
|
100
|
+
const run = deps.run ?? defaultRun;
|
|
101
|
+
const sleep = deps.sleep ?? blockingSleep;
|
|
102
|
+
const target = `gui/${deps.uid ?? process.getuid?.() ?? 0}/${AUTO_UPDATE_LABEL}`;
|
|
103
|
+
for (let waited = 0;; waited += BUSY_POLL_MS) {
|
|
104
|
+
if (!/^\s*state = running\s*$/m.test(run("launchctl", ["print", target]).stdout))
|
|
105
|
+
return true;
|
|
106
|
+
if (waited >= (deps.busyWaitMs ?? BUSY_WAIT_MS))
|
|
107
|
+
return false;
|
|
108
|
+
sleep(BUSY_POLL_MS);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/** Put the agent file back and load it again, best effort, then drop the backup. */
|
|
112
|
+
function restore(deps, platform, agent, original, backup) {
|
|
113
|
+
if (!existsSync(agent) || !readFileSync(agent).equals(original)) {
|
|
114
|
+
writeFileAtomic(agent, original.toString("utf8"));
|
|
115
|
+
const run = deps.run ?? defaultRun;
|
|
116
|
+
try {
|
|
117
|
+
if (platform === "darwin")
|
|
118
|
+
run("launchctl", ["bootstrap", `gui/${deps.uid ?? process.getuid?.() ?? 0}`, agent]);
|
|
119
|
+
else
|
|
120
|
+
run("systemctl", ["--user", "daemon-reload"]);
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
// the file is back; the scheduler picks it up at the next login or reload
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
unlinkSync(backup);
|
|
127
|
+
}
|
|
128
|
+
/** Point an older runner update agent at this install. Never throws. Every outcome for an agent this
|
|
129
|
+
* CLI recognizes is also appended to the agent's own update.log: npm hides a postinstall's output on
|
|
130
|
+
* a global install, so that log is where a person, or a later run, reads what happened. */
|
|
131
|
+
export function repointAgent(deps) {
|
|
132
|
+
const seen = {};
|
|
133
|
+
const result = decide(deps, seen);
|
|
134
|
+
if (seen.dir !== undefined) {
|
|
135
|
+
try {
|
|
136
|
+
appendFileSync(join(seen.dir, "update.log"), `${new Date().toISOString()} ${logLine(result, deps.version)}\n`);
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
// the agent's log is a courtesy; the outcome is already decided
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return result;
|
|
143
|
+
}
|
|
144
|
+
function decide(deps, seen) {
|
|
145
|
+
try {
|
|
146
|
+
const platform = deps.platform ?? process.platform;
|
|
147
|
+
let program;
|
|
148
|
+
let agent;
|
|
149
|
+
if (platform === "darwin") {
|
|
150
|
+
agent = launchAgentPath(deps.home);
|
|
151
|
+
if (!existsSync(agent))
|
|
152
|
+
return { action: "skipped", reason: "no_agent" };
|
|
153
|
+
program = plistProgram(readFileSync(agent, "utf8"));
|
|
154
|
+
}
|
|
155
|
+
else if (platform === "linux") {
|
|
156
|
+
agent = systemdUnitPaths(deps.home).service;
|
|
157
|
+
if (!existsSync(agent))
|
|
158
|
+
return { action: "skipped", reason: "no_agent" };
|
|
159
|
+
program = unitProgram(readFileSync(agent, "utf8"));
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
return { action: "skipped", reason: "unsupported_platform" };
|
|
163
|
+
}
|
|
164
|
+
if (program === null)
|
|
165
|
+
return { action: "skipped", reason: "agent_unrecognized" };
|
|
166
|
+
seen.dir = program.dir;
|
|
167
|
+
const from = real(program.cli);
|
|
168
|
+
if (from !== null && from === real(deps.self))
|
|
169
|
+
return { action: "skipped", reason: "already_current" };
|
|
170
|
+
// A program that no longer exists runs nothing, so any working copy is better. One that exists
|
|
171
|
+
// moves only when it is a catalyst CLI older than this one.
|
|
172
|
+
let fromVersion = null;
|
|
173
|
+
if (from !== null) {
|
|
174
|
+
fromVersion = cliVersionAt(program.cli);
|
|
175
|
+
if (fromVersion === null)
|
|
176
|
+
return { action: "skipped", reason: "agent_cli_unrecognized" };
|
|
177
|
+
if (compareVersions(fromVersion, deps.version) >= 0)
|
|
178
|
+
return { action: "skipped", reason: "agent_cli_not_older" };
|
|
179
|
+
}
|
|
180
|
+
const canWrite = deps.canWrite ?? writable;
|
|
181
|
+
const files = platform === "linux" ? [agent, systemdUnitPaths(deps.home).timer].filter((f) => existsSync(f)) : [agent];
|
|
182
|
+
if (![...files, dirname(agent)].every(canWrite))
|
|
183
|
+
return { action: "skipped", reason: "agent_not_writable", agent };
|
|
184
|
+
// launchctl bootout stops a running job, and the daily job starts on the same quarter hour as the
|
|
185
|
+
// agent does: wait for an update in progress to finish rather than cut it off.
|
|
186
|
+
if (platform === "darwin" && !waitIdle(deps))
|
|
187
|
+
return { action: "skipped", reason: "agent_busy" };
|
|
188
|
+
const original = readFileSync(agent);
|
|
189
|
+
const backup = `${agent}.before-${deps.version}`;
|
|
190
|
+
copyFileSync(agent, backup);
|
|
191
|
+
let status;
|
|
192
|
+
try {
|
|
193
|
+
status = enableAutoUpdate({
|
|
194
|
+
platform,
|
|
195
|
+
home: deps.home,
|
|
196
|
+
uid: deps.uid,
|
|
197
|
+
cli: deps.self,
|
|
198
|
+
path: scheduledPath(deps.self, deps.nodeExecutable, program.path ?? deps.env.PATH),
|
|
199
|
+
claude: program.claude ?? resolveClaude(deps.home, deps.env),
|
|
200
|
+
run: deps.run,
|
|
201
|
+
}, program.dir);
|
|
202
|
+
}
|
|
203
|
+
finally {
|
|
204
|
+
// A repoint that did not take puts the agent back exactly as it was, and leaves no backup.
|
|
205
|
+
if (status?.state !== "on")
|
|
206
|
+
restore(deps, platform, agent, original, backup);
|
|
207
|
+
}
|
|
208
|
+
if (status.state !== "on")
|
|
209
|
+
return { action: "failed", detail: status.detail };
|
|
210
|
+
return { action: "repointed", from: program.cli, fromVersion, to: deps.self, backup };
|
|
211
|
+
}
|
|
212
|
+
catch (err) {
|
|
213
|
+
return { action: "failed", detail: (err instanceof Error ? err.message : String(err)).slice(0, 160) };
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
/** One line for the person, or null when there is nothing to say. */
|
|
217
|
+
export function repointLine(result, version) {
|
|
218
|
+
if (result.action === "repointed")
|
|
219
|
+
return `catalyst: the runner update agent now runs ${result.to} (${version}) instead of ${result.from} (${result.fromVersion ?? "missing"}). The previous agent file is kept at ${result.backup}.`;
|
|
220
|
+
if (result.action === "skipped" && result.reason === "agent_not_writable")
|
|
221
|
+
return `catalyst: the runner update agent at ${result.agent} runs an older catalyst, and this install cannot change it. Ask whoever installed it to run: catalyst runner auto-update on`;
|
|
222
|
+
if (result.action === "skipped" && result.reason === "agent_busy")
|
|
223
|
+
return "catalyst: the runner update agent was busy updating, so it keeps its current catalyst until the next install. Or run: catalyst runner auto-update on";
|
|
224
|
+
if (result.action === "failed")
|
|
225
|
+
return `catalyst: could not point the runner update agent at this install (${result.detail}). Run: catalyst runner auto-update on`;
|
|
226
|
+
return null;
|
|
227
|
+
}
|
|
228
|
+
/** The update.log line for any outcome: the person's line, or for a quiet skip, its reason. */
|
|
229
|
+
export function logLine(result, version) {
|
|
230
|
+
return repointLine(result, version) ?? `catalyst ${version}: the runner update agent was left as it is (${result.action === "skipped" ? result.reason : result.action}).`;
|
|
231
|
+
}
|
|
232
|
+
/** npm's postinstall, for a global install: this install's own command, version and node. */
|
|
233
|
+
export function postinstallRepoint(env = process.env) {
|
|
234
|
+
const version = readManifest().version;
|
|
235
|
+
const linked = env.npm_config_prefix ? join(env.npm_config_prefix, "bin", "catalyst") : null;
|
|
236
|
+
const self = linked !== null && existsSync(linked) ? linked : fileURLToPath(new URL("../bin/catalyst.js", import.meta.url));
|
|
237
|
+
const home = env.HOME;
|
|
238
|
+
if (!home)
|
|
239
|
+
return null;
|
|
240
|
+
return repointLine(repointAgent({ home, self, version, nodeExecutable: process.execPath, env }), version);
|
|
241
|
+
}
|
package/dist/runner-command.js
CHANGED
|
@@ -2,7 +2,7 @@ import { isAbsolute, resolve } from "node:path";
|
|
|
2
2
|
import { flagBool, flagString, positionals } from "./args.js";
|
|
3
3
|
import { CliError, UsageError } from "./errors.js";
|
|
4
4
|
import { autoUpdateStatus, disableAutoUpdate, enableAutoUpdate, scheduledPath, } from "./runner-auto-update.js";
|
|
5
|
-
import { dockerRunnerUpdateEngine, readReleaseState, RELEASE_COMPONENTS, runnerDir, runRunnerUpdate, updateResultLines, } from "./runner-update.js";
|
|
5
|
+
import { dockerRunnerUpdateEngine, readReleaseState, RELEASE_COMPONENTS, runnerDir, runRunnerUpdate, updateResultLines, lastProblem, lastProblemLines, } from "./runner-update.js";
|
|
6
6
|
import { dockerFleetUpdateEngine, runFleetUpdate } from "./runner-update-fleet.js";
|
|
7
7
|
import { runUpdate, sharedReleaseRead, updateReportJson, updateReportLines } from "./update.js";
|
|
8
8
|
import { effectivePolicy, readUpdatePolicy, setComponentPolicy } from "./update-policy.js";
|
|
@@ -140,12 +140,14 @@ export async function cmdRunner(args, ctx, deps = {}) {
|
|
|
140
140
|
ctx.stdout(line);
|
|
141
141
|
return result.exitCode;
|
|
142
142
|
}
|
|
143
|
+
// A check also names the last failed or rolled-back update, which a clean read would hide (CTC-6709).
|
|
144
|
+
const state = readReleaseState(dir());
|
|
143
145
|
if (args.json) {
|
|
144
146
|
const { schemaVersion, release, components, projects } = updateReportJson(report);
|
|
145
|
-
ctx.stdout(JSON.stringify({ ...result, schemaVersion, release, components, projects }));
|
|
147
|
+
ctx.stdout(JSON.stringify({ ...result, schemaVersion, release, components, projects, lastProblem: lastProblem(state) }));
|
|
146
148
|
}
|
|
147
149
|
else
|
|
148
|
-
for (const line of [...updateResultLines(result), ...updateReportLines(report)])
|
|
150
|
+
for (const line of [...updateResultLines(result), ...lastProblemLines(state), ...updateReportLines(report)])
|
|
149
151
|
ctx.stdout(line);
|
|
150
152
|
return result.exitCode;
|
|
151
153
|
}
|
package/dist/runner-update.js
CHANGED
|
@@ -604,6 +604,20 @@ const OUTCOME_TEXT = {
|
|
|
604
604
|
rolled_back: "The new supervisor did not become ready, so the previous images were restored.",
|
|
605
605
|
failed: "The update could not finish. Look at the runner with docker compose ps.",
|
|
606
606
|
};
|
|
607
|
+
/** The last run's record when it ended badly: a failed or rolled-back update a person should know
|
|
608
|
+
* about even after a later check reads clean (CTC-6709). Null otherwise. */
|
|
609
|
+
export function lastProblem(state) {
|
|
610
|
+
const last = state.last;
|
|
611
|
+
return last !== null && (last.outcome === "failed" || last.outcome === "rolled_back") ? last : null;
|
|
612
|
+
}
|
|
613
|
+
/** One line naming the last failed or rolled-back update, for `runner update --check`. */
|
|
614
|
+
export function lastProblemLines(state) {
|
|
615
|
+
const last = lastProblem(state);
|
|
616
|
+
if (last === null)
|
|
617
|
+
return [];
|
|
618
|
+
const what = last.outcome === "rolled_back" ? "was rolled back" : "failed";
|
|
619
|
+
return [`The last update, at ${new Date(last.atMs).toISOString()}, ${what} (${last.detail}; offered generation ${last.generation ?? "none"}).`];
|
|
620
|
+
}
|
|
607
621
|
export function updateResultLines(result) {
|
|
608
622
|
return [
|
|
609
623
|
OUTCOME_TEXT[result.outcome],
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@catalyst-cloud/cli",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.35",
|
|
4
4
|
"description": "Set up Catalyst Cloud, check your tickets and manage your account from the command line.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
"typecheck": "tsc --noEmit",
|
|
40
40
|
"build": "tsc -p tsconfig.build.json",
|
|
41
41
|
"prepare": "tsc -p tsconfig.build.json",
|
|
42
|
+
"postinstall": "node bin/postinstall.js",
|
|
42
43
|
"test": "vitest run",
|
|
43
44
|
"test:coverage": "vitest run --coverage",
|
|
44
45
|
"version:sync": "node scripts/sync-plugin-version.mjs",
|
|
@@ -4,7 +4,7 @@ description: >-
|
|
|
4
4
|
Catalyst's GitHub: show a ticket's pull request with its checks, reviews and review threads, say whether it is mergeable under this repository's policy, and explain what a PR accumulates as the ticket moves (the branch, the draft, the rewrite, the force-pushes, the labels, the queue). Use when someone asks "show me the PR", "what are the checks saying", "why hasn't it merged", "what does the queue need", "what does that label mean", or wants the branch and merge conventions Catalyst follows.
|
|
5
5
|
allowed-tools: Bash(catalyst:*) Bash(npx -p @catalyst-cloud/cli catalyst:*)
|
|
6
6
|
---
|
|
7
|
-
<!-- vendored-from: @catalyst-cloud/catalyst-skills@0.16.
|
|
7
|
+
<!-- vendored-from: @catalyst-cloud/catalyst-skills@0.16.35 — written in this repository for customer accounts -->
|
|
8
8
|
|
|
9
9
|
# Catalyst's GitHub
|
|
10
10
|
|
|
@@ -4,7 +4,7 @@ description: >-
|
|
|
4
4
|
Catalyst's view of Linear on the customer's own cloud account. Reads a ticket with its comments, relations, labels, linked pull requests and agent sessions inline, from the cloud API by default, with local replica reads available as an explicit opt-in, always naming the source; searches tickets, PRs, projects and initiatives; writes comments, card moves, labels and new tickets through the account's agent proxy as the app actor, with every route, stage id, label id and marker read from the account's contract. Knows what a ticket accumulates as Catalyst works it (phase-outcome comments, the document per phase, the agent session, the labels, the single blocks relation, the bookkeeping marker, the eyes acknowledgement). Use when a person says "show me the ticket", "what did Catalyst write on it", "comment on it", "move it", "label it" or "file a ticket". Not for raising a decision for a human (what-needs-me) and not for pull requests (catalyst-github).
|
|
5
5
|
allowed-tools: Bash(catalyst:*) Bash(npx -p @catalyst-cloud/cli catalyst:*)
|
|
6
6
|
---
|
|
7
|
-
<!-- vendored-from: @catalyst-cloud/catalyst-skills@0.16.
|
|
7
|
+
<!-- vendored-from: @catalyst-cloud/catalyst-skills@0.16.35 — written in this repository for customer accounts -->
|
|
8
8
|
|
|
9
9
|
# Catalyst Linear
|
|
10
10
|
|
|
@@ -4,7 +4,7 @@ description: >-
|
|
|
4
4
|
Set up Catalyst Cloud and say whether setup is working. Walks a person from nothing to their first ticket running, one step at a time: the coding account, the project, the integrations and connected accounts, each repository's settings file, then one real ticket moving. Also logs this machine in, reads the readiness verdict with who can fix each failure, and checks the optional local replica. Use when someone says "set me up", "onboard me", "I just signed up", "get me started", "what do I do first", "log me in", "which account is this machine on", "am I set up", "what is missing", "why does nothing happen" or "is the replica running", when a login expired, or when a skill script exits 2 saying this machine is not connected. Does every step a key can do through the catalyst CLI, hands over the exact page for the steps only a browser can do, and never claims a step it did not watch succeed.
|
|
5
5
|
allowed-tools: Bash(catalyst:*) Bash(npx -p @catalyst-cloud/cli catalyst:*)
|
|
6
6
|
---
|
|
7
|
-
<!-- vendored-from: @catalyst-cloud/catalyst-skills@0.16.
|
|
7
|
+
<!-- vendored-from: @catalyst-cloud/catalyst-skills@0.16.35 — written in this repository for customer accounts -->
|
|
8
8
|
|
|
9
9
|
# Onboard me
|
|
10
10
|
|
|
@@ -4,7 +4,7 @@ description: >-
|
|
|
4
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 account's event stream for the scope through the catalyst 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
5
|
allowed-tools: Bash(catalyst:*) Bash(npx -p @catalyst-cloud/cli catalyst:*)
|
|
6
6
|
---
|
|
7
|
-
<!-- vendored-from: @catalyst-cloud/catalyst-skills@0.16.
|
|
7
|
+
<!-- vendored-from: @catalyst-cloud/catalyst-skills@0.16.35 — written in this repository for customer accounts -->
|
|
8
8
|
|
|
9
9
|
# Run this project
|
|
10
10
|
|
package/skills/unstick/SKILL.md
CHANGED
|
@@ -4,7 +4,7 @@ description: >-
|
|
|
4
4
|
Get a stuck Catalyst Cloud ticket moving again. Use when the person asks "why is this parked and can you release it?", "unpark this", "get things flowing again", "the outage is over, retry what failed", or hands you a ticket that is not moving. Reads why nothing runs and what holds the ticket through the catalyst CLI, decides whether the recorded cause is fixed, previews the release, releases every governor holding the ticket the right way (or one failure class across a team), and raises an ask only for what a person has to do. Never releases a cause it cannot show changed without saying so.
|
|
5
5
|
allowed-tools: Bash(catalyst:*) Bash(npx -p @catalyst-cloud/cli catalyst:*)
|
|
6
6
|
---
|
|
7
|
-
<!-- vendored-from: @catalyst-cloud/catalyst-skills@0.16.
|
|
7
|
+
<!-- vendored-from: @catalyst-cloud/catalyst-skills@0.16.35 — written in this repository for customer accounts -->
|
|
8
8
|
|
|
9
9
|
# Unstick
|
|
10
10
|
|
|
@@ -4,7 +4,7 @@ description: >-
|
|
|
4
4
|
The human's decision inbox on Catalyst Cloud, and the one way an agent raises a decision on their behalf. Use when the person asks "what needs me?", "what am I blocking?", or when active work is gated on a choice only they can make. Lists open asks ranked by how much open work each one holds, files an ask through the cloud's ask route with the account's own template, and records the answer so the held work releases. Never answers as the human, never duplicates an open ask.
|
|
5
5
|
allowed-tools: Bash(catalyst:*) Bash(npx -p @catalyst-cloud/cli catalyst:*)
|
|
6
6
|
---
|
|
7
|
-
<!-- vendored-from: @catalyst-cloud/catalyst-skills@0.16.
|
|
7
|
+
<!-- vendored-from: @catalyst-cloud/catalyst-skills@0.16.35 — written in this repository for customer accounts -->
|
|
8
8
|
|
|
9
9
|
# What needs me
|
|
10
10
|
|
|
@@ -4,7 +4,7 @@ description: >-
|
|
|
4
4
|
What environment variable names does this repository need, and where does each one come from? Scans the repository offline (no login, no network) and lists the names in three groups (build/test, deploy-only, bindings), each with where it was found, what uses it, and where a local value would come from. Never reads a value or prints one. Also checks the environment variable table in `.catalyst/catalyst.toml` offline. Use when someone asks "what does catalyst.env.json mean", "what env vars does this repo need", "why does the container need this", or before reviewing or writing the declaration.
|
|
5
5
|
allowed-tools: Bash(catalyst:*) Bash(npx -p @catalyst-cloud/cli catalyst:*)
|
|
6
6
|
---
|
|
7
|
-
<!-- vendored-from: @catalyst-cloud/catalyst-skills@0.16.
|
|
7
|
+
<!-- vendored-from: @catalyst-cloud/catalyst-skills@0.16.35 — written in this repository for customer accounts -->
|
|
8
8
|
|
|
9
9
|
# What this repository needs
|
|
10
10
|
|
|
@@ -4,7 +4,7 @@ description: >-
|
|
|
4
4
|
Use for "what's happening?", "what machines do we have?", "how much capacity do we have?", "where are we?", "why is that stuck?", "what closed?", "what's next?", "how does this work?", "why did it do that?", "how does it prioritise?" and "what does this setting do, where is it set?" in Catalyst Cloud, or asks for something to be done rather than known. Reads the contract via catalyst CLI. Read-only: route work/decisions, never answer as the human.
|
|
5
5
|
allowed-tools: Bash(catalyst:*) Bash(npx -p @catalyst-cloud/cli catalyst:*)
|
|
6
6
|
---
|
|
7
|
-
<!-- vendored-from: @catalyst-cloud/catalyst-skills@0.16.
|
|
7
|
+
<!-- vendored-from: @catalyst-cloud/catalyst-skills@0.16.35 — written in this repository for customer accounts -->
|
|
8
8
|
|
|
9
9
|
# What's happening
|
|
10
10
|
|