Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
80 commits
Select commit Hold shift + click to select a range
0e2aa51
docs: add v0.2 redesign spec
Nowhitestar May 5, 2026
e3d69a0
docs: add v0.2 implementation plans (1-4)
Nowhitestar May 5, 2026
1359f99
chore: v0.2 project scaffolding
Nowhitestar May 5, 2026
63b4ffb
chore: dedupe gitignore + ignore egg-info
Nowhitestar May 5, 2026
d65d0cb
feat(core): add MMPError exception hierarchy
Nowhitestar May 5, 2026
4b11dae
feat(core): add host module for XDG paths and host detection
Nowhitestar May 5, 2026
8b53ca0
feat(core): add PlatformRules + Violation
Nowhitestar May 5, 2026
48845b7
feat(core): add Manifest schema, loading, normalization
Nowhitestar May 5, 2026
f3f93e9
fix(manifest): error on missing ./body path instead of silent fallthr…
Nowhitestar May 5, 2026
9b75ef5
feat(core): add CredentialStore with age FileBackend + EnvBackend
Nowhitestar May 5, 2026
c27654d
refactor(credentials): drop env-prefix scan; require explicit require…
Nowhitestar May 5, 2026
5d7bbd2
feat(core): add Provider ABC and ProviderRegistry
Nowhitestar May 5, 2026
c9bd11b
refactor(provider): type violations list, harden import error, docume…
Nowhitestar May 5, 2026
ebc8ccd
feat(core): add Run lifecycle with result/log/checkpoint
Nowhitestar May 5, 2026
6d6169a
fix(run): collision-safe run dir, ASCII-only slug, empty-targets hand…
Nowhitestar May 5, 2026
554a73f
feat(providers): scaffold wechat_article provider
Nowhitestar May 5, 2026
5f402ca
refactor(wechat_article): move API helper to provider internal
Nowhitestar May 5, 2026
8505aee
feat(wechat_article): add platform rules
Nowhitestar May 5, 2026
40cfc8e
feat(wechat_article): implement validate + prepare
Nowhitestar May 5, 2026
813c623
feat(wechat_article): implement execute + health_check
Nowhitestar May 5, 2026
e58ce0e
feat(cli): add mmp.py with validate/publish/setup/list/resume/doctor
Nowhitestar May 5, 2026
86e082d
test(integration): wechat_article dry-run end-to-end
Nowhitestar May 5, 2026
f640fca
build: Makefile with lint/typecheck/unit/smoke; rewrite smoke for v0.…
Nowhitestar May 5, 2026
26935e3
docs: rewrite SKILL.md for v0.2 architecture
Nowhitestar May 5, 2026
307b7fa
docs: update HANDOFF and README for v0.2 plan-1 progress
Nowhitestar May 5, 2026
14fe95d
chore: lint/typecheck cleanup
Nowhitestar May 5, 2026
5914630
fix(examples): update to v0.2 schema with body/cover/caption assets
Nowhitestar May 5, 2026
cfb2ae3
docs: add safety-policy.md (referenced by SKILL.md and README.md)
Nowhitestar May 5, 2026
f79eb86
fix(cli): inline body when copying manifest into run dir for self-con…
Nowhitestar May 5, 2026
5359119
feat(core): add settings.toml read/write
Nowhitestar May 6, 2026
6750f25
feat(wizard): add loader + stage placeholders
Nowhitestar May 6, 2026
7b517ea
feat(wizard): build_context for Claude consumption
Nowhitestar May 6, 2026
8d04ea4
feat(wizard): add stage 1 source_extraction prompt
Nowhitestar May 6, 2026
df438e1
feat(wizard): add stage 2 target_selection prompt
Nowhitestar May 6, 2026
eab933e
feat(wizard): add stage 3 manifest_assembly prompt
Nowhitestar May 6, 2026
c1fa20d
feat(wizard): add credential_setup prompt
Nowhitestar May 6, 2026
cce19c6
feat(wizard): commit_manifest validates and writes to run dir
Nowhitestar May 6, 2026
df46697
feat(cli): wire mmp wizard --dump-context and --commit
Nowhitestar May 6, 2026
e6a4875
feat(cli): improve setup prompts; reference credential_setup.md
Nowhitestar May 6, 2026
ee373a1
docs(skill): add wizard mode and public-publish gate
Nowhitestar May 6, 2026
58a4851
docs(handoff): note Plan 2 progress
Nowhitestar May 6, 2026
3094e14
chore(plan-2): final cleanup
Nowhitestar May 6, 2026
43f2867
feat(wizard): per-account credential status; group accounts by provider
Nowhitestar May 6, 2026
05753a5
feat(xiaohongshu): scaffold provider yaml + rules
Nowhitestar May 6, 2026
7807574
feat(xiaohongshu): implement provider with local draft via draft.sh
Nowhitestar May 6, 2026
b1d863a
feat(wechat_image): scaffold provider yaml + rules; move calibration …
Nowhitestar May 6, 2026
f1bf59b
feat(wechat_image): implement provider with browser-flow guide draft
Nowhitestar May 6, 2026
c207abe
feat(x_article): implement provider as payload-only stub
Nowhitestar May 6, 2026
f1113f1
feat(substack): implement provider as payload-only stub
Nowhitestar May 6, 2026
20beb12
test(integration): image-post dry-run covers xhs + wechat-image
Nowhitestar May 6, 2026
2d3c844
test(integration): longform multi-target dry-run e2e
Nowhitestar May 6, 2026
fea1340
refactor: deprecate v0.1 scripts as thin shims; move wechat notes
Nowhitestar May 6, 2026
d3f4b00
docs(skill): mark all 5 providers available; smoke covers all of them
Nowhitestar May 6, 2026
a095c01
docs(handoff): note Plan 3 completion
Nowhitestar May 6, 2026
ea936d2
chore(plan-3): final cleanup
Nowhitestar May 6, 2026
d1b5760
fix(providers): use mode_actual=stub for connector-not-implemented dr…
Nowhitestar May 6, 2026
7ac1f5f
docs: add architecture.md user-facing overview
Nowhitestar May 6, 2026
15c4c9a
docs: add provider authoring guide
Nowhitestar May 6, 2026
b95a7eb
docs: add credentials & vault user guide
Nowhitestar May 6, 2026
e19df68
docs: add manual-verification checklist
Nowhitestar May 6, 2026
edc56f6
docs: archive legacy references; drop superseded files
Nowhitestar May 6, 2026
18d8af1
feat: add Claude Code plugin manifest
Nowhitestar May 6, 2026
3cef53a
docs(skill): final v0.2.0 polish; remove plan-N markers
Nowhitestar May 6, 2026
cbceb6c
docs: rewrite README for v0.2 install + usage
Nowhitestar May 6, 2026
f043e0f
chore: bump 0.2.0 + CHANGELOG
Nowhitestar May 6, 2026
233d6ec
ci: GitHub Actions matrix (ubuntu + macos × py3.10/3.11/3.12)
Nowhitestar May 6, 2026
accb8bb
docs(handoff): note Plan 4 completion + v0.3 roadmap
Nowhitestar May 6, 2026
a13cc1a
fix(packaging): add tomli py3.10 dep + mmp console script entry point
Nowhitestar May 6, 2026
2996ed2
fix(cli): capability gate at validate/publish; reject mode not in pro…
Nowhitestar May 6, 2026
44e3551
chore(deps): drop unused requests; wechat_api uses stdlib urllib
Nowhitestar May 6, 2026
6219079
feat(examples): add multi-target longform-multi.yaml (3 platforms)
Nowhitestar May 6, 2026
69416bf
docs(provider-contract): align trust model with v0.2 behavior; defer …
Nowhitestar May 6, 2026
95ce224
docs(readme): note plugin marketplace submission is pending
Nowhitestar May 6, 2026
e716c1e
fix(examples): shorten image-post.yaml title to ≤20 chars (xhs limit)
Nowhitestar May 6, 2026
f72c522
fix(manifest): resolve assets.cover/images/video relative paths to ab…
Nowhitestar May 6, 2026
aba9ddd
fix(xiaohongshu): align with real draft.sh contract; fix empty requir…
Nowhitestar May 6, 2026
d6ec27a
docs: mark v0.2 ship checklist verified; document 7 real-account bugs…
Nowhitestar May 6, 2026
a7ad16c
chore: ruff format + add format check to make lint
Nowhitestar May 6, 2026
a46dd3b
fix(mypy): silence tomli import-not-found on py3.11+ (cond dep)
Nowhitestar May 6, 2026
b002388
fix(mypy): also silence unused-ignore so local py3.10 (with tomli) is…
Nowhitestar May 6, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions .claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"name": "multi-media-publisher",
"version": "0.2.0",
"description": "Cross-platform content publishing orchestration: 小红书 / 微信图文 / 微信公众号文章 / X Articles / Substack. Wizard-driven manifest creation, draft-first safety, encrypted credential vault.",
"skills": ["./SKILL.md"],
"scripts": {
"publish": "scripts/mmp.py publish",
"validate": "scripts/mmp.py validate",
"setup": "scripts/mmp.py setup",
"list": "scripts/mmp.py list",
"resume": "scripts/mmp.py resume",
"doctor": "scripts/mmp.py doctor",
"wizard": "scripts/mmp.py wizard"
},
"homepage": "https://github.com/yxliao-lewis/multi-media-publisher",
"license": "MIT",
"keywords": [
"publishing",
"social-media",
"xiaohongshu",
"wechat",
"x",
"substack",
"claude-code",
"openclaw"
]
}
50 changes: 50 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
name: CI

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
test:
name: ${{ matrix.os }} / py${{ matrix.python-version }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]
python-version: ["3.10", "3.11", "3.12"]

