Blog Composer
用于管理Jekyll静态博客的撰写、编辑与发布流程。
通过GitHub Pages将博客文章导入Medium,无需API密钥。
openclaw skills install @ylnhari/medium-blog-post-creator命令、参数、文件名以原文为准
无需 API 密钥即可将格式完整的博客文章发布到 Medium。
自 2025 年 1 月 1 日起,Medium 停止发放新的集成令牌。本技能通过唯一仍稳定的导入路径绕过此限制:编写一个受限的 HTML 文章,将其作为公开 URL 部署至 GitHub Pages,再由 Medium 自身的 URL 导入工具抓取并转换为可编辑区块。
该流程为 端到端代理驱动 —— 无需记忆外部仓库或本地路径。它会向用户询问所需信息,在用户的 GitHub 账户中搭建静态博客仓库,将每篇博文部署至 GitHub Pages,并自动操控浏览器完成 Medium 的导入流程,直至草稿准备就绪以供审阅。始终在 草稿状态 停止 —— 永不自动发布。
当工作流指向这些文件时,请加载它们:
references/html-standards.md:Medium 导入器接受的 HTML 子集。撰写文章前请务必阅读。references/configuration.md:持久化配置的模式、解析规则和重置方式(用于第 0 步)。assets/post-template.html:实现所有 HTML 规则的可填写文章模板。assets/meta.template.json:每篇文章的元数据侧边文件结构。当用户说出以下任意语句时触发:
或提供内容(Markdown、大纲、笔记),并提及 Medium。
| 必需项 | 原因 |
|---|---|
| GitHub 账号 | 用于托管静态博客仓库并通过 GitHub Pages 提供服务 |
| Medium 账号 | 文章导入的目标位置 |
**gh CLI**(已认证) | 代理在 shell 中创建/克隆/推送仓库 |
**git** | 标准仓库操作 |
**OpenClaw browser 工具**(已登录 Medium) | 控制 Medium 的 URL 导入界面 |
gh 和 git 在 frontmatter 的 requires.bins 中声明,以便 OpenClaw 检测。但仍需在运行时验证:gh auth status 必须显示已认证用户,且浏览器工具必须已登录 Medium。若任一先决条件缺失,立即停止并告知用户需安装或登录的内容。不得使用占位符继续。
请一次性向用户询问以下内容;若部分信息缺失,使用默认值,并明确说明你的假设后再继续:
technical / general / beginner。默认:technical。casual / formal / tutorial。默认:casual。short(约 500 字) / medium(约 1000 字) / long(约 2000 字以上)。默认:medium。octocat。blog — 一个普通项目仓库,通过 https://<username>.github.io/blog/ 访问。 (进阶用法:将仓库命名为 <username>.github.io 可使博客位于账户根目录 https://<username>.github.io/,但会占用唯一的用户站点,因此大多数人仍保留 blog 名称。)
共 11 步(第 0 步至第 10 步)。即使首次运行也不可跳过第 0 步。
加载每安装实例的配置($MEDIUM_BLOG_CONFIG,否则为每用户配置目录),并检查是否已克隆博客仓库,若有,则读取其本地标记文件(<repo-local-path>/.medium-skill-config.json)。完整模式与规则详见 references/configuration.md。随后:
last_repo(即命名了仓库)**:复用该仓库。跳过所有设置问题,直接进入第 2 步,确保本地克隆存在(新克隆会携带标记)。<repo-local-path> = <default_working_dir>/<github_repo>。last_repo 未设置**(例如代理已在博客仓库内):采用标记中的仓库,并保存 last_repo —— 不重新提问。last_repo,无有效标记)**:询问上述全部输入;第 2 步将创建仓库,写入标记,并将 last_repo 保存至全局配置,以便后续运行跳过设置。last_repo。在代理的 shell 中验证:gh --version、gh auth status(需已认证)、git --version,以及 OpenClaw browser 工具是否可达。任一检查失败,立即停止并报告具体缺失项。
构建一个 最小化的静态网站:仅包含一个列出文章的页面,每篇文章独立存放于文件夹中。不使用框架,无构建步骤 —— 仅使用原生 HTML、CSS 和 JSON 索引文件。<repo-local-path> 是克隆仓库的本地路径(通常为 <default_working_dir>/<github_repo>)。
**确定 <pages-base-url>**(用于每篇文章的 URL 及标记文件中的 pages_url)。GitHub 为用户/组织站点和项目站点提供不同路径,因此需一次性计算并重复使用:
<github_repo> 等于 <github_owner>.github.io(即 用户/组织站点),则基础地址为 https://<github_owner>.github.io/。https://<github_owner>.github.io/<github_repo>/。Keep结尾斜杠。文章将位于
<pages-base-url>posts/<YYYY-MM-DD>-<slug>/。
切勿无条件手动构建 https://<owner>.github.io/<repo>/... —— 对于 owner.github.io 类型的仓库,这会导致错误的双重路径。
**如果仓库已知(last_repo 已设置,或存在标记文件):**
该仓库已存在,且在初始设置时已启用 Pages。跳过创建和启用 Pages 的步骤;只需确保本地克隆存在且为最新状态:
mkdir -p "<default_working_dir>"
if [ ! -d "<repo-local-path>" ]; then
gh repo clone <github_owner>/<github_repo> "<repo-local-path>"
else
cd "<repo-local-path>" && git pull --rebase origin <branch>
fi如果尚未知道任何仓库(全新设置):
<default_working_dir> 运行,使克隆结果落在 <repo-local-path>): mkdir -p "<default_working_dir>"
cd "<default_working_dir>"
gh repo create <github_owner>/<github_repo> \
--public \
--description "Static blog for Medium cross-posting" \
--clone \
--add-readme
# 现在克隆到 <default_working_dir>/<github_repo> = <repo-local-path>gh repo clone <github_owner>/<github_repo> "<repo-local-path>"。references/configuration.md): # 在 Unix 系统(bash)
printf '%s\n' '{' \
' "schema_version": 1,' \
" \"github_owner\": \"<github_owner>\"," \
" \"github_repo\": \"<github_repo>\"," \
" \"pages_url\": \"<pages-base-url>\"," \
" \"default_author\": \"<github_owner>\"," \
" \"branch\": \"<branch>\"" \
'}' > "<repo-local-path>/.medium-skill-config.json" # 在 Windows 系统(PowerShell)
@{
schema_version = 1
github_owner = "<github_owner>"
github_repo = "<github_repo>"
pages_url = "<pages-base-url>"
default_author = "<github_owner>"
branch = "<branch>"
} | ConvertTo-Json | Set-Content "<repo-local-path>\.medium-skill-config.json".nojekyll 文件,以确保 GitHub Pages 直接服务 HTML(不进行 Jekyll 处理),然后将其与标记文件一起提交: cd "<repo-local-path>"
touch .nojekyll # Windows PowerShell: New-Item -ItemType File .nojekyll
git add .nojekyll .medium-skill-config.json
git commit -m "chore: mark repo as managed by medium-blog-post-creator + add .nojekyll"
git push origin <branch><github_repo> == <github_owner>.github.io):Pages 会自动在域名根路径启用。无需额外操作。409 表示已启用,属于正常情况: gh api -X POST "repos/<github_owner>/<github_repo>/pages" \
--input - <<'JSON' || true
{"source":{"branch":"<branch>","path":"/"}}
JSON如果调用因任何原因失败(非“已启用”错误),请引导用户手动在以下地址启用 Pages:
https://github.com/<github_owner>/<github_repo>/settings/pages
(来源:部署源 → <branch> / /(根目录)),并在确认后继续。
last_repo(包含 github_owner、github_repo、pages_url = <pages-base-url>、branch)写入安装级配置文件。格式见 references/configuration.md。**请先阅读 references/html-standards.md**,然后基于 assets/post-template.html 编写文章,输出至:
<repo-local-path>/posts/<YYYY-MM-DD>-<slug>/index.html<slug> 由标题生成:全小写,仅允许连字符,最多 60 个字符(例如:running-llms-locally)。正文应包含:
<h2> 小节;<blockquote>;禁止虚构事实、填充内容或 LLM 虚假陈述。
根据 assets/meta.template.json 写入 <repo-local-path>/posts/<YYYY-MM-DD>-<slug>/meta.json:
{
"title": "完整文章标题",
"date": "YYYY-MM-DD",
"author": "<github-用户名或组织>",
"description": "1–2 句摘要,最多 200 字符",
"tags": ["标签1", "标签2", "标签3"],
"status": "draft",
"medium_url": null,
"cover_image": "https://images.unsplash.com/photo-...?w=1200&q=80"
}medium_url 在第 8 步填写,以实现端到端追踪。
仓库维护一个顶层的 posts/index.json(按时间倒序排列):
posts/index.json;若不存在,创建为空数组 []。条目结构如下:
{
"slug": "YYYY-MM-DD-slug",
"title": "完整文章标题",
"date": "YYYY-MM-DD",
"description": "1–2 句摘要,最多 200 字符",
"tags": ["标签1", "标签2"],
"cover_image": "https://images.unsplash.com/photo-...?w=1200&q=80",
"status": "draft",
"medium_url": null
}cd "<repo-local-path>"
git add posts/<YYYY-MM-DD>-<slug>/ posts/index.json
git commit -m "feat(posts): add \"<POST TITLE>\" [<YYYY-MM-DD>]"
git push origin <branch>如果推送因认证错误失败,请提示用户运行 gh auth login 并重试(最多尝试 2 次),随后报告错误并停止。
Pages 已在第二步中启用,因此本步骤仅需等待新提交上线。
根据第二步中解析的 <pages-base-url> 构建文章 URL —— 切勿手动拼接路径。
本技能用于将博客文章从 GitHub Pages 部署到 Medium 平台。整个流程包括:生成文章、等待页面可访问、通过浏览器导入到 Medium、更新元数据并报告结果。
gh 命令行工具请确认已安装 [GitHub CLI](https://cli.github.com)(gh),并完成身份验证:
gh auth status如果未认证,请运行:
gh auth login若仓库不存在,尝试创建它;否则直接克隆现有仓库。
注意:若创建失败(如仓库已存在),请自动切换为克隆模式。
将文章文件(如 posts/YYYY-MM-DD-slug/ 目录)添加到 Git,并提交:
git add posts/<YYYY-MM-DD>-<slug>/
git commit -m "feat(posts): add new blog post <POST TITLE>"
git push origin <branch>等待页面部署生效。使用以下脚本轮询检查是否返回 HTTP 200。
$url = "<pages-base-url>posts/<YYYY-MM-DD>-<slug>/"
$deadline = (Get-Date).AddMinutes(3)
while ((Get-Date) -lt $deadline) {
try {
$code = (Invoke-WebRequest -Uri $url -UseBasicParsing -MaximumRedirection 5 -ErrorAction Stop).StatusCode
if ($code -eq 200) { exit 0 }
} catch {
$code = 404
}
if ($code -eq 200) { exit 0 }
Start-Sleep -Seconds 15
}
exit 1deadline=$(( $(date +%s) + 180 ))
url="<pages-base-url>posts/<YYYY-MM-DD>-<slug>/"
while [ "$(date +%s)" -lt "$deadline" ]; do
code=$(curl -fsS -o /dev/null -w "%{http_code}" "$url" 2>/dev/null || echo 000)
if [ "$code" = "200" ]; then exit 0; fi
sleep 15
done
exit 1⚠️ 若超时(仍返回
404),请引导用户前往仓库的 Actions 页面(https://github.com/<github_owner>/<github_repo>/actions)查看构建状态,并重新尝试。
**在 URL 返回 200 之前,不得进入下一步。**
使用 OpenClaw 的 browser 工具操作 Medium 的导入功能。
https://medium.com/p/import,等待页面完全加载。<pages-base-url>posts/<YYYY-MM-DD>-<slug>/(注意:必须包含末尾斜杠 —— Medium 导入器对路径格式敏感)。
https://medium.com/p/<draft-id>/edit。
posts/<YYYY-MM-DD>-<slug>/meta.json 中的 medium_url 字段设置为草稿编辑器 URL。posts/index.json 中对应文章的 medium_url。git add posts/<YYYY-MM-DD>-<slug>/meta.json posts/index.json
git commit -m "chore(posts): link medium draft for \"<POST TITLE>\""
git push origin <branch>✅ Post created: "<TITLE>"
📄 GitHub Pages: <pages-base-url>posts/<YYYY-MM-DD>-<slug>/
📝 Medium draft: <draft-editor-url>
The draft is ready for your review. Open the Medium link, make any edits, and
click Publish when you're happy with it.✅ 成功消息仅在所有步骤正常完成后输出。
❌ 若任一步骤失败,请明确指出具体步骤、错误详情及下一步操作。禁止虚构成功提示。
| 问题 | 解决方法 |
|---|---|
gh 未安装 | 请用户安装:https://cli.github.com |
gh auth status 显示未认证 | 运行 gh auth login |
| 仓库创建失败(已存在) | 改为克隆已有仓库 |
| Git 推送因权限失败 | 运行 gh auth login 后重试(最多 2 次) |
| GitHub Pages 返回 404 | 等待 60 秒后重试。若 3 分钟内仍失败,请检查 Actions 页 |
| Medium 导入内容为空 | 确保页面是静态 HTML,先在浏览器中打开 Pages 地址验证 |
| Medium 导入静默失败 | 确认 Pages 地址返回 HTTP 200 且包含末尾斜杠 |
posts/index.json 出现合并冲突 | 执行 git pull --rebase origin <branch>,然后重新添加变更 |
| 浏览器在 Medium 页面超时 | 关闭标签页,新开一个,重新执行导航步骤 |
| Medium UI 无法添加某标签 | 跳过该标签,继续处理其他标签。不进行循环重试 |
gh auth login 和用户当前浏览器会话负责。Medium 对每日发布有配额限制(截至 2026 年初:每天最多 2 篇文章 + 100 条回复)。
本技能仅生成草稿,因此不会触发发布限制。但用户应避免每天通过本技能自动发布超过 2 篇文章。
已收录 1 个 Skill