@deepstorm/cli 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -71,13 +71,17 @@ bash packages/reef/hooks/reef-scope-split.sh
71
71
 
72
72
  ## 配置文件
73
73
 
74
- 安装后生成 `.deepstorm/scope-config.json`:
74
+ 安装后配置写入 `.deepstorm/settings.json` 的 `reef.scope` 字段:
75
75
 
76
76
  ```json
77
77
  {
78
- "enabled": true,
79
- "ciEnabled": true,
80
- "domains": []
78
+ "reef": {
79
+ "scope": {
80
+ "enabled": true,
81
+ "ciEnabled": true,
82
+ "domains": []
83
+ }
84
+ }
81
85
  }
82
86
  ```
83
87
 
@@ -85,6 +89,8 @@ bash packages/reef/hooks/reef-scope-split.sh
85
89
  - `ciEnabled`: CI 门禁开关(独立控制)
86
90
  - `domains`: 项目业务领域列表(空 = AI 自由分类,非空 = AI 对齐到这些领域)
87
91
 
92
+ > **注意:** 旧版 `.deepstorm/scope-config.json` 已不再使用。如果存在该文件,运行 `deepstorm scope setup` 会自动迁移到 `settings.json`。
93
+
88
94
  ## 使用流程
89
95
 
90
96
  1. **安装**: 运行一次 `reef-scope-setup.sh install`
@@ -8,7 +8,7 @@ deepstorm:
8
8
 
9
9
  # Sweep Setup — E2E 测试项目初始化
10
10
 
11
- 初始化当前目录为 Sweep E2E 测试项目。根据 DeepStorm setup 阶段选择的 E2E 框架(如 Playwright),创建对应的项目骨架。
11
+ 初始化当前目录(或指定子目录)为 Sweep E2E 测试项目。根据 DeepStorm setup 阶段选择的 E2E 框架(如 Playwright),创建对应的项目骨架。
12
12
 
13
13
  > **注意:** setup 是 flow-create 和 flow-run 的硬性前置条件。如果当前目录未初始化,其他 skill 会报错引导执行 `/sweep-init`。
14
14
 
@@ -41,27 +41,79 @@ cat .deepstorm/settings.json 2>/dev/null | grep -o '"e2eFramework"[^,]*' | head
41
41
 
42
42
  ---
43
43
 
44
+ ### 步骤 0A:选择生成位置
45
+
46
+ 在执行初始化前,先询问用户的 E2E 测试项目类型,确定文件生成的目标目录。
47
+
48
+ > **注意:** TARGET_DIR 是后续所有文件操作的前缀,在整个初始化流程中使用。
49
+
50
+ #### 0A.1 询问项目类型
51
+
52
+ ```
53
+ 📂 请问你的 E2E 测试是哪种类型?
54
+
55
+ a) 独立项目 → 生成在当前目录
56
+ b) 与其他项目混放 → 生成到子目录
57
+ ```
58
+
59
+ **如果选择 a(独立项目):**
60
+ - 设置 TARGET_DIR 为当前目录(`.`)
61
+ - 继续到步骤 1
62
+
63
+ **如果选择 b(混放项目):**
64
+ - 进一步询问子目录选择:
65
+
66
+ ```
67
+ 请选择目标子目录:
68
+ 1) e2e/ 👈 推荐
69
+ 2) tests/e2e/
70
+ 3) 自定义路径
71
+ ```
72
+
73
+ - 如果选择 3(自定义),AI 询问用户输入路径
74
+ - 验证用户输入的路径:不能为空、不能是绝对路径、不能包含 `..`
75
+ - 设置 TARGET_DIR 为所选路径(如 `"e2e"`、`"tests/e2e"` 等)
76
+ - 如果子目录已存在且非空,输出警告并询问用户是否确认继续
77
+
78
+ #### 0A.2 记录 TARGET_DIR
79
+
80
+ `TARGET_DIR` 变量在后续所有步骤中使用。写入 `.deepstorm/settings.json` 中的 `sweep.e2eProjectPath` 字段将在步骤 8 中完成。
81
+
82
+ ---
83
+
44
84
  ### 步骤 1:检查初始化状态
45
85
 
