AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Baoyu Wechat Summary

skill-jimliu-baoyu-skills-baoyu-wechat-summary · by JimLiu

Summarizes WeChat group chat highlights into a structured digest using the local wx-cli binary (https://github.com/jackwener/wx-cli). Generates a normal digest by default; a roast (毒舌) version is opt-in. Maintains per-group history (history.json + history-digests.jsonl), per-user profiles, and per-group fact memory (memory.md) across runs, with privacy guardrails baked in. Use when the user asks…

No reviews yet
0 installs
19 views
0.0% view→install

Install

$ agentstack add skill-jimliu-baoyu-skills-baoyu-wechat-summary

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No 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 No
  • 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.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-jimliu-baoyu-skills-baoyu-wechat-summary)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
1mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

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 →
Are you the author of Baoyu Wechat Summary? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

WeChat Group Summary

群聊精华提取专家。把零散的微信群聊记录提炼成结构化、可读性强的简报,并维护跨次运行的群聊历史与群友画像。底层依赖外部 wx-cli 二进制(wx 命令),不打包脚本。

> ⚠️ Sandbox restriction > > wx-cli reads from ~/.wx-cli/ (config, cache, daemon socket) and from WeChat's data directory (~/Library/Containers/com.tencent.xinWeChat/ on macOS). Both paths are outside Claude Code's default sandbox. Every wx command in this skill needs to run with dangerouslyDisableSandbox: true from the start — don't waste a sandbox attempt first. The user can use /sandbox to view/edit restrictions.

User Input Tools

When this skill prompts the user, follow this tool-selection rule (priority order):

  1. Prefer built-in user-input tools exposed by the current agent runtime — e.g., AskUserQuestion, request_user_input, clarify, ask_user, or any equivalent.
  2. Fallback: if no such tool exists, emit a numbered plain-text message and ask the user to reply with the chosen number/answer for each question.
  3. Batching: if the tool supports multiple questions per call, combine all applicable questions into a single call; if only single-question, ask them one at a time in priority order.

Concrete AskUserQuestion references below are examples — substitute the local equivalent in other runtimes.

Prerequisites

