authorgravatar for mvanhorn@users.noreply.github.comMatt Van Horn <mvanhorn@users.noreply.github.com> 2026-07-17 14:22:19-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-07-17 23:22:19+02:00
log4e3f2f04d577152672110303b432a72218fe4e61
tree60068a9baf1ed5eced0700da46e23f013c9400c3
parent1047dac0c5296b1c548e607f061f95227a706b49
signature Signed by PGP key B5690EEEBB952194

fix(server): thread cwd through Claude capability probe (#2048) (#2124)

Co-authored-by: Julius Marminge <julius0216@outlook.com>

4 files changed, 17 insertions(+), 4 deletions(-)

apps/server/src/provider/Drivers/ClaudeDriver.ts+3-2
......@@ -119,6 +119,7 @@ export const ClaudeDriver: ProviderDriver<ClaudeSettings, ClaudeDriverEnv> = {
119119 Effect.gen(function* () {
120120 const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
121121 const path = yield* Path.Path;
122 const { cwd } = yield* ServerConfig;
122123 const httpClient = yield* HttpClient.HttpClient;
123124 const serverSettings = yield* ServerSettingsService;
124125 const eventLoggers = yield* ProviderEventLoggers;
......@@ -154,11 +155,11 @@ export const ClaudeDriver: ProviderDriver<ClaudeSettings, ClaudeDriverEnv> = {
154155 capacity: 1,
155156 timeToLive: CAPABILITIES_PROBE_TTL,
156157 lookup: () =>
157 probeClaudeCapabilities(effectiveConfig, processEnv).pipe(
158 probeClaudeCapabilities(effectiveConfig, processEnv, cwd).pipe(
158159 Effect.provideService(Path.Path, path),
159160 ),
160161 });
161 const capabilitiesCacheKey = yield* makeClaudeCapabilitiesCacheKey(effectiveConfig);
162 const capabilitiesCacheKey = yield* makeClaudeCapabilitiesCacheKey(effectiveConfig, cwd);
162163
163164 const checkProvider = checkClaudeProviderStatus(
164165 effectiveConfig,
apps/server/src/provider/Drivers/ClaudeHome.test.ts+10-1
......@@ -34,11 +34,20 @@ it.layer(NodeServices.layer)("ClaudeHome", (it) => {
3434 expect((yield* makeClaudeEnvironment({ homePath })).CLAUDE_CONFIG_DIR).toBe(resolved);
3535 expect(yield* makeClaudeContinuationGroupKey({ homePath })).toBe(`claude:home:${resolved}`);
3636 expect(yield* makeClaudeCapabilitiesCacheKey({ binaryPath: "claude", homePath })).toBe(
37 `claude\0${resolved}`,
37 `claude\0${resolved}\0`,
3838 );
3939 }),
4040 );
4141
42 it.effect("separates capability probes by cwd", () =>
43 Effect.gen(function* () {
44 const config = { binaryPath: "claude", homePath: "" };
45 const first = yield* makeClaudeCapabilitiesCacheKey(config, "/repo-a");
46 const second = yield* makeClaudeCapabilitiesCacheKey(config, "/repo-b");
47 expect(first).not.toBe(second);
48 }),
49 );
50
4251 it.effect("keeps continuation compatible across instances with the same Claude HOME", () =>
4352 Effect.gen(function* () {
4453 const path = yield* Path.Path;
apps/server/src/provider/Drivers/ClaudeHome.ts+2-1
......@@ -44,8 +44,9 @@ export const makeClaudeContinuationGroupKey = Effect.fn("makeClaudeContinuationG
4444export const makeClaudeCapabilitiesCacheKey = Effect.fn("makeClaudeCapabilitiesCacheKey")(
4545 function* (
4646 config: Pick<ClaudeSettings, "binaryPath" | "homePath">,
47 cwd?: string,
4748 ): Effect.fn.Return<string, never, Path.Path> {
4849 const resolvedHomePath = yield* resolveClaudeHomePath(config);
49 return `${config.binaryPath}\0${resolvedHomePath}`;
50 return `${config.binaryPath}\0${resolvedHomePath}\0${cwd ?? ""}`;
5051 },
5152);
apps/server/src/provider/Layers/ClaudeProvider.ts+2
......@@ -583,6 +583,7 @@ function waitForAbortSignal(signal: AbortSignal): Promise<void> {
583583const probeClaudeCapabilities = (
584584 claudeSettings: ClaudeSettings,
585585 environment?: NodeJS.ProcessEnv,
586 cwd?: string,
586587) => {
587588 const abort = new AbortController();
588589 return Effect.gen(function* () {
......@@ -602,6 +603,7 @@ const probeClaudeCapabilities = (
602603 settingSources: ["user", "project", "local"],
603604 allowedTools: [],
604605 env: claudeEnvironment,
606 ...(cwd ? { cwd } : {}),
605607 stderr: () => {},
606608 },
607609 });