@rotorsoft/gent 1.15.2 → 1.16.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 +89 -57
- 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.16.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";
|
|
@@ -2797,6 +2806,23 @@ function getAvailableActions(state) {
|
|
|
2797
2806
|
import chalk3 from "chalk";
|
|
2798
2807
|
var stripAnsi = (str) => str.replace(/\x1b\[[0-9;]*m/g, "");
|
|
2799
2808
|
var visibleLen = (str) => stripAnsi(str).length;
|
|
2809
|
+
function truncateAnsi(text, max) {
|
|
2810
|
+
if (visibleLen(text) <= max) return text;
|
|
2811
|
+
let visible = 0;
|
|
2812
|
+
let i = 0;
|
|
2813
|
+
while (i < text.length && visible < max - 1) {
|
|
2814
|
+
if (text[i] === "\x1B") {
|
|
2815
|
+
const end = text.indexOf("m", i);
|
|
2816
|
+
if (end !== -1) {
|
|
2817
|
+
i = end + 1;
|
|
2818
|
+
continue;
|
|
2819
|
+
}
|
|
2820
|
+
}
|
|
2821
|
+
visible++;
|
|
2822
|
+
i++;
|
|
2823
|
+
}
|
|
2824
|
+
return text.slice(0, i) + "\x1B[0m\u2026";
|
|
2825
|
+
}
|
|
2800
2826
|
function termWidth() {
|
|
2801
2827
|
return Math.min(process.stdout.columns || 80, 90);
|
|
2802
2828
|
}
|
|
@@ -2836,8 +2862,9 @@ function botRow(w) {
|
|
|
2836
2862
|
}
|
|
2837
2863
|
function row(text, w) {
|
|
2838
2864
|
const inner = w - 4;
|
|
2839
|
-
const
|
|
2840
|
-
|
|
2865
|
+
const fitted = truncateAnsi(text, inner);
|
|
2866
|
+
const pad = Math.max(0, inner - visibleLen(fitted));
|
|
2867
|
+
return chalk3.dim("\u2502") + " " + fitted + " ".repeat(pad) + " " + chalk3.dim("\u2502");
|
|
2841
2868
|
}
|
|
2842
2869
|
function workflowBadge(status) {
|
|
2843
2870
|
switch (status) {
|
|
@@ -2917,14 +2944,24 @@ function renderActionPanel(title, content) {
|
|
|
2917
2944
|
}
|
|
2918
2945
|
console.log(botRow(w));
|
|
2919
2946
|
}
|
|
2920
|
-
function renderSettingsTo(state, w, out) {
|
|
2947
|
+
function renderSettingsTo(state, w, out, versionCheck) {
|
|
2921
2948
|
const provider = getProviderDisplayName(state.config.ai.provider);
|
|
2922
2949
|
const provTag = state.isAIProviderAvailable ? chalk3.green(provider) : chalk3.red(provider);
|
|
2923
2950
|
const ghTag = state.isGhAuthenticated ? chalk3.green("authenticated") : chalk3.red("not authenticated");
|
|
2924
2951
|
out(row(chalk3.dim("Provider: ") + provTag, w));
|
|
2925
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
|
+
}
|
|
2926
2963
|
}
|
|
2927
|
-
function buildDashboardLines(state, actions, hint, refreshing) {
|
|
2964
|
+
function buildDashboardLines(state, actions, hint, refreshing, versionCheck) {
|
|
2928
2965
|
const lines = [];
|
|
2929
2966
|
const out = (line) => lines.push(line);
|
|
2930
2967
|
const w = termWidth();
|
|
@@ -2932,7 +2969,7 @@ function buildDashboardLines(state, actions, hint, refreshing) {
|
|
|
2932
2969
|
const version2 = getVersion();
|
|
2933
2970
|
const titleLabel = `gent v${version2}`;
|
|
2934
2971
|
out(topRow(titleLabel, w));
|
|
2935
|
-
renderSettingsTo(state, w, out);
|
|
2972
|
+
renderSettingsTo(state, w, out, versionCheck);
|
|
2936
2973
|
if (!state.isGitRepo) {
|
|
2937
2974
|
out(row(chalk3.red("Not a git repository"), w));
|
|
2938
2975
|
out(row(chalk3.dim("Run gent init in a git repo to get started"), w));
|
|
@@ -2953,12 +2990,12 @@ function buildDashboardLines(state, actions, hint, refreshing) {
|
|
|
2953
2990
|
if (state.issue) {
|
|
2954
2991
|
out(
|
|
2955
2992
|
row(
|
|
2956
|
-
chalk3.cyan(`#${state.issue.number}`) + "
|
|
2993
|
+
chalk3.dim("\xB7 ") + chalk3.cyan(`#${state.issue.number}`) + " " + chalk3.bold(state.issue.title),
|
|
2957
2994
|
w
|
|
2958
2995
|
)
|
|
2959
2996
|
);
|
|
2960
2997
|
const desc = extractDescription(state.issue.body, descMax);
|
|
2961
|
-
if (desc) out(row(chalk3.dim(desc), w));
|
|
2998
|
+
if (desc) out(row(" " + chalk3.dim(desc), w));
|
|
2962
2999
|
const tags = [];
|
|
2963
3000
|
if (state.workflowStatus !== "none")
|
|
2964
3001
|
tags.push(workflowBadge(state.workflowStatus));
|
|
@@ -2966,13 +3003,13 @@ function buildDashboardLines(state, actions, hint, refreshing) {
|
|
|
2966
3003
|
const l = state.issue.labels.find((x) => x.startsWith(prefix));
|
|
2967
3004
|
if (l) tags.push(chalk3.dim(l));
|
|
2968
3005
|
}
|
|
2969
|
-
if (tags.length) out(row(tags.join(" "), w));
|
|
3006
|
+
if (tags.length) out(row(" " + tags.join(" "), w));
|
|
2970
3007
|
} else {
|
|
2971
|
-
out(row(chalk3.dim("No linked issue"), w));
|
|
3008
|
+
out(row(chalk3.dim(" No linked issue"), w));
|
|
2972
3009
|
}
|
|
2973
3010
|
}
|
|
2974
3011
|
section("Branch");
|
|
2975
|
-
let branchLine = chalk3.magenta(state.branch);
|
|
3012
|
+
let branchLine = chalk3.dim("\xB7 ") + chalk3.magenta(state.branch);
|
|
2976
3013
|
if (state.isOnMain && !state.hasUncommittedChanges) {
|
|
2977
3014
|
branchLine += chalk3.dim(" \xB7 ready to start new work");
|
|
2978
3015
|
}
|
|
@@ -2985,15 +3022,20 @@ function buildDashboardLines(state, actions, hint, refreshing) {
|
|
|
2985
3022
|
if (!state.hasUncommittedChanges && !state.hasUnpushedCommits && state.commits.length > 0) {
|
|
2986
3023
|
bits.push(chalk3.green("\u25CF synced"));
|
|
2987
3024
|
}
|
|
2988
|
-
if (bits.length) out(row(bits.join(chalk3.dim(" \xB7 ")), w));
|
|
3025
|
+
if (bits.length) out(row(" " + bits.join(chalk3.dim(" \xB7 ")), w));
|
|
2989
3026
|
if (state.pr || !state.isOnMain) {
|
|
2990
3027
|
section("Pull Request");
|
|
2991
3028
|
if (state.pr) {
|
|
2992
|
-
const titleText = state.pr.title ? "
|
|
2993
|
-
out(
|
|
3029
|
+
const titleText = state.pr.title ? " " + state.pr.title : "";
|
|
3030
|
+
out(
|
|
3031
|
+
row(
|
|
3032
|
+
chalk3.dim("\xB7 ") + chalk3.cyan(`#${state.pr.number}`) + titleText,
|
|
3033
|
+
w
|
|
3034
|
+
)
|
|
3035
|
+
);
|
|
2994
3036
|
out(
|
|
2995
3037
|
row(
|
|
2996
|
-
prBadge(state.pr.state, state.pr.isDraft) + reviewBadge(state.pr.reviewDecision),
|
|
3038
|
+
" " + prBadge(state.pr.state, state.pr.isDraft) + reviewBadge(state.pr.reviewDecision),
|
|
2997
3039
|
w
|
|
2998
3040
|
)
|
|
2999
3041
|
);
|
|
@@ -3001,7 +3043,7 @@ function buildDashboardLines(state, actions, hint, refreshing) {
|
|
|
3001
3043
|
const n = state.reviewFeedback.length;
|
|
3002
3044
|
out(
|
|
3003
3045
|
row(
|
|
3004
|
-
chalk3.yellow(`${n} actionable comment${n !== 1 ? "s" : ""} pending`),
|
|
3046
|
+
" " + chalk3.yellow(`${n} actionable comment${n !== 1 ? "s" : ""} pending`),
|
|
3005
3047
|
w
|
|
3006
3048
|
)
|
|
3007
3049
|
);
|
|
@@ -3009,14 +3051,14 @@ function buildDashboardLines(state, actions, hint, refreshing) {
|
|
|
3009
3051
|
if (state.hasUIChanges && state.isPlaywrightAvailable && state.config.video.enabled && state.pr.state === "open") {
|
|
3010
3052
|
out(
|
|
3011
3053
|
row(
|
|
3012
|
-
chalk3.cyan("UI changes detected") + chalk3.dim(" \xB7 video capture available"),
|
|
3054
|
+
" " + chalk3.cyan("UI changes detected") + chalk3.dim(" \xB7 video capture available"),
|
|
3013
3055
|
w
|
|
3014
3056
|
)
|
|
3015
3057
|
);
|
|
3016
3058
|
}
|
|
3017
|
-
out(row(chalk3.dim(state.pr.url), w));
|
|
3059
|
+
out(row(" " + chalk3.dim(state.pr.url), w));
|
|
3018
3060
|
} else {
|
|
3019
|
-
out(row(chalk3.dim("No PR created"), w));
|
|
3061
|
+
out(row(chalk3.dim(" No PR created"), w));
|
|
3020
3062
|
}
|
|
3021
3063
|
}
|
|
3022
3064
|
if (state.commits.length > 0 || !state.isOnMain) {
|
|
@@ -3024,13 +3066,13 @@ function buildDashboardLines(state, actions, hint, refreshing) {
|
|
|
3024
3066
|
if (state.commits.length > 0) {
|
|
3025
3067
|
const max = 6;
|
|
3026
3068
|
for (const c of state.commits.slice(0, max)) {
|
|
3027
|
-
out(row(
|
|
3069
|
+
out(row(chalk3.dim("\xB7 ") + c, w));
|
|
3028
3070
|
}
|
|
3029
3071
|
if (state.commits.length > max) {
|
|
3030
|
-
out(row(chalk3.dim(
|
|
3072
|
+
out(row(chalk3.dim(` \u2026 and ${state.commits.length - max} more`), w));
|
|
3031
3073
|
}
|
|
3032
3074
|
} else {
|
|
3033
|
-
out(row(chalk3.dim("No commits"), w));
|
|
3075
|
+
out(row(chalk3.dim(" No commits"), w));
|
|
3034
3076
|
}
|
|
3035
3077
|
}
|
|
3036
3078
|
if (hint) {
|
|
@@ -3048,8 +3090,8 @@ function buildDashboardLines(state, actions, hint, refreshing) {
|
|
|
3048
3090
|
out(botRow(w));
|
|
3049
3091
|
return lines;
|
|
3050
3092
|
}
|
|
3051
|
-
function renderDashboard(state, actions, hint, refreshing) {
|
|
3052
|
-
const lines = buildDashboardLines(state, actions, hint, refreshing);
|
|
3093
|
+
function renderDashboard(state, actions, hint, refreshing, versionCheck) {
|
|
3094
|
+
const lines = buildDashboardLines(state, actions, hint, refreshing, versionCheck);
|
|
3053
3095
|
for (const line of lines) {
|
|
3054
3096
|
console.log(line);
|
|
3055
3097
|
}
|
|
@@ -3071,23 +3113,6 @@ function modalDivRow(w) {
|
|
|
3071
3113
|
function modalBotRow(w) {
|
|
3072
3114
|
return chalk4.bold("\u2514" + "\u2500".repeat(w - 2) + "\u2518");
|
|
3073
3115
|
}
|
|
3074
|
-
function truncateAnsi(text, max) {
|
|
3075
|
-
if (visibleLen(text) <= max) return text;
|
|
3076
|
-
let visible = 0;
|
|
3077
|
-
let i = 0;
|
|
3078
|
-
while (i < text.length && visible < max - 1) {
|
|
3079
|
-
if (text[i] === "\x1B") {
|
|
3080
|
-
const end = text.indexOf("m", i);
|
|
3081
|
-
if (end !== -1) {
|
|
3082
|
-
i = end + 1;
|
|
3083
|
-
continue;
|
|
3084
|
-
}
|
|
3085
|
-
}
|
|
3086
|
-
visible++;
|
|
3087
|
-
i++;
|
|
3088
|
-
}
|
|
3089
|
-
return text.slice(0, i) + "\x1B[0m\u2026";
|
|
3090
|
-
}
|
|
3091
3116
|
function modalRow(text, w) {
|
|
3092
3117
|
const inner = w - 4;
|
|
3093
3118
|
const fitted = truncateAnsi(text, inner);
|
|
@@ -3122,9 +3147,10 @@ function buildSelectContent(items, selectedIndex, maxWidth) {
|
|
|
3122
3147
|
} else {
|
|
3123
3148
|
const isSelected = selectableIdx === selectedIndex;
|
|
3124
3149
|
const prefix = isSelected ? chalk4.cyan.bold("> ") : " ";
|
|
3125
|
-
const
|
|
3150
|
+
const bullet = chalk4.dim("\xB7 ");
|
|
3151
|
+
const label = truncateAnsi(item.name, maxWidth - 4);
|
|
3126
3152
|
lines.push(
|
|
3127
|
-
prefix + (isSelected ? chalk4.bold(label) : label)
|
|
3153
|
+
prefix + bullet + (isSelected ? chalk4.bold(label) : label)
|
|
3128
3154
|
);
|
|
3129
3155
|
selectableIdx++;
|
|
3130
3156
|
}
|
|
@@ -3821,10 +3847,12 @@ async function offerCreateBranch2(issueNumber, title, dashboardLines) {
|
|
|
3821
3847
|
await createBranch(branchName, defaultBranch);
|
|
3822
3848
|
return true;
|
|
3823
3849
|
}
|
|
3850
|
+
var VERSION_CHECK_INTERVAL_MS = 30 * 60 * 1e3;
|
|
3824
3851
|
async function tuiCommand() {
|
|
3825
3852
|
let running = true;
|
|
3826
3853
|
let lastActions = [];
|
|
3827
3854
|
let lastDashboardLines = [];
|
|
3855
|
+
let versionCheck = null;
|
|
3828
3856
|
const config = loadConfig();
|
|
3829
3857
|
let lastState = {
|
|
3830
3858
|
isGitRepo: true,
|
|
@@ -3852,11 +3880,15 @@ async function tuiCommand() {
|
|
|
3852
3880
|
while (running) {
|
|
3853
3881
|
if (needsRefresh) {
|
|
3854
3882
|
clearScreen();
|
|
3855
|
-
renderDashboard(lastState, lastActions, void 0, true);
|
|
3856
|
-
const state = await
|
|
3883
|
+
renderDashboard(lastState, lastActions, void 0, true, versionCheck);
|
|
3884
|
+
const [state, versionResult] = await Promise.all([
|
|
3885
|
+
aggregateState(),
|
|
3886
|
+
checkForUpdates(VERSION_CHECK_INTERVAL_MS).catch(() => null)
|
|
3887
|
+
]);
|
|
3857
3888
|
const actions = getAvailableActions(state);
|
|
3858
3889
|
lastState = state;
|
|
3859
3890
|
lastActions = actions;
|
|
3891
|
+
if (versionResult) versionCheck = versionResult;
|
|
3860
3892
|
}
|
|
3861
3893
|
let hint;
|
|
3862
3894
|
if (lastState.isOnMain) {
|
|
@@ -3866,7 +3898,7 @@ async function tuiCommand() {
|
|
|
3866
3898
|
} else if (lastState.hasActionableFeedback) {
|
|
3867
3899
|
hint = "Review feedback needs attention";
|
|
3868
3900
|
}
|
|
3869
|
-
lastDashboardLines = buildDashboardLines(lastState, lastActions, hint);
|
|
3901
|
+
lastDashboardLines = buildDashboardLines(lastState, lastActions, hint, false, versionCheck);
|
|
3870
3902
|
clearScreen();
|
|
3871
3903
|
for (const line of lastDashboardLines) {
|
|
3872
3904
|
console.log(line);
|