refactor(exam): SKILL 中文化,增加用户确认阶段,默认子代理分批处理
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Validate a question JSON file against the bundled schema.
|
||||
*
|
||||
* Usage:
|
||||
* node validate.mjs <json_file>
|
||||
* echo '<json>' | node validate.mjs -
|
||||
*
|
||||
* Exit codes: 0 = valid, 1 = errors found, 2 = usage error.
|
||||
*/
|
||||
|
||||
import { readFileSync, existsSync } from "node:fs";
|
||||
import { resolve, dirname } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const SCHEMA_PATH = resolve(__dirname, "../schema/question.schema.json");
|
||||
|
||||
const VALID_TYPES = [
|
||||
"single_choice", "multiple_choice", "true_false",
|
||||
"fill_blank", "short_answer", "code_reading", "scenario",
|
||||
];
|
||||
|
||||
const ID_RE = /^(sc|mc|tf|fb|sa|cr|sn)-\d{3}$/;
|
||||
|
||||
function loadJson(path) {
|
||||
const raw = path === "-" ? readFileSync(0, "utf-8") : readFileSync(path, "utf-8");
|
||||
return JSON.parse(raw);
|
||||
}
|
||||
|
||||
function validate(data) {
|
||||
const errors = [];
|
||||
const requiredTop = ["topic", "type", "schema_version", "questions"];
|
||||
for (const field of requiredTop) {
|
||||
if (!(field in data)) errors.push(`Missing required field: ${field}`);
|
||||
}
|
||||
if (!VALID_TYPES.includes(data.type)) errors.push(`Invalid type: ${data.type}`);
|
||||
if (data.schema_version !== "1.0.0")
|
||||
errors.push(`schema_version must be '1.0.0', got '${data.schema_version}'`);
|
||||
|
||||
for (const [i, q] of (data.questions || []).entries()) {
|
||||
const p = `questions[${i}]`;
|
||||
for (const field of ["id", "type", "difficulty", "tags", "question", "explanation"]) {
|
||||
if (!(field in q)) errors.push(`${p}: missing '${field}'`);
|
||||
}
|
||||
if (!VALID_TYPES.includes(q.type)) errors.push(`${p}: invalid type '${q.type}'`);
|
||||
if (q.difficulty !== undefined && !(q.difficulty >= 1 && q.difficulty <= 5))
|
||||
errors.push(`${p}: difficulty must be 1-5`);
|
||||
if (q.id !== undefined && !ID_RE.test(q.id))
|
||||
errors.push(`${p}: id '${q.id}' doesn't match pattern {type}-{000}`);
|
||||
}
|
||||
return errors;
|
||||
}
|
||||
|
||||
function main() {
|
||||
if (process.argv.length !== 3) {
|
||||
console.error(`Usage: node ${process.argv[1]} <json_file | ->`);
|
||||
process.exit(2);
|
||||
}
|
||||
|
||||
if (!existsSync(SCHEMA_PATH)) {
|
||||
console.error(`❌ Schema not found: ${SCHEMA_PATH}`);
|
||||
process.exit(2);
|
||||
}
|
||||
|
||||
const data = loadJson(process.argv[2]);
|
||||
const errors = validate(data);
|
||||
|
||||
if (errors.length) {
|
||||
console.log(`❌ ${errors.length} error(s):`);
|
||||
for (const e of errors) console.log(` • ${e}`);
|
||||
process.exit(1);
|
||||
} else {
|
||||
console.log(`✅ Valid — ${(data.questions || []).length} question(s)`);
|
||||
process.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
Reference in New Issue
Block a user