首页/AI 智能体记忆知识/baoyu-wechat-summary
B

baoyu-wechat-summary

by @jimliuv
4.5(120)

提取微信群聊精华,生成结构化摘要,并支持毒舌版本。它能维护群聊历史和用户画像,帮助用户快速回顾群聊内容,理解群友动态,提升信息获取效率。

wechatsummarizationgroup-chathistory-managementuser-profilesGitHub
安装方式
npx skills add https://github.com/jimliu/baoyu-skills --skill baoyu-wechat-summary
compare_arrows

Before / After 效果对比

1
使用前

每天面对海量微信群消息,手动筛选、阅读并总结关键信息耗时巨大,容易遗漏重要内容,导致信息过载和决策滞后。

使用后

自动生成结构化群聊摘要,快速掌握核心内容和群友动态,大幅减少信息处理时间,确保不错过任何重要讨论。

SKILL.md

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:
    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:
    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.
  3. 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.
  4. 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:

PriorityPathScope
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.mdXDG
3$HOME/.baoyu-skills/baoyu-wechat-summary/EXTEND.mdUser home
ResultAction
FoundRead, parse, apply. On first use in session, briefly remind: "Using preferences from [path]. Edit it to change defaults."
Not foundMUST 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.

KeyTypeDefaultPurpose
self_wxidstring(required)The owning account's wxid. Messages whose from_wxid matches this are attributed to the user.
self_displaystring(required)Display name to substitute for the user's own messages in digest text.
default_versionnormal / roast / bothnormalWhich version(s) to generate when the user doesn't say otherwise.
default_time_rangestring (e.g. 7d, 24h, 1d)(none)Default range when the user omits time and there's no incremental anchor.
data_rootpath{project_root}/wechatOverride where digest folders live.

A starter template lives at 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 "<own nickname>", 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 "<group_name>" --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 < 200 messages), pipe JSON into the agent directly:

wx history "<group_name_or_id>" --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:

wx history "<group_name_or_id>" --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 <= the last_message_time from history.json. If zero messages remain, tell the user "上次摘要后没有新消息,已跳过生成" and exit.

Step 3.5: Parse the message schema

wx history --json returns an array of message objects. Use the fields that are present; tolerate missing fields:

  • id / msg_id / local_id — message identifier (use whichever wx-cli emits). Reference IDs in working notes as anchors when building the skeleton.
  • from_wxid — stable sender identifier
  • from_nickname — display name (may be the group remark or original nickname)
  • content — text payload. Examples:
    • Plain text → use as-is
    • [图片] → opaque placeholder; see image handling below
    • [表情] → emoji/sticker; skip in body unless surrounded by discussion
    • [视频] / [文件] → media reference; skip unless discussed
    • [链接] <title> or [链接/文件] <title> → 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 "<nick>" --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 for the full file format.

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.
  3. 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.

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 — 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)— 一句话概括(锚点 id:54052, 54055, 54063)
2. [HH:MM-HH:MM] 话题名称(参与者:D, E)— 一句话概括(锚点 id:54100-54112)
...

== 可能需要图片上下文的话题 ==
- 话题 3:锚点 id=49661(图片是讨论主体)

== 发言统计 ==
1. XXX — N 条  2. YYY — N 条  ...

Topic principles:

  • Topic-switch signals: time gap > 30 min, participant change, content jump.
  • 2+ participants OR substantive content qualifies as a topic; pure emoji-banter does not.
  • Strict attribution: each topic must record "who said what". Don't fuse adjacent messages from different senders just because they're close in time — when minutes apart or interleaved with others, split into separate topics.

...

用户评价 (0)

发表评价

效果
易用性
文档
兼容性

暂无评价

统计数据

安装量9.3K
评分4.5 / 5.0
版本
更新日期2026年9月20日
对比案例1 组

用户评分

4.5(120)
5
37%
4
43%
3
13%
2
5%
1
2%

为此 Skill 评分

0.0

兼容平台

🤖claude-code

时间线

创建2026年5月28日
最后更新2026年9月20日
🎁 Agent 知识卡片
调研问卷