p-doom/crowd-code-dataset-0.2 huggingface dataset:
[content] src/extension.ts (typescript)
		vscode.window.onDidStartTerminalShellExecution(async (event: vscode.TerminalShellExecutionStartEvent) => {
			if (recording.isRecording) {
				if (isCurrentFileExported()) {
					return
				}
				const commandLine = event.execution.commandLine.value
				recording.sequence++
				addToFileQueue(
					buildCsvRow({
						sequence: recording.sequence,
						rangeOffset: 0,
						rangeLength: 0,
						text: commandLine,
						type: ChangeType.TERMINAL_COMMAND,
					})
				)
				appendToFile()

				const stream = event.execution.read()
				for await (const data of stream) {
					recording.sequence++
					addToFileQueue(
						buildCsvRow({ sequence: recording.sequence, rangeOffset: 0, rangeLength: 0, text: data, type: ChangeType.TERMINAL_OUTPUT })
					)
					appendToFile()
				}
			}
[content] src/extension.ts (typescript)
	context.subscriptions.push(
		vscode.window.onDidChangeActiveTerminal((terminal: vscode.Terminal | undefined) => {
			if (terminal && recording.isRecording) {
				if (isCurrentFileExported()) {
					return
				}
				recording.sequence++
				addToFileQueue(
					buildCsvRow({
						sequence: recording.sequence,
						rangeOffset: 0,
						rangeLength: 0,
						text: terminal.name,
						type: ChangeType.TERMINAL_FOCUS,
					})
				)
				appendToFile()
				actionsProvider.setCurrentFile(`Terminal: ${terminal.name}`)
			}
		})
	)
[content] src/extension.ts (typescript)
				recording.sequence++
				const csvRowParams: CSVRowBuilder = {
					sequence: recording.sequence,
					rangeOffset: editor.document.offsetAt(selection.start),
					rangeLength: editor.document.offsetAt(selection.end) - editor.document.offsetAt(selection.start),
					text: selectedText,
					type: changeType,
				}
				addToFileQueue(buildCsvRow(csvRowParams))
				appendToFile()
				actionsProvider.setCurrentFile(editor.document.fileName)
			}
		})
	)
[content] src/extension.ts (typescript)
				switch (event.kind) {
					case vscode.TextEditorSelectionChangeKind.Keyboard:
						changeType = ChangeType.SELECTION_KEYBOARD
						break
					case vscode.TextEditorSelectionChangeKind.Mouse:
						changeType = ChangeType.SELECTION_MOUSE
						break
					case vscode.TextEditorSelectionChangeKind.Command:
						changeType = ChangeType.SELECTION_COMMAND
						break
					default:
						throw new TypeError("Unknown selection change kind.")
				}
[content] src/extension.ts (typescript)
			recording.sequence++
			addToFileQueue(
				buildCsvRow({
					sequence: recording.sequence,
					rangeOffset: 0,
					rangeLength: 0,
					text: tabEventText,
					type: ChangeType.TAB,
				})
			)
			appendToFile()
			actionsProvider.setCurrentFile(editor.document.fileName)
		}
	})

	context.subscriptions.push(
		vscode.window.onDidChangeTextEditorSelection(event => {
			if (recording.isRecording && event.textEditor === vscode.window.activeTextEditor) {
				if (isCurrentFileExported()) {
					return
				}

				const editor = event.textEditor
				// For simplicity, we'll log the primary selection.
				const selection = event.selections[0]
				if (!selection) {
					return
				}

				const selectedText = editor.document.getText(selection)
				let changeType: string
[content] src/extension.ts (typescript)
			if (recording.activatedFiles) {
				if (!recording.activatedFiles.has(currentFileUri)) {
					tabEventText = editor.document.getText()
					recording.activatedFiles.add(currentFileUri)
				}
			} else {
				throw new Error("Warning: recording.activatedFiles was not available during TAB event logging.")
			}
[content] src/extension.ts (typescript)
	vscode.window.onDidChangeActiveTextEditor(editor => {
		updateStatusBarItem()
		if (editor && recording.isRecording) {
			if (isCurrentFileExported()) {
				return
			}
			const currentFileUri = editor.document.uri.toString()
			let tabEventText = ''
[content] src/extension.ts (typescript)
				if (result === 'Yes') {
					try {
						const itemPath = getFullPath(item, exportPath)
						await deleteFileOrFolder(itemPath)
						recordFilesProvider.refresh()
					} catch (err) {
						vscode.window.showErrorMessage(`Error deleting ${item.label}: ${err}`)
					}
				}
			}
		)
[content] src/extension.ts (typescript)
	// Save anonUserId globally for user to copy
	const userName = process.env.USER || process.env.USERNAME || "coder";
	const machineId = vscode.env.machineId ?? null;
	const rawId = `${machineId}:${userName}`;
	const anonUserId = crypto.createHash('sha256').update(rawId).digest('hex') as string;
[content] src/extension.ts (typescript)
	updateStatusBarItem,
	startRecording,
	stopRecording,
	isCurrentFileExported,
	commands,
	recording,
	addToFileQueue,
	buildCsvRow,
	appendToFile,
	panicButton,
} from './recording'
import { ChangeType, CSVRowBuilder } from './types'
[content] src/extension.ts (typescript)
	// Listen for terminal commands to detect git operations
	context.subscriptions.push(
		vscode.window.onDidStartTerminalShellExecution((event: vscode.TerminalShellExecutionStartEvent) => {
			const command = event.execution.commandLine.value
			checkTerminalForGitCommand(command)
		})
	)

	// Create status bar item
	statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, 9000)
	updateStatusBarItem()
	context.subscriptions.push(statusBarItem)

	// Ensure consent is obtained when the extension is first activated
	await ensureConsent()

	// Autostart recording regardless of consent. The consent only gates data upload.
	logToOutput('Autostarting recording...', 'info')
	startRecording().catch((err) => logToOutput(`Autostart recording failed unexpectedly: ${err}`, 'error'))
}

export function deactivate(): void {
	logToOutput('Deactivating crowd-code v2.0', 'info')

	if (recording.isRecording) {
		stopRecording()
	}

	// Cleanup all modules
	cleanupViewportCapture()
	cleanupTerminalCapture()
	cleanupFilesystemWatcher()
	cleanupActionDeduplicator()
	cleanupGitProvider()

	statusBarItem.dispose()
}
[content] src/extension.ts (typescript)
	// Listen for configuration changes
	context.subscriptions.push(vscode.workspace.onDidChangeConfiguration(onConfigurationChange))

	// Initialize capture modules
	logToOutput('Initializing capture modules...', 'info')

	// Initialize viewport capture (handles scroll detection via onDidChangeTextEditorVisibleRanges)
	initializeViewportCapture(context)

	// Initialize terminal capture (handles terminal focus and command execution)
	initializeTerminalCapture(context)

	// Initialize filesystem watcher (handles external file changes)
	await initializeFilesystemWatcher(context)

	// Initialize action deduplicator
	initializeActionDeduplicator()

	// Initialize git provider and set up callback
	setGitActionCallback(handleGitAction)
	initializeGitProvider(context)
[content] src/extension.ts (typescript)
			if (result === 'Yes') {
				try {
					const itemPath = getFullPath(item, exportPath)
					await deleteFileOrFolder(itemPath)
					recordFilesProvider.refresh()
				} catch (err) {
					vscode.window.showErrorMessage(`Error deleting ${item.label}: ${err}`)
				}
			}
		})