46
- 检查当前目录是否存在 `.sweep-init` 标记文件。
86
+ 检查当前项目是否已初始化为 E2E 测试项目。从 `.deepstorm/settings.json` 读取 `sweep.e2eProjectPath` 判断。
87
+
88
+ #### 1.1 检查 settings.json
89
+
90
+ ```bash
91
+ E2E_PATH=$(cat .deepstorm/settings.json 2>/dev/null | grep -o '"e2eProjectPath"[^,]*' | head -1 | cut -d'"' -f4)
92
+ ```
47
93
 
48
- #### 1.1 已初始化 → 退出
94
+ #### 1.2 已初始化 → 退出
49
95
 
50
- - **WHEN** `.sweep-init` 文件已存在
51
- - **THEN** 提示"当前目录已初始化为 Sweep 测试项目。如需重新初始化,请删除 .sweep-init 文件后重试。"并退出
96
+ - **WHEN** `sweep.e2eProjectPath` 已设置(或旧 `.sweep-init` 文件存在)
97
+ - **THEN** 提示"当前项目已初始化为 Sweep 测试项目。如需重新初始化,请删除 `sweep.e2eProjectPath` 配置或 `.sweep-init` 文件后重试。"并退出
52
98
 
53
- #### 1.2 未初始化 继续
99
+ #### 1.3 向后兼容:旧 .sweep-init 文件检测
54
100
 
55
- - **WHEN** `.sweep-init` 文件不存在
101
+ - **WHEN** `settings.json` 中 `sweep.e2eProjectPath` 未设置,但 `.sweep-init` 文件存在
102
+ - **THEN** 提示"ℹ️ 检测到旧版 .sweep-init 标记。建议删除该文件后重新运行 /sweep-init 以使用新版配置。"
103
+ - **THEN** 提示已初始化,退出
104
+
105
+ #### 1.4 未初始化 → 继续
106
+
107
+ - **WHEN** 以上均不满足
56
108
  - **THEN** 继续执行初始化流程
57
109
 
58
110
  ---
59
111
 
60
112
  ### 步骤 2:创建项目目录结构
61
113
 
62
- 创建 E2E 测试项目的目录骨架。
114
+ `{TARGET_DIR}` 下创建 E2E 测试项目的目录骨架。
63
115
 
64
- #### 目录结构
116
+ #### 目录结构(当 TARGET_DIR 为 `"."` 时)
65
117
 
66
118
  ```
67
119
  .
@@ -75,19 +127,27 @@ cat .deepstorm/settings.json 2>/dev/null | grep -o '"e2eFramework"[^,]*' | head
75
127
  └── tsconfig.json # TypeScript 配置
76
128
  ```
77
129
 
78
- > 如果框架为 `playwright`,还会额外生成 `playwright.config.ts`。
130
+ > 如果 TARGET_DIR 为 `"e2e"`,以上所有文件在 `e2e/` 下生成。如果框架为 `playwright`,还会额外生成 `playwright.config.ts`。
79
131
 
80
132
  #### 执行
81
133
 
82
134
  ```bash
83
- mkdir -p flows flows/reports scripts
135
+ # 计算目标目录:TARGET_DIR "." 时用当前目录,否则用 TARGET_DIR
136
+ if [ "$TARGET_DIR" != "." ]; then
137
+ mkdir -p "$TARGET_DIR"/flows "$TARGET_DIR"/flows/reports "$TARGET_DIR"/scripts
138
+ echo "📂 将在 $TARGET_DIR/ 目录下生成 E2E 测试项目"
139
+ else
140
+ mkdir -p flows flows/reports scripts
141
+ fi
84
142
  ```
85
143
 
86
144
  ---
87
145
 
88
146
  ### 步骤 3:生成项目配置文件(框架感知)
89
147
 
90
- 根据步骤 0 读取的框架配置,生成对应的配置文件。
148
+ 根据步骤 0 读取的框架配置,在 `{TARGET_DIR}` 下生成对应的配置文件。
149
+
150
+ > 所有文件写入路径以 `{TARGET_DIR}` 为前缀。当 `TARGET_DIR` 为 `"."` 时,路径不变。
91
151
 
92
152
  #### 3.1 package.json
93
153
 
