refactor(ipc): modularize IPC handlers into separate modules#26
refactor(ipc): modularize IPC handlers into separate modules#26newbe36524 merged 1 commit intomainfrom
Conversation
Refactor monolithic IPC handler code into separate, focused modules for better maintainability and code organization. - Create dedicated handler modules for window, server, webService, version, dependency, packageSource, license, onboarding, dataDirectory, region, LLM, RSS, debug, and view operations - Add shared types for handler responses and error handling - Centralize handler exports in handlers/index.ts - Update preload API to support new handler structure - Integrate new handler system in main.ts Co-Authored-By: Hagicode <noreply@hagicode.com>
📝 WalkthroughWalkthroughThis PR removes the entire UI/UX Pro Max skill system (including BM25 search, design system generation, and CLI scripts) and introduces comprehensive data directory management across the Electron application, spanning main process, IPC handlers, path validation, configuration persistence, and renderer UI. Changes
Sequence Diagram(s)sequenceDiagram
participant Renderer as Renderer
participant IPC as IPC Layer
participant PathMgr as PathManager
participant ConfigMgr as ConfigManager
participant FS as File System
Renderer->>IPC: data-directory:set(path)
IPC->>PathMgr: validatePath(path)
PathMgr->>FS: check absolute, writability
PathMgr-->>IPC: ValidationResult
IPC->>ConfigMgr: setDataDirectoryPath(path)
ConfigMgr->>FS: persist to appsettings.yml
FS-->>ConfigMgr: ✓
IPC->>PathMgr: getStorageInfo(path)
PathMgr->>FS: calculateDirectorySize()
FS-->>PathMgr: StorageInfo
PathMgr-->>IPC: StorageInfo
IPC-->>Renderer: { success, error?, storageInfo }
Renderer->>Renderer: Update Redux state
Renderer->>Renderer: Render UI with validation + storage
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
Note
Due to the large number of review comments, Critical severity comments were prioritized as inline comments.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/main/llm-installation-manager.ts (1)
216-220:⚠️ Potential issue | 🟡 MinorHardcoded Chinese error message should use i18n.
The error message
'无法找到可用的终端模拟器'is hardcoded in Chinese. For consistency with the application's internationalization, this should be a translatable string or an error code that the renderer can translate.🌐 Suggested fix
return { success: false, - error: '无法找到可用的终端模拟器', + error: 'NO_TERMINAL_EMULATOR_FOUND', };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/llm-installation-manager.ts` around lines 216 - 220, Replace the hardcoded Chinese error string in the returned object (the block returning { success: false, error: '无法找到可用的终端模拟器' }) with a translatable value: either call the app i18n helper (e.g., i18n.t('errors.no_terminal_emulator') or t('errors.no_terminal_emulator')) or return a stable error code (e.g., { success: false, errorCode: 'NO_TERMINAL_EMULATOR' }) so the renderer can localize it; update llm-installation-manager.ts accordingly and ensure any new error key is added to the translation files or handled by the renderer.
🟠 Major comments (17)
src/main/web-service-manager.ts-1441-1444 (1)
1441-1444:⚠️ Potential issue | 🟠 MajorClear
DataDirwhen no directory is configured.If
getDataDirectory()returns empty/undefined, the previousDataDirstays inappsettings.yml, so the service can keep using stale path configuration.Suggested fix
const dataDir = this.pathManager.getDataDirectory(); if (dataDir) { config.DataDir = dataDir; log.info('[WebService] Syncing data directory to config:', dataDir); + } else { + delete config.DataDir; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/web-service-manager.ts` around lines 1441 - 1444, When getDataDirectory() returns empty/undefined the previous config.DataDir value is left unchanged causing stale paths to persist; update the logic around getDataDirectory() (where config and DataDir are handled) so that when dataDir is falsy you explicitly clear config.DataDir (set to undefined or empty string) and log that the DataDir was cleared (use the same log object, e.g. log.info) instead of only setting it when dataDir is present.src/renderer/components/settings/DataDirectorySettings.tsx-38-43 (1)
38-43: 🛠️ Refactor suggestion | 🟠 MajorRemove debug logging before merging.
This
useEffectlogs i18n state on every render. Remove or guard behind a development flag before production:🧹 Proposed fix
- // Debug: Check i18n initialization - useEffect(() => { - console.log('[DataDirectorySettings] t function:', typeof t); - console.log('[DataDirectorySettings] i18n ready:', !!t); - console.log('[DataDirectorySettings] Sample translation:', t('settings.title')); - }, [t]);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/renderer/components/settings/DataDirectorySettings.tsx` around lines 38 - 43, Remove the debug logging inside the useEffect in DataDirectorySettings: delete the three console.log calls that reference t (the i18n function) or wrap them in a development-only guard (e.g., if (process.env.NODE_ENV !== 'production') { ... }) so they do not run in production; ensure the useEffect still lists [t] as its dependency and contains no leftover debug code.src/main/ipc/handlers/windowHandlers.ts-68-117 (1)
68-117:⚠️ Potential issue | 🟠 MajorValidate URL scheme before loading external content.
loadURL(url)accepts any string. A malicious or misconfigured call could loadfile://orjavascript:URLs. Validate the URL scheme:🛡️ Proposed URL validation
ipcMain.handle('open-hagicode-in-app', async (_, url: string) => { if (!url) { console.error('[WindowHandlers] No URL provided for open-hagicode-in-app'); return false; } + + // Validate URL scheme + try { + const parsedUrl = new URL(url); + if (!['http:', 'https:'].includes(parsedUrl.protocol)) { + console.error('[WindowHandlers] Invalid URL protocol:', parsedUrl.protocol); + return false; + } + } catch { + console.error('[WindowHandlers] Invalid URL format:', url); + return false; + } + try { console.log('[WindowHandlers] Opening Hagicode in app window:', url);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/ipc/handlers/windowHandlers.ts` around lines 68 - 117, In the ipcMain.handle('open-hagicode-in-app') handler, validate the incoming url before calling hagicodeWindow.loadURL: parse the url (using the URL constructor or a safe parser) and ensure its protocol is only 'http:' or 'https:' (reject 'file:', 'javascript:', data:, etc.), log an error and return false for invalid schemes; perform this check early (before creating the BrowserWindow) and only call loadURL(url) when the scheme is allowed to prevent loading local or executable content.src/main/ipc/handlers/windowHandlers.ts-87-94 (1)
87-94:⚠️ Potential issue | 🟠 MajorAdd URL validation for security defense-in-depth.
While
sandbox: falsereduces renderer isolation, the security risk is mitigated here because:
- The preload script safely exposes only IPC channels—no dangerous Node.js APIs (fs, child_process, os, etc.) are exposed
- The URL is sourced internally from
webServiceManager.getStatus().url, which returns localhostHowever, add explicit URL validation in the handler to enforce localhost-only loading as a defensive practice:
const isLocalhost = url.startsWith('http://localhost:') || url.startsWith('http://127.0.0.1:'); if (!isLocalhost) { console.error('[WindowHandlers] URL must be localhost:', url); return false; }This prevents potential issues if the URL source ever changes.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/ipc/handlers/windowHandlers.ts` around lines 87 - 94, Add explicit localhost-only URL validation in the window creation/loading handler (use the same URL from webServiceManager.getStatus().url) before calling BrowserWindow.loadURL or similar; check that the URL starts with 'http://localhost:' or 'http://127.0.0.1:' (e.g., const isLocalhost = url.startsWith('http://localhost:') || url.startsWith('http://127.0.0.1:');) and if not, log an error (console.error or processLogger.error) and return false to abort loading; update the handler that creates/loads the BrowserWindow (the function that references preloadPath/webPreferences and calls loadURL) to include this guard.src/main/llm-installation-manager.ts-126-140 (1)
126-140:⚠️ Potential issue | 🟠 MajorRemove
shell: truefrom Windows spawn call to prevent command injection.The
promptvariable is constructed fromfileName(line 120), which is extracted from the manifest'sllmPromptpath. Whilepath.basename()appears to extract just the filename, it preserves shell metacharacters (backticks,$(), etc.). Withshell: true, these characters would be interpreted by cmd.exe on Windows, allowing command injection if an attacker can control or modify the manifest file.Since the command and arguments are already separated as an array,
shell: falseis both safer and functionally equivalent. Remove theshell: trueoption to eliminate the shell interpretation vector.Recommended fix
if (platform === 'win32') { // Windows: Directly spawn claude process try { log.info('[LlmInstallationManager] Windows command:', `claude "${prompt}"`); spawn('claude', [prompt], { detached: true, stdio: 'ignore', cwd: promptDir, - shell: true, }).unref();🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/llm-installation-manager.ts` around lines 126 - 140, In the Windows branch of LlmInstallationManager where you spawn 'claude' using the prompt variable (derived from fileName/llmPrompt), remove the shell: true option from the spawn options so the command is executed without a shell; keep the argument array ([prompt]), detached, stdio: 'ignore', cwd: promptDir and the .unref() call intact to preserve behavior while preventing shell interpretation of prompt (and thus command injection).src/main/ipc/handlers/dataDirectoryHandlers.ts-113-120 (1)
113-120:⚠️ Potential issue | 🟠 MajorDo not return success when
appsettings.ymlsync fails.Line 113-118 and Line 180-185 log YAML update failures but still return
success: true, which can hide config drift.🛠️ Suggested fix
- try { - const updatedVersions = await state.yamlConfigManager.updateAllDataDirs(dataDirPath); - log.info('[DataDirectoryHandlers] Updated appsettings.yml for versions:', updatedVersions); - } catch (error) { - log.warn('[DataDirectoryHandlers] Failed to update appsettings.yml:', error); - } - - return { success: true }; + try { + const updatedVersions = await state.yamlConfigManager.updateAllDataDirs(dataDirPath); + log.info('[DataDirectoryHandlers] Updated appsettings.yml for versions:', updatedVersions); + } catch (error) { + log.warn('[DataDirectoryHandlers] Failed to update appsettings.yml:', error); + return { + success: false, + error: 'Data directory updated, but appsettings.yml synchronization failed' + }; + } + + return { success: true };Also applies to: 180-187
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/ipc/handlers/dataDirectoryHandlers.ts` around lines 113 - 120, The handler currently always returns { success: true } even when state.yamlConfigManager.updateAllDataDirs(...) fails; change the try/catch in the handler(s) so that on catch you do not return success: true — either rethrow the error or return { success: false, error: String(error) } (include the error message) instead, and ensure both occurrences that call updateAllDataDirs (the block around updateAllDataDirs(...) and the second block at lines ~180-187) are updated to propagate failure rather than reporting success; keep log.warn(...) but make the response reflect the failure.src/main/ipc/handlers/packageSourceHandlers.ts-63-63 (1)
63-63:⚠️ Potential issue | 🟠 MajorType
configIPC payloads explicitly to avoidanyleakage.Lines 63 and 107 accept untyped
configpayloads on critical config mutation and validation paths, violating strict type safety requirements. Existing type definitions forPackageSourceConfig(and variantsLocalFolderConfig,GitHubReleaseConfig,HttpIndexConfig) are available insrc/main/package-sources/package-source.tsand can be imported and applied directly.🛠️ Suggested fix
import { ipcMain, BrowserWindow } from 'electron'; import { VersionManager } from '../../version-manager.js'; +import type { PackageSourceConfig } from '../../package-sources/package-source.js'; // Module state interface PackageSourceHandlerState { versionManager: VersionManager | null; mainWindow: BrowserWindow | null; } ... - ipcMain.handle('package-source:set-config', async (_, config) => { + ipcMain.handle('package-source:set-config', async (_, config: PackageSourceConfig) => { if (!state.versionManager) { return { success: false, error: 'Version manager not initialized' }; } try { const success = await state.versionManager.setSourceConfig(config);And for line 107:
- ipcMain.handle('package-source:validate-config', async (_, config) => { + ipcMain.handle('package-source:validate-config', async (_, config: PackageSourceConfig) => { if (!state.versionManager) { return { valid: false, error: 'Version manager not initialized' }; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/ipc/handlers/packageSourceHandlers.ts` at line 63, Import the explicit config types (PackageSourceConfig and/or the specific variants LocalFolderConfig, GitHubReleaseConfig, HttpIndexConfig) from the package-source type definitions and annotate the IPC handler parameters so the untyped `config` is no longer `any`: change the handler signature for the 'package-source:set-config' ipcMain.handle from async (_, config) => to async (_event, config: PackageSourceConfig) => (or the appropriate union of variant types) and do the same for the validation handler on the validation path (the handler around line 107) so both mutation and validation code paths use the imported, explicit types.src/main/ipc/handlers/webServiceHandlers.ts-130-147 (1)
130-147:⚠️ Potential issue | 🟠 MajorReturn shape mismatch:
{ success: result }incorrectly nests aStartResultobject.Line 146 wraps the entire
StartResultobject (which contains{ success: boolean; resultSession; parsedResult; url?; port? }), but the renderer expects{ success: boolean; error?: {...}; warning?: {...} }. This creates{ success: StartResult }instead of a flat boolean, breaking the IPC contract and violating TypeScript strict mode type safety.🛠️ Suggested fix
- return { success: result }; + return result;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/ipc/handlers/webServiceHandlers.ts` around lines 130 - 147, The handler currently returns the entire StartResult object as { success: result } which nests StartResult instead of returning the flat IPC shape; change the return to extract and return result.success as a boolean and map any error/warning fields from StartResult into the top-level response (e.g., return { success: result.success, error: result.error?.… , warning: result.warning?.… } or omit when absent), ensuring the returned shape matches the renderer contract and update related typings if necessary; locate the call site around state.webServiceManager.start() / StartResult and adjust the return value and types accordingly.src/main/main.ts-2148-2154 (1)
2148-2154:⚠️ Potential issue | 🟠 MajorApply data-directory changes atomically.
configManager.setDataDirectoryPath(dataDirPath)runs beforepathManager.setDataDirectory(dataDirPath). If the second step throws, persisted config and in-memory state diverge.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/main.ts` around lines 2148 - 2154, Make the data-directory update atomic: set the in-memory value on PathManager first and only persist to electron-store after that succeeds, or if you prefer to persist first then rollback the in-memory state on failure; specifically, call pathManager.setDataDirectory(dataDirPath) before configManager.setDataDirectoryPath(dataDirPath) and wrap the persistence step in a try/catch that on failure restores the previous PathManager value (capture previous via pathManager.getDataDirectory() or equivalent) and logs the error so persisted and in-memory state cannot diverge.src/main/main.ts-24-39 (1)
24-39:⚠️ Potential issue | 🟠 MajorModular IPC handlers are imported but not actually wired.
This file still registers the legacy monolithic handlers directly, and these new
register*Handlersimports are never invoked. That leaves the refactor unintegrated and creates two drifting handler implementations.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/main.ts` around lines 24 - 39, The newly imported modular handlers (registerWindowHandlers, registerServerHandlers, registerWebServiceHandlers, registerVersionHandlers, registerDependencyHandlers, registerPackageSourceHandlers, registerLicenseHandlers, registerOnboardingHandlers, registerDataDirectoryHandlers, registerRegionHandlers, registerLlmHandlers, registerRssHandlers, registerDebugHandlers, registerViewHandlers) are never invoked; call each of these register*Handlers during the app initialization where the legacy monolithic handler registration currently happens (e.g., inside your main init/start function) so the modular handlers are wired up, and remove or replace the legacy monolithic registration to avoid duplicate handler implementations.src/renderer/store/slices/dataDirectorySlice.ts-176-188 (1)
176-188:⚠️ Potential issue | 🟠 Major
isLoadingStoragecan staytrueforever after errors.
fetchStorageInfosets loading totrue, but the catch path throws without resetting it. Add afinallyreset (or rejected extraReducer) so the spinner always clears.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/renderer/store/slices/dataDirectorySlice.ts` around lines 176 - 188, In fetchStorageInfo ensure isLoadingStorage is cleared on all paths: after dispatch(dataDirectorySlice.actions.setLoadingStorage(true)) add a finally (or an extraReducer for the rejected case) that dispatches dataDirectorySlice.actions.setLoadingStorage(false) so the spinner is always reset; keep existing error handling that dispatches setSaveError and re-throws, but guarantee setLoadingStorage(false) runs whether the await calls in fetchStorageInfo, window.electronAPI.dataDirectory.get(), or window.electronAPI.dataDirectory.getStorageInfo(currentPath) succeed or fail.src/main/path-manager.ts-415-419 (1)
415-419:⚠️ Potential issue | 🟠 Major
getStorageInfocomputes total capacity from directory metadata.Using directory
stat.sizeas disk total produces invalidavailableandusedPercentagevalues. Compute volume totals from filesystem-level stats (or return unknown totals) instead of deriving from directory entry size.Also applies to: 423-430
src/renderer/store/slices/dataDirectorySlice.ts-100-103 (1)
100-103:⚠️ Potential issue | 🟠 MajorSave/restore errors are being cleared immediately.
Failure paths set
saveError, then callsetSaving(false), butsetSavingresetssaveErrortonull. The UI loses the actual failure message.🛠️ Proposed fix
setSaving: (state, action: PayloadAction<boolean>) => { state.isSaving = action.payload; - state.saveError = null; },- dispatch(dataDirectorySlice.actions.setSaveError(errorMessage)); - dispatch(dataDirectorySlice.actions.setSaving(false)); + dispatch(dataDirectorySlice.actions.setSaveError(errorMessage));- dispatch(dataDirectorySlice.actions.setSaveError(errorMessage)); - dispatch(dataDirectorySlice.actions.setSaving(false)); + dispatch(dataDirectorySlice.actions.setSaveError(errorMessage));Also applies to: 165-167, 218-220
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/renderer/store/slices/dataDirectorySlice.ts` around lines 100 - 103, The setSaving reducer in dataDirectorySlice (setSaving) currently sets state.isSaving and unconditionally clears state.saveError, which wipes out errors set by failure paths; change setSaving to only update state.isSaving and do not modify state.saveError, and instead rely on the dedicated setSaveError reducer to clear or set errors when appropriate—update any callsites that expect setSaving(false) to clear errors to call setSaveError(null) explicitly if they truly intend to clear the error; apply the same change pattern to the other similar reducers/usages in this slice (the other setSaving-like occurrences).src/main/main.ts-1997-2000 (1)
1997-2000:⚠️ Potential issue | 🟠 MajorGuard startup against invalid persisted data-directory values.
pathManager.setDataDirectory(...)can throw; this call is currently unguarded during startup. A stale/invalid stored path can abort app launch instead of falling back safely.🛠️ Proposed hardening
- if (dataDirectoryPath) { - pathManager.setDataDirectory(dataDirectoryPath); - } + if (dataDirectoryPath) { + try { + pathManager.setDataDirectory(dataDirectoryPath); + } catch (error) { + const fallbackPath = pathManager.getDefaultDataDirectory(); + log.warn('[Config] Invalid data directory, falling back to default:', error); + pathManager.setDataDirectory(fallbackPath); + configManager.setDataDirectoryPath(fallbackPath); + dataDirectoryPath = fallbackPath; + } + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/main.ts` around lines 1997 - 2000, Wrap the call to pathManager.setDataDirectory(dataDirectoryPath) in a try/catch that validates dataDirectoryPath before and catches any thrown errors from setDataDirectory; on error, log the failure (including the error) and do not rethrow so startup can continue with the default/fallback path, and ensure any subsequent use of pathManager expects the default state if setDataDirectory failed.src/preload/index.ts-57-68 (1)
57-68:⚠️ Potential issue | 🟠 MajorFix
packageSourceinterface to match the runtime implementation.The interface (lines 57-68) declares
saveConfig,removeSource, andgetCurrentConfig, but the runtime object (lines 146-162) usessetConfiginstead ofsaveConfigand omitsremoveSourceandgetCurrentConfigentirely. This creates a type-safety gap where code compiles against a contract that doesn't exist at runtime.
- Replace
saveConfigwithsetConfigin the interface definition- Remove
removeSourceandgetCurrentConfigfrom the interface, or implement them in the runtime- Consider adding
onConfigChangeto the interface if it is a stable API🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/preload/index.ts` around lines 57 - 68, The packageSource interface does not match the runtime implementation: replace the saveConfig member with setConfig (to match the runtime's setConfig function), remove the removeSource and getCurrentConfig members from the interface unless you implement corresponding runtime functions, and optionally add an onConfigChange member if you plan to expose that stable API; update the packageSource interface signatures (getConfig, getAllConfigs, setConfig, switchSource, validateConfig, scanFolder, fetchGithub, fetchHttpIndex and optionally onConfigChange) so they exactly match the runtime object’s method names and return types.src/main/ipc/handlers/versionHandlers.ts-273-276 (1)
273-276:⚠️ Potential issue | 🟠 MajorExpose a typed method in
VersionManagerto update the default channel instead of accessing private properties.The code uses
(state.versionManager as any).packageSourceConfigManagerto bypass type safety and access a private property. Add a public method toVersionManagerlikeupdateSourceDefaultChannel(sourceId: string, channel: string): voidto encapsulate this operation and maintain strict type safety.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/ipc/handlers/versionHandlers.ts` around lines 273 - 276, Add a typed public method on VersionManager (e.g., updateSourceDefaultChannel(sourceId: string, channel: string): void) that internally calls this.packageSourceConfigManager.updateSource(sourceId, { defaultChannel: channel }); then replace the casty usage in the handler (state.versionManager as any).packageSourceConfigManager.updateSource(...) with state.versionManager.updateSourceDefaultChannel(currentConfig.id, channel) so callers use the new public API and no private property is accessed directly.src/preload/index.ts-84-85 (1)
84-85:⚠️ Potential issue | 🟠 MajorUpdate
restoreDefaultreturn type to discriminated union for type safety.The implementation correctly returns either
{ success: true; path: string }or{ success: false; error: string }, but the type definition incorrectly declares{ success: boolean; path: string }, makingpathalways required and omitting theerrorfield. Renderer code accessesresult.errorwhen handling failures, which violates strict type checking.🛠️ Proposed type fix
- restoreDefault: () => Promise<{ success: boolean; path: string }>; + restoreDefault: () => Promise< + | { success: true; path: string } + | { success: false; error: string } + >;As per coding guidelines:
src/**/*.{ts,tsx}: Maintain strict type safety with TypeScript strict mode enabled.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/preload/index.ts` around lines 84 - 85, The exported preload type for restoreDefault is wrong: update the declaration of restoreDefault in src/preload/index.ts to use a discriminated union matching the implementation (either { success: true; path: string } or { success: false; error: string }) so callers can narrow on success and safely access path or error; leave openPicker as-is but ensure restoreDefault's signature replaces the current { success: boolean; path: string } shape with the two-branch union using the success boolean literal types.
🟡 Minor comments (3)
src/renderer/i18n/index.ts-53-56 (1)
53-56:⚠️ Potential issue | 🟡 MinorFix misleading namespace debug output.
Line 56 currently logs language keys as namespaces.
Object.keys(i18n.store.data)returns locales (e.g.,en-US), not namespace names.Suggested fix
.then(() => { // Log successful initialization for debugging console.log('[i18n] Initialized successfully'); console.log('[i18n] Current language:', i18n.language); - console.log('[i18n] Available namespaces:', Object.keys(i18n.store.data)); + const currentNamespaces = Object.keys(i18n.store.data[i18n.language] ?? {}); + console.log('[i18n] Available namespaces:', currentNamespaces); })🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/renderer/i18n/index.ts` around lines 53 - 56, The debug message is logging locales instead of namespaces; change the third console.log so it derives namespaces from i18n.store.data by fetching the namespace keys for the active locale (e.g., use Object.keys(i18n.store.data[i18n.language] || {})) or compute the union of namespace keys across locales, and log that result; update the log in the i18n initialization block that references i18n and i18n.store.data so it prints actual namespace names rather than locale keys.src/main/llm-installation-manager.ts-141-152 (1)
141-152:⚠️ Potential issue | 🟡 MinorIncomplete escaping for osascript may allow injection.
Line 144 escapes
"and$, but other characters like backticks, single quotes, or backslashes could still cause issues in the AppleScript/shell context. Consider using a more robust escaping approach or validating the prompt content.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/llm-installation-manager.ts` around lines 141 - 152, The macOS branch in LlmInstallationManager builds constructedCommand using escapedPrompt but only escapes double quotes and dollar signs, leaving backticks, single quotes and backslashes vulnerable to injection when calling execAsync; fix by robustly sanitizing or safely quoting the prompt before embedding in the osascript string (e.g., replace/backslash-escape backslashes, single quotes, backticks and newlines or use a safe quoting helper), or validate/whitelist the prompt content, and update the construction of constructedCommand (and escapedPrompt) so the final osascript string is safe before calling execAsync.src/main/ipc/handlers/debugHandlers.ts-37-50 (1)
37-50:⚠️ Potential issue | 🟡 MinorSilent no-op when
configManageris null.If
state.configManageris null, the handler silently succeeds without actually persisting or broadcasting the debug mode. Consider returning an error to inform the caller:🛡️ Proposed fix
ipcMain.handle('set-debug-mode', async (_, mode: { ignoreDependencyCheck: boolean }) => { try { + if (!state.configManager) { + return { + success: false, + error: 'ConfigManager not initialized', + }; + } const storeKey = 'debugMode'; - state.configManager?.getStore().set(storeKey, mode); + state.configManager.getStore().set(storeKey, mode); state.mainWindow?.webContents.send('debug-mode-changed', mode); return { success: true };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/ipc/handlers/debugHandlers.ts` around lines 37 - 50, The handler registered with ipcMain.handle('set-debug-mode') currently silently no-ops if state.configManager is null; update the handler in debugHandlers.ts to explicitly check state.configManager before calling getStore(): if it's missing, return { success: false, error: 'configManager not available' } (or similar) instead of proceeding, and only call state.configManager.getStore().set(...) and state.mainWindow?.webContents.send('debug-mode-changed', mode) when configManager exists; keep the existing try/catch for other errors but ensure the null-configManager case is handled and reported to the caller.
🧹 Nitpick comments (14)
src/renderer/components/SettingsPage.tsx (1)
5-5: Use renderer path alias for this import.Please switch this relative renderer import to
@/*alias for consistency with project rules.Suggested fix
-import { DataDirectorySettings } from './settings/DataDirectorySettings'; +import { DataDirectorySettings } from '@/components/settings/DataDirectorySettings';As per coding guidelines
src/renderer/**/*.{ts,tsx}: Use@/* path aliases for imports within the renderer process.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/renderer/components/SettingsPage.tsx` at line 5, In SettingsPage.tsx replace the relative import of DataDirectorySettings with the renderer path alias; specifically change the './settings/DataDirectorySettings' import used in the SettingsPage component to '@/components/settings/DataDirectorySettings' so it follows the src/renderer `@/`* alias convention.src/main/ipc/handlers/rssHandlers.ts (1)
17-59: Make handler registration idempotent.If this register function is called more than once,
ipcMain.handlecan collide on existing channels.Suggested fix
export function registerRssHandlers(manager: RSSFeedManager | null): void { rssFeedManager = manager; + ipcMain.removeHandler('rss-get-feed-items'); + ipcMain.removeHandler('rss-refresh-feed'); + ipcMain.removeHandler('rss-get-last-update'); // RSS get feed items handler ipcMain.handle('rss-get-feed-items', async () => {#!/bin/bash # Verify call sites and absence/presence of de-registration guards rg -nP "registerRssHandlers\s*\(" --type=ts -C2 rg -nP "ipcMain\.removeHandler\('rss-(get-feed-items|refresh-feed|get-last-update)'\)" --type=ts -C2🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/ipc/handlers/rssHandlers.ts` around lines 17 - 59, The registerRssHandlers function registers ipcMain.handle on three channels and will collide if called multiple times; make it idempotent by removing any existing handlers before registering: call ipcMain.removeHandler for 'rss-get-feed-items', 'rss-refresh-feed', and 'rss-get-last-update' at the start of registerRssHandlers (while still assigning rssFeedManager = manager) so each invocation first clears previous handlers and then re-registers the handlers that call rssFeedManager.getFeedItems, rssFeedManager.refreshFeed, and rssFeedManager.getLastUpdateTime; ensure error handling remains unchanged.src/main/ipc/handlers/viewHandlers.ts (3)
1-1: Remove unusedBrowserWindowimport.
BrowserWindowis imported but never used in this module.🧹 Proposed fix
-import { ipcMain, BrowserWindow } from 'electron'; +import { ipcMain } from 'electron';🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/ipc/handlers/viewHandlers.ts` at line 1, Remove the unused BrowserWindow import from the top-level import statement in viewHandlers.ts: update the import that currently reads "import { ipcMain, BrowserWindow } from 'electron';" to only import the actually used symbol(s) (e.g., "ipcMain") so BrowserWindow is no longer imported but leave other imports unchanged.
38-73: Consider using sharedHandlerResponsetypes for consistency.The
switch-viewhandler returns ad-hoc response objects with varying shapes (reason,canStart,url,error). Using the sharedHandlerResponse<T>from./types.tswould provide consistency across handlers and better type safety on the renderer side.This is a low-priority suggestion since the current approach works, but aligning with the shared types would improve maintainability.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/ipc/handlers/viewHandlers.ts` around lines 38 - 73, The handler for ipcMain.handle('switch-view') currently returns ad-hoc shapes; import and use the shared HandlerResponse<T> from ./types.ts and annotate the handler's return type accordingly (e.g., HandlerResponse<{ url?: string; canStart?: boolean }>), then replace the various return objects so they conform to that type (map 'success'/'reason'/'error' into the HandlerResponse fields and include url or canStart in the data payload when present); ensure the error catch returns a HandlerResponse with success: false and a standardized error/message field and that the webServiceManager.getStatus branch returns success: true with data.url when running.
75-79: Stub implementation returns hardcoded value.
get-current-viewalways returns'system'regardless of actual view state. The comment acknowledges this needs tracking, but consider adding a TODO or tracking issue to avoid forgetting.Would you like me to open an issue to track implementing proper view state tracking?
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/ipc/handlers/viewHandlers.ts` around lines 75 - 79, The IPC handler ipcMain.handle('get-current-view') currently returns the hardcoded string 'system'; replace this stub with real view-state tracking by adding a mutable state (e.g., currentView) in the same module and a complementary ipcMain.handle('set-current-view') or ipcMain.on('view-changed') handler that updates currentView when the renderer notifies the main process, then have get-current-view return currentView (and add a TODO/comment referencing an issue if you want to postpone full implementation). Ensure you reference the existing handler name ('get-current-view') and add the update handler name ('set-current-view' or 'view-changed') so future reviewers can find the change.src/main/config.ts (1)
128-168: Inconsistent accessor patterns for directory getters.
getShutdownDirectory,getRecordingDirectory, andgetLogsDirectoryusethis.store.get()with type assertions, whilegetDataDirectoryPathuses the typedthis.get()method. Use the consistent pattern for type safety:♻️ Proposed fix for consistency
getShutdownDirectory(): string | undefined { - return this.store.get('shutdownDirectory') as string | undefined; + return this.get('shutdownDirectory'); } getRecordingDirectory(): string | undefined { - return this.store.get('recordingDirectory') as string | undefined; + return this.get('recordingDirectory'); } getLogsDirectory(): string | undefined { - return this.store.get('logsDirectory') as string | undefined; + return this.get('logsDirectory'); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/config.ts` around lines 128 - 168, The getters getShutdownDirectory, getRecordingDirectory, and getLogsDirectory use this.store.get(...) with type assertions which is inconsistent with getDataDirectoryPath; change each to use the class helper this.get('key') to get a properly typed value (e.g., this.get('shutdownDirectory')), remove the manual type assertions, and keep the corresponding setters setShutdownDirectory, setRecordingDirectory, and setLogsDirectory unchanged; update the three getter implementations so they return string | undefined via this.get(...) for consistent type safety.src/renderer/components/settings/DataDirectorySettings.tsx (2)
62-67: Intentionally omittinglocalPathfrom dependency array needs clarification.The effect syncs
localPathwith the globalpath, butlocalPathis intentionally excluded to prevent infinite loops. Add a comment to clarify this intentional omission, or use a ref to track the previous path value:// eslint-disable-next-line react-hooks/exhaustive-deps -- localPath excluded to prevent sync loops🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/renderer/components/settings/DataDirectorySettings.tsx` around lines 62 - 67, In DataDirectorySettings, the useEffect that syncs localPath with path deliberately omits localPath from the dependency array to avoid update loops; update the code to make this intent explicit by adding a clarifying comment above the effect (or use a ref to track previous path if preferred) and, if keeping the omission, add an eslint disable comment such as: eslint-disable-next-line react-hooks/exhaustive-deps with an explanation referencing the useEffect in DataDirectorySettings to prevent accidental future changes.
94-108: Add type narrowing for thunk result payload.Line 101 accesses
validationResult?.payload?.isValidwithout proper type narrowing. The thunk result type could befulfilledorrejected, and the payload shape differs:🛡️ Proposed fix with type guard
if (result && !result.canceled && result.filePath) { - // Validate the selected path const validationResult = await dispatch(validatePath(result.filePath)); - if (validationResult?.payload?.isValid) { + if ( + validatePath.fulfilled.match(validationResult) && + validationResult.payload?.isValid + ) { setLocalPath(result.filePath); } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/renderer/components/settings/DataDirectorySettings.tsx` around lines 94 - 108, The code in handleBrowse reads validationResult?.payload?.isValid without narrowing the thunk result; update the logic that calls validatePath so you only read payload when the thunk succeeded—either by calling unwrapResult(validatePath(...)) (from `@reduxjs/toolkit`) or by checking validationResult?.type === 'fulfilled' before accessing validationResult.payload.isValid, then call setLocalPath(result.filePath) only when the payload.isValid is true; ensure any rejected result path is handled (or ignored) to satisfy TypeScript.src/main/ipc/handlers/llmHandlers.ts (1)
99-117: Usepath.joinfor cross-platform path construction.Line 103 uses string template for path concatenation (
${versionPath}/manifest.json). While this often works on Windows with Node.js APIs, usingpath.joinis more robust and idiomatic:♻️ Proposed fix
+import path from 'node:path';At line 103:
- const manifestPath = `${versionPath}/manifest.json`; + const manifestPath = path.join(versionPath, 'manifest.json');🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/ipc/handlers/llmHandlers.ts` around lines 99 - 117, Replace the string-template path concatenation with path.join to build manifest paths cross-platform: inside the ipcMain.handle('llm:get-manifest-path') callback where you call PathManager.getInstance() and getInstalledVersionPath(versionId), import Node's path module if not already present, compute manifestPath using path.join(versionPath, 'manifest.json') instead of `${versionPath}/manifest.json`, and keep the same return/error handling in the handler.src/main/ipc/handlers/types.ts (1)
29-32: Consider a tuple-based signature for heterogeneous arguments.The
...args: T[]rest parameter constrains all arguments to be the same typeT. Handlers with multiple arguments of different types (e.g.,(event, path: string, options: object)) won't type-check correctly with this signature.If handlers need heterogeneous args, consider:
export type IpcHandler<TArgs extends unknown[] = unknown[], R = unknown> = ( event: Electron.IpcMainInvokeEvent, ...args: TArgs ) => Promise<R>;However, if all current handlers use single or homogeneous arguments, the current definition is acceptable.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/ipc/handlers/types.ts` around lines 29 - 32, Change the IpcHandler type to support heterogeneous argument tuples: replace the single-type generic T and the rest param ...args: T[] in the IpcHandler definition with a tuple-style generic (e.g., TArgs extends unknown[] = unknown[]) and use ...args: TArgs so handlers can declare varied argument types; update the exported type name IpcHandler<TArgs extends unknown[] = unknown[], R = unknown> and the function signature to accept event: Electron.IpcMainInvokeEvent, ...args: TArgs => Promise<R>.src/main/ipc/handlers/debugHandlers.ts (1)
53-62: Type assertion without validation could cause runtime issues.Line 56 uses
as { ignoreDependencyCheck: boolean }without validating the stored value's shape. If the config store contains malformed data (e.g., from a schema migration), this could propagate invalid data to callers.Consider adding a runtime check or using a validation library:
🛡️ Proposed defensive check
ipcMain.handle('get-debug-mode', async () => { try { const storeKey = 'debugMode'; - const mode = state.configManager?.getStore().get(storeKey, { ignoreDependencyCheck: false }) as { ignoreDependencyCheck: boolean }; - return mode; + const stored = state.configManager?.getStore().get(storeKey, { ignoreDependencyCheck: false }); + const mode = typeof stored === 'object' && stored !== null && typeof (stored as any).ignoreDependencyCheck === 'boolean' + ? stored as { ignoreDependencyCheck: boolean } + : { ignoreDependencyCheck: false }; + return mode; } catch (error) {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/ipc/handlers/debugHandlers.ts` around lines 53 - 62, The handler registered with ipcMain.handle('get-debug-mode') currently casts the retrieved value using "as { ignoreDependencyCheck: boolean }" without validating it; replace that unsafe assertion by fetching via state.configManager?.getStore().get(storeKey) and perform a runtime shape check (ensure the result is an object and that typeof result.ignoreDependencyCheck === 'boolean'); if the check passes return the object, otherwise log a warning and return the safe default { ignoreDependencyCheck: false } — update the code paths in the get-debug-mode handler (references: ipcMain.handle('get-debug-mode'), state.configManager?.getStore().get) to implement this validation and fallback.src/renderer/store/index.ts (1)
13-13: Use renderer alias import for the new slice.Replace the relative import with the configured
@/*alias pattern.🛠️ Suggested fix
-import dataDirectoryReducer from './slices/dataDirectorySlice'; +import dataDirectoryReducer from '@/store/slices/dataDirectorySlice';As per coding guidelines,
src/renderer/**/*.{ts,tsx}: Use@/*path aliases for imports within the renderer process.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/renderer/store/index.ts` at line 13, Replace the relative import of the new slice in src/renderer/store/index.ts with the renderer alias pattern; locate the import that brings in dataDirectoryReducer and change it to use the configured "@/..." alias (keeping the same module/specifier that points to the slice file) so the import uses the renderer alias rather than a relative path.src/main/ipc/handlers/serverHandlers.ts (1)
7-15: UseServerStatusinstead of rawstringfor status callbacks.The file imports
ServerStatus(line 2) but usesstringfor status callback parameters on lines 7, 14, and 25, weakening type safety. Replace all three occurrences with the importedServerStatustype to maintain strict typing as required by your coding guidelines.🛠️ Suggested fix
-let serverStatusSetter: ((status: string) => void) | null = null; +let serverStatusSetter: ((status: ServerStatus) => void) | null = null; ... export function initServerHandlers( client: HagicoServerClient | null, - setStatus: (status: string) => void + setStatus: (status: ServerStatus) => void ): void { ... export function registerServerHandlers( client: HagicoServerClient | null, - setServerStatusFn?: (status: string) => void + setServerStatusFn?: (status: ServerStatus) => void ): void {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/ipc/handlers/serverHandlers.ts` around lines 7 - 15, The status callback types are declared as raw strings; change them to use the imported ServerStatus type for stronger typing: update the serverStatusSetter declaration ((status: string) => void) | null to ((status: ServerStatus) => void) | null, change the setStatus parameter in initServerHandlers from (status: string) => void to (status: ServerStatus) => void, and update any other function/type signatures in this file that accept a status string (e.g., the callback referenced around line 25) so they all use ServerStatus consistently.src/main/ipc/handlers/dependencyHandlers.ts (1)
139-142: Avoid duplicate dependency status recomputation after installs.Both install flows run
checkVersionDependencies(versionId)twice in sequence. Reuse a single computed result before emittingdependency-status-changedto avoid redundant I/O.Also applies to: 219-222
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/ipc/handlers/dependencyHandlers.ts` around lines 139 - 142, The code calls state.versionManager.checkVersionDependencies(versionId) twice causing redundant work; capture its result once (e.g., const updatedDependencies = await state.versionManager.checkVersionDependencies(versionId)) and reuse that single result for any subsequent logic and the state.mainWindow?.webContents.send('dependency-status-changed', updatedDependencies) emission in the install flows (the first occurrence near the install handler and the second occurrence referenced later around the other install flow), replacing the duplicate call with the stored variable so the dependency status is computed only once.
ℹ️ Review info
Configuration used: defaults
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (25)
.claude/skills/ui-ux-pro-max/data/charts.csvis excluded by!**/*.csv.claude/skills/ui-ux-pro-max/data/colors.csvis excluded by!**/*.csv.claude/skills/ui-ux-pro-max/data/icons.csvis excluded by!**/*.csv.claude/skills/ui-ux-pro-max/data/landing.csvis excluded by!**/*.csv.claude/skills/ui-ux-pro-max/data/products.csvis excluded by!**/*.csv.claude/skills/ui-ux-pro-max/data/react-performance.csvis excluded by!**/*.csv.claude/skills/ui-ux-pro-max/data/stacks/astro.csvis excluded by!**/*.csv.claude/skills/ui-ux-pro-max/data/stacks/flutter.csvis excluded by!**/*.csv.claude/skills/ui-ux-pro-max/data/stacks/html-tailwind.csvis excluded by!**/*.csv.claude/skills/ui-ux-pro-max/data/stacks/jetpack-compose.csvis excluded by!**/*.csv.claude/skills/ui-ux-pro-max/data/stacks/nextjs.csvis excluded by!**/*.csv.claude/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csvis excluded by!**/*.csv.claude/skills/ui-ux-pro-max/data/stacks/nuxtjs.csvis excluded by!**/*.csv.claude/skills/ui-ux-pro-max/data/stacks/react-native.csvis excluded by!**/*.csv.claude/skills/ui-ux-pro-max/data/stacks/react.csvis excluded by!**/*.csv.claude/skills/ui-ux-pro-max/data/stacks/shadcn.csvis excluded by!**/*.csv.claude/skills/ui-ux-pro-max/data/stacks/svelte.csvis excluded by!**/*.csv.claude/skills/ui-ux-pro-max/data/stacks/swiftui.csvis excluded by!**/*.csv.claude/skills/ui-ux-pro-max/data/stacks/vue.csvis excluded by!**/*.csv.claude/skills/ui-ux-pro-max/data/styles.csvis excluded by!**/*.csv.claude/skills/ui-ux-pro-max/data/typography.csvis excluded by!**/*.csv.claude/skills/ui-ux-pro-max/data/ui-reasoning.csvis excluded by!**/*.csv.claude/skills/ui-ux-pro-max/data/ux-guidelines.csvis excluded by!**/*.csv.claude/skills/ui-ux-pro-max/data/web-interface.csvis excluded by!**/*.csvpackage-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (34)
.claude/skills/ui-ux-pro-max/SKILL.md.claude/skills/ui-ux-pro-max/scripts/core.py.claude/skills/ui-ux-pro-max/scripts/design_system.py.claude/skills/ui-ux-pro-max/scripts/search.pysrc/main/config-manager.tssrc/main/config.tssrc/main/ipc/handlers/dataDirectoryHandlers.tssrc/main/ipc/handlers/debugHandlers.tssrc/main/ipc/handlers/dependencyHandlers.tssrc/main/ipc/handlers/index.tssrc/main/ipc/handlers/licenseHandlers.tssrc/main/ipc/handlers/llmHandlers.tssrc/main/ipc/handlers/onboardingHandlers.tssrc/main/ipc/handlers/packageSourceHandlers.tssrc/main/ipc/handlers/regionHandlers.tssrc/main/ipc/handlers/rssHandlers.tssrc/main/ipc/handlers/serverHandlers.tssrc/main/ipc/handlers/types.tssrc/main/ipc/handlers/versionHandlers.tssrc/main/ipc/handlers/viewHandlers.tssrc/main/ipc/handlers/webServiceHandlers.tssrc/main/ipc/handlers/windowHandlers.tssrc/main/llm-installation-manager.tssrc/main/main.tssrc/main/path-manager.tssrc/main/web-service-manager.tssrc/preload/index.tssrc/renderer/components/SettingsPage.tsxsrc/renderer/components/settings/DataDirectorySettings.tsxsrc/renderer/i18n/index.tssrc/renderer/i18n/locales/en-US/pages.jsonsrc/renderer/i18n/locales/zh-CN/pages.jsonsrc/renderer/store/index.tssrc/renderer/store/slices/dataDirectorySlice.ts
💤 Files with no reviewable changes (4)
- .claude/skills/ui-ux-pro-max/scripts/design_system.py
- .claude/skills/ui-ux-pro-max/SKILL.md
- .claude/skills/ui-ux-pro-max/scripts/search.py
- .claude/skills/ui-ux-pro-max/scripts/core.py
| /** | ||
| * Get current language | ||
| */ | ||
| getCurrentLanguage(): string | undefined { | ||
| return this.store.get('language') as string | undefined; | ||
| } | ||
|
|
||
| /** | ||
| * Set current language | ||
| */ | ||
| setCurrentLanguage(language: string): void { | ||
| this.store.set('language', language); | ||
| } |
There was a problem hiding this comment.
Language accessors read/write wrong config key - schema mismatch.
getCurrentLanguage() and setCurrentLanguage() access 'language' at the root level, but AppConfig defines language under settings.language. This will not read/write the correct value:
🐛 Proposed fix
/**
* Get current language
*/
- getCurrentLanguage(): string | undefined {
- return this.store.get('language') as string | undefined;
+ getCurrentLanguage(): string {
+ return this.get('settings').language;
}
/**
* Set current language
*/
setCurrentLanguage(language: string): void {
- this.store.set('language', language);
+ const current = this.get('settings');
+ this.set('settings', { ...current, language });
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/main/config.ts` around lines 170 - 182, The getCurrentLanguage and
setCurrentLanguage methods are using the wrong config key ('language' at root)
so they don't read/write AppConfig.settings.language; update
getCurrentLanguage() to return this.store.get('settings.language') as string |
undefined and update setCurrentLanguage(language: string) to call
this.store.set('settings.language', language) (ensure the methods remain typed
as string | undefined and void respectively).
| await licenseManager.saveLicense(licenseKey); | ||
| return { success: true }; | ||
| } catch (error) { |
There was a problem hiding this comment.
Propagate saveLicense result instead of forcing success.
Line 43 always returns success, even when saveLicense returns { success: false, error } without throwing.
Suggested fix
try {
- await licenseManager.saveLicense(licenseKey);
- return { success: true };
+ return await licenseManager.saveLicense(licenseKey);
} catch (error) {
console.error('Failed to save license:', error);
return {📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| await licenseManager.saveLicense(licenseKey); | |
| return { success: true }; | |
| } catch (error) { | |
| return await licenseManager.saveLicense(licenseKey); | |
| } catch (error) { |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/main/ipc/handlers/licenseHandlers.ts` around lines 42 - 44, The handler
currently always returns { success: true } after calling
licenseManager.saveLicense, hiding failures; change it to await and store the
result from licenseManager.saveLicense(licenseKey) (e.g., const result = await
licenseManager.saveLicense(licenseKey)) and return that result directly so
callers receive { success: false, error } when saveLicense fails, while
preserving the existing catch block behavior for actual thrown errors.
| const invalidChars = /[<>:"|?*]/; | ||
| if (invalidChars.test(dirPath)) { | ||
| return { | ||
| isValid: false, | ||
| message: 'Path contains invalid characters: < > : " | ? *', | ||
| }; |
There was a problem hiding this comment.
Windows absolute paths are incorrectly rejected in validation.
The invalid-character regex includes :, so valid Windows paths like C:\... fail validation.
🐛 Proposed fix (apply to both sync and async validators)
- const invalidChars = /[<>:"|?*]/;
- if (invalidChars.test(dirPath)) {
+ const pathWithoutDrive = process.platform === 'win32'
+ ? dirPath.replace(/^[a-zA-Z]:/, '')
+ : dirPath;
+ const invalidChars = /[<>:"|?*]/;
+ if (invalidChars.test(pathWithoutDrive)) {Also applies to: 339-344
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/main/path-manager.ts` around lines 262 - 267, The validator currently
rejects paths because invalidChars = /[<>:"|?*]/ includes ':' which breaks
Windows drive-letter paths (e.g., C:\...). Update both the sync and async
validators to allow a single colon as the drive-letter separator: instead of
blindly testing invalidChars against dirPath, first check for a Windows drive
prefix like /^[A-Za-z]:[\\/]/ and skip the colon check when that prefix is
present; alternatively construct the invalidChars regex to exclude ':' when
dirPath matches the drive-letter pattern. Adjust the logic around the
invalidChars test and the returned message so Windows absolute paths
(drive-letter) are accepted while other uses of ':' remain rejected.
| import type { Dispatch } from '@reduxjs/toolkit'; | ||
|
|
||
| // App dispatch type combining all slice dispatches | ||
| export type AppDispatch = Dispatch< | ||
| | typeof import('./slices/webServiceSlice').actions | ||
| | typeof import('./slices/i18nSlice').actions | ||
| | typeof import('./slices/dependencySlice').actions | ||
| | typeof import('./slices/viewSlice').actions | ||
| | typeof import('./slices/packageSourceSlice').actions | ||
| | typeof import('./slices/licenseSlice').actions | ||
| | typeof import('./slices/onboardingSlice').actions | ||
| | typeof import('./slices/rssFeedSlice').actions | ||
| | typeof import('./slices/claudeConfigSlice').actions | ||
| | typeof import('./slices/agentCliSlice').actions | ||
| | typeof import('./slices/llmInstallationSlice').actions | ||
| | typeof import('./slices/dataDirectorySlice').actions | ||
| >; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's see the file size and structure
wc -l src/renderer/store/index.tsRepository: HagiCode-org/desktop
Length of output: 95
🏁 Script executed:
# Read the relevant sections: lines 1-50 to see the duplicate AppDispatch
head -50 src/renderer/store/index.ts | cat -nRepository: HagiCode-org/desktop
Length of output: 2882
🏁 Script executed:
# Also read around line 179 to see the second declaration
sed -n '170,185p' src/renderer/store/index.ts | cat -nRepository: HagiCode-org/desktop
Length of output: 600
🏁 Script executed:
# Verify the duplicate AppDispatch will cause TypeScript errors by checking if both declarations are active
rg -n "^export type AppDispatch" src/renderer/store/index.tsRepository: HagiCode-org/desktop
Length of output: 155
🏁 Script executed:
# Check the Redux Toolkit version to confirm the pattern issue
rg -n "@reduxjs/toolkit" package.json | head -5Repository: HagiCode-org/desktop
Length of output: 101
🏁 Script executed:
# Verify the path alias setup in the project
cat tsconfig.json | grep -A 10 "paths"Repository: HagiCode-org/desktop
Length of output: 46
🏁 Script executed:
# Verify RTK dispatch typing best practice by checking imports of AppDispatch elsewhere
rg -n "AppDispatch" src/renderer --type ts --type tsx | head -10Repository: HagiCode-org/desktop
Length of output: 91
🏁 Script executed:
# Check AppDispatch usage in the renderer directory
rg -n "AppDispatch" src/renderer | head -15Repository: HagiCode-org/desktop
Length of output: 1628
Remove the duplicate and incorrect AppDispatch declaration on lines 20–33.
The file has two AppDispatch export declarations: the manual union type at lines 20–33 (incorrect for RTK dispatch typing) and the inferred type at line 179 (typeof store.dispatch). The inferred type is the correct pattern for Redux Toolkit dispatch typing and is used consistently throughout the codebase. Remove the manual declaration to avoid TypeScript compilation errors and maintain proper RTK patterns.
🛠️ Suggested fix
-import type { Dispatch } from '@reduxjs/toolkit';
-
-// App dispatch type combining all slice dispatches
-export type AppDispatch = Dispatch<
- | typeof import('./slices/webServiceSlice').actions
- | typeof import('./slices/i18nSlice').actions
- | typeof import('./slices/dependencySlice').actions
- | typeof import('./slices/viewSlice').actions
- | typeof import('./slices/packageSourceSlice').actions
- | typeof import('./slices/licenseSlice').actions
- | typeof import('./slices/onboardingSlice').actions
- | typeof import('./slices/rssFeedSlice').actions
- | typeof import('./slices/claudeConfigSlice').actions
- | typeof import('./slices/agentCliSlice').actions
- | typeof import('./slices/llmInstallationSlice').actions
- | typeof import('./slices/dataDirectorySlice').actions
->;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| import type { Dispatch } from '@reduxjs/toolkit'; | |
| // App dispatch type combining all slice dispatches | |
| export type AppDispatch = Dispatch< | |
| | typeof import('./slices/webServiceSlice').actions | |
| | typeof import('./slices/i18nSlice').actions | |
| | typeof import('./slices/dependencySlice').actions | |
| | typeof import('./slices/viewSlice').actions | |
| | typeof import('./slices/packageSourceSlice').actions | |
| | typeof import('./slices/licenseSlice').actions | |
| | typeof import('./slices/onboardingSlice').actions | |
| | typeof import('./slices/rssFeedSlice').actions | |
| | typeof import('./slices/claudeConfigSlice').actions | |
| | typeof import('./slices/agentCliSlice').actions | |
| | typeof import('./slices/llmInstallationSlice').actions | |
| | typeof import('./slices/dataDirectorySlice').actions | |
| >; |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/renderer/store/index.ts` around lines 17 - 33, There is a duplicate,
incorrect AppDispatch type declared as a manual union of slice action types;
remove that manual export (the union type block exported as AppDispatch) and
keep the existing inferred dispatch type using typeof store.dispatch (the
AppDispatch defined later from store.dispatch) so the code uses the correct
Redux Toolkit pattern; update any imports that referenced the removed manual
declaration to rely on the inferred AppDispatch from typeof store.dispatch.
Refactor monolithic IPC handler code into separate, focused modules for better maintainability and code organization.
Summary by CodeRabbit