weclaude 0.0.4 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +105 -28
  3. package/cli/{wrc.sh → weclaude.sh} +34 -18
  4. package/commands/wrc.md +4 -4
  5. package/config.example.jsonc +6 -6
  6. package/dist/cli/init.js +10 -10
  7. package/dist/cli/init.js.map +1 -1
  8. package/dist/cli/sync.js +35 -18
  9. package/dist/cli/sync.js.map +1 -1
  10. package/dist/daemon/approval.js +487 -37
  11. package/dist/daemon/approval.js.map +1 -1
  12. package/dist/daemon/cc-bridge.js +37 -20
  13. package/dist/daemon/cc-bridge.js.map +1 -1
  14. package/dist/daemon/claim.js +20 -1
  15. package/dist/daemon/claim.js.map +1 -1
  16. package/dist/daemon/detail.js +500 -0
  17. package/dist/daemon/detail.js.map +1 -0
  18. package/dist/daemon/http.js +2 -1
  19. package/dist/daemon/http.js.map +1 -1
  20. package/dist/daemon/inbound.js +115 -21
  21. package/dist/daemon/inbound.js.map +1 -1
  22. package/dist/daemon/index.js +30 -8
  23. package/dist/daemon/index.js.map +1 -1
  24. package/dist/daemon/mirror-bridge.js +1010 -153
  25. package/dist/daemon/mirror-bridge.js.map +1 -1
  26. package/dist/daemon/mirror-store.js +39 -0
  27. package/dist/daemon/mirror-store.js.map +1 -0
  28. package/dist/daemon/pending.js +46 -0
  29. package/dist/daemon/pending.js.map +1 -1
  30. package/dist/daemon/session-cache.js +71 -3
  31. package/dist/daemon/session-cache.js.map +1 -1
  32. package/dist/daemon/spawn-tmux.js +132 -0
  33. package/dist/daemon/spawn-tmux.js.map +1 -0
  34. package/dist/mcp/server.js +104 -65
  35. package/dist/mcp/server.js.map +1 -1
  36. package/dist/shared/config-writer.js +1 -1
  37. package/dist/shared/config.js +34 -20
  38. package/dist/shared/config.js.map +1 -1
  39. package/dist/shared/paths.js +6 -0
  40. package/dist/shared/paths.js.map +1 -1
  41. package/docs/DESIGN-INIT.md +6 -6
  42. package/docs/ONBOARDING.md +25 -25
  43. package/hooks/pre-tool-use.sh +42 -7
  44. package/launchd/{com.cc-wecom.daemon.plist.template → com.weclaude.daemon.plist.template} +3 -3
  45. package/package.json +10 -11
  46. package/scripts/install.sh +6 -6
  47. package/scripts/uninstall.sh +3 -3
  48. package/systemd/{cc-wecom.service.template → weclaude.service.template} +3 -3
@@ -1,18 +1,53 @@
1
1
  #!/usr/bin/env bash
2
- # cc-wecom PreToolUse hook: forward to local daemon → long-poll → emit decision.
2
+ # weclaude PreToolUse hook: forward to local daemon → long-poll → emit decision.
3
3
  # Any failure → ask (never break workflow).
4
4
  set -uo pipefail
5
5
 
6
- DAEMON_URL="${CC_WECOM_DAEMON_URL:-http://127.0.0.1:17890/approve}"
7
- HOOK_TIMEOUT="${CC_WECOM_HOOK_TIMEOUT:-1810}"
6
+ DAEMON_URL="${WECLAUDE_DAEMON_URL:-http://127.0.0.1:17890/approve}"
7
+ HOOK_TIMEOUT="${WECLAUDE_HOOK_TIMEOUT:-1810}"
8
+ STATE_DIR="${WECLAUDE_STATE_DIR:-$HOME/.weclaude/state}"
9
+ # Fallback policy when the daemon is unreachable / replies garbage. ask|allow|deny.
10
+ # Default keeps the safe behavior; set to `allow` in trusted local-only setups.
11
+ FALLBACK="${WECLAUDE_HOOK_FALLBACK:-ask}"
8
12
 
