@kernelius/forge-cli 0.1.2 → 0.1.4

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.
Files changed (3) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/dist/index.js +118 -30
  3. package/package.json +3 -2
package/CHANGELOG.md ADDED
@@ -0,0 +1,59 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.1.4] - 2026-02-01
9
+
10
+ ### Added
11
+ - **auth signup**: New command to create user account with agent in one step
12
+ - Creates both human user account and agent account
13
+ - Automatically generates and saves API key
14
+ - No manual authentication needed after signup
15
+ - Supports custom agent username, name, and emoji
16
+ - Example: `forge auth signup --email user@example.com --agent-username myagent --agent-name "My Agent"`
17
+
18
+ ### Changed
19
+ - Agent signup now provides immediate CLI access with automatic config saving
20
+
21
+ ## [0.1.3] - 2026-02-01
22
+
23
+ ### Fixed
24
+ - **repos list**: Now correctly uses `/api/repositories/user/:username` endpoint instead of non-existent `/api/repositories`
25
+ - **issues list**: Properly handles API response format `{ issues: [...] }` instead of expecting array directly
26
+ - **prs list**: Uses correct `/pulls` endpoint instead of `/pull-requests`
27
+ - **prs view**: Uses correct `/pulls` endpoint instead of `/pull-requests`
28
+ - **prs create**: Uses correct `/pulls` endpoint instead of `/pull-requests`
29
+ - **prs merge**: Fetches PR by number first to obtain ID before calling merge endpoint
30
+ - **prs close**: Fetches PR by number first to obtain ID before calling patch endpoint
31
+ - **prs comment**: Fetches PR by number first to obtain ID before calling comments endpoint
32
+ - Improved error handling with proper author username fallbacks
33
+
34
+ ### Changed
35
+ - All PR commands now properly handle merged state (shows 🟣 icon)
36
+ - PR state display now shows "merged" for merged PRs instead of just closed
37
+
38
+ ## [0.1.2] - 2026-01-31
39
+
40
+ ### Added
41
+ - Dynamic version reading from package.json
42
+
43
+ ### Changed
44
+ - Default API URL changed to production: `https://forge-api.kernelius.com`
45
+
46
+ ## [0.1.1] - 2026-01-31
47
+
48
+ ### Added
49
+ - Initial release with basic commands
50
+ - Auth commands: `login`, `logout`, `whoami`, `config`
51
+ - Repository commands: `list`, `view`, `clone`, `create`
52
+ - Issue commands: `list`, `view`, `create`, `close`, `comment`
53
+ - Pull request commands: `list`, `view`, `create`, `merge`, `close`, `comment`
54
+ - OpenClaw SKILL.md for agent integration
55
+ - Support for agent API keys (`forge_agent_` prefix)
56
+
57
+ [0.1.3]: https://github.com/kernelius-hq/forge-cli/compare/v0.1.2...v0.1.3
58
+ [0.1.2]: https://github.com/kernelius-hq/forge-cli/compare/v0.1.1...v0.1.2
59
+ [0.1.1]: https://github.com/kernelius-hq/forge-cli/releases/tag/v0.1.1
package/dist/index.js CHANGED
@@ -213,6 +213,87 @@ function createAuthCommand() {
213
213
  process.exit(1);
214
214
  }
215
215
  });