@@ -115,7 +175,7 @@ mkdir -p flows flows/reports scripts
115
175
 
116
176
  #### 3.2 框架配置文件
117
177
 
118
- **当框架为 `playwright` 时**,写入 `playwright.config.ts`,包含多环境 baseURL 设置:
178
+ **当框架为 `playwright` 时**,写入 `{TARGET_DIR}/playwright.config.ts`,包含多环境 baseURL 设置:
119
179
 
120
180
  ```ts
121
181
  import { defineConfig } from '@playwright/test';
@@ -164,23 +224,43 @@ export default defineConfig({
164
224
  3. 生产环境(prod)baseURL
165
225
  4. 默认目标环境(test / staging / prod)
166
226
 
167
- #### 4.2 写入 .env
227
+ #### 4.2 写入 settings.json
168
228
 
169
- 只写入环境地址配置,不包含 MCP 连接说明(MCP 配置在 `.mcp.json` 中统一管理):
229
+ 将多环境配置写入 `.deepstorm/settings.json` `sweep.environments` 字段。**不再生成 `.env` 文件。**
230
+
231
+ 使用 Bash 和 `jq` 合并写入(或 AI 读取-修改-写回):
170
232
 
171
233
  ```bash
172
- # .env(gitignored,不提交)
173
- BASE_URL_TEST=http://localhost:3000
174
- BASE_URL_STAGING=https://staging.example.com
175
- BASE_URL_PROD=https://prod.example.com
176
- DEFAULT_ENV=test
234
+ # 先读取现有 settings.json
235
+ SETTINGS=$(cat .deepstorm/settings.json 2>/dev/null || echo "{}")
236
+
237
+ # 构造 environments 配置并写入
238
+ # (使用 jq 或 AI 逐字段修改)
177
239
  ```
178
240
 
241
+ 写入后的 settings.json 结构示例:
242
+
243
+ ```json
244
+ {
245
+ "sweep": {
246
+ "e2eFramework": "playwright",
247
+ "environments": {
248
+ "test": { "baseUrl": "http://localhost:3000" },
249
+ "staging": { "baseUrl": "https://staging.example.com" },
250
+ "prod": { "baseUrl": "https://prod.example.com" },
251
+ "default": "test"
252
+ }
253
+ }
254
+ }
255
+ ```
256
+
257
+ > **注意:** API Key 等敏感信息仍然放在 `.env` 文件(gitignored)中。此处仅迁移多环境 baseURL 配置。
258
+
179
259
  ---
180
260
 
181
261
  ### 步骤 5:创建 topology.yaml 初始模板
182
262
 
183
- 写入功能模块拓扑文件基础结构:
263
+ 写入功能模块拓扑文件基础结构到 `{TARGET_DIR}/flows/topology.yaml`:
184
264
 
185
265
  ```yaml
186
266
  # flows/topology.yaml
@@ -202,7 +282,7 @@ modules:
202
282
 
203
283
  ### 步骤 6:创建 flow-selector.mjs
204
284
 
205
- 使用 Read 读取本 skill 目录下的 `scripts/flow-selector.mjs` 文件,用 Write 写入目标项目的 `scripts/flow-selector.mjs`。
285
+ 使用 Read 读取本 skill 目录下的 `scripts/flow-selector.mjs` 文件,用 Write 写入目标项目的 `{TARGET_DIR}/scripts/flow-selector.mjs`。
206
286
 
207
287
  > **说明:** `flow-selector.mjs` 是基于 `@inquirer/checkbox` 的层级选择工具脚本(供 flow-run skill 调用)。
208
288
  > 它与本 SKILL.md 同属一个 skill,作为 skill 的子文件分开维护,避免内联代码块导致的重复。
@@ -236,16 +316,37 @@ cat .mcp.json 2>/dev/null | grep -c "deepstorm-playwright"
236
316
 
237
317
  ### 步骤 8:完成初始化
238
318
 
239
- #### 8.1 创建初始化标记
319
+ #### 8.1 写入 e2eProjectPath 到 settings.json
320
+
321
+ 将 `sweep.e2eProjectPath` 写入 `.deepstorm/settings.json`。**不再创建 `.sweep-init` 标记文件。**
240
322
 