[content] src/extension.ts (typescript)
	// Generate anonymous user ID
	const userName = process.env.USER || process.env.USERNAME || 'coder'
	const machineId = vscode.env.machineId ?? null
	const rawId = `${machineId}:${userName}`
	const anonUserId = crypto.createHash('sha256').update(rawId).digest('hex')
[content] src/extension.ts (typescript)
/**
 * Handle git actions from the git provider
 */
function handleGitAction(action: GitAction): void {
	if (!recording.isRecording) return

	// Git actions are logged as external actions
	// The recording module will handle the observation capture
	logToOutput(`Git action detected: ${action.operation} - ${action.details}`, 'info')
}
[content] src/extension.ts (typescript)
import {
	initializeViewportCapture,
	cleanupViewportCapture,
	initializeTerminalCapture,
	cleanupTerminalCapture,
	initializeFilesystemWatcher,
	cleanupFilesystemWatcher,
	initializeActionDeduplicator,
	cleanupActionDeduplicator,
} from './capture'
import { initializeGitProvider, cleanupGitProvider, setGitActionCallback, checkTerminalForGitCommand } from './gitProvider'
import type { GitAction } from './types'
[content] src/extension.ts (typescript)
				switch (event.kind) {
					case vscode.TextEditorSelectionChangeKind.Keyboard:
						changeType = ChangeType.SELECTION_KEYBOARD
						break
					case vscode.TextEditorSelectionChangeKind.Mouse:
						changeType = ChangeType.SELECTION_MOUSE
						break
					case vscode.TextEditorSelectionChangeKind.Command:
						changeType = ChangeType.SELECTION_COMMAND
						break
					default:
						throw new TypeError("Unknown selection change kind.")
				}

				recording.sequence++
				const csvRowParams: CSVRowBuilder = {
					sequence: recording.sequence,
					rangeOffset: editor.document.offsetAt(selection.start),
					rangeLength: editor.document.offsetAt(selection.end) - editor.document.offsetAt(selection.start),
					text: selectedText,
					type: changeType,
				}
				addToFileQueue(buildCsvRow(csvRowParams))
				appendToFile()
				actionsProvider.setCurrentFile(editor.document.fileName)
			}
		})
	)

	context.subscriptions.push(
		vscode.window.onDidChangeActiveTerminal((terminal: vscode.Terminal | undefined) => {
			if (terminal && recording.isRecording) {
				if (isCurrentFileExported()) {
					return
				}
				recording.sequence++
				addToFileQueue(
					buildCsvRow({
						sequence: recording.sequence,
						rangeOffset: 0,
						rangeLength: 0,
						text: terminal.name,
						type: ChangeType.TERMINAL_FOCUS,
					})
				)
				appendToFile()
				actionsProvider.setCurrentFile(`Terminal: ${terminal.name}`)
			}
		})
	)
