* fix(baoyu-post-to-x): render CJK-adjacent bold/italics in articles
md-to-html.ts preprocessed markdown with remark + remark-cjk-friendly, then re-stringified and re-parsed with marked. remark-cjk-friendly parses emphasis adjacent to CJK correctly, but remark-stringify re-emits the **bold*/*italic* markers, which marked then FAILS to parse: its flanking test does not treat a CJK character after the closing delimiter as valid punctuation/whitespace. Result: literal **...** asterisks leak into the pasted X Article body whenever the closing delimiter is directly followed by a CJK character.
Fix: convert strong/emphasis mdast nodes (already identified correctly by remark-cjk-friendly) into raw inline <strong>/<em> HTML before stringify, so marked passes them through untouched. A small recursive serializer preserves nested links, inline code, images, etc.
- add remarkStrongEmToHtml remark plugin + inline mdast serializer
- add unist-util-visit as an explicit dependency
- add regression test covering CJK-adjacent bold and italics
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(baoyu-post-to-x): 修复紧贴中文的加粗/斜体在 X Articles 渲染为字面 ** 的问题
按维护者建议改用 remark-cjk-friendly 标准用法,替代自定义插件/序列化器:
- 删除 remarkStrongEmToHtml 插件与 serializeMdastNode 序列化器
- preprocessCjkMarkdown 简化为 remarkParse + remarkCjkFriendly + remarkStringify 往返
- 移除原 markdown 阶段的 &#x...; 实体解码(该解码会还原 cjk-friendly 为
骗过 marked flanking 判定而生成的实体,正是 CJK 加粗失败的根因)
- 改在 marked 渲染出最终 HTML 后再 decodeHtmlEntities,此时 markdown 语法
已不存在,解码安全且保持输出干净
- 移除不再使用的 unist-util-visit 显式依赖
- 测试补充引用式链接 **[docs][d]** 在强调内的回归断言(顺带修复 Codex
指出的 linkReference 渲染为纯文本的 P2 问题)
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(baoyu-post-to-x): 移除渲染后无差别实体解码,避免篡改作者字面实体
回应 Codex review (P2):decodeHtmlEntities 对整个渲染 HTML 做无差别
&#x...; 解码,会把作者为显示字面 HTML 而写的实体(如 <b>)
解码成真实标签,存在内容篡改与 HTML 注入风险。
该解码本就只是为了让输出 HTML「好看」,而 remark-cjk-friendly 引入的
实体是合法 HTML 字符引用,粘进 X 编辑器时能正确渲染;且这些实体只会
出现在强调 span 的外侧(边界字符),不会出现在 span 内部。因此直接
移除解码,既消除过度解码风险,又无功能损失。
- 删除 decodeHtmlEntities 及其在 convertMarkdownToHtml 的调用
- 新增测试:作者字面实体 <b> 不被解码为真实 <b>,CJK 加粗
仍正常渲染
- bun test 8 个用例全部通过;真实文章端到端验证 17 处加粗、0 字面 **、
0 真实标签注入
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: ruofeng <ruofeng.wang@rd.group>
Co-authored-by: Claude <noreply@anthropic.com>
Gemini 3 stopped emitting the legacy `image_generation_content` marker in
the candidate text and moved the generated image into a later response
part. The `wants_generated` gate only checked the candidate text / a single
structured field, so on Gemini 3 it was never true and the image was
silently dropped: generate_content returned an empty result even though the
gg-dl image URL was present in the raw response.
- Broaden `wants_generated` to also fire when the raw response contains a
`gg-dl/` generated-image URL or an `image_generation_content` marker.
- When a candidate has no image part, skip extraction instead of throwing
ImageGenerationError, so multi-candidate responses don't fail wholesale.
Verified against a live Gemini 3 account: image generation returns the
generated image again with the default gemini-3-pro model.
Stop Codex from satisfying a request by copying an unrelated pre-existing
image from generated_images instead of generating a new one. Verification
now requires a PNG in this thread's generated_images dir (or an image_gen
stream item, kept as a forward-compatible signal), and the spawned-agent
instruction forbids reading or reusing history images before image_gen is
called. Removes the findCpToTarget fallback that allowed the shortcut.
Validated against a real codex exec run: image_gen leaves no stream item,
so the filesystem check is the load-bearing signal; locked in via a
condensed real-stream regression fixture.
Closes#185
On Windows, Bun throws EEXIST for mkdir(dir, { recursive: true }) when the
directory already exists, contradicting Node's documented contract (it should
resolve silently). Image generation then succeeds but the file save fails
whenever the output directory already exists (e.g. the Desktop):
EEXIST: file already exists, mkdir 'C:\Users\...\Desktop'
Add an ensureDir() helper that tolerates EEXIST only when the path really is a
directory (rethrowing otherwise, so a genuine EEXIST against an existing file
is not swallowed), and route writeImage() and migrateLegacyExtendConfig()
through it. Covered by a cross-platform unit test.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
parseYouTubeVideoId handled watch, youtu.be, /shorts/ and /live/ URLs but
not the common /embed/<id> player form, so embed links returned null and the
YouTube adapter treated them as "no document". Add an /embed/ branch
mirroring the existing /shorts/ and /live/ handling, with a regression test.
Add the ability to specify the original article URL ("阅读原文" / "Read
Original" link) when publishing to WeChat Official Account draft via API.
Changes:
- ArticleOptions interface: add contentSourceUrl optional field
- publishToDraft: conditionally write content_source_url for news articles
- CLI: add --source-url parameter
- Frontmatter: extract sourceUrl / contentSourceUrl / content_source_url
- Priority chain: CLI --source-url → frontmatter → (none)
- SKILL.md: document the new option and draft/add payload rule
The field is only written for article_type=news per WeChat API spec.
content_source_url is optional (max 1KB) per the official draft/add API.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Obsidian creates markdown image links with URL-encoded filenames
(e.g. `Pasted%20image%2020260524.png`) but the actual file has spaces.
Add `decodeURIComponent()` before path resolution in both baoyu-md's
shared `resolveImagePath()` and baoyu-post-to-x's independent version.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add the outline.md + prompts/ → batch.json one-liner to SKILL.md's Usage section so the
build-batch helper is discoverable next to --batchfile, and update build-batch.ts --help
to print the bun / npx-y bun invocations with the {baseDir}/scripts/... layout used by
the rest of the skill.
Add a `Codex via codex exec` branch to each skill's backend-selection
ladder and ship a per-skill references/codex-imagegen.md with the
invocation contract (preferred baoyu-image-gen --provider codex-cli
path, direct-wrapper fallback, parameter notes, stdout schema, batch
semantics).
Replace the long inline `scripts/codex-imagegen.sh` invocation block in
SKILL.md with a pointer to references/codex-imagegen.md. The reference
documents the preferred `baoyu-image-gen --provider codex-cli` path and
keeps the direct-wrapper fallback for runtimes without baoyu-image-gen.
Expose Codex CLI's built-in image_gen tool through baoyu-image-gen's
standard CLI + batch flow as a dedicated provider. The provider spawns
the bundled scripts/codex-imagegen/main.ts (synced from
packages/baoyu-codex-imagegen/src/) so the skill remains self-contained
and inherits retry, cache, file-lock, and JSONL logging. No
OPENAI_API_KEY required — uses the user's Codex subscription.
New env vars: BAOYU_CODEX_IMAGEGEN_{BIN,CACHE_DIR,TIMEOUT_MS,RETRIES,LOG_FILE}.
Hyphenated provider names now resolve under-scored env vars (e.g.,
BAOYU_IMAGE_GEN_CODEX_CLI_CONCURRENCY). codex-cli is never auto-selected
— pin via --provider or default_provider in EXTEND.md.
Move the codex-imagegen wrapper out of scripts/ into its own workspace
package alongside baoyu-md, baoyu-chrome-cdp, and baoyu-fetch. Drop the
bash shim — src/main.ts now carries a `#\!/usr/bin/env bun` shebang and
serves as the sole entrypoint. Add scripts/sync-codex-imagegen.sh so
skills/baoyu-image-gen/scripts/codex-imagegen/ can be regenerated from
packages/baoyu-codex-imagegen/src/ for skill self-containment. Update
CLAUDE.md, docs/codex-imagegen-backend.md, and the CI workflow paths
accordingly.