@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.
- package/README.md +11 -178
- package/bin/catalyst-skills.js +3 -49
- package/bin/catalyst.js +3 -0
- package/package.json +8 -29
- package/CHANGELOG.md +0 -41
- package/LICENSE +0 -21
- package/dist/args.js +0 -261
- package/dist/ask.js +0 -158
- package/dist/browser.js +0 -20
- package/dist/cli.js +0 -356
- package/dist/config.js +0 -148
- package/dist/contract-types.js +0 -4
- package/dist/contract.js +0 -178
- package/dist/detach.js +0 -10
- package/dist/errors.js +0 -27
- package/dist/events.js +0 -106
- package/dist/execution.js +0 -369
- package/dist/oauth.js +0 -300
- package/dist/prompt.js +0 -35
- package/dist/query.js +0 -225
- package/dist/ready.js +0 -163
- package/dist/release.js +0 -142
- package/dist/replica.js +0 -325
- package/dist/sdk.js +0 -26
- package/dist/skill-shape.js +0 -179
- package/dist/skills.js +0 -78
- package/dist/transport.js +0 -205
- package/dist/ts-deps-loader.js +0 -68
- package/dist/watch/consumer.js +0 -141
- package/dist/watch/cursor-file.js +0 -62
- package/dist/watch.js +0 -175
- package/dist/write.js +0 -224
- package/skills/catalyst-github/SKILL.md +0 -35
- package/skills/catalyst-github/agents/openai.yaml +0 -6
- package/skills/catalyst-github/agents/portability.yaml +0 -4
- package/skills/catalyst-github/references/is-it-mergeable.md +0 -57
- package/skills/catalyst-github/references/what-a-pr-accumulates.md +0 -61
- package/skills/catalyst-github/scripts/is-it-mergeable.mjs +0 -124
- package/skills/catalyst-github/scripts/lib/cli.mjs +0 -103
- package/skills/catalyst-github/scripts/lib/credential.mjs +0 -29
- package/skills/catalyst-github/scripts/lib/pull.mjs +0 -82
- package/skills/catalyst-github/scripts/read-pr.mjs +0 -97
- package/skills/catalyst-linear/SKILL.md +0 -43
- package/skills/catalyst-linear/agents/openai.yaml +0 -6
- package/skills/catalyst-linear/agents/portability.yaml +0 -5
- package/skills/catalyst-linear/references/reading-a-ticket.md +0 -50
- package/skills/catalyst-linear/references/what-a-ticket-accumulates.md +0 -53
- package/skills/catalyst-linear/references/writing-to-linear.md +0 -43
- package/skills/catalyst-linear/scripts/comment.mjs +0 -59
- package/skills/catalyst-linear/scripts/create-ticket.mjs +0 -44
- package/skills/catalyst-linear/scripts/label.mjs +0 -48
- package/skills/catalyst-linear/scripts/lib/cli.mjs +0 -164
- package/skills/catalyst-linear/scripts/lib/credential.mjs +0 -29
- package/skills/catalyst-linear/scripts/move.mjs +0 -41
- package/skills/catalyst-linear/scripts/read-ticket.mjs +0 -93
- package/skills/catalyst-linear/scripts/search.mjs +0 -49
- package/skills/catalyst-setup/SKILL.md +0 -32
- package/skills/catalyst-setup/agents/openai.yaml +0 -6
- package/skills/catalyst-setup/agents/portability.yaml +0 -4
- package/skills/catalyst-setup/references/what-each-check-means.md +0 -66
- package/skills/catalyst-setup/scripts/check.mjs +0 -75
- package/skills/catalyst-setup/scripts/lib/cli.mjs +0 -103
- package/skills/catalyst-setup/scripts/lib/credential.mjs +0 -29
- package/skills/catalyst-setup/scripts/replica-status.mjs +0 -46
- package/skills/connect-me/SKILL.md +0 -63
- package/skills/connect-me/agents/openai.yaml +0 -6
- package/skills/connect-me/agents/portability.yaml +0 -5
- package/skills/connect-me/references/keeping-the-replica-running.md +0 -88
- package/skills/connect-me/scripts/lib/cli.mjs +0 -185
- package/skills/connect-me/scripts/lib/credential.mjs +0 -29
- package/skills/connect-me/scripts/verify-connection.mjs +0 -68
- package/skills/how-catalyst-works/SKILL.md +0 -42
- package/skills/how-catalyst-works/agents/openai.yaml +0 -6
- package/skills/how-catalyst-works/agents/portability.yaml +0 -4
- package/skills/how-catalyst-works/references/coding-accounts.md +0 -51
- package/skills/how-catalyst-works/references/stages-and-mapping.md +0 -54
- package/skills/how-catalyst-works/references/the-ladder.md +0 -41
- package/skills/how-catalyst-works/references/what-runs-next.md +0 -77
- package/skills/how-catalyst-works/references/when-a-phase-fails.md +0 -57
- package/skills/how-catalyst-works/scripts/explain-ticket.mjs +0 -41
- package/skills/how-catalyst-works/scripts/lib/cli.mjs +0 -164
- package/skills/how-catalyst-works/scripts/lib/credential.mjs +0 -29
- package/skills/how-catalyst-works/scripts/show-my-map.mjs +0 -94
- package/skills/how-catalyst-works/scripts/whats-running.mjs +0 -65
- package/skills/run-this-project/SKILL.md +0 -45
- package/skills/run-this-project/agents/openai.yaml +0 -6
- package/skills/run-this-project/agents/portability.yaml +0 -5
- package/skills/run-this-project/assets/stall-policy.json +0 -15
- package/skills/run-this-project/references/making-work-ready.md +0 -60
- package/skills/run-this-project/references/reacting-to-events.md +0 -76
- package/skills/run-this-project/references/stalls-and-escalation.md +0 -63
- package/skills/run-this-project/scripts/lib/cli.mjs +0 -185
- package/skills/run-this-project/scripts/lib/credential.mjs +0 -29
- package/skills/run-this-project/scripts/make-ready.mjs +0 -64
- package/skills/run-this-project/scripts/scope-status.mjs +0 -0
- package/skills/run-this-project/scripts/watch-scope.mjs +0 -61
- package/skills/unstick/SKILL.md +0 -41
- package/skills/unstick/agents/openai.yaml +0 -6
- package/skills/unstick/agents/portability.yaml +0 -5
- package/skills/unstick/references/playbook.md +0 -51
- package/skills/unstick/scripts/lib/cli.mjs +0 -135
- package/skills/unstick/scripts/lib/credential.mjs +0 -29
- package/skills/unstick/scripts/unstick.mjs +0 -57
- package/skills/what-needs-me/SKILL.md +0 -41
- package/skills/what-needs-me/agents/openai.yaml +0 -6
- package/skills/what-needs-me/agents/portability.yaml +0 -5
- package/skills/what-needs-me/references/raising-a-decision.md +0 -41
- package/skills/what-needs-me/references/reading-the-inbox.md +0 -38
- package/skills/what-needs-me/references/settling-an-answer.md +0 -37
- package/skills/what-needs-me/scripts/inbox.mjs +0 -56
- package/skills/what-needs-me/scripts/lib/cli.mjs +0 -135
- package/skills/what-needs-me/scripts/lib/credential.mjs +0 -29
- package/skills/what-needs-me/scripts/raise.mjs +0 -53
- package/skills/what-needs-me/scripts/settle.mjs +0 -73
- package/skills/whats-happening/SKILL.md +0 -43
- package/skills/whats-happening/agents/openai.yaml +0 -6
- package/skills/whats-happening/agents/portability.yaml +0 -4
- package/skills/whats-happening/assets/status-reply.json +0 -77
- package/skills/whats-happening/references/reading-the-board.md +0 -43
- package/skills/whats-happening/references/reprioritising.md +0 -37
- package/skills/whats-happening/references/routing-work.md +0 -36
- package/skills/whats-happening/references/status-reply.md +0 -34
- package/skills/whats-happening/references/why-is-it-stuck.md +0 -62
- package/skills/whats-happening/scripts/explain.mjs +0 -28
- package/skills/whats-happening/scripts/lib/cli.mjs +0 -135
- package/skills/whats-happening/scripts/lib/credential.mjs +0 -29
- package/skills/whats-happening/scripts/snapshot.mjs +0 -145
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// lib/cli.mjs — the one way a skill script reaches Catalyst Cloud: it spawns the catalyst-skills CLI
|
|
3
|
-
// this machine connected with (the path recorded in customer.json, else npx) and hands back its
|
|
4
|
-
// output. Scripts import it; a person runs it with --help to see what it does. No dependencies.
|
|
5
|
-
import { spawnSync } from "node:child_process";
|
|
6
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
7
|
-
import { join } from "node:path";
|
|
8
|
-
import { pathToFileURL } from "node:url";
|
|
9
|
-
import { CONNECT_COMMAND, hasCredential } from "./credential.mjs";
|
|
10
|
-
|
|
11
|
-
export const PACKAGE = "@catalyst-cloud/catalyst-skills";
|
|
12
|
-
export const CONNECT_HINT = CONNECT_COMMAND;
|
|
13
|
-
|
|
14
|
-
/** ~/.config/catalyst-cloud/customer.json, honouring CATALYST_SKILLS_HOME (used by tests) over HOME. */
|
|
15
|
-
export function configPath() {
|
|
16
|
-
const home = process.env.CATALYST_SKILLS_HOME ?? process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
17
|
-
return join(home, ".config", "catalyst-cloud", "customer.json");
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/** The config, or a one-line reason it could not be read. Never throws. */
|
|
21
|
-
export function loadConfig() {
|
|
22
|
-
const path = configPath();
|
|
23
|
-
if (!existsSync(path)) return { ok: false, reason: `no config at ${path}` };
|
|
24
|
-
try {
|
|
25
|
-
const cfg = JSON.parse(readFileSync(path, "utf8"));
|
|
26
|
-
if (!hasCredential(cfg) || typeof cfg.account !== "string") {
|
|
27
|
-
return { ok: false, reason: `${path} is missing a key or login, or the account` };
|
|
28
|
-
}
|
|
29
|
-
return { ok: true, cfg, path };
|
|
30
|
-
} catch (err) {
|
|
31
|
-
return { ok: false, reason: `${path} is unreadable: ${err instanceof Error ? err.message : String(err)}` };
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/** Exit 2 with the one line every script prints when this machine is not connected to a tenant. */
|
|
36
|
-
export function notConfigured(reason) {
|
|
37
|
-
console.error(`not connected to a Catalyst Cloud tenant (${reason}) — run: ${CONNECT_HINT}`);
|
|
38
|
-
process.exit(2);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Run one catalyst-skills verb. Returns { code, stdout, stderr }. The CLI is `node <cliPath>` when
|
|
43
|
-
* the config recorded one that still exists, else `npx @catalyst-cloud/catalyst-skills`.
|
|
44
|
-
* Exits 2 (not configured) before spawning anything when the config is absent or unreadable.
|
|
45
|
-
*/
|
|
46
|
-
export function runCli(args, { stdin } = {}) {
|
|
47
|
-
const loaded = loadConfig();
|
|
48
|
-
if (!loaded.ok) notConfigured(loaded.reason);
|
|
49
|
-
const { cfg } = loaded;
|
|
50
|
-
let cmd;
|
|
51
|
-
let argv;
|
|
52
|
-
let shell = false;
|
|
53
|
-
if (typeof cfg.cliPath === "string" && existsSync(cfg.cliPath)) {
|
|
54
|
-
cmd = process.execPath;
|
|
55
|
-
argv = [cfg.cliPath, ...args];
|
|
56
|
-
} else {
|
|
57
|
-
cmd = process.platform === "win32" ? "npx.cmd" : "npx";
|
|
58
|
-
argv = [PACKAGE, ...args];
|
|
59
|
-
shell = process.platform === "win32";
|
|
60
|
-
}
|
|
61
|
-
const r = spawnSync(cmd, argv, {
|
|
62
|
-
encoding: "utf8",
|
|
63
|
-
input: stdin,
|
|
64
|
-
env: process.env,
|
|
65
|
-
shell,
|
|
66
|
-
maxBuffer: 64 * 1024 * 1024,
|
|
67
|
-
});
|
|
68
|
-
if (r.error) {
|
|
69
|
-
console.error(`could not run ${cmd}: ${r.error.message} — re-run ${CONNECT_HINT} to record the CLI path`);
|
|
70
|
-
process.exit(2);
|
|
71
|
-
}
|
|
72
|
-
return { code: r.status ?? 1, stdout: r.stdout ?? "", stderr: r.stderr ?? "" };
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/** Print the CLI's stderr (the source line, the contract line, any refusal) on our stderr. */
|
|
76
|
-
export function relayStderr(r) {
|
|
77
|
-
const text = r.stderr.trimEnd();
|
|
78
|
-
if (text) console.error(text);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/** A non-zero CLI result ends the script with the same code, its stdout shown so nothing is lost. */
|
|
82
|
-
export function exitOnFailure(r) {
|
|
83
|
-
if (r.code === 0) return;
|
|
84
|
-
relayStderr(r);
|
|
85
|
-
const out = r.stdout.trimEnd();
|
|
86
|
-
if (out) console.log(out);
|
|
87
|
-
process.exit(r.code);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
export function parseJson(text) {
|
|
91
|
-
try {
|
|
92
|
-
return JSON.parse(text.trim());
|
|
93
|
-
} catch {
|
|
94
|
-
return null;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* A small flag parser: `spec.value` names flags that take a value, `spec.bool` boolean flags,
|
|
100
|
-
* `spec.repeat` value flags that may repeat (collected into arrays). Unknown flags are a usage error.
|
|
101
|
-
*/
|
|
102
|
-
export function parseFlags(argv, spec = {}) {
|
|
103
|
-
const value = new Set(spec.value ?? []);
|
|
104
|
-
const bool = new Set(spec.bool ?? []);
|
|
105
|
-
const repeat = new Set(spec.repeat ?? []);
|
|
106
|
-
const flags = {};
|
|
107
|
-
const positionals = [];
|
|
108
|
-
for (let i = 0; i < argv.length; i++) {
|
|
109
|
-
const a = argv[i];
|
|
110
|
-
if (a === "--") {
|
|
111
|
-
positionals.push(...argv.slice(i + 1));
|
|
112
|
-
break;
|
|
113
|
-
}
|
|
114
|
-
if (!a.startsWith("--")) {
|
|
115
|
-
positionals.push(a);
|
|
116
|
-
continue;
|
|
117
|
-
}
|
|
118
|
-
let name = a.slice(2);
|
|
119
|
-
let inline;
|
|
120
|
-
const eq = name.indexOf("=");
|
|
121
|
-
if (eq !== -1) {
|
|
122
|
-
inline = name.slice(eq + 1);
|
|
123
|
-
name = name.slice(0, eq);
|
|
124
|
-
}
|
|
125
|
-
if (bool.has(name)) {
|
|
126
|
-
flags[name] = true;
|
|
127
|
-
} else if (value.has(name) || repeat.has(name)) {
|
|
128
|
-
const v = inline ?? argv[++i];
|
|
129
|
-
if (v === undefined || v === "") usage(`--${name} needs a value`);
|
|
130
|
-
if (repeat.has(name)) (flags[name] ??= []).push(v);
|
|
131
|
-
else flags[name] = v;
|
|
132
|
-
} else {
|
|
133
|
-
usage(`unknown option --${name}`);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
return { flags, positionals };
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
export function usage(message) {
|
|
140
|
-
console.error(message);
|
|
141
|
-
process.exit(1);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
export function wantsHelp(argv) {
|
|
145
|
-
return argv.includes("--help") || argv.includes("-h");
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
const runDirectly = process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href;
|
|
149
|
-
if (runDirectly) {
|
|
150
|
-
if (wantsHelp(process.argv.slice(2)) || process.argv.length <= 2) {
|
|
151
|
-
console.log(
|
|
152
|
-
[
|
|
153
|
-
"lib/cli.mjs — shared helper for this skill's scripts (not a command of its own)",
|
|
154
|
-
"",
|
|
155
|
-
"Reads ~/.config/catalyst-cloud/customer.json and runs the catalyst-skills CLI recorded there",
|
|
156
|
-
`(or npx ${PACKAGE} when no path is recorded). Exit 2 when the machine is not connected.`,
|
|
157
|
-
"",
|
|
158
|
-
`Connect first with: ${CONNECT_HINT}`,
|
|
159
|
-
`Config in use: ${configPath()}`,
|
|
160
|
-
].join("\n"),
|
|
161
|
-
);
|
|
162
|
-
process.exit(0);
|
|
163
|
-
}
|
|
164
|
-
}
|
|
@@ -1,29 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// move.mjs — move a card by SLOT, never by stage name: the CLI resolves the slot to this team's
|
|
3
|
-
// live state id from the contract and refuses a slot that is unmapped or whose state is gone.
|
|
4
|
-
import { exitOnFailure, parseFlags, parseJson, relayStderr, runCli, usage, wantsHelp } from "./lib/cli.mjs";
|
|
5
|
-
|
|
6
|
-
const HELP = `Usage: node scripts/move.mjs <ticket> (--slot <slot> | --state-type <type>) [--json]
|
|
7
|
-
|
|
8
|
-
Moves a ticket's card. Wraps: catalyst-skills write state. Spends one unit of the daily write budget.
|
|
9
|
-
|
|
10
|
-
<ticket> the Linear identifier, e.g. KEY-123
|
|
11
|
-
--slot <slot> one of the eleven slots: dispatch, intake, research, plan, implement,
|
|
12
|
-
remediate, verify, review, pr, done, canceled. Moving to dispatch is how
|
|
13
|
-
work is dispatched.
|
|
14
|
-
--state-type <type> the team's first state of this Linear type, resolved from its live
|
|
15
|
-
workflow states; use "backlog" to park a card (Backlog is not a slot)
|
|
16
|
-
--json print the CLI's JSON result
|
|
17
|
-
|
|
18
|
-
Exit 0 moved, 1 on a usage error, 2 when this machine is not connected to a tenant or the move
|
|
19
|
-
was refused (the slot is unmapped for the team, its state no longer exists in Linear, the daily
|
|
20
|
-
budget is spent; the line says which).`;
|
|
21
|
-
|
|
22
|
-
const argv = process.argv.slice(2);
|
|
23
|
-
if (wantsHelp(argv)) {
|
|
24
|
-
console.log(HELP);
|
|
25
|
-
process.exit(0);
|
|
26
|
-
}
|
|
27
|
-
const { flags, positionals } = parseFlags(argv, { bool: ["json"], value: ["slot", "state-type"] });
|
|
28
|
-
const ticket = positionals[0];
|
|
29
|
-
if (!ticket) usage("move needs a ticket identifier (see --help)");
|
|
30
|
-
if (!flags.slot && !flags["state-type"]) usage("move needs --slot <slot> or --state-type <type> (see --help)");
|
|
31
|
-
if (flags.slot && flags["state-type"]) usage("give --slot or --state-type, not both");
|
|
32
|
-
|
|
33
|
-
const args = ["write", "state", ticket, "--json"];
|
|
34
|
-
if (flags.slot) args.push("--slot", flags.slot);
|
|
35
|
-
else args.push("--state-type", flags["state-type"]);
|
|
36
|
-
const r = runCli(args);
|
|
37
|
-
exitOnFailure(r);
|
|
38
|
-
relayStderr(r);
|
|
39
|
-
const result = parseJson(r.stdout);
|
|
40
|
-
if (flags.json) console.log(JSON.stringify(result ?? {}));
|
|
41
|
-
else console.log(`${ticket} moved to ${flags.slot ? `slot ${flags.slot}` : `the team's ${flags["state-type"]} state`}`);
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// read-ticket.mjs — one ticket with its comments, relations, labels, linked pull requests and agent
|
|
3
|
-
// sessions inline, from the replica when it is fresh, else the origin-fresh API. The source line the
|
|
4
|
-
// CLI prints on stderr is the freshness verdict; it is always shown.
|
|
5
|
-
import { parseFlags, parseJson, relayStderr, runCli, usage, wantsHelp } from "./lib/cli.mjs";
|
|
6
|
-
|
|
7
|
-
const HELP = `Usage: node scripts/read-ticket.mjs <ticket> [--comments] [--source replica|api] [--json]
|
|
8
|
-
|
|
9
|
-
Reads one ticket record. Wraps: catalyst-skills query issue.
|
|
10
|
-
|
|
11
|
-
<ticket> the Linear identifier, e.g. KEY-123
|
|
12
|
-
--comments also print every comment (id, author, time, body)
|
|
13
|
-
--source replica|api force one source; default is the replica when fresh, else the API
|
|
14
|
-
--json print the full record as JSON
|
|
15
|
-
|
|
16
|
-
The first stderr line names the source ("source: replica (cursor N)" or "source: api (replica
|
|
17
|
-
stale|absent|not configured)"); quote it when the freshness of the answer matters.
|
|
18
|
-
|
|
19
|
-
Exit 0 found, 1 not found or a usage error, 2 when this machine is not connected to a tenant or the
|
|
20
|
-
cloud refused the read (the line says which).`;
|
|
21
|
-
|
|
22
|
-
const argv = process.argv.slice(2);
|
|
23
|
-
if (wantsHelp(argv)) {
|
|
24
|
-
console.log(HELP);
|
|
25
|
-
process.exit(0);
|
|
26
|
-
}
|
|
27
|
-
const { flags, positionals } = parseFlags(argv, { bool: ["comments", "json"], value: ["source"] });
|
|
28
|
-
const ticket = positionals[0];
|
|
29
|
-
if (!ticket) usage("read-ticket needs a ticket identifier (see --help)");
|
|
30
|
-
|
|
31
|
-
const args = ["query", "issue", ticket, "--json"];
|
|
32
|
-
if (flags.source) args.push("--source", flags.source);
|
|
33
|
-
const r = runCli(args);
|
|
34
|
-
relayStderr(r);
|
|
35
|
-
if (r.code !== 0) {
|
|
36
|
-
const out = r.stdout.trimEnd();
|
|
37
|
-
if (out) console.log(out);
|
|
38
|
-
process.exit(r.code);
|
|
39
|
-
}
|
|
40
|
-
const row = parseJson(r.stdout);
|
|
41
|
-
if (!row || typeof row !== "object") {
|
|
42
|
-
console.log(r.stdout.trimEnd());
|
|
43
|
-
process.exit(1);
|
|
44
|
-
}
|
|
45
|
-
if (flags.json) {
|
|
46
|
-
console.log(JSON.stringify(row));
|
|
47
|
-
process.exit(0);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const line = (label, value) => {
|
|
51
|
-
if (value === undefined || value === null || value === "") return;
|
|
52
|
-
console.log(`${label}: ${typeof value === "string" ? value : JSON.stringify(value)}`);
|
|
53
|
-
};
|
|
54
|
-
const names = (list, key) => (Array.isArray(list) ? list.map((x) => (x && typeof x === "object" ? (x[key] ?? JSON.stringify(x)) : String(x))) : []);
|
|
55
|
-
|
|
56
|
-
line("ticket", row.identifier ?? row.id);
|
|
57
|
-
line("title", row.title);
|
|
58
|
-
line("state", row.state);
|
|
59
|
-
line("priority", row.priority_label ?? row.priority);
|
|
60
|
-
line("assignee", row.assignee_name ?? row.assignee);
|
|
61
|
-
line("delegate", row.delegate_name ?? row.delegate);
|
|
62
|
-
line("team", row.team_key ?? row.team_name ?? row.team_id);
|
|
63
|
-
line("project", row.project_name ?? row.project_id);
|
|
64
|
-
line("cycle", row.cycle_name ?? row.cycle_id);
|
|
65
|
-
line("parent", row.parent_identifier);
|
|
66
|
-
line("labels", names(row.labels, "name").join(", "));
|
|
67
|
-
line("url", row.url);
|
|
68
|
-
line("updated", row.updated_at);
|
|
69
|
-
if (Array.isArray(row.relations) && row.relations.length) {
|
|
70
|
-
console.log(`relations (${row.relations.length}):`);
|
|
71
|
-
for (const rel of row.relations) console.log(` ${rel.type ?? "?"}: ${rel.issue_identifier ?? ""} -> ${rel.related_identifier ?? JSON.stringify(rel)}`);
|
|
72
|
-
}
|
|
73
|
-
if (Array.isArray(row.linked_pulls) && row.linked_pulls.length) {
|
|
74
|
-
console.log(`linked pull requests (${row.linked_pulls.length}):`);
|
|
75
|
-
for (const pr of row.linked_pulls) console.log(` #${pr.number ?? "?"} ${pr.repo_id ?? ""} [${pr.node_id ?? ""}]`);
|
|
76
|
-
}
|
|
77
|
-
if (Array.isArray(row.agent_sessions)) line("agent sessions", row.agent_sessions.length);
|
|
78
|
-
if (Array.isArray(row.activity)) line("activity entries", row.activity.length);
|
|
79
|
-
if (Array.isArray(row.comments)) line("comments", row.comments.length);
|
|
80
|
-
if (row.description) {
|
|
81
|
-
console.log("description:");
|
|
82
|
-
console.log(String(row.description).trimEnd());
|
|
83
|
-
}
|
|
84
|
-
if (flags.comments && Array.isArray(row.comments)) {
|
|
85
|
-
console.log(`== comments (${row.comments.length})`);
|
|
86
|
-
for (const c of row.comments) {
|
|
87
|
-
const who = c.author_name ?? c.author_id ?? "?";
|
|
88
|
-
const bot = c.is_bot ? " [bot]" : "";
|
|
89
|
-
const parent = c.parent_id ? ` reply-to ${c.parent_id}` : "";
|
|
90
|
-
console.log(`-- ${c.id} · ${who}${bot} · ${c.updated_at ?? c.created_at ?? ""}${parent}`);
|
|
91
|
-
console.log(String(c.body ?? "").trimEnd());
|
|
92
|
-
}
|
|
93
|
-
}
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// search.mjs — tenant search across ticket identifiers and titles, pull-request titles, project and
|
|
3
|
-
// initiative names. Always the origin-fresh API (the replica holds no search view).
|
|
4
|
-
import { exitOnFailure, parseFlags, parseJson, relayStderr, runCli, usage, wantsHelp } from "./lib/cli.mjs";
|
|
5
|
-
|
|
6
|
-
const HELP = `Usage: node scripts/search.mjs <terms...> [--limit <n>] [--json]
|
|
7
|
-
|
|
8
|
-
Searches your tenant for tickets, pull requests, projects and initiatives matching the terms.
|
|
9
|
-
Wraps: catalyst-skills query search.
|
|
10
|
-
|
|
11
|
-
<terms> one or more words; matched against identifiers, titles and names
|
|
12
|
-
--limit <n> max rows (default 50)
|
|
13
|
-
--json print the rows as JSON
|
|
14
|
-
|
|
15
|
-
Exit 0 (an empty result is still 0 and prints "no matches"), 1 on a usage error, 2 when this
|
|
16
|
-
machine is not connected to a tenant or the cloud refused the search (the line says which).`;
|
|
17
|
-
|
|
18
|
-
const argv = process.argv.slice(2);
|
|
19
|
-
if (wantsHelp(argv)) {
|
|
20
|
-
console.log(HELP);
|
|
21
|
-
process.exit(0);
|
|
22
|
-
}
|
|
23
|
-
const { flags, positionals } = parseFlags(argv, { bool: ["json"], value: ["limit"] });
|
|
24
|
-
if (positionals.length === 0) usage("search needs at least one term (see --help)");
|
|
25
|
-
|
|
26
|
-
const args = ["query", "search", ...positionals, "--json"];
|
|
27
|
-
if (flags.limit) args.push("--limit", flags.limit);
|
|
28
|
-
const r = runCli(args);
|
|
29
|
-
exitOnFailure(r);
|
|
30
|
-
relayStderr(r);
|
|
31
|
-
const rows = parseJson(r.stdout);
|
|
32
|
-
if (flags.json) {
|
|
33
|
-
console.log(JSON.stringify(rows ?? r.stdout.trimEnd()));
|
|
34
|
-
process.exit(0);
|
|
35
|
-
}
|
|
36
|
-
const list = Array.isArray(rows) ? rows : rows && typeof rows === "object" ? Object.values(rows).flat() : [];
|
|
37
|
-
if (list.length === 0) {
|
|
38
|
-
console.log("no matches");
|
|
39
|
-
process.exit(0);
|
|
40
|
-
}
|
|
41
|
-
for (const row of list) {
|
|
42
|
-
if (!row || typeof row !== "object") {
|
|
43
|
-
console.log(String(row));
|
|
44
|
-
continue;
|
|
45
|
-
}
|
|
46
|
-
const kind = row.kind ?? (row.identifier ? "issue" : row.number ? "pull" : "row");
|
|
47
|
-
const id = row.identifier ?? (row.number !== undefined ? `#${row.number}` : row.id ?? "");
|
|
48
|
-
console.log(`${kind} ${id} ${row.title ?? row.name ?? ""}${row.state ? ` (${row.state})` : ""}`);
|
|
49
|
-
}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: catalyst-setup
|
|
3
|
-
description: >-
|
|
4
|
-
Am I set up? Machine readiness (Node, the tenant connection, the cached contract, the CLI path, the skills, the SDK, the optional replica) plus tenant readiness from the contract's ten per-team checks, in one verdict: what passes, what is blocked, what is merely waiting, and who can click what. Use when someone asks "am I set up", "what is missing", "why does nothing happen", "is the replica running", or right after connecting a new machine. Reports; never repairs.
|
|
5
|
-
allowed-tools: Bash(catalyst-skills:*) Bash(npx @catalyst-cloud/catalyst-skills:*)
|
|
6
|
-
---
|
|
7
|
-
<!-- vendored-from: @catalyst-cloud/catalyst-skills — written in this repository for customer tenants -->
|
|
8
|
-
|
|
9
|
-
# Am I set up?
|
|
10
|
-
|
|
11
|
-
You answer one question with one verdict and one list. The verdict is READY or NOT READY. The list is who can click what: the fixes only the person at the keyboard can make, the fixes only a tenant owner or admin can make in settings, and the checks that are simply waiting for the first event. The live check ids, severities, states and the people who can answer come from the contract; the scripts print them, you never restate them.
|
|
12
|
-
|
|
13
|
-
## Run first
|
|
14
|
-
|
|
15
|
-
- `node scripts/check.mjs --help` — every machine and tenant check, one line each, the fix and who for each failure, the verdict, then the who-can-click-what list. Exit 1 when NOT READY.
|
|
16
|
-
- `node scripts/replica-status.mjs --help` — the optional replica's verdict with no network (add `--probe` for how far behind it is). Exit 0 fresh, 1 stale, 2 not connected, 3 absent.
|
|
17
|
-
|
|
18
|
-
## Load on demand
|
|
19
|
-
|
|
20
|
-
| when | read |
|
|
21
|
-
| -- | -- |
|
|
22
|
-
| any check is red, unknown or waiting and the person asks what it proves, how to fix it, or who can | `references/what-each-check-means.md` |
|
|
23
|
-
|
|
24
|
-
## Rules
|
|
25
|
-
|
|
26
|
-
- Report, never repair. No tenant-reachable repair verb exists for a key yet; the settings page is where a tenant owner or admin fixes a mapping, a connection or a label, and you say which one.
|
|
27
|
-
- End every answer with the verdict and the who-can-click-what list, in that order.
|
|
28
|
-
- A stale or absent replica is a note, never a failure: every skill reads the API meanwhile and says so. Do not tell the person to start it unless they want local SQL or cheaper repeated reads.
|
|
29
|
-
- Not connected (exit 2) means the connect step, not a retry: `npx @catalyst-cloud/catalyst-skills login`, which logs the person in keyless in their browser; with a personal key from Settings → API keys instead, prefix it with `CATALYST_CLOUD_TOKEN=<your personal key>`. Never guess a tenant; the login (or the key) is the only selector.
|
|
30
|
-
- Waiting is not failing. "No write observed", "no delivery observed" and "no host connected" clear themselves the first time the thing happens; say that instead of raising them.
|
|
31
|
-
- Unknown is not a pass. A check the engine could not run is reported as such, never rounded up.
|
|
32
|
-
- Never run a check in a loop. If the person wants to know when a waiting check clears, that is the project-running skill's watch.
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
interface:
|
|
2
|
-
display_name: "Am I set up?"
|
|
3
|
-
short_description: "Machine and tenant readiness in one verdict: what passes, what is blocked, and who can click what"
|
|
4
|
-
default_prompt: "Use $catalyst-setup to tell me whether this machine and my tenant are ready, and what is missing."
|
|
5
|
-
policy:
|
|
6
|
-
allow_implicit_invocation: true
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
# What each check means
|
|
2
|
-
|
|
3
|
-
This page restates invariants: the ten per-team readiness checks the cloud runs, what each proves, its fix and who can click it; the machine checks the CLI adds; and the four replica verdicts. The live values are never restated: each check's severity and whether it needs a human's answer come from the contract's `readinessChecks[]`, each team's current states from `teams[].readiness`, and the people who can answer from `humans[]`. `node scripts/check.mjs` prints all of it; this page is how to read what it printed.
|
|
4
|
-
|
|
5
|
-
## How a team's readiness is scored
|
|
6
|
-
|
|
7
|
-
A team's status is one of `ready`, `degraded`, `blocked` or `unchecked`. The engine always reports all eleven checks; a check it could not run is `unknown`, never `pass`. Any failing check the contract marks as blocking makes the team blocked; any unknown, and any failing check marked degrading, makes it degraded. Unchecked means no readiness pass has run for that team yet, which is a note, not a failure. Readiness is stamped with the account-wide mapping revision it was computed against, so a stale verdict is visible as such.
|
|
8
|
-
|
|
9
|
-
## The eleven checks
|
|
10
|
-
|
|
11
|
-
| check id | proves | when it fails, the fix | who clicks |
|
|
12
|
-
| -- | -- | -- | -- |
|
|
13
|
-
| `oauth_scope` | Catalyst holds the Linear permissions it needs | re-authorise the Linear connection to grant the missing scope | a tenant owner or admin, in settings |
|
|
14
|
-
| `token_live` | the Linear connection is accepted right now | reconnect Linear (expired or revoked), or wait and re-check (Linear unreachable). A distinct reason says Linear was never connected at all | owner or admin |
|
|
15
|
-
| `team_visible` | Catalyst can see this team | most often the team was made private: grant Catalyst access in Linear's team settings, then re-check | owner or admin, in Linear |
|
|
16
|
-
| `mapped_states_exist` | every stage Catalyst mapped still exists in Linear | re-map the team; nothing in the workspace was changed. A pending-write reason means a mapping was just saved and the read has not caught up: wait, do not re-map | owner or admin, in settings |
|
|
17
|
-
| `mapping_total` | every stage Catalyst moves tickets into is mapped | map the missing stages. "Absent" means the team was never mapped; "incomplete" means a few of the load-bearing stages are missing | owner or admin, in settings |
|
|
18
|
-
| `types_compatible` | each load-bearing mapped stage is the right kind (dispatch and intake unstarted or backlog, PR started, done completed, canceled canceled) | change the mapping to a stage of the right kind | owner or admin, in settings |
|
|
19
|
-
| `labels_present` | the labels Catalyst uses exist in the workspace | none needed by a person: Catalyst creates them the first time it uses them | nobody |
|
|
20
|
-
| `writes_land` | Catalyst has written to this team successfully | "no write observed" is waiting, not failing: it clears the first time Catalyst moves a ticket. "Write refused" means Linear rejected the last write: check the connection and the team's permissions | owner or admin when refused; otherwise nobody |
|
|
21
|
-
| `webhook_covers_team` | events for this team are arriving | confirmed once a repository is registered and events flow; "no delivery observed" is waiting | owner or admin, by registering the repository |
|
|
22
|
-
| `hosts_current` | no connected host runs an older mapping revision | a host that is behind re-loads the mapping on its next connect; a host that did not report its revision is flagged rather than assumed current; "no host connected" is waiting | whoever runs that host |
|
|
23
|
-
| `environment_declared` | a committed environment declaration for the team's default repository is in effect (`.catalyst/environment.json` was ingested, is valid, and its latest proposal is approved) | `no_team_repo_default`: register a repository and make it the team's default; `no_environment_declaration`: commit the declaration file to that repository; `declaration_invalid` / `declaration_read_failed`: fix the file (the ingest names the error); `declaration_awaiting_approval`: an owner or admin approves the proposal in Settings → Environment |
|
|
24
|
-
|
|
25
|
-
Three checks are informational by design (labels, event delivery, host currency): they degrade a team but never block it. Which are which is served on `readinessChecks[].severity`; do not memorise the split.
|
|
26
|
-
|
|
27
|
-
## Reasons that look like failures and are not
|
|
28
|
-
|
|
29
|
-
- `no_write_observed`, `no_delivery_observed`, `no_host_connected`: nothing has happened yet. Expected on a fresh tenant; they clear on their own.
|
|
30
|
-
- `stages_pending_write`: a mapping was saved and the read predates it. Re-check shortly; re-mapping would be a second write for no reason.
|
|
31
|
-
- `unknown` on any check: the engine could not look. It is not a pass and not a fail; say so.
|
|
32
|
-
|
|
33
|
-
## The machine checks the CLI adds
|
|
34
|
-
|
|
35
|
-
`catalyst-skills ready` prepends checks about this machine before the tenant's:
|
|
36
|
-
|
|
37
|
-
| id | proves | fix |
|
|
38
|
-
| -- | -- | -- |
|
|
39
|
-
| `node` | Node 22 or newer, which the SDK's built-in SQLite engine needs | install Node 22+ |
|
|
40
|
-
| `config` | this machine is connected: `customer.json` exists and loads | `npx @catalyst-cloud/catalyst-skills login` (keyless: the person approves in their browser); or, with a personal key minted at Settings → API keys, the same command prefixed with `CATALYST_CLOUD_TOKEN=<your personal key>` |
|
|
41
|
-
| `contract` | the tenant contract is cached and its major version is one this bundle accepts | `catalyst-skills contract --refresh`; a version outside the range means update the bundle. A 403 naming an older cloud means the cloud has not yet deployed personal-key access |
|
|
42
|
-
| `cliPath` | the CLI path recorded at login still exists, so skill scripts can spawn it | re-run login |
|
|
43
|
-
| `skills` | every skill of this bundle is present where the CLI installed them | `catalyst-skills install` |
|
|
44
|
-
| `sdk` | the SDK loads, so the replica and the watch are available | run under Node 22.15 or newer; every read still works through the API meanwhile |
|
|
45
|
-
| `replica` | the optional replica is fresh | a note, never a failure; see below |
|
|
46
|
-
|
|
47
|
-
## The replica's four verdicts
|
|
48
|
-
|
|
49
|
-
`node scripts/replica-status.mjs` needs no network and exits with the verdict:
|
|
50
|
-
|
|
51
|
-
| exit | verdict | what a skill does with it |
|
|
52
|
-
| -- | -- | -- |
|
|
53
|
-
| 0 | fresh: a live writer, a heartbeat younger than the staleness threshold, a non-empty cursor | reads the replica and says so |
|
|
54
|
-
| 1 | stale: the file exists but the writer is gone, the heartbeat is old, or there is no cursor | reads the API and says so; `catalyst-skills replica start --detach` brings it back |
|
|
55
|
-
| 2 | not connected to a tenant | the connect step first |
|
|
56
|
-
| 3 | absent: no replica file at all | reads the API; the replica is optional and one command away |
|
|
57
|
-
|
|
58
|
-
`--probe` adds the one network call: it compares the local cursor with the cloud's head and prints how far behind the replica is, the only honest "how stale" number. A skill must never refuse to work because the replica is down, and must never silently read a stale one; both halves are one exit code away.
|
|
59
|
-
|
|
60
|
-
## Who can click what
|
|
61
|
-
|
|
62
|
-
The last block `check.mjs` prints groups every failure by the person it needs. Machine fixes name "you", the person at the keyboard. Tenant fixes name the owner or admin roles the contract lists (as Linear user ids, since that is how the cloud knows them), because the settings page that repairs a mapping, a connection or a label is theirs. A check the contract marks as not needing an answer names nobody: it is informational or self-clearing. This skill reports; it never repairs, because no key-reachable repair verb exists yet.
|
|
63
|
-
|
|
64
|
-
## Setting up one team at a time
|
|
65
|
-
|
|
66
|
-
A team starts receiving work only once its stages are saved, and that is done one team at a time: a tenant owner or admin opens Settings → Linear teams, picks the team and presses Map my stages (or Adopt the Catalyst workflow). No other team's stages or tickets change; only the labels Adopt creates are shared across the workspace. Before saving, the screen lists which of that team's tickets would start and which stay where they are. Once saved, the tickets in the team's dispatch stage start; tickets in other stages that Catalyst never worked on stay where they are. To pilot safely, pick a low-stakes team, move anything in its dispatch stage that should not start back to Backlog, and leave the other teams unmapped. `gitAutomation` in the contract plays no part: nothing reads it.
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// check.mjs — "am I set up?" in one verdict: the machine checks the CLI runs (Node, the connection,
|
|
3
|
-
// the cached contract, the CLI path, the skills, the SDK, the optional replica) plus every team's
|
|
4
|
-
// readiness vector from the tenant contract, then the list of who can click what. Wraps
|
|
5
|
-
// `catalyst-skills ready --json`; reports, never repairs. Exit 0 READY, 1 NOT READY, 2 not connected.
|
|
6
|
-
import { parseJson, runCli } from "./lib/cli.mjs";
|
|
7
|
-
|
|
8
|
-
const HELP = `Usage: node scripts/check.mjs [--json]
|
|
9
|
-
|
|
10
|
-
Prints one line per check (ok / note / FAIL), the fix and who can apply it for every failure, the
|
|
11
|
-
verdict, and a "who can click what" list grouped by the person or role each fix needs.
|
|
12
|
-
A note never flips the verdict (the replica is optional; an informational readiness check is a note).
|
|
13
|
-
|
|
14
|
-
--json prints the CLI's report document {ready, checks[]} unchanged.
|
|
15
|
-
Exit 0 READY, 1 NOT READY, 2 this machine is not connected to a tenant yet.`;
|
|
16
|
-
|
|
17
|
-
const args = process.argv.slice(2);
|
|
18
|
-
if (args.includes("--help") || args.includes("-h")) {
|
|
19
|
-
console.log(HELP);
|
|
20
|
-
process.exit(0);
|
|
21
|
-
}
|
|
22
|
-
const unknown = args.filter((a) => a !== "--json");
|
|
23
|
-
if (unknown.length > 0) {
|
|
24
|
-
console.error(`unknown argument: ${unknown.join(" ")}`);
|
|
25
|
-
console.log(HELP);
|
|
26
|
-
process.exit(1);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const res = runCli(["ready", "--json"]);
|
|
30
|
-
if (res.code === 2) {
|
|
31
|
-
console.error(res.stderr.trim() || "catalyst-skills ready refused");
|
|
32
|
-
process.exit(2);
|
|
33
|
-
}
|
|
34
|
-
const report = parseJson(res.stdout);
|
|
35
|
-
if (!report || !Array.isArray(report.checks)) {
|
|
36
|
-
console.error(res.stderr.trim() || res.stdout.trim() || "catalyst-skills ready printed no report");
|
|
37
|
-
process.exit(res.code === 0 ? 1 : res.code);
|
|
38
|
-
}
|
|
39
|
-
if (args.includes("--json")) {
|
|
40
|
-
console.log(JSON.stringify(report));
|
|
41
|
-
process.exit(report.ready ? 0 : 1);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const failures = [];
|
|
45
|
-
for (const c of report.checks) {
|
|
46
|
-
const tag = c.note ? "note" : c.ok ? "ok " : "FAIL";
|
|
47
|
-
console.log(`${tag} ${c.line}`);
|
|
48
|
-
if (!c.ok && !c.note) {
|
|
49
|
-
if (c.fix) console.log(` fix: ${c.fix}`);
|
|
50
|
-
if (c.who) console.log(` who: ${c.who}`);
|
|
51
|
-
failures.push(c);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
console.log(report.ready ? "READY" : "NOT READY");
|
|
55
|
-
|
|
56
|
-
if (failures.length > 0) {
|
|
57
|
-
console.log("");
|
|
58
|
-
console.log("Who can click what:");
|
|
59
|
-
const byWho = new Map();
|
|
60
|
-
for (const c of failures) {
|
|
61
|
-
const who = c.who ?? "unknown (the check named nobody)";
|
|
62
|
-
if (!byWho.has(who)) byWho.set(who, []);
|
|
63
|
-
byWho.get(who).push(c);
|
|
64
|
-
}
|
|
65
|
-
for (const [who, list] of byWho) {
|
|
66
|
-
console.log(` ${who}:`);
|
|
67
|
-
for (const c of list) console.log(` - ${c.id}: ${c.fix ?? c.line}`);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
const notes = report.checks.filter((c) => c.note && !c.ok);
|
|
71
|
-
if (notes.length > 0) {
|
|
72
|
-
console.log("");
|
|
73
|
-
console.log(`Notes that do not block: ${notes.map((c) => c.id).join(", ")} — see references/what-each-check-means.md`);
|
|
74
|
-
}
|
|
75
|
-
process.exit(report.ready ? 0 : 1);
|