Logo

用 Claude Agent Skills 实现博客一键发布:从繁琐流程到自然语言交互

Published on
...
Authors

引言:从多步骤到一句话

想象一下这两种发布博客的体验:

传统方式(即使有自动化脚本):

./check_sensitive.sh

python publish.py \
  -f "ready/article.md" \
  -t "文章标题" \
  -c tech \
  --tags "标签1,标签2" \
  -d "文章描述"

python sync.py

# 4. 预览
cd blog && hugo server -D

# 5. 部署
cd blog && hugo && git add . && git commit -m "..." && git push

使用 Claude Agent Skills:

我:帮我发布这篇文章

Claude:
✅ 已检查敏感信息
✅ 已发布文章到 Hugo
✅ 已同步到 Obsidian
✅ 已构建并部署
🎉 发布完成!文章已上线。

这就是 AI Agent 的魔力。

本文将分享我如何借助 Claude Agent Skills,将 Obsidian 到 Hugo Blog 的复杂发布流程封装为一句话交互,实现真正的"一键发布"。

更重要的是: 所有相关文件(Skill 定义、Python 脚本、配置文件)都集中在一个文件夹里,复制一个文件夹就能移植到任何项目,真正做到"开箱即用"。


第一部分:理解 Claude Agent Skills

什么是 Claude Agent Skills?

Claude Agent Skills 是 Claude Code 提供的一个强大特性,允许用户将复杂的工作流封装为可复用的"技能",然后通过自然语言交互来触发执行。

核心概念

  1. Skills(技能)

    • 本质是一个包含详细指令的 Markdown 文件
    • 描述了 Claude 应该如何完成特定任务
    • 可以调用系统工具(文件操作、命令执行等)
    • 支持复杂的决策逻辑和错误处理
  2. Skill Prompt(技能提示词)

    • 详细描述任务的步骤和要求
    • 定义输入参数和输出格式
    • 包含错误处理和边界情况处理逻辑
  3. 自然语言触发

    • 用户用日常语言描述需求
    • Claude 自动识别并执行相应的 Skill
    • 无需记忆复杂的命令行参数

技术架构

