src / mcp / scratchpadResolution.ts
import fs from "node:fs/promises";
import path from "node:path";
import { ensureMediaState, resolveScratchpadFolder, scratchpadFolderRemediation, type McpResultPreset } from "./core-bundle.mjs";
import { logger } from "./mcpLogger.js";
/**
* scratchpadFolder is optional only for the generic MCP_MADE_FOR_BIONIC=false path
* (falls back to chatWorkingDirectories itself when omitted). The Bionic tool-chaining path
* (previewInChat=false) requires an explicit scratchpadFolder — pass requireFolder=true there —
* since its HTML report / preview files are meant to live in a per-session folder, not directly
* in the shared chatWorkingDirectories root.
*/
export async function resolveEffectiveScratchpadPath(chatWorkingDirectories: string, requestedFolder: unknown, requireFolder = false, preset?: McpResultPreset): Promise<string> {
const requested = typeof requestedFolder === "string" ? requestedFolder.trim() : "";
if (requested) {
return resolveScratchpadFolder(chatWorkingDirectories, requested, logger);
}
if (requireFolder) {
throw new Error(`scratchpadFolder is required. ${scratchpadFolderRemediation(preset)}`);
}
const basePath = path.resolve(chatWorkingDirectories);
let stats;
try {
stats = await fs.stat(basePath);
} catch (error) {
throw new Error(`CHAT_WORKING_DIRECTORIES is not accessible: ${basePath} (${error instanceof Error ? error.message : String(error)})`);
}
if (!stats.isDirectory()) {
throw new Error(`CHAT_WORKING_DIRECTORIES is not a directory: ${basePath}`);
}
await ensureMediaState(basePath);
return basePath;
}
src / mcp / scratchpadResolution.ts
import fs from "node:fs/promises";
import path from "node:path";
import { ensureMediaState, resolveScratchpadFolder, scratchpadFolderRemediation, type McpResultPreset } from "./core-bundle.mjs";
import { logger } from "./mcpLogger.js";
/**
* scratchpadFolder is optional only for the generic MCP_MADE_FOR_BIONIC=false path
* (falls back to chatWorkingDirectories itself when omitted). The Bionic tool-chaining path
* (previewInChat=false) requires an explicit scratchpadFolder — pass requireFolder=true there —
* since its HTML report / preview files are meant to live in a per-session folder, not directly
* in the shared chatWorkingDirectories root.
*/
export async function resolveEffectiveScratchpadPath(chatWorkingDirectories: string, requestedFolder: unknown, requireFolder = false, preset?: McpResultPreset): Promise<string> {
const requested = typeof requestedFolder === "string" ? requestedFolder.trim() : "";
if (requested) {
return resolveScratchpadFolder(chatWorkingDirectories, requested, logger);
}
if (requireFolder) {
throw new Error(`scratchpadFolder is required. ${scratchpadFolderRemediation(preset)}`);
}
const basePath = path.resolve(chatWorkingDirectories);
let stats;
try {
stats = await fs.stat(basePath);
} catch (error) {
throw new Error(`CHAT_WORKING_DIRECTORIES is not accessible: ${basePath} (${error instanceof Error ? error.message : String(error)})`);
}
if (!stats.isDirectory()) {
throw new Error(`CHAT_WORKING_DIRECTORIES is not a directory: ${basePath}`);
}
await ensureMediaState(basePath);
return basePath;
}