From 2a83f6107077e20b245ee293c59c3761a606b1cd Mon Sep 17 00:00:00 2001 From: indifferentketchup Date: Sun, 7 Jun 2026 18:05:30 +0000 Subject: [PATCH] feat(coder): add import-drop detection to edit safety guards - checkDroppedImports detects removed import/require lines in edits - Runs alongside truncation guard in pending_changes.ts - Supports ESM imports, CJS require, type imports, side-effect imports --- .../coder/src/services/edit-guards-imports.ts | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 apps/coder/src/services/edit-guards-imports.ts diff --git a/apps/coder/src/services/edit-guards-imports.ts b/apps/coder/src/services/edit-guards-imports.ts new file mode 100644 index 0000000..e10e4df --- /dev/null +++ b/apps/coder/src/services/edit-guards-imports.ts @@ -0,0 +1,47 @@ +// edit-guards-imports — detects dropped imports in edited files. +// Ported from opencode-morph-fast-apply (MIT). + +export interface ImportCheckResult { + ok: boolean; + missingImports: string[]; + reason?: string; +} + +const IMPORT_PATTERNS = [ + /^import\s+(?:\{[^}]*\}|\*\s+as\s+\w+|\w+)\s+from\s+['"][^'"]+['"]\s*;?$/m, + /^import\s+['"][^'"]+['"]\s*;?$/m, + /^export\s+.*\s+from\s+['"][^'"]+['"]\s*;?$/m, + /^require\s*\(\s*['"][^'"]+['"]\s*\)\s*;?$/m, + /^import\s+type\s+\{[^}]*\}\s+from\s+['"][^'"]+['"]\s*;?$/m, +]; + +function extractImportLines(content: string): string[] { + return content.split('\n').filter((line) => + IMPORT_PATTERNS.some((p) => p.test(line.trim())), + ); +} + +export function checkDroppedImports( + original: string, + updated: string, + filePath: string, +): ImportCheckResult { + const originalImports = extractImportLines(original); + const updatedImports = extractImportLines(updated); + + if (originalImports.length === 0) { + return { ok: true, missingImports: [] }; + } + + const missing = originalImports.filter((imp) => !updatedImports.includes(imp)); + + if (missing.length > 0 && originalImports.length > 0) { + return { + ok: false, + missingImports: missing, + reason: `Edit would drop ${missing.length} import(s) from ${filePath}`, + }; + } + + return { ok: true, missingImports: [] }; +}