最近我寻找一个能从终端管理 Dev.to 文章的 CLI 工具。我每月撰写 4-5 篇文章,痴迷于追踪分析数据,并希望实现基于 git 的工作流。

我找到了 9 个现有工具,全部尝试后结果如下:

  • devto-cli (Node): 最后一次提交是 2 年前。安装即崩溃。
  • dev-to-git (Node): 仅支持同步到本地。无法推回。
  • slinkity: 已废弃。
  • forem-cli: 仅实现了 40+ 个端点中的 3 个。

每个工具都只做同一件事:发布文章。仅此而已。也许能拉取。也许能验证标签。

与此同时,Dev.to API 拥有 40+ 个端点,包括分析、语义搜索、机器学习驱动的内容概念、粉丝互动、趋势追踪和阅读列表管理。没有人使用它们。

于是我构建了 devpub

目录


devpub 的独特功能

# 基础功能(所有工具都支持)
devpub push -f articles/my-post.md
devpub pull

# 终端中的分析
devpub stats
# Views: 246.5K | Reactions: 4.4K | Comments: 402 | Followers: 18.9K

# 包含热门文章的完整仪表板
devpub dashboard

# AI 驱动搜索(语义而非关键词)
devpub search "building serverless apps" --semantic

# 即时趋势
devpub trends

# 发布前捕捉问题
devpub validate

Enter fullscreen mode Exit fullscreen mode

区别不在于单一功能,而在于覆盖范围。以下是对比:

功能 devpub 其他工具
发布/更新文章 Yes Yes
拉取文章到本地 Yes 部分
分析(7 个端点) Yes No
语义搜索 Yes No
趋势发现 Yes No
文章验证 Yes No
速率限制(30 req/30s) Yes No
失败重试机制 Yes No
Concepts API(ML 主题) Yes No

我在 Dev.to API 中发现的内容

在构建 devpub 的过程中,我发现了一些未在明显位置文档化的 API 端点:

1. 语义搜索 -- Dev.to 拥有完整的基于嵌入的搜索系统,使用 Gemini embeddings(768 维向量)和 pgvector。你可以按含义搜索文章,而非仅关键词。该端点返回余弦相似度分数。

2. Concepts API -- 这些是机器学习生成的主题分类,带有每日指标:页面浏览量、反应数、评论数、流行度评分。比手动标签强大得多。

3. V1 Accept Header -- V1 API 需要 Accept: application/vnd.forem.api-v1+json。没有它,你将获得 V0 响应。我未在任何竞争对手的代码中看到这一点。

4. 嵌套分析响应 -- 分析端点返回嵌套对象,如 {"page_views": {"total": 246454, "average_read_time_in_seconds": 306}},而非扁平整数。我检查过的每个工具要么不使用分析,要么会在这种结构上崩溃。


构建故事(真实发生的事)

我在一天内构建了 devpub 的核心。周一下午 2 点开始,当晚就有了可用的 CLI。以下是诚实的开发时间线:

第 1-2 小时:研究

在编写任何代码之前,我分析了 9 个竞争工具。下载、阅读源码,映射每个工具使用的 API 端点。发现最“完整”的工具仅覆盖了 40+ 个端点中的 12 个。大多数仅覆盖 3-5 个。

然后我阅读了完整的 Forem API 文档。不是每个人都读的摘要页,而是完整的 V1 规范。这就是我发现语义搜索、概念和分析端点的地方——没人知道它们存在。

第 3 小时:脚手架

pyproject.toml、src 布局、Click CLI 入口点。无聊的工作,但我用 15 分钟就让 devpub --help 工作了。这里的关键决定是使用 httpx 而非 requests。httpx 提供连接池、适当的超时以及后续异步选项,而无需更改接口。

第 4-5 小时:API 客户端

我在这里花了最多时间。不是因为 HTTP 调用困难。而是因为我希望客户端从第一天起就达到生产级:

  • 真正有效的速率限制(滑动窗口,而非仅睡眠计时器)
  • 针对 429 和 5xx 的指数退避重试
  • 适当的错误消息而非堆栈跟踪

第一个版本没有这些。它只是调用 raise_for_status() 并向用户抛出丑陋的 httpx.HTTPStatusError 异常。我在测试中发现,当我拉取自己的 86 篇文章并在第 30 篇文章时遇到速率限制时,整个程序崩溃了。

第 6 小时:针对真实账户测试

这里事情变得有趣了。我第一次调用 devpub stats 时崩溃了:

