wezard 0.3.3

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 (117) hide show
  1. package/.claude-plugin/marketplace.json +14 -0
  2. package/.claude-plugin/plugin.json +14 -0
  3. package/CLAUDE.md +83 -0
  4. package/LICENSE +21 -0
  5. package/README.md +410 -0
  6. package/cli/weclaude.sh +236 -0
  7. package/commands/audit.md +7 -0
  8. package/commands/wrc.md +7 -0
  9. package/config.example.jsonc +102 -0
  10. package/dist/cli/audit.js +156 -0
  11. package/dist/cli/audit.js.map +1 -0
  12. package/dist/cli/init.js +311 -0
  13. package/dist/cli/init.js.map +1 -0
  14. package/dist/cli/sync.js +175 -0
  15. package/dist/cli/sync.js.map +1 -0
  16. package/dist/daemon/approval.js +1301 -0
  17. package/dist/daemon/approval.js.map +1 -0
  18. package/dist/daemon/ask.js +101 -0
  19. package/dist/daemon/ask.js.map +1 -0
  20. package/dist/daemon/audit.js +168 -0
  21. package/dist/daemon/audit.js.map +1 -0
  22. package/dist/daemon/cc-bridge.js +204 -0
  23. package/dist/daemon/cc-bridge.js.map +1 -0
  24. package/dist/daemon/cfg-sync.js +240 -0
  25. package/dist/daemon/cfg-sync.js.map +1 -0
  26. package/dist/daemon/claim.js +95 -0
  27. package/dist/daemon/claim.js.map +1 -0
  28. package/dist/daemon/detail.js +153 -0
  29. package/dist/daemon/detail.js.map +1 -0
  30. package/dist/daemon/graph.js +176 -0
  31. package/dist/daemon/graph.js.map +1 -0
  32. package/dist/daemon/http.js +83 -0
  33. package/dist/daemon/http.js.map +1 -0
  34. package/dist/daemon/inbound.js +919 -0
  35. package/dist/daemon/inbound.js.map +1 -0
  36. package/dist/daemon/index.js +569 -0
  37. package/dist/daemon/index.js.map +1 -0
  38. package/dist/daemon/last-response.js +59 -0
  39. package/dist/daemon/last-response.js.map +1 -0
  40. package/dist/daemon/mirror-bridge.js +3512 -0
  41. package/dist/daemon/mirror-bridge.js.map +1 -0
  42. package/dist/daemon/mirror-store.js +39 -0
  43. package/dist/daemon/mirror-store.js.map +1 -0
  44. package/dist/daemon/outbound.js +42 -0
  45. package/dist/daemon/outbound.js.map +1 -0
  46. package/dist/daemon/peers.js +146 -0
  47. package/dist/daemon/peers.js.map +1 -0
  48. package/dist/daemon/pending.js +90 -0
  49. package/dist/daemon/pending.js.map +1 -0
  50. package/dist/daemon/quota.js +164 -0
  51. package/dist/daemon/quota.js.map +1 -0
  52. package/dist/daemon/redact.js +27 -0
  53. package/dist/daemon/redact.js.map +1 -0
  54. package/dist/daemon/session-cache.js +149 -0
  55. package/dist/daemon/session-cache.js.map +1 -0
  56. package/dist/daemon/session-scan.js +273 -0
  57. package/dist/daemon/session-scan.js.map +1 -0
  58. package/dist/daemon/sessions.js +35 -0
  59. package/dist/daemon/sessions.js.map +1 -0
  60. package/dist/daemon/spawn-tmux.js +213 -0
  61. package/dist/daemon/spawn-tmux.js.map +1 -0
  62. package/dist/daemon/tips.js +25 -0
  63. package/dist/daemon/tips.js.map +1 -0
  64. package/dist/daemon/topics.js +197 -0
  65. package/dist/daemon/topics.js.map +1 -0
  66. package/dist/daemon/usage.js +358 -0
  67. package/dist/daemon/usage.js.map +1 -0
  68. package/dist/daemon/wedoc.js +210 -0
  69. package/dist/daemon/wedoc.js.map +1 -0
  70. package/dist/daemon/ws.js +67 -0
  71. package/dist/daemon/ws.js.map +1 -0
  72. package/dist/mcp/server.js +385 -0
  73. package/dist/mcp/server.js.map +1 -0
  74. package/dist/shared/ansi.js +134 -0
  75. package/dist/shared/ansi.js.map +1 -0
  76. package/dist/shared/chat-http.js +195 -0
  77. package/dist/shared/chat-http.js.map +1 -0
  78. package/dist/shared/chat-render.js +53 -0
  79. package/dist/shared/chat-render.js.map +1 -0
  80. package/dist/shared/chat-view.js +116 -0
  81. package/dist/shared/chat-view.js.map +1 -0
  82. package/dist/shared/cli-backends.js +304 -0
  83. package/dist/shared/cli-backends.js.map +1 -0
  84. package/dist/shared/config-writer.js +39 -0
  85. package/dist/shared/config-writer.js.map +1 -0
  86. package/dist/shared/config.js +278 -0
  87. package/dist/shared/config.js.map +1 -0
  88. package/dist/shared/detail-render.js +836 -0
  89. package/dist/shared/detail-render.js.map +1 -0
  90. package/dist/shared/detail-store.js +205 -0
  91. package/dist/shared/detail-store.js.map +1 -0
  92. package/dist/shared/highlight.js +154 -0
  93. package/dist/shared/highlight.js.map +1 -0
  94. package/dist/shared/lan-ip.js +35 -0
  95. package/dist/shared/lan-ip.js.map +1 -0
  96. package/dist/shared/log.js +18 -0
  97. package/dist/shared/log.js.map +1 -0
  98. package/dist/shared/md-chunk.js +99 -0
  99. package/dist/shared/md-chunk.js.map +1 -0
  100. package/dist/shared/paths.js +12 -0
  101. package/dist/shared/paths.js.map +1 -0
  102. package/dist/shared/session-label.js +67 -0
  103. package/dist/shared/session-label.js.map +1 -0
  104. package/dist/shared/web-assets.js +51 -0
  105. package/dist/shared/web-assets.js.map +1 -0
  106. package/dist/svr/index.js +246 -0
  107. package/dist/svr/index.js.map +1 -0
  108. package/hooks/hooks.json +16 -0
  109. package/hooks/pre-tool-use.sh +167 -0
  110. package/launchd/com.weclaude.daemon.plist.template +45 -0
  111. package/package.json +97 -0
  112. package/scripts/install.sh +73 -0
  113. package/scripts/postinstall.sh +12 -0
  114. package/scripts/uninstall.sh +44 -0
  115. package/systemd/weclaude.service.template +17 -0
  116. package/web/chat.css +171 -0
  117. package/web/chat.js +367 -0
