@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 +108 -21
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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.
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
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
|
|
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
|
|
3686
|
-
const { stdout: diffStat } = await
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
});
|