steps:
- uses: actions/checkout@v4

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: pip
cache-dependency-path: pyproject.toml

- name: Install
run: |
python -m pip install --upgrade pip
pip install -e ".[dev]"

- name: Lint (ruff)
run: |
python -m ruff check .
python -m ruff format --check .

- name: Typecheck (mypy)
run: python -m mypy core

- name: Unit tests
run: python -m pytest -q

- name: Smoke test
env:
# Provide ephemeral, fake home so smoke writes its vault into a
# job-local dir (the smoke script also overrides XDG_CONFIG_HOME).
HOME: ${{ runner.temp }}
run: python scripts/test_local.py
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,10 @@ __pycache__/
runs/
.env
*.log

# v0.2 additions
.coverage
.pytest_cache/
.mypy_cache/
.ruff_cache/
*.egg-info/
55 changes: 55 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Changelog

## 0.2.0 — 2026-05-06

Major refactor: provider abstraction + dual-host distribution + wizard.

Real-account verified end-to-end: `wechat-article` creates real drafts
on `mp.weixin.qq.com`; `xiaohongshu` creates real local drafts via the
xhs skill's `draft.sh`. 7 integration bugs discovered + fixed during
verification. See `docs/HANDOFF.md` "Discovered during real-account
verification" for the full list.

