Skip to content

Commit eb205f9

Browse files
committed
feat(profile): 实现延迟加载优化以提升启动性能
将 psutils 模块从全量同步加载改为分层延迟加载,启动时仅 dot-source 6 个核心子模块,其余子模块通过 OnIdle 事件异步加载。同时将 fnm 初始化改为缓存模式,并替换环境变量检测的 API 为高效 .NET 原生调用。通过这些优化将 Full 模式加载时间从 ~1.77s 降至 ~1.1s 以下。
1 parent de9887b commit eb205f9

6 files changed

Lines changed: 319 additions & 0 deletions

File tree

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
schema: spec-driven
2+
created: 2026-02-11
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
## Context
2+
3+
上一轮优化(`profile-loading-optimization`)已将加载时间从约 2s 降至 ~1.77s。当前各阶段耗时:
4+
5+
| 阶段 | 耗时 | 占比 |
6+
|------|------|------|
7+
| dot-source-definitions | 178ms | 10% |
8+
| mode-decision | 88ms | 5% |
9+
| core-loaders | 680ms | 38% |
10+
| initialize-environment | 822ms | 47% |
11+
| **总计** | **1768ms** | 100% |
12+
13+
`core-loaders` 阶段的 680ms 几乎全部花在 `Import-Module psutils.psd1`(同步加载 19 个 NestedModules),但 profile 启动路径实际只使用其中 5-6 个子模块的约 8 个函数。`initialize-environment` 阶段中 `fnm env --use-on-cd` 每次都启动外部进程(未缓存)、`Get-ProfileModeDecision` 中的 `Get-Item -Path "Env:..."` 使用低效 Provider API 也有显著开销。
14+
15+
已有的延迟加载先例:`environment.ps1` 中 zoxide 的 `z` 函数已使用 stub-and-replace 懒加载模式。
16+
17+
## Goals / Non-Goals
18+
19+
**Goals:**
20+
21+
- 将 Full 模式加载时间从 ~1.77s 降至 ~1.1s 以下
22+
- 用户看到 prompt 后,所有 psutils 函数的 Tab 补全在 1-2 秒内可用
23+
- profile 启动路径中的核心函数(`Invoke-WithCache``Set-Proxy``Sync-PathFromBash` 等)立即可用
24+
- fnm 初始化与 starship/zoxide 保持一致的缓存策略
25+
- 环境变量检测使用高效 .NET API
26+
27+
**Non-Goals:**
28+
29+
- 不拆分 `psutils.psd1` 为多个独立模块(保持单一模块的发布和维护简单性)
30+
- 不编译为二进制模块(改动量过大,投入产出比低)
31+
- 不修改 psutils 子模块本身的代码逻辑
32+
- 不改变 UltraMinimal / Minimal 模式的行为
33+
34+
## Decisions
35+
36+
### Decision 1: psutils 分层延迟加载策略 — 同步 dot-source 核心子模块 + OnIdle 延迟全量加载 + PSModulePath 兜底
37+
38+
**选择:** 三层组合方案
39+
40+
**方案描述:**
41+
42+
1. **同步阶段(启动时)**:在 `loadModule.ps1` 中不再 `Import-Module psutils.psd1`,改为直接 dot-source 6 个 profile 必需子模块:
43+
- `os.psm1` — 被 cache.psm1 依赖
44+
- `cache.psm1``Invoke-WithCache``Invoke-WithFileCache`
45+
- `test.psm1``Test-EXEProgram`
46+
- `env.psm1``Sync-PathFromBash`
47+
- `proxy.psm1``Set-Proxy`
48+
- `wrapper.psm1``Set-CustomAlias``Get-CustomAlias`
49+
50+
2. **异步阶段(空闲时)**:注册 `Register-EngineEvent -SourceIdentifier PowerShell.OnIdle -MaxTriggerCount 1`,在用户首次空闲时执行 `Import-Module psutils.psd1 -Force -Global`,静默加载完整模块(覆盖 dot-source 的函数,补全其余 14 个子模块的函数)。
51+
52+
3. **兜底阶段(PSModulePath)**:将 psutils 目录追加到 `$env:PSModulePath`,确保即使 OnIdle 未触发,用户调用未加载函数时 PowerShell 仍能自动发现并加载完整模块。
53+
54+
**替代方案:**
55+
56+
- **方案 B:拆分为 psutils-core.psd1 + psutils.psd1**:更干净但需维护两个 manifest,增加长期维护成本。
57+
- **方案 C:仅靠 PSModulePath 自动加载**:不做同步加载,完全按需。但首次调用任一函数都会触发全量加载(19 模块),且 profile 启动路径自身需要核心函数。
58+
- **方案 D:Stub 函数模式**:为每个延迟函数创建 stub,精细但需生成 60+ 个 stub 函数,维护成本高。
59+
60+
**选择 A 的理由:** 不需要改动 psutils.psd1 或子模块本身,仅改动 `loadModule.ps1`。OnIdle 在主 runspace 中执行(非后台 Job),所以函数直接可用于 Tab 补全。dot-source 在后续 `Import-Module -Force` 时会被模块系统正确覆盖,无冲突。
61+
62+
### Decision 2: fnm 初始化改用 Invoke-WithFileCache
63+
64+
**选择:** 与 starship/zoxide 使用相同的 `Invoke-WithFileCache` 模式
65+
66+
**当前实现:**
67+
```powershell
68+
fnm env --use-on-cd | Out-String | Invoke-Expression
69+
```
70+
每次 profile 加载都启动 `fnm` 外部进程(~50-100ms)。
71+
72+
**优化后:**
73+
```powershell
74+
$fnmFile = Invoke-WithFileCache -Key "fnm-init-powershell" -MaxAge ([TimeSpan]::FromDays(7)) `
75+
-Generator { fnm env --use-on-cd } -BaseDir (Join-Path $profileRoot '.cache')
76+
. $fnmFile
77+
```
78+
缓存命中时仅 dot-source 缓存文件,无外部进程开销。
79+
80+
**替代方案:** 无(这是已验证有效的模式,starship/zoxide 已成功使用)。
81+
82+
### Decision 3: 环境变量检测 API 替换
83+
84+
**选择:**`[System.Environment]::GetEnvironmentVariable($Name)` 替换 `Get-Item -Path "Env:$Name"`
85+
86+
**理由:**
87+
- `Get-Item -Path "Env:$Name"` 走 PowerShell Provider 子系统,每次约 ~10ms
88+
- `[Environment]::GetEnvironmentVariable()` 是 .NET 原生调用,约 ~0.1ms
89+
- `Get-ProfileModeDecision` 中调用 `Test-EnvSwitchEnabled` / `Test-EnvValuePresent` 约 8-10 次,累积开销 ~80ms → 优化后 ~1ms
90+
91+
### Decision 4: wrapper.ps1 加载纳入 OnIdle 延迟
92+
93+
**选择:**`wrapper.ps1` 的 dot-source 从同步阶段移到 OnIdle 事件中
94+
95+
**理由:** `wrapper.ps1` 中定义的 `yaz``Add-CondaEnv``Get-FunctionWrapperInfo` 等函数在 profile 启动路径中不被调用(`Set-AliasProfile` 使用的 `Set-CustomAlias` 来自 `wrapper.psm1` 子模块而非 `wrapper.ps1`)。延迟加载可节省 ~20-30ms。
96+
97+
**注意:** `Set-AliasProfile` 中有对 `$AliasDescPrefix` 变量的引用,如果这个变量依赖 `wrapper.ps1` 中的某些定义,需要确认。经检查,`$AliasDescPrefix` 来自 `wrapper.psm1` 模块级变量 `$Global:DefaultAliasDespPrefix`,该模块已在同步阶段 dot-source,所以安全。
98+
99+
## Risks / Trade-offs
100+
101+
### [风险] PowerShell.OnIdle 事件兼容性不稳定
102+
**缓解:** PSModulePath 兜底机制确保即使 OnIdle 永远不触发,用户首次调用未加载函数时 PowerShell 自动发现并加载完整模块。行为等价于"首次使用时延迟加载"。
103+
104+
### [风险] dot-source 的函数在 global scope 而非 module scope
105+
**缓解:** OnIdle 的 `Import-Module -Force -Global` 会用模块系统重新注册同名函数,覆盖 dot-source 版本。在覆盖前的短暂窗口期内,函数功能完全一致(代码相同),仅作用域不同。对 profile 场景无实际影响。
106+
107+
### [风险] OnIdle 触发前用户调用了未加载的扩展函数(如 Get-Tree)
108+
**缓解:** PSModulePath 自动加载兜底。PowerShell 发现 `psutils.psd1``FunctionsToExport` 包含该函数名,自动执行 `Import-Module`。用户感知到的是首次调用稍慢(~400ms),后续正常。
109+
110+
### [风险] fnm 缓存文件过期导致环境变量不正确
111+
**缓解:** 缓存有效期设为 7 天(与 starship/zoxide 一致)。`fnm env` 输出中的路径是固定的(基于 fnm 安装位置),不会频繁变化。用户手动更新 fnm 版本后,可通过删除 `.cache/fnm-init-powershell*` 强制刷新。
112+
113+
### [权衡] Tab 补全延迟窗口
114+
OnIdle 触发前(通常 prompt 显示后 1-2 秒),非核心子模块的函数 Tab 补全不可用。但 PSModulePath 兜底确保输入完整命令名后可执行。实际影响极小——用户打开 shell 后通常有 1-2 秒的"看一眼 prompt"时间,足够 OnIdle 完成加载。
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
## Why
2+
3+
上一轮 Profile 加载优化(`profile-loading-optimization`)将加载时间从约 2s 降至约 1.77s,解决了 starship 缓存、Tab 补全、代理探测等问题。但当前 Full 模式仍需 ~1.77s,其中 `core-loaders` 阶段(680ms)因 psutils 全量加载 19 个子模块成为最大瓶颈,`initialize-environment` 阶段(822ms)中 fnm 每次启动外部进程、环境变量检测使用低效 Provider API 等问题也有显著优化空间。需要通过延迟加载、缓存和 API 替换将 Full 模式加载时间降至 ~1.1s 以下。
4+
5+
## What Changes
6+
7+
- 将 psutils 模块从全量同步加载改为**分层延迟加载**:启动时仅 dot-source 5-6 个 profile 必需子模块,其余 14 个子模块通过 `Register-EngineEvent PowerShell.OnIdle` 在空闲时静默加载,并以 `PSModulePath` 自动发现机制兜底
8+
-`fnm env --use-on-cd` 初始化改为 `Invoke-WithFileCache` 缓存模式,与 starship/zoxide 的缓存策略保持一致
9+
-`Get-ProfileModeDecision` 中的环境变量检测从 `Get-Item -Path "Env:$Name"`(PowerShell Provider)替换为 `[System.Environment]::GetEnvironmentVariable()`(.NET 原生调用)
10+
-`wrapper.ps1` 加载改为延迟加载,在 OnIdle 或首次调用时才 dot-source
11+
12+
## Capabilities
13+
14+
### New Capabilities
15+
16+
- `psutils-deferred-loading`: psutils 模块分层延迟加载能力,将 19 个子模块拆为同步核心集与异步延迟集,通过 OnIdle 事件和 PSModulePath 自动发现实现无感全量加载
17+
18+
### Modified Capabilities
19+
20+
- `unified-profile`: 修改模块加载流程(分层延迟)、环境变量检测 API、fnm 初始化缓存、wrapper 延迟加载
21+
22+
## Impact
23+
24+
- **profile/core/loadModule.ps1**: 核心改造 — 从 `Import-Module psutils.psd1` 改为 dot-source 核心子模块 + OnIdle 延迟全量加载 + PSModulePath 兜底
25+
- **profile/core/mode.ps1**: `Test-EnvSwitchEnabled` / `Test-EnvValuePresent` 函数 API 替换
26+
- **profile/features/environment.ps1**: fnm 初始化改用 `Invoke-WithFileCache` 缓存、wrapper.ps1 延迟加载
27+
- **profile/core/loaders.ps1**: 调整 `$InvokeProfileCoreLoaders` 脚本块中的加载顺序和方式
28+
- **psutils/modules/**: 子模块本身不改动,仅改变加载时机
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
## ADDED Requirements
2+
3+
### Requirement: psutils 分层延迟加载
4+
5+
Profile 启动时 SHALL 仅同步 dot-source psutils 的核心子模块集合(`os.psm1``cache.psm1``test.psm1``env.psm1``proxy.psm1``wrapper.psm1`),不执行 `Import-Module psutils.psd1`。其余子模块 SHALL 通过异步机制延迟加载。
6+
7+
#### Scenario: 同步阶段仅加载核心子模块
8+
9+
- **WHEN** profile 以 Full 或 Minimal 模式启动并进入 core-loaders 阶段
10+
- **THEN** SHALL 仅 dot-source 6 个核心子模块(`os``cache``test``env``proxy``wrapper`),不加载其余 14 个子模块
11+
12+
#### Scenario: 核心函数在启动后立即可用
13+
14+
- **WHEN** profile 启动完成(prompt 已显示)
15+
- **THEN** 以下函数 SHALL 立即可调用:`Invoke-WithCache``Invoke-WithFileCache``Get-CacheStats``Clear-ExpiredCache``Set-Proxy``Close-Proxy``Start-Proxy``Sync-PathFromBash``Get-Dotenv``Import-EnvPath``Set-EnvPath``Add-EnvPath``Remove-FromEnvPath``Get-OperatingSystem``Test-Administrator``Test-EXEProgram``Test-ApplicationInstalled``Set-CustomAlias``Get-CustomAlias`
16+
17+
### Requirement: OnIdle 异步全量加载
18+
19+
Profile 启动后 SHALL 注册 `PowerShell.OnIdle` 引擎事件(`-MaxTriggerCount 1`),在用户首次空闲时执行 `Import-Module psutils.psd1 -Force -Global` 加载完整模块。
20+
21+
#### Scenario: 空闲时静默加载完整模块
22+
23+
- **WHEN** 用户在 prompt 显示后首次空闲(OnIdle 事件触发)
24+
- **THEN** SHALL 执行完整 psutils 模块导入,覆盖同步阶段 dot-source 的函数,所有 70+ 个函数 SHALL 可用于 Tab 补全和直接调用
25+
26+
#### Scenario: OnIdle 仅触发一次
27+
28+
- **WHEN** OnIdle 事件已触发并完成全量加载
29+
- **THEN** SHALL 不再触发后续 OnIdle 加载(通过 `-MaxTriggerCount 1` 保证)
30+
31+
#### Scenario: OnIdle 加载失败不影响已有函数
32+
33+
- **WHEN** OnIdle 事件中的 `Import-Module` 执行失败
34+
- **THEN** 同步阶段已加载的核心函数 SHALL 继续正常工作,错误 SHALL 通过 `Write-Warning` 静默记录
35+
36+
### Requirement: PSModulePath 兜底发现
37+
38+
`loadModule.ps1` SHALL 将 psutils 模块目录追加到 `$env:PSModulePath`(如尚未存在),确保 PowerShell 自动加载机制可以发现 `psutils.psd1`
39+
40+
#### Scenario: 未加载函数首次调用触发自动加载
41+
42+
- **WHEN** 用户在 OnIdle 触发前调用了一个非核心子模块的函数(如 `Get-Tree`
43+
- **THEN** PowerShell 的自动模块加载机制 SHALL 通过 `PSModulePath` 发现 `psutils.psd1` 并执行 `Import-Module`,函数 SHALL 成功执行
44+
45+
#### Scenario: PSModulePath 不重复追加
46+
47+
- **WHEN** psutils 模块目录已存在于 `$env:PSModulePath`
48+
- **THEN** SHALL 不重复追加,保持 PSModulePath 干净
49+
50+
### Requirement: 核心子模块 dot-source 加载顺序
51+
52+
同步阶段的 dot-source SHALL 遵循依赖顺序:`os.psm1` 必须在 `cache.psm1` 之前加载(`cache.psm1` 的模块级代码依赖 `Get-OperatingSystem`)。
53+
54+
#### Scenario: 依赖顺序正确
55+
56+
- **WHEN** profile 同步阶段加载核心子模块
57+
- **THEN** SHALL 按以下顺序 dot-source:`os``cache``test``env``proxy``wrapper`
58+
59+
#### Scenario: 子模块加载失败时终止启动
60+
61+
- **WHEN** 任一核心子模块 dot-source 失败
62+
- **THEN** SHALL 抛出错误并终止 profile 加载(与当前 `Import-Module -ErrorAction Stop` 行为一致)
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
## MODIFIED Requirements
2+
3+
### Requirement: 统一工具初始化
4+
5+
`Initialize-Environment` 函数 SHALL 维护统一的工具初始化表,并依据当前模式控制初始化范围。每个工具的初始化逻辑 SHALL 内部判断平台适用性。
6+
7+
#### Scenario: Full 模式初始化完整工具链
8+
9+
- **WHEN** 当前模式为 Full 且工具已安装
10+
- **THEN** SHALL 执行 starship、zoxide、fnm、sccache 等初始化逻辑,并按平台规则完成配置
11+
12+
#### Scenario: 通用工具初始化
13+
14+
- **WHEN** starship 或 zoxide 已安装
15+
- **THEN** SHALL 在所有平台上初始化该工具,使用 `Invoke-WithFileCache` 缓存初始化脚本
16+
17+
#### Scenario: 平台特定工具初始化
18+
19+
- **WHEN** 在 Windows 平台且 sccache 已安装
20+
- **THEN** SHALL 设置 `$env:RUSTC_WRAPPER = 'sccache'`
21+
- **WHEN** 在 Unix 平台且 fnm 已安装
22+
- **THEN** SHALL 使用 `Invoke-WithFileCache` 缓存 `fnm env --use-on-cd` 输出并 dot-source 缓存文件,而非每次启动外部进程
23+
24+
#### Scenario: Minimal 模式跳过交互增强
25+
26+
- **WHEN** 当前模式为 Minimal
27+
- **THEN** SHALL 跳过工具初始化和别名注册,但保留模块函数可用性
28+
29+
#### Scenario: UltraMinimal 模式仅保留最小能力
30+
31+
- **WHEN** 当前模式为 UltraMinimal
32+
- **THEN** SHALL 跳过模块加载、工具初始化、代理检测、PATH 同步、别名与包装函数注册,仅保留 UTF8 设置、`POWERSHELL_SCRIPTS_ROOT` 与基础变量兼容
33+
34+
### Requirement: 模式解析优先级与自动降级
35+
36+
`profile/profile.ps1` SHALL 按固定优先级解析运行模式,优先级为 `POWERSHELL_PROFILE_FULL` > `POWERSHELL_PROFILE_MODE` > `POWERSHELL_PROFILE_ULTRA_MINIMAL` > 自动判定 > 默认值。环境变量检测 SHALL 使用 `[System.Environment]::GetEnvironmentVariable()` .NET 原生 API 而非 `Get-Item -Path "Env:..."` PowerShell Provider。
37+
38+
#### Scenario: Full 开关最高优先级
39+
40+
- **WHEN** `POWERSHELL_PROFILE_FULL=1` 且同时设置了其他模式变量
41+
- **THEN** 系统 SHALL 强制使用 Full 模式并忽略其他模式变量
42+
43+
#### Scenario: 显式模式变量生效
44+
45+
- **WHEN** `POWERSHELL_PROFILE_MODE` 设置为 `full``minimal``ultra`
46+
- **THEN** 系统 SHALL 使用对应模式并记录来源为显式配置
47+
48+
#### Scenario: UltraMinimal 显式开关生效
49+
50+
- **WHEN** `POWERSHELL_PROFILE_ULTRA_MINIMAL=1` 且未设置 `POWERSHELL_PROFILE_FULL=1`
51+
- **THEN** 系统 SHALL 使用 UltraMinimal 模式
52+
53+
#### Scenario: Codex 或沙盒环境自动降级
54+
55+
- **WHEN** 未设置显式模式变量且检测到 `CODEX_THREAD_ID``CODEX_SANDBOX_NETWORK_DISABLED`
56+
- **THEN** 系统 SHALL 自动降级为 UltraMinimal 模式
57+
58+
#### Scenario: 默认使用 Full
59+
60+
- **WHEN** 未命中任何显式配置与自动降级条件
61+
- **THEN** 系统 SHALL 使用 Full 模式
62+
63+
#### Scenario: 环境变量检测性能
64+
65+
- **WHEN** `Get-ProfileModeDecision` 执行环境变量检测
66+
- **THEN** SHALL 使用 `[System.Environment]::GetEnvironmentVariable()` 而非 `Get-Item -Path "Env:..."`,单次检测耗时 SHALL 低于 1ms
67+
68+
### Requirement: 模块化入口编排
69+
70+
`profile.ps1` 在完成拆分后 SHALL 继续作为统一入口,并以可维护的模块化方式组织内部实现。core-loaders 阶段 SHALL 使用分层延迟加载策略加载 psutils 模块。
71+
72+
#### Scenario: 入口路径保持不变
73+
74+
- **WHEN** 用户执行 `./profile/profile.ps1`
75+
- **THEN** 系统 SHALL 通过统一入口完成初始化,且不要求用户修改现有调用方式
76+
77+
#### Scenario: wrapper.ps1 延迟加载
78+
79+
- **WHEN** profile 进入 core-loaders 阶段
80+
- **THEN** `wrapper.ps1` 的 dot-source SHALL 被纳入 OnIdle 延迟加载,不在同步阶段执行
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
## 1. 环境变量检测 API 替换
2+
3+
- [ ] 1.1 将 `profile/core/mode.ps1``Test-EnvSwitchEnabled` 函数的 `Get-Item -Path "Env:$Name"` 替换为 `[System.Environment]::GetEnvironmentVariable($Name)`,保持返回值语义不变
4+
- [ ] 1.2 将 `profile/core/mode.ps1``Test-EnvValuePresent` 函数的 `Get-Item -Path "Env:$Name"` 替换为 `[System.Environment]::GetEnvironmentVariable($Name)`,保持返回值语义不变
5+
- [ ] 1.3 验证 `Get-ProfileModeDecision` 在各种环境变量组合下的行为与替换前一致(Full / Minimal / UltraMinimal / Codex 自动降级 / 默认)
6+
7+
## 2. psutils 分层延迟加载
8+
9+
- [ ] 2.1 改造 `profile/core/loadModule.ps1`:移除 `Import-Module psutils.psd1`,改为按依赖顺序 dot-source 6 个核心子模块(`os``cache``test``env``proxy``wrapper`
10+
- [ ] 2.2 在 `loadModule.ps1` 中将 psutils 模块父目录追加到 `$env:PSModulePath`(去重检查),作为自动加载兜底
11+
- [ ] 2.3 在 `loadModule.ps1` 中注册 `Register-EngineEvent -SourceIdentifier PowerShell.OnIdle -MaxTriggerCount 1 -Action { Import-Module psutils.psd1 -Force -Global }`,实现空闲时全量加载
12+
- [ ] 2.4 为 OnIdle 事件的 Action 添加 `try/catch` 错误处理,失败时通过 `Write-Warning` 静默记录
13+
- [ ] 2.5 保留 `loadModule.ps1` 中的 PSModulePath 去重逻辑(现有的 `HashSet` 去重代码)
14+
15+
## 3. wrapper.ps1 延迟加载
16+
17+
- [ ] 3.1 将 `profile/core/loaders.ps1``wrapper.ps1` 的 dot-source 从同步阶段移到 OnIdle 事件中(与 psutils 全量加载合并在同一个 OnIdle Action 中)
18+
- [ ] 3.2 确认 `Set-AliasProfile` 所依赖的 `Set-CustomAlias` / `Get-CustomAlias` 来自 `wrapper.psm1`(核心子模块同步阶段已加载),不依赖 `wrapper.ps1`
19+
20+
## 4. fnm 初始化缓存化
21+
22+
- [ ] 4.1 将 `profile/features/environment.ps1` 中 fnm 初始化从 `fnm env --use-on-cd | Out-String | Invoke-Expression` 改为 `Invoke-WithFileCache -Key "fnm-init-powershell" -MaxAge ([TimeSpan]::FromDays(7)) -Generator { fnm env --use-on-cd } -BaseDir (Join-Path $profileRoot '.cache')` + dot-source 缓存文件
23+
- [ ] 4.2 验证 fnm 缓存文件内容可被正确 dot-source(环境变量设置和 `use-on-cd` hook 正常工作)
24+
25+
## 5. 验证与回归测试
26+
27+
- [ ] 5.1 使用 `POWERSHELL_PROFILE_TIMING=1` 运行 profile,验证 `core-loaders` 阶段从 ~680ms 降至 ~230ms
28+
- [ ] 5.2 验证 Full 模式下总加载时间降至 ~1.1s 以下
29+
- [ ] 5.3 验证 prompt 显示后执行 `Get-Tree`(非核心函数)可正常工作(PSModulePath 自动加载兜底)
30+
- [ ] 5.4 验证 OnIdle 触发后所有 70+ 个 psutils 函数可 Tab 补全
31+
- [ ] 5.5 验证 UltraMinimal 模式行为不变(跳过所有模块加载)
32+
- [ ] 5.6 验证 Minimal 模式行为不变(加载模块但跳过工具和别名)
33+
- [ ] 5.7 运行 `pnpm test:fast` 确保现有 Pester 测试全部通过

0 commit comments

Comments
 (0)