重开仓库内容:改推五个技能(browser-harness / humanizer / humanizer-zh / product-planning / session-mechanism)
按授权清空原有内容后重新提交(原 oil-ui-pro 一并移出,可从历史恢复)。 browser-harness 剔除 .venv 等运行环境;根 .gitignore 补记 .venv/ 与 node_modules/。
This commit is contained in:
1 parent
9a152a1952
commit
777f7fe5d0
240 files changed
+52518
-4409
No files matched your search
@@ -0,0 +1,185 @@
|
||||
# AgentList Discovery
|
||||
|
||||
`https://agentlist.com` — public directory of skills, agents, MCPs, configs, and paid services. Field-tested with browser-harness on 2026-05-03.
|
||||
|
||||
## Do This First
|
||||
|
||||
Use the public API for read-only discovery. It is faster and returns full listing content without browser UI parsing.
|
||||
|
||||
```python
|
||||
import json
|
||||
|
||||
listings = json.loads(http_get("https://agentlist.com/api/listings?category=skill&q=github&limit=10"))
|
||||
for item in listings:
|
||||
print(item["title"], item["id"], item["vote_count"])
|
||||
```
|
||||
|
||||
The web UI is useful for visual verification, voting, signing in with passkeys, and submitting listings.
|
||||
|
||||
## Public API
|
||||
|
||||
All read endpoints below worked without authentication.
|
||||
|
||||
```python
|
||||
import json
|
||||
|
||||
# Search everything
|
||||
items = json.loads(http_get("https://agentlist.com/api/listings?q=github&limit=10"))
|
||||
|
||||
# Filter by category
|
||||
skills = json.loads(http_get("https://agentlist.com/api/listings?category=skill&q=browser&limit=10"))
|
||||
agents = json.loads(http_get("https://agentlist.com/api/listings?category=agent&limit=10"))
|
||||
mcps = json.loads(http_get("https://agentlist.com/api/listings?category=mcp&limit=10"))
|
||||
configs = json.loads(http_get("https://agentlist.com/api/listings?category=config&limit=10"))
|
||||
paid = json.loads(http_get("https://agentlist.com/api/listings?category=paid&limit=10"))
|
||||
|
||||
# Sort and paginate
|
||||
new_items = json.loads(http_get("https://agentlist.com/api/listings?sort=new&skip=0&limit=20"))
|
||||
top_items = json.loads(http_get("https://agentlist.com/api/listings?sort=top&limit=20"))
|
||||
trending_items = json.loads(http_get("https://agentlist.com/api/listings?sort=trending&limit=20"))
|
||||
|
||||
# Fetch one listing
|
||||
listing = json.loads(http_get("https://agentlist.com/api/listings/6ea8f4f2-83cf-4625-a3cd-98b49d49a7b2"))
|
||||
```
|
||||
|
||||
Useful fields seen on listing objects:
|
||||
|
||||
- `id`, `category`, `title`, `description`, `content`
|
||||
- `author_pubkey`, `vote_count`, `fetch_count`, `viewer_has_voted`
|
||||
- `created_at`, `updated_at`, `reviewed_at`, `reviewed_by`
|
||||
- MCP/config/paid-specific fields may be present: `repo_url`, `api_spec_url`, `transport`, `package`, `tools`, `target_tool`, `config_format`, `filename_hint`, `api_base_url`, `pricing_info`, `payment_method`
|
||||
|
||||
## Raw Skill Content
|
||||
|
||||
Use `/raw/{id}` when you only need the agent-readable content, not metadata.
|
||||
|
||||
```python
|
||||
skill_md = http_get("https://agentlist.com/raw/6ea8f4f2-83cf-4625-a3cd-98b49d49a7b2")
|
||||
```
|
||||
|
||||
For skill-folder loaders, the hosted `skills.agentlist.com` path also works:
|
||||
|
||||
```python
|
||||
skill_md = http_get("https://skills.agentlist.com/skill/6ea8f4f2-83cf-4625-a3cd-98b49d49a7b2/SKILL.md")
|
||||
```
|
||||
|
||||
Detail pages display a "Load in your local or cloud based agent" box with the folder URL:
|
||||
|
||||
```text
|
||||
https://skills.agentlist.com/skill/{id}/
|
||||
```
|
||||
|
||||
## Browser Navigation
|
||||
|
||||
```python
|
||||
new_tab("https://agentlist.com")
|
||||
wait_for_load()
|
||||
wait(1)
|
||||
print(page_info())
|
||||
```
|
||||
|
||||
The homepage is server-rendered enough to read immediately after load. It contains:
|
||||
|
||||
- Category buttons: `Skills`, `Agents`, `MCPs`, `Configs`, `Paid For`
|
||||
- Sort controls: `Top`, `New`, `Trending`
|
||||
- Search input: `input[type=search]`
|
||||
- Main listing table with headers `#`, `Title`, `Author`, `Date`, `Votes`
|
||||
|
||||
Extract visible rows:
|
||||
|
||||
```python
|
||||
import json
|
||||
|
||||
rows = json.loads(js(r"""
|
||||
JSON.stringify(Array.from(document.querySelectorAll("table:first-of-type tbody tr")).map(tr => {
|
||||
const cells = Array.from(tr.children).map(td => td.innerText.trim());
|
||||
return {
|
||||
rank: cells[0],
|
||||
title_description: cells[1],
|
||||
author: cells[2],
|
||||
date: cells[3],
|
||||
votes: cells[4]
|
||||
};
|
||||
}))
|
||||
"""))
|
||||
```
|
||||
|
||||
Listing title links use `/listing/{uuid}`:
|
||||
|
||||
```python
|
||||
links = json.loads(js(r"""
|
||||
JSON.stringify(Array.from(document.querySelectorAll('a[href^="/listing/"], a[href*="/listing/"]')).map(a => ({
|
||||
text: a.innerText.trim(),
|
||||
href: a.href
|
||||
})))
|
||||
"""))
|
||||
```
|
||||
|
||||
## UI Search Gotcha
|
||||
|
||||
`fill_input("input[type=search]", "github")` double-entered characters during testing (`github` became `ggiitthhuubb`). Prefer direct JS value assignment plus an `input` event, or use the API.
|
||||
|
||||
```python
|
||||
js("""
|
||||
const input = document.querySelector('input[type=search]');
|
||||
input.value = 'github';
|
||||
input.dispatchEvent(new Event('input', {bubbles: true}));
|
||||
""")
|
||||
wait(1)
|
||||
print(page_info()) # URL becomes https://agentlist.com/?q=github
|
||||
```
|
||||
|
||||
## Listing Detail Pages
|
||||
|
||||
Detail URL pattern:
|
||||
|
||||
```text
|
||||
https://agentlist.com/listing/{id}
|
||||
```
|
||||
|
||||
On skill detail pages, the rendered content includes raw code blocks, API notes, discussion, and a load URL. Detail pages also keep the homepage table lower on the page, so use scoped selectors when extracting the detail body.
|
||||
|
||||
```python
|
||||
new_tab("https://agentlist.com/listing/6ea8f4f2-83cf-4625-a3cd-98b49d49a7b2")
|
||||
wait_for_load()
|
||||
wait(1)
|
||||
|
||||
data = json.loads(js(r"""
|
||||
JSON.stringify({
|
||||
title: document.querySelector(".listing-title, h2")?.innerText || null,
|
||||
load_urls: Array.from(document.querySelectorAll("code")).map(e => e.innerText.trim()).filter(t => t.includes("skills.agentlist.com/skill/")),
|
||||
code_blocks: Array.from(document.querySelectorAll("pre, code")).map(e => e.innerText.slice(0, 300)).slice(0, 20)
|
||||
})
|
||||
"""))
|
||||
```
|
||||
|
||||
The top-right user pill and `Sign out` button can appear when already authenticated. Do not assume a logged-out session.
|
||||
|
||||
## Submit Page
|
||||
|
||||
Submit URL:
|
||||
|
||||
```text
|
||||
https://agentlist.com/submit
|
||||
```
|
||||
|
||||
Observed fields:
|
||||
|
||||
- `#title` — title input
|
||||
- `#description` — short description input
|
||||
- `input[type=url]` — GitHub import URL
|
||||
- first `textarea` — Markdown content
|
||||
- `#scripts_index_html` — optional JavaScript skill payload
|
||||
|
||||
Observed category buttons:
|
||||
|
||||
```python
|
||||
buttons = json.loads(js(r"""
|
||||
JSON.stringify(Array.from(document.querySelectorAll("button.category-btn")).map(b => ({
|
||||
text: b.innerText.trim(),
|
||||
selected: b.classList.contains("selected")
|
||||
})))
|
||||
"""))
|
||||
```
|
||||
|
||||
The GitHub import button is `button.btn.btn-secondary` with text `Import`. The final submit button has text `Submit listing`. Submissions are signed with the user's Nostr identity; do not submit or vote unless the user explicitly asked for that exact action.
|
||||
Reference in new issue
Block a user