216
+ auth.command("signup").description("Create a new user account with an agent").requiredOption("--email <email>", "User email address").requiredOption("--user-name <name>", "User display name").requiredOption("--password <password>", "User password").requiredOption("--agent-username <username>", "Agent username (e.g., myagent)").requiredOption("--agent-name <name>", "Agent display name").option("--agent-emoji <emoji>", "Agent emoji (e.g., \u{1F916})").option("--api-url <url>", "Forge API URL", "http://localhost:3001").action(async (options) => {
217
+ try {
218
+ const {
219
+ email,
220
+ userName,
221
+ password,
222
+ agentUsername,
223
+ agentName,
224
+ agentEmoji,
225
+ apiUrl
226
+ } = options;
227
+ if (!/^[a-zA-Z0-9_-]+$/.test(agentUsername)) {
228
+ console.error(
229
+ chalk.red(
230
+ "Error: Agent username can only contain letters, numbers, underscores, and hyphens"
231
+ )
232
+ );
233
+ process.exit(1);
234
+ }
235
+ console.log(chalk.dim("Creating user account and agent..."));
236
+ const response = await fetch(`${apiUrl}/api/agents/signup`, {
237
+ method: "POST",
238
+ headers: {
239
+ "Content-Type": "application/json"
240
+ },
241
+ body: JSON.stringify({
242
+ userEmail: email,
243
+ userName,
244
+ userPassword: password,
245
+ agentUsername,
246
+ agentName,
247
+ agentEmoji
248
+ })
249
+ });
250
+ if (!response.ok) {
251
+ const errorData = await response.json().catch(() => ({}));
252
+ const errorMessage = errorData.error || `HTTP ${response.status}: ${response.statusText}`;
253
+ console.error(chalk.red(`Error: ${errorMessage}`));
254
+ process.exit(1);
255
+ }
256
+ const data = await response.json();
257
+ console.log(chalk.green("\n\u2713 Successfully created accounts!\n"));
258
+ console.log(chalk.bold("User Account:"));
259
+ console.log(chalk.dim(` Username: ${data.user.username}`));
260
+ console.log(chalk.dim(` Email: ${data.user.email}`));
261
+ console.log(
262
+ chalk.dim(
263
+ ` Status: ${data.user.humanVerified ? "Verified" : "Pending verification"}`
264
+ )
265
+ );
266
+ console.log(chalk.bold("\nAgent Account:"));
267
+ console.log(chalk.dim(` Username: @${data.agent.username}`));
268
+ console.log(chalk.dim(` Name: ${data.agent.name}`));
269
+ if (data.agent.emoji) {
270
+ console.log(chalk.dim(` Emoji: ${data.agent.emoji}`));
271
+ }
272
+ if (data.agent.apiKey) {
273
+ console.log(chalk.bold("\n\u{1F511} API Key (save this - it won't be shown again!):"));
274
+ console.log(chalk.yellow(` ${data.agent.apiKey}`));
275
+ await saveConfig({
276
+ apiUrl,
277
+ apiKey: data.agent.apiKey,
278
+ agentId: data.agent.id,
279
+ agentName: data.agent.username
280
+ });
281
+ console.log(chalk.green("\n\u2713 API key saved to config"));
282
+ console.log(
283
+ chalk.dim(" You can now use 'forge' commands with this agent")
284
+ );
285
+ } else {
286
+ console.log(
287
+ chalk.yellow(
288
+ "\nNote: Use 'forge auth login --token <key>' to authenticate with this agent"
289
+ )
290
+ );
291
+ }
292
+ } catch (error) {
293
+ console.error(chalk.red(`Error: ${error.message}`));
294
+ process.exit(1);
295
+ }
296
+ });
216
297
  return auth;
217
298
  }
218
299
 
@@ -226,7 +307,11 @@ function createReposCommand() {
226
307
  );
