From 931ab94c25b56dd2f9f0aa53de6aae314bca8cb3 Mon Sep 17 00:00:00 2001 From: Rhuan Barreto Date: Wed, 25 Mar 2026 14:50:16 +0100 Subject: [PATCH] fix: suppress all GCM interactive prompts in credential store GIT_TERMINAL_PROMPT=0 and GCM_INTERACTIVE=never were not sufficient to prevent Git Credential Manager from showing GUI dialogs on Windows or askpass prompts on Linux. Add GCM_GUI_PROMPT=false, GIT_ASKPASS="", and SSH_ASKPASS="" to fully suppress all prompt vectors across platforms. This fixes unwanted credential prompts when running `archgate login status` and when the CLI is invoked as a subprocess by editor plugins. --- .../agent-memory/archgate-developer/MEMORY.md | 1 + .claude/settings.json | 5 +++-- src/helpers/credential-store.ts | 22 +++++++++++++++++-- 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/.claude/agent-memory/archgate-developer/MEMORY.md b/.claude/agent-memory/archgate-developer/MEMORY.md index 0d886e8a..ced7f6e1 100644 --- a/.claude/agent-memory/archgate-developer/MEMORY.md +++ b/.claude/agent-memory/archgate-developer/MEMORY.md @@ -39,6 +39,7 @@ Skipping steps 2 or 3 is a workflow violation. The user should NEVER have to inv - **`CHANGELOG.md` is auto-generated — exclude from Prettier** — `CHANGELOG.md` is written by `TrigenSoftware/simple-release-action` during the release PR workflow. It is committed directly and never formatted by Prettier. It MUST be in `.prettierignore`; otherwise `bun run format:check` (part of `bun run validate`) will fail on the release PR. Do not attempt to run prettier on it post-commit. - **`mock.module("node:fetch", ...)` does NOT intercept `globalThis.fetch` in Bun** — Bun's runtime fetch is `globalThis.fetch`, not the `node:fetch` module. Using `mock.module` silently fails; the real network is hit. Always mock fetch by assigning `globalThis.fetch = mockFn as unknown as typeof fetch` directly, and restore via `mock.restore()` in `afterEach`. TypeScript type cast: `as never` is insufficient for the `typeof fetch` type (which includes `preconnect`) — always use `as unknown as typeof fetch`. Also captured in ARCH-005 Don'ts. - **Git credential tests need system-level isolation on Windows** — Overriding `Bun.env.HOME` is NOT sufficient to isolate `git credential fill/approve` calls in tests. Windows Credential Manager is a system-level API, not file-based. Tests MUST set `Bun.env.GIT_CONFIG_NOSYSTEM = "1"` and `Bun.env.GIT_CONFIG_GLOBAL = ` to prevent git from reading the real credential helper config. Without this, tests on machines with stored credentials will pick up real tokens. +- **GCM prompt suppression requires 5 env vars** — `GIT_TERMINAL_PROMPT=0` alone does NOT prevent Git Credential Manager (GCM) from showing GUI prompts on Windows or askpass prompts on Linux. The full set for `gitCredentialEnv()` in `src/helpers/credential-store.ts` is: `GIT_TERMINAL_PROMPT=0`, `GCM_INTERACTIVE=never`, `GCM_GUI_PROMPT=false`, `GIT_ASKPASS=""`, `SSH_ASKPASS=""`. Omitting any one can trigger unexpected prompts in editor/MCP contexts where the CLI runs as a subprocess. - **Module-level `{ ...Bun.env }` captures env at import time** — Spreading `Bun.env` into a module-level constant freezes the env snapshot. Tests that override `Bun.env.HOME` after import won't affect the constant. Fix: use a function that returns `{ ...Bun.env, ... }` on each call so it picks up test-time overrides. Applied in `src/helpers/credential-store.ts`. ## Validation Pipeline diff --git a/.claude/settings.json b/.claude/settings.json index 7bf6b4c4..c1e17267 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -1,4 +1,5 @@ { + "attribution": { "commit": "", "pr": "" }, "permissions": { "allow": [ "Bash(oxfmt *)", @@ -18,7 +19,6 @@ "Grep" ] }, - "attribution": { "commit": "", "pr": "" }, "hooks": { "PreToolUse": [ { @@ -44,5 +44,6 @@ ] } ] - } + }, + "enabledPlugins": { "sentry@claude-plugins-official": true } } diff --git a/src/helpers/credential-store.ts b/src/helpers/credential-store.ts index 597ab590..956697dd 100644 --- a/src/helpers/credential-store.ts +++ b/src/helpers/credential-store.ts @@ -19,9 +19,27 @@ import { internalPath } from "./paths"; const CREDENTIAL_HOST = "plugins.archgate.dev"; const CREDENTIAL_TIMEOUT_MS = 3_000; -/** Build env for git credential commands at call time (not import time). */ +/** + * Build env for git credential commands at call time (not import time). + * + * Suppresses ALL interactive prompts — terminal, GUI, and askpass — across + * platforms and Git Credential Manager (GCM) versions: + * + * - GIT_TERMINAL_PROMPT=0 — git's own terminal prompt + * - GCM_INTERACTIVE=never — GCM interactive mode (terminal + GUI) + * - GCM_GUI_PROMPT=false — GCM GUI-only prompt (Windows toast/dialog) + * - GIT_ASKPASS="" — external askpass program + * - SSH_ASKPASS="" — SSH askpass fallback (some helpers reuse it) + */ function gitCredentialEnv(): Record { - return { ...Bun.env, GIT_TERMINAL_PROMPT: "0", GCM_INTERACTIVE: "never" }; + return { + ...Bun.env, + GIT_TERMINAL_PROMPT: "0", + GCM_INTERACTIVE: "never", + GCM_GUI_PROMPT: "false", + GIT_ASKPASS: "", + SSH_ASKPASS: "", + }; } export interface StoredCredentials {