@rotorsoft/gent 1.18.0 → 1.20.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/index.js CHANGED
@@ -913,6 +913,27 @@ async function getAuthorInitials() {
913
913
  }
914
914
  return "dev";
915
915
  }
916
+ async function getRepoInfo() {
917
+ try {
918
+ const { stdout } = await execa2("git", [
919
+ "config",
920
+ "--get",
921
+ "remote.origin.url"
922
+ ]);
923
+ const url = stdout.trim();
924
+ const sshMatch = url.match(/git@github\.com:([^/]+)\/([^.]+)/);
925
+ if (sshMatch) {
926
+ return { owner: sshMatch[1], repo: sshMatch[2] };
927
+ }
928
+ const httpsMatch = url.match(/github\.com\/([^/]+)\/([^.]+)/);
929
+ if (httpsMatch) {
930
+ return { owner: httpsMatch[1], repo: httpsMatch[2] };
931
+ }
932
+ return null;
933
+ } catch {
934
+ return null;
935
+ }
936
+ }
916
937
  async function getCommitsSinceBase(base = "main") {
917
938
  try {
918
939
  const { stdout } = await execa2("git", [
@@ -2221,7 +2242,7 @@ import { homedir } from "os";
2221
2242
  // package.json
2222
2243
  var package_default = {
2223
2244
  name: "@rotorsoft/gent",
2224
- version: "1.18.0",
2245
+ version: "1.20.0",
2225
2246
  description: "AI-powered GitHub workflow CLI - leverage AI (Claude, Gemini, or Codex) to create tickets, implement features, and manage PRs",
2226
2247
  keywords: [
2227
2248
  "cli",
@@ -2643,7 +2664,7 @@ async function statusCommand() {
2643
2664
  }
2644
2665
 
2645
2666
  // src/commands/tui.ts
2646
- import { execa as execa4 } from "execa";
2667
+ import { execa as execa5 } from "execa";
2647
2668
 
2648
2669
  // src/tui/state.ts
2649
2670
  var envCache = null;
@@ -2671,7 +2692,8 @@ async function aggregateState() {
2671
2692
  reviewFeedback: [],
2672
2693
  hasActionableFeedback: false,
2673
2694
  hasUIChanges: false,
2674
- isPlaywrightAvailable: false
2695
+ isPlaywrightAvailable: false,
2696
+ hasValidRemote: false
2675
2697
  };
2676
2698
  }
2677
2699
  const config = loadConfig();
@@ -2689,11 +2711,12 @@ async function aggregateState() {
2689
2711
  };
2690
2712
  }
2691
2713
  const { isGhAuthenticated, isAIProviderAvailable } = envCache;
2692
- const [branch, isOnMain, uncommitted, baseBranch] = await Promise.all([
2714
+ const [branch, isOnMain, uncommitted, baseBranch, repoInfo] = await Promise.all([
2693
2715
  getCurrentBranch(),
2694
2716
  isOnMainBranch(),
2695
2717
  hasUncommittedChanges(),
2696
- getDefaultBranch()
2718
+ getDefaultBranch(),
2719
+ getRepoInfo()
2697
2720
  ]);
2698
2721
  const hasConfig = configExists();
2699
2722
  const hasProgress = progressExists(config);
@@ -2769,7 +2792,8 @@ async function aggregateState() {
2769
2792
  reviewFeedback,
2770
2793
  hasActionableFeedback,
2771
2794
  hasUIChanges: uiChanges,
2772
- isPlaywrightAvailable: playwrightAvailable
2795
+ isPlaywrightAvailable: playwrightAvailable,
2796
+ hasValidRemote: repoInfo !== null
2773
2797
  };
2774
2798
  }
2775
2799
 
@@ -2780,7 +2804,9 @@ function getAvailableActions(state) {
2780
2804
  actions.push({ id: "quit", label: "quit", shortcut: "q" });
2781
2805
  return actions;
2782
2806
  }
2783
- actions.push({ id: "create", label: "new", shortcut: "n" });
2807
+ if (state.hasValidRemote) {
2808
+ actions.push({ id: "create", label: "new", shortcut: "n" });
2809
+ }
2784
2810
  if (!state.isOnMain) {
2785
2811
  if (state.hasUncommittedChanges) {
2786
2812
  actions.push({ id: "commit", label: "commit", shortcut: "c" });
@@ -2788,14 +2814,18 @@ function getAvailableActions(state) {
2788
2814
  if (state.hasUnpushedCommits && state.commits.length > 0) {
2789
2815
  actions.push({ id: "push", label: "push", shortcut: "s" });
2790
2816
  }
2791
- if (!state.pr && state.commits.length > 0) {
2817
+ if (state.hasValidRemote && !state.pr && state.commits.length > 0) {
2792
2818
  actions.push({ id: "pr", label: "pr", shortcut: "p" });
2793
2819
  }
2794
2820
  if (state.issue && state.pr?.state !== "merged") {
2795
2821
  actions.push({ id: "run", label: "run", shortcut: "r" });
2796
2822
  }
2797
2823
  }
2798
- actions.push({ id: "list", label: "list", shortcut: "l" });
2824
+ if (state.hasValidRemote) {
2825
+ actions.push({ id: "list", label: "list", shortcut: "l" });
2826
+ } else {
2827
+ actions.push({ id: "github-remote", label: "github", shortcut: "g" });
2828
+ }
2799
2829
  actions.push({ id: "refresh", label: "refresh", shortcut: "f" });
2800
2830
  actions.push({ id: "switch-provider", label: "ai", shortcut: "a" });
2801
2831
  actions.push({ id: "quit", label: "quit", shortcut: "q" });
@@ -3083,7 +3113,17 @@ function buildDashboardLines(state, actions, hint, refreshing, versionCheck) {
3083
3113
  out(row(chalk3.dim(" No commits"), w));
3084
3114
  }
3085
3115
  }
3086
- if (hint) {
3116
+ if (!state.hasValidRemote) {
3117
+ section("Hint");
3118
+ out(
3119
+ row(
3120
+ chalk3.yellow(
3121
+ "Press [g] to create a GitHub repo and push"
3122
+ ),
3123
+ w
3124
+ )
3125
+ );
3126
+ } else if (hint) {
3087
3127
  section("Hint");
3088
3128
  out(row(chalk3.yellow(hint), w));
3089
3129
  }
@@ -3596,6 +3636,38 @@ function showStatus(title, message, dashboardLines) {
3596
3636
  renderOverlay(dashboardLines, lines, w);
3597
3637
  }
3598
3638
 
3639
+ // src/commands/github-remote.ts
3640
+ import { execa as execa4 } from "execa";
3641
+ async function githubRemoteCommand() {
3642
+ const isAuthenticated = await checkGhAuth();
3643
+ if (!isAuthenticated) {
3644
+ logger.error("GitHub CLI is not authenticated. Run: gh auth login");
3645
+ return false;
3646
+ }
3647
+ try {
3648
+ try {
3649
+ const { stdout } = await execa4("git", [
3650
+ "config",
3651
+ "--get",
3652
+ "remote.origin.url"
3653
+ ]);
3654
+ if (stdout.trim()) {
3655
+ logger.error("Remote origin already exists: " + stdout.trim());
3656
+ return false;
3657
+ }
3658
+ } catch {
3659
+ }
3660
+ logger.info("Creating GitHub repository...");
3661
+ await execa4("gh", ["repo", "create", "--source=.", "--push", "--private"]);
3662
+ const branch = await getCurrentBranch();
3663
+ logger.success(`Repository created and ${branch} pushed to GitHub`);
3664
+ return true;
3665
+ } catch (error) {
3666
+ logger.error(`Failed to create remote: ${error}`);
3667
+ return false;
3668
+ }
3669
+ }
3670
+
3599
3671
  // src/commands/tui.ts
3600
3672
  var CANCEL = /* @__PURE__ */ Symbol("cancel");
3601
3673
  async function waitForKey(validKeys) {
@@ -3665,6 +3737,17 @@ async function executeAction(actionId, state, dashboardLines) {
3665
3737
  await handleRun(state);
3666
3738
  return CONTINUE;
3667
3739
  }
3740
+ case "github-remote": {
3741
+ const confirmed = await showConfirm({
3742
+ title: "Push to GitHub",
3743
+ message: "Create a private GitHub repo and push?",
3744
+ dashboardLines
3745
+ });
3746
+ if (!confirmed) return SKIP_REFRESH;
3747
+ showStatus("Pushing", "Creating GitHub repo and pushing...", dashboardLines);
3748
+ await githubRemoteCommand();
3749
+ return CONTINUE;
3750
+ }
3668
3751
  case "switch-provider":
3669
3752
  await handleSwitchProvider(state, dashboardLines);
3670
3753
  return SKIP_REFRESH;
@@ -3676,19 +3759,19 @@ async function executeAction(actionId, state, dashboardLines) {
3676
3759
  }
3677
3760
  async function handleCommit(state, dashboardLines) {
3678
3761
  try {
3679
- const { stdout: status } = await execa4("git", ["status", "--short"]);
3762
+ const { stdout: status } = await execa5("git", ["status", "--short"]);
3680
3763
  if (!status.trim()) {
3681
3764
  showStatus("Commit", "No changes to commit", dashboardLines);
3682
3765
  await new Promise((r) => setTimeout(r, 1500));
3683
3766
  return false;
3684
3767
  }
3685
- await execa4("git", ["add", "-A"]);
3686
- const { stdout: diffStat } = await execa4("git", [
3768
+ await execa5("git", ["add", "-A"]);
3769
+ const { stdout: diffStat } = await execa5("git", [
3687
3770
  "diff",
3688
3771
  "--cached",
3689
3772
  "--stat"
3690
3773
  ]);
3691
- const { stdout: diffPatch } = await execa4("git", ["diff", "--cached"]);
3774
+ const { stdout: diffPatch } = await execa5("git", ["diff", "--cached"]);
3692
3775
  const diffContent = (diffStat + "\n\n" + diffPatch).slice(0, 4e3);
3693
3776
  const issueNumber = state.issue?.number ?? null;
3694
3777
  const issueTitle = state.issue?.title ?? null;
@@ -3703,7 +3786,7 @@ async function handleCommit(state, dashboardLines) {
3703
3786
  dashboardLines
3704
3787
  });
3705
3788
  if (!mode) {
3706
- await execa4("git", ["reset", "HEAD"]);
3789
+ await execa5("git", ["reset", "HEAD"]);
3707
3790
  return false;
3708
3791
  }
3709
3792
  let message;
@@ -3725,7 +3808,7 @@ async function handleCommit(state, dashboardLines) {
3725
3808
  );
3726
3809
  }
3727
3810
  if (message === CANCEL) {
3728
- await execa4("git", ["reset", "HEAD"]);
3811
+ await execa5("git", ["reset", "HEAD"]);
3729
3812
  return false;
3730
3813
  }
3731
3814
  const confirmed = await showConfirm({
@@ -3734,7 +3817,7 @@ async function handleCommit(state, dashboardLines) {
3734
3817
  dashboardLines
3735
3818
  });
3736
3819
  if (!confirmed) {
3737
- await execa4("git", ["reset", "HEAD"]);
3820
+ await execa5("git", ["reset", "HEAD"]);
3738
3821
  return false;
3739
3822
  }
3740
3823
  const providerEmail = getProviderEmail(provider);
@@ -3742,7 +3825,7 @@ async function handleCommit(state, dashboardLines) {
3742
3825
 
3743
3826
  Co-Authored-By: ${providerName} <${providerEmail}>`;
3744
3827
  showStatus("Committing", "Committing changes...", dashboardLines);
3745
- await execa4("git", ["commit", "-m", fullMessage]);
3828
+ await execa5("git", ["commit", "-m", fullMessage]);
3746
3829
  return true;
3747
3830
  } catch (error) {
3748
3831
  logger.error(`Commit failed: ${error}`);
@@ -3822,10 +3905,10 @@ ${feedbackLines}`);
3822
3905
  }
3823
3906
  async function handlePush(dashboardLines) {
3824
3907
  try {
3825
- const { stdout: branch } = await execa4("git", ["branch", "--show-current"]);
3908
+ const { stdout: branch } = await execa5("git", ["branch", "--show-current"]);
3826
3909
  const branchName = branch.trim();
3827
3910
  showStatus("Pushing", `Pushing ${branchName} to remote...`, dashboardLines);
3828
- await execa4("git", ["push", "-u", "origin", branchName]);
3911
+ await execa5("git", ["push", "-u", "origin", branchName]);
3829
3912
  } catch (error) {
3830
3913
  logger.error(`Push failed: ${error}`);
3831
3914
  }
@@ -4023,7 +4106,8 @@ async function tuiCommand() {
4023
4106
  reviewFeedback: [],
4024
4107
  hasActionableFeedback: false,
4025
4108
  hasUIChanges: false,
4026
- isPlaywrightAvailable: false
4109
+ isPlaywrightAvailable: false,
4110
+ hasValidRemote: true
4027
4111
  };
4028
4112
  let needsRefresh = true;
4029
4113
  let isFirstLoad = true;
@@ -4139,6 +4223,9 @@ program.command("fix").description("Apply PR review feedback using AI").option(
4139
4223
  program.command("status").description("Show current workflow status").action(async () => {
4140
4224
  await statusCommand();
4141
4225
  });
4226
+ program.command("github-remote").description("Create a GitHub repository and push local repo").action(async () => {
4227
+ await githubRemoteCommand();
4228
+ });
4142
4229
  program.command("ui").description("Launch interactive dashboard").action(async () => {
4143
4230
  await tuiCommand();
4144
4231
  });