Skip to content

Commit 1c86d48

Browse files
authored
Merge pull request #2 from MoonshotAI/main
fix(v2): detect MSYS2 bash from native toolchain git exec paths (MoonshotAI#1590)
2 parents 6a2f70e + 8a4ee05 commit 1c86d48

3 files changed

Lines changed: 118 additions & 5 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@moonshot-ai/kimi-code": patch
3+
---
4+
5+
Fix bash auto-detection on Windows in the experimental v2 engine when git comes from a native MSYS2 toolchain (ucrt64/clang64/clangarm64).

packages/agent-core-v2/src/_base/execEnv/environmentProbe.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@
77
* same suite runs identically on any host OS. `probeHostEnvironmentFromNode()`
88
* bundles the Node defaults for production callers and memoises the promise.
99
*
10-
* On Windows the probe expects Git Bash (the canonical POSIX shell that ships
11-
* with Git for Windows). If it cannot be located the function throws a plain
12-
* `Error` with the checked paths in the message; the App-scope host-environment
13-
* service catches that at first resolution. Set `KIMI_SHELL_PATH` to override.
10+
* On Windows the probe expects bash from Git for Windows or MSYS2. If it
11+
* cannot be located the function throws a plain `Error` with the checked paths
12+
* in the message; the App-scope host-environment service catches that at first
13+
* resolution. Set `KIMI_SHELL_PATH` to override.
1414
*
1515
* Vendored from `@moonshot-ai/kaos` `environment.ts` — kept as a pure helper
1616
* with no DI dependencies.
@@ -57,6 +57,14 @@ export interface HostEnvironmentProbeDeps {
5757

5858
const GIT_EXEC_PATH_TIMEOUT_MS = 5_000;
5959

60+
const MINGW_PREFIX_SET: ReadonlySet<string> = new Set([
61+
'mingw32',
62+
'mingw64',
63+
'ucrt64',
64+
'clang64',
65+
'clangarm64',
66+
]);
67+
6068
function resolveOsKind(platform: string): OsKind {
6169
switch (platform) {
6270
case 'darwin':
@@ -222,7 +230,7 @@ function gitBashCandidatesFromGitExecPath(execPath: string): readonly string[] {
222230
const parts = normalized.split('\\');
223231
for (let i = parts.length - 1; i >= 0; i -= 1) {
224232
const segment = parts[i]?.toLowerCase();
225-
if (segment === 'mingw32' || segment === 'mingw64') {
233+
if (segment !== undefined && MINGW_PREFIX_SET.has(segment)) {
226234
const root = parts.slice(0, i).join('\\');
227235
if (root.length > 0) {
228236
return gitBashCandidatesFromGitRoot(root);
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/**
2+
* Host environment probe — MSYS2 bash detection.
3+
*
4+
* Pins the Windows shell probe against native MSYS2 toolchains: a git whose
5+
* `git --exec-path` reports an `ucrt64` / `clang64` / `clangarm64` prefix
6+
* (e.g. `C:/msys64/ucrt64/libexec/git-core`) must walk back to the MSYS2 root
7+
* and resolve the shared bash at `usr\bin\bash.exe`, instead of failing to
8+
* detect any shell.
9+
*
10+
* All tests expect `probeHostEnvironment()` to be a pure function of injected
11+
* platform probes (no ambient state) so the same suite runs identically on
12+
* macOS/Linux/Windows CI runners.
13+
*
14+
* Ported from `packages/kaos/test/environment.test.ts` (the MSYS2 cases added
15+
* by the bash-detection fix); the v1 file carries the full POSIX / Git for
16+
* Windows / Scoop shim matrix, which the vendored probe shares verbatim.
17+
*/
18+
19+
import { describe, expect, it } from 'vitest';
20+
21+
import {
22+
probeHostEnvironment,
23+
type HostEnvironmentProbeDeps,
24+
} from '#/_base/execEnv/environmentProbe';
25+
26+
interface StubOpts {
27+
readonly platform: string;
28+
readonly env?: Record<string, string | undefined>;
29+
readonly existingPaths?: readonly string[];
30+
readonly execFileResults?: Readonly<Record<string, string>>;
31+
}
32+
33+
/** Build a stub deps bag mimicking Node's `os` + `process` surface. */
34+
function stubDeps(opts: StubOpts): HostEnvironmentProbeDeps {
35+
const existing = new Set(opts.existingPaths ?? []);
36+
return {
37+
platform: opts.platform,
38+
arch: 'x86_64',
39+
release: '1.2.3',
40+
homeDir: 'C:\\Users\\me',
41+
env: opts.env ?? {},
42+
isFile: async (path: string) => existing.has(path),
43+
execFileText: async (file: string, args: readonly string[]) =>
44+
opts.execFileResults?.[execFileKey(file, args)],
45+
};
46+
}
47+
48+
function execFileKey(file: string, args: readonly string[]): string {
49+
return [file, ...args].join('\0');
50+
}
51+
52+
describe('probeHostEnvironment', () => {
53+
it('resolves MSYS2 ucrt64 native git through git --exec-path', async () => {
54+
const gitExe = 'C:\\msys64\\ucrt64\\bin\\git.exe';
55+
const env = await probeHostEnvironment(
56+
stubDeps({
57+
platform: 'win32',
58+
env: { PATH: 'C:\\msys64\\ucrt64\\bin' },
59+
execFileResults: {
60+
[execFileKey(gitExe, ['--exec-path'])]: 'C:/msys64/ucrt64/libexec/git-core\n',
61+
},
62+
existingPaths: [gitExe, 'C:\\msys64\\usr\\bin\\bash.exe'],
63+
}),
64+
);
65+
expect(env.shellName).toBe('bash');
66+
expect(env.shellPath).toBe('C:\\msys64\\usr\\bin\\bash.exe');
67+
});
68+
69+
it('resolves MSYS2 clang64 native git through git --exec-path', async () => {
70+
const gitExe = 'C:\\msys64\\clang64\\bin\\git.exe';
71+
const env = await probeHostEnvironment(
72+
stubDeps({
73+
platform: 'win32',
74+
env: { PATH: 'C:\\msys64\\clang64\\bin' },
75+
execFileResults: {
76+
[execFileKey(gitExe, ['--exec-path'])]: 'C:/msys64/clang64/libexec/git-core\n',
77+
},
78+
existingPaths: [gitExe, 'C:\\msys64\\usr\\bin\\bash.exe'],
79+
}),
80+
);
81+
expect(env.shellName).toBe('bash');
82+
expect(env.shellPath).toBe('C:\\msys64\\usr\\bin\\bash.exe');
83+
});
84+
85+
it('resolves MSYS2 clangarm64 native git through git --exec-path', async () => {
86+
const gitExe = 'C:\\msys64\\clangarm64\\bin\\git.exe';
87+
const env = await probeHostEnvironment(
88+
stubDeps({
89+
platform: 'win32',
90+
env: { PATH: 'C:\\msys64\\clangarm64\\bin' },
91+
execFileResults: {
92+
[execFileKey(gitExe, ['--exec-path'])]: 'C:/msys64/clangarm64/libexec/git-core\n',
93+
},
94+
existingPaths: [gitExe, 'C:\\msys64\\usr\\bin\\bash.exe'],
95+
}),
96+
);
97+
expect(env.shellName).toBe('bash');
98+
expect(env.shellPath).toBe('C:\\msys64\\usr\\bin\\bash.exe');
99+
});
100+
});

0 commit comments

Comments
 (0)