From 16f3fa51a3170ddd2e34fe4f16c25247878d0c41 Mon Sep 17 00:00:00 2001 From: iliya Date: Thu, 19 Mar 2026 14:07:14 +0200 Subject: [PATCH] refactor: clean up code and improve readability across multiple files - Removed unnecessary eslint disable comments related to intentional mutations for clarity. - Updated import statements for consistency and organization. - Refactored regex patterns and utility functions in various components to enhance performance and maintainability. - Improved error handling and notifications for API errors in the teams IPC module. - Streamlined the handling of DOM mutations in the ChatHistory and other components for better readability. - Enhanced type definitions and added utility functions to improve code structure and type safety. --- electron.vite.config.1773920150269.mjs | 105 ------------------ src/main/ipc/teams.ts | 4 +- .../services/discovery/SubagentResolver.ts | 2 - .../infrastructure/CliInstallerService.ts | 10 +- src/main/services/team/TeamDataService.ts | 1 - .../services/team/TeamProvisioningService.ts | 2 - .../services/team/actionModeInstructions.ts | 3 +- src/main/utils/metadataExtraction.ts | 30 +++-- src/main/utils/teamNotificationBuilder.ts | 3 +- src/main/workers/team-fs-worker.ts | 5 +- src/renderer/components/chat/ChatHistory.tsx | 6 +- .../components/team/CliLogsRichView.tsx | 4 +- .../team/activity/ThoughtBodyContent.tsx | 2 +- .../team/dialogs/TaskCommentInput.tsx | 2 +- .../components/team/editor/EditorFileTree.tsx | 1 - .../components/ui/MentionableTextarea.tsx | 1 - .../components/ui/auto-resize-textarea.tsx | 1 - 17 files changed, 28 insertions(+), 154 deletions(-) delete mode 100644 electron.vite.config.1773920150269.mjs diff --git a/electron.vite.config.1773920150269.mjs b/electron.vite.config.1773920150269.mjs deleted file mode 100644 index 63a288e9..00000000 --- a/electron.vite.config.1773920150269.mjs +++ /dev/null @@ -1,105 +0,0 @@ -// electron.vite.config.ts -import { defineConfig, externalizeDepsPlugin } from "electron-vite"; -import react from "@vitejs/plugin-react"; -import { readFileSync } from "fs"; -import { resolve } from "path"; -var __electron_vite_injected_dirname = "/Users/belief/dev/projects/claude/claude_team"; -var pkg = JSON.parse(readFileSync(resolve(__electron_vite_injected_dirname, "package.json"), "utf-8")); -var prodDeps = Object.keys(pkg.dependencies || {}); -var bundledDeps = prodDeps.filter((d) => d !== "node-pty" && d !== "agent-teams-controller"); -function nativeModuleStub() { - const STUB_ID = "\0native-stub"; - return { - name: "native-module-stub", - resolveId(source) { - if (source.endsWith(".node")) return STUB_ID; - return null; - }, - load(id) { - if (id === STUB_ID) return "export default {}"; - return null; - } - }; -} -var electron_vite_config_default = defineConfig({ - main: { - plugins: [ - externalizeDepsPlugin({ - exclude: bundledDeps - }), - nativeModuleStub() - ], - resolve: { - alias: { - "@main": resolve(__electron_vite_injected_dirname, "src/main"), - "@shared": resolve(__electron_vite_injected_dirname, "src/shared"), - "@preload": resolve(__electron_vite_injected_dirname, "src/preload") - } - }, - build: { - outDir: "dist-electron/main", - rollupOptions: { - input: { - index: resolve(__electron_vite_injected_dirname, "src/main/index.ts"), - "team-fs-worker": resolve(__electron_vite_injected_dirname, "src/main/workers/team-fs-worker.ts") - }, - output: { - // CJS format so bundled deps can use __dirname/require. - // Use .cjs extension since package.json has "type": "module". - format: "cjs", - entryFileNames: "[name].cjs", - // Set UV_THREADPOOL_SIZE before any module code runs. - // Must be in the banner because ESM→CJS hoists imports above top-level code. - // On Windows, fs.watch({recursive:true}) occupies a UV pool thread per watcher; - // with 3+ watchers + concurrent fs/DNS/spawn, the default 4 threads deadlock. - banner: `if(!process.env.UV_THREADPOOL_SIZE){process.env.UV_THREADPOOL_SIZE='24'}` - } - } - } - }, - preload: { - plugins: [externalizeDepsPlugin()], - resolve: { - alias: { - "@preload": resolve(__electron_vite_injected_dirname, "src/preload"), - "@shared": resolve(__electron_vite_injected_dirname, "src/shared"), - "@main": resolve(__electron_vite_injected_dirname, "src/main") - } - }, - build: { - outDir: "dist-electron/preload", - rollupOptions: { - input: { - index: resolve(__electron_vite_injected_dirname, "src/preload/index.ts") - }, - output: { - format: "cjs", - entryFileNames: "[name].js" - } - } - } - }, - renderer: { - optimizeDeps: { - include: ["@codemirror/language-data"] - }, - resolve: { - alias: { - "@renderer": resolve(__electron_vite_injected_dirname, "src/renderer"), - "@shared": resolve(__electron_vite_injected_dirname, "src/shared"), - "@main": resolve(__electron_vite_injected_dirname, "src/main") - } - }, - plugins: [react()], - build: { - rollupOptions: { - input: { - index: resolve(__electron_vite_injected_dirname, "src/renderer/index.html") - } - } - } - } -}); -export { - electron_vite_config_default as default -}; diff --git a/src/main/ipc/teams.ts b/src/main/ipc/teams.ts index 414edb55..2eee6d42 100644 --- a/src/main/ipc/teams.ts +++ b/src/main/ipc/teams.ts @@ -63,6 +63,7 @@ import { import { AGENT_BLOCK_CLOSE, AGENT_BLOCK_OPEN } from '@shared/constants/agentBlocks'; import { KANBAN_COLUMN_IDS } from '@shared/constants/kanban'; import { MAX_TEXT_LENGTH } from '@shared/constants/teamLimits'; +import { isApiErrorMessage } from '@shared/utils/apiErrorDetector'; import { extractFlagsFromHelp, extractUserFlags, @@ -70,7 +71,6 @@ import { } from '@shared/utils/cliArgsParser'; import { createLogger } from '@shared/utils/logger'; import { isRateLimitMessage } from '@shared/utils/rateLimitDetector'; -import { isApiErrorMessage } from '@shared/utils/apiErrorDetector'; import crypto from 'crypto'; import { BrowserWindow, type IpcMain, type IpcMainInvokeEvent, Notification } from 'electron'; import * as fs from 'fs'; @@ -235,7 +235,7 @@ function checkApiErrorMessages( } // Extract status code for summary - const statusMatch = msg.text.match(/^API Error:\s*(\d{3})/); + const statusMatch = /^API Error:\s*(\d{3})/.exec(msg.text); const statusCode = statusMatch?.[1] ?? '???'; void NotificationManager.getInstance() diff --git a/src/main/services/discovery/SubagentResolver.ts b/src/main/services/discovery/SubagentResolver.ts index 3ba119fd..240e8e80 100644 --- a/src/main/services/discovery/SubagentResolver.ts +++ b/src/main/services/discovery/SubagentResolver.ts @@ -312,7 +312,6 @@ export class SubagentResolver { * Intentionally mutates the subagent in place for consistency with other resolution methods. */ private enrichSubagentFromTask(subagent: Process, taskCall: ToolCall): void { - /* eslint-disable no-param-reassign -- Mutation is intentional; subagent is enriched in place */ subagent.parentTaskId = taskCall.id; subagent.description = taskCall.taskDescription; subagent.subagentType = taskCall.taskSubagentType; @@ -323,7 +322,6 @@ export class SubagentResolver { if (teamName && memberName) { subagent.team = { teamName, memberName, memberColor: '' }; } - /* eslint-enable no-param-reassign -- End of intentional mutation block */ } /** diff --git a/src/main/services/infrastructure/CliInstallerService.ts b/src/main/services/infrastructure/CliInstallerService.ts index 5240a51a..765a3204 100644 --- a/src/main/services/infrastructure/CliInstallerService.ts +++ b/src/main/services/infrastructure/CliInstallerService.ts @@ -329,8 +329,8 @@ export class CliInstallerService { loggedIn?: boolean; authMethod?: string; }; - result.authLoggedIn = auth.loggedIn === true; // eslint-disable-line no-param-reassign -- intentional mutation of shared result object - result.authMethod = auth.authMethod ?? null; // eslint-disable-line no-param-reassign -- intentional mutation of shared result object + result.authLoggedIn = auth.loggedIn === true; + result.authMethod = auth.authMethod ?? null; logger.info( `Auth status: loggedIn=${result.authLoggedIn}, method=${result.authMethod ?? 'null'}` + (authAttempt > 1 ? ` (attempt ${authAttempt})` : '') @@ -347,7 +347,7 @@ export class CliInstallerService { logger.warn( `Auth status check failed after ${AUTH_STATUS_MAX_RETRIES} attempts: ${getErrorMessage(err)}` ); - result.authLoggedIn = false; // eslint-disable-line no-param-reassign -- intentional mutation of shared result object + result.authLoggedIn = false; } } } @@ -378,13 +378,13 @@ export class CliInstallerService { private async fetchLatestVersion(result: CliInstallationStatus): Promise { try { const latestRaw = await fetchText(`${GCS_BASE}/latest`); - result.latestVersion = normalizeVersion(latestRaw); // eslint-disable-line no-param-reassign -- intentional mutation of shared result object + result.latestVersion = normalizeVersion(latestRaw); logger.info( `Latest CLI version: "${latestRaw.trim()}" → normalized: "${result.latestVersion}"` ); if (result.installedVersion && result.latestVersion) { - result.updateAvailable = isVersionOlder(result.installedVersion, result.latestVersion); // eslint-disable-line no-param-reassign -- intentional mutation of shared result object + result.updateAvailable = isVersionOlder(result.installedVersion, result.latestVersion); logger.info( `Update available: ${result.updateAvailable} (${result.installedVersion} → ${result.latestVersion})` ); diff --git a/src/main/services/team/TeamDataService.ts b/src/main/services/team/TeamDataService.ts index aeb48a46..b8cdce82 100644 --- a/src/main/services/team/TeamDataService.ts +++ b/src/main/services/team/TeamDataService.ts @@ -652,7 +652,6 @@ export class TeamDataService { 2000 ); if (branch && branch !== leadBranch) { - // eslint-disable-next-line no-param-reassign -- intentional in-place enrichment member.gitBranch = branch; } } catch { diff --git a/src/main/services/team/TeamProvisioningService.ts b/src/main/services/team/TeamProvisioningService.ts index 4b215949..07dfc7b3 100644 --- a/src/main/services/team/TeamProvisioningService.ts +++ b/src/main/services/team/TeamProvisioningService.ts @@ -1,4 +1,3 @@ -/* eslint-disable no-param-reassign -- ProvisioningRun object is intentionally mutated as a state tracker throughout the provisioning lifecycle */ import { ConfigManager } from '@main/services/infrastructure/ConfigManager'; import { NotificationManager } from '@main/services/infrastructure/NotificationManager'; import { killProcessTree, spawnCli } from '@main/utils/childProcess'; @@ -7586,4 +7585,3 @@ export class TeamProvisioningService { }); } } -/* eslint-enable no-param-reassign -- Re-enable after TeamProvisioningService class */ diff --git a/src/main/services/team/actionModeInstructions.ts b/src/main/services/team/actionModeInstructions.ts index 1297853d..ae6692a1 100644 --- a/src/main/services/team/actionModeInstructions.ts +++ b/src/main/services/team/actionModeInstructions.ts @@ -1,9 +1,8 @@ import { AGENT_BLOCK_CLOSE, AGENT_BLOCK_OPEN } from '@shared/constants/agentBlocks'; +import * as agentTeamsControllerModule from 'agent-teams-controller'; import type { AgentActionMode } from '@shared/types'; -import * as agentTeamsControllerModule from 'agent-teams-controller'; - const { protocols } = agentTeamsControllerModule; const LEAD_DELEGATE_DESCRIPTION = diff --git a/src/main/utils/metadataExtraction.ts b/src/main/utils/metadataExtraction.ts index 8ec70183..f861401e 100644 --- a/src/main/utils/metadataExtraction.ts +++ b/src/main/utils/metadataExtraction.ts @@ -10,6 +10,7 @@ import { LocalFileSystemProvider } from '../services/infrastructure/LocalFileSys import { type ChatHistoryEntry, isTextContent, type UserEntry } from '../types'; import type { FileSystemProvider } from '../services/infrastructure/FileSystemProvider'; +import type { Readable } from 'stream'; const logger = createLogger('Util:metadataExtraction'); @@ -29,6 +30,16 @@ function byteLen(chunk: string): number { return Buffer.byteLength(chunk, 'utf8'); } +function createStreamCleanup(rl: readline.Interface, fileStream: Readable): () => void { + let cleaned = false; + return (): void => { + if (cleaned) return; + cleaned = true; + rl.close(); + fileStream.destroy(); + }; +} + /** * Extract CWD (current working directory) from the first entry. * Used to get the actual project path from encoded directory names. @@ -58,17 +69,8 @@ export async function extractCwd( let bytes = 0; let timedOut = false; - let cleaned = false; - // Close readline FIRST so `for await` exits, then destroy the stream. - // Calling only stream.destroy() can leave readline hanging when it has - // a partial line buffered (e.g. a 400KB+ JSONL line read in 64KB chunks). - const cleanup = (): void => { - if (cleaned) return; - cleaned = true; - rl.close(); - fileStream.destroy(); - }; + const cleanup = createStreamCleanup(rl, fileStream); const timer = setTimeout(() => { timedOut = true; @@ -140,14 +142,8 @@ export async function extractFirstUserMessagePreview( let bytes = 0; let timedOut = false; - let cleaned = false; - const cleanup = (): void => { - if (cleaned) return; - cleaned = true; - rl.close(); - fileStream.destroy(); - }; + const cleanup = createStreamCleanup(rl, fileStream); const timer = setTimeout(() => { timedOut = true; diff --git a/src/main/utils/teamNotificationBuilder.ts b/src/main/utils/teamNotificationBuilder.ts index f038a588..40b5e810 100644 --- a/src/main/utils/teamNotificationBuilder.ts +++ b/src/main/utils/teamNotificationBuilder.ts @@ -5,9 +5,8 @@ * to convert domain-level team payloads into the unified notification format. */ -import { randomUUID } from 'crypto'; - import { stripAgentBlocks } from '@shared/constants/agentBlocks'; +import { randomUUID } from 'crypto'; import type { DetectedError } from '../services/error/ErrorMessageBuilder'; import type { TriggerColor } from '@shared/constants/triggerColors'; diff --git a/src/main/workers/team-fs-worker.ts b/src/main/workers/team-fs-worker.ts index d02fe020..b5910cbb 100644 --- a/src/main/workers/team-fs-worker.ts +++ b/src/main/workers/team-fs-worker.ts @@ -241,14 +241,13 @@ function nowMs(): number { } function bumpSkipReason(reasons: Record, reason: string): void { - // eslint-disable-next-line no-param-reassign -- accumulator mutation is intentional reasons[reason] = (reasons[reason] || 0) + 1; } function pushSlowest(list: SlowEntry[], entry: SlowEntry, maxLen: number): void { list.push(entry); list.sort((a, b) => b.ms - a.ms); - // eslint-disable-next-line no-param-reassign -- truncate in-place is intentional + if (list.length > maxLen) list.length = maxLen; } @@ -734,10 +733,8 @@ async function readTasksDirForTeam( } function mergeTaskDiag(target: GetAllTasksDiag, source: TaskReadDiag): void { - // eslint-disable-next-line no-param-reassign -- accumulator mutation is intentional target.skipped += source.skipped; for (const [reason, count] of Object.entries(source.skipReasons)) { - // eslint-disable-next-line no-param-reassign -- accumulator mutation is intentional target.skipReasons[reason] = (target.skipReasons[reason] || 0) + count; } } diff --git a/src/renderer/components/chat/ChatHistory.tsx b/src/renderer/components/chat/ChatHistory.tsx index df86fd30..96a2eeb2 100644 --- a/src/renderer/components/chat/ChatHistory.tsx +++ b/src/renderer/components/chat/ChatHistory.tsx @@ -615,20 +615,18 @@ export const ChatHistory = ({ tabId }: ChatHistoryProps): JSX.Element => { container .querySelectorAll('mark[data-search-result="current"]') .forEach((prev) => { - /* eslint-disable no-param-reassign -- Directly mutating DOM element style/attributes is necessary for search result highlighting */ prev.setAttribute('data-search-result', 'match'); prev.style.backgroundColor = 'var(--highlight-bg-inactive)'; prev.style.color = 'var(--highlight-text-inactive)'; prev.style.boxShadow = ''; - /* eslint-enable no-param-reassign -- Re-enable after DOM mutations */ }); } - /* eslint-disable no-param-reassign -- Directly mutating DOM element style/attributes is necessary for current search result highlighting */ + el.setAttribute('data-search-result', 'current'); el.style.backgroundColor = 'var(--highlight-bg)'; el.style.color = 'var(--highlight-text)'; el.style.boxShadow = '0 0 0 1px var(--highlight-ring)'; - /* eslint-enable no-param-reassign -- Re-enable after DOM mutations */ + el.scrollIntoView({ behavior: 'smooth', block: 'center' }); }; diff --git a/src/renderer/components/team/CliLogsRichView.tsx b/src/renderer/components/team/CliLogsRichView.tsx index 9cd465c8..993cfa15 100644 --- a/src/renderer/components/team/CliLogsRichView.tsx +++ b/src/renderer/components/team/CliLogsRichView.tsx @@ -105,10 +105,8 @@ function restoreViewport( ): void { if (viewport.mode === 'edge') { if (viewport.edge === 'newest') { - // eslint-disable-next-line no-param-reassign -- DOM scroll positioning requires direct mutation container.scrollTop = order === 'newest-first' ? 0 : container.scrollHeight; } else { - // eslint-disable-next-line no-param-reassign -- DOM scroll positioning requires direct mutation container.scrollTop = order === 'newest-first' ? container.scrollHeight : 0; } return; @@ -121,7 +119,7 @@ function restoreViewport( const containerRect = container.getBoundingClientRect(); const elRect = el.getBoundingClientRect(); const currentOffset = elRect.top - containerRect.top; - // eslint-disable-next-line no-param-reassign -- DOM scroll positioning requires direct mutation + container.scrollTop += currentOffset - viewport.offsetTop; } diff --git a/src/renderer/components/team/activity/ThoughtBodyContent.tsx b/src/renderer/components/team/activity/ThoughtBodyContent.tsx index b14d7297..de9c61f4 100644 --- a/src/renderer/components/team/activity/ThoughtBodyContent.tsx +++ b/src/renderer/components/team/activity/ThoughtBodyContent.tsx @@ -4,7 +4,6 @@ import { MarkdownViewer } from '@renderer/components/chat/viewers/MarkdownViewer import { CopyButton } from '@renderer/components/common/CopyButton'; import { Tooltip, TooltipContent, TooltipTrigger } from '@renderer/components/ui/tooltip'; import { CARD_ICON_MUTED, CARD_TEXT_LIGHT } from '@renderer/constants/cssVariables'; -import { isApiErrorMessage } from '@shared/utils/apiErrorDetector'; import { linkifyAllMentionsInMarkdown } from '@renderer/utils/mentionLinkify'; import { areStringArraysEqual, @@ -12,6 +11,7 @@ import { areThoughtMessagesEquivalentForRender, } from '@renderer/utils/messageRenderEquality'; import { linkifyTaskIdsInMarkdown, parseTaskLinkHref } from '@renderer/utils/taskReferenceUtils'; +import { isApiErrorMessage } from '@shared/utils/apiErrorDetector'; import { Reply } from 'lucide-react'; import { formatTimeWithSec, ToolSummaryTooltipContent } from './LeadThoughtsGroup'; diff --git a/src/renderer/components/team/dialogs/TaskCommentInput.tsx b/src/renderer/components/team/dialogs/TaskCommentInput.tsx index f0c53fb7..bd07fa46 100644 --- a/src/renderer/components/team/dialogs/TaskCommentInput.tsx +++ b/src/renderer/components/team/dialogs/TaskCommentInput.tsx @@ -291,7 +291,7 @@ export const TaskCommentInput = ({ className="hidden" onChange={(e) => { if (e.target.files) addFiles(e.target.files); - // eslint-disable-next-line no-param-reassign -- reset file input to allow re-selecting same file + e.target.value = ''; }} /> diff --git a/src/renderer/components/team/editor/EditorFileTree.tsx b/src/renderer/components/team/editor/EditorFileTree.tsx index 9c0a4271..3172ccab 100644 --- a/src/renderer/components/team/editor/EditorFileTree.tsx +++ b/src/renderer/components/team/editor/EditorFileTree.tsx @@ -583,7 +583,6 @@ const RootDropZone = React.forwardRef< (el: HTMLDivElement | null) => { setNodeRef(el); if (typeof ref === 'function') ref(el); - // eslint-disable-next-line no-param-reassign -- combining forwarded ref with droppable ref else if (ref) ref.current = el; }, [ref, setNodeRef] diff --git a/src/renderer/components/ui/MentionableTextarea.tsx b/src/renderer/components/ui/MentionableTextarea.tsx index c78e3ff9..a62d351c 100644 --- a/src/renderer/components/ui/MentionableTextarea.tsx +++ b/src/renderer/components/ui/MentionableTextarea.tsx @@ -371,7 +371,6 @@ export const MentionableTextarea = React.forwardRef