mirror of
https://github.com/HKUDS/CLI-Anything.git
synced 2026-07-07 23:23:40 +08:00
517 lines
17 KiB
HTML
517 lines
17 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>CLI-Hub</title>
|
|
<style>
|
|
:root {
|
|
--bg: #0d1117;
|
|
--surface: #161b22;
|
|
--border: #30363d;
|
|
--text: #e6edf3;
|
|
--text-muted: #8b949e;
|
|
--accent: #58a6ff;
|
|
--accent-hover: #79c0ff;
|
|
--green: #3fb950;
|
|
--tag-bg: #1f2937;
|
|
}
|
|
|
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
|
|
body {
|
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
|
|
background: var(--bg);
|
|
color: var(--text);
|
|
line-height: 1.6;
|
|
}
|
|
|
|
/* ── Header ── */
|
|
.header {
|
|
text-align: center;
|
|
padding: 3.5rem 2rem 2.5rem;
|
|
border-bottom: 1px solid var(--border);
|
|
max-width: 960px;
|
|
margin: 0 auto;
|
|
}
|
|
|
|
.header h1 {
|
|
font-size: 2.8rem;
|
|
font-weight: 800;
|
|
letter-spacing: -0.02em;
|
|
margin-bottom: 0.6rem;
|
|
}
|
|
|
|
.header h1 span { color: var(--accent); }
|
|
|
|
.tagline {
|
|
font-size: 1.2rem;
|
|
color: var(--text-muted);
|
|
line-height: 1.5;
|
|
max-width: 800px;
|
|
margin: 0 auto 1.25rem;
|
|
}
|
|
|
|
.tagline strong { color: var(--text); font-weight: 600; }
|
|
|
|
.header-links {
|
|
display: flex;
|
|
justify-content: center;
|
|
gap: 0.5rem;
|
|
margin-bottom: 1.5rem;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.header-btn {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 0.4rem;
|
|
padding: 0.45rem 1rem;
|
|
background: var(--surface);
|
|
border: 1px solid var(--border);
|
|
border-radius: 6px;
|
|
color: var(--text);
|
|
text-decoration: none;
|
|
font-size: 0.85rem;
|
|
font-weight: 500;
|
|
transition: border-color 0.15s, color 0.15s;
|
|
}
|
|
|
|
.header-btn:hover { border-color: var(--accent); color: var(--accent); }
|
|
|
|
.cta {
|
|
font-size: 0.95rem;
|
|
color: var(--text-muted);
|
|
line-height: 1.65;
|
|
max-width: 800px;
|
|
margin: 0 auto;
|
|
}
|
|
|
|
.cta em { color: var(--accent); font-style: normal; font-weight: 600; }
|
|
|
|
.stats {
|
|
display: flex;
|
|
justify-content: center;
|
|
gap: 3rem;
|
|
margin-top: 1.75rem;
|
|
}
|
|
|
|
.stat-num {
|
|
font-size: 2rem;
|
|
font-weight: 700;
|
|
color: var(--accent);
|
|
}
|
|
|
|
.stat-label {
|
|
font-size: 0.8rem;
|
|
color: var(--text-muted);
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.05em;
|
|
}
|
|
|
|
/* ── Controls ── */
|
|
.controls {
|
|
max-width: 1200px;
|
|
margin: 1.5rem auto;
|
|
padding: 0 1rem;
|
|
display: flex;
|
|
gap: 0.75rem;
|
|
flex-wrap: wrap;
|
|
align-items: center;
|
|
}
|
|
|
|
.search {
|
|
flex: 1;
|
|
min-width: 200px;
|
|
padding: 0.6rem 1rem;
|
|
background: var(--surface);
|
|
border: 1px solid var(--border);
|
|
border-radius: 6px;
|
|
color: var(--text);
|
|
font-size: 0.95rem;
|
|
outline: none;
|
|
}
|
|
|
|
.search:focus { border-color: var(--accent); }
|
|
|
|
.filter-btn {
|
|
padding: 0.45rem 0.85rem;
|
|
background: var(--surface);
|
|
border: 1px solid var(--border);
|
|
border-radius: 20px;
|
|
color: var(--text-muted);
|
|
font-size: 0.82rem;
|
|
cursor: pointer;
|
|
transition: all 0.15s;
|
|
}
|
|
|
|
.filter-btn:hover { border-color: var(--accent); color: var(--text); }
|
|
.filter-btn.active { background: var(--accent); color: #000; border-color: var(--accent); font-weight: 600; }
|
|
|
|
/* ── Grid ── */
|
|
.grid {
|
|
max-width: 1200px;
|
|
margin: 1rem auto 3rem;
|
|
padding: 0 1rem;
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
|
|
gap: 1rem;
|
|
}
|
|
|
|
.card {
|
|
background: var(--surface);
|
|
border: 1px solid var(--border);
|
|
border-radius: 8px;
|
|
padding: 1.25rem;
|
|
transition: border-color 0.15s;
|
|
}
|
|
|
|
.card:hover { border-color: var(--accent); }
|
|
|
|
.card-head {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
margin-bottom: 0.5rem;
|
|
}
|
|
|
|
.card-title {
|
|
font-size: 1.2rem;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.card-title a {
|
|
color: var(--text);
|
|
text-decoration: none;
|
|
}
|
|
|
|
.card-title a:hover { color: var(--accent); }
|
|
|
|
.card-title a svg {
|
|
width: 12px;
|
|
height: 12px;
|
|
margin-left: 4px;
|
|
vertical-align: middle;
|
|
opacity: 0.4;
|
|
}
|
|
|
|
.card-title a:hover svg { opacity: 1; }
|
|
|
|
.card-version {
|
|
font-size: 0.75rem;
|
|
color: var(--green);
|
|
background: rgba(63, 185, 80, 0.15);
|
|
padding: 0.15rem 0.5rem;
|
|
border-radius: 10px;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.card-desc {
|
|
color: var(--text-muted);
|
|
font-size: 0.9rem;
|
|
margin-bottom: 0.75rem;
|
|
}
|
|
|
|
.card-requires {
|
|
font-size: 0.8rem;
|
|
color: var(--text-muted);
|
|
margin-bottom: 0.75rem;
|
|
}
|
|
|
|
.card-requires strong { color: var(--text); }
|
|
|
|
.card-install {
|
|
background: #0d1117;
|
|
border: 1px solid var(--border);
|
|
border-radius: 6px;
|
|
padding: 0.5rem 0.75rem;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: 0.5rem;
|
|
margin-bottom: 0.75rem;
|
|
}
|
|
|
|
.card-install code {
|
|
font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
|
|
font-size: 0.78rem;
|
|
color: var(--accent);
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
flex: 1;
|
|
}
|
|
|
|
.copy-btn {
|
|
background: none;
|
|
border: 1px solid var(--border);
|
|
border-radius: 4px;
|
|
color: var(--text-muted);
|
|
cursor: pointer;
|
|
padding: 0.25rem 0.5rem;
|
|
font-size: 0.75rem;
|
|
transition: all 0.15s;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.copy-btn:hover { color: var(--text); border-color: var(--accent); }
|
|
|
|
.card-links {
|
|
display: flex;
|
|
gap: 0.75rem;
|
|
font-size: 0.82rem;
|
|
}
|
|
|
|
.card-links a {
|
|
color: var(--accent);
|
|
text-decoration: none;
|
|
}
|
|
|
|
.card-links a:hover { text-decoration: underline; color: var(--accent-hover); }
|
|
|
|
.card-contributor {
|
|
font-size: 0.8rem;
|
|
color: var(--text-muted);
|
|
margin-bottom: 0.75rem;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.35rem;
|
|
}
|
|
|
|
.card-contributor a {
|
|
color: var(--accent);
|
|
text-decoration: none;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.card-contributor a:hover { text-decoration: underline; color: var(--accent-hover); }
|
|
|
|
.card-contributor .community-badge {
|
|
font-size: 0.68rem;
|
|
color: #d2a8ff;
|
|
background: rgba(210, 168, 255, 0.15);
|
|
padding: 0.1rem 0.4rem;
|
|
border-radius: 10px;
|
|
margin-left: 0.25rem;
|
|
}
|
|
|
|
.category-tag {
|
|
font-size: 0.72rem;
|
|
color: var(--text-muted);
|
|
background: var(--tag-bg);
|
|
padding: 0.1rem 0.45rem;
|
|
border-radius: 4px;
|
|
margin-left: 0.5rem;
|
|
}
|
|
|
|
.empty {
|
|
grid-column: 1 / -1;
|
|
text-align: center;
|
|
padding: 3rem;
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
/* ── Footer ── */
|
|
.footer {
|
|
text-align: center;
|
|
padding: 2rem 1rem;
|
|
border-top: 1px solid var(--border);
|
|
color: var(--text-muted);
|
|
font-size: 0.85rem;
|
|
}
|
|
|
|
.footer a { color: var(--accent); text-decoration: none; }
|
|
.footer a:hover { text-decoration: underline; }
|
|
|
|
@media (max-width: 480px) {
|
|
.grid { grid-template-columns: 1fr; }
|
|
.header h1 { font-size: 2rem; }
|
|
.header { padding: 2rem 1rem 1.5rem; }
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="header">
|
|
<h1><span>CLI-</span><span style="color: #86868b;">Anything</span> <span>Hub</span></h1>
|
|
<p class="tagline"><strong>Any software. Any codebase. Any Web API.</strong> Generate an agent-native CLI and let AI agents operate it — install with a single pip command.</p>
|
|
<div class="header-links">
|
|
<a class="header-btn" href="https://github.com/HKUDS/CLI-Anything" target="_blank">
|
|
<svg viewBox="0 0 98 96" width="18" height="18" fill="currentColor"><path fill-rule="evenodd" clip-rule="evenodd" d="M48.854 0C21.839 0 0 22 0 49.217c0 21.756 13.993 40.172 33.405 46.69 2.427.49 3.316-1.059 3.316-2.362 0-1.141-.08-5.052-.08-9.127-13.59 2.934-16.42-5.867-16.42-5.867-2.184-5.704-5.42-7.17-5.42-7.17-4.448-3.015.324-3.015.324-3.015 4.934.326 7.523 5.052 7.523 5.052 4.367 7.496 11.404 5.378 14.235 4.074.404-3.178 1.699-5.378 3.074-6.6C29.304 70.25 17.9 66.013 17.9 47.02c0-5.52 1.94-10.046 5.127-13.58-.486-1.302-2.264-6.437.486-13.34 0 0 4.206-1.302 13.59 5.216 3.963-1.14 8.17-1.628 12.34-1.628 4.17 0 8.376.568 12.34 1.628 9.384-6.518 13.59-5.216 13.59-5.216 2.75 6.903.972 12.038.486 13.34 3.268 3.534 5.127 8.06 5.127 13.58 0 19.074-11.485 23.15-22.328 24.29 1.78 1.548 3.316 4.481 3.316 9.126 0 6.6-.08 11.897-.08 13.526 0 1.304.89 2.853 3.316 2.364 19.412-6.52 33.405-24.935 33.405-46.691C97.707 22 75.788 0 48.854 0z"/></svg>
|
|
Star on GitHub
|
|
</a>
|
|
<a class="header-btn" href="https://github.com/HKUDS/CLI-Anything/blob/main/CONTRIBUTING.md" target="_blank">
|
|
<svg viewBox="0 0 16 16" width="16" height="16" fill="currentColor"><path d="M11.013 1.427a1.75 1.75 0 0 1 2.474 0l1.086 1.086a1.75 1.75 0 0 1 0 2.474l-8.61 8.61c-.21.21-.47.364-.756.445l-3.251.93a.75.75 0 0 1-.927-.928l.929-3.25c.081-.286.235-.547.445-.758l8.61-8.61Zm.176 4.823L9.75 4.81l-6.286 6.287a.253.253 0 0 0-.064.108l-.558 1.953 1.953-.558a.253.253 0 0 0 .108-.064Zm1.238-3.763a.25.25 0 0 0-.354 0L10.811 3.75l1.439 1.44 1.263-1.263a.25.25 0 0 0 0-.354Z"/></svg>
|
|
Contributing Guide
|
|
</a>
|
|
<a class="header-btn" href="https://github.com/HKUDS/CLI-Anything/blob/main/.github/PULL_REQUEST_TEMPLATE.md" target="_blank">
|
|
<svg viewBox="0 0 16 16" width="16" height="16" fill="currentColor"><path d="M1.5 3.25a2.25 2.25 0 1 1 3 2.122v5.256a2.251 2.251 0 1 1-1.5 0V5.372A2.25 2.25 0 0 1 1.5 3.25Zm5.677-.177L9.573.677A.25.25 0 0 1 10 .854V2.5h1A2.5 2.5 0 0 1 13.5 5v5.628a2.251 2.251 0 1 1-1.5 0V5a1 1 0 0 0-1-1h-1v1.646a.25.25 0 0 1-.427.177L7.177 3.427a.25.25 0 0 1 0-.354ZM3.75 2.5a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Zm0 9.5a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Zm8.25.75a.75.75 0 1 0 1.5 0 .75.75 0 0 0-1.5 0Z"/></svg>
|
|
PR Template
|
|
</a>
|
|
</div>
|
|
<p class="cta">We welcome contributions for <em>any</em> application — desktop apps, dev tools, cloud services, SaaS APIs, creative suites, and beyond. If it has a GUI or an API, it deserves an agent-native CLI. Open a PR and bring it to the hub!</p>
|
|
<div class="stats">
|
|
<div class="stat">
|
|
<div class="stat-num" id="cli-count">-</div>
|
|
<div class="stat-label">CLIs Available</div>
|
|
</div>
|
|
<div class="stat">
|
|
<div class="stat-num" id="category-count">-</div>
|
|
<div class="stat-label">Categories</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="controls">
|
|
<input class="search" type="text" placeholder="Search CLIs..." id="search">
|
|
<div id="filters"></div>
|
|
</div>
|
|
|
|
<div class="grid" id="grid"></div>
|
|
|
|
<div class="footer">
|
|
<p>Want to add or update a CLI? Read the <a href="https://github.com/HKUDS/CLI-Anything/blob/main/CONTRIBUTING.md" target="_blank">Contributing Guide</a> and use the <a href="https://github.com/HKUDS/CLI-Anything/blob/main/.github/PULL_REQUEST_TEMPLATE.md" target="_blank">PR Template</a> — Powered by <a href="https://github.com/HKUDS/CLI-Anything" target="_blank">CLI-Anything</a></p>
|
|
</div>
|
|
|
|
<script>
|
|
const REPO = 'https://github.com/HKUDS/CLI-Anything';
|
|
const REGISTRY_URLS = [
|
|
'../../registry.json',
|
|
'https://raw.githubusercontent.com/HKUDS/CLI-Anything/main/registry.json'
|
|
];
|
|
const EXTERNAL_LINK_SVG = '<svg viewBox="0 0 16 16" fill="currentColor"><path d="M3.75 2h3.5a.75.75 0 0 1 0 1.5H4.56l6.72 6.72a.75.75 0 1 1-1.06 1.06L3.5 4.56v2.69a.75.75 0 0 1-1.5 0v-3.5A1.75 1.75 0 0 1 3.75 2Zm7.5 0h1A1.75 1.75 0 0 1 14 3.75v8.5A1.75 1.75 0 0 1 12.25 14h-8.5A1.75 1.75 0 0 1 2 12.25v-1a.75.75 0 0 1 1.5 0v1c0 .138.112.25.25.25h8.5a.25.25 0 0 0 .25-.25v-8.5a.25.25 0 0 0-.25-.25h-1a.75.75 0 0 1 0-1.5Z"/></svg>';
|
|
|
|
let clis = [];
|
|
let activeFilter = 'all';
|
|
|
|
async function loadRegistry() {
|
|
try {
|
|
let resp;
|
|
for (const url of REGISTRY_URLS) {
|
|
try {
|
|
resp = await fetch(url);
|
|
if (resp.ok) break;
|
|
} catch (_) { continue; }
|
|
}
|
|
if (!resp || !resp.ok) throw new Error('All sources failed');
|
|
const data = await resp.json();
|
|
clis = data.clis;
|
|
document.getElementById('cli-count').textContent = clis.length;
|
|
const categories = [...new Set(clis.map(c => c.category))];
|
|
document.getElementById('category-count').textContent = categories.length;
|
|
buildFilters(categories);
|
|
render();
|
|
} catch (e) {
|
|
document.getElementById('grid').innerHTML =
|
|
'<div class="empty">Failed to load registry. <a href="' + REPO + '/blob/main/registry.json" style="color:var(--accent)">Check source</a>.</div>';
|
|
}
|
|
}
|
|
|
|
const CATEGORY_LABELS = { 'ai': 'AI', '3d': '3D' };
|
|
function catLabel(cat) {
|
|
return CATEGORY_LABELS[cat] || cat.charAt(0).toUpperCase() + cat.slice(1);
|
|
}
|
|
|
|
function buildFilters(categories) {
|
|
const container = document.getElementById('filters');
|
|
const allBtn = document.createElement('button');
|
|
allBtn.className = 'filter-btn active';
|
|
allBtn.textContent = 'All';
|
|
allBtn.dataset.cat = 'all';
|
|
allBtn.onclick = () => setFilter('all');
|
|
container.appendChild(allBtn);
|
|
|
|
categories.sort().forEach(cat => {
|
|
const btn = document.createElement('button');
|
|
btn.className = 'filter-btn';
|
|
btn.textContent = catLabel(cat);
|
|
btn.dataset.cat = cat;
|
|
btn.onclick = () => setFilter(cat);
|
|
container.appendChild(btn);
|
|
});
|
|
}
|
|
|
|
function setFilter(cat) {
|
|
activeFilter = cat;
|
|
document.querySelectorAll('.filter-btn').forEach(b => {
|
|
b.classList.toggle('active', b.dataset.cat === cat);
|
|
});
|
|
render();
|
|
}
|
|
|
|
function render() {
|
|
const query = document.getElementById('search').value.toLowerCase();
|
|
const filtered = clis.filter(c => {
|
|
const matchFilter = activeFilter === 'all' || c.category === activeFilter;
|
|
const matchSearch = !query ||
|
|
c.display_name.toLowerCase().includes(query) ||
|
|
c.description.toLowerCase().includes(query) ||
|
|
c.name.toLowerCase().includes(query);
|
|
return matchFilter && matchSearch;
|
|
});
|
|
|
|
const grid = document.getElementById('grid');
|
|
if (filtered.length === 0) {
|
|
grid.innerHTML = '<div class="empty">No CLIs match your search.</div>';
|
|
return;
|
|
}
|
|
|
|
grid.innerHTML = filtered.map(c => {
|
|
const requiresHtml = c.requires
|
|
? `<div class="card-requires"><strong>Requires:</strong> ${esc(c.requires)}</div>`
|
|
: '';
|
|
const skillLink = c.skill_md
|
|
? `<a href="${REPO}/blob/main/${c.skill_md}" target="_blank">SKILL.md</a>`
|
|
: '';
|
|
const sourceLink = `<a href="${REPO}/tree/main/${c.name}/agent-harness" target="_blank">Source</a>`;
|
|
const titleHtml = c.homepage
|
|
? `<a href="${esc(c.homepage)}" target="_blank">${esc(c.display_name)}${EXTERNAL_LINK_SVG}</a>`
|
|
: esc(c.display_name);
|
|
const isTeam = c.contributor === 'CLI-Anything-Team';
|
|
const contributorHtml = c.contributor
|
|
? `<div class="card-contributor">
|
|
<svg viewBox="0 0 16 16" width="14" height="14" fill="currentColor" style="opacity:0.5"><path d="M10.561 8.073a6.005 6.005 0 0 1 3.432 5.142.75.75 0 1 1-1.498.07 4.5 4.5 0 0 0-8.99 0 .75.75 0 0 1-1.498-.07 6.004 6.004 0 0 1 3.431-5.142 3.999 3.999 0 1 1 5.123 0ZM10.5 5a2.5 2.5 0 1 0-5 0 2.5 2.5 0 0 0 5 0Z"/></svg>
|
|
<a href="${esc(c.contributor_url)}" target="_blank">${esc(c.contributor)}</a>${isTeam ? '' : '<span class="community-badge">Community</span>'}
|
|
</div>`
|
|
: '';
|
|
|
|
return `
|
|
<div class="card">
|
|
<div class="card-head">
|
|
<span>
|
|
<span class="card-title">${titleHtml}</span>
|
|
<span class="category-tag">${catLabel(c.category)}</span>
|
|
</span>
|
|
<span class="card-version">v${esc(c.version)}</span>
|
|
</div>
|
|
<div class="card-desc">${esc(c.description)}</div>
|
|
${contributorHtml}
|
|
${requiresHtml}
|
|
<div class="card-install">
|
|
<code>${esc(c.install_cmd)}</code>
|
|
<button class="copy-btn" onclick="copyCmd(this, '${esc(c.install_cmd)}')">Copy</button>
|
|
</div>
|
|
<div class="card-links">${sourceLink}${skillLink ? ' · ' + skillLink : ''}</div>
|
|
</div>`;
|
|
}).join('');
|
|
}
|
|
|
|
function copyCmd(btn, cmd) {
|
|
navigator.clipboard.writeText(cmd).then(() => {
|
|
btn.textContent = 'Copied!';
|
|
setTimeout(() => btn.textContent = 'Copy', 1500);
|
|
});
|
|
}
|
|
|
|
function esc(s) {
|
|
if (!s) return '';
|
|
const d = document.createElement('div');
|
|
d.textContent = s;
|
|
return d.innerHTML;
|
|
}
|
|
|
|
document.getElementById('search').addEventListener('input', render);
|
|
loadRegistry();
|
|
</script>
|
|
</body>
|
|
</html>
|