@deepstorm/cli 0.9.2 → 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/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-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/sweep-init/SKILL.md +12 -125
- package/dist/skills/sweep-init/scripts/init-project.mjs +166 -0
- package/dist/skills/sweep-run/SKILL.md +11 -35
- package/dist/skills/sweep-run/scripts/env-manager.mjs +71 -2
- package/dist/skills/sweep-run/scripts/generate-report.mjs +116 -0
- package/package.json +2 -2
|
@@ -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
|
+
}
|
|
@@ -58,52 +58,28 @@ node scripts/env-manager.mjs --framework
|
|
|
58
58
|
|
|
59
59
|
### 步骤 1:检查初始化状态与路径导航
|
|
60
60
|
|
|
61
|
-
从 `.deepstorm/settings.json` 读取 `sweep.e2eProjectPath`,确定 E2E
|
|
61
|
+
从 `.deepstorm/settings.json` 读取 `sweep.e2eProjectPath`,确定 E2E 测试项目的位置。
|
|
62
62
|
|
|
63
63
|
```bash
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
CUR="$PWD"
|
|
67
|
-
while [ "$CUR" != "/" ]; do
|
|
68
|
-
if [ -f "$CUR/.deepstorm/settings.json" ]; then
|
|
69
|
-
DEEPSTORM_DIR="$CUR"
|
|
70
|
-
break
|
|
71
|
-
fi
|
|
72
|
-
CUR=$(dirname "$CUR")
|
|
73
|
-
done
|
|
74
|
-
|
|
75
|
-
if [ -n "$DEEPSTORM_DIR" ]; then
|
|
76
|
-
E2E_PATH=$(grep -o '"e2eProjectPath"[^,]*' "$DEEPSTORM_DIR/.deepstorm/settings.json" | head -1 | cut -d'"' -f4)
|
|
77
|
-
else
|
|
78
|
-
E2E_PATH=""
|
|
79
|
-
fi
|
|
64
|
+
node scripts/env-manager.mjs --project-root
|
|
65
|
+
# 输出: {"found":true,"path":"/abs/path/to/project"}
|
|
80
66
|
```
|
|
81
67
|
|
|
82
68
|
#### 1.1 配置存在 → 路径导航
|
|
83
69
|
|
|
84
|
-
- **WHEN** `
|
|
85
|
-
- **THEN**
|
|
70
|
+
- **WHEN** `--project-root` 输出 `found: true`
|
|
71
|
+
- **THEN** 读取 `sweep.e2eProjectPath`:
|
|
86
72
|
```bash
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
TARGET_DIR="$DEEPSTORM_DIR/$E2E_PATH"
|
|
92
|
-
fi
|
|
93
|
-
if [ -d "$TARGET_DIR" ]; then
|
|
94
|
-
echo "📂 切换到 E2E 项目目录: $E2E_PATH"
|
|
95
|
-
cd "$TARGET_DIR"
|
|
96
|
-
else
|
|
97
|
-
echo "❌ E2E 项目目录不存在: $E2E_PATH,请重新运行 /sweep-init"
|
|
98
|
-
exit 1
|
|
99
|
-
fi
|
|
100
|
-
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)
|
|
101
77
|
```
|
|
102
78
|
|
|
103
79
|
#### 1.2 配置不存在 → 报错退出
|
|
104
80
|
|
|
105
|
-
- **WHEN** `
|
|
106
|
-
- **THEN** 提示"❌
|
|
81
|
+
- **WHEN** `--project-root` 输出 `found: false`
|
|
82
|
+
- **THEN** 提示"❌ 未检测到深风项目。请先运行 deepstorm setup。"并退出
|
|
107
83
|
|
|
108
84
|
---
|
|
109
85
|
|
|
@@ -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;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* generate-report.mjs
|
|
5
|
+
* 生成格式化的 E2E 测试报告(markdown)。
|
|
6
|
+
*
|
|
7
|
+
* 用法:
|
|
8
|
+
* node generate-report.mjs --results <json> -o report.md
|
|
9
|
+
* node generate-report.mjs --help
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { writeFileSync } from 'node:fs';
|
|
13
|
+
import { resolve } from 'node:path';
|
|
14
|
+
|
|
15
|
+
// ── Pure functions ────────────────────────────────────────────────
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* 生成 markdown 格式的测试报告
|
|
19
|
+
* @param {object} data
|
|
20
|
+
* @param {string} data.fileName - .flow.md 文件名
|
|
21
|
+
* @param {string} data.timestamp - 执行时间
|
|
22
|
+
* @param {string} data.env - 目标环境名
|
|
23
|
+
* @param {string} data.envUrl - 环境 URL
|
|
24
|
+
* @param {string} data.mode - 执行模式
|
|
25
|
+
* @param {Array} data.flows - [{ id, title, steps: [{n,op,vf,result}] }]
|
|
26
|
+
* @param {number} data.passed - 通过步骤数
|
|
27
|
+
* @param {number} data.failed - 失败步骤数
|
|
28
|
+
* @param {number} data.skipped - 跳过的步骤数
|
|
29
|
+
* @returns {string} markdown 报告内容
|
|
30
|
+
*/
|
|
31
|
+
export function generateReport(data) {
|
|
32
|
+
const total = data.passed + data.failed + data.skipped;
|
|
33
|
+
const passRate = total > 0
|
|
34
|
+
? Math.round((data.passed / total) * 100) : 0;
|
|
35
|
+
|
|
36
|
+
const lines = [];
|
|
37
|
+
lines.push('# 测试执行报告\n');
|
|
38
|
+
lines.push(`**文件:** ${data.fileName}`);
|
|
39
|
+
lines.push(`**执行时间:** ${data.timestamp}`);
|
|
40
|
+
lines.push(`**目标环境:** ${data.env} (${data.envUrl || '-'})`);
|
|
41
|
+
lines.push(`**执行模式:** ${data.mode}\n`);
|
|
42
|
+
lines.push('---\n');
|
|
43
|
+
|
|
44
|
+
for (const flow of data.flows) {
|
|
45
|
+
const status = flow.failed > 0 ? 'X' : 'OK';
|
|
46
|
+
lines.push(`## Flow: ${flow.id} - ${flow.title} ${status}\n`);
|
|
47
|
+
lines.push('| 步骤 | 操作 | 验证 | 结果 |');
|
|
48
|
+
lines.push('|------|------|------|------|');
|
|
49
|
+
|
|
50
|
+
for (const step of flow.steps) {
|
|
51
|
+
let resultStr = '';
|
|
52
|
+
if (step.result === 'skip') resultStr = '>';
|
|
53
|
+
else if (step.result === 'pass') resultStr = 'OK';
|
|
54
|
+
else if (step.result === 'fail') resultStr = 'X';
|
|
55
|
+
else resultStr = step.result || '-';
|
|
56
|
+
|
|
57
|
+
lines.push(`| ${step.n} | ${step.op} | ${step.vf} | ${resultStr} |`);
|
|
58
|
+
}
|
|
59
|
+
lines.push('');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
lines.push('---\n');
|
|
63
|
+
lines.push('## 汇总\n');
|
|
64
|
+
lines.push('| 项目 | 值 |');
|
|
65
|
+
lines.push('|------|-----|');
|
|
66
|
+
lines.push(`| 总 Flows 数 | ${data.flows.length} |`);
|
|
67
|
+
lines.push(`| 总步骤数 | ${total} |`);
|
|
68
|
+
lines.push(`| 通过 | ${data.passed} |`);
|
|
69
|
+
lines.push(`| 失败 | ${data.failed} |`);
|
|
70
|
+
lines.push(`| 跳过 | ${data.skipped} |`);
|
|
71
|
+
lines.push(`| 通过率 | ${passRate}% |\n`);
|
|
72
|
+
|
|
73
|
+
return lines.join('\n');
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// ── CLI entry ─────────────────────────────────────────────────────
|
|
77
|
+
|
|
78
|
+
if (import.meta.filename === process.argv[1]) {
|
|
79
|
+
if (process.argv.includes('--help') || process.argv.includes('-h')) {
|
|
80
|
+
console.log(`
|
|
81
|
+
generate-report.mjs — 生成测试报告
|
|
82
|
+
|
|
83
|
+
用法:
|
|
84
|
+
node generate-report.mjs --results <json> -o report.md
|
|
85
|
+
node generate-report.mjs --help
|
|
86
|
+
`);
|
|
87
|
+
process.exit(0);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const resultsIdx = process.argv.indexOf('--results');
|
|
91
|
+
const outIdx = process.argv.indexOf('-o');
|
|
92
|
+
|
|
93
|
+
if (resultsIdx < 0) {
|
|
94
|
+
console.error('--results 必填');
|
|
95
|
+
process.exit(1);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const resultsJson = process.argv[resultsIdx + 1];
|
|
99
|
+
let data;
|
|
100
|
+
try {
|
|
101
|
+
data = JSON.parse(resultsJson);
|
|
102
|
+
} catch {
|
|
103
|
+
console.error('--results 值不是有效 JSON');
|
|
104
|
+
process.exit(1);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const report = generateReport(data);
|
|
108
|
+
|
|
109
|
+
if (outIdx >= 0) {
|
|
110
|
+
const outPath = resolve(process.cwd(), process.argv[outIdx + 1]);
|
|
111
|
+
writeFileSync(outPath, report, 'utf8');
|
|
112
|
+
console.log(JSON.stringify({ written: true, path: outPath }));
|
|
113
|
+
} else {
|
|
114
|
+
process.stdout.write(report + '\n');
|
|
115
|
+
}
|
|
116
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deepstorm/cli",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.3",
|
|
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.9.
|
|
19
|
+
"@deepstorm/pilot": "^0.9.3"
|
|
20
20
|
},
|
|
21
21
|
"devDependencies": {
|
|
22
22
|
"@types/node": "^22.0.0",
|