### Added

- `core/` host-agnostic Python: manifest schema, provider registry,
credential vault (age-encrypted), run lifecycle, platform rules, settings,
wizard fragments
- 5 bundled providers: `wechat-article`, `xiaohongshu`, `wechat-image`,
`x-article`, `substack`
- `scripts/mmp.py` unified CLI: `validate`, `publish`, `setup`, `list`,
`resume`, `doctor`, `wizard`
- `.claude-plugin/plugin.json` for Claude Code plugin marketplace
- 3-stage conversational wizard (source extraction → target selection →
manifest assembly) + credential setup wizard
- Manifest schema v0.2: `schema_version`, full-form/short-form targets,
`defaults` block, `dry-run` mode, account override per target
- Age-encrypted vault at `~/.config/mmp/credentials.json.age`
- GitHub Actions CI matrix (macOS + ubuntu × Python 3.10/3.11/3.12)
- User-facing docs: `architecture.md`, `provider-contract.md`,
`credentials.md`, `safety-policy.md`, `manual-verification.md`

### Changed

- Default `mode` is `draft`; `publish` requires explicit confirmation
- Manifest fields normalized; lock-file `manifest.lock.json` written per run
- `runs/<ts>-<slug>/` is now self-contained: manifest, lock, packs, result,
log, checkpoints, artifacts
- `mode_actual` introduces `"stub"` value to distinguish connector-not-implemented
draft fallbacks (x-article, substack) from real dry-run

