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,176 @@
1
+ // Loop graph: drive several tagged sessions in one chat as a pipeline.
2
+ //
3
+ // A graph binds `#tag` sessions (each free to run a different CLI / model /
4
+ // cwd) into an ordered list of steps, then walks that list `rounds` times:
5
+ //
6
+ // send(step.prompt) → wait for that agent to go idle → capture its reply
7
+ // → feed it forward as `{{last}}` into the next step
8
+ //
9
+ // It is a *loop* graph because the step list cycles: `fix → review → fix →
10
+ // review …` until the `until` sentinel shows up in a reply or the round budget
11
+ // runs out. That covers the useful shape (propose/critique, build/test,
12
+ // draft/edit) without inventing a scheduler — the ordering IS the graph.
13
+ //
14
+ // The runner owns no session state: every effect goes through injected deps
15
+ // backed by the mirror bridge, so a graph step is exactly what a human typing
16
+ // `#fix …` into WeCom would do. Runs live in memory only — a daemon reload
17
+ // cancels them, which is honest: the panes survive, the automation doesn't.
18
+ import { randomUUID } from "node:crypto";
19
+ // ── Pure helpers ──────────────────────────────────────────────────────
20
+ const targetOf = (base, tag) => (tag ? `${base}#${tag}` : base);
21
+ /** Substitute `{{…}}` refs from the accumulated reply context. Unknown keys are
22
+ * left verbatim — a prompt legitimately containing `{{foo}}` shouldn't be
23
+ * silently emptied. */
24
+ export const interpolate = (template, ctx) => template.replace(/\{\{\s*#?([\p{L}\p{N}_-]+)\s*\}\}/gu, (whole, key) => key in ctx ? ctx[key] : whole);
25
+ /** Reply satisfies the convergence sentinel. */
26
+ export const converged = (reply, until) => !!until && reply.toLowerCase().includes(until.toLowerCase());
27
+ const clamp = (n, lo, hi) => Math.min(hi, Math.max(lo, n));
28
+ // ── Idle detection ────────────────────────────────────────────────────
29
+ // After an inject the pane needs a moment to pick the text up, so "not busy"
30
+ // right away means nothing. Ramp: give the turn RAMP_MS to start; once we have
31
+ // seen it busy (or the ramp expires), require IDLE_CONFIRM consecutive quiet
32
+ // polls before declaring the turn finished — a single quiet sample lands in
33
+ // the gap between two tool calls often enough to matter.
34
+ const POLL_MS = 2500;
35
+ const RAMP_MS = 20_000;
36
+ const IDLE_CONFIRM = 3;
37
+ const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
38
+ export const waitForIdle = async (target, isBusy, timeoutMs, aborted) => {
39
+ const deadline = Date.now() + timeoutMs;
40
+ const rampUntil = Date.now() + RAMP_MS;
41
+ let sawBusy = false;
42
+ let quiet = 0;
43
+ while (Date.now() < deadline) {
44
+ if (aborted())
45
+ return { idle: false, reason: "stopped" };
46
+ await sleep(POLL_MS);
47
+ const busy = await isBusy(target);
48
+ if (busy) {
49
+ sawBusy = true;
50
+ quiet = 0;
51
+ continue;
52
+ }
53
+ // Still inside the ramp and the turn never started — keep waiting for it
54
+ // rather than mistaking "hasn't begun" for "already done".
55
+ if (!sawBusy && Date.now() < rampUntil)
56
+ continue;
57
+ if (++quiet >= IDLE_CONFIRM)
58
+ return { idle: true };
59
+ }
60
+ return { idle: false, reason: "idle wait timed out" };
61
+ };
62
+ // ── Runner ────────────────────────────────────────────────────────────
63
+ const runs = new Map();
64
+ const abortFlags = new Map();
65
+ export const getRun = (runId) => runs.get(runId);
66
+ export const listRuns = (base) => Array.from(runs.values()).filter((r) => !base || r.base === base);
67
+ export const stopRun = (runId) => {
68
+ const r = runs.get(runId);
69
+ if (!r || r.status !== "running")
70
+ return false;
71
+ abortFlags.set(runId, true);
72
+ return true;
73
+ };
74
+ const renderStep = (s, total) => `轮 ${s.round}/${total} · \`#${s.tag}\` ${s.status === "done" ? "✅" : s.status === "timeout" ? "⏱" : "❌"}`;
75
+ /** Validate a spec before spending a spawn on it. Returns "" when valid. */
76
+ export const validateSpec = (spec) => {
77
+ if (!spec.base.trim())
78
+ return "base (chat) 未解析";
79
+ if (spec.steps.length === 0)
80
+ return "steps 不能为空";
81
+ const known = new Set(spec.nodes.map((n) => n.tag));
82
+ const orphan = spec.steps.find((s) => !known.has(s.to));
83
+ if (orphan)
84
+ return `step 指向未声明的节点 \`#${orphan.to}\``;
85
+ const selfless = spec.nodes.find((n) => !n.tag.trim());
86
+ if (selfless)
87
+ return "node.tag 不能为空 —— 每个节点都必须是带 tag 的会话";
88
+ return "";
89
+ };
90
+ export const startGraph = (spec, deps) => {
91
+ const runId = randomUUID().slice(0, 8);
92
+ const rounds = clamp(spec.rounds ?? 1, 1, 50);
93
+ const idleTimeoutMs = clamp(spec.idleTimeoutSec ?? 900, 30, 7200) * 1000;
94
+ const run = { runId, base: spec.base, spec, status: "running", history: [], startedAt: Date.now() };
95
+ runs.set(runId, run);
96
+ abortFlags.set(runId, false);
97
+ const aborted = () => abortFlags.get(runId) === true;
98
+ const finish = (status, error) => {
99
+ run.status = status;
100
+ run.endedAt = Date.now();
101
+ if (error)
102
+ run.error = error;
103
+ abortFlags.delete(runId);
104
+ const done = run.history.filter((h) => h.status === "done").length;
105
+ deps.notify(spec.base, [
106
+ `🕸 图 \`${runId}\` ${status === "converged" ? "已收敛" : status === "done" ? "完成" : status === "stopped" ? "已停止" : "异常终止"}`,
107
+ `完成 ${done}/${run.history.length} 步${error ? ` · ${error}` : ""}`,
108
+ ].join("\n"));
109
+ deps.log.info({ runId, status, steps: run.history.length, error }, "graph: finished");
110
+ };
111
+ const loop = async () => {
112
+ // Reply context, keyed by tag plus the rolling `last`. Rebuilt as the walk
113
+ // proceeds so a step can quote either its immediate predecessor or any
114
+ // named node's most recent output.
115
+ const ctx = { last: "" };
116
+ for (let round = 1; round <= rounds; round++) {
117
+ ctx.round = String(round);
118
+ for (const step of spec.steps) {
119
+ if (aborted()) {
120
+ finish("stopped");
121
+ return;
122
+ }
123
+ const node = spec.nodes.find((n) => n.tag === step.to);
124
+ const target = targetOf(spec.base, step.to);
125
+ const prompt = interpolate(step.prompt, ctx);
126
+ const rec = { round, tag: step.to, prompt, reply: "", status: "running", startedAt: Date.now() };
127
+ run.history.push(rec);
128
+ deps.notify(spec.base, `🕸 \`${runId}\` 轮 ${round}/${rounds} → \`#${step.to}\` 开始`);
129
+ const ready = await deps.ensureNode(target, node);
130
+ if (!ready.ok) {
131
+ rec.status = "error";
132
+ rec.reason = ready.reason;
133
+ rec.endedAt = Date.now();
134
+ finish("error", `节点 #${step.to} 就绪失败: ${ready.reason ?? "unknown"}`);
135
+ return;
136
+ }
137
+ const sent = await deps.send(target, prompt);
138
+ if (!sent.ok) {
139
+ rec.status = "error";
140
+ rec.reason = sent.reason;
141
+ rec.endedAt = Date.now();
142
+ finish("error", `注入 #${step.to} 失败: ${sent.reason ?? "unknown"}`);
143
+ return;
144
+ }
145
+ const idle = await waitForIdle(target, deps.isBusy, idleTimeoutMs, aborted);
146
+ rec.endedAt = Date.now();
147
+ rec.reply = await deps.lastText(target);
148
+ ctx[step.to] = rec.reply;
149
+ ctx.last = rec.reply;
150
+ if (!idle.idle) {
151
+ rec.status = idle.reason === "stopped" ? "error" : "timeout";
152
+ rec.reason = idle.reason;
153
+ if (idle.reason === "stopped") {
154
+ finish("stopped");
155
+ return;
156
+ }
157
+ // A timeout is not fatal: the agent may simply be slow, and the reply
158
+ // captured so far is still worth handing to the next node. Report and
159
+ // keep walking — stalling the whole graph on one slow step is worse.
160
+ deps.notify(spec.base, `🕸 \`${runId}\` ${renderStep(rec, rounds)} (超时, 继续)`);
161
+ continue;
162
+ }
163
+ rec.status = "done";
164
+ deps.notify(spec.base, `🕸 \`${runId}\` ${renderStep(rec, rounds)}`);
165
+ if (converged(rec.reply, spec.until)) {
166
+ finish("converged");
167
+ return;
168
+ }
169
+ }
170
+ }
171
+ finish("done");
172
+ };
173
+ void loop().catch((e) => finish("error", e.message));
174
+ return run;
175
+ };
176
+ //# sourceMappingURL=graph.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graph.js","sourceRoot":"","sources":["../../daemon/graph.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,83 @@
1
+ // Local control HTTP — talks to hooks, slash commands, MCP server.
2
+ // Bound to 127.0.0.1; trust = "anyone with loopback access".
3
+ // Endpoints intentionally tiny & sync-friendly.
4
+ import { createServer } from "node:http";
5
+ import { URL } from "node:url";
6
+ import { expandHome } from "../shared/paths.js";
7
+ import { listPending } from "./pending.js";
8
+ const json = (res, status, body) => {
9
+ res.statusCode = status;
10
+ res.setHeader("content-type", "application/json");
11
+ res.end(JSON.stringify(body));
12
+ };
13
+ const readBody = async (req) => {
14
+ const chunks = [];
15
+ for await (const c of req)
16
+ chunks.push(c);
17
+ if (chunks.length === 0)
18
+ return {};
19
+ try {
20
+ return JSON.parse(Buffer.concat(chunks).toString("utf8"));
21
+ }
22
+ catch {
23
+ return {};
24
+ }
25
+ };
26
+ const routes = {
27
+ "GET /healthz": (_req, res, _url, { ws }) => {
28
+ json(res, 200, { ok: true, wsConnected: ws.client.isConnected });
29
+ },
30
+ "GET /status": (_req, res, _url, { cfg, ws, sourcePath }) => {
31
+ json(res, 200, {
32
+ ok: true,
33
+ wsConnected: ws.client.isConnected,
34
+ botId: cfg.bot.botId,
35
+ defaultChat: cfg.defaultChat,
36
+ pending: listPending().length,
37
+ uptimeSec: Math.round(process.uptime()),
38
+ sourcePath,
39
+ logFile: expandHome(cfg.daemon.logFile),
40
+ });
41
+ },
42
+ "GET /pending": (_req, res) => {
43
+ json(res, 200, { pending: listPending() });
44
+ },
45
+ "POST /shutdown": async (_req, res, _url, { ws, log }) => {
46
+ json(res, 200, { ok: true });
47
+ log.info("shutdown via HTTP");
48
+ await ws.shutdown();
49
+ setTimeout(() => process.exit(0), 100);
50
+ },
51
+ // /approve and /message are wired by callers later (approval, slash).
52
+ };
53
+ export const startHttp = (ctx) => {
54
+ const dynamic = new Map();
55
+ const server = createServer(async (req, res) => {
56
+ const url = new URL(req.url ?? "/", `http://${req.headers.host ?? "localhost"}`);
57
+ const key = `${req.method ?? "GET"} ${url.pathname}`;
58
+ const handler = dynamic.get(key) ?? routes[key];
59
+ if (!handler) {
60
+ json(res, 404, { error: "not_found", route: key });
61
+ return;
62
+ }
63
+ try {
64
+ await handler(req, res, url, ctx);
65
+ }
66
+ catch (e) {
67
+ ctx.log.error({ err: e.message, route: key }, "http handler failed");
68
+ if (!res.headersSent)
69
+ json(res, 500, { error: "internal", message: e.message });
70
+ }
71
+ });
72
+ server.listen(ctx.cfg.daemon.port, ctx.cfg.daemon.host, () => {
73
+ ctx.log.info({ host: ctx.cfg.daemon.host, port: ctx.cfg.daemon.port }, "HTTP listening");
74
+ });
75
+ return {
76
+ close: () => new Promise((resolve) => {
77
+ server.close(() => resolve());
78
+ }),
79
+ register: (key, handler) => dynamic.set(key, handler),
80
+ };
81
+ };
82
+ export { readBody, json };
83
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../../daemon/http.ts"],"names":[],"mappings":"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"}