[content] src/extension.ts (typescript)
	// Initialize git provider and set up callback
	setGitActionCallback(handleGitAction)
	initializeGitProvider(context)

	// Listen for terminal commands to detect git operations
	context.subscriptions.push(
		vscode.window.onDidStartTerminalShellExecution((event: vscode.TerminalShellExecutionStartEvent) => {
			const command = event.execution.commandLine.value
			checkTerminalForGitCommand(command)
		})
	)

	// Create status bar item
	statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, 9000)
	updateStatusBarItem()
	context.subscriptions.push(statusBarItem)

	// Ensure consent is obtained when the extension is first activated
	await ensureConsent()

	// Autostart recording regardless of consent. The consent only gates data upload.
	logToOutput('Autostarting recording...', 'info')
	startRecording().catch((err) => logToOutput(`Autostart recording failed unexpectedly: ${err}`, 'error'))
}

export function deactivate(): void {
	logToOutput('Deactivating crowd-code v2.0', 'info')

	if (recording.isRecording) {
		stopRecording()
	}

	// Cleanup all modules
	cleanupViewportCapture()
	cleanupTerminalCapture()
	cleanupFilesystemWatcher()
	cleanupActionDeduplicator()
	cleanupGitProvider()

	statusBarItem.dispose()
}
[content] src/extension.ts (typescript)
		vscode.window.onDidChangeActiveTerminal((terminal: vscode.Terminal | undefined) => {
			if (terminal && recording.isRecording) {
				if (isCurrentFileExported()) {
					return
				}
				recording.sequence++
				addToFileQueue(
					buildCsvRow({
						sequence: recording.sequence,
						rangeOffset: 0,
						rangeLength: 0,
						text: terminal.name,
						type: ChangeType.TERMINAL_FOCUS,
					})
				)
				appendToFile()
				actionsProvider.setCurrentFile(`Terminal: ${terminal.name}`)
			}
		})
	)

	context.subscriptions.push(
		vscode.window.onDidStartTerminalShellExecution(async (event: vscode.TerminalShellExecutionStartEvent) => {
			if (recording.isRecording) {
				if (isCurrentFileExported()) {
					return
				}
				const commandLine = event.execution.commandLine.value
				recording.sequence++
				addToFileQueue(
					buildCsvRow({
						sequence: recording.sequence,
						rangeOffset: 0,
						rangeLength: 0,
						text: commandLine,
						type: ChangeType.TERMINAL_COMMAND,
					})
				)
				appendToFile()

				const stream = event.execution.read()
				for await (const data of stream) {
					recording.sequence++
					addToFileQueue(
						buildCsvRow({ sequence: recording.sequence, rangeOffset: 0, rangeLength: 0, text: data, type: ChangeType.TERMINAL_OUTPUT })
					)
					appendToFile()
				}
			}
[content] src/extension.ts (typescript)
				switch (event.kind) {
					case vscode.TextEditorSelectionChangeKind.Keyboard:
						changeType = ChangeType.SELECTION_KEYBOARD
						break
					case vscode.TextEditorSelectionChangeKind.Mouse:
						changeType = ChangeType.SELECTION_MOUSE
						break
					case vscode.TextEditorSelectionChangeKind.Command:
						changeType = ChangeType.SELECTION_COMMAND
						break
					default:
						throw new TypeError("Unknown selection change kind.")
				}

				recording.sequence++
				const csvRowParams: CSVRowBuilder = {
					sequence: recording.sequence,
					rangeOffset: editor.document.offsetAt(selection.start),
					rangeLength: editor.document.offsetAt(selection.end) - editor.document.offsetAt(selection.start),
					text: selectedText,
					type: changeType,
				}
				addToFileQueue(buildCsvRow(csvRowParams))
				appendToFile()
				actionsProvider.setCurrentFile(editor.document.fileName)
			}
		})
	)
[content] src/extension.ts (typescript)
	// Initialize git provider and set up callback
	setGitActionCallback(handleGitAction)
	initializeGitProvider(context)

	// Create status bar item
	statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, 9000)
	updateStatusBarItem()
	context.subscriptions.push(statusBarItem)

	// Ensure consent is obtained when the extension is first activated
	await ensureConsent()

	// Autostart recording regardless of consent. The consent only gates data upload.
	logToOutput('Autostarting recording...', 'info')
	startRecording().catch((err) => logToOutput(`Autostart recording failed unexpectedly: ${err}`, 'error'))
}

