@corelayer-ai/cli 0.1.0 → 0.2.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.
@@ -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}\nClaude Code will now automatically use the Corelayer CLI for production tasks.\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
+ }
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);
@@ -19,6 +20,8 @@ Usage:
19
20
  corelayer groups list
20
21
  corelayer integrations list [--group <groupId>]
21
22
  corelayer config <get|set> ...
23
+ corelayer install-skill (install Claude Code skill)
24
+ corelayer uninstall-skill (remove Claude Code skill)
22
25
 
23
26
  Global flags:
24
27
  --json
@@ -102,6 +105,12 @@ async function main() {
102
105
  case "config":
103
106
  await runConfig(rest, ctx);
104
107
  return;
108
+ case "install-skill":
109
+ await runInstallSkill(rest, ctx);
110
+ return;
111
+ case "uninstall-skill":
112
+ await runUninstallSkill(rest, ctx);
113
+ return;
105
114
  default:
106
115
  process.stderr.write(`Unknown command: ${command}\n`);
107
116
  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
  }
@@ -0,0 +1,31 @@
1
+ import crypto from "node:crypto";
2
+ const MAX_CALLBACK_BODY_BYTES = 8 * 1024;
3
+ export function parseTrustedOrigin(authBaseUrl) {
4
+ const parsed = new URL(authBaseUrl);
5
+ return parsed.origin;
6
+ }
7
+ export function hashCliAuthCode(code) {
8
+ return crypto.createHash("sha256").update(code).digest("hex");
9
+ }
10
+ export async function readJsonBody(req) {
11
+ const chunks = [];
12
+ let total = 0;
13
+ for await (const chunk of req) {
14
+ const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
15
+ total += buffer.length;
16
+ if (total > MAX_CALLBACK_BODY_BYTES) {
17
+ throw new Error("Callback body is too large");
18
+ }
19
+ chunks.push(buffer);
20
+ }
21
+ const raw = Buffer.concat(chunks).toString("utf8").trim();
22
+ if (!raw) {
23
+ return {};
24
+ }
25
+ try {
26
+ return JSON.parse(raw);
27
+ }
28
+ catch {
29
+ throw new Error("Callback body is not valid JSON");
30
+ }
31
+ }
@@ -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();
package/package.json CHANGED
@@ -1,13 +1,14 @@
1
1
  {
2
2
  "name": "@corelayer-ai/cli",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
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.