npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

weclaude

v0.3.3

Published

Bridge Claude Code and WeCom: PreToolUse approval cards + bidirectional tmux session mirror with image paste

Readme

weclaude

把 Claude Code 装进企业微信。 在地铁上、被窝里、开会摸鱼时,照样能跟你电脑上的 Claude 干活。

demo

  • 🛎 远程审批 — Claude 要跑 Bash / Edit?审批卡片直推 IM,点 ✅/❌/⏱(放行 N 分钟)。
  • 📋 计划审批 — Claude 在 plan mode 结束(ExitPlanMode)时,把计划摘要 + 审批卡推到 IM:点 ✅同意 让它退出 plan mode 开始执行,或 ✏️继续改 让它留在 plan mode 继续完善。同 AskUserQuestion 多选题也镜像为投票卡。
  • 🪞 会话镜像 — 你电脑上跑的 Claude 流式打字、tool_use、思考过程,实时同步到企业微信;IM 里发消息原样落进 CLI 输入框。
  • 🖼 图片直贴 — 企业微信发图,自动走 macOS 剪贴板 + tmux 粘贴,Claude 当贴图处理(不走 Read,不耗 token)。
  • 🔍 细节页 — 工具调用 / 审批请求都生成本地 HTML 详情页,IM 里点链接看完整 input / result / git diff。
  • 📡 MCP 主动推送 — Claude 通过 wecom__send_markdown / wecom__send_card / wecom__ask_user 主动汇报或问询。
  • 📄 文档读写 — Claude 通过 wecom_doc_list_tools / wecom_doc_call 直接调企业微信智能机器人的 doc / smartsheet / smartpage MCP,新建在线文档、写 Markdown、读链接、操作智能表格——全程在内网,不需要 corp access_token。
  • 🗂 多会话发现/切换 — Claude 通过 list_claude_sessions / switch_claude_session / new_claude_session 列出本机 tmux 内所有在跑的 Claude 会话(带摘要 + 稳定动物 emoji 标签)、把 IM 镜像切到其中任一个、或在指定路径新开一个会话。审批卡标题也带同一枚 emoji,多个会话兜底到同一 IM 时一眼区分。
  • 🔄 重启即续 — 电脑重启 / tmux 全没了 / daemon 崩了都不掉档:IM ↔ 会话绑定持久化在 ~/.weclaude/mirror-attachments.json,下一条 IM 消息自动 claude --resume 拉起新 tmux pane,历史完整继承;tmux attach -t weclaude 接管即可。

快速开始

前置:macOS / Linux、Node ≥ 20、PATH 里能找到 claudeclaude-internal、企业微信「智能机器人」的 botId + secret。镜像模式额外需要 tmux

npm install -g weclaude
weclaude init

init 会交互式问你 4 个问题,把配置落到 ~/.weclaude/

| 问什么 | 落到哪 | | --- | --- | | botId / secret | ~/.weclaude/secrets.json | | 用哪个 Claude(claude / claude-internal / 自定义路径) | ~/.weclaude/config.jsonc | | 用哪种模式(headless / mirror,见下文) | ~/.weclaude/config.jsonc | | 是否开启 PreToolUse 远程审批 | ~/.weclaude/config.jsonc |

然后自动:编译 → 注入 hook/MCP → 装常驻 daemon(macOS launchd / Linux systemd --user)→ 等 WebSocket 鉴权。

最后一步:绑定默认会话。 CLI 提示后,在企业微信里给机器人发:

将本对话设置为默认会话

这是唯一绕过白名单的入口,10 分钟窗口,消费完立刻关。后续所有消息都按白名单鉴权。

其它安装方式 / 内网与无 systemd 环境

从某个 fork / 分支装(自带改动的版本)dist/ 不入库,但 prepare 脚本会在 git 安装时自动编译,所以可以直接:

npm i -g github:<你的用户名>/weclaude          # 默认分支
npm i -g github:<你的用户名>/weclaude#<分支名>   # 指定分支

内网 / 需要正向代理:weclaude 的出站(WeCom WebSocket、智能机器人文档 MCP)会读环境变量里的代理。安装与运行都带上 HTTPS_PROXY

HTTPS_PROXY=http://your-proxy:port npm i -g github:<你的用户名>/weclaude
# 也可在 config.jsonc 写死: bot.proxy = "http://your-proxy:port"(优先级高于环境变量)

