From dc2056a5d56f42a5937158f17223ba53b76b3f78 Mon Sep 17 00:00:00 2001 From: Nick Shirokov Date: Wed, 18 Mar 2026 20:56:11 +0300 Subject: [PATCH] fix(web-test): resolve file paths relative to project root, not cwd All user-facing file functions now resolve relative paths from the project root (where .claude/ lives) instead of process.cwd(). Fixes showImage, startRecording, addNarration, openFile failing when the skill is installed in a different project. Co-Authored-By: Claude Opus 4.6 (1M context) --- .claude/skills/web-test/scripts/browser.mjs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/.claude/skills/web-test/scripts/browser.mjs b/.claude/skills/web-test/scripts/browser.mjs index 2afbaccc..2913da5f 100644 --- a/.claude/skills/web-test/scripts/browser.mjs +++ b/.claude/skills/web-test/scripts/browser.mjs @@ -21,6 +21,13 @@ import { switchTabScript, resolveGridScript } from './dom.mjs'; +// Project root: 4 levels up from .claude/skills/web-test/scripts/browser.mjs +const __fn_browser = fileURLToPath(import.meta.url); +const projectRoot = pathResolve(dirname(__fn_browser), '..', '..', '..', '..'); + +/** Resolve a user-provided path relative to the project root (not cwd). */ +const resolveProjectPath = (p) => pathResolve(projectRoot, p); + let browser = null; let page = null; let sessionPrefix = null; // e.g. "http://localhost:8081/bpdemo/ru_RU" @@ -483,7 +490,7 @@ function normalizeE1cibUrl(url) { export async function openFile(filePath) { ensureConnected(); await dismissPendingErrors(); - const absPath = pathResolve(filePath); + const absPath = resolveProjectPath(filePath); const MAX_ATTEMPTS = 2; // 1st may trigger security dialog, 2nd is the real open for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) { @@ -3764,7 +3771,7 @@ export async function startRecording(outputPath, opts = {}) { const ffmpegPath = resolveFfmpeg(opts.ffmpegPath); // Ensure output directory exists - const resolvedPath = pathResolve(outputPath); + const resolvedPath = resolveProjectPath(outputPath); mkdirSync(dirname(resolvedPath), { recursive: true }); // Create CDP session for screencast @@ -4009,6 +4016,7 @@ export function getCaptions() { */ export async function addNarration(videoPath, opts = {}) { if (!videoPath) return { file: null, duration: 0, size: 0, captions: 0 }; + videoPath = resolveProjectPath(videoPath); const ffmpegPath = resolveFfmpeg(opts.ffmpegPath); const ttsProvider = getTtsProvider(opts.provider || 'edge'); const ttsOpts = { voice: opts.voice, apiKey: opts.apiKey, apiUrl: opts.apiUrl, model: opts.model }; @@ -4287,7 +4295,7 @@ export async function showImage(imagePath, opts = {}) { const useBlur = opts.background ? false : preset.blur; // Read image and base64-encode - const absPath = pathResolve(imagePath); + const absPath = resolveProjectPath(imagePath); if (!fsExistsSync(absPath)) { throw new Error(`showImage: file not found: ${absPath}`); } @@ -4663,8 +4671,6 @@ function resolveFfmpeg(explicit) { catch { /* fall through */ } // 4. tools/ffmpeg/bin/ffmpeg.exe relative to project root - const __filename = fileURLToPath(import.meta.url); - const projectRoot = pathResolve(dirname(__filename), '..', '..', '..', '..'); const localPath = pathResolve(projectRoot, 'tools', 'ffmpeg', 'bin', 'ffmpeg.exe'); if (fsExistsSync(localPath)) { try { execFileSync(localPath, ['-version'], { stdio: 'ignore', timeout: 5000 }); return localPath; } @@ -4694,8 +4700,6 @@ async function resolveEdgeTts() { } catch { /* fall through */ } // 2. tools/tts/ relative to project root - const __fn = fileURLToPath(import.meta.url); - const projectRoot = pathResolve(dirname(__fn), '..', '..', '..', '..'); const localPath = pathResolve(projectRoot, 'tools', 'tts', 'node_modules', 'node-edge-tts', 'dist', 'edge-tts.js'); if (fsExistsSync(localPath)) { try {