chore: reconcile publish/agent surfaces after PR batch

- agent.yaml: register epic-* commands (#2236) and vue-review (#2241)
- package.json files: drop stray skills/ml-adoption-playbook entry (follows orphan-skill publish pattern; not in install-modules.json)
- unicode-safety: strip decorative emoji from dashboard-web.js (#2100) and brand-discovery refs (#2221) to pass the CI gate
- agent-compress: raise catalog token canary 5000 -> 6000 for the 67-agent catalog

Full suite green (2836/2836).
This commit is contained in:
Affaan Mustafa 2026-06-15 14:21:28 -04:00
parent 141286a02a
commit 6e2544ffa2
15 changed files with 320 additions and 245 deletions

View File

@ -33,9 +33,7 @@
1.
2.
3.
### Open questions / threads to pursue in later modules
3. ### Open questions / threads to pursue in later modules
### Contradictions or tensions between participants (multi-founder only)

View File

@ -37,9 +37,7 @@
### Alternative framings (vary the category or the differentiator)
1.
2.
### White-space hypothesis (what no competitor is claiming that this brand could own)
2. ### White-space hypothesis (what no competitor is claiming that this brand could own)
### Open questions / ambiguities

View File

@ -52,8 +52,6 @@
1.
2.
3.
### What the brand must never sound or look like (the anti-personality)
3. ### What the brand must never sound or look like (the anti-personality)
### Open questions / tensions with Module 50 Voice

View File

@ -56,6 +56,4 @@
1.
2.
3.
### Open questions / tensions with Module 40 Personality
3. ### Open questions / tensions with Module 40 Personality

View File

@ -32,15 +32,11 @@
### Trueline draft (Neumeier: "[Brand] is the only [category] that [unique claim].")
>
### Alternative truelines (23 variations, vary level of abstraction)
> ### Alternative truelines (23 variations, vary level of abstraction)
1.
2.
3.
### Brand story arc
3. ### Brand story arc
| Beat | Content |
|---|---|

View File

@ -166,6 +166,13 @@ commands:
- cpp-review
- cpp-test
- ecc-guide
- epic-claim
- epic-decompose
- epic-publish
- epic-review
- epic-sync
- epic-unblock
- epic-validate
- evolve
- fastapi-review
- feature-dev
@ -240,6 +247,7 @@ commands:
- test-coverage
- update-codemaps
- update-docs
- vue-review
tags:
- agent-harness
- developer-tools

View File

@ -232,7 +232,6 @@
"skills/mcp-server-patterns/",
"skills/messages-ops/",
"skills/mle-workflow/",
"skills/ml-adoption-playbook/",
"skills/motion-ui/",
"skills/mysql-patterns/",
"skills/nanoclaw-repl/",

View File

@ -363,20 +363,20 @@ function renderHTML(data) {
<div class="suggest" id="suggest"></div>
</div>
<div class="lang-wrap">
<button class="lang-btn" onclick="toggleLang()">🌐 <span id="lang-label">EN</span></button>
<button class="lang-btn" onclick="toggleLang()"> <span id="lang-label">EN</span></button>
<div class="lang-drop" id="lang-drop"></div>
</div>
<button class="icon-btn" onclick="toggleTheme()" title="Toggle theme"></button>
<button class="icon-btn" onclick="toggleTheme()" title="Toggle theme"></button>
</div>
</div>
<div class="nav" id="nav">
<button class="nav-it active" data-tab="agents" onclick="showTab('agents',this)"><span id="nav-agents">🤖 Agents</span> <span class="ct" id="nav-ct-agents"></span></button>
<button class="nav-it" data-tab="skills" onclick="showTab('skills',this)"><span id="nav-skills">📚 Skills</span> <span class="ct" id="nav-ct-skills"></span></button>
<button class="nav-it" data-tab="commands" onclick="showTab('commands',this)"><span id="nav-commands"> Commands</span> <span class="ct" id="nav-ct-commands"></span></button>
<button class="nav-it" data-tab="rules" onclick="showTab('rules',this)"><span id="nav-rules">📏 Rules</span> <span class="ct" id="nav-ct-rules"></span></button>
<button class="nav-it" data-tab="mcps" onclick="showTab('mcps',this)"><span id="nav-mcps">🔌 MCPs</span> <span class="ct" id="nav-ct-mcps"></span></button>
<button class="nav-it" data-tab="hooks" onclick="showTab('hooks',this)"><span id="nav-hooks">🪝 Hooks</span> <span class="ct" id="nav-ct-hooks"></span></button>
<button class="nav-it active" data-tab="agents" onclick="showTab('agents',this)"><span id="nav-agents"> Agents</span> <span class="ct" id="nav-ct-agents"></span></button>
<button class="nav-it" data-tab="skills" onclick="showTab('skills',this)"><span id="nav-skills"> Skills</span> <span class="ct" id="nav-ct-skills"></span></button>
<button class="nav-it" data-tab="commands" onclick="showTab('commands',this)"><span id="nav-commands"> Commands</span> <span class="ct" id="nav-ct-commands"></span></button>
<button class="nav-it" data-tab="rules" onclick="showTab('rules',this)"><span id="nav-rules"> Rules</span> <span class="ct" id="nav-ct-rules"></span></button>
<button class="nav-it" data-tab="mcps" onclick="showTab('mcps',this)"><span id="nav-mcps"> MCPs</span> <span class="ct" id="nav-ct-mcps"></span></button>
<button class="nav-it" data-tab="hooks" onclick="showTab('hooks',this)"><span id="nav-hooks"> Hooks</span> <span class="ct" id="nav-ct-hooks"></span></button>
</div>
<div class="out" id="app"></div>
@ -456,12 +456,12 @@ function applyLang() {
document.getElementById('t-contribution').textContent = t('contribution');
document.getElementById('search').placeholder = t('search');
// Update label text only — counter spans are separate siblings
document.getElementById('nav-agents').childNodes[0].textContent = '🤖 ' + t('agents');
document.getElementById('nav-skills').childNodes[0].textContent = '📚 ' + t('skills');
document.getElementById('nav-commands').childNodes[0].textContent = ' ' + t('commands');
document.getElementById('nav-rules').childNodes[0].textContent = '📏 ' + t('rules');
document.getElementById('nav-mcps').childNodes[0].textContent = '🔌 ' + t('mcps');
document.getElementById('nav-hooks').childNodes[0].textContent = '🪝 ' + t('hooks');
document.getElementById('nav-agents').childNodes[0].textContent = ' ' + t('agents');
document.getElementById('nav-skills').childNodes[0].textContent = ' ' + t('skills');
document.getElementById('nav-commands').childNodes[0].textContent = ' ' + t('commands');
document.getElementById('nav-rules').childNodes[0].textContent = ' ' + t('rules');
document.getElementById('nav-mcps').childNodes[0].textContent = ' ' + t('mcps');
document.getElementById('nav-hooks').childNodes[0].textContent = ' ' + t('hooks');
// Update counter spans by their own IDs (avoids duplicate IDs in DOM)
document.getElementById('nav-ct-agents').textContent = AGENTS.length;
document.getElementById('nav-ct-skills').textContent = SKILLS.length;
@ -514,7 +514,7 @@ function renderMain() {
const recent = recents().filter(r => r.n && /^[\\w\\-./@]+$/.test(r.n));
if (recent.length) {
const icons = {agents:'🤖',skills:'📚',commands:'',rules:'📏',mcps:'🔌',hooks:'🪝'};
const icons = {agents:'',skills:'',commands:'',rules:'',mcps:'',hooks:''};
const rb = document.createElement('div');
rb.className = 'recent-bar';
rb.innerHTML = '<span class="rb-lbl">'+t('recentlyViewed')+'</span><span class="rb-items"></span><span class="rb-clear" onclick="clearRecents()">✕ '+t('clearHistory')+'</span>';
@ -551,21 +551,21 @@ function renderAgents(list) {
const el = document.getElementById('panel-agents');
if (!el) return;
const cats = ['all','reviewer','builder','architect','security'];
const lbls = [t('all'),'👁️ '+t('reviewers'),'🔧 '+t('buildResolvers'),'🏗️ '+t('architects'),'🔒 '+t('security')];
const lbls = [t('all'),' '+t('reviewers'),' '+t('buildResolvers'),' '+t('architects'),' '+t('security')];
el.innerHTML = '<div class="filters" id="af">'+cats.map((c,i)=>'<button'+(i===0?' class="active"':'')+' onclick="filterAgents(\\''+c+'\\',this)">'+lbls[i]+'</button>').join('')+
'</div><div class="grid" id="ag">'+
list.map((a,i)=>{const m=(a.m||'').toLowerCase(),bd=m.includes('opus')?'opus':m.includes('sonnet')?'sonnet':m.includes('haiku')?'haiku':'';const tag=aType(a.n),ic=tag==='reviewer'?0:tag==='builder'?1:tag==='architect'?2:tag==='security'?3:4;
return '<div class="card" data-tag="'+tag+'" data-model="'+a.m+'" onclick="location.hash=\\'#/agents/'+encodeURIComponent(a.n)+'\\'">'+
'<div class="top"><div class="il"><div class="ic '+iBg(ic)+'">'+ICONS[ic]+'</div><span class="nm">'+esc(a.n)+'</span></div>'+(bd?'<span class="bd '+bd+'">'+a.m+'</span>':'')+'</div>'+
'<div class="desc">'+esc(a.d.slice(0,150))+'</div>'+
'<div class="tags">'+a.t.slice(0,5).map(t=>'<span class="t">'+esc(t)+'</span>').join('')+'</div><span class="ar"></span></div>';}).join('')+'</div>';
'<div class="tags">'+a.t.slice(0,5).map(t=>'<span class="t">'+esc(t)+'</span>').join('')+'</div><span class="ar"></span></div>';}).join('')+'</div>';
}
function renderSkills(list) {
const el = document.getElementById('panel-skills'); if (!el) return;
el.innerHTML = '<div class="filters" id="sf">'+['all','sec','test','pattern','design','research','data','agent','devops'].map((c,i)=>'<button'+(i===0?' class="active"':'')+' onclick="filterSkills(\\''+c+'\\',this)">'+[t('all'),'🔒 '+t('security'),'🧪 '+t('testing'),'📐 '+t('patterns'),'🎨 '+t('design'),'🔬 '+t('research'),'🗄️ '+t('data'),'🤖 '+t('agent'),'⚙️ '+t('devops')][i]+'</button>').join('')+
el.innerHTML = '<div class="filters" id="sf">'+['all','sec','test','pattern','design','research','data','agent','devops'].map((c,i)=>'<button'+(i===0?' class="active"':'')+' onclick="filterSkills(\\''+c+'\\',this)">'+[t('all'),' '+t('security'),' '+t('testing'),' '+t('patterns'),' '+t('design'),' '+t('research'),' '+t('data'),' '+t('agent'),' '+t('devops')][i]+'</button>').join('')+
'</div><div class="grid" id="sg">'+list.map((s,i)=>'<div class="card" onclick="location.hash=\\'#/skills/'+encodeURIComponent(s.n)+'\\'">'+
'<div class="top"><div class="il"><div class="ic '+iBg(i%6)+'">'+ICONS[i%6]+'</div><span class="nm">'+esc(s.n)+'</span></div></div>'+
'<div class="desc">'+esc(s.d||'—')+'</div><span class="ar"></span></div>').join('')+'</div>';
'<div class="desc">'+esc(s.d||'—')+'</div><span class="ar"></span></div>').join('')+'</div>';
}
function renderCommands(list) {
const el = document.getElementById('panel-commands'); if (!el) return;
@ -616,19 +616,19 @@ function renderRules(list) {
}
function renderMcps(list) {
const el = document.getElementById('panel-mcps'); if (!el) return;
if (!list.length) { el.innerHTML = '<div class="empty"><div class="eic">🔌</div><h3>'+esc(t('noMcps'))+'</h3><p>'+esc(t('checkMcps'))+'</p></div>'; return; }
if (!list.length) { el.innerHTML = '<div class="empty"><div class="eic"></div><h3>'+esc(t('noMcps'))+'</h3><p>'+esc(t('checkMcps'))+'</p></div>'; return; }
const grid = document.createElement('div'); grid.className = 'mcp-grid';
list.forEach(m => {
const div = document.createElement('div'); div.className = 'mcp-cd';
div.onclick = () => { location.hash = '#/mcps/'+encodeURIComponent(m.f); };
div.innerHTML = '<h3>📄 '+esc(m.f)+'</h3>'+m.s.map(s => '<span class="st">'+esc(s.n)+' <small>'+esc((s.cmd||'').slice(0,40))+'</small></span>').join('');
div.innerHTML = '<h3> '+esc(m.f)+'</h3>'+m.s.map(s => '<span class="st">'+esc(s.n)+' <small>'+esc((s.cmd||'').slice(0,40))+'</small></span>').join('');
grid.appendChild(div);
});
el.innerHTML = ''; el.appendChild(grid);
}
function renderHooks(list) {
const el = document.getElementById('panel-hooks'); if (!el) return;
if (!list.length) { el.innerHTML = '<div class="empty"><div class="eic">🪝</div><h3>'+esc(t('noHooks'))+'</h3></div>'; return; }
if (!list.length) { el.innerHTML = '<div class="empty"><div class="eic"></div><h3>'+esc(t('noHooks'))+'</h3></div>'; return; }
const wrap = document.createElement('div'); wrap.className = 'hw';
const tbl = document.createElement('table'); tbl.className = 'ht';
tbl.innerHTML = '<thead><tr><th>'+esc(t('event'))+'</th><th>'+esc(t('matcher'))+'</th><th>'+esc(t('description'))+'</th><th>'+esc(t('id'))+'</th></tr></thead><tbody></tbody>';
@ -730,7 +730,7 @@ function showSuggestions() {
const groups = {};
results.forEach(r=>{if(!groups[r.t])groups[r.t]=[];groups[r.t].push(r);});
sug.innerHTML = Object.entries(groups).map(([type,items]) =>
'<div class="sg"><div class="sg-label">'+(type==='agents'?'🤖 '+t('agents'):type==='skills'?'📚 '+t('skills'):' '+t('commands'))+'</div>'+
'<div class="sg"><div class="sg-label">'+(type==='agents'?' '+t('agents'):type==='skills'?' '+t('skills'):' '+t('commands'))+'</div>'+
items.map(r=>'<div class="si" onclick="location.hash=\\'#/'+r.t+'/'+encodeURIComponent(r.n)+'\\';document.getElementById(\\'suggest\\').classList.remove(\\'show\\');document.getElementById(\\'search\\').blur()">'+
'<span class="ic '+r.ic+'">'+r.e+'</span><span class="sn">'+esc(r.n)+'</span><span class="sd">'+esc(r.d)+'</span></div>').join('')+'</div>'
).join('');
@ -767,7 +767,7 @@ const server = http.createServer((req, res) => {
if (require.main === module) {
server.listen(PORT, () => {
console.log(`\n 🧩 ECC Capabilities → http://localhost:${PORT}\n`);
console.log(`\n ECC Capabilities → http://localhost:${PORT}\n`);
try { const { spawn } = require('child_process'); const p = process.platform; const c = p === 'darwin' ? 'open' : p === 'win32' ? 'start' : 'xdg-open'; if (c === 'start') spawn('cmd', ['/c', 'start', `http://localhost:${PORT}`], { stdio: 'ignore' }); else spawn(c, [`http://localhost:${PORT}`], { stdio: 'ignore' }); } catch { /* best-effort auto-open */ }
});
}

View File

@ -33,9 +33,7 @@
1.
2.
3.
### Open questions / threads to pursue in later modules
3. ### Open questions / threads to pursue in later modules
### Contradictions or tensions between participants (multi-founder only)

View File

@ -37,9 +37,7 @@
### Alternative framings (vary the category or the differentiator)
1.
2.
### White-space hypothesis (what no competitor is claiming that this brand could own)
2. ### White-space hypothesis (what no competitor is claiming that this brand could own)
### Open questions / ambiguities

View File

@ -52,8 +52,6 @@
1.
2.
3.
### What the brand must never sound or look like (the anti-personality)
3. ### What the brand must never sound or look like (the anti-personality)
### Open questions / tensions with Module 50 Voice

View File

@ -56,6 +56,4 @@
1.
2.
3.
### Open questions / tensions with Module 40 Personality
3. ### Open questions / tensions with Module 40 Personality

View File

@ -32,15 +32,11 @@
### Trueline draft (Neumeier: "[Brand] is the only [category] that [unique claim].")
>
### Alternative truelines (23 variations, vary level of abstraction)
> ### Alternative truelines (23 variations, vary level of abstraction)
1.
2.
3.
### Brand story arc
3. ### Brand story arc
| Beat | Content |
|---|---|

View File

@ -9,16 +9,7 @@ const path = require('path');
const fs = require('fs');
const os = require('os');
const {
parseFrontmatter,
extractSummary,
loadAgent,
loadAgents,
compressToCatalog,
compressToSummary,
buildAgentCatalog,
lazyLoadAgent,
} = require('../../scripts/lib/agent-compress');
const { parseFrontmatter, extractSummary, loadAgent, loadAgents, compressToCatalog, compressToSummary, buildAgentCatalog, lazyLoadAgent } = require('../../scripts/lib/agent-compress');
function test(name, fn) {
try {
@ -40,7 +31,8 @@ function runTests() {
// --- parseFrontmatter ---
if (test('parseFrontmatter extracts YAML frontmatter and body', () => {
if (
test('parseFrontmatter extracts YAML frontmatter and body', () => {
const content = '---\nname: test-agent\ndescription: A test\ntools: ["Read", "Grep"]\nmodel: sonnet\n---\n\nBody text here.';
const { frontmatter, body } = parseFrontmatter(content);
assert.strictEqual(frontmatter.name, 'test-agent');
@ -48,73 +40,116 @@ function runTests() {
assert.deepStrictEqual(frontmatter.tools, ['Read', 'Grep']);
assert.strictEqual(frontmatter.model, 'sonnet');
assert.ok(body.includes('Body text here.'));
})) passed++; else failed++;
})
)
passed++;
else failed++;
if (test('parseFrontmatter handles content without frontmatter', () => {
if (
test('parseFrontmatter handles content without frontmatter', () => {
const content = 'Just a regular markdown file.';
const { frontmatter, body } = parseFrontmatter(content);
assert.deepStrictEqual(frontmatter, {});
assert.strictEqual(body, content);
})) passed++; else failed++;
})
)
passed++;
else failed++;
if (test('parseFrontmatter handles colons in values', () => {
if (
test('parseFrontmatter handles colons in values', () => {
const content = '---\nname: test\ndescription: Use this: it works\n---\n\nBody.';
const { frontmatter } = parseFrontmatter(content);
assert.strictEqual(frontmatter.description, 'Use this: it works');
})) passed++; else failed++;
})
)
passed++;
else failed++;
if (test('parseFrontmatter strips surrounding quotes', () => {
if (
test('parseFrontmatter strips surrounding quotes', () => {
const content = '---\nname: "quoted-name"\n---\n\nBody.';
const { frontmatter } = parseFrontmatter(content);
assert.strictEqual(frontmatter.name, 'quoted-name');
})) passed++; else failed++;
})
)
passed++;
else failed++;
if (test('parseFrontmatter handles content ending right after closing ---', () => {
if (
test('parseFrontmatter handles content ending right after closing ---', () => {
const content = '---\nname: test\ndescription: No body\n---';
const { frontmatter, body } = parseFrontmatter(content);
assert.strictEqual(frontmatter.name, 'test');
assert.strictEqual(frontmatter.description, 'No body');
assert.strictEqual(body, '');
})) passed++; else failed++;
})
)
passed++;
else failed++;
// --- extractSummary ---
if (test('extractSummary returns the first paragraph of the body', () => {
if (
test('extractSummary returns the first paragraph of the body', () => {
const body = '# Heading\n\nThis is the first paragraph. It has two sentences.\n\nSecond paragraph.';
const summary = extractSummary(body);
assert.strictEqual(summary, 'This is the first paragraph.');
})) passed++; else failed++;
})
)
passed++;
else failed++;
if (test('extractSummary returns empty string for empty body', () => {
if (
test('extractSummary returns empty string for empty body', () => {
assert.strictEqual(extractSummary(''), '');
assert.strictEqual(extractSummary('# Only Headings\n\n## Another'), '');
})) passed++; else failed++;
})
)
passed++;
else failed++;
if (test('extractSummary skips code blocks', () => {
if (
test('extractSummary skips code blocks', () => {
const body = '```\ncode here\n```\n\nActual summary sentence.';
const summary = extractSummary(body);
assert.strictEqual(summary, 'Actual summary sentence.');
})) passed++; else failed++;
})
)
passed++;
else failed++;
if (test('extractSummary respects maxSentences', () => {
if (
test('extractSummary respects maxSentences', () => {
const body = 'First sentence. Second sentence. Third sentence.';
const one = extractSummary(body, 1);
const two = extractSummary(body, 2);
assert.strictEqual(one, 'First sentence.');
assert.strictEqual(two, 'First sentence. Second sentence.');
})) passed++; else failed++;
})
)
passed++;
else failed++;
if (test('extractSummary skips plain bullet items', () => {
if (
test('extractSummary skips plain bullet items', () => {
const body = '- plain bullet\n- another bullet\n\nActual paragraph here.';
const summary = extractSummary(body);
assert.strictEqual(summary, 'Actual paragraph here.');
})) passed++; else failed++;
})
)
passed++;
else failed++;
if (test('extractSummary skips asterisk bullets and numbered lists', () => {
if (
test('extractSummary skips asterisk bullets and numbered lists', () => {
const body = '* star bullet\n1. numbered item\n2. second item\n\nReal paragraph.';
const summary = extractSummary(body);
assert.strictEqual(summary, 'Real paragraph.');
})) passed++; else failed++;
})
)
passed++;
else failed++;
// --- loadAgent / loadAgents ---
@ -124,7 +159,8 @@ function runTests() {
fs.writeFileSync(path.join(tmpDir, 'test-agent.md'), agentContent);
fs.writeFileSync(path.join(tmpDir, 'not-an-agent.txt'), 'ignored');
if (test('loadAgent reads and parses a single agent file', () => {
if (
test('loadAgent reads and parses a single agent file', () => {
const agent = loadAgent(path.join(tmpDir, 'test-agent.md'));
assert.strictEqual(agent.name, 'test-agent');
assert.strictEqual(agent.description, 'A test agent');
@ -133,24 +169,36 @@ function runTests() {
assert.ok(agent.body.includes('Test agent body paragraph'));
assert.strictEqual(agent.fileName, 'test-agent');
assert.ok(agent.byteSize > 0);
})) passed++; else failed++;
})
)
passed++;
else failed++;
if (test('loadAgents reads all .md files from a directory', () => {
if (
test('loadAgents reads all .md files from a directory', () => {
const agents = loadAgents(tmpDir);
assert.strictEqual(agents.length, 1);
assert.strictEqual(agents[0].name, 'test-agent');
})) passed++; else failed++;
})
)
passed++;
else failed++;
if (test('loadAgents returns empty array for non-existent directory', () => {
if (
test('loadAgents returns empty array for non-existent directory', () => {
const agents = loadAgents(path.join(os.tmpdir(), 'does-not-exist-agent-compress-test'));
assert.deepStrictEqual(agents, []);
})) passed++; else failed++;
})
)
passed++;
else failed++;
// --- compressToCatalog / compressToSummary ---
const sampleAgent = loadAgent(path.join(tmpDir, 'test-agent.md'));
if (test('compressToCatalog strips body and keeps only metadata', () => {
if (
test('compressToCatalog strips body and keeps only metadata', () => {
const catalog = compressToCatalog(sampleAgent);
assert.strictEqual(catalog.name, 'test-agent');
assert.strictEqual(catalog.description, 'A test agent');
@ -158,18 +206,26 @@ function runTests() {
assert.strictEqual(catalog.model, 'haiku');
assert.strictEqual(catalog.body, undefined);
assert.strictEqual(catalog.byteSize, undefined);
})) passed++; else failed++;
})
)
passed++;
else failed++;
if (test('compressToSummary includes first paragraph summary', () => {
if (
test('compressToSummary includes first paragraph summary', () => {
const summary = compressToSummary(sampleAgent);
assert.strictEqual(summary.name, 'test-agent');
assert.ok(summary.summary.includes('Test agent body paragraph'));
assert.strictEqual(summary.body, undefined);
})) passed++; else failed++;
})
)
passed++;
else failed++;
// --- buildAgentCatalog ---
if (test('buildAgentCatalog in catalog mode produces minimal output with stats', () => {
if (
test('buildAgentCatalog in catalog mode produces minimal output with stats', () => {
const result = buildAgentCatalog(tmpDir, { mode: 'catalog' });
assert.strictEqual(result.agents.length, 1);
assert.strictEqual(result.agents[0].body, undefined);
@ -178,71 +234,101 @@ function runTests() {
assert.ok(result.stats.originalBytes > 0);
assert.ok(result.stats.compressedBytes < result.stats.originalBytes);
assert.ok(result.stats.compressedTokenEstimate > 0);
})) passed++; else failed++;
})
)
passed++;
else failed++;
if (test('buildAgentCatalog in summary mode includes summaries', () => {
if (
test('buildAgentCatalog in summary mode includes summaries', () => {
const result = buildAgentCatalog(tmpDir, { mode: 'summary' });
assert.ok(result.agents[0].summary);
assert.strictEqual(result.agents[0].body, undefined);
})) passed++; else failed++;
})
)
passed++;
else failed++;
if (test('buildAgentCatalog in full mode preserves body', () => {
if (
test('buildAgentCatalog in full mode preserves body', () => {
const result = buildAgentCatalog(tmpDir, { mode: 'full' });
assert.ok(result.agents[0].body);
})) passed++; else failed++;
})
)
passed++;
else failed++;
if (test('buildAgentCatalog throws on invalid mode', () => {
assert.throws(
() => buildAgentCatalog(tmpDir, { mode: 'invalid' }),
/Invalid mode "invalid"/
);
})) passed++; else failed++;
if (
test('buildAgentCatalog throws on invalid mode', () => {
assert.throws(() => buildAgentCatalog(tmpDir, { mode: 'invalid' }), /Invalid mode "invalid"/);
})
)
passed++;
else failed++;
if (test('buildAgentCatalog supports filter function', () => {
if (
test('buildAgentCatalog supports filter function', () => {
// Add a second agent
fs.writeFileSync(
path.join(tmpDir, 'other-agent.md'),
'---\nname: other\ndescription: Other agent\ntools: ["Bash"]\nmodel: opus\n---\n\nOther body.'
);
fs.writeFileSync(path.join(tmpDir, 'other-agent.md'), '---\nname: other\ndescription: Other agent\ntools: ["Bash"]\nmodel: opus\n---\n\nOther body.');
const result = buildAgentCatalog(tmpDir, {
filter: a => a.model === 'opus',
filter: a => a.model === 'opus'
});
assert.strictEqual(result.agents.length, 1);
assert.strictEqual(result.agents[0].name, 'other');
// Clean up
fs.unlinkSync(path.join(tmpDir, 'other-agent.md'));
})) passed++; else failed++;
})
)
passed++;
else failed++;
// --- lazyLoadAgent ---
if (test('lazyLoadAgent loads a single agent by name', () => {
if (
test('lazyLoadAgent loads a single agent by name', () => {
const agent = lazyLoadAgent(tmpDir, 'test-agent');
assert.ok(agent);
assert.strictEqual(agent.name, 'test-agent');
assert.ok(agent.body.includes('Test agent body paragraph'));
})) passed++; else failed++;
})
)
passed++;
else failed++;
if (test('lazyLoadAgent returns null for non-existent agent', () => {
if (
test('lazyLoadAgent returns null for non-existent agent', () => {
const agent = lazyLoadAgent(tmpDir, 'does-not-exist');
assert.strictEqual(agent, null);
})) passed++; else failed++;
})
)
passed++;
else failed++;
if (test('lazyLoadAgent rejects path traversal attempts', () => {
if (
test('lazyLoadAgent rejects path traversal attempts', () => {
const agent = lazyLoadAgent(tmpDir, '../etc/passwd');
assert.strictEqual(agent, null);
})) passed++; else failed++;
})
)
passed++;
else failed++;
if (test('lazyLoadAgent rejects names with invalid characters', () => {
if (
test('lazyLoadAgent rejects names with invalid characters', () => {
const agent = lazyLoadAgent(tmpDir, 'foo/bar');
assert.strictEqual(agent, null);
const agent2 = lazyLoadAgent(tmpDir, 'foo bar');
assert.strictEqual(agent2, null);
})) passed++; else failed++;
})
)
passed++;
else failed++;
// --- Real agents directory ---
const realAgentsDir = path.resolve(__dirname, '../../agents');
if (test('buildAgentCatalog works with real agents directory', () => {
if (
test('buildAgentCatalog works with real agents directory', () => {
if (!fs.existsSync(realAgentsDir)) return; // skip if not present
const result = buildAgentCatalog(realAgentsDir, { mode: 'catalog' });
assert.ok(result.agents.length > 0, 'Should find at least one agent');
@ -250,16 +336,22 @@ function runTests() {
// Verify significant compression ratio
const ratio = result.stats.compressedBytes / result.stats.originalBytes;
assert.ok(ratio < 0.5, `Compression ratio ${ratio.toFixed(2)} should be < 0.5`);
})) passed++; else failed++;
})
)
passed++;
else failed++;
if (test('catalog mode token estimate is under 5000 for real agents', () => {
if (
test('catalog mode token estimate is under 6000 for real agents', () => {
if (!fs.existsSync(realAgentsDir)) return;
const result = buildAgentCatalog(realAgentsDir, { mode: 'catalog' });
assert.ok(
result.stats.compressedTokenEstimate < 5000,
`Token estimate ${result.stats.compressedTokenEstimate} exceeds 5000`
);
})) passed++; else failed++;
// Canary tracks catalog growth: raised 5000 -> 6000 for the 67-agent catalog
// after adding spec-miner (#2253), agent-evaluator (#2220), vue-reviewer (#2241).
assert.ok(result.stats.compressedTokenEstimate < 6000, `Token estimate ${result.stats.compressedTokenEstimate} exceeds 6000`);
})
)
passed++;
else failed++;
// Cleanup
fs.rmSync(tmpDir, { recursive: true, force: true });