daemon 进程同样需要能读到代理变量(见下方守护脚本里 export HTTPS_PROXY)。

无 systemd 的环境(容器等)init 装 daemon 这步在 macOS 走 launchd、Linux 走 systemd --user。若机器没有 systemd user session(很多容器:PID 1 非 systemd、无 XDG_RUNTIME_DIR),这步会失败——其余配置(hook / MCP / 插件)已生效,只差 daemon 没被托管。用一个简单的重启循环守护即可,存成 ~/.weclaude/daemonctl.sh

#!/usr/bin/env bash
# 无 systemd 时的 weclaude daemon 守护:setsid 脱离终端的重启循环。
set -uo pipefail
REPO="$(npm root -g)/weclaude"
NODE="$(command -v node)"
WC="$HOME/.weclaude"; PIDFILE="$WC/supervisor.pid"; LOG="$WC/daemon.log"
mkdir -p "$WC"
# 内网:daemon 出站要走代理。按需改成你的代理地址,或删掉这两行。
export HTTPS_PROXY="${HTTPS_PROXY:-http://your-proxy:port}"; export https_proxy="$HTTPS_PROXY"
export NO_PROXY="${NO_PROXY:-127.0.0.1,localhost}"; export no_proxy="$NO_PROXY"
is_running(){ [[ -f "$PIDFILE" ]] && kill -0 "$(cat "$PIDFILE")" 2>/dev/null; }
case "${1:-start}" in
  start)   is_running && { echo "already running"; exit 0; }
           setsid bash "$0" __loop </dev/null >>"$LOG" 2>&1 & echo $! >"$PIDFILE"
           sleep 2; echo "[daemonctl] started (pid $(cat "$PIDFILE"))" ;;
  __loop)  while [[ -f "$PIDFILE" ]]; do "$NODE" "$REPO/dist/daemon/index.js" >>"$LOG" 2>&1
             [[ -f "$PIDFILE" ]] || break; sleep 5; done ;;
  stop)    [[ -f "$PIDFILE" ]] && { pid=$(cat "$PIDFILE"); rm -f "$PIDFILE"
             pkill -P "$pid" 2>/dev/null||true; kill "$pid" 2>/dev/null||true; }
           pkill -f "$REPO/dist/daemon/index.js" 2>/dev/null||true; echo "[daemonctl] stopped" ;;
  restart) bash "$0" stop; sleep 1; bash "$0" start ;;
  status)  is_running && echo "running (pid $(cat "$PIDFILE"))" || echo "stopped"
           curl -sS -m2 http://127.0.0.1:17890/status 2>/dev/null && echo || echo "(HTTP :17890 无响应)" ;;
esac
chmod +x ~/.weclaude/daemonctl.sh
~/.weclaude/daemonctl.sh start    # 起 daemon(自带崩溃重启)
~/.weclaude/daemonctl.sh status   # 看状态

容器重启不会自动拉起 daemon——把 ~/.weclaude/daemonctl.sh start 加进 shell profile 或容器入口即可(幂等,已在跑就 no-op)。


两种模式怎么选

| | headless | mirror 🌟 | | --- | --- | --- | | 怎么跑 | IM 来消息 → 后台 claude -p 跑一轮 | IM 来消息 → tmux 粘进活的 TUI | | CLI 看得见吗 | 看不见(headless 子进程) | 看得见(IM 消息像你自己敲进去的) | | 状态续接 | 靠 --resume <sid> 续 session | 一对一绑定 IM 聊天 ↔ tmux 窗口,原地累计 | | 流式输出 | 转发 assistant 文本块 | 完整流式:打字机、tool_use、思考过程都看得到 | | 适合谁 | 简单单轮问答 / CI 类自动化 | 真·远程结对编程 |

镜像模式下,IM 里发 /new 直接开新 tmux 窗口 + 新 Claude 会话;/clear 清当前上下文;带图消息自动注入剪贴板。所有 IM 聊天共享一个 tmux session(默认名 weclaude),每个聊天一个独立 window,关 tmux / daemon 崩了 / 整机重启都能自愈:IM↔会话绑定 write-through 落到 ~/.weclaude/mirror-attachments.json,daemon 起来就 eager restore;重启后 pane 全死,下一条 IM 消息触发 claude --resume <sid> 拉起新 pane,--resume fork 出的新 jsonl 由 watcher 从 EOF 无缝接管(不会把整段历史再推一遍到 IM)。中途在别处 /clear 把 jsonl rotate 掉也不丢绑定,会自愈到同项目目录下最新的 jsonl。