### Deprecated

- `scripts/prepare_image_post.py`, `scripts/prepare_longform.py`,
`scripts/execute_image_post.py`, `scripts/adapt_content.py`,
`scripts/publish_manifest.py`, `scripts/wechat_api_draft.py` — kept as
thin shims; removal in v0.3

### Removed

- Legacy `references/*.md` (moved to `docs/` or `providers/<name>/notes.md`)

## 0.1.0 — 2026-05 (pre-redesign)

- Initial OpenClaw skill: prepare/execute scripts, image-post + longform
pipelines, WeChat API dry-run helper, local smoke test.
23 changes: 19 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,21 @@
PYTHON ?= python3
.PHONY: test lint typecheck unit smoke clean

.PHONY: test
test: lint typecheck unit smoke

test:
$(PYTHON) scripts/test_local.py
unit:
python3 -m pytest -q

lint:
python3 -m ruff check .
python3 -m ruff format --check .

typecheck:
python3 -m mypy core

smoke:
python3 scripts/test_local.py

clean:
rm -rf .pytest_cache .mypy_cache .ruff_cache __pycache__
find . -name "__pycache__" -type d -exec rm -rf {} +
find . -name "*.pyc" -delete
143 changes: 104 additions & 39 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,56 +1,121 @@
# Multi-media Publisher / 多媒体发布
# multi-media-publisher

统一调度多平台内容发布的 OpenClaw skill。
Publish one piece of content to many platforms — 小红书, 微信图文, 微信公众号
文章, X Articles, Substack — through one manifest, with draft-first safety
and an encrypted credential vault.

## MVP 目标
Works as a Claude Code plugin **and** an OpenClaw skill from the same source.

- 图文内容:小红书 + 微信图文内容
- 长文章:微信公众号文章 + X Articles + Substack 草稿
- 未来:视频号 / 小红书视频 / 抖音 / B站 / YouTube Shorts
## Status

## 当前状态
v0.2 — provider abstraction + 5 first-party providers + conversational
manifest wizard + age-encrypted vault. See [`docs/HANDOFF.md`](docs/HANDOFF.md).

已完成:
## Install

- Skill 骨架:`SKILL.md`
- Manifest schema:`references/manifest-schema.md`
- 平台矩阵:`references/platform-map.md`
- 发布安全策略:`references/publishing-policy.md`
- 候选技能调研:`references/candidate-skills.md`
- 工作流规划:`references/workflows.md`
- 示例 manifest:`examples/image-post.yaml`, `examples/longform.yaml`
- 辅助脚本:
- `scripts/publish_manifest.py`:校验 manifest 并创建 run skeleton
- `scripts/adapt_content.py`:生成各平台 content pack 草稿
- `scripts/prepare_image_post.py`:生成小红书/微信图文 payload 和预览,不发布;同时生成 `wechat-article-api-bridge` payload,可被 `wechat_api_draft.py --dry-run` 直接验证
- `scripts/execute_image_post.py`:执行已确认的草稿动作;当前支持小红书本地草稿 + 微信图文操作指南
- `scripts/wechat_api_draft.py`:微信公众号 API 草稿助手,支持 dry-run;`draft-from-payload` 可读取 payload 内的 `cover`
- `scripts/prepare_longform.py`:生成公众号文章 / X Articles / Substack 长文 payload 和预览,不发布
- `scripts/test_local.py` + `Makefile`:本地 smoke test,覆盖 py_compile、image-post prepare、微信 guide、小红书本地 draft、WeChat API dry-run、longform prepare
### As a Claude Code plugin