9
13
  emit() {
10
14
  local decision="$1" reason="${2:-}"
11
- printf '%s\n' "{\"hookSpecificOutput\":{\"hookEventName\":\"PreToolUse\",\"permissionDecision\":\"${decision}\",\"permissionDecisionReason\":\"cc-wecom: ${reason}\"}}"
15
+ # jq 拼 JSON: reason 可能含字面引号(askq deny 把答案塞进 reason),
16
+ # printf 直接拼会漏出内层 " 把 JSON 撕烂, Claude Code 解析失败 fallback
17
+ # 弹原生 picker → askq 失效。
18
+ if command -v jq >/dev/null 2>&1; then
19
+ jq -cn --arg d "$decision" --arg r "weclaude: $reason" \
20
+ '{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:$d,permissionDecisionReason:$r}}'
21
+ else
22
+ # jq 缺失兜底: 手动转义 \ 和 " (足够覆盖 reason 里的字面引号)。
23
+ local esc="${reason//\\/\\\\}"; esc="${esc//\"/\\\"}"
24
+ printf '%s\n' "{\"hookSpecificOutput\":{\"hookEventName\":\"PreToolUse\",\"permissionDecision\":\"${decision}\",\"permissionDecisionReason\":\"weclaude: ${esc}\"}}"
25
+ fi
12
26
  exit 0
13
27
  }
14
28
  ask() { emit "ask" "${1:-bridge unreachable}"; }
15
29
 
30
+ # Daemon-down fallback. Consults the persisted auto-approve window first so a
31
+ # session-level "allow N min" survives daemon restart / outage; otherwise falls
32
+ # back to FALLBACK. SESSION_ID must already be parsed.
33
+ bridge_down() {
34
+ local reason="$1"
35
+ local sf="$STATE_DIR/auto-windows.json"
36
+ if [[ -n "${SESSION_ID:-}" && -r "$sf" ]] && command -v jq >/dev/null 2>&1; then
37
+ local active
38
+ active=$(jq -r --arg s "$SESSION_ID" \
39
+ 'if (.windows[$s].until // 0) > (now * 1000) then "1" else "0" end' \
40
+ "$sf" 2>/dev/null) || active=""
41
+ if [[ "$active" == "1" ]]; then
42
+ emit "allow" "auto-window (offline): $reason"
43
+ fi
44
+ fi
45
+ case "$FALLBACK" in
46
+ allow|deny|ask) emit "$FALLBACK" "$reason" ;;
47
+ *) emit "ask" "$reason" ;;
48
+ esac
49
+ }
50
+
16
51
  PAYLOAD=$(cat) || ask "stdin read failed"
17
52
  command -v jq >/dev/null 2>&1 || ask "jq missing"
18
53
 
@@ -70,12 +105,12 @@ BODY=$(jq -nc \
70
105
  RESP=$(curl -sS --max-time "$HOOK_TIMEOUT" \
71
106
  -H 'Content-Type: application/json' \
72
107
  -d "$BODY" \
73
- "$DAEMON_URL" 2>/dev/null) || ask "daemon curl failed"
108
+ "$DAEMON_URL" 2>/dev/null) || bridge_down "daemon curl failed"
74
109
 
75
- DECISION=$(printf '%s' "$RESP" | jq -r '.decision // "ask"' 2>/dev/null) || ask "bad daemon response"
110
+ DECISION=$(printf '%s' "$RESP" | jq -r '.decision // "ask"' 2>/dev/null) || bridge_down "bad daemon response"
76
111
  REASON=$(printf '%s' "$RESP" | jq -r '.reason // ""' 2>/dev/null)
77
112
 
78
113
  case "$DECISION" in
