@deepstorm/cli 0.1.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.
- package/README.md +72 -0
- package/dist/agents/reef-inspect-figma.md +77 -0
- package/dist/agents/reef-review-backend.md.tmpl +112 -0
- package/dist/agents/reef-review-frontend.md.tmpl +78 -0
- package/dist/agents/reef-review-infra.md +47 -0
- package/dist/agents/reef-review-security.md.tmpl +80 -0
- package/dist/agents/reef-scope-analysis.md +64 -0
- package/dist/build-registry.js +375 -0
- package/dist/cli.js +8581 -0
- package/dist/config-schema.json +133 -0
- package/dist/env-examples/context7.env-example +19 -0
- package/dist/env-examples/feishu-wiki.env-example +16 -0
- package/dist/env-examples/figma.env-example +16 -0
- package/dist/env-examples/github.env-example +20 -0
- package/dist/env-examples/jira.env-example +20 -0
- package/dist/hooks/mcp-hook.sh +77 -0
- package/dist/hooks/reef-auto-format.sh.tmpl +72 -0
- package/dist/hooks/reef-block-dangerous.sh +70 -0
- package/dist/hooks/reef-hooks.json +72 -0
- package/dist/hooks/reef-intent-detect.sh +129 -0
- package/dist/hooks/reef-protect-files.sh +55 -0
- package/dist/hooks/reef-run-tests.sh +84 -0
- package/dist/hooks/reef-scope-check.sh +386 -0
- package/dist/hooks/reef-scope-ci.sh +28 -0
- package/dist/hooks/reef-scope-gate.sh +115 -0
- package/dist/hooks/reef-scope-pre-commit.sh.tmpl +28 -0
- package/dist/hooks/reef-scope-setup.sh +204 -0
- package/dist/hooks/reef-scope-split.sh +203 -0
- package/dist/hooks/sweep-hooks.json +14 -0
- package/dist/hooks/sweep-mcp-hook.sh +77 -0
- package/dist/hooks/tide-hooks.json +14 -0
- package/dist/hooks/tide-session-preload.sh +17 -0
- package/dist/mcp/code-hosting/github.json +20 -0
- package/dist/mcp/design-tools/figma.json +19 -0
- package/dist/mcp/docs-reference/context7.json +28 -0
- package/dist/mcp/e2e-testing/playwright.json +13 -0
- package/dist/mcp/knowledge-base/feishu-wiki.json +19 -0
- package/dist/mcp/project-management/jira.json +27 -0
- package/dist/mcp-skills/deepflow-mcp-feishu-wiki-read/SKILL.md +65 -0
- package/dist/mcp-skills/deepflow-mcp-feishu-wiki-write/SKILL.md +63 -0
- package/dist/mcp-skills/deepflow-mcp-figma-read/SKILL.md +98 -0
- package/dist/mcp-skills/deepflow-mcp-github-read/SKILL.md +62 -0
- package/dist/mcp-skills/deepflow-mcp-github-write/SKILL.md +63 -0
- package/dist/mcp-skills/deepflow-mcp-jira-read/SKILL.md +80 -0
- package/dist/mcp-skills/deepflow-mcp-jira-write/SKILL.md +74 -0
- package/dist/mcp-skills/deepflow-mcp-playwright-read/SKILL.md +79 -0
- package/dist/mcp-skills/deepstorm-mcp-feishu-wiki-read/SKILL.md +65 -0
- package/dist/mcp-skills/deepstorm-mcp-feishu-wiki-write/SKILL.md +63 -0
- package/dist/mcp-skills/deepstorm-mcp-figma-read/SKILL.md +98 -0
- package/dist/mcp-skills/deepstorm-mcp-github-read/SKILL.md +62 -0
- package/dist/mcp-skills/deepstorm-mcp-github-write/SKILL.md +63 -0
- package/dist/mcp-skills/deepstorm-mcp-jira-read/SKILL.md +80 -0
- package/dist/mcp-skills/deepstorm-mcp-jira-write/SKILL.md +74 -0
- package/dist/mcp-skills/deepstorm-mcp-playwright-read/SKILL.md +79 -0
- package/dist/registry.json +818 -0
- package/dist/skills/atoll-ops/SKILL.md +46 -0
- package/dist/skills/reef-commit/SKILL.md +127 -0
- package/dist/skills/reef-gen-backend/SKILL.md.tmpl +87 -0
- package/dist/skills/reef-gen-backend/variants/java/steps.md +28 -0
- package/dist/skills/reef-gen-backend/variants/python/steps.md +70 -0
- package/dist/skills/reef-gen-frontend/SKILL.md.tmpl +83 -0
- package/dist/skills/reef-gen-frontend/variants/angular/steps.md +30 -0
- package/dist/skills/reef-harden/EXAMPLES.md +89 -0
- package/dist/skills/reef-harden/SKILL.md +136 -0
- package/dist/skills/reef-pr/SKILL.md +97 -0
- package/dist/skills/reef-review/SKILL.md.tmpl +107 -0
- package/dist/skills/reef-scope/SKILL.md +134 -0
- package/dist/skills/reef-start/SKILL.md.tmpl +562 -0
- package/dist/skills/reef-start/references/jira-start-subagent.md +60 -0
- package/dist/skills/reef-style-backend/SKILL.md.tmpl +134 -0
- package/dist/skills/reef-style-backend/fragments/java/ai/spring-ai/examples/chat-client.md +96 -0
- package/dist/skills/reef-style-backend/fragments/java/ai/spring-ai/examples/rag.md +94 -0
- package/dist/skills/reef-style-backend/fragments/java/ai/spring-ai/examples/structured-output.md +62 -0
- package/dist/skills/reef-style-backend/fragments/java/ai/spring-ai/examples/tool-calling.md +68 -0
- package/dist/skills/reef-style-backend/fragments/java/ai/spring-ai/quick-reference.md +220 -0
- package/dist/skills/reef-style-backend/fragments/java/api-spec/quick-reference.md +148 -0
- package/dist/skills/reef-style-backend/fragments/java/db-migration/liquibase/examples/database-migration.md +131 -0
- package/dist/skills/reef-style-backend/fragments/java/db-migration/liquibase/quick-reference.md +103 -0
- package/dist/skills/reef-style-backend/fragments/java/dependency-management/quick-reference.md +119 -0
- package/dist/skills/reef-style-backend/fragments/java/exception-handling/examples/error-code-enum.md +101 -0
- package/dist/skills/reef-style-backend/fragments/java/exception-handling/quick-reference.md +181 -0
- package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/examples/controller.md +95 -0
- package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/examples/dto-mapper.md +121 -0
- package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/examples/infrastructure.md +179 -0
- package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/examples/service-entity.md +202 -0
- package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/examples/testing.md +107 -0
- package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/quick-reference.md +83 -0
- package/dist/skills/reef-style-backend/fragments/java/orm/hibernate/quick-reference.md +150 -0
- package/dist/skills/reef-style-backend/fragments/java/security-redlines/quick-reference.md +197 -0
- package/dist/skills/reef-style-backend/fragments/java/test/data-jpa-test/examples/user-repository-test.md +69 -0
- package/dist/skills/reef-style-backend/fragments/java/test/data-jpa-test/quick-reference.md +101 -0
- package/dist/skills/reef-style-backend/fragments/java/test/junit5/examples/user-service-test.md +61 -0
- package/dist/skills/reef-style-backend/fragments/java/test/junit5/quick-reference.md +100 -0
- package/dist/skills/reef-style-backend/fragments/java/test/spring-mvc-test/examples/user-controller-test.md +61 -0
- package/dist/skills/reef-style-backend/fragments/java/test/spring-mvc-test/quick-reference.md +85 -0
- package/dist/skills/reef-style-backend/fragments/java/test/spring-service-test/examples/user-service-integration-test.md +56 -0
- package/dist/skills/reef-style-backend/fragments/java/test/spring-service-test/quick-reference.md +83 -0
- package/dist/skills/reef-style-backend/fragments/python/alembic-migration/quick-reference.md +77 -0
- package/dist/skills/reef-style-backend/fragments/python/api-spec/quick-reference.md +164 -0
- package/dist/skills/reef-style-backend/fragments/python/dependency-management/quick-reference.md +139 -0
- package/dist/skills/reef-style-backend/fragments/python/exception-handling/quick-reference.md +177 -0
- package/dist/skills/reef-style-backend/fragments/python/fastapi-quick-reference/quick-reference.md +101 -0
- package/dist/skills/reef-style-backend/fragments/python/langchain/quick-reference.md +135 -0
- package/dist/skills/reef-style-backend/fragments/python/pytest-testing/quick-reference.md +111 -0
- package/dist/skills/reef-style-backend/fragments/python/ruff-mypy-toolchain/quick-reference.md +83 -0
- package/dist/skills/reef-style-backend/fragments/python/security-redlines/quick-reference.md +207 -0
- package/dist/skills/reef-style-backend/fragments/python/sqlalchemy-orm/quick-reference.md +91 -0
- package/dist/skills/reef-style-backend/variants/java/examples/code-wrapping.md +227 -0
- package/dist/skills/reef-style-backend/variants/java/examples/contributor-pattern.md +97 -0
- package/dist/skills/reef-style-backend/variants/java/quick-reference.md +117 -0
- package/dist/skills/reef-style-backend/variants/python/examples/crud-router.md +71 -0
- package/dist/skills/reef-style-backend/variants/python/examples/pydantic-schema.md +45 -0
- package/dist/skills/reef-style-backend/variants/python/examples/pytest-fixture.md +60 -0
- package/dist/skills/reef-style-backend/variants/python/examples/sqlalchemy-model.md +31 -0
- package/dist/skills/reef-style-backend/variants/python/quick-reference.md +111 -0
- package/dist/skills/reef-style-frontend/SKILL.md.tmpl +70 -0
- package/dist/skills/reef-style-frontend/fragments/css/tailwind/quick-reference.md +79 -0
- package/dist/skills/reef-style-frontend/fragments/test/vitest/examples/testing.md +150 -0
- package/dist/skills/reef-style-frontend/fragments/test/vitest/quick-reference.md +141 -0
- package/dist/skills/reef-style-frontend/fragments/ts-config/strict/quick-reference.md +112 -0
- package/dist/skills/reef-style-frontend/fragments/ui-lib/primeng/examples/ui-components.md +134 -0
- package/dist/skills/reef-style-frontend/fragments/ui-lib/primeng/quick-reference.md +66 -0
- package/dist/skills/reef-style-frontend/variants/angular/examples/code-wrapping.md +252 -0
- package/dist/skills/reef-style-frontend/variants/angular/examples/component-types-pipes.md +56 -0
- package/dist/skills/reef-style-frontend/variants/angular/examples/entity-types.md +100 -0
- package/dist/skills/reef-style-frontend/variants/angular/examples/forms-layer.md +119 -0
- package/dist/skills/reef-style-frontend/variants/angular/examples/service-routing.md +95 -0
- package/dist/skills/reef-style-frontend/variants/angular/quick-reference.md +100 -0
- package/dist/skills/reef-testcase/SKILL.md +138 -0
- package/dist/skills/reef-testcase/references/coverage-dimensions.md +99 -0
- package/dist/skills/reef-testcase/references/test-case-template.md +63 -0
- package/dist/skills/sweep-init/SKILL.md +286 -0
- package/dist/skills/sweep-init/scripts/flow-selector.mjs +305 -0
- package/dist/skills/sweep-plan/SKILL.md.tmpl +312 -0
- package/dist/skills/sweep-plan/references/test-flow-template.md +46 -0
- package/dist/skills/sweep-run/SKILL.md +437 -0
- package/dist/skills/sweep-run/scripts/env-manager.mjs +208 -0
- package/dist/skills/sweep-run/scripts/flow-parser.mjs +329 -0
- package/dist/skills/sweep-run/scripts/flow-selector.mjs +483 -0
- package/dist/skills/sweep-run/scripts/mcp-manager.mjs +208 -0
- package/dist/skills/sweep-run/scripts/spec-compiler.mjs +303 -0
- package/dist/skills/tide-discuss/SKILL.md.tmpl +449 -0
- package/dist/skills/tide-discuss/references/checklists.md +88 -0
- package/dist/skills/tide-discuss/references/data-format.md +237 -0
- package/dist/skills/tide-discuss/references/prd-template.md +134 -0
- package/dist/skills/tide-discuss/references/publish-flow.md +167 -0
- package/dist/skills/tide-discuss/references/role-prompts.md +105 -0
- package/package.json +38 -0
|
@@ -0,0 +1,437 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: sweep-run
|
|
3
|
+
description: 执行 .flow.md 测试意图文档。根据项目配置的 E2E 框架(如 Playwright),默认使用混合执行模式(Hybrid Execution):先通过预编译的 .flow.spec.ts 原生 Playwright 执行获得 ~20x 加速,失败后由 AI 驱动诊断和自动修复。支持交互式模块选择、直接参数指定、多环境切换、批量执行、多 Agent 并行。
|
|
4
|
+
allowed-tools: Read, Write, Bash, WebFetch, Agent
|
|
5
|
+
deepstorm:
|
|
6
|
+
tool: sweep
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Sweep Flow Run — 测试流程执行
|
|
10
|
+
|
|
11
|
+
读取 .flow.md 测试意图文档,根据项目配置的 E2E 框架,通过对应 MCP 服务逐步骤操作浏览器执行测试流程,验证每个步骤的预期结果。
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## 使用方式
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
/sweep-run # 交互式选择(默认:混合执行 - 原生 Playwright + AI 自愈)
|
|
19
|
+
/sweep-run --all # 全量执行(默认:混合执行)
|
|
20
|
+
/sweep-run --all --browser # 全量执行 + 打开浏览器窗口逐步骤调试
|
|
21
|
+
/sweep-run --all --no-parallel # 全量执行 + 批量模式(单线程,仅适用于原生执行)
|
|
22
|
+
/sweep-run --all --native # 全量执行 + 纯原生模式(失败不自愈,仅供调试)
|
|
23
|
+
/sweep-run --path user-system # 按模块路径
|
|
24
|
+
/sweep-run flows/login.flow.md # 单文件
|
|
25
|
+
/sweep-run flows/login.flow.md --flow L02 # 单 Flow
|
|
26
|
+
/sweep-run --env staging # 指定测试环境
|
|
27
|
+
/sweep-run --text # 强制使用文本输入模式选择(不启动 TTY checkbox)
|
|
28
|
+
/sweep-run --all --text # 全量 + 文本模式
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### 执行模式说明
|
|
32
|
+
|
|
33
|
+
| 模式 | 参数 | MCP 模式 | 说明 | 加速比 |
|
|
34
|
+
|------|------|---------|------|--------|
|
|
35
|
+
| **混合执行(默认)** | (无)或 `--fast` | headless | 预编译 `.flow.spec.ts` → 原生 Playwright 执行(`--reporter=line`);失败 AI 自愈 | ~10-20x |
|
|
36
|
+
| 纯原生(不自愈) | `--native` | 不启动 | 只用原生 Playwright 执行,失败不自愈 | ~20x |
|
|
37
|
+
| 批量(单线程) | `--no-parallel` | headless | 原生执行 + 单线程顺序运行 | ~10x |
|
|
38
|
+
| 浏览器调试 | `--browser` | headed | AI 逐步骤 MCP 调用 + 浏览器窗口可见 | 1x |
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## 工作流
|
|
43
|
+
|
|
44
|
+
### 前置检查:读取框架配置
|
|
45
|
+
|
|
46
|
+
在执行前,先读取 `.claude/settings.json` 中 `deepstorm.sweep.e2eFramework` 配置,确定当前项目使用的 E2E 框架。
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
node scripts/env-manager.mjs --framework
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
```json
|
|
53
|
+
{"framework":"playwright","source":"settings"}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
- **playwright** → 通过 Playwright MCP(`deepstorm-playwright`)执行浏览器操作
|
|
57
|
+
- **null** → 提示"E2E 框架未配置,请运行 deepstorm setup 重新配置"并退出
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
### 步骤 1:检查初始化状态
|
|
62
|
+
|
|
63
|
+
- **WHEN** `.sweep-init` 不存在
|
|
64
|
+
- **THEN** 提示"当前目录尚未初始化为 Sweep 测试项目。请先运行 /sweep-init 初始化。"并退出
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
### 步骤 2:确定执行范围
|
|
69
|
+
|
|
70
|
+
根据用户输入参数或交互选择,确定要执行的 .flow.md 文件列表。
|
|
71
|
+
|
|
72
|
+
#### 2.1 交互模式(无参数)
|
|
73
|
+
|
|
74
|
+
**必须运行** `node scripts/flow-selector.mjs` 进行选择。
|
|
75
|
+
|
|
76
|
+
1. 先检查 `scripts/flow-selector.mjs` 是否存在
|
|
77
|
+
2. 运行 `node scripts/flow-selector.mjs`
|
|
78
|
+
3. 脚本自动检测 TTY 可用性:
|
|
79
|
+
- **TTY 可用** → 展示 `@inquirer/checkbox` 勾选列表(空格勾选、回车确认、a=全选)
|
|
80
|
+
- **TTY 不可用** → 自动回退到 **readline 文本输入模式**(序号选择、逗号分隔、输入 a 全选)
|
|
81
|
+
- 也可通过 `--text` 强制文本模式、`--tui` 强制 checkbox 模式
|
|
82
|
+
4. 读取 `.sweep-selection.json` 获取选中结果
|
|
83
|
+
5. 列出将要执行的文件,让用户确认后开始执行(**以下为示例格式,实际内容根据项目动态生成**)
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
选中的模块:user-system, tasks
|
|
87
|
+
|
|
88
|
+
将要执行:
|
|
89
|
+
flows/user-system/register.flow.md(3 个 Flow)
|
|
90
|
+
flows/tasks/task-crud.flow.md(4 个 Flow)
|
|
91
|
+
|
|
92
|
+
确认开始执行?(y/n)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
#### 2.2 直接参数模式
|
|
96
|
+
|
|
97
|
+
| 参数 | 行为 | 示例 |
|
|
98
|
+
|------|------|------|
|
|
99
|
+
| `--all` | 执行 flows/ 下所有 .flow.md | `/sweep-run --all` |
|
|
100
|
+
| `--path {module}` | 执行指定模块路径下的所有 .flow | `/sweep-run --path user-system/login` |
|
|
101
|
+
| `{file-path}` | 执行指定文件 | `/sweep-run flows/login.flow.md` |
|
|
102
|
+
| `{file-path} --flow {ID}` | 只执行文件中的某个 Flow | `/sweep-run login.flow.md --flow L02` |
|
|
103
|
+
| `--env {env}` | 切换目标环境 | `/sweep-run --all --env staging` |
|
|
104
|
+
| `--browser` | 打开浏览器窗口逐步骤调试 | `/sweep-run --all --browser` |
|
|
105
|
+
| `--no-parallel` | 使用批量但不并行 | `/sweep-run --all --no-parallel` |
|
|
106
|
+
| `--text` | 强制文本输入模式选择(跳过 TTY checkbox) | `/sweep-run --text` |
|
|
107
|
+
| `--tui` | 强制 TUI checkbox 模式 | `/sweep-run --tui` |
|
|
108
|
+
|
|
109
|
+
#### 2.3 文件不存在处理
|
|
110
|
+
|
|
111
|
+
如指定的文件或 --path 不存在:
|
|
112
|
+
- 提示"指定路径不存在"
|
|
113
|
+
- 读取 topology.yaml 展示可用模块
|
|
114
|
+
- 引导用户重新选择
|
|
115
|
+
|
|
116
|
+
#### 2.4 自动编译(含 freshness 检查)
|
|
117
|
+
|
|
118
|
+
对每个待执行文件运行编译器,由脚本自动判断是否需要重新编译(若 `.flow.spec.ts` 已是最新则跳过):
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
node scripts/spec-compiler.mjs <path-to-flow.md>
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
脚本输出 `⏭ SKIP`(跳过)或 `✅ Generated`(新编译)。
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
### 步骤 3:解析 .flow.md
|
|
129
|
+
|
|
130
|
+
对于每个待执行的 .flow.md 文件,按以下结构解析。**注意:混合执行模式下解析由 `flow-parser.mjs` 自动完成,AI 仅需在编译失败或 `--browser` 模式下人工阅读理解。**
|
|
131
|
+
|
|
132
|
+
```
|
|
133
|
+
文件头:功能名称、来源、创建时间
|
|
134
|
+
场景清单:[{ID, 场景, 类型, 优先级}, ...]
|
|
135
|
+
Flows数组:
|
|
136
|
+
[{ID, 标题, 前置条件, 步骤: [{序号, 描述, 验证点}], 环境要求}]
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
提取每个 Flow 中的:
|
|
140
|
+
- 前置条件(描述性文本,用于理解上下文)
|
|
141
|
+
- 步骤列表(有序操作 + 验证点)
|
|
142
|
+
- 环境要求(用于 --env 默认值)
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
### 步骤 4:配置目标环境
|
|
147
|
+
|
|
148
|
+
#### 4.1 读取 --env 参数
|
|
149
|
+
|
|
150
|
+
通过 `env-manager.mjs` 解析目标环境:
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
node scripts/env-manager.mjs --env staging
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
输出示例:
|
|
157
|
+
|
|
158
|
+
```json
|
|
159
|
+
{"env":"staging","baseUrl":"https://staging.example.com","availableEnvs":[
|
|
160
|
+
{"name":"test","key":"BASE_URL_TEST","url":"https://test.example.com"},
|
|
161
|
+
{"name":"staging","key":"BASE_URL_STAGING","url":"https://staging.example.com"}
|
|
162
|
+
]}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
- `--env staging` → 从 `.env` 读 `BASE_URL_STAGING`
|
|
166
|
+
- `--env test` → 从 `.env` 读 `BASE_URL_TEST`
|
|
167
|
+
- 不传参 → 使用 `.env` 中的 `DEFAULT_ENV`,未设置则默认 `test`
|
|
168
|
+
|
|
169
|
+
#### 4.2 设置环境变量
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
export BASE_URL=$(node scripts/env-manager.mjs --env test --print 2>/dev/null | tail -1 | cut -d= -f2-)
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
或从 JSON 结果提取:
|
|
176
|
+
```bash
|
|
177
|
+
BASE_URL=$(node scripts/env-manager.mjs --env test | node -pe "JSON.parse(require('fs').readFileSync('/dev/stdin','utf8')).baseUrl")
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
#### 4.3 环境不存在处理
|
|
181
|
+
|
|
182
|
+
如果 `baseUrl` 为 `null`(指定的环境在 `.env` 中找不到对应配置):
|
|
183
|
+
|
|
184
|
+
- 提示"未找到 [{env}] 环境的 baseURL 配置"
|
|
185
|
+
- 列出 `availableEnvs` 中已有的环境
|
|
186
|
+
- 引导用户选择已有环境或退出
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
### 步骤 5:MCP 服务管理
|
|
191
|
+
|
|
192
|
+
在执行前确认配置存在并按执行模式启动 MCP 服务。
|
|
193
|
+
|
|
194
|
+
#### 5.1 确认 MCP 可用性
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
node scripts/env-manager.mjs --check-mcp
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
输出示例:`{"available":true,"mcpName":"deepstorm-playwright"}`
|
|
201
|
+
|
|
202
|
+
- **`available: true`** → MCP 服务已配置,继续执行
|
|
203
|
+
- **`available: false`** → 提示"对应框架的 MCP 服务未配置。请运行 deepstorm setup 并选择对应 MCP 服务。"并退出
|
|
204
|
+
|
|
205
|
+
#### 5.2 启动 MCP 服务
|
|
206
|
+
|
|
207
|
+
通过 `mcp-manager.mjs` 根据执行模式(见执行模式说明表格)自动管理:
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
# headless 模式(默认):MCP 仅用于自愈诊断
|
|
211
|
+
node scripts/mcp-manager.mjs --mode=headless
|
|
212
|
+
|
|
213
|
+
# headed 模式:浏览器调试
|
|
214
|
+
node scripts/mcp-manager.mjs --mode=headed
|
|
215
|
+
|
|
216
|
+
# 跳过 MCP(--native 模式)
|
|
217
|
+
node scripts/mcp-manager.mjs --mode=skip
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
输出 JSON:
|
|
221
|
+
```json
|
|
222
|
+
{"action":"started","mode":"headless","pid":12345,"port":54321}
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
`action` 取值:`started`(新启动)/ `already-ok`(已在正确模式)/ `switched`(切换后重启)/ `skipped`(跳过)。
|
|
226
|
+
|
|
227
|
+
#### 5.3 停止 / 检查状态
|
|
228
|
+
|
|
229
|
+
```bash
|
|
230
|
+
node scripts/mcp-manager.mjs --stop
|
|
231
|
+
node scripts/mcp-manager.mjs --status
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
### 步骤 6:执行测试
|
|
237
|
+
|
|
238
|
+
根据选择的执行模式执行测试流程。
|
|
239
|
+
|
|
240
|
+
#### 6.1 混合执行(默认)
|
|
241
|
+
|
|
242
|
+
**适用场景:** 日常执行,兼顾速度和容错。
|
|
243
|
+
|
|
244
|
+
```mermaid
|
|
245
|
+
flowchart TD
|
|
246
|
+
A["MCP 已启动为 headless 模式"] --> B{"检查 .flow.spec.ts 是否最新?"}
|
|
247
|
+
B -->|"最新"| C["npx playwright test --reporter=line<br>(原生执行,毫秒级启动,实时显示进度)"]
|
|
248
|
+
B -->|"过时或不存在"| D["解析器 + 编译器<br>重新生成 .spec.ts"]
|
|
249
|
+
D --> C
|
|
250
|
+
C --> E{"全部通过?"}
|
|
251
|
+
E -->|"OK"| F["报告通过<br>(执行模式:native)"]
|
|
252
|
+
E -->|"失败"| G["AI 自愈引擎介入"]
|
|
253
|
+
G --> H["读取 Playwright 失败日志 + 截图"]
|
|
254
|
+
H --> I["通过 Playwright MCP(headless)<br>打开浏览器诊断"]
|
|
255
|
+
I --> J{"元素变了还是真 Bug?"}
|
|
256
|
+
J -->|"元素选择器失效"| K["更新 .flow.spec.ts 定位器<br>添加 auto-repaired 注释"]
|
|
257
|
+
K --> L["重跑确认"]
|
|
258
|
+
L --> M{"修复有效?"}
|
|
259
|
+
M -->|"是"| N["自愈成功"]
|
|
260
|
+
M -->|"否(>=3次失败)"| O["自愈放弃"]
|
|
261
|
+
J -->|"真 Bug"| P["保留失败报告<br>(确认 Bug)"]
|
|
262
|
+
G --> Q["输出最终报告<br>(执行模式:hybrid)"]
|
|
263
|
+
N --> Q
|
|
264
|
+
O --> Q
|
|
265
|
+
P --> Q
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
**并行策略:** 依赖 Playwright 内置多 worker 并行(默认 = CPU 核数)。**所有 spec 文件统一通过单个 `npx playwright test` 命令执行**,不得拆分为多 Agent 或后台任务。Playwright 自身的 worker 池已实现了跨文件的并行执行,速度与手动拆分相当,且用户能实时看到每行测试结果输出。
|
|
269
|
+
|
|
270
|
+
**执行命令:** 必须使用 `--reporter=line,json`(逗号分隔,两个 reporter 同时启用),line 用于终端实时显示每个测试的开始和结果,json 用于程序化解析测试结果:
|
|
271
|
+
|
|
272
|
+
```bash
|
|
273
|
+
# 前台执行,不跑后台,确保用户能看到实时输出
|
|
274
|
+
npx playwright test --reporter=line,json <spec-path>
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
> ⚠️ **关键约束:** 必须直接在前台运行 `npx playwright test`,不得使用 `run_in_background: true` 或任何形式的后台/异步执行方式。用户需要看到 Playwright 逐条输出的测试进度(`[1/7] [chromium] › ... ✓`)。
|
|
278
|
+
|
|
279
|
+
> **注意:** CLI 上 `--reporter=json` 与 line 都输出到 stdout,解析 JSON 时需从末尾提取。如需分离输出,建议在 `playwright.config.ts` 中配置:`reporter: [['line'], ['json', { outputFile: 'results.json' }]]`
|
|
280
|
+
|
|
281
|
+
**自愈更新说明:** 当 AI 更新了 `.flow.spec.ts` 中的定位器后,会在文件中添加 `// auto-repaired: {timestamp}` 注释记录旧→新选择器的变化。
|
|
282
|
+
|
|
283
|
+
#### 6.2 纯原生模式(--native)
|
|
284
|
+
|
|
285
|
+
与混合执行相同使用 `.flow.spec.ts` 原生执行,但失败后不自愈。**注意:** 不启动 MCP 服务。
|
|
286
|
+
|
|
287
|
+
#### 6.3 浏览器调试模式(--browser)
|
|
288
|
+
|
|
289
|
+
浏览器窗口可见(headed),对每个 Flow 逐步骤执行。
|
|
290
|
+
|
|
291
|
+
```mermaid
|
|
292
|
+
flowchart LR
|
|
293
|
+
PRE["MCP 已启动为 headed 模式"] --> S1["步骤 1/N:执行浏览器操作<br>(导航 / 点击 / 输入等)"]
|
|
294
|
+
S1 --> V{"验证点"}
|
|
295
|
+
V -->|"通过"| NEXT["继续下一步"]
|
|
296
|
+
V -->|"失败"| FAIL["记录失败,继续"]
|
|
297
|
+
NEXT --> S2["步骤 2/N:..."]
|
|
298
|
+
S2 --> V
|
|
299
|
+
FAIL --> S2
|
|
300
|
+
S2 --> STOP["执行完成"]
|
|
301
|
+
STOP --> POST["MCP 恢复 headless 模式"]
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
**前置条件:** MCP 为 headed 模式(浏览器窗口可见)→ 打开浏览器 → 导航到 baseURL → 确保页面正常加载。
|
|
305
|
+
|
|
306
|
+
**操作类型:**
|
|
307
|
+
|
|
308
|
+
| 操作 | MCP 调用 | 验证方式 |
|
|
309
|
+
|------|---------|---------|
|
|
310
|
+
| 导航 | navigate/goto | 检查页面标题/URL |
|
|
311
|
+
| 点击 | click | 检查预期元素/状态变化 |
|
|
312
|
+
| 输入 | fill | 检查输入回显或后续页面状态 |
|
|
313
|
+
| 选择 | select | 检查选项是否生效 |
|
|
314
|
+
| 等待 | waitFor | 检查超时或条件满足 |
|
|
315
|
+
|
|
316
|
+
**执行完成后:** 杀掉 headed 进程,以 `--headless` 重启恢复默认模式。
|
|
317
|
+
|
|
318
|
+
#### 6.4 验证点检查
|
|
319
|
+
|
|
320
|
+
每个验证点对应一个断言检查。验证点失败则记录失败步骤,继续执行后续步骤。全部执行完成后汇总失败信息。**不中断执行。**
|
|
321
|
+
|
|
322
|
+
---
|
|
323
|
+
|
|
324
|
+
### 步骤 7:AI 自愈引擎
|
|
325
|
+
|
|
326
|
+
当混合执行的原生 Playwright 测试出现失败时,AI 自愈引擎介入诊断和修复。
|
|
327
|
+
|
|
328
|
+
**触发条件:** 仅默认模式(混合执行),原生执行有失败时自动触发。`--native` 不自愈,`--browser` 不经过原生执行。
|
|
329
|
+
|
|
330
|
+
**诊断流程:**
|
|
331
|
+
|
|
332
|
+
1. 读取 Playwright 失败日志(定位器、错误类型)
|
|
333
|
+
2. 提取失败上下文(失败的 test 块名称、步骤和验证点)
|
|
334
|
+
3. 通过 Playwright MCP(headless)打开浏览器诊断
|
|
335
|
+
4. AI 对比实际页面元素与 `.flow.spec.ts` 中的定位器
|
|
336
|
+
|
|
337
|
+
**判定与处理:**
|
|
338
|
+
|
|
339
|
+
| 判定结果 | 操作 | 报告中标注 |
|
|
340
|
+
|----------|------|-----------|
|
|
341
|
+
| **元素选择器失效**(元素存在但 CSS class/data-testid 变化) | 更新定位器;添加 `// auto-repaired: {timestamp} - {old} -> {new}` 注释;**重跑**确认 | 自愈成功 |
|
|
342
|
+
| **数据累积**(多次运行导致同名元素重复,`.first()`/`.last()` 选错目标) | 通过 `page.evaluate()` 调用 API 清理历史残留数据后重跑 | 自愈成功 |
|
|
343
|
+
| **真 Bug**(元素不存在、页面行为不符合预期) | 不修改 spec 文件 | 确认 Bug |
|
|
344
|
+
| **修复无效**(更新后重跑仍然失败) | 放弃修复 | 自愈放弃 |
|
|
345
|
+
|
|
346
|
+
**循环保护:** 同一 Flow 最多重试 **3 次**,超限则停止并保留最后一次失败报告。
|
|
347
|
+
|
|
348
|
+
---
|
|
349
|
+
|
|
350
|
+
### 步骤 8:输出测试报告
|
|
351
|
+
|
|
352
|
+
#### 8.1 终端实时输出
|
|
353
|
+
|
|
354
|
+
```
|
|
355
|
+
[14:30:01] 正在执行:user-system/login.flow.md
|
|
356
|
+
------------------------------------------
|
|
357
|
+
Flow: L01 - 正常登录成功
|
|
358
|
+
|
|
359
|
+
OK 步骤 1/4:打开登录页面
|
|
360
|
+
-> 验证:页面加载完成 v
|
|
361
|
+
|
|
362
|
+
OK 步骤 2/4:输入邮箱
|
|
363
|
+
-> 验证:输入框显示 v
|
|
364
|
+
|
|
365
|
+
X 步骤 3/4:点击登录按钮
|
|
366
|
+
-> 验证:URL 跳转到 /dashboard x
|
|
367
|
+
期望:/dashboard
|
|
368
|
+
实际:/login
|
|
369
|
+
|
|
370
|
+
> 步骤 4/4:跳过(前置步骤失败)
|
|
371
|
+
|
|
372
|
+
------------------------------------------
|
|
373
|
+
汇总:1 flow, 1/3 通过, 1 失败, 1 跳过
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
#### 8.2 报告持久化
|
|
377
|
+
|
|
378
|
+
执行完成后,将完整报告写入 `flows/reports/{filename}-{timestamp}.report.md`:
|
|
379
|
+
|
|
380
|
+
```markdown
|
|
381
|
+
# 测试执行报告
|
|
382
|
+
|
|
383
|
+
**文件:** flows/user-system/login.flow.md
|
|
384
|
+
**执行时间:** 2026-06-13 14:30
|
|
385
|
+
**目标环境:** test (https://test.example.com)
|
|
386
|
+
**执行模式:** hybrid(原生执行 + AI 自愈修复)
|
|
387
|
+
|
|
388
|
+
---
|
|
389
|
+
|
|
390
|
+
## Flow: L01 - 正常登录成功 X
|
|
391
|
+
|
|
392
|
+
| 步骤 | 操作 | 验证 | 结果 |
|
|
393
|
+
|------|------|------|------|
|
|
394
|
+
| 1/4 | 打开登录页面 | 页面加载完成 | OK |
|
|
395
|
+
| 2/4 | 输入邮箱 | 输入框显示 | OK |
|
|
396
|
+
| 3/4 | 点击登录按钮 | URL 跳转 /dashboard | X |
|
|
397
|
+
| 4/4 | (跳过) | - | > |
|
|
398
|
+
|
|
399
|
+
### 失败详情
|
|
400
|
+
|
|
401
|
+
**步骤 3/4:** 点击登录按钮
|
|
402
|
+
- 期望:URL 跳转到 /dashboard
|
|
403
|
+
- 实际:URL 保持在 /login
|
|
404
|
+
- 可能原因:登录失败或页面未正确响应
|
|
405
|
+
|
|
406
|
+
---
|
|
407
|
+
|
|
408
|
+
## 汇总
|
|
409
|
+
|
|
410
|
+
| 项目 | 值 |
|
|
411
|
+
|------|-----|
|
|
412
|
+
| 总 Flow 数 | 1 |
|
|
413
|
+
| 总步骤数 | 3 |
|
|
414
|
+
| 通过 | 2 |
|
|
415
|
+
| 失败 | 1 |
|
|
416
|
+
| 跳过 | 1 |
|
|
417
|
+
| 通过率 | 66% |
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
#### 8.3 报告文件名格式
|
|
421
|
+
|
|
422
|
+
`{flow-file-name}-{YYYYMMDD}-{HHmm}.report.md`,示例:`login-20260613-1430.report.md`
|
|
423
|
+
|
|
424
|
+
---
|
|
425
|
+
|
|
426
|
+
## 检查清单
|
|
427
|
+
|
|
428
|
+
- [ ] 前置检查:框架配置已读取 `.claude/settings.json`
|
|
429
|
+
- [ ] 项目已初始化(`.sweep-init` 存在)
|
|
430
|
+
- [ ] 执行范围已确定(--all / --path / 交互选择)
|
|
431
|
+
- [ ] 执行模式已选择(hybrid / native / no-parallel / browser)
|
|
432
|
+
- [ ] MCP 服务按模式正确启动(available → headless / headed / 不启动)
|
|
433
|
+
- [ ] Freshness 检查:`.flow.spec.ts` 已是最新或重新编译
|
|
434
|
+
- [ ] 目标环境已配置(--env 或默认)
|
|
435
|
+
- [ ] 执行完成 + AI 自愈(如有失败)已执行
|
|
436
|
+
- [ ] 测试报告已输出(终端 + `flows/reports/*.report.md`)
|
|
437
|
+
- [ ] 报告中记录了执行模式与总用时
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Env Manager — 测试环境配置管理
|
|
5
|
+
*
|
|
6
|
+
* 读取 .env 文件,解析目标环境的 BASE_URL,输出结构化结果。
|
|
7
|
+
* 支持检查 E2E 框架配置和 MCP 可用性。
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* node env-manager.mjs --env test # 解析指定环境
|
|
11
|
+
* node env-manager.mjs --env test --print # 输出 export 语句
|
|
12
|
+
* node env-manager.mjs --framework # 读取 E2E 框架配置
|
|
13
|
+
* node env-manager.mjs --check-mcp # 检查 MCP 服务可用性
|
|
14
|
+
* node env-manager.mjs --list # 列出所有可用环境
|
|
15
|
+
*
|
|
16
|
+
* import { resolveEnv, listEnvs, readFramework, checkMcpAvailable } from './env-manager.mjs'
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
20
|
+
import { resolve } from 'node:path';
|
|
21
|
+
|
|
22
|
+
// ── Path helpers (lazy, respect process.cwd() changes) ────────────
|
|
23
|
+
|
|
24
|
+
function getEnvPath() {
|
|
25
|
+
return resolve(process.cwd(), '.env');
|
|
26
|
+
}
|
|
27
|
+
function getSettingsPath() {
|
|
28
|
+
return resolve(process.cwd(), '.claude/settings.json');
|
|
29
|
+
}
|
|
30
|
+
function getMcpPath() {
|
|
31
|
+
return resolve(process.cwd(), '.mcp.json');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// ── .env parsing ──────────────────────────────────────────────────
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Parse .env file content into a key-value map.
|
|
38
|
+
* Handles `KEY=VALUE` lines, ignores comments and blank lines.
|
|
39
|
+
*/
|
|
40
|
+
export function parseDotEnv(content) {
|
|
41
|
+
const env = {};
|
|
42
|
+
for (const line of content.split('\n')) {
|
|
43
|
+
const trimmed = line.trim();
|
|
44
|
+
if (!trimmed || trimmed.startsWith('#')) continue;
|
|
45
|
+
const sepIdx = trimmed.indexOf('=');
|
|
46
|
+
if (sepIdx === -1) continue;
|
|
47
|
+
const key = trimmed.slice(0, sepIdx).trim();
|
|
48
|
+
let value = trimmed.slice(sepIdx + 1).trim();
|
|
49
|
+
// Strip surrounding quotes
|
|
50
|
+
if ((value.startsWith('"') && value.endsWith('"')) ||
|
|
51
|
+
(value.startsWith("'") && value.endsWith("'"))) {
|
|
52
|
+
value = value.slice(1, -1);
|
|
53
|
+
}
|
|
54
|
+
env[key] = value;
|
|
55
|
+
}
|
|
56
|
+
return env;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Read and parse .env file. Returns empty object if file doesn't exist.
|
|
61
|
+
*/
|
|
62
|
+
export function readDotEnv() {
|
|
63
|
+
if (!existsSync(getEnvPath())) return {};
|
|
64
|
+
try {
|
|
65
|
+
const content = readFileSync(getEnvPath(), 'utf-8');
|
|
66
|
+
return parseDotEnv(content);
|
|
67
|
+
} catch {
|
|
68
|
+
return {};
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Get the default environment name.
|
|
74
|
+
* Falls back to 'test' if not configured.
|
|
75
|
+
*/
|
|
76
|
+
export function getDefaultEnv(envMap) {
|
|
77
|
+
return envMap.DEFAULT_ENV || 'test';
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* List all available environments from .env.
|
|
82
|
+
* Detects BASE_URL_{ENV} patterns and returns their names.
|
|
83
|
+
*/
|
|
84
|
+
export function listEnvs(envMap) {
|
|
85
|
+
const envs = [];
|
|
86
|
+
for (const key of Object.keys(envMap)) {
|
|
87
|
+
const match = key.match(/^BASE_URL_(.+)$/);
|
|
88
|
+
if (match) {
|
|
89
|
+
envs.push({
|
|
90
|
+
name: match[1].toLowerCase(),
|
|
91
|
+
key: key,
|
|
92
|
+
url: envMap[key],
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return envs;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Resolve target environment config.
|
|
101
|
+
*
|
|
102
|
+
* @param {string} [envName] - Target environment name (e.g. 'staging', 'test')
|
|
103
|
+
* @returns {{ env: string, baseUrl: string|null, availableEnvs: Array<{name:string,url:string}> }}
|
|
104
|
+
*
|
|
105
|
+
* If envName is omitted, uses DEFAULT_ENV from .env (or 'test').
|
|
106
|
+
* If the environment is not found, baseUrl is null and availableEnvs lists alternatives.
|
|
107
|
+
*/
|
|
108
|
+
export function resolveEnv(envName) {
|
|
109
|
+
const envMap = readDotEnv();
|
|
110
|
+
const name = envName || getDefaultEnv(envMap);
|
|
111
|
+
const availableEnvs = listEnvs(envMap);
|
|
112
|
+
|
|
113
|
+
const baseUrlKey = `BASE_URL_${name.toUpperCase()}`;
|
|
114
|
+
const baseUrl = envMap[baseUrlKey] || null;
|
|
115
|
+
|
|
116
|
+
return { env: name, baseUrl, availableEnvs };
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// ── Framework config ──────────────────────────────────────────────
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Read E2E framework config from .claude/settings.json.
|
|
123
|
+
*
|
|
124
|
+
* @returns {{ framework: string|null, source: string }}
|
|
125
|
+
* framework: 'playwright' | 'cypress' | null
|
|
126
|
+
* source: 'settings' | 'default' | 'missing-file'
|
|
127
|
+
*/
|
|
128
|
+
export function readFramework() {
|
|
129
|
+
if (!existsSync(getSettingsPath())) {
|
|
130
|
+
return { framework: null, source: 'missing-file' };
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
try {
|
|
134
|
+
const content = readFileSync(getSettingsPath(), 'utf-8');
|
|
135
|
+
const config = JSON.parse(content);
|
|
136
|
+
const framework = config.deepstorm?.sweep?.e2eFramework || null;
|
|
137
|
+
return { framework, source: framework ? 'settings' : 'default' };
|
|
138
|
+
} catch {
|
|
139
|
+
return { framework: null, source: 'parse-error' };
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// ── MCP availability ──────────────────────────────────────────────
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Check if the required MCP service is configured in .mcp.json.
|
|
147
|
+
*
|
|
148
|
+
* @param {string} [mcpName='deepstorm-playwright'] - MCP service name to check
|
|
149
|
+
* @returns {{ available: boolean, mcpName: string }}
|
|
150
|
+
*/
|
|
151
|
+
export function checkMcpAvailable(mcpName = 'deepstorm-playwright') {
|
|
152
|
+
if (!existsSync(getMcpPath())) {
|
|
153
|
+
return { available: false, mcpName };
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
try {
|
|
157
|
+
const content = readFileSync(getMcpPath(), 'utf-8');
|
|
158
|
+
const config = JSON.parse(content);
|
|
159
|
+
const available = !!(config.mcpServers?.[mcpName] || config[mcpName]);
|
|
160
|
+
return { available, mcpName };
|
|
161
|
+
} catch {
|
|
162
|
+
return { available: false, mcpName };
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// ── CLI entry point ────────────────────────────────────────────────
|
|
167
|
+
|
|
168
|
+
if (process.argv[1] === import.meta.filename) {
|
|
169
|
+
const args = process.argv.slice(2);
|
|
170
|
+
|
|
171
|
+
if (args.includes('--list')) {
|
|
172
|
+
const envMap = readDotEnv();
|
|
173
|
+
const envs = listEnvs(envMap);
|
|
174
|
+
const defaultEnv = getDefaultEnv(envMap);
|
|
175
|
+
console.log(JSON.stringify({ default: defaultEnv, environments: envs }));
|
|
176
|
+
process.exit(0);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (args.includes('--framework')) {
|
|
180
|
+
const result = readFramework();
|
|
181
|
+
console.log(JSON.stringify(result));
|
|
182
|
+
process.exit(0);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (args.includes('--check-mcp')) {
|
|
186
|
+
const mcpName = args.find(a => a.startsWith('--mcp='))?.split('=')[1] || 'deepstorm-playwright';
|
|
187
|
+
const result = checkMcpAvailable(mcpName);
|
|
188
|
+
console.log(JSON.stringify(result));
|
|
189
|
+
process.exit(0);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Default: resolve env
|
|
193
|
+
const envFlag = args.find(a => a.startsWith('--env='));
|
|
194
|
+
const envName = envFlag ? envFlag.split('=')[1] : undefined;
|
|
195
|
+
const result = resolveEnv(envName);
|
|
196
|
+
|
|
197
|
+
if (args.includes('--print')) {
|
|
198
|
+
if (result.baseUrl) {
|
|
199
|
+
console.log(`export BASE_URL=${result.baseUrl}`);
|
|
200
|
+
} else {
|
|
201
|
+
console.error(`# No base URL found for environment "${result.env}"`);
|
|
202
|
+
console.error(`# Available: ${result.availableEnvs.map(e => e.name).join(', ')}`);
|
|
203
|
+
process.exit(1);
|
|
204
|
+
}
|
|
205
|
+
} else {
|
|
206
|
+
console.log(JSON.stringify(result));
|
|
207
|
+
}
|
|
208
|
+
}
|