Skip to content

refactor: extracted 3 more hooks from App.tsx#412

Merged
reachraza merged 6 commits intomainfrom
code-refactor
Feb 18, 2026
Merged

refactor: extracted 3 more hooks from App.tsx#412
reachraza merged 6 commits intomainfrom
code-refactor

Conversation

@reachraza
Copy link
Collaborator

@reachraza reachraza commented Feb 18, 2026

Summary

Continues the App.tsx decomposition (Phases 2E–2G), extracting three more custom hooks to reduce the monolith by ~830 lines (9,556 → 8,767). Each hook is fully self-contained, reads from Zustand stores directly, and has comprehensive unit tests.

useSessionRestoration (405 lines, 49 tests)

Extracts session loading, migration, and corruption recovery into hooks/session/useSessionRestoration.ts:

  • Session & group loading on mount with React Strict Mode guard
  • Legacy field migrations: projectRoot, autoRunFolderPath, fileTreeAutoRefreshInterval
  • Corruption recovery: missing aiTabs → creates default tab with warning; toolType: 'terminal' → migrates to claude-code
  • Git info restoration: local repos checked synchronously, SSH remotes fetched in background
  • Tab state reset: all tabs reset to idle on restart (processes don't survive app restart)
  • initialLoadComplete proxy ref bridging .current API to store boolean

useInputKeyDown (319 lines, 47 tests)

Extracts the main input keyboard event handler into hooks/input/useInputKeyDown.ts:

  • Tab completion dropdown navigation (ArrowUp/Down, Tab filter cycling, Enter/Escape)
  • @ mention completion dropdown navigation and insertion
  • Slash command autocomplete with mode-aware filtering (AI-only vs terminal-only commands)
  • Enter-to-send logic respecting per-mode settings (AI vs terminal) read at call time via getState()
  • Cmd+F → output search, Escape → blur input, ArrowUp → command history

useAppInitialization (291 lines, 43 tests)

Extracts all one-time startup effects into hooks/ui/useAppInitialization.ts:

  • Splash screen coordination (waits for settings + sessions)
  • GitHub CLI availability check
  • Windows warning modal (with suppression + once-only guard)
  • File gist URLs loading/saving (persisted to settings)
  • Beta updates sync to electron-updater
  • Update check on startup (2s delay, modal if available)
  • Leaderboard stats sync from server (3s delay, merges if server has higher values)
  • SpecKit + OpenSpec command loading
  • SSH remote configs loading
  • Stats DB corruption check (warning toast)
  • Notification settings sync (toast duration, audio feedback, OS notifications)
  • Playground debug function (window.playground())

Key design decisions

  • Zero-parameter hooks: All three hooks read from Zustand stores via selectors or getState() — no callback dependencies passed in from App.tsx. This eliminated hook ordering constraints and stale closure risks.
  • getState() inside callbacks/effects: Leaderboard sync, standing ovation checks, and enter-to-send all read settings at call time rather than closing over reactive values, preventing stale closures.
  • Proxy ref for initialLoadComplete: Bridges the legacy ref.current = true API to useSessionStore.setInitialLoadComplete() so both patterns stay in sync without a migration.

Stats

Metric Value
Lines removed from App.tsx ~830
New hook files 3
New test files 3
New unit tests 139 (49 + 47 + 43)
Total tests passing 20,119
tsc --noEmit Clean

Test plan

  • Startup: App launches, splash screen dismisses, agents restored in Left Bar with correct state
  • Session migration: Agents without projectRoot / autoRunFolderPath get defaults on restart
  • Corruption recovery: Agent with missing tabs recovers with default tab + warning log
  • Git info: Local repo shows branch indicator; SSH remote agent loads git info in background
  • Input — Enter to send: Sends in AI mode (Enter), terminal mode (Enter), respects Shift+Enter for newlines
  • Input — Tab completion: Terminal mode partial path → Tab opens dropdown → Arrow/Enter navigates/selects
  • Input — @ mentions: AI mode @ opens dropdown → filter/select inserts mention
  • Input — Slash commands: / opens menu → mode-specific filtering → Tab/Enter selects
  • Input — Escape: Blurs input, focuses terminal output
  • Input — Cmd+F: Opens output search bar
  • GitHub CLI: PR creation available when gh is installed/authenticated, disabled otherwise
  • Notification settings: Changing toast duration / audio feedback / OS notifications takes effect immediately

Summary by CodeRabbit

  • Refactor

    • Startup initialization moved into a dedicated initialization flow for more reliable app startup, update checks, GitHub CLI detection, gist URL handling, and Windows warning handling.
    • Keyboard input handling centralized to improve Tab/Enter/Escape behavior, command autocomplete, mentions, and history navigation.
    • Session restoration consolidated to improve migration, corruption recovery, and SSH/git background syncing.
  • Tests

    • Added extensive tests covering initialization, input key handling, and session restoration flows.

Move session restoration, migration, corruption recovery, background git
info fetching, and session/group loading into a dedicated hook. App.tsx
reduced from 9,556 to 9,191 lines (−365).
Move input keyboard handling (tab completion, @ mentions, slash commands,
enter-to-send, command history, escape) into a dedicated hook. App.tsx
reduced from 9,191 to 9,018 lines (−173).
Move 14 startup effects into a dedicated hook: splash screen coordination,
GitHub CLI check, Windows warning, gist URLs, beta updates sync, update
check, leaderboard sync, SpecKit/OpenSpec loading, SSH configs, stats DB
check, notification settings sync, and playground debug. The hook reads
directly from Zustand stores, eliminating the handleSyncAutoRunStats
dependency by inlining the store update. App.tsx reduced by ~253 lines
(9,018 → 8,765). 43 new tests added (20,118 total, all passing).
@coderabbitai
Copy link

coderabbitai bot commented Feb 18, 2026

No actionable comments were generated in the recent review. 🎉


📝 Walkthrough

Walkthrough

Extracts startup and input logic from App.tsx into three new public hooks (useAppInitialization, useSessionRestoration, useInputKeyDown) and adds extensive unit tests validating initialization, session restoration, and input key handling across many success and failure paths.

Changes

Cohort / File(s) Summary
App refactor & hook usage
src/renderer/App.tsx
Removed inline startup/input logic and wired App to useAppInitialization, useSessionRestoration, and useInputKeyDown. Dropped direct SpecKitCommand / OpenSpecCommand type imports.
New UI init hook
src/renderer/hooks/ui/useAppInitialization.ts, src/renderer/hooks/ui/index.ts
New hook centralizing startup effects (splash teardown, gh CLI checks, Windows warning modal, gist URL load/save, beta/update sync, update checks, leaderboard sync, SpecKit/OpenSpec loading, SSH configs, stats DB init, notification sync). Exposes AppInitializationReturn (ghCliAvailable, sshRemoteConfigs, speckitCommands, openspecCommands, saveFileGistUrl).
New session restoration hook
src/renderer/hooks/session/useSessionRestoration.ts, src/renderer/hooks/session/index.ts
New hook for loading/migrating/restoring persisted sessions/groups on mount: migrations, corruption recovery (default AI tab creation), runtime resets, toolType migrations, background git info fetches, and exposes SessionRestorationReturn including initialLoadComplete and restoreSession.
New input keydown hook
src/renderer/hooks/input/useInputKeyDown.ts, src/renderer/hooks/input/index.ts
New hook implementing centralized input keyboard handling: tab completion, @-mention, slash commands, enter-to-send logic (mode/modifiers), command history navigation, global shortcuts, focus/escape flows. Exports InputKeyDownDeps/InputKeyDownReturn and handleInputKeyDown.
Tests: app init, session restoration, input key handling
src/__tests__/renderer/hooks/useAppInitialization.test.ts, src/__tests__/renderer/hooks/useSessionRestoration.test.ts, src/__tests__/renderer/hooks/useInputKeyDown.test.ts
Adds comprehensive tests covering splash coordination, gh CLI states, Windows warnings, gist URL persistence, update checks, leaderboard sync, spec command loading, SSH config loading, session migration/corruption/recovery, background git info, and extensive input key interactions with many edge cases.
Public exports updated
src/renderer/hooks/input/index.ts, src/renderer/hooks/session/index.ts, src/renderer/hooks/ui/index.ts
Re-exports added for the three new hooks and their return/type interfaces, expanding the public hooks surface.

Sequence Diagram(s)

sequenceDiagram
  participant App
  participant InitHook as useAppInitialization
  participant SessionHook as useSessionRestoration
  participant InputHook as useInputKeyDown
  participant Settings as SettingsStore
  participant Maestro as window.maestro

  App->>SessionHook: mount -> call useSessionRestoration()
  App->>InitHook: mount -> call useAppInitialization()
  App->>InputHook: mount -> call useInputKeyDown()
  InitHook->>Settings: read settings / check loaded state
  SessionHook->>Settings: load sessions/groups -> restoreSession(...)
  InitHook->>Maestro: request gh cli status, ssh configs, commands, updates
  Maestro-->>InitHook: respond with status, configs, commands, update info
  InitHook->>SessionHook: wait for initialLoadComplete -> coordinate splash hide
  InputHook->>App: expose handleInputKeyDown -> App attaches to input component
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 60.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'refactor: extracted 3 more hooks from App.tsx' accurately and concisely describes the main change: three new hooks (useSessionRestoration, useInputKeyDown, useAppInitialization) extracted from App.tsx to reduce its complexity by ~830 lines.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch code-refactor

Comment @coderabbitai help to get the list of available commands and usage tips.

@greptile-apps
Copy link

greptile-apps bot commented Feb 18, 2026

Greptile Summary

Continues App.tsx decomposition by extracting three custom hooks (Phases 2E-2G), successfully removing ~830 lines while maintaining full functionality.

Key Changes

  • useSessionRestoration (405 lines): Extracts session loading, legacy field migrations (projectRoot, autoRunFolderPath, fileTreeAutoRefreshInterval), corruption recovery (missing aiTabs, invalid toolType: 'terminal'), and background git info fetching for SSH remotes
  • useInputKeyDown (319 lines): Extracts keyboard event handling for tab completion, @ mention navigation, slash command autocomplete, and mode-aware enter-to-send logic
  • useAppInitialization (291 lines): Consolidates all startup effects including splash screen coordination, GitHub CLI detection, Windows warning modal, beta updates sync, leaderboard sync, SpecKit/OpenSpec loading, SSH config loading, and notification settings sync

Design Excellence

  • Zero-parameter hooks: All three hooks read from Zustand stores via selectors or getState(), eliminating callback dependencies and stale closure risks
  • getState() in callbacks: Settings read at call time (not closure) for enter-to-send logic prevents stale values
  • Proxy ref pattern: initialLoadComplete bridges legacy .current API to store boolean, maintaining backward compatibility
  • Comprehensive testing: 139 new unit tests (49 + 47 + 43) with proper mocking and edge case coverage

Impact

  • Reduced complexity: App.tsx down from 9,556 to 8,767 lines (796-line effective reduction after accounting for hook invocation code)
  • Improved testability: Extracted logic now has isolated unit tests, improving maintainability
  • Better organization: Related functionality grouped into cohesive modules with clear responsibilities
  • Type safety maintained: All exports properly typed with TypeScript interfaces

Confidence Score: 5/5

  • This PR is safe to merge with high confidence - well-structured refactoring with comprehensive test coverage
  • Score reflects excellent code organization, comprehensive testing (139 new tests, 20,119 total passing), clean extraction with zero-parameter hooks eliminating dependency issues, proper TypeScript types throughout, and successful reduction of 830 lines from App.tsx while maintaining all functionality. The refactoring follows established patterns, includes proper error handling, and has thorough migration logic for legacy data. No logical errors, breaking changes, or security concerns identified.
  • No files require special attention - all changes are well-structured and thoroughly tested

Important Files Changed

Filename Overview
src/renderer/hooks/session/useSessionRestoration.ts extracted session restoration logic with comprehensive migration and corruption recovery, well-structured with clear separation of concerns
src/renderer/hooks/input/useInputKeyDown.ts extracted input keyboard handler with clean dependency injection pattern, properly handles all input modes and completion states
src/renderer/hooks/ui/useAppInitialization.ts consolidated all startup effects into a single hook with appropriate delays and error handling, properly coordinated with splash screen
src/tests/renderer/hooks/useSessionRestoration.test.ts comprehensive test coverage (49 tests) for session restoration including edge cases, migrations, and corruption recovery scenarios
src/tests/renderer/hooks/useInputKeyDown.test.ts thorough test coverage (47 tests) for keyboard handling including tab completion, mentions, slash commands, and enter-to-send logic
src/tests/renderer/hooks/useAppInitialization.test.ts complete test suite (43 tests) covering all startup effects, splash coordination, and platform-specific behavior with proper mocking
src/renderer/App.tsx successfully reduced by ~830 lines while maintaining functionality, hooks properly integrated with clean dependency injection

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[App.tsx ~9.5k lines] -->|Phase 2E| B[useSessionRestoration]
    A -->|Phase 2F| C[useInputKeyDown]
    A -->|Phase 2G| D[useAppInitialization]
    
    B -->|405 lines| E[Session Loading & Migration]
    B -->|49 tests| F[Corruption Recovery]
    B -->|Background| G[Git Info Fetching]
    
    C -->|319 lines| H[Tab Completion Navigation]
    C -->|47 tests| I[Mention Autocomplete]
    C -->|Mode-aware| J[Slash Commands & Enter-to-Send]
    
    D -->|291 lines| K[Splash Screen Coordination]
    D -->|43 tests| L[Platform Checks & CLI Detection]
    D -->|Startup| M[Commands & Stats Loading]
    
    E --> N[App.tsx ~8.7k lines]
    F --> N
    G --> N
    H --> N
    I --> N
    J --> N
    K --> N
    L --> N
    M --> N
    
    N -->|Result| O[830 lines removed]
    N -->|Result| P[139 new unit tests]
    N -->|Result| Q[Zero-parameter hooks with Zustand]
    
    style A fill:#ff6b6b
    style N fill:#51cf66
    style O fill:#51cf66
    style P fill:#51cf66
    style Q fill:#51cf66
Loading

Last reviewed commit: 9cca90c

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (5)
src/renderer/hooks/ui/useAppInitialization.ts (2)

226-241: Silent error swallowing may hide operational issues.

The .catch(console.error) pattern on lines 240 and 258 logs errors but doesn't propagate them to Sentry for tracking. Per coding guidelines, unexpected errors should bubble up to Sentry. Consider whether SSH config or stats DB failures warrant error tracking.

If these are expected/recoverable failures that shouldn't alert:

✏️ Optional: Add explicit comment documenting why errors are swallowed
 	// --- SSH remote configs loading ---
 	useEffect(() => {
 		window.maestro?.sshRemote
 			?.getConfigs()
 			.then((result) => {
 				if (result.success && result.configs) {
 					setSshRemoteConfigs(
 						result.configs.map((c: { id: string; name: string }) => ({
 							id: c.id,
 							name: c.name,
 						}))
 					);
 				}
 			})
-			.catch(console.error);
+			// Expected failure if SSH is not configured - non-critical for app startup
+			.catch((error) => console.debug('[SSH] Config loading failed (non-critical):', error));
 	}, []);

As per coding guidelines: "Do NOT silently swallow exceptions with try-catch-console.error blocks."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/renderer/hooks/ui/useAppInitialization.ts` around lines 226 - 241, The
current useEffect calling window.maestro?.sshRemote?.getConfigs swallows errors
via .catch(console.error); change this to report unexpected errors to Sentry (or
your app error-reporting pipeline) instead of only console.error, e.g., catch
the error, call the Sentry report function and then either rethrow or explicitly
document why it can be ignored; update the promise chain around useEffect ->
window.maestro?.sshRemote?.getConfigs() and the setSshRemoteConfigs handling to
ensure errors are sent to Sentry (or rethrown) and add an inline comment if you
intentionally want to ignore recoverable failures.

118-128: Silent catch hides file gist URL loading failures.

The empty .catch(() => {}) on line 127 provides no visibility into failures. At minimum, add debug logging to aid troubleshooting.

✏️ Suggested fix
 	useEffect(() => {
 		window.maestro.settings
 			.get('fileGistUrls')
 			.then((savedUrls) => {
 				if (savedUrls && typeof savedUrls === 'object') {
 					useTabStore.getState().setFileGistUrls(savedUrls as Record<string, GistInfo>);
 				}
 			})
-			.catch(() => {});
+			.catch((error) => {
+				console.debug('[App] Failed to load file gist URLs (non-critical):', error);
+			});
 	}, []);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/renderer/hooks/ui/useAppInitialization.ts` around lines 118 - 128, The
empty catch on the window.maestro.settings.get('fileGistUrls') promise hides
failures; update the catch in the useEffect inside useAppInitialization (where
useTabStore.getState().setFileGistUrls is called) to log the caught error (using
the app logger or console.debug/console.error) with context like "failed to load
fileGistUrls" so failures are visible for debugging while preserving existing
behavior.
src/renderer/hooks/session/useSessionRestoration.ts (2)

230-236: Dead code path: aiSuccess is always true.

The aiSpawnResult and aiSuccess are hardcoded constants, making the else branch at lines 303-313 unreachable. If this is intentional (deferred spawn), consider removing the dead branch or adding a comment explaining future intent.

-		const aiSpawnResult = { pid: 0, success: true };
-		const aiSuccess = true;
-
-		if (aiSuccess) {
+		// Don't eagerly spawn AI processes on session restore:
+		// - Batch mode agents spawn per message in useInputProcessing
+		// - Terminal uses runCommand (fresh shells per command)
+		// aiPid stays at 0 until user sends their first message
+		const aiSpawnResult = { pid: 0, success: true };
+		{
 			// ... existing code ...
-		} else {
-			console.error(`Failed to restore session ${session.id}`);
-			return {
-				...session,
-				aiPid: -1,
-				terminalPid: 0,
-				state: 'error' as SessionState,
-				isLive: false,
-				liveUrl: undefined,
-			};
 		}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/renderer/hooks/session/useSessionRestoration.ts` around lines 230 - 236,
The variables aiSpawnResult and aiSuccess in useSessionRestoration.ts are
hardcoded (aiSpawnResult = { pid: 0, success: true } and aiSuccess = true) which
makes the downstream else branch unreachable; either remove the dead else branch
or derive aiSuccess from aiSpawnResult (e.g., set aiSuccess =
aiSpawnResult.success) and ensure any spawn logic updates aiSpawnResult when you
later implement deferred spawning. Update the code around the
aiSpawnResult/aiSuccess usage (referencing aiSpawnResult and aiSuccess in
useSessionRestoration) accordingly and add a short comment explaining the chosen
approach (deferred spawn vs. immediate spawn result).

46-51: Consider documenting the stable reference pattern.

The useMemo(() => useSessionStore.getState(), []) pattern extracts stable action references once. This is correct but non-obvious. A brief comment would help future maintainers understand why these aren't in useCallback dependency arrays.

✏️ Suggested documentation
 	// --- Store actions (stable, non-reactive) ---
+	// These actions are extracted once and never change, so they don't need
+	// to be listed in useCallback dependencies throughout this hook.
 	const { setSessions, setGroups, setActiveSessionId, setSessionsLoaded } = useMemo(
 		() => useSessionStore.getState(),
 		[]
 	);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/renderer/hooks/session/useSessionRestoration.ts` around lines 46 - 51,
Add a short explanatory comment above the useMemo calls in
useSessionRestoration.ts clarifying that useMemo(() =>
useSessionStore.getState(), []) and useMemo(() => useGroupChatStore.getState(),
[]) are used to extract stable, non-reactive action references (setSessions,
setGroups, setActiveSessionId, setSessionsLoaded, setGroupChats) once so they
can be used safely outside React dependency arrays; mention that these are
intentionally not wrapped in useCallback or included in effect dependencies
because they are stable references returned by the store and will not change.
src/__tests__/renderer/hooks/useInputKeyDown.test.ts (1)

161-172: Add missing assertion for Ctrl+F test consistency.

The Ctrl+F test only verifies preventDefault was called, but doesn't verify outputSearchOpen state like the Cmd+F test does. For consistency and complete coverage, add the same assertion.

✏️ Suggested addition
 	it('opens output search on Ctrl+F', () => {
 		const deps = createMockDeps();
 		const { result } = renderHook(() => useInputKeyDown(deps));
 		const e = createKeyEvent('f', { ctrlKey: true });

 		act(() => {
 			result.current.handleInputKeyDown(e);
 		});

 		expect(e.preventDefault).toHaveBeenCalled();
+		expect(useUIStore.getState().outputSearchOpen).toBe(true);
 	});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/__tests__/renderer/hooks/useInputKeyDown.test.ts` around lines 161 - 172,
The Ctrl+F test "opens output search on Ctrl+F" is missing the assertion that
the output search state was toggled; update the test in useInputKeyDown.test.ts
to, after calling result.current.handleInputKeyDown(e) and asserting
e.preventDefault, also assert that the mock dependency state reflects the open
search (e.g., verify createMockDeps()'s state.outputSearchOpen or the
appropriate setter was called) — mirror the same assertion used in the Cmd+F
test so the test verifies both preventDefault and that useInputKeyDown actually
opened the output search.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/__tests__/renderer/hooks/useInputKeyDown.test.ts`:
- Around line 161-172: The Ctrl+F test "opens output search on Ctrl+F" is
missing the assertion that the output search state was toggled; update the test
in useInputKeyDown.test.ts to, after calling
result.current.handleInputKeyDown(e) and asserting e.preventDefault, also assert
that the mock dependency state reflects the open search (e.g., verify
createMockDeps()'s state.outputSearchOpen or the appropriate setter was called)
— mirror the same assertion used in the Cmd+F test so the test verifies both
preventDefault and that useInputKeyDown actually opened the output search.

In `@src/renderer/hooks/session/useSessionRestoration.ts`:
- Around line 230-236: The variables aiSpawnResult and aiSuccess in
useSessionRestoration.ts are hardcoded (aiSpawnResult = { pid: 0, success: true
} and aiSuccess = true) which makes the downstream else branch unreachable;
either remove the dead else branch or derive aiSuccess from aiSpawnResult (e.g.,
set aiSuccess = aiSpawnResult.success) and ensure any spawn logic updates
aiSpawnResult when you later implement deferred spawning. Update the code around
the aiSpawnResult/aiSuccess usage (referencing aiSpawnResult and aiSuccess in
useSessionRestoration) accordingly and add a short comment explaining the chosen
approach (deferred spawn vs. immediate spawn result).
- Around line 46-51: Add a short explanatory comment above the useMemo calls in
useSessionRestoration.ts clarifying that useMemo(() =>
useSessionStore.getState(), []) and useMemo(() => useGroupChatStore.getState(),
[]) are used to extract stable, non-reactive action references (setSessions,
setGroups, setActiveSessionId, setSessionsLoaded, setGroupChats) once so they
can be used safely outside React dependency arrays; mention that these are
intentionally not wrapped in useCallback or included in effect dependencies
because they are stable references returned by the store and will not change.

In `@src/renderer/hooks/ui/useAppInitialization.ts`:
- Around line 226-241: The current useEffect calling
window.maestro?.sshRemote?.getConfigs swallows errors via .catch(console.error);
change this to report unexpected errors to Sentry (or your app error-reporting
pipeline) instead of only console.error, e.g., catch the error, call the Sentry
report function and then either rethrow or explicitly document why it can be
ignored; update the promise chain around useEffect ->
window.maestro?.sshRemote?.getConfigs() and the setSshRemoteConfigs handling to
ensure errors are sent to Sentry (or rethrown) and add an inline comment if you
intentionally want to ignore recoverable failures.
- Around line 118-128: The empty catch on the
window.maestro.settings.get('fileGistUrls') promise hides failures; update the
catch in the useEffect inside useAppInitialization (where
useTabStore.getState().setFileGistUrls is called) to log the caught error (using
the app logger or console.debug/console.error) with context like "failed to load
fileGistUrls" so failures are visible for debugging while preserving existing
behavior.

- Add missing outputSearchOpen assertion to Ctrl+F test (useInputKeyDown)
- Remove dead aiSpawnResult/aiSuccess variables and unreachable else branch (useSessionRestoration)
- Expand comment explaining useMemo store action extraction pattern (useSessionRestoration)
- Upgrade SSH config error handling from bare console.error to console.warn with context (useAppInitialization)
- Replace silent catch on fileGistUrls load with console.debug for debuggability (useAppInitialization)
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/__tests__/renderer/hooks/useInputKeyDown.test.ts`:
- Around line 111-137: The beforeEach reset misses the UI flag outputSearchOpen,
causing cross-test leakage; update the beforeEach to set outputSearchOpen: false
on the mocked InputContext (add outputSearchOpen: false to the
Object.assign(mockInputContext, {...}) block) and/or explicitly reset the UI
store by calling useUIStore.setState({ outputSearchOpen: false }) so tests that
toggle Cmd+F don’t leak state into subsequent tests.

In `@src/renderer/hooks/session/useSessionRestoration.ts`:
- Around line 83-118: The catch blocks in fetchGitInfoInBackground (and the
similar blocks around the other catches) currently only console.warn errors and
swallow them; update these catches to distinguish expected/recoverable errors by
checking a specific error code or type (e.g., error.code or instanceof a known
error) and handle those by setting sshConnectionFailed via setSessions as now,
but for unexpected failures call captureException (or captureMessage) from the
sentry utility and then rethrow (or at minimum capture and rethrow) to avoid
swallowing; reference the gitService methods (isRepo, getBranches, getTags), the
state updater setSessions, and import captureException/captureMessage from your
sentry helper and invoke it inside the catch before rethrowing or marking the
session failure.

In `@src/renderer/hooks/ui/useAppInitialization.ts`:
- Around line 83-93: Several useEffect catch blocks (e.g., the GitHub CLI
availability check that calls window.maestro.git.checkGhCli()) currently swallow
errors; replace those console-only catches with Sentry reporting and targeted
handling: import captureException/captureMessage from src/utils/sentry and in
each catch call captureException(error, { extra: { context: 'checkGhCli' } }) or
captureMessage with a descriptive string, include relevant local state/context
(function name like checkGhCli, checkGhAuth, or the affected setter such as
setGhCliAvailable) and known error codes should be handled explicitly (map known
codes to user-friendly behavior) while rethrowing or allowing unexpected errors
to bubble after reporting.

Comment on lines +83 to +118
try {
const isGitRepo = await gitService.isRepo(cwd, sshRemoteId);

let gitBranches: string[] | undefined;
let gitTags: string[] | undefined;
let gitRefsCacheTime: number | undefined;
if (isGitRepo) {
[gitBranches, gitTags] = await Promise.all([
gitService.getBranches(cwd, sshRemoteId),
gitService.getTags(cwd, sshRemoteId),
]);
gitRefsCacheTime = Date.now();
}

setSessions((prev) =>
prev.map((s) =>
s.id === sessionId
? {
...s,
isGitRepo,
gitBranches,
gitTags,
gitRefsCacheTime,
sshConnectionFailed: false,
}
: s
)
);
} catch (error) {
console.warn(
`[fetchGitInfoInBackground] Failed to fetch git info for session ${sessionId}:`,
error
);
setSessions((prev) =>
prev.map((s) => (s.id === sessionId ? { ...s, sshConnectionFailed: true } : s))
);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Route caught errors to Sentry (or rethrow) instead of console-only logging.

At Line 83-118, 304-314, and 368-377, errors are caught and only logged to console while continuing. This conflicts with the repo rule against swallowing exceptions. Please handle only expected/recoverable errors with explicit codes, and use captureException/captureMessage from src/utils/sentry for unexpected failures (or rethrow after capture).

Also applies to: 304-314, 368-377

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/renderer/hooks/session/useSessionRestoration.ts` around lines 83 - 118,
The catch blocks in fetchGitInfoInBackground (and the similar blocks around the
other catches) currently only console.warn errors and swallow them; update these
catches to distinguish expected/recoverable errors by checking a specific error
code or type (e.g., error.code or instanceof a known error) and handle those by
setting sshConnectionFailed via setSessions as now, but for unexpected failures
call captureException (or captureMessage) from the sentry utility and then
rethrow (or at minimum capture and rethrow) to avoid swallowing; reference the
gitService methods (isRepo, getBranches, getTags), the state updater
setSessions, and import captureException/captureMessage from your sentry helper
and invoke it inside the catch before rethrowing or marking the session failure.

Comment on lines +83 to +93
// --- GitHub CLI availability check ---
useEffect(() => {
window.maestro.git
.checkGhCli()
.then((status) => {
setGhCliAvailable(status.installed && status.authenticated);
})
.catch(() => {
setGhCliAvailable(false);
});
}, []);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Replace console-only catch blocks with Sentry reporting or targeted handling.

At Line 83-93, 105-115, 118-129, 147-158, 172-198, 204-224, 231-246, and 249-265, errors are caught and only logged. This violates the repo rule against silently swallowing exceptions; use captureException/captureMessage from src/utils/sentry with context or let unexpected errors bubble after handling known error codes.

Also applies to: 105-115, 118-129, 147-158, 172-198, 204-224, 231-246, 249-265

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/renderer/hooks/ui/useAppInitialization.ts` around lines 83 - 93, Several
useEffect catch blocks (e.g., the GitHub CLI availability check that calls
window.maestro.git.checkGhCli()) currently swallow errors; replace those
console-only catches with Sentry reporting and targeted handling: import
captureException/captureMessage from src/utils/sentry and in each catch call
captureException(error, { extra: { context: 'checkGhCli' } }) or captureMessage
with a descriptive string, include relevant local state/context (function name
like checkGhCli, checkGhAuth, or the affected setter such as setGhCliAvailable)
and known error codes should be handled explicitly (map known codes to
user-friendly behavior) while rethrowing or allowing unexpected errors to bubble
after reporting.

…Each

Prevents Cmd+F / Ctrl+F tests from leaking outputSearchOpen state
into subsequent test cases.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

Comments