@rotorsoft/gent 1.14.2 → 1.15.1

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
@@ -2150,7 +2150,7 @@ import { homedir } from "os";
2150
2150
  // package.json
2151
2151
  var package_default = {
2152
2152
  name: "@rotorsoft/gent",
2153
- version: "1.14.2",
2153
+ version: "1.15.1",
2154
2154
  description: "AI-powered GitHub workflow CLI - leverage AI (Claude, Gemini, or Codex) to create tickets, implement features, and manage PRs",
2155
2155
  keywords: [
2156
2156
  "cli",
@@ -2703,6 +2703,7 @@ function getAvailableActions(state) {
2703
2703
  if (state.isOnMain) {
2704
2704
  actions.push({ id: "create", label: "new", shortcut: "n" });
2705
2705
  actions.push({ id: "list", label: "list", shortcut: "l" });
2706
+ actions.push({ id: "refresh", label: "refresh", shortcut: "f" });
2706
2707
  actions.push({ id: "switch-provider", label: "ai", shortcut: "a" });
2707
2708
  actions.push({ id: "quit", label: "quit", shortcut: "q" });
2708
2709
  return actions;
@@ -2719,10 +2720,8 @@ function getAvailableActions(state) {
2719
2720
  if (state.issue && state.pr?.state !== "merged") {
2720
2721
  actions.push({ id: "run", label: "run", shortcut: "u" });
2721
2722
  }
2722
- if (state.pr && (state.pr.state === "merged" || state.pr.state === "closed")) {
2723
- actions.push({ id: "checkout-main", label: "main", shortcut: "m" });
2724
- }
2725
2723
  actions.push({ id: "list", label: "list", shortcut: "l" });
2724
+ actions.push({ id: "refresh", label: "refresh", shortcut: "f" });
2726
2725
  actions.push({ id: "switch-provider", label: "ai", shortcut: "a" });
2727
2726
  actions.push({ id: "quit", label: "quit", shortcut: "q" });
2728
2727
  return actions;
@@ -2880,58 +2879,38 @@ function buildDashboardLines(state, actions, hint, refreshing) {
2880
2879
  out(botRow(w));
2881
2880
  return lines;
2882
2881
  }
2883
- if (state.isOnMain) {
2884
- out(
2885
- row(
2886
- chalk3.magenta(state.branch) + chalk3.dim(" \xB7 ready to start new work"),
2887
- w
2888
- )
2889
- );
2890
- if (state.hasUncommittedChanges) {
2891
- out(row(chalk3.yellow("\u25CF uncommitted changes"), w));
2892
- }
2893
- if (hint) {
2894
- out(midRow("Hint", w));
2895
- out(row(chalk3.yellow(hint), w));
2896
- }
2897
- out(divRow(w));
2898
- if (refreshing) {
2899
- out(row(chalk3.yellow("Refreshing\u2026"), w));
2900
- } else {
2901
- for (const line of formatCommandBar(actions, w)) {
2902
- out(row(line, w));
2903
- }
2904
- }
2905
- out(botRow(w));
2906
- out("");
2907
- return lines;
2908
- }
2909
2882
  const section = (title) => {
2910
2883
  out(midRow(title, w));
2911
2884
  };
2912
- section("Ticket");
2913
- if (state.issue) {
2914
- out(
2915
- row(
2916
- chalk3.cyan(`#${state.issue.number}`) + " " + chalk3.bold(truncate(state.issue.title, descMax - 6)),
2917
- w
2918
- )
2919
- );
2920
- const desc = extractDescription(state.issue.body, descMax);
2921
- if (desc) out(row(chalk3.dim(desc), w));
2922
- const tags = [];
2923
- if (state.workflowStatus !== "none")
2924
- tags.push(workflowBadge(state.workflowStatus));
2925
- for (const prefix of ["type:", "priority:", "risk:", "area:"]) {
2926
- const l = state.issue.labels.find((x) => x.startsWith(prefix));
2927
- if (l) tags.push(chalk3.dim(l));
2928
- }
2929
- if (tags.length) out(row(tags.join(" "), w));
2930
- } else {
2931
- out(row(chalk3.dim("No linked issue"), w));
2885
+ if (state.issue || !state.isOnMain) {
2886
+ section("Ticket");
2887
+ if (state.issue) {
2888
+ out(
2889
+ row(
2890
+ chalk3.cyan(`#${state.issue.number}`) + " " + chalk3.bold(truncate(state.issue.title, descMax - 6)),
2891
+ w
2892
+ )
2893
+ );
2894
+ const desc = extractDescription(state.issue.body, descMax);
2895
+ if (desc) out(row(chalk3.dim(desc), w));
2896
+ const tags = [];
2897
+ if (state.workflowStatus !== "none")
2898
+ tags.push(workflowBadge(state.workflowStatus));
2899
+ for (const prefix of ["type:", "priority:", "risk:", "area:"]) {
2900
+ const l = state.issue.labels.find((x) => x.startsWith(prefix));
2901
+ if (l) tags.push(chalk3.dim(l));
2902
+ }
2903
+ if (tags.length) out(row(tags.join(" "), w));
2904
+ } else {
2905
+ out(row(chalk3.dim("No linked issue"), w));
2906
+ }
2932
2907
  }
2933
2908
  section("Branch");
2934
- out(row(chalk3.magenta(state.branch), w));
2909
+ let branchLine = chalk3.magenta(state.branch);
2910
+ if (state.isOnMain && !state.hasUncommittedChanges) {
2911
+ branchLine += chalk3.dim(" \xB7 ready to start new work");
2912
+ }
2913
+ out(row(branchLine, w));
2935
2914
  const bits = [];
2936
2915
  if (state.commits.length > 0)
2937
2916
  bits.push(chalk3.dim(`${state.commits.length} ahead`));
@@ -2941,48 +2920,52 @@ function buildDashboardLines(state, actions, hint, refreshing) {
2941
2920
  bits.push(chalk3.green("\u25CF synced"));
2942
2921
  }
2943
2922
  if (bits.length) out(row(bits.join(chalk3.dim(" \xB7 ")), w));
2944
- section("Pull Request");
2945
- if (state.pr) {
2946
- const titleText = state.pr.title ? " " + truncate(state.pr.title, descMax - 12) : "";
2947
- out(row(chalk3.cyan(`#${state.pr.number}`) + titleText, w));
2948
- out(
2949
- row(
2950
- prBadge(state.pr.state, state.pr.isDraft) + reviewBadge(state.pr.reviewDecision),
2951
- w
2952
- )
2953
- );
2954
- if (state.hasActionableFeedback) {
2955
- const n = state.reviewFeedback.length;
2923
+ if (state.pr || !state.isOnMain) {
2924
+ section("Pull Request");
2925
+ if (state.pr) {
2926
+ const titleText = state.pr.title ? " " + truncate(state.pr.title, descMax - 12) : "";
2927
+ out(row(chalk3.cyan(`#${state.pr.number}`) + titleText, w));
2956
2928
  out(
2957
2929
  row(
2958
- chalk3.yellow(`${n} actionable comment${n !== 1 ? "s" : ""} pending`),
2959
- w
2960
- )
2961
- );
2962
- }
2963
- if (state.hasUIChanges && state.isPlaywrightAvailable && state.config.video.enabled && state.pr.state === "open") {
2964
- out(
2965
- row(
2966
- chalk3.cyan("UI changes detected") + chalk3.dim(" \xB7 video capture available"),
2930
+ prBadge(state.pr.state, state.pr.isDraft) + reviewBadge(state.pr.reviewDecision),
2967
2931
  w
2968
2932
  )
2969
2933
  );
2934
+ if (state.hasActionableFeedback) {
2935
+ const n = state.reviewFeedback.length;
2936
+ out(
2937
+ row(
2938
+ chalk3.yellow(`${n} actionable comment${n !== 1 ? "s" : ""} pending`),
2939
+ w
2940
+ )
2941
+ );
2942
+ }
2943
+ if (state.hasUIChanges && state.isPlaywrightAvailable && state.config.video.enabled && state.pr.state === "open") {
2944
+ out(
2945
+ row(
2946
+ chalk3.cyan("UI changes detected") + chalk3.dim(" \xB7 video capture available"),
2947
+ w
2948
+ )
2949
+ );
2950
+ }
2951
+ out(row(chalk3.dim(state.pr.url), w));
2952
+ } else {
2953
+ out(row(chalk3.dim("No PR created"), w));
2970
2954
  }
2971
- out(row(chalk3.dim(state.pr.url), w));
2972
- } else {
2973
- out(row(chalk3.dim("No PR created"), w));
2974
2955
  }
2975
- section("Commits");
2976
- if (state.commits.length > 0) {
2977
- const max = 6;
2978
- for (const c of state.commits.slice(0, max)) {
2979
- out(row(c.substring(0, w - 5), w));
2980
- }
2981
- if (state.commits.length > max) {
2982
- out(row(chalk3.dim(`\u2026 and ${state.commits.length - max} more`), w));
2956
+ if (state.commits.length > 0 || !state.isOnMain) {
2957
+ section("Commits");
2958
+ if (state.commits.length > 0) {
2959
+ const max = 6;
2960
+ for (const c of state.commits.slice(0, max)) {
2961
+ out(row(c.substring(0, w - 5), w));
2962
+ }
2963
+ if (state.commits.length > max) {
2964
+ out(row(chalk3.dim(`\u2026 and ${state.commits.length - max} more`), w));
2965
+ }
2966
+ } else {
2967
+ out(row(chalk3.dim("No commits"), w));
2983
2968
  }
2984
- } else {
2985
- out(row(chalk3.dim("No commits"), w));
2986
2969
  }
2987
2970
  if (hint) {
2988
2971
  section("Hint");
@@ -3446,8 +3429,7 @@ async function executeAction(actionId, state, dashboardLines) {
3446
3429
  case "switch-provider":
3447
3430
  await handleSwitchProvider(state, dashboardLines);
3448
3431
  return SKIP_REFRESH;
3449
- case "checkout-main":
3450
- await handleCheckoutMain(dashboardLines);
3432
+ case "refresh":
3451
3433
  return CONTINUE;
3452
3434
  default:
3453
3435
  return SKIP_REFRESH;
@@ -3625,21 +3607,12 @@ async function handleSwitchProvider(state, dashboardLines) {
3625
3607
  setRuntimeProvider(provider);
3626
3608
  state.config.ai.provider = provider;
3627
3609
  }
3628
- async function handleCheckoutMain(dashboardLines) {
3629
- try {
3630
- showStatus("Switching", "Switching to main...", dashboardLines);
3631
- await execa4("git", ["checkout", "main"]);
3632
- await execa4("git", ["pull"]);
3633
- } catch (error) {
3634
- logger.error(`Checkout failed: ${error}`);
3635
- }
3636
- }
3637
3610
  async function handleList(dashboardLines) {
3638
3611
  try {
3639
3612
  showStatus("Loading", "Fetching tickets...", dashboardLines);
3640
3613
  const config = loadConfig();
3641
3614
  const workflowLabels = getWorkflowLabels(config);
3642
- const [inProgress, ready, prs, localBranches] = await Promise.all([
3615
+ const [inProgress, ready, prs, localBranches, dirty] = await Promise.all([
3643
3616
  listIssues({
3644
3617
  labels: [workflowLabels.inProgress],
3645
3618
  state: "open",
@@ -3651,7 +3624,8 @@ async function handleList(dashboardLines) {
3651
3624
  limit: 20
3652
3625
  }),
3653
3626
  listOpenPrs(30),
3654
- listLocalBranches()
3627
+ listLocalBranches(),
3628
+ hasUncommittedChanges()
3655
3629
  ]);
3656
3630
  sortByPriority(inProgress);
3657
3631
  sortByPriority(ready);
@@ -3659,8 +3633,16 @@ async function handleList(dashboardLines) {
3659
3633
  const currentBranch = await getCurrentBranch();
3660
3634
  const defaultBranch = await getDefaultBranch();
3661
3635
  const items = [];
3662
- const mainLabel = defaultBranch + (currentBranch === defaultBranch ? " (current)" : "");
3663
- items.push({ name: mainLabel, value: "__main__" });
3636
+ const isMain = currentBranch === defaultBranch;
3637
+ const mainLabel = defaultBranch + (isMain ? " (current)" : "");
3638
+ if (dirty && !isMain) {
3639
+ items.push({
3640
+ name: `${mainLabel} (disabled - uncommitted changes)`,
3641
+ value: "__main_disabled__"
3642
+ });
3643
+ } else {
3644
+ items.push({ name: mainLabel, value: "__main__" });
3645
+ }
3664
3646
  const inProgressChoices = choices.filter(
3665
3647
  (c) => c.category === "in-progress"
3666
3648
  );
@@ -3693,28 +3675,23 @@ async function handleList(dashboardLines) {
3693
3675
  });
3694
3676
  }
3695
3677
  }
3696
- if (choices.length === 0) {
3697
- showStatus("List", "No tickets found", dashboardLines);
3698
- await new Promise((r) => setTimeout(r, 1500));
3699
- return false;
3700
- }
3701
3678
  const selected = await showSelect({
3702
3679
  title: "Switch Ticket",
3703
3680
  items,
3704
3681
  dashboardLines
3705
3682
  });
3706
3683
  if (!selected) return false;
3684
+ if (selected === "__main_disabled__") {
3685
+ showStatus(
3686
+ "Uncommitted Changes",
3687
+ "Commit or stash changes before switching to main",
3688
+ dashboardLines
3689
+ );
3690
+ await new Promise((r) => setTimeout(r, 2e3));
3691
+ return false;
3692
+ }
3707
3693
  if (selected === "__main__") {
3708
3694
  if (currentBranch === defaultBranch) return false;
3709
- const dirty2 = await hasUncommittedChanges();
3710
- if (dirty2) {
3711
- const ok = await showConfirm({
3712
- title: "Uncommitted Changes",
3713
- message: "You have uncommitted changes. Continue?",
3714
- dashboardLines
3715
- });
3716
- if (!ok) return false;
3717
- }
3718
3695
  showStatus("Switching", `Switching to ${defaultBranch}...`, dashboardLines);
3719
3696
  await checkoutBranch(defaultBranch);
3720
3697
  return true;
@@ -3722,7 +3699,6 @@ async function handleList(dashboardLines) {
3722
3699
  const issueNumber = parseInt(selected, 10);
3723
3700
  const ticket = choices.find((c) => c.issueNumber === issueNumber);
3724
3701
  if (!ticket) return false;
3725
- const dirty = await hasUncommittedChanges();
3726
3702
  if (dirty) {
3727
3703
  const ok = await showConfirm({
3728
3704
  title: "Uncommitted Changes",