From 8a2f3acd4a4269f71bbfd3705a2230ad54c6c301 Mon Sep 17 00:00:00 2001 From: StillKnotKnown <192589389+StillKnotKnown@users.noreply.github.com> Date: Mon, 19 Jan 2026 15:59:25 +0200 Subject: [PATCH] fix: Windows CLI detection and version selection UX improvements (#1341) * fix: Windows CLI detection and version selection UX improvements - Add fallback to default npm global path (%APPDATA%\npm) when npm.cmd is not in PATH (happens when packaged app launches from GUI) - Apply fallback to both sync and async versions of getNpmGlobalPrefix() - Update version selection warning dialogs with clear terminal messaging - Change button text to "Open Terminal & Switch/Update" for clarity - Add i18n translations for terminal note (en/fr) Fixes Claude Code CLI not being detected in packaged Windows builds. Improves UX by clearly indicating terminal will open for version changes. * fix: use APPDATA env var for Windows npm path fallback Use process.env.APPDATA instead of hardcoded 'AppData\Roaming' path for better robustness on Windows systems with custom or localized AppData locations. Provides fallback to hardcoded path for minimal environments. Addresses feedback from PR review. * refactor: use established APPDATA pattern from platform/paths.ts Update Windows npm fallback to use the concise pattern `process.env.APPDATA || path.join(os.homedir(), 'AppData', 'Roaming')` which matches the established pattern in platform/paths.ts (lines 224, 277). This improves codebase consistency and is more concise than the previous ternary expression. Addresses MEDIUM findings from Auto Claude PR Review. * refactor: use platform module helpers and extract npm path constant - Use getNpmCommand() from platform module instead of inline ternary - Extract Windows npm fallback path as WINDOWS_NPM_FALLBACK_PATH constant This eliminates code duplication and improves consistency with the codebase's platform abstraction layer. Addresses LOW findings from Auto Claude PR Review: - [e3eaee8f94e5] Duplicated Windows fallback path constant - [7ae39c782e02] Could use getNpmCommand() from platform module --------- Co-authored-by: StillKnotKnown --- apps/frontend/src/main/env-utils.ts | 45 ++++++++++++++++-- .../components/ClaudeCodeStatusBadge.tsx | 16 ++++++- .../shared/i18n/locales/en/navigation.json | 6 ++- .../shared/i18n/locales/fr/navigation.json | 6 ++- package-lock.json | 46 +++++++++++++------ 5 files changed, 94 insertions(+), 25 deletions(-) diff --git a/apps/frontend/src/main/env-utils.ts b/apps/frontend/src/main/env-utils.ts index 3cb143dc..8fbee2c0 100644 --- a/apps/frontend/src/main/env-utils.ts +++ b/apps/frontend/src/main/env-utils.ts @@ -16,10 +16,25 @@ import { promises as fsPromises } from 'fs'; import { execFileSync, execFile } from 'child_process'; import { promisify } from 'util'; import { getSentryEnvForSubprocess } from './sentry'; -import { isWindows, isUnix, getPathDelimiter } from './platform'; +import { isWindows, isUnix, getPathDelimiter, getNpmCommand } from './platform'; const execFileAsync = promisify(execFile); +/** + * Windows npm global fallback path + * + * On Windows, npm global packages are installed in %APPDATA%\npm by default. + * This constant provides the fallback path construction for when the npm + * command itself is not in PATH (e.g., packaged Electron apps launched from GUI). + * + * Uses process.env.APPDATA for enterprise environments with redirected profiles, + * falling back to the default home directory location. + */ +const WINDOWS_NPM_FALLBACK_PATH = (): string => { + const appDataPath = process.env.APPDATA || path.join(os.homedir(), 'AppData', 'Roaming'); + return path.join(appDataPath, 'npm'); +}; + /** * Check if a path exists asynchronously (non-blocking) * @@ -54,8 +69,8 @@ let npmGlobalPrefixCachePromise: Promise | null = null; */ function getNpmGlobalPrefix(): string | null { try { - // On Windows, use npm.cmd for proper command resolution - const npmCommand = isWindows() ? 'npm.cmd' : 'npm'; + // Use platform module helper for npm command name + const npmCommand = getNpmCommand(); // Use --location=global to bypass workspace context and avoid ENOWORKSPACES error const rawPrefix = execFileSync(npmCommand, ['config', 'get', 'prefix', '--location=global'], { @@ -80,7 +95,16 @@ function getNpmGlobalPrefix(): string | null { const normalizedPath = path.normalize(binPath); return fs.existsSync(normalizedPath) ? normalizedPath : null; - } catch { + } catch (error) { + // Fallback for Windows: try default npm global location when npm.cmd is not in PATH + // This happens when the packaged app launches from GUI without full shell environment + if (isWindows()) { + const defaultNpmPath = WINDOWS_NPM_FALLBACK_PATH(); + if (fs.existsSync(defaultNpmPath)) { + console.warn('[env-utils] npm command not found, using default npm path:', defaultNpmPath); + return defaultNpmPath; + } + } return null; } } @@ -321,7 +345,8 @@ async function getNpmGlobalPrefixAsync(): Promise { // Start the async fetch npmGlobalPrefixCachePromise = (async () => { try { - const npmCommand = isWindows() ? 'npm.cmd' : 'npm'; + // Use platform module helper for npm command name + const npmCommand = getNpmCommand(); const { stdout } = await execFileAsync(npmCommand, ['config', 'get', 'prefix', '--location=global'], { encoding: 'utf-8', @@ -345,6 +370,16 @@ async function getNpmGlobalPrefixAsync(): Promise { npmGlobalPrefixCache = await existsAsync(normalizedPath) ? normalizedPath : null; return npmGlobalPrefixCache; } catch (error) { + // Fallback for Windows: try default npm global location when npm.cmd is not in PATH + // This happens when the packaged app launches from GUI without full shell environment + if (isWindows()) { + const defaultNpmPath = WINDOWS_NPM_FALLBACK_PATH(); + if (await existsAsync(defaultNpmPath)) { + console.warn('[env-utils] npm command not found, using default npm path:', defaultNpmPath); + npmGlobalPrefixCache = defaultNpmPath; + return defaultNpmPath; + } + } console.warn(`[env-utils] Failed to get npm global prefix: ${error}`); npmGlobalPrefixCache = null; return null; diff --git a/apps/frontend/src/renderer/components/ClaudeCodeStatusBadge.tsx b/apps/frontend/src/renderer/components/ClaudeCodeStatusBadge.tsx index a46010d6..8fd34ee3 100644 --- a/apps/frontend/src/renderer/components/ClaudeCodeStatusBadge.tsx +++ b/apps/frontend/src/renderer/components/ClaudeCodeStatusBadge.tsx @@ -642,12 +642,18 @@ export function ClaudeCodeStatusBadge({ className }: ClaudeCodeStatusBadgeProps) "navigation:claudeCode.updateWarningDescription", "Updating will close all running Claude Code sessions. Any unsaved work in those sessions may be lost. Make sure to save your work before proceeding." )} + + {t( + "navigation:claudeCode.updateWarningTerminalNote", + "A terminal window will open to run the installation command. Please wait for the installation to complete before continuing." + )} + {t("common:cancel", "Cancel")} - {t("navigation:claudeCode.updateAnyway", "Update Anyway")} + {t("navigation:claudeCode.updateAnyway", "Open Terminal & Update")} @@ -667,6 +673,12 @@ export function ClaudeCodeStatusBadge({ className }: ClaudeCodeStatusBadgeProps) "navigation:claudeCode.rollbackWarningDescription", "Switching versions will close all running Claude Code sessions. Any unsaved work in those sessions may be lost. Make sure to save your work before proceeding." )} + + {t( + "navigation:claudeCode.rollbackWarningTerminalNote", + "A terminal window will open to run the installation command. Please wait for the installation to complete before continuing." + )} + @@ -674,7 +686,7 @@ export function ClaudeCodeStatusBadge({ className }: ClaudeCodeStatusBadgeProps) {t("common:cancel", "Cancel")} - {t("navigation:claudeCode.switchAnyway", "Switch Anyway")} + {t("navigation:claudeCode.switchAnyway", "Open Terminal & Switch")} diff --git a/apps/frontend/src/shared/i18n/locales/en/navigation.json b/apps/frontend/src/shared/i18n/locales/en/navigation.json index c5409543..204f9a9b 100644 --- a/apps/frontend/src/shared/i18n/locales/en/navigation.json +++ b/apps/frontend/src/shared/i18n/locales/en/navigation.json @@ -58,7 +58,8 @@ "viewChangelogAriaLabel": "View Claude Code Changelog (opens in new window)", "updateWarningTitle": "Update Claude Code?", "updateWarningDescription": "Updating will close all running Claude Code sessions. Any unsaved work in those sessions may be lost. Make sure to save your work before proceeding.", - "updateAnyway": "Update Anyway", + "updateWarningTerminalNote": "A terminal window will open to run the installation command. Please wait for the installation to complete before continuing.", + "updateAnyway": "Open Terminal & Update", "switchVersion": "Switch Version", "selectVersion": "Select version", "loadingVersions": "Loading versions...", @@ -66,7 +67,8 @@ "installingVersion": "Installing version {{version}}...", "rollbackWarningTitle": "Switch to version {{version}}?", "rollbackWarningDescription": "Switching versions will close all running Claude Code sessions. Any unsaved work in those sessions may be lost. Make sure to save your work before proceeding.", - "switchAnyway": "Switch Anyway", + "rollbackWarningTerminalNote": "A terminal window will open to run the installation command. Please wait for the installation to complete before continuing.", + "switchAnyway": "Open Terminal & Switch", "currentVersion": "Current", "switchInstallation": "Switch Installation", "selectInstallation": "Select installation", diff --git a/apps/frontend/src/shared/i18n/locales/fr/navigation.json b/apps/frontend/src/shared/i18n/locales/fr/navigation.json index 19646a2f..c40ee36c 100644 --- a/apps/frontend/src/shared/i18n/locales/fr/navigation.json +++ b/apps/frontend/src/shared/i18n/locales/fr/navigation.json @@ -58,7 +58,8 @@ "viewChangelogAriaLabel": "Voir le journal des modifications Claude Code (s'ouvre dans une nouvelle fenêtre)", "updateWarningTitle": "Mettre à jour Claude Code ?", "updateWarningDescription": "La mise à jour fermera toutes les sessions Claude Code en cours. Tout travail non sauvegardé dans ces sessions pourrait être perdu. Assurez-vous de sauvegarder votre travail avant de continuer.", - "updateAnyway": "Mettre à jour quand même", + "updateWarningTerminalNote": "Une fenêtre de terminal s'ouvrira pour exécuter la commande d'installation. Veuillez attendre la fin de l'installation avant de continuer.", + "updateAnyway": "Ouvrir le terminal et mettre à jour", "switchVersion": "Changer de version", "selectVersion": "Sélectionner une version", "loadingVersions": "Chargement des versions...", @@ -66,7 +67,8 @@ "installingVersion": "Installation de la version {{version}}...", "rollbackWarningTitle": "Passer à la version {{version}} ?", "rollbackWarningDescription": "Le changement de version fermera toutes les sessions Claude Code en cours. Tout travail non sauvegardé dans ces sessions pourrait être perdu. Assurez-vous de sauvegarder votre travail avant de continuer.", - "switchAnyway": "Changer quand même", + "rollbackWarningTerminalNote": "Une fenêtre de terminal s'ouvrira pour exécuter la commande d'installation. Veuillez attendre la fin de l'installation avant de continuer.", + "switchAnyway": "Ouvrir le terminal et changer", "currentVersion": "Actuelle", "switchInstallation": "Changer d'installation", "selectInstallation": "Sélectionner une installation", diff --git a/package-lock.json b/package-lock.json index 63585b13..9e9495ea 100644 --- a/package-lock.json +++ b/package-lock.json @@ -244,6 +244,7 @@ "integrity": "sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/generator": "^7.28.6", @@ -808,6 +809,7 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=18" }, @@ -851,6 +853,7 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=18" } @@ -890,6 +893,7 @@ "resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.3.1.tgz", "integrity": "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==", "license": "MIT", + "peer": true, "dependencies": { "@dnd-kit/accessibility": "^3.1.1", "@dnd-kit/utilities": "^3.2.2", @@ -1308,7 +1312,6 @@ "dev": true, "license": "BSD-2-Clause", "optional": true, - "peer": true, "dependencies": { "cross-dirname": "^0.1.0", "debug": "^4.3.4", @@ -1330,7 +1333,6 @@ "dev": true, "license": "MIT", "optional": true, - "peer": true, "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -1347,7 +1349,6 @@ "dev": true, "license": "MIT", "optional": true, - "peer": true, "dependencies": { "universalify": "^2.0.0" }, @@ -1362,7 +1363,6 @@ "dev": true, "license": "MIT", "optional": true, - "peer": true, "engines": { "node": ">= 10.0.0" } @@ -2242,6 +2242,7 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", "license": "Apache-2.0", + "peer": true, "engines": { "node": ">=8.0.0" } @@ -2263,6 +2264,7 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-2.4.0.tgz", "integrity": "sha512-jn0phJ+hU7ZuvaoZE/8/Euw3gvHJrn2yi+kXrymwObEPVPjtwCmkvXDRQCWli+fCTTF/aSOtXaLr7CLIvv3LQg==", "license": "Apache-2.0", + "peer": true, "engines": { "node": "^18.19.0 || >=20.6.0" }, @@ -2275,6 +2277,7 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.4.0.tgz", "integrity": "sha512-KtcyFHssTn5ZgDu6SXmUznS80OFs/wN7y6MyFRRcKU6TOw8hNcGxKvt8hsdaLJfhzUszNSjURetq5Qpkad14Gw==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, @@ -2290,6 +2293,7 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.208.0.tgz", "integrity": "sha512-Eju0L4qWcQS+oXxi6pgh7zvE2byogAkcsVv0OjHF/97iOz1N/aKE6etSGowYkie+YA1uo6DNwdSxaaNnLvcRlA==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@opentelemetry/api-logs": "0.208.0", "import-in-the-middle": "^2.0.0", @@ -2692,6 +2696,7 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.4.0.tgz", "integrity": "sha512-RWvGLj2lMDZd7M/5tjkI/2VHMpXebLgPKvBUd9LRasEWR2xAynDwEYZuLvY9P2NGG73HF07jbbgWX2C9oavcQg==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@opentelemetry/core": "2.4.0", "@opentelemetry/semantic-conventions": "^1.29.0" @@ -2708,6 +2713,7 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.4.0.tgz", "integrity": "sha512-WH0xXkz/OHORDLKqaxcUZS0X+t1s7gGlumr2ebiEgNZQl2b0upK2cdoD0tatf7l8iP74woGJ/Kmxe82jdvcWRw==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@opentelemetry/core": "2.4.0", "@opentelemetry/resources": "2.4.0", @@ -2725,6 +2731,7 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.39.0.tgz", "integrity": "sha512-R5R9tb2AXs2IRLNKLBJDynhkfmx7mX0vi8NkhZb3gUkPWHn6HXk5J8iQ/dql0U3ApfWym4kXXmBDRGO+oeOfjg==", "license": "Apache-2.0", + "peer": true, "engines": { "node": ">=14" } @@ -4919,6 +4926,7 @@ "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", @@ -5217,6 +5225,7 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.8.tgz", "integrity": "sha512-3MbSL37jEchWZz2p2mjntRZtPt837ij10ApxKfgmXCTuHWagYg7iA5bqPw6C8BMPfwidlvfPI/fxOc42HLhcyg==", "license": "MIT", + "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -5227,6 +5236,7 @@ "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", "devOptional": true, "license": "MIT", + "peer": true, "peerDependencies": { "@types/react": "^19.2.0" } @@ -5492,6 +5502,7 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -5524,6 +5535,7 @@ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -5965,6 +5977,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -6663,8 +6676,7 @@ "integrity": "sha512-+R08/oI0nl3vfPcqftZRpytksBXDzOUveBq/NBVx0sUp1axwzPQrKinNx5yd5sxPu8j1wIy8AfnVQ+5eFdha6Q==", "dev": true, "license": "MIT", - "optional": true, - "peer": true + "optional": true }, "node_modules/cross-env": { "version": "10.1.0", @@ -6981,6 +6993,7 @@ "integrity": "sha512-ce4Ogns4VMeisIuCSK0C62umG0lFy012jd8LMZ6w/veHUeX4fqfDrGe+HTWALAEwK6JwKP+dhPvizhArSOsFbg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "app-builder-lib": "26.4.0", "builder-util": "26.3.4", @@ -7137,6 +7150,7 @@ "dev": true, "hasInstallScript": true, "license": "MIT", + "peer": true, "dependencies": { "@electron/get": "^2.0.0", "@types/node": "^22.7.7", @@ -7385,7 +7399,6 @@ "dev": true, "hasInstallScript": true, "license": "MIT", - "peer": true, "dependencies": { "@electron/asar": "^3.2.1", "debug": "^4.1.1", @@ -7406,7 +7419,6 @@ "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "graceful-fs": "^4.1.2", "jsonfile": "^4.0.0", @@ -8416,6 +8428,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "@babel/runtime": "^7.28.4" }, @@ -8742,6 +8755,7 @@ "integrity": "sha512-mjzqwWRD9Y1J1KUi7W97Gja1bwOOM5Ug0EZ6UDK3xS7j7mndrkwozHtSblfomlzyB4NepioNt+B2sOSzczVgtQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@acemir/cssom": "^0.9.28", "@asamuzakjp/dom-selector": "^6.7.6", @@ -11257,6 +11271,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -11332,7 +11347,6 @@ "dev": true, "license": "MIT", "optional": true, - "peer": true, "dependencies": { "commander": "^9.4.0" }, @@ -11350,7 +11364,6 @@ "dev": true, "license": "MIT", "optional": true, - "peer": true, "engines": { "node": "^12.20.0 || >=14" } @@ -11477,6 +11490,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz", "integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -11486,6 +11500,7 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz", "integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==", "license": "MIT", + "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -11888,7 +11903,6 @@ "deprecated": "Rimraf versions prior to v4 are no longer supported", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "glob": "^7.1.3" }, @@ -12467,7 +12481,8 @@ "version": "4.1.18", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.18.tgz", "integrity": "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/tapable": { "version": "2.3.0", @@ -12570,7 +12585,6 @@ "integrity": "sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "mkdirp": "^0.5.1", "rimraf": "~2.6.2" @@ -12634,7 +12648,6 @@ "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "minimist": "^1.2.6" }, @@ -12726,6 +12739,7 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -12884,6 +12898,7 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "devOptional": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -13190,6 +13205,7 @@ "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", @@ -13782,6 +13798,7 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -14223,6 +14240,7 @@ "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.5.tgz", "integrity": "sha512-k7Nwx6vuWx1IJ9Bjuf4Zt1PEllcwe7cls3VNzm4CQ1/hgtFUK2bRNG3rvnpPUhFjmqJKAKtjV576KnUkHocg/g==", "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" }