From 950e1ff8a39475943fdbed5d4fe88e30f632d17d Mon Sep 17 00:00:00 2001 From: Mateusz Ruszkowski Date: Tue, 20 Jan 2026 13:29:10 +0100 Subject: [PATCH] fix(frontend): replace require() with ES imports for ESM compatibility Fixes "require is not defined" error in Electron main process caused by using CommonJS require() in ESM context (type: "module" in package.json). Changes: - claude-profile-manager.ts: import os, use os.homedir() - log-service.ts: import unlinkSync from fs - context/utils.ts: import os, use path.join(os.homedir()...) - subprocess-runner.ts: import app from electron at top level - worktree-handlers.ts: import promises as fsPromises from fs Co-Authored-By: Claude Opus 4.5 --- apps/frontend/src/main/claude-profile-manager.ts | 3 ++- apps/frontend/src/main/ipc-handlers/context/utils.ts | 3 ++- .../main/ipc-handlers/github/utils/subprocess-runner.ts | 9 +-------- .../src/main/ipc-handlers/task/worktree-handlers.ts | 6 +----- apps/frontend/src/main/log-service.ts | 4 ++-- 5 files changed, 8 insertions(+), 17 deletions(-) diff --git a/apps/frontend/src/main/claude-profile-manager.ts b/apps/frontend/src/main/claude-profile-manager.ts index 5ef0083e04..76691b24fb 100644 --- a/apps/frontend/src/main/claude-profile-manager.ts +++ b/apps/frontend/src/main/claude-profile-manager.ts @@ -14,6 +14,7 @@ import { app } from 'electron'; import { join } from 'path'; import { mkdir } from 'fs/promises'; +import os from 'os'; import type { ClaudeProfile, ClaudeProfileSettings, @@ -381,7 +382,7 @@ export class ClaudeProfileManager { if (profile?.configDir && !profile.isDefault) { // Expand ~ to home directory for the environment variable const expandedConfigDir = profile.configDir.startsWith('~') - ? profile.configDir.replace(/^~/, require('os').homedir()) + ? profile.configDir.replace(/^~/, os.homedir()) : profile.configDir; env.CLAUDE_CONFIG_DIR = expandedConfigDir; console.warn('[ClaudeProfileManager] Using configDir for profile:', profile.name, expandedConfigDir); diff --git a/apps/frontend/src/main/ipc-handlers/context/utils.ts b/apps/frontend/src/main/ipc-handlers/context/utils.ts index 6611e99740..d6779a4fa2 100644 --- a/apps/frontend/src/main/ipc-handlers/context/utils.ts +++ b/apps/frontend/src/main/ipc-handlers/context/utils.ts @@ -1,5 +1,6 @@ import { app } from 'electron'; import path from 'path'; +import os from 'os'; import { existsSync, readFileSync } from 'fs'; export interface EnvironmentVars { @@ -215,7 +216,7 @@ export interface GraphitiDatabaseDetails { export function getGraphitiDatabaseDetails(projectEnvVars: EnvironmentVars): GraphitiDatabaseDetails { const dbPath = projectEnvVars['GRAPHITI_DB_PATH'] || process.env.GRAPHITI_DB_PATH || - require('path').join(require('os').homedir(), '.auto-claude', 'memories'); + path.join(os.homedir(), '.auto-claude', 'memories'); const database = projectEnvVars['GRAPHITI_DATABASE'] || process.env.GRAPHITI_DATABASE || diff --git a/apps/frontend/src/main/ipc-handlers/github/utils/subprocess-runner.ts b/apps/frontend/src/main/ipc-handlers/github/utils/subprocess-runner.ts index 99d85a5caa..e3b2e457fc 100644 --- a/apps/frontend/src/main/ipc-handlers/github/utils/subprocess-runner.ts +++ b/apps/frontend/src/main/ipc-handlers/github/utils/subprocess-runner.ts @@ -11,6 +11,7 @@ import { promisify } from 'util'; import path from 'path'; import { fileURLToPath } from 'url'; import fs from 'fs'; +import { app } from 'electron'; // ESM-compatible __dirname const __filename = fileURLToPath(import.meta.url); @@ -335,14 +336,6 @@ export function getRunnerPath(backendPath: string): string { * 3. Current working directory */ export function getBackendPath(project: Project): string | null { - // Import app module for production path detection - let app: any; - try { - app = require('electron').app; - } catch { - // Electron not available in tests - } - // Check if this is a development repo (has apps/backend structure) const appsBackendPath = path.join(project.path, 'apps', 'backend'); if (fs.existsSync(path.join(appsBackendPath, 'runners', 'github', 'runner.py'))) { diff --git a/apps/frontend/src/main/ipc-handlers/task/worktree-handlers.ts b/apps/frontend/src/main/ipc-handlers/task/worktree-handlers.ts index 1171b8374a..33726adfed 100644 --- a/apps/frontend/src/main/ipc-handlers/task/worktree-handlers.ts +++ b/apps/frontend/src/main/ipc-handlers/task/worktree-handlers.ts @@ -3,7 +3,7 @@ import { IPC_CHANNELS, AUTO_BUILD_PATHS, DEFAULT_APP_SETTINGS, DEFAULT_FEATURE_M import type { IPCResult, WorktreeStatus, WorktreeDiff, WorktreeDiffFile, WorktreeMergeResult, WorktreeDiscardResult, WorktreeListResult, WorktreeListItem, WorktreeCreatePROptions, WorktreeCreatePRResult, SupportedIDE, SupportedTerminal, AppSettings } from '../../../shared/types'; import path from 'path'; import { minimatch } from 'minimatch'; -import { existsSync, readdirSync, statSync, readFileSync } from 'fs'; +import { existsSync, readdirSync, statSync, readFileSync, promises as fsPromises } from 'fs'; import { execSync, execFileSync, spawn, spawnSync, exec, execFile } from 'child_process'; import { projectStore } from '../../project-store'; import { getConfiguredPythonPath, PythonEnvManager, pythonEnvManager as pythonEnvManagerSingleton } from '../../python-env-manager'; @@ -2196,7 +2196,6 @@ export function registerWorktreeHandlers( const commitMsgPath = path.join(specDir, 'suggested_commit_message.txt'); try { if (existsSync(commitMsgPath)) { - const { promises: fsPromises } = require('fs'); suggestedCommitMessage = (await fsPromises.readFile(commitMsgPath, 'utf-8')).trim(); debug('Read suggested commit message:', suggestedCommitMessage?.substring(0, 100)); } @@ -2219,8 +2218,6 @@ export function registerWorktreeHandlers( planPaths.push({ path: path.join(worktreeSpecDir, AUTO_BUILD_PATHS.IMPLEMENTATION_PLAN), isMain: false }); } - const { promises: fsPromises } = require('fs'); - // Update plan file with retry logic for transient failures // Uses EAFP pattern (try/catch) instead of LBYL (existsSync check) to avoid TOCTOU race conditions const updatePlanWithRetry = async (planPath: string, isMain: boolean): Promise => { @@ -2838,7 +2835,6 @@ export function registerWorktreeHandlers( const planPath = path.join(specDir, AUTO_BUILD_PATHS.IMPLEMENTATION_PLAN); // Use EAFP pattern (try/catch) instead of LBYL (existsSync check) to avoid TOCTOU race conditions - const { promises: fsPromises } = require('fs'); const isFileNotFound = (err: unknown): boolean => !!(err && typeof err === 'object' && 'code' in err && err.code === 'ENOENT'); diff --git a/apps/frontend/src/main/log-service.ts b/apps/frontend/src/main/log-service.ts index 58ba24b7a3..1b64341eaa 100644 --- a/apps/frontend/src/main/log-service.ts +++ b/apps/frontend/src/main/log-service.ts @@ -1,5 +1,5 @@ import path from 'path'; -import { existsSync, mkdirSync, appendFileSync, readdirSync, readFileSync, writeFileSync, statSync } from 'fs'; +import { existsSync, mkdirSync, appendFileSync, readdirSync, readFileSync, writeFileSync, statSync, unlinkSync } from 'fs'; export interface LogSession { sessionId: string; @@ -293,7 +293,7 @@ export class LogService { for (const file of toDelete) { const filePath = path.join(logsDir, file); try { - require('fs').unlinkSync(filePath); + unlinkSync(filePath); console.warn(`[LogService] Deleted old log session: ${file}`); } catch (_e) { // Ignore deletion errors