## 基本用法
(Marketplace submission pending — for now, clone the repo and point Claude
Code at the directory.)

```bash
python3 skills/multi-media-publisher/scripts/publish_manifest.py \
skills/multi-media-publisher/examples/image-post.yaml
git clone https://github.com/yxliao-lewis/multi-media-publisher.git
# Then in Claude Code: settings → plugins → load from directory
```

After v0.2.0 marketplace submission lands, the install will be:

```
/plugin install multi-media-publisher
```

### As an OpenClaw skill

Clone into your skills directory:

```bash
git clone https://github.com/yxliao-lewis/multi-media-publisher.git \
~/.openclaw/skills/multi-media-publisher
```

### Python deps

```bash
pip install -e ".[dev]"
```

Requires Python 3.10+.

## Quickstart

python3 skills/multi-media-publisher/scripts/adapt_content.py \
skills/multi-media-publisher/examples/longform.yaml \
--out /tmp/mmp-adapt
### Conversational wizard (recommended)

python3 skills/multi-media-publisher/scripts/prepare_image_post.py \
/path/to/image-post.yaml
In Claude Code or OpenClaw, just say what you want:

python3 skills/multi-media-publisher/scripts/execute_image_post.py \
/path/to/run-dir --target xiaohongshu --yes-draft
> 帮我把这篇文章发到公众号、X 长文章、Substack 草稿。

python3 skills/multi-media-publisher/scripts/wechat_api_draft.py \
draft-from-payload /path/to/run-dir/packs/wechat-article-api-bridge/payload.json --dry-run
Claude reads `core/wizard/*.md` and walks you through source extraction →
target selection → manifest assembly → draft.

python3 skills/multi-media-publisher/scripts/prepare_longform.py \
skills/multi-media-publisher/examples/longform.yaml
### CLI

```bash
# Validate a manifest
mmp validate examples/longform.yaml

# Configure credentials for a provider
mmp setup wechat-article

# Run a publish (defaults to draft mode in the manifest)
mmp publish examples/longform.yaml

# List providers / accounts / runs
mmp list providers
mmp list accounts
mmp list runs

# Self-check
mmp doctor
```

## Safety

- Default `mode: draft`. Public publishing requires explicit `mode: publish`
in the manifest **plus** an in-conversation confirmation.
- Credentials are stored in `~/.config/mmp/credentials.json.age` (age-encrypted).
- Secrets never appear in `result.json`, `publish-log.md`, or printed output.
- Full policy: [`docs/safety-policy.md`](docs/safety-policy.md).

## Architecture

- [`docs/architecture.md`](docs/architecture.md) — high-level overview
- [`docs/provider-contract.md`](docs/provider-contract.md) — write your own provider
- [`docs/credentials.md`](docs/credentials.md) — vault and ENV usage
- [`docs/manual-verification.md`](docs/manual-verification.md) — pre-release checklist
- [`docs/superpowers/specs/`](docs/superpowers/specs/) — full design spec

## Project layout

make -C skills/multi-media-publisher test
```
core/ # host-agnostic Python (manifest, providers, vault, runs)
providers/ # bundled first-party providers
wechat_article/
xiaohongshu/
wechat_image/
x_article/
substack/
scripts/mmp.py # CLI entry
.claude-plugin/ # Claude Code plugin manifest
SKILL.md # OpenClaw + Claude Code skill manifest
docs/ # user-facing docs
tests/ # core tests + integration tests
```

## Contributing

- New provider? Read [`docs/provider-contract.md`](docs/provider-contract.md).
- Bug or design discussion? Open an issue.

## License

脚本不会真实发布。真实外发必须通过已验证的平台 skill,并遵守 draft-first / confirmation-first 策略。
MIT.
Loading
Loading