publish-to-emdash

Convert 1 file blog markdown thành Portable Text và POST lên EmDash CMS admin API. Dùng khi user muốn publish/upload/đăng bài từ markdown lên site EmDash. Tự guide user setup API Token lần đầu.

Cài skill này

Chạy trong terminal, sau đó restart Claude Code.

curl -fsSL https://raw.githubusercontent.com/kaiitc/helloai-kit/main/install.sh | bash -s -- skill publish-to-emdash

Paste prompt dưới vào Claude Code / Codex CLI chat, AI sẽ chạy cài giúp.

Cài giúp mình skill "publish-to-emdash" từ HelloAI Kit nhé. Chạy trong terminal:

curl -fsSL https://raw.githubusercontent.com/kaiitc/helloai-kit/main/install.sh | bash -s -- skill publish-to-emdash

Xong check folder ~/.claude/skills/publish-to-emdash/ có tồn tại không rồi báo lại.

View source on GitHub →

Publish to EmDash

Skill này gồm 2 việc:

  1. Convert file .md (có frontmatter) sang Portable Text theo spec EmDash.
  2. POST lên EmDash admin API POST /_emdash/api/content/<collection> với Bearer token.

Tự động detect user đã setup credentials chưa. Chưa có → guide setup trước.

Prerequisites

  • Node 20+ (native fetch).
  • 2 env vars đã set:
    • EMDASH_API_URL - base URL site EmDash, vd: https://ngovan.com
    • EMDASH_API_TOKEN - Bearer token tạo từ admin UI.
  • Deps đã install: cd ~/.claude/skills/publish-to-emdash/scripts && npm install (một lần).

Flow khi user invoke

Bước 1: Kiểm tra setup

Chạy bash -c 'echo "URL=${EMDASH_API_URL:-MISSING} TOKEN=${EMDASH_API_TOKEN:+SET}"' để check env.

Nếu thiếu env → hiện hướng dẫn setup (xem section “Setup lần đầu” bên dưới). Không tiếp tục.

Nếu chưa install deps → chạy cd ~/.claude/skills/publish-to-emdash/scripts && npm install.

Bước 2: Convert + Publish

Xác định file MD user muốn publish (nếu user chưa nói rõ, hỏi).

Chạy:

node ~/.claude/skills/publish-to-emdash/scripts/publish.mjs <path-to-file.md> [--collection posts] [--status draft|published] [--dry-run]

Options:

  • --collection <name> - default posts
  • --status draft|published - default draft (an toàn, không publish live ngay)
  • --dry-run - convert + in JSON payload, KHÔNG POST. Dùng khi user muốn preview.

Bước 3: Report kết quả

Script sẽ in ID + slug + URL admin link. Chuyển kết quả cho user.

Nếu API trả lỗi (401 auth, 400 payload, 500 server), in error đầy đủ, hỏi user quyết định retry hay không.

Setup lần đầu (guide user khi thiếu env)

Copy-paste đoạn này cho user:

Chưa setup EmDash credentials. Làm theo 3 bước:

1. Login admin của bạn tại: <EMDASH_API_URL>/_emdash/admin/
   (Chưa biết URL? Là base URL site EmDash, vd: https://ngovan.com)

2. Vào Settings → API Tokens (hoặc Access → Tokens tuỳ version).
   Tạo token mới, scope: content read/write.
   COPY token ngay - EmDash không show lại sau khi close dialog.

3. Set env vars (thêm vào ~/.zshrc để persist):

   export EMDASH_API_URL="https://ngovan.com"
   export EMDASH_API_TOKEN="paste-token-vừa-copy"

4. Reload shell: source ~/.zshrc

5. Test: node ~/.claude/skills/publish-to-emdash/scripts/publish.mjs --test-auth

Sau khi user set xong, chạy lại step 1 để verify rồi tiếp tục publish.

Portable Text mapping

Script convert MD → PT theo Sanity spec (EmDash tương thích):

  • Paragraph → {_type:'block', style:'normal', children:[spans]}
  • Heading 1-6 → {_type:'block', style:'h1'..'h6'}
  • Bold/italic/inlineCode → mark trong span
  • Link → mark + markDef với href
  • List (ordered/unordered) → listItem:'number'|'bullet' + level
  • Blockquote → style:'blockquote'
  • Code block → {_type:'code', language, code}
  • Image → {_type:'image', url, alt}
  • HR → skip (EmDash không có style tương đương)

Frontmatter fields được map như sau (script tự lo):

  • titledata.title
  • descriptiondata.description (nếu có)
  • slug → top-level slug (nếu trống, script tự sinh từ title)
  • tagsdata.tags
  • draft: truestatus: 'draft', falsestatus: 'published'
  • publishedAtdata.publishedAt
  • Body markdown → data.content (Portable Text array)
  • Placeholder *[screenshot ...]* được giữ nguyên dạng emphasis span (user sẽ replace ảnh thủ công trong admin sau).

Nguyên tắc

  • KHÔNG tự publish bài draft: true với status: 'published'. Luôn tôn trọng frontmatter.
  • KHÔNG modify file MD gốc.
  • Nếu Portable Text output có warning (block type không biết map), in warning nhưng vẫn tiếp tục.
  • Nếu bài đã tồn tại trên EmDash (trùng slug), API sẽ trả 409/400 - báo user, hỏi update thay vì create.