79
114
  allow|deny|ask) emit "$DECISION" "$REASON" ;;
80
- *) ask "unknown decision: $DECISION" ;;
115
+ *) bridge_down "unknown decision: $DECISION" ;;
81
116
  esac
@@ -5,7 +5,7 @@
5
5
  <plist version="1.0">
6
6
  <dict>
7
7
  <key>Label</key>
8
- <string>com.cc-wecom.daemon</string>
8
+ <string>com.weclaude.daemon</string>
9
9
 
10
10
  <key>ProgramArguments</key>
11
11
  <array>
@@ -38,8 +38,8 @@
38
38
  <integer>10</integer>
39
39
 
40
40
  <key>StandardOutPath</key>
41
- <string>__HOME__/.cc-wecom/daemon.stdout.log</string>
41
+ <string>__HOME__/.weclaude/daemon.stdout.log</string>
42
42
  <key>StandardErrorPath</key>
43
- <string>__HOME__/.cc-wecom/daemon.stderr.log</string>
43
+ <string>__HOME__/.weclaude/daemon.stderr.log</string>
44
44
  </dict>
45
45
  </plist>
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "weclaude",
3
- "version": "0.0.4",
4
- "description": "WeCom remote control for Claude Code IM-side approval forwarding + remote /wrc",
3
+ "version": "0.1.1",
4
+ "description": "Bridge Claude Code and WeCom: PreToolUse approval cards + bidirectional tmux session mirror with image paste",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "keywords": [
@@ -14,14 +14,13 @@
14
14
  "mcp"
15
15
  ],
16
16
  "bin": {
17
- "wrc": "./cli/wrc.sh",
18
- "cc-wecom": "./cli/wrc.sh",
19
- "wrc-daemon": "./dist/daemon/index.js",
20
- "wrc-mcp": "./dist/mcp/server.js",
21
- "wrc-init": "./dist/cli/init.js"
17
+ "weclaude": "./cli/weclaude.sh",
18
+ "weclaude-daemon": "./dist/daemon/index.js",
19
+ "weclaude-mcp": "./dist/mcp/server.js",
20
+ "weclaude-init": "./dist/cli/init.js"
22
21
  },
23
22
  "files": [
24
- "cli/wrc.sh",
23
+ "cli/weclaude.sh",
25
24
  "dist/cli/init.js",
26
25
  "dist/cli/init.js.map",
27
26
  "dist/cli/sync.js",
@@ -36,8 +35,8 @@
36
35
  "hooks/hooks.json",
37
36
  "scripts/install.sh",
38
37
  "scripts/uninstall.sh",
39
- "launchd/com.cc-wecom.daemon.plist.template",
40
- "systemd/cc-wecom.service.template",
38
+ "launchd/com.weclaude.daemon.plist.template",
39
+ "systemd/weclaude.service.template",
41
40
  "config.example.jsonc",
42
41
  "commands/wrc.md",
43
42
  "README.md",
@@ -48,7 +47,7 @@
48
47
  "scripts": {
49
48
  "build": "tsc -p tsconfig.json && chmod +x dist/cli/init.js dist/daemon/index.js dist/mcp/server.js",
50
49
  "dev:daemon": "tsx daemon/index.ts",
51
- "dev:cli": "tsx cli/wrc.ts",
50
+ "dev:cli": "tsx cli/weclaude.ts",
52
51
  "typecheck": "tsc --noEmit",
53
52
  "prepublishOnly": "rm -rf dist && npm run build",
54
53
  "prepack": "test -f dist/daemon/index.js || npm run build"
@@ -5,7 +5,7 @@ set -euo pipefail
5
5
  REPO="$(cd "$(dirname "$0")/.." && pwd)"
6
6
  NODE="$(command -v node)"
7
7
  HOME_DIR="$HOME"
8
- LABEL="com.cc-wecom.daemon"
8
+ LABEL="com.weclaude.daemon"
9
9
 
10
10
  [[ -x "$NODE" ]] || { echo "node not found in PATH"; exit 1; }
11
11
 
@@ -15,7 +15,7 @@ if [[ ! -f "$REPO/dist/daemon/index.js" ]]; then
15
15
  (cd "$REPO" && npm install --silent && npx tsc -p tsconfig.json)
16
16
  fi
17
17
 
18
- mkdir -p "$HOME_DIR/.cc-wecom"
18
+ mkdir -p "$HOME_DIR/.weclaude"
19
19
 
20
20
  OS="$(uname -s)"
21
21
  case "$OS" in
@@ -33,18 +33,18 @@ case "$OS" in
33
33
  Linux)
34
34
  UNIT_DIR="$HOME_DIR/.config/systemd/user"
35
35
  mkdir -p "$UNIT_DIR"
36
- UNIT_DST="$UNIT_DIR/cc-wecom.service"
36
+ UNIT_DST="$UNIT_DIR/weclaude.service"
37
37
  sed \
38
38
  -e "s|__NODE__|$NODE|g" \
39
39
  -e "s|__REPO__|$REPO|g" \
40
40
  -e "s|__HOME__|$HOME_DIR|g" \
41
- "$REPO/systemd/cc-wecom.service.template" > "$UNIT_DST"
41
+ "$REPO/systemd/weclaude.service.template" > "$UNIT_DST"
42
42
  systemctl --user daemon-reload
43
- systemctl --user enable --now cc-wecom.service
43
+ systemctl --user enable --now weclaude.service
44
44
  echo "[install] systemd unit enabled: $UNIT_DST"
45
45
  ;;
46
46
  *)
