{"version":3,"file":"collector.mjs","names":[],"sources":["../../src/files-touched/collector.ts"],"sourcesContent":["import type { SessionEntry } from '@earendil-works/pi-coding-agent';\n\nimport { firstDefinedString, getTrackedToolActions } from '@live-compaction/files-touched/parsers';\nimport {\n\tbuildRootMappings,\n\tdisplayPathForTrackedPath,\n\tnormalizeTrackedPath,\n\tresolveCanonicalPath,\n\tresolveMoveRedirect,\n} from '@live-compaction/files-touched/paths';\nimport type { FilesTouchedEntry, FileTouchOperation } from '@live-compaction/files-touched/types';\n\ntype FileMove = { from: string; to: string };\n\ntype TrackedTouchRecord = {\n\tpath: string;\n\toperation: FileTouchOperation;\n\ttimestamp: number;\n};\n\nfunction extractTextFromContent(content: unknown): string {\n\tif (typeof content === 'string') return content;\n\tif (!Array.isArray(content)) return '';\n\treturn content\n\t\t.map((block) => {\n\t\t\tif (!block || typeof block !== 'object') return '';\n\t\t\treturn typeof (block as { text?: unknown }).text === 'string'\n\t\t\t\t? (block as { text: string }).text\n\t\t\t\t: '';\n\t\t})\n\t\t.filter(Boolean)\n\t\t.join('\\n');\n}\n\nfunction getToolCallId(value: unknown): string | null {\n\tif (!value || typeof value !== 'object') return null;\n\treturn firstDefinedString(\n\t\t(value as { id?: unknown }).id,\n\t\t(value as { toolCallId?: unknown }).toolCallId,\n\t\t(value as { tool_call_id?: unknown }).tool_call_id,\n\t\t(value as { tool_use_id?: unknown }).tool_use_id,\n\t);\n}\n\nexport function collectFilesTouched(\n\tentries: SessionEntry[],\n\tcwd?: string | null,\n): FilesTouchedEntry[] {\n\t// --- Pass 1: collect tool call intentions ---\n\tconst toolCalls = new Map<string, ReturnType<typeof getTrackedToolActions>>();\n\n\tfor (const entry of entries) {\n\t\tif (entry.type !== 'message') continue;\n\t\tconst msg = entry.message;\n\t\tif (msg.role !== 'assistant' || !Array.isArray(msg.content)) continue;\n\t\tfor (const block of msg.content) {\n\t\t\tif (\n\t\t\t\t!block ||\n\t\t\t\ttypeof block !== 'object' ||\n\t\t\t\t(block as { type?: unknown }).type !== 'toolCall'\n\t\t\t) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst toolCallId = getToolCallId(block);\n\t\t\tconst toolName =\n\t\t\t\ttypeof (block as { name?: unknown }).name === 'string'\n\t\t\t\t\t? (block as { name: string }).name\n\t\t\t\t\t: '';\n\t\t\tconst args = (block as { arguments?: unknown }).arguments;\n\t\t\tconst argObject =\n\t\t\t\targs && typeof args === 'object' && !Array.isArray(args)\n\t\t\t\t\t? (args as Record<string, unknown>)\n\t\t\t\t\t: {};\n\t\t\tif (!toolCallId || !toolName) continue;\n\t\t\tconst actions = getTrackedToolActions(toolName, argObject);\n\t\t\tif (actions.length > 0) toolCalls.set(toolCallId, actions);\n\t\t}\n\t}\n\n\t// --- Pass 2: confirm via tool results ---\n\tconst touches: TrackedTouchRecord[] = [];\n\tconst moves: FileMove[] = [];\n\n\tfor (const entry of entries) {\n\t\tif (entry.type !== 'message') continue;\n\t\tconst msg = entry.message;\n\t\tif (msg.role !== 'toolResult' || msg.isError) continue;\n\n\t\tconst toolCallId = firstDefinedString(\n\t\t\tmsg.toolCallId,\n\t\t\t(msg as { tool_call_id?: unknown }).tool_call_id,\n\t\t\t(msg as { tool_use_id?: unknown }).tool_use_id,\n\t\t);\n\t\tif (!toolCallId) continue;\n\n\t\tconst actions = toolCalls.get(toolCallId);\n\t\tif (!actions || actions.length === 0) continue;\n\n\t\tconst toolResultText = extractTextFromContent(msg.content);\n\t\tconst isNoOpEdit = /applied:\\s*0|no changes applied|nothing to (?:do|change)/i.test(\n\t\t\ttoolResultText,\n\t\t);\n\t\tfor (const action of actions) {\n\t\t\tif (action.kind === 'move') {\n\t\t\t\tmoves.push({ from: action.from, to: action.to });\n\t\t\t\ttouches.push({ path: action.to, operation: 'move', timestamp: msg.timestamp });\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (isNoOpEdit && action.operation === 'edit') continue;\n\t\t\ttouches.push({ path: action.path, operation: action.operation, timestamp: msg.timestamp });\n\t\t}\n\t}\n\n\t// --- Build root mappings ---\n\tconst rootMappings = buildRootMappings(\n\t\t[...touches.map((t) => t.path), ...moves.flatMap((m) => [m.from, m.to])],\n\t\tcwd,\n\t);\n\n\t// --- Build move redirects ---\n\tconst redirects = new Map<string, string>();\n\tfor (const move of moves) {\n\t\tconst fromPath = normalizeTrackedPath(move.from, rootMappings, cwd);\n\t\tconst toPath = normalizeTrackedPath(move.to, rootMappings, cwd);\n\t\tif (fromPath && toPath && fromPath !== toPath) redirects.set(fromPath, toPath);\n\t}\n\n\t// --- Merge into canonical paths ---\n\tconst merged = new Map<string, { operations: Set<FileTouchOperation>; lastTimestamp: number }>();\n\tfor (const touch of touches) {\n\t\tconst normalizedPath = normalizeTrackedPath(touch.path, rootMappings, cwd);\n\t\tconst canonicalPath = resolveMoveRedirect(normalizedPath, redirects);\n\t\tif (!canonicalPath) continue;\n\t\tconst existing = merged.get(canonicalPath);\n\t\tif (existing) {\n\t\t\texisting.operations.add(touch.operation);\n\t\t\tif (touch.timestamp > existing.lastTimestamp) existing.lastTimestamp = touch.timestamp;\n\t\t\tcontinue;\n\t\t}\n\t\tmerged.set(canonicalPath, {\n\t\t\toperations: new Set([touch.operation]),\n\t\t\tlastTimestamp: touch.timestamp,\n\t\t});\n\t}\n\n\t// --- Resolve display paths ---\n\tconst prepared = [...merged.entries()]\n\t\t.map(([canonicalPath, value]) => {\n\t\t\tconst resolvedPath = resolveCanonicalPath(canonicalPath, rootMappings, cwd);\n\t\t\treturn {\n\t\t\t\tcanonicalPath,\n\t\t\t\tpath: resolvedPath,\n\t\t\t\tdisplayPath: displayPathForTrackedPath(canonicalPath, resolvedPath, cwd),\n\t\t\t\toperations: value.operations,\n\t\t\t\tlastTimestamp: value.lastTimestamp,\n\t\t\t};\n\t\t})\n\t\t.sort((left, right) => right.lastTimestamp - left.lastTimestamp);\n\n\tconst displayCounts = new Map<string, number>();\n\tfor (const file of prepared) {\n\t\tdisplayCounts.set(file.displayPath, (displayCounts.get(file.displayPath) ?? 0) + 1);\n\t}\n\n\treturn prepared.map((file) => ({\n\t\tpath: file.path,\n\t\tdisplayPath: (displayCounts.get(file.displayPath) ?? 0) > 1 ? file.path : file.displayPath,\n\t\toperations: file.operations,\n\t\tlastTimestamp: file.lastTimestamp,\n\t}));\n}\n"],"mappings":"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"}