← back to Agent Cabinet
server.js
1139 lines
// The Cabinet viewer — serves the Mermaid mindmap of Steve's executive agent hierarchy.
// Source: cabinet.yaml (re-rendered on each request so edits show up immediately).
const fs = require('fs');
const path = require('path');
const http = require('http');
const os = require('os');
const { execFile, execSync } = require('child_process');
const PORT = 9766;
const ROOT = __dirname;
const CABINET = path.join(ROOT, 'cabinet.yaml');
const HOME = os.homedir();
// Strict: agent/skill ids are lowercase kebab/dot. Guards the open/run endpoints
// against command injection — a name that fails this never reaches the shell.
const NAME_RE = /^[a-z0-9][a-z0-9._-]*$/;
// Resolve a node's on-disk source. Agents live in ~/.claude/agents/<name>.md,
// skills in ~/.claude/skills/<name>/SKILL.md (some skills are flat .md; some
// "agents" are actually skills, and vice-versa — so we probe both homes).
function resolveSource(kind, name) {
if (!NAME_RE.test(name || '')) return null;
const c = [];
if (kind === 'skill') {
c.push(path.join(HOME, '.claude', 'skills', name, 'SKILL.md'));
c.push(path.join(HOME, '.claude', 'skills', name + '.md'));
c.push(path.join(HOME, '.claude', 'agents', name + '.md'));
} else { // subagent / agent / officer
c.push(path.join(HOME, '.claude', 'agents', name + '.md'));
c.push(path.join(HOME, '.claude', 'skills', name, 'SKILL.md'));
}
for (const p of c) { try { if (fs.statSync(p).isFile()) return { path: p, exists: true }; } catch (e) {} }
return { path: c[0], exists: false };
}
function invocationFor(kind, name) {
return kind === 'skill' ? '/' + name : 'Use the ' + name + ' subagent — tell it what you need.';
}
// "Run" = open a NEW interactive claude session (iTerm2 tab, Terminal fallback)
// seeded with the invocation, so it runs supervised, not headless/autonomous.
function spawnClaude(kind, name, cb) {
if (!NAME_RE.test(name || '')) return cb(new Error('bad name'));
const inv = invocationFor(kind, name);
const cmd = 'cd ' + HOME + " && claude '" + inv.replace(/'/g, "'\\''") + "'";
const esc = cmd.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
const useITerm = fs.existsSync('/Applications/iTerm.app');
const script = useITerm
? 'tell application "iTerm2"\n activate\n try\n set w to current window\n on error\n set w to (create window with default profile)\n end try\n tell w\n create tab with default profile\n tell current session of current tab to write text "' + esc + '"\n end tell\nend tell'
: 'tell application "Terminal"\n activate\n do script "' + esc + '"\nend tell';
execFile('osascript', ['-e', script], { timeout: 15000 }, (err) => cb(err, cmd));
}
// Cross-officer SUGGESTIONS (must mirror pyramid.html). Each is a proposal that a
// skill/agent under `from` should ALSO live under `to`. Approve writes it into
// cabinet.yaml under the target VP; Remove just dismisses it. State persists.
const SUGGESTIONS = [
{ id:'dw-legal-compliance', kind:'skill', from:'vp-dw-commerce', to:'vp-compliance-policy', reason:'Settlement / vendor-contract review is also a compliance gate' },
{ id:'security-auditor', kind:'subagent', from:'vp-engineering', to:'vp-compliance-policy', reason:'Security posture (OWASP/auth) overlaps policy & compliance' },
{ id:'best-practices', kind:'skill', from:'vp-compliance-policy',to:'vp-engineering', reason:'Standing-rules pre-flight before any code / prod op' },
{ id:'la-research-agent', kind:'subagent', from:'vp-directories', to:'vp-research-content', reason:'LA public-records work IS a core research capability' },
{ id:'analytics-agent', kind:'subagent', from:'vp-operations', to:'vp-research-content', reason:'GA4 traffic data feeds site audits & competitor analysis' },
{ id:'comms-compliance', kind:'subagent', from:'vp-compliance-policy',to:'vp-dw-marketing', reason:'Every mailer / social send must clear CAN-SPAM first' },
{ id:'dw-vendor-landing', kind:'skill', from:'vp-dw-marketing', to:'vp-dw-commerce', reason:'Vendor landings are storefront surfaces commerce owns' },
{ id:'collection-creator', kind:'skill', from:'vp-dw-marketing', to:'vp-dw-commerce', reason:'Collection/merchandising lives in the Shopify catalog' },
{ id:'new-arrivals-rotator',kind:'skill', from:'vp-dw-marketing', to:'vp-dw-commerce', reason:'Auto-rotating Shopify collections are catalog ops' },
{ id:'pairs-well-with', kind:'skill', from:'vp-dw-marketing', to:'vp-dw-commerce', reason:'Per-SKU recommendations are a product-page feature' },
{ id:'exa-agent', kind:'subagent', from:'vp-research-content', to:'vp-directories', reason:'Web research enriches directory listings' },
{ id:'logo-agent', kind:'skill', from:'vp-dw-marketing', to:'vp-research-content', reason:'Tournament brand-mark picker is a design/research tool' },
];
const SUGSTATE_FILE = path.join(ROOT, 'pyramid-suggest-state.json');
const sugKey = (s) => s.kind + ':' + s.id + ':' + s.from + ':' + s.to;
function loadSugState() { try { return JSON.parse(fs.readFileSync(SUGSTATE_FILE, 'utf8')); } catch (e) { return { decisions: {} }; } }
function saveSugState(s) { try { fs.writeFileSync(SUGSTATE_FILE, JSON.stringify(s, null, 2)); } catch (e) {} }
// Approve = idempotently add "- <kind>: <id>" under the target VP's directors block.
function applyToCabinet(s) {
const lines = fs.readFileSync(CABINET, 'utf8').split('\n');
const vpIdx = lines.findIndex(l => new RegExp('^ - vp:\\s*' + s.to + '\\s*$').test(l));
if (vpIdx < 0) return { added: false, error: 'target VP not found' };
let dirIdx = -1, end = lines.length;
for (let i = vpIdx + 1; i < lines.length; i++) {
if (/^ - vp:/.test(lines[i])) { end = i; break; }
if (dirIdx < 0 && /^ directors:/.test(lines[i])) dirIdx = i;
}
if (dirIdx < 0) return { added: false, error: 'directors block not found' };
const esc = s.id.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
for (let i = dirIdx + 1; i < end; i++) {
if (new RegExp('^\\s+- ' + s.kind + ':\\s*' + esc + '\\s*$').test(lines[i])) return { added: false, already: true };
}
lines.splice(dirIdx + 1, 0,
' - ' + s.kind + ': ' + s.id,
' owns: [cross-officer — approved via pyramid (primary: ' + s.from + ')]');
fs.writeFileSync(CABINET, lines.join('\n'));
return { added: true };
}
// Auto-commit ONLY cabinet.yaml (path-limited, so other dirty files aren't swept
// in). Local commit, no push. Author = Steve, with Claude co-author trailer.
// Serialize cabinet read-modify-write-commit sections so concurrent approve
// clicks never (a) race each other for the git index, or (b) let one commit
// sweep another's just-written block (which would leave it commit-less). Each
// critical section = apply-to-file THEN commit, run start-to-finish before the
// next begins. The lock-retry inside commit then only absorbs collisions with
// EXTERNAL committers (yolo-loop, council jobs).
let _cabinetChain = Promise.resolve();
function withCabinetLock(fn) {
const run = _cabinetChain.then(fn, fn);
_cabinetChain = run.then(() => {}, () => {});
return run;
}
function commitCabinetMsg(msg, cb) {
const full = msg + '\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>';
// External committers (yolo-loop, council jobs) can still hold .git/index.lock —
// retry the add+commit a few times with backoff so a click never silently leaves
// an uncommitted (orphaned) change.
const MAX = 6;
const isLock = (e, se) => /index\.lock|unable to create|another git process|cannot lock ref/i.test((se || '') + ' ' + (e && e.message || ''));
const run = (args, opts, done) => execFile('git', ['-C', ROOT, ...args], opts, done);
const attempt = (n) => {
run(['add', 'cabinet.yaml'], {}, (e1, _o1, se1) => {
if (e1) { if (isLock(e1, se1) && n < MAX) return setTimeout(() => attempt(n + 1), 150 * n); return cb(e1); }
run(['-c', 'user.name=Steve Abrams', '-c', 'user.email=steve@designerwallcoverings.com',
'commit', '-m', full, '--', 'cabinet.yaml'], { timeout: 15000 }, (e2, _o2, se2) => {
if (e2) { if (isLock(e2, se2) && n < MAX) return setTimeout(() => attempt(n + 1), 150 * n); return cb(e2); }
run(['rev-parse', '--short', 'HEAD'], {}, (e3, out) => cb(e3, (out || '').trim()));
});
});
};
attempt(1);
}
function gitCommitCabinet(s, cb) {
commitCabinetMsg('cabinet: approve cross-officer ' + s.kind + ' ' + s.id + ' under ' + s.to +
' (from ' + s.from + ') via pyramid', cb);
}
// ── Org-GAPS overlay (must mirror pyramid.html GAPS) ──
// Surfaces the structural gaps Steve flagged: missing officers + orphan agents
// not yet in the cabinet. Approving a gap WRITES the change into cabinet.yaml
// (new-vp = a whole VP block; adopt-orphan = a director under an existing VP)
// and auto-commits. This is the dotted-line "approve to execute" mechanism,
// extended from cross-officer SUGGESTIONS to "stand up an officer / adopt an orphan".
const GAPS = [
{ id: 'vp-security', type: 'new-vp',
domain: 'Security posture, incident response, secrets, firewalls, monitoring',
triggers: [
'security | breach | incident | intrusion | backdoor',
'secret | token | rotate | leak | exposed key',
'firewall | port | hardening | cve | dependency audit',
'security-dashboard | rkhunter | chkrootkit | security monitor',
],
directors: [
{ kind: 'subagent', name: 'security-auditor', note: 'cross-officer (primary: vp-engineering)' },
{ kind: 'subagent', name: 'secrets-manager', note: 'cross-officer (primary: vp-operations)' },
{ kind: 'subagent', name: 'security-dashboard', note: 'monitoring fleet — :9889 + Kamatera security-monitor.sh' },
{ kind: 'skill', name: 'open-firewalls', note: 'cross-officer (primary: vp-operations)' },
{ kind: 'skill', name: 'secrets', note: 'cross-officer (primary: vp-operations)' },
{ kind: 'subagent', name: 'comms-compliance', note: 'cross-officer (primary: vp-compliance-policy)' },
] },
{ id: 'vp-special-projects', type: 'new-vp',
domain: 'Non-DW / side / product-builder projects (wallpapersback, apartment, site-factory, abrams)',
triggers: [
'wallpapersback | wallco (legacy alias) | mural | seam',
'apartment wallpaper | peel and stick',
'site-factory | small-business-builder | sdcc',
'agentabrams | 4square | abrams portfolio',
'special project | side project',
],
directors: [
{ kind: 'subagent', name: 'wallpapersback-agent', note: 'wallpapersback.com storefront + gen pipeline' },
{ kind: 'subagent', name: 'apartment-wallpaper-agent', note: 'apartmentwallpaper.com peel-and-stick' },
{ kind: 'subagent', name: 'seam-debug-agent', note: 'wallpapersback.com seam/joint healer' },
{ kind: 'skill', name: 'site-factory', note: 'multi-site Next.js generator' },
{ kind: 'skill', name: 'small-business-builder', note: 'SDCC analysis + rebuild' },
] },
{ id: 'agent-architect', type: 'adopt-orphan', kind: 'subagent', to: 'vp-operations', note: 'owns cabinet.yaml + this viewer' },
{ id: 'abramstasks-agent', type: 'adopt-orphan', kind: 'subagent', to: 'vp-operations', note: 'plan→parallel-Claudes orchestrator' },
{ id: 'context-manager', type: 'adopt-orphan', kind: 'subagent', to: 'vp-engineering', note: 'multi-agent context management' },
{ id: 'dfm', type: 'adopt-orphan', kind: 'subagent', to: 'vp-operations', note: 'autonomous task executor — confirm purpose' },
];
const GAPSTATE_FILE = path.join(ROOT, 'pyramid-gap-state.json');
const gapKey = (g) => g.type === 'new-vp' ? 'vp:' + g.id : g.kind + ':' + g.id + ':' + g.to;
function loadGapState() { try { return JSON.parse(fs.readFileSync(GAPSTATE_FILE, 'utf8')); } catch (e) { return { decisions: {} }; } }
function saveGapState(s) { try { fs.writeFileSync(GAPSTATE_FILE, JSON.stringify(s, null, 2)); } catch (e) {} }
// new-vp → insert a full VP block before architecture_principles (idempotent).
// adopt-orphan → append a director under the target VP's directors (idempotent).
function applyGapToCabinet(g) {
const lines = fs.readFileSync(CABINET, 'utf8').split('\n');
if (g.type === 'new-vp') {
if (lines.some(l => new RegExp('^ - vp:\\s*' + g.id + '\\s*$').test(l))) return { added: false, already: true };
const apIdx = lines.findIndex(l => /^architecture_principles:/.test(l));
if (apIdx < 0) return { added: false, error: 'architecture_principles anchor not found' };
const block = [' - vp: ' + g.id, ' domain: ' + g.domain, ' triggers:'];
g.triggers.forEach(t => block.push(' - ' + t));
block.push(' directors:');
g.directors.forEach(d => { block.push(' - ' + d.kind + ': ' + d.name); if (d.note) block.push(' owns: [' + d.note + ']'); });
block.push('');
lines.splice(apIdx, 0, ...block);
fs.writeFileSync(CABINET, lines.join('\n'));
return { added: true };
}
// adopt-orphan
const vpIdx = lines.findIndex(l => new RegExp('^ - vp:\\s*' + g.to + '\\s*$').test(l));
if (vpIdx < 0) return { added: false, error: 'target VP ' + g.to + ' not found (approve it first)' };
let dirIdx = -1, end = lines.length;
for (let i = vpIdx + 1; i < lines.length; i++) {
if (/^ - vp:/.test(lines[i]) || /^architecture_principles:/.test(lines[i])) { end = i; break; }
if (dirIdx < 0 && /^ directors:/.test(lines[i])) dirIdx = i;
}
if (dirIdx < 0) return { added: false, error: 'directors block not found' };
const esc = g.id.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
for (let i = dirIdx + 1; i < end; i++) {
if (new RegExp('^\\s+- ' + g.kind + ':\\s*' + esc + '\\s*$').test(lines[i])) return { added: false, already: true };
}
const ins = [' - ' + g.kind + ': ' + g.id];
if (g.note) ins.push(' owns: [adopted orphan via pyramid gaps — ' + g.note + ']');
lines.splice(dirIdx + 1, 0, ...ins);
fs.writeFileSync(CABINET, lines.join('\n'));
return { added: true };
}
// ── Idea 4: Live Health Overlay — read DW canary verdicts, map to officers ──
const HEALTH_SOURCES = {
'vp-operations': ['dw-uptime-probe', 'dw-backup-canary', 'dw-canary-meta-watchdog'],
'vp-dw-commerce': ['dw-scraper-canary', 'dw-map-auditor'],
'vp-compliance-policy': ['dw-leak-scanner'],
};
function readCanary(skill) {
try {
const j = JSON.parse(fs.readFileSync(path.join(HOME, '.claude', 'skills', skill, 'data', 'latest.json'), 'utf8'));
const ts = j.scanned_at || j.checked_at || null;
const down = (j.down || 0) + (j.leaking || 0);
const fail = (j.fail || 0);
const warn = (j.warn || 0) + (j.degraded || 0) + (j.unknown || 0);
const sev = (down > 0 || fail > 0) ? 'FAIL' : (warn > 0 ? 'WARN' : 'PASS');
const bits = [];
if (j.up != null) bits.push(j.up + ' up'); if (j.down) bits.push(j.down + ' down'); if (j.degraded) bits.push(j.degraded + ' degraded');
if (j.vendors != null) bits.push(j.vendors + ' vendors'); if (j.fail) bits.push(j.fail + ' fail'); if (j.warn) bits.push(j.warn + ' warn');
if (j.unknown) bits.push(j.unknown + ' unknown'); if (j.leaking != null) bits.push((j.leaking || 0) + ' leaking');
if (j.repos != null) bits.push(j.repos + ' repos'); if (j.canaries != null) bits.push(j.canaries + ' canaries');
const ageH = ts ? +((Date.now() - Date.parse(ts)) / 3.6e6).toFixed(1) : null;
return { skill, sev, ts, ageH, summary: bits.join(' · ') };
} catch (e) { return { skill, sev: 'NA', summary: 'no data' }; }
}
const officerStatus = (sev) => sev === 'FAIL' ? 'down' : sev === 'WARN' ? 'warn' : sev === 'PASS' ? 'up' : 'na';
// ── Scheduled/upcoming lane — compute next launchd fire time from a plist spec ──
const DOW = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
function nextCal(s, nowMs) {
const d = new Date(nowMs); d.setSeconds(0, 0);
const H = s.Hour != null ? s.Hour : 0, M = s.Minute != null ? s.Minute : 0;
d.setHours(H, M, 0, 0);
if (s.Month != null) {
d.setMonth(s.Month - 1); if (s.Day != null) d.setDate(s.Day); d.setHours(H, M, 0, 0);
if (d.getTime() <= nowMs) { d.setFullYear(d.getFullYear() + 1); }
return d.getTime();
}
if (s.Weekday != null) {
const wd = s.Weekday === 7 ? 0 : s.Weekday;
let add = (wd - d.getDay() + 7) % 7; if (add === 0 && d.getTime() <= nowMs) add = 7;
d.setDate(d.getDate() + add); return d.getTime();
}
if (s.Day != null) {
d.setDate(s.Day); d.setHours(H, M, 0, 0);
if (d.getTime() <= nowMs) { d.setMonth(d.getMonth() + 1); d.setDate(s.Day); d.setHours(H, M, 0, 0); }
return d.getTime();
}
if (d.getTime() <= nowMs) d.setDate(d.getDate() + 1);
return d.getTime();
}
function calCadence(s) {
const hm = ('0' + (s.Hour || 0)).slice(-2) + ':' + ('0' + (s.Minute || 0)).slice(-2);
if (s.Month != null) return 'one-time ' + s.Month + '/' + (s.Day || 1) + ' ' + hm;
if (s.Weekday != null) return 'weekly ' + DOW[s.Weekday === 7 ? 0 : s.Weekday] + ' ' + hm;
if (s.Day != null) return 'monthly day ' + s.Day + ' ' + hm;
return 'daily ' + hm;
}
function humanInt(sec) { return sec % 3600 === 0 ? 'every ' + sec / 3600 + 'h' : sec % 60 === 0 ? 'every ' + sec / 60 + 'm' : 'every ' + sec + 's'; }
// Real launchd state in 2 calls (NOT plist contents) — a job present in the plist
// dir but absent from `launchctl list` is UNLOADED and will NOT fire, even though
// its StartCalendarInterval still "computes" a next-fire. This is what kept the
// schedule lane crying wolf in both directions (dead jobs shown as upcoming fires).
function launchState() {
const loaded = new Map(); // label(no prefix) -> { pid, exit }
const disabled = new Set();
try {
execSync('launchctl list', { encoding: 'utf8' }).split('\n').forEach(l => {
const m = l.match(/^(-|\d+)\s+(-?\d+)\s+com\.steve\.(\S+)/);
if (m) loaded.set(m[3], { pid: m[1] === '-' ? null : +m[1], exit: +m[2] });
});
} catch (e) {}
try {
execSync('launchctl print-disabled gui/' + process.getuid() + ' 2>/dev/null', { encoding: 'utf8' })
.split('\n').forEach(l => { const m = l.match(/"com\.steve\.(\S+?)"\s*=>\s*disabled/); if (m) disabled.add(m[1]); });
} catch (e) {}
return { loaded, disabled };
}
function parseYaml(text) {
// Tiny YAML subset parser — good enough for cabinet.yaml's shape.
// Produces {president, cabinet:[{vp, domain, triggers:[], directors:[{skill?,subagent?,owns?}]}]}
const lines = text.split('\n');
const out = { cabinet: [] };
let cur = null, mode = null, trigList = null, dirList = null;
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
if (!line.trim() || line.trim().startsWith('#')) continue;
const m = line.match(/^(\s*)(.+?):\s*(.*)$/);
if (line.match(/^ - vp:\s*(.+)$/)) {
cur = { vp: line.split(':')[1].trim(), triggers: [], directors: [], domain: '' };
out.cabinet.push(cur); mode = 'vp'; continue;
}
if (cur && mode === 'vp') {
if (line.match(/^ domain:\s*(.+)$/)) cur.domain = line.split(/domain:/)[1].trim();
else if (line.match(/^ triggers:/)) { trigList = cur.triggers; }
else if (line.match(/^ directors:/)) { dirList = cur.directors; trigList = null; }
else if (line.match(/^ - (.+)$/) && trigList) trigList.push(line.replace(/^\s*-\s*/, '').trim());
else if (line.match(/^ - skill:\s*(.+)$/)) dirList && dirList.push({ kind: 'skill', name: line.split(':')[1].trim() });
else if (line.match(/^ - subagent:\s*(.+)$/)) dirList && dirList.push({ kind: 'subagent', name: line.split(':')[1].trim() });
}
}
return out;
}
function buildMermaidFlowchart(cabinet) {
const lines = ['flowchart TD'];
const meta = {}; // nodeId -> { kind, name, label, domain?, triggers?, directors? } for the click-to-drill modal
// Root
lines.push(' Steve(["👔 STEVE<br/>President"]):::president');
lines.push(' click Steve nodeClick');
meta['Steve'] = { kind: 'root', name: 'president', label: '👔 Steve — President' };
// VPs
for (const v of cabinet.cabinet) {
const cls = v.vp.replace(/-/g, '_');
lines.push(` ${cls}["${v.vp}<br/><i>${(v.domain || '').slice(0, 60)}…</i>"]:::vp`);
lines.push(` Steve --> ${cls}`);
lines.push(` click ${cls} nodeClick`);
meta[cls] = { kind: 'subagent', name: v.vp, label: v.vp,
domain: v.domain || '', triggers: v.triggers || [],
directors: v.directors.map(d => ({ kind: d.kind, name: d.name })) };
for (const d of v.directors.slice(0, 12)) {
const safe = d.name.replace(/[^a-zA-Z0-9_]/g, '_');
const id = `${safe}__${cls}`;
const label = d.kind === 'skill' ? `/${d.name}` : `@${d.name}`;
const klass = d.kind === 'skill' ? 'skill' : 'subagent';
lines.push(` ${id}["${label}"]:::${klass}`);
lines.push(` ${cls} --> ${id}`);
lines.push(` click ${id} nodeClick`);
meta[id] = { kind: d.kind, name: d.name, label, under: v.vp };
}
}
// Class styles
lines.push(' classDef president fill:#000,stroke:#000,color:#fff,font-weight:900,font-size:16px;');
lines.push(' classDef vp fill:#fef3c7,stroke:#000,stroke-width:3px,color:#000,font-weight:800;');
lines.push(' classDef skill fill:#dcfce7,stroke:#16a34a,color:#14532d;');
lines.push(' classDef subagent fill:#ede9fe,stroke:#7c3aed,color:#4c1d95;');
return { mm: lines.join('\n'), meta };
}
function render(cabinet) {
const { mm, meta } = buildMermaidFlowchart(cabinet);
const stats = {
vpCount: cabinet.cabinet.length,
skillCount: cabinet.cabinet.reduce((a, v) => a + v.directors.filter(d => d.kind === 'skill').length, 0),
subagentCount: cabinet.cabinet.reduce((a, v) => a + v.directors.filter(d => d.kind === 'subagent').length, 0),
};
return `<!doctype html>
<html><head>
<meta charset="utf-8">
<title>The Cabinet — Steve's executive agent hierarchy</title>
<script src="https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js"></script>
<style>
body { font-family: -apple-system, BlinkMacSystemFont, sans-serif; margin: 0; background: #fafaf6; color: #111; }
header { padding: 28px 40px; border-bottom: 4px solid #000; background: #fff; }
h1 { font-size: 28px; margin: 0 0 6px; letter-spacing: -.02em; font-weight: 900; }
.stats { color: #555; font-size: 13px; }
.stats b { color: #000; }
main { padding: 24px 40px; }
.mermaid { background: #fff; border: 4px solid #000; padding: 28px; box-shadow: 6px 6px 0 #000; overflow-x: auto; }
footer { padding: 16px 40px 40px; color: #888; font-size: 11px; }
pre { background: #f5f5f0; padding: 12px; border: 2px solid #000; font-size: 11px; overflow:auto }
details { margin: 24px 0; }
summary { font-weight: 700; cursor: pointer; padding: 8px; background:#fff; border:2px solid #000; }
table { border-collapse: collapse; margin-top: 12px; font-size: 12px; }
td, th { border: 1px solid #000; padding: 6px 10px; text-align: left; vertical-align: top; }
th { background: #f5f5f0; }
/* click-to-drill: nodes look clickable */
.mermaid .node { cursor: pointer; }
.mermaid .node:hover { filter: brightness(0.94); }
.hint { color:#888; font-size:12px; margin-top:8px; }
/* modal */
#nm-overlay { position: fixed; inset: 0; background: rgba(0,0,0,.45); display: none; align-items: center; justify-content: center; z-index: 9999; }
#nm-overlay.open { display: flex; }
#nm-card { background:#fff; border:4px solid #000; box-shadow: 8px 8px 0 #000; width: min(720px, 92vw); max-height: 86vh; display:flex; flex-direction:column; }
#nm-card.max { width: 96vw; max-height: 94vh; }
#nm-head { display:flex; align-items:center; gap:10px; padding:14px 18px; border-bottom:3px solid #000; background:#fafaf6; }
#nm-head h2 { font-size:18px; margin:0; font-weight:900; flex:1; letter-spacing:-.01em; }
.nm-badge { font-size:11px; font-weight:800; padding:2px 8px; border:2px solid #000; border-radius:10px; text-transform:uppercase; }
.nm-badge.skill { background:#dcfce7; color:#14532d; border-color:#16a34a; }
.nm-badge.subagent { background:#ede9fe; color:#4c1d95; border-color:#7c3aed; }
.nm-badge.root { background:#000; color:#fff; }
#nm-body { padding:16px 18px; overflow:auto; }
#nm-body h3 { font-size:12px; text-transform:uppercase; letter-spacing:.04em; color:#666; margin:16px 0 6px; }
#nm-body h3:first-child { margin-top:0; }
.nm-inv { font-family: ui-monospace, monospace; background:#f5f5f0; border:2px solid #000; padding:6px 10px; display:inline-block; font-size:13px; }
.nm-desc { font-size:14px; line-height:1.5; }
.nm-tools span, .nm-trig span { display:inline-block; font-size:11px; background:#eef; border:1px solid #99c; border-radius:8px; padding:2px 8px; margin:2px 4px 2px 0; }
.nm-trig span { background:#fff7ed; border-color:#fdba74; }
.nm-btn { font: inherit; font-weight:700; font-size:12px; padding:6px 12px; border:2px solid #000; background:#fff; cursor:pointer; box-shadow:2px 2px 0 #000; }
.nm-btn:active { transform: translate(2px,2px); box-shadow:none; }
.nm-btn.icon { padding:6px 9px; }
#nm-src { white-space:pre-wrap; font-family:ui-monospace,monospace; font-size:11px; background:#f5f5f0; border:2px solid #000; padding:10px; max-height:40vh; overflow:auto; }
</style>
</head>
<body>
<header>
<h1>The Cabinet</h1>
<div class="stats">
<b>${stats.vpCount}</b> VPs ·
<b>${stats.subagentCount}</b> Director subagents ·
<b>${stats.skillCount}</b> Director skills ·
Source: <code>cabinet.yaml</code> ·
Architect: <code>~/.claude/agents/agent-architect.md</code>
</div>
</header>
<main>
<div class="mermaid">
${mm}
</div>
<div class="hint">💡 Click any node — VP, skill, or subagent — to open its details inline (description, tools, source). Use ⛶ to expand.</div>
<details>
<summary>Cabinet roster (table view)</summary>
<table>
<tr><th>VP</th><th>Domain</th><th>Triggers</th><th>Directors</th></tr>
${cabinet.cabinet.map(v => `
<tr>
<td><b>${v.vp}</b></td>
<td>${v.domain || ''}</td>
<td>${(v.triggers || []).map(t => `<div>${t}</div>`).join('')}</td>
<td>${v.directors.map(d => `<div>${d.kind === 'skill' ? '/' : '@'}${d.name}</div>`).join('')}</td>
</tr>
`).join('')}
</table>
</details>
<details>
<summary>How to add a new VP / Director</summary>
<pre>1. Edit ~/Projects/agent-cabinet/cabinet.yaml
2. Run: claude → "agent architect: design [role]"
3. The architect will generate ~/.claude/agents/<name>.md
4. Restart pm2 agent-cabinet (or refresh this page) to see the mindmap update.</pre>
</details>
</main>
<footer>Steve = President · Last rendered ${new Date().toLocaleString()}</footer>
<script>mermaid.initialize({startOnLoad:true, theme:'default', securityLevel:'loose', flowchart:{padding:18, useMaxWidth:false, htmlLabels:true, curve:'basis', nodeSpacing:30, rankSpacing:55}});</script>
<!-- ── click-to-drill modal ── -->
<div id="nm-overlay" onclick="if(event.target===this)nmClose()">
<div id="nm-card">
<div id="nm-head">
<span id="nm-badge" class="nm-badge"></span>
<h2 id="nm-title">…</h2>
<button class="nm-btn icon" title="Expand" onclick="document.getElementById('nm-card').classList.toggle('max')">⛶</button>
<button class="nm-btn icon" title="Close" onclick="nmClose()">✕</button>
</div>
<div id="nm-body"></div>
</div>
</div>
<script>
const NODE_META = ${JSON.stringify(meta)};
const esc = s => String(s==null?'':s).replace(/[&<>"]/g, c => ({'&':'&','<':'<','>':'>','"':'"'}[c]));
// pull description + tools out of an agent/skill markdown frontmatter block
function parseFront(md){
const out = { desc:'', tools:'', body: md||'' };
const m = /^---\\n([\\s\\S]*?)\\n---\\n?([\\s\\S]*)$/.exec(md||'');
if(!m) return out;
out.body = m[2] || '';
const fm = m[1];
const d = /(^|\\n)description:\\s*(.+?)(\\n[a-zA-Z_]+:|$)/s.exec(fm);
if(d) out.desc = d[2].replace(/^["'>|]\\s*/,'').replace(/\\n\\s+/g,' ').trim();
const t = /(^|\\n)tools:\\s*(.+)/.exec(fm);
if(t) out.tools = t[2].trim();
return out;
}
async function nodeClick(id){
const m = NODE_META[id]; if(!m) return;
const card = document.getElementById('nm-card'); card.classList.remove('max');
document.getElementById('nm-badge').className = 'nm-badge ' + m.kind;
document.getElementById('nm-badge').textContent = m.kind==='root'?'president':m.kind;
document.getElementById('nm-title').textContent = m.label || m.name;
const body = document.getElementById('nm-body');
const inv = m.kind==='skill' ? ('/'+m.name) : ('Use the '+m.name+' subagent — tell it what you need.');
body.innerHTML = '<h3>Invoke</h3><span class="nm-inv">'+esc(inv)+'</span>' +
(m.domain ? '<h3>Domain</h3><div class="nm-desc">'+esc(m.domain)+'</div>' : '') +
(m.triggers&&m.triggers.length ? '<h3>Triggers</h3><div class="nm-trig">'+m.triggers.map(t=>'<span>'+esc(t)+'</span>').join('')+'</div>' : '') +
(m.directors&&m.directors.length ? '<h3>Directors ('+m.directors.length+')</h3><div class="nm-trig">'+m.directors.map(d=>'<span>'+(d.kind==='skill'?'/':'@')+esc(d.name)+'</span>').join('')+'</div>' : '') +
'<h3>Description</h3><div class="nm-desc" id="nm-desc">loading…</div>' +
'<div id="nm-actions" style="margin-top:14px;display:flex;gap:8px;flex-wrap:wrap"></div>';
document.getElementById('nm-overlay').classList.add('open');
// fetch the on-disk source for description + tools + body
if(m.kind==='root'){ document.getElementById('nm-desc').textContent = 'The President. Everything rolls up to Steve; each VP owns a domain below.'; return; }
try{
const kind = m.kind==='skill'?'skill':'subagent';
const r = await fetch('/api/source?kind='+encodeURIComponent(kind)+'&name='+encodeURIComponent(m.name));
const data = await r.json();
const f = parseFront(data.content||'');
document.getElementById('nm-desc').innerHTML = f.desc ? esc(f.desc) : '<i>(no description in source' + (data.exists?'':' — no on-disk file found') + ')</i>';
let extra = '';
if(f.tools) extra += '<h3>Tools</h3><div class="nm-tools">'+f.tools.split(',').map(t=>'<span>'+esc(t.trim())+'</span>').join('')+'</div>';
if(data.exists){
extra += '<h3>Source</h3><details><summary style="cursor:pointer;font-size:12px">'+esc(data.path)+'</summary><div id="nm-src">'+esc((f.body||data.content).slice(0,20000))+'</div></details>';
}
if(extra){ const d=document.createElement('div'); d.innerHTML=extra; document.getElementById('nm-body').insertBefore(d, document.getElementById('nm-actions')); }
const acts = document.getElementById('nm-actions');
if(data.exists) acts.innerHTML =
'<button class="nm-btn" onclick="nmOpen(\\''+kind+'\\',\\''+esc(m.name)+'\\')">📂 Open source file</button>' +
'<button class="nm-btn" onclick="nmRun(\\''+kind+'\\',\\''+esc(m.name)+'\\')">▶ Run (new claude tab)</button>';
}catch(e){ document.getElementById('nm-desc').textContent = 'could not load source'; }
}
function nmClose(){ document.getElementById('nm-overlay').classList.remove('open'); }
async function nmOpen(kind,name){ try{ await fetch('/api/open?kind='+encodeURIComponent(kind)+'&name='+encodeURIComponent(name)); }catch(e){} }
async function nmRun(kind,name){ try{ await fetch('/api/run?kind='+encodeURIComponent(kind)+'&name='+encodeURIComponent(name)); }catch(e){} }
document.addEventListener('keydown', e => { if(e.key==='Escape') nmClose(); });
</script>
</body></html>`;
}
// ---------- /game route — gamified roster with avatars, stats, synergies ----------
// Deterministic emoji avatar pool. Pick by name hash so each director has stable avatar.
const ANIMAL_AVATARS = ['🦊','🦅','🐺','🐉','🦁','🐯','🐼','🦉','🦄','🦋','🐙','🐢','🦏','🦎','🐝','🦂','🦇','🐠','🦜','🐧','🦩','🦚','🦔','🐿️','🦦','🐍','🦖','🐊','🐎','🐕','🦌','🦃','🦘','🐫','🦬','🦭','🐬','🦣','🪿','🐈⬛'];
const TOOL_AVATARS = ['⚙️','🔧','🛠️','🔨','⚒️','🪛','🪚','📏','📐','🧰','🧲','🔬','🔭','🧪','🧫','💎','🪞','🪪','🗝️','🪝','🧭','📡','💾','💿','📀','💽','🖥️','🖨️','⌨️','🖱️','🛡️','⚔️','🏹','🗡️','🪙','💰','💼','🎯','🎲','🎮'];
function avatarFor(kind, name) {
// Deterministic FNV-1a hash
let h = 2166136261;
for (let i = 0; i < name.length; i++) { h ^= name.charCodeAt(i); h = (h * 16777619) >>> 0; }
const pool = kind === 'subagent' ? ANIMAL_AVATARS : TOOL_AVATARS;
return pool[h % pool.length];
}
// Stats derived from name length + kind for variety (nothing scientific — game flavor).
function statsFor(kind, name) {
let h = 5381;
for (let i = 0; i < name.length; i++) h = ((h << 5) + h + name.charCodeAt(i)) >>> 0;
const r = (n, mod) => ((h >>> n) % mod) + 1;
return {
POWER: kind === 'subagent' ? 60 + r(0, 40) : 40 + r(2, 50), // subagents punch harder
SPEED: kind === 'skill' ? 60 + r(4, 40) : 40 + r(6, 50), // skills are faster
DEPTH: 30 + r(8, 70),
REACH: 20 + r(10, 80),
};
}
// Specialty: one-liner derived from name (heuristic mapping)
const SPECIALTIES = {
// operations
'secrets': 'API token routing master',
'domain-setup': 'Bare domain → live HTTPS site',
'cloudflare-manager': 'Edge-network conjurer',
'open-firewalls': 'Port-opening locksmith',
'process-hawk': 'Watches every pm2 like a hawk',
'pm2-crash-watcher': 'Last-hour crash reporter',
'deployment-engineer': 'CI/CD pipeline weaver',
'devops-engineer': 'Cloud ops generalist',
'domain-name-agent': 'WHOIS + DNS keeper',
'onboard-domain-agent': 'New-domain summoner',
'secrets-manager': 'Master vault keeper',
'analytics-agent': 'GA4 fleet commander',
// dw-commerce
'dw-site-build': 'Universal DW storefront blueprint',
'dw-shopify-theme-optimizer': 'Liquid speed alchemist',
'dw-marketing-copy': 'Conversion-tuned wordsmith',
'dw-seo-optimizer': 'Meta + structured-data tuner',
'dw-purchasing-vendor': 'Vendor email diplomat',
'dw-sku-analyst': 'SKU dedup detective',
'dw-legal-compliance': 'Settlement-clause guardian',
'room-setting-generator': 'Photoreal room conjurer',
'commercial-room-setting-generator': 'Hospitality-render specialist',
// research-content
'exa-agent': 'Grounded web search oracle',
'website-analysis': 'Synthesis-grade audit polymath',
'four-horsemen': 'Figma + Paper + Canva + 21st orchestrator',
'reviewed-demo-video': 'Pre+post 4-horsemen review video',
'session-debrief': 'VC-pitch debrief MP4 maker',
'clone-voice': 'Voice-clone enrollment master',
'mockups': 'N hand-built homepage variants',
'site-audit': 'Synthesis report + 90-day roadmap',
'peer-survey': 'Side-by-side competitor screenshots',
'competitors': 'Differentiation-vector hunter',
'tools-pack': 'Browser-side widget pack builder',
'extension': 'MV3 Chrome extension scaffolder',
'info-hub': 'Authoritative-source curator',
'debate-team-fast': '4-LLM consensus engine',
// engineering
'code-reviewer': 'PR audit specialist',
'security-auditor': 'OWASP & auth-flow guardian',
'performance-engineer': 'Profiler & bottleneck hunter',
'database-optimizer': 'N+1 slayer',
'backend-architect': 'API & microservice designer',
'frontend-developer': 'React UI craftsman',
'ai-engineer': 'LLM + RAG integration',
// compliance
'comms-compliance': 'CAN-SPAM / TCPA / CCPA gatekeeper',
'best-practices-reviewer': 'MEMORY.md pre-flight checker',
'best-practices': 'Standing-rules reviewer',
};
// Synergy graph: which directors pair naturally (for "Synergy" display).
// Curated minimal map — keeps the game readable.
const SYNERGIES = {
'dw-sku-analyst': ['shopify-vendor-bulk-updater', 'fullproduct'],
'shopify-vendor-bulk-updater': ['dw-sku-analyst', 'enrich-ai-tags'],
'secrets': ['domain-setup', 'cloudflare-manager'],
'domain-setup': ['cloudflare-manager', 'open-firewalls', 'secrets'],
'reviewed-demo-video': ['app-demo-video', 'four-horsemen', 'clone-voice'],
'session-debrief': ['app-demo-video', 'clone-voice', 'use-voice'],
'comms-compliance': ['best-practices-reviewer', 'dw-legal-compliance'],
'best-practices-reviewer': ['comms-compliance'],
'lawyer-build-agent': ['comms-compliance', 'la-research-agent'],
'doctor-agent': ['comms-compliance'],
'la-research-agent': ['lawyer-build-agent', 'exa-agent'],
'exa-agent': ['search-specialist', 'technical-researcher', 'la-research-agent'],
'website-analysis': ['site-audit', 'peer-survey', 'competitors', 'mockups', 'tools-pack', 'info-hub'],
'site-audit': ['website-analysis'],
'peer-survey': ['website-analysis', 'competitors'],
'mockups': ['four-horsemen', 'stampede', 'website-analysis'],
'four-horsemen': ['mockups', 'stampede', 'reviewed-demo-video'],
'stampede': ['four-horsemen', 'mockups'],
'pm2-crash-watcher': ['process-hawk', 'devops-engineer'],
'process-hawk': ['pm2-crash-watcher'],
'analytics-agent': ['dw-seo-optimizer', 'website-analysis'],
'code-reviewer': ['architect-review', 'security-auditor'],
'security-auditor': ['code-reviewer', 'best-practices-reviewer'],
'ai-engineer': ['prompt-engineer', 'mcp-expert', 'claude-api'],
'prompt-engineer': ['ai-engineer', 'prompt-cache-optimization'],
'debate-team-fast': ['claude-codex'],
'enrich-ai-tags': ['shopify-vendor-bulk-updater'],
};
const VP_THEME = {
'vp-operations': { color: '#1d4ed8', boss: '🛡️', bossName: 'Operator', tagline: 'KEEPS THE LIGHTS ON' },
'vp-dw-commerce': { color: '#16a34a', boss: '💰', bossName: 'Quartermaster', tagline: 'CATALOG, REVENUE, RETAIL' },
'vp-directories': { color: '#ea580c', boss: '📜', bossName: 'Cartographer', tagline: 'VERTICAL DIRECTORIES' },
'vp-engineering': { color: '#7c3aed', boss: '⚒️', bossName: 'Architect', tagline: 'CODE QUALITY & SHIP' },
'vp-research-content': { color: '#0891b2', boss: '🔭', bossName: 'Scout', tagline: 'KNOW + CREATE' },
'vp-compliance-policy': { color: '#dc2626', boss: '⚖️', bossName: 'Sentinel', tagline: 'GUARDS THE GATES' },
};
function renderGame() {
// Need full director list (including vendor scrapers).
// Re-extract from cabinet.yaml directly — handles the multi-line array case.
const text = fs.readFileSync(CABINET, 'utf8');
const allLines = text.split('\n');
const fullCabinet = [];
let cur = null;
for (let i = 0; i < allLines.length; i++) {
const line = allLines[i];
if (line.match(/^ - vp:\s*(.+)$/)) {
cur = { vp: line.split(':')[1].trim(), domain: '', directors: [] };
fullCabinet.push(cur);
} else if (cur) {
const dm = line.match(/^ domain:\s*(.+)$/);
if (dm) cur.domain = dm[1].trim();
let m;
if ((m = line.match(/^\s+- skill:\s*([\w./-]+)/))) cur.directors.push({ kind: 'skill', name: m[1] });
else if ((m = line.match(/^\s+- subagent:\s*([\w./-]+)/))) cur.directors.push({ kind: 'subagent', name: m[1] });
else if (line.includes('alphabetical_skill_set:')) {
let buf = line, j = i;
while (!buf.includes(']') && j < allLines.length - 1) { j++; buf += ' ' + allLines[j]; }
const inner = (buf.match(/\[([^\]]+)\]/) || ['',''])[1];
for (const raw of inner.split(',')) {
const nm = raw.replace(/["']/g, '').trim();
if (/^[a-zA-Z][\w-]+$/.test(nm)) cur.directors.push({ kind: 'skill', name: nm });
}
}
}
}
// Dedup
for (const v of fullCabinet) {
const seen = new Set();
v.directors = v.directors.filter(d => { const k = d.kind+':'+d.name; if (seen.has(k)) return false; seen.add(k); return true; });
}
const totalDirectors = fullCabinet.reduce((a, v) => a + v.directors.length, 0);
function card(d) {
const av = avatarFor(d.kind, d.name);
const s = statsFor(d.kind, d.name);
const spec = SPECIALTIES[d.name] || (d.kind === 'subagent' ? 'Domain specialist' : 'Tool · skill bundle');
const synergies = (SYNERGIES[d.name] || []).slice(0, 3);
const sigil = d.kind === 'subagent' ? '@' : '/';
return `
<div class="card ${d.kind}" data-name="${d.name}" data-kind="${d.kind}">
<div class="avatar">${av}</div>
<div class="kind-tag">${d.kind === 'subagent' ? 'SUBAGENT' : 'SKILL'}</div>
<div class="name">${sigil}${d.name}</div>
<div class="spec">${spec}</div>
<div class="stats">
<div><span>POW</span><b>${s.POWER}</b></div>
<div><span>SPD</span><b>${s.SPEED}</b></div>
<div><span>DEP</span><b>${s.DEPTH}</b></div>
<div><span>RCH</span><b>${s.REACH}</b></div>
</div>
${synergies.length ? `<div class="synergy">↔ ${synergies.map(x => `<span class="syn-pill" data-target="${x}">${x}</span>`).join(' ')}</div>` : ''}
</div>`;
}
const departmentBlocks = fullCabinet.map(v => {
const t = VP_THEME[v.vp] || { color: '#666', boss: '👑', bossName: 'Boss', tagline: '' };
const cards = v.directors.map(card).join('');
return `
<section class="dept" style="--theme:${t.color}">
<header class="dept-head">
<div class="boss-avatar">${t.boss}</div>
<div>
<div class="boss-title">${t.bossName.toUpperCase()}</div>
<h2>${v.vp}</h2>
<div class="boss-tag">${t.tagline} · ${v.directors.length} on team</div>
</div>
</header>
<div class="grid">${cards}</div>
</section>`;
}).join('');
return `<!doctype html>
<html><head>
<meta charset="utf-8">
<title>The Cabinet — Steve's Agent Roster (Game Mode)</title>
<style>
:root { --ink:#000; --paper:#fafaf6; }
* { box-sizing: border-box; }
body { font-family: -apple-system, BlinkMacSystemFont, sans-serif; margin:0; background: var(--paper); color: var(--ink); }
header.global {
padding: 36px 40px 24px; border-bottom: 4px solid #000; background:
linear-gradient(135deg, #fef3c7 0%, #fde68a 50%, #fcd34d 100%);
}
h1 { font-size: 38px; margin: 0 0 6px; letter-spacing: -.03em; font-weight: 900; font-family: 'Archivo Black', -apple-system, sans-serif; }
.stats-line { font-size: 13px; color: #444; margin-top: 8px; }
.stats-line b { color: #000; }
main { padding: 24px 32px 60px; max-width: 1840px; margin: 0 auto; }
.dept {
margin: 32px 0;
background: #fff;
border: 4px solid #000;
box-shadow: 8px 8px 0 #000;
}
.dept-head {
display: flex; align-items: center; gap: 22px;
padding: 22px 28px;
background: var(--theme);
color: #fff;
border-bottom: 4px solid #000;
}
.boss-avatar { font-size: 56px; line-height: 1; }
.dept-head h2 { margin: 2px 0; font-size: 26px; font-weight: 900; letter-spacing: -.02em; }
.boss-title { font-size: 11px; letter-spacing: .15em; font-weight: 800; opacity: .85; }
.boss-tag { font-size: 12px; opacity: .85; margin-top: 2px; letter-spacing: .04em; font-weight: 600; }
.grid {
display: grid; gap: 14px; padding: 22px;
grid-template-columns: repeat(auto-fill, minmax(218px, 1fr));
}
.card {
background: #fff;
border: 2px solid #000;
border-radius: 0;
padding: 14px;
position: relative;
transition: transform .15s ease, box-shadow .15s ease;
cursor: pointer;
}
.card:hover {
transform: translate(-3px, -3px);
box-shadow: 4px 4px 0 var(--theme);
z-index: 10;
}
.card.subagent { background: #ede9fe; }
.card.skill { background: #dcfce7; }
.card .avatar { font-size: 32px; line-height: 1; margin-bottom: 6px; }
.card .kind-tag {
position: absolute; top: 8px; right: 8px;
font-size: 9px; font-weight: 800; letter-spacing: .12em;
background: #000; color: #fff;
padding: 2px 6px;
}
.card .name { font-weight: 800; font-size: 13px; word-break: break-word; margin-bottom: 4px; }
.card .spec { font-size: 11px; color: #444; min-height: 30px; line-height: 1.35; margin-bottom: 8px; font-style: italic; }
.card .stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: 4px; margin: 8px 0; }
.card .stats div { background: rgba(0,0,0,.05); padding: 4px 0; text-align: center; font-size: 9px; border: 1px solid rgba(0,0,0,.08); }
.card .stats span { display: block; font-weight: 700; opacity: .65; letter-spacing: .08em; font-size: 8px; }
.card .stats b { display: block; font-size: 13px; color: #000; }
.card .synergy {
margin-top: 6px; font-size: 10px; line-height: 1.5;
border-top: 1px dashed rgba(0,0,0,.2); padding-top: 6px;
}
.syn-pill {
background: #000; color: #fff; padding: 1px 6px;
margin-right: 2px; cursor: pointer;
font-family: ui-monospace, monospace;
}
.syn-pill:hover { background: var(--theme); }
/* Highlight when synergy clicked */
.card.glow {
box-shadow: 0 0 0 4px gold, 6px 6px 0 #000;
animation: pulse 1s ease infinite;
}
@keyframes pulse { 0%,100% { box-shadow: 0 0 0 4px gold, 6px 6px 0 #000; } 50% { box-shadow: 0 0 0 8px gold, 6px 6px 0 #000; } }
.legend {
display: inline-flex; gap: 10px; margin-top: 14px; flex-wrap: wrap;
}
.legend span { background: #000; color: #fff; padding: 4px 10px; font-size: 11px; font-weight: 700; }
footer { text-align: center; color: #666; padding: 30px; font-size: 11px; }
</style>
</head><body>
<header class="global">
<h1>🏛 The Cabinet — Game Mode</h1>
<div class="stats-line"><b>Steve</b> (President) · <b>${fullCabinet.length}</b> Departments · <b>${totalDirectors}</b> directors on the roster</div>
<div class="legend">
<span style="background:#1d4ed8">🛡️ Operations</span>
<span style="background:#16a34a">💰 DW Commerce</span>
<span style="background:#ea580c">📜 Directories</span>
<span style="background:#7c3aed">⚒️ Engineering</span>
<span style="background:#0891b2">🔭 Research/Content</span>
<span style="background:#dc2626">⚖️ Compliance</span>
</div>
</header>
<main>${departmentBlocks}</main>
<footer>
Click any synergy pill to highlight the partner agent. Cards auto-glow gold for 2 seconds when summoned.<br>
Edit roster: <code>~/Projects/agent-cabinet/cabinet.yaml</code> · Last rendered ${new Date().toLocaleString()}
</footer>
<script>
document.querySelectorAll('.syn-pill').forEach(p => {
p.addEventListener('click', e => {
e.stopPropagation();
const target = p.dataset.target;
const card = document.querySelector('.card[data-name="' + target + '"]');
if (!card) { alert('Partner ' + target + ' not on the roster — add via agent-architect.'); return; }
card.scrollIntoView({ behavior: 'smooth', block: 'center' });
card.classList.add('glow');
setTimeout(() => card.classList.remove('glow'), 2200);
});
});
document.querySelectorAll('.card').forEach(c => {
c.addEventListener('click', () => {
const n = c.dataset.name, k = c.dataset.kind;
const trigger = k === 'subagent' ? '@' + n : '/' + n;
navigator.clipboard?.writeText(trigger);
c.classList.add('glow');
setTimeout(() => c.classList.remove('glow'), 1200);
});
});
</script>
</body></html>`;
}
const server = http.createServer((req, res) => {
if (req.url === '/health') { res.writeHead(200); res.end('ok'); return; }
const u = new URL(req.url, 'http://localhost');
const q = u.searchParams;
const J = (obj) => { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify(obj)); };
if (u.pathname === '/pyramid.html' || u.pathname === '/pyramid/') {
res.writeHead(301, { Location: '/pyramid' });
res.end();
return;
}
if (u.pathname === '/pyramid') {
try {
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
res.end(fs.readFileSync(path.join(ROOT, 'pyramid.html'), 'utf8'));
} catch (e) { res.writeHead(500); res.end('pyramid.html missing: ' + e.message); }
return;
}
if (u.pathname === '/activity' || u.pathname === '/activity/') {
try { res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' }); res.end(fs.readFileSync(path.join(ROOT, 'activity.html'), 'utf8')); }
catch (e) { res.writeHead(500); res.end('activity.html missing: ' + e.message); }
return;
}
if (u.pathname === '/api/source') {
const r = resolveSource(q.get('kind'), q.get('name'));
if (!r) return J({ ok: false, error: 'bad name' });
let content = '';
if (r.exists) { try { content = fs.readFileSync(r.path, 'utf8').slice(0, 40000); } catch (e) {} }
return J({ ok: true, kind: q.get('kind'), name: q.get('name'), path: r.path, exists: r.exists, content });
}
if (u.pathname === '/api/open') {
const r = resolveSource(q.get('kind'), q.get('name'));
if (!r || !r.exists) return J({ ok: false, error: 'no on-disk source (plugin/MCP skill or generic agent)' });
execFile('open', [r.path], (err) => J({ ok: !err, path: r.path, error: err ? String(err) : undefined }));
return;
}
if (u.pathname === '/api/run') {
spawnClaude(q.get('kind'), q.get('name'), (err, cmd) => J({ ok: !err, cmd, error: err ? String(err) : undefined }));
return;
}
if (u.pathname === '/api/suggestions') {
return J({ ok: true, list: SUGGESTIONS, decisions: (loadSugState().decisions) || {} });
}
if (u.pathname === '/api/suggest') {
const key = q.get('key'), action = q.get('action');
const s = SUGGESTIONS.find(x => sugKey(x) === key);
if (!s) return J({ ok: false, error: 'unknown suggestion' });
if (!['approve', 'remove', 'reset'].includes(action)) return J({ ok: false, error: 'bad action' });
const st = loadSugState(); st.decisions = st.decisions || {};
if (action === 'remove') { st.decisions[key] = 'removed'; saveSugState(st); return J({ ok: true, action, decisions: st.decisions }); }
if (action === 'reset') { delete st.decisions[key]; saveSugState(st); return J({ ok: true, action, decisions: st.decisions }); }
// approve: record, write into cabinet.yaml, then auto-commit (only if newly added)
st.decisions[key] = 'approved'; saveSugState(st);
const cab = applyToCabinet(s);
if (cab && cab.added) {
gitCommitCabinet(s, (gerr, hash) => J({ ok: true, action, decisions: st.decisions, cabinet: cab, commit: gerr ? null : hash, gitError: gerr ? String(gerr) : undefined }));
} else {
J({ ok: true, action, decisions: st.decisions, cabinet: cab });
}
return;
}
if (u.pathname === '/api/gaps') {
return J({ ok: true, list: GAPS, decisions: (loadGapState().decisions) || {} });
}
if (u.pathname === '/api/gap') {
const key = q.get('key'), action = q.get('action');
const g = GAPS.find(x => gapKey(x) === key);
if (!g) return J({ ok: false, error: 'unknown gap' });
if (!['approve', 'remove', 'reset'].includes(action)) return J({ ok: false, error: 'bad action' });
const st = loadGapState(); st.decisions = st.decisions || {};
if (action === 'remove') { st.decisions[key] = 'removed'; saveGapState(st); return J({ ok: true, action, decisions: st.decisions }); }
if (action === 'reset') { delete st.decisions[key]; saveGapState(st); return J({ ok: true, action, decisions: st.decisions }); }
// approve: run apply-to-file + commit as ONE serialized critical section, so
// parallel clicks neither race the index nor sweep each other's writes.
withCabinetLock(() => new Promise((resolve) => {
const cab = applyGapToCabinet(g);
if (cab && cab.error) { J({ ok: false, error: cab.error }); return resolve(); }
const st2 = loadGapState(); st2.decisions = st2.decisions || {};
st2.decisions[key] = 'approved'; saveGapState(st2);
if (cab && cab.added) {
const msg = g.type === 'new-vp'
? 'cabinet: stand up ' + g.id + ' officer via pyramid gaps overlay'
: 'cabinet: adopt orphan ' + g.kind + ' ' + g.id + ' under ' + g.to + ' via pyramid gaps overlay';
commitCabinetMsg(msg, (gerr, hash) => { J({ ok: true, action, decisions: st2.decisions, cabinet: cab, commit: gerr ? null : hash, gitError: gerr ? String(gerr) : undefined }); resolve(); });
} else {
J({ ok: true, action, decisions: st2.decisions, cabinet: cab }); resolve();
}
}));
return;
}
if (u.pathname === '/api/queue-idea') {
const title = q.get('title') || 'Cabinet idea';
const summary = q.get('summary') || '';
const payload = JSON.stringify({ title, summary, note: summary, project: 'agent-cabinet', source: 'cabinet-pyramid' });
const cr = http.request({ host: '127.0.0.1', port: 3333, path: '/api/parking-lot', method: 'POST',
headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(payload) }, timeout: 5000 },
(cres) => { let d = ''; cres.on('data', c => d += c); cres.on('end', () => J({ ok: cres.statusCode >= 200 && cres.statusCode < 300, status: cres.statusCode })); });
cr.on('error', (e) => J({ ok: false, error: String(e) }));
cr.on('timeout', () => { cr.destroy(); J({ ok: false, error: 'CNCP timeout' }); });
cr.write(payload); cr.end();
return;
}
if (u.pathname === '/api/health') {
const canaries = {}, officers = {}, rank = { down: 3, warn: 2, up: 1, na: 0 };
for (const off in HEALTH_SOURCES) {
let worst = 'na'; const details = [];
HEALTH_SOURCES[off].forEach(sk => {
const c = readCanary(sk); canaries[sk] = c;
const st = officerStatus(c.sev); if (rank[st] > rank[worst]) worst = st;
details.push(sk + ': ' + c.sev + (c.summary ? ' (' + c.summary + ')' : ''));
});
officers[off] = { status: worst, detail: details.join(' · ') };
}
return J({ ok: true, generated_at: new Date().toISOString(), officers, canaries });
}
if (u.pathname === '/api/cost') {
let total = 0, n = 0, first = null, last = null; const byApp = {}, byVendor = {};
try {
const lines = fs.readFileSync(path.join(HOME, '.claude', 'cost-ledger.jsonl'), 'utf8').split('\n');
for (const ln of lines) {
if (!ln.trim()) continue; let j; try { j = JSON.parse(ln); } catch (e) { continue; }
const c = +j.cost_usd || 0; total += c; n++;
byApp[j.app || '?'] = (byApp[j.app || '?'] || 0) + c;
byVendor[j.vendor || '?'] = (byVendor[j.vendor || '?'] || 0) + c;
if (j.ts) { if (!first || j.ts < first) first = j.ts; if (!last || j.ts > last) last = j.ts; }
}
} catch (e) { return J({ ok: false, error: 'no cost-ledger' }); }
const sortObj = (o) => Object.entries(o).map(([k, v]) => ({ name: k, usd: +v.toFixed(4) })).sort((a, b) => b.usd - a.usd);
return J({ ok: true, total_usd: +total.toFixed(2), entries: n, first, last, by_app: sortObj(byApp).slice(0, 80), by_vendor: sortObj(byVendor).slice(0, 20) });
}
if (u.pathname === '/api/activity') {
const ev = [];
// 1) git commits from active repos
[['agent-cabinet', ROOT], ['cncp-starter', path.join(HOME, 'cncp-starter')]].forEach(([nm, r]) => {
try {
execSync('git -C "' + r + '" log -20 --pretty=format:%cI%x09%h%x09%s', { encoding: 'utf8' }).split('\n').forEach(l => {
const p = l.split('\t'); if (p[0]) ev.push({ ts: p[0], type: 'commit', title: p.slice(2).join(' '), detail: nm + ' · ' + p[1], repo: nm, hash: p[1] });
});
} catch (e) {}
});
// 2) canary runs (last verdict each)
['dw-uptime-probe', 'dw-scraper-canary', 'dw-map-auditor', 'dw-leak-scanner', 'dw-backup-canary', 'dw-canary-meta-watchdog'].forEach(sk => {
const c = readCanary(sk); if (c.ts) ev.push({ ts: c.ts, type: 'canary', verdict: c.sev, title: sk + ' — ' + c.sev, detail: c.summary || '', skill: sk });
});
// 3) recent paid API calls
try {
const lines = fs.readFileSync(path.join(HOME, '.claude', 'cost-ledger.jsonl'), 'utf8').trim().split('\n');
lines.slice(-30).forEach(l => { try { const j = JSON.parse(l); ev.push({ ts: j.ts, type: 'cost', title: '$' + (+j.cost_usd).toFixed(4) + ' · ' + j.app, detail: (j.vendor || '') + (j.note ? ' · ' + j.note : '') }); } catch (e) {} });
} catch (e) {}
// 4) overnight watch ticks (lines carry no date → use the run-notes file's mtime day)
try {
const rnf = path.join(HOME, '.claude', 'run-notes', 'overnight-2026-06-15.md');
const md = new Date(fs.statSync(rnf).mtimeMs);
const dstr = md.getFullYear() + '-' + ('0' + (md.getMonth() + 1)).slice(-2) + '-' + ('0' + md.getDate()).slice(-2);
fs.readFileSync(rnf, 'utf8').split('\n').forEach(l => {
const m = l.match(/tick\s+(\d{1,2}):(\d{2})\s*PT/i);
if (m) ev.push({ ts: dstr + 'T' + ('0' + m[1]).slice(-2) + ':' + m[2] + ':00-07:00', type: 'tick', title: 'watch tick ' + m[1] + ':' + m[2], detail: l.replace(/^[-*\s]+/, '').replace(/\*\*/g, '').slice(0, 140) });
});
} catch (e) {}
// 5) CNCP wins (async) → finalize
const finish = (wins) => {
(wins || []).forEach(w => {
const t = w.createdAt ? new Date(w.createdAt) : (w.date ? new Date(w.date) : null);
ev.push({ ts: (t || new Date()).toISOString(), type: 'win', title: w.title || 'win', detail: (w.project || '') + (w.commit ? ' · ' + w.commit : '') });
});
ev.sort((a, b) => (Date.parse(b.ts) || 0) - (Date.parse(a.ts) || 0));
J({ ok: true, count: ev.length, generated_at: new Date().toISOString(), events: ev.slice(0, 250) });
};
const wr = http.request({ host: '127.0.0.1', port: 3333, path: '/api/wins', method: 'GET', timeout: 4000 }, (cr) => {
let d = ''; cr.on('data', c => d += c); cr.on('end', () => { let arr = []; try { const j = JSON.parse(d); arr = j.wins || j.items || (Array.isArray(j) ? j : []); } catch (e) {} finish((arr || []).slice(0, 50)); });
});
wr.on('error', () => finish([])); wr.on('timeout', () => { wr.destroy(); finish([]); }); wr.end();
return;
}
if (u.pathname === '/api/schedule') {
const now = Date.now(), upcoming = [], continuous = [];
let files = [];
try { files = fs.readdirSync(path.join(HOME, 'Library', 'LaunchAgents')).filter(f => /^com\.steve\..*\.plist$/.test(f)); } catch (e) {}
const { loaded, disabled } = launchState(); // real launchd state, not plist contents
files.forEach(f => {
let d; try { d = JSON.parse(execSync('plutil -convert json -o - "' + path.join(HOME, 'Library', 'LaunchAgents', f) + '"', { encoding: 'utf8' })); } catch (e) { return; }
const label = (d.Label || f).replace(/^com\.steve\./, '');
// A job will only actually fire if it's loaded AND not disabled. Flag the rest
// so the dashboard shows "won't fire" instead of a phantom upcoming run.
const st = loaded.get(label);
const isDisabled = disabled.has(label);
const wontFire = !st || isDisabled;
const health = { loaded: !!st, disabled: isDisabled, lastExit: st ? st.exit : null,
wontFire, reason: isDisabled ? 'disabled' : (!st ? 'unloaded' : (st.exit && st.exit !== 0 ? 'last exit ' + st.exit : null)) };
if (d.StartInterval) {
if (d.StartInterval >= 600) continuous.push({ label, cadence: humanInt(d.StartInterval), every_s: d.StartInterval, ...health });
return;
}
const sc = d.StartCalendarInterval;
if (!sc) return; // KeepAlive / RunAtLoad — not a scheduled fire
const specs = Array.isArray(sc) ? sc : [sc];
let best = Infinity, spec = null;
specs.forEach(s => { const t = nextCal(s, now); if (t < best) { best = t; spec = s; } });
if (spec) upcoming.push({ label, next_ts: new Date(best).toISOString(), cadence: Array.isArray(sc) ? (specs.length + '×/day') : calCadence(spec), ...health });
});
// sort: firing jobs by next fire; won't-fire jobs sink to the bottom of the lane
upcoming.sort((a, b) => (a.wontFire - b.wontFire) || (Date.parse(a.next_ts) - Date.parse(b.next_ts)));
continuous.sort((a, b) => (a.wontFire - b.wontFire) || (a.every_s - b.every_s));
return J({ ok: true, generated_at: new Date().toISOString(), upcoming, continuous });
}
if (u.pathname === '/api/job') {
const lab = (q.get('label') || '').replace(/^com\.steve\./, '');
if (!/^[a-z0-9][a-z0-9._-]*$/.test(lab)) return J({ ok: false, error: 'bad label' });
const full = 'com.steve.' + lab, pf = path.join(HOME, 'Library', 'LaunchAgents', full + '.plist');
let d; try { d = JSON.parse(execSync('plutil -convert json -o - "' + pf + '"', { encoding: 'utf8' })); } catch (e) { return J({ ok: false, error: 'plist not found' }); }
const program = d.ProgramArguments || (d.Program ? [d.Program] : []);
let pid = null, lastExit = null, loaded = false;
try { const s = execSync('launchctl list ' + full + ' 2>/dev/null', { encoding: 'utf8' }); loaded = true; const mp = s.match(/"PID"\s*=\s*(\d+)/); if (mp) pid = +mp[1]; const me = s.match(/"LastExitStatus"\s*=\s*(-?\d+)/); if (me) lastExit = +me[1]; } catch (e) {}
let sched = '—';
if (d.StartInterval) sched = humanInt(d.StartInterval);
else if (d.StartCalendarInterval) { const sc = d.StartCalendarInterval; sched = Array.isArray(sc) ? sc.map(calCadence).join(' · ') : calCadence(sc); }
else if (d.KeepAlive) sched = 'KeepAlive (always running)';
let logTail = ''; const lp = d.StandardOutPath || d.StandardErrorPath;
if (lp && lp[0] === '/') { try { logTail = execSync('tail -n 25 "' + lp + '" 2>/dev/null', { encoding: 'utf8' }); } catch (e) {} }
return J({ ok: true, label: full, schedule: sched, program, stdout: d.StandardOutPath || null, stderr: d.StandardErrorPath || null, runAtLoad: !!d.RunAtLoad, status: { loaded, pid, lastExit }, logTail: logTail.slice(-4000) });
}
if (u.pathname === '/api/commit') {
const repo = q.get('repo'), hash = q.get('hash');
const map = { 'agent-cabinet': ROOT, 'cncp-starter': path.join(HOME, 'cncp-starter') };
if (!map[repo]) return J({ ok: false, error: 'unknown repo' });
if (!/^[0-9a-f]{6,40}$/.test(hash || '')) return J({ ok: false, error: 'bad hash' });
try { const out = execSync('git -C "' + map[repo] + '" show --stat --format="%H%n%an <%ae>%n%cI%n%n%B" ' + hash, { encoding: 'utf8', maxBuffer: 1 << 20 }); return J({ ok: true, repo, hash, detail: out.slice(0, 8000) }); }
catch (e) { return J({ ok: false, error: 'git show failed' }); }
}
if (u.pathname === '/api/canary') {
const sk = q.get('skill'); if (!/^[a-z0-9][a-z0-9._-]*$/.test(sk || '')) return J({ ok: false, error: 'bad skill' });
try { return J({ ok: true, skill: sk, data: JSON.parse(fs.readFileSync(path.join(HOME, '.claude', 'skills', sk, 'data', 'latest.json'), 'utf8')) }); }
catch (e) { return J({ ok: false, error: 'no data' }); }
}
if (req.url === '/cabinet.yaml') {
try {
const yaml = fs.readFileSync(CABINET, 'utf8');
res.writeHead(200, { 'Content-Type': 'text/yaml' });
res.end(yaml);
} catch (e) {
res.writeHead(500);
res.end(`Could not read cabinet.yaml: ${e.message}`);
}
return;
}
if (req.url === '/game' || req.url === '/game/') {
try {
const html = renderGame();
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
res.end(html);
} catch (e) {
res.writeHead(500);
res.end(`Game render error: ${e.message}\n${e.stack}`);
}
return;
}
try {
const cab = parseYaml(fs.readFileSync(CABINET, 'utf8'));
const html = render(cab);
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
res.end(html);
} catch (e) {
res.writeHead(500);
res.end(`Cabinet render error: ${e.message}`);
}
});
server.listen(PORT, '0.0.0.0', () => {
console.log(`The Cabinet viewer on http://localhost:${PORT}`);
console.log(`(also tailnet) http://100.65.187.120:${PORT}`);
});