241
323
  ```bash
242
- echo "sweep-e2e-project" > .sweep-init
324
+ # 值由步骤 0A 确定:
325
+ # 独立项目 → "."
326
+ # 混放项目 → 用户选择的路径(如 "e2e")
327
+ ```
328
+
329
+ 写入后的 settings.json 结构示例:
330
+
331
+ ```json
332
+ {
333
+ "sweep": {
334
+ "e2eFramework": "playwright",
335
+ "e2eProjectPath": "e2e",
336
+ "environments": { ... }
337
+ }
338
+ }
243
339
  ```
244
340
 
245
341
  #### 8.2 安装依赖
246
342
 
247
343
  ```bash
248
- npm install
344
+ # 在目标目录中执行 npm install
345
+ if [ "$TARGET_DIR" != "." ]; then
346
+ cd "$TARGET_DIR" && npm install
347
+ else
348
+ npm install
349
+ fi
249
350
  ```
250
351
 
251
352
  #### 8.3 输出完成信息
@@ -254,15 +355,16 @@ npm install
254
355
  ✅ Sweep E2E 测试项目初始化完成!
255
356
 
256
357
  框架:{所选框架}
358
+ 位置:{TARGET_DIR}
257
359
 
258
360
  项目结构:
259
- flows/ — 测试意图文档目录
260
- flows/reports/ — 执行报告目录
261
- scripts/ — 辅助脚本
262
- (框架配置文件) — {框架名} 配置文件
361
+ {TARGET_DIR}/flows/ — 测试意图文档目录
362
+ {TARGET_DIR}/flows/reports/ — 执行报告目录
363
+ {TARGET_DIR}/scripts/ — 辅助脚本
364
+ (框架配置文件) — {框架名} 配置文件
263
365
 
264
366
  下一步:
265
- 1. 编辑 flows/topology.yaml 添加你的功能模块
367
+ 1. 编辑 {TARGET_DIR}/flows/topology.yaml 添加你的功能模块
266
368
  2. 运行 /sweep-plan 创建测试意图文档
267
369
  3. 运行 /sweep-run 执行测试流程
