1- <!DOCTYPE html>
1+ const fs = require ( 'fs' ) ;
2+
3+ const readme = fs . readFileSync ( 'README.md' , 'utf8' ) ;
4+
5+ // Parse official plugins
6+ const officialMatch = readme . match ( / # # O f f i c i a l P l u g i n s \n \n [ \s \S ] * ?\n ( (?: - \S .* \n ) + ) / ) ;
7+ // Parse community plugins - split by ### subcategories
8+ const communityMatch = readme . match ( / # # C o m m u n i t y P l u g i n s \n \n [ \s \S ] * ?(? = \n # # | \n - - - ) / ) ;
9+
10+ function parseLine ( line ) {
11+ // Match: - [Name](url) - Description (linked)
12+ let m = line . match ( / ^ - \[ ( [ ^ \] ] + ) \] \( ( [ ^ ) ] + ) \) \s * [ - – ] \s * ( .+ ) / ) ;
13+ if ( m ) return { name : m [ 1 ] , url : m [ 2 ] , desc : m [ 3 ] . trim ( ) } ;
14+ // Match: - Name - Description (unlinked, official)
15+ m = line . match ( / ^ - ( [ A - Z ] [ A - Z a - z 0 - 9 \s ] + ?) \s * [ - – ] \s * ( .+ ) / ) ;
16+ if ( m ) return { name : m [ 1 ] . trim ( ) , url : 'https://developers.openai.com/codex/plugins' , desc : m [ 2 ] . trim ( ) } ;
17+ return null ;
18+ }
19+
20+ function parseSection ( text ) {
21+ if ( ! text ) return [ ] ;
22+ const lines = text . split ( '\n' ) . filter ( l => l . startsWith ( '- ' ) ) ;
23+ return lines . map ( parseLine ) . filter ( Boolean ) ;
24+ }
25+
26+ const official = parseSection ( officialMatch ?. [ 1 ] || '' ) ;
27+
28+ // Parse community with subcategories
29+ const community = [ ] ;
30+ const catLines = ( communityMatch ?. [ 0 ] || '' ) . split ( '\n' ) ;
31+ let currentCat = 'General' ;
32+ for ( const line of catLines ) {
33+ const catMatch = line . match ( / ^ # # # ( .+ ) / ) ;
34+ if ( catMatch ) { currentCat = catMatch [ 1 ] ; continue ; }
35+ const plugin = parseLine ( line ) ;
36+ if ( plugin ) community . push ( { ...plugin , cat : currentCat } ) ;
37+ }
38+
39+ const total = official . length + community . length ;
40+
41+ // Count unique categories
42+ const cats = [ ...new Set ( community . map ( p => p . cat ) ) ] ;
43+
44+ const siteHtml = `<!DOCTYPE html>
245<html lang="en">
346<head>
447 <meta charset="UTF-8">
5497</head>
5598<body class="bg-white text-gray-900 antialiased">
5699
57- <!-- Nav -->
58100 <nav class="border-b border-gray-200">
59101 <div class="max-w-5xl mx-auto px-6 h-14 flex items-center justify-between">
60102 <a href="/" class="font-mono text-sm text-gray-500 hover:text-gray-900 transition-colors">
69111
70112 <div class="max-w-5xl mx-auto px-6">
71113
72- <!-- Hero -->
73114 <header class="pt-16 pb-12 border-b border-gray-200">
74115 <p class="font-mono text-xs uppercase tracking-widest text-hol-purple mb-4">curated directory</p>
75116 <h1 class="font-mono text-4xl font-bold leading-tight text-gray-900 max-w-lg">
@@ -84,116 +125,79 @@ <h1 class="font-mono text-4xl font-bold leading-tight text-gray-900 max-w-lg">
84125 </div>
85126 </header>
86127
87- <!-- Search -->
88128 <div class="py-5 border-b border-gray-200">
89129 <input type="text" id="q" placeholder="filter plugins..." autocomplete="off"
90130 class="w-full max-w-xs font-mono text-sm px-3 py-2 bg-surface-50 border border-gray-200 rounded-lg outline-none focus:border-hol-purple/50 transition-colors placeholder:text-gray-300">
91131 </div>
92132
93- <!-- Official -->
94133 <section id="sec-official" class="pt-8 pb-4">
95134 <div class="flex items-center gap-3 mb-5">
96135 <h2 class="font-mono text-xs font-medium uppercase tracking-widest text-gray-400">Official</h2>
97- < span class ="font-mono text-[10px] text-gray-300 bg-surface-100 px-1.5 py-0.5 rounded " id =" official-count " > 12 </ span >
136+ <span class="font-mono text-[10px] text-gray-300 bg-surface-100 px-1.5 py-0.5 rounded"> ${ official . length } </span>
98137 <div class="flex-1 h-px bg-gray-200"></div>
99138 </div>
100139 <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3" id="list-official"></div>
101140 </section>
102141
103- <!-- Community -->
104142 <section id="sec-community" class="pt-8 pb-4">
105143 <div class="flex items-center gap-3 mb-5">
106144 <h2 class="font-mono text-xs font-medium uppercase tracking-widest text-gray-400">Community</h2>
107- < span class ="font-mono text-[10px] text-gray-300 bg-surface-100 px-1.5 py-0.5 rounded " id =" community-count " > 14 </ span >
145+ <span class="font-mono text-[10px] text-gray-300 bg-surface-100 px-1.5 py-0.5 rounded"> ${ community . length } </span>
108146 <div class="flex-1 h-px bg-gray-200"></div>
109147 </div>
110148 <div id="cats"></div>
111149 </section>
112150
113151 <div id="empty" class="hidden py-16 text-center font-mono text-sm text-gray-300">No results.</div>
114152
115- <!-- Footer -->
116153 <footer class="py-10 mt-4 border-t border-gray-200">
117154 <p class="font-mono text-xs text-gray-300">
118- Apache 2.0 · < a href ="https://hol.org " target ="_blank " rel ="noopener " class ="text-gray-400 hover:text-gray-900 transition-colors "> HOL</ a > · < span id =" total " > 26 </ span > plugins
155+ Apache 2.0 · <a href="https://hol.org" target="_blank" rel="noopener" class="text-gray-400 hover:text-gray-900 transition-colors">HOL</a> · ${ total } plugins
119156 </p>
120157 </footer>
121158 </div>
122159
123160 <script>
124- const O = [
125- [ "Box" , "Access and manage files" , "https://developers.openai.com/codex/plugins" ] ,
126- [ "Cloudflare" , "Manage Workers, Pages, DNS, and infrastructure" , "https://developers.openai.com/codex/plugins" ] ,
127- [ "Figma" , "Inspect designs, extract specs, document components" , "https://developers.openai.com/codex/plugins" ] ,
128- [ "GitHub" , "Review changes, manage issues, interact with repos" , "https://developers.openai.com/codex/plugins" ] ,
129- [ "Gmail" , "Read, search, compose emails" , "https://developers.openai.com/codex/plugins" ] ,
130- [ "Google Drive" , "Edit and manage files" , "https://developers.openai.com/codex/plugins" ] ,
131- [ "Hugging Face" , "Browse models, datasets, spaces" , "https://developers.openai.com/codex/plugins" ] ,
132- [ "Linear" , "Issues, projects, workflows" , "https://developers.openai.com/codex/plugins" ] ,
133- [ "Notion" , "Pages, databases, content" , "https://developers.openai.com/codex/plugins" ] ,
134- [ "Sentry" , "Error monitoring, triage, performance" , "https://developers.openai.com/codex/plugins" ] ,
135- [ "Slack" , "Messages, channels, conversations" , "https://developers.openai.com/codex/plugins" ] ,
136- [ "Vercel" , "Deploy, preview, manage projects" , "https://developers.openai.com/codex/plugins" ]
137- ] ;
138-
139- const C = [
140- [ "Registry Broker" , "Delegate tasks to specialist agents via HOL Registry" , "https://github.com/hashgraph-online/registry-broker-codex-plugin" , "Development" ] ,
141- [ "Project Autopilot" , "Structured project workflow: planning, execution, handoff" , "https://github.com/AlexMi64/codex-project-autopilot" , "Development" ] ,
142- [ "Codex Reviewer" , "Second-pass review of Claude-driven plans" , "https://github.com/schuettc/codex-reviewer" , "Development" ] ,
143- [ "HOTL Plugin" , "Human-on-the-Loop coding workflow plugin for Codex, Claude Code, and Cline with structured planning, review, and verification guardrails" , "https://github.com/yimwoo/hotl-plugin" , "Development" ] ,
144- [ "AgentOps" , "DevOps for coding agents with persistent memory" , "https://github.com/boshu2/agentops" , "Development" ] ,
145- [ "Codex Be Serious" , "Enforce formal written register across output" , "https://github.com/lulucatdev/codex-be-serious" , "Development" ] ,
146- [ "Chrome DevTools" , "Wrapper for chrome-devtools-mcp" , "https://github.com/win4r/chrome-devtools-codex-plugin" , "Integrations" ] ,
147- [ "Launch Fast" , "Rapid SaaS deployment adapter" , "https://github.com/BlockchainHB/launchfast_codex_plugin" , "Integrations" ] ,
148- [ "PapersFlow" , "Paper discovery, citation verification, DeepScan" , "https://github.com/papersflow-ai/papersflow-codex-plugin" , "Integrations" ] ,
149- [ "Apple Productivity" , "Calendar and Reminders for macOS" , "https://github.com/matk0shub/apple-productivity-mcp" , "Integrations" ] ,
150- [ "Yandex Direct" , "Yandex Direct, Wordstat, Metrika, Roistat" , "https://github.com/nebelov/yandex-direct-for-all" , "Integrations" ] ,
151- [ "OpenProject" , "Team collaboration via OpenProject" , "https://github.com/varaprasadreddy9676/team-codex-plugins" , "Integrations" ] ,
152- [ "OrgX" , "MCP access and initiative-aware skills" , "https://github.com/useorgx/orgx-codex-plugin" , "Integrations" ] ,
153- [ "Codex Mem" , "Capture and inject session context" , "https://github.com/2kDarki/codex-mem" , "Integrations" ]
154- ] ;
155-
156- const card = ( [ name , desc , url ] , official ) => `
157- <a href="${ url } " target="_blank" rel="noopener"
161+ const O = ${ JSON . stringify ( official ) } ;
162+ const C = ${ JSON . stringify ( community . map ( p => [ p . name , p . desc , p . url , p . cat ] ) ) } ;
163+
164+ const card = ([name, desc, url], official) => \`
165+ <a href="\${url}" target="_blank" rel="noopener"
158166 class="group block p-4 rounded-xl border border-gray-200 bg-surface-50 hover:border-hol-purple/30 hover:bg-white hover:shadow-sm transition-all">
159167 <div class="flex items-start justify-between gap-2">
160- <h3 class="font-mono text-sm font-medium text-gray-900">${ name } </h3>
161- <span class="font-mono text-[10px] font-medium uppercase tracking-wide px-2 py-0.5 rounded shrink-0 ${ official ? 'bg-purple-50 text-hol-purple' : 'bg-emerald-50 text-emerald-600' } ">${ official ? 'Official' : 'Verified' } </span>
168+ <h3 class="font-mono text-sm font-medium text-gray-900">\ ${name}</h3>
169+ <span class="font-mono text-[10px] font-medium uppercase tracking-wide px-2 py-0.5 rounded shrink-0 \ ${official ? 'bg-purple-50 text-hol-purple' : 'bg-emerald-50 text-emerald-600'}">\ ${official ? 'Official' : 'Verified'}</span>
162170 </div>
163- <p class="mt-2 text-sm text-gray-400 leading-relaxed line-clamp-2">${ desc } </p>
164- </a>` ;
171+ <p class="mt-2 text-sm text-gray-400 leading-relaxed line-clamp-2">\ ${desc}</p>
172+ </a>\ `;
165173
166174 const q = document.getElementById('q');
167175 const lo = document.getElementById('list-official');
168176 const catsEl = document.getElementById('cats');
169177 const empty = document.getElementById('empty');
170178 const secO = document.getElementById('sec-official');
171179 const secC = document.getElementById('sec-community');
172- const oCount = document . getElementById ( 'official-count' ) ;
173- const cCount = document . getElementById ( 'community-count' ) ;
174180
175181 function render() {
176182 const s = q.value.toLowerCase();
177183 const fo = s ? O.filter(([n,d]) => n.toLowerCase().includes(s) || d.toLowerCase().includes(s)) : O;
178184 const fc = s ? C.filter(([n,d]) => n.toLowerCase().includes(s) || d.toLowerCase().includes(s)) : C;
179-
180185 secO.style.display = fo.length ? '' : 'none';
181186 secC.style.display = fc.length ? '' : 'none';
182187 empty.classList.toggle('hidden', fo.length || fc.length);
183- oCount . textContent = fo . length ;
184- cCount . textContent = fc . length ;
185-
186188 lo.innerHTML = fo.map(p => card(p, true)).join('');
187-
188189 const groups = {};
189190 fc.forEach(p => { (groups[p[3]] = groups[p[3]] || []).push(p); });
190191 catsEl.innerHTML = Object.entries(groups).map(([cat, items]) =>
191- `<p class="font-mono text-[10px] uppercase tracking-widest text-gray-300 font-medium mt-8 mb-3">${ cat } </p><div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3">${ items . map ( p => card ( p , false ) ) . join ( '' ) } </div>`
192+ \ `<p class="font-mono text-[10px] uppercase tracking-widest text-gray-300 font-medium mt-8 mb-3">\ ${cat}</p><div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3">\ ${items.map(p => card(p, false)).join('')}</div>\ `
192193 ).join('');
193194 }
194-
195195 render();
196196 q.addEventListener('input', render);
197197 </script>
198198</body>
199- </ html >
199+ </html>` ;
200+
201+ fs . mkdirSync ( 'dist' , { recursive : true } ) ;
202+ fs . writeFileSync ( 'dist/index.html' , siteHtml ) ;
203+ console . log ( `Built site: ${ total } plugins (${ official . length } official, ${ community . length } community)` ) ;
0 commit comments