💡 mirror 不要求你必须先在 CLI 里开 tmux:在企业微信里直接发 /new 就能从零起一个新 tmux 窗口 + Claude 会话;甚至首次发任意消息都会自动 spawn + 绑定(首条消息既是绑定信号也是第一句 prompt)。回家打开终端 tmux attach -t weclaude 接管即可。


体验是什么样

审批场景

你正在地铁上,电脑上的 Claude 想 rm -rf node_modules 重装。企业微信叮一声弹卡片:

🛎 授权请求: Bash rm -rf node_modules [✅ 允许] [❌ 拒绝] [⏱ 5 分钟内自动允许]

你点 ✅,卡片立刻刷新成 ✅ Bash · 已允许,电脑上的 Claude 解除阻塞继续跑。

镜像场景

你 tmux 里开着 Claude 在写代码。出门后给机器人发:

把刚才那个函数改成异步的

这条消息自动粘进 CLI 输入框 + 回车提交。Claude 的回应、调用了哪些工具、改了哪些文件,逐字流式推回你 IM。回家打开终端,对话一字不少都在那里。

文档场景

你给 Claude 说:"周报给我整理成一篇企业微信文档"。Claude 自己调 wecom_doc_list_tools 看可用方法,再调 wecom_doc_callcreate_doc 新建文档、edit_doc_content 写入 Markdown,最后把链接贴回会话——全程不离开 Claude,文档归属到你的 userid,每日 20 篇限额按 userid 计。


文档 / 智能表格 / 智能文档

weclaude 把企业微信智能机器人的远端 MCP(doc / smartsheet / contact 等)桥接到本地 Claude,不走 corp access_token:daemon 直接复用 botId+secret 的 WS 长连,通过 aibot_get_mcp_config 命令拉到每个 category 的 Streamable HTTP MCP URL,再以 JSON-RPC 调 tools/list / tools/callx-openclaw-wecom-userid header 透传文档归属人。

前置一次性授权:在企业微信「工作台 - 智能机器人 - 你的机器人 - 可使用权限」里勾选「文档」「智能表格」对应能力。第一次调用如果未授权,远端会返回结构化错误(errcode 851013)+ 授权链接,原样转给 Claude,照着点同意即可。

Claude 侧自动发现:模型先调一次 wecom_doc_list_tools(category="doc") 拿到工具列表(create_doc / edit_doc_content / smartpage_create / upload_doc_image ...)和入参 schema,再用 wecom_doc_call(category, method, args) 执行。category 当前可选:

| category | 能干嘛 | | --- | --- | | doc | 在线文档、智能文档、文档图片上传 | | smartsheet | 智能表格的字段 / 记录读写 | | contact | 通讯录查询(按需授权) |

curl 验证

# 列 doc 类目下所有可用工具
curl -sS -X POST http://127.0.0.1:17890/wedoc/list \
  -H 'content-type: application/json' \
  -d '{"category":"doc"}'

# 创建一篇文档
curl -sS -X POST http://127.0.0.1:17890/wedoc/call \
  -H 'content-type: application/json' \
  -d '{"category":"doc","method":"create_doc","args":{"doc_type":3,"doc_name":"demo"}}'

# 写入 Markdown
curl -sS -X POST http://127.0.0.1:17890/wedoc/call \
  -H 'content-type: application/json' \
  -d '{"category":"doc","method":"edit_doc_content",
       "args":{"docid":"<返回的 docid>","content_type":1,"content":"# 标题\n正文"}}'

# 授权变更后清缓存
curl -sS -X POST http://127.0.0.1:17890/wedoc/invalidate -H 'content-type: application/json' -d '{}'

requesterUserId 解析顺序:调用方显式传入 → defaultChatuser:<id> 部分 → 不传(远端会拒,错误原样回到 Claude,比 daemon 提前判更透明)。


事件订阅 / 定时广播

一个轻量 pub/sub:任意群或单聊都能订阅一个 topic(自定义事件名),任何授权用户都能广播;daemon 内置分钟级调度器,每天定点自动推送。订阅关系与定时任务持久化到 ~/.weclaude/config.jsonctopics 段,weclaude reload 后自动恢复。

IM 命令(在已授权的会话里说,中英文均可):

