Repository: google-gemini/gemini-cli (TypeScript)

Issue:
Git Bash on windows doesn't run the gemini command.
### What happened?

When trying to run ```gemini``` command in a git bash terminal on windows, nothing happens. The program doesn't start the auth process, nor does it seem to even operate. The terminal just gets stale and nothing happens. Running it as administrator doesn't help either.

<img width="756" height="455" alt="Image" src="https://github.com/user-attachments/assets/c26c37f6-c4bc-4313-a53b-420d307caf2e" />

### What did you expect to happen?

Authentication prompt, and theme selector to initiate.

### Client information

<details>

```console
CLI Version            0.1.13                                      │
│ Git Commit             412b78c5 (local modifications)              │
│ Model                  gemini-2.5-pro                              │
│ Sandbox                no sandbox                                  │
│ OS                     win32                                       │
│ Auth Method            OAuth
```

</details>


### Login information

_No response_

### Anything else we need to know?

_No response_


Discussion: Hey @jacob314, i have been thinking about how to solve this issue, this happens because when input is not TTY, piped input is expected but there is no piped input, this keeps the cli waiting for input from stdin. 

There are multiple ways to solve this issue.

**Approach1**: wait for some time T, if there is no input from stdin under that time T then stop waiting for stdin, here's a [draft POC mr](https://github.com/google-gemini/gemini-cli/pull/6397)

**Approach2**: allow user to continue adding text until the user adds some text like `quit` or `exit` and we use that as an equivalent of stream end. (But this approach changes how gemini behaves) 

let me know what you think 🙂 
only waiting 500ms seems acceptable. I would be up for going with that approach. I would also be open to detecting precisely what the terminal launching Gemini CLI is and special casing based on that.



Fix (patch):
diff --git a/packages/cli/src/gemini.tsx b/packages/cli/src/gemini.tsx
index b285a5af223..f9cacfb7afe 100644
--- a/packages/cli/src/gemini.tsx
+++ b/packages/cli/src/gemini.tsx
@@ -342,7 +342,9 @@ export async function main() {
     }
   }
   if (!input) {
-    console.error('No input provided via stdin.');
+    console.error(
+      `No input provided via stdin. Input can be provided by piping data into gemini or using the --prompt option.`,
+    );
     process.exit(1);
   }
 
diff --git a/packages/cli/src/utils/readStdin.ts b/packages/cli/src/utils/readStdin.ts
index 26ebeb3a881..3ccdaee7509 100644
--- a/packages/cli/src/utils/readStdin.ts
+++ b/packages/cli/src/utils/readStdin.ts
@@ -9,86 +9,57 @@ export async function readStdin(): Promise<string> {
   return new Promise((resolve, reject) => {
     let data = '';
     let totalSize = 0;
-    let hasReceivedData = false;
-
     process.stdin.setEncoding('utf8');
 
-    function cleanup() {
-      clearTimeout(timeout);
-      process.stdin.removeListener('readable', onReadable);
-      process.stdin.removeListener('end', onEnd);
-      process.stdin.removeListener('error', onError);
-    }
-
-    function processChunk(chunk: string): boolean {
-      hasReceivedData = true;
-      if (totalSize + chunk.length > MAX_STDIN_SIZE) {
-        const remainingSize = MAX_STDIN_SIZE - totalSize;
-        data += chunk.slice(0, remainingSize);
-        console.warn(
-          `Warning: stdin input truncated to ${MAX_STDIN_SIZE} bytes.`,
-        );
-        process.stdin.destroy();
-        return true; // Indicates truncation occurred
-      } else {
-        data += chunk;
-        totalSize += chunk.length;
-        return false;
-      }
-    }
-
-    function checkInitialState(): boolean {
-      if (process.stdin.destroyed || process.stdin.readableEnded) {
-        cleanup();
-        resolve('');
-        return true;
-      }
-
-      const chunk = process.stdin.read();
-      if (chunk !== null) {
-        processChunk(chunk);
-        return false;
-      }
+    const pipedInputShouldBeAvailableInMs = 500;
+    let pipedInputTimerId: null | NodeJS.Timeout = setTimeout(() => {
+      // stop reading if input is not available yet, this is needed
+      // in terminals where stdin is never TTY and nothing's piped
+      // which causes the program to get stuck expecting data from stdin
+      onEnd();
+    }, pipedInputShouldBeAvailableInMs);
 
-      if (!process.stdin.readable) {
-        cleanup();
-        resolve('');
-        return true;
-      }
-
-      return false;
-    }
-
-    if (checkInitialState()) {
-      return;
-    }
-
-    function onReadable() {
+    const onReadable = () => {
       let chunk;
       while ((chunk = process.stdin.read()) !== null) {
-        const truncated = processChunk(chunk);
-        if (truncated) {
+        if (pipedInputTimerId) {
+          clearTimeout(pipedInputTimerId);
+          pipedInputTimerId = null;
+        }
+
+        if (totalSize + chunk.length > MAX_STDIN_SIZE) {
+          const remainingSize = MAX_STDIN_SIZE - totalSize;
+          data += chunk.slice(0, remainingSize);
+          console.warn(
+            `Warning: stdin input truncated to ${MAX_STDIN_SIZE} bytes.`,
+          );
+          process.stdin.destroy(); // Stop reading further
           break;
         }
+        data += chunk;
+        totalSize += chunk.length;
       }
-    }
+    };
 
-    function onEnd() {
+    const onEnd = () => {
       cleanup();
       resolve(data);
-    }
+    };
 
-    function onError(err: Error) {
+    const onError = (err: Error) => {
       cleanup();
       reject(err);
-    }
+    };
 
-    const timeout = setTimeout(() => {
-      if (!hasReceivedData) {
-        cleanup();
-        resolve('');
+    const cleanup = () => {
+      if (pipedInputTimerId) {
+        clearTimeout(pipedInputTimerId);
+        pipedInputTimerId = null;
       }
-    }, 50);
+      process.stdin.removeListener('readable', onReadable);
+      process.stdin.removeListener('end', onEnd);
+      process.stdin.removeListener('error', onError);
+    };
 
     process.stdin.on('readable', onReadable);
     process.stdin.on('end', onEnd);