export function deactivate(): void {
	logToOutput('Deactivating crowd-code v2.0', 'info')

	if (recording.isRecording) {
		stopRecording()
	}

	// Cleanup all modules
	cleanupViewportCapture()
	cleanupTerminalCapture()
	cleanupFilesystemWatcher()
	cleanupActionDeduplicator()
	cleanupGitProvider()

	statusBarItem.dispose()
}
[content] src/extension.ts (typescript)
				switch (event.kind) {
					case vscode.TextEditorSelectionChangeKind.Keyboard:
						changeType = ChangeType.SELECTION_KEYBOARD
						break
					case vscode.TextEditorSelectionChangeKind.Mouse:
						changeType = ChangeType.SELECTION_MOUSE
						break
					case vscode.TextEditorSelectionChangeKind.Command:
						changeType = ChangeType.SELECTION_COMMAND
						break
					default:
						throw new TypeError("Unknown selection change kind.")
				}

				recording.sequence++
				const csvRowParams: CSVRowBuilder = {
					sequence: recording.sequence,
					rangeOffset: editor.document.offsetAt(selection.start),
					rangeLength: editor.document.offsetAt(selection.end) - editor.document.offsetAt(selection.start),
					text: selectedText,
					type: changeType,
				}
				addToFileQueue(buildCsvRow(csvRowParams))
				appendToFile()
				actionsProvider.setCurrentFile(editor.document.fileName)
			}
		})
	)

	context.subscriptions.push(
		vscode.window.onDidChangeActiveTerminal((terminal: vscode.Terminal | undefined) => {
			if (terminal && recording.isRecording) {
				if (isCurrentFileExported()) {
					return
				}
				recording.sequence++
				addToFileQueue(
					buildCsvRow({
						sequence: recording.sequence,
						rangeOffset: 0,
						rangeLength: 0,
						text: terminal.name,
						type: ChangeType.TERMINAL_FOCUS,
					})
				)
				appendToFile()
				actionsProvider.setCurrentFile(`Terminal: ${terminal.name}`)
			}
		})
	)

	context.subscriptions.push(
		vscode.window.onDidStartTerminalShellExecution(async (event: vscode.TerminalShellExecutionStartEvent) => {
			if (recording.isRecording) {
				if (isCurrentFileExported()) {
					return
				}
				const commandLine = event.execution.commandLine.value
				recording.sequence++
				addToFileQueue(
					buildCsvRow({
						sequence: recording.sequence,
						rangeOffset: 0,
						rangeLength: 0,
						text: commandLine,
						type: ChangeType.TERMINAL_COMMAND,
					})
				)
				appendToFile()

				const stream = event.execution.read()
				for await (const data of stream) {
					recording.sequence++
					addToFileQueue(
						buildCsvRow({ sequence: recording.sequence, rangeOffset: 0, rangeLength: 0, text: data, type: ChangeType.TERMINAL_OUTPUT })
					)
					appendToFile()
				}
			}
		})
	)
[content] src/extension.ts (typescript)
	// Initialize git provider (detects branch changes, refreshes file cache)
	initializeGitProvider(context)

	// Create status bar item
	statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, 9000)
	updateStatusBarItem()
	context.subscriptions.push(statusBarItem)

	// Ensure consent is obtained when the extension is first activated
	await ensureConsent()

	// Autostart recording regardless of consent. The consent only gates data upload.
	logToOutput('Autostarting recording...', 'info')
	startRecording().catch((err) => logToOutput(`Autostart recording failed unexpectedly: ${err}`, 'error'))
}

export function deactivate(): void {
	logToOutput('Deactivating crowd-code v2.0', 'info')

	if (recording.isRecording) {
		stopRecording()
	}

	// Cleanup all modules
	cleanupViewportCapture()
	cleanupTerminalCapture()
	cleanupFilesystemWatcher()
	cleanupActionDeduplicator()
	cleanupGitProvider()

	statusBarItem.dispose()
}
[tab] src/capture/index.ts (typescript)
/**
 * Capture Module Index
 * Exports all capture-related functionality
 */

// Viewport capture
export {
	setObservationCallback,
	captureViewportState,
	captureObservation,
	captureNow,
	initializeViewportCapture,
	cleanupViewportCapture,
	resetObservationState
} from './viewportCapture'

// Terminal capture
export {
	getTerminalStates,
	getTerminalState,
	setTerminalCallbacks,
	initializeTerminalCapture,
	cleanupTerminalCapture,
	resetTerminalState
} from './terminalCapture'

// Filesystem watcher
export {
	setFileChangeCallback,
	getCachedContent,
	computeContentHash,
	updateCacheFromDocument,
	initializeFilesystemWatcher,
	cleanupFilesystemWatcher,
	resetFilesystemState,
	refreshCache
} from './filesystemWatcher'

// Action deduplicator
export {
	recordUserAction,
	isDuplicateAction,
	recordUserActionFromContent,
	isDuplicateFromContent,
	initializeActionDeduplicator,
	cleanupActionDeduplicator,
	resetActionDeduplicator,
	getTrackedActionCount
} from './actionDeduplicator'
[content] src/types.ts (typescript)
export enum ChangeType {
	HEADING = 'heading',
	CONTENT = 'content',
	TAB = 'tab',
	TERMINAL_FOCUS = 'terminal_focus',
	TERMINAL_COMMAND = 'terminal_command',
	TERMINAL_OUTPUT = 'terminal_output',
	SELECTION_KEYBOARD = 'selection_keyboard',
	SELECTION_MOUSE = 'selection_mouse',
	SELECTION_COMMAND = 'selection_command',
	GIT_BRANCH_CHECKOUT = 'git_branch_checkout',
[content] src/types.ts (typescript)
	sessionId: string
	events: RecordingEvent[]
}

// ============================================================================
// Deduplication Types
// ============================================================================

export interface RecentAction {
	timestamp: number
	contentHash: string
}

// ============================================================================
// File Cache Types
// ============================================================================

