@corelayer-ai/cli 0.3.0 → 0.4.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.
@@ -1,6 +1,34 @@
1
1
  import { readConfig, updateConfig } from "../lib/config.js";
2
2
  import { fail, printJson } from "../lib/output.js";
3
+ function printHelp() {
4
+ process.stdout.write(`corelayer config - Read and write local CLI config
5
+
6
+ USAGE:
7
+ corelayer config <subcommand> [key] [value]
8
+
9
+ SUBCOMMANDS:
10
+ get [key] Print the full config, or one value
11
+ set <key> <value> Set a config value
12
+
13
+ KEYS:
14
+ api-url API server URL
15
+ default-group Default group ID for issues and integrations commands
16
+
17
+ OPTIONS:
18
+ --json Output as JSON (get only)
19
+ -h, --help Print help
20
+
21
+ EXAMPLES:
22
+ corelayer config get
23
+ corelayer config get api-url
24
+ corelayer config set default-group g_123
25
+ `);
26
+ }
3
27
  export async function runConfig(args, ctx) {
28
+ if (args.includes("--help") || args.includes("-h")) {
29
+ printHelp();
30
+ return;
31
+ }
4
32
  const sub = args[0];
5
33
  if (sub === "get") {
6
34
  const key = args[1];
@@ -1,7 +1,29 @@
1
1
  import { CorelayerClient } from "../lib/api-client.js";
2
2
  import { resolveToken, resolveApiUrl } from "../lib/config.js";
3
3
  import { fail, formatDate, printJson, printTable } from "../lib/output.js";
4
+ function printHelp() {
5
+ process.stdout.write(`corelayer groups - List and inspect groups
6
+
7
+ USAGE:
8
+ corelayer groups <subcommand> [options]
9
+
10
+ SUBCOMMANDS:
11
+ list List all groups you have access to
12
+
13
+ OPTIONS:
14
+ --json Output as JSON
15
+ -h, --help Print help
16
+
17
+ EXAMPLES:
18
+ corelayer groups list
19
+ corelayer groups list --json
20
+ `);
21
+ }
4
22
  export async function runGroups(args, ctx) {
23
+ if (args.includes("--help") || args.includes("-h")) {
24
+ printHelp();
25
+ return;
26
+ }
5
27
  const sub = args[0];
6
28
  if (sub !== "list") {
7
29
  fail("Usage: corelayer groups list");
@@ -13,7 +13,37 @@ function getSkillSourcePath() {
13
13
  const packageDir = dirname(distDir);
14
14
  return join(packageDir, "skill", "SKILL.md");
15
15
  }
16
- export async function runInstallSkill(_args, ctx) {
16
+ function printInstallHelp() {
17
+ process.stdout
18
+ .write(`corelayer install-skill - Install the Corelayer agent skill
19
+
20
+ USAGE:
21
+ corelayer install-skill
22
+
23
+ DESCRIPTION:
24
+ Copies the Corelayer skill into ~/.claude/skills/corelayer so coding agents
25
+ can use it. Run 'corelayer uninstall-skill' to remove it.
26
+
27
+ OPTIONS:
28
+ -h, --help Print help
29
+ `);
30
+ }
31
+ function printUninstallHelp() {
32
+ process.stdout
33
+ .write(`corelayer uninstall-skill - Remove the Corelayer agent skill
34
+
35
+ USAGE:
36
+ corelayer uninstall-skill
37
+
38
+ OPTIONS:
39
+ -h, --help Print help
40
+ `);
41
+ }
42
+ export async function runInstallSkill(args, ctx) {
43
+ if (args.includes("--help") || args.includes("-h")) {
44
+ printInstallHelp();
45
+ return;
46
+ }
17
47
  const source = getSkillSourcePath();
18
48
  if (!existsSync(source)) {
19
49
  fail("Skill file not found. The CLI package may be incomplete.");
@@ -26,7 +56,11 @@ export async function runInstallSkill(_args, ctx) {
26
56
  process.stdout.write(`Corelayer skill installed to ${targetFile}\n`);
27
57
  }
28
58
  }
29
- export async function runUninstallSkill(_args, ctx) {
59
+ export async function runUninstallSkill(args, ctx) {
60
+ if (args.includes("--help") || args.includes("-h")) {
61
+ printUninstallHelp();
62
+ return;
63
+ }
30
64
  const targetDir = getSkillTargetDir();
31
65
  const targetFile = join(targetDir, "SKILL.md");
32
66
  if (!existsSync(targetFile)) {
@@ -7,7 +7,30 @@ function readFlag(args, flag) {
7
7
  return undefined;
8
8
  return args[index + 1];
9
9
  }
10
+ function printHelp() {
11
+ process.stdout.write(`corelayer integrations - List integrations for a group
12
+
13
+ USAGE:
14
+ corelayer integrations <subcommand> [options]
15
+
16
+ SUBCOMMANDS:
17
+ list List integrations for a group
18
+
19
+ OPTIONS:
20
+ --group <id> Group ID (or set default-group via 'corelayer config set')
21
+ --json Output as JSON
22
+ -h, --help Print help
23
+
24
+ EXAMPLES:
25
+ corelayer integrations list --group g_123
26
+ corelayer integrations list --json
27
+ `);
28
+ }
10
29
  export async function runIntegrations(args, ctx) {
30
+ if (args.includes("--help") || args.includes("-h")) {
31
+ printHelp();
32
+ return;
33
+ }
11
34
  const sub = args[0];
12
35
  if (sub !== "list") {
13
36
  fail("Usage: corelayer integrations list [--group <groupId>]");
@@ -25,7 +25,9 @@ function removeFlags(args) {
25
25
  arg === "--severity" ||
26
26
  arg === "--limit" ||
27
27
  arg === "--page" ||
28
- arg === "--feedback") {
28
+ arg === "--feedback" ||
29
+ arg === "--last-seen-before" ||
30
+ arg === "--last-seen-before-or-on") {
29
31
  skipNext = true;
30
32
  continue;
31
33
  }
@@ -45,7 +47,49 @@ async function confirmDelete() {
45
47
  rl.close();
46
48
  return answer.trim().toLowerCase() === "y";
47
49
  }
50
+ function printHelp() {
51
+ process.stdout.write(`corelayer issues - List, inspect, and manage issues
52
+
53
+ USAGE:
54
+ corelayer issues <subcommand> [options]
55
+
56
+ SUBCOMMANDS:
57
+ list List issues in a group
58
+ get <id> Show details for one issue
59
+ summary Summarize issue health for a group
60
+ close <id> Close an issue (write operation)
61
+ bulk-close Close many issues in one request (write operation)
62
+ reopen <id> Reopen a closed issue (write operation)
63
+ delete <id> Delete an issue (write operation)
64
+
65
+ OPTIONS:
66
+ --group <id> Group ID (or set default-group via 'corelayer config set')
67
+ --status <status> Filter by status (list)
68
+ --severity <sev> Filter by severity (list)
69
+ --limit <n> Limit results (list)
70
+ --page <n> Page number (list)
71
+ --feedback <text> Feedback message (close)
72
+ --last-seen-before <date|preset>
73
+ Filter bulk-close to issues seen before a cutoff
74
+ --last-seen-before-or-on <date|preset>
75
+ Filter bulk-close to issues seen on or before a cutoff
76
+ --yes Skip confirmation prompt (delete)
77
+ --json Output as JSON
78
+ -h, --help Print help
79
+
80
+ EXAMPLES:
81
+ corelayer issues list --group g_123 --status Open
82
+ corelayer issues get i_456 --json
83
+ corelayer issues close i_456 --feedback "fixed in deploy 2026-04-07"
84
+ corelayer issues bulk-close --group g_123 --last-seen-before-or-on 14days --feedback "Not seen in two weeks"
85
+ corelayer issues delete i_456 --yes
86
+ `);
87
+ }
48
88
  export async function runIssues(args, ctx) {
89
+ if (args.includes("--help") || args.includes("-h")) {
90
+ printHelp();
91
+ return;
92
+ }
49
93
  const sub = args[0];
50
94
  const config = readConfig();
51
95
  const token = resolveToken();
@@ -123,6 +167,39 @@ export async function runIssues(args, ctx) {
123
167
  }
124
168
  return;
125
169
  }
170
+ if (sub === "bulk-close") {
171
+ const groupId = readFlag(args, "--group") || config.defaults?.group;
172
+ if (!groupId) {
173
+ fail("groupId is required. Use --group <groupId> or set default-group via corelayer config set default-group <groupId>");
174
+ }
175
+ const lastSeenBefore = readFlag(args, "--last-seen-before");
176
+ const lastSeenBeforeOrOn = readFlag(args, "--last-seen-before-or-on");
177
+ if (!lastSeenBefore && !lastSeenBeforeOrOn) {
178
+ fail('Usage: corelayer issues bulk-close --group <groupId> (--last-seen-before <date|preset> | --last-seen-before-or-on <date|preset>) [--feedback "..."]');
179
+ }
180
+ const feedback = readFlag(args, "--feedback");
181
+ const result = await client.bulkIssueAction({
182
+ action: "close",
183
+ groupId,
184
+ feedback,
185
+ filters: {
186
+ status: readFlag(args, "--status") || "Open",
187
+ severity: readFlag(args, "--severity"),
188
+ lastSeenBefore,
189
+ lastSeenBeforeOrOn,
190
+ },
191
+ });
192
+ if (ctx.json) {
193
+ printJson(result);
194
+ return;
195
+ }
196
+ process.stdout.write(`Bulk close complete. Closed ${result.affected} issues`);
197
+ if (result.skipped > 0) {
198
+ process.stdout.write(`, skipped ${result.skipped}`);
199
+ }
200
+ process.stdout.write(".\n");
201
+ return;
202
+ }
126
203
  if (sub === "reopen") {
127
204
  const issueId = commandArgs[0];
128
205
  if (!issueId) {
@@ -162,5 +239,5 @@ export async function runIssues(args, ctx) {
162
239
  printJson(summary);
163
240
  return;
164
241
  }
165
- fail("Usage: corelayer issues <list|get|close|reopen|delete|summary> [options]");
242
+ fail("Usage: corelayer issues <list|get|close|bulk-close|reopen|delete|summary> [options]");
166
243
  }
@@ -10,6 +10,26 @@ function readFlag(args, flag) {
10
10
  function hasFlag(args, flag) {
11
11
  return args.includes(flag);
12
12
  }
13
+ function printHelp() {
14
+ process.stdout.write(`corelayer login - Authenticate with Corelayer
15
+
16
+ USAGE:
17
+ corelayer login Open browser to authenticate
18
+ corelayer login --code <code> --api-url <url> Complete login with an auth code
19
+ corelayer login --with-token --api-url <url> Save a token piped on stdin
20
+
21
+ OPTIONS:
22
+ --code <code> Auth code from the browser flow (manual login)
23
+ --with-token Read an API key from stdin and save it to config
24
+ --api-url <url> API server URL (required for --code and --with-token)
25
+ -h, --help Print help
26
+
27
+ EXAMPLES:
28
+ corelayer login
29
+ corelayer login --code abc123 --api-url https://api.corelayer.com
30
+ echo $CORELAYER_API_KEY | corelayer login --with-token --api-url https://api.corelayer.com
31
+ `);
32
+ }
13
33
  async function readStdin() {
14
34
  if (process.stdin.isTTY) {
15
35
  fail("No token provided on stdin. Usage: echo $CORELAYER_API_KEY | corelayer login --with-token");
@@ -21,6 +41,10 @@ async function readStdin() {
21
41
  return Buffer.concat(chunks).toString("utf8").trim();
22
42
  }
23
43
  export async function runLogin(args, ctx) {
44
+ if (hasFlag(args, "--help") || hasFlag(args, "-h")) {
45
+ printHelp();
46
+ return;
47
+ }
24
48
  const code = readFlag(args, "--code");
25
49
  const withToken = hasFlag(args, "--with-token");
26
50
  if (withToken) {
@@ -1,5 +1,19 @@
1
1
  import { clearToken } from "../lib/config.js";
2
- export async function runLogout(_args, ctx) {
2
+ function printHelp() {
3
+ process.stdout.write(`corelayer logout - Sign out and clear local credentials
4
+
5
+ USAGE:
6
+ corelayer logout
7
+
8
+ OPTIONS:
9
+ -h, --help Print help
10
+ `);
11
+ }
12
+ export async function runLogout(args, ctx) {
13
+ if (args.includes("--help") || args.includes("-h")) {
14
+ printHelp();
15
+ return;
16
+ }
3
17
  clearToken();
4
18
  if (!ctx.quiet) {
5
19
  process.stdout.write("Logged out.\n");
package/dist/index.js CHANGED
@@ -10,38 +10,36 @@ import { runLogout } from "./commands/logout.js";
10
10
  const require = createRequire(import.meta.url);
11
11
  const { version } = require("../package.json");
12
12
  function printHelp() {
13
- process.stdout.write(`Corelayer CLI
13
+ process.stdout
14
+ .write(`Corelayer CLI - Manage groups, issues, and integrations from your terminal
14
15
 
15
- Usage:
16
- corelayer login (opens browser)
17
- corelayer login --code <CODE> --api-url <url> (manual)
18
- corelayer login --with-token --api-url <url> (pipe token from stdin)
19
- corelayer logout
20
- corelayer issues <list|get|close|reopen|delete|summary> ...
21
- corelayer groups list
22
- corelayer integrations list [--group <groupId>]
23
- corelayer config <get|set> ...
24
- corelayer install-skill (install Corelayer skill)
25
- corelayer uninstall-skill (remove Corelayer skill)
16
+ USAGE:
17
+ corelayer [global options] <command> [command options]
26
18
 
27
- Global flags:
28
- --json
29
- --quiet, -q
30
- --api-url <url>
31
- --no-color
19
+ COMMANDS:
20
+ login Authenticate with Corelayer (opens browser)
21
+ logout Sign out and clear local credentials
22
+ groups List and inspect groups
23
+ issues List, inspect, and manage issues
24
+ integrations List integrations for a group
25
+ config Read and write local CLI config
26
+ install-skill Install the Corelayer agent skill
27
+ uninstall-skill Remove the Corelayer agent skill
32
28
 
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)
29
+ GLOBAL OPTIONS:
30
+ --json Output as JSON for scripting
31
+ -q, --quiet Suppress non-essential output
32
+ --api-url <url> Override the configured API server
33
+ --no-color Disable colored output
34
+ -h, --help Print help
35
+ -v, --version Print version
37
36
 
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>
37
+ ENVIRONMENT VARIABLES:
38
+ CORELAYER_API_KEY API key for non-interactive auth (skips login)
39
+ CORELAYER_API_URL Server URL (default: https://api.corelayer.com)
40
+ CORELAYER_AUTH_URL Auth server URL (default: https://app.corelayer.com)
42
41
 
43
- Save a token to config:
44
- echo $CORELAYER_API_KEY | corelayer login --with-token --api-url <url>
42
+ Run 'corelayer <command> --help' for more information on a command.
45
43
  `);
46
44
  }
47
45
  function parseGlobalFlags(argv) {
@@ -6,9 +6,14 @@ async function parseError(res) {
6
6
  catch {
7
7
  body = null;
8
8
  }
9
- return (body?.error ||
9
+ const message = body?.error ||
10
10
  body?.message ||
11
- `${res.status} ${res.statusText || "Request failed"}`);
11
+ `${res.status} ${res.statusText || "Request failed"}`;
12
+ const retryAfter = res.headers.get("retry-after");
13
+ if (res.status === 429 && retryAfter) {
14
+ return `${message}. Retry after ${retryAfter}s.`;
15
+ }
16
+ return message;
12
17
  }
13
18
  export class CorelayerClient {
14
19
  baseUrl;
@@ -65,6 +70,22 @@ export class CorelayerClient {
65
70
  feedback,
66
71
  });
67
72
  }
73
+ async bulkIssueAction(params) {
74
+ const filters = new URLSearchParams();
75
+ for (const [key, value] of Object.entries(params.filters || {})) {
76
+ if (value !== undefined && value !== null && value !== "") {
77
+ filters.set(key, String(value));
78
+ }
79
+ }
80
+ return this.request("POST", "/api/v1/issues/bulk", {
81
+ action: params.action,
82
+ groupId: params.groupId,
83
+ issueIds: params.issueIds,
84
+ selectAll: !params.issueIds || params.issueIds.length === 0,
85
+ filters: filters.toString() || undefined,
86
+ feedback: params.feedback,
87
+ });
88
+ }
68
89
  async reopenIssue(issueId) {
69
90
  await this.request("PATCH", `/api/v1/issues/${issueId}`, {
70
91
  status: "Open",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@corelayer-ai/cli",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Corelayer CLI",
5
5
  "type": "module",
6
6
  "bin": {
package/skill/SKILL.md CHANGED
@@ -47,6 +47,7 @@ Returns: title, status, severity, event count, what happened, root cause, and ne
47
47
 
48
48
  ```bash
49
49
  corelayer issues close <issueId> --feedback "Fixed in PR #123"
50
+ corelayer issues bulk-close --group <groupId> --last-seen-before-or-on 14days --feedback "Not seen in two weeks"
50
51
  corelayer issues reopen <issueId>
51
52
  corelayer issues delete <issueId> --yes
52
53
  ```
@@ -95,6 +96,10 @@ Returns a JSON severity breakdown for the group.
95
96
 
96
97
  Close an issue. Optionally provide feedback (e.g., link to the fix).
97
98
 
99
+ ### `corelayer issues bulk-close --group <id> (--last-seen-before <date|preset> | --last-seen-before-or-on <date|preset>) [--feedback "..."]`
100
+
101
+ Close many stale issues in one request. Presets such as `14days` are supported.
102
+
98
103
  ### `corelayer issues reopen <issueId>`
99
104
 
100
105
  Reopen a previously closed issue.