48 lines
2.0 KiB
JavaScript
Raw Permalink Normal View History

#!/usr/bin/env node
"use strict";
const fs = require("node:fs");
const path = require("node:path");
function walk(root, out = []) {
for (const entry of fs.readdirSync(root, { withFileTypes: true })) {
if ([".git", "node_modules"].includes(entry.name)) continue;
const full = path.join(root, entry.name);
if (entry.isDirectory()) walk(full, out);
else if (entry.name === "MODULE.hdlp") out.push(full);
}
return out;
}
function parseModule(file, repoRoot) {
const text = fs.readFileSync(file, "utf8");
const block = text.match(/```yaml\s*\n([\s\S]*?)```/);
if (!block) throw new Error(`${file}: missing yaml block`);
const data = {};
for (const line of block[1].split("\n")) {
const match = line.match(/^([a-z_]+):\s*(.*?)\s*$/);
if (match) data[match[1]] = match[2].replace(/^['"]|['"]$/g, "");
}
for (const key of ["module_id", "name", "human_id", "persona_id", "origin_memory_id", "origin_memory_path", "source_path", "runtime_id", "status"]) {
if (!data[key]) throw new Error(`${file}: missing ${key}`);
}
if (!/^[A-Z0-9-]+$/.test(data.module_id) || !data.module_id.includes("-MOD-")) throw new Error(`${file}: invalid module_id`);
return { ...data, manifest_path: path.relative(repoRoot, file).split(path.sep).join("/") };
}
function buildRegistry(repoRoot) {
const modules = walk(repoRoot).map(file => parseModule(file, repoRoot));
const seen = new Set();
for (const item of modules) {
if (seen.has(item.module_id)) throw new Error(`duplicate module_id: ${item.module_id}`);
seen.add(item.module_id);
}
modules.sort((a, b) => a.module_id.localeCompare(b.module_id));
return { schema: "ZL-MODULE-REGISTRY-1", generated_at: new Date().toISOString(), module_count: modules.length, modules };
}
if (require.main === module) {
const repoRoot = path.resolve(process.argv[2] || path.join(__dirname, "../.."));
process.stdout.write(`${JSON.stringify(buildRegistry(repoRoot), null, 2)}\n`);
}
module.exports = { buildRegistry, parseModule };