@rotorsoft/gent 1.25.0 → 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,8 +390,12 @@ async function invokeAIInteractive(prompt, config, providerOverride) {
390
390
  };
391
391
  }
392
392
  case "gemini": {
393
+ const args = prompt.trim() ? [prompt] : [];
393
394
  return {
394
- result: execa2("gemini", ["-i", prompt], { stdio: "inherit" }),
395
+ result: execa2("gemini", args, {
396
+ stdio: "inherit",
397
+ env: buildGeminiInteractiveEnv()
398
+ }),
395
399
  provider
396
400
  };
397
401
  }
@@ -404,6 +408,45 @@ async function invokeAIInteractive(prompt, config, providerOverride) {
404
408
  }
405
409
  }
406
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
+ }
407
450
  function getProviderDisplayName(provider) {
408
451
  switch (provider) {
409
452
  case "claude":
@@ -437,6 +480,17 @@ function isRateLimitError(error, provider) {
437
480
  }
438
481
  return false;
439
482
  }
483
+ function buildGeminiInteractiveEnv() {
484
+ const env = { ...process.env };
485
+ delete env.CI;
486
+ delete env.CONTINUOUS_INTEGRATION;
487
+ for (const key of Object.keys(env)) {
488
+ if (key.startsWith("CI_")) {
489
+ delete env[key];
490
+ }
491
+ }
492
+ return env;
493
+ }
440
494
  async function invokeClaudeInternal(options) {
441
495
  const args = ["--print"];
442
496
  if (options.permissionMode) {
@@ -490,15 +544,16 @@ async function invokeGeminiInternal(options) {
490
544
  args.push(options.prompt);
491
545
  if (options.printOutput) {
492
546
  const subprocess = execa2("gemini", args, {
493
- stdio: "inherit"
547
+ stdio: ["ignore", "inherit", "inherit"]
494
548
  });
495
549
  await subprocess;
496
550
  return "";
497
551
  } else if (options.streamOutput) {
498
552
  return new Promise((resolve, reject) => {
499
553
  const child = spawn("gemini", args, {
500
- stdio: ["inherit", "pipe", "pipe"]
554
+ stdio: ["pipe", "pipe", "pipe"]
501
555
  });
556
+ child.stdin.end();
502
557
  let output = "";
503
558
  let firstData = true;
504
559
  child.stdout.on("data", (chunk) => {
@@ -1488,33 +1543,23 @@ async function runCommand(issueNumberArg, options) {
1488
1543
  const spinner = createSpinner(aiSpinnerText(providerName, "implement ticket"));
1489
1544
  spinner.start();
1490
1545
  const beforeSha = await getCurrentCommitSha();
1491
- let wasCancelled = false;
1492
- const handleSignal = () => {
1493
- wasCancelled = true;
1494
- };
1495
- process.on("SIGINT", handleSignal);
1496
- process.on("SIGTERM", handleSignal);
1497
1546
  spinner.stop();
1498
1547
  let aiExitCode;
1548
+ let wasCancelled = false;
1499
1549
  let usedProvider = provider;
1500
1550
  try {
1501
- const { result, provider: actualProvider } = await invokeAIInteractive(
1551
+ const session = await runInteractiveSession(
1502
1552
  prompt,
1503
1553
  config,
1504
1554
  options.provider
1505
1555
  );
1506
- usedProvider = actualProvider;
1507
- aiExitCode = result.exitCode ?? void 0;
1556
+ aiExitCode = session.exitCode;
1557
+ wasCancelled = session.signalCancelled;
1558
+ usedProvider = session.provider;
1508
1559
  } catch (error) {
1509
- if (error && typeof error === "object" && "exitCode" in error) {
1510
- aiExitCode = error.exitCode;
1511
- }
1512
1560
  logger.error(
1513
1561
  `${getProviderDisplayName(usedProvider)} session failed: ${error}`
1514
1562
  );
1515
- } finally {
1516
- process.off("SIGINT", handleSignal);
1517
- process.off("SIGTERM", handleSignal);
1518
1563
  }
1519
1564
  logger.newline();
1520
1565
  const commitsCreated = await hasNewCommits(beforeSha);
@@ -2127,29 +2172,19 @@ ${summary}`
2127
2172
  const spinner = createSpinner(aiSpinnerText(providerName, "apply fixes"));
2128
2173
  spinner.start();
2129
2174
  const beforeSha = await getCurrentCommitSha();
2130
- let wasCancelled = false;
2131
- const handleSignal = () => {
2132
- wasCancelled = true;
2133
- };
2134
- process.on("SIGINT", handleSignal);
2135
- process.on("SIGTERM", handleSignal);
2136
2175
  spinner.stop();
2137
2176
  let aiExitCode;
2177
+ let wasCancelled = false;
2138
2178
  try {
2139
- const { result } = await invokeAIInteractive(
2179
+ const session = await runInteractiveSession(
2140
2180
  prompt,
2141
2181
  config,
2142
2182
  options.provider
2143
2183
  );
2144
- aiExitCode = result.exitCode ?? void 0;
2184
+ aiExitCode = session.exitCode;
2185
+ wasCancelled = session.signalCancelled;
2145
2186
  } catch (error) {
2146
- if (error && typeof error === "object" && "exitCode" in error) {
2147
- aiExitCode = error.exitCode;
2148
- }
2149
2187
  logger.error(`${providerName} session failed: ${error}`);
2150
- } finally {
2151
- process.off("SIGINT", handleSignal);
2152
- process.off("SIGTERM", handleSignal);
2153
2188
  }
2154
2189
  logger.newline();
2155
2190
  if (wasCancelled) {
@@ -2218,7 +2253,7 @@ import { homedir } from "os";
2218
2253
  // package.json
2219
2254
  var package_default = {
2220
2255
  name: "@rotorsoft/gent",
2221
- version: "1.25.0",
2256
+ version: "1.25.2",
2222
2257
  description: "AI-powered GitHub workflow CLI - leverage AI (Claude, Gemini, or Codex) to create tickets, implement features, and manage PRs",
2223
2258
  keywords: [
2224
2259
  "cli",
@@ -3748,6 +3783,36 @@ async function executeAction(actionId, state, dashboardLines) {
3748
3783
  return SKIP_REFRESH;
3749
3784
  }
3750
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
+ }
3751
3816
  async function handleCommit(state, dashboardLines) {
3752
3817
  try {
3753
3818
  const { stdout: status } = await execa4("git", ["status", "--short"]);
@@ -3802,8 +3867,7 @@ Co-Authored-By: ${providerName} <${providerEmail}>`;
3802
3867
  ]);
3803
3868
  logger.newline();
3804
3869
  try {
3805
- const { result } = await invokeAIInteractive(prompt, state.config);
3806
- await result;
3870
+ await runAISession(prompt, state.config);
3807
3871
  return true;
3808
3872
  } catch (error) {
3809
3873
  logger.error(`${providerName} commit failed: ${error}`);
@@ -3854,8 +3918,7 @@ ${feedbackLines}`);
3854
3918
  ]);
3855
3919
  console.log();
3856
3920
  try {
3857
- const { result } = await invokeAIInteractive(prompt, state.config);
3858
- await result;
3921
+ await runAISession(prompt, state.config);
3859
3922
  } catch (error) {
3860
3923
  logger.error(`${providerName} session failed: ${error}`);
3861
3924
  }