订阅 sync-daily                       # 当前会话订阅 topic(群里说→群订阅,单聊里说→个人订阅)
退订 sync-daily
广播 sync-daily 内容:xxx              # 立即广播给所有订阅者
每天 08:00 广播 sync-daily 内容:xxx   # 定时(也接受「每天8点」「每天8:30」「每日 08:00」)
广播列表 / 订阅列表                    # 查看当前状态
取消广播 sync-daily                    # 删掉该 topic 的所有定时

典型用法:在群 A 说 订阅 sync-daily,在群 B 说 每天8点广播 sync-daily 内容:早会 10 分钟后开始——第二天早 8 点群 A 自动收到。

外部触发(CI / 监控 / 脚本):

curl -sS -X POST http://127.0.0.1:17890/publish \
  -H 'content-type: application/json' \
  -d '{"topic":"ci-fail","markdown":"🔴 build #1234 failed on main"}'

调用方只关心事件名,运维通过 IM 命令改订阅者,代码零改动。


消息是怎么同步的

审批流headless / mirror 都走这条):

sequenceDiagram
    autonumber
    participant CC as Claude Code
    participant Hook as PreToolUse hook
    participant D as weclaude daemon
    participant WS as WeCom WS
    participant U as 你 (IM)

    CC->>Hook: 触发 Bash "rm -rf foo"
    Hook->>D: POST /approval (长轮询 ≤30min)
    D->>WS: sendCard(按钮卡片)
    WS->>U: 推送审批卡片
    Note over D,U: daemon 在内存里 park 这个 HTTP 请求<br/>等 click 事件 / 自动窗口 / 超时三选一
    U->>WS: 点 ✅
    WS->>D: template_card_event
    D->>WS: updateTemplateCard("已允许")
    D-->>Hook: 200 {decision: allow}
    Hook-->>CC: stdout 决策 JSON
    CC->>CC: 放行 Bash

镜像流mirror 模式,IM ↔ tmux 双向):

sequenceDiagram
    autonumber
    participant U as 你 (IM)
    participant WS as WeCom WS
    participant D as weclaude daemon
    participant T as tmux + claude
    participant J as session.jsonl

    U->>WS: "改成异步的"
    WS->>D: message.text
    D->>D: allowFrom 鉴权
    D->>T: load-buffer + paste-buffer + Enter
    T->>J: assistant 流式追加
    par tail
        D->>J: fs.watch 增量读
        J-->>D: 新 assistant / tool_use 块
        D->>WS: replyStream(流式片段)
        WS->>U: 推送流式回复
    and 工具拦截
        T->>D: PreToolUse hook (走上面的审批流)
    end

要点:

  • daemon 是唯一和 WeCom 说话的进程,hook 和 MCP 都只是它的 HTTP 客户端
  • 审批是长轮询 + 内存 park,没有 webhook、不依赖外网回调
  • 镜像是单向 paste + 单向 tail:IM→tmux 走粘贴键,tmux→IM 走读 jsonl,两条管道独立、互不阻塞