Before invoking the workflow, verify the environment. Run these checks in order; stop at the first failure and surface the exact next command the user needs.

  1. wx-cli installed — run wx --version. If missing, tell the user to install it themselves (npm install -g @jackwener/wx-cli or use one of the alternatives at https://github.com/jackwener/wx-cli). Do NOT auto-install — this repo forbids piped/silent installs.
  2. ~/.wx-cli directory owned by the current usersudo wx init historically chowned this directory to root, which breaks every subsequent non-sudo wx call. Check:

``bash ls -la ~/.wx-cli/ 2>/dev/null | head -5 ` If the directory exists but the owner is root (or anything other than $(whoami)), tell the user to repair it themselves: `bash sudo chown -R $(whoami) ~/.wx-cli sudo rm -f ~/.wx-cli/daemon.pid ~/.wx-cli/daemon.sock wx daemon start ` The skill should NOT run sudo` on the user's behalf.

  1. wx-cli initializedwx sessions should return data. If it fails with "no keys" / "init required", instruct the user to run wx init while WeChat is running (on macOS, codesign --force --deep --sign - /Applications/WeChat.app first). Prefer non-sudo init; only fall back to sudo wx init if the user's wx-cli version requires it — and warn them that they'll need step 2's chown after.
  2. WeChat 4.x running and logged in — required for the daemon to find data files.

Preferences (EXTEND.md)

Check EXTEND.md in priority order — the first one found wins:

| Priority | Path | Scope | |----------|------|-------| | 1 | .baoyu-skills/baoyu-wechat-summary/EXTEND.md (relative to project root) | Project | | 2 | ${XDG_CONFIG_HOME:-$HOME/.config}/baoyu-skills/baoyu-wechat-summary/EXTEND.md | XDG | | 3 | $HOME/.baoyu-skills/baoyu-wechat-summary/EXTEND.md | User home |

| Result | Action | |--------|--------| | Found | Read, parse, apply. On first use in session, briefly remind: "Using preferences from [path]. Edit it to change defaults." | | Not found | MUST run first-time setup (BLOCKING) before generating any digest — do NOT silently use defaults. |

Supported keys

EXTEND.md is plain text with key: value or key=value lines, # for comments, case-insensitive keys.

| Key | Type | Default | Purpose | |-----|------|---------|---------| | self_wxid | string | (required) | The owning account's wxid. Messages whose from_wxid matches this are attributed to the user. | | self_display | string | (required) | Display name to substitute for the user's own messages in digest text. | | default_version | normal / roast / both | normal | Which version(s) to generate when the user doesn't say otherwise. | | default_time_range | string (e.g. 7d, 24h, 1d) | (none) | Default range when the user omits time and there's no incremental anchor. | | data_root | path | {project_root}/wechat | Override where digest folders live. | | bot_aliases | comma-separated strings | bot, 精华bot | Names that trigger the 「@bot 答疑」 section. A message containing @ (case-insensitive) is treated as a question/request aimed at the digest bot. Pick names that do NOT match any real group member or existing bot, to avoid ambiguity. |

A starter template lives at [EXTEND.md.example](EXTEND.md.example).

First-Time Setup (BLOCKING)

If no EXTEND.md is found, do NOT silently proceed.

Step A — Try to auto-discover self_wxid and self_display first. Run (in order, stop at the first that succeeds):

# 1. If wx-cli exposes a whoami, use it
wx whoami --json 2>/dev/null

# 2. Otherwise, find self-sent messages in recent sessions
wx sessions --json --limit 20 2>/dev/null

For option 2, scan the sessions for any private/group thread the user has sent into and read one of their own from_wxid / from_nickname pairs. If you can confidently pre-fill both values, use them as defaults in the question below; otherwise leave the fields blank for the user to fill in.

Step B — Confirm with one AskUserQuestion call (batched), pre-filling whatever auto-discovery found:

  • self_wxid (e.g., wxid_abc123) — fall-back hint: the user can find it with wx contacts --query "", or by inspecting any of their own sent messages in wx sessions --json
  • self_display (e.g., 宝玉) — how they want their messages attributed
  • default_version — pick one of normal / roast / both
  • data_root — where digest folders live. Default: {project_root}/wechat. Enter a custom absolute path (e.g. ~/Documents/wechat-digests) or leave blank for default.
  • Save location — pick one of project / XDG / home

Write EXTEND.md to the chosen path. If the user provided a non-default data_root, include it as an uncommented line; otherwise omit it (the default applies automatically). Confirm "Preferences saved to [path]. Edit it any time to change defaults.", then continue with the digest workflow.

Workflow

Step 1: Parse the user's request

Extract:

  • Group name (or partial name for fuzzy matching)
  • Time range — interpret flexibly:
  • "最近 1 天" / "今天" / "last 24 hours" → 1 day
  • "最近 3 天" → 3 days
  • "最近 7 天" / "这周" → 7 days
  • "最近 30 天" / "最近一个月" → 30 days
  • "某天" (e.g. "3 月 5 号") → that specific date
  • "某天到某天" (e.g. "3 月 1 号到 3 月 5 号") → date range
  • "从上次开始" / "继续" / "接着上次" / "since last" → incremental mode: read history.json for this group, use last_digest.last_message_time as the start
  • No time specified → incremental mode. If no history.json exists yet, fall back to default_time_range from EXTEND.md if set, else last 24 hours.
  • Version(s) to generate:
  • Start from default_version in EXTEND.md.
  • User request overrides: keywords "毒舌"/"roast"/"挑衅"/"再来个毒的"/"sass" → force include_roast=true. Keywords "只要正经的"/"normal only"/"不要毒舌" → force include_normal=true, include_roast=false. "都来一份"/"两个版本都要"/"both" → both.
  • At least one of include_normal/include_roast must end up true.

Convert relative ranges into absolute --since YYYY-MM-DD --until YYYY-MM-DD pairs using today's local date.

Step 2: Find the group + resolve folder path

wx contacts --query "" --json

Filter for entries whose username ends in @chatroom. If multiple groups match, use AskUserQuestion to disambiguate. If none match, fall back to wx sessions --json and search there before asking the user.

Once resolved, compute the folder path:

{data_root}/{group_id}-{sanitized_group_name}/

where data_root is from EXTEND.md (default {project_root}/wechat).

Sanitize the group name — replace any of / \ : * ? " | NUL and control characters with _. Trim trailing dots and whitespace. Don't strip emoji or Chinese characters.

Group-rename detection: list existing folders under {data_root}/ and find any folder whose name starts with {group_id}-. If one exists but the suffix differs (group was renamed), rename the existing folder to the new {group_id}-{sanitized_new_name} form. If a target with the new name already exists (rare), keep both and prefer the existing one for this run.

Step 3: Fetch messages

For small batches (single-day digest, typically " --since YYYY-MM-DD --until YYYY-MM-DD -n 5000 --json


For **large batches** (weekly / monthly digests, > 200 messages), redirect to `$TMPDIR` first so the raw payload never sits in conversation context:

```bash
wx history "" --since YYYY-MM-DD --until YYYY-MM-DD -n 5000 --json > "$TMPDIR/wx-messages.json"
wc -c "$TMPDIR/wx-messages.json"
jq 'length' "$TMPDIR/wx-messages.json"

Then read the file in slices via Read with offset + limit, or process with jq queries (e.g. jq '.[0:200]', jq '[.[] | {id, from_nickname, timestamp, content: (.content | .[0:50])}]' for a lightweight skeleton pass). Reading all 500+ messages at once will burn token budget unnecessarily.

Notes:

  • --since is inclusive; --until is interpreted as a date (the whole day). If the user asked for "today only", set both to today.
  • -n 5000 is a defensive cap; for very active groups, raise it and re-fetch.
  • Filter the returned messages by their timestamp to be safe (some daemons may return adjacent days).
  • Range splitting: for ranges > 7 days OR > 500 messages, prefer generating per-3-day digests and then a meta-summary over forcing one giant digest — the categorization quality degrades sharply past a week's worth of unrelated topics.

Incremental mode: after the fetch, drop any message whose timestamp is ` or [链接/文件] ` → shared article; the title IS the information — quote it and credit the sharer

  • [系统] ... revokemsg → revoked; exclude from digest and from leaderboard
  • timestamp — convert to MM-DD HH:MM for display (and use full ISO for generated_at)
  • chat_type — sanity-check group
  • Quote/reply — try quote_id, reply_to, quoted_msg_id, or any nested quote object. If present, use it as strong attribution. If absent, fall back to context but flag the inferred link as uncertain.

Step 3.6: Resolve self + ambiguous nicknames

  • Substitute self_display for every message whose from_wxid matches self_wxid (from EXTEND.md). Apply this in the leaderboard, portraits, and body text. The user MUST appear under their real display name and count toward stats — never skip them.
  • Scan all unique senders for ambiguous handles: ≤2 characters, common programming words (nil, null, test, admin, user, undefined), single emoji, or otherwise low-information. For each, run wx contacts --query "" --json --limit 5 and pick a meaningful name in this priority: remark > nickname > wxid. Apply the substitution everywhere in the digest.

Step 3.7: Load user profiles

For each unique sender appearing in this batch:

  • Look in {folder}/profiles/{wxid}-*.md by wxid prefix match. Read the matched file if found.
  • If include_roast, also look in {folder}/profiles-roast/{wxid}-*.md for the roast pass.

Compile a condensed profile context block as internal working memory — do NOT write it into the final digest. Example shape:

== 群友历史画像(来自 profiles/)==
K. H:空中直播员 / 生活百科全书。常见话题:旅行、金融、美食。经典金句:"要不要买moderna"。
可可苏玛:...

Rules:

  • Only load profiles for users active in this batch — never preload everyone.
  • Profile is background, not template. Current messages are still the primary source.
  • Use historical labels for continuity ("又双叒叕化身空中直播员") or contrast ("一向省钱的 XX 今天居然...").
  • Strict separation: normal pass reads only profiles/, roast pass reads only profiles-roast/. Never cross-load.

See [references/profiles.md](references/profiles.md) for the full file format.

Step 3.7.5: Load group memory(群级事实记忆)

除了按人的 profiles,每个群还有一份全局事实记忆 {folder}/memory.md,记录群友指正过、确认过的客观事实(如"某个报错提示的真实原因"、"某产品名的正确写法"、"某事件的实际经过")。

  1. 如果 memory.md 存在,读入作为内部背景知识(不写入最终摘要)
  2. 写摘要时必须遵守其中的事实修正——上一期摘要里说错、已被群友指正的说法,这一期绝不能再犯。例如记忆中有"『当前微信版本不支持』是 AI Agent 无法获取微信链接导致的提示,普通用户可正常打开",就不能再把它当成"骗点击"的梗来写
  3. 记忆条目是事实约束,不是风格指令——它只纠正"说什么",不改变 normal/roast 两个版本各自的语气和写法
  4. 标注为「群友说法(未验证)」的条目,引用时保留这个限定,不当成已证实的事实陈述
  5. 文件不存在则跳过,属正常情况

Step 3.8: Detect existing in-chat digests (optional)

Some users (e.g., the original 宝玉 workflow) post digests directly into the group as messages. If we don't notice these, the new digest will re-cover the same ground.

Scan the fetched messages for signals of a prior in-chat digest:

  • from_wxid == self_wxid AND
  • content contains 群聊精华 OR 消息统计: OR 📊 消息统计 OR a leaderboard pattern (e.g. ^\d+\. .+: \d+ 条), AND
  • content length > 1500 chars.

If a match is found:

  1. Extract the digest's covered date or range from the title line (e.g., xxx 群聊精华 · 2026-05-12 or ... · 2026-05-10 ~ 2026-05-12).
  2. Surface the finding to the user via AskUserQuestion:
  • "Detected an in-chat digest by you covering {范围}. Use {范围 end + 1} as the start instead of history.json?"
  • Options: Yes, skip up to {end of detected range} / No, use history.json / No, cover everything in the requested range.
  1. Apply the chosen anchor.

This is a heuristic — when uncertain (multiple matches, malformed title), default to history.json and tell the user what was skipped.

Step 3.9: Detect @bot requests (if any)

Some group members address the digest bot directly — e.g. @bot 帮我把昨天的讨论捋一下 or @精华bot 这个链接讲了啥. Catch these so each digest can answer them in a dedicated section instead of dropping them as noise.

Trigger: a message whose text contains @ for any alias in bot_aliases (from EXTEND.md; default bot, 精华bot; case-insensitive). Aliases are stored as bare names — match the @ prefix plus the alias.

Extract into an internal worklist == @bot 请求清单 == (working memory only — never written to the final digest):

  • Asker's real name — after Step 3.6 resolution; substitute self_display for the self_wxid user.
  • Request body — the text after stripping the @ prefix. If the message is a reply (per Step 3.5's quote/reply fields), include the quoted message as context.
  • Anchor local_id for back-reference.

Misfire filtering: if a real member's nickname happens to equal an alias, judge by context. Keep only messages genuinely aimed at the digest bot (a question or request for it); skip clear person-to-person talk — a reply to that real person, or banter teasing them. (Choosing a bot_aliases value no real member uses avoids this at the source; the filter is a backstop.) Pure greetings/banter (@bot 在吗) may be kept with a brief reply.

Answer-source constraint (honored when rendering the section per [references/output-formats.md](references/output-formats.md)): answer from the group chat context plus your own knowledge only — no web access. For any request needing real-time or external information you can't verify, say so honestly (这个我查不到实时数据,需要联网确认) rather than fabricating.

No hits → both versions omit the @bot 答疑 section entirely.

Do this in the same read-through as Round 1's skeleton (via its == @bot 请求清单 == block) so the messages aren't scanned twice.

Generate the digest in three rounds so nothing slips through. The methodology stays here in SKILL.md; the content/style rules live in [references/output-formats.md](references/output-formats.md) — read that file in Round 2 before drafting.

Round 1 — Build the skeleton

Read every message in order. Skip image fetching/decoding in this round. List every distinct discussion topic. Bias toward over-listing — trim in Round 3.

Internal working format (not written to the final file):

== 话题清单(共 N 条消息)==
1. [HH:MM-HH:MM] 话题名称(参与者:A, B, C)—

…

## Source & license

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

- **Author:** [JimLiu](https://github.com/JimLiu)
- **Source:** [JimLiu/baoyu-skills](https://github.com/JimLiu/baoyu-skills)
- **License:** MIT

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

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.