TypeError: '>=' not supported between instances of 'dict' and 'int'

Enter fullscreen mode Exit fullscreen mode

原来分析端点返回 {"page_views": {"total": 246454}},而非 {"page_views": 246454}。嵌套字典。现有工具无法正确处理,因为没有工具使用分析。

健康检查端点也让我惊讶。在 V1 API(带 Accept 头)中,/health_checks/app 需要身份验证。没有头,它返回 401。所以我将健康检查改为调用 /users/me

第 7 小时:push --all 惊吓

在测试中,push --all 差点将我的 README.md 发布到 Dev.to。原始逻辑是:查找任何带有 frontmatter 中 title.md 文件并推送。我的 README 有带标题的 YAML frontmatter。

通过要求同时包含 titlepublished 键,并仅扫描已知目录(articles/posts/content/drafts/)来修复。小事一桩,但想象一下意外将你的 CONTRIBUTING.md 作为 Dev.to 文章发布。

我会做不同的事

  1. 从 pull 命令开始,而不是 push。 Pull 迫使你在构建数据模型之前理解 API 响应格式。我先基于文档构建了模型,然后在真实响应看起来不同时不得不修复。

  2. 从一开始就进行模拟测试。 我先编写了所有代码,然后编写测试。应该在编写客户端方法的同时编写 API 模拟响应。这样会立即发现嵌套字典问题。

  3. 在 v0.0.1 中发布速率限制器。 我最初认为“我稍后添加”。在真实测试的 30 分钟内就遇到了限制。应该从第一个提交开始就存在。


架构(面向贡献者)

src/devpub/
  api/        # 带速率限制和重试的 HTTP 客户端
  cli/        # Click 命令 + Rich 终端输出  
  core/       # 业务逻辑(文章、同步、验证、配置)
  templates/  # 文章脚手架(5 个模板)

Enter fullscreen mode Exit fullscreen mode

关键决定:

  • Python + Click + Rich -- 大多数贡献者熟悉,优秀的终端用户体验
  • httpx -- 支持异步,内置超时处理
  • 滑动窗口速率限制器 -- 每 30 秒 30 个请求,自动睡眠
  • 3 次带退避的重试 -- 处理 429 和 5xx 而不崩溃
  • 基于 frontmatter 的追踪 -- 文章 ID 存储在你的 markdown 文件中,无需数据库

57 个测试,全部通过

$ pytest -v
57 passed in 1.08s

Enter fullscreen mode Exit fullscreen mode

测试使用 respx 模拟 HTTP 层。CI 中没有真实 API 调用。覆盖:

  • API 客户端(所有端点、错误代码、重试)
  • 文章模型(frontmatter 往返、slug 生成、标签解析)
  • 同步逻辑(推送新文章、更新现有文章、dry run、错误处理)
  • 验证(标题长度、标签数量、正文检查、规范 URL)

试用

pip install devpub
export DEVPUB_API_KEY=your_key_here
devpub doctor

Enter fullscreen mode Exit fullscreen mode

或从源码安装:

git clone https://github.com/simplynadaf/devpub.git
cd devpub
pip install -e .

Enter fullscreen mode Exit fullscreen mode

在以下地址获取你的 API 密钥:https://dev.to/settings/extensions


与 AI 代理配合

每个命令都返回结构化输出,静默处理速率限制,并支持 --dry-run。AI 编码代理(Claude Code、Copilot、Cursor)可以将 devpub 作为其发布层。代理编写文章,devpub 验证、推送并追踪性能。初始设置后无需人工干预。


贡献

该项目处于 beta 阶段。欢迎 PR。一些需要帮助的事项:

  • 性能:pull 命令为每篇文章发出一个 API 调用。我们能批量处理吗?
  • 图像重写:推送时相对路径应变为 GitHub 原始 URL
  • 终端图表--graph 标志被接受但尚未实现
  • Hashnode 适配器:跨平台发布脚手架已就绪,需要实现

issues 中查看 good first issue 标签。


GitHub: github.com/simplynadaf/devpub

如果这为你节省了时间,请为仓库加星。如果有问题,请提交 issue。如果你想要一个功能,请发送 PR。

你当前的 Dev.to 工作流是什么?你是在浏览器编辑器中编写,还是有本地设置?好奇人们遇到的痛点是什么。


📺 在 YouTube 上观看完整演示


Sarvar Nadaf 构建 - 云架构师
关注我: Dev.to | GitHub | YouTube | LinkedIn