用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/simple10/llm-wiki-skills --skill channel-circle命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
Frame.io guest-share capture for this wiki — tree enumeration, per-job asset capture, document notes.
Substack capture for this wiki — archive-API enumeration, paywall split, per-post extraction.
Gmail mailboxes for this wiki — cursor pull and daily ledger, one watch per mailbox.
正在显示 SKILL.md
| name | channel-circle |
| description | Circle.so capture for this wiki — persistent-profile Chrome, HLS/Wistia media, captions, outage probe. |
| argument-hint | --stage harvest|process --capture-dir <dir> [--url <url>] |
| user-invocable | false |
You harvest Circle.so-hosted communities for this wiki. You are normally
dispatched by the harvest worker loop: a claimed job carrying skill: channel-circle means its watch named this skill, and this file is
authoritative for how the venue is captured. The job already carries the
resolved config — min_date, access, assets, tags, areas — honor it;
never re-ask.
This copy is wiki-owned. Improve it as you learn the venue (new
fingerprints, changed selectors, corrected routes) — that is the intended
lifecycle, and skills list reporting it as diverged is provenance, not a
problem. Keep claims about the pipeline's own scripts out of here; those go
to the human via the run report.
Circle.so is a hosted community/course platform (React SPA, Rails backend).
Communities run on *.circle.so or a custom domain (e.g.
community.example.com), so fingerprint on assets/cookies, not the
hostname.
You are invoked by name through the Skill tool, not read as a document, and
--stage says which half of the pipeline is calling:
--stage harvest — a claimed job carries skill: channel-circle. Capture
the lesson into --capture-dir with the bundled script below. The job already
carries the resolved config — min_date, access, assets, tags, areas —
honor it; never re-ask.--stage process — the capture is on disk. This venue has no deterministic
builder: the generic scaffolder makes the note, and your job is the venue
knowledge below — which chrome to strip, how captions and HLS media are
referenced, what a paywalled or errored lesson looks like.Chaining to another unit? Invoke it by name through the Skill tool — never read a sibling's SKILL.md and improvise its behavior from what you read.
Run the bundled capture script instead of a generic Playwright fetch:
llm-wiki-ops run ops/skills/channel-circle/scripts/capture_lesson.py <root> <lesson-url> --out <capture-dir> \
[--headed] [--timeout-ms 45000]
<capture-dir> is the
job's own capture_dir — host-derived, already inside the watch's slice.page.html (rendered DOM — asset-discovery ground truth),
net.json (the stream-URL network log), meta.json (title, final URL,
discovered sidebar links), and resolves in-page <track> captions to
captions/<lang>.vtt while the page is live — blob: track srcs are
unreachable after the browser session ends.to_markdown.py and assets.py from
the plugin's scripts on its outputs as usual.llm-wiki-ops run scripts/login.py <domain> — saves the
<domain>.storage credential AND a persistent Chrome profile named
<domain>.<domain>.storage credential is deliberately unused here. A fresh
bundled-Chromium context gets a Turnstile challenge; real Chrome
(channel="chrome") with the persistent per-domain profile clears it,
because the profile carries the cf_clearance the human's login earned
and that clearance is fingerprint-bound. capture_lesson.py therefore
launches the persistent profile and never loads the bare
<domain>.storage credential into fresh Chromium — login still writes it
(other tools consume it), but on Circle it does not authorize anything by
itself..circle.so and the community's own domain.assets-v2.circle.so and
app.circle.so/rails/active_storage/... (Rails ActiveStorage image
proxy) referenced in page HTML./c/<space-slug>/sections/<section-id>/lessons/<lesson-id>;
login flow /users/sign_in → /sign_in#email, page title
"Sign in | ".cdn-media.circle.so..circle.so and the custom
domain — fingerprint on asset hosts, not hostname./c/<slug>/sections/<id>/lessons/<id> links; the body shows "Topic N of
M", giving an expected count to check completeness against. Extract the
tree at once for section-scoped runs rather than daisy-chaining "next"./c/<slug>) also renders all lesson links in
its DOM when the backend is healthy — enumeration can come from the
root, not only a lesson page. Each <a>'s text carries the lesson title
capture.json.min_date filter);
where publish/created dates live is still to be determined./users/sign_in.
With a valid session the lesson renders fully. No free/paid split
observed within a course — licensing is community-wide.page.goto(..., wait_until="commit") then wait on a content
selector (main / [class*="lesson"]) plus a short fixed pause.
Waiting on domcontentloaded/networkidle times out (~45s) — long-poll
capture_lesson.py implements this.to_markdown.py with no --selector extracts the lesson body
cleanly (title, "Topic N of M", headings, pull-quote, bullets); an
explicit content selector has not been needed.image candidates but never appear in the lesson markdown. For a
text+video lesson the only real asset is the HLS video — don't
bulk-download the chrome images.https://assets-v2.circle.so/<opaque-hash> with no file extension in
the URL. Extensionless URLs are easy to miss in extension-keyed asset
detection, so verify the manifest actually lists the Resources files —
grep page.md for assets-v2.circle.so links and add any missing ones
by hand as type: "file"; download resolves the real extension from the
content type.https://cdn-media.circle.so/bcdn_token=<tok>&token_path=%2F<uuid>%2F&expires=<ts>/<uuid>/hls/playlist.m3u8
(BunnyCDN signed-token style; playlist_N.m3u8 are renditions). The
.m3u8 URL appears only in the network log (XHR), never in the DOM —
capturing the network log is mandatory, which capture_lesson.py does.&) copy of the HLS URL —
keep the clean network-log URL, drop the entity-encoded duplicate; the
encoded form does not fetch.cdn-media.circle.so can be transiently slow: a first-attempt HLS
timeout that succeeds on immediate retry is normal, and under concurrent
load (2 workers sharing the domain) expect roughly one retry per lesson.
Always retry once before burning failure budget or diagnosing an outage.fast.wistia.net/embed/iframe/<media-id>). A single Wistia embed's
network log surfaces 4 stream-shaped URLs for the same video, not 4
videos: keep fast.wistia.com/embed/medias/<media-id>.m3u8 (the true
master, keyed to the iframe's media id); drop
embed-cloudfront.wistia.com/deliveries/<hash>.m3u8 entries (per-
rendition adaptive-bitrate manifests the player already selected from
the master) and anything ending in .ts (raw segment fragments). One
master per player/media id.<track> captions that capture_lesson.py
resolves to captions/en.vtt in-browser. Every lesson of one observed
section had an en track, so --transcript if-no-captions enqueued
zero generated-transcript jobs.channel="chrome" profile, no --headed)
renders the SPA and fires the HLS XHR fine when Circle's backend is
healthy — headed is not required for the render itself.Circle backend outages present as an authenticated app shell: no
/sign_in redirect, cf_clearance valid, but internal_api/spaces?include_sidebar=true
(and related internal_api/*) return HTTP 500, the
.standard-layout-v2__content-wrapper div renders empty (an
access/licensing block would instead show an upgrade CTA), page <title>
stays the generic community name, and no lesson links / HLS XHR appear.
The status page can be all green throughout. Fix signal: the wrapper gains
children AND the 5xx count hits 0. Probe with the bundled script
(diagnostic only — not part of the worker loop):
llm-wiki-ops run ops/skills/channel-circle/scripts/outage_probe.py <root> <course-url> [--settle-ms 8000]
Always exits 0 and prints a JSON verdict; fixed means auth is OK, no 5xx
was seen, and the content wrapper has real children. Use it to gate a
harvest behind an outage.