@corelayer-ai/cli 0.4.4 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/install-skill.js +11 -19
- package/dist/commands/issues.js +85 -13
- package/dist/commands/preflight.js +137 -0
- package/dist/index.js +5 -0
- package/dist/lib/api-client.js +15 -3
- package/package.json +2 -2
- package/skill/SKILL.md +56 -5
- package/skill/references/preflight.md +71 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { existsSync,
|
|
1
|
+
import { existsSync, cpSync, rmSync } from "node:fs";
|
|
2
2
|
import { join, dirname } from "node:path";
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
@@ -7,11 +7,11 @@ const SKILL_NAME = "corelayer";
|
|
|
7
7
|
function getSkillTargetDir() {
|
|
8
8
|
return join(homedir(), ".claude", "skills", SKILL_NAME);
|
|
9
9
|
}
|
|
10
|
-
function
|
|
10
|
+
function getSkillSourceDir() {
|
|
11
11
|
const thisFile = fileURLToPath(import.meta.url);
|
|
12
12
|
const distDir = dirname(dirname(thisFile));
|
|
13
13
|
const packageDir = dirname(distDir);
|
|
14
|
-
return join(packageDir, "skill"
|
|
14
|
+
return join(packageDir, "skill");
|
|
15
15
|
}
|
|
16
16
|
function printInstallHelp() {
|
|
17
17
|
process.stdout
|
|
@@ -44,16 +44,15 @@ export async function runInstallSkill(args, ctx) {
|
|
|
44
44
|
printInstallHelp();
|
|
45
45
|
return;
|
|
46
46
|
}
|
|
47
|
-
const
|
|
48
|
-
if (!existsSync(
|
|
49
|
-
fail("Skill
|
|
47
|
+
const sourceDir = getSkillSourceDir();
|
|
48
|
+
if (!existsSync(join(sourceDir, "SKILL.md"))) {
|
|
49
|
+
fail("Skill files not found. The CLI package may be incomplete.");
|
|
50
50
|
}
|
|
51
51
|
const targetDir = getSkillTargetDir();
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
copyFileSync(source, targetFile);
|
|
52
|
+
rmSync(targetDir, { recursive: true, force: true });
|
|
53
|
+
cpSync(sourceDir, targetDir, { recursive: true });
|
|
55
54
|
if (!ctx.quiet) {
|
|
56
|
-
process.stdout.write(`Corelayer skill installed to ${
|
|
55
|
+
process.stdout.write(`Corelayer skill installed to ${targetDir}\n`);
|
|
57
56
|
}
|
|
58
57
|
}
|
|
59
58
|
export async function runUninstallSkill(args, ctx) {
|
|
@@ -62,17 +61,10 @@ export async function runUninstallSkill(args, ctx) {
|
|
|
62
61
|
return;
|
|
63
62
|
}
|
|
64
63
|
const targetDir = getSkillTargetDir();
|
|
65
|
-
|
|
66
|
-
if (!existsSync(targetFile)) {
|
|
64
|
+
if (!existsSync(targetDir)) {
|
|
67
65
|
fail("Corelayer skill is not installed.");
|
|
68
66
|
}
|
|
69
|
-
|
|
70
|
-
try {
|
|
71
|
-
rmdirSync(targetDir);
|
|
72
|
-
}
|
|
73
|
-
catch {
|
|
74
|
-
// directory not empty or already removed
|
|
75
|
-
}
|
|
67
|
+
rmSync(targetDir, { recursive: true, force: true });
|
|
76
68
|
if (!ctx.quiet) {
|
|
77
69
|
process.stdout.write("Corelayer skill uninstalled.\n");
|
|
78
70
|
}
|
package/dist/commands/issues.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { createInterface } from "node:readline/promises";
|
|
2
1
|
import { stdin as input, stdout as output } from "node:process";
|
|
2
|
+
import { createInterface } from "node:readline/promises";
|
|
3
3
|
import { CorelayerClient } from "../lib/api-client.js";
|
|
4
|
-
import { readConfig,
|
|
4
|
+
import { readConfig, resolveApiUrl, resolveToken } from "../lib/config.js";
|
|
5
5
|
import { fail, formatDate, printJson, printTable } from "../lib/output.js";
|
|
6
6
|
function readFlag(args, flag) {
|
|
7
7
|
const index = args.indexOf(flag);
|
|
@@ -27,7 +27,9 @@ function removeFlags(args) {
|
|
|
27
27
|
arg === "--page" ||
|
|
28
28
|
arg === "--feedback" ||
|
|
29
29
|
arg === "--last-seen-before" ||
|
|
30
|
-
arg === "--last-seen-before-or-on"
|
|
30
|
+
arg === "--last-seen-before-or-on" ||
|
|
31
|
+
arg === "--since" ||
|
|
32
|
+
arg === "--duration") {
|
|
31
33
|
skipNext = true;
|
|
32
34
|
continue;
|
|
33
35
|
}
|
|
@@ -55,8 +57,9 @@ USAGE:
|
|
|
55
57
|
|
|
56
58
|
SUBCOMMANDS:
|
|
57
59
|
list List issues in a group
|
|
58
|
-
get <id>
|
|
60
|
+
get <id|slug> Show details for one issue
|
|
59
61
|
summary Summarize issue health for a group
|
|
62
|
+
diff Show issues new or changed since a timestamp
|
|
60
63
|
close <id> Close an issue (write operation)
|
|
61
64
|
bulk-close Close many issues in one request (write operation)
|
|
62
65
|
reopen <id> Reopen a closed issue (write operation)
|
|
@@ -66,25 +69,49 @@ OPTIONS:
|
|
|
66
69
|
--group <id> Group ID (or set default-group via 'corelayer config set')
|
|
67
70
|
--status <status> Filter by status (list)
|
|
68
71
|
--severity <sev> Filter by severity (list)
|
|
69
|
-
--limit <n> Limit results (list)
|
|
70
|
-
--page <n> Page number (list)
|
|
72
|
+
--limit <n> Limit results (list, diff)
|
|
73
|
+
--page <n> Page number (list, diff)
|
|
71
74
|
--feedback <text> Feedback message (close)
|
|
72
75
|
--last-seen-before <date|preset>
|
|
73
76
|
Filter bulk-close to issues seen before a cutoff
|
|
74
77
|
--last-seen-before-or-on <date|preset>
|
|
75
78
|
Filter bulk-close to issues seen on or before a cutoff
|
|
79
|
+
--since <timestamp|preset>
|
|
80
|
+
Diff: ISO 8601 timestamp or preset (1hour, 24hours, 7days, 14days, 30days)
|
|
81
|
+
--duration <duration>
|
|
82
|
+
Diff: human-friendly duration (e.g. 2hours, 4hours, 30m, 1d)
|
|
76
83
|
--yes Skip confirmation prompt (delete)
|
|
77
84
|
--json Output as JSON
|
|
78
85
|
-h, --help Print help
|
|
79
86
|
|
|
80
87
|
EXAMPLES:
|
|
81
88
|
corelayer issues list --group g_123 --status Open
|
|
82
|
-
corelayer issues get
|
|
89
|
+
corelayer issues get corelayer-prod-engine-20260602-1 --json
|
|
90
|
+
corelayer issues diff --group g_123 --since 2026-06-01T00:00:00Z
|
|
91
|
+
corelayer issues diff --group g_123 --since 24hours
|
|
92
|
+
corelayer issues diff --group g_123 --duration 2hours --limit 50
|
|
83
93
|
corelayer issues close i_456 --feedback "fixed in deploy 2026-04-07"
|
|
84
94
|
corelayer issues bulk-close --group g_123 --last-seen-before-or-on 14days --feedback "Not seen in two weeks"
|
|
85
95
|
corelayer issues delete i_456 --yes
|
|
86
96
|
`);
|
|
87
97
|
}
|
|
98
|
+
function parseDuration(value) {
|
|
99
|
+
const match = value
|
|
100
|
+
.trim()
|
|
101
|
+
.toLowerCase()
|
|
102
|
+
.match(/^(\d+(?:\.\d+)?)\s*(m|min|minutes?|h|hr|hours?|d|days?)$/);
|
|
103
|
+
if (!match)
|
|
104
|
+
return null;
|
|
105
|
+
const n = parseFloat(match[1]);
|
|
106
|
+
const unit = match[2];
|
|
107
|
+
if (unit.startsWith("m"))
|
|
108
|
+
return Math.round(n * 60 * 1000);
|
|
109
|
+
if (unit.startsWith("h"))
|
|
110
|
+
return Math.round(n * 60 * 60 * 1000);
|
|
111
|
+
if (unit.startsWith("d"))
|
|
112
|
+
return Math.round(n * 24 * 60 * 60 * 1000);
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
88
115
|
export async function runIssues(args, ctx) {
|
|
89
116
|
if (args.includes("--help") || args.includes("-h")) {
|
|
90
117
|
printHelp();
|
|
@@ -114,7 +141,7 @@ export async function runIssues(args, ctx) {
|
|
|
114
141
|
printJson(result);
|
|
115
142
|
return;
|
|
116
143
|
}
|
|
117
|
-
const rows = result.issues.map(issue => [
|
|
144
|
+
const rows = result.issues.map((issue) => [
|
|
118
145
|
issue.id,
|
|
119
146
|
issue.slug || "-",
|
|
120
147
|
issue.severity || "-",
|
|
@@ -129,11 +156,11 @@ export async function runIssues(args, ctx) {
|
|
|
129
156
|
return;
|
|
130
157
|
}
|
|
131
158
|
if (sub === "get") {
|
|
132
|
-
const
|
|
133
|
-
if (!
|
|
134
|
-
fail("Usage: corelayer issues get <issueId>");
|
|
159
|
+
const issueIdentifier = commandArgs[0];
|
|
160
|
+
if (!issueIdentifier) {
|
|
161
|
+
fail("Usage: corelayer issues get <issueId|slug>");
|
|
135
162
|
}
|
|
136
|
-
const issue = await client.getIssue(
|
|
163
|
+
const issue = await client.getIssue(issueIdentifier);
|
|
137
164
|
if (ctx.json) {
|
|
138
165
|
printJson(issue);
|
|
139
166
|
return;
|
|
@@ -239,5 +266,50 @@ export async function runIssues(args, ctx) {
|
|
|
239
266
|
printJson(summary);
|
|
240
267
|
return;
|
|
241
268
|
}
|
|
242
|
-
|
|
269
|
+
if (sub === "diff") {
|
|
270
|
+
const groupId = readFlag(args, "--group") || config.defaults?.group;
|
|
271
|
+
if (!groupId) {
|
|
272
|
+
fail("groupId is required. Use --group <groupId> or set default-group via corelayer config set default-group <groupId>");
|
|
273
|
+
}
|
|
274
|
+
const sinceFlag = readFlag(args, "--since");
|
|
275
|
+
const durationFlag = readFlag(args, "--duration");
|
|
276
|
+
if (!sinceFlag && !durationFlag) {
|
|
277
|
+
fail("One of --since <timestamp|preset> or --duration <duration> is required.\n" +
|
|
278
|
+
"Examples: --since 2026-06-01T00:00:00Z --since 24hours --duration 2hours");
|
|
279
|
+
}
|
|
280
|
+
let sinceValue;
|
|
281
|
+
if (durationFlag) {
|
|
282
|
+
const ms = parseDuration(durationFlag);
|
|
283
|
+
if (ms === null) {
|
|
284
|
+
fail(`Invalid --duration value: "${durationFlag}". Examples: 30m, 2hours, 4h, 1d, 7days`);
|
|
285
|
+
}
|
|
286
|
+
sinceValue = new Date(Date.now() - ms).toISOString();
|
|
287
|
+
}
|
|
288
|
+
else {
|
|
289
|
+
sinceValue = sinceFlag;
|
|
290
|
+
}
|
|
291
|
+
const result = await client.diffIssues(groupId, sinceValue, {
|
|
292
|
+
limit: readFlag(args, "--limit"),
|
|
293
|
+
page: readFlag(args, "--page"),
|
|
294
|
+
});
|
|
295
|
+
if (ctx.json) {
|
|
296
|
+
printJson(result);
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
process.stdout.write(`DIFF SINCE ${result.since}\n`);
|
|
300
|
+
const rows = result.issues.map((issue) => [
|
|
301
|
+
issue.id,
|
|
302
|
+
issue.slug || "-",
|
|
303
|
+
issue.severity || "-",
|
|
304
|
+
issue.status || "-",
|
|
305
|
+
issue.title || "-",
|
|
306
|
+
formatDate(issue.last_seen_at || issue.lastSeenAt),
|
|
307
|
+
]);
|
|
308
|
+
printTable(["ID", "SLUG", "SEVERITY", "STATUS", "TITLE", "LAST SEEN"], rows);
|
|
309
|
+
if (!ctx.quiet) {
|
|
310
|
+
process.stdout.write(`${result.total} issue${result.total === 1 ? "" : "s"} changed since ${result.since}\n`);
|
|
311
|
+
}
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
fail("Usage: corelayer issues <list|get|close|bulk-close|reopen|delete|summary|diff> [options]");
|
|
243
315
|
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { execSync } from "node:child_process";
|
|
2
|
+
import { CorelayerClient } from "../lib/api-client.js";
|
|
3
|
+
import { resolveToken, resolveApiUrl } from "../lib/config.js";
|
|
4
|
+
import { fail, printJson } from "../lib/output.js";
|
|
5
|
+
const MAX_DIFF_CHARS = 12_000;
|
|
6
|
+
function printHelp() {
|
|
7
|
+
process.stdout.write(`corelayer preflight - Check a change against org memory before you push
|
|
8
|
+
|
|
9
|
+
Surfaces failure modes and things to double-check that your org has hit before for
|
|
10
|
+
changes like this one. Run inside a git repo with no arguments and it derives the PR
|
|
11
|
+
context from your branch; override any piece with the flags below.
|
|
12
|
+
|
|
13
|
+
USAGE:
|
|
14
|
+
corelayer preflight [options]
|
|
15
|
+
|
|
16
|
+
OPTIONS:
|
|
17
|
+
--title <text> PR title (default: latest commit subject / branch name)
|
|
18
|
+
--description <text> PR description (default: commits since base + changed files)
|
|
19
|
+
--repo <name> Repository name (default: parsed from the git remote)
|
|
20
|
+
--base <ref> Base branch to diff against (default: origin/HEAD, else main)
|
|
21
|
+
--diff Include the (truncated) code diff in what's sent
|
|
22
|
+
--json Output as JSON
|
|
23
|
+
-h, --help Print help
|
|
24
|
+
|
|
25
|
+
EXAMPLES:
|
|
26
|
+
corelayer preflight
|
|
27
|
+
corelayer preflight --diff
|
|
28
|
+
corelayer preflight --description "Add Redis cache to the payments worker" --repo payments-api
|
|
29
|
+
`);
|
|
30
|
+
}
|
|
31
|
+
function readFlag(args, flag) {
|
|
32
|
+
const index = args.indexOf(flag);
|
|
33
|
+
if (index === -1)
|
|
34
|
+
return undefined;
|
|
35
|
+
return args[index + 1];
|
|
36
|
+
}
|
|
37
|
+
function git(args) {
|
|
38
|
+
try {
|
|
39
|
+
return execSync(`git ${args}`, {
|
|
40
|
+
encoding: "utf8",
|
|
41
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
42
|
+
}).trim();
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function detectRepo() {
|
|
49
|
+
const url = git("config --get remote.origin.url");
|
|
50
|
+
if (!url)
|
|
51
|
+
return undefined;
|
|
52
|
+
const match = url.replace(/\.git$/, "").match(/([^/:]+)$/);
|
|
53
|
+
return match ? match[1] : undefined;
|
|
54
|
+
}
|
|
55
|
+
function detectBase() {
|
|
56
|
+
const head = git("rev-parse --abbrev-ref origin/HEAD"); // e.g. origin/main
|
|
57
|
+
if (head && head.startsWith("origin/"))
|
|
58
|
+
return head.slice("origin/".length);
|
|
59
|
+
for (const candidate of ["main", "master"]) {
|
|
60
|
+
if (git(`rev-parse --verify ${candidate}`))
|
|
61
|
+
return candidate;
|
|
62
|
+
}
|
|
63
|
+
return "main";
|
|
64
|
+
}
|
|
65
|
+
function severityColor(sev, noColor) {
|
|
66
|
+
const label = (sev || "n/a").toUpperCase();
|
|
67
|
+
if (noColor)
|
|
68
|
+
return label;
|
|
69
|
+
const code = sev === "critical" || sev === "high"
|
|
70
|
+
? "31" // red
|
|
71
|
+
: sev === "medium"
|
|
72
|
+
? "33" // yellow
|
|
73
|
+
: "90"; // grey
|
|
74
|
+
return `\x1b[${code}m${label}\x1b[0m`;
|
|
75
|
+
}
|
|
76
|
+
function printChecks(checks, noColor) {
|
|
77
|
+
checks.forEach((check, i) => {
|
|
78
|
+
process.stdout.write(`${i + 1}. ${check.advice}\n`);
|
|
79
|
+
const seen = check.occurrenceCount > 1 ? ` · seen ${check.occurrenceCount}×` : "";
|
|
80
|
+
const where = check.scope === "resource" ? check.anchorLabel : check.scope;
|
|
81
|
+
process.stdout.write(` ${severityColor(check.severity, noColor)} · "${check.title}" · ${where}${seen}\n`);
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
export async function runPreflight(args, ctx) {
|
|
85
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
86
|
+
printHelp();
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
const base = readFlag(args, "--base") || detectBase();
|
|
90
|
+
const commits = git(`log ${base}..HEAD --pretty=format:%s%n%b`) || "";
|
|
91
|
+
const files = git(`diff --name-only ${base}...HEAD`) || "";
|
|
92
|
+
const branch = git("rev-parse --abbrev-ref HEAD");
|
|
93
|
+
const repo = readFlag(args, "--repo") || detectRepo();
|
|
94
|
+
const title = readFlag(args, "--title") || commits.split("\n")[0]?.trim() || branch;
|
|
95
|
+
let description = readFlag(args, "--description");
|
|
96
|
+
if (!description) {
|
|
97
|
+
const parts = [];
|
|
98
|
+
if (commits.trim())
|
|
99
|
+
parts.push(commits.trim());
|
|
100
|
+
if (files.trim())
|
|
101
|
+
parts.push(`Changed files:\n${files.trim()}`);
|
|
102
|
+
description = parts.join("\n\n");
|
|
103
|
+
}
|
|
104
|
+
if (args.includes("--diff")) {
|
|
105
|
+
const diff = git(`diff ${base}...HEAD`);
|
|
106
|
+
if (diff)
|
|
107
|
+
description += `\n\nDiff:\n${diff.slice(0, MAX_DIFF_CHARS)}`;
|
|
108
|
+
}
|
|
109
|
+
if (!description.trim()) {
|
|
110
|
+
fail(`No PR context found. Run inside a git repo with commits ahead of "${base}", or pass --description.`);
|
|
111
|
+
}
|
|
112
|
+
const token = resolveToken();
|
|
113
|
+
if (!token) {
|
|
114
|
+
fail("Not authenticated. Set CORELAYER_API_KEY or run: corelayer login");
|
|
115
|
+
}
|
|
116
|
+
const apiUrl = resolveApiUrl(ctx.apiUrlOverride);
|
|
117
|
+
const client = new CorelayerClient(apiUrl, token);
|
|
118
|
+
const result = await client.preflight({
|
|
119
|
+
description,
|
|
120
|
+
title: title || undefined,
|
|
121
|
+
repo,
|
|
122
|
+
});
|
|
123
|
+
if (ctx.json) {
|
|
124
|
+
printJson(result);
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
if (result.checks.length === 0) {
|
|
128
|
+
if (!ctx.quiet) {
|
|
129
|
+
process.stdout.write("✓ Preflight: nothing in org memory to double-check for this change.\n");
|
|
130
|
+
}
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
if (!ctx.quiet) {
|
|
134
|
+
process.stdout.write(`Preflight — ${result.checks.length} thing(s) to double-check before you push:\n\n`);
|
|
135
|
+
}
|
|
136
|
+
printChecks(result.checks, ctx.noColor);
|
|
137
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -8,6 +8,7 @@ import { runIssues } from "./commands/issues.js";
|
|
|
8
8
|
import { runInstallSkill, runUninstallSkill, } from "./commands/install-skill.js";
|
|
9
9
|
import { runLogin } from "./commands/login.js";
|
|
10
10
|
import { runLogout } from "./commands/logout.js";
|
|
11
|
+
import { runPreflight } from "./commands/preflight.js";
|
|
11
12
|
const require = createRequire(import.meta.url);
|
|
12
13
|
const { version } = require("../package.json");
|
|
13
14
|
function printHelp() {
|
|
@@ -23,6 +24,7 @@ COMMANDS:
|
|
|
23
24
|
api-keys Create Corelayer API keys
|
|
24
25
|
groups List and inspect groups
|
|
25
26
|
issues List, inspect, and manage issues
|
|
27
|
+
preflight Check a change against org memory before you push
|
|
26
28
|
integrations List integrations for a group
|
|
27
29
|
config Read and write local CLI config
|
|
28
30
|
install-skill Install the Corelayer agent skill
|
|
@@ -119,6 +121,9 @@ async function main() {
|
|
|
119
121
|
case "integrations":
|
|
120
122
|
await runIntegrations(rest, ctx);
|
|
121
123
|
return;
|
|
124
|
+
case "preflight":
|
|
125
|
+
await runPreflight(rest, ctx);
|
|
126
|
+
return;
|
|
122
127
|
case "config":
|
|
123
128
|
await runConfig(rest, ctx);
|
|
124
129
|
return;
|
package/dist/lib/api-client.js
CHANGED
|
@@ -64,6 +64,9 @@ export class CorelayerClient {
|
|
|
64
64
|
async getIssue(issueId) {
|
|
65
65
|
return this.request("GET", `/api/v1/issues/${issueId}`);
|
|
66
66
|
}
|
|
67
|
+
async preflight(params) {
|
|
68
|
+
return this.request("POST", "/api/v1/preflight", params);
|
|
69
|
+
}
|
|
67
70
|
async closeIssue(issueId, feedback) {
|
|
68
71
|
await this.request("PATCH", `/api/v1/issues/${issueId}`, {
|
|
69
72
|
status: "Closed",
|
|
@@ -94,6 +97,15 @@ export class CorelayerClient {
|
|
|
94
97
|
async deleteIssue(issueId) {
|
|
95
98
|
await this.request("DELETE", `/api/v1/issues/${issueId}`);
|
|
96
99
|
}
|
|
100
|
+
async diffIssues(groupId, since, options = {}) {
|
|
101
|
+
const params = new URLSearchParams({ groupId, since });
|
|
102
|
+
for (const [key, value] of Object.entries(options)) {
|
|
103
|
+
if (value !== undefined && value !== null && value !== "") {
|
|
104
|
+
params.set(key, String(value));
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return this.request("GET", `/api/v1/issues/diff?${params.toString()}`);
|
|
108
|
+
}
|
|
97
109
|
async getIssueSummary(groupId) {
|
|
98
110
|
const params = new URLSearchParams({ groupId });
|
|
99
111
|
return this.request("GET", `/api/v1/issues/summary?${params.toString()}`);
|
|
@@ -130,13 +142,13 @@ export class CorelayerClient {
|
|
|
130
142
|
const suffix = params.toString() ? `?${params.toString()}` : "";
|
|
131
143
|
const raw = await this.request("GET", `/api/v1/integrations/accounts${suffix}`);
|
|
132
144
|
return {
|
|
133
|
-
accounts: (raw.accounts ?? []).map(account => ({
|
|
145
|
+
accounts: (raw.accounts ?? []).map((account) => ({
|
|
134
146
|
id: account.id,
|
|
135
147
|
group_id: account.group_id,
|
|
136
148
|
provider_code: account.provider_code,
|
|
137
149
|
is_active: account.is_active,
|
|
138
150
|
created_at: account.created_at,
|
|
139
|
-
resources: (account.resources ?? []).map(resource => ({
|
|
151
|
+
resources: (account.resources ?? []).map((resource) => ({
|
|
140
152
|
id: resource.id,
|
|
141
153
|
resource_type: resource.resource_type,
|
|
142
154
|
external_id: resource.external_id,
|
|
@@ -144,7 +156,7 @@ export class CorelayerClient {
|
|
|
144
156
|
is_active: resource.is_active,
|
|
145
157
|
})),
|
|
146
158
|
})),
|
|
147
|
-
providers: (raw.providers ?? []).map(provider => ({
|
|
159
|
+
providers: (raw.providers ?? []).map((provider) => ({
|
|
148
160
|
provider_code: provider.provider_code,
|
|
149
161
|
display_name: provider.display_name,
|
|
150
162
|
})),
|
package/package.json
CHANGED
package/skill/SKILL.md
CHANGED
|
@@ -5,7 +5,9 @@ description: >
|
|
|
5
5
|
error analysis, root cause analysis, issue triage, Corelayer, or wants to
|
|
6
6
|
check/manage/close/reopen production issues. Also use when the user says
|
|
7
7
|
"check prod", "what's broken", "any errors", "issue summary", or references
|
|
8
|
-
the corelayer CLI.
|
|
8
|
+
the corelayer CLI. Also use before opening or pushing a pull request, or when
|
|
9
|
+
the user asks what could break, what to watch out for, or for a preflight /
|
|
10
|
+
pre-merge check on a change.
|
|
9
11
|
user-invocable: true
|
|
10
12
|
---
|
|
11
13
|
|
|
@@ -37,12 +39,35 @@ corelayer issues list --status open
|
|
|
37
39
|
### Inspect a specific issue
|
|
38
40
|
|
|
39
41
|
```bash
|
|
40
|
-
corelayer issues get <issueId>
|
|
41
|
-
corelayer issues get <issueId> --json
|
|
42
|
+
corelayer issues get <issueId|slug>
|
|
43
|
+
corelayer issues get <issueId|slug> --json
|
|
42
44
|
```
|
|
43
45
|
|
|
44
46
|
Returns: title, status, severity, event count, what happened, root cause, and next steps.
|
|
45
47
|
|
|
48
|
+
### Diff issues since a timestamp (post-deploy triage)
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
corelayer issues diff --group <groupId> --since 2026-06-01T00:00:00Z
|
|
52
|
+
corelayer issues diff --group <groupId> --since 24hours
|
|
53
|
+
corelayer issues diff --group <groupId> --duration 2hours --limit 50
|
|
54
|
+
corelayer issues diff --group <groupId> --duration 30m --json
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Returns issues that are new or changed since the given timestamp. Useful after PR merges/deploys to identify regressions or newly surfaced issues.
|
|
58
|
+
|
|
59
|
+
### Preflight a change before you push
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
corelayer preflight # derives PR context from the current git branch
|
|
63
|
+
corelayer preflight --diff # include the code diff in the check
|
|
64
|
+
corelayer preflight --json
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Returns failure modes and things to double-check that the org has hit before for a change
|
|
68
|
+
like this; empty means memory has nothing to flag. See [references/preflight.md](references/preflight.md)
|
|
69
|
+
for flags, how to read the output, and the `corelayer.preflight` MCP tool.
|
|
70
|
+
|
|
46
71
|
### Triage issues
|
|
47
72
|
|
|
48
73
|
```bash
|
|
@@ -84,7 +109,7 @@ Setting a default group avoids repeating `--group` on every command.
|
|
|
84
109
|
List issues for a group. Requires `--group` or a configured default group.
|
|
85
110
|
Filterable by status (`open`, `closed`) and severity (`critical`, `high`, `medium`, `low`).
|
|
86
111
|
|
|
87
|
-
### `corelayer issues get <issueId>`
|
|
112
|
+
### `corelayer issues get <issueId|slug>`
|
|
88
113
|
|
|
89
114
|
Get full details for an issue including AI-generated analysis: what happened, root cause, and next steps.
|
|
90
115
|
|
|
@@ -92,6 +117,18 @@ Get full details for an issue including AI-generated analysis: what happened, ro
|
|
|
92
117
|
|
|
93
118
|
Returns a JSON severity breakdown for the group.
|
|
94
119
|
|
|
120
|
+
### `corelayer issues diff --group <id> (--since <timestamp|preset> | --duration <duration>) [--limit <n>] [--page <n>]`
|
|
121
|
+
|
|
122
|
+
Returns issues that are new or updated since the given point in time. Useful for post-deploy triage — run after a PR merge to see what regressions or new errors have surfaced.
|
|
123
|
+
|
|
124
|
+
`--since` accepts an ISO 8601 timestamp (`2026-06-01T00:00:00Z`) or a preset: `1hour`, `24hours`, `7days`, `14days`, `30days`.
|
|
125
|
+
|
|
126
|
+
`--duration` accepts human-friendly durations: `30m`, `2hours`, `4h`, `1d`, `7days`. The duration is subtracted from the current time to compute the since-timestamp.
|
|
127
|
+
|
|
128
|
+
`--limit` and `--page` paginate results. The API defaults to 20 results and caps `--limit` at 100.
|
|
129
|
+
|
|
130
|
+
Use `--json` for machine-readable output suitable for agent automation.
|
|
131
|
+
|
|
95
132
|
### `corelayer issues close <issueId> [--feedback "..."]`
|
|
96
133
|
|
|
97
134
|
Close an issue. Optionally provide feedback (e.g., link to the fix).
|
|
@@ -108,6 +145,10 @@ Reopen a previously closed issue.
|
|
|
108
145
|
|
|
109
146
|
Soft-delete an issue. Prompts for confirmation unless `--yes` is passed.
|
|
110
147
|
|
|
148
|
+
### `corelayer preflight [--title <t>] [--description <d>] [--repo <name>] [--base <ref>] [--diff]`
|
|
149
|
+
|
|
150
|
+
Check a pending change against the org's accumulated memory before opening a PR. With no flags it derives repo/title/description from the current git branch (commits + changed files since the base branch); any flag overrides its piece, and `--diff` includes the patch. Returns grounded, second-person reminders for this change, or nothing when no relevant lesson exists. Full guidance in [references/preflight.md](references/preflight.md).
|
|
151
|
+
|
|
111
152
|
### `corelayer groups list`
|
|
112
153
|
|
|
113
154
|
List all groups in the organization.
|
|
@@ -139,9 +180,19 @@ Clear stored credentials.
|
|
|
139
180
|
```bash
|
|
140
181
|
corelayer issues summary
|
|
141
182
|
corelayer issues list --severity critical
|
|
142
|
-
corelayer issues get <
|
|
183
|
+
corelayer issues get <issueId|slug> --json
|
|
143
184
|
```
|
|
144
185
|
|
|
186
|
+
### Before you open a pull request
|
|
187
|
+
|
|
188
|
+
```bash
|
|
189
|
+
corelayer preflight
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Run it once the change is ready, before pushing. Surface any returned checks to the user as
|
|
193
|
+
reminders to double-check (they're advisory, not blockers); if it returns nothing, say memory
|
|
194
|
+
had nothing to flag. See [references/preflight.md](references/preflight.md).
|
|
195
|
+
|
|
145
196
|
### Scripting and automation
|
|
146
197
|
|
|
147
198
|
Use `--json` and `--quiet` flags for machine-readable output:
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Preflight — check a change against org memory before you push
|
|
2
|
+
|
|
3
|
+
Preflight asks Corelayer's organizational brain: *"for a change like this, what has
|
|
4
|
+
bitten this org before?"* It returns specific, second-person reminders grounded in past
|
|
5
|
+
incidents, facts, decisions, and standing preferences for the services the change touches —
|
|
6
|
+
e.g. an env-var flag that's broken prod deploys before, a packaging step a service has missed,
|
|
7
|
+
a config that needs to be set in every environment. It returns nothing when no relevant lesson
|
|
8
|
+
exists, so an empty result is a meaningful "all clear from memory."
|
|
9
|
+
|
|
10
|
+
Run it after the work is done and before opening the PR, as a final review pass.
|
|
11
|
+
|
|
12
|
+
## CLI
|
|
13
|
+
|
|
14
|
+
Run inside the git repo with no arguments — it derives the PR context from the branch:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
corelayer preflight
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
- **repo** ← parsed from the git remote (`origin`)
|
|
21
|
+
- **title** ← the latest commit subject (falls back to the branch name)
|
|
22
|
+
- **description** ← commit messages since the base branch + the list of changed files
|
|
23
|
+
- **base** ← `origin/HEAD` (else `main`/`master`)
|
|
24
|
+
|
|
25
|
+
Override any piece, or include the actual diff for a richer signal:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
corelayer preflight --diff
|
|
29
|
+
corelayer preflight --base develop
|
|
30
|
+
corelayer preflight --title "Add Redis cache" --description "Caches payment lookups in the worker" --repo payments-api
|
|
31
|
+
corelayer preflight --json # machine-readable, for automation
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
If there are no commits ahead of the base branch (nothing to describe), pass `--description`
|
|
35
|
+
explicitly.
|
|
36
|
+
|
|
37
|
+
## Reading the output
|
|
38
|
+
|
|
39
|
+
Each check is one reminder plus its grounding:
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
1. Double-check the prod env-var flags — deploys here have failed before when they were missing.
|
|
43
|
+
CRITICAL · "Missing/Incorrect Environment Variables in Deployments" · payments-api · seen 30×
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
- **severity** — `critical | high | medium | low` of the underlying lesson.
|
|
47
|
+
- **"title"** — the lesson the reminder is grounded in (cite-and-verify; the wording is generated, the lesson is real).
|
|
48
|
+
- **service / scope** — the service the lesson is anchored to, or `system` / `cross-cutting` for org-wide lessons.
|
|
49
|
+
- **seen N×** — how many times the org has hit this; higher = more established.
|
|
50
|
+
|
|
51
|
+
`--json` also returns `consideredCount` (lessons in the org's catalog) and `pulledCount`
|
|
52
|
+
(lessons the judge read in full for this change).
|
|
53
|
+
|
|
54
|
+
## How to act on it
|
|
55
|
+
|
|
56
|
+
- Treat the checks as **reminders, not blockers** — preflight is advisory and fails open
|
|
57
|
+
(it never blocks a push). Surface them to the user and let them decide.
|
|
58
|
+
- An **empty result is normal and useful** — it means nothing in memory maps to this change.
|
|
59
|
+
Don't manufacture concerns; say memory had nothing to flag.
|
|
60
|
+
- Higher `seen N×` and `critical`/`high` severity deserve the most attention.
|
|
61
|
+
|
|
62
|
+
## MCP equivalent
|
|
63
|
+
|
|
64
|
+
If the Corelayer MCP server is connected, the same capability is the `corelayer.preflight`
|
|
65
|
+
tool (read-only), taking `{ description, title?, repo? }`. Use it when you already have the
|
|
66
|
+
PR text in context and don't need the CLI's git gathering.
|
|
67
|
+
|
|
68
|
+
## Notes
|
|
69
|
+
|
|
70
|
+
- Scoped to your organization via the API key — it only sees your org's memory.
|
|
71
|
+
- Read-only; requires authentication (`corelayer login` or `CORELAYER_API_KEY`).
|