diff --git a/src/cli/bg/engines/tmux.ts b/src/cli/bg/engines/tmux.ts index c937549ce..d327f9d92 100644 --- a/src/cli/bg/engines/tmux.ts +++ b/src/cli/bg/engines/tmux.ts @@ -68,13 +68,3 @@ export class TmuxEngine implements BgEngine { } } } - -export function getTmuxInstallHint(): string { - if (process.platform === 'darwin') { - return 'Install with: brew install tmux' - } - if (process.platform === 'win32') { - return 'tmux is not natively available on Windows. Consider using WSL.' - } - return 'Install with: sudo apt install tmux (or your package manager)' -} diff --git a/src/cli/update.ts b/src/cli/update.ts deleted file mode 100644 index a0cd35f5d..000000000 --- a/src/cli/update.ts +++ /dev/null @@ -1,422 +0,0 @@ -import chalk from 'chalk' -import { logEvent } from 'src/services/analytics/index.js' -import { - getLatestVersion, - type InstallStatus, - installGlobalPackage, -} from 'src/utils/autoUpdater.js' -import { regenerateCompletionCache } from 'src/utils/completionCache.js' -import { - getGlobalConfig, - type InstallMethod, - saveGlobalConfig, -} from 'src/utils/config.js' -import { logForDebugging } from 'src/utils/debug.js' -import { getDoctorDiagnostic } from 'src/utils/doctorDiagnostic.js' -import { gracefulShutdown } from 'src/utils/gracefulShutdown.js' -import { - installOrUpdateClaudePackage, - localInstallationExists, -} from 'src/utils/localInstaller.js' -import { - installLatest as installLatestNative, - removeInstalledSymlink, -} from 'src/utils/nativeInstaller/index.js' -import { getPackageManager } from 'src/utils/nativeInstaller/packageManagers.js' -import { writeToStdout } from 'src/utils/process.js' -import { gte } from 'src/utils/semver.js' -import { getInitialSettings } from 'src/utils/settings/settings.js' - -export async function update() { - logEvent('tengu_update_check', {}) - writeToStdout(`Current version: ${MACRO.VERSION}\n`) - - const channel = getInitialSettings()?.autoUpdatesChannel ?? 'latest' - writeToStdout(`Checking for updates to ${channel} version...\n`) - - logForDebugging('update: Starting update check') - - // Run diagnostic to detect potential issues - logForDebugging('update: Running diagnostic') - const diagnostic = await getDoctorDiagnostic() - logForDebugging(`update: Installation type: ${diagnostic.installationType}`) - logForDebugging( - `update: Config install method: ${diagnostic.configInstallMethod}`, - ) - - // Check for multiple installations - if (diagnostic.multipleInstallations.length > 1) { - writeToStdout('\n') - writeToStdout(chalk.yellow('Warning: Multiple installations found') + '\n') - for (const install of diagnostic.multipleInstallations) { - const current = - diagnostic.installationType === install.type - ? ' (currently running)' - : '' - writeToStdout(`- ${install.type} at ${install.path}${current}\n`) - } - } - - // Display warnings if any exist - if (diagnostic.warnings.length > 0) { - writeToStdout('\n') - for (const warning of diagnostic.warnings) { - logForDebugging(`update: Warning detected: ${warning.issue}`) - - // Don't skip PATH warnings - they're always relevant - // The user needs to know that 'which claude' points elsewhere - logForDebugging(`update: Showing warning: ${warning.issue}`) - - writeToStdout(chalk.yellow(`Warning: ${warning.issue}\n`)) - - writeToStdout(chalk.bold(`Fix: ${warning.fix}\n`)) - } - } - - // Update config if installMethod is not set (but skip for package managers) - const config = getGlobalConfig() - if ( - !config.installMethod && - diagnostic.installationType !== 'package-manager' - ) { - writeToStdout('\n') - writeToStdout('Updating configuration to track installation method...\n') - let detectedMethod: 'local' | 'native' | 'global' | 'unknown' = 'unknown' - - // Map diagnostic installation type to config install method - switch (diagnostic.installationType) { - case 'npm-local': - detectedMethod = 'local' - break - case 'native': - detectedMethod = 'native' - break - case 'npm-global': - detectedMethod = 'global' - break - default: - detectedMethod = 'unknown' - } - - saveGlobalConfig(current => ({ - ...current, - installMethod: detectedMethod, - })) - writeToStdout(`Installation method set to: ${detectedMethod}\n`) - } - - // Check if running from development build - if (diagnostic.installationType === 'development') { - writeToStdout('\n') - writeToStdout( - chalk.yellow('Warning: Cannot update development build') + '\n', - ) - await gracefulShutdown(1) - } - - // Check if running from a package manager - if (diagnostic.installationType === 'package-manager') { - const packageManager = await getPackageManager() - writeToStdout('\n') - - if (packageManager === 'homebrew') { - writeToStdout('Claude is managed by Homebrew.\n') - const latest = await getLatestVersion(channel) - if (latest && !gte(MACRO.VERSION, latest)) { - writeToStdout(`Update available: ${MACRO.VERSION} → ${latest}\n`) - writeToStdout('\n') - writeToStdout('To update, run:\n') - writeToStdout(chalk.bold(' brew upgrade claude-code') + '\n') - } else { - writeToStdout('Claude is up to date!\n') - } - } else if (packageManager === 'winget') { - writeToStdout('Claude is managed by winget.\n') - const latest = await getLatestVersion(channel) - if (latest && !gte(MACRO.VERSION, latest)) { - writeToStdout(`Update available: ${MACRO.VERSION} → ${latest}\n`) - writeToStdout('\n') - writeToStdout('To update, run:\n') - writeToStdout( - chalk.bold(' winget upgrade Anthropic.ClaudeCode') + '\n', - ) - } else { - writeToStdout('Claude is up to date!\n') - } - } else if (packageManager === 'apk') { - writeToStdout('Claude is managed by apk.\n') - const latest = await getLatestVersion(channel) - if (latest && !gte(MACRO.VERSION, latest)) { - writeToStdout(`Update available: ${MACRO.VERSION} → ${latest}\n`) - writeToStdout('\n') - writeToStdout('To update, run:\n') - writeToStdout(chalk.bold(' apk upgrade claude-code') + '\n') - } else { - writeToStdout('Claude is up to date!\n') - } - } else { - // pacman, deb, and rpm don't get specific commands because they each have - // multiple frontends (pacman: yay/paru/makepkg, deb: apt/apt-get/aptitude/nala, - // rpm: dnf/yum/zypper) - writeToStdout('Claude is managed by a package manager.\n') - writeToStdout('Please use your package manager to update.\n') - } - - await gracefulShutdown(0) - } - - // Check for config/reality mismatch (skip for package-manager installs) - if ( - config.installMethod && - diagnostic.configInstallMethod !== 'not set' && - diagnostic.installationType !== 'package-manager' - ) { - const runningType = diagnostic.installationType - const configExpects = diagnostic.configInstallMethod - - // Map installation types for comparison - const typeMapping: Record = { - 'npm-local': 'local', - 'npm-global': 'global', - native: 'native', - development: 'development', - unknown: 'unknown', - } - - const normalizedRunningType = typeMapping[runningType] || runningType - - if ( - normalizedRunningType !== configExpects && - configExpects !== 'unknown' - ) { - writeToStdout('\n') - writeToStdout(chalk.yellow('Warning: Configuration mismatch') + '\n') - writeToStdout(`Config expects: ${configExpects} installation\n`) - writeToStdout(`Currently running: ${runningType}\n`) - writeToStdout( - chalk.yellow( - `Updating the ${runningType} installation you are currently using`, - ) + '\n', - ) - - // Update config to match reality - saveGlobalConfig(current => ({ - ...current, - installMethod: normalizedRunningType as InstallMethod, - })) - writeToStdout( - `Config updated to reflect current installation method: ${normalizedRunningType}\n`, - ) - } - } - - // Handle native installation updates first - if (diagnostic.installationType === 'native') { - logForDebugging( - 'update: Detected native installation, using native updater', - ) - try { - const result = await installLatestNative(channel, true) - - // Handle lock contention gracefully - if (result.lockFailed) { - const pidInfo = result.lockHolderPid - ? ` (PID ${result.lockHolderPid})` - : '' - writeToStdout( - chalk.yellow( - `Another Claude process${pidInfo} is currently running. Please try again in a moment.`, - ) + '\n', - ) - await gracefulShutdown(0) - } - - if (!result.latestVersion) { - process.stderr.write('Failed to check for updates\n') - await gracefulShutdown(1) - } - - if (result.latestVersion === MACRO.VERSION) { - writeToStdout( - chalk.green(`Claude Code is up to date (${MACRO.VERSION})`) + '\n', - ) - } else { - writeToStdout( - chalk.green( - `Successfully updated from ${MACRO.VERSION} to version ${result.latestVersion}`, - ) + '\n', - ) - await regenerateCompletionCache() - } - await gracefulShutdown(0) - } catch (error) { - process.stderr.write('Error: Failed to install native update\n') - process.stderr.write(String(error) + '\n') - process.stderr.write('Try running "claude doctor" for diagnostics\n') - await gracefulShutdown(1) - } - } - - // Fallback to existing JS/npm-based update logic - // Remove native installer symlink since we're not using native installation - // But only if user hasn't migrated to native installation - if (config.installMethod !== 'native') { - await removeInstalledSymlink() - } - - logForDebugging('update: Checking npm registry for latest version') - logForDebugging(`update: Package URL: ${MACRO.PACKAGE_URL}`) - const npmTag = channel === 'stable' ? 'stable' : 'latest' - const npmCommand = `npm view ${MACRO.PACKAGE_URL}@${npmTag} version` - logForDebugging(`update: Running: ${npmCommand}`) - const latestVersion = await getLatestVersion(channel) - logForDebugging( - `update: Latest version from npm: ${latestVersion || 'FAILED'}`, - ) - - if (!latestVersion) { - logForDebugging('update: Failed to get latest version from npm registry') - process.stderr.write(chalk.red('Failed to check for updates') + '\n') - process.stderr.write('Unable to fetch latest version from npm registry\n') - process.stderr.write('\n') - process.stderr.write('Possible causes:\n') - process.stderr.write(' • Network connectivity issues\n') - process.stderr.write(' • npm registry is unreachable\n') - process.stderr.write(' • Corporate proxy/firewall blocking npm\n') - if (MACRO.PACKAGE_URL && !MACRO.PACKAGE_URL.startsWith('@anthropic')) { - process.stderr.write( - ' • Internal/development build not published to npm\n', - ) - } - process.stderr.write('\n') - process.stderr.write('Try:\n') - process.stderr.write(' • Check your internet connection\n') - process.stderr.write(' • Run with --debug flag for more details\n') - const packageName = - MACRO.PACKAGE_URL || - (process.env.USER_TYPE === 'ant' - ? '@anthropic-ai/claude-cli' - : '@anthropic-ai/claude-code') - process.stderr.write( - ` • Manually check: npm view ${packageName} version\n`, - ) - - process.stderr.write(' • Check if you need to login: npm whoami\n') - await gracefulShutdown(1) - } - - // Check if versions match exactly, including any build metadata (like SHA) - if (latestVersion === MACRO.VERSION) { - writeToStdout( - chalk.green(`Claude Code is up to date (${MACRO.VERSION})`) + '\n', - ) - await gracefulShutdown(0) - } - - writeToStdout( - `New version available: ${latestVersion} (current: ${MACRO.VERSION})\n`, - ) - writeToStdout('Installing update...\n') - - // Determine update method based on what's actually running - let useLocalUpdate = false - let updateMethodName = '' - - switch (diagnostic.installationType) { - case 'npm-local': - useLocalUpdate = true - updateMethodName = 'local' - break - case 'npm-global': - useLocalUpdate = false - updateMethodName = 'global' - break - case 'unknown': { - // Fallback to detection if we can't determine installation type - const isLocal = await localInstallationExists() - useLocalUpdate = isLocal - updateMethodName = isLocal ? 'local' : 'global' - writeToStdout( - chalk.yellow('Warning: Could not determine installation type') + '\n', - ) - writeToStdout( - `Attempting ${updateMethodName} update based on file detection...\n`, - ) - break - } - default: - process.stderr.write( - `Error: Cannot update ${diagnostic.installationType} installation\n`, - ) - await gracefulShutdown(1) - } - - writeToStdout(`Using ${updateMethodName} installation update method...\n`) - - logForDebugging(`update: Update method determined: ${updateMethodName}`) - logForDebugging(`update: useLocalUpdate: ${useLocalUpdate}`) - - let status: InstallStatus - - if (useLocalUpdate) { - logForDebugging( - 'update: Calling installOrUpdateClaudePackage() for local update', - ) - status = await installOrUpdateClaudePackage(channel) - } else { - logForDebugging('update: Calling installGlobalPackage() for global update') - status = await installGlobalPackage() - } - - logForDebugging(`update: Installation status: ${status}`) - - switch (status) { - case 'success': - writeToStdout( - chalk.green( - `Successfully updated from ${MACRO.VERSION} to version ${latestVersion}`, - ) + '\n', - ) - await regenerateCompletionCache() - break - case 'no_permissions': - process.stderr.write( - 'Error: Insufficient permissions to install update\n', - ) - if (useLocalUpdate) { - process.stderr.write('Try manually updating with:\n') - process.stderr.write( - ` cd ~/.claude/local && npm update ${MACRO.PACKAGE_URL}\n`, - ) - } else { - process.stderr.write('Try running with sudo or fix npm permissions\n') - process.stderr.write( - 'Or consider using native installation with: claude install\n', - ) - } - await gracefulShutdown(1) - break - case 'install_failed': - process.stderr.write('Error: Failed to install update\n') - if (useLocalUpdate) { - process.stderr.write('Try manually updating with:\n') - process.stderr.write( - ` cd ~/.claude/local && npm update ${MACRO.PACKAGE_URL}\n`, - ) - } else { - process.stderr.write( - 'Or consider using native installation with: claude install\n', - ) - } - await gracefulShutdown(1) - break - case 'in_progress': - process.stderr.write( - 'Error: Another instance is currently performing an update\n', - ) - process.stderr.write('Please wait and try again later\n') - await gracefulShutdown(1) - break - } - await gracefulShutdown(0) -} diff --git a/src/components/design-system/Divider.tsx b/src/components/design-system/Divider.tsx deleted file mode 100644 index 3ec1c0cf0..000000000 --- a/src/components/design-system/Divider.tsx +++ /dev/null @@ -1 +0,0 @@ -export { Divider } from '@anthropic/ink'; diff --git a/src/components/design-system/FuzzyPicker.tsx b/src/components/design-system/FuzzyPicker.tsx deleted file mode 100644 index 3ca71160c..000000000 --- a/src/components/design-system/FuzzyPicker.tsx +++ /dev/null @@ -1 +0,0 @@ -export { FuzzyPicker } from '@anthropic/ink'; diff --git a/src/components/design-system/LoadingState.tsx b/src/components/design-system/LoadingState.tsx deleted file mode 100644 index c382e7964..000000000 --- a/src/components/design-system/LoadingState.tsx +++ /dev/null @@ -1 +0,0 @@ -export { LoadingState } from '@anthropic/ink'; diff --git a/src/components/design-system/Pane.tsx b/src/components/design-system/Pane.tsx deleted file mode 100644 index 3160b1138..000000000 --- a/src/components/design-system/Pane.tsx +++ /dev/null @@ -1 +0,0 @@ -export { Pane } from '@anthropic/ink'; diff --git a/src/components/design-system/ProgressBar.tsx b/src/components/design-system/ProgressBar.tsx deleted file mode 100644 index 31f9a6e5f..000000000 --- a/src/components/design-system/ProgressBar.tsx +++ /dev/null @@ -1 +0,0 @@ -export { ProgressBar } from '@anthropic/ink'; diff --git a/src/components/design-system/Ratchet.tsx b/src/components/design-system/Ratchet.tsx deleted file mode 100644 index 1283ac9e1..000000000 --- a/src/components/design-system/Ratchet.tsx +++ /dev/null @@ -1 +0,0 @@ -export { Ratchet } from '@anthropic/ink'; diff --git a/src/components/design-system/StatusIcon.tsx b/src/components/design-system/StatusIcon.tsx deleted file mode 100644 index 05e75b564..000000000 --- a/src/components/design-system/StatusIcon.tsx +++ /dev/null @@ -1 +0,0 @@ -export { StatusIcon } from '@anthropic/ink'; diff --git a/src/components/design-system/Tabs.tsx b/src/components/design-system/Tabs.tsx deleted file mode 100644 index 01433ba1d..000000000 --- a/src/components/design-system/Tabs.tsx +++ /dev/null @@ -1 +0,0 @@ -export { Tab, Tabs, useTabHeaderFocus, useTabsWidth } from '@anthropic/ink'; diff --git a/src/components/design-system/ThemeProvider.tsx b/src/components/design-system/ThemeProvider.tsx deleted file mode 100644 index db5b2a52f..000000000 --- a/src/components/design-system/ThemeProvider.tsx +++ /dev/null @@ -1 +0,0 @@ -export { ThemeProvider, usePreviewTheme, useTheme, useThemeSetting } from '@anthropic/ink'; diff --git a/src/components/design-system/ThemedBox.tsx b/src/components/design-system/ThemedBox.tsx deleted file mode 100644 index f40832612..000000000 --- a/src/components/design-system/ThemedBox.tsx +++ /dev/null @@ -1 +0,0 @@ -export { Box as default } from '@anthropic/ink'; diff --git a/src/components/design-system/color.ts b/src/components/design-system/color.ts deleted file mode 100644 index 56c8612de..000000000 --- a/src/components/design-system/color.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { type ColorType, colorize, type Color } from '@anthropic/ink' -import { getTheme, type Theme, type ThemeName } from '../../utils/theme.js' - -/** - * Curried theme-aware color function. Resolves theme keys to raw color - * values before delegating to the ink renderer's colorize. - */ -export function color( - c: keyof Theme | Color | undefined, - theme: ThemeName, - type: ColorType = 'foreground', -): (text: string) => string { - return text => { - if (!c) { - return text - } - // Raw color values bypass theme lookup - if ( - c.startsWith('rgb(') || - c.startsWith('#') || - c.startsWith('ansi256(') || - c.startsWith('ansi:') - ) { - return colorize(text, c, type) - } - // Theme key lookup - return colorize(text, getTheme(theme)[c as keyof Theme], type) - } -} diff --git a/src/components/tasks/src/tasks/DreamTask/DreamTask.ts b/src/components/tasks/src/tasks/DreamTask/DreamTask.ts deleted file mode 100644 index e2e0bffe2..000000000 --- a/src/components/tasks/src/tasks/DreamTask/DreamTask.ts +++ /dev/null @@ -1,18 +0,0 @@ -// Type re-exports for DreamTask — bridges the component tree to the task registry. -// The real implementation lives in src/tasks/DreamTask/DreamTask.ts. -// Note: Currently unused — BackgroundTasksDialog.tsx imports directly from -// src/tasks/DreamTask/DreamTask.js. Kept for decompilation completeness. - -export type { - DreamTaskState, - DreamPhase, - DreamTurn, -} from '../../../../../tasks/DreamTask/DreamTask.js' -export { - isDreamTask, - registerDreamTask, - addDreamTurn, - completeDreamTask, - failDreamTask, - DreamTask, -} from '../../../../../tasks/DreamTask/DreamTask.js' diff --git a/src/components/tasks/src/tasks/LocalWorkflowTask/LocalWorkflowTask.ts b/src/components/tasks/src/tasks/LocalWorkflowTask/LocalWorkflowTask.ts deleted file mode 100644 index 64c2cfba7..000000000 --- a/src/components/tasks/src/tasks/LocalWorkflowTask/LocalWorkflowTask.ts +++ /dev/null @@ -1,2 +0,0 @@ -// Auto-generated stub — replace with real implementation -export {} diff --git a/src/components/tasks/src/types/utils.ts b/src/components/tasks/src/types/utils.ts deleted file mode 100644 index 64c2cfba7..000000000 --- a/src/components/tasks/src/types/utils.ts +++ /dev/null @@ -1,2 +0,0 @@ -// Auto-generated stub — replace with real implementation -export {} diff --git a/src/constants/outputStyles.ts b/src/constants/outputStyles.ts index 01932903d..38b4181c6 100644 --- a/src/constants/outputStyles.ts +++ b/src/constants/outputStyles.ts @@ -185,8 +185,8 @@ export async function getOutputStyleConfig(): Promise const forcedStyles = Object.values(allStyles).filter( (style): style is OutputStyleConfig => style !== null && - (style as any).source === 'plugin' && - (style as any).forceForPlugin === true, + style.source === 'plugin' && + style.forceForPlugin === true, ) const firstForcedStyle = forcedStyles[0] @@ -209,8 +209,3 @@ export async function getOutputStyleConfig(): Promise return allStyles[outputStyle] ?? null } - -export function hasCustomOutputStyle(): boolean { - const style = getSettings_DEPRECATED()?.outputStyle - return style !== undefined && style !== DEFAULT_OUTPUT_STYLE_NAME -} diff --git a/src/hooks/fileSuggestions.ts b/src/hooks/fileSuggestions.ts index 10522e877..20108a7c3 100644 --- a/src/hooks/fileSuggestions.ts +++ b/src/hooks/fileSuggestions.ts @@ -387,13 +387,7 @@ async function getFilesUsingGit( * For example, if the input is ['src/index.js', 'src/utils/helpers.js'], * the output will be ['src/', 'src/utils/']. * @param files An array of file paths - * @returns An array of unique directory names with a trailing separator */ -export function getDirectoryNames(files: string[]): string[] { - const directoryNames = new Set() - collectDirectoryNames(files, 0, files.length, directoryNames) - return [...directoryNames].map(d => d + path.sep) -} /** * Async variant: yields every ~10k files so 270k+ file lists don't block diff --git a/src/hooks/notifs/useAutoModeUnavailableNotification.ts b/src/hooks/notifs/useAutoModeUnavailableNotification.ts deleted file mode 100644 index 028a80cad..000000000 --- a/src/hooks/notifs/useAutoModeUnavailableNotification.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { feature } from 'bun:bundle' -import { useEffect, useRef } from 'react' -import { useNotifications } from 'src/context/notifications.js' -import { getIsRemoteMode } from '../../bootstrap/state.js' -import { useAppState } from '../../state/AppState.js' -import type { PermissionMode } from '../../utils/permissions/PermissionMode.js' -import { - getAutoModeUnavailableNotification, - getAutoModeUnavailableReason, -} from '../../utils/permissions/permissionSetup.js' -import { hasAutoModeOptIn } from '../../utils/settings/settings.js' - -/** - * Shows a one-shot notification when the shift-tab carousel wraps past where - * auto mode would have been. Covers all reasons (settings, circuit-breaker, - * org-allowlist). The startup case (defaultMode: auto silently downgraded) is - * handled by verifyAutoModeGateAccess → checkAndDisableAutoModeIfNeeded. - */ -export function useAutoModeUnavailableNotification(): void { - const { addNotification } = useNotifications() - const mode = useAppState(s => s.toolPermissionContext.mode) - const isAutoModeAvailable = useAppState( - s => s.toolPermissionContext.isAutoModeAvailable, - ) - const shownRef = useRef(false) - const prevModeRef = useRef(mode) - - useEffect(() => { - const prevMode = prevModeRef.current - prevModeRef.current = mode - - if (!feature('TRANSCRIPT_CLASSIFIER')) return - if (getIsRemoteMode()) return - if (shownRef.current) return - - const wrappedPastAutoSlot = - mode === 'default' && - prevMode !== 'default' && - prevMode !== 'auto' && - !isAutoModeAvailable && - hasAutoModeOptIn() - - if (!wrappedPastAutoSlot) return - - const reason = getAutoModeUnavailableReason() - if (!reason) return - - shownRef.current = true - addNotification({ - key: 'auto-mode-unavailable', - text: getAutoModeUnavailableNotification(reason), - color: 'warning', - priority: 'medium', - }) - }, [mode, isAutoModeAvailable, addNotification]) -} diff --git a/src/keybindings/match.ts b/src/keybindings/match.ts deleted file mode 100644 index ab4b976ef..000000000 --- a/src/keybindings/match.ts +++ /dev/null @@ -1,2 +0,0 @@ -// Re-export from @anthropic/ink keybindings module -export { getKeyName, matchesKeystroke, matchesBinding } from '@anthropic/ink' diff --git a/src/migrations/migrateAutoUpdatesToSettings.ts b/src/migrations/migrateAutoUpdatesToSettings.ts deleted file mode 100644 index c541713b1..000000000 --- a/src/migrations/migrateAutoUpdatesToSettings.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { logEvent } from 'src/services/analytics/index.js' -import { getGlobalConfig, saveGlobalConfig } from '../utils/config.js' -import { logError } from '../utils/log.js' -import { - getSettingsForSource, - updateSettingsForSource, -} from '../utils/settings/settings.js' -/** - * Migration: Move user-set autoUpdates preference to settings.json env var - * Only migrates if user explicitly disabled auto-updates (not for protection) - * This preserves user intent while allowing native installations to auto-update - */ -export function migrateAutoUpdatesToSettings(): void { - const globalConfig = getGlobalConfig() - - // Only migrate if autoUpdates was explicitly set to false by user preference - // (not automatically for native protection) - if ( - globalConfig.autoUpdates !== false || - globalConfig.autoUpdatesProtectedForNative === true - ) { - return - } - - try { - const userSettings = getSettingsForSource('userSettings') || {} - - // Always set DISABLE_AUTOUPDATER to preserve user intent - // We need to overwrite even if it exists, to ensure the migration is complete - updateSettingsForSource('userSettings', { - ...userSettings, - env: { - ...userSettings.env, - DISABLE_AUTOUPDATER: '1', - }, - }) - - logEvent('tengu_migrate_autoupdates_to_settings', { - was_user_preference: true, - already_had_env_var: !!userSettings.env?.DISABLE_AUTOUPDATER, - }) - - // explicitly set, so this takes effect immediately - process.env.DISABLE_AUTOUPDATER = '1' - - // Remove autoUpdates from global config after successful migration - saveGlobalConfig(current => { - const { - autoUpdates: _, - autoUpdatesProtectedForNative: __, - ...updatedConfig - } = current - return updatedConfig - }) - } catch (error) { - logError(new Error(`Failed to migrate auto-updates: ${error}`)) - logEvent('tengu_migrate_autoupdates_error', { - has_error: true, - }) - } -} diff --git a/src/services/mcp/adapter/analytics.ts b/src/services/mcp/adapter/analytics.ts deleted file mode 100644 index c30b7b572..000000000 --- a/src/services/mcp/adapter/analytics.ts +++ /dev/null @@ -1,24 +0,0 @@ -// Host analytics adapter — bridges logEvent to mcp-client's AnalyticsSink interface - -import type { AnalyticsSink } from '@claude-code-best/mcp-client' -import { - type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, - logEvent, -} from '../../analytics/index.js' - -/** - * Creates an AnalyticsSink implementation that delegates to the host's logEvent. - */ -export function createMcpAnalytics(): AnalyticsSink { - return { - trackEvent(event: string, metadata: Record) { - logEvent( - event, - metadata as Record< - string, - AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS - >, - ) - }, - } -} diff --git a/src/services/mcp/adapter/auth.ts b/src/services/mcp/adapter/auth.ts deleted file mode 100644 index e10f86a2e..000000000 --- a/src/services/mcp/adapter/auth.ts +++ /dev/null @@ -1,28 +0,0 @@ -// Host auth provider adapter — bridges OAuth token management to mcp-client's AuthProvider interface - -import type { AuthProvider } from '@claude-code-best/mcp-client' -import { - getClaudeAIOAuthTokens, - checkAndRefreshOAuthTokenIfNeeded, - handleOAuth401Error, -} from '../../../utils/auth.js' - -/** - * Creates an AuthProvider implementation using the host's OAuth token management. - */ -export function createMcpAuth(): AuthProvider { - return { - async getTokens() { - const tokens = getClaudeAIOAuthTokens() - if (!tokens) return null - return { accessToken: tokens.accessToken } - }, - async refreshTokens() { - await checkAndRefreshOAuthTokenIfNeeded() - }, - async handleOAuthError(error: unknown) { - const currentToken = getClaudeAIOAuthTokens()?.accessToken ?? '' - await handleOAuth401Error(currentToken) - }, - } -} diff --git a/src/services/mcp/adapter/featureGate.ts b/src/services/mcp/adapter/featureGate.ts deleted file mode 100644 index 2afddd747..000000000 --- a/src/services/mcp/adapter/featureGate.ts +++ /dev/null @@ -1,15 +0,0 @@ -// Host feature gate adapter — bridges feature() to mcp-client's FeatureGate interface - -import type { FeatureGate } from '@claude-code-best/mcp-client' -import { feature } from 'bun:bundle' - -/** - * Creates a FeatureGate implementation using the host's feature flag system. - */ -export function createMcpFeatureGate(): FeatureGate { - return { - isEnabled(flag: string) { - return feature(flag) - }, - } -} diff --git a/src/services/mcp/adapter/httpConfig.ts b/src/services/mcp/adapter/httpConfig.ts deleted file mode 100644 index cebf3d60f..000000000 --- a/src/services/mcp/adapter/httpConfig.ts +++ /dev/null @@ -1,15 +0,0 @@ -// Host HTTP config adapter — bridges getUserAgent/getSessionId to mcp-client's HttpConfig interface - -import type { HttpConfig } from '@claude-code-best/mcp-client' -import { getMCPUserAgent } from '../../../utils/http.js' -import { getSessionId } from '../../../bootstrap/state.js' - -/** - * Creates an HttpConfig implementation using the host's user agent and session ID. - */ -export function createMcpHttpConfig(): HttpConfig { - return { - getUserAgent: () => getMCPUserAgent(), - getSessionId: () => getSessionId(), - } -} diff --git a/src/services/mcp/adapter/imageProcessor.ts b/src/services/mcp/adapter/imageProcessor.ts deleted file mode 100644 index 616823360..000000000 --- a/src/services/mcp/adapter/imageProcessor.ts +++ /dev/null @@ -1,20 +0,0 @@ -// Host image processor adapter — bridges maybeResizeAndDownsampleImageBuffer to mcp-client's ImageProcessor interface - -import type { ImageProcessor } from '@claude-code-best/mcp-client' -import { maybeResizeAndDownsampleImageBuffer } from '../../../utils/imageResizer.js' - -/** - * Creates an ImageProcessor implementation using the host's image resizing. - */ -export function createMcpImageProcessor(): ImageProcessor { - return { - async resizeAndDownsample(buffer: Buffer) { - const result = await maybeResizeAndDownsampleImageBuffer( - buffer, - buffer.length, - 'png', - ) - return result.buffer - }, - } -} diff --git a/src/services/mcp/adapter/index.ts b/src/services/mcp/adapter/index.ts deleted file mode 100644 index 5fd0f7881..000000000 --- a/src/services/mcp/adapter/index.ts +++ /dev/null @@ -1,32 +0,0 @@ -// Host dependency injection — assembles McpClientDependencies from host infrastructure -// This is the single entry point for creating the dependencies object used by createMcpManager() - -import type { McpClientDependencies } from '@claude-code-best/mcp-client' -import { createMcpLogger } from './logger.js' -import { createMcpHttpConfig } from './httpConfig.js' -import { createMcpProxyConfig } from './proxy.js' -import { createMcpAnalytics } from './analytics.js' -import { createMcpSubprocessEnv } from './subprocessEnv.js' -import { createMcpStorage } from './storage.js' -import { createMcpImageProcessor } from './imageProcessor.js' -import { createMcpAuth } from './auth.js' -/** - * Creates the full set of MCP client dependencies using host infrastructure. - * All adapters are lazy — they only call into host modules when invoked. - * - * Note: featureGate is omitted because Bun's feature() requires string-literal - * arguments at compile time and cannot accept runtime variables. The interface - * field is optional and the mcp-client package does not use it currently. - */ -export function createMcpDependencies(): McpClientDependencies { - return { - logger: createMcpLogger(), - httpConfig: createMcpHttpConfig(), - proxy: createMcpProxyConfig(), - analytics: createMcpAnalytics(), - subprocessEnv: createMcpSubprocessEnv(), - storage: createMcpStorage(), - imageProcessor: createMcpImageProcessor(), - auth: createMcpAuth(), - } -} diff --git a/src/services/mcp/adapter/logger.ts b/src/services/mcp/adapter/logger.ts deleted file mode 100644 index 473fa8421..000000000 --- a/src/services/mcp/adapter/logger.ts +++ /dev/null @@ -1,38 +0,0 @@ -// Host logger adapter — bridges logMCPDebug/logMCPError to mcp-client's Logger interface - -import type { Logger } from '@claude-code-best/mcp-client' -import { logMCPDebug, logMCPError } from '../../../utils/log.js' - -/** - * Creates a Logger implementation that delegates to the host's MCP logging system. - */ -export function createMcpLogger(): Logger { - return { - debug(message: string, ...args: unknown[]) { - // Extract server name from bracketed prefix if present: [serverName] message - const match = message.match(/^\[([^\]]+)\]\s*(.*)/) - if (match) { - logMCPDebug(match[1], match[2]) - } - // Silently ignore messages without server name prefix - }, - info(message: string, ...args: unknown[]) { - const match = message.match(/^\[([^\]]+)\]\s*(.*)/) - if (match) { - logMCPDebug(match[1], match[2]) - } - }, - warn(message: string, ...args: unknown[]) { - const match = message.match(/^\[([^\]]+)\]\s*(.*)/) - if (match) { - logMCPError(match[1], message) - } - }, - error(message: string, ...args: unknown[]) { - const match = message.match(/^\[([^\]]+)\]\s*(.*)/) - if (match) { - logMCPError(match[1], args[0] ?? message) - } - }, - } -} diff --git a/src/services/mcp/adapter/proxy.ts b/src/services/mcp/adapter/proxy.ts deleted file mode 100644 index 5fe74dc24..000000000 --- a/src/services/mcp/adapter/proxy.ts +++ /dev/null @@ -1,30 +0,0 @@ -// Host proxy config adapter — bridges proxy/MTLS to mcp-client's ProxyConfig interface - -import type { ProxyConfig } from '@claude-code-best/mcp-client' -import { - getProxyFetchOptions, - getWebSocketProxyAgent, - getWebSocketProxyUrl, -} from '../../../utils/proxy.js' -import { getWebSocketTLSOptions } from '../../../utils/mtls.js' - -/** - * Creates a ProxyConfig implementation using the host's proxy and TLS settings. - */ -export function createMcpProxyConfig(): ProxyConfig { - return { - getFetchOptions() { - return getProxyFetchOptions() as Record - }, - getWebSocketAgent(url: string) { - return getWebSocketProxyAgent(url) - }, - getWebSocketUrl(url: string) { - return getWebSocketProxyUrl(url) - }, - getTLSOptions() { - const opts = getWebSocketTLSOptions() - return opts as Record | undefined - }, - } -} diff --git a/src/services/mcp/adapter/storage.ts b/src/services/mcp/adapter/storage.ts deleted file mode 100644 index e3455640a..000000000 --- a/src/services/mcp/adapter/storage.ts +++ /dev/null @@ -1,27 +0,0 @@ -// Host content storage adapter — bridges persistBinaryContent to mcp-client's ContentStorage interface - -import type { ContentStorage } from '@claude-code-best/mcp-client' -import { persistBinaryContent } from '../../../utils/mcpOutputStorage.js' -import { - persistToolResult, - isPersistError, -} from '../../../utils/toolResultStorage.js' - -/** - * Creates a ContentStorage implementation using the host's binary persistence. - */ -export function createMcpStorage(): ContentStorage { - return { - async persistBinaryContent(data: Buffer, ext: string) { - const result = await persistBinaryContent( - data, - ext, - `mcp-adapter-${Date.now()}`, - ) - if ('error' in result) { - throw new Error(result.error) - } - return result.filepath - }, - } -} diff --git a/src/services/mcp/adapter/subprocessEnv.ts b/src/services/mcp/adapter/subprocessEnv.ts deleted file mode 100644 index 9e049a672..000000000 --- a/src/services/mcp/adapter/subprocessEnv.ts +++ /dev/null @@ -1,15 +0,0 @@ -// Host subprocess environment adapter - -import type { SubprocessEnvProvider } from '@claude-code-best/mcp-client' -import { subprocessEnv } from '../../../utils/subprocessEnv.js' - -/** - * Creates a SubprocessEnvProvider using the host's subprocess environment logic. - */ -export function createMcpSubprocessEnv(): SubprocessEnvProvider { - return { - getEnv(additional?: Record) { - return { ...subprocessEnv(), ...additional } as Record - }, - } -} diff --git a/src/utils/agentId.ts b/src/utils/agentId.ts index d9f50c4de..7e4b63d09 100644 --- a/src/utils/agentId.ts +++ b/src/utils/agentId.ts @@ -66,34 +66,3 @@ export function generateRequestId( const timestamp = Date.now() return `${requestType}-${timestamp}@${agentId}` } - -/** - * Parses a request ID into its components. - * Returns null if the request ID doesn't match the expected format. - */ -export function parseRequestId( - requestId: string, -): { requestType: string; timestamp: number; agentId: string } | null { - const atIndex = requestId.indexOf('@') - if (atIndex === -1) { - return null - } - - const prefix = requestId.slice(0, atIndex) - const agentId = requestId.slice(atIndex + 1) - - const lastDashIndex = prefix.lastIndexOf('-') - if (lastDashIndex === -1) { - return null - } - - const requestType = prefix.slice(0, lastDashIndex) - const timestampStr = prefix.slice(lastDashIndex + 1) - const timestamp = parseInt(timestampStr, 10) - - if (isNaN(timestamp)) { - return null - } - - return { requestType, timestamp, agentId } -} diff --git a/src/utils/asciicast.ts b/src/utils/asciicast.ts index d3d89dfa9..35dcea737 100644 --- a/src/utils/asciicast.ts +++ b/src/utils/asciicast.ts @@ -48,31 +48,6 @@ export function _resetRecordingStateForTesting(): void { recordingState.timestamp = 0 } -/** - * Find all .cast files for the current session. - * Returns paths sorted by filename (chronological by timestamp suffix). - */ -export function getSessionRecordingPaths(): string[] { - const sessionId = getSessionId() - const projectsDir = join(getClaudeConfigHomeDir(), 'projects') - const projectDir = join(projectsDir, sanitizePath(getOriginalCwd())) - try { - // eslint-disable-next-line custom-rules/no-sync-fs -- called during /share before upload, not in hot path - const entries = getFsImplementation().readdirSync(projectDir) - const names = ( - typeof entries[0] === 'string' - ? entries - : (entries as { name: string }[]).map(e => e.name) - ) as string[] - const files = names - .filter(f => f.startsWith(sessionId) && f.endsWith('.cast')) - .sort() - return files.map(f => join(projectDir, f)) - } catch { - return [] - } -} - /** * Rename the recording file to match the current session ID. * Called after --resume/--continue changes the session ID via switchSession(). @@ -124,14 +99,6 @@ function getTerminalSize(): { cols: number; rows: number } { return { cols, rows } } -/** - * Flush pending recording data to disk. - * Call before reading the .cast file (e.g., during /share). - */ -export async function flushAsciicastRecorder(): Promise { - await recorder?.flush() -} - /** * Install the asciicast recorder. * Wraps process.stdout.write to capture all terminal output with timestamps. diff --git a/src/utils/computerUse/win32/ocr.ts b/src/utils/computerUse/win32/ocr.ts deleted file mode 100644 index 02536d919..000000000 --- a/src/utils/computerUse/win32/ocr.ts +++ /dev/null @@ -1,250 +0,0 @@ -/** - * OCR module using Windows.Media.Ocr.OcrEngine via PowerShell. - * Captures a screen region or window, then runs WinRT OCR to extract text. - */ - -import { ps as runPs } from './shared.js' - -export interface OcrLine { - text: string - bounds: { x: number; y: number; w: number; h: number } -} - -export interface OcrResult { - text: string - lines: OcrLine[] - language: string -} - -function emptyResult(language: string): OcrResult { - return { text: '', lines: [], language } -} - -/** - * PowerShell script that: - * 1. Screenshots a screen region using CopyFromScreen - * 2. Saves to temp PNG - * 3. Loads via WinRT BitmapDecoder -> SoftwareBitmap - * 4. Runs OcrEngine.RecognizeAsync - * 5. Outputs JSON with text, lines, and bounding rects - */ -function buildOcrRegionScript( - x: number, - y: number, - w: number, - h: number, - lang: string, -): string { - return ` -Add-Type -AssemblyName System.Drawing -Add-Type -AssemblyName System.Runtime.WindowsRuntime - -# Load WinRT types -$null = [Windows.Media.Ocr.OcrEngine, Windows.Foundation, ContentType = WindowsRuntime] -$null = [Windows.Graphics.Imaging.SoftwareBitmap, Windows.Foundation, ContentType = WindowsRuntime] -$null = [Windows.Graphics.Imaging.BitmapDecoder, Windows.Foundation, ContentType = WindowsRuntime] -$null = [Windows.Storage.StorageFile, Windows.Foundation, ContentType = WindowsRuntime] -$null = [Windows.Storage.Streams.RandomAccessStream, Windows.Foundation, ContentType = WindowsRuntime] -$null = [Windows.Globalization.Language, Windows.Foundation, ContentType = WindowsRuntime] - -# Await helper for WinRT async operations -$asTaskGeneric = ([System.WindowsRuntimeSystemExtensions].GetMethods() | Where-Object { - $_.Name -eq 'AsTask' -and $_.GetParameters().Count -eq 1 -and - $_.GetParameters()[0].ParameterType.Name -eq 'IAsyncOperation\`1' -})[0] -Function Await($WinRtTask, $ResultType) { - $asTask = $asTaskGeneric.MakeGenericMethod($ResultType) - $netTask = $asTask.Invoke($null, @($WinRtTask)) - $netTask.Wait(-1) | Out-Null - $netTask.Result -} - -try { - # Step 1: Screenshot region - $bmp = New-Object System.Drawing.Bitmap(${w}, ${h}) - $g = [System.Drawing.Graphics]::FromImage($bmp) - $g.CopyFromScreen(${x}, ${y}, 0, 0, (New-Object System.Drawing.Size(${w}, ${h}))) - $g.Dispose() - - # Step 2: Save to temp file - $tmpFile = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), "ocrtemp_$([guid]::NewGuid().ToString('N')).png") - $bmp.Save($tmpFile, [System.Drawing.Imaging.ImageFormat]::Png) - $bmp.Dispose() - - # Step 3: Open as StorageFile -> BitmapDecoder -> SoftwareBitmap - $storageFile = Await ([Windows.Storage.StorageFile]::GetFileFromPathAsync($tmpFile)) ([Windows.Storage.StorageFile]) - $stream = Await ($storageFile.OpenAsync([Windows.Storage.FileAccessMode]::Read)) ([Windows.Storage.Streams.IRandomAccessStream]) - $decoder = Await ([Windows.Graphics.Imaging.BitmapDecoder]::CreateAsync($stream)) ([Windows.Graphics.Imaging.BitmapDecoder]) - $softwareBmp = Await ($decoder.GetSoftwareBitmapAsync()) ([Windows.Graphics.Imaging.SoftwareBitmap]) - - # Step 4: Create OCR engine - $ocrLang = New-Object Windows.Globalization.Language('${lang}') - $engine = [Windows.Media.Ocr.OcrEngine]::TryCreateFromLanguage($ocrLang) - if ($engine -eq $null) { - # Fallback to en-US - $ocrLang = New-Object Windows.Globalization.Language('en-US') - $engine = [Windows.Media.Ocr.OcrEngine]::TryCreateFromLanguage($ocrLang) - } - if ($engine -eq $null) { - Write-Output '{"text":"","lines":[],"language":"${lang}"}' - return - } - - # Step 5: Run OCR - $ocrResult = Await ($engine.RecognizeAsync($softwareBmp)) ([Windows.Media.Ocr.OcrResult]) - - # Step 6: Extract lines with bounding rects - $lines = @() - foreach ($line in $ocrResult.Lines) { - $minX = [double]::MaxValue; $minY = [double]::MaxValue - $maxX = 0.0; $maxY = 0.0 - foreach ($word in $line.Words) { - $r = $word.BoundingRect - if ($r.X -lt $minX) { $minX = $r.X } - if ($r.Y -lt $minY) { $minY = $r.Y } - if (($r.X + $r.Width) -gt $maxX) { $maxX = $r.X + $r.Width } - if (($r.Y + $r.Height) -gt $maxY) { $maxY = $r.Y + $r.Height } - } - $lines += @{ - text = $line.Text - bounds = @{ - x = [int]$minX - y = [int]$minY - w = [int]($maxX - $minX) - h = [int]($maxY - $minY) - } - } - } - - $output = @{ - text = $ocrResult.Text - lines = $lines - language = $ocrLang.LanguageTag - } - Write-Output (ConvertTo-Json $output -Depth 4 -Compress) - - # Cleanup - $stream.Dispose() - Remove-Item $tmpFile -ErrorAction SilentlyContinue -} catch { - Write-Output '{"text":"","lines":[],"language":"${lang}"}' -} -` -} - -/** - * PowerShell script to get a window's bounding rect by title. - */ -function buildGetWindowRectScript(windowTitle: string): string { - const escaped = windowTitle.replace(/'/g, "''") - return ` -Add-Type @' -using System; -using System.Runtime.InteropServices; -public class WinRect { - [DllImport("user32.dll", CharSet=CharSet.Unicode)] - public static extern IntPtr FindWindow(string c, string t); - [DllImport("user32.dll")] - public static extern bool GetWindowRect(IntPtr h, out RECT r); - [StructLayout(LayoutKind.Sequential)] - public struct RECT { public int L, T, R, B; } - public static string Get(string title) { - IntPtr hwnd = FindWindow(null, title); - if (hwnd == IntPtr.Zero) return "NOT_FOUND"; - RECT r; GetWindowRect(hwnd, out r); - int w = r.R - r.L; int h = r.B - r.T; - if (w <= 0 || h <= 0) return "INVALID_SIZE"; - return r.L + "," + r.T + "," + w + "," + h; - } -} -'@ -[WinRect]::Get('${escaped}') -` -} - -function parseOcrOutput(raw: string, lang: string): OcrResult { - if (!raw) return emptyResult(lang) - try { - const parsed = JSON.parse(raw) - return { - text: parsed.text ?? '', - lines: Array.isArray(parsed.lines) - ? parsed.lines.map((l: any) => ({ - text: l.text ?? '', - bounds: { - x: l.bounds?.x ?? 0, - y: l.bounds?.y ?? 0, - w: l.bounds?.w ?? 0, - h: l.bounds?.h ?? 0, - }, - })) - : [], - language: parsed.language ?? lang, - } - } catch { - return emptyResult(lang) - } -} - -/** - * Perform OCR on a screen region. - * Screenshots the specified rectangle, then runs WinRT OcrEngine. - * - * @param x - Left coordinate - * @param y - Top coordinate - * @param w - Width in pixels - * @param h - Height in pixels - * @param lang - BCP-47 language tag (default 'en-US'). Confirmed: 'en-US', 'zh-Hans-CN' - */ -export async function ocrRegion( - x: number, - y: number, - w: number, - h: number, - lang?: string, -): Promise { - const language = lang ?? 'en-US' - if (w <= 0 || h <= 0) return emptyResult(language) - - try { - const script = buildOcrRegionScript(x, y, w, h, language) - const raw = runPs(script) - return parseOcrOutput(raw, language) - } catch { - return emptyResult(language) - } -} - -/** - * Perform OCR on a specific window by its title. - * Gets the window rect, then delegates to ocrRegion. - * - * @param windowTitle - Exact window title to find via FindWindow - * @param lang - BCP-47 language tag (default 'en-US') - */ -export async function ocrWindow( - windowTitle: string, - lang?: string, -): Promise { - const language = lang ?? 'en-US' - - try { - const rectScript = buildGetWindowRectScript(windowTitle) - const raw = runPs(rectScript) - const trimmed = raw.trim() - - if (!trimmed || trimmed === 'NOT_FOUND' || trimmed === 'INVALID_SIZE') { - return emptyResult(language) - } - - const parts = trimmed.split(',') - if (parts.length !== 4) return emptyResult(language) - - const [x, y, w, h] = parts.map(Number) - if (!w || !h) return emptyResult(language) - - return ocrRegion(x, y, w, h, lang) - } catch { - return emptyResult(language) - } -} diff --git a/src/utils/dxt/helpers.ts b/src/utils/dxt/helpers.ts index 85414ed3c..cdc89e0ac 100644 --- a/src/utils/dxt/helpers.ts +++ b/src/utils/dxt/helpers.ts @@ -20,7 +20,8 @@ export async function validateManifest( const errors = parseResult.error.flatten() const errorMessages = [ ...Object.entries(errors.fieldErrors).map( - ([field, errs]) => `${field}: ${(errs as any)?.join(', ')}`, + ([field, errs]) => + `${field}: ${(errs as string[] | undefined)?.join(', ')}`, ), ...(errors.formErrors || []), ] diff --git a/src/utils/mcpInstructionsDelta.ts b/src/utils/mcpInstructionsDelta.ts index e3c83cf3d..54d209b11 100644 --- a/src/utils/mcpInstructionsDelta.ts +++ b/src/utils/mcpInstructionsDelta.ts @@ -65,8 +65,9 @@ export function getMcpInstructionsDelta( attachmentCount++ if (msg.attachment!.type !== 'mcp_instructions_delta') continue midCount++ - for (const n of (msg.attachment! as any).addedNames) announced.add(n) - for (const n of (msg.attachment! as any).removedNames) announced.delete(n) + const delta = msg.attachment! as unknown as McpInstructionsDelta + for (const n of delta.addedNames) announced.add(n) + for (const n of delta.removedNames) announced.delete(n) } const connected = mcpClients.filter( diff --git a/src/utils/sessionTitle.ts b/src/utils/sessionTitle.ts index 52fbe7093..6e156b9eb 100644 --- a/src/utils/sessionTitle.ts +++ b/src/utils/sessionTitle.ts @@ -37,8 +37,8 @@ export function extractConversationText(messages: Message[]): string { if ('isMeta' in msg && msg.isMeta) continue if ( 'origin' in msg && - (msg as any).origin && - (msg as any).origin.kind !== 'human' + (msg as unknown as { origin?: { kind?: string } }).origin && + (msg as unknown as { origin: { kind?: string } }).origin.kind !== 'human' ) continue const content = msg.message!.content @@ -116,7 +116,9 @@ export async function generateSessionTitle( }, }) - const text = extractTextContent(result.message.content as any) + const text = extractTextContent( + result.message.content as readonly { readonly type: string }[], + ) const parsed = titleSchema().safeParse(safeParseJSON(text)) const title = parsed.success ? parsed.data.title.trim() || null : null diff --git a/src/utils/systemThemeWatcher.ts b/src/utils/systemThemeWatcher.ts deleted file mode 100644 index d010aa4da..000000000 --- a/src/utils/systemThemeWatcher.ts +++ /dev/null @@ -1,8 +0,0 @@ -// Auto-generated stub — replace with real implementation -export {} -export const watchSystemTheme: ( - querier: unknown, - setTheme: React.Dispatch< - React.SetStateAction - >, -) => () => void = () => () => {} diff --git a/src/utils/warningHandler.ts b/src/utils/warningHandler.ts index 3c9572e6c..d91322208 100644 --- a/src/utils/warningHandler.ts +++ b/src/utils/warningHandler.ts @@ -48,15 +48,6 @@ function isInternalWarning(warning: Error): boolean { // Store reference to our warning handler so we can detect if it's already installed let warningHandler: ((warning: Error) => void) | null = null -// For testing only - allows resetting the warning handler state -export function resetWarningHandler(): void { - if (warningHandler) { - process.removeListener('warning', warningHandler) - } - warningHandler = null - warningCounts.clear() -} - export function initializeWarningHandler(): void { // Only set up handler once - check if our handler is already installed const currentListeners = process.listeners('warning')