diff --git a/scripts/dashboard-web.js b/scripts/dashboard-web.js index c4a54ae81..cd6cdecb9 100644 --- a/scripts/dashboard-web.js +++ b/scripts/dashboard-web.js @@ -54,9 +54,7 @@ const PORT = parsePort(process.argv[2] || process.env.ECC_DASHBOARD_PORT || '345 const HOST = resolveDashboardHost(); const ROOT = path.resolve(__dirname, '..'); -function readFrontmatter(p) { - try { - const c = fs.readFileSync(p, 'utf8'); +function parseFrontmatter(c) { const m = c.match(/^---\n([\s\S]*?)\n---/); if (!m) return {}; const fm = {}; @@ -73,9 +71,18 @@ function readFrontmatter(p) { } fm._body = c.replace(/^---[\s\S]*?---\n*/, '').trim(); return fm; - } catch { return {}; } } -function readSkill(p) { try { const c = fs.readFileSync(p, 'utf8'); const fm = readFrontmatter(p); return { d: fm.description || '', b: c.replace(/^---[\s\S]*?---\n*/, '').trim() }; } catch { return { d: '', b: '' }; } } +function readFrontmatter(p) { + try { return parseFrontmatter(fs.readFileSync(p, 'utf8')); } + catch { return {}; } +} +function readSkill(p) { + try { + const c = fs.readFileSync(p, 'utf8'); + const fm = parseFrontmatter(c); + return { d: fm.description || '', b: c.replace(/^---[\s\S]*?---\n*/, '').trim() }; + } catch { return { d: '', b: '' }; } +} function loadAgents(_root) { const root = _root || ROOT; diff --git a/tests/scripts/dashboard-web.test.js b/tests/scripts/dashboard-web.test.js index b3809def4..b06ab2e2c 100644 --- a/tests/scripts/dashboard-web.test.js +++ b/tests/scripts/dashboard-web.test.js @@ -393,6 +393,28 @@ test('readSkill parses skill frontmatter and body', () => { cleanup(testRoot); }); +test('readSkill reads each skill file only once', () => { + const { readSkill } = require(SCRIPT); + testRoot = createTempDir('ecc-test-'); + const skillPath = path.join(testRoot, 'SKILL.md'); + writeFile(testRoot, 'SKILL.md', '---\ndescription: A test skill\n---\nbody'); + const originalReadFileSync = fs.readFileSync; + let readCount = 0; + fs.readFileSync = (...args) => { + readCount++; + return originalReadFileSync(...args); + }; + + try { + readSkill(skillPath); + } finally { + fs.readFileSync = originalReadFileSync; + cleanup(testRoot); + } + + assert.strictEqual(readCount, 1); +}); + test('readSkill returns empty defaults for missing file', () => { const { readSkill } = require(SCRIPT); const skill = readSkill('/nonexistent/skill/SKILL.md');