mirror of
https://fastgit.cc/github.com/HKUDS/CLI-Anything
synced 2026-04-30 22:02:01 +08:00
Merge origin/main into notebooklm-agent-harness-scaffold
This commit is contained in:
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -25,6 +25,7 @@ Fixes #<!-- issue number -->
|
||||
- [ ] Unit tests at `cli_anything/<software>/tests/test_core.py` are present and pass without backend
|
||||
- [ ] E2E tests at `cli_anything/<software>/tests/test_full_e2e.py` are present
|
||||
- [ ] `README.md` includes the new software (with link to harness directory)
|
||||
- [ ] `registry.json` includes an entry for the new software (for the [CLI-Hub](https://hkuds.github.io/CLI-Anything/hub/))
|
||||
- [ ] `repl_skin.py` in `utils/` is an unmodified copy from the plugin
|
||||
|
||||
### For Existing CLI Modifications
|
||||
@@ -34,6 +35,7 @@ Fixes #<!-- issue number -->
|
||||
- [ ] All unit tests pass: `python3 -m pytest cli_anything/<software>/tests/test_core.py -v`
|
||||
- [ ] All E2E tests pass: `python3 -m pytest cli_anything/<software>/tests/test_full_e2e.py -v`
|
||||
- [ ] No test regressions — no previously passing tests were removed or weakened
|
||||
- [ ] `registry.json` entry is updated if version, description, or requirements changed
|
||||
|
||||
### General Checklist
|
||||
|
||||
|
||||
@@ -14,7 +14,8 @@ Adding a new CLI harness is the most impactful contribution. Before submitting a
|
||||
2. **`SKILL.md`** — the AI-discoverable skill definition exists inside the Python package at `cli_anything/<software>/SKILL.md`.
|
||||
3. **Tests** — unit tests (`test_core.py`, passable without backend) and E2E tests (`test_full_e2e.py`) are present and passing.
|
||||
4. **`README.md`** — the project README includes the new software with a link to its harness directory.
|
||||
5. **`repl_skin.py`** — an unmodified copy from the plugin exists in `utils/`.
|
||||
5. **`registry.json`** — add an entry for the new software so it appears on the [CLI-Hub](https://hkuds.github.io/CLI-Anything/hub/).
|
||||
6. **`repl_skin.py`** — an unmodified copy from the plugin exists in `utils/`.
|
||||
|
||||
### B) New Features
|
||||
|
||||
@@ -32,6 +33,36 @@ Bug fixes resolve incorrect behavior in existing harnesses or the plugin.
|
||||
- Include a test that reproduces the bug and verifies the fix.
|
||||
- Ensure all existing tests for the affected harness still pass.
|
||||
|
||||
## CLI-Hub & Registry
|
||||
|
||||
All available CLIs are listed in `registry.json` at the repo root and displayed on the [CLI-Hub](https://hkuds.github.io/CLI-Anything/hub/). The hub reads `registry.json` directly from `main`, so it updates immediately when a PR is merged.
|
||||
|
||||
### Adding a new CLI to the Hub
|
||||
|
||||
Include an entry in `registry.json` as part of your PR. Each entry has this shape:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "my-software",
|
||||
"display_name": "My Software",
|
||||
"version": "1.0.0",
|
||||
"description": "Short description of what the CLI does",
|
||||
"requires": "backend software or null",
|
||||
"install_cmd": "pip install git+https://github.com/HKUDS/CLI-Anything.git#subdirectory=my-software/agent-harness",
|
||||
"entry_point": "cli-anything-my-software",
|
||||
"skill_md": "my-software/agent-harness/cli_anything/my_software/skills/SKILL.md",
|
||||
"category": "category-name"
|
||||
}
|
||||
```
|
||||
|
||||
### Updating an existing CLI on the Hub
|
||||
|
||||
When you modify an existing harness, update its `registry.json` entry in the same PR:
|
||||
|
||||
- Bump the `version` field to reflect the change.
|
||||
- Update `description`, `requires`, or `category` if they changed.
|
||||
- The hub will reflect the update as soon as the PR is merged to `main`.
|
||||
|
||||
## Development Setup
|
||||
|
||||
Each generated CLI lives in `<software>/agent-harness/` and is an independent Python package:
|
||||
|
||||
@@ -42,7 +42,6 @@ CLI-Anything: Bridging the Gap Between AI Agents and the World's Software</stron
|
||||
> Thanks to all invaluable efforts from the community! More updates continuously on the way everyday..
|
||||
|
||||
- **2026-03-18** 🧠 Added an **experimental NotebookLM harness scaffold** from the community. It wraps the installed `notebooklm` CLI for notebook discovery, source management, chat, artifact workflows, downloads, and sharing while preserving a clear experimental/community-maintained boundary.
|
||||
|
||||
- **2026-03-17** 🌐 Launched the **[CLI-Hub](https://hkuds.github.io/CLI-Anything/hub/)** — a central registry where you can browse, search, and install any CLI with a single `pip` command. Contributors can add new CLIs or update existing ones by simply opening a PR with a `registry.json` entry. The hub updates automatically on merge.
|
||||
|
||||
- **2026-03-16** 🤖 Added **SKILL.md generation** (Phase 6.5) — every generated CLI now ships with an AI-discoverable skill definition inside the Python package. ReplSkin auto-detects the skill file after `pip install`, and the REPL banner displays the absolute path for agents. Includes `skill_generator.py`, Jinja2 template, `package_data` in all setup.py files, and 51 new tests.
|
||||
|
||||
482
docs/hub/index.html
Normal file
482
docs/hub/index.html
Normal file
@@ -0,0 +1,482 @@
|
||||
<!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); }
|
||||
|
||||
.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>CLI-<span>Anything</span> Hub</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);
|
||||
|
||||
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>
|
||||
${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>
|
||||
Reference in New Issue
Block a user