268
370
  ```
@@ -272,15 +374,16 @@ npm install
272
374
  ## 检查清单
273
375
 
274
376
  - [ ] 已读取框架配置(`deepstorm.sweep.e2eFramework`)
275
- - [ ] `.sweep-init` 不存在 → 继续
276
- - [ ] 项目目录结构已创建(flows/、flows/reports/、scripts/)
377
+ - [ ] 步骤 0A:已选择目标目录(`TARGET_DIR` 已确定)
378
+ - [ ] 步骤 1:检查通过(`e2eProjectPath` 未设置,且 `.sweep-init` 不存在)
379
+ - [ ] 项目目录结构已创建(`{TARGET_DIR}/flows/`、`{TARGET_DIR}/flows/reports/`、`{TARGET_DIR}/scripts/`)
277
380
  - [ ] package.json 已写入(含对应框架的依赖)
278
381
  - [ ] 框架配置文件已生成(如 playwright.config.ts)
279
382
  - [ ] tsconfig.json 已写入
280
383
  - [ ] 用户已输入三个环境的 baseURL
281
- - [ ] .env 已写入(gitignored)
384
+ - [ ] environments 已写入 `.deepstorm/settings.json` 的 `sweep.environments`
282
385
  - [ ] topology.yaml 初始模板已创建
283
386
  - [ ] flow-selector.mjs 已写入
284
387
  - [ ] 已确认 MCP 配置状态(从 `.mcp.json` 读取)
285
- - [ ] .sweep-init 标记文件已创建
286
- - [ ] npm install 已执行
388
+ - [ ] `sweep.e2eProjectPath` 已写入 `.deepstorm/settings.json`
389
+ - [ ] npm install 已在目标目录执行
@@ -17,7 +17,7 @@ mcpCapabilities:
17
17
 
18
18
  ## 前置条件
19
19
 
20
- 运行 `/sweep-plan` 前,请确保当前目录已通过 `/sweep-init` 初始化(存在 `.sweep-init` 标记文件和 `flows/` 目录)。
20
+ 运行 `/sweep-plan` 前,请确保项目已通过 `/sweep-init` 初始化(`.deepstorm/settings.json` 中存在 `sweep.e2eProjectPath` 配置)。
21
21
 
22
22
  ---
23
23
 
@@ -44,10 +44,67 @@ mcpCapabilities:
44
44
 
45
45
  ## 工作流
46
46
 
47
- ### 步骤 1:检查初始化状态
47
+ ### 步骤 1:检查初始化状态与路径导航
48
48
 
49
- - **WHEN** `.sweep-init` 不存在
50
- - **THEN** 提示"当前目录尚未初始化为 Sweep 测试项目。请先运行 /sweep-init 初始化。"并退出
49
+ `.deepstorm/settings.json` 读取 `sweep.e2eProjectPath`,确定 E2E 测试项目的位置,如非根目录则自动切换。支持从子目录向上查找以兼容用户在 E2E 项目子目录中执行的情况。
50
+
51
+ #### 1.1 读取配置(支持向上查找)
52
+
53
+ ```bash
54
+ # 向上查找 .deepstorm/settings.json
55
+ DEEPSTORM_DIR=""
56
+ CUR="$PWD"
57
+ while [ "$CUR" != "/" ]; do
58
+ if [ -f "$CUR/.deepstorm/settings.json" ]; then
59
+ DEEPSTORM_DIR="$CUR"
60
+ break
61
+ fi
62
+ CUR=$(dirname "$CUR")
63
+ done
64
+
65
+ if [ -n "$DEEPSTORM_DIR" ]; then
66
+ E2E_PATH=$(grep -o '"e2eProjectPath"[^,]*' "$DEEPSTORM_DIR/.deepstorm/settings.json" | head -1 | cut -d'"' -f4)
67
+ else
68
+ E2E_PATH=""
69
+ fi
70
+ ```
71
+
72
+ #### 1.2 配置存在 → 路径导航
73
+
74
+ - **WHEN** `E2E_PATH` 不为空
75
+ - **THEN** 判断路径值。注意 `E2E_PATH` 是基于 `DEEPSTORM_DIR`(settings.json 所在目录)的相对路径,若当前在子目录则需拼接:
76
+ ```bash
77
+ if [ "$E2E_PATH" != "." ]; then
78
+ # 若从子目录找到的 settings.json,E2E_PATH 相对于 DEEPSTORM_DIR
79
+ TARGET_DIR="$E2E_PATH"
80
+ if [ -n "$DEEPSTORM_DIR" ] && [ "$DEEPSTORM_DIR" != "$PWD" ]; then
81
+ TARGET_DIR="$DEEPSTORM_DIR/$E2E_PATH"
82
+ fi
83
+ if [ -d "$TARGET_DIR" ]; then
84
+ echo "📂 切换到 E2E 项目目录: $E2E_PATH"
85
+ cd "$TARGET_DIR"
86
+ else
87
+ echo "❌ E2E 项目目录不存在: $E2E_PATH,请重新运行 /sweep-init"
88
+ exit 1
89
+ fi
90
+ fi
91
+ ```
92
+
93
+ #### 1.3 配置不存在 → 向后兼容检查
94
+
95
+ - **WHEN** `E2E_PATH` 为空
96
+ - **THEN** 检查旧 `.sweep-init` 文件是否存在(从当前目录和 DEEPSTORM_DIR 向上查找):
97
+ ```bash
98
+ if [ -f ".sweep-init" ]; then
99
+ echo "ℹ️ 检测到旧版 .sweep-init 标记。建议运行 /sweep-init 刷新配置。"
100
+ # 继续执行(兼容旧项目)
101
+ elif [ -n "$DEEPSTORM_DIR" ] && [ -f "$DEEPSTORM_DIR/.sweep-init" ]; then
102
+ echo "ℹ️ 检测到旧版 .sweep-init 标记(项目根: $DEEPSTORM_DIR)。建议运行 /sweep-init 刷新配置。"
103
+ else
104
+ echo "❌ 未检测到 E2E 项目。请先运行 /sweep-init 初始化。"
105
+ exit 1
106
+ fi
107
+ ```
51
108
 
52
109
  ### 步骤 2:获取测试需求上下文(MCP 动态适配)
53
110
 
@@ -303,7 +360,7 @@ node scripts/spec-compiler.mjs flows/user-system/login/login.flow.md
303
360
 
304
361
  ## 检查清单
305
362
 
306
- - [ ] 项目已初始化(检查 .sweep-init
363
+ - [ ] 项目已初始化(读取 settings.json → sweep.e2eProjectPath
307
364
  - [ ] 获取到测试需求上下文(Issue / PRD / 描述)
308
365
  - [ ] 确定放置模块位置(topology.yaml)
309
366
  - [ ] 确定文件名
@@ -58,10 +58,65 @@ node scripts/env-manager.mjs --framework
58
58
 
59
59
  ---
60
60
 
61
- ### 步骤 1:检查初始化状态
61
+ ### 步骤 1:检查初始化状态与路径导航
62
62
 
63
- - **WHEN** `.sweep-init` 不存在
64
- - **THEN** 提示"当前目录尚未初始化为 Sweep 测试项目。请先运行 /sweep-init 初始化。"并退出
63
+ `.deepstorm/settings.json` 读取 `sweep.e2eProjectPath`,确定 E2E 测试项目的位置,如非根目录则自动切换。支持从子目录向上查找以兼容用户在 E2E 项目子目录中执行的情况。
64
+
65
+ ```bash
66
+ # 向上查找 .deepstorm/settings.json
67
+ DEEPSTORM_DIR=""
68
+ CUR="$PWD"
69
+ while [ "$CUR" != "/" ]; do
70
+ if [ -f "$CUR/.deepstorm/settings.json" ]; then
71
+ DEEPSTORM_DIR="$CUR"
72
+ break
73
+ fi
74
+ CUR=$(dirname "$CUR")
75
+ done
76
+
77
+ if [ -n "$DEEPSTORM_DIR" ]; then
78
+ E2E_PATH=$(grep -o '"e2eProjectPath"[^,]*' "$DEEPSTORM_DIR/.deepstorm/settings.json" | head -1 | cut -d'"' -f4)
79
+ else
80
+ E2E_PATH=""
81
+ fi
82
+ ```
83
+
84
+ #### 1.1 配置存在 → 路径导航
85
+
86
+ - **WHEN** `E2E_PATH` 不为空
87
+ - **THEN** 判断路径值。注意 `E2E_PATH` 是基于 `DEEPSTORM_DIR`(settings.json 所在目录)的相对路径,若当前在子目录则需拼接:
88
+ ```bash
89
+ if [ "$E2E_PATH" != "." ]; then
90
+ # 若从子目录找到的 settings.json,E2E_PATH 相对于 DEEPSTORM_DIR
91
+ TARGET_DIR="$E2E_PATH"
92
+ if [ -n "$DEEPSTORM_DIR" ] && [ "$DEEPSTORM_DIR" != "$PWD" ]; then
93
+ TARGET_DIR="$DEEPSTORM_DIR/$E2E_PATH"
94
+ fi
95
+ if [ -d "$TARGET_DIR" ]; then
96
+ echo "📂 切换到 E2E 项目目录: $E2E_PATH"
97
+ cd "$TARGET_DIR"
98
+ else
99
+ echo "❌ E2E 项目目录不存在: $E2E_PATH,请重新运行 /sweep-init"
100
+ exit 1
101
+ fi
102
+ fi
103
+ ```
104
+
105
+ #### 1.2 配置不存在 → 向后兼容检查
106
+
107
+ - **WHEN** `E2E_PATH` 为空
108
+ - **THEN** 检查旧 `.sweep-init` 文件是否存在(从当前目录和 DEEPSTORM_DIR 向上查找):
109
+ ```bash
110
+ if [ -f ".sweep-init" ]; then
111
+ echo "ℹ️ 检测到旧版 .sweep-init 标记。建议运行 /sweep-init 刷新配置。"
112
+ # 继续执行(兼容旧项目)
113
+ elif [ -n "$DEEPSTORM_DIR" ] && [ -f "$DEEPSTORM_DIR/.sweep-init" ]; then
114
+ echo "ℹ️ 检测到旧版 .sweep-init 标记(项目根: $DEEPSTORM_DIR)。建议运行 /sweep-init 刷新配置。"
115
+ else
116
+ echo "❌ 未检测到 E2E 项目。请先运行 /sweep-init 初始化。"
117
+ exit 1
118
+ fi
119
+ ```
65
120
 
66
121
  ---
67
122
 
@@ -162,9 +217,9 @@ node scripts/env-manager.mjs --env staging
162
217
  ]}
163
218
  ```