export interface CachedFileContent {
	content: string
	timestamp: number
[content] src/types.ts (typescript)
// ============================================================================
// Recording State
// ============================================================================

export interface RecordingState {
[content] src/types.ts (typescript)
	timestamp: number
	type: 'action'
	action: Action
}

export type RecordingEvent = ObservationEvent | ActionEvent

// ============================================================================
// Recording Session Types
// ============================================================================

export interface RecordingSession {
	version: '2.0'
	sessionId: string
[content] src/types.ts (typescript)
}

export interface EditAction {
	kind: 'edit'
	source: ActionSource
	file: string
	diff: EditDiff
}

export interface SelectionAction {
	kind: 'selection'
	source: ActionSource
	file: string
	selectionStart: CursorPosition
	selectionEnd: CursorPosition
	selectedText: string
}

export interface TabSwitchAction {
	kind: 'tab_switch'
	source: ActionSource
	file: string
	previousFile: string | null
}

export interface TerminalFocusAction {
	kind: 'terminal_focus'
	source: ActionSource
	terminalId: string
	terminalName: string
}

export interface TerminalCommandAction {
	kind: 'terminal_command'
	source: ActionSource
	terminalId: string
	terminalName: string
	command: string
}

export interface TerminalOutputAction {
	kind: 'terminal_output'
	source: ActionSource
	terminalId: string
	terminalName: string
	output: string
}

export interface FileChangeAction {
	kind: 'file_change'
	source: ActionSource
	file: string
	changeType: 'create' | 'change' | 'delete'
	diff: string | null
}

export interface GitAction {
	kind: 'git'
	source: ActionSource
	operation: 'checkout' | 'stash' | 'pull' | 'merge' | 'other'
	details: string
}

export interface ScrollAction {
	kind: 'scroll'
	source: ActionSource
	file: string
}

export type Action =
	| EditAction
	| SelectionAction
	| TabSwitchAction
	| TerminalFocusAction
	| TerminalCommandAction
	| TerminalOutputAction
	| FileChangeAction
	| GitAction
	| ScrollAction

// ============================================================================
// Event Types
// ============================================================================

export interface ObservationEvent {
	sequence: number
	timestamp: number
	type: 'observation'
	observation: Observation
[content] src/types.ts (typescript)
export interface TerminalState {
	id: string
	name: string
	recentHistory: TerminalEntry[]
}

// ============================================================================
// Observation Types
// ============================================================================

export interface Observation {
	viewport: ViewportState | null
	activeTerminals: TerminalState[]
}

// ============================================================================
// Action Types
// ============================================================================

export interface EditDiff {
[content] src/types.ts (typescript)
// ============================================================================
// Terminal Types
// ============================================================================

export interface TerminalEntry {
	type: 'command' | 'output'
	content: string
	timestamp: number
[content] src/types.ts (typescript)
// ============================================================================
// Action Sources
// ============================================================================

export type ActionSource = 'user' | 'external'

// ============================================================================
// Viewport Types
// ============================================================================

export interface CursorPosition {
	line: number
	character: number
}

export interface ViewportState {
	file: string
	startLine: number
	endLine: number
[content] src/types.ts (typescript)
/**
 * crowd-code 2.0 Type Definitions
 * Observation-Action schema for state-based capture
 */

// ============================================================================
// Consent Types
// ============================================================================
[content] src/capture/actionDeduplicator.ts (typescript)
/**
 * Action Deduplicator Module
 * Prevents double-capturing of user actions that are seen by both
 * VS Code API and filesystem watcher
 */

import type { RecentAction } from '../types'
import * as crypto from 'node:crypto' 

const DEDUP_WINDOW_MS = 500

// Recent user actions tracked for deduplication
// Key: file path, Value: recent action info
const recentUserActions = new Map<string, RecentAction>()

// Cleanup interval
let cleanupIntervalId: NodeJS.Timeout | null = null
const CLEANUP_INTERVAL_MS = 5000 // Clean up old entries every 5 seconds

/**
 * Check if a filesystem event is a duplicate of a recent user action
 * Returns true if the event should be skipped (it's a duplicate)
 */
export function isDuplicateAction(filePath: string, contentHash: string): boolean {
	const recent = recentUserActions.get(filePath)
	if (!recent) {
		return false
	}

	const timeDiff = Date.now() - recent.timestamp
	if (timeDiff >= DEDUP_WINDOW_MS) {
		// Outside deduplication window
		return false
	}

	// Within window and same content hash = duplicate
	return recent.contentHash === contentHash
}

/**
 * Record a user action from a document change event
 * This marks the action so that duplicate filesystem events can be detected
 */
export function recordUserActionFromContent(filePath: string, content: string): void {
	const hash = crypto.createHash('md5').update(content).digest('hex')
	recentUserActions.set(filePath, {
		timestamp: Date.now(),
		contentHash: hash
	})
}

/**
 * Check if a filesystem event is a duplicate using file content
 * Convenience method that computes the hash from content
 */
export function isDuplicateFromContent(filePath: string, content: string): boolean {
	const hash = crypto.createHash('md5').update(content).digest('hex')
	return isDuplicateAction(filePath, hash)
}

/**
 * Clean up old entries from the deduplication map
 */
function cleanupOldEntries(): void {
	const now = Date.now()
	const expiredPaths: string[] = []

	for (const [filePath, action] of recentUserActions) {
		if (now - action.timestamp > DEDUP_WINDOW_MS * 2) {
			expiredPaths.push(filePath)
		}
	}

	for (const path of expiredPaths) {
		recentUserActions.delete(path)
	}
}

/**
 * Initialize the action deduplicator
 */
export function initializeActionDeduplicator(): void {
	// Start periodic cleanup
	cleanupIntervalId = setInterval(cleanupOldEntries, CLEANUP_INTERVAL_MS)
}

/**
 * Cleanup the action deduplicator
 */
export function cleanupActionDeduplicator(): void {
	if (cleanupIntervalId) {
		clearInterval(cleanupIntervalId)
		cleanupIntervalId = null
	}
	recentUserActions.clear()
}

/**
 * Reset the deduplicator state
 */
export function resetActionDeduplicator(): void {
	recentUserActions.clear()
}

/**
 * Get the number of tracked actions (for debugging/testing)
 */
export function getTrackedActionCount(): number {
	return recentUserActions.size
}
[content] src/recording.ts (typescript)
	// Skip if this is a side effect of a recent user interaction
	// (VS Code edits, terminal commands, etc. all update lastUserInteractionTime)
	const timeSinceLastInteraction = Date.now() - lastUserInteractionTime
	if (timeSinceLastInteraction < USER_INTERACTION_WINDOW_MS) {
		return
[content] src/recording.ts (typescript)
	// Check if this is a duplicate of a user action
	if (changeType === 'change') {
		// Read current content to check for deduplication
		try {
			const content = fs.readFileSync(file, 'utf-8')
			if (isDuplicateFromContent(file, content)) {
				// This filesystem event is a duplicate of a recent VS Code API event
				return
			}
		} catch {
			// File might not exist or be readable
		}
[content] src/recording.ts (typescript)
	const isWithinWindow = timeSinceLastInteraction < USER_INTERACTION_WINDOW_MS
	const isUserCausedChange = isWithinWindow && (lastUserEditedFile === null || lastUserEditedFile === relativePath)

	if (isUserCausedChange) {
[content] src/gitProvider.ts (typescript)
    if (isCurrentFileExported()) {
        logToOutput('Current file is exported, skipping git checkout event', 'info')
        return
    }

    recording.sequence++
    const checkoutMessage = `Switched from branch '${previousState.branch}' to '${newState.branch}'`
    
    logToOutput(`Recording git checkout: ${checkoutMessage}`, 'info')
    
    addToFileQueue(
        buildCsvRow({
            sequence: recording.sequence,
            rangeOffset: 0,
            rangeLength: 0,
            text: checkoutMessage,
            type: ChangeType.GIT_BRANCH_CHECKOUT,
        })
    )
    appendToFile()
    
    // Reset the file cache since files might have different content on the new branch
    logToOutput('Resetting file cache due to branch checkout', 'info')
    if (recording.activatedFiles) {
        recording.activatedFiles.clear()
    }
[content] src/gitProvider.ts (typescript)
function updateGitState(): void {
    getCurrentGitBranchFromCommand().then(branchName => {
        if (branchName) {
            const workspaceFolder = vscode.workspace.workspaceFolders?.[0]
            const repository = workspaceFolder?.uri.fsPath || 'unknown'
            
            const newState = { branch: branchName, repository }
            logToOutput(`Initial git state: ${newState}`, 'info')
            
            lastKnownBranch = branchName
            currentGitState = newState
        }
    }).catch(error => {
        logToOutput(`Error getting initial git branch: ${error}`, 'error')
    })
}

/**
 * Gets the current git branch using git command
 */
async function getCurrentGitBranchFromCommand(): Promise<string | null> {
    try {
        const workspaceFolder = vscode.workspace.workspaceFolders?.[0]
        if (!workspaceFolder) {
            return null
        }
        
        const execAsync = util.promisify(child_process.exec)
        const { stdout } = await execAsync('git branch --show-current', { 
            cwd: workspaceFolder.uri.fsPath 
        })
        return stdout.trim()
    } catch (error) {
        logToOutput(`Error executing git command: ${error}`, 'error')
        return null
    }
}

/**
 * Handles branch checkout events
 */
function handleBranchCheckout(newState: LocalGitState, previousState: LocalGitState): void {
    if (!recording.isRecording) {
        logToOutput('Not recording, skipping git checkout event', 'info')
        return
    }
[content] src/gitProvider.ts (typescript)
    try {
        const workspaceFolder = vscode.workspace.workspaceFolders?.[0]
        if (!workspaceFolder) {
            logToOutput('No workspace folder found', 'info')
            return
        }
        
        // Check if this is a git repository
        const gitDir = vscode.Uri.joinPath(workspaceFolder.uri, '.git')
        try {
            await vscode.workspace.fs.stat(gitDir)
            logToOutput('Git repository found', 'info')
            
            // Get initial state
            updateGitState()
            
            // Set up periodic checking for branch changes
            gitStateCheckInterval = setInterval(() => {
                if (recording.isRecording) {
                    checkForBranchChanges()
                }
            }, 5000) // Check every 5 seconds when recording
            
            // Watch for changes in .git/HEAD file
            gitHeadWatcher = vscode.workspace.createFileSystemWatcher(
                new vscode.RelativePattern(workspaceFolder, '.git/HEAD')
            )
            
            gitHeadWatcher.onDidChange(() => {
                logToOutput('Git HEAD file changed, checking for branch checkout...', 'info')
                setTimeout(() => checkForBranchChanges(), 100) // Small delay to ensure file is written
            })
            
            gitWatcherInitialized = true
            logToOutput('Git provider initialized successfully', 'info')
        } catch (error) {
            logToOutput(`Not a git repository: ${error}`, 'error') 
        }
        
    } catch (error) {
        logToOutput(`Error initializing git provider: ${error}`, 'error')
    }
}

/**
 * Checks for branch changes
 */
async function checkForBranchChanges(): Promise<void> {
    try {
        const newBranch = await getCurrentGitBranchFromCommand()
        if (newBranch && newBranch !== lastKnownBranch) {
            const workspaceFolder = vscode.workspace.workspaceFolders?.[0]
            const repository = workspaceFolder?.uri.fsPath || 'unknown'
            
            const newState = { branch: newBranch, repository }
            
            if (lastKnownBranch) {
                logToOutput(`Branch checkout detected: ${lastKnownBranch} -> ${newBranch}`, 'info')
                handleBranchCheckout(newState, { branch: lastKnownBranch, repository })
            }
            
            lastKnownBranch = newBranch
            currentGitState = newState
        }
    } catch (error) {
        logToOutput(`Error checking for branch changes: ${error}`, 'error')

    }
[content] src/gitProvider.ts (typescript)
/**
 * Initializes the git detection using file system watchers and git commands
 */
export function initializeGitProvider(): void {
    logToOutput('Initializing git provider using file system watchers...', 'info')
    
    // Try to initialize immediately
    tryInitializeGitProvider().catch(error => {
        logToOutput(`Error in initial git provider initialization: ${error}`, 'error')
    })
    
    // Also try after a delay in case git is not ready yet
    setTimeout(() => {
        if (!gitWatcherInitialized) {
            logToOutput('Retrying git provider initialization...', 'info')
            tryInitializeGitProvider().catch(error => {
                logToOutput(`Error in retry git provider initialization: ${error}`, 'error')
            })
        }
    }, 2000)
    
    // Listen for workspace changes
    vscode.workspace.onDidChangeWorkspaceFolders(() => {
        if (!gitWatcherInitialized) {
            logToOutput('Workspace folders changed, retrying git provider initialization...', 'info')
            tryInitializeGitProvider().catch(error => {
                logToOutput(`Error in workspace change git provider initialization: ${error}`, 'error')
            })
        }
    })
[content] src/gitProvider.ts (typescript)
export function cleanupGitProvider(): void {
	if (gitStateCheckInterval) {
		clearInterval(gitStateCheckInterval)
		gitStateCheckInterval = undefined
	}

	if (gitHeadWatcher) {
		gitHeadWatcher.dispose()
		gitHeadWatcher = undefined
	}

	if (gitStashWatcher) {
		gitStashWatcher.dispose()
		gitStashWatcher = undefined
	}

	if (gitRefsWatcher) {
		gitRefsWatcher.dispose()
		gitRefsWatcher = undefined
	}

	currentGitState = null
	gitWatcherInitialized = false
}
[content] src/gitProvider.ts (typescript)
 * Initialize the git provider
 */
export function initializeGitProvider(context: vscode.ExtensionContext): void {
	logToOutput('Initializing git provider...', 'info')

	// Try to initialize immediately
	tryInitializeGitProvider().catch(error => {
		logToOutput(`Error in initial git provider initialization: ${error}`, 'error')
	})

	// Retry after a delay
	setTimeout(() => {
		if (!gitWatcherInitialized) {
			logToOutput('Retrying git provider initialization...', 'info')
			tryInitializeGitProvider().catch(error => {
				logToOutput(`Error in retry git provider initialization: ${error}`, 'error')
			})
		}
	}, 2000)

	// Listen for workspace changes
	context.subscriptions.push(
		vscode.workspace.onDidChangeWorkspaceFolders(() => {
			if (!gitWatcherInitialized) {
				logToOutput('Workspace folders changed, retrying git provider initialization...', 'info')
				tryInitializeGitProvider().catch(error => {
					logToOutput(`Error in workspace change git provider initialization: ${error}`, 'error')
				})
			}
		})
	)
}

/**
 * Cleanup the git provider
[content] src/gitProvider.ts (typescript)
interface RecentGitOperation {
	type: GitOperationType
	timestamp: number
}

// ============================================================================
// State
// ============================================================================

let currentGitState: GitState | null = null
[content] src/gitProvider.ts (typescript)
// ============================================================================
// Types
// ============================================================================

interface GitState {
	branch: string
	repository: string
	lastCommit: string | null
[content] src/gitProvider.ts (typescript)
export function cleanupGitProvider(): void {
	if (gitHeadWatcher) {
		gitHeadWatcher.dispose()
		gitHeadWatcher = undefined
	}

	if (gitRefsWatcher) {
		gitRefsWatcher.dispose()
		gitRefsWatcher = undefined
	}

	sawHeadChange = false
	lastGitOperationTime = 0
	gitWatcherInitialized = false
}
[content] src/gitProvider.ts (typescript)
 * Check if there was a recent git operation
 * Returns 'git_checkout' if HEAD changed, 'git' for other operations, null otherwise
 */
export function getRecentGitOperation(): 'git' | 'git_checkout' | null {
	if (Date.now() - lastGitOperationTime > GIT_OPERATION_WINDOW_MS) {
		return null
	}
	const result = sawHeadChange ? 'git_checkout' : 'git'
	sawHeadChange = false // reset for next operation
	return result
}

/**
 * Initialize the git provider
 */
export function initializeGitProvider(context: vscode.ExtensionContext): void {
	logToOutput('Initializing git provider...', 'info')

	const workspaceFolder = vscode.workspace.workspaceFolders?.[0]
	if (!workspaceFolder) {
		logToOutput('No workspace folder found', 'info')
		return
	}

	// Check if this is a git repository
	const gitDir = vscode.Uri.joinPath(workspaceFolder.uri, '.git')
	vscode.workspace.fs.stat(gitDir).then(
		() => {
			logToOutput('Git repository found', 'info')

			// Watch .git/HEAD for branch changes (checkout)
			gitHeadWatcher = vscode.workspace.createFileSystemWatcher(
				new vscode.RelativePattern(workspaceFolder, '.git/HEAD')
			)
			gitHeadWatcher.onDidChange(() => {
				logToOutput('Git HEAD changed (checkout detected)', 'info')
				sawHeadChange = true
				lastGitOperationTime = Date.now()
			})

			// Watch .git/refs for other git operations (commit, pull, stash, etc.)
			gitRefsWatcher = vscode.workspace.createFileSystemWatcher(
				new vscode.RelativePattern(workspaceFolder, '.git/refs/**/*')
			)
			gitRefsWatcher.onDidChange(() => {
				logToOutput('Git refs changed', 'info')
				lastGitOperationTime = Date.now()
			})
			gitRefsWatcher.onDidCreate(() => {
				logToOutput('Git refs created', 'info')
				lastGitOperationTime = Date.now()
			})
			gitRefsWatcher.onDidDelete(() => {
				logToOutput('Git refs deleted', 'info')
				lastGitOperationTime = Date.now()
			})

			gitWatcherInitialized = true
			logToOutput('Git provider initialized successfully', 'info')
		},
		() => {
			logToOutput('Not a git repository', 'info')
		}
	)

	// Listen for workspace changes
	context.subscriptions.push(
		vscode.workspace.onDidChangeWorkspaceFolders(() => {
			if (!gitWatcherInitialized) {
				logToOutput('Workspace folders changed, reinitializing git provider...', 'info')
				initializeGitProvider(context)
			}
		})
	)
}

/**
 * Cleanup the git provider
[content] src/gitProvider.ts (typescript)
let gitRefsWatcher: vscode.FileSystemWatcher | undefined

// ============================================================================
// Public API
// ============================================================================
[content] src/gitProvider.ts (typescript)
    if (gitStateCheckInterval) {
        clearInterval(gitStateCheckInterval)
        gitStateCheckInterval = undefined
    }

    if (gitHeadWatcher) {
        gitHeadWatcher.dispose()
        gitHeadWatcher = undefined
    }
}
[content] src/gitProvider.ts (typescript)
	// Reinitialize on workspace changes (different .git directory)
	context.subscriptions.push(
		vscode.workspace.onDidChangeWorkspaceFolders(() => {
			logToOutput('Workspace folders changed, reinitializing git provider...', 'info')
			cleanupGitProvider()
			initializeGitProvider(context)
		})
	)
}

/**
 * Cleanup the git provider
 */
export function cleanupGitProvider(): void {
	if (gitHeadWatcher) {
		gitHeadWatcher.dispose()
		gitHeadWatcher = undefined
	}

	if (gitRefsWatcher) {
		gitRefsWatcher.dispose()
		gitRefsWatcher = undefined
	}

	sawHeadChange = false
	lastGitOperationTime = 0
	gitWatcherInitialized = false
}
[content] src/gitProvider.ts (typescript)
}

function disposeWatchers(): void {
	gitHeadWatcher?.dispose()
	gitHeadWatcher = undefined
	gitRefsWatcher?.dispose()
	gitRefsWatcher = undefined
}

/**
 * Initialize the git provider
 */
export function initializeGitProvider(context: vscode.ExtensionContext): void {
	logToOutput('Initializing git provider...', 'info')

	setupGitWatchers()

	// Reinitialize on workspace changes
	context.subscriptions.push(
		vscode.workspace.onDidChangeWorkspaceFolders(() => {
			logToOutput('Workspace changed, reinitializing git provider...', 'info')
			setupGitWatchers()
		})
	)
}

/**
 * Cleanup the git provider
 */
export function cleanupGitProvider(): void {
	disposeWatchers()
	sawHeadChange = false
	lastGitOperationTime = 0
}
[content] src/recording.ts (typescript)
	// Set up callbacks
	setObservationCallback(handleScrollObservation)
	setTerminalCallbacks(handleTerminalFocus, handleTerminalCommand, handleTerminalOutput)
	await initializeFilesystemWatcher(extContext)
[content] src/recording.ts (typescript)
	// Initialize capture modules with callbacks
	initializeViewportCapture(extContext, handleScrollObservation)
	initializeTerminalCapture(extContext, {
		onFocus: handleTerminalFocus,
		onCommand: handleTerminalCommand,
		onOutput: handleTerminalOutput,
	})
	await initializeFilesystemWatcher(extContext, handleFileChange)