diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json
index d18506c..4f522c6 100644
--- a/.claude-plugin/marketplace.json
+++ b/.claude-plugin/marketplace.json
@@ -6,7 +6,7 @@
},
"metadata": {
"description": "Skills shared by Baoyu for improving daily work efficiency",
- "version": "1.25.3"
+ "version": "1.25.4"
},
"plugins": [
{
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a012409..66ccecf 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,14 @@
English | [中文](./CHANGELOG.zh.md)
+## 1.25.4 - 2026-01-29
+
+### Fixes
+- `baoyu-markdown-to-html`: generate proper `
` tags with `data-local-path` attribute instead of text placeholders.
+- `baoyu-post-to-wechat`: fix API publishing to read image paths from `data-local-path` attribute; fix title/cover extraction from corresponding `.md` frontmatter when publishing HTML files.
+- `baoyu-post-to-wechat`: fix CLI argument parsing to handle unknown parameters gracefully; add `--summary` parameter support.
+- `baoyu-post-to-wechat`: fix browser publishing to convert `
` tags back to text placeholders before paste.
+
## 1.25.3 - 2026-01-28
### Features
diff --git a/CHANGELOG.zh.md b/CHANGELOG.zh.md
index 8c0eb7d..e04d988 100644
--- a/CHANGELOG.zh.md
+++ b/CHANGELOG.zh.md
@@ -2,6 +2,14 @@
[English](./CHANGELOG.md) | 中文
+## 1.25.4 - 2026-01-29
+
+### 修复
+- `baoyu-markdown-to-html`:生成带 `data-local-path` 属性的 `
` 标签,而非纯文本占位符。
+- `baoyu-post-to-wechat`:修复 API 发布时从 `data-local-path` 属性读取图片路径;修复发布 HTML 文件时从对应 `.md` 的 frontmatter 提取标题和封面图。
+- `baoyu-post-to-wechat`:修复命令行参数解析,正确跳过未知参数;新增 `--summary` 参数支持。
+- `baoyu-post-to-wechat`:修复浏览器发布模式,粘贴前将 `
` 标签转换回文本占位符。
+
## 1.25.3 - 2026-01-28
### 新功能
diff --git a/skills/baoyu-markdown-to-html/scripts/main.ts b/skills/baoyu-markdown-to-html/scripts/main.ts
index 03de6e9..9c34c30 100644
--- a/skills/baoyu-markdown-to-html/scripts/main.ts
+++ b/skills/baoyu-markdown-to-html/scripts/main.ts
@@ -214,7 +214,6 @@ export async function convertMarkdown(markdownPath: string, options?: { title?:
}
fs.copyFileSync(tempHtmlPath, finalHtmlPath);
- console.error(`[markdown-to-html] HTML saved to: ${finalHtmlPath}`);
const contentImages: ImageInfo[] = [];
for (const img of images) {
@@ -226,6 +225,15 @@ export async function convertMarkdown(markdownPath: string, options?: { title?:
});
}
+ let htmlContent = fs.readFileSync(finalHtmlPath, 'utf-8');
+ for (const img of contentImages) {
+ const imgTag = `
`;
+ htmlContent = htmlContent.replace(img.placeholder, imgTag);
+ }
+ fs.writeFileSync(finalHtmlPath, htmlContent, 'utf-8');
+
+ console.error(`[markdown-to-html] HTML saved to: ${finalHtmlPath}`);
+
return {
title,
author,
diff --git a/skills/baoyu-post-to-wechat/scripts/wechat-api.ts b/skills/baoyu-post-to-wechat/scripts/wechat-api.ts
index 82c8e3d..06ed041 100644
--- a/skills/baoyu-post-to-wechat/scripts/wechat-api.ts
+++ b/skills/baoyu-post-to-wechat/scripts/wechat-api.ts
@@ -177,7 +177,7 @@ async function uploadImagesInHtml(
accessToken: string,
baseDir: string
): Promise<{ html: string; firstMediaId: string }> {
- const imgRegex = /
]+src=["']([^"']+)["'][^>]*>/gi;
+ const imgRegex = /
]*\ssrc=["']([^"']+)["'][^>]*>/gi;
const matches = [...html.matchAll(imgRegex)];
if (matches.length === 0) {
@@ -188,7 +188,7 @@ async function uploadImagesInHtml(
let updatedHtml = html;
for (const match of matches) {
- const [, src] = match;
+ const [fullTag, src] = match;
if (!src) continue;
if (src.startsWith("https://mmbiz.qpic.cn")) {
@@ -198,15 +198,21 @@ async function uploadImagesInHtml(
continue;
}
- console.error(`[wechat-api] Uploading image: ${src}`);
+ const localPathMatch = fullTag.match(/data-local-path=["']([^"']+)["']/);
+ const imagePath = localPathMatch ? localPathMatch[1]! : src;
+
+ console.error(`[wechat-api] Uploading image: ${imagePath}`);
try {
- const resp = await uploadImage(src, accessToken, baseDir);
- updatedHtml = updatedHtml.replace(src, resp.url);
+ const resp = await uploadImage(imagePath, accessToken, baseDir);
+ const newTag = fullTag
+ .replace(/\ssrc=["'][^"']+["']/, ` src="${resp.url}"`)
+ .replace(/\sdata-local-path=["'][^"']+["']/, "");
+ updatedHtml = updatedHtml.replace(fullTag, newTag);
if (!firstMediaId) {
firstMediaId = resp.media_id;
}
} catch (err) {
- console.error(`[wechat-api] Failed to upload ${src}:`, err);
+ console.error(`[wechat-api] Failed to upload ${imagePath}:`, err);
}
}
@@ -310,6 +316,7 @@ Arguments:
Options:
--title
Override title
+ --summary Article summary (for future use)
--theme Theme name for markdown (default, grace, simple). Default: default
--cover Cover image path (local or URL)
--dry-run Parse and render only, don't publish
@@ -337,6 +344,7 @@ interface CliArgs {
filePath: string;
isHtml: boolean;
title?: string;
+ summary?: string;
theme: string;
cover?: string;
dryRun: boolean;
@@ -358,12 +366,16 @@ function parseArgs(argv: string[]): CliArgs {
const arg = argv[i]!;
if (arg === "--title" && argv[i + 1]) {
args.title = argv[++i];
+ } else if (arg === "--summary" && argv[i + 1]) {
+ args.summary = argv[++i];
} else if (arg === "--theme" && argv[i + 1]) {
args.theme = argv[++i]!;
} else if (arg === "--cover" && argv[i + 1]) {
args.cover = argv[++i];
} else if (arg === "--dry-run") {
args.dryRun = true;
+ } else if (arg.startsWith("--") && argv[i + 1] && !argv[i + 1]!.startsWith("-")) {
+ i++;
} else if (!arg.startsWith("-")) {
args.filePath = arg;
}
@@ -405,6 +417,15 @@ async function main(): Promise {
if (args.isHtml) {
htmlPath = filePath;
htmlContent = extractHtmlContent(htmlPath);
+ const mdPath = filePath.replace(/\.html$/i, ".md");
+ if (fs.existsSync(mdPath)) {
+ const mdContent = fs.readFileSync(mdPath, "utf-8");
+ const parsed = parseFrontmatter(mdContent);
+ frontmatter = parsed.frontmatter;
+ if (!title && frontmatter.title) {
+ title = frontmatter.title;
+ }
+ }
if (!title) {
title = extractHtmlTitle(fs.readFileSync(htmlPath, "utf-8"));
}
diff --git a/skills/baoyu-post-to-wechat/scripts/wechat-article.ts b/skills/baoyu-post-to-wechat/scripts/wechat-article.ts
index b237156..5e70825 100644
--- a/skills/baoyu-post-to-wechat/scripts/wechat-article.ts
+++ b/skills/baoyu-post-to-wechat/scripts/wechat-article.ts
@@ -115,7 +115,7 @@ async function sendPaste(cdp?: CdpConnection, sessionId?: string): Promise
}
}
-async function copyHtmlFromBrowser(cdp: CdpConnection, htmlFilePath: string): Promise {
+async function copyHtmlFromBrowser(cdp: CdpConnection, htmlFilePath: string, contentImages: ImageInfo[] = []): Promise {
const absolutePath = path.isAbsolute(htmlFilePath) ? htmlFilePath : path.resolve(process.cwd(), htmlFilePath);
const fileUrl = `file://${absolutePath}`;
@@ -128,6 +128,28 @@ async function copyHtmlFromBrowser(cdp: CdpConnection, htmlFilePath: string): Pr
await cdp.send('Runtime.enable', {}, { sessionId });
await sleep(2000);
+ if (contentImages.length > 0) {
+ console.log('[wechat] Replacing img tags with placeholders for browser paste...');
+ const replacements = contentImages.map(img => ({ placeholder: img.placeholder, localPath: img.localPath }));
+ await cdp.send<{ result: { value: unknown } }>('Runtime.evaluate', {
+ expression: `
+ (function() {
+ const replacements = ${JSON.stringify(replacements)};
+ for (const r of replacements) {
+ const imgs = document.querySelectorAll('img[src="' + r.placeholder + '"], img[data-local-path="' + r.localPath + '"]');
+ for (const img of imgs) {
+ const text = document.createTextNode(r.placeholder);
+ img.parentNode.replaceChild(text, img);
+ }
+ }
+ return true;
+ })()
+ `,
+ returnByValue: true,
+ }, { sessionId });
+ await sleep(500);
+ }
+
console.log('[wechat] Selecting #output content...');
await cdp.send<{ result: { value: unknown } }>('Runtime.evaluate', {
expression: `
@@ -176,7 +198,7 @@ async function parseMarkdownWithPlaceholders(markdownPath: string, theme?: strin
return JSON.parse(output);
}
-function parseHtmlMeta(htmlPath: string): { title: string; author: string; summary: string } {
+function parseHtmlMeta(htmlPath: string): { title: string; author: string; summary: string; contentImages: ImageInfo[] } {
const content = fs.readFileSync(htmlPath, 'utf-8');
let title = '';
@@ -203,7 +225,45 @@ function parseHtmlMeta(htmlPath: string): { title: string; author: string; summa
}
}
- return { title, author, summary };
+ const mdPath = htmlPath.replace(/\.html$/i, '.md');
+ if (fs.existsSync(mdPath)) {
+ const mdContent = fs.readFileSync(mdPath, 'utf-8');
+ const fmMatch = mdContent.match(/^---\r?\n([\s\S]*?)\r?\n---/);
+ if (fmMatch) {
+ const lines = fmMatch[1]!.split('\n');
+ for (const line of lines) {
+ const colonIdx = line.indexOf(':');
+ if (colonIdx > 0) {
+ const key = line.slice(0, colonIdx).trim();
+ let value = line.slice(colonIdx + 1).trim();
+ if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
+ value = value.slice(1, -1);
+ }
+ if (key === 'title' && !title) title = value;
+ if (key === 'author' && !author) author = value;
+ if ((key === 'description' || key === 'summary') && !summary) summary = value;
+ }
+ }
+ }
+ }
+
+ const contentImages: ImageInfo[] = [];
+ const imgRegex = /
]*\ssrc=["']([^"']+)["'][^>]*>/gi;
+ const matches = [...content.matchAll(imgRegex)];
+ for (const match of matches) {
+ const [fullTag, src] = match;
+ if (!src || src.startsWith('http')) continue;
+ const localPathMatch = fullTag.match(/data-local-path=["']([^"']+)["']/);
+ if (localPathMatch) {
+ contentImages.push({
+ placeholder: src,
+ localPath: localPathMatch[1]!,
+ originalPath: src,
+ });
+ }
+ }
+
+ return { title, author, summary, contentImages };
}
async function selectAndReplacePlaceholder(session: ChromeSession, placeholder: string): Promise {
@@ -273,9 +333,13 @@ export async function postArticle(options: ArticleOptions): Promise {
effectiveAuthor = effectiveAuthor || meta.author;
effectiveSummary = effectiveSummary || meta.summary;
effectiveHtmlFile = htmlFile;
+ if (meta.contentImages.length > 0) {
+ contentImages = meta.contentImages;
+ }
console.log(`[wechat] Title: ${effectiveTitle || '(empty)'}`);
console.log(`[wechat] Author: ${effectiveAuthor || '(empty)'}`);
console.log(`[wechat] Summary: ${effectiveSummary || '(empty)'}`);
+ console.log(`[wechat] Found ${contentImages.length} images to insert`);
}
if (effectiveTitle && effectiveTitle.length > 64) throw new Error(`Title too long: ${effectiveTitle.length} chars (max 64)`);
@@ -416,7 +480,7 @@ export async function postArticle(options: ArticleOptions): Promise {
if (effectiveHtmlFile && fs.existsSync(effectiveHtmlFile)) {
console.log(`[wechat] Copying HTML content from: ${effectiveHtmlFile}`);
- await copyHtmlFromBrowser(cdp, effectiveHtmlFile);
+ await copyHtmlFromBrowser(cdp, effectiveHtmlFile, contentImages);
await sleep(500);
console.log('[wechat] Pasting into editor...');
await pasteFromClipboardInEditor(session);