一个聊天里跑多个会话(#tag 路由)

同一个 WeCom 聊天里可以同时挂多个并行 Claude session,靠消息里的 #tag 前缀路由。不带 tag 就是默认 session,与旧行为一致。

创建 & 切换

/new #docs        新开一个标签为 docs 的会话(tmux 窗口名也叫 docs)
/new #api         再开一个,与 #docs 完全独立(独立 sessionId / jsonl / cwd)
/new              默认 session,老玩法

消息路由

只要消息文本里任意位置带 #tag(空白/句首/句尾分隔),就路由到那个 tagged session:

#docs 帮我把 README 的目录补一下
帮我看下这个报错 #api
/pwd #docs        → 只看 docs 会话的项目路径
/stop #api        → 只打断 api 会话
/clear #docs      → 只清 docs 会话上下文

不带 tag 的消息始终落到默认 session。

回复标识

tagged session 的每条回复自带 emoji #tag 前缀(emoji 由 tag 名 hash 决定,稳定),一眼分辨来自哪个 session:

🦊 `#docs`

(这里是 docs 会话的 Claude 回复……)

默认 session 无前缀,视觉上保持简洁。

tag 语法[\p{L}\p{N}_-]{1,32},支持中英文数字与 _-;一条消息里只识别第一个 #tag,后续的 #foo 原样透传给 Claude(不会误伤代码里的 #include 或 issue 引用)。

cwd 是聊天级的,不是 session 级:同一聊天里所有 tagged / 默认 session 共用一个 cwd。/new #foo 会在当前聊天绑定的 cwd 下起 pane;任意 session 里让 AI 调 cd MCP,写入的 pendingCwd 也挂在聊天层,下次 /new(无论带不带 tag)都会读到。这样多 session 天然对齐到同一个项目根,切换 tag 时不用重新指路径。


多 CLI 后端(claude / claude-internal / codebuddy

daemon 同时挂载所有已安装的 CLI,不是二选一:你可以一个 tmux 窗口跑 claude、另一个跑 codebuddy,各自绑不同的 IM 聊天。会话身份就是它的 jsonl 路径,daemon 由路径反推是哪个 CLI 写的,--resume 用哪个二进制、jsonl 用哪套 schema 解析、project-dir 怎么编码,全部由此派生。

/new                 沿用「当前会话」的 CLI 新开
/new codebuddy       换到 codebuddy 新开
/new claude-internal 换到 claude-internal 新开

默认后端由 wrc.defaultCli 决定(缺省 claude),二进制路径可用 wrc.cliBackends.<name>.bin 覆盖。

#tag 完全正交,两者可以任意组合、顺序不限:

/new codebuddy #docs    用 codebuddy 起一个 docs 标签会话
/new #docs codebuddy    等价写法
#docs 帮我改 README      → 路由到那个 codebuddy 会话
/clear #docs            → 只清它,且仍留在 codebuddy 上

切换 CLI 后 tag 路由的所有行为都保持不变:

  • /clear #tag rotate 出的新 jsonl 仍落在该 CLI 的 projects 目录,watcher 按该后端的 dialect 迁移绑定;
  • pane 挂了自愈 --resume 用的是该会话所属的二进制,不会串到 defaultCli
  • 首次 /new #tag 没有自己的历史记录时,继承本聊天基础会话的 CLI(与 cwd 的聊天级继承规则一致),不会悄悄退回默认后端;
  • /sessions 列表在混用多个 CLI 时,每行自动标注 (codebuddy) 之类的来源。

常用命令

IM 里发 /help 可随时拉出完整命令表;每次 /new/clear 之后,回执会随机附一条功能提示,用来慢慢摊开命令面。

/new · /clear · /stop · /n          会话控制
/sessions [emoji|id]                 列出 / 切换 live 会话
/new <cli> [#tag]                    切换 CLI 后端 / 开并行会话
/id · /pwd · /usage · /cost · /audit  信息查询(免授权)
/help                                全部命令

本机 shell:

weclaude status              # 看 daemon + WS 健康
weclaude logs -f             # 实时日志
weclaude send <chat> <text>  # 主动推消息
weclaude reload              # 重启 daemon(改了配置后用)
weclaude unsync              # 卸载 hook/MCP(保留 daemon)
weclaude uninstall           # 完整卸载(先于 npm uninstall)

⚠️ 卸载顺序:先 weclaude uninstallnpm uninstall -g weclaude。否则 launchd/systemd 会一直尝试拉起已删除的二进制。~/.weclaude/ 下的 config/secrets 不会被清,二次安装可无缝复用。


常见问题

Q: hook 不触发? cat ~/.claude/settings.json | jq .hooks.PreToolUse,没东西就跑 weclaude sync 重写。

Q: 卡片点了没反应? 企业微信卡片就地刷新只有 5 秒窗口,超时不刷新是正常的,决策本身仍然生效。

Q: daemon 起不来? weclaude logs -f 看;常见是 botId / secret 写错卡在 WebSocket 鉴权。

Q: 多机部署? config.jsonc 可以纳入 dotfiles;secrets.json 每台机器独立填。第二台机器跑 weclaude init 会跳过覆盖提示,但仍要重新走 claim 步骤拿本机 IM principal。


架构一瞥

三个进程走 127.0.0.1:17890 + ~/.weclaude/

 WeCom ── WebSocket ──► daemon (常驻, launchd/systemd)
                          │
                          ├── HTTP :17890 ◄── hook (pre-tool-use.sh, 长轮询)
                          ├── HTTP :17890 ◄── MCP server (claude 的 stdio 子进程)
                          └── spawn `claude -p` (headless)  或  tail jsonl + tmux paste (mirror)

daemon 是唯一持有 WeCom WS 连接的进程,hook 和 MCP 都是它的薄 HTTP 客户端。

详情看 CLAUDE.md

License

MIT