From d8947473b822b263cdc7e9708bd62bb8ae89b31e Mon Sep 17 00:00:00 2001 From: Alex Newman Date: Tue, 7 Apr 2026 11:06:32 -0700 Subject: [PATCH] fix: escape filePath in recovery hints to prevent malformed output Filenames containing quotes, backslashes, or newlines could produce malformed smart_outline/smart_unfold examples in the deny message. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/cli/handlers/file-context.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/cli/handlers/file-context.ts b/src/cli/handlers/file-context.ts index 4fbdb47a..6dcc5e7e 100644 --- a/src/cli/handlers/file-context.ts +++ b/src/cli/handlers/file-context.ts @@ -107,6 +107,8 @@ function deduplicateObservations( } function formatFileTimeline(observations: ObservationRow[], filePath: string): string { + // Escape filePath for safe interpolation into recovery hints (quotes, backslashes, newlines) + const safePath = filePath.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n'); // Group observations by day const byDay = new Map(); for (const obs of observations) { @@ -128,7 +130,7 @@ function formatFileTimeline(observations: ObservationRow[], filePath: string): s `Read blocked: This file has prior observations. Choose the cheapest path:`, `- **Already know enough?** The timeline below may be all you need (semantic priming).`, `- **Need details?** get_observations([IDs]) — ~300 tokens each.`, - `- **Need current code?** smart_outline("${filePath}") for structure (~1-2k tokens), smart_unfold("${filePath}", "") for a specific function (~400-2k tokens).`, + `- **Need current code?** smart_outline("${safePath}") for structure (~1-2k tokens), smart_unfold("${safePath}", "") for a specific function (~400-2k tokens).`, `- **Need to edit?** Use smart tools for line numbers, then sed via Bash (Edit requires Read, but you already have the context).`, ];