From 6fb9e60dea54e2e5658a5f8f06fe18ae871b82fe Mon Sep 17 00:00:00 2001 From: murashit Date: Thu, 25 Jun 2026 08:39:57 +0900 Subject: [PATCH] Enable strict typed ESLint rules --- docs/development.md | 2 +- eslint.config.mjs | 12 +++++------- src/app-server/connection/short-lived-client.ts | 3 +-- src/app-server/services/ephemeral-structured-turn.ts | 6 ++---- src/app-server/services/thread-archive-markdown.ts | 2 +- .../app-server/mappers/message-stream/turn-items.ts | 3 ++- .../chat/application/runtime/settings-actions.ts | 3 ++- 7 files changed, 14 insertions(+), 17 deletions(-) diff --git a/docs/development.md b/docs/development.md index 2e68ad44..47bb070a 100644 --- a/docs/development.md +++ b/docs/development.md @@ -14,7 +14,7 @@ Use this as the normal edit loop. `npm run format` applies Biome formatting and Use focused scripts for tight loops: `npm run typecheck`, `npm run test`, `npm run build`, or the targeted `npm run check:*` scripts. CI and release preflight run the same `npm run check` command as local development. -Biome owns formatting, import organization, general JavaScript/TypeScript/JSON/CSS linting, GritQL source-shape plugins, GritQL import-boundary restrictions, and GritQL CSS source policy. Biome warnings fail `npm run check`; info diagnostics stay advisory, including accessibility while Obsidian-specific UI semantics are reviewed rule by rule. The CSS usage script still checks dead authored classes. ESLint remains for typed unsafe-any checks, Obsidian plugin policy, and Codex Panel responsibility-boundary rules that need TypeScript type information. +Biome owns formatting, import organization, general JavaScript/TypeScript/JSON/CSS linting, GritQL source-shape plugins, GritQL import-boundary restrictions, and GritQL CSS source policy. Biome warnings fail `npm run check`; info diagnostics stay advisory, including accessibility while Obsidian-specific UI semantics are reviewed rule by rule. The CSS usage script still checks dead authored classes. ESLint remains for typed strict TypeScript checks, Obsidian plugin policy, and Codex Panel responsibility-boundary rules that need TypeScript type information. The typed strict preset is used with `@typescript-eslint/require-await` disabled because Obsidian and panel-owned async-shaped boundaries sometimes return already-complete work. ## Generated and Loaded Files diff --git a/eslint.config.mjs b/eslint.config.mjs index 5cb6e7e1..589b1bd2 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -4,12 +4,7 @@ import ts from "typescript"; import tseslint from "typescript-eslint"; const sourceTypeScriptFiles = ["src/**/*.{ts,tsx}"]; -const unsafeAnyTypeScriptRules = { - "@typescript-eslint/no-unsafe-argument": "error", - "@typescript-eslint/no-unsafe-assignment": "error", - "@typescript-eslint/no-unsafe-call": "error", - "@typescript-eslint/no-unsafe-member-access": "error", -}; +const strictTypeCheckedTypeScriptRules = Object.assign({}, ...tseslint.configs.strictTypeChecked.map((config) => config.rules ?? {})); const codexPanelRuleIds = { chatStateDirectMutation: "codex-panel/no-chat-state-direct-mutation", imperativeDom: "codex-panel/no-imperative-dom", @@ -133,7 +128,10 @@ export default defineConfig([ setTimeout: "readonly", }, }, - rules: unsafeAnyTypeScriptRules, + rules: { + ...strictTypeCheckedTypeScriptRules, + "@typescript-eslint/require-await": "off", + }, }, { files: sourceTypeScriptFiles, diff --git a/src/app-server/connection/short-lived-client.ts b/src/app-server/connection/short-lived-client.ts index c1a25392..7f6818e4 100644 --- a/src/app-server/connection/short-lived-client.ts +++ b/src/app-server/connection/short-lived-client.ts @@ -7,8 +7,7 @@ export async function withShortLivedAppServerClient( operation: (client: AppServerClient) => Promise, options: AppServerClientAccessOptions = {}, ): Promise { - let client!: AppServerClient; - client = new AppServerClient(codexPath, cwd, { + const client = new AppServerClient(codexPath, cwd, { onNotification: () => undefined, onServerRequest: (request) => { client.rejectServerRequest( diff --git a/src/app-server/services/ephemeral-structured-turn.ts b/src/app-server/services/ephemeral-structured-turn.ts index 3d9814cf..4c1d716d 100644 --- a/src/app-server/services/ephemeral-structured-turn.ts +++ b/src/app-server/services/ephemeral-structured-turn.ts @@ -68,7 +68,6 @@ export async function runEphemeralStructuredTurn(options: RunEphemeralStructured throwIfAborted(options.signal, options.abortMessage); let state = createEphemeralStructuredTurnState(); const timers = options.timers ?? DEFAULT_EPHEMERAL_STRUCTURED_TURN_TIMERS; - let timeout: ReturnType | undefined; let handleNotification: (notification: ServerNotification) => void = () => undefined; let operationAbortError: Error | null = null; const operationAbort = new AbortController(); @@ -84,7 +83,7 @@ export async function runEphemeralStructuredTurn(options: RunEphemeralStructured () => operationAbortError ?? ephemeralStructuredTurnAbortError(options.abortMessage), ); - timeout = timers.setTimeout(() => { + const timeout = timers.setTimeout(() => { if (state.lifecycle.kind === "completed") return; state = completeEphemeralStructuredTurnState(state); abortOperation(new Error(options.timedOutMessage)); @@ -99,9 +98,8 @@ export async function runEphemeralStructuredTurn(options: RunEphemeralStructured }; }); - let client!: EphemeralStructuredTurnRuntimeCapableClient; const clientFactory = options.clientFactory ?? ((codexPath, cwd, handlers) => new AppServerClient(codexPath, cwd, handlers)); - client = clientFactory(options.codexPath, options.cwd, { + const client = clientFactory(options.codexPath, options.cwd, { onNotification: (notification) => { handleNotification(notification); }, diff --git a/src/app-server/services/thread-archive-markdown.ts b/src/app-server/services/thread-archive-markdown.ts index 1995a752..f5e4bf61 100644 --- a/src/app-server/services/thread-archive-markdown.ts +++ b/src/app-server/services/thread-archive-markdown.ts @@ -32,7 +32,7 @@ export async function exportArchivedThreadMarkdown( now = new Date(), ): Promise { const context = templateContext(thread, now); - const normalizePath = destination.normalizePath; + const normalizePath = (path: string): string => destination.normalizePath(path); const folder = folderPathFromTemplate(settings.archiveExportFolderTemplate, context, normalizePath); const filename = filenameFromTemplate(settings.archiveExportFilenameTemplate, context, normalizePath); await ensureFolder(destination, folder); diff --git a/src/features/chat/app-server/mappers/message-stream/turn-items.ts b/src/features/chat/app-server/mappers/message-stream/turn-items.ts index b3a07fd6..66dd247c 100644 --- a/src/features/chat/app-server/mappers/message-stream/turn-items.ts +++ b/src/features/chat/app-server/mappers/message-stream/turn-items.ts @@ -468,7 +468,8 @@ export function shouldSuppressLifecycleItem(item: TurnItem): boolean { return item.type === "agentMessage" || item.type === "userMessage"; } -function ignoredUnsupportedTurnItem(_item: never): null { +function ignoredUnsupportedTurnItem(item: never): null { + void item; return null; } diff --git a/src/features/chat/application/runtime/settings-actions.ts b/src/features/chat/application/runtime/settings-actions.ts index 8443eed1..2eac2df5 100644 --- a/src/features/chat/application/runtime/settings-actions.ts +++ b/src/features/chat/application/runtime/settings-actions.ts @@ -199,7 +199,8 @@ function autoReviewToggleMessage(state: AutoReviewState): string { return state === "enabled" ? "Auto-review on for subsequent turns." : "Auto-review off for subsequent turns."; } -function collaborationModeWarningMessage(_warning: NonNullable): string { +function collaborationModeWarningMessage(warning: NonNullable): string { + void warning; return "No effective model is available. Sending without a mode override."; }