@@ -0,0 +1,273 @@
1
+ // Scan the host for live `claude` sessions running inside tmux, and summarize
2
+ // what each is doing from its transcript tail. Used by the /sessions/* routes
3
+ // so a WeCom-side Claude can list / switch / describe sibling sessions.
4
+ //
5
+ // Why daemon-side: an MCP tool runs *inside one* claude process and can only
6
+ // see its own $TMUX_PANE / cwd / sessionId. Correlating *other* sessions to
7
+ // their tmux pane (required for IM→CLI inject) needs a host-wide /proc + tmux
8
+ // sweep, which only the resident daemon is positioned to do.
9
+ //
10
+ // Scope: tmux-hosted sessions only (a session with no pane can't receive
11
+ // pasted input, so it's not a useful mirror target). Linux-only (reads /proc);
12
+ // returns [] elsewhere, degrading gracefully.
13
+ import { spawn } from "node:child_process";
14
+ import { readdirSync, readFileSync, existsSync, statSync, readlinkSync } from "node:fs";
15
+ import { join, basename } from "node:path";
16
+ import { activeBackends, backendForPath, projectDirsFor } from "../shared/cli-backends.js";
17
+ import { expandHome } from "../shared/paths.js";
18
+ import { labelFor } from "../shared/session-label.js";
19
+ import { summarizeTail } from "./peers.js";
20
+ const dirnameOfNode = () => {
21
+ const i = process.execPath.lastIndexOf("/");
22
+ return i > 0 ? process.execPath.slice(0, i) : "";
23
+ };
24
+ const augmentedPath = (orig) => {
25
+ const extras = [dirnameOfNode(), "/opt/homebrew/bin", "/usr/local/bin", `${process.env.HOME ?? ""}/.local/bin`].filter(Boolean);
26
+ const seen = new Set();
27
+ return [orig ?? "", ...extras]
28
+ .flatMap((p) => p.split(":"))
29
+ .filter((p) => p && !seen.has(p) && (seen.add(p), true))
30
+ .join(":");
31
+ };
32
+ const runTmux = (args) => new Promise((resolve) => {
33
+ const p = spawn("tmux", args, {
34
+ env: { ...process.env, PATH: augmentedPath(process.env.PATH) },
35
+ stdio: ["ignore", "pipe", "ignore"],
36
+ });
37
+ let out = "";
38
+ p.stdout?.on("data", (c) => (out += c.toString("utf8")));
39
+ p.on("error", () => resolve({ ok: false, stdout: "" }));
40
+ p.on("close", (code) => resolve({ ok: code === 0, stdout: out }));
41
+ });
42
+ // Read /proc/<pid>/stat → ppid. comm (2nd field) is wrapped in parens and may
43
+ // itself contain spaces/parens, so split on the LAST ')' and index from there:
44
+ // fields after comm are state ppid ... → ppid is index 1 of the tail.
45
+ const ppidOf = (pid) => {
46
+ try {
47
+ const stat = readFileSync(`/proc/${pid}/stat`, "utf8");
48
+ const rp = stat.lastIndexOf(")");
49
+ if (rp < 0)
50
+ return null;
51
+ const rest = stat.slice(rp + 2).split(" ");
52
+ const ppid = parseInt(rest[1] ?? "", 10);
53
+ return Number.isInteger(ppid) ? ppid : null;
54
+ }
55
+ catch {
56
+ return null;
57
+ }
58
+ };
59
+ const cmdlineOf = (pid) => {
60
+ try {
61
+ return readFileSync(`/proc/${pid}/cmdline`, "utf8").replace(/\0/g, " ").trim();
62
+ }
63
+ catch {
64
+ return "";
65
+ }
66
+ };
67
+ const cwdOf = (pid) => {
68
+ try {
69
+ return readlinkSync(`/proc/${pid}/cwd`);
70
+ }
71
+ catch {
72
+ return "";
73
+ }
74
+ };
75
+ // Pull a `--session-id <uuid>` out of a cmdline. Returns "" when absent
76
+ // (e.g. claude launched with bare `-r`/`--resume` — no explicit id to bind).
77
+ const sessionIdFromCmd = (cmd) => {
78
+ const m = cmd.match(/--session-id[= ]+([0-9a-fA-F-]{36})/);
79
+ return m?.[1] ?? "";
80
+ };
81
+ // A process belongs to an agent CLI if its cmdline mentions any active
82
+ // backend's binary name. Derived from the registry rather than hardcoded, so a
83
+ // custom `cliBackends.<name>.bin` is recognized too — without this, codebuddy
84
+ // panes were invisible to the scan and could never be listed or switched to.
85
+ const looksLikeAgentCli = (cmd) => activeBackends().some((b) => cmd.toLowerCase().includes(basename(b.bin).toLowerCase()));
86
+ // Transcript roots of every active backend, in registry order (primary first).
87
+ const projectRoots = () => activeBackends().map((b) => expandHome(b.projectsDir));
88
+ // Locate `<sessionId>.jsonl` (UUID is globally unique, so we don't have to
89
+ // reverse the cwd→dir encoding). Skips subagent transcripts (they live under a
90
+ // `<sid>/subagents/` subdir, never directly under a project dir).
91
+ const findJsonl = (sessionId) => {
92
+ const name = `${sessionId}.jsonl`;
93
+ for (const root of projectRoots()) {
94
+ if (!existsSync(root))
95
+ continue;
96
+ let subs;
97
+ try {
98
+ subs = readdirSync(root, { withFileTypes: true });
99
+ }
100
+ catch {
101
+ continue;
102
+ }
103
+ for (const d of subs) {
104
+ if (!d.isDirectory())
105
+ continue;
106
+ const p = join(root, d.name, name);
107
+ if (existsSync(p))
108
+ return p;
109
+ }
110
+ }
111
+ return "";
112
+ };
113
+ // cwd→projectDir encoding is backend-specific; projectDirsFor yields one
114
+ // candidate per active backend (primary first) using each one's own dialect.
115
+ // (The old local copies diverged — the Claude encoder here also mapped `_`,
116
+ // which does NOT match what Claude Code writes.)
117
+ // Expected jsonl path for a freshly-spawned session whose transcript doesn't
118
+ // exist on disk yet (a CLI only writes it after the first user input). Used so
119
+ // a brand-new `new_claude_session` still shows up in the list immediately.
120
+ const expectedJsonl = (cwd, sessionId) => {
121
+ if (!cwd || !sessionId)
122
+ return "";
123
+ const cands = projectDirsFor(cwd);
124
+ const hit = cands.find(({ dir }) => existsSync(dir)) ?? cands[0];
125
+ return hit ? join(hit.dir, `${sessionId}.jsonl`) : "";
126
+ };
127
+ // Resume-mode fallback: a claude launched with bare `-r`/`--resume` carries no
128
+ // `--session-id` on its cmdline. Infer the session from its cwd: the most
129
+ // recently written `<uuid>.jsonl` directly under that project dir. `taken`
130
+ // holds session ids already claimed by explicit-id processes so two sessions
131
+ // sharing a cwd don't both resolve to the same newest file.
132
+ const inferByCwd = (cwd, taken) => {
133
+ if (!cwd)
134
+ return null;
135
+ for (const { dir } of projectDirsFor(cwd)) {
136
+ if (!existsSync(dir))
137
+ continue;
138
+ let files;
139
+ try {
140
+ files = readdirSync(dir).filter((n) => n.endsWith(".jsonl"));
141
+ }
142
+ catch {
143
+ continue;
144
+ }
145
+ const cands = files
146
+ .map((n) => ({ sid: n.slice(0, -6), p: join(dir, n) }))
147
+ .filter((c) => !taken.has(c.sid))
148
+ .map((c) => ({
149
+ ...c,
150
+ m: (() => {
151
+ try {
152
+ return statSync(c.p).mtimeMs;
153
+ }
154
+ catch {
155
+ return 0;
156
+ }
157
+ })(),
158
+ }))
159
+ .sort((a, b) => b.m - a.m);
160
+ const top = cands[0];
161
+ if (top)
162
+ return { sessionId: top.sid, jsonlPath: top.p };
163
+ }
164
+ return null;
165
+ };
166
+ // Build pane_pid → {paneId, session}. We map the *pane's* root pid; the claude
167
+ // worker is a descendant, so we walk its ppid chain up to find a matching pane.
168
+ const tmuxPaneIndex = async () => {
169
+ const r = await runTmux(["list-panes", "-a", "-F", "#{pane_pid}|#{pane_id}|#{session_name}"]);
170
+ const idx = new Map();
171
+ if (!r.ok)
172
+ return idx;
173
+ for (const line of r.stdout.split("\n")) {
174
+ const [panePid, paneId, session] = line.split("|");
175
+ const pid = parseInt(panePid ?? "", 10);
176
+ if (Number.isInteger(pid) && paneId)
177
+ idx.set(pid, { paneId, session: session ?? "" });
178
+ }
179
+ return idx;
180
+ };
181
+ // Climb the ppid chain from `pid` until we hit a pid that owns a tmux pane.
182
+ // Bounded to avoid pathological loops.
183
+ const tmuxOwnerOf = (pid, paneIdx) => {
184
+ let cur = pid;
185
+ for (let i = 0; i < 40 && cur != null && cur > 1; i++) {
186
+ const hit = paneIdx.get(cur);
187
+ if (hit)
188
+ return hit;
189
+ const pp = ppidOf(cur);
190
+ if (pp == null || pp === cur)
191
+ break;
192
+ cur = pp;
193
+ }
194
+ return null;
195
+ };
196
+ export const scanClaudeSessions = async () => {
197
+ const paneIdx = await tmuxPaneIndex();
198
+ let pids;
199
+ try {
200
+ pids = readdirSync("/proc").filter((n) => /^\d+$/.test(n)).map((n) => parseInt(n, 10));
201
+ }
202
+ catch {
203
+ return []; // not Linux / no /proc
204
+ }
205
+ // Collect tmux-hosted claude workers, then collapse to ONE session per pane
206
+ // (a tmux pane runs exactly one claude session; a pane may host several
207
+ // claude processes — shim + worker + nested — which must not each count as
208
+ // a separate session). Per pane: prefer a process carrying an explicit
209
+ // `--session-id`; else fall back to one resume-mode inference by cwd.
210
+ const byPane = new Map();
211
+ for (const pid of pids) {
212
+ const cmd = cmdlineOf(pid);
213
+ if (!cmd || !looksLikeAgentCli(cmd))
214
+ continue;
215
+ const owner = tmuxOwnerOf(pid, paneIdx);
216
+ if (!owner)
217
+ continue; // tmux scope
218
+ const explicitSid = sessionIdFromCmd(cmd);
219
+ const prev = byPane.get(owner.paneId);
220
+ if (!prev || (!prev.explicitSid && explicitSid)) {
221
+ byPane.set(owner.paneId, { pid, owner, cwd: cwdOf(pid), explicitSid });
222
+ }
223
+ }
224
+ const seen = new Set();
225
+ const out = [];
226
+ const emit = (sessionId, jsonlPath, cwd, owner) => {
227
+ if (!sessionId || !jsonlPath || seen.has(sessionId))
228
+ return;
229
+ seen.add(sessionId);
230
+ let lastActivity = 0;
231
+ try {
232
+ lastActivity = statSync(jsonlPath).mtimeMs;
233
+ }
234
+ catch {
235
+ /* fresh session: no transcript yet */
236
+ }
237
+ out.push({
238
+ sessionId,
239
+ label: labelFor(sessionId),
240
+ cwd,
241
+ jsonlPath,
242
+ tmuxSession: owner.session,
243
+ tmuxPane: owner.paneId,
244
+ lastActivity,
245
+ summary: summarizeTail(jsonlPath),
246
+ cli: backendForPath(jsonlPath).name,
247
+ });
248
+ };
249
+ const panes = Array.from(byPane.values());
250
+ // Pass 1: explicit `--session-id` — exact, no ambiguity. Seeds `seen` so
251
+ // resume inference in pass 2 can't re-claim these ids. A freshly-spawned
252
+ // session may not have written its jsonl yet — fall back to the expected
253
+ // path so it still appears (summary reads as a new/empty session).
254
+ for (const w of panes) {
255
+ if (!w.explicitSid)
256
+ continue;
257
+ const jsonlPath = findJsonl(w.explicitSid) || expectedJsonl(w.cwd, w.explicitSid);
258
+ if (jsonlPath)
259
+ emit(w.explicitSid, jsonlPath, w.cwd, w.owner);
260
+ }
261
+ // Pass 2: resume-mode (`-r`/`--resume`, no id) — infer via cwd→newest jsonl,
262
+ // skipping any session already claimed in pass 1.
263
+ for (const w of panes) {
264
+ if (w.explicitSid)
265
+ continue;
266
+ const inf = inferByCwd(w.cwd, seen);
267
+ if (inf)
268
+ emit(inf.sessionId, inf.jsonlPath, w.cwd, w.owner);
269
+ }
270
+ out.sort((a, b) => b.lastActivity - a.lastActivity);
271
+ return out;
272
+ };
273
+ //# sourceMappingURL=session-scan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-scan.js","sourceRoot":"","sources":["../../daemon/session-scan.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ // Persisted principal → claude session_id map. Single JSON file, write-through.
2
+ import { readFileSync, writeFileSync, existsSync, mkdirSync } from "node:fs";
3
+ import { dirname } from "node:path";
4
+ import { expandHome } from "../shared/paths.js";
5
+ export const loadSessionStore = (filePath) => {
6
+ const abs = expandHome(filePath);
7
+ let map = {};
8
+ if (existsSync(abs)) {
9
+ try {
10
+ map = JSON.parse(readFileSync(abs, "utf8"));
11
+ }
12
+ catch {
13
+ map = {};
14
+ }
15
+ }
16
+ else {
17
+ mkdirSync(dirname(abs), { recursive: true });
18
+ }
19
+ const persist = () => {
20
+ writeFileSync(abs, JSON.stringify(map, null, 2), "utf8");
21
+ };
22
+ return {
23
+ get: (p) => map[p],
24
+ set: (p, sid) => {
25
+ map[p] = sid;
26
+ persist();
27
+ },
28
+ drop: (p) => {
29
+ delete map[p];
30
+ persist();
31
+ },
32
+ all: () => ({ ...map }),
33
+ };
34
+ };
35
+ //# sourceMappingURL=sessions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessions.js","sourceRoot":"","sources":["../../daemon/sessions.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAShD,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,QAAgB,EAAgB,EAAE;IACjE,MAAM,GAAG,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;IACjC,IAAI,GAAG,GAA2B,EAAE,CAAC;IACrC,IAAI,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACpB,IAAI,CAAC;YACH,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,GAAG,EAAE,MAAM,CAAC,CAA2B,CAAC;QACxE,CAAC;QAAC,MAAM,CAAC;YACP,GAAG,GAAG,EAAE,CAAC;QACX,CAAC;IACH,CAAC;SAAM,CAAC;QACN,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC/C,CAAC;IAED,MAAM,OAAO,GAAG,GAAS,EAAE;QACzB,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;IAC3D,CAAC,CAAC;IAEF,OAAO;QACL,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;QAClB,GAAG,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE;YACd,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC;YACb,OAAO,EAAE,CAAC;QACZ,CAAC;QACD,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE;YACV,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC;YACd,OAAO,EAAE,CAAC;QACZ,CAAC;QACD,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,GAAG,GAAG,EAAE,CAAC;KACxB,CAAC;AACJ,CAAC,CAAC"}
@@ -0,0 +1,213 @@
1
+ // Auto-spawn helper for mirror mode.
2
+ //
3
+ // Goal: from a WeCom inbound, materialize a fresh `claude` process inside a
4
+ // shared tmux session and attach the resulting jsonl to the WeCom chat —
5
+ // no human-in-the-loop `/wrc` needed.
6
+ //
7
+ // Layout: ONE shared tmux session named `cfg.wrc.tmuxPrefix` (default
8
+ // `weclaude`); each chat gets its own window inside it. The pane id (`%N`)
9
+ // uniquely identifies a chat's claude process — pane ids are monotonic per
10
+ // tmux server lifetime, so they're safe to persist and re-validate via
11
+ // `display-message -t %N`. Window names are cosmetic (sanitized principal id;
12
+ // WeCom doesn't expose a display name).
13
+ //
14
+ // Pipeline:
15
+ // uuid = randomUUID (deterministic session)
16
+ // tmux has-session -t weclaude → create or reuse
17
+ // tmux new-window/new-session -P -F '#{pane_id}' … (→ paneId)
18
+ // tmux send-keys -t %paneId "claudeBin --session-id <uuid> …" Enter
19
+ //
20
+ // We do NOT wait for claude to write anything to the jsonl. The jsonl is the
21
+ // transcript file, populated as claude runs; the empty file is enough for
22
+ // MirrorBridge.attach() (existsSync) and startMirrorTail (statSync.size=0 →
23
+ // tail from offset 0). The brief post-launch settle exists only to give the
24
+ // pane's shell time to finish sourcing rc + claude TUI time to grab the pty
25
+ // before the first paste-buffer inject hits.
26
+ import { spawn } from "node:child_process";
27
+ import { randomUUID } from "node:crypto";
28
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
29
+ import { homedir } from "node:os";
30
+ import { basename, dirname, join } from "node:path";
31
+ import { expandHome } from "../shared/paths.js";
32
+ import { activateBackend, CLI_BACKEND_DEFAULTS, primaryBackend } from "../shared/cli-backends.js";
33
+ const NODE_BIN_DIR = dirname(process.execPath);
34
+ const augmentedPath = (orig) => {
35
+ const extras = [NODE_BIN_DIR, "/opt/homebrew/bin", "/usr/local/bin", `${process.env.HOME ?? ""}/.local/bin`].filter(Boolean);
36
+ const seen = new Set();
37
+ return [orig ?? "", ...extras]
38
+ .flatMap((p) => p.split(":"))
39
+ .filter((p) => p && !seen.has(p) && (seen.add(p), true))
40
+ .join(":");
41
+ };
42
+ // A spawn has no transcript to derive a backend from, so the caller picks one
43
+ // (`cli`) and we fall back to the primary (`wrc.defaultCli`). Everything the
44
+ // pane needs — binary, transcript root, project-dir encoding, trust-marker file
45
+ // — comes from that single descriptor, which is what lets one daemon host
46
+ // claude and codebuddy panes at the same time.
47
+ const backendFor = (cfg, cli) => {
48
+ const primary = primaryBackend();
49
+ if (!cli || cli === primary.name)
50
+ return primary;
51
+ const base = CLI_BACKEND_DEFAULTS[cli];
52
+ const override = cfg.wrc.cliBackends?.[cli]?.bin;
53
+ // Activate before spawning: this CLI's transcript root may not exist yet, in
54
+ // which case the boot-time probe skipped it and backendForPath would misread
55
+ // the jsonl we are about to create as the primary's dialect.
56
+ return activateBackend(override ? { ...base, bin: override } : base);
57
+ };
58
+ export const runTmux = (args) => new Promise((resolve) => {
59
+ const proc = spawn("tmux", args, {
60
+ env: { ...process.env, PATH: augmentedPath(process.env.PATH) },
61
+ stdio: ["ignore", "pipe", "pipe"],
62
+ });
63
+ let out = "";
64
+ let err = "";
65
+ proc.stdout?.on("data", (c) => (out += c.toString("utf8")));
66
+ proc.stderr?.on("data", (c) => (err += c.toString("utf8")));
67
+ proc.on("error", (e) => resolve({ ok: false, stdout: "", stderr: e.message, code: null }));
68
+ proc.on("close", (code) => resolve({ ok: code === 0, stdout: out, stderr: err, code }));
69
+ });
70
+ const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
71
+ // Single-quote shell-escape: wrap in '…' and escape embedded ' as '\''.
72
+ const shQuote = (a) => (/[\s"'`$\\]/.test(a) ? `'${a.replace(/'/g, "'\\''")}'` : a);
73
+ // tmux window names are free-form but a status-bar friendly slug avoids
74
+ // surprises (no whitespace / colons / quoting hazards). Principals are
75
+ // shortened: `user:foo` → `u-foo`, `chat:foo` → `c-foo`, and get truncated to
76
+ // 8 chars total (status bar real estate is scarce; collisions don't matter —
77
+ // we always address windows by pane id, never by name). Explicit tag names
78
+ // (not `user:`/`chat:` prefixed) are treated as user-authored labels and kept
79
+ // up to 24 chars so `/new #my-tag` shows readably in the tmux status bar.
80
+ const safeWindowName = (s) => {
81
+ const isPrincipal = s.startsWith("user:") || s.startsWith("chat:");
82
+ const compact = s.startsWith("user:") ? `u-${s.slice(5)}`
83
+ : s.startsWith("chat:") ? `c-${s.slice(5)}`
84
+ : s;
85
+ const slug = compact.replace(/[^A-Za-z0-9_.\-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
86
+ return (slug || "claude").slice(0, isPrincipal ? 8 : 24);
87
+ };
88
+ // Time the daemon waits between launching claude and returning. Covers shell
89
+ // rc sourcing + claude TUI grabbing the pty + reaching interactive state so
90
+ // the very next paste-buffer inject lands inside claude's input box AND the
91
+ // trailing Enter is honored as submit (not eaten by the startup banner).
92
+ // 1.5s used to leave Enter racing with TUI init — text would land but the
93
+ // submit got dropped, leaving the prompt typed-but-unsent.
94
+ const TUI_SETTLE_MS = 3000;
95
+ const claudeConfigCandidates = (claudeBin) => {
96
+ const home = homedir();
97
+ const name = basename(claudeBin);
98
+ if (name === "claude")
99
+ return [join(home, ".claude.json")];
100
+ return [join(home, `.${name}`, ".claude.json"), join(home, `.${name}.json`)];
101
+ };
102
+ export const trustWorkspace = (claudeBin, cwd, log) => {
103
+ const candidates = claudeConfigCandidates(claudeBin);
104
+ const target = candidates.find(existsSync) ?? candidates[0];
105
+ let cfg = {};
106
+ try {
107
+ if (existsSync(target))
108
+ cfg = JSON.parse(readFileSync(target, "utf8"));
109
+ }
110
+ catch (e) {
111
+ log.warn({ target, err: e.message }, "trustWorkspace: parse failed; rewriting");
112
+ }
113
+ const projects = (cfg.projects ??= {});
114
+ const proj = projects[cwd] ?? {};
115
+ if (proj.hasTrustDialogAccepted && (proj.projectOnboardingSeenCount ?? 0) >= 1 && proj.hasClaudeMdExternalIncludesApproved) {
116
+ return; // already trusted — no write needed
117
+ }
118
+ projects[cwd] = {
119
+ ...proj,
120
+ hasTrustDialogAccepted: true,
121
+ projectOnboardingSeenCount: Math.max(proj.projectOnboardingSeenCount ?? 0, 1),
122
+ hasClaudeMdExternalIncludesApproved: true,
123
+ hasClaudeMdExternalIncludesWarningShown: true,
124
+ };
125
+ try {
126
+ mkdirSync(dirname(target), { recursive: true });
127
+ writeFileSync(target, JSON.stringify(cfg, null, 2));
128
+ log.info({ target, cwd }, "trustWorkspace: marker written");
129
+ }
130
+ catch (e) {
131
+ log.warn({ target, err: e.message }, "trustWorkspace: write failed");
132
+ }
133
+ };
134
+ export const spawnTmuxClaude = async ({ cfg, log, resumeSessionId, windowName, cwdOverride, cli, model }) => {
135
+ const backend = backendFor(cfg, cli);
136
+ const cwd = expandHome((cwdOverride ?? "").trim() || cfg.wrc.cwd);
137
+ const projectDir = join(expandHome(backend.projectsDir), backend.encodeProjectDir(cwd));
138
+ const sessionId = resumeSessionId ?? randomUUID();
139
+ const jsonlPath = join(projectDir, `${sessionId}.jsonl`);
140
+ const tmuxName = cfg.wrc.tmuxPrefix; // shared session for all chats
141
+ const winName = safeWindowName(windowName ?? sessionId);
142
+ // Probe tmux availability up front — clearer error than a generic spawn fail.
143
+ const probe = await runTmux(["-V"]);
144
+ if (!probe.ok)
145
+ return { ok: false, reason: `tmux not available: ${probe.stderr.trim() || probe.code}` };
146
+ // Ensure cwd / projectDir exist (tmux `new-session -c` fails if cwd missing;
147
+ // default `~/.weclaude/workspace` often hasn't been touched yet). Do NOT
148
+ // pre-create the jsonl: `claude --session-id <uuid>` refuses to start when
149
+ // the transcript file already exists ("session id is already in use"). We
150
+ // poll for it after spawn instead.
151
+ try {
152
+ mkdirSync(cwd, { recursive: true });
153
+ mkdirSync(projectDir, { recursive: true });
154
+ }
155
+ catch (e) {
156
+ return { ok: false, reason: `prepare cwd failed: ${e.message}` };
157
+ }
158
+ // Pre-trust the workspace so claude TUI doesn't park on the "Do you trust
159
+ // this folder?" / onboarding prompts — those prompts swallow paste-buffer
160
+ // injects and the approval card never fires. Best-effort: log + continue
161
+ // on failure (worst case the user clicks through the prompts manually).
162
+ trustWorkspace(backend.bin, cwd, log);
163
+ // Reuse the shared session if alive; otherwise create it. Either way, end
164
+ // up with a fresh window whose pane id we capture via `-P -F '#{pane_id}'`.
165
+ //
166
+ // Seed the new pane's environment (`-e`, tmux ≥3.0) BEFORE its shell sources
167
+ // rc: oh-my-zsh runs its update check while sourcing .zshrc and, when due,
168
+ // blocks on `[oh-my-zsh] Would you like to update? [Y/n]`. That prompt eats
169
+ // the `claude …` command line + the trailing Enter, so claude never launches
170
+ // and the injected message falls through to a bare shell (`command not
171
+ // found: hi`). These two legacy vars (still honored for back-compat) disable
172
+ // the prompt regardless of the user's zstyle config.
173
+ const paneEnv = ["-e", "DISABLE_AUTO_UPDATE=true", "-e", "DISABLE_UPDATE_PROMPT=true"];
174
+ const has = await runTmux(["has-session", "-t", tmuxName]);
175
+ const created = has.code === 0
176
+ // `${tmuxName}:` (trailing colon) forces session-only resolution → next free
177
+ // window index. Bare `-t weclaude` is ambiguous: if a *window* is also named
178
+ // `weclaude`, tmux matches it and tries to reuse its index → "index N in use".
179
+ ? await runTmux(["new-window", "-d", "-t", `${tmuxName}:`, "-n", winName, "-c", cwd, ...paneEnv, "-P", "-F", "#{pane_id}"])
180
+ : await runTmux(["new-session", "-d", "-s", tmuxName, "-n", winName, "-c", cwd, ...paneEnv, "-P", "-F", "#{pane_id}"]);
181
+ if (!created.ok) {
182
+ const verb = has.code === 0 ? "new-window" : "new-session";
183
+ return { ok: false, reason: `tmux ${verb} failed: ${created.stderr.trim() || created.code}` };
184
+ }
185
+ const tmuxPane = created.stdout.split("\n").map((s) => s.trim()).filter(Boolean)[0] ?? "";
186
+ if (!tmuxPane) {
187
+ return { ok: false, reason: "tmux returned no pane id" };
188
+ }
189
+ log.info({ tmuxName, winName, tmuxPane, cwd, sessionId, cli: backend.name, reused: has.code === 0 }, "spawn-tmux: pane created");
190
+ // DISABLE_AUTOUPDATER=1 防止新 pane 启动时弹出 "An update is available" 询问 ——
191
+ // 该交互会吞掉首条 paste-buffer 注入,导致仅落字符不进入 claude 输入框。
192
+ const cmd = [
193
+ "DISABLE_AUTOUPDATER=1",
194
+ backend.bin,
195
+ ...(resumeSessionId ? ["--resume", sessionId] : ["--session-id", sessionId]),
196
+ ...(model?.trim() ? ["--model", model.trim()] : []),
197
+ ...cfg.wrc.extraArgs,
198
+ ].map((a, i) => (i === 0 ? a : shQuote(a))).join(" ");
199
+ const sent = await runTmux(["send-keys", "-t", tmuxPane, cmd, "Enter"]);
200
+ if (!sent.ok) {
201
+ // Kill only this window/pane, never the shared session.
202
+ await runTmux(["kill-pane", "-t", tmuxPane]);
203
+ return { ok: false, reason: `tmux send-keys failed: ${sent.stderr.trim() || sent.code}` };
204
+ }
205
+ // Settle so the next paste-buffer inject doesn't race claude's TUI startup.
206
+ // Note: claude does NOT create the transcript jsonl until it processes the
207
+ // first user input, so we don't wait for the file here — mirror tail tolerates
208
+ // a missing jsonl and starts emitting once claude writes the first line.
209
+ await sleep(TUI_SETTLE_MS);
210
+ log.info({ tmuxName, tmuxPane, sessionId, jsonlPath, cwd, cli: backend.name }, "spawn-tmux: ready");
211
+ return { ok: true, sessionId, jsonlPath, tmuxPane, tmuxSession: tmuxName, cwd, cli: backend.name };
212
+ };
213
+ //# sourceMappingURL=spawn-tmux.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spawn-tmux.js","sourceRoot":"","sources":["../../daemon/spawn-tmux.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ // Rotating one-line hints appended to the `/new` / `/clear` project-info push.
2
+ //
3
+ // Why random-one instead of a fixed "输入 /help": a static line becomes visual
4
+ // noise within a day and stops being read, while the command surface is wide
5
+ // enough that most users never discover half of it. Sampling one tip per
6
+ // session boundary keeps the footer cheap (one line) but eventually exposes
7
+ // everything. `/help` is in the pool too, so the discoverability entry point
8
+ // still surfaces on its own.
9
+ const TIPS = [
10
+ "`/help` 查看全部命令",
11
+ "`#tag` 前缀可在同一聊天并行跑多个会话,如 `#docs 帮我改 README`",
12
+ "`/sessions` 列出所有 live 会话,`/sessions 🐼` 切过去",
13
+ "`/new codebuddy` 换个 CLI 后端开会话(claude / claude-internal / codebuddy)",
14
+ "`/stop` 打断当前生成,`/n` 补一个回车",
15
+ "`/usage` 看真实订阅额度 %,`/cost` 看 token 成本估算",
16
+ "`/audit` 看本会话(含 subagent)的 token / 成本明细",
17
+ "换项目:让 AI 调 `cd` MCP 工具,然后 `/new` 或 `/clear` 生效",
18
+ "直接发图片即可,会自动粘进 CLI 输入框",
19
+ "引用自己发过的命令再发一次,可以绕开微信的相同文本去重",
20
+ "`订阅 <topic>` + `每天 08:30 广播 <topic> <内容>` 可做定时播报",
21
+ "`/pwd` 看当前项目路径,`/id` 看会话 / 权限 id",
22
+ ];
23
+ /** One random tip, already formatted as a markdown quote line. */
24
+ export const randomTip = () => `> 💡 ${TIPS[Math.floor(Math.random() * TIPS.length)]}`;
25
+ //# sourceMappingURL=tips.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tips.js","sourceRoot":"","sources":["../../daemon/tips.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,EAAE;AACF,6EAA6E;AAC7E,6EAA6E;AAC7E,yEAAyE;AACzE,4EAA4E;AAC5E,6EAA6E;AAC7E,6BAA6B;AAE7B,MAAM,IAAI,GAAsB;IAC9B,gBAAgB;IAChB,6CAA6C;IAC7C,6CAA6C;IAC7C,qEAAqE;IACrE,2BAA2B;IAC3B,yCAAyC;IACzC,yCAAyC;IACzC,gDAAgD;IAChD,uBAAuB;IACvB,6BAA6B;IAC7B,kDAAkD;IAClD,kCAAkC;CACnC,CAAC;AAEF,kEAAkE;AAClE,MAAM,CAAC,MAAM,SAAS,GAAG,GAAW,EAAE,CAAC,QAAQ,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,CAAE,EAAE,CAAC"}