用户请求("帮我发布文章"Claude 自然语言理解
匹配相应 Skill
加载 Skill Prompt
执行具体步骤
    ├─ 读取文件
    ├─ 运行命令
    ├─ 处理数据
    └─ 返回结果
友好的用户反馈

为什么选择 Agent Skills?

对比其他自动化方案

方案优势劣势
Shell 脚本轻量、快速需要记忆命令、参数复杂、不灵活
Python 脚本功能强大、易扩展仍需命令行操作、交互性差
Obsidian 插件与编辑器深度集成开发复杂、需要 TypeScript
Claude Skills自然语言交互、智能决策需要 Claude Code

Agent Skills 的独特优势:

  1. 自然交互:无需记忆命令,用人话描述需求即可
  2. 智能决策:能根据上下文自动处理边界情况
  3. 灵活适应:可以处理"发布这篇文章"、"发布 ready 目录下最新的文章"等不同表述
  4. 错误处理:自动检测问题并给出建议
  5. 可组合:一个 Skill 可以调用多个底层工具

第二部分:底层技术实现

在封装 Agent Skills 之前,我们需要先构建底层的自动化工具。这一部分是技术基础。

内容创作者的困境

作为一个技术博主,我面临着典型的内容管理挑战:

  • Obsidian 是我的主力笔记工具,用于日常记录、知识管理、想法沉淀
  • Hugo Blog 是我的对外发布平台,用于分享技术文章

但当我想把 Obsidian 中的某篇笔记发布到博客时,需要经历一个繁琐的流程:复制内容、添加 Front Matter、处理图片、转换链接、移动文件......整个流程至少需要 10-15 分钟。

双系统并存的核心挑战

1. Front Matter 格式差异

Obsidian(YAML):

---
Date: 2024-11-13
tags:
  - 标签1
  - 标签2
project: 项目名
---

特点:

  • 使用 --- 包裹
  • 字段灵活,无强制要求
  • 支持嵌套结构
  • 大小写不敏感

Hugo(TOML):

+++
title = "文章标题"
date = "2024-11-13"
lastmod = "2024-11-13"
author = ["geekhuashan"]
draft = false
categories = ["tech"]
tags = ["标签1", "标签2"]
description = "文章描述"
+++

特点:

  • 使用 +++ 包裹
  • 必须包含特定字段(title, date, author, categories)
  • 严格的类型要求(数组、字符串、布尔值)
  • 需要符合 TOML 规范

2. 图片链接差异

Obsidian Wiki-style:

![obsidian-hugo-complete-publishing-system-1.png](/tech/obsidian-hugo-complete-publishing-system-1.png)
![Pasted image 20230320223703.png](/Pasted image 20230320223703.png)

特点:

  • 简洁的 Wiki 语法
  • 自动从 Attachment/ 目录查找
  • 支持空格和特殊字符

Hugo 标准 Markdown:

![Mac Mini 实物图](/tech/mac-mini.png)
![配置截图](/tech/pasted-image-20230320223703.png)

特点:

  • 标准 Markdown 语法
  • 需要明确的路径(相对于 static/
  • 需要 alt 文本(用于 SEO 和可访问性)
  • 文件名建议使用连字符,避免空格

3. 文件组织差异

Obsidian 目录结构:

Main/
├── 07 blog idea/           # 博客草稿(混合存放)
│   ├── draft/             # 写作中的草稿
│   ├── ready/             # 待发布队列
│   └── published/         # 已发布备份
└── Attachment/             # 所有附件统一存放
    ├── Mac mini.png
    └── ...

Hugo 目录结构:

blog/
├── content/posts/          # 按分类组织文章
│   ├── tech/
│   ├── read/
│   ├── life/
│   └── project/
└── static/                 # 按分类组织图片
    ├── tech/
    ├── read/
    └── life/

4. 兼容性矩阵

特性ObsidianHugo需要转换
Front MatterYAMLTOML✅ 是
图片链接![xxx](/xxx)![](xxx)✅ 是
图片路径相对路径绝对路径✅ 是
文件名空格支持不建议✅ 是
Wiki 链接[xxx](/xxx)[xxx](/xxx)❌ 否(Hugo 已支持)
Markdown 语法标准 + 扩展标准❌ 否

第一步:自动化发布工具(v1.0)

技术方案设计

基于上述分析,我决定开发一个 Python 脚本来自动化整个发布流程。

核心功能规划

  1. 草稿扫描与选择

    • 自动扫描 ready/ 队列目录
    • 显示所有可用草稿及修改时间
    • 支持交互式选择
  2. Front Matter 转换

    • 解析 YAML Front Matter
    • 提取现有字段(如 Date, tags)
    • 交互式收集缺失字段(title, category, description)
    • 生成完整的 TOML Front Matter
  3. 图片处理

    • 提取所有 ![xxx](/xxx)![](xxx) 引用
    • Attachment/ 目录中递归查找图片
    • 标准化文件名(去空格、小写化)
    • 复制到 blog/static/**category**/
    • 生成图片路径映射表
  4. 链接转换

    • 替换 ![obsidian-hugo-complete-publishing-system-4.png](/tech/obsidian-hugo-complete-publishing-system-4.png)![obsidian-hugo-complete-publishing-system-4.png](/tech/obsidian-hugo-complete-publishing-system-4.png)
    • 保持 Wiki 链接 [article](/article) 不变(Hugo 支持)
    • 处理边界情况(文件名中的特殊字符)
  5. 文件发布

    • 组合 Front Matter 和正文
    • 生成带日期前缀的文件名(2025-11-14_slug.md
    • 保存到 blog/content/posts/**category**/
    • ready/ 队列删除已发布文件

技术栈选择

Python 3.x

  • 理由:跨平台、生态丰富、适合脚本开发
  • 核心库:pathlib(路径操作)、re(正则匹配)、shutil(文件复制)
  • 第三方库:pyyaml(YAML 解析)、questionary(交互式 CLI,可选)

YAML 配置文件

  • 理由:可读性好、易于维护
  • 用途:路径映射、默认值、图片命名规则

核心实现细节

1. Front Matter 转换

核心挑战: YAML 到 TOML 的类型映射

def generate_toml_front_matter(self, metadata: Dict) -> str:
    """生成 TOML Front Matter"""
    toml_lines = ["+++"]

    # 字符串字段 - 需要引号包裹
    toml_lines.append(f'title = "**metadata["title"]**"')
    toml_lines.append(f'date = "**metadata["date"]**"')
    toml_lines.append(f'lastmod = "**metadata["lastmod"]**"')

    # 数组字段 - 需要方括号和引号
    toml_lines.append(f'author = ["**metadata["author"]**"]')
    toml_lines.append(f'categories = ["**metadata["category"]**"]')

    # 标签数组 - 动态生成
    if metadata['tags']:
        tags_str = ", ".join([f'"**tag**"' for tag in metadata['tags']])
        toml_lines.append(f'tags = [**tags_str**]')

    # 布尔字段 - 小写 true/false
    toml_lines.append(f'draft = **str(metadata["draft"]).lower()**')

    # 描述 - 需要转义引号
    if metadata['description']:
        desc = metadata['description'].replace('"', '\\"')
        toml_lines.append(f'description = "**desc**"')

    toml_lines.append("+++")
    return "\n".join(toml_lines)

关键点:

  • TOML 字符串必须用双引号包裹
  • 数组语法:["item1", "item2"]
  • 布尔值:小写 true/false(不是 Python 的 True/False
  • 特殊字符转义:描述中可能包含引号

2. 图片提取与处理

核心挑战: 支持两种图片引用格式

def extract_image_references(self, body: str) -> List[str]:
    """提取文章中的所有图片引用"""
    # 匹配 Obsidian Wiki-style: ![obsidian-hugo-complete-publishing-system-4.png](/tech/obsidian-hugo-complete-publishing-system-4.png)
    wiki_pattern = r'!\[\[([^\]]+)\]\]'
    wiki_images = re.findall(wiki_pattern, body)

    # 匹配标准 Markdown 本地图片: ![obsidian-hugo-complete-publishing-system-4.png](/tech/obsidian-hugo-complete-publishing-system-4.png)
    md_pattern = r'!\[([^\]]*)\]\((?!http)([^\)]+)\)'
    md_images = [match[1] for match in re.findall(md_pattern, body)]

    return wiki_images + md_images

图片文件名标准化:

def normalize_image_filename(self, filename: str) -> str:
    """标准化图片文件名"""
    # 处理空格:Mac mini.png → mac-mini.png
    if config['remove_spaces']:
        filename = filename.replace(' ', '-')

    # 转换为小写:Test.PNG → test.png
    if config['lowercase']:
        name, ext = os.path.splitext(filename)
        filename = name.lower() + ext.lower()

    return filename

实际效果:

  • Mac mini.pngmac-mini.png
  • Pasted image 20230320223703.pngpasted-image-20230320223703.png
  • Test File.JPGtest-file.jpg

3. 链接替换

核心挑战: 精确匹配并替换,避免误伤

def replace_image_links(self, body: str, image_mapping: Dict[str, str], category: str) -> str:
    """替换图片链接为 Hugo 格式"""
    result = body

    for original_name, new_name in image_mapping.items():
        # 替换 Wiki-style 链接
        wiki_pattern = rf'!\[\[**re.escape(original_name)**\]\]'
        hugo_link = f'![**new_name**](/**category**/{new_name})'
        result = re.sub(wiki_pattern, hugo_link, result)

        # 替换标准 Markdown 链接(保留原有 alt 文本)
        md_pattern = rf'!\[([^\]]*)\]\(**re.escape(original_name)**\)'
        hugo_link_with_alt = rf'![\1](/**category**/{new_name})'
        result = re.sub(md_pattern, hugo_link_with_alt, result)

    return result

关键点:

  • 使用 re.escape() 处理文件名中的特殊字符(如括号、空格)
  • 保留标准 Markdown 链接的 alt 文本
  • 生成绝对路径 /category/image.png(Hugo 要求)

使用效果演示

发布流程:

$ python publish.py

============================================================
  Obsidian → Hugo Blog 自动发布工具
============================================================

🔍 正在扫描草稿...

📝 可用的草稿文件:

  [1] 实验室自动化:从传统到智能的转型之路.md (修改于: 2024-08-19 09:23)
  [2] tailscale-derp-guide.md (修改于: 2024-06-07 16:52)

选择要发布的草稿: 2

✅ 已选择: tailscale-derp-guide.md

📋 请提供发布信息:

文章标题 [Tailscale 完全指南]: Tailscale完全指南:从自建DERP服务器到Mac子网路由
选择分类:
  ❯ tech
    read
    life
    project

标签 (逗号分隔): Tailscale, DERP, Mac, 子网路由, 网络, 服务器部署
文章描述 (用于 SEO): 一篇关于如何自建Tailscale DERP服务器以及配置Mac作为子网路由的详细指南

🖼️  正在处理图片...
找到 3 张图片
✅ 复制图片: Mac mini.png -> mac-mini.png
✅ 复制图片: Pasted image 20230320223703.png -> pasted-image-20230320223703.png
✅ 复制图片: tailscale-diagram.png -> tailscale-diagram.png

📝 正在生成文章...

============================================================
✅ 发布完成!
============================================================

📍 文章位置: blog/content/posts/tech/2025-11-14_tailscale-derp-guide.md
📍 分类: tech
📍 标签: Tailscale, DERP, Mac, 子网路由, 网络, 服务器部署

💡 下一步:
   1. 同步: python sync.py
   2. 预览: cd blog && hugo server -D
   3. 部署: cd blog && git push

性能数据(v1.0)

指标手动流程自动化工具提升
发布耗时10-15 分钟1-2 分钟83%
图片处理手动查找、复制、重命名自动完成100%
Front Matter手动编写自动生成100%
链接转换手动查找替换自动批量替换100%
错误率~20%(漏图片、错路径)<5%75%

第二步:双向同步(v2.0)

单向发布的局限性

v1.0 版本的自动化发布工具极大地提升了发布效率,但很快我就发现了新的问题:

单向发布导致的内容管理困境:

  1. 修改不同步:有时我会在 Hugo Blog 中直接修改文章(比如通过 GitHub Web 快速修正错误),但这些修改不会同步回 Obsidian
  2. 备份不完整:Obsidian 的备份只在发布时更新,如果在 Hugo 中修改了文章,Obsidian 中的版本就过时了
  3. 管理混乱:已发布文章没有按分类组织,难以管理
  4. 双重维护:需要在两边分别维护同一篇文章,容易出错

我需要的不是单向发布,而是双向同步。

双向同步系统设计

需求分析

  1. 按分类组织

    • Obsidian 的 published/ 目录应该按照 tech/read/life/project 分类组织
    • 与 Hugo Blog 的 content/posts/ 目录结构保持一致
  2. 智能同步检测

    • 自动检测哪些文章需要同步
    • 基于文件修改时间判断同步方向
    • 避免不必要的文件复制
  3. 灵活的同步方向

    • 支持双向同步(Obsidian ↔ Hugo)
    • 支持单向同步(仅 Obsidian → Hugo 或 Hugo → Obsidian)
    • 用户可以根据实际情况选择

同步策略

核心原则:以修改时间为准

# 伪代码
for each file in all_files:
    if file exists only in Obsidian:
        action = "新增到 Hugo"
    elif file exists only in Hugo:
        action = "新增到 Obsidian"
    elif obsidian_mtime > hugo_mtime:
        action = "更新 Hugo"
    elif hugo_mtime > obsidian_mtime:
        action = "更新 Obsidian"
    else:
        action = "已同步,跳过"

时间比较精度:

  • 使用秒级精度(stat().st_mtime
  • 时间差大于 1 秒才认为有修改(避免文件系统精度问题)

目录结构改造

改造前(v1.0):

Main/07 blog idea/
├── draft/
├── ready/
├── published/              # 扁平结构
│   ├── article-1.md
│   ├── article-2.md
│   └── ...
└── *.md

改造后(v2.0):

Main/07 blog idea/
├── draft/
├── ready/
├── published/              # 按分类组织
│   ├── tech/              # 技术类
│   ├── read/              # 阅读类
│   ├── life/              # 生活类
│   └── project/           # 项目类
└── *.md

核心实现

1. 文件扫描与比较

def scan_files(self, directory: Path, category: str) -> Dict[str, Path]:
    """扫描指定目录下的文章文件"""
    files = {}
    category_dir = directory / category
    if category_dir.exists():
        for file in category_dir.glob("*.md"):
            if file.is_file():
                files[file.name] = file
    return files

def compare_files(self, obsidian_files: Dict[str, Path],
                  hugo_files: Dict[str, Path]) -> Tuple[List, List, List]:
    """
    比较 Obsidian 和 Hugo 的文件
    返回:(需要同步到 Hugo 的, 需要同步到 Obsidian 的, 冲突的)
    """
    to_hugo = []      # Obsidian 更新 → Hugo
    to_obsidian = []  # Hugo 更新 → Obsidian
    conflicts = []    # 两边都修改过的文件

    # Obsidian 中有但 Hugo 中没有,或 Obsidian 更新
    for filename, obs_path in obsidian_files.items():
        if filename not in hugo_files:
            to_hugo.append((filename, obs_path, None, 'new'))
        else:
            hugo_path = hugo_files[filename]
            obs_mtime = self.get_file_mtime(obs_path)
            hugo_mtime = self.get_file_mtime(hugo_path)

            # 时间差超过 1 秒才认为有修改
            time_diff = abs(obs_mtime - hugo_mtime)
            if time_diff > 1:
                if obs_mtime > hugo_mtime:
                    to_hugo.append((filename, obs_path, hugo_path, 'update'))
                else:
                    to_obsidian.append((filename, hugo_path, obs_path, 'update'))

    # Hugo 中有但 Obsidian 中没有
    for filename, hugo_path in hugo_files.items():
        if filename not in obsidian_files:
            to_obsidian.append((filename, hugo_path, None, 'new'))

    return to_hugo, to_obsidian, conflicts

2. 同步执行

def sync_file(self, source: Path, target: Path, action: str):
    """同步单个文件"""
    try:
        target.parent.mkdir(parents=True, exist_ok=True)
        shutil.copy2(source, target)  # 保留文件元数据
        return True
    except Exception as e:
        print(f"❌ 同步失败 **source.name**: **e**")
        return False

def sync_category(self, category: str, direction: str = 'both') -> Dict:
    """
    同步指定分类的文章
    direction: 'to_hugo', 'to_obsidian', 'both'
    """
    print(f"\n📂 同步分类: **category**")
    print("-" * 60)

    # 扫描文件
    obsidian_files = self.scan_files(self.published_folder, category)
    hugo_files = self.scan_files(self.hugo_content, category)

    print(f"Obsidian: **len(obsidian_files)** 篇文章")
    print(f"Hugo Blog: **len(hugo_files)** 篇文章")

    # 比较文件
    to_hugo, to_obsidian, conflicts = self.compare_files(obsidian_files, hugo_files)

    stats = **
        'to_hugo': 0,
        'to_obsidian': 0,
        'skipped': 0,
        'failed': 0
    **

    # 同步到 Hugo
    if direction in ['to_hugo', 'both'] and to_hugo:
        print(f"\n📤 需要同步到 Hugo: **len(to_hugo)** 篇")
        for filename, source, target, action in to_hugo:
            action_text = "新增" if action == 'new' else "更新"
            target_path = self.hugo_content / category / filename
            if self.sync_file(source, target_path, action):
                print(f"  ✅ **action_text**: **filename**")
                stats['to_hugo'] += 1
            else:
                stats['failed'] += 1

    # 同步到 Obsidian
    if direction in ['to_obsidian', 'both'] and to_obsidian:
        print(f"\n📥 需要同步到 Obsidian: **len(to_obsidian)** 篇")
        for filename, source, target, action in to_obsidian:
            action_text = "新增" if action == 'new' else "更新"
            target_path = self.published_folder / category / filename
            if self.sync_file(source, target_path, action):
                print(f"  ✅ **action_text**: **filename**")
                stats['to_obsidian'] += 1
            else:
                stats['failed'] += 1

    return stats

命令行接口

# 查看同步状态
python sync.py --status

# 双向同步
python sync.py
python sync.py --direction both

# 单向同步
python sync.py --direction to_hugo
python sync.py --direction to_obsidian

# 预览模式
python sync.py --dry-run

# 交互式
python sync.py -i

使用效果

初次同步:从 Hugo 同步到 Obsidian

我的 Hugo Blog 中已有 37 篇文章,首次运行同步脚本:

$ python sync.py --direction to_obsidian

============================================================
  Obsidian ↔ Hugo Blog 双向同步工具
============================================================
同步方向: Hugo → Obsidian

📂 同步分类: tech
------------------------------------------------------------
Obsidian: 0 篇文章
Hugo Blog: 20 篇文章

📥 需要同步到 Obsidian: 20  ✅ 新增: ai-agent-seo-optimization.md
  ✅ 新增: tailscale-derp-guide.md
  ✅ 新增: github-to-cloudflare-pages.md
  ... (20)

📂 同步分类: read
------------------------------------------------------------
Obsidian: 0 篇文章
Hugo Blog: 5 篇文章

📥 需要同步到 Obsidian: 5  ✅ 新增: Getting things done.md
  ... (5)

============================================================
✅ 同步完成!
============================================================

📊 同步统计:
  📤 Obsidian → Hugo: 0  📥 Hugo → Obsidian: 37
总计: 37 篇文章已同步

日常使用:双向同步

场景 1:在 Hugo 中快速修正错误

发现文章有个错别字,通过 GitHub Web 快速修改:

# 同步回 Obsidian
$ python sync.py --direction to_obsidian

📂 同步分类: tech
📥 需要同步到 Obsidian: 1  ✅ 更新: obsidian-to-hugo-automation.md

场景 2:定期双向同步

每周运行一次,确保两边始终一致:

# 双向同步
$ python sync.py

📂 同步分类: tech
Obsidian: 20 篇文章
Hugo Blog: 21 篇文章

📥 需要同步到 Obsidian: 1  ✅ 新增: new-article.md

📂 同步分类: read
Obsidian: 6 篇文章
Hugo Blog: 5 篇文章

📤 需要同步到 Hugo: 1  ✅ 新增: new-book-review.md

性能数据(v2.0)

指标数据
同步 37 篇文章~2 秒
状态查看~0.5 秒
单个文件同步~10ms
内存占用~15MB

第三步:功能完善(v3.0)

在基础功能完善后,我又根据实际使用需求,添加了几个重要功能。

1. Unsplash 封面图集成

问题: 许多技术文章没有配图,显得单调

解决方案: 集成 Unsplash API,自动为无图片文章添加高质量封面图

实现思路

  1. 检测文章是否包含图片
  2. 如果无图片,根据标题/标签生成搜索关键词
  3. 调用 Unsplash API 搜索相关图片
  4. 展示 10 张图片供用户选择
  5. 下载选中图片到 static/**category**/cover.jpg
  6. 在 Front Matter 添加 image 字段
  7. 在文章末尾添加摄影师署名(符合 Unsplash 使用条款)

配置

编辑 publish_config.yaml

unsplash:
  access_key: "YOUR_ACCESS_KEY_HERE"
  enabled: true

使用效果

$ python publish.py

💡 文章中没有图片,可以添加 Unsplash 封面图
🔍 正在 Unsplash 搜索封面图: "programming"...
✅ 找到 10 张图片

📸 可选图片:
  [1] Modern workspace with laptop - by John Doe
  [2] Abstract technology background - by Jane Smith
  [3] Code on screen - by Developer X
  ...

选择图片 (1-10, 0=跳过): 1

✅ 封面图已下载: blog/static/tech/cover.jpg
📝 已添加摄影师署名

搜索关键词策略

智能生成,优先级:

  1. 标签中的英文词 → 如 Python, Docker
  2. 标题中的英文词 → 如 Guide, Tutorial
  3. 分类默认词 → tech"technology programming"

2. 敏感信息安全检查

问题: 技术文章中可能包含敏感信息(API Keys、公司名称、IP 地址等)

解决方案: 提供自动化的敏感信息检查脚本

检查清单

  • ✓ 搜索 "key"、"token"、"password" - 检查 API keys
  • ✓ 搜索 "BASF"、"巴斯夫" - 检查公司名称
  • ✓ 搜索 "license" - 检查许可证信息
  • ✓ 检查 IP 地址(192.168.x.x)
  • ✓ 检查代码块中的配置文件
  • ✓ 检查截图中的敏感信息

使用方式

# 发布前自动检查
./check_sensitive.sh

脱敏方法

  • API Key: sk-xxxxxsk-***[已隐藏]***
  • 公司名: BASF上海研发中心某化工企业研发中心
  • 服务器: 192.168.1.100192.168.x.x

3. 非交互式发布支持

问题: 交互式发布不适合批量操作和脚本集成

解决方案: 支持完整的命令行参数

使用方式

python publish.py \
  -f "ready/article.md" \
  -t "文章标题" \
  -c tech \
  --tags "标签1,标签2,标签3" \
  -s "article-slug" \
  -d "文章描述(100-160字符)"

可用参数:

  • -f, --file: 文章路径(必需)
  • -t, --title: 标题
  • -c, --category: 分类(tech/read/life/project)
  • --tags: 标签(逗号分隔)
  • -s, --slug: 英文 slug(可选,自动生成)
  • -d, --description: SEO 描述
  • -y, --non-interactive: 非交互模式(使用默认值)

自动生成特性:

  • 自动生成带日期前缀的文件名(2025-11-14_slug.md
  • 自动生成英文 slug(基于标题智能转换)
  • 自动生成 lastmod 字段(当前日期)

第三部分:Agent Skills 封装(★核心创新★)

有了底层的 Python 脚本后,最重要的一步就是将其封装为 Claude Agent Skill,实现真正的"一键发布"。

设计思路:从命令行到自然语言

传统方式的问题

  • 需要记忆多个命令和参数
  • 需要按顺序执行多个步骤
  • 容易遗漏某个步骤(比如忘记运行 sync.py)
  • 出错时需要手动诊断和修复

Agent Skills 的目标

  • 用户只需要说"帮我发布文章"
  • Claude 自动理解用户意图
  • 自动执行所有必要步骤
  • 智能处理错误和边界情况
  • 给出清晰的反馈

Skill 核心能力

一个完整的发布 Skill 需要具备:

1. 理解意图

  • "发布这篇文章" → 发布当前打开的文件
  • "发布 ready 目录下最新的文章" → 扫描并选择最新文章
  • "发布关于 XX 的文章" → 根据标题搜索匹配

2. 自动化流程

  • 安全检查(敏感信息扫描)
  • 文章发布(调用 publish.py)
  • 双向同步(调用 sync.py)
  • 预览/部署(可选)

3. 智能决策

  • 自动从文章内容提取元数据
  • 智能生成标题、描述、标签
  • 自动选择合适的分类
  • 处理图片引用

4. 错误处理

  • 检测并修复常见问题
  • 给出清晰的错误提示
  • 提供解决建议

Skill 文件结构

我的 obsidian-hugo-publisher Skill 文件结构(所有文件集中在一起,方便移植):

~/.claude/
└── skills/
    └── obsidian-hugo-publisher/
        ├── SKILL.md                 # 主 Skill 定义
        ├── WORKFLOW.md              # 发布工作流
        ├── COMMANDS.md              # 命令参考
        ├── publish.py               # 发布脚本
        ├── sync.py                  # 同步脚本
        ├── unsplash_cover.py        # Unsplash 封面图模块
        ├── publish_config.yaml      # 配置文件
        └── check_sensitive.sh       # 敏感信息检查脚本

重要说明:

  • 📍 Skills 应该放在用户主目录 ~/.claude/skills/ 而不是项目目录
  • 📍 这是 Claude Code 的全局 Skills 目录
  • 📍 所有项目都可以共享使用这些 Skills

设计优势:

  • ✅ 所有相关文件集中在一个目录
  • ✅ 一键复制即可移植到其他项目
  • ✅ 便于版本管理和分享
  • ✅ 全局可用,任何项目都能调用

Skill Prompt 核心内容

完整发布流程定义

## 当用户请求发布文章时

### 步骤 1:理解用户意图
- 如果用户指定了文件,使用该文件
- 如果用户说"这篇文章",使用当前打开的文件
- 如果没有指定,扫描 ready/ 目录并让用户选择

### 步骤 2:安全检查(必须执行)
- 运行:`.claude/skills/obsidian-hugo-publisher/check_sensitive.sh`
- 检查是否包含敏感信息:
  - API keys, tokens, passwords
  - 公司名称(BASF、巴斯夫)
  - IP 地址、服务器信息
- 如果发现敏感信息,**停止发布**并提示用户

### 步骤 3:提取或询问元数据
- 尝试从文章内容自动提取:
  - 标题(从 YAML Front Matter 或第一个 # 标题)
  - 现有标签
  - 分类(从文件路径或 project 字段推断)
- 如果缺失,智能询问用户:
  - 标题(必需)
  - 分类:tech/read/life/project
  - 标签(逗号分隔)
  - SEO 描述

### 步骤 4:执行发布
运行发布命令(使用 skill 目录下的脚本):
```bash
python .claude/skills/obsidian-hugo-publisher/publish.py \
  -f "ready/article.md" \
  -t "文章标题" \
  -c tech \
  --tags "标签1,标签2,标签3" \
  -d "文章描述"

步骤 5:同步到 Obsidian(关键)

发布后必须运行同步:

python .claude/skills/obsidian-hugo-publisher/sync.py

步骤 6:询问用户下一步

  • 是否需要本地预览? → cd blog && hugo server -D
  • 是否需要部署? → cd blog && hugo && git add . && git commit && git push

### 使用体验演示

#### 场景 1:发布指定文章

我:帮我发布 ready 目录下关于 Obsidian Hugo 的文章

Claude: 🔍 正在扫描 ready 目录... 找到文章:2025-11-14_obsidian-hugo-complete-publishing-system.md

📖 正在读取文章内容... 已提取标题:用 Claude Agent Skills 实现博客一键发布 已识别分类:tech 已提取标签:Claude, Agent Skills, AI, 自动化

🔒 正在检查敏感信息... ✅ 未发现敏感信息

请补充 SEO 描述(100-160字符):

借助 Claude Agent Skills 将复杂发布流程封装为一句话交互

📤 正在发布文章... ✅ 文章已发布到:blog/content/posts/tech/2025-11-14_obsidian-hugo-claude-agent-skills.md

🔄 正在同步到 Obsidian... ✅ 已同步到:Main/07 blog idea/published/tech/

🎉 发布完成!

下一步选项:

  1. 本地预览(hugo server -D)
  2. 构建并部署(hugo && git push)
  3. 完成

请选择:


#### 场景 2:一句话完整发布

我:帮我发布文章并部署

Claude: [执行完整流程...] ✅ 已检查敏感信息 ✅ 已发布文章 ✅ 已同步到 Obsidian ✅ 已构建站点 ✅ 已部署到 GitHub

🎉 文章已上线!


#### 场景 3:智能错误处理

我:帮我发布文章

Claude: 🔍 正在扫描 ready 目录... 找到文章:sensitive-article.md

🔒 正在检查敏感信息... ⚠️ 发现敏感信息!

问题 1:第 42 行包含可能的 API Key sk-proj-xxxxxxxxxxxxx

问题 2:第 156 行包含公司名称 BASF上海研发中心

❌ 发布已停止

建议修复:

  1. 将 API Key 替换为:sk-[已隐藏]
  2. 将公司名称替换为:某化工企业研发中心

修复后重新运行发布。


### 对比:传统方式 vs Agent Skills

| 维度           | 传统 Python 脚本                  | Agent Skills            |
| -------------- | --------------------------------- | ----------------------- |
| **触发方式**   | `python publish.py -f ... -t ...` | "帮我发布文章"          |
| **参数记忆**   | 需要记忆所有参数                  | 无需记忆,自然对话      |
| **元数据输入** | 必须在命令行指定                  | 智能提取 + 交互式补充   |
| **错误处理**   | 手动查看错误信息并修复            | 自动检测 + 给出修复建议 |
| **流程遗漏**   | 容易忘记某个步骤(如 sync)       | 自动执行完整流程        |
| **灵活性**     | 固定参数                          | 理解多种表述方式        |
| **学习成本**   | 需要学习命令行参数                | **零学习成本**          |
| **体验**       | 工具化、机械                      | 对话式、自然            |

### 技术实现细节

#### Skill 如何调用 Python 脚本

Claude Agent 通过 `Bash` 工具执行系统命令:
- 使用 Bash 工具运行 `python .claude/skills/obsidian-hugo-publisher/publish.py` 并传递参数
- 解析命令输出,提取成功/失败信息
- 如果失败,分析错误信息并给出建议

**路径设计:** 所有脚本都放在 skill 目录下,确保:
- ✅ 便于移植(只需复制一个文件夹)
- ✅ 便于版本管理
- ✅ 避免路径混乱

#### Skill 如何智能提取元数据

1. 使用 Read 工具读取文章内容
2. 使用正则表达式解析 YAML Front Matter
3. 提取第一个 # 标题作为备选标题
4. 分析内容关键词,推荐相关标签
5. 提取第一段作为描述候选

#### Skill 如何处理用户输入

Claude 的自然语言理解能力:
- "发布这篇文章" → 使用当前打开的文件
- "发布 XXX 文章" → 在 ready/ 目录搜索标题匹配的文件
- "发布最新的文章" → 按修改时间排序,选择最新的
- "发布关于 AI 的文章" → 搜索标题/内容包含 "AI" 的文章

---

## 完整使用指南

### 方式 1:使用 Agent Skills(★推荐★)

这是最简单的方式,只需要和 Claude 对话即可完成整个发布流程。

#### 最简单的发布

我:帮我发布文章


Claude 会:
1. 扫描 ready 目录
2. 让你选择要发布的文章
3. 自动检查敏感信息
4. 智能提取元数据(标题、标签等)
5. 询问缺失的信息(分类、描述)
6. 执行发布和同步
7. 询问是否需要预览或部署

**全程只需要回答几个简单问题,其他都自动完成!**

#### 指定文章发布

我:帮我发布 ready 目录下关于 AI 的文章


Claude 会自动搜索标题匹配的文章并发布。

#### 完整发布并部署

我:帮我发布文章并部署到线上


Claude 会执行完整流程:发布 → 同步 → 构建 → 部署,全自动。

**对比:**
- **Agent Skills 方式**1 句话,3-5 次交互,零学习成本
- **传统方式**5 个命令,需要记忆所有参数

---

### 方式 2:使用 Python 脚本(传统方式)

如果不想使用 Agent Skills,也可以直接运行 Python 脚本:

#### 1️⃣ 安全检查

```bash
./check_sensitive.sh

2️⃣ 发布文章

python publish.py \
  -f "ready/article.md" \
  -t "文章标题" \
  -c tech \
  --tags "标签1,标签2,标签3" \
  -d "文章描述"

3️⃣ 同步

发布后必须运行同步,将文章从 blog/content/posts/ 同步到 published/

python sync.py

同步关系

  • blog/content/posts/published/ (主要方向)
  • published/ 是从 blog 同步的备份

5️⃣ 本地预览

cd blog
hugo server -D

访问 http://localhost:1313 检查文章效果。

6️⃣ 构建和部署

cd blog
hugo
git add .
git commit -m "发布新文章:[文章标题]"
git push origin main

命令速查表

发布相关

# 发布单篇文章(交互式)
python publish.py

# 查看待发布队列
ls -lh "Main/07 blog idea/ready/"

# 查看已发布文章
ls -lh "Main/07 blog idea/published/"

同步相关

# 双向同步(推荐,发布后必须运行)
python sync.py

# 查看同步状态(不执行同步)
python sync.py --status

# 单向同步:Hugo → Obsidian
python sync.py --direction to_obsidian

# 单向同步:Obsidian → Hugo
python sync.py --direction to_hugo

# 交互式同步
python sync.py --interactive

# 预览模式(查看会同步哪些文件)
python sync.py --dry-run

预览和部署

# 本地预览(包含草稿)
cd blog && hugo server -D

# 构建静态站点
cd blog && hugo

# 提交并推送
cd blog && git add . && git commit -m "发布新文章" && git push

工作流最佳实践

✅ 应该做的

  1. 发布前检查敏感信息(重要)

    ./check_sensitive.sh  # 自动检查敏感信息
    
    • 搜索并隐藏 API keys、license keys
    • 替换公司名称(BASF → 某化工企业)
    • 检查截图中的敏感信息
  2. 发布后立即同步

    python publish.py   # 发布
    python sync.py      # 同步(关键步骤)
    
  3. 使用描述性图片文件名

    • docker-setup-diagram.png
    • IMG_1234.png
  4. 精心编写 SEO 元数据

    • 标题:简洁,包含关键词,50-60 字符
    • 描述:100-160 字符,概括核心内容
    • 标签:3-5 个相关标签
  5. 保持 ready/ 队列清空

    • ready/ 是临时队列,不是长期存放处
    • 写完就发布

❌ 避免做的

  1. 发布前不检查敏感信息(危险)

    • ⚠️ 可能泄露 API keys、密码、license
    • ⚠️ 可能暴露公司内部信息(BASF等)
    • ⚠️ 可能违反保密协议
    • 始终运行 ./check_sensitive.sh 进行检查
  2. 发布后不运行 sync.py

    • published/ 不会自动更新
    • 必须通过 sync.py 同步
  3. 不要将文章长期存放在 ready/ 中

    • draft/ 中修改
    • 完成后移动到 ready/
  4. 不要使用中文图片文件名

    • 可能导致 URL 编码问题

完整工作流示例

场景 1:发布一篇新文章

# 1. 将文章从 draft/ 移动到 ready/
mv "Main/07 blog idea/draft/my-article.md" "Main/07 blog idea/ready/"

# 2. 安全检查(必做)
./check_sensitive.sh

# 3. 发布文章
python publish.py
# 选择文章,填写元数据...

# 4. 同步到 Obsidian(重要)
python sync.py

# 5. 预览
cd blog && hugo server -D

# 6. 部署
cd blog
hugo
git add .
git commit -m "发布新文章:我的文章标题"
git push

场景 2:在 Hugo 中修改了文章

如果在 blog/content/posts/ 中直接编辑了文章,同步回 Obsidian:

python sync.py --direction to_obsidian

场景 3:检查发布状态

# 查看待发布队列
ls -lh "Main/07 blog idea/ready/"

# 查看同步状态
python sync.py --status

# 查看 Hugo 文章
ls -lh "blog/content/posts/tech/"

技术总结

技术亮点

1. Front Matter 智能转换

挑战: YAML 和 TOML 的类型系统差异 解决: 严格遵循 TOML 规范,特别注意引号、数组、布尔值格式

2. 图片文件名处理

挑战: 文件名中的空格和特殊字符 解决: 使用 re.escape() 转义特殊字符,确保正则匹配准确

3. 时间戳精度处理

挑战: 不同文件系统的时间戳精度不同 解决: 使用 1 秒容差,避免误判

time_diff = abs(obs_mtime - hugo_mtime)
if time_diff > 1:  # 只有差异大于 1 秒才认为有修改
    # 进行同步

4. 目录自动创建

挑战: 目标目录可能不存在 解决: 使用 mkdir(parents=True, exist_ok=True)

target.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(source, target)

5. 元数据保留

挑战: 保持文件修改时间一致 解决: 使用 shutil.copy2() 而不是 shutil.copy()

shutil.copy2(source, target)  # 保留修改时间等元数据

完整性能数据

功能手动操作自动化工具提升
发布单篇文章10-15 分钟1-2 分钟83%
同步 37 篇文章N/A(手动不现实)~2 秒
图片处理手动逐个复制自动批量100%
Front Matter手动编写自动生成100%
错误率~20%<5%75%

投资回报率

开发总耗时: 约 12 小时

  • v1.0 自动化发布:6 小时
  • v2.0 双向同步:4 小时
  • v3.0 功能完善:2 小时

回报计算:

  • 假设每月发布 4 篇文章
  • 每篇节省 10 分钟
  • 每月节省 40 分钟
  • 18 个月即可回本(12 小时 ÷ 40 分钟/月)

隐性收益:

  • 心理负担减轻,发布积极性提升
  • 发布频率从每月 2 篇提升至每月 4-5 篇
  • 内容管理更规范,两边始终保持一致
  • 降低了错误率,提升了内容质量

未来展望

潜在改进方向

1. 冲突检测与解决

目前的版本只是简单地以修改时间为准,未来可以实现:

  • 三方合并:类似 Git 的合并策略
  • 手动选择:检测到冲突时,让用户手动选择保留哪一方
  • diff 显示:显示两边的差异,辅助决策

2. 增量同步

目前每次都会扫描所有文件,可以优化为:

  • 记录同步历史:使用 SQLite 数据库记录上次同步时间
  • 只检查修改过的文件:减少文件系统操作
  • 哈希校验:使用文件哈希而不是时间戳判断是否修改

3. 实时同步

使用文件监控实现实时同步:

from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler

class SyncHandler(FileSystemEventHandler):
    def on_modified(self, event):
        # 自动触发同步
        pass

4. Obsidian 插件集成

使用 Obsidian Shell Commands 插件,设置快捷键一键发布:

# 命令配置
cd /Users/huashan/Documents/Obsidian && python publish.py

# 绑定快捷键
Cmd+Shift+P

效果:在 Obsidian 中编辑文章时,按快捷键即可触发发布流程。

5. 图片压缩

集成 Pillow 库,自动压缩图片:

from PIL import Image

def compress_image(source_path: Path, target_path: Path, quality: int = 85):
    """压缩图片"""
    with Image.open(source_path) as img:
        # 转换 RGBA 为 RGB(PNG → JPG)
        if img.mode == 'RGBA':
            img = img.convert('RGB')

        # 压缩并保存
        img.save(target_path, optimize=True, quality=quality)

收益: 减少图片体积 50-70%,提升博客加载速度。

6. AI 辅助元数据生成

使用 AI 自动生成:

  • SEO 友好的文章描述
  • 相关标签推荐
  • 英文 slug 智能生成
  • 图片 alt 文本生成(提升 SEO 和可访问性)

总结

三层架构:从底层到交互

这个项目展示了一个完整的自动化发布系统的三层架构:

第一层:底层工具(Python 脚本)

✅ Front Matter 转换(YAML → TOML) ✅ 图片提取与处理 ✅ 文件名标准化 ✅ 链接转换 ✅ 双向同步 ✅ 安全检查 ✅ 封面图集成

价值:将手动流程自动化

第二层:命令行接口

✅ 参数化发布命令 ✅ 灵活的配置系统 ✅ 友好的交互式 CLI ✅ 详细的进度反馈

价值:提供可编程的接口

第三层:Agent Skills(★核心创新★)

✅ 一句话触发完整流程 ✅ 智能理解用户意图 ✅ 自动提取和推断元数据 ✅ 智能错误处理和建议 ✅ 零学习成本

价值将工具变成对话伙伴

效率提升对比

方案耗时步骤数学习成本错误率
手动操作10-15 分钟10+ 个步骤~20%
Python 脚本2-3 分钟5 个命令~5%
Agent Skills1-2 分钟1 句话<3%

关键洞察:AI Agent 的真正价值

不仅仅是自动化,更是交互方式的革命:

  1. 从命令到对话

    • 传统:需要记忆命令、参数、顺序
    • Agent:用人话说需求即可
  2. 从被动到主动

    • 传统:工具等待指令
    • Agent:理解意图、主动询问、智能决策
  3. 从工具到伙伴

    • 传统:你需要懂工具的语言
    • Agent:工具懂你的语言
  4. 从降低门槛到消除门槛

    • 传统:简化操作流程
    • Agent:直接说出想法

最终成果

效率提升:

  • 发布耗时:10-15 分钟 → 1 句话90%+ 提升
  • 同步 37 篇文章:手动不现实 → 2 秒(无限提升
  • 错误率:20% → <3%(85% 降低
  • 学习成本:需要学习命令行 → 零学习成本

体验提升:

  • 心理负担几乎为零,想发就发
  • 发布频率显著提升
  • 内容管理更规范
  • 创作流程完全不被打断

这就是 AI Agent 的真正价值:不仅是节省时间,更是让人机交互回归自然,让技术真正为人服务。

延伸思考

Claude Agent Skills 的应用场景

这个发布系统只是一个例子。Agent Skills 可以应用到更多场景:

  1. 开发工作流

    • "帮我创建一个新的 React 组件"
    • "运行测试并修复所有失败的用例"
    • "重构这个函数,提升可读性"
  2. 数据处理

    • "分析这份 CSV 文件,生成可视化报告"
    • "从这些日志中提取错误信息"
    • "合并这两个数据库的数据"
  3. 内容管理

    • "整理我的笔记,按主题分类"
    • "生成本周的工作总结"
    • "将这篇文章翻译成英文"
  4. 运维自动化

    • "检查服务器状态并生成报告"
    • "备份所有数据库"
    • "部署最新版本到生产环境"

设计 Agent Skills 的最佳实践

  1. 清晰的职责边界:一个 Skill 应该专注于一个明确的任务域
  2. 完整的错误处理:预见并处理所有可能的异常情况
  3. 智能的默认值:减少用户输入,提升体验
  4. 友好的反馈:让用户清楚知道 Agent 在做什么
  5. 可组合性:Skill 可以调用其他 Skill 或工具
  6. 便于移植:将所有相关文件(脚本、配置、文档)集中在一个目录
    • ✅ 一键复制即可使用
    • ✅ 便于版本管理和分享
    • ✅ 降低使用门槛

AI 工作流的未来

从这个项目可以看到:

  • AI 不是替代工具,而是增强工具
  • 最好的 AI Agent 是隐形的:用户感觉不到在"用工具",而是在"完成任务"
  • 自然语言是未来的 API:描述需求比调用接口更自然

项目文件

完整文件结构

所有文件集中在一个 skill 目录,便于移植和分享:

~/.claude/
└── skills/
    └── obsidian-hugo-publisher/
        ├── SKILL.md                 # 主 Skill 定义
        ├── WORKFLOW.md              # 发布工作流
        ├── COMMANDS.md              # 命令参考
        ├── publish.py               # 发布脚本
        ├── sync.py                  # 同步脚本
        ├── unsplash_cover.py        # Unsplash 封面图模块
        ├── publish_config.yaml      # 配置文件
        └── check_sensitive.sh       # 敏感信息检查脚本

文件说明:

Skill 定义文件:

  • SKILL.md - Claude Agent 的核心指令文件,定义如何执行发布任务
  • WORKFLOW.md - 详细的发布工作流程说明
  • COMMANDS.md - 命令参考和使用指南

可执行脚本:

  • publish.py - Python 发布脚本,处理 Front Matter 转换、图片处理等
  • sync.py - Python 同步脚本,实现 Obsidian 和 Hugo 的双向同步
  • unsplash_cover.py - Unsplash 封面图模块,自动获取和下载高质量封面图
  • check_sensitive.sh - Bash 脚本,检查敏感信息

配置文件:

  • publish_config.yaml - 配置文件,包含路径、默认值、Unsplash API key 等

快速开始

方式 1:使用 Agent Skills(★推荐★)

步骤:

  1. 安装 Claude Code

  2. 复制 Skill 文件夹

    # 将整个 skill 文件夹复制到你的 Obsidian 根目录
    cp -r obsidian-hugo-publisher ~/.claude/skills/
    # 或者直接 git clone 到 .claude/skills/ 目录
    
  3. 配置 publish_config.yaml

    paths:
      obsidian_root: "/Users/你的用户名/Documents/Obsidian"
      blog_root: "/Users/你的用户名/blog"
      ready_folder: "Main/07 blog idea/ready"
      published_folder: "Main/07 blog idea/published"
    
    unsplash:
      access_key: "你的_UNSPLASH_ACCESS_KEY"
      enabled: true
    
  4. 开始使用

    • 在 Claude Code 中打开你的 Obsidian 目录
    • 直接说:"帮我发布文章"
    • 就这么简单!✨

优势:

  • ✅ 一键复制,即刻可用
  • ✅ 所有依赖文件都在一起
  • ✅ 便于更新和维护
  • ✅ 零学习成本

方式 2:使用 Python 脚本(传统方式)

如果不想使用 Agent Skills,也可以直接运行 Python 脚本:

  1. 安装依赖

    pip install pyyaml requests pillow
    
  2. 配置文件 编辑 .claude/skills/obsidian-hugo-publisher/publish_config.yaml

  3. 运行脚本

    python .claude/skills/obsidian-hugo-publisher/publish.py
    

移植到其他项目

只需要 3 步:

  1. 复制整个 skill 文件夹

    cp -r .claude/skills/obsidian-hugo-publisher /path/to/new/project/.claude/skills/
    
  2. 修改配置文件 更新 publish_config.yaml 中的路径

  3. 开始使用 在新项目中对 Claude 说"帮我发布文章"

就是这么简单! 所有相关文件都在一个文件夹里,真正做到"一次配置,到处使用"。

延伸阅读

Claude Agent Skills 相关

自动化工具开发

  • [github-to-cloudflare-pages](/从 GitHub Pages 到 Cloudflare Pages 的迁移实践)
  • tailscale-derp-guide

技术文档


更新日志

  • 2025-11-14:v1.0 完成 Python 自动化发布工具开发
  • 2025-11-14:v2.0 完成双向同步功能开发
  • 2025-11-14:v3.0 添加 Unsplash 封面图、敏感信息检查、非交互式发布
  • 2025-11-14:v4.0 封装为 Claude Agent Skills,实现一键发布
  • 2025-11-14:发布完整技术分享文章
obsidian-hugo-claude-agent-skills-1.png

关键词:Claude Agent Skills, AI Agent, 自动化工作流, Obsidian, Hugo, 自然语言交互, 博客发布


Photo by Juan Encalada on Unsplash

用 Claude Agent Skills 实现博客一键发布:从繁琐流程到自然语言交互 | 原子比特之间