# Cmm Publishing

> Use this CMM skill to assemble publish packages and prepare platform draft, browser-review, manual-upload, or schedule-planning workflows from content, visual, and video packages. It directly creates CMM publish packages, support checks, metadata, asset mapping, and approval gates. It supports WeChat draft box via built-in scripts/wechat_draft.py plus browser fallback via scripts/wechat_browser_d…

- **Type:** Skill
- **Install:** `agentstack add skill-chemny-cmm-content-factory-cmm-publishing`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [chemny](https://agentstack.voostack.com/s/chemny)
- **Installs:** 0
- **Category:** [Web & Browser](https://agentstack.voostack.com/c/web-and-browser)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [chemny](https://github.com/chemny)
- **Source:** https://github.com/chemny/cmm-content-factory/tree/main/skills/cmm-publishing

## Install

```sh
agentstack add skill-chemny-cmm-content-factory-cmm-publishing
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# CMM Publishing

`cmm-publishing` is the publishing and draft-routing layer in the CMM content
factory.

It consumes `content_package`, optional `visual_package`, and optional
`video_package`, then produces a `publish_package` with platform support,
metadata, asset mapping, approval gates, and execution route. After explicit
confirmation, an approved backend can create a draft or browser-review state.

## Boundary

Use this skill for:

- Assembling a `publish_package`.
- Deciding whether the target should be `draft_box`, `browser_review`, or
  `schedule_plan`, `manual_upload`, or `unsupported`.
- Preparing platform metadata, asset mapping, and checklist output.
- Running backend preflight before platform execution.
- Choosing a safer fallback when the preferred backend is unavailable.
- Selecting WeChat, X, Weibo, Xiaohongshu, or social scheduling workflows only
  as approved execution backends.
- Recording publishing gaps for platforms not yet supported.

Do not use this skill to:

- Write the content draft.
- Generate images or render videos.
- Click `Publish`, `Post`, `发布`, or any externally visible submit action by
  default.

## Publish Boundary

This skill assembles a `publish_package` and prepares drafts / manual-upload
instructions. Built-in publish paths: WeChat draft box API
(`scripts/wechat_draft.py`), WeChat browser draft fallback
(`scripts/wechat_browser_draft.mjs`), X (`scripts/x_publish.mjs`), Weibo
(`scripts/weibo_publish.mjs`), and Douyin (`scripts/douyin_publish.py`). Browser
paths default to **fill-only**: the assistant fills the content and stops before
final public publishing, and the **user clicks publish themselves in the
browser** (the assistant does not auto-click, even on confirmation — see
Confirmation Rules).
Live auto-publish to platforms not yet built in (e.g. Xiaohongshu
live-fill) remains an **optional plugin**, never routed to by default. If a
platform-specific gap appears, record it in `platform_gaps` and update CMM
contracts or docs.

## Input

Preferred input:

```yaml
content_package:
  platform: ""
  format: ""
  title: ""
  body_markdown: ""
  summary: ""
  sources_used: []
  publish_notes: ""
  status: draft

visual_package:
  asset_paths: []
  status: draft

video_package:
  render_path: ""
  cover_path: ""
  publish_copy: ""
  status: draft
```

Read `references/publish-package-contract.md` for exact output fields. Read
`references/publishing-method.md` for support matrix, metadata, approval rules,
backend preflight, and fallback behavior. Read
`references/wechat-quality-gate.md` before WeChat draft creation; use
`templates/wechat-layout-package-template.md` for the WeChat layout handoff.

## Workflow

1. Validate available packages.
2. Resolve target platform.
3. Choose target mode:
   - `draft_box`: API or platform draft when supported.
   - `browser_review`: browser opens with content filled, user reviews manually.
   - `schedule_plan`: planning/calendar output only.
   - `unsupported`: platform gap recorded.
4. Build `publish_package`.
5. Add metadata, checklist, platform gaps, and approval requirements.
6. Run preflight for the selected backend before platform execution:
   - runtime dependencies
   - credentials or logged-in state
   - API/IP allowlist constraints where detectable
   - required cover/image/video assets
   - backend capability gaps
7. If preflight fails, either choose a documented fallback or set
   `publish_status: blocked` with a concrete `platform_gaps` item.
   When both "repair preferred backend" and "fallback backend" are plausible,
   do not switch automatically. Report the reason, whether any draft/assets were
   created, present the decision options, and wait for the user to choose.
8. Ask for explicit confirmation before invoking any platform skill unless the
   user already requested draft/browser/manual preparation in the current turn.
   For WeChat long-form outputs coming from CMM Content Factory, the local
   preview gate outranks same-turn draft intent: do not invoke the WeChat
   draft-box backend until the user has seen and confirmed the rendered preview
   and then confirmed draft-box creation.
9. Route to an approved backend only after confirmation or same-turn draft intent
   where no stricter preview gate applies.
10. Stop before any final public post unless the user explicitly confirms the
   final publish action in the same turn.

## Routing

Default output is a publish package + draft / manual-upload instructions.
X, Weibo, Douyin, and WeChat draft are **built-in** (review-before-publish by
default). The right-hand column marks platforms whose live publish still needs
the **optional live-publish plugin**; without the relevant runtime, follow the
manual-upload path.

| Platform | CMM target (default) | Live publish (built-in / optional plugin) |
|---|---|---|
| WeChat Official Account | `draft_box` via built-in `scripts/wechat_draft.py`; browser fallback via `scripts/wechat_browser_draft.mjs` when API/IP allowlist blocks | — (draft/fill paths are built-in) |
| X / Twitter | **built-in** browser publish via `scripts/x_publish.mjs` (raw CDP, spawns native Chrome, persistent login, default review-before-publish); see `references/x-publish.md`. Falls back to manual upload. | — (built-in) |
| Weibo | **built-in** browser publish via `scripts/weibo_publish.mjs` (raw CDP, spawns native Chrome, persistent login, default review-before-publish); see `references/weibo-publish.md`. Falls back to manual upload. | — (built-in) |
| Xiaohongshu / Rednote image note | publish package + manual upload | optional browser-publish plugin |
| Xiaohongshu / Rednote video note | publish package + manual upload | optional browser-publish plugin |
| Cross-platform calendar | `schedule_plan` (plan only) | optional scheduler plugin |
| Douyin | **built-in** browser publish via `sau` (`scripts/douyin_publish.py`, default `--review-before-publish`); see `references/douyin-publish.md`. Falls back to `manual_upload` if `sau` unavailable. | — (built-in) |
| Bilibili | `manual_upload` or `unsupported` | none until verified |

## publish_package

```yaml
publish_package:
  platform: ""
  target: draft_box
  title: ""
  body_ref: ""
  body_markdown: ""
  asset_refs: []
  video_ref: ""
  metadata: {}
  approval_status: pending
  publish_status: not_started
  route: ""
  approval_gate:
    draft_creation: pending
    final_publish: required_same_turn
  backend_preflight: []
  fallback_history: []
  readiness_gate: {}
  checklist: []
  platform_gaps: []
  external_result: {}
```

## Confirmation Rules

- Building `publish_package` does not require confirmation.
- If the user explicitly asks for a platform draft, draft box, browser review,
  or manual-upload package in the current turn, set
  `approval_status: target_intent_recorded` until any stricter upstream preview
  gate is satisfied. After preview confirmation and explicit draft-box/browser
  confirmation, set `approval_status: approved_for_draft` and allow draft
  preparation.
- Invoking a browser/API/platform skill requires explicit confirmation unless
  the current user request already clearly asks for draft/browser/manual
  preparation and no stricter preview gate applies.
- **Fill-only by default — the user clicks the final publish button themselves.**
  For every browser-driven platform, the assistant fills the draft/composer/creator
  backend, stops at the publish button, and hands the open browser back to the user
  to publish. The assistant does **not** click the final publish button by default,
  **even when the user confirms** — the publish click is the user's action in the
  browser. (Rationale: an async submit / re-render at publish time has corrupted
  posts before, e.g. body text dropped on click.) The assistant fills, then
  screenshots or reports state for the user to review and publish.
- `--auto-publish` / `--submit` and any auto-click switch are used **only** when the
  user, in the current turn, explicitly asks for automatic publishing; otherwise
  always stop at the publish button.
- Never use `--submit` or click final publish buttons by default.
- If a routed legacy skill has a stricter confirmation rule, follow the stricter
  rule.

## Readiness Gate

| Gate | Requirement |
|---|---|
| `content_ready` | Draft exists and is not blocked. |
| `asset_ready` | Required cover/images/video are present or gap is explicit. |
| `metadata_ready` | Title, summary, tags, cover, and account/platform notes are prepared where relevant. |
| `approval_ready` | Draft creation and final publish approvals are separated. |
| `platform_supported` | Platform route is supported, manual, or explicitly unsupported. |

## Platform Notes

### WeChat

- Prefer draft-box behavior.
- Covers are required for many article flows.
- API/IP allowlist or browser login may be required.
- Preflight API credentials and IP allowlist before doing full API draft work
  when possible.
- If API fails with IP allowlist or token permission errors, fall back to the
  built-in browser route (`scripts/wechat_browser_draft.mjs`) when Chrome/login
  requirements pass.
- Browser draft creation may not set the cover image through all backends. If
  the selected backend cannot confirm cover insertion, record an `asset_gap`
  and include cover path in `checklist`.

### X

- **Built-in** publish via `scripts/x_publish.mjs` (CMM-native, raw CDP, zero npm
  deps — uses Node ≥21 built-in WebSocket). Spawns native Chrome with a
  persistent profile (`~/.cmm/chrome-x`); the first run prompts a one-time x.com
  login (plain browser, so Google/X SSO is not blocked), reused thereafter. No
  stored credentials. `--cdp URL` can attach to an already-running debug-port Chrome.
- Text is injected via CDP `Input.insertText` (real keyboard input: writes X's
  Draft.js EditorState so it survives re-render; `execCommand('insertText')` only
  touched the DOM and got wiped on publish — see `references/x-publish.md`). Needs
  the tab in front + a real click to focus. Media via CDP `DOM.setFileInputFiles`.
- Fill-only: fill the composer, stop at the Post button; the **user clicks Post
  themselves** in the browser (`--auto-publish` only on explicit same-turn request).
- See `references/x-publish.md`. Falls back to `manual_upload` if
  Chrome/debug-port/login is unavailable.

### Weibo

- **Built-in** publish via `scripts/weibo_publish.mjs` (CMM-native, raw CDP, zero
  npm deps). Spawns native Chrome with a persistent profile (`~/.cmm/chrome-weibo`);
  first run prompts a one-time weibo.com login. No stored credentials.
- Fills the home composer `` via CDP `Input.insertText`; images/videos
  (≤18 total) via CDP `DOM.setFileInputFiles`.
- Fill-only: fill the composer, stop at the Send button; the **user clicks Send
  themselves** in the browser (`--auto-publish` only on explicit same-turn request).
- See `references/weibo-publish.md`. Falls back to `manual_upload` if
  Chrome/debug-port/login is unavailable.

### Xiaohongshu / Rednote

#### Preflight 检查（发布前必做）

| 项目 | 限制 |
|------|------|
| 标题 | ≤ 20 字 |
| 正文（去掉末尾话题标签行后）| ≤ 950 字（平台上限 1000，留安全边距）|
| 图片数量 | 1–9 张 |
| 图片格式 | jpg / png / webp，单张 ≤ 32MB |

#### 发布流程（图文笔记，已验证可用）

**Step 1 — 直接导航到上传图文页**

```
https://creator.xiaohongshu.com/publish/publish?from=menu&target=image
```

不要通过「发布笔记」下拉菜单点击，直接用 URL 跳转。

**Step 2 — 核对账号**

用 `get_page_text` 或 `javascript_tool` 读页面右上角账号名，确认是目标账号后再继续。账号不对让用户先切换，不要继续填写内容。

**Step 3 — 上传图片**

当前限制：Chrome 扩展 `file_upload` 工具只允许上传会话已共享的路径，本地 Documents 目录不在范围内。
**现行操作**：通知用户从 Finder（提前 `open` 图片目录）手动将图片拖入上传区，按顺序（封面在前）。
**待解方案**：在 Claude 桌面应用设置里将 `~/.cmm/uploads/` 加入 connected folders，生图流水线自动 `cp` 到该目录后可全自动上传。

**Step 4 — 填写标题**

用 `find` 定位标题 input（placeholder: "填写标题会有更多赞哦"），用 `computer.type` 或 `form_input` 填入。

**Step 5 — 填写正文**

正文区是 contenteditable div，用 `javascript_tool` 注入：

```javascript
const div = document.querySelectorAll('[contenteditable="true"]')[0];
div.focus();
document.execCommand('selectAll');
document.execCommand('insertText', false, bodyText);
```

话题标签直接写在正文末尾（`#话题` 格式），小红书会自动解析。
注入完成后用 `javascript_tool` 读取页面字符计数器（`X/1000`）确认字数正确。

**Step 6 — 停在发布按钮前**

所有内容填完后停止，不点「发布」。告知用户内容已就绪，由用户自行确认并点击发布。

#### 平台限制记录

- 无内置发布脚本，不支持 API draft box。
- 图片上传路径限制（见 Step 3）为当前已知 gap，记录在 `platform_gaps`。
- Final public publish 必须用户同步确认，不得代为点击。

### Douyin (built-in) and Bilibili

- Do not pretend final public publishing is complete without confirmation.
- **Douyin: built-in browser publish** via `sau` (see `references/douyin-publish.md`,
  `scripts/douyin_publish.py`). Preflight gates: account selected, login/cookie
  valid, video/image + title/tags prepared. Default `--review-before-publish`
  (fill then stop at the publish button; the **user clicks publish themselves** in
  the open browser — do not auto-click even on confirmation).
  If `sau` is unavailable, fall back to `manual_upload` with a complete package.
- Keep generated content/assets ready for manual upload even when login is not ready.
- For Bilibili, output `target: manual_upload` when a complete manual package
  can be prepared; otherwise output `target: unsupported`.
- Include a `platform_gaps` item explaining missing login, missing account,
  unsupported backend, or manual-upload requirements.

## Stop Conditions

Stop and ask before:

- Calling platform APIs.
- Opening browser automation for logged-in platforms.
- Uploading images or videos.
- Clicking any final publish/post button.
- Scheduling external posts.

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [chemny](https://github.com/chemny)
- **Source:** [chemny/cmm-content-factory](https://github.com/chemny/cmm-content-factory)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** yes
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-chemny-cmm-content-factory-cmm-publishing
- Seller: https://agentstack.voostack.com/s/chemny
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