164
219
 
165
- - `--env staging` → 从 `.env` `BASE_URL_STAGING`
166
- - `--env test` → 从 `.env` `BASE_URL_TEST`
167
- - 不传参 → 使用 `.env` 中的 `DEFAULT_ENV`,未设置则默认 `test`
220
+ - `--env staging` → 从 `settings.json` `sweep.environments.staging` 读取(回退到 `.env`)
221
+ - `--env test` → 从 `settings.json` `sweep.environments.test` 读取(回退到 `.env`)
222
+ - 不传参 → 使用 `settings.json` 的 `sweep.environments.default`(回退到 `.env` `DEFAULT_ENV`),未设置则默认 `test`
168
223
 
169
224
  #### 4.2 设置环境变量
170
225
 
@@ -426,7 +481,7 @@ Flow: L01 - 正常登录成功
426
481
  ## 检查清单
427
482
 
428
483
  - [ ] 前置检查:框架配置已读取(`.deepstorm/settings.json` → `sweep.e2eFramework`)
429
- - [ ] 项目已初始化(`.sweep-init` 存在)
484
+ - [ ] 项目已初始化(读取 settings.json → sweep.e2eProjectPath)
430
485
  - [ ] 执行范围已确定(--all / --path / 交互选择)
431
486
  - [ ] 执行模式已选择(hybrid / native / no-parallel / browser)
