@rotorsoft/gent 1.24.0 → 1.24.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
@@ -334,6 +334,7 @@ import chalk from "chalk";
334
334
  // src/lib/ai-provider.ts
335
335
  import { spawn } from "child_process";
336
336
  import { execa as execa2 } from "execa";
337
+ var AI_DEFAULT_TIMEOUT_MS = 3e4;
337
338
  async function getOtherAvailableProviders(currentProvider) {
338
339
  const allProviders = ["claude", "gemini", "codex"];
339
340
  const others = allProviders.filter((p) => p !== currentProvider);
@@ -479,7 +480,8 @@ async function invokeClaudeInternal(options) {
479
480
  child.on("error", reject);
480
481
  });
481
482
  } else {
482
- const { stdout } = await execa2("claude", args);
483
+ const timeout = options.timeout ?? AI_DEFAULT_TIMEOUT_MS;
484
+ const { stdout } = await execa2("claude", args, { timeout });
483
485
  return stdout;
484
486
  }
485
487
  }
@@ -523,7 +525,8 @@ async function invokeGeminiInternal(options) {
523
525
  child.on("error", reject);
524
526
  });
525
527
  } else {
526
- const { stdout } = await execa2("gemini", args);
528
+ const timeout = options.timeout ?? AI_DEFAULT_TIMEOUT_MS;
529
+ const { stdout } = await execa2("gemini", args, { timeout });
527
530
  return stdout;
528
531
  }
529
532
  }
@@ -566,7 +569,8 @@ async function invokeCodexInternal(options) {
566
569
  child.on("error", reject);
567
570
  });
568
571
  } else {
569
- const { stdout } = await execa2("codex", args);
572
+ const timeout = options.timeout ?? AI_DEFAULT_TIMEOUT_MS;
573
+ const { stdout } = await execa2("codex", args, { timeout });
570
574
  return stdout;
571
575
  }
572
576
  }
@@ -694,21 +698,27 @@ ${issue ? `Closes #${issue.number}` : ""}
694
698
 
