Install
$ agentstack add skill-chemny-cmm-content-factory-cmm-publishing ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ● Network access Used
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →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:
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
- Validate available packages.
- Resolve target platform.
- 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.
- Build
publish_package. - Add metadata, checklist, platform gaps, and approval requirements.
- 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
- 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.
- 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.
- Route to an approved backend only after confirmation or same-turn draft intent
where no stricter preview gate applies.
- 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
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_packagedoes 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/--submitand 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
--submitor 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
- 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 tomanual_uploadif
Chrome/debug-port/login is unavailable.
- 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 CDPInput.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 tomanual_uploadif
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 注入:
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(seereferences/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_uploadwhen a complete manual package
can be prepared; otherwise output target: unsupported.
- Include a
platform_gapsitem 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
- Source: chemny/cmm-content-factory
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.