// @effect-diagnostics nodeBuiltinImport:off - cleanup uses Node's retrying rm, which the FileSystem service does not expose.import * as ClaudeSdk from "@anthropic-ai/claude-agent-sdk";import { vi } from "vite-plus/test";import * as Deferred from "effect/Deferred";import * as Fiber from "effect/Fiber";import * as TestClock from "effect/testing/TestClock";import { ClaudeSettings } from "@t3tools/contracts";import * as NodeFSP from "node:fs/promises";import * as NodeServices from "@effect/platform-node/NodeServices";import { assert, it } from "@effect/vitest";import * as Effect from "effect/Effect";import * as FileSystem from "effect/FileSystem";import * as Path from "effect/Path";import * as Schema from "effect/Schema"; import { buildClaudeCapabilitiesProbeQueryOptions, CLAUDE_CAPABILITIES_PROBE_SETTING_SOURCES, probeClaudeCapabilities,} from "./ClaudeProvider.ts"; vi.mock("@anthropic-ai/claude-agent-sdk", { spy: true }); const decodeClaudeSettings = Schema.decodeSync(ClaudeSettings); it("isolates Claude capability probes without dropping workspace setting sources", () => { const abortController = new AbortController(); const options = buildClaudeCapabilitiesProbeQueryOptions({ executablePath: "/usr/bin/claude", abortController, environment: { HOME: "/home/user", ENABLE_CLAUDEAI_MCP_SERVERS: "true", FORCE_CODE_TERMINAL: "1", }, cwd: "/workspace/project", }); assert.deepEqual(options.mcpServers, {}); assert.equal(options.strictMcpConfig, true); assert.equal(options.cwd, "/workspace/project"); assert.deepEqual(options.settingSources, [...CLAUDE_CAPABILITIES_PROBE_SETTING_SOURCES]); assert.deepEqual(options.settings, { disableAllHooks: true }); assert.deepEqual(options.allowedTools, []); assert.equal(options.persistSession, false); assert.equal(options.pathToClaudeCodeExecutable, "/usr/bin/claude"); assert.equal(options.abortController, abortController); assert.equal(options.env?.HOME, "/home/user"); assert.equal(options.env?.ENABLE_CLAUDEAI_MCP_SERVERS, "false"); assert.equal(options.env?.FORCE_CODE_TERMINAL, undefined); assert.equal(options.env?.CLAUDE_CODE_AUTO_CONNECT_IDE, "0"); assert.equal(options.env?.CLAUDE_CODE_IDE_SKIP_AUTO_INSTALL, "1");}); it.layer(NodeServices.layer)("Claude capability probe SDK boundary", (it) => { it.effect("serializes strict no-MCP options and still resolves account capabilities", () => Effect.gen(function* () { const fs = yield* FileSystem.FileSystem; const path = yield* Path.Path; const tempDir = yield* fs.makeTempDirectoryScoped({ prefix: "t3-claude-probe-sdk-" }); const executablePath = path.join(tempDir, "fake-claude.mjs"); const invocationPath = path.join(tempDir, "invocation.json"); // The probe aborts the SDK without awaiting the child's exit, and on // Windows a directory that is still some process's cwd cannot be // removed. Keep the workspace outside the scoped directory and let it // go with a retrying removal once the child has gone. const workspaceCwd = yield* fs.makeTempDirectory({ prefix: "t3-claude-probe-cwd-" }); // Node's own retry rather than an Effect schedule: it.effect runs on a // TestClock, so a scheduled retry would wait for time nobody advances. // If the child still holds the directory after that, an empty temp // directory is left behind rather than failing the test for it. yield* Effect.addFinalizer(() => Effect.promise(() => NodeFSP.rm(workspaceCwd, { recursive: true, force: true, maxRetries: 20, retryDelay: 250, }).catch(() => undefined), ), ); yield* fs.writeFileString( executablePath, [ "#!/usr/bin/env node", 'import { existsSync, readFileSync, writeFileSync } from "node:fs";', 'import { createInterface } from "node:readline";', "const args = process.argv.slice(2);", 'const mcpConfigIndex = args.indexOf("--mcp-config");', "const rawMcpConfig = mcpConfigIndex >= 0 ? args[mcpConfigIndex + 1] : undefined;", "let mcpConfig;", "if (rawMcpConfig) {", ' const contents = existsSync(rawMcpConfig) ? readFileSync(rawMcpConfig, "utf8") : rawMcpConfig;', " try { mcpConfig = JSON.parse(contents); } catch { mcpConfig = contents; }", "}", "writeFileSync(process.env.T3_PROBE_INVOCATION_PATH, JSON.stringify({", " args,", " cwd: process.cwd(),", " connectorEnv: process.env.ENABLE_CLAUDEAI_MCP_SERVERS,", " mcpConfig,", "}));", "const lines = createInterface({ input: process.stdin });", 'lines.on("line", (line) => {', " const message = JSON.parse(line);", ' if (message.type !== "control_request") return;', " const reply = (response) => process.stdout.write(JSON.stringify({", ' type: "control_response",', ' response: { subtype: "success", request_id: message.request_id, response },', ' }) + "\\n");', ' if (message.request?.subtype === "initialize") {', " reply({", ' commands: [{ name: "review", description: "Review changes", argumentHint: "[path]" }],', " agents: [],", ' output_style: "default",', ' available_output_styles: ["default"],', " models: [],", ' account: { email: "dev@example.com", subscriptionType: "pro", tokenSource: "oauth" },', " });", " }", " // The probe follows initialize with get_usage on the same process.", ' if (message.request?.subtype === "get_usage") {', " reply({", " session: {},", ' subscription_type: "pro",', " rate_limits_available: true,", ' rate_limits: { five_hour: { utilization: 12, resets_at: "2026-07-18T14:39:00Z" } },', " behaviors: null,", " });", " }", "});", "setInterval(() => {}, 1_000);", "", ].join("\n"), ); yield* fs.chmod(executablePath, 0o755); const capabilities = yield* probeClaudeCapabilities( decodeClaudeSettings({ binaryPath: executablePath }), { ...process.env, T3_PROBE_INVOCATION_PATH: invocationPath, ENABLE_CLAUDEAI_MCP_SERVERS: "true", }, workspaceCwd, ); assert.deepEqual(capabilities, { email: "dev@example.com", subscriptionType: "pro", tokenSource: "oauth", apiProvider: undefined, slashCommands: [ { name: "review", description: "Review changes", input: { hint: "[path]" }, }, ], usage: { rate_limits_available: true, rate_limits: { five_hour: { utilization: 12, resets_at: "2026-07-18T14:39:00Z" } }, }, }); // @effect-diagnostics-next-line preferSchemaOverJson:off const invocation = JSON.parse(yield* fs.readFileString(invocationPath)) as { readonly args: ReadonlyArray<string>; readonly cwd: string; readonly connectorEnv: string; readonly mcpConfig: unknown; }; assert.equal(invocation.cwd, yield* fs.realPath(workspaceCwd)); assert.equal(invocation.connectorEnv, "false"); assert.equal(invocation.args.includes("--strict-mcp-config"), true); assert.equal(invocation.args.includes("--mcp-config"), false); assert.equal(invocation.mcpConfig, undefined); assert.equal(invocation.args.includes("--setting-sources=user,project,local"), true); const settingsFlagIndex = invocation.args.indexOf("--settings"); assert.notEqual(settingsFlagIndex, -1); // @effect-diagnostics-next-line preferSchemaOverJson:off const flagSettings = JSON.parse(invocation.args[settingsFlagIndex + 1] ?? "{}") as { readonly disableAllHooks?: boolean; }; assert.equal(flagSettings.disableAllHooks, true); }).pipe(Effect.scoped), );}); it.effect("preserves initialized capabilities when optional usage times out", () => Effect.gen(function* () { const usageStarted = yield* Deferred.make<void>(); let abortSignal: AbortSignal | undefined; const query = vi.spyOn(ClaudeSdk, "query").mockImplementation(({ options }) => { abortSignal = options?.abortController?.signal; return { initializationResult: async () => ({ account: { email: "dev@example.com", subscriptionType: "pro", tokenSource: "oauth" }, commands: [{ name: "review", description: "Review changes", argumentHint: "[path]" }], }), usage_EXPERIMENTAL_MAY_CHANGE_DO_NOT_RELY_ON_THIS_API_YET: () => { Deferred.doneUnsafe(usageStarted, Effect.void); return new Promise(() => {}); }, } as ReturnType<typeof ClaudeSdk.query>; }); yield* Effect.addFinalizer(() => Effect.sync(() => query.mockRestore())); const probe = yield* probeClaudeCapabilities( decodeClaudeSettings({ binaryPath: "claude" }), ).pipe(Effect.forkChild); yield* Deferred.await(usageStarted); yield* TestClock.adjust("4 seconds"); const capabilities = yield* Fiber.join(probe); assert.equal(capabilities?.email, "dev@example.com"); assert.equal(capabilities?.subscriptionType, "pro"); assert.equal(capabilities?.tokenSource, "oauth"); assert.deepEqual(capabilities?.slashCommands, [ { name: "review", description: "Review changes", input: { hint: "[path]" } }, ]); assert.equal(capabilities?.usage, undefined); assert.equal(abortSignal?.aborted, true); }).pipe(Effect.scoped, Effect.provide(NodeServices.layer)),);
// @effect-diagnostics nodeBuiltinImport:off - cleanup uses Node's retrying rm, which the FileSystem service does not expose.
import * as ClaudeSdk from "@anthropic-ai/claude-agent-sdk";
import { vi } from "vite-plus/test";
import * as Deferred from "effect/Deferred";
import * as Fiber from "effect/Fiber";
import * as TestClock from "effect/testing/TestClock";
import { ClaudeSettings } from "@t3tools/contracts";
import * as NodeFSP from "node:fs/promises";
import * as NodeServices from "@effect/platform-node/NodeServices";
import { assert, it } from "@effect/vitest";
import * as Effect from "effect/Effect";
import * as FileSystem from "effect/FileSystem";
import * as Path from "effect/Path";
import * as Schema from "effect/Schema";
import {
buildClaudeCapabilitiesProbeQueryOptions,
CLAUDE_CAPABILITIES_PROBE_SETTING_SOURCES,
probeClaudeCapabilities,
} from "./ClaudeProvider.ts";
vi.mock("@anthropic-ai/claude-agent-sdk", { spy: true });
const decodeClaudeSettings = Schema.decodeSync(ClaudeSettings);
it("isolates Claude capability probes without dropping workspace setting sources", () => {
const abortController = new AbortController();
const options = buildClaudeCapabilitiesProbeQueryOptions({
executablePath: "/usr/bin/claude",
abortController,
environment: {
HOME: "/home/user",
ENABLE_CLAUDEAI_MCP_SERVERS: "true",
FORCE_CODE_TERMINAL: "1",
},
cwd: "/workspace/project",
});
assert.deepEqual(options.mcpServers, {});
assert.equal(options.strictMcpConfig, true);
assert.equal(options.cwd, "/workspace/project");
assert.deepEqual(options.settingSources, [...CLAUDE_CAPABILITIES_PROBE_SETTING_SOURCES]);
assert.deepEqual(options.settings, { disableAllHooks: true });
assert.deepEqual(options.allowedTools, []);
assert.equal(options.persistSession, false);
assert.equal(options.pathToClaudeCodeExecutable, "/usr/bin/claude");
assert.equal(options.abortController, abortController);
assert.equal(options.env?.HOME, "/home/user");
assert.equal(options.env?.ENABLE_CLAUDEAI_MCP_SERVERS, "false");
assert.equal(options.env?.FORCE_CODE_TERMINAL, undefined);
assert.equal(options.env?.CLAUDE_CODE_AUTO_CONNECT_IDE, "0");
assert.equal(options.env?.CLAUDE_CODE_IDE_SKIP_AUTO_INSTALL, "1");
});
it.layer(NodeServices.layer)("Claude capability probe SDK boundary", (it) => {
it.effect("serializes strict no-MCP options and still resolves account capabilities", () =>
Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
const path = yield* Path.Path;
const tempDir = yield* fs.makeTempDirectoryScoped({ prefix: "t3-claude-probe-sdk-" });
const executablePath = path.join(tempDir, "fake-claude.mjs");
const invocationPath = path.join(tempDir, "invocation.json");
// The probe aborts the SDK without awaiting the child's exit, and on
// Windows a directory that is still some process's cwd cannot be
// removed. Keep the workspace outside the scoped directory and let it
// go with a retrying removal once the child has gone.
const workspaceCwd = yield* fs.makeTempDirectory({ prefix: "t3-claude-probe-cwd-" });
// Node's own retry rather than an Effect schedule: it.effect runs on a
// TestClock, so a scheduled retry would wait for time nobody advances.
// If the child still holds the directory after that, an empty temp
// directory is left behind rather than failing the test for it.
yield* Effect.addFinalizer(() =>
Effect.promise(() =>
NodeFSP.rm(workspaceCwd, {
recursive: true,
force: true,
maxRetries: 20,
retryDelay: 250,
}).catch(() => undefined),
),
);
yield* fs.writeFileString(
executablePath,
[
"#!/usr/bin/env node",
'import { existsSync, readFileSync, writeFileSync } from "node:fs";',
'import { createInterface } from "node:readline";',
"const args = process.argv.slice(2);",
'const mcpConfigIndex = args.indexOf("--mcp-config");',
"const rawMcpConfig = mcpConfigIndex >= 0 ? args[mcpConfigIndex + 1] : undefined;",
"let mcpConfig;",
"if (rawMcpConfig) {",
' const contents = existsSync(rawMcpConfig) ? readFileSync(rawMcpConfig, "utf8") : rawMcpConfig;',
" try { mcpConfig = JSON.parse(contents); } catch { mcpConfig = contents; }",
"}",
"writeFileSync(process.env.T3_PROBE_INVOCATION_PATH, JSON.stringify({",
" args,",
" cwd: process.cwd(),",
" connectorEnv: process.env.ENABLE_CLAUDEAI_MCP_SERVERS,",
" mcpConfig,",
"}));",
"const lines = createInterface({ input: process.stdin });",
'lines.on("line", (line) => {',
" const message = JSON.parse(line);",
' if (message.type !== "control_request") return;',
" const reply = (response) => process.stdout.write(JSON.stringify({",
' type: "control_response",',
' response: { subtype: "success", request_id: message.request_id, response },',
' }) + "\\n");',
' if (message.request?.subtype === "initialize") {',
" reply({",
' commands: [{ name: "review", description: "Review changes", argumentHint: "[path]" }],',
" agents: [],",
' output_style: "default",',
' available_output_styles: ["default"],',
" models: [],",
' account: { email: "dev@example.com", subscriptionType: "pro", tokenSource: "oauth" },',
" });",
" }",
" // The probe follows initialize with get_usage on the same process.",
' if (message.request?.subtype === "get_usage") {',
" reply({",
" session: {},",
' subscription_type: "pro",',
" rate_limits_available: true,",
' rate_limits: { five_hour: { utilization: 12, resets_at: "2026-07-18T14:39:00Z" } },',
" behaviors: null,",
" });",
" }",
"});",
"setInterval(() => {}, 1_000);",
"",
].join("\n"),
);
yield* fs.chmod(executablePath, 0o755);
const capabilities = yield* probeClaudeCapabilities(
decodeClaudeSettings({ binaryPath: executablePath }),
{
...process.env,
T3_PROBE_INVOCATION_PATH: invocationPath,
ENABLE_CLAUDEAI_MCP_SERVERS: "true",
},
workspaceCwd,
);
assert.deepEqual(capabilities, {
email: "dev@example.com",
subscriptionType: "pro",
tokenSource: "oauth",
apiProvider: undefined,
slashCommands: [
{
name: "review",
description: "Review changes",
input: { hint: "[path]" },
},
],
usage: {
rate_limits_available: true,
rate_limits: { five_hour: { utilization: 12, resets_at: "2026-07-18T14:39:00Z" } },
},
});
// @effect-diagnostics-next-line preferSchemaOverJson:off
const invocation = JSON.parse(yield* fs.readFileString(invocationPath)) as {
readonly args: ReadonlyArray<string>;
readonly cwd: string;
readonly connectorEnv: string;
readonly mcpConfig: unknown;
};
assert.equal(invocation.cwd, yield* fs.realPath(workspaceCwd));
assert.equal(invocation.connectorEnv, "false");
assert.equal(invocation.args.includes("--strict-mcp-config"), true);
assert.equal(invocation.args.includes("--mcp-config"), false);
assert.equal(invocation.mcpConfig, undefined);
assert.equal(invocation.args.includes("--setting-sources=user,project,local"), true);
const settingsFlagIndex = invocation.args.indexOf("--settings");
assert.notEqual(settingsFlagIndex, -1);
// @effect-diagnostics-next-line preferSchemaOverJson:off
const flagSettings = JSON.parse(invocation.args[settingsFlagIndex + 1] ?? "{}") as {
readonly disableAllHooks?: boolean;
};
assert.equal(flagSettings.disableAllHooks, true);
}).pipe(Effect.scoped),
);
});
it.effect("preserves initialized capabilities when optional usage times out", () =>
Effect.gen(function* () {
const usageStarted = yield* Deferred.make<void>();
let abortSignal: AbortSignal | undefined;
const query = vi.spyOn(ClaudeSdk, "query").mockImplementation(({ options }) => {
abortSignal = options?.abortController?.signal;
return {
initializationResult: async () => ({
account: { email: "dev@example.com", subscriptionType: "pro", tokenSource: "oauth" },
commands: [{ name: "review", description: "Review changes", argumentHint: "[path]" }],
}),
usage_EXPERIMENTAL_MAY_CHANGE_DO_NOT_RELY_ON_THIS_API_YET: () => {
Deferred.doneUnsafe(usageStarted, Effect.void);
return new Promise(() => {});
},
} as ReturnType<typeof ClaudeSdk.query>;
});
yield* Effect.addFinalizer(() => Effect.sync(() => query.mockRestore()));
const probe = yield* probeClaudeCapabilities(
decodeClaudeSettings({ binaryPath: "claude" }),
).pipe(Effect.forkChild);
yield* Deferred.await(usageStarted);
yield* TestClock.adjust("4 seconds");
const capabilities = yield* Fiber.join(probe);
assert.equal(capabilities?.email, "dev@example.com");
assert.equal(capabilities?.subscriptionType, "pro");
assert.equal(capabilities?.tokenSource, "oauth");
assert.deepEqual(capabilities?.slashCommands, [
{ name: "review", description: "Review changes", input: { hint: "[path]" } },
]);
assert.equal(capabilities?.usage, undefined);
assert.equal(abortSignal?.aborted, true);
}).pipe(Effect.scoped, Effect.provide(NodeServices.layer)),
);