{"repo":"NousResearch/hermes-agent","public_id":"54888","display_label":"Windows Git binary paths with spaces breaking review","summary":"Bug reports and fixes addressing simple-git failures on Windows when the Git binary path contains spaces, causing errors in the desktop review pane. Multiple fix attempts target the same underlying path-handling issue.","subsystem":"apps/desktop","counts":{"members":5,"open_issues":0,"open_issues_distinct":0,"open_prs":0,"closed_issues":2,"merged_prs":1,"attempt_prs":2},"landed_fix":{"status":"merged_to_main","basis":"structural","pr":67612},"resolution":{"state":"partially_resolved","by":"pr-67612"},"freshness":{"state":"verified","last_verified_at":"2026-07-19T19:22:23+00:00","is_stale":false},"grouping":{"backbone":"STRUCTURAL_SPANNING","link_density":0.43599999999999994},"fix_edges":[{"issue_number":54888,"pr_number":67612,"tier":"verified","basis":"closing_ref","confidence":0.95,"recoverable_from_timeline":true},{"issue_number":64810,"pr_number":67612,"tier":"verified","basis":"superseded_ref","confidence":0.9,"recoverable_from_timeline":false}],"members":[{"kind":"issue","number":54888,"title":"[Bug]: Hermes Desktop git-review errors on Windows — GitPluginError: restricted characters","url":null,"state":"closed"},{"kind":"issue","number":64810,"title":"Bug: Desktop review pane shows \"No diff to show\" on Windows — simple-git rejects binary paths with spaces","url":null,"state":"closed"},{"kind":"pr","number":64713,"title":"fix(desktop): support spaced Windows Git paths in review","url":null,"state":"closed","close_reason":{"refs":[{"kind":"commit","value":"e361c5e20","number":0},{"kind":"pr","value":"#67612","number":67612}],"category":"merged_via","implication":"superseded"},"merged":false},{"kind":"pr","number":64812,"title":"fix(desktop): handle Windows simple-git binary paths with spaces","url":null,"state":"closed","close_reason":{"refs":[{"kind":"commit","value":"e361c5e20402375c74a65ca52810c6a380461226","number":0},{"kind":"pr","value":"#67612","number":67612},{"kind":"pr","value":"#60156","number":60156},{"kind":"pr","value":"#64713","number":64713},{"kind":"pr","value":"#55337","number":55337},{"kind":"pr","value":"#55337","number":55337}],"category":"implemented_elsewhere","implication":"superseded"},"merged":false},{"kind":"pr","number":67612,"title":"fix(desktop): support spaced Windows Git paths in review","url":null,"state":"merged","close_reason":{"category":"merged","implication":"superseded"},"merged":true,"is_fix_pr":true,"fix_tier":"verified","files_changed":["apps/desktop/electron/git-review-ops.test.ts","apps/desktop/electron/git-review-ops.ts"],"fix_hunks":[{"file":"apps/desktop/electron/git-review-ops.test.ts","hunks":["@@ -6,7 +6,7 @@ import path from 'node:path'\n \n import { afterEach, test } from 'vitest'\n \n-import { repoStatus, resolveRenamePath } from './git-review-ops'\n+import { gitFor, repoStatus, resolveRenamePath } from './git-review-ops'\n \n const tempDirs: string[] = []\n ","@@ -34,6 +34,30 @@ test('resolveRenamePath: plain path is unchanged', () => {\n   assert.equal(resolveRenamePath('src/a.ts'), 'src/a.ts')\n })\n \n+test('gitFor accepts an internally resolved git binary path containing spaces', () => {\n+  assert.doesNotThrow(() => gitFor(process.cwd(), 'C:\\\\Program Files\\\\Git\\\\cmd\\\\git.exe'))\n+})\n+\n+test('gitFor runs git through a spaced binary path', async () => {\n+  if (process.platform !== 'win32') {\n+    return\n+  }\n+\n+  const gitBin = path.join(process.env.ProgramFiles || String.raw`C:\\Program Files`, 'Git', 'cmd', 'git.exe')\n+\n+  if (!fs.existsSync(gitBin)) {\n+    return\n+  }\n+\n+  const repo = makeRepo()\n+\n+  fs.writeFileSync(path.join(repo, 'changed.txt'), 'review me\\n')\n+\n+  const status = await gitFor(repo, gitBin).status()\n+\n+  assert.equal(status.not_added.includes('changed.txt'), true)\n+})\n+\n test('resolveRenamePath: simple rename resolves to the new path', () => {\n   assert.equal(resolveRenamePath('old.ts => new.ts'), 'new.ts')\n })"]},{"file":"apps/desktop/electron/git-review-ops.ts","hunks":["@@ -43,7 +43,20 @@ function runGh(args, cwd, ghBin): Promise<{ ok: boolean; stdout: string }> {\n }\n \n function gitFor(cwd, gitBin) {\n-  return simpleGit({ baseDir: cwd, binary: gitBin || 'git', maxConcurrentProcesses: 4, trimmed: false })\n+  // `gitBin` is resolved inside the Electron main process from known install\n+  // locations or PATH — never renderer/user input. simple-git's custom-binary\n+  // validation rejects paths containing spaces (the default Windows install is\n+  // `C:\\Program Files\\Git\\cmd\\git.exe`), which silently broke the Review pane.\n+  // For spaced paths, opt into simple-git's trusted-binary escape hatch instead\n+  // of falling back to PATH (often absent in GUI-launched apps, and PATH lookup\n+  // could resolve a repo-local git.exe).\n+  return simpleGit({\n+    baseDir: cwd,\n+    binary: gitBin || 'git',\n+    maxConcurrentProcesses: 4,\n+    trimmed: false,\n+    ...(gitBin && /\\s/.test(gitBin) ? { unsafe: { allowUnsafeCustomBinary: true } } : {})\n+  })\n }\n \n // simple-git reports renames as `old => new` (and `dir/{old => new}/f`); resolve","@@ -680,6 +693,7 @@ async function repoStatus(repoPath, gitBin) {\n export {\n   branchBase,\n   fileDiffVsHead,\n+  gitFor,\n   repoStatus,\n   resolveRenamePath,\n   reviewCommit,\n"]}]}],"trust":{"default_tier":"verified","includes_suggested":false}}