From 181741d769d47fc5d57b6894d94c55a2a9a70c5d Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Sat, 30 May 2026 13:13:40 -0700 Subject: [PATCH] chore: enforce exhaustive switch handling --- .oxlintrc.json | 5 +- config/oxlint-switch-exhaustiveness.json | 19 +++++ config/tsconfig.web.json | 5 +- package.json | 4 +- pnpm-lock.yaml | 69 ++++++++++++++++++- src/main/azure-devops/pull-request-mappers.ts | 2 + src/main/bitbucket/pull-request-mappers.ts | 1 + src/main/browser/browser-cookie-import.ts | 4 +- src/main/daemon/daemon-server.ts | 7 +- src/main/daemon/headless-emulator.ts | 2 + src/main/ghostty/discovery.ts | 9 ++- src/main/gitea/pull-request-mappers.ts | 1 + src/main/github/project-view.ts | 1 + src/main/github/project-view/mutations.ts | 16 ++--- src/main/runtime/orchestration/coordinator.ts | 4 +- src/main/terminal-history.ts | 6 +- .../src/components/GitHubItemDialog.tsx | 4 +- .../src/components/PullRequestPage.tsx | 4 +- .../dashboard/DashboardAgentRow.tsx | 3 +- .../components/github-pr-reviewer-display.ts | 2 + .../right-sidebar/SourceControl.tsx | 13 ---- .../right-sidebar/checks-panel-empty-state.ts | 3 +- .../right-sidebar/source-control-actions.ts | 2 - .../source-control-discard-confirmation.ts | 4 -- .../source-control-dropdown-items.ts | 3 +- .../components/settings/ComputerUsePane.tsx | 2 +- .../settings/DeveloperPermissionsPane.tsx | 2 +- .../src/components/settings/SshTargetCard.tsx | 2 +- .../sidebar/WorktreeCardHelpers.tsx | 2 +- .../src/components/sidebar/smart-sort.ts | 4 -- .../sidebar/worktree-card-compact-agents.tsx | 3 +- .../status-bar/SshStatusSegment.tsx | 10 +-- src/renderer/src/lib/new-workspace.ts | 2 +- src/renderer/src/lib/pr-comment-audience.ts | 2 +- src/renderer/src/store/slices/github.ts | 3 +- src/renderer/src/store/slices/ui.ts | 2 +- src/shared/agent-hook-listener.ts | 15 ---- src/shared/github-project-group-sort.ts | 2 - 38 files changed, 159 insertions(+), 85 deletions(-) create mode 100644 config/oxlint-switch-exhaustiveness.json diff --git a/.oxlintrc.json b/.oxlintrc.json index a8d1eab43fe..f414c9066eb 100644 --- a/.oxlintrc.json +++ b/.oxlintrc.json @@ -39,7 +39,10 @@ "typescript/prefer-function-type": "error", "typescript/prefer-includes": "error", "typescript/prefer-optional-chain": "error", - "typescript/switch-exhaustiveness-check": "error", + "typescript/switch-exhaustiveness-check": [ + "error", + { "allowDefaultCaseForExhaustiveSwitch": false } + ], "curly": "error", "no-unneeded-ternary": "error", "no-useless-return": "error", diff --git a/config/oxlint-switch-exhaustiveness.json b/config/oxlint-switch-exhaustiveness.json new file mode 100644 index 00000000000..c967a7b9b5d --- /dev/null +++ b/config/oxlint-switch-exhaustiveness.json @@ -0,0 +1,19 @@ +{ + "$schema": "../node_modules/oxlint/configuration_schema.json", + "plugins": ["typescript"], + "categories": { + "correctness": "off", + "suspicious": "off", + "pedantic": "off", + "perf": "off", + "style": "off", + "restriction": "off", + "nursery": "off" + }, + "rules": { + "typescript/switch-exhaustiveness-check": [ + "error", + { "allowDefaultCaseForExhaustiveSwitch": false } + ] + } +} diff --git a/config/tsconfig.web.json b/config/tsconfig.web.json index 844bf185d3c..92a6b9f2831 100644 --- a/config/tsconfig.web.json +++ b/config/tsconfig.web.json @@ -10,10 +10,9 @@ "compilerOptions": { "composite": true, "jsx": "react-jsx", - "baseUrl": "..", "paths": { - "@renderer/*": ["src/renderer/src/*"], - "@/*": ["src/renderer/src/*"] + "@renderer/*": ["../src/renderer/src/*"], + "@/*": ["../src/renderer/src/*"] } } } diff --git a/package.json b/package.json index 56168cb3810..2a02b6bfe2e 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,8 @@ "main": "./out/main/index.js", "scripts": { "format": "oxfmt --write .", - "lint": "oxlint && node config/scripts/check-styled-scrollbars.mjs", + "lint": "oxlint && pnpm run lint:switch-exhaustiveness && node config/scripts/check-styled-scrollbars.mjs", + "lint:switch-exhaustiveness": "oxlint --type-aware --config config/oxlint-switch-exhaustiveness.json src/main src/preload src/shared src/relay src/cli src/renderer/src config tests --quiet", "prepare": "husky", "test": "node config/scripts/ensure-native-runtime.mjs --runtime=node && vitest run --config config/vitest.config.ts", "check:styled-scrollbars": "node config/scripts/check-styled-scrollbars.mjs", @@ -157,6 +158,7 @@ "lint-staged": "^16.4.0", "oxfmt": "^0.52.0", "oxlint": "^1.67.0", + "oxlint-tsgolint": "0.23.0", "react": "^19.2.5", "react-dom": "^19.2.5", "react-grab": "^0.1.33", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8964578c9d9..035d8397492 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -310,7 +310,10 @@ importers: version: 0.52.0 oxlint: specifier: ^1.67.0 - version: 1.67.0 + version: 1.67.0(oxlint-tsgolint@0.23.0) + oxlint-tsgolint: + specifier: 0.23.0 + version: 0.23.0 react: specifier: ^19.2.5 version: 19.2.5 @@ -1272,6 +1275,36 @@ packages: cpu: [x64] os: [win32] + '@oxlint-tsgolint/darwin-arm64@0.23.0': + resolution: {integrity: sha512-gOs9PVr2wEg4ox9z0aJo+RKhhImW86YL5N6yav8BK/rgPsIrwN/igSZ+pbRr723NFvUNKde9fgMhRA6JrXAOZw==} + cpu: [arm64] + os: [darwin] + + '@oxlint-tsgolint/darwin-x64@0.23.0': + resolution: {integrity: sha512-kjJ8B+7n4tB9VJdxS5A9GdJt6/bYpzbu4lXp2uO1S3sRmCB5gDEABlGoiePNApRWaW+xqL4b4xgiE727jSLhuA==} + cpu: [x64] + os: [darwin] + + '@oxlint-tsgolint/linux-arm64@0.23.0': + resolution: {integrity: sha512-6dCZuKNu135seMXilkRk9SpCx6i1XgmiipYGalLij5WVRX6ZYS8c4xI7preN/zv9fCXhsQclTIMDu2Y/cytTjw==} + cpu: [arm64] + os: [linux] + + '@oxlint-tsgolint/linux-x64@0.23.0': + resolution: {integrity: sha512-3bdilnyA7kmSTjK27rvjIjSxL5SIg3wt7vwNiRkouWB83ytssyKnuGvxSYJxgMEmFpSutzaBzcCUM2jDtPGcgA==} + cpu: [x64] + os: [linux] + + '@oxlint-tsgolint/win32-arm64@0.23.0': + resolution: {integrity: sha512-j+OEp44SVYiQ+ZD+uttsX7u6L9SvmbbQ77SO1pSFCcJlsVMeCk8qZsjhKfGKuT/jIA+ipOJMVs/+pqUfObBWNw==} + cpu: [arm64] + os: [win32] + + '@oxlint-tsgolint/win32-x64@0.23.0': + resolution: {integrity: sha512-5MyjFuqf+g8OUPJBSGWHJtmoWnzFJYyOg4To9WMQshZYEWig/vtu7JtJ03VWnzHv9LJkAUeApY0gVCOywFR/iQ==} + cpu: [x64] + os: [win32] + '@oxlint/binding-android-arm-eabi@1.67.0': resolution: {integrity: sha512-VrSi571rDv1N8HaEDM+DEX8nmT0y9jJo8tzzW13vsOWTx59xQczCIJx68n2zWOXRT5YKZsOZXp4qkHN/10x4mw==} engines: {node: ^20.19.0 || >=22.12.0} @@ -5165,6 +5198,10 @@ packages: vite-plus: optional: true + oxlint-tsgolint@0.23.0: + resolution: {integrity: sha512-3mBv3CoPbh8dFbzfDGIWa2ytZjn2v+3EX4aKRXjIhsoGFzG8GCjfRirz3rwZf1wYbZzsNLTSgpw8VjQuWdp/jA==} + hasBin: true + oxlint@1.67.0: resolution: {integrity: sha512-blwwaHPdoH8piQ5/z0KHeoHFR7FZgl12WluKJfu4qFLPkZl6mK04PkLE45Fw1NxfBRSlh40Gu7MkxHUw++ociQ==} engines: {node: ^20.19.0 || >=22.12.0} @@ -7245,6 +7282,24 @@ snapshots: '@oxfmt/binding-win32-x64-msvc@0.52.0': optional: true + '@oxlint-tsgolint/darwin-arm64@0.23.0': + optional: true + + '@oxlint-tsgolint/darwin-x64@0.23.0': + optional: true + + '@oxlint-tsgolint/linux-arm64@0.23.0': + optional: true + + '@oxlint-tsgolint/linux-x64@0.23.0': + optional: true + + '@oxlint-tsgolint/win32-arm64@0.23.0': + optional: true + + '@oxlint-tsgolint/win32-x64@0.23.0': + optional: true + '@oxlint/binding-android-arm-eabi@1.67.0': optional: true @@ -11489,7 +11544,16 @@ snapshots: '@oxfmt/binding-win32-ia32-msvc': 0.52.0 '@oxfmt/binding-win32-x64-msvc': 0.52.0 - oxlint@1.67.0: + oxlint-tsgolint@0.23.0: + optionalDependencies: + '@oxlint-tsgolint/darwin-arm64': 0.23.0 + '@oxlint-tsgolint/darwin-x64': 0.23.0 + '@oxlint-tsgolint/linux-arm64': 0.23.0 + '@oxlint-tsgolint/linux-x64': 0.23.0 + '@oxlint-tsgolint/win32-arm64': 0.23.0 + '@oxlint-tsgolint/win32-x64': 0.23.0 + + oxlint@1.67.0(oxlint-tsgolint@0.23.0): optionalDependencies: '@oxlint/binding-android-arm-eabi': 1.67.0 '@oxlint/binding-android-arm64': 1.67.0 @@ -11510,6 +11574,7 @@ snapshots: '@oxlint/binding-win32-arm64-msvc': 1.67.0 '@oxlint/binding-win32-ia32-msvc': 1.67.0 '@oxlint/binding-win32-x64-msvc': 1.67.0 + oxlint-tsgolint: 0.23.0 p-cancelable@2.1.1: {} diff --git a/src/main/azure-devops/pull-request-mappers.ts b/src/main/azure-devops/pull-request-mappers.ts index d804029e53f..062a99fa0b4 100644 --- a/src/main/azure-devops/pull-request-mappers.ts +++ b/src/main/azure-devops/pull-request-mappers.ts @@ -58,6 +58,7 @@ export function mapAzureDevOpsMergeable(mergeStatus: string | null | undefined): return 'MERGEABLE' case 'conflicts': return 'CONFLICTING' + case undefined: default: return 'UNKNOWN' } @@ -80,6 +81,7 @@ function classifyAzureDevOpsStatus(state: string | null | undefined): CheckStatu case 'queued': case 'running': return 'pending' + case undefined: default: return 'neutral' } diff --git a/src/main/bitbucket/pull-request-mappers.ts b/src/main/bitbucket/pull-request-mappers.ts index 92c99d7f0a4..4f68c320235 100644 --- a/src/main/bitbucket/pull-request-mappers.ts +++ b/src/main/bitbucket/pull-request-mappers.ts @@ -50,6 +50,7 @@ export function mapBitbucketPullRequestState( case 'SUPERSEDED': return 'closed' case 'OPEN': + case undefined: default: return 'open' } diff --git a/src/main/browser/browser-cookie-import.ts b/src/main/browser/browser-cookie-import.ts index 54eb9b0407f..7079141f4e6 100644 --- a/src/main/browser/browser-cookie-import.ts +++ b/src/main/browser/browser-cookie-import.ts @@ -744,7 +744,9 @@ export function getUserAgentForBrowser( const v = readBrowserVersion('/Applications/Comet.app') return v ? `Mozilla/5.0 (${platform}) ${chromeBase} Chrome/${v} Safari/537.36` : null } - default: + case 'firefox': + case 'safari': + case 'manual': return null } } diff --git a/src/main/daemon/daemon-server.ts b/src/main/daemon/daemon-server.ts index 02e8cc61fd2..e8eb9733827 100644 --- a/src/main/daemon/daemon-server.ts +++ b/src/main/daemon/daemon-server.ts @@ -264,6 +264,9 @@ export class DaemonServer { } } + case 'cancelCreateOrAttach': + return {} + case 'write': try { this.lastInputAtBySessionId.set(request.payload.sessionId, performance.now()) @@ -330,10 +333,8 @@ export class DaemonServer { } process.nextTick(() => this.shutdown()) return {} - - default: - throw new Error(`Unknown request type: ${(request as { type: string }).type}`) } + throw new Error(`Unknown request type: ${(request as { type: string }).type}`) } private sendExitEvent( diff --git a/src/main/daemon/headless-emulator.ts b/src/main/daemon/headless-emulator.ts index 90155072a94..2611ea025c4 100644 --- a/src/main/daemon/headless-emulator.ts +++ b/src/main/daemon/headless-emulator.ts @@ -285,6 +285,8 @@ export class HeadlessEmulator { case 'any': seqs.push('\x1b[?1003h') break + case 'none': + break } // Why: xterm tracks the mouse protocol and SGR encoding as independent // modes, so snapshots must preserve the encoding even when reporting is off. diff --git a/src/main/ghostty/discovery.ts b/src/main/ghostty/discovery.ts index 8dbfaf85afd..958171438fd 100644 --- a/src/main/ghostty/discovery.ts +++ b/src/main/ghostty/discovery.ts @@ -35,7 +35,14 @@ export function getGhosttyConfigPaths(): string[] { // Why: path.win32.join preserves backslashes even when tests run on macOS/Linux. return [path.win32.join(base, 'config'), path.win32.join(base, 'config.ghostty')] } - default: + case 'aix': + case 'android': + case 'cygwin': + case 'freebsd': + case 'haiku': + case 'netbsd': + case 'openbsd': + case 'sunos': return [] } } diff --git a/src/main/gitea/pull-request-mappers.ts b/src/main/gitea/pull-request-mappers.ts index 090e38bd39d..616a7ea01c5 100644 --- a/src/main/gitea/pull-request-mappers.ts +++ b/src/main/gitea/pull-request-mappers.ts @@ -48,6 +48,7 @@ function classifyGiteaStatus(status: string | null | undefined): CheckStatus { case 'pending': return 'pending' case 'skipped': + case undefined: default: return 'neutral' } diff --git a/src/main/github/project-view.ts b/src/main/github/project-view.ts index bc6f576a636..7eaeecfbe4a 100644 --- a/src/main/github/project-view.ts +++ b/src/main/github/project-view.ts @@ -274,6 +274,7 @@ export function normalizeFieldValue( .filter((u): u is GitHubProjectUser => u !== null) return { kind: 'users', fieldId, users } } + case undefined: default: // Unknown __typename → forward-compat: drop silently, do not throw, // do not classify as drift (see design §Error Handling). diff --git a/src/main/github/project-view/mutations.ts b/src/main/github/project-view/mutations.ts index d07db5e5efb..3d3d66757ea 100644 --- a/src/main/github/project-view/mutations.ts +++ b/src/main/github/project-view/mutations.ts @@ -62,13 +62,10 @@ function graphqlValueForFieldMutation(value: GitHubProjectFieldMutationValue): s return 'number: $value' case 'date': return 'date: $value' - default: - // Why: defensive default. If a new mutation kind is added to the type - // but not handled here, returning undefined would silently produce a - // broken GraphQL query. Throw so updateProjectItemFieldValue can map it - // to a validation_error rather than dispatching a malformed mutation. - throw new UnknownFieldMutationKindError((value as { kind: string }).kind) } + // Why: keep a runtime guard for malformed IPC payloads while lint enforces + // that every typed mutation kind is handled above. + throw new UnknownFieldMutationKindError((value as { kind: string }).kind) } function mutationValueVar(value: GitHubProjectFieldMutationValue): { @@ -86,11 +83,10 @@ function mutationValueVar(value: GitHubProjectFieldMutationValue): { return { type: 'Float!', val: value.number } case 'date': return { type: 'Date!', val: value.date } - default: - // Why: see graphqlValueForFieldMutation — surface unknown kinds loudly - // instead of returning undefined and dispatching an invalid mutation. - throw new UnknownFieldMutationKindError((value as { kind: string }).kind) } + // Why: see graphqlValueForFieldMutation — surface unknown kinds loudly + // instead of returning undefined and dispatching an invalid mutation. + throw new UnknownFieldMutationKindError((value as { kind: string }).kind) } export async function updateProjectItemFieldValue( diff --git a/src/main/runtime/orchestration/coordinator.ts b/src/main/runtime/orchestration/coordinator.ts index f7fce4b1b2f..db17fc8d087 100644 --- a/src/main/runtime/orchestration/coordinator.ts +++ b/src/main/runtime/orchestration/coordinator.ts @@ -263,7 +263,9 @@ export class Coordinator { case 'status': this.opts.onLog(`Status from ${msg.from_handle}: ${msg.subject}`) break - default: + case 'dispatch': + case 'handoff': + case 'merge_ready': break } } diff --git a/src/main/terminal-history.ts b/src/main/terminal-history.ts index 63c7344524d..e92581e0b5a 100644 --- a/src/main/terminal-history.ts +++ b/src/main/terminal-history.ts @@ -62,7 +62,11 @@ function historyFilename(shell: ShellKind): string | null { case 'bash': return 'bash_history' // Phase 2: fish and PowerShell use different mechanisms - default: + case 'fish': + case 'pwsh': + case 'powershell': + case 'cmd': + case 'unknown': return null } } diff --git a/src/renderer/src/components/GitHubItemDialog.tsx b/src/renderer/src/components/GitHubItemDialog.tsx index a598e254454..ffc19124d49 100644 --- a/src/renderer/src/components/GitHubItemDialog.tsx +++ b/src/renderer/src/components/GitHubItemDialog.tsx @@ -1525,7 +1525,9 @@ function mapPRFileStatus(status: GitHubPRFile['status']): GitBranchChangeEntry[' return 'renamed' case 'copied': return 'copied' - default: + case 'changed': + case 'modified': + case 'unchanged': return 'modified' } } diff --git a/src/renderer/src/components/PullRequestPage.tsx b/src/renderer/src/components/PullRequestPage.tsx index 6de0c166b9b..89ae8734610 100644 --- a/src/renderer/src/components/PullRequestPage.tsx +++ b/src/renderer/src/components/PullRequestPage.tsx @@ -1532,7 +1532,9 @@ function mapPRFileStatus(status: GitHubPRFile['status']): GitBranchChangeEntry[' return 'renamed' case 'copied': return 'copied' - default: + case 'changed': + case 'modified': + case 'unchanged': return 'modified' } } diff --git a/src/renderer/src/components/dashboard/DashboardAgentRow.tsx b/src/renderer/src/components/dashboard/DashboardAgentRow.tsx index 53db7e1260f..928c1afc722 100644 --- a/src/renderer/src/components/dashboard/DashboardAgentRow.tsx +++ b/src/renderer/src/components/dashboard/DashboardAgentRow.tsx @@ -22,9 +22,8 @@ function asDotState(state: AgentStatusState | 'idle'): AgentDotState { case 'done': case 'idle': return state - default: - return 'idle' } + return 'idle' } function formatTimeAgo(ts: number, now: number): string { diff --git a/src/renderer/src/components/github-pr-reviewer-display.ts b/src/renderer/src/components/github-pr-reviewer-display.ts index 1988b0c42a4..4f950947fa5 100644 --- a/src/renderer/src/components/github-pr-reviewer-display.ts +++ b/src/renderer/src/components/github-pr-reviewer-display.ts @@ -57,6 +57,8 @@ function formatReviewState(state: string | null | undefined): string { return 'Dismissed' case 'PENDING': return 'Pending' + case null: + case undefined: default: return 'Reviewed' } diff --git a/src/renderer/src/components/right-sidebar/SourceControl.tsx b/src/renderer/src/components/right-sidebar/SourceControl.tsx index ae16bb63ed9..4ade1cacc32 100644 --- a/src/renderer/src/components/right-sidebar/SourceControl.tsx +++ b/src/renderer/src/components/right-sidebar/SourceControl.tsx @@ -2922,14 +2922,6 @@ function SourceControlInner(): React.JSX.Element { case 'publish': case 'rebase_base': void runRemoteAction(kind === 'rebase_base' ? 'rebase' : kind) - return - default: { - // Why: exhaustiveness check — if a new DropdownActionKind is added - // to the union, TypeScript will flag this assignment so we can't - // silently drop a case. - const _exhaustive: never = kind - void _exhaustive - } } }, [ @@ -3263,11 +3255,6 @@ function SourceControlInner(): React.JSX.Element { case 'publish': case 'create_pr': handleActionInvoke(primaryAction.kind) - return - default: { - const _exhaustive: never = primaryAction.kind - void _exhaustive - } } }, [handleActionInvoke, handleStageAllPrimary, primaryAction.kind]) diff --git a/src/renderer/src/components/right-sidebar/checks-panel-empty-state.ts b/src/renderer/src/components/right-sidebar/checks-panel-empty-state.ts index 75e1e04589c..3f2aa5ae2eb 100644 --- a/src/renderer/src/components/right-sidebar/checks-panel-empty-state.ts +++ b/src/renderer/src/components/right-sidebar/checks-panel-empty-state.ts @@ -71,7 +71,8 @@ export function getChecksPanelEmptyStateCopy( title: 'No pull request found', description: 'GitHub refresh is paused by the current rate-limit budget' } - default: + case 'skipped': + case undefined: return { title: `No ${reviewLabel} found`, description: `Create a ${reviewLabel} to start checks and review.` diff --git a/src/renderer/src/components/right-sidebar/source-control-actions.ts b/src/renderer/src/components/right-sidebar/source-control-actions.ts index d79bd53595e..3ae829cd710 100644 --- a/src/renderer/src/components/right-sidebar/source-control-actions.ts +++ b/src/renderer/src/components/right-sidebar/source-control-actions.ts @@ -9,7 +9,5 @@ export function getSourceControlActions(area: GitStagingArea): SourceControlActi case 'unstaged': case 'untracked': return ['discard', 'stage'] - default: - return [] } } diff --git a/src/renderer/src/components/right-sidebar/source-control-discard-confirmation.ts b/src/renderer/src/components/right-sidebar/source-control-discard-confirmation.ts index 463e721c0dc..9f191e35399 100644 --- a/src/renderer/src/components/right-sidebar/source-control-discard-confirmation.ts +++ b/src/renderer/src/components/right-sidebar/source-control-discard-confirmation.ts @@ -69,9 +69,5 @@ export function getDiscardAreaConfirmationCopy( : `This will revert unstaged changes in ${count} files. This cannot be undone.`, confirmLabel: 'Discard all' } - default: { - const _exhaustive: never = area - return _exhaustive - } } } diff --git a/src/renderer/src/components/right-sidebar/source-control-dropdown-items.ts b/src/renderer/src/components/right-sidebar/source-control-dropdown-items.ts index 20b4b84569f..6084df2e2a8 100644 --- a/src/renderer/src/components/right-sidebar/source-control-dropdown-items.ts +++ b/src/renderer/src/components/right-sidebar/source-control-dropdown-items.ts @@ -405,7 +405,8 @@ export function resolveDropdownItems(inputs: DropdownActionInputs): DropdownEntr return 'A pull request already exists' case 'fork_head_unsupported': return 'Fork head unsupported' - default: + case null: + case undefined: return upstreamLoading ? 'Checking branch status…' : 'Branch is not ready' } })() diff --git a/src/renderer/src/components/settings/ComputerUsePane.tsx b/src/renderer/src/components/settings/ComputerUsePane.tsx index 5cedbf54477..cfb3ff27c7d 100644 --- a/src/renderer/src/components/settings/ComputerUsePane.tsx +++ b/src/renderer/src/components/settings/ComputerUsePane.tsx @@ -60,7 +60,7 @@ function statusLabel(status: ComputerUsePermissionStatus | undefined): string { case 'unsupported': return 'macOS only' case 'not-granted': - default: + case undefined: return 'Not enabled' } } diff --git a/src/renderer/src/components/settings/DeveloperPermissionsPane.tsx b/src/renderer/src/components/settings/DeveloperPermissionsPane.tsx index f7cf7707dfd..4a606426392 100644 --- a/src/renderer/src/components/settings/DeveloperPermissionsPane.tsx +++ b/src/renderer/src/components/settings/DeveloperPermissionsPane.tsx @@ -111,7 +111,7 @@ function statusLabel(status: DeveloperPermissionStatus | undefined): string { case 'ready': return 'Entitled' case 'unknown': - default: + case undefined: return 'Check manually' } } diff --git a/src/renderer/src/components/settings/SshTargetCard.tsx b/src/renderer/src/components/settings/SshTargetCard.tsx index 821e807e53e..aae50d8d9d2 100644 --- a/src/renderer/src/components/settings/SshTargetCard.tsx +++ b/src/renderer/src/components/settings/SshTargetCard.tsx @@ -43,7 +43,7 @@ export function statusColor(status: SshConnectionStatus): string { case 'reconnection-failed': case 'error': return 'bg-red-500' - default: + case 'disconnected': return 'bg-muted-foreground/40' } } diff --git a/src/renderer/src/components/sidebar/WorktreeCardHelpers.tsx b/src/renderer/src/components/sidebar/WorktreeCardHelpers.tsx index 46de66e50fa..43f53b76ede 100644 --- a/src/renderer/src/components/sidebar/WorktreeCardHelpers.tsx +++ b/src/renderer/src/components/sidebar/WorktreeCardHelpers.tsx @@ -25,7 +25,7 @@ export function checksLabel(status: CheckStatus): string { return 'Failing' case 'pending': return 'Pending' - default: + case 'neutral': return '' } } diff --git a/src/renderer/src/components/sidebar/smart-sort.ts b/src/renderer/src/components/sidebar/smart-sort.ts index 84c38bcd1a9..4c87740a944 100644 --- a/src/renderer/src/components/sidebar/smart-sort.ts +++ b/src/renderer/src/components/sidebar/smart-sort.ts @@ -103,10 +103,6 @@ export function buildWorktreeComparator( (b.manualOrder ?? b.sortOrder) - (a.manualOrder ?? a.sortOrder) || a.displayName.localeCompare(b.displayName) ) - default: { - const _exhaustive: never = sortBy - return _exhaustive - } } } } diff --git a/src/renderer/src/components/sidebar/worktree-card-compact-agents.tsx b/src/renderer/src/components/sidebar/worktree-card-compact-agents.tsx index 31afa82f8e2..35d1e00d753 100644 --- a/src/renderer/src/components/sidebar/worktree-card-compact-agents.tsx +++ b/src/renderer/src/components/sidebar/worktree-card-compact-agents.tsx @@ -15,9 +15,8 @@ function asDotState(state: AgentStatusState | 'idle'): AgentDotState { case 'done': case 'idle': return state - default: - return 'idle' } + return 'idle' } function getAgentDotState(agent: DashboardAgentRowData): AgentDotState { diff --git a/src/renderer/src/components/status-bar/SshStatusSegment.tsx b/src/renderer/src/components/status-bar/SshStatusSegment.tsx index ae083093ae1..d61840412df 100644 --- a/src/renderer/src/components/status-bar/SshStatusSegment.tsx +++ b/src/renderer/src/components/status-bar/SshStatusSegment.tsx @@ -48,7 +48,7 @@ function overallDotColor(status: 'connected' | 'partial' | 'disconnected' | 'con return 'bg-yellow-500' case 'connecting': return 'bg-yellow-500' - default: + case 'disconnected': return 'bg-muted-foreground/40' } } @@ -61,7 +61,7 @@ function overallLabel(status: 'connected' | 'partial' | 'disconnected' | 'connec return 'Partial' case 'connecting': return 'Connecting…' - default: + case 'disconnected': return 'Disconnected' } } @@ -80,7 +80,8 @@ function syncStatusLabel(status: RemoteWorkspaceSyncStatus | undefined): string return 'Sync error' case 'offline': return 'Sync unavailable' - default: + case 'idle': + case undefined: return 'Sync idle' } } @@ -97,7 +98,8 @@ function syncStatusTone(status: RemoteWorkspaceSyncStatus | undefined): string { return 'text-yellow-500' case 'synced': return 'text-emerald-500' - default: + case 'idle': + case undefined: return 'text-muted-foreground' } } diff --git a/src/renderer/src/lib/new-workspace.ts b/src/renderer/src/lib/new-workspace.ts index 6cc91987c96..973a2da88d6 100644 --- a/src/renderer/src/lib/new-workspace.ts +++ b/src/renderer/src/lib/new-workspace.ts @@ -33,7 +33,7 @@ export function getTaskPresetQuery(presetId: TaskViewPresetId | null): string { return 'author:@me is:pr is:open' case 'review': return 'review-requested:@me is:pr is:open' - default: + case null: return 'is:issue is:open' } } diff --git a/src/renderer/src/lib/pr-comment-audience.ts b/src/renderer/src/lib/pr-comment-audience.ts index fb27cdf7fcb..d8888512b6a 100644 --- a/src/renderer/src/lib/pr-comment-audience.ts +++ b/src/renderer/src/lib/pr-comment-audience.ts @@ -88,7 +88,7 @@ export function getPRCommentAudienceEmptyLabel(filter: PRCommentAudienceFilter): return 'No bot comments.' case 'human': return 'No human comments.' - default: + case 'all': return 'No comments yet.' } } diff --git a/src/renderer/src/store/slices/github.ts b/src/renderer/src/store/slices/github.ts index f6f1a89c7b1..6e717433d16 100644 --- a/src/renderer/src/store/slices/github.ts +++ b/src/renderer/src/store/slices/github.ts @@ -200,9 +200,8 @@ function optimisticFieldValueFromMutation( return { kind: 'number', fieldId, number: value.number } case 'date': return { kind: 'date', fieldId, date: value.date } - default: - return null } + return null } function applyRowPatch( diff --git a/src/renderer/src/store/slices/ui.ts b/src/renderer/src/store/slices/ui.ts index 5f137d1d044..6f12d38c4d2 100644 --- a/src/renderer/src/store/slices/ui.ts +++ b/src/renderer/src/store/slices/ui.ts @@ -155,7 +155,7 @@ function presetToQuery(presetId: TaskViewPresetId | null): string { return 'review-requested:@me is:pr is:open' case 'my-prs': return 'author:@me is:pr is:open' - default: + case null: return 'is:issue is:open' } } diff --git a/src/shared/agent-hook-listener.ts b/src/shared/agent-hook-listener.ts index aa837442e33..96fefa92d6a 100644 --- a/src/shared/agent-hook-listener.ts +++ b/src/shared/agent-hook-listener.ts @@ -1812,11 +1812,6 @@ function isNewTurnEvent(source: AgentHookSource, eventName: unknown): boolean { } case 'hermes': return eventName === 'pre_llm_call' || eventName === 'on_session_start' - default: { - const _exhaustive: never = source - void _exhaustive - return false - } } } @@ -1901,11 +1896,6 @@ function extractToolFields( return extractCopilotToolFields(normalizeCopilotEventName(eventName), hookPayload) case 'hermes': return extractHermesToolFields(eventName, hookPayload) - default: { - const _exhaustive: never = source - void _exhaustive - return {} - } } } @@ -2839,11 +2829,6 @@ export function normalizeHookPayload( case 'hermes': payload = normalizeHermesEvent(state, eventName, promptText, paneKey, hookPayloadRecord) break - default: { - const _exhaustive: never = source - void _exhaustive - payload = null - } } // Why: connectionId stays null at the listener layer. The local server keeps diff --git a/src/shared/github-project-group-sort.ts b/src/shared/github-project-group-sort.ts index 561c1709ddc..fe91c78051e 100644 --- a/src/shared/github-project-group-sort.ts +++ b/src/shared/github-project-group-sort.ts @@ -87,8 +87,6 @@ function deriveStringValue(value: GitHubProjectRow['fieldValuesByFieldId'][strin return value.labels.map((l) => l.name).join(', ') case 'users': return value.users.map((u) => u.login).join(', ') - default: - return '' } }