@deepstorm/cli 0.9.1 → 0.9.3
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/dist/cli.js +8 -5
- package/dist/skills/reef-commit/SKILL.md +27 -100
- package/dist/skills/reef-commit/scripts/branch-check.mjs +109 -0
- package/dist/skills/reef-commit/scripts/check-openspec-status.mjs +92 -0
- package/dist/skills/reef-commit/scripts/collect-git-context.mjs +186 -0
- package/dist/skills/reef-commit/scripts/run-tests.sh +91 -0
- package/dist/skills/reef-commit/scripts/stash-and-switch.sh +44 -0
- package/dist/skills/reef-gen-backend/SKILL.md.tmpl +10 -4
- package/dist/skills/reef-gen-frontend/SKILL.md.tmpl +10 -4
- package/dist/skills/reef-harden/SKILL.md +11 -19
- package/dist/skills/reef-harden/scripts/find-change-dir.mjs +157 -0
- package/dist/skills/reef-pr/SKILL.md +7 -19
- package/dist/skills/reef-pr/scripts/create-pr.mjs +217 -0
- package/dist/skills/reef-start/SKILL.md.tmpl +1 -0
- package/dist/skills/reef-start/references/stage-4-implementation.md +9 -5
- package/dist/skills/reef-start/references/superpowers-gate.md +3 -0
- package/dist/skills/sweep-init/SKILL.md +12 -125
- package/dist/skills/sweep-init/scripts/init-project.mjs +166 -0
- package/dist/skills/sweep-run/SKILL.md +33 -49
- package/dist/skills/sweep-run/scripts/env-manager.mjs +71 -2
- package/dist/skills/sweep-run/scripts/flow-selector.mjs +58 -1
- package/dist/skills/sweep-run/scripts/generate-report.mjs +116 -0
- package/package.json +2 -2
|
@@ -36,11 +36,14 @@ PLAN_FILE="docs/superpowers/plans/$(date +%Y-%m-%d)-$CHANGE.md"
|
|
|
36
36
|
适用于:文档修改、配置文件调整、SKILL.md 流程修改、测试框架搭建、简单重构(测试覆盖充分)
|
|
37
37
|
|
|
38
38
|
```
|
|
39
|
-
1.
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
39
|
+
1. 🔴 确认 code-style 已加载 — 检查 `reef:reef-style-backend` 或 `reef:reef-style-frontend` 是否已通过 Skill tool 加载
|
|
40
|
+
- 已加载 → 进入步骤 2
|
|
41
|
+
- 未加载 → 暂停并先通过 Skill tool 加载对应技能,阅读 quick-reference.md 后再继续
|
|
42
|
+
2. 直接实现代码变更
|
|
43
|
+
3. 执行后置验证:build → lint → test
|
|
44
|
+
4. 如果验证失败 → 修复 → 重新验证 → 通过后才标完成
|
|
45
|
+
5. 如果发现复杂度超预期 → 暂停并向用户建议升级为 tdd mode
|
|
46
|
+
6. ✅ 标记 task 为完成
|
|
44
47
|
```
|
|
45
48
|
|
|
46
49
|
> **⚠️ Plan mode 风险预警:** plan mode 不要求前置测试,但**后置验证不可跳过**。如果 build/lint/test 任何一步失败,该 task **不得**被标记为完成。
|
|
@@ -50,6 +53,7 @@ PLAN_FILE="docs/superpowers/plans/$(date +%Y-%m-%d)-$CHANGE.md"
|
|
|
50
53
|
适用于:新增业务逻辑、Bug 修复、权限/安全变更、资金/幂等性变更、状态机/并发逻辑
|
|
51
54
|
|
|
52
55
|
**🔴 RED — 先写测试**
|
|
56
|
+
- 先确认 code-style 已加载:检查 `reef:reef-style-backend` 或 `reef:reef-style-frontend` 是否已通过 Skill tool 加载,未加载则先加载
|
|
53
57
|
- 根据 spec 的 Scenario 编写单元测试
|
|
54
58
|
- 运行测试,确认失败(红)
|
|
55
59
|
- 如果测试意外通过了,说明测试写的太弱,需改进
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
| 技能 | 类型 | 对本变更的要求 |
|
|
17
17
|
|------|------|---------------|
|
|
18
18
|
| {skill-name} | {类型} | {要求描述} |
|
|
19
|
+
| `reef:reef-style-backend` / `reef:reef-style-frontend` | 🟢 **Code Style** | 所有新增/修改的代码必须遵循项目编码规范,加载后阅读 `quick-reference.md` 和必要维度规范 |
|
|
19
20
|
|
|
20
21
|
### Plan Mode 纪律确认
|
|
21
22
|
|
|
@@ -39,6 +40,7 @@
|
|
|
39
40
|
| 技能 | 类型 | 对本变更的要求 |
|
|
40
41
|
|------|------|---------------|
|
|
41
42
|
| test-driven-development | 🔴 **Rigid** | 每个代码行为改动必须先写测试、看失败、再写实现 |
|
|
43
|
+
| `reef:reef-style-backend` / `reef:reef-style-frontend` | 🟢 **Code Style** | 所有新增/修改的代码必须遵循项目编码规范,加载后阅读 `quick-reference.md` 和必要维度规范 |
|
|
42
44
|
|
|
43
45
|
### TDD 纪律确认
|
|
44
46
|
|
|
@@ -57,6 +59,7 @@
|
|
|
57
59
|
- [ ] 📝 **实现计划已生成**(superpowers:writing-plans 完成,存于 `docs/superpowers/plans/`)
|
|
58
60
|
- [ ] 用户已审阅并批准所有 SDD 文档及实现计划
|
|
59
61
|
- [ ] 🔍 **Superpowers 技能已加载**(Skill 工具已调用)
|
|
62
|
+
- [ ] 🎨 **Code-Style 技能已加载**(`reef:reef-style-backend`/`reef:reef-style-frontend` 已通过 Skill 工具加载并阅读规范)
|
|
60
63
|
- [ ] 🚨 **Rigid 纪律已向用户声明并获得确认**
|
|
61
64
|
- [ ] Path A 检查项:Git 分支已从 main 岔出
|
|
62
65
|
- [ ] Path B 检查项:OpenSpec change 已创建,brainstorming 文件已产出
|
|
@@ -103,107 +103,21 @@ E2E_PATH=$(cat .deepstorm/settings.json 2>/dev/null | grep -o '"e2eProjectPath"[
|
|
|
103
103
|
|
|
104
104
|
---
|
|
105
105
|
|
|
106
|
-
### 步骤 2
|
|
106
|
+
### 步骤 2+3:创建项目目录与配置文件
|
|
107
107
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
#### 目录结构(当 TARGET_DIR 为 `"."` 时)
|
|
111
|
-
|
|
112
|
-
```
|
|
113
|
-
.
|
|
114
|
-
├── flows/ # 测试意图文档目录
|
|
115
|
-
│ ├── topology.yaml # 功能模块拓扑定义(待用户编辑)
|
|
116
|
-
│ └── reports/ # 执行报告持久化目录
|
|
117
|
-
├── scripts/
|
|
118
|
-
│ └── flow-selector.mjs # 层级选择工具(预置)
|
|
119
|
-
├── .env # 环境变量(gitignored,即模板)
|
|
120
|
-
├── package.json # 依赖声明
|
|
121
|
-
└── tsconfig.json # TypeScript 配置
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
> 如果 TARGET_DIR 为 `"e2e"`,以上所有文件在 `e2e/` 下生成。如果框架为 `playwright`,还会额外生成 `playwright.config.ts`。
|
|
125
|
-
|
|
126
|
-
#### 执行
|
|
108
|
+
使用 `init-project.mjs` 一键创建目录结构并生成所有配置文件:
|
|
127
109
|
|
|
128
110
|
```bash
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
echo "📂 将在 $TARGET_DIR/ 目录下生成 E2E 测试项目"
|
|
133
|
-
else
|
|
134
|
-
mkdir -p flows flows/reports scripts
|
|
135
|
-
fi
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
---
|
|
139
|
-
|
|
140
|
-
### 步骤 3:生成项目配置文件(框架感知)
|
|
141
|
-
|
|
142
|
-
根据步骤 0 读取的框架配置,在 `{TARGET_DIR}` 下生成对应的配置文件。
|
|
143
|
-
|
|
144
|
-
> 所有文件写入路径以 `{TARGET_DIR}` 为前缀。当 `TARGET_DIR` 为 `"."` 时,路径不变。
|
|
145
|
-
|
|
146
|
-
#### 3.1 package.json
|
|
147
|
-
|
|
148
|
-
写入基础 `package.json`。如果框架为 `playwright`,包含 Playwright 依赖:
|
|
149
|
-
|
|
150
|
-
```json
|
|
151
|
-
{
|
|
152
|
-
"name": "sweep-e2e",
|
|
153
|
-
"version": "1.0.0",
|
|
154
|
-
"private": true,
|
|
155
|
-
"type": "module",
|
|
156
|
-
"scripts": {
|
|
157
|
-
"test": "playwright test",
|
|
158
|
-
"report": "playwright show-report"
|
|
159
|
-
},
|
|
160
|
-
"devDependencies": {
|
|
161
|
-
"@playwright/test": "^1.50.0",
|
|
162
|
-
"@inquirer/checkbox": "^4.0.0",
|
|
163
|
-
"@types/node": "^22.0.0"
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
```
|
|
167
|
-
|
|
168
|
-
> 当 E2E 框架未知或未配置时,`package.json` 中仅包含 `@inquirer/checkbox` 和 `@types/node` 依赖,**不包含** `@playwright/test`。
|
|
169
|
-
|
|
170
|
-
#### 3.2 框架配置文件
|
|
171
|
-
|
|
172
|
-
**当框架为 `playwright` 时**,写入 `{TARGET_DIR}/playwright.config.ts`,包含多环境 baseURL 设置:
|
|
173
|
-
|
|
174
|
-
```ts
|
|
175
|
-
import { defineConfig } from '@playwright/test';
|
|
176
|
-
|
|
177
|
-
export default defineConfig({
|
|
178
|
-
use: {
|
|
179
|
-
baseURL: process.env.BASE_URL || 'http://localhost:3000',
|
|
180
|
-
},
|
|
181
|
-
timeout: 30000,
|
|
182
|
-
retries: 0,
|
|
183
|
-
reporter: [['line'], ['html', { outputFolder: 'flows/reports' }]],
|
|
184
|
-
projects: [
|
|
185
|
-
{ name: 'chromium', use: { browserName: 'chromium' } },
|
|
186
|
-
],
|
|
187
|
-
});
|
|
111
|
+
node packages/sweep/skills/sweep-init/scripts/init-project.mjs \
|
|
112
|
+
--dir "$TARGET_DIR" \
|
|
113
|
+
--framework "$(node scripts/env-manager.mjs --framework | node -pe "JSON.parse(require('fs').readFileSync('/dev/stdin','utf8')).framework || ''")"
|
|
188
114
|
```
|
|
189
115
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
```json
|
|
197
|
-
{
|
|
198
|
-
"compilerOptions": {
|
|
199
|
-
"target": "ES2022",
|
|
200
|
-
"module": "ESNext",
|
|
201
|
-
"moduleResolution": "bundler",
|
|
202
|
-
"strict": true,
|
|
203
|
-
"esModuleInterop": true
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
```
|
|
116
|
+
脚本会自动:
|
|
117
|
+
- 创建 `flows/`、`flows/reports/`、`scripts/` 目录
|
|
118
|
+
- 生成 `package.json`(含框架对应依赖)、`tsconfig.json`、`playwright.config.ts`(仅 playwright)
|
|
119
|
+
- 不覆盖已有的 `topology.yaml`
|
|
120
|
+
- 执行 `npm install`
|
|
207
121
|
|
|
208
122
|
---
|
|
209
123
|
|
|
@@ -312,38 +226,11 @@ cat .mcp.json 2>/dev/null | grep -c "deepstorm-playwright"
|
|
|
312
226
|
|
|
313
227
|
#### 8.1 写入 e2eProjectPath 到 settings.json
|
|
314
228
|
|
|
315
|
-
将 `sweep.e2eProjectPath` 写入 `.deepstorm/settings.json`。**不再创建 `.sweep-init` 标记文件。**
|
|
316
|
-
|
|
317
|
-
```bash
|
|
318
|
-
# 值由步骤 0A 确定:
|
|
319
|
-
# 独立项目 → "."
|
|
320
|
-
# 混放项目 → 用户选择的路径(如 "e2e")
|
|
321
|
-
```
|
|
322
|
-
|
|
323
|
-
写入后的 settings.json 结构示例:
|
|
324
|
-
|
|
325
|
-
```json
|
|
326
|
-
{
|
|
327
|
-
"sweep": {
|
|
328
|
-
"e2eFramework": "playwright",
|
|
329
|
-
"e2eProjectPath": "e2e",
|
|
330
|
-
"environments": { ... }
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
```
|
|
334
|
-
|
|
335
|
-
#### 8.2 安装依赖
|
|
336
|
-
|
|
337
229
|
```bash
|
|
338
|
-
|
|
339
|
-
if [ "$TARGET_DIR" != "." ]; then
|
|
340
|
-
cd "$TARGET_DIR" && npm install
|
|
341
|
-
else
|
|
342
|
-
npm install
|
|
343
|
-
fi
|
|
230
|
+
node scripts/env-manager.mjs --set-e2e-path "$TARGET_DIR"
|
|
344
231
|
```
|
|
345
232
|
|
|
346
|
-
#### 8.
|
|
233
|
+
#### 8.2 输出完成信息
|
|
347
234
|
|
|
348
235
|
```
|
|
349
236
|
✅ Sweep E2E 测试项目初始化完成!
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* init-project.mjs
|
|
5
|
+
* 初始化 E2E 测试项目骨架。
|
|
6
|
+
*
|
|
7
|
+
* 用法:
|
|
8
|
+
* node init-project.mjs # 在当前目录
|
|
9
|
+
* node init-project.mjs --dir e2e # 指定子目录
|
|
10
|
+
* node init-project.mjs --framework playwright
|
|
11
|
+
* node init-project.mjs --help
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { mkdirSync, writeFileSync, existsSync } from 'node:fs';
|
|
15
|
+
import { resolve, join } from 'node:path';
|
|
16
|
+
import { execSync } from 'node:child_process';
|
|
17
|
+
|
|
18
|
+
// ── Template writers ──────────────────────────────────────────────
|
|
19
|
+
|
|
20
|
+
function writePackageJson(targetDir, framework) {
|
|
21
|
+
const deps = {
|
|
22
|
+
'@inquirer/checkbox': '^4.0.0',
|
|
23
|
+
'@types/node': '^22.0.0',
|
|
24
|
+
};
|
|
25
|
+
if (framework === 'playwright') {
|
|
26
|
+
deps['@playwright/test'] = '^1.50.0';
|
|
27
|
+
}
|
|
28
|
+
const scripts = { report: 'playwright show-report' };
|
|
29
|
+
if (framework === 'playwright') {
|
|
30
|
+
scripts.test = 'playwright test';
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
writeFileSync(join(targetDir, 'package.json'), JSON.stringify({
|
|
34
|
+
name: 'sweep-e2e',
|
|
35
|
+
version: '1.0.0',
|
|
36
|
+
private: true,
|
|
37
|
+
type: 'module',
|
|
38
|
+
scripts,
|
|
39
|
+
devDependencies: deps,
|
|
40
|
+
}, null, 2) + '\n');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function writePlaywrightConfig(targetDir) {
|
|
44
|
+
writeFileSync(join(targetDir, 'playwright.config.ts'), [
|
|
45
|
+
"import { defineConfig } from '@playwright/test';",
|
|
46
|
+
'',
|
|
47
|
+
'export default defineConfig({',
|
|
48
|
+
' use: {',
|
|
49
|
+
" baseURL: process.env.BASE_URL || 'http://localhost:3000',",
|
|
50
|
+
' },',
|
|
51
|
+
' timeout: 30000,',
|
|
52
|
+
' retries: 0,',
|
|
53
|
+
" reporter: [['line'], ['html', { outputFolder: 'flows/reports' }]],",
|
|
54
|
+
' projects: [',
|
|
55
|
+
" { name: 'chromium', use: { browserName: 'chromium' } },",
|
|
56
|
+
' ],',
|
|
57
|
+
'});',
|
|
58
|
+
'',
|
|
59
|
+
].join('\n'));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function writeTsconfig(targetDir) {
|
|
63
|
+
writeFileSync(join(targetDir, 'tsconfig.json'), JSON.stringify({
|
|
64
|
+
compilerOptions: {
|
|
65
|
+
target: 'ES2022',
|
|
66
|
+
module: 'ESNext',
|
|
67
|
+
moduleResolution: 'bundler',
|
|
68
|
+
strict: true,
|
|
69
|
+
esModuleInterop: true,
|
|
70
|
+
},
|
|
71
|
+
}, null, 2) + '\n');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function writeTopologyYaml(targetDir) {
|
|
75
|
+
writeFileSync(join(targetDir, 'flows', 'topology.yaml'), [
|
|
76
|
+
'# flows/topology.yaml',
|
|
77
|
+
'name: E2E 测试拓扑',
|
|
78
|
+
'version: 1',
|
|
79
|
+
'modules:',
|
|
80
|
+
' - name: example',
|
|
81
|
+
' description: 示例模块',
|
|
82
|
+
' children:',
|
|
83
|
+
' - name: feature1',
|
|
84
|
+
' description: 功能 1',
|
|
85
|
+
' features: []',
|
|
86
|
+
'',
|
|
87
|
+
].join('\n'));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// ── Main ──────────────────────────────────────────────────────────
|
|
91
|
+
|
|
92
|
+
export function initProject(opts = {}) {
|
|
93
|
+
const {
|
|
94
|
+
framework = null, // 'playwright' | null
|
|
95
|
+
dir = '.',
|
|
96
|
+
} = opts;
|
|
97
|
+
|
|
98
|
+
const targetDir = resolve(process.cwd(), dir);
|
|
99
|
+
const flowsDir = join(targetDir, 'flows');
|
|
100
|
+
const reportsDir = join(flowsDir, 'reports');
|
|
101
|
+
const scriptsDir = join(targetDir, 'scripts');
|
|
102
|
+
|
|
103
|
+
// 创建目录
|
|
104
|
+
mkdirSync(flowsDir, { recursive: true });
|
|
105
|
+
mkdirSync(reportsDir, { recursive: true });
|
|
106
|
+
mkdirSync(scriptsDir, { recursive: true });
|
|
107
|
+
|
|
108
|
+
// 写入配置文件
|
|
109
|
+
writePackageJson(targetDir, framework);
|
|
110
|
+
writeTsconfig(targetDir);
|
|
111
|
+
|
|
112
|
+
if (framework === 'playwright') {
|
|
113
|
+
writePlaywrightConfig(targetDir);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (!existsSync(join(flowsDir, 'topology.yaml'))) {
|
|
117
|
+
writeTopologyYaml(targetDir);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// npm install
|
|
121
|
+
try {
|
|
122
|
+
execSync('npm install', {
|
|
123
|
+
cwd: targetDir,
|
|
124
|
+
stdio: 'pipe',
|
|
125
|
+
timeout: 60000,
|
|
126
|
+
});
|
|
127
|
+
} catch {
|
|
128
|
+
// npm install 失败不阻塞
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const created = [
|
|
132
|
+
'flows/', 'flows/reports/', 'scripts/',
|
|
133
|
+
'package.json', 'tsconfig.json',
|
|
134
|
+
];
|
|
135
|
+
if (framework === 'playwright') created.push('playwright.config.ts');
|
|
136
|
+
if (!existsSync(join(flowsDir, 'topology.yaml'))) {
|
|
137
|
+
created.push('flows/topology.yaml');
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return { dir: targetDir, created, framework };
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// ── CLI entry ─────────────────────────────────────────────────────
|
|
144
|
+
|
|
145
|
+
if (import.meta.filename === process.argv[1]) {
|
|
146
|
+
if (process.argv.includes('--help') || process.argv.includes('-h')) {
|
|
147
|
+
console.log(`
|
|
148
|
+
init-project.mjs — E2E 测试项目初始化
|
|
149
|
+
|
|
150
|
+
用法:
|
|
151
|
+
node init-project.mjs 当前目录
|
|
152
|
+
node init-project.mjs --dir e2e 指定子目录
|
|
153
|
+
node init-project.mjs --framework playwright 指定框架
|
|
154
|
+
node init-project.mjs --help
|
|
155
|
+
`);
|
|
156
|
+
process.exit(0);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const dirIdx = process.argv.indexOf('--dir');
|
|
160
|
+
const fwIdx = process.argv.indexOf('--framework');
|
|
161
|
+
const result = initProject({
|
|
162
|
+
dir: dirIdx >= 0 ? process.argv[dirIdx + 1] : '.',
|
|
163
|
+
framework: fwIdx >= 0 ? process.argv[fwIdx + 1] : null,
|
|
164
|
+
});
|
|
165
|
+
process.stdout.write(JSON.stringify(result, null, 2) + '\n');
|
|
166
|
+
}
|
|
@@ -24,8 +24,6 @@ deepstorm:
|
|
|
24
24
|
/sweep-run flows/login.flow.md # 单文件
|
|
25
25
|
/sweep-run flows/login.flow.md --flow L02 # 单 Flow
|
|
26
26
|
/sweep-run --env staging # 指定测试环境
|
|
27
|
-
/sweep-run --text # 强制使用文本输入模式选择(不启动 TTY checkbox)
|
|
28
|
-
/sweep-run --all --text # 全量 + 文本模式
|
|
29
27
|
```
|
|
30
28
|
|
|
31
29
|
### 执行模式说明
|
|
@@ -60,52 +58,28 @@ node scripts/env-manager.mjs --framework
|
|
|
60
58
|
|
|
61
59
|
### 步骤 1:检查初始化状态与路径导航
|
|
62
60
|
|
|
63
|
-
从 `.deepstorm/settings.json` 读取 `sweep.e2eProjectPath`,确定 E2E
|
|
61
|
+
从 `.deepstorm/settings.json` 读取 `sweep.e2eProjectPath`,确定 E2E 测试项目的位置。
|
|
64
62
|
|
|
65
63
|
```bash
|
|
66
|
-
|
|
67
|
-
|
|
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
|
|
64
|
+
node scripts/env-manager.mjs --project-root
|
|
65
|
+
# 输出: {"found":true,"path":"/abs/path/to/project"}
|
|
82
66
|
```
|
|
83
67
|
|
|
84
68
|
#### 1.1 配置存在 → 路径导航
|
|
85
69
|
|
|
86
|
-
- **WHEN** `
|
|
87
|
-
- **THEN**
|
|
70
|
+
- **WHEN** `--project-root` 输出 `found: true`
|
|
71
|
+
- **THEN** 读取 `sweep.e2eProjectPath`:
|
|
88
72
|
```bash
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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
|
|
73
|
+
E2E_PATH=$(grep -o '"e2eProjectPath"[^,]*' \
|
|
74
|
+
"$(node scripts/env-manager.mjs --project-root | \
|
|
75
|
+
node -pe "JSON.parse(require('fs').readFileSync('/dev/stdin','utf8')).path")/.deepstorm/settings.json" \
|
|
76
|
+
| head -1 | cut -d'"' -f4)
|
|
103
77
|
```
|
|
104
78
|
|
|
105
79
|
#### 1.2 配置不存在 → 报错退出
|
|
106
80
|
|
|
107
|
-
- **WHEN** `
|
|
108
|
-
- **THEN** 提示"❌
|
|
81
|
+
- **WHEN** `--project-root` 输出 `found: false`
|
|
82
|
+
- **THEN** 提示"❌ 未检测到深风项目。请先运行 deepstorm setup。"并退出
|
|
109
83
|
|
|
110
84
|
---
|
|
111
85
|
|
|
@@ -113,18 +87,30 @@ fi
|
|
|
113
87
|
|
|
114
88
|
根据用户输入参数或交互选择,确定要执行的 .flow.md 文件列表。
|
|
115
89
|
|
|
116
|
-
#### 2.1 交互模式(无参数)
|
|
90
|
+
#### 2.1 交互模式(无参数) — 对话式选择
|
|
117
91
|
|
|
118
|
-
|
|
92
|
+
不使用 `@inquirer/checkbox` 交互选择器(在 Claude Code 环境中 TTY/TUI 不可靠),改为**对话式选择**:
|
|
119
93
|
|
|
120
|
-
1.
|
|
121
|
-
2.
|
|
122
|
-
3.
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
94
|
+
1. 运行 `node scripts/flow-selector.mjs --list` 获取可用 .flow.md 文件列表
|
|
95
|
+
2. 检查输出是否为有效 JSON(含 `files[]`),若脚本报错则提示用户后退出
|
|
96
|
+
3. 在消息中直接列出可用文件供用户选择,参考格式:
|
|
97
|
+
|
|
98
|
+
```markdown
|
|
99
|
+
📋 可用测试模块:
|
|
100
|
+
|
|
101
|
+
1. user-system/register(3 个用例:L01-L03)
|
|
102
|
+
2. user-system/login(2 个用例:L01-L02)
|
|
103
|
+
3. tasks/crud(4 个用例:T01-T04)
|
|
104
|
+
|
|
105
|
+
请选择:全部执行 / 输入序号(如 1,3) / 输入模块名
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
4. 等待用户在聊天中回复选择
|
|
109
|
+
5. 根据用户选择,直接写入 `.sweep-selection.json`:
|
|
110
|
+
- **全部执行** → `{"type":"all"}`
|
|
111
|
+
- **指定文件全部 Flow** → `{"type":"selection","files":[{"file":"/abs/path/to/file.flow.md","flows":[],"all":true}]}`
|
|
112
|
+
- **指定文件的特定 Flow** → `{"type":"selection","files":[{"file":"/abs/path/to/file.flow.md","flows":["L01"],"all":false}]}`
|
|
113
|
+
6. 输出选中文件的总览,确认后继续执行(**以下为示例格式,实际内容根据项目动态生成**):
|
|
128
114
|
|
|
129
115
|
```
|
|
130
116
|
选中的模块:user-system, tasks
|
|
@@ -147,8 +133,6 @@ fi
|
|
|
147
133
|
| `--env {env}` | 切换目标环境 | `/sweep-run --all --env staging` |
|
|
148
134
|
| `--browser` | 打开浏览器窗口逐步骤调试 | `/sweep-run --all --browser` |
|
|
149
135
|
| `--no-parallel` | 使用批量但不并行 | `/sweep-run --all --no-parallel` |
|
|
150
|
-
| `--text` | 强制文本输入模式选择(跳过 TTY checkbox) | `/sweep-run --text` |
|
|
151
|
-
| `--tui` | 强制 TUI checkbox 模式 | `/sweep-run --tui` |
|
|
152
136
|
|
|
153
137
|
#### 2.3 文件不存在处理
|
|
154
138
|
|
|
@@ -20,8 +20,8 @@
|
|
|
20
20
|
* import { resolveEnv, listEnvs, readFramework, checkMcpAvailable } from './env-manager.mjs'
|
|
21
21
|
*/
|
|
22
22
|
|
|
23
|
-
import { readFileSync, existsSync } from 'node:fs';
|
|
24
|
-
import { resolve } from 'node:path';
|
|
23
|
+
import { readFileSync, writeFileSync, existsSync } from 'node:fs';
|
|
24
|
+
import { resolve, dirname, sep } from 'node:path';
|
|
25
25
|
|
|
26
26
|
// ── Path helpers (lazy, respect process.cwd() changes) ────────────
|
|
27
27
|
|
|
@@ -217,6 +217,55 @@ export function readFramework() {
|
|
|
217
217
|
|
|
218
218
|
// ── MCP availability ──────────────────────────────────────────────
|
|
219
219
|
|
|
220
|
+
// ── Project root discovery ──────────────────────────────────────────
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Walk up from startDir to find .deepstorm/settings.json
|
|
224
|
+
*
|
|
225
|
+
* @param {string} [startDir] - defaults to process.cwd()
|
|
226
|
+
* @returns {string|null} resolved dir containing settings.json, or null
|
|
227
|
+
*/
|
|
228
|
+
export function resolveProjectRoot(startDir) {
|
|
229
|
+
let dir = startDir ? resolve(startDir) : process.cwd();
|
|
230
|
+
while (true) {
|
|
231
|
+
if (existsSync(resolve(dir, '.deepstorm', 'settings.json'))) {
|
|
232
|
+
return dir;
|
|
233
|
+
}
|
|
234
|
+
const parent = dirname(dir);
|
|
235
|
+
if (parent === dir) return null;
|
|
236
|
+
dir = parent;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Write or update a key in .deepstorm/settings.json.
|
|
242
|
+
*
|
|
243
|
+
* @param {string} keyPath dot-separated path, e.g. "sweep.e2eProjectPath"
|
|
244
|
+
* @param {*} value
|
|
245
|
+
* @returns {boolean}
|
|
246
|
+
*/
|
|
247
|
+
export function writeDeepstormConfig(keyPath, value) {
|
|
248
|
+
const filePath = resolve(process.cwd(), '.deepstorm', 'settings.json');
|
|
249
|
+
if (!existsSync(filePath)) return false;
|
|
250
|
+
try {
|
|
251
|
+
const content = readFileSync(filePath, 'utf-8');
|
|
252
|
+
const config = JSON.parse(content);
|
|
253
|
+
const keys = keyPath.split('.');
|
|
254
|
+
let obj = config;
|
|
255
|
+
for (let i = 0; i < keys.length - 1; i++) {
|
|
256
|
+
if (!obj[keys[i]] || typeof obj[keys[i]] !== 'object') {
|
|
257
|
+
obj[keys[i]] = {};
|
|
258
|
+
}
|
|
259
|
+
obj = obj[keys[i]];
|
|
260
|
+
}
|
|
261
|
+
obj[keys[keys.length - 1]] = value;
|
|
262
|
+
writeFileSync(filePath, JSON.stringify(config, null, 2) + '\n');
|
|
263
|
+
return true;
|
|
264
|
+
} catch {
|
|
265
|
+
return false;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
220
269
|
/**
|
|
221
270
|
* Check if the required MCP service is configured in .mcp.json.
|
|
222
271
|
*
|
|
@@ -263,6 +312,26 @@ if (process.argv[1] === import.meta.filename) {
|
|
|
263
312
|
process.exit(0);
|
|
264
313
|
}
|
|
265
314
|
|
|
315
|
+
if (args.includes('--project-root')) {
|
|
316
|
+
const startIdx = args.indexOf('--project-root');
|
|
317
|
+
const startDir = startIdx + 1 < args.length ? args[startIdx + 1] : undefined;
|
|
318
|
+
const result = resolveProjectRoot(startDir);
|
|
319
|
+
if (result) {
|
|
320
|
+
console.log(JSON.stringify({ found: true, path: result }));
|
|
321
|
+
} else {
|
|
322
|
+
console.log(JSON.stringify({ found: false }));
|
|
323
|
+
}
|
|
324
|
+
process.exit(0);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
if (args.includes('--set-e2e-path')) {
|
|
328
|
+
const idx = args.indexOf('--set-e2e-path');
|
|
329
|
+
const path = idx + 1 < args.length ? args[idx + 1] : '.';
|
|
330
|
+
const ok = writeDeepstormConfig('sweep.e2eProjectPath', path);
|
|
331
|
+
console.log(JSON.stringify({ ok, path }));
|
|
332
|
+
process.exit(0);
|
|
333
|
+
}
|
|
334
|
+
|
|
266
335
|
// Default: resolve env
|
|
267
336
|
const envFlag = args.find(a => a.startsWith('--env='));
|
|
268
337
|
const envName = envFlag ? envFlag.split('=')[1] : undefined;
|