@corelayer-ai/cli 0.1.0 → 0.1.1

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.
@@ -1,17 +1,16 @@
1
1
  import { CorelayerClient } from "../lib/api-client.js";
2
- import { readConfig } from "../lib/config.js";
2
+ import { resolveToken, resolveApiUrl } from "../lib/config.js";
3
3
  import { fail, formatDate, printJson, printTable } from "../lib/output.js";
4
4
  export async function runGroups(args, ctx) {
5
5
  const sub = args[0];
6
6
  if (sub !== "list") {
7
7
  fail("Usage: corelayer groups list");
8
8
  }
9
- const config = readConfig();
10
- const token = config.token;
9
+ const token = resolveToken();
11
10
  if (!token) {
12
- fail("Not logged in. Run: corelayer login");
11
+ fail("Not authenticated. Set CORELAYER_API_KEY or run: corelayer login");
13
12
  }
14
- const apiUrl = ctx.apiUrlOverride || config.apiUrl;
13
+ const apiUrl = resolveApiUrl(ctx.apiUrlOverride);
15
14
  const client = new CorelayerClient(apiUrl, token);
16
15
  const result = await client.listGroups();
17
16
  if (ctx.json) {
@@ -0,0 +1,45 @@
1
+ import { existsSync, mkdirSync, copyFileSync, unlinkSync, rmdirSync, } from "node:fs";
2
+ import { join, dirname } from "node:path";
3
+ import { homedir } from "node:os";
4
+ import { fileURLToPath } from "node:url";
5
+ import { fail } from "../lib/output.js";
6
+ const SKILL_NAME = "corelayer";
7
+ function getSkillTargetDir() {
8
+ return join(homedir(), ".claude", "skills", SKILL_NAME);
9
+ }
10
+ function getSkillSourcePath() {
11
+ const thisFile = fileURLToPath(import.meta.url);
12
+ const distDir = dirname(dirname(thisFile));
13
+ const packageDir = dirname(distDir);
14
+ return join(packageDir, "skill", "SKILL.md");
15
+ }
16
+ export async function runInstallSkill(_args, ctx) {
17
+ const source = getSkillSourcePath();
18
+ if (!existsSync(source)) {
19
+ fail("Skill file not found. The CLI package may be incomplete.");
20
+ }
21
+ const targetDir = getSkillTargetDir();
22
+ const targetFile = join(targetDir, "SKILL.md");
23
+ mkdirSync(targetDir, { recursive: true });
24
+ copyFileSync(source, targetFile);
25
+ if (!ctx.quiet) {
26
+ process.stdout.write(`Corelayer skill installed to ${targetFile}\n`);
27
+ }
28
+ }
29
+ export async function runUninstallSkill(_args, ctx) {
30
+ const targetDir = getSkillTargetDir();
31
+ const targetFile = join(targetDir, "SKILL.md");
32
+ if (!existsSync(targetFile)) {
33
+ fail("Corelayer skill is not installed.");
34
+ }
35
+ unlinkSync(targetFile);
36
+ try {
37
+ rmdirSync(targetDir);
38
+ }
39
+ catch {
40
+ // directory not empty or already removed
41
+ }
42
+ if (!ctx.quiet) {
43
+ process.stdout.write("Corelayer skill uninstalled.\n");
44
+ }
45
+ }
@@ -1,5 +1,5 @@
1
1
  import { CorelayerClient } from "../lib/api-client.js";
2
- import { readConfig } from "../lib/config.js";
2
+ import { readConfig, resolveToken, resolveApiUrl } from "../lib/config.js";
3
3
  import { fail, printJson, printTable } from "../lib/output.js";
4
4
  function readFlag(args, flag) {
5
5
  const index = args.indexOf(flag);
@@ -13,12 +13,12 @@ export async function runIntegrations(args, ctx) {
13
13
  fail("Usage: corelayer integrations list [--group <groupId>]");
14
14
  }
15
15
  const config = readConfig();
16
- const token = config.token;
16
+ const token = resolveToken();
17
17
  if (!token) {
18
- fail("Not logged in. Run: corelayer login");
18
+ fail("Not authenticated. Set CORELAYER_API_KEY or run: corelayer login");
19
19
  }
20
20
  const groupId = readFlag(args, "--group") || config.defaults?.group;
21
- const apiUrl = ctx.apiUrlOverride || config.apiUrl;
21
+ const apiUrl = resolveApiUrl(ctx.apiUrlOverride);
22
22
  const client = new CorelayerClient(apiUrl, token);
23
23
  const result = await client.listIntegrations(groupId);
24
24
  if (ctx.json) {
@@ -1,7 +1,7 @@
1
1
  import { createInterface } from "node:readline/promises";
2
2
  import { stdin as input, stdout as output } from "node:process";
3
3
  import { CorelayerClient } from "../lib/api-client.js";
4
- import { readConfig } from "../lib/config.js";
4
+ import { readConfig, resolveToken, resolveApiUrl } 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);
@@ -48,11 +48,11 @@ async function confirmDelete() {
48
48
  export async function runIssues(args, ctx) {
49
49
  const sub = args[0];
50
50
  const config = readConfig();
51
- const token = config.token;
51
+ const token = resolveToken();
52
52
  if (!token) {
53
- fail("Not logged in. Run: corelayer login");
53
+ fail("Not authenticated. Set CORELAYER_API_KEY or run: corelayer login");
54
54
  }
55
- const apiUrl = ctx.apiUrlOverride || config.apiUrl;
55
+ const apiUrl = resolveApiUrl(ctx.apiUrlOverride);
56
56
  const client = new CorelayerClient(apiUrl, token);
57
57
  const commandArgs = removeFlags(args.slice(1));
58
58
  if (sub === "list") {
@@ -1,4 +1,5 @@
1
1
  import { loginWithBrowser, loginWithCodeDirect } from "../lib/auth.js";
2
+ import { updateConfig } from "../lib/config.js";
2
3
  import { fail } from "../lib/output.js";
3
4
  function readFlag(args, flag) {
4
5
  const index = args.indexOf(flag);
@@ -6,8 +7,34 @@ function readFlag(args, flag) {
6
7
  return undefined;
7
8
  return args[index + 1];
8
9
  }
10
+ function hasFlag(args, flag) {
11
+ return args.includes(flag);
12
+ }
13
+ async function readStdin() {
14
+ if (process.stdin.isTTY) {
15
+ fail("No token provided on stdin. Usage: echo $CORELAYER_API_KEY | corelayer login --with-token");
16
+ }
17
+ const chunks = [];
18
+ for await (const chunk of process.stdin) {
19
+ chunks.push(chunk);
20
+ }
21
+ return Buffer.concat(chunks).toString("utf8").trim();
22
+ }
9
23
  export async function runLogin(args, ctx) {
10
24
  const code = readFlag(args, "--code");
25
+ const withToken = hasFlag(args, "--with-token");
26
+ if (withToken) {
27
+ const token = await readStdin();
28
+ if (!token) {
29
+ fail("Empty token. Usage: echo $CORELAYER_API_KEY | corelayer login --with-token");
30
+ }
31
+ const apiUrl = ctx.apiUrlOverride || "https://api.corelayer.com";
32
+ updateConfig({ token, apiUrl });
33
+ if (!ctx.quiet) {
34
+ process.stdout.write(`Token saved. API URL: ${apiUrl}\n`);
35
+ }
36
+ return;
37
+ }
11
38
  let email;
12
39
  let orgName;
13
40
  if (code) {
package/dist/index.js CHANGED
@@ -4,6 +4,7 @@ import { runConfig } from "./commands/config.js";
4
4
  import { runGroups } from "./commands/groups.js";
5
5
  import { runIntegrations } from "./commands/integrations.js";
6
6
  import { runIssues } from "./commands/issues.js";
7
+ import { runInstallSkill, runUninstallSkill, } from "./commands/install-skill.js";
7
8
  import { runLogin } from "./commands/login.js";
8
9
  import { runLogout } from "./commands/logout.js";
9
10
  const require = createRequire(import.meta.url);
@@ -14,17 +15,33 @@ function printHelp() {
14
15
  Usage:
15
16
  corelayer login (opens browser)
16
17
  corelayer login --code <CODE> --api-url <url> (manual)
18
+ corelayer login --with-token --api-url <url> (pipe token from stdin)
17
19
  corelayer logout
18
20
  corelayer issues <list|get|close|reopen|delete|summary> ...
19
21
  corelayer groups list
20
22
  corelayer integrations list [--group <groupId>]
21
23
  corelayer config <get|set> ...
24
+ corelayer install-skill (install Corelayer skill)
25
+ corelayer uninstall-skill (remove Corelayer skill)
22
26
 
23
27
  Global flags:
24
28
  --json
25
29
  --quiet, -q
26
30
  --api-url <url>
27
31
  --no-color
32
+
33
+ Environment variables:
34
+ CORELAYER_API_KEY API key for non-interactive auth (skips login)
35
+ CORELAYER_API_URL Server URL (default: https://api.corelayer.com)
36
+ CORELAYER_AUTH_URL Auth server URL (default: https://app.corelayer.com)
37
+
38
+ Non-interactive auth (CI/CD, agents):
39
+ export CORELAYER_API_KEY=cl_live_...
40
+ export CORELAYER_API_URL=https://api.corelayer.com
41
+ corelayer issues list --group <groupId>
42
+
43
+ Save a token to config:
44
+ echo $CORELAYER_API_KEY | corelayer login --with-token --api-url <url>
28
45
  `);
29
46
  }
30
47
  function parseGlobalFlags(argv) {
@@ -102,6 +119,12 @@ async function main() {
102
119
  case "config":
103
120
  await runConfig(rest, ctx);
104
121
  return;
122
+ case "install-skill":
123
+ await runInstallSkill(rest, ctx);
124
+ return;
125
+ case "uninstall-skill":
126
+ await runUninstallSkill(rest, ctx);
127
+ return;
105
128
  default:
106
129
  process.stderr.write(`Unknown command: ${command}\n`);
107
130
  printHelp();
package/dist/lib/auth.js CHANGED
@@ -151,7 +151,13 @@ export async function loginWithBrowser() {
151
151
  return result;
152
152
  }
153
153
  function validateServerUrl(url) {
154
- const parsed = new URL(url);
154
+ let parsed;
155
+ try {
156
+ parsed = new URL(url);
157
+ }
158
+ catch {
159
+ throw new Error("Invalid server URL. Use a full URL such as https://app.corelayer.com or http://localhost:3000.");
160
+ }
155
161
  if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
156
162
  throw new Error(`Unsupported protocol "${parsed.protocol}" — only http: and https: are allowed.`);
157
163
  }
@@ -4,7 +4,7 @@ import path from "node:path";
4
4
  const CONFIG_DIR = path.join(os.homedir(), ".corelayer");
5
5
  const CONFIG_PATH = path.join(CONFIG_DIR, "config.json");
6
6
  function defaultApiUrl() {
7
- return process.env.CORELAYER_API_URL || "http://localhost:3000";
7
+ return process.env.CORELAYER_API_URL || "https://api.corelayer.com";
8
8
  }
9
9
  export function getConfigPath() {
10
10
  return CONFIG_PATH;
@@ -37,6 +37,7 @@ export function writeConfig(config) {
37
37
  encoding: "utf8",
38
38
  mode: 0o600,
39
39
  });
40
+ fs.chmodSync(CONFIG_PATH, 0o600);
40
41
  }
41
42
  export function updateConfig(partial) {
42
43
  const current = readConfig();
@@ -51,6 +52,12 @@ export function updateConfig(partial) {
51
52
  writeConfig(merged);
52
53
  return merged;
53
54
  }
55
+ export function resolveToken() {
56
+ return process.env.CORELAYER_API_KEY || readConfig().token;
57
+ }
58
+ export function resolveApiUrl(override) {
59
+ return override || readConfig().apiUrl;
60
+ }
54
61
  export function clearToken() {
55
62
  const current = readConfig();
56
63
  delete current.token;
package/package.json CHANGED
@@ -1,13 +1,14 @@
1
1
  {
2
2
  "name": "@corelayer-ai/cli",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Corelayer CLI",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "corelayer": "./dist/index.js"
8
8
  },
9
9
  "files": [
10
- "dist"
10
+ "dist",
11
+ "skill"
11
12
  ],
12
13
  "engines": {
13
14
  "node": ">=18.0.0"
package/skill/SKILL.md ADDED
@@ -0,0 +1,150 @@
1
+ ---
2
+ name: corelayer
3
+ description: >
4
+ Use when the user mentions production issues, errors, monitoring, incidents,
5
+ error analysis, root cause analysis, issue triage, Corelayer, or wants to
6
+ check/manage/close/reopen production issues. Also use when the user says
7
+ "check prod", "what's broken", "any errors", "issue summary", or references
8
+ the corelayer CLI.
9
+ user-invocable: true
10
+ ---
11
+
12
+ # Corelayer CLI
13
+
14
+ Corelayer is an AI-powered error analysis and monitoring platform. The `corelayer` CLI lets you manage production issues, view root cause analyses, and triage errors directly from the terminal.
15
+
16
+ ## Prerequisites
17
+
18
+ The CLI must be installed and authenticated. Config lives at `~/.corelayer/config.json`.
19
+
20
+ If not logged in, run:
21
+
22
+ ```bash
23
+ corelayer login
24
+ ```
25
+
26
+ ## Quick reference
27
+
28
+ ### Check production health
29
+
30
+ ```bash
31
+ corelayer issues summary --group <groupId>
32
+ corelayer issues list --group <groupId>
33
+ corelayer issues list --severity critical
34
+ corelayer issues list --status open
35
+ ```
36
+
37
+ ### Inspect a specific issue
38
+
39
+ ```bash
40
+ corelayer issues get <issueId>
41
+ corelayer issues get <issueId> --json
42
+ ```
43
+
44
+ Returns: title, status, severity, event count, what happened, root cause, and next steps.
45
+
46
+ ### Triage issues
47
+
48
+ ```bash
49
+ corelayer issues close <issueId> --feedback "Fixed in PR #123"
50
+ corelayer issues reopen <issueId>
51
+ corelayer issues delete <issueId> --yes
52
+ ```
53
+
54
+ ### List groups and integrations
55
+
56
+ ```bash
57
+ corelayer groups list
58
+ corelayer integrations list --group <groupId>
59
+ ```
60
+
61
+ ### Configuration
62
+
63
+ ```bash
64
+ corelayer config set default-group <groupId>
65
+ corelayer config get
66
+ ```
67
+
68
+ Setting a default group avoids repeating `--group` on every command.
69
+
70
+ ## Global flags
71
+
72
+ | Flag | Purpose |
73
+ | ----------------- | -------------------------------------------------- |
74
+ | `--json` | Output as JSON (useful for piping/parsing) |
75
+ | `--quiet` / `-q` | Suppress non-error output |
76
+ | `--no-color` | Disable colored output |
77
+ | `--api-url <url>` | Override the configured API server for one command |
78
+
79
+ ## Command reference
80
+
81
+ ### `corelayer issues list [--group <id>] [--status <s>] [--severity <s>] [--limit <n>] [--page <n>]`
82
+
83
+ List issues for a group. Requires `--group` or a configured default group.
84
+ Filterable by status (`open`, `closed`) and severity (`critical`, `high`, `medium`, `low`).
85
+
86
+ ### `corelayer issues get <issueId>`
87
+
88
+ Get full details for an issue including AI-generated analysis: what happened, root cause, and next steps.
89
+
90
+ ### `corelayer issues summary [--group <id>]`
91
+
92
+ Returns a JSON severity breakdown for the group.
93
+
94
+ ### `corelayer issues close <issueId> [--feedback "..."]`
95
+
96
+ Close an issue. Optionally provide feedback (e.g., link to the fix).
97
+
98
+ ### `corelayer issues reopen <issueId>`
99
+
100
+ Reopen a previously closed issue.
101
+
102
+ ### `corelayer issues delete <issueId> [--yes]`
103
+
104
+ Soft-delete an issue. Prompts for confirmation unless `--yes` is passed.
105
+
106
+ ### `corelayer groups list`
107
+
108
+ List all groups in the organization.
109
+
110
+ ### `corelayer integrations list [--group <id>]`
111
+
112
+ List connected integration accounts and their resource counts.
113
+
114
+ ### `corelayer config get [key]`
115
+
116
+ Get config values. Keys: `api-url`, `default-group`. No key returns the full config.
117
+
118
+ ### `corelayer config set <key> <value>`
119
+
120
+ Set config values. Keys: `api-url`, `default-group`.
121
+
122
+ ### `corelayer login`
123
+
124
+ Authenticate via browser. Or use `corelayer login --code <CODE> --api-url <url>` for manual auth.
125
+
126
+ ### `corelayer logout`
127
+
128
+ Clear stored credentials.
129
+
130
+ ## Workflows
131
+
132
+ ### "What's happening in production?"
133
+
134
+ ```bash
135
+ corelayer issues summary
136
+ corelayer issues list --severity critical
137
+ corelayer issues get <id> --json
138
+ ```
139
+
140
+ ### Scripting and automation
141
+
142
+ Use `--json` and `--quiet` flags for machine-readable output:
143
+
144
+ ```bash
145
+ corelayer issues list --json --group <id> | jq '.issues[] | select(.severity == "critical")'
146
+ ```
147
+
148
+ ### Background agent usage
149
+
150
+ When running as a background agent, always use `--json` for structured output and `--quiet` to suppress interactive messages. Set `default-group` in config so group IDs don't need to be passed every time.