@kkeve/suite 0.0.1 → 0.0.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/README.md +53 -0
- package/dist/bin/keve.d.ts +13 -0
- package/dist/bin/keve.js +164 -0
- package/dist/commands/auth-update.d.ts +16 -0
- package/dist/commands/auth-update.js +88 -0
- package/dist/commands/clean.d.ts +10 -0
- package/dist/commands/clean.js +78 -0
- package/dist/commands/config.d.ts +17 -0
- package/dist/commands/config.js +99 -0
- package/dist/commands/init.d.ts +15 -0
- package/dist/commands/init.js +292 -0
- package/dist/commands/report.d.ts +12 -0
- package/dist/commands/report.js +159 -0
- package/dist/commands/run.d.ts +29 -0
- package/dist/commands/run.js +417 -0
- package/dist/commands/test.d.ts +12 -0
- package/dist/commands/test.js +102 -0
- package/dist/commands/vitest.d.ts +14 -0
- package/dist/commands/vitest.js +166 -0
- package/dist/config.d.ts +109 -0
- package/dist/config.js +291 -0
- package/dist/core/action-log-writer.d.ts +47 -0
- package/dist/{tools/write-file.js → core/action-log-writer.js} +42 -26
- package/dist/core/auth.d.ts +17 -0
- package/dist/core/auth.js +224 -0
- package/dist/core/cdp-resolver.d.ts +5 -0
- package/dist/core/cdp-resolver.js +93 -0
- package/dist/core/generateReport/report.d.ts +19 -0
- package/dist/core/generateReport/report.js +91 -0
- package/dist/core/generateReport/reportData.d.ts +215 -0
- package/dist/core/generateReport/reportData.js +735 -0
- package/dist/core/global-setup.d.ts +6 -0
- package/dist/core/global-setup.js +113 -0
- package/dist/core/keve-aspect.d.ts +54 -0
- package/dist/core/keve-aspect.js +52 -0
- package/dist/core/keve-decorators.d.ts +51 -0
- package/dist/core/keve-decorators.js +95 -0
- package/dist/core/keve-registry.d.ts +38 -0
- package/dist/core/keve-registry.js +25 -0
- package/dist/core/keve-report.d.ts +27 -0
- package/dist/core/keve-report.js +357 -0
- package/dist/core/keve-test.d.ts +43 -0
- package/dist/core/keve-test.js +364 -0
- package/dist/core/learned-actions.d.ts +24 -0
- package/dist/core/learned-actions.js +65 -0
- package/dist/core/pw-config.d.ts +19 -0
- package/dist/core/pw-config.js +180 -0
- package/dist/index.d.ts +8 -23
- package/dist/index.js +36 -43
- package/dist/page-agent/agent.d.ts +129 -0
- package/dist/page-agent/agent.js +585 -0
- package/dist/page-agent/hooks.d.ts +13 -0
- package/dist/page-agent/hooks.js +218 -0
- package/dist/page-agent/system_prompt.md +240 -0
- package/dist/page-agent/tools.d.ts +29 -0
- package/dist/page-agent/tools.js +297 -0
- package/doc/README.md +124 -0
- package/doc/agents/keve-dev-test-agent.md +140 -0
- package/doc/agents/keve-test-agent.md +178 -0
- package/doc/asserts//346/265/213/350/257/225/345/220/204/351/230/266/346/256/265/350/257/264/346/230/216.png +0 -0
- package/doc/keve-agent-demo.html +370 -0
- package/doc/skills/keve-bddcase-gen/SKILL.md +332 -0
- package/doc/skills/keve-bddcase-gen/references/test-case-rules.md +91 -0
- package/doc/skills/keve-bddcase-gen/references/test-cases-example.md +103 -0
- package/doc/skills/keve-component-gen/SKILL.md +255 -0
- package/doc/skills/keve-component-gen/references/component-mock-strategy.md +74 -0
- package/doc/skills/keve-component-gen/references/component-model-template.md +98 -0
- package/doc/skills/keve-component-gen/references/component-test-template.md +100 -0
- package/doc/skills/keve-component-gen/references/vue-test-utils-guide.md +91 -0
- package/doc/skills/keve-e2e-gen/SKILL.md +179 -0
- package/doc/skills/keve-e2e-gen/references/code-analysis-guide.md +208 -0
- package/doc/skills/keve-e2e-gen/references/decisions.md +36 -0
- package/doc/skills/keve-e2e-gen/references/pom-generation-guide.md +140 -0
- package/doc/skills/keve-e2e-gen/references/script-generation-guide.md +236 -0
- package/doc/skills/keve-e2e-gen/references/test-auditor-guide.md +189 -0
- package/doc/skills/keve-e2e-gen/scripts/extract-components.sh +85 -0
- package/doc/skills/keve-e2e-gen/scripts/inject-testids.sh +95 -0
- package/doc/skills/keve-static-analysis/SKILL.md +362 -0
- package/doc/skills/keve-static-analysis/references/codegraph-analysis-guide.md +108 -0
- package/doc/skills/keve-static-analysis/references/git-impact-analysis.md +81 -0
- package/doc/skills/keve-static-analysis/references/lint-fix-guide.md +69 -0
- package/doc/skills/keve-static-analysis/references/runtime-risk-patterns.md +92 -0
- package/doc/skills/keve-static-analysis/references/security-scan-guide.md +32 -0
- package/doc/skills/keve-static-analysis/references/ts-error-patterns.md +98 -0
- package/doc/skills/keve-static-analysis/references/vue-security-guide.md +72 -0
- package/doc/skills/keve-test-eval/SKILL.md +78 -0
- package/doc/skills/keve-test-eval/references/common-fix-guide.md +166 -0
- package/doc/skills/keve-test-eval/references/confidence-data-guide.md +78 -0
- package/doc/skills/keve-test-eval/references/fix-loop-guide.md +67 -0
- package/doc/skills/keve-unit-gen/SKILL.md +203 -0
- package/doc/skills/keve-unit-gen/references/boundary-conditions.md +78 -0
- package/doc/skills/keve-unit-gen/references/unit-mock-strategy.md +71 -0
- package/doc/skills/keve-unit-gen/references/unit-test-template.md +105 -0
- package/package.json +46 -24
- package/dist/agent/factory.d.ts +0 -25
- package/dist/agent/factory.js +0 -99
- package/dist/agent/index.d.ts +0 -3
- package/dist/agent/index.js +0 -7
- package/dist/agent/loader.d.ts +0 -22
- package/dist/agent/loader.js +0 -117
- package/dist/agent/types.d.ts +0 -9
- package/dist/agent/types.js +0 -2
- package/dist/agent-loop/complete-task.d.ts +0 -28
- package/dist/agent-loop/complete-task.js +0 -70
- package/dist/agent-loop/index.d.ts +0 -16
- package/dist/agent-loop/index.js +0 -26
- package/dist/agent-loop/loop.d.ts +0 -42
- package/dist/agent-loop/loop.js +0 -282
- package/dist/agent-loop/runtime.d.ts +0 -40
- package/dist/agent-loop/runtime.js +0 -163
- package/dist/agent-loop/types.d.ts +0 -161
- package/dist/agent-loop/types.js +0 -105
- package/dist/context/index.d.ts +0 -8
- package/dist/context/index.js +0 -29
- package/dist/context/tokenCalculation.d.ts +0 -55
- package/dist/context/tokenCalculation.js +0 -95
- package/dist/context/truncation.d.ts +0 -67
- package/dist/context/truncation.js +0 -142
- package/dist/context/types.d.ts +0 -7
- package/dist/context/types.js +0 -27
- package/dist/llm/OpenAIClient.d.ts +0 -31
- package/dist/llm/OpenAIClient.js +0 -455
- package/dist/llm/constants.d.ts +0 -2
- package/dist/llm/constants.js +0 -6
- package/dist/llm/errors.d.ts +0 -25
- package/dist/llm/errors.js +0 -51
- package/dist/llm/index.d.ts +0 -49
- package/dist/llm/index.js +0 -158
- package/dist/llm/types.d.ts +0 -124
- package/dist/llm/types.js +0 -2
- package/dist/llm/utils.d.ts +0 -29
- package/dist/llm/utils.js +0 -284
- package/dist/skill/frontmatter-parser.d.ts +0 -50
- package/dist/skill/frontmatter-parser.js +0 -152
- package/dist/skill/index.d.ts +0 -12
- package/dist/skill/index.js +0 -17
- package/dist/skill/loader.d.ts +0 -96
- package/dist/skill/loader.js +0 -256
- package/dist/tools/ask-user.d.ts +0 -8
- package/dist/tools/ask-user.js +0 -47
- package/dist/tools/bash.d.ts +0 -47
- package/dist/tools/bash.js +0 -213
- package/dist/tools/edit-file.d.ts +0 -9
- package/dist/tools/edit-file.js +0 -76
- package/dist/tools/glob.d.ts +0 -8
- package/dist/tools/glob.js +0 -101
- package/dist/tools/grep.d.ts +0 -10
- package/dist/tools/grep.js +0 -76
- package/dist/tools/index.d.ts +0 -24
- package/dist/tools/index.js +0 -47
- package/dist/tools/read-files.d.ts +0 -8
- package/dist/tools/read-files.js +0 -81
- package/dist/tools/write-file.d.ts +0 -8
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* keve init - Initialize .keve/ directory and inject Skills
|
|
4
|
+
*
|
|
5
|
+
* Creates the .keve/ workspace, Playwright infrastructure,
|
|
6
|
+
* and copies keve skills into .codeflicker/skills/.
|
|
7
|
+
*
|
|
8
|
+
* Note: keveAssert is exported directly from @ks-data/keve-suite (import { keveAssert } from '@ks-data/keve-suite')
|
|
9
|
+
* — no file needs to be written to the project.
|
|
10
|
+
*/
|
|
11
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
12
|
+
if (k2 === undefined) k2 = k;
|
|
13
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
14
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
15
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
16
|
+
}
|
|
17
|
+
Object.defineProperty(o, k2, desc);
|
|
18
|
+
}) : (function(o, m, k, k2) {
|
|
19
|
+
if (k2 === undefined) k2 = k;
|
|
20
|
+
o[k2] = m[k];
|
|
21
|
+
}));
|
|
22
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
23
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
24
|
+
}) : function(o, v) {
|
|
25
|
+
o["default"] = v;
|
|
26
|
+
});
|
|
27
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
28
|
+
var ownKeys = function(o) {
|
|
29
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
30
|
+
var ar = [];
|
|
31
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
32
|
+
return ar;
|
|
33
|
+
};
|
|
34
|
+
return ownKeys(o);
|
|
35
|
+
};
|
|
36
|
+
return function (mod) {
|
|
37
|
+
if (mod && mod.__esModule) return mod;
|
|
38
|
+
var result = {};
|
|
39
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
40
|
+
__setModuleDefault(result, mod);
|
|
41
|
+
return result;
|
|
42
|
+
};
|
|
43
|
+
})();
|
|
44
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
45
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
46
|
+
};
|
|
47
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
48
|
+
exports.init = init;
|
|
49
|
+
const fs = __importStar(require("node:fs"));
|
|
50
|
+
const os = __importStar(require("node:os"));
|
|
51
|
+
const path = __importStar(require("node:path"));
|
|
52
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
53
|
+
const pw_config_1 = require("../core/pw-config");
|
|
54
|
+
// Skills bundled in this package — copied to global ~/.codeflicker/skills/
|
|
55
|
+
const KEVE_SKILLS = [
|
|
56
|
+
'keve-bddcase-gen',
|
|
57
|
+
'keve-e2e-gen',
|
|
58
|
+
'keve-test-eval',
|
|
59
|
+
'keve-static-analysis',
|
|
60
|
+
'keve-unit-gen',
|
|
61
|
+
'keve-component-gen',
|
|
62
|
+
];
|
|
63
|
+
// Agents bundled in this package — copied to global ~/.codeflicker/agents/
|
|
64
|
+
const KEVE_AGENTS = [
|
|
65
|
+
'keve-test-agent',
|
|
66
|
+
'keve-dev-test-agent',
|
|
67
|
+
];
|
|
68
|
+
const KEVE_YAML_TEMPLATE = `# ━━━ 目录结构规范(Single Source of Truth)━━━━━━━━━━━━━━━━━
|
|
69
|
+
# 输入端 → keve_test_spec/(跨 MR 共享,项目长期资产,git tracked)
|
|
70
|
+
# keve.yaml ← 本文件(全局配置 + 目录约定)
|
|
71
|
+
# keve-test.config.ts ← Playwright 配置
|
|
72
|
+
# vitest.config.ts ← Vitest 配置
|
|
73
|
+
# e2e/poms/ ← POM 类
|
|
74
|
+
# e2e/locator-catalog.json ← 定位器目录
|
|
75
|
+
# e2e/__test__/ ← E2E 测试脚本 *.spec.ts
|
|
76
|
+
# component/models/ ← 组件行为模型 *.model.ts(场景+Props+断言,即组件 fixture)
|
|
77
|
+
# component/__test__/ ← 组件测试脚本 *.test.ts(引用 model)
|
|
78
|
+
# component/registry.json ← 组件注册表
|
|
79
|
+
# component/coverage.yaml ← 组件测试覆盖索引
|
|
80
|
+
# util/__test__/ ← 单元测试脚本 *.test.ts
|
|
81
|
+
# 输出端 → .keve/(执行产物,gitignored)
|
|
82
|
+
# coverage/ ← vitest --coverage 报告(HTML/JSON)
|
|
83
|
+
# analysis-report.yaml ← 静态分析结果
|
|
84
|
+
# analysis-report.html ← 静态分析可视化报告
|
|
85
|
+
# test-plan.yaml ← 全流程进度追踪
|
|
86
|
+
# MR 级模块 → .keve/{branch}/
|
|
87
|
+
# test-cases.yaml ← BDD 用例定义
|
|
88
|
+
# test-artifacts/ ← E2E 执行产物(round-N/, latest/, confidence-data.jsonl)
|
|
89
|
+
# {MR_ID} 替换规则:KEVE_TASK_DIR=.keve/MR123 或默认 .keve/current
|
|
90
|
+
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
91
|
+
|
|
92
|
+
project:
|
|
93
|
+
name: '' # 项目名称
|
|
94
|
+
|
|
95
|
+
state:
|
|
96
|
+
current_round: 0
|
|
97
|
+
# last_run_at: auto
|
|
98
|
+
|
|
99
|
+
llm:
|
|
100
|
+
model: app-xxx
|
|
101
|
+
base_url: https://xxx
|
|
102
|
+
# api_key: from env KEVE_LLM_API_KEY
|
|
103
|
+
|
|
104
|
+
keveAPI: https://xxx
|
|
105
|
+
openAPI: https://xxx
|
|
106
|
+
|
|
107
|
+
execution:
|
|
108
|
+
timeout_ms: 1200000
|
|
109
|
+
retries: 0
|
|
110
|
+
reporter: json
|
|
111
|
+
screenshot: only-on-failure
|
|
112
|
+
`;
|
|
113
|
+
const VITEST_CONFIG_TEMPLATE = `import { defineConfig } from 'vitest/config';
|
|
114
|
+
import vue from '@vitejs/plugin-vue';
|
|
115
|
+
import * as path from 'node:path';
|
|
116
|
+
|
|
117
|
+
const projectRoot = path.resolve(__dirname, '..');
|
|
118
|
+
|
|
119
|
+
// Shared resolve config — applied to all projects
|
|
120
|
+
const sharedResolve = {
|
|
121
|
+
alias: {
|
|
122
|
+
'@': path.resolve(projectRoot, 'src'),
|
|
123
|
+
'@/keve_test_spec': path.resolve(projectRoot, 'keve_test_spec'),
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
export default defineConfig({
|
|
128
|
+
plugins: [vue()],
|
|
129
|
+
resolve: sharedResolve,
|
|
130
|
+
test: {
|
|
131
|
+
// Multi-project: unit (node) + component (jsdom)
|
|
132
|
+
projects: [
|
|
133
|
+
{
|
|
134
|
+
resolve: sharedResolve,
|
|
135
|
+
plugins: [vue()],
|
|
136
|
+
test: {
|
|
137
|
+
name: 'unit',
|
|
138
|
+
environment: 'node',
|
|
139
|
+
include: ['keve_test_spec/util/__test__/**/*.test.ts'],
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
resolve: sharedResolve,
|
|
144
|
+
plugins: [vue()],
|
|
145
|
+
test: {
|
|
146
|
+
name: 'component',
|
|
147
|
+
environment: 'jsdom',
|
|
148
|
+
include: ['keve_test_spec/component/__test__/**/*.test.ts'],
|
|
149
|
+
setupFiles: [],
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
],
|
|
153
|
+
coverage: {
|
|
154
|
+
provider: 'v8',
|
|
155
|
+
reporter: ['json', 'text', 'html'],
|
|
156
|
+
reportsDirectory: path.join(projectRoot, '.keve', 'coverage'),
|
|
157
|
+
include: ['src/utils/**', 'src/hooks/**', 'src/composables/**', 'src/components/**'],
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
});
|
|
161
|
+
`;
|
|
162
|
+
function copyDirRecursive(src, dest) {
|
|
163
|
+
if (!fs.existsSync(dest))
|
|
164
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
165
|
+
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
166
|
+
for (const entry of entries) {
|
|
167
|
+
const srcPath = path.join(src, entry.name);
|
|
168
|
+
const destPath = path.join(dest, entry.name);
|
|
169
|
+
if (entry.isDirectory()) {
|
|
170
|
+
copyDirRecursive(srcPath, destPath);
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
fs.copyFileSync(srcPath, destPath);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
async function init(options) {
|
|
178
|
+
const cwd = process.cwd();
|
|
179
|
+
const keveDir = path.join(cwd, '.keve');
|
|
180
|
+
console.log(chalk_1.default.cyan('\n🔧 keve init\n'));
|
|
181
|
+
// Check if .keve already exists
|
|
182
|
+
if (fs.existsSync(keveDir) && !options.force) {
|
|
183
|
+
console.log(chalk_1.default.yellow(' ⚠ .keve/ already exists. Use --force to overwrite.'));
|
|
184
|
+
console.log(chalk_1.default.gray(' Existing config preserved.'));
|
|
185
|
+
}
|
|
186
|
+
// Create directories — shared modules in project root, MR-specific under .keve/
|
|
187
|
+
const dirs = [
|
|
188
|
+
keveDir,
|
|
189
|
+
// Shared: keve_test_spec/ (cross-MR, long-term assets)
|
|
190
|
+
path.join(cwd, 'keve_test_spec'),
|
|
191
|
+
// E2E
|
|
192
|
+
path.join(cwd, 'keve_test_spec', 'e2e', 'poms'),
|
|
193
|
+
path.join(cwd, 'keve_test_spec', 'e2e', '__test__'),
|
|
194
|
+
// Component
|
|
195
|
+
path.join(cwd, 'keve_test_spec', 'component', 'models'),
|
|
196
|
+
path.join(cwd, 'keve_test_spec', 'component', '__test__'),
|
|
197
|
+
// Unit
|
|
198
|
+
path.join(cwd, 'keve_test_spec', 'util', '__test__'),
|
|
199
|
+
];
|
|
200
|
+
for (const dir of dirs) {
|
|
201
|
+
if (!fs.existsSync(dir)) {
|
|
202
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
203
|
+
console.log(chalk_1.default.gray(` Created: ${path.relative(cwd, dir)}`));
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
console.log(chalk_1.default.gray(` Exists: ${path.relative(cwd, dir)}`));
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
// Copy keve skills into global ~/.codeflicker/skills/
|
|
210
|
+
// Skills live in <package-root>/doc/skills/ (sibling of dist/)
|
|
211
|
+
// __dirname points to dist/commands/, so go up 2 levels to package root
|
|
212
|
+
const packageRoot = path.resolve(__dirname, '..', '..');
|
|
213
|
+
const skillsSourceDir = path.join(packageRoot, 'doc/skills');
|
|
214
|
+
const skillsTargetDir = path.join(cwd, '.codeflicker', 'skills');
|
|
215
|
+
for (const skillName of KEVE_SKILLS) {
|
|
216
|
+
const srcDir = path.join(skillsSourceDir, skillName);
|
|
217
|
+
const destDir = path.join(skillsTargetDir, skillName);
|
|
218
|
+
if (!fs.existsSync(srcDir)) {
|
|
219
|
+
console.log(chalk_1.default.yellow(` ⚠ Skill source not found: ${skillName} (skipping)`));
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
// Copy entire skill directory recursively
|
|
223
|
+
copyDirRecursive(srcDir, destDir);
|
|
224
|
+
console.log(chalk_1.default.green(` ✓ Installed Skill: ${skillName}`));
|
|
225
|
+
}
|
|
226
|
+
// Copy keve agents into global ~/.codeflicker/agents/
|
|
227
|
+
const agentsSourceDir = path.join(packageRoot, 'doc/agents');
|
|
228
|
+
const agentsTargetDir = path.join(os.homedir(), '.codeflicker', 'agents');
|
|
229
|
+
for (const agentName of KEVE_AGENTS) {
|
|
230
|
+
const srcFile = path.join(agentsSourceDir, `${agentName}.md`);
|
|
231
|
+
const destFile = path.join(agentsTargetDir, `${agentName}.md`);
|
|
232
|
+
if (!fs.existsSync(srcFile)) {
|
|
233
|
+
console.log(chalk_1.default.yellow(` ⚠ Agent source not found: ${agentName} (skipping)`));
|
|
234
|
+
continue;
|
|
235
|
+
}
|
|
236
|
+
fs.mkdirSync(agentsTargetDir, { recursive: true });
|
|
237
|
+
fs.copyFileSync(srcFile, destFile);
|
|
238
|
+
console.log(chalk_1.default.green(` ✓ Installed Agent: ${agentName}`));
|
|
239
|
+
}
|
|
240
|
+
// Write keve.yaml(with directory spec comments)
|
|
241
|
+
const keveYamlPath = path.join(cwd, 'keve_test_spec', 'keve.yaml');
|
|
242
|
+
if (!fs.existsSync(keveYamlPath) || options.force) {
|
|
243
|
+
fs.writeFileSync(keveYamlPath, KEVE_YAML_TEMPLATE, 'utf-8');
|
|
244
|
+
console.log(chalk_1.default.green(' ✓ Created keve.yaml (with directory spec)'));
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
console.log(chalk_1.default.gray(' Exists: keve.yaml (preserved)'));
|
|
248
|
+
}
|
|
249
|
+
// Write Playwright config
|
|
250
|
+
const pwConfigPath = path.join(cwd, 'keve_test_spec', 'keve-test.config.ts');
|
|
251
|
+
const pwOptions = {
|
|
252
|
+
baseUrl: options.baseUrl || process.env.BASE_URL || process.env.PWGEN_TARGET_URL,
|
|
253
|
+
cdp: options.cdp,
|
|
254
|
+
webServer: !options.cdp,
|
|
255
|
+
};
|
|
256
|
+
if (!fs.existsSync(pwConfigPath) || options.force) {
|
|
257
|
+
fs.writeFileSync(pwConfigPath, (0, pw_config_1.generatePwConfig)(pwOptions), 'utf-8');
|
|
258
|
+
console.log(chalk_1.default.green(' ✓ Created keve-test.config.ts'));
|
|
259
|
+
console.log(chalk_1.default.gray(` baseURL: ${pwOptions.baseUrl || '(from env)'}`));
|
|
260
|
+
console.log(chalk_1.default.gray(` CDP: ${pwOptions.cdp ? 'enabled' : 'disabled'}`));
|
|
261
|
+
}
|
|
262
|
+
else {
|
|
263
|
+
const existingContent = fs.readFileSync(pwConfigPath, 'utf-8');
|
|
264
|
+
if (!existingContent.includes('keve-report') && !existingContent.includes('KeveReporter')) {
|
|
265
|
+
console.log(chalk_1.default.yellow(' ⚠ keve-test.config.ts exists but missing KeveReporter'));
|
|
266
|
+
console.log(chalk_1.default.gray(' Run: keve config --force to regenerate'));
|
|
267
|
+
}
|
|
268
|
+
else {
|
|
269
|
+
console.log(chalk_1.default.gray(' Exists: keve-test.config.ts (preserved)'));
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
// Write Vitest config
|
|
273
|
+
const vitestConfigPath = path.join(cwd, 'keve_test_spec', 'vitest.config.ts');
|
|
274
|
+
if (!fs.existsSync(vitestConfigPath) || options.force) {
|
|
275
|
+
fs.writeFileSync(vitestConfigPath, VITEST_CONFIG_TEMPLATE, 'utf-8');
|
|
276
|
+
console.log(chalk_1.default.green(' ✓ Created vitest.config.ts'));
|
|
277
|
+
}
|
|
278
|
+
else {
|
|
279
|
+
console.log(chalk_1.default.gray(' Exists: vitest.config.ts (preserved)'));
|
|
280
|
+
}
|
|
281
|
+
// Note: keveAssert is exported from @ks-data/keve-suite directly
|
|
282
|
+
// No file needs to be written — users import it:
|
|
283
|
+
// import { keveAssert } from '@ks-data/keve-suite';
|
|
284
|
+
console.log(chalk_1.default.gray(' ⓘ keveAssert available via: import { keveAssert } from "@ks-data/keve-suite"'));
|
|
285
|
+
console.log(chalk_1.default.green('\n ✓ keve init complete'));
|
|
286
|
+
console.log(chalk_1.default.gray('\n Next steps:'));
|
|
287
|
+
console.log(chalk_1.default.gray(' 1. keve dev-test — 静态分析 + 单测 + 组件测试(开发阶段)'));
|
|
288
|
+
console.log(chalk_1.default.gray(' 2. keve bddcase — 生成 BDD 测试用例'));
|
|
289
|
+
console.log(chalk_1.default.gray(' 3. keve e2e-gen — 生成 Playwright E2E 脚本'));
|
|
290
|
+
console.log(chalk_1.default.gray(' 4. keve run — 执行 E2E + 评估 + 报告'));
|
|
291
|
+
console.log(chalk_1.default.gray(' 5. npx playwright install — 安装 Playwright 浏览器(首次)\n'));
|
|
292
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* keve report - Generate report-data.json
|
|
3
|
+
*
|
|
4
|
+
* Usually called automatically by `keve run` after test execution.
|
|
5
|
+
* Only generates report-data.json — frontend Vue report page consumes this.
|
|
6
|
+
*/
|
|
7
|
+
export interface ReportOptions {
|
|
8
|
+
results?: string;
|
|
9
|
+
cases?: string;
|
|
10
|
+
round?: string;
|
|
11
|
+
}
|
|
12
|
+
export declare function report(options: ReportOptions): Promise<void>;
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* keve report - Generate report-data.json
|
|
4
|
+
*
|
|
5
|
+
* Usually called automatically by `keve run` after test execution.
|
|
6
|
+
* Only generates report-data.json — frontend Vue report page consumes this.
|
|
7
|
+
*/
|
|
8
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
9
|
+
if (k2 === undefined) k2 = k;
|
|
10
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
11
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
12
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
13
|
+
}
|
|
14
|
+
Object.defineProperty(o, k2, desc);
|
|
15
|
+
}) : (function(o, m, k, k2) {
|
|
16
|
+
if (k2 === undefined) k2 = k;
|
|
17
|
+
o[k2] = m[k];
|
|
18
|
+
}));
|
|
19
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
20
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
21
|
+
}) : function(o, v) {
|
|
22
|
+
o["default"] = v;
|
|
23
|
+
});
|
|
24
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
25
|
+
var ownKeys = function(o) {
|
|
26
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
27
|
+
var ar = [];
|
|
28
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
29
|
+
return ar;
|
|
30
|
+
};
|
|
31
|
+
return ownKeys(o);
|
|
32
|
+
};
|
|
33
|
+
return function (mod) {
|
|
34
|
+
if (mod && mod.__esModule) return mod;
|
|
35
|
+
var result = {};
|
|
36
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
37
|
+
__setModuleDefault(result, mod);
|
|
38
|
+
return result;
|
|
39
|
+
};
|
|
40
|
+
})();
|
|
41
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
42
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
43
|
+
};
|
|
44
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
|
+
exports.report = report;
|
|
46
|
+
const fs = __importStar(require("node:fs"));
|
|
47
|
+
const path = __importStar(require("node:path"));
|
|
48
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
49
|
+
const config_1 = require("../config");
|
|
50
|
+
const index_1 = require("../index");
|
|
51
|
+
/**
|
|
52
|
+
* Resolve paths for report inputs
|
|
53
|
+
*/
|
|
54
|
+
function resolvePaths(options) {
|
|
55
|
+
const keveDir = (0, config_1.findKeveDir)();
|
|
56
|
+
// Determine round
|
|
57
|
+
let round = null;
|
|
58
|
+
if (options.round) {
|
|
59
|
+
round = parseInt(options.round, 10);
|
|
60
|
+
}
|
|
61
|
+
else if (keveDir) {
|
|
62
|
+
const branchDir = (0, config_1.getBranchDir)(keveDir);
|
|
63
|
+
const artifactsBase = branchDir ? path.join(branchDir, 'test-artifacts') : path.join(keveDir, 'test-artifacts');
|
|
64
|
+
if (fs.existsSync(artifactsBase)) {
|
|
65
|
+
const dirs = fs.readdirSync(artifactsBase)
|
|
66
|
+
.filter(d => d.startsWith('round-'))
|
|
67
|
+
.map(d => parseInt(d.replace('round-', ''), 10))
|
|
68
|
+
.filter(n => !isNaN(n));
|
|
69
|
+
if (dirs.length > 0)
|
|
70
|
+
round = Math.max(...dirs);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// Resolve test-results.json
|
|
74
|
+
let resultsPath = options.results;
|
|
75
|
+
if (!resultsPath && keveDir && round !== null) {
|
|
76
|
+
const branchDir = (0, config_1.getBranchDir)(keveDir);
|
|
77
|
+
const resultBase = branchDir ? path.join(branchDir, 'test-artifacts', `round-${round}`) : path.join(keveDir, 'test-artifacts', `round-${round}`);
|
|
78
|
+
const candidate = path.join(resultBase, 'test-results.json');
|
|
79
|
+
if (fs.existsSync(candidate))
|
|
80
|
+
resultsPath = candidate;
|
|
81
|
+
}
|
|
82
|
+
// Resolve test-cases.yaml
|
|
83
|
+
let casesPath = options.cases;
|
|
84
|
+
if (!casesPath && keveDir) {
|
|
85
|
+
const candidates = [
|
|
86
|
+
path.join(keveDir, 'test-cases.yaml'),
|
|
87
|
+
path.join(process.cwd(), 'keve_test_spec', 'test-cases.yaml'),
|
|
88
|
+
];
|
|
89
|
+
for (const c of candidates) {
|
|
90
|
+
if (fs.existsSync(c)) {
|
|
91
|
+
casesPath = c;
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
// Resolve confidence-data.jsonl
|
|
97
|
+
let confidencePath = '';
|
|
98
|
+
if (keveDir && round !== null) {
|
|
99
|
+
const branchDir = (0, config_1.getBranchDir)(keveDir);
|
|
100
|
+
const resultBase = branchDir ? path.join(branchDir, 'test-artifacts', `round-${round}`) : path.join(keveDir, 'test-artifacts', `round-${round}`);
|
|
101
|
+
const candidate = path.join(resultBase, 'confidence-data.jsonl');
|
|
102
|
+
if (fs.existsSync(candidate))
|
|
103
|
+
confidencePath = candidate;
|
|
104
|
+
}
|
|
105
|
+
return { resultsPath, casesPath, confidencePath, round, keveDir };
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Generate report-data.json only
|
|
109
|
+
*/
|
|
110
|
+
async function invokeReport(options) {
|
|
111
|
+
const { resultsPath, casesPath, confidencePath, keveDir } = resolvePaths(options);
|
|
112
|
+
if (!resultsPath || !fs.existsSync(resultsPath)) {
|
|
113
|
+
console.error(chalk_1.default.red(' ✗ test-results.json not found'));
|
|
114
|
+
console.error(chalk_1.default.gray(` Use --results to specify path`));
|
|
115
|
+
process.exit(1);
|
|
116
|
+
}
|
|
117
|
+
const projectRoot = keveDir || path.dirname(resultsPath);
|
|
118
|
+
console.log(chalk_1.default.cyan('\n📊 keve report\n'));
|
|
119
|
+
try {
|
|
120
|
+
const reportData = await (0, index_1.generateReportData)({
|
|
121
|
+
projectRoot,
|
|
122
|
+
resultsPath,
|
|
123
|
+
confidencePath: confidencePath || undefined,
|
|
124
|
+
casesPath: casesPath || undefined,
|
|
125
|
+
});
|
|
126
|
+
const roundDir = reportData.roundDir;
|
|
127
|
+
const latestDir = reportData.latestDir;
|
|
128
|
+
fs.mkdirSync(roundDir, { recursive: true });
|
|
129
|
+
// 只写 round-N/report-data.json
|
|
130
|
+
const json = JSON.stringify(reportData, null, 2);
|
|
131
|
+
fs.writeFileSync(path.join(roundDir, 'report-data.json'), json);
|
|
132
|
+
// latest → round-N symlink
|
|
133
|
+
try {
|
|
134
|
+
if (fs.existsSync(latestDir)) {
|
|
135
|
+
const stat = fs.lstatSync(latestDir);
|
|
136
|
+
if (stat.isSymbolicLink()) {
|
|
137
|
+
fs.unlinkSync(latestDir);
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
fs.rmSync(latestDir, { recursive: true, force: true });
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
fs.symlinkSync(path.basename(roundDir), latestDir, 'junction');
|
|
144
|
+
}
|
|
145
|
+
catch { /* ignore latest symlink error */ }
|
|
146
|
+
const summaryInner = reportData.summary?.summary || reportData.summary;
|
|
147
|
+
console.log(chalk_1.default.green(` ✓ report-data.json generated at ${path.relative(process.cwd(), path.join(roundDir, 'report-data.json'))}`));
|
|
148
|
+
console.log(` Total: ${summaryInner.total} | Passed: ${summaryInner.passed} | Failed: ${summaryInner.failed}`);
|
|
149
|
+
}
|
|
150
|
+
catch (err) {
|
|
151
|
+
console.error(chalk_1.default.red(` ✗ Report generation failed: ${err.message}`));
|
|
152
|
+
if (process.env.KEVE_VERBOSE)
|
|
153
|
+
console.error(err);
|
|
154
|
+
process.exit(1);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
async function report(options) {
|
|
158
|
+
await invokeReport(options);
|
|
159
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* keve run - Execute Playwright tests
|
|
3
|
+
*
|
|
4
|
+
* Wraps `npx playwright test` with round management and output organization.
|
|
5
|
+
* Does NOT hardcode reporter or resultDir — the Playwright config handles those.
|
|
6
|
+
* Only adds: round number management, retry-from/cases filtering, and post-run report generation.
|
|
7
|
+
*/
|
|
8
|
+
export interface RunOptions {
|
|
9
|
+
config?: string;
|
|
10
|
+
round?: string;
|
|
11
|
+
retryFrom?: string;
|
|
12
|
+
cases?: string;
|
|
13
|
+
grep?: string;
|
|
14
|
+
verbose?: boolean;
|
|
15
|
+
}
|
|
16
|
+
export interface RunResult {
|
|
17
|
+
exitCode: number;
|
|
18
|
+
resultsFile: string;
|
|
19
|
+
resultDir: string;
|
|
20
|
+
taskDir: string;
|
|
21
|
+
round: number;
|
|
22
|
+
passed: number;
|
|
23
|
+
failed: number;
|
|
24
|
+
skipped: number;
|
|
25
|
+
}
|
|
26
|
+
export declare class KeveRunError extends Error {
|
|
27
|
+
constructor(message: string);
|
|
28
|
+
}
|
|
29
|
+
export declare function run(options: RunOptions): Promise<RunResult>;
|