#!/usr/bin/env node const fs = require('fs'); const path = require('path'); const { PACKAGE_ROOT, OUTPUT_CATEGORIES, OUTPUT_PERSISTENT_STORES, outputsRoot } = require('../mcp/src/core/output-paths'); const DOCS_ALLOWED = new Set(['OUTPUT-STANDARD.md', 'DASHBOARD-DEV-LOG.md', 'RELEASE.md', 'specs', 'guides', 'generated']); const RUN_DIR_RE = /^\d{6}-[a-z0-9\u4e00-\u9fa5-]+$/; const DATE_DIR_RE = /^\d{4}-\d{2}-\d{2}$/; const problems = []; function listEntries(dir) { if (!fs.existsSync(dir)) return []; return fs.readdirSync(dir, { withFileTypes: true }); } function checkOutputs() { const root = outputsRoot(); for (const entry of listEntries(root)) { if (!entry.isDirectory()) { problems.push(`outputs 根目录不允许直接放文件:outputs/${entry.name}`); continue; } if (!OUTPUT_CATEGORIES.includes(entry.name)) { problems.push(`outputs 下存在未注册类别:outputs/${entry.name}(允许:${OUTPUT_CATEGORIES.join(', ')})`); continue; } checkCategory(path.join(root, entry.name), entry.name); } } function checkCategory(dir, category) { const persistentStores = new Set(OUTPUT_PERSISTENT_STORES[category] || []); for (const entry of listEntries(dir)) { if (!entry.isDirectory()) continue; // latest 模式文件 if (persistentStores.has(entry.name)) continue; // 注册的持续知识库 if (!DATE_DIR_RE.test(entry.name)) { problems.push(`outputs/${category}/${entry.name} 不是 YYYY-MM-DD 日期目录`); continue; } const dateDir = path.join(dir, entry.name); for (const run of listEntries(dateDir)) { const rel = `outputs/${category}/${entry.name}/${run.name}`; if (!run.isDirectory()) { problems.push(`${rel} 应为 run 目录而不是文件`); continue; } if (!RUN_DIR_RE.test(run.name)) { problems.push(`${rel} 不符合 HHmmss- 命名`); } if (!fs.existsSync(path.join(dateDir, run.name, 'manifest.json'))) { problems.push(`${rel} 缺少 manifest.json`); } } } } function checkDocs() { const docsDir = path.join(PACKAGE_ROOT, 'docs'); for (const entry of listEntries(docsDir)) { if (!DOCS_ALLOWED.has(entry.name)) { problems.push(`docs 第一层存在未注册条目:docs/${entry.name}(允许:${[...DOCS_ALLOWED].join(', ')})`); } } } checkOutputs(); checkDocs(); if (problems.length) { console.error('输出目录标准校验失败:'); for (const problem of problems) console.error(` - ${problem}`); process.exit(1); } console.log('输出目录标准校验通过(outputs 与 docs 均符合 docs/OUTPUT-STANDARD.md)。');