baoyu-wechat-summary

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-g

By jimliu · 8,965 installs

npx skills add jimliu/baoyu-skills --skill baoyu-wechat-summary

Source repository · Upstream listing

WeChat Group Summary 群聊精华提取专家。把零散的微信群聊记录提炼成结构化、可读性强的简报,并维护跨次运行的群聊历史与群友画像。底层依赖外部 [wx cli](https://github.com/jackwener/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. References(按需加载) 本文件只保留工作流骨架;细节拆在 references/ 下, 执行到对应步骤时再读,不要一开始全部读入 : 参考文件 内容 何时读 [references/setup.md](references/setup.md) 环境检查(wx cli 安装/权限/初始化)、wx cli 命令速查、排障手册 新环境首次运行,或任何 wx 命令失败时 [references/output formats.md](references/output formats.md) 两版摘要的 Section 顺序、格式与内容规范、输出骨架、自检清单 Round 2 动笔前 [references/profiles.md](references/profiles.md) 画像文件格式、更新规则、隐私红线、回溯流程 Step 3.7 / 8.5 / Step 9 [references/group memory.md](references/group memory.md) 群级事实记忆的写入门槛、防注入、格式 Step 8.6 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 快速验证环境: wx version 有输出且 wx sessions 返回数据即可继续。任何一步失败,或是首次在新环境运行 → 读 [references/setup.md](references/setup.md)(完整环境检查、wx cli 命令速查、排障手册),停在第一个失败项并给用户确切的修复命令。 绝不自动安装、绝不替用户跑 sudo 。 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 @<alias (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): 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 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: 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 2.5: Look up the group owner(群主) 群主是谁 必须有据可查 ,不能凭历史摘要、群友玩笑或印象推断(群主可能换届,历史摘要里的说法会过期): 检查输出中是否有 owner / role 字段标识群主;有则以此为准 如果 wx cli 版本不暴露群主信息,则查 memory.md「群基本档案」里有出处的记录;两处都没有 → 摘要里不要断言谁是群主 查到的结果与「群基本档案」不一致时以本次查询为准,更新档案并追加修订记录(注明查询日期) Step 3: Fetch messages Always redirect the fetch to a $TMPDIR file — this file is the single source of truth for the whole run: Round 3's attribution audit greps it, and the statistics are computed from it. Never write the digest purely from conversation memory. For small batches (single day digest, typically < 200 messages), you may additionally pipe JSON into the agent directly for reading: For large batches (weekly / monthly digests, 200 messages), the $TMPDIR redirect also keeps the raw payload out of conversation context: 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 , and write the filtered set back to the $TMPDIR file (so audits and stats run on exactly what the digest covers). Caution: last message time is MM DD HH:MM — plain string comparison breaks across a year boundary (12 31 vs 01 01); compare by date semantics there. 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. 硬规则 : nil 、空白、单标点这类占位符样式的名字 绝不允许原样出现在摘要里 。contacts 查不到 remark 时,用「昵称(wxid 后 4 位)」形式区分(如 nil(…n77g) ),确保读者知道这是谁、且与其他人不混淆。已解析过的映射写入 memory.md「群基本档案」,下期直接复用不再重查。 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: Rules: Only load profiles for users active in this batch — never preload e