#!/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 };