695
699
  Only output the PR description, nothing else.`;
696
700
  }
697
- function buildCommitMessagePrompt(diff, issueNumber, issueTitle) {
698
- const issueContext = issueNumber ? `
699
- Related Issue: #${issueNumber}${issueTitle ? ` - ${issueTitle}` : ""}
700
- ` : "";
701
- return `Generate a concise git commit message for the following changes.
702
- ${issueContext}
703
- ## Diff
704
- ${diff}
701
+ function buildCommitPrompt(issueNumber, issueTitle, config) {
702
+ const provider = config.ai.provider;
703
+ const providerName = getProviderDisplayName(provider);
704
+ const providerEmail = getProviderEmail(provider);
705
+ const issueContext = issueNumber ? `Related Issue: #${issueNumber}${issueTitle ? ` - ${issueTitle}` : ""}` : "No linked issue";
706
+ return `Create a git commit for the staged changes.
707
+
708
+ Context: ${issueContext}
709
+
710
+ Steps:
711
+ 1. Run \`git diff --cached --stat\` to see what files changed
712
+ 2. Run \`git diff --cached\` to see the actual changes
713
+ 3. Generate a commit message following these rules:
714
+ - Use conventional commit format: <type>: <short description>
715
+ - Types: feat, fix, refactor, chore, docs, test, style, perf
716
+ - Keep the first line under 72 characters
717
+ - Add a blank line, then: Co-Authored-By: ${providerName} <${providerEmail}>
718
+ 4. Run \`git commit -m "<your message>"\` to create the commit
719
+ 5. Exit when done
705
720
 
706
- Rules:
707
- - Use conventional commit format: <type>: <short description>
708
- - Types: feat, fix, refactor, chore, docs, test, style, perf
709
- - Keep the first line under 72 characters
710
- - Do NOT include a body or footer
711
- - Output ONLY the commit message, nothing else`;
721
+ Do not ask for confirmation - just create the commit.`;
712
722
  }
713
723
  function parseTicketMeta(output) {
714
724
  const metaMatch = output.match(
@@ -2206,7 +2216,7 @@ import { homedir } from "os";
2206
2216
  // package.json
2207
2217
  var package_default = {
2208
2218
  name: "@rotorsoft/gent",
2209
- version: "1.24.0",
2219
+ version: "1.24.1",
2210
2220
  description: "AI-powered GitHub workflow CLI - leverage AI (Claude, Gemini, or Codex) to create tickets, implement features, and manage PRs",
2211
2221
  keywords: [
2212
2222
  "cli",
@@ -3629,7 +3639,6 @@ function showStatus(title, message, dashboardLines) {
3629
3639
  }
3630
3640
 
3631
3641
  // src/commands/tui.ts
3632
- var CANCEL = /* @__PURE__ */ Symbol("cancel");
3633
3642
  async function waitForKey(validKeys) {
3634
3643
  return new Promise((resolve) => {
3635
3644
  const { stdin } = process;
@@ -3746,13 +3755,6 @@ async function handleCommit(state, dashboardLines) {
3746
3755
  return false;
3747
3756
  }
3748
3757
  await execa4("git", ["add", "-A"]);
3749
- const { stdout: diffStat } = await execa4("git", [
3750
- "diff",
3751
- "--cached",
3752
- "--stat"
3753
- ]);
3754
- const { stdout: diffPatch } = await execa4("git", ["diff", "--cached"]);
3755
- const diffContent = (diffStat + "\n\n" + diffPatch).slice(0, 4e3);
3756
3758
  const issueNumber = state.issue?.number ?? null;
3757
3759
  const issueTitle = state.issue?.title ?? null;
3758
3760
  const provider = state.config.ai.provider;
@@ -3769,75 +3771,45 @@ async function handleCommit(state, dashboardLines) {
3769
3771
  await execa4("git", ["reset", "HEAD"]);
3770
3772
  return false;
3771
3773
  }
3772
- let message;
3773
3774
  if (mode === "manual") {
3774
3775
  const input = await showInput({
3775
3776
  title: "Commit Message",
3776
3777
  label: "Enter commit message:",
3777
3778
  dashboardLines
3778
3779
  });
3779
- message = input || CANCEL;
3780
- } else {
3781
- showStatus("Generating", aiSpinnerText(providerName, "generate commit message"), dashboardLines);
3782
- message = await generateCommitMessage(
3783
- diffContent,
3784
- issueNumber,
3785
- issueTitle,
3786
- state,
3787
- dashboardLines
3788
- );
3789
- }
3790
- if (message === CANCEL) {
3791
- await execa4("git", ["reset", "HEAD"]);
3792
- return false;
3793
- }
3794
- const confirmed = await showConfirm({
3795
- title: "Commit",
3796
- message: `Message: ${message.length > 50 ? message.slice(0, 50) + "\u2026" : message}`,
3797
- dashboardLines
3798
- });
3799
- if (!confirmed) {
3800
- await execa4("git", ["reset", "HEAD"]);
3801
- return false;
3802
- }
3803
- const providerEmail = getProviderEmail(provider);
3804
- const fullMessage = `${message}
3780
+ if (!input) {
3781
+ await execa4("git", ["reset", "HEAD"]);
3782
+ return false;
3783
+ }
3784
+ const providerEmail = getProviderEmail(provider);
3785
+ const fullMessage = `${input}
3805
3786
 
3806
3787
  Co-Authored-By: ${providerName} <${providerEmail}>`;
3807
- showStatus("Committing", "Committing changes...", dashboardLines);
3808
- await execa4("git", ["commit", "-m", fullMessage]);
3809
- return true;
3788
+ showStatus("Committing", "Committing changes...", dashboardLines);
3789
+ await execa4("git", ["commit", "-m", fullMessage]);
3790
+ return true;
3791
+ } else {
3792
+ const prompt = buildCommitPrompt(issueNumber, issueTitle, state.config);
3793
+ clearScreen();
3794
+ renderActionPanel(`${providerName} Commit`, [
3795
+ "Creating commit for staged changes...",
3796
+ issueNumber ? `Related: #${issueNumber} ${issueTitle ?? ""}` : "No linked issue"
3797
+ ]);
3798
+ console.log();
3799
+ try {
3800
+ const { result } = await invokeAIInteractive(prompt, state.config);
3801
+ await result;
3802
+ return true;
3803
+ } catch (error) {
3804
+ logger.error(`${providerName} commit failed: ${error}`);
3805
+ return false;
3806
+ }
3807
+ }
3810
3808
  } catch (error) {
3811
3809
  logger.error(`Commit failed: ${error}`);
3812
3810
  return false;
3813
3811
  }
3814
3812
  }
3815
- async function generateCommitMessage(diffContent, issueNumber, issueTitle, state, dashboardLines) {
3816
- try {
3817
- const prompt = buildCommitMessagePrompt(
3818
- diffContent,
3819
- issueNumber,
3820
- issueTitle
3821
- );
3822
- const result = await invokeAI({ prompt, streamOutput: false }, state.config);
3823
- let message = result.output.trim().split("\n")[0].trim();
3824
- for (const q of ['"', "'", "`"]) {
3825
- if (message.startsWith(q) && message.endsWith(q)) {
3826
- message = message.slice(1, -1);
3827
- break;
3828
- }
3829
- }
3830
- message = message.replace(/^```\w*\s*/, "").replace(/\s*```$/, "");
3831
- return message;
3832
- } catch {
3833
- const input = await showInput({
3834
- title: "Commit Message",
3835
- label: "AI generation failed. Enter commit message:",
3836
- dashboardLines
3837
- });
3838
- return input || CANCEL;
3839
- }
3840
- }
3841
3813
  async function handleRun(state) {
3842
3814
  if (!state.issue) {
3843
3815
  logger.error("No linked issue found");