From a26700e137b0e1e486db7b3ec6d625dd52ae8f85 Mon Sep 17 00:00:00 2001 From: memosr Date: Sat, 6 Jun 2026 09:53:24 +0300 Subject: [PATCH] fix: use shellParse for cmd tokenization to match genValidationFile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit src/lib/genValidationFile.ts tokenizes the forge command with shellParse from shell-quote before storing it in the JSON config — correctly handling quoted arguments like --sig "run()". src/lib/validation-service.ts then read that same cmd field back and re-tokenized with cfg.cmd.trim().split(/\\s+/), a naive whitespace split. For auto-generated configs this happened to work because genValidationFile strips quotes before joining. But the README example shows manually authored configs that include shell-quoted args: "cmd": "forge script ... --sig \"run()\"" The naive whitespace split passes \"run()\" (with quotes) as the --sig value to spawn, not run(). The forge invocation then fails to find any function matching the literal quoted string, producing an opaque error unrelated to the actual problem. Replaced the naive split with shellParse from the same shell-quote package already imported in genValidationFile. Filtered the result to plain string tokens, since shell-quote returns OpEntry objects for shell operators (|, ;, >, etc.) that have no meaning when spawning forge directly. Both the write path (genValidationFile) and the read path (validation-service) now use the same tokenizer, so any cmd string that round-trips through the JSON config is parsed identically. --- src/lib/validation-service.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib/validation-service.ts b/src/lib/validation-service.ts index ddf1baf..cb5a4e5 100644 --- a/src/lib/validation-service.ts +++ b/src/lib/validation-service.ts @@ -1,3 +1,4 @@ +import { parse as shellParse } from 'shell-quote'; import { promises as fs } from 'fs'; import path from 'path'; import { TASK_ORIGIN_COMMON_NAMES, TASK_ORIGIN_SIGNATURE_FILE_NAMES } from './constants'; @@ -91,7 +92,7 @@ async function runStateDiffSimulation( }> { try { console.log('Running state-diff simulation...'); - const forgeCmd = cfg.cmd.trim().split(/\s+/); + const forgeCmd = shellParse(cfg.cmd).filter((token): token is string => typeof token === 'string'); const stateDiffResult = await stateDiffClient.simulate(cfg.rpcUrl, forgeCmd, scriptPath); console.log(