@rotorsoft/gent 1.25.1 → 1.25.2

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
@@ -390,7 +390,7 @@ async function invokeAIInteractive(prompt, config, providerOverride) {
390
390
  };
391
391
  }
392
392
  case "gemini": {
393
- const args = prompt.trim() ? ["chat", prompt] : ["chat"];
393
+ const args = prompt.trim() ? [prompt] : [];
394
394
  return {
395
395
  result: execa2("gemini", args, {
396
396
  stdio: "inherit",
@@ -408,6 +408,45 @@ async function invokeAIInteractive(prompt, config, providerOverride) {
408
408
  }
409
409
  }
410
410
  }
411
+ async function runInteractiveSession(prompt, config, providerOverride) {
412
+ const provider = providerOverride ?? config.ai.provider;
413
+ const savedSigint = process.rawListeners("SIGINT").slice();
414
+ const savedSigterm = process.rawListeners("SIGTERM").slice();
415
+ process.removeAllListeners("SIGINT");
416
+ process.removeAllListeners("SIGTERM");
417
+ let signalCancelled = false;
418
+ const handler = () => {
419
+ signalCancelled = true;
420
+ };
421
+ process.on("SIGINT", handler);
422
+ process.on("SIGTERM", handler);
423
+ let exitCode;
424
+ try {
425
+ const { result } = await invokeAIInteractive(
426
+ prompt,
427
+ config,
428
+ providerOverride
429
+ );
430
+ try {
431
+ const r = await result;
432
+ exitCode = r.exitCode ?? void 0;
433
+ } catch (error) {
434
+ if (error && typeof error === "object" && "exitCode" in error) {
435
+ exitCode = error.exitCode;
436
+ }
437
+ }
438
+ } finally {
439
+ process.removeAllListeners("SIGINT");
440
+ process.removeAllListeners("SIGTERM");
441
+ for (const fn of savedSigint) {
442
+ process.on("SIGINT", fn);
443
+ }
444
+ for (const fn of savedSigterm) {
445
+ process.on("SIGTERM", fn);
446
+ }
447
+ }
448
+ return { exitCode, signalCancelled, provider };
449
+ }
411
450
  function getProviderDisplayName(provider) {
412
451
  switch (provider) {
413
452
  case "claude":
@@ -505,15 +544,16 @@ async function invokeGeminiInternal(options) {
505
544
  args.push(options.prompt);
506
545
  if (options.printOutput) {
507
546
  const subprocess = execa2("gemini", args, {
508
- stdio: "inherit"
547
+ stdio: ["ignore", "inherit", "inherit"]
509
548
  });
510
549
  await subprocess;
511
550
  return "";
512
551
  } else if (options.streamOutput) {
513
552
  return new Promise((resolve, reject) => {
514
553
  const child = spawn("gemini", args, {
515
- stdio: ["inherit", "pipe", "pipe"]
554
+ stdio: ["pipe", "pipe", "pipe"]
516
555
  });
556
+ child.stdin.end();
517
557
  let output = "";
518
558
  let firstData = true;
519
559
  child.stdout.on("data", (chunk) => {
@@ -1503,33 +1543,23 @@ async function runCommand(issueNumberArg, options) {
1503
1543
  const spinner = createSpinner(aiSpinnerText(providerName, "implement ticket"));
1504
1544
  spinner.start();
1505
1545
  const beforeSha = await getCurrentCommitSha();
1506
- let wasCancelled = false;
1507
- const handleSignal = () => {
1508
- wasCancelled = true;
1509
- };
1510
- process.on("SIGINT", handleSignal);
1511
- process.on("SIGTERM", handleSignal);
1512
1546
  spinner.stop();
1513
1547
  let aiExitCode;
1548
+ let wasCancelled = false;
1514
1549
  let usedProvider = provider;
1515
1550
  try {
1516
- const { result, provider: actualProvider } = await invokeAIInteractive(
1551
+ const session = await runInteractiveSession(
1517
1552
  prompt,
1518
1553
  config,
1519
1554
  options.provider
1520
1555
  );
1521
- usedProvider = actualProvider;
1522
- aiExitCode = result.exitCode ?? void 0;
1556
+ aiExitCode = session.exitCode;
1557
+ wasCancelled = session.signalCancelled;
1558
+ usedProvider = session.provider;
1523
1559
  } catch (error) {
1524
- if (error && typeof error === "object" && "exitCode" in error) {
1525
- aiExitCode = error.exitCode;
1526
- }
1527
1560
  logger.error(
1528
1561
  `${getProviderDisplayName(usedProvider)} session failed: ${error}`
1529
1562
  );
1530
- } finally {
1531
- process.off("SIGINT", handleSignal);
1532
- process.off("SIGTERM", handleSignal);
1533
1563
  }
1534
1564
  logger.newline();
1535
1565
  const commitsCreated = await hasNewCommits(beforeSha);
@@ -2142,29 +2172,19 @@ ${summary}`
2142
2172
  const spinner = createSpinner(aiSpinnerText(providerName, "apply fixes"));
2143
2173
  spinner.start();
2144
2174
  const beforeSha = await getCurrentCommitSha();
2145
- let wasCancelled = false;
2146
- const handleSignal = () => {
2147
- wasCancelled = true;
2148
- };
2149
- process.on("SIGINT", handleSignal);
2150
- process.on("SIGTERM", handleSignal);
2151
2175
  spinner.stop();
2152
2176
  let aiExitCode;
2177
+ let wasCancelled = false;
2153
2178
  try {
2154
- const { result } = await invokeAIInteractive(
2179
+ const session = await runInteractiveSession(
2155
2180
  prompt,
2156
2181
  config,
2157
2182
  options.provider
2158
2183
  );
2159
- aiExitCode = result.exitCode ?? void 0;
2184
+ aiExitCode = session.exitCode;
2185
+ wasCancelled = session.signalCancelled;
2160
2186
  } catch (error) {
2161
- if (error && typeof error === "object" && "exitCode" in error) {
2162
- aiExitCode = error.exitCode;
2163
- }
2164
2187
  logger.error(`${providerName} session failed: ${error}`);
2165
- } finally {
2166
- process.off("SIGINT", handleSignal);
2167
- process.off("SIGTERM", handleSignal);
2168
2188
  }
2169
2189
  logger.newline();
2170
2190
  if (wasCancelled) {
@@ -2233,7 +2253,7 @@ import { homedir } from "os";
2233
2253
  // package.json
2234
2254
  var package_default = {
2235
2255
  name: "@rotorsoft/gent",
2236
- version: "1.25.1",
2256
+ version: "1.25.2",
2237
2257
  description: "AI-powered GitHub workflow CLI - leverage AI (Claude, Gemini, or Codex) to create tickets, implement features, and manage PRs",
2238
2258
  keywords: [
2239
2259
  "cli",
@@ -3763,6 +3783,36 @@ async function executeAction(actionId, state, dashboardLines) {
3763
3783
  return SKIP_REFRESH;
3764
3784
  }
3765
3785
  }
3786
+ function drainStdin() {
3787
+ return new Promise((resolve) => {
3788
+ const { stdin } = process;
3789
+ if (!stdin.isTTY) {
3790
+ resolve();
3791
+ return;
3792
+ }
3793
+ stdin.setRawMode(true);
3794
+ stdin.resume();
3795
+ const discard = () => {
3796
+ };
3797
+ stdin.on("data", discard);
3798
+ setTimeout(() => {
3799
+ stdin.removeListener("data", discard);
3800
+ stdin.pause();
3801
+ stdin.setRawMode(false);
3802
+ resolve();
3803
+ }, 50);
3804
+ });
3805
+ }
3806
+ async function runAISession(prompt, config) {
3807
+ try {
3808
+ await runInteractiveSession(prompt, config);
3809
+ } finally {
3810
+ try {
3811
+ await drainStdin();
3812
+ } catch {
3813
+ }
3814
+ }
3815
+ }
3766
3816
  async function handleCommit(state, dashboardLines) {
3767
3817
  try {
3768
3818
  const { stdout: status } = await execa4("git", ["status", "--short"]);
@@ -3817,8 +3867,7 @@ Co-Authored-By: ${providerName} <${providerEmail}>`;
3817
3867
  ]);
3818
3868
  logger.newline();
3819
3869
  try {
3820
- const { result } = await invokeAIInteractive(prompt, state.config);
3821
- await result;
3870
+ await runAISession(prompt, state.config);
3822
3871
  return true;
3823
3872
  } catch (error) {
3824
3873
  logger.error(`${providerName} commit failed: ${error}`);
@@ -3869,8 +3918,7 @@ ${feedbackLines}`);
3869
3918
  ]);
3870
3919
  console.log();
3871
3920
  try {
3872
- const { result } = await invokeAIInteractive(prompt, state.config);
3873
- await result;
3921
+ await runAISession(prompt, state.config);
3874
3922
  } catch (error) {
3875
3923
  logger.error(`${providerName} session failed: ${error}`);
3876
3924
  }