@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
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Sweep Flow Selector —
|
|
4
|
+
* Sweep Flow Selector — 交互/非交互式选择工具
|
|
5
5
|
*
|
|
6
6
|
* 支持选择粒度:
|
|
7
7
|
* 1. 模块级别(eg. user-system)
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
* 将选中结果写入 .sweep-selection.json 并输出到 stdout。
|
|
14
14
|
*
|
|
15
15
|
* 使用方式:
|
|
16
|
+
* node scripts/flow-selector.mjs --list # 非交互:列出可用文件(JSON)
|
|
16
17
|
* node scripts/flow-selector.mjs # 自动检测 TTY(checkbox 或文本回退)
|
|
17
18
|
* node scripts/flow-selector.mjs --tui # 强制 TUI checkbox 模式
|
|
18
19
|
* node scripts/flow-selector.mjs --text # 强制文本输入模式
|
|
@@ -142,6 +143,45 @@ function scanFlowFiles(dir) {
|
|
|
142
143
|
return result;
|
|
143
144
|
}
|
|
144
145
|
|
|
146
|
+
// ── 非交互模式:构建文件列表 JSON ─────────────────────────
|
|
147
|
+
|
|
148
|
+
function buildFileList(baseFlowsDir) {
|
|
149
|
+
const topologyPath = join(baseFlowsDir, 'topology.yaml');
|
|
150
|
+
const files = [];
|
|
151
|
+
|
|
152
|
+
if (existsSync(topologyPath)) {
|
|
153
|
+
const yaml = readFileSync(topologyPath, 'utf-8');
|
|
154
|
+
const modules = parseTopology(yaml);
|
|
155
|
+
|
|
156
|
+
function walkModules(mods, prefix = '') {
|
|
157
|
+
for (const mod of mods) {
|
|
158
|
+
const path = prefix ? `${prefix}/${mod.name}` : mod.name;
|
|
159
|
+
if (mod.children && mod.children.length > 0) {
|
|
160
|
+
walkModules(mod.children, path);
|
|
161
|
+
} else {
|
|
162
|
+
const flowFile = resolveFlowFile(path, baseFlowsDir);
|
|
163
|
+
if (flowFile) {
|
|
164
|
+
const content = readFileSync(flowFile, 'utf-8');
|
|
165
|
+
const flows = parseFlows(content);
|
|
166
|
+
files.push({ file: flowFile, relative: path, flows });
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
walkModules(modules);
|
|
172
|
+
} else {
|
|
173
|
+
const scanned = scanFlowFiles(baseFlowsDir);
|
|
174
|
+
for (const file of scanned) {
|
|
175
|
+
const content = readFileSync(file, 'utf-8');
|
|
176
|
+
const flows = parseFlows(content);
|
|
177
|
+
const rel = relative(baseFlowsDir, file);
|
|
178
|
+
files.push({ file, relative: rel, flows });
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return { files, totalFiles: files.length, totalFlows: files.reduce((s, f) => s + f.flows.length, 0) };
|
|
183
|
+
}
|
|
184
|
+
|
|
145
185
|
// ── 构建 Choice 树 ──────────────────────────────────────────
|
|
146
186
|
|
|
147
187
|
function buildChoicesWithFlows(modules, baseFlowsDir, prefix = '') {
|
|
@@ -341,6 +381,22 @@ async function main() {
|
|
|
341
381
|
const cliArgs = process.argv.slice(2);
|
|
342
382
|
const forceText = cliArgs.includes('--text');
|
|
343
383
|
const forceTui = cliArgs.includes('--tui');
|
|
384
|
+
const listMode = cliArgs.includes('--list');
|
|
385
|
+
|
|
386
|
+
// ── 非交互模式:直接输出可用文件列表 JSON ──────────────
|
|
387
|
+
if (listMode) {
|
|
388
|
+
if (!existsSync(BASE_FLOWS_DIR)) {
|
|
389
|
+
console.error(JSON.stringify({ error: 'flows/ 目录不存在' }));
|
|
390
|
+
process.exit(1);
|
|
391
|
+
}
|
|
392
|
+
const list = buildFileList(BASE_FLOWS_DIR);
|
|
393
|
+
if (list.files.length === 0) {
|
|
394
|
+
console.error(JSON.stringify({ error: '没有找到任何 .flow.md 文件' }));
|
|
395
|
+
process.exit(1);
|
|
396
|
+
}
|
|
397
|
+
console.log(JSON.stringify(list, null, 2));
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
344
400
|
|
|
345
401
|
try {
|
|
346
402
|
let modules = [];
|
|
@@ -471,6 +527,7 @@ export {
|
|
|
471
527
|
buildChoicesWithFlows,
|
|
472
528
|
normalizeSelection,
|
|
473
529
|
buildFlatFileChoices,
|
|
530
|
+
buildFileList,
|
|
474
531
|
askTextSelection,
|
|
475
532
|
isTtyAvailable,
|
|
476
533
|
getCheckbox,
|
|
@@ -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",
|