mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-06-16 08:26:52 +08:00
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:
parent
141286a02a
commit
6e2544ffa2
@ -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)
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -56,6 +56,4 @@
|
||||
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
|
||||
### Open questions / tensions with Module 40 Personality
|
||||
3. ### Open questions / tensions with Module 40 Personality
|
||||
|
||||
@ -32,15 +32,11 @@
|
||||
|
||||
### Trueline draft (Neumeier: "[Brand] is the only [category] that [unique claim].")
|
||||
|
||||
>
|
||||
|
||||
### Alternative truelines (2–3 variations, vary level of abstraction)
|
||||
> ### Alternative truelines (2–3 variations, vary level of abstraction)
|
||||
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
|
||||
### Brand story arc
|
||||
3. ### Brand story arc
|
||||
|
||||
| Beat | Content |
|
||||
|---|---|
|
||||
|
||||
@ -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
|
||||
|
||||
@ -25,7 +25,7 @@ Invoke the `orch-build-mvp` skill with `$ARGUMENTS` as the doc path. The skill
|
||||
(via the shared `orch-pipeline` engine, full pipeline incl. Scaffold) will:
|
||||
|
||||
1. Read the spec; extract scope, locked decisions, and a feature list ordered as
|
||||
**thin vertical slices** (one end-to-end path first). → **GATE 1** (approve slice plan).
|
||||
**thin vertical slices** (one end-to-end path first). → **GATE 1** (approve slice plan).
|
||||
2. Scaffold the first end-to-end slice.
|
||||
3. Reuse the GAN harness: translate the SDD into `gan-harness/spec.md` +
|
||||
`eval-rubric.md`, then drive `/gan-build "<brief>" --skip-planner`
|
||||
|
||||
@ -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/",
|
||||
|
||||
@ -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 */ }
|
||||
});
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -56,6 +56,4 @@
|
||||
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
|
||||
### Open questions / tensions with Module 40 Personality
|
||||
3. ### Open questions / tensions with Module 40 Personality
|
||||
|
||||
@ -32,15 +32,11 @@
|
||||
|
||||
### Trueline draft (Neumeier: "[Brand] is the only [category] that [unique claim].")
|
||||
|
||||
>
|
||||
|
||||
### Alternative truelines (2–3 variations, vary level of abstraction)
|
||||
> ### Alternative truelines (2–3 variations, vary level of abstraction)
|
||||
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
|
||||
### Brand story arc
|
||||
3. ### Brand story arc
|
||||
|
||||
| Beat | Content |
|
||||
|---|---|
|
||||
|
||||
@ -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,81 +31,125 @@ function runTests() {
|
||||
|
||||
// --- parseFrontmatter ---
|
||||
|
||||
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');
|
||||
assert.strictEqual(frontmatter.description, 'A test');
|
||||
assert.deepStrictEqual(frontmatter.tools, ['Read', 'Grep']);
|
||||
assert.strictEqual(frontmatter.model, 'sonnet');
|
||||
assert.ok(body.includes('Body text here.'));
|
||||
})) passed++; else failed++;
|
||||
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');
|
||||
assert.strictEqual(frontmatter.description, 'A test');
|
||||
assert.deepStrictEqual(frontmatter.tools, ['Read', 'Grep']);
|
||||
assert.strictEqual(frontmatter.model, 'sonnet');
|
||||
assert.ok(body.includes('Body text here.'));
|
||||
})
|
||||
)
|
||||
passed++;
|
||||
else failed++;
|
||||
|
||||
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++;
|
||||
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++;
|
||||
|
||||
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++;
|
||||
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++;
|
||||
|
||||
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++;
|
||||
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++;
|
||||
|
||||
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++;
|
||||
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++;
|
||||
|
||||
// --- extractSummary ---
|
||||
|
||||
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++;
|
||||
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++;
|
||||
|
||||
if (test('extractSummary returns empty string for empty body', () => {
|
||||
assert.strictEqual(extractSummary(''), '');
|
||||
assert.strictEqual(extractSummary('# Only Headings\n\n## Another'), '');
|
||||
})) passed++; else failed++;
|
||||
if (
|
||||
test('extractSummary returns empty string for empty body', () => {
|
||||
assert.strictEqual(extractSummary(''), '');
|
||||
assert.strictEqual(extractSummary('# Only Headings\n\n## Another'), '');
|
||||
})
|
||||
)
|
||||
passed++;
|
||||
else failed++;
|
||||
|
||||
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++;
|
||||
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++;
|
||||
|
||||
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++;
|
||||
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++;
|
||||
|
||||
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++;
|
||||
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++;
|
||||
|
||||
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++;
|
||||
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++;
|
||||
|
||||
// --- loadAgent / loadAgents ---
|
||||
|
||||
@ -124,142 +159,199 @@ 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', () => {
|
||||
const agent = loadAgent(path.join(tmpDir, 'test-agent.md'));
|
||||
assert.strictEqual(agent.name, 'test-agent');
|
||||
assert.strictEqual(agent.description, 'A test agent');
|
||||
assert.deepStrictEqual(agent.tools, ['Read']);
|
||||
assert.strictEqual(agent.model, 'haiku');
|
||||
assert.ok(agent.body.includes('Test agent body paragraph'));
|
||||
assert.strictEqual(agent.fileName, 'test-agent');
|
||||
assert.ok(agent.byteSize > 0);
|
||||
})) passed++; else failed++;
|
||||
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');
|
||||
assert.deepStrictEqual(agent.tools, ['Read']);
|
||||
assert.strictEqual(agent.model, 'haiku');
|
||||
assert.ok(agent.body.includes('Test agent body paragraph'));
|
||||
assert.strictEqual(agent.fileName, 'test-agent');
|
||||
assert.ok(agent.byteSize > 0);
|
||||
})
|
||||
)
|
||||
passed++;
|
||||
else failed++;
|
||||
|
||||
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++;
|
||||
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++;
|
||||
|
||||
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++;
|
||||
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++;
|
||||
|
||||
// --- compressToCatalog / compressToSummary ---
|
||||
|
||||
const sampleAgent = loadAgent(path.join(tmpDir, 'test-agent.md'));
|
||||
|
||||
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');
|
||||
assert.deepStrictEqual(catalog.tools, ['Read']);
|
||||
assert.strictEqual(catalog.model, 'haiku');
|
||||
assert.strictEqual(catalog.body, undefined);
|
||||
assert.strictEqual(catalog.byteSize, undefined);
|
||||
})) passed++; else failed++;
|
||||
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');
|
||||
assert.deepStrictEqual(catalog.tools, ['Read']);
|
||||
assert.strictEqual(catalog.model, 'haiku');
|
||||
assert.strictEqual(catalog.body, undefined);
|
||||
assert.strictEqual(catalog.byteSize, undefined);
|
||||
})
|
||||
)
|
||||
passed++;
|
||||
else failed++;
|
||||
|
||||
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++;
|
||||
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++;
|
||||
|
||||
// --- buildAgentCatalog ---
|
||||
|
||||
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);
|
||||
assert.strictEqual(result.stats.totalAgents, 1);
|
||||
assert.strictEqual(result.stats.mode, 'catalog');
|
||||
assert.ok(result.stats.originalBytes > 0);
|
||||
assert.ok(result.stats.compressedBytes < result.stats.originalBytes);
|
||||
assert.ok(result.stats.compressedTokenEstimate > 0);
|
||||
})) passed++; else failed++;
|
||||
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);
|
||||
assert.strictEqual(result.stats.totalAgents, 1);
|
||||
assert.strictEqual(result.stats.mode, 'catalog');
|
||||
assert.ok(result.stats.originalBytes > 0);
|
||||
assert.ok(result.stats.compressedBytes < result.stats.originalBytes);
|
||||
assert.ok(result.stats.compressedTokenEstimate > 0);
|
||||
})
|
||||
)
|
||||
passed++;
|
||||
else failed++;
|
||||
|
||||
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++;
|
||||
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++;
|
||||
|
||||
if (test('buildAgentCatalog in full mode preserves body', () => {
|
||||
const result = buildAgentCatalog(tmpDir, { mode: 'full' });
|
||||
assert.ok(result.agents[0].body);
|
||||
})) passed++; else failed++;
|
||||
if (
|
||||
test('buildAgentCatalog in full mode preserves body', () => {
|
||||
const result = buildAgentCatalog(tmpDir, { mode: 'full' });
|
||||
assert.ok(result.agents[0].body);
|
||||
})
|
||||
)
|
||||
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', () => {
|
||||
// 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.'
|
||||
);
|
||||
const result = buildAgentCatalog(tmpDir, {
|
||||
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++;
|
||||
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.');
|
||||
const result = buildAgentCatalog(tmpDir, {
|
||||
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++;
|
||||
|
||||
// --- lazyLoadAgent ---
|
||||
|
||||
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++;
|
||||
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++;
|
||||
|
||||
if (test('lazyLoadAgent returns null for non-existent agent', () => {
|
||||
const agent = lazyLoadAgent(tmpDir, 'does-not-exist');
|
||||
assert.strictEqual(agent, null);
|
||||
})) passed++; else failed++;
|
||||
if (
|
||||
test('lazyLoadAgent returns null for non-existent agent', () => {
|
||||
const agent = lazyLoadAgent(tmpDir, 'does-not-exist');
|
||||
assert.strictEqual(agent, null);
|
||||
})
|
||||
)
|
||||
passed++;
|
||||
else failed++;
|
||||
|
||||
if (test('lazyLoadAgent rejects path traversal attempts', () => {
|
||||
const agent = lazyLoadAgent(tmpDir, '../etc/passwd');
|
||||
assert.strictEqual(agent, null);
|
||||
})) passed++; else failed++;
|
||||
if (
|
||||
test('lazyLoadAgent rejects path traversal attempts', () => {
|
||||
const agent = lazyLoadAgent(tmpDir, '../etc/passwd');
|
||||
assert.strictEqual(agent, null);
|
||||
})
|
||||
)
|
||||
passed++;
|
||||
else failed++;
|
||||
|
||||
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++;
|
||||
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++;
|
||||
|
||||
// --- Real agents directory ---
|
||||
|
||||
const realAgentsDir = path.resolve(__dirname, '../../agents');
|
||||
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');
|
||||
assert.ok(result.stats.compressedBytes < result.stats.originalBytes, 'Catalog should be smaller than original');
|
||||
// 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++;
|
||||
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');
|
||||
assert.ok(result.stats.compressedBytes < result.stats.originalBytes, 'Catalog should be smaller than original');
|
||||
// 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++;
|
||||
|
||||
if (test('catalog mode token estimate is under 5000 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++;
|
||||
if (
|
||||
test('catalog mode token estimate is under 6000 for real agents', () => {
|
||||
if (!fs.existsSync(realAgentsDir)) return;
|
||||
const result = buildAgentCatalog(realAgentsDir, { mode: 'catalog' });
|
||||
// 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 });
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user