/* Enables identification of file moves and renames by comparing content hashes, supporting integrity verification. Do not perform raw content parsing or analysis beyond hash computation. Do not manage file system state or configurations outside of move detection. RFC-1097: step 6 — compass.migrate codemod run Mechanical v1 to v2 header migration across the workspace: 942 files rewritten — CHANGE_SUMMARY windows collapsed into history, forbidden v1 blocks stripped, KEY_DECISIONS seeded from @ai-invariant comments (5 files) or TODO placeholders (103 files), blocks reordered to canonical order. RFC-1097: sweep — werkstatt-engine clean Sweep batch 4: 73 Compass headers on headerless engine files (certification, component-runtime, isolation, evolution, testing), real KEY_DECISIONS on 75 files (kernel, cache, dht, swim, gitmesh, runtime), ~80 purpose expansions (CONTRACT-02/PURPOSE-02), non-goals on 13 CONTRACT-03 files, CS-07 history literal fix repo-wide (253 files). Policy: .template.ts/.template.astro excludedPaths. werkstatt-engine now 0 diagnostics. */ /** * Detect file moves and renames by matching content hashes. * Matches deleted and added files to identify moves without content changes. */ import path from "node:path"; import { byteHashFile } from "@warpgogol/werkstatt-engine/fingerprint"; import { loadEntitiesById } from "./registry.ts"; import type { MoveCandidate } from "./types.ts"; export async function detectMoves(args: { cwd: string; deletedPaths: string[]; addedPaths: string[]; }): Promise { const { cwd, deletedPaths, addedPaths } = args; if (!deletedPaths.length || !addedPaths.length) return []; const entities = await loadEntitiesById(cwd); const deletedByHash = new Map(); for (const from of deletedPaths) { const entityId = Object.keys(entities).find((id) => entities[id]?.currentPath === from); if (!entityId) continue; const hash = entities[entityId]?.contentHash; if (!hash) continue; deletedByHash.set(hash, { entityId, from }); } const moves: MoveCandidate[] = []; for (const to of addedPaths) { const absPath = path.join(cwd, to); const hash = await byteHashFile(absPath).catch(() => null); if (!hash) continue; const match = deletedByHash.get(hash); if (!match) continue; moves.push({ entityId: match.entityId, from: match.from, to, confidence: 1, method: "same-hash", }); } return moves; }