diff --git a/.gitignore b/.gitignore index a2f33c5d5c..86abee56a2 100644 --- a/.gitignore +++ b/.gitignore @@ -105,3 +105,6 @@ packages/server/.env skills-lock.json test-results/ .archon/ralph/ + +# Atlas Intelligence local — 1Password-backed .env template (not for upstream) +.env.template diff --git a/packages/git/src/exec.ts b/packages/git/src/exec.ts index 9380e1e8b8..99a9ccb9bc 100644 --- a/packages/git/src/exec.ts +++ b/packages/git/src/exec.ts @@ -4,6 +4,29 @@ import { promisify } from 'util'; const promisifiedExecFile = promisify(execFile); +/** + * Resolve the bash binary path in a platform-aware way. + * + * On Windows, CreateProcess searches the System32 directory BEFORE the PATH + * env var. Bare `spawn('bash', ...)` therefore resolves to + * `C:\Windows\System32\bash.exe` (the WSL launcher), whose bash has broken + * `${VAR}` expansion when invoked in `-c` mode and uses `/mnt/c/` path + * convention instead of `/c/`. Both break workflow bash nodes. + * + * Fix: on Windows, default to the Git Bash absolute path. Overridable via + * ARCHON_BASH_PATH for non-standard Git installs (e.g. user-scope installer + * at %LOCALAPPDATA%\Programs\Git\bin\bash.exe). + * + * See: coleam00/Archon#1326 + */ +export function resolveBashPath(): string { + if (process.env.ARCHON_BASH_PATH) return process.env.ARCHON_BASH_PATH; + if (process.platform === 'win32') { + return 'C:\\Program Files\\Git\\bin\\bash.exe'; + } + return 'bash'; +} + /** Wrapper around child_process.execFile for test mockability */ export async function execFileAsync( cmd: string, diff --git a/packages/git/src/index.ts b/packages/git/src/index.ts index 8cfdc865f7..e426d8cf0a 100644 --- a/packages/git/src/index.ts +++ b/packages/git/src/index.ts @@ -11,7 +11,7 @@ export type { export { toRepoPath, toBranchName, toWorktreePath } from './types'; // Process and filesystem wrappers -export { execFileAsync, mkdirAsync } from './exec'; +export { execFileAsync, mkdirAsync, resolveBashPath } from './exec'; // Worktree operations export { diff --git a/packages/workflows/src/dag-executor.ts b/packages/workflows/src/dag-executor.ts index facfbd1068..9fe7788728 100644 --- a/packages/workflows/src/dag-executor.ts +++ b/packages/workflows/src/dag-executor.ts @@ -7,7 +7,7 @@ */ import { readFile } from 'fs/promises'; import { resolve, isAbsolute } from 'path'; -import { execFileAsync } from '@archon/git'; +import { execFileAsync, resolveBashPath } from '@archon/git'; import { discoverScripts } from './script-discovery'; import type { WorkflowAssistantOptions, @@ -1359,7 +1359,7 @@ async function executeBashNode( const timeout = node.timeout ?? SUBPROCESS_DEFAULT_TIMEOUT; try { - const { stdout, stderr } = await execFileAsync('bash', ['-c', finalScript], { + const { stdout, stderr } = await execFileAsync(resolveBashPath(), ['-c', finalScript], { cwd, timeout, }); @@ -2018,7 +2018,7 @@ async function executeLoopNode( nodeOutputs, true // escapedForBash ); - await execFileAsync('bash', ['-c', substitutedBash], { cwd }); + await execFileAsync(resolveBashPath(), ['-c', substitutedBash], { cwd }); bashComplete = true; // exit 0 = complete } catch (e) { const bashErr = e as NodeJS.ErrnoException;