#!/usr/bin/env node import { readFile } from "node:fs/promises"; import path from "node:path"; const root = path.resolve(process.argv[2] || "."); const planId = process.argv[3]; const skillName = process.argv[4]; if (!planId || !skillName) { console.error("Usage: node scripts/validate_sgrobot_artifacts.mjs "); process.exit(1); } const planPath = path.join(root, "plan_templates", planId, "PlanTemplate.toml"); const skillPath = path.join(root, ".sgclaw-workspace", "skills", skillName, "SKILL.toml"); const scriptPath = path.join(root, ".sgclaw-workspace", "skills", skillName, "scripts", "execute_trace.js"); const tracePath = path.join(root, ".sgclaw-workspace", "skills", skillName, "trace.semantic.json"); const [plan, skill, script, traceText] = await Promise.all([ readFile(planPath, "utf8"), readFile(skillPath, "utf8"), readFile(scriptPath, "utf8"), readFile(tracePath, "utf8"), ]); const trace = JSON.parse(traceText); const checks = [ ["plan has [plan]", plan.includes("[plan]")], ["plan id matches", plan.includes(`id = "${planId}"`)], ["plan opens page via browser.open_url", plan.includes('skills = ["browser.open_url"]')], ["plan calls generated skill", plan.includes(`skills = ["${skillName}"]`)], ["plan saves result", plan.includes('skills = ["json.save_result"]')], ["write flow requires approval", plan.includes("requires_approval = true")], ["skill name matches", skill.includes(`name = "${skillName}"`)], ["skill uses sgBrowser action", skill.includes('kind = "sgbrowser_action"')], ["skill uses sgBrowser JS by domain", skill.includes('command = "sgBrowserExcuteJsCodeByDomain"')], ["skill references execute script", skill.includes('script_path = "scripts/execute_trace.js"')], ["runtime script contains trace", script.includes(`"id": "${trace.id}"`)], ["runtime script returns structured JSON", script.includes("action_results")], ["trace has realistic steps", Array.isArray(trace.steps) && trace.steps.length >= 4], ]; const failed = checks.filter(([, ok]) => !ok); for (const [name, ok] of checks) { console.log(`${ok ? "ok" : "not ok"} - ${name}`); } if (failed.length > 0) { process.exitCode = 1; }