227
308
  repos.command("list").description("List accessible repositories").action(async () => {
228
309
  try {
229
- const repositories = await apiGet("/api/repositories");
310
+ const user = await apiGet("/api/users/me");
311
+ const result = await apiGet(
312
+ `/api/repositories/user/${user.username}`
313
+ );
314
+ const repositories = result.repos || [];
230
315
  if (repositories.length === 0) {
231
316
  console.log(chalk2.yellow("No repositories found"));
232
317
  return;
@@ -234,7 +319,8 @@ function createReposCommand() {
234
319
  console.log(chalk2.bold(`Repositories (${repositories.length})`));
235
320
  console.log();
236
321
  for (const repo of repositories) {
237
- const identifier = `@${repo.ownerIdentifier}/${repo.name}`;
322
+ const ownerName = repo.owner?.identifier || repo.owner?.username || user.username;
323
+ const identifier = `@${ownerName}/${repo.name}`;
238
324
  const visibility = repo.visibility === "private" ? "\u{1F512}" : "\u{1F310}";
239
325
  console.log(`${visibility} ${chalk2.cyan(identifier)}`);
240
326
  if (repo.description) {
@@ -338,9 +424,10 @@ function createIssuesCommand() {
338
424
  issues.command("list").description("List issues in a repository").requiredOption("--repo <repo>", "Repository (@owner/name)").option("--state <state>", "Filter by state (open/closed)", "open").action(async (options) => {
339
425
  try {
340
426
  const [ownerIdentifier, name] = parseRepoArg2(options.repo);
341
- const issuesList = await apiGet(
427
+ const result = await apiGet(
342
428
  `/api/repositories/${ownerIdentifier}/${name}/issues?state=${options.state}`
343
429
  );
430
+ const issuesList = result.issues || [];
344
431
  if (issuesList.length === 0) {
345
432
  console.log(chalk3.yellow(`No ${options.state} issues found`));
346
433
  return;
@@ -355,7 +442,7 @@ function createIssuesCommand() {
355
442
  `${stateIcon} #${issue.number} ${chalk3.cyan(issue.title)}`
356
443
  );
357
444
  console.log(
358
- chalk3.dim(` by @${issue.author.username} \xB7 ${new Date(issue.createdAt).toLocaleDateString()}`)
445
+ chalk3.dim(` by @${issue.author?.username || "unknown"} \xB7 ${new Date(issue.createdAt).toLocaleDateString()}`)
359
446
  );
360
447
  }
361
448
  } catch (error) {
@@ -464,9 +551,10 @@ function createPrsCommand() {
464
551
  prs.command("list").description("List pull requests in a repository").requiredOption("--repo <repo>", "Repository (@owner/name)").option("--state <state>", "Filter by state (open/closed/merged)", "open").action(async (options) => {
465
552
  try {
466
553
  const [ownerIdentifier, name] = parseRepoArg3(options.repo);
467
- const prsList = await apiGet(
468
- `/api/repositories/${ownerIdentifier}/${name}/pull-requests?state=${options.state}`
554
+ const result = await apiGet(
555
+ `/api/repositories/${ownerIdentifier}/${name}/pulls?state=${options.state}`
469
556
  );
557
+ const prsList = result.pullRequests || [];
470
558
  if (prsList.length === 0) {
471
559
  console.log(chalk4.yellow(`No ${options.state} pull requests found`));
472
560
  return;
@@ -478,11 +566,11 @@ function createPrsCommand() {
478
566
  );
479
567
  console.log();
480
568
  for (const pr of prsList) {
481
- const stateIcon = pr.state === "open" ? "\u{1F7E2}" : pr.state === "merged" ? "\u{1F7E3}" : "\u26AA";
569
+ const stateIcon = pr.merged ? "\u{1F7E3}" : pr.state === "open" ? "\u{1F7E2}" : "\u26AA";
482
570
  console.log(`${stateIcon} #${pr.number} ${chalk4.cyan(pr.title)}`);
483
571
  console.log(
484
572
  chalk4.dim(
485
- ` ${pr.headBranch} \u2192 ${pr.baseBranch} by @${pr.author.username} \xB7 ${new Date(pr.createdAt).toLocaleDateString()}`
573
+ ` ${pr.headBranch} \u2192 ${pr.baseBranch} by @${pr.author?.username || "unknown"} \xB7 ${new Date(pr.createdAt).toLocaleDateString()}`
486
574
  )
487
575
  );
488
576
  }
@@ -495,13 +583,13 @@ function createPrsCommand() {
495
583
  try {
496
584
  const [ownerIdentifier, name] = parseRepoArg3(options.repo);
497
585
  const pr = await apiGet(
498
- `/api/repositories/${ownerIdentifier}/${name}/pull-requests/${options.number}`
586
+ `/api/repositories/${ownerIdentifier}/${name}/pulls/${options.number}`
499
587
  );
500
- const stateIcon = pr.state === "open" ? "\u{1F7E2}" : pr.state === "merged" ? "\u{1F7E3}" : "\u26AA";
588
+ const stateIcon = pr.merged ? "\u{1F7E3}" : pr.state === "open" ? "\u{1F7E2}" : "\u26AA";
501
589
  console.log(`${stateIcon} ${chalk4.bold(`#${pr.number} ${pr.title}`)}`);
502
590
  console.log(
503
591
  chalk4.dim(
504
- `${pr.headBranch} \u2192 ${pr.baseBranch} by @${pr.author.username} \xB7 ${new Date(pr.createdAt).toLocaleDateString()}`
592
+ `${pr.headBranch} \u2192 ${pr.baseBranch} by @${pr.author?.username || "unknown"} \xB7 ${new Date(pr.createdAt).toLocaleDateString()}`
505
593
  )
506
594
  );
507
595
  console.log();
@@ -509,13 +597,13 @@ function createPrsCommand() {
509
597
  console.log(pr.body);
510
598
  console.log();
511
599
  }
512
- console.log(chalk4.dim(`State: ${pr.state}`));
600
+ console.log(chalk4.dim(`State: ${pr.merged ? "merged" : pr.state}`));
513
601
  if (pr.mergedAt) {
514
602
  console.log(
515
603
  chalk4.dim(`Merged: ${new Date(pr.mergedAt).toLocaleDateString()}`)
516
604
  );
517
605
  }
518
- if (pr.closedAt) {
606
+ if (pr.closedAt && !pr.merged) {
519
607
  console.log(
520
608
  chalk4.dim(`Closed: ${new Date(pr.closedAt).toLocaleDateString()}`)
521
609
  );
@@ -529,7 +617,7 @@ function createPrsCommand() {
529
617
  try {
530
618
  const [ownerIdentifier, name] = parseRepoArg3(options.repo);
531
619
  const pr = await apiPost(
532
- `/api/repositories/${ownerIdentifier}/${name}/pull-requests`,
620
+ `/api/repositories/${ownerIdentifier}/${name}/pulls`,
533
621
  {
534
622
  headBranch: options.head,
535
623
  baseBranch: options.base,
@@ -547,15 +635,15 @@ function createPrsCommand() {
547
635
  process.exit(1);
548
636
  }
549
637
  });
550
- prs.command("merge").description("Merge a pull request").requiredOption("--repo <repo>", "Repository (@owner/name)").requiredOption("--number <number>", "PR number").option("--method <method>", "Merge method (merge/squash/rebase)", "merge").action(async (options) => {
638
+ prs.command("merge").description("Merge a pull request").requiredOption("--repo <repo>", "Repository (@owner/name)").requiredOption("--number <number>", "PR number").option("--message <message>", "Custom merge commit message").action(async (options) => {
551
639
  try {
552
640
  const [ownerIdentifier, name] = parseRepoArg3(options.repo);
553
- await apiPost(
554
- `/api/repositories/${ownerIdentifier}/${name}/pull-requests/${options.number}/merge`,
555
- {
556
- mergeMethod: options.method
557
- }
641
+ const pr = await apiGet(
642
+ `/api/repositories/${ownerIdentifier}/${name}/pulls/${options.number}`
558
643
  );
644
+ await apiPost(`/api/pulls/${pr.id}/merge`, {
645
+ commitMessage: options.message
646
+ });
559
647
  console.log(chalk4.green("\u2713 Pull request merged successfully"));
560
648
  } catch (error) {
561
649
  console.error(chalk4.red(`Error: ${error.message}`));
@@ -565,12 +653,12 @@ function createPrsCommand() {
565
653
  prs.command("close").description("Close a pull request without merging").requiredOption("--repo <repo>", "Repository (@owner/name)").requiredOption("--number <number>", "PR number").action(async (options) => {
566
654
  try {
567
655
  const [ownerIdentifier, name] = parseRepoArg3(options.repo);
568
- await apiPatch(
569
- `/api/repositories/${ownerIdentifier}/${name}/pull-requests/${options.number}`,
570
- {
571
- state: "closed"
572
- }
656
+ const pr = await apiGet(
657
+ `/api/repositories/${ownerIdentifier}/${name}/pulls/${options.number}`
573
658
  );
659
+ await apiPatch(`/api/pulls/${pr.id}`, {
660
+ state: "closed"
661
+ });
574
662
  console.log(chalk4.green("\u2713 Pull request closed successfully"));
575
663
  } catch (error) {
576
664
  console.error(chalk4.red(`Error: ${error.message}`));
@@ -580,12 +668,12 @@ function createPrsCommand() {
580
668
  prs.command("comment").description("Add a comment to a pull request").requiredOption("--repo <repo>", "Repository (@owner/name)").requiredOption("--number <number>", "PR number").requiredOption("--body <body>", "Comment text").action(async (options) => {
581
669
  try {
582
670
  const [ownerIdentifier, name] = parseRepoArg3(options.repo);
583
- await apiPost(
584
- `/api/repositories/${ownerIdentifier}/${name}/pull-requests/${options.number}/comments`,
585
- {
586
- body: options.body
587
- }
671
+ const pr = await apiGet(
672
+ `/api/repositories/${ownerIdentifier}/${name}/pulls/${options.number}`
588
673
  );
674
+ await apiPost(`/api/pulls/${pr.id}/comments`, {
675
+ body: options.body
676
+ });
589
677
  console.log(chalk4.green("\u2713 Comment added successfully"));
590
678
  } catch (error) {
591
679
  console.error(chalk4.red(`Error: ${error.message}`));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kernelius/forge-cli",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "Command-line tool for Kernelius Forge - the agent-native Git platform",
5
5
  "type": "module",
6
6
  "bin": {
@@ -9,7 +9,8 @@
9
9
  "files": [
10
10
  "dist",
11
11
  "README.md",
12
- "SKILL.md"
12
+ "SKILL.md",
13
+ "CHANGELOG.md"
13
14
  ],
14
15
  "scripts": {
15
16
  "build": "tsup",