47
47
  echo "unsupported OS: $OS"; exit 1 ;;
48
48
  esac
49
49
 
50
- echo "[install] done. Logs at $HOME_DIR/.cc-wecom/daemon.{stdout,stderr,log}"
50
+ echo "[install] done. Logs at $HOME_DIR/.weclaude/daemon.{stdout,stderr,log}"
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env bash
2
2
  set -euo pipefail
3
- LABEL="com.cc-wecom.daemon"
3
+ LABEL="com.weclaude.daemon"
4
4
  HOME_DIR="$HOME"
5
5
  OS="$(uname -s)"
6
6
 
@@ -14,9 +14,9 @@ case "$OS" in
14
14
  fi
15
15
  ;;
16
16
  Linux)
17
- UNIT="$HOME_DIR/.config/systemd/user/cc-wecom.service"
17
+ UNIT="$HOME_DIR/.config/systemd/user/weclaude.service"
18
18
  if [[ -f "$UNIT" ]]; then
19
- systemctl --user disable --now cc-wecom.service 2>/dev/null || true
19
+ systemctl --user disable --now weclaude.service 2>/dev/null || true
20
20
  rm -f "$UNIT"
21
21
  systemctl --user daemon-reload
22
22
  echo "[uninstall] removed $UNIT"
@@ -1,5 +1,5 @@
1
1
  [Unit]
2
- Description=cc-wecom daemon (WeCom remote control for Claude Code)
2
+ Description=weclaude daemon (WeCom remote control for Claude Code)
3
3
  After=network-online.target
4
4
  Wants=network-online.target
5
5
 
@@ -9,8 +9,8 @@ WorkingDirectory=__REPO__
9
9
  ExecStart=__NODE__ __REPO__/dist/daemon/index.js
10
10
  Restart=always
11
11
  RestartSec=5
12
- StandardOutput=append:__HOME__/.cc-wecom/daemon.stdout.log
13
- StandardError=append:__HOME__/.cc-wecom/daemon.stderr.log
12
+ StandardOutput=append:__HOME__/.weclaude/daemon.stdout.log
13
+ StandardError=append:__HOME__/.weclaude/daemon.stderr.log
14
14
  Environment=NODE_ENV=production
15
15
 
16
16
  [Install]