@@ -87,6 +87,8 @@ interface UpdateCacheEntry {
8787const CACHE_VERSION = 1
8888
8989export interface UpdateCheckOptions {
90+ /** Current working directory. Injected so project-local installation detection is testable. */
91+ cwd ?: string
9092 currentVersion ?: string
9193 env ?: NodeJS . ProcessEnv
9294 /** Whether stderr is a terminal. Injected so the suppression rule is testable. */
@@ -117,10 +119,26 @@ function isEnabled(value: string | undefined): boolean {
117119 return normalized !== '' && normalized !== '0' && normalized !== 'false'
118120}
119121
120- /** Skips npx, which resolves latest, and checkouts, whose manifest trails npm. */
121- function isUnadvisableInstall ( modulePath : string ) : boolean {
122+ /** Whether the package is installed in a node_modules tree above the working directory. */
123+ function isProjectLocalInstall ( modulePath : string , cwd : string ) : boolean {
124+ const normalizedModulePath = normalizeModulePath ( modulePath )
125+ const nodeModulesIndex = normalizedModulePath . indexOf ( '/node_modules/' )
126+ if ( nodeModulesIndex < 0 ) return false
127+
128+ const installRoot = normalizedModulePath . slice ( 0 , nodeModulesIndex )
129+ const workingDirectory = normalizeModulePath ( cwd ) . replace ( / \/ + $ / , '' )
130+ return workingDirectory === installRoot || workingDirectory . startsWith ( `${ installRoot } /` )
131+ }
132+
133+ /** Skips ephemeral, project-local, and checkout installs that global advice cannot update. */
134+ function isUnadvisableInstall ( modulePath : string , env : NodeJS . ProcessEnv , cwd : string ) : boolean {
122135 const normalized = normalizeModulePath ( modulePath )
123- return normalized . includes ( '/_npx/' ) || normalized . includes ( '/packages/sim-cli/' )
136+ return (
137+ env . npm_command === 'exec' ||
138+ normalized . includes ( '/_npx/' ) ||
139+ normalized . includes ( '/packages/sim-cli/' ) ||
140+ isProjectLocalInstall ( modulePath , cwd )
141+ )
124142}
125143
126144/** Normalizes separators and case before installation-path comparisons. */
@@ -185,11 +203,12 @@ try {
185203}
186204`
187205
188- /** Preserves proxy/TLS settings without copying the registry credential into the probe. */
206+ /** Preserves proxy/TLS settings without copying CLI credentials into the probe. */
189207function registryProcessEnv ( ) : NodeJS . ProcessEnv {
190208 const env = { ...process . env }
191209 for ( const key of Object . keys ( env ) ) {
192- if ( key . toLowerCase ( ) === 'npm_config_registry' ) delete env [ key ]
210+ const normalized = key . toLowerCase ( )
211+ if ( normalized === 'npm_config_registry' || normalized === 'sim_api_key' ) delete env [ key ]
193212 }
194213 return env
195214}
@@ -421,12 +440,13 @@ export async function announceUpdateIfAvailable(options: UpdateCheckOptions = {}
421440 const env = options . env ?? process . env
422441 const isTty = options . isTty ?? process . stderr . isTTY === true
423442 const modulePath = options . modulePath ?? fileURLToPath ( import . meta. url )
443+ const cwd = options . cwd ?? process . cwd ( )
424444 const now = options . now ?? new Date ( )
425445
426446 if ( isEnabled ( env . SIM_NO_UPDATE_CHECK ) ) return
427447 if ( ! isTty ) return
428448 if ( CI_VARIABLES . some ( ( variable ) => isEnabled ( env [ variable ] ) ) ) return
429- if ( isUnadvisableInstall ( modulePath ) ) return
449+ if ( isUnadvisableInstall ( modulePath , env , cwd ) ) return
430450
431451 const currentVersion = options . currentVersion ?? CLI_VERSION
432452 const current = parseStableVersion ( currentVersion )
0 commit comments