Skip to content

fix: Windows npm path for Gemini OAuth + feat: WORKING.md bootstrap (#46368, #46367)#46371

Open
xuwei-xy wants to merge 1 commit intoopenclaw:mainfrom
xuwei-xy:batch4-pr46368-pr46367-v2
Open

fix: Windows npm path for Gemini OAuth + feat: WORKING.md bootstrap (#46368, #46367)#46371
xuwei-xy wants to merge 1 commit intoopenclaw:mainfrom
xuwei-xy:batch4-pr46368-pr46367-v2

Conversation

@xuwei-xy
Copy link
Copy Markdown

@openclaw-barnacle openclaw-barnacle bot added extensions: google-gemini-cli-auth Extension: google-gemini-cli-auth agents Agent runtime and tooling size: S labels Mar 14, 2026
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 14, 2026

Greptile Summary

This PR combines two fixes: a Windows-specific fallback for resolving Gemini CLI OAuth credentials via %APPDATA%\npm\node_modules when gemini is not on PATH, and opt-in loading of WORKING.md as a working-memory bootstrap file in agent workspaces.

  • oauth.ts: Clean and well-guarded — the APPDATA fallback is added both in extractGeminiCliCredentials (for the no-PATH case) and in resolveGeminiCliDirs (for the has-PATH case on Windows). No issues found.
  • oauth.test.ts: New test correctly exercises the fallback, but process.env.PATH is set to "/nonexistent" inside the try block and is never saved or restored in the finally block, leaking a broken PATH to all subsequent tests in the file.
  • workspace.ts: WORKING.md opt-in loading follows the same pattern as MEMORY.md and is straightforward. However, DEFAULT_WORKING_FILENAME was not added to MINIMAL_BOOTSTRAP_ALLOWLIST, so subagent and cron sessions will not receive this file — worth confirming whether this is intentional.
  • workspace.test.ts: Two new tests covering presence and absence of WORKING.md are correct and sufficient.

Confidence Score: 3/5

  • Mostly safe to merge; one test isolation bug (PATH leak) should be fixed before merging to avoid flaky downstream tests.
  • The production code changes are solid and well-scoped. The score is lowered primarily because of the process.env.PATH leak in the new test, which can silently break other tests in the same suite. There is also a design question around whether WORKING.md should be visible to subagent/cron sessions that should be clarified.
  • extensions/google-gemini-cli-auth/oauth.test.ts — PATH environment variable is not restored in the finally block.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: extensions/google-gemini-cli-auth/oauth.test.ts
Line: 192-237

Comment:
**`process.env.PATH` not restored in finally block**

The test saves and restores `process.platform` and `APPDATA`, but it modifies `process.env.PATH = "/nonexistent"` without saving the original value or restoring it in the `finally` block. This leaks the corrupted `PATH` into all subsequently running tests in the same test file, which can cause `findInPath` to silently return `null` for every subsequent test that depends on the real PATH.

```suggestion
  it("falls back to APPDATA npm global path on Windows when gemini is not in PATH", async () => {
    const originalPlatform = process.platform;
    const originalAppdata = process.env.APPDATA;
    const originalPath = process.env.PATH;
    try {
      Object.defineProperty(process, "platform", { value: "win32", configurable: true });
      process.env.APPDATA = join(rootDir, "fake", "AppData", "Roaming");
      process.env.PATH = "/nonexistent";
```

And in the `finally` block, add:
```
    } finally {
      Object.defineProperty(process, "platform", { value: originalPlatform, configurable: true });
      if (originalAppdata !== undefined) {
        process.env.APPDATA = originalAppdata;
      } else {
        delete process.env.APPDATA;
      }
      if (originalPath !== undefined) {
        process.env.PATH = originalPath;
      } else {
        delete process.env.PATH;
      }
    }
```

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: src/agents/workspace.ts
Line: 526-533

Comment:
**WORKING.md excluded from subagent/cron sessions**

`DEFAULT_WORKING_FILENAME` is not added to `MINIMAL_BOOTSTRAP_ALLOWLIST` (line 555–561), so WORKING.md will be silently stripped by `filterBootstrapFilesForSession` when the session is a subagent or cron job. If WORKING.md is meant to convey the current active task context, subagents spawned during that task would lack this context, which could impair their effectiveness.

If the exclusion is intentional (e.g., WORKING.md is private to the main session), a short comment near `MINIMAL_BOOTSTRAP_ALLOWLIST` clarifying the design decision would help future readers. Otherwise, add `DEFAULT_WORKING_FILENAME` to that set.

How can I resolve this? If you propose a fix, please make it concise.

Last reviewed commit: ce79b76

Comment on lines +192 to +237
it("falls back to APPDATA npm global path on Windows when gemini is not in PATH", async () => {
const originalPlatform = process.platform;
const originalAppdata = process.env.APPDATA;
try {
Object.defineProperty(process, "platform", { value: "win32", configurable: true });
process.env.APPDATA = join(rootDir, "fake", "AppData", "Roaming");
process.env.PATH = "/nonexistent";

const appdataOauth2Path = join(
process.env.APPDATA,
"npm",
"node_modules",
"@google",
"gemini-cli",
"node_modules",
"@google",
"gemini-cli-core",
"dist",
"src",
"code_assist",
"oauth2.js",
);

mockExistsSync.mockImplementation((p: string) => {
const normalized = normalizePath(p);
if (normalized === normalizePath(appdataOauth2Path)) {
return true;
}
return false;
});
mockReadFileSync.mockReturnValue(FAKE_OAUTH2_CONTENT);

const { extractGeminiCliCredentials, clearCredentialsCache } = await import("./oauth.js");
clearCredentialsCache();
const result = extractGeminiCliCredentials();

expectFakeCliCredentials(result);
} finally {
Object.defineProperty(process, "platform", { value: originalPlatform, configurable: true });
if (originalAppdata !== undefined) {
process.env.APPDATA = originalAppdata;
} else {
delete process.env.APPDATA;
}
}
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

process.env.PATH not restored in finally block

The test saves and restores process.platform and APPDATA, but it modifies process.env.PATH = "/nonexistent" without saving the original value or restoring it in the finally block. This leaks the corrupted PATH into all subsequently running tests in the same test file, which can cause findInPath to silently return null for every subsequent test that depends on the real PATH.

Suggested change
it("falls back to APPDATA npm global path on Windows when gemini is not in PATH", async () => {
const originalPlatform = process.platform;
const originalAppdata = process.env.APPDATA;
try {
Object.defineProperty(process, "platform", { value: "win32", configurable: true });
process.env.APPDATA = join(rootDir, "fake", "AppData", "Roaming");
process.env.PATH = "/nonexistent";
const appdataOauth2Path = join(
process.env.APPDATA,
"npm",
"node_modules",
"@google",
"gemini-cli",
"node_modules",
"@google",
"gemini-cli-core",
"dist",
"src",
"code_assist",
"oauth2.js",
);
mockExistsSync.mockImplementation((p: string) => {
const normalized = normalizePath(p);
if (normalized === normalizePath(appdataOauth2Path)) {
return true;
}
return false;
});
mockReadFileSync.mockReturnValue(FAKE_OAUTH2_CONTENT);
const { extractGeminiCliCredentials, clearCredentialsCache } = await import("./oauth.js");
clearCredentialsCache();
const result = extractGeminiCliCredentials();
expectFakeCliCredentials(result);
} finally {
Object.defineProperty(process, "platform", { value: originalPlatform, configurable: true });
if (originalAppdata !== undefined) {
process.env.APPDATA = originalAppdata;
} else {
delete process.env.APPDATA;
}
}
});
it("falls back to APPDATA npm global path on Windows when gemini is not in PATH", async () => {
const originalPlatform = process.platform;
const originalAppdata = process.env.APPDATA;
const originalPath = process.env.PATH;
try {
Object.defineProperty(process, "platform", { value: "win32", configurable: true });
process.env.APPDATA = join(rootDir, "fake", "AppData", "Roaming");
process.env.PATH = "/nonexistent";

And in the finally block, add:

    } finally {
      Object.defineProperty(process, "platform", { value: originalPlatform, configurable: true });
      if (originalAppdata !== undefined) {
        process.env.APPDATA = originalAppdata;
      } else {
        delete process.env.APPDATA;
      }
      if (originalPath !== undefined) {
        process.env.PATH = originalPath;
      } else {
        delete process.env.PATH;
      }
    }
Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/google-gemini-cli-auth/oauth.test.ts
Line: 192-237

Comment:
**`process.env.PATH` not restored in finally block**

The test saves and restores `process.platform` and `APPDATA`, but it modifies `process.env.PATH = "/nonexistent"` without saving the original value or restoring it in the `finally` block. This leaks the corrupted `PATH` into all subsequently running tests in the same test file, which can cause `findInPath` to silently return `null` for every subsequent test that depends on the real PATH.

```suggestion
  it("falls back to APPDATA npm global path on Windows when gemini is not in PATH", async () => {
    const originalPlatform = process.platform;
    const originalAppdata = process.env.APPDATA;
    const originalPath = process.env.PATH;
    try {
      Object.defineProperty(process, "platform", { value: "win32", configurable: true });
      process.env.APPDATA = join(rootDir, "fake", "AppData", "Roaming");
      process.env.PATH = "/nonexistent";
```

And in the `finally` block, add:
```
    } finally {
      Object.defineProperty(process, "platform", { value: originalPlatform, configurable: true });
      if (originalAppdata !== undefined) {
        process.env.APPDATA = originalAppdata;
      } else {
        delete process.env.APPDATA;
      }
      if (originalPath !== undefined) {
        process.env.PATH = originalPath;
      } else {
        delete process.env.PATH;
      }
    }
```

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +526 to +533
// WORKING.md is opt-in: only loaded when present in the workspace.
const workingPath = path.join(resolvedDir, DEFAULT_WORKING_FILENAME);
try {
await fs.access(workingPath);
entries.push({ name: DEFAULT_WORKING_FILENAME, filePath: workingPath });
} catch {
// WORKING.md not present — skip
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WORKING.md excluded from subagent/cron sessions

DEFAULT_WORKING_FILENAME is not added to MINIMAL_BOOTSTRAP_ALLOWLIST (line 555–561), so WORKING.md will be silently stripped by filterBootstrapFilesForSession when the session is a subagent or cron job. If WORKING.md is meant to convey the current active task context, subagents spawned during that task would lack this context, which could impair their effectiveness.

If the exclusion is intentional (e.g., WORKING.md is private to the main session), a short comment near MINIMAL_BOOTSTRAP_ALLOWLIST clarifying the design decision would help future readers. Otherwise, add DEFAULT_WORKING_FILENAME to that set.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agents/workspace.ts
Line: 526-533

Comment:
**WORKING.md excluded from subagent/cron sessions**

`DEFAULT_WORKING_FILENAME` is not added to `MINIMAL_BOOTSTRAP_ALLOWLIST` (line 555–561), so WORKING.md will be silently stripped by `filterBootstrapFilesForSession` when the session is a subagent or cron job. If WORKING.md is meant to convey the current active task context, subagents spawned during that task would lack this context, which could impair their effectiveness.

If the exclusion is intentional (e.g., WORKING.md is private to the main session), a short comment near `MINIMAL_BOOTSTRAP_ALLOWLIST` clarifying the design decision would help future readers. Otherwise, add `DEFAULT_WORKING_FILENAME` to that set.

How can I resolve this? If you propose a fix, please make it concise.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling extensions: google-gemini-cli-auth Extension: google-gemini-cli-auth size: S

Projects

None yet

1 participant