432
487
  - [ ] MCP 服务按模式正确启动(available → headless / headed / 不启动)
@@ -27,6 +27,65 @@ function getEnvPath() {
27
27
  function getMcpPath() {
28
28
  return resolve(process.cwd(), '.mcp.json');
29
29
  }
30
+ function getDeepstormSettingsPath() {
31
+ return resolve(process.cwd(), '.deepstorm', 'settings.json');
32
+ }
33
+
34
+ // ── settings.json reading ─────────────────────────────────────────
35
+
36
+ /**
37
+ * Read .deepstorm/settings.json and extract environments config.
38
+ * Returns the sweep.environments object or null if not configured.
39
+ */
40
+ export function readDeepstormEnvironments() {
41
+ const settingsPath = getDeepstormSettingsPath();
42
+ if (!existsSync(settingsPath)) return null;
43
+ try {
44
+ const content = readFileSync(settingsPath, 'utf-8');
45
+ const config = JSON.parse(content);
46
+ return config.sweep?.environments || null;
47
+ } catch {
48
+ return null;
49
+ }
50
+ }
51
+
52
+ /**
53
+ * Read .deepstorm/settings.json and extract e2eProjectPath.
54
+ * Returns the project path string or null if not configured.
55
+ */
56
+ export function readE2eProjectPath() {
57
+ const settingsPath = getDeepstormSettingsPath();
58
+ if (!existsSync(settingsPath)) return null;
59
+ try {
60
+ const content = readFileSync(settingsPath, 'utf-8');
61
+ const config = JSON.parse(content);
62
+ return config.sweep?.e2eProjectPath || null;
63
+ } catch {
64
+ return null;
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Convert settings.json environments format to the same structure
70
+ * that listEnvs() returns from .env, for a unified return format.
71
+ *
72
+ * Input: { test: { baseUrl: "..." }, staging: { baseUrl: "..." }, default: "test" }
73
+ * Output: { envMap: { BASE_URL_TEST: "...", BASE_URL_STAGING: "...", DEFAULT_ENV: "test" },
74
+ * defaultEnv: "test" }
75
+ */
76
+ function deepstormEnvsToEnvMap(envs) {
77
+ if (!envs) return { envMap: {}, defaultEnv: 'test' };
78
+ const envMap = {};
79
+ const defaultEnv = envs.default || 'test';
80
+ for (const [name, config] of Object.entries(envs)) {
81
+ if (name === 'default') continue;
82
+ if (config && config.baseUrl) {
83
+ envMap[`BASE_URL_${name.toUpperCase()}`] = config.baseUrl;
84
+ }
85
+ }
86
+ envMap.DEFAULT_ENV = defaultEnv;
87
+ return { envMap, defaultEnv };
88
+ }
30
89
 
31
90
  // ── .env parsing ──────────────────────────────────────────────────
32
91
 
@@ -68,17 +127,40 @@ export function readDotEnv() {
68
127
 
69
128
  /**
70
129
  * Get the default environment name.
130
+ * Priority: settings.json > .env
71
131
  * Falls back to 'test' if not configured.
72
132
  */
73
133
  export function getDefaultEnv(envMap) {
134
+ const dsEnvs = readDeepstormEnvironments();
135
+ if (dsEnvs && dsEnvs.default) {
136
+ return dsEnvs.default;
137
+ }
74
138
  return envMap.DEFAULT_ENV || 'test';
75
139
  }
76
140
 
77
141
  /**
78
- * List all available environments from .env.
142
+ * List all available environments.
143
+ * Priority: settings.json > .env
79
144
  * Detects BASE_URL_{ENV} patterns and returns their names.
80
145
  */
81
146
  export function listEnvs(envMap) {
147
+ const dsEnvs = readDeepstormEnvironments();
148
+ if (dsEnvs) {
149
+ const envs = [];
150
+ for (const [name, config] of Object.entries(dsEnvs)) {
151
+ if (name === 'default') continue;
152
+ if (config && config.baseUrl) {
153
+ envs.push({
154
+ name: name.toLowerCase(),
155
+ key: `BASE_URL_${name.toUpperCase()}`,
156
+ url: config.baseUrl,
157
+ });
158
+ }
159
+ }
160
+ return envs;
161
+ }
162
+
163
+ // Fallback to .env-based envMap
82
164
  const envs = [];
83
165
  for (const key of Object.keys(envMap)) {
84
166
  const match = key.match(/^BASE_URL_(.+)$/);
@@ -96,13 +178,27 @@ export function listEnvs(envMap) {
96
178
  /**
97
179
  * Resolve target environment config.
98
180
  *
181
+ * Priority: settings.json > .env
182
+ *
99
183
  * @param {string} [envName] - Target environment name (e.g. 'staging', 'test')
100
184
  * @returns {{ env: string, baseUrl: string|null, availableEnvs: Array<{name:string,url:string}> }}
101
185
  *
102
- * If envName is omitted, uses DEFAULT_ENV from .env (or 'test').
186
+ * If envName is omitted, uses DEFAULT_ENV (or 'test').
103
187
  * If the environment is not found, baseUrl is null and availableEnvs lists alternatives.
104
188
  */
105
189
  export function resolveEnv(envName) {
190
+ // Try settings.json first
191
+ const dsEnvs = readDeepstormEnvironments();
192
+ if (dsEnvs) {
193
+ const { envMap, defaultEnv } = deepstormEnvsToEnvMap(dsEnvs);
194
+ const name = envName || defaultEnv;
195
+ const availableEnvs = listEnvs(envMap);
196
+ const baseUrlKey = `BASE_URL_${name.toUpperCase()}`;
197
+ const baseUrl = envMap[baseUrlKey] || null;
198
+ return { env: name, baseUrl, availableEnvs };
199
+ }
200
+
201
+ // Fallback to .env
106
202
  const envMap = readDotEnv();
107
203
  const name = envName || getDefaultEnv(envMap);
108
204
  const availableEnvs = listEnvs(envMap);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deepstorm/cli",
3
- "version": "0.8.0",
3
+ "version": "0.9.0",
4
4
  "description": "DeepStorm CLI — 一键配置项目开发环境",
5
5
  "license": "MIT",
6
6
  "author": "billkang",
@@ -16,7 +16,7 @@
16
16
  "dotenv": "^17.4.2",
17
17
  "handlebars": "^4.7.8",
18
18
  "js-yaml": "^4.1.0",
19
- "@deepstorm/pilot": "^0.8.0"
19
+ "@deepstorm/pilot": "^0.9.0"
20
20
  },
21
21
  "devDependencies": {
22
22
  "@types/node": "^22.0.0",