🎉 init: 小龙的工作空间
This commit is contained in:
@@ -0,0 +1,206 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* 合同管理工具 — 完整 E2E 生成 + 验证
|
||||
* 使用 Certified Software Generator v1.1 的标准 Pipeline
|
||||
* 不修改任何 Generator 源码
|
||||
*/
|
||||
|
||||
import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync } from "node:fs";
|
||||
import { resolve, dirname, join } from "node:path";
|
||||
import { execSync } from "node:child_process";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const __dirname = dirname(resolve(fileURLToPath(import.meta.url)));
|
||||
const WORKSPACE = resolve(__dirname, "..");
|
||||
const SCRIPTS = resolve(WORKSPACE, "scripts");
|
||||
const B = resolve(WORKSPACE, ".benchmark", "contract-mgmt");
|
||||
|
||||
const INPUT = "开发一个合同管理桌面工具,支持合同创建、合同审批、合同到期提醒、客户管理、合同金额统计、操作日志";
|
||||
|
||||
mkdirSync(B, { recursive: true });
|
||||
|
||||
function cmd(c, opts = {}) {
|
||||
try {
|
||||
return execSync(c, { cwd: WORKSPACE, encoding: "utf8", ...opts, stdio: opts.stdio ?? "pipe" });
|
||||
} catch (e) {
|
||||
return { error: e.message, stderr: e.stderr?.toString() || "", stdout: e.stdout?.toString() || "" };
|
||||
}
|
||||
}
|
||||
|
||||
function runAgent(script, args) {
|
||||
const t0 = Date.now();
|
||||
const result = cmd(`node ${join(SCRIPTS, script)} ${args}`);
|
||||
const ms = Date.now() - t0;
|
||||
if (result.error) {
|
||||
try { return { ms, ...JSON.parse(result.stdout?.trim() || "{}") }; } catch { return { ms, error: result.error }; }
|
||||
}
|
||||
try { return { ms, ...JSON.parse(result.trim()) }; } catch { return { ms, raw: result }; }
|
||||
}
|
||||
|
||||
function countFiles(dir) {
|
||||
try {
|
||||
return parseInt(execSync(`find ${dir} -type f 2>/dev/null | wc -l`, { encoding: "utf8", cwd: WORKSPACE }).trim(), 10);
|
||||
} catch { return 0; }
|
||||
}
|
||||
|
||||
// Validation functions (same as e2e-benchmark-v1.mjs)
|
||||
function validateFrontend(dir) {
|
||||
if (!existsSync(dir)) return { pass: false, reason: "dir missing" };
|
||||
const checks = {
|
||||
packageJson: existsSync(join(dir, "package.json")),
|
||||
appDir: existsSync(join(dir, "app")),
|
||||
services: existsSync(join(dir, "services")) || existsSync(join(dir, "src", "services")),
|
||||
types: existsSync(join(dir, "types")) || existsSync(join(dir, "src", "types")),
|
||||
};
|
||||
const fileCount = countFiles(dir);
|
||||
return { pass: checks.packageJson && checks.appDir && fileCount > 10, fileCount, ...checks };
|
||||
}
|
||||
|
||||
function validateBackend(dir) {
|
||||
if (!existsSync(dir)) return { pass: false, reason: "dir missing" };
|
||||
const checks = {
|
||||
packageJson: existsSync(join(dir, "package.json")),
|
||||
routes: existsSync(join(dir, "src", "routes")),
|
||||
services: existsSync(join(dir, "src", "services")),
|
||||
auth: existsSync(join(dir, "src", "routes", "auth.ts")) || existsSync(join(dir, "src", "routes", "auth-route.ts")),
|
||||
db: existsSync(join(dir, "src", "db")) || existsSync(join(dir, "src", "schema")),
|
||||
};
|
||||
const fileCount = countFiles(dir);
|
||||
return { pass: checks.packageJson && checks.routes && checks.services && fileCount > 10, fileCount, ...checks };
|
||||
}
|
||||
|
||||
function validateFullstack(dir) {
|
||||
if (!existsSync(dir)) return { pass: false, reason: "dir missing" };
|
||||
const checks = {
|
||||
packageJson: existsSync(join(dir, "package.json")),
|
||||
web: existsSync(join(dir, "apps", "web")),
|
||||
api: existsSync(join(dir, "apps", "api")),
|
||||
shared: existsSync(join(dir, "packages")),
|
||||
};
|
||||
const fileCount = countFiles(dir);
|
||||
return { pass: checks.packageJson && checks.web && checks.api && fileCount > 30, fileCount, ...checks };
|
||||
}
|
||||
|
||||
function validateElectron(dir) {
|
||||
if (!existsSync(dir)) return { pass: false, reason: "dir missing" };
|
||||
const checks = {
|
||||
packageJson: existsSync(join(dir, "package.json")),
|
||||
main: existsSync(join(dir, "electron", "main.ts")),
|
||||
preload: existsSync(join(dir, "electron", "preload.ts")),
|
||||
ipc: existsSync(join(dir, "electron", "ipc.ts")),
|
||||
yml: existsSync(join(dir, "electron-builder.yml")),
|
||||
};
|
||||
const fileCount = countFiles(dir);
|
||||
return { pass: checks.packageJson && checks.main && checks.preload && fileCount >= 8, fileCount, ...checks };
|
||||
}
|
||||
|
||||
function validateRelease(dir) {
|
||||
if (!existsSync(dir)) return { pass: false, reason: "dir missing" };
|
||||
const rDir = join(dir, "release");
|
||||
const checks = {
|
||||
versionJson: existsSync(join(rDir, "version.json")),
|
||||
manifest: existsSync(join(rDir, "manifests", "manifest.json")),
|
||||
checksums: existsSync(join(rDir, "checksums", "checksums.txt")),
|
||||
releaseNotes: existsSync(join(rDir, "release-notes", "release-notes.md")),
|
||||
buildInfo: existsSync(join(rDir, "build-info.json")),
|
||||
};
|
||||
if (checks.checksums) {
|
||||
const lines = readFileSync(join(rDir, "checksums", "checksums.txt"), "utf8").trim().split("\n").filter(l => l.length > 0);
|
||||
checks.checksumCount = lines.length;
|
||||
checks.checksumsValid = lines.every(l => l.split(/\s+/)[0]?.length === 64);
|
||||
}
|
||||
if (checks.buildInfo) {
|
||||
try {
|
||||
const bi = JSON.parse(readFileSync(join(rDir, "build-info.json"), "utf8"));
|
||||
checks.buildInfoValid = !!(bi.nodeVersion && bi.generatorVersion);
|
||||
} catch { checks.buildInfoValid = false; }
|
||||
}
|
||||
const fileCount = countFiles(dir);
|
||||
return { pass: checks.versionJson && checks.manifest && checks.checksums && checks.releaseNotes && checks.buildInfo, fileCount, ...checks };
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════
|
||||
// MAIN
|
||||
// ═══════════════════════════════════════════════════════
|
||||
|
||||
console.log("═".repeat(60));
|
||||
console.log(" 合同管理工具 — Certified Generator v1.1 E2E 验证");
|
||||
console.log("═".repeat(60));
|
||||
console.log(`\n需求: ${INPUT}\n`);
|
||||
|
||||
// SF-01: PRD
|
||||
console.log("▸ SF-01 Project Intake...");
|
||||
const s1 = runAgent("project-intake-agent.mjs", `--input "${INPUT}" --output "${join(B, "prd.json")}"`);
|
||||
console.log(` ✓ PRD: ${s1.projectName || "?"} (${s1.ms}ms) ${s1.error ? "❌ " + s1.error : "✅"}`);
|
||||
|
||||
// SF-02: Architecture
|
||||
console.log("▸ SF-02 Architecture...");
|
||||
const s2 = runAgent("architecture-agent.mjs", `--input "${join(B, "prd.json")}" --output "${join(B, "arch.json")}"`);
|
||||
console.log(` ✓ Architecture: ${s2.moduleCount || "?"} modules (${s2.ms}ms) ${s2.error ? "❌ " + s2.error : "✅"}`);
|
||||
|
||||
// SF-03: Frontend
|
||||
console.log("▸ SF-03 Frontend Builder...");
|
||||
const feOut = join(B, "frontend");
|
||||
const s3 = runAgent("frontend-builder-agent.mjs", `--prd "${join(B, "prd.json")}" --arch "${join(B, "arch.json")}" --output "${feOut}"`);
|
||||
const feVal = validateFrontend(feOut);
|
||||
console.log(` ✓ Frontend: ${feVal.fileCount} files (${s3.ms}ms) ${feVal.pass ? "✅" : "❌"}`);
|
||||
|
||||
// SF-04: Backend
|
||||
console.log("▸ SF-04 Backend Builder...");
|
||||
const beOut = join(B, "backend");
|
||||
const s4 = runAgent("backend-builder-agent.mjs", `--prd "${join(B, "prd.json")}" --arch "${join(B, "arch.json")}" --output "${beOut}"`);
|
||||
const beVal = validateBackend(beOut);
|
||||
console.log(` ✓ Backend: ${beVal.fileCount} files (${s4.ms}ms) ${beVal.pass ? "✅" : "❌"}`);
|
||||
|
||||
// SF-05: Fullstack
|
||||
console.log("▸ SF-05 Fullstack Composer...");
|
||||
const fsOut = join(B, "fullstack");
|
||||
const s5 = runAgent("fullstack-composer-agent.mjs", `--prd "${join(B, "prd.json")}" --arch "${join(B, "arch.json")}" --output "${fsOut}"`);
|
||||
const fsVal = validateFullstack(fsOut);
|
||||
console.log(` ✓ Fullstack: ${fsVal.fileCount} files (${s5.ms}ms) ${fsVal.pass ? "✅" : "❌"}`);
|
||||
|
||||
// SF-06: Electron
|
||||
console.log("▸ SF-06 Electron Builder...");
|
||||
const elOut = join(B, "electron");
|
||||
const s6 = runAgent("electron-builder-agent.mjs", `--input "${fsOut}" --output "${elOut}" --prd "${join(B, "prd.json")}"`);
|
||||
const elVal = validateElectron(elOut);
|
||||
console.log(` ✓ Electron: ${elVal.fileCount} files (${s6.ms}ms) ${elVal.pass ? "✅" : "❌"}`);
|
||||
|
||||
// SF-07: Release
|
||||
console.log("▸ SF-07 Release Builder...");
|
||||
const rlOut = join(B, "release");
|
||||
const s7 = runAgent("release-builder-agent.mjs", `--input "${fsOut}" --output "${rlOut}"`);
|
||||
const rlVal = validateRelease(rlOut);
|
||||
console.log(` ✓ Release: ${rlVal.fileCount} files (${s7.ms}ms) ${rlVal.pass ? "✅" : "❌"}`);
|
||||
|
||||
// Summary
|
||||
const totalMs = s1.ms + s2.ms + s3.ms + s4.ms + s5.ms + s6.ms + s7.ms;
|
||||
const totalFiles = (s3.stats?.totalFiles || feVal.fileCount) + (s4.stats?.totalFiles || beVal.fileCount) + (s5.stats?.totalFiles || fsVal.fileCount) + (s6.stats?.totalFiles || elVal.fileCount) + (s7.stats?.totalFiles || rlVal.fileCount);
|
||||
const allPass = feVal.pass && beVal.pass && fsVal.pass && elVal.pass && rlVal.pass;
|
||||
|
||||
console.log("\n" + "═".repeat(60));
|
||||
console.log(` 总计: ${totalFiles} files | ${totalMs}ms | ${allPass ? "✅ ALL PASS" : "❌ HAS FAILURES"}`);
|
||||
console.log("═".repeat(60));
|
||||
|
||||
// Output summary as JSON
|
||||
const summary = {
|
||||
input: INPUT,
|
||||
projectName: s1.projectName,
|
||||
domain: s1.domain,
|
||||
stages: {
|
||||
prd: { ms: s1.ms, error: s1.error },
|
||||
arch: { ms: s2.ms, modules: s2.moduleCount, error: s2.error },
|
||||
frontend: { ms: s3.ms, files: feVal.fileCount, pass: feVal.pass, error: s3.error },
|
||||
backend: { ms: s4.ms, files: beVal.fileCount, pass: beVal.pass, error: s4.error },
|
||||
fullstack: { ms: s5.ms, files: fsVal.fileCount, pass: fsVal.pass, error: s5.error },
|
||||
electron: { ms: s6.ms, files: elVal.fileCount, pass: elVal.pass, error: s6.error },
|
||||
release: { ms: s7.ms, files: rlVal.fileCount, pass: rlVal.pass, error: s7.error },
|
||||
},
|
||||
totalMs,
|
||||
totalFiles,
|
||||
allPass,
|
||||
outputDir: B,
|
||||
};
|
||||
|
||||
writeFileSync(join(B, "summary.json"), JSON.stringify(summary, null, 2));
|
||||
console.log(`\nSummary: ${join(B, "summary.json")}`);
|
||||
Reference in New Issue
Block a user