@rotorsoft/gent 1.15.3 → 1.17.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 +72 -42
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2221,7 +2221,7 @@ import { homedir } from "os";
|
|
|
2221
2221
|
// package.json
|
|
2222
2222
|
var package_default = {
|
|
2223
2223
|
name: "@rotorsoft/gent",
|
|
2224
|
-
version: "1.
|
|
2224
|
+
version: "1.17.0",
|
|
2225
2225
|
description: "AI-powered GitHub workflow CLI - leverage AI (Claude, Gemini, or Codex) to create tickets, implement features, and manage PRs",
|
|
2226
2226
|
keywords: [
|
|
2227
2227
|
"cli",
|
|
@@ -2646,6 +2646,7 @@ async function statusCommand() {
|
|
|
2646
2646
|
import { execa as execa4 } from "execa";
|
|
2647
2647
|
|
|
2648
2648
|
// src/tui/state.ts
|
|
2649
|
+
var envCache = null;
|
|
2649
2650
|
async function aggregateState() {
|
|
2650
2651
|
const isGitRepo = await checkGitRepo();
|
|
2651
2652
|
if (!isGitRepo) {
|
|
@@ -2675,16 +2676,20 @@ async function aggregateState() {
|
|
|
2675
2676
|
}
|
|
2676
2677
|
const config = loadConfig();
|
|
2677
2678
|
const workflowLabels = getWorkflowLabels(config);
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2679
|
+
if (!envCache) {
|
|
2680
|
+
const [ghAuth, aiAvail] = await Promise.all([
|
|
2681
|
+
checkGhAuth(),
|
|
2682
|
+
checkAIProvider(config.ai.provider)
|
|
2683
|
+
]);
|
|
2684
|
+
envCache = {
|
|
2685
|
+
isGhAuthenticated: ghAuth,
|
|
2686
|
+
isAIProviderAvailable: aiAvail,
|
|
2687
|
+
isPlaywrightAvailable: false
|
|
2688
|
+
// resolved below on first feature branch visit
|
|
2689
|
+
};
|
|
2690
|
+
}
|
|
2691
|
+
const { isGhAuthenticated, isAIProviderAvailable } = envCache;
|
|
2692
|
+
const [branch, isOnMain, uncommitted, baseBranch] = await Promise.all([
|
|
2688
2693
|
getCurrentBranch(),
|
|
2689
2694
|
isOnMainBranch(),
|
|
2690
2695
|
hasUncommittedChanges(),
|
|
@@ -2706,16 +2711,20 @@ async function aggregateState() {
|
|
|
2706
2711
|
let playwrightAvailable = false;
|
|
2707
2712
|
if (!isOnMain) {
|
|
2708
2713
|
const issueNumber = extractIssueNumber(branch);
|
|
2714
|
+
const needsPlaywrightCheck = !envCache.isPlaywrightAvailable;
|
|
2709
2715
|
const [issueResult, prResult, changedFiles, playwrightResult] = await Promise.all([
|
|
2710
2716
|
issueNumber ? getIssue(issueNumber).catch(() => null) : Promise.resolve(null),
|
|
2711
2717
|
getPrStatus().catch(() => null),
|
|
2712
2718
|
getChangedFiles(baseBranch),
|
|
2713
|
-
isPlaywrightAvailable()
|
|
2719
|
+
needsPlaywrightCheck ? isPlaywrightAvailable() : Promise.resolve(envCache.isPlaywrightAvailable)
|
|
2714
2720
|
]);
|
|
2715
2721
|
issue = issueResult;
|
|
2716
2722
|
pr = prResult;
|
|
2717
2723
|
uiChanges = hasUIChanges(changedFiles);
|
|
2718
2724
|
playwrightAvailable = playwrightResult;
|
|
2725
|
+
if (needsPlaywrightCheck) {
|
|
2726
|
+
envCache.isPlaywrightAvailable = playwrightResult;
|
|
2727
|
+
}
|
|
2719
2728
|
if (issue) {
|
|
2720
2729
|
if (issue.labels.includes(workflowLabels.ready)) {
|
|
2721
2730
|
workflowStatus = "ready";
|
|
@@ -2935,14 +2944,24 @@ function renderActionPanel(title, content) {
|
|
|
2935
2944
|
}
|
|
2936
2945
|
console.log(botRow(w));
|
|
2937
2946
|
}
|
|
2938
|
-
function renderSettingsTo(state, w, out) {
|
|
2947
|
+
function renderSettingsTo(state, w, out, versionCheck) {
|
|
2939
2948
|
const provider = getProviderDisplayName(state.config.ai.provider);
|
|
2940
2949
|
const provTag = state.isAIProviderAvailable ? chalk3.green(provider) : chalk3.red(provider);
|
|
2941
2950
|
const ghTag = state.isGhAuthenticated ? chalk3.green("authenticated") : chalk3.red("not authenticated");
|
|
2942
2951
|
out(row(chalk3.dim("Provider: ") + provTag, w));
|
|
2943
2952
|
out(row(chalk3.dim("GitHub: ") + ghTag, w));
|
|
2953
|
+
if (versionCheck?.updateAvailable && versionCheck.latestVersion) {
|
|
2954
|
+
out(
|
|
2955
|
+
row(
|
|
2956
|
+
chalk3.yellow(
|
|
2957
|
+
`Update available: ${versionCheck.currentVersion} \u2192 ${versionCheck.latestVersion}`
|
|
2958
|
+
) + chalk3.dim(' \u2014 run "npm install -g @rotorsoft/gent" to upgrade'),
|
|
2959
|
+
w
|
|
2960
|
+
)
|
|
2961
|
+
);
|
|
2962
|
+
}
|
|
2944
2963
|
}
|
|
2945
|
-
function buildDashboardLines(state, actions, hint, refreshing) {
|
|
2964
|
+
function buildDashboardLines(state, actions, hint, refreshing, versionCheck) {
|
|
2946
2965
|
const lines = [];
|
|
2947
2966
|
const out = (line) => lines.push(line);
|
|
2948
2967
|
const w = termWidth();
|
|
@@ -2950,7 +2969,7 @@ function buildDashboardLines(state, actions, hint, refreshing) {
|
|
|
2950
2969
|
const version2 = getVersion();
|
|
2951
2970
|
const titleLabel = `gent v${version2}`;
|
|
2952
2971
|
out(topRow(titleLabel, w));
|
|
2953
|
-
renderSettingsTo(state, w, out);
|
|
2972
|
+
renderSettingsTo(state, w, out, versionCheck);
|
|
2954
2973
|
if (!state.isGitRepo) {
|
|
2955
2974
|
out(row(chalk3.red("Not a git repository"), w));
|
|
2956
2975
|
out(row(chalk3.dim("Run gent init in a git repo to get started"), w));
|
|
@@ -3071,8 +3090,8 @@ function buildDashboardLines(state, actions, hint, refreshing) {
|
|
|
3071
3090
|
out(botRow(w));
|
|
3072
3091
|
return lines;
|
|
3073
3092
|
}
|
|
3074
|
-
function renderDashboard(state, actions, hint, refreshing) {
|
|
3075
|
-
const lines = buildDashboardLines(state, actions, hint, refreshing);
|
|
3093
|
+
function renderDashboard(state, actions, hint, refreshing, versionCheck) {
|
|
3094
|
+
const lines = buildDashboardLines(state, actions, hint, refreshing, versionCheck);
|
|
3076
3095
|
for (const line of lines) {
|
|
3077
3096
|
console.log(line);
|
|
3078
3097
|
}
|
|
@@ -3119,7 +3138,7 @@ function buildModalFrame(title, contentLines, footerText, width) {
|
|
|
3119
3138
|
function isSeparator(entry) {
|
|
3120
3139
|
return "separator" in entry;
|
|
3121
3140
|
}
|
|
3122
|
-
function buildSelectContent(items, selectedIndex, maxWidth) {
|
|
3141
|
+
function buildSelectContent(items, selectedIndex, maxWidth, currentIndex) {
|
|
3123
3142
|
const lines = [];
|
|
3124
3143
|
let selectableIdx = 0;
|
|
3125
3144
|
for (const item of items) {
|
|
@@ -3127,12 +3146,12 @@ function buildSelectContent(items, selectedIndex, maxWidth) {
|
|
|
3127
3146
|
lines.push(chalk4.dim(item.separator));
|
|
3128
3147
|
} else {
|
|
3129
3148
|
const isSelected = selectableIdx === selectedIndex;
|
|
3149
|
+
const isCurrent = currentIndex != null && selectableIdx === currentIndex;
|
|
3130
3150
|
const prefix = isSelected ? chalk4.cyan.bold("> ") : " ";
|
|
3131
3151
|
const bullet = chalk4.dim("\xB7 ");
|
|
3132
3152
|
const label = truncateAnsi(item.name, maxWidth - 4);
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
);
|
|
3153
|
+
const styledLabel = isSelected ? chalk4.bold(label) : isCurrent ? chalk4.cyan(label) : label;
|
|
3154
|
+
lines.push(prefix + bullet + styledLabel);
|
|
3136
3155
|
selectableIdx++;
|
|
3137
3156
|
}
|
|
3138
3157
|
}
|
|
@@ -3301,9 +3320,9 @@ async function showSelect(opts) {
|
|
|
3301
3320
|
const w = modalWidth();
|
|
3302
3321
|
const maxItems = selectableCount(opts.items);
|
|
3303
3322
|
if (maxItems === 0) return null;
|
|
3304
|
-
let selectedIndex = 0;
|
|
3323
|
+
let selectedIndex = opts.initialIndex ?? 0;
|
|
3305
3324
|
const render = () => {
|
|
3306
|
-
const content = buildSelectContent(opts.items, selectedIndex, w - 6);
|
|
3325
|
+
const content = buildSelectContent(opts.items, selectedIndex, w - 6, opts.currentIndex);
|
|
3307
3326
|
const footer = "\u2191\u2193 Navigate Enter Select Esc Cancel";
|
|
3308
3327
|
const lines = buildModalFrame(opts.title, content, footer, w);
|
|
3309
3328
|
renderOverlay(opts.dashboardLines, lines, w);
|
|
@@ -3706,6 +3725,8 @@ async function handleList(dashboardLines) {
|
|
|
3706
3725
|
const currentBranch = await getCurrentBranch();
|
|
3707
3726
|
const defaultBranch = await getDefaultBranch();
|
|
3708
3727
|
const items = [];
|
|
3728
|
+
let initialIndex = 0;
|
|
3729
|
+
let selectableIdx = 0;
|
|
3709
3730
|
const isMain = currentBranch === defaultBranch;
|
|
3710
3731
|
const mainLabel = defaultBranch + (isMain ? " (current)" : "");
|
|
3711
3732
|
if (dirty && !isMain) {
|
|
@@ -3716,42 +3737,42 @@ async function handleList(dashboardLines) {
|
|
|
3716
3737
|
} else {
|
|
3717
3738
|
items.push({ name: mainLabel, value: "__main__" });
|
|
3718
3739
|
}
|
|
3740
|
+
if (isMain) initialIndex = selectableIdx;
|
|
3741
|
+
selectableIdx++;
|
|
3719
3742
|
const inProgressChoices = choices.filter(
|
|
3720
3743
|
(c) => c.category === "in-progress"
|
|
3721
3744
|
);
|
|
3722
3745
|
const openPrChoices = choices.filter((c) => c.category === "open-pr");
|
|
3723
3746
|
const readyChoices = choices.filter((c) => c.category === "ready");
|
|
3724
|
-
|
|
3725
|
-
|
|
3726
|
-
|
|
3747
|
+
const addChoices = (list) => {
|
|
3748
|
+
for (const c of list) {
|
|
3749
|
+
const isCurrent = c.branch === currentBranch;
|
|
3727
3750
|
items.push({
|
|
3728
3751
|
name: `#${c.issueNumber} ${c.title}`,
|
|
3729
3752
|
value: String(c.issueNumber)
|
|
3730
3753
|
});
|
|
3754
|
+
if (isCurrent) initialIndex = selectableIdx;
|
|
3755
|
+
selectableIdx++;
|
|
3731
3756
|
}
|
|
3757
|
+
};
|
|
3758
|
+
if (inProgressChoices.length > 0) {
|
|
3759
|
+
items.push({ separator: "\u2500\u2500 In Progress \u2500\u2500" });
|
|
3760
|
+
addChoices(inProgressChoices);
|
|
3732
3761
|
}
|
|
3733
3762
|
if (openPrChoices.length > 0) {
|
|
3734
3763
|
items.push({ separator: "\u2500\u2500 Open PRs \u2500\u2500" });
|
|
3735
|
-
|
|
3736
|
-
items.push({
|
|
3737
|
-
name: `#${c.issueNumber} ${c.title}`,
|
|
3738
|
-
value: String(c.issueNumber)
|
|
3739
|
-
});
|
|
3740
|
-
}
|
|
3764
|
+
addChoices(openPrChoices);
|
|
3741
3765
|
}
|
|
3742
3766
|
if (readyChoices.length > 0) {
|
|
3743
3767
|
items.push({ separator: "\u2500\u2500 Ready \u2500\u2500" });
|
|
3744
|
-
|
|
3745
|
-
items.push({
|
|
3746
|
-
name: `#${c.issueNumber} ${c.title}`,
|
|
3747
|
-
value: String(c.issueNumber)
|
|
3748
|
-
});
|
|
3749
|
-
}
|
|
3768
|
+
addChoices(readyChoices);
|
|
3750
3769
|
}
|
|
3751
3770
|
const selected = await showSelect({
|
|
3752
3771
|
title: "Switch Ticket",
|
|
3753
3772
|
items,
|
|
3754
|
-
dashboardLines
|
|
3773
|
+
dashboardLines,
|
|
3774
|
+
initialIndex,
|
|
3775
|
+
currentIndex: initialIndex
|
|
3755
3776
|
});
|
|
3756
3777
|
if (!selected) return false;
|
|
3757
3778
|
if (selected === "__main_disabled__") {
|
|
@@ -3828,10 +3849,12 @@ async function offerCreateBranch2(issueNumber, title, dashboardLines) {
|
|
|
3828
3849
|
await createBranch(branchName, defaultBranch);
|
|
3829
3850
|
return true;
|
|
3830
3851
|
}
|
|
3852
|
+
var VERSION_CHECK_INTERVAL_MS = 30 * 60 * 1e3;
|
|
3831
3853
|
async function tuiCommand() {
|
|
3832
3854
|
let running = true;
|
|
3833
3855
|
let lastActions = [];
|
|
3834
3856
|
let lastDashboardLines = [];
|
|
3857
|
+
let versionCheck = null;
|
|
3835
3858
|
const config = loadConfig();
|
|
3836
3859
|
let lastState = {
|
|
3837
3860
|
isGitRepo: true,
|
|
@@ -3856,14 +3879,21 @@ async function tuiCommand() {
|
|
|
3856
3879
|
isPlaywrightAvailable: false
|
|
3857
3880
|
};
|
|
3858
3881
|
let needsRefresh = true;
|
|
3882
|
+
let isFirstLoad = true;
|
|
3859
3883
|
while (running) {
|
|
3860
3884
|
if (needsRefresh) {
|
|
3861
3885
|
clearScreen();
|
|
3862
|
-
renderDashboard(lastState, lastActions, void 0, true);
|
|
3863
|
-
const
|
|
3886
|
+
renderDashboard(lastState, lastActions, void 0, true, versionCheck);
|
|
3887
|
+
const checkInterval = isFirstLoad ? 0 : VERSION_CHECK_INTERVAL_MS;
|
|
3888
|
+
isFirstLoad = false;
|
|
3889
|
+
const [state, versionResult] = await Promise.all([
|
|
3890
|
+
aggregateState(),
|
|
3891
|
+
checkForUpdates(checkInterval).catch(() => null)
|
|
3892
|
+
]);
|
|
3864
3893
|
const actions = getAvailableActions(state);
|
|
3865
3894
|
lastState = state;
|
|
3866
3895
|
lastActions = actions;
|
|
3896
|
+
if (versionResult) versionCheck = versionResult;
|
|
3867
3897
|
}
|
|
3868
3898
|
let hint;
|
|
3869
3899
|
if (lastState.isOnMain) {
|
|
@@ -3873,7 +3903,7 @@ async function tuiCommand() {
|
|
|
3873
3903
|
} else if (lastState.hasActionableFeedback) {
|
|
3874
3904
|
hint = "Review feedback needs attention";
|
|
3875
3905
|
}
|
|
3876
|
-
lastDashboardLines = buildDashboardLines(lastState, lastActions, hint);
|
|
3906
|
+
lastDashboardLines = buildDashboardLines(lastState, lastActions, hint, false, versionCheck);
|
|
3877
3907
|
clearScreen();
|
|
3878
3908
|
for (const line of lastDashboardLines) {
|
|
3879
3909
|
console.log(line);
|