@andyqiu/codeforge 0.5.28 → 0.6.0

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.
package/dist/index.js CHANGED
@@ -17,739 +17,6 @@ var __export = (target, all) => {
17
17
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
18
18
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
19
19
 
20
- // lib/runtime-paths.ts
21
- import { mkdirSync as mkdirSync2, readFileSync, writeFileSync, existsSync as existsSync2 } from "node:fs";
22
- import * as path2 from "node:path";
23
- import * as os from "node:os";
24
- import * as crypto from "node:crypto";
25
- function runtimeRoot(env = process.env, platform = process.platform, homeDir = os.homedir()) {
26
- const xdg = env["XDG_DATA_HOME"];
27
- if (xdg && xdg.trim().length > 0) {
28
- return normalize2(path2.join(xdg, "codeforge"));
29
- }
30
- if (platform === "win32") {
31
- const localAppData = env["LOCALAPPDATA"];
32
- if (localAppData && localAppData.trim().length > 0) {
33
- return normalize2(path2.join(localAppData, "codeforge"));
34
- }
35
- return normalize2(path2.join(homeDir, "AppData", "Local", "codeforge"));
36
- }
37
- return normalize2(path2.join(homeDir, ".local", "share", "codeforge"));
38
- }
39
- function projectKey(absRoot) {
40
- const resolved = path2.resolve(absRoot);
41
- return crypto.createHash("sha256").update(resolved).digest("hex").slice(0, 12);
42
- }
43
- function runtimeDir(absRoot, opts = {}) {
44
- const env = opts.env ?? process.env;
45
- const platform = opts.platform ?? process.platform;
46
- const homeDir = opts.homeDir ?? os.homedir();
47
- const ensure = opts.ensure !== false;
48
- const resolvedRoot = path2.resolve(absRoot);
49
- const root = runtimeRoot(env, platform, homeDir);
50
- const dir = normalize2(path2.join(root, "projects", projectKey(resolvedRoot)));
51
- if (!ensure)
52
- return dir;
53
- mkdirSync2(dir, { recursive: true });
54
- const metaFile = path2.join(dir, ".meta.json");
55
- if (existsSync2(metaFile)) {
56
- const existing = readMetaSafe(metaFile);
57
- if (existing && existing.absPath && existing.absPath !== resolvedRoot) {
58
- throw new Error(`runtime dir hash collision: ${projectKey(resolvedRoot)} already used by ${existing.absPath}, current is ${resolvedRoot}`);
59
- }
60
- } else {
61
- const meta = {
62
- absPath: resolvedRoot,
63
- hostname: os.hostname(),
64
- createdAt: new Date().toISOString()
65
- };
66
- writeFileSync(metaFile, JSON.stringify(meta, null, 2), "utf8");
67
- }
68
- return dir;
69
- }
70
- function plansDir(absRoot) {
71
- return normalize2(path2.join(runtimeDir(absRoot, { ensure: false }), "plans"));
72
- }
73
- function planFilePath(absRoot, title) {
74
- const dir = plansDir(absRoot);
75
- const ts = formatTimestampLocal(new Date);
76
- const slug = titleToSlug(title);
77
- return normalize2(path2.join(dir, `${ts}-${slug}.md`));
78
- }
79
- function subagentLogPath(absRoot, parentID, childID) {
80
- return normalize2(path2.join(runtimeDir(absRoot, { ensure: false }), "subagents", parentID, `${childID}.log`));
81
- }
82
- function normalize2(p) {
83
- return path2.normalize(path2.resolve(p));
84
- }
85
- function readMetaSafe(file) {
86
- try {
87
- const raw = readFileSync(file, "utf8");
88
- const parsed = JSON.parse(raw);
89
- if (typeof parsed.absPath !== "string")
90
- return null;
91
- return {
92
- absPath: parsed.absPath,
93
- hostname: typeof parsed.hostname === "string" ? parsed.hostname : "",
94
- createdAt: typeof parsed.createdAt === "string" ? parsed.createdAt : ""
95
- };
96
- } catch {
97
- return null;
98
- }
99
- }
100
- function formatTimestampLocal(d) {
101
- const pad = (n, w = 2) => String(n).padStart(w, "0");
102
- return `${d.getFullYear()}${pad(d.getMonth() + 1)}${pad(d.getDate())}` + `-${pad(d.getHours())}${pad(d.getMinutes())}${pad(d.getSeconds())}`;
103
- }
104
- function titleToSlug(title) {
105
- if (!title)
106
- return "untitled";
107
- const lowered = title.toLowerCase();
108
- const cleaned = lowered.replace(/[^a-z0-9]+/g, "-");
109
- const collapsed = cleaned.replace(/-+/g, "-");
110
- const truncated = collapsed.slice(0, 30);
111
- const trimmed = truncated.replace(/^-+|-+$/g, "");
112
- return trimmed.length > 0 ? trimmed : "untitled";
113
- }
114
- var init_runtime_paths = () => {};
115
-
116
- // lib/global-config.ts
117
- import { readFileSync as readFileSync2, existsSync as existsSync3, statSync } from "node:fs";
118
- import * as path3 from "node:path";
119
- import * as os2 from "node:os";
120
- function __resetGlobalConfigCache() {
121
- cache.clear();
122
- warnedTokenInFile.clear();
123
- }
124
- function cacheGet(key) {
125
- const entry = cache.get(key);
126
- if (!entry)
127
- return;
128
- if (Date.now() > entry.expireAt) {
129
- cache.delete(key);
130
- return;
131
- }
132
- return entry.value;
133
- }
134
- function cacheSet(key, value) {
135
- cache.set(key, { expireAt: Date.now() + CACHE_TTL_MS, value });
136
- }
137
- function loadJsonIfExists(filePath) {
138
- const cacheKey = `file:${filePath}`;
139
- const cached = cacheGet(cacheKey);
140
- if (cached !== undefined)
141
- return cached;
142
- if (!existsSync3(filePath)) {
143
- cacheSet(cacheKey, null);
144
- return null;
145
- }
146
- let raw;
147
- try {
148
- raw = readFileSync2(filePath, "utf8");
149
- } catch (err) {
150
- warnOnce(`load-fail:${filePath}`, `[codeforge] 读取 ${filePath} 失败:${errorMessage(err)}(已忽略)`);
151
- cacheSet(cacheKey, null);
152
- return null;
153
- }
154
- if (raw.charCodeAt(0) === 65279)
155
- raw = raw.slice(1);
156
- try {
157
- const parsed = JSON.parse(raw);
158
- cacheSet(cacheKey, parsed);
159
- return parsed;
160
- } catch (err) {
161
- warnOnce(`parse-fail:${filePath}`, `[codeforge] JSON 解析失败 ${filePath}:${errorMessage(err)}(已忽略)`);
162
- cacheSet(cacheKey, null);
163
- return null;
164
- }
165
- }
166
- function globalConfigDir(env = process.env) {
167
- const xdg = env["XDG_CONFIG_HOME"];
168
- const base = xdg && xdg.length > 0 ? xdg : path3.join(os2.homedir(), ".config");
169
- return path3.join(base, "codeforge");
170
- }
171
- function projectConfigDir(root = process.cwd()) {
172
- return path3.join(root, ".codeforge");
173
- }
174
- function getCodeforgeConfig(opts = {}) {
175
- const root = opts.root ?? process.cwd();
176
- const env = opts.env ?? process.env;
177
- const cacheKey = `codeforge:${root}`;
178
- const cached = cacheGet(cacheKey);
179
- if (cached !== undefined)
180
- return cached;
181
- const builtin = {};
182
- const globalCfg = readJsonObject(path3.join(globalConfigDir(env), "codeforge.json"));
183
- const projectCfg = readJsonObject(path3.join(projectConfigDir(root), "codeforge.json"));
184
- const merged = { ...builtin, ...globalCfg, ...projectCfg };
185
- cacheSet(cacheKey, merged);
186
- return merged;
187
- }
188
- function getKhConfig(opts = {}) {
189
- const root = opts.root ?? process.cwd();
190
- const env = opts.env ?? process.env;
191
- const cacheKey = `kh:${root}:${env["KNOWLEDGE_HUB_URL"] ?? ""}:${env["KNOWLEDGE_API_KEY"] ? "1" : "0"}`;
192
- const cached = cacheGet(cacheKey);
193
- if (cached !== undefined)
194
- return cached;
195
- const globalKh = readJsonObject(path3.join(globalConfigDir(env), "kh.json"));
196
- const projectKh = readJsonObject(path3.join(projectConfigDir(root), "kh.json"));
197
- for (const [label, src] of [
198
- ["全局 kh.json", globalKh],
199
- ["项目 kh.json", projectKh]
200
- ]) {
201
- if (containsTokenField(src)) {
202
- warnOnce(`token-in-file:${label}`, `[codeforge] 检测到 ${label} 写了 token / apiKey / secret 等敏感字段,已忽略。` + ` API key 必须通过环境变量 KNOWLEDGE_API_KEY 提供。`);
203
- }
204
- }
205
- const url = pickString(env["KNOWLEDGE_HUB_URL"]) ?? pickString(projectKh?.["url"]) ?? pickString(globalKh?.["url"]) ?? DEFAULT_KH_URL;
206
- const timeoutMs = pickPositiveInt(projectKh?.["timeoutMs"]) ?? pickPositiveInt(globalKh?.["timeoutMs"]) ?? DEFAULT_KH_TIMEOUT_MS;
207
- const maxRetries = pickNonNegativeInt(projectKh?.["maxRetries"]) ?? pickNonNegativeInt(globalKh?.["maxRetries"]) ?? DEFAULT_KH_MAX_RETRIES;
208
- const apiKey = pickString(env["KNOWLEDGE_API_KEY"]);
209
- const cfg = apiKey ? { url, apiKey, timeoutMs, maxRetries } : { url, timeoutMs, maxRetries };
210
- cacheSet(cacheKey, cfg);
211
- return cfg;
212
- }
213
- function readJsonObject(filePath) {
214
- const raw = loadJsonIfExists(filePath);
215
- if (!raw || typeof raw !== "object" || Array.isArray(raw))
216
- return null;
217
- return raw;
218
- }
219
- function containsTokenField(obj) {
220
- if (!obj)
221
- return false;
222
- const banned = [
223
- "token",
224
- "apikey",
225
- "api_key",
226
- "apiKey",
227
- "authorization",
228
- "auth",
229
- "bearer",
230
- "secret",
231
- "access_token",
232
- "refresh_token",
233
- "client_secret",
234
- "password",
235
- "passwd",
236
- "credential",
237
- "credentials"
238
- ];
239
- const bannedLower = new Set(banned.map((b) => b.toLowerCase()));
240
- for (const key of Object.keys(obj)) {
241
- if (bannedLower.has(key.toLowerCase())) {
242
- return true;
243
- }
244
- }
245
- return false;
246
- }
247
- function pickString(v) {
248
- if (typeof v !== "string")
249
- return;
250
- const s = v.trim();
251
- return s.length > 0 ? s : undefined;
252
- }
253
- function pickPositiveInt(v) {
254
- if (typeof v !== "number" || !Number.isFinite(v))
255
- return;
256
- const n = Math.floor(v);
257
- return n > 0 ? n : undefined;
258
- }
259
- function pickNonNegativeInt(v) {
260
- if (typeof v !== "number" || !Number.isFinite(v))
261
- return;
262
- const n = Math.floor(v);
263
- return n >= 0 ? n : undefined;
264
- }
265
- function errorMessage(err) {
266
- return err instanceof Error ? err.message : String(err);
267
- }
268
- function warnOnce(key, msg) {
269
- if (warnedTokenInFile.has(key))
270
- return;
271
- warnedTokenInFile.add(key);
272
- console.warn(msg);
273
- }
274
- var DEFAULT_KH_URL = "http://10.5.60.26:8900/mcp", DEFAULT_KH_TIMEOUT_MS = 5000, DEFAULT_KH_MAX_RETRIES = 1, CACHE_TTL_MS = 5000, cache, warnedTokenInFile;
275
- var init_global_config = __esm(() => {
276
- init_runtime_paths();
277
- cache = new Map;
278
- warnedTokenInFile = new Set;
279
- });
280
-
281
- // lib/kh-client-stdio.ts
282
- function makeRpcId() {
283
- const n = nextRpcId++;
284
- const r = Math.floor(Math.random() * 65535).toString(16);
285
- return `cf-${n}-${r}`;
286
- }
287
- function createHttpMcpTransport(opts) {
288
- const apiKey = opts.apiKey?.trim();
289
- if (!apiKey)
290
- return null;
291
- const fetchImpl = opts.fetchImpl ?? (typeof globalThis.fetch === "function" ? globalThis.fetch.bind(globalThis) : null);
292
- if (!fetchImpl)
293
- return null;
294
- const url = opts.url;
295
- if (!url || typeof url !== "string")
296
- return null;
297
- const timeoutMs = opts.timeoutMs && opts.timeoutMs > 0 ? opts.timeoutMs : DEFAULT_TIMEOUT_MS;
298
- const maxRetries = typeof opts.maxRetries === "number" && opts.maxRetries >= 0 ? Math.floor(opts.maxRetries) : DEFAULT_MAX_RETRIES;
299
- const sleep = opts.sleep ?? defaultSleep;
300
- return async (call) => {
301
- const body = JSON.stringify({
302
- jsonrpc: "2.0",
303
- id: makeRpcId(),
304
- method: "tools/call",
305
- params: {
306
- name: call.tool,
307
- arguments: call.args
308
- }
309
- });
310
- let lastErr;
311
- for (let attempt = 0;attempt <= maxRetries; attempt++) {
312
- try {
313
- const result = await sendOnce({ url, apiKey, body, timeoutMs, fetchImpl });
314
- return result;
315
- } catch (err) {
316
- lastErr = err;
317
- if (err instanceof HttpStatusError && err.status >= 400 && err.status < 500) {
318
- throw err;
319
- }
320
- if (attempt < maxRetries) {
321
- const delay = RETRY_BASE_DELAY_MS * Math.pow(2, attempt);
322
- await sleep(delay);
323
- }
324
- }
325
- }
326
- throw lastErr ?? new Error("kh stdio transport: unknown failure");
327
- };
328
- }
329
- async function sendOnce(ctx) {
330
- const controller = new AbortController;
331
- const timer = setTimeout(() => controller.abort(), ctx.timeoutMs);
332
- let resp;
333
- try {
334
- resp = await ctx.fetchImpl(ctx.url, {
335
- method: "POST",
336
- headers: {
337
- "Content-Type": "application/json",
338
- Accept: "application/json, text/event-stream",
339
- Authorization: `Bearer ${ctx.apiKey}`
340
- },
341
- body: ctx.body,
342
- signal: controller.signal
343
- });
344
- } catch (err) {
345
- if (isAbortError(err)) {
346
- throw new Error(`kh stdio transport: request timeout after ${ctx.timeoutMs}ms`);
347
- }
348
- throw err instanceof Error ? err : new Error(String(err));
349
- } finally {
350
- clearTimeout(timer);
351
- }
352
- if (!resp.ok) {
353
- throw new HttpStatusError(resp.status, `kh stdio transport: HTTP ${resp.status}`);
354
- }
355
- let raw;
356
- try {
357
- raw = await resp.text();
358
- } catch (err) {
359
- throw new Error(`kh stdio transport: read body failed — ${errorMessage2(err)}`);
360
- }
361
- let unwrapped;
362
- try {
363
- unwrapped = unwrapSseIfNeeded(raw, resp.headers.get("content-type"));
364
- } catch (err) {
365
- throw err instanceof Error ? err : new Error(String(err));
366
- }
367
- let json;
368
- try {
369
- json = JSON.parse(unwrapped);
370
- } catch (err) {
371
- throw new Error(`kh stdio transport: invalid JSON response — ${errorMessage2(err)}`);
372
- }
373
- return parseRpcEnvelope(json);
374
- }
375
- function unwrapSseIfNeeded(rawBody, contentType) {
376
- const ct = (contentType ?? "").toLowerCase();
377
- if (!ct.includes("text/event-stream")) {
378
- return rawBody;
379
- }
380
- const normalized = rawBody.replace(/\r\n/g, `
381
- `);
382
- const firstBlankLine = normalized.indexOf(`
383
-
384
- `);
385
- const firstEvent = firstBlankLine >= 0 ? normalized.slice(0, firstBlankLine) : normalized;
386
- const dataLines = [];
387
- for (const line of firstEvent.split(`
388
- `)) {
389
- if (line.startsWith("data:")) {
390
- const value = line.slice(5).startsWith(" ") ? line.slice(6) : line.slice(5);
391
- dataLines.push(value);
392
- }
393
- }
394
- if (dataLines.length === 0) {
395
- throw new Error("kh stdio transport: SSE response missing data");
396
- }
397
- return dataLines.join(`
398
- `);
399
- }
400
- function parseRpcEnvelope(json) {
401
- if (!json || typeof json !== "object") {
402
- throw new Error("kh stdio transport: response is not an object");
403
- }
404
- const obj = json;
405
- if (obj["error"]) {
406
- const e = obj["error"];
407
- throw new Error(`kh stdio transport: rpc error ${e.code ?? "?"} — ${e.message ?? "unknown"}`);
408
- }
409
- const result = obj["result"];
410
- if (result === undefined || result === null) {
411
- throw new Error("kh stdio transport: response missing result");
412
- }
413
- if (typeof result === "object") {
414
- const r = result;
415
- const content = r["content"];
416
- if (Array.isArray(content) && content.length > 0) {
417
- const first = content[0];
418
- if (first && typeof first["text"] === "string") {
419
- const text = first["text"];
420
- try {
421
- return JSON.parse(text);
422
- } catch {
423
- return text;
424
- }
425
- }
426
- }
427
- }
428
- return result;
429
- }
430
- function isAbortError(err) {
431
- if (!err || typeof err !== "object")
432
- return false;
433
- const name = err.name;
434
- return name === "AbortError" || name === "TimeoutError";
435
- }
436
- function defaultSleep(ms) {
437
- return new Promise((resolve5) => setTimeout(resolve5, ms));
438
- }
439
- function errorMessage2(err) {
440
- return err instanceof Error ? err.message : String(err);
441
- }
442
- var DEFAULT_TIMEOUT_MS = 5000, DEFAULT_MAX_RETRIES = 1, RETRY_BASE_DELAY_MS = 200, nextRpcId = 1, HttpStatusError;
443
- var init_kh_client_stdio = __esm(() => {
444
- HttpStatusError = class HttpStatusError extends Error {
445
- status;
446
- constructor(status, msg) {
447
- super(msg);
448
- this.status = status;
449
- this.name = "HttpStatusError";
450
- }
451
- };
452
- });
453
-
454
- // lib/kh-client.ts
455
- var exports_kh_client = {};
456
- __export(exports_kh_client, {
457
- detectProjectContext: () => detectProjectContext,
458
- defaultTransport: () => defaultTransport,
459
- __resetDefaultTransportCache: () => __resetDefaultTransportCache,
460
- KhClient: () => KhClient,
461
- KhCategorySchema: () => KhCategorySchema
462
- });
463
- import { z } from "zod";
464
- function defaultTransport() {
465
- const g = globalThis;
466
- if (typeof g.__codeforgeMcpCall === "function")
467
- return g.__codeforgeMcpCall;
468
- return resolveStdioTransport();
469
- }
470
- function resolveStdioTransport() {
471
- try {
472
- const cfg = getKhConfig();
473
- return createHttpMcpTransport({
474
- url: cfg.url,
475
- apiKey: cfg.apiKey,
476
- timeoutMs: cfg.timeoutMs,
477
- maxRetries: cfg.maxRetries
478
- });
479
- } catch (err) {
480
- warnOnce2(`stdio-transport-init`, `[codeforge] KH HTTP transport 初始化失败:${errorMessage3(err)}(已降级)`);
481
- return null;
482
- }
483
- }
484
- function __resetDefaultTransportCache() {
485
- __resetGlobalConfigCache();
486
- }
487
- function detectProjectContext() {
488
- return {
489
- projectName: process.env["CODEFORGE_PROJECT"] ?? guessProjectName(),
490
- currentTask: process.env["CODEFORGE_TASK"],
491
- caller: process.env["CODEFORGE_CALLER"] ?? "tool"
492
- };
493
- }
494
- function guessProjectName() {
495
- try {
496
- const cwd = process.cwd();
497
- return cwd.split(/[\\/]/).pop() ?? "unknown";
498
- } catch {
499
- return "unknown";
500
- }
501
- }
502
-
503
- class KhClient {
504
- server;
505
- transport;
506
- context;
507
- maxRetries;
508
- constructor(opts = {}) {
509
- this.server = opts.serverIdentifier ?? process.env["CODEFORGE_KH_SERVER"] ?? "knowledge-hub";
510
- this.transport = opts.transport ?? defaultTransport();
511
- this.context = opts.context ?? detectProjectContext();
512
- this.maxRetries = opts.maxRetries ?? 0;
513
- }
514
- async search(input) {
515
- const query = (input.query ?? "").trim();
516
- if (!query) {
517
- return {
518
- ok: false,
519
- reason: "invalid_input",
520
- message: "query 不能为空"
521
- };
522
- }
523
- if (!this.transport) {
524
- return {
525
- ok: false,
526
- reason: "transport_unavailable",
527
- message: "MCP transport 未注入;KH 调用降级(不阻塞主流程)"
528
- };
529
- }
530
- const args = {
531
- query,
532
- limit: input.limit ?? 5,
533
- _context: {
534
- project: this.context.projectName,
535
- caller: this.context.caller,
536
- prefer: input.prefer ?? ["bugfix", "architecture", "convention"]
537
- }
538
- };
539
- try {
540
- const raw = await this.callWithRetry({
541
- server: this.server,
542
- tool: "smart_search",
543
- args
544
- });
545
- return normalizeSearchResult(raw, query);
546
- } catch (err) {
547
- return {
548
- ok: false,
549
- reason: "kh_returned_error",
550
- message: err instanceof Error ? err.message : String(err)
551
- };
552
- }
553
- }
554
- async save(input) {
555
- const insight = (input.insight ?? "").trim();
556
- if (!insight) {
557
- return {
558
- ok: false,
559
- reason: "invalid_input",
560
- message: "insight 不能为空"
561
- };
562
- }
563
- if (insight.length > 5000) {
564
- return {
565
- ok: false,
566
- reason: "invalid_input",
567
- message: `insight 过长 (${insight.length} > 5000 字),建议分多条沉淀`
568
- };
569
- }
570
- const categoryParse = KhCategorySchema.safeParse(input.category);
571
- const category = categoryParse.success ? categoryParse.data : input.category;
572
- if (!this.transport) {
573
- return {
574
- ok: false,
575
- reason: "transport_unavailable",
576
- message: "MCP transport 未注入;KH 沉淀降级(不阻塞主流程)"
577
- };
578
- }
579
- const tags = uniq([
580
- ...input.tags ?? [],
581
- `project:${this.context.projectName}`,
582
- `caller:${this.context.caller}`
583
- ]);
584
- try {
585
- const raw = await this.callWithRetry({
586
- server: this.server,
587
- tool: "save_chat_insight",
588
- args: { insight, category, tags }
589
- });
590
- return normalizeSaveResult(raw);
591
- } catch (err) {
592
- return {
593
- ok: false,
594
- reason: "kh_returned_error",
595
- message: err instanceof Error ? err.message : String(err)
596
- };
597
- }
598
- }
599
- async callWithRetry(call) {
600
- if (!this.transport)
601
- throw new Error("transport not available");
602
- const full = {
603
- server: "server" in call && call.server || this.server,
604
- tool: call.tool,
605
- args: call.args
606
- };
607
- let lastErr;
608
- for (let attempt = 0;attempt <= this.maxRetries; attempt++) {
609
- try {
610
- return await this.transport(full);
611
- } catch (err) {
612
- lastErr = err;
613
- }
614
- }
615
- throw lastErr;
616
- }
617
- hasTransport() {
618
- return this.transport !== null;
619
- }
620
- async addKnowledge(args) {
621
- return this.callKhTool("add_knowledge", args);
622
- }
623
- async uploadDocument(args) {
624
- return this.callKhTool("upload_document", args);
625
- }
626
- async uploadDocumentFromUrl(args) {
627
- return this.callKhTool("upload_document_from_url", args);
628
- }
629
- async updateKnowledge(args) {
630
- return this.callKhTool("update_knowledge", args);
631
- }
632
- async flagOutdated(args) {
633
- return this.callKhTool("flag_outdated", args);
634
- }
635
- async deleteKnowledge(args) {
636
- return this.callKhTool("delete_knowledge", args);
637
- }
638
- async listRecent(args = {}) {
639
- return this.callKhTool("list_recent", args);
640
- }
641
- async updateWorkingMemory(args) {
642
- return this.callKhTool("update_working_memory", args);
643
- }
644
- async getWorkingMemory(args = {}) {
645
- return this.callKhTool("get_working_memory", args);
646
- }
647
- async updateUserPreference(args) {
648
- return this.callKhTool("update_user_preference", args);
649
- }
650
- async webSearch(args) {
651
- return this.callKhTool("web_search", args);
652
- }
653
- async generateImage(args) {
654
- return this.callKhTool("generate_image", args);
655
- }
656
- async callKhTool(tool, args) {
657
- if (!this.transport) {
658
- return {
659
- ok: false,
660
- reason: "transport_unavailable",
661
- message: `MCP transport 未注入;KH ${tool} 调用降级(不阻塞主流程)`
662
- };
663
- }
664
- try {
665
- return await this.callWithRetry({ tool, args });
666
- } catch (err) {
667
- return {
668
- ok: false,
669
- reason: "kh_returned_error",
670
- message: err instanceof Error ? err.message : String(err)
671
- };
672
- }
673
- }
674
- }
675
- function normalizeSearchResult(raw, query) {
676
- const obj = raw ?? {};
677
- const list = obj["insights"] ?? obj["matches"] ?? obj["results"] ?? [];
678
- const insights = list.map((item) => normalizeInsight(item)).filter((x) => x !== null);
679
- return {
680
- ok: true,
681
- query,
682
- insights,
683
- hint: typeof obj["hint"] === "string" ? obj["hint"] : undefined
684
- };
685
- }
686
- function normalizeInsight(raw) {
687
- if (!raw || typeof raw !== "object")
688
- return null;
689
- const r = raw;
690
- const id = pickString2(r, ["id", "_id", "uuid"]);
691
- const title = pickString2(r, ["title", "name"]) ?? "(untitled)";
692
- const content = pickString2(r, ["content", "body", "text"]) ?? "";
693
- if (!id)
694
- return null;
695
- return {
696
- id,
697
- title,
698
- content,
699
- category: pickString2(r, ["category", "type"]) ?? "reference",
700
- scope: pickString2(r, ["scope", "namespace"]) ?? "unknown",
701
- confidence: typeof r["confidence"] === "number" ? r["confidence"] : 0,
702
- tags: Array.isArray(r["tags"]) ? r["tags"] : undefined,
703
- source: pickString2(r, ["source", "origin"])
704
- };
705
- }
706
- function normalizeSaveResult(raw) {
707
- const r = raw ?? {};
708
- return {
709
- ok: true,
710
- id: pickString2(r, ["id", "_id"]) ?? "unknown",
711
- title: pickString2(r, ["title"]) ?? "(no title)",
712
- category: pickString2(r, ["category"]) ?? "unknown",
713
- scope: pickString2(r, ["scope"]) ?? "unknown"
714
- };
715
- }
716
- function pickString2(obj, keys) {
717
- for (const k of keys) {
718
- const v = obj[k];
719
- if (typeof v === "string" && v.length > 0)
720
- return v;
721
- }
722
- return;
723
- }
724
- function uniq(arr) {
725
- return Array.from(new Set(arr));
726
- }
727
- function errorMessage3(err) {
728
- return err instanceof Error ? err.message : String(err);
729
- }
730
- function warnOnce2(key, msg) {
731
- if (_warnedKeys.has(key))
732
- return;
733
- _warnedKeys.add(key);
734
- console.warn(msg);
735
- }
736
- var KhCategorySchema, _warnedKeys;
737
- var init_kh_client = __esm(() => {
738
- init_global_config();
739
- init_kh_client_stdio();
740
- KhCategorySchema = z.enum([
741
- "convention",
742
- "anti_pattern",
743
- "architecture",
744
- "reference",
745
- "workflow",
746
- "gotcha",
747
- "tooling",
748
- "decision"
749
- ]);
750
- _warnedKeys = new Set;
751
- });
752
-
753
20
  // lib/worktree-ops.ts
754
21
  var exports_worktree_ops = {};
755
22
  __export(exports_worktree_ops, {
@@ -8826,7 +8093,7 @@ var autoCommitServer = async (ctx) => {
8826
8093
  var handler3 = autoCommitServer;
8827
8094
 
8828
8095
  // plugins/channels.ts
8829
- import { promises as fs2, statSync as statSync2 } from "node:fs";
8096
+ import { promises as fs2, statSync } from "node:fs";
8830
8097
  import * as path4 from "node:path";
8831
8098
 
8832
8099
  // lib/channels.ts
@@ -8940,8 +8207,6 @@ async function sendOne(ev, ch, deps) {
8940
8207
  return await sendFile(ev, ch, deps);
8941
8208
  case "exec":
8942
8209
  return await sendExec(ev, ch, deps);
8943
- case "kh":
8944
- return await sendKh(ev, ch, deps);
8945
8210
  case "mcp":
8946
8211
  return await sendMcp(ev, ch, deps);
8947
8212
  case "slack":
@@ -9041,29 +8306,6 @@ async function sendExec(ev, ch, deps) {
9041
8306
  error: "exec sink 配置无效:必须提供 argv[] 或 command(且 shell:true)"
9042
8307
  };
9043
8308
  }
9044
- async function sendKh(ev, ch, deps) {
9045
- if (!deps.khSave) {
9046
- return { name: ch.name, type: "kh", status: "error", error: "deps.khSave 未注入" };
9047
- }
9048
- const titleTpl = ch.title_template ?? "${event}";
9049
- const title = renderChannelTemplate(titleTpl, ev).rendered.slice(0, 200) || ev.event;
9050
- const contentTpl = ch.content_template ?? "${message|}\n${title|}\n${event} @ ${session_id|<no-session>}";
9051
- const content = renderChannelTemplate(contentTpl, ev).rendered.trim() || JSON.stringify(ev.data ?? {});
9052
- const tags = dedupeTags(ch.tags, ev.data?.["tags"]);
9053
- const r = await deps.khSave({
9054
- title,
9055
- content: content.slice(0, 2000),
9056
- category: ch.category ?? "其它",
9057
- tags
9058
- });
9059
- return {
9060
- name: ch.name,
9061
- type: "kh",
9062
- status: r.ok ? "sent" : "error",
9063
- rendered: title,
9064
- error: r.ok ? undefined : r.error ?? "kh 写入失败"
9065
- };
9066
- }
9067
8309
  async function sendMcp(ev, ch, deps) {
9068
8310
  if (!deps.mcpCall) {
9069
8311
  return { name: ch.name, type: "mcp", status: "error", error: "deps.mcpCall 未注入" };
@@ -9474,20 +8716,6 @@ async function sendLark(ev, ch, deps) {
9474
8716
  };
9475
8717
  }
9476
8718
  }
9477
- function dedupeTags(defaults, evTags) {
9478
- const set = new Set;
9479
- if (defaults) {
9480
- for (const t of defaults)
9481
- if (typeof t === "string" && t)
9482
- set.add(t);
9483
- }
9484
- if (Array.isArray(evTags)) {
9485
- for (const t of evTags)
9486
- if (typeof t === "string" && t)
9487
- set.add(t);
9488
- }
9489
- return set.size > 0 ? [...set] : undefined;
9490
- }
9491
8719
  function describe2(err) {
9492
8720
  if (err instanceof Error)
9493
8721
  return err.message;
@@ -9511,8 +8739,191 @@ function makeChannelEvent(event, data = {}) {
9511
8739
  };
9512
8740
  }
9513
8741
 
8742
+ // lib/global-config.ts
8743
+ import { readFileSync as readFileSync2, existsSync as existsSync3 } from "node:fs";
8744
+ import * as path3 from "node:path";
8745
+ import * as os2 from "node:os";
8746
+
8747
+ // lib/runtime-paths.ts
8748
+ import { mkdirSync as mkdirSync2, readFileSync, writeFileSync, existsSync as existsSync2 } from "node:fs";
8749
+ import * as path2 from "node:path";
8750
+ import * as os from "node:os";
8751
+ import * as crypto from "node:crypto";
8752
+ function runtimeRoot(env = process.env, platform = process.platform, homeDir = os.homedir()) {
8753
+ const xdg = env["XDG_DATA_HOME"];
8754
+ if (xdg && xdg.trim().length > 0) {
8755
+ return normalize2(path2.join(xdg, "codeforge"));
8756
+ }
8757
+ if (platform === "win32") {
8758
+ const localAppData = env["LOCALAPPDATA"];
8759
+ if (localAppData && localAppData.trim().length > 0) {
8760
+ return normalize2(path2.join(localAppData, "codeforge"));
8761
+ }
8762
+ return normalize2(path2.join(homeDir, "AppData", "Local", "codeforge"));
8763
+ }
8764
+ return normalize2(path2.join(homeDir, ".local", "share", "codeforge"));
8765
+ }
8766
+ function projectKey(absRoot) {
8767
+ const resolved = path2.resolve(absRoot);
8768
+ return crypto.createHash("sha256").update(resolved).digest("hex").slice(0, 12);
8769
+ }
8770
+ function runtimeDir(absRoot, opts = {}) {
8771
+ const env = opts.env ?? process.env;
8772
+ const platform = opts.platform ?? process.platform;
8773
+ const homeDir = opts.homeDir ?? os.homedir();
8774
+ const ensure = opts.ensure !== false;
8775
+ const resolvedRoot = path2.resolve(absRoot);
8776
+ const root = runtimeRoot(env, platform, homeDir);
8777
+ const dir = normalize2(path2.join(root, "projects", projectKey(resolvedRoot)));
8778
+ if (!ensure)
8779
+ return dir;
8780
+ mkdirSync2(dir, { recursive: true });
8781
+ const metaFile = path2.join(dir, ".meta.json");
8782
+ if (existsSync2(metaFile)) {
8783
+ const existing = readMetaSafe(metaFile);
8784
+ if (existing && existing.absPath && existing.absPath !== resolvedRoot) {
8785
+ throw new Error(`runtime dir hash collision: ${projectKey(resolvedRoot)} already used by ${existing.absPath}, current is ${resolvedRoot}`);
8786
+ }
8787
+ } else {
8788
+ const meta = {
8789
+ absPath: resolvedRoot,
8790
+ hostname: os.hostname(),
8791
+ createdAt: new Date().toISOString()
8792
+ };
8793
+ writeFileSync(metaFile, JSON.stringify(meta, null, 2), "utf8");
8794
+ }
8795
+ return dir;
8796
+ }
8797
+ function plansDir(absRoot) {
8798
+ return normalize2(path2.join(runtimeDir(absRoot, { ensure: false }), "plans"));
8799
+ }
8800
+ function planFilePath(absRoot, title) {
8801
+ const dir = plansDir(absRoot);
8802
+ const ts = formatTimestampLocal(new Date);
8803
+ const slug = titleToSlug(title);
8804
+ return normalize2(path2.join(dir, `${ts}-${slug}.md`));
8805
+ }
8806
+ function subagentLogPath(absRoot, parentID, childID) {
8807
+ return normalize2(path2.join(runtimeDir(absRoot, { ensure: false }), "subagents", parentID, `${childID}.log`));
8808
+ }
8809
+ function normalize2(p) {
8810
+ return path2.normalize(path2.resolve(p));
8811
+ }
8812
+ function readMetaSafe(file) {
8813
+ try {
8814
+ const raw = readFileSync(file, "utf8");
8815
+ const parsed = JSON.parse(raw);
8816
+ if (typeof parsed.absPath !== "string")
8817
+ return null;
8818
+ return {
8819
+ absPath: parsed.absPath,
8820
+ hostname: typeof parsed.hostname === "string" ? parsed.hostname : "",
8821
+ createdAt: typeof parsed.createdAt === "string" ? parsed.createdAt : ""
8822
+ };
8823
+ } catch {
8824
+ return null;
8825
+ }
8826
+ }
8827
+ function formatTimestampLocal(d) {
8828
+ const pad = (n, w = 2) => String(n).padStart(w, "0");
8829
+ return `${d.getFullYear()}${pad(d.getMonth() + 1)}${pad(d.getDate())}` + `-${pad(d.getHours())}${pad(d.getMinutes())}${pad(d.getSeconds())}`;
8830
+ }
8831
+ function titleToSlug(title) {
8832
+ if (!title)
8833
+ return "untitled";
8834
+ const lowered = title.toLowerCase();
8835
+ const cleaned = lowered.replace(/[^a-z0-9]+/g, "-");
8836
+ const collapsed = cleaned.replace(/-+/g, "-");
8837
+ const truncated = collapsed.slice(0, 30);
8838
+ const trimmed = truncated.replace(/^-+|-+$/g, "");
8839
+ return trimmed.length > 0 ? trimmed : "untitled";
8840
+ }
8841
+
8842
+ // lib/global-config.ts
8843
+ var CACHE_TTL_MS = 5000;
8844
+ var cache = new Map;
8845
+ function cacheGet(key) {
8846
+ const entry = cache.get(key);
8847
+ if (!entry)
8848
+ return;
8849
+ if (Date.now() > entry.expireAt) {
8850
+ cache.delete(key);
8851
+ return;
8852
+ }
8853
+ return entry.value;
8854
+ }
8855
+ function cacheSet(key, value) {
8856
+ cache.set(key, { expireAt: Date.now() + CACHE_TTL_MS, value });
8857
+ }
8858
+ function loadJsonIfExists(filePath) {
8859
+ const cacheKey = `file:${filePath}`;
8860
+ const cached = cacheGet(cacheKey);
8861
+ if (cached !== undefined)
8862
+ return cached;
8863
+ if (!existsSync3(filePath)) {
8864
+ cacheSet(cacheKey, null);
8865
+ return null;
8866
+ }
8867
+ let raw;
8868
+ try {
8869
+ raw = readFileSync2(filePath, "utf8");
8870
+ } catch (err) {
8871
+ warnOnce(`load-fail:${filePath}`, `[codeforge] 读取 ${filePath} 失败:${errorMessage(err)}(已忽略)`);
8872
+ cacheSet(cacheKey, null);
8873
+ return null;
8874
+ }
8875
+ if (raw.charCodeAt(0) === 65279)
8876
+ raw = raw.slice(1);
8877
+ try {
8878
+ const parsed = JSON.parse(raw);
8879
+ cacheSet(cacheKey, parsed);
8880
+ return parsed;
8881
+ } catch (err) {
8882
+ warnOnce(`parse-fail:${filePath}`, `[codeforge] JSON 解析失败 ${filePath}:${errorMessage(err)}(已忽略)`);
8883
+ cacheSet(cacheKey, null);
8884
+ return null;
8885
+ }
8886
+ }
8887
+ function globalConfigDir(env = process.env) {
8888
+ const xdg = env["XDG_CONFIG_HOME"];
8889
+ const base = xdg && xdg.length > 0 ? xdg : path3.join(os2.homedir(), ".config");
8890
+ return path3.join(base, "codeforge");
8891
+ }
8892
+ function projectConfigDir(root = process.cwd()) {
8893
+ return path3.join(root, ".codeforge");
8894
+ }
8895
+ function getCodeforgeConfig(opts = {}) {
8896
+ const root = opts.root ?? process.cwd();
8897
+ const env = opts.env ?? process.env;
8898
+ const cacheKey = `codeforge:${root}`;
8899
+ const cached = cacheGet(cacheKey);
8900
+ if (cached !== undefined)
8901
+ return cached;
8902
+ const builtin = {};
8903
+ const globalCfg = readJsonObject(path3.join(globalConfigDir(env), "codeforge.json"));
8904
+ const projectCfg = readJsonObject(path3.join(projectConfigDir(root), "codeforge.json"));
8905
+ const merged = { ...builtin, ...globalCfg, ...projectCfg };
8906
+ cacheSet(cacheKey, merged);
8907
+ return merged;
8908
+ }
8909
+ function readJsonObject(filePath) {
8910
+ const raw = loadJsonIfExists(filePath);
8911
+ if (!raw || typeof raw !== "object" || Array.isArray(raw))
8912
+ return null;
8913
+ return raw;
8914
+ }
8915
+ function errorMessage(err) {
8916
+ return err instanceof Error ? err.message : String(err);
8917
+ }
8918
+ var warnedTokenInFile = new Set;
8919
+ function warnOnce(key, msg) {
8920
+ if (warnedTokenInFile.has(key))
8921
+ return;
8922
+ warnedTokenInFile.add(key);
8923
+ console.warn(msg);
8924
+ }
8925
+
9514
8926
  // plugins/channels.ts
9515
- init_global_config();
9516
8927
  var PLUGIN_NAME4 = "channels";
9517
8928
  logLifecycle(PLUGIN_NAME4, "import", {});
9518
8929
  var fallbackLog = makePluginLogger(PLUGIN_NAME4);
@@ -9522,7 +8933,6 @@ var KNOWN_TYPES = new Set([
9522
8933
  "webhook",
9523
8934
  "file",
9524
8935
  "exec",
9525
- "kh",
9526
8936
  "mcp",
9527
8937
  "slack",
9528
8938
  "lark"
@@ -9531,7 +8941,6 @@ var REQUIRED_FIELDS = {
9531
8941
  webhook: ["url"],
9532
8942
  file: ["path"],
9533
8943
  exec: [],
9534
- kh: [],
9535
8944
  mcp: ["server", "tool"],
9536
8945
  slack: ["webhook_url"],
9537
8946
  lark: ["webhook_url"]
@@ -9586,11 +8995,11 @@ function resolveProjectConfigPaths(root) {
9586
8995
  legacy: path4.join(projectConfigDir(r), "config", "channels.json")
9587
8996
  };
9588
8997
  }
9589
- var _warnedKeys2 = new Set;
9590
- function warnOnce3(key, msg, log3) {
9591
- if (_warnedKeys2.has(key))
8998
+ var _warnedKeys = new Set;
8999
+ function warnOnce2(key, msg, log3) {
9000
+ if (_warnedKeys.has(key))
9592
9001
  return;
9593
- _warnedKeys2.add(key);
9002
+ _warnedKeys.add(key);
9594
9003
  log3.warn(msg);
9595
9004
  }
9596
9005
  function loadChannelsFromEnv(log3 = fallbackLog) {
@@ -9613,10 +9022,10 @@ function loadChannelsFromGlobal(log3 = fallbackLog) {
9613
9022
  if (!raw)
9614
9023
  return [];
9615
9024
  try {
9616
- const st = statSync2(filePath);
9025
+ const st = statSync(filePath);
9617
9026
  const perm = st.mode & 511;
9618
9027
  if ((perm & 63) !== 0) {
9619
- warnOnce3(`global-perm:${filePath}`, `[channels] 全局 channels.json 权限 0${perm.toString(8)} 含 group/other 可读位,` + `含 webhook 时建议 chmod 600 ${filePath}`, log3);
9028
+ warnOnce2(`global-perm:${filePath}`, `[channels] 全局 channels.json 权限 0${perm.toString(8)} 含 group/other 可读位,` + `含 webhook 时建议 chmod 600 ${filePath}`, log3);
9620
9029
  }
9621
9030
  } catch {}
9622
9031
  const arr = Array.isArray(raw) ? raw : raw.channels;
@@ -9713,31 +9122,6 @@ async function ensureDeps() {
9713
9122
  const cp = await import("node:child_process");
9714
9123
  const util = await import("node:util");
9715
9124
  const execFileAsync = util.promisify(cp.execFile);
9716
- const khSave = async (input) => {
9717
- try {
9718
- const { KhClient: KhClient2 } = await Promise.resolve().then(() => (init_kh_client(), exports_kh_client));
9719
- const cli = new KhClient2;
9720
- const insight = `${input.title}
9721
-
9722
- ${input.content}`.trim();
9723
- const r = await cli.save({
9724
- insight,
9725
- category: input.category,
9726
- tags: input.tags
9727
- });
9728
- if (r.ok)
9729
- return { ok: true, id: r.id };
9730
- return {
9731
- ok: false,
9732
- error: `${r.reason}: ${r.message}`
9733
- };
9734
- } catch (err) {
9735
- return {
9736
- ok: false,
9737
- error: err instanceof Error ? err.message : String(err)
9738
- };
9739
- }
9740
- };
9741
9125
  const mcpCall = async (server, tool) => {
9742
9126
  return {
9743
9127
  ok: false,
@@ -9793,7 +9177,6 @@ ${input.content}`.trim();
9793
9177
  };
9794
9178
  }
9795
9179
  },
9796
- khSave,
9797
9180
  mcpCall,
9798
9181
  log: (lvl, msg, data) => fallbackLog[lvl]?.(msg, data)
9799
9182
  };
@@ -10019,507 +9402,13 @@ var chatAgentCachePlugin = async (ctx) => {
10019
9402
  };
10020
9403
  var handler5 = chatAgentCachePlugin;
10021
9404
 
10022
- // plugins/codeforge-tools.ts
10023
- import { tool } from "@opencode-ai/plugin";
10024
-
10025
- // tools/smart-search.ts
10026
- init_kh_client();
10027
- import { z as z2 } from "zod";
10028
- var description = [
10029
- "查询 Knowledge Hub 团队历史经验。",
10030
- "**何时调用**:",
10031
- "- planner agent 接到新需求,第一件事就是 smart_search",
10032
- "- 遇到不确定的技术选型 / 配置 / 部署问题",
10033
- "- 用户提到「以前」「之前」「历史」「为什么」「怎么部署」「dev 地址」等触发词",
10034
- "- 涉及团队专有名词(如 quant、Andy、KH 等)",
10035
- "**何时不需要**:纯通用语法、纯机械改动、用户已指明确切代码位置。"
10036
- ].join(`
10037
- `);
10038
- var ArgsSchema = z2.object({
10039
- query: z2.string().min(1).describe("自然语言查询词,例如「登录限流方案」「dev 服务器部署流程」"),
10040
- limit: z2.number().int().min(1).max(15).optional().describe("返回条数,默认 5;超过 15 会被 KH 截断"),
10041
- prefer: z2.array(KhCategorySchema).optional().describe("偏好分类(best-effort 提示给 KH 排序);程序员场景默认 architecture / anti_pattern / convention")
10042
- });
10043
- var _client = null;
10044
- function getClient() {
10045
- if (!_client)
10046
- _client = new KhClient;
10047
- return _client;
10048
- }
10049
- async function execute(input) {
10050
- const parsed = ArgsSchema.safeParse(input);
10051
- if (!parsed.success) {
10052
- return {
10053
- ok: false,
10054
- reason: "invalid_input",
10055
- message: parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ")
10056
- };
10057
- }
10058
- const result = await getClient().search(parsed.data);
10059
- if (result.ok && result.insights.length === 0) {
10060
- return {
10061
- ...result,
10062
- hint: result.hint ?? "无相关团队经验。建议:(1) 用更宽泛的关键词重试;(2) 直接基于 repo-map 出方案;(3) 任务完成后用 save_chat_insight 沉淀新经验。"
10063
- };
10064
- }
10065
- return result;
10066
- }
10067
- // tools/save-chat-insight.ts
10068
- init_kh_client();
10069
- import { z as z3 } from "zod";
10070
- var description2 = [
10071
- "把刚解决的问题 / 学到的经验沉淀回 Knowledge Hub,让团队下次能搜到。",
10072
- "**何时调用**:",
10073
- "- 修完一个非平凡的 bug(含问题→原因→解决三段式)",
10074
- "- 发现一个值得记录的架构决策 / 技术权衡",
10075
- "- 踩了一个会让别人也踩的坑(anti_pattern / gotcha)",
10076
- "**何时不需要**:trivial 修改、通用问答、敏感信息(含 secret 一律不入库)。"
10077
- ].join(`
10078
- `);
10079
- var ArgsSchema2 = z3.object({
10080
- insight: z3.string().min(20, "insight 太短(< 20 字),描述不够具体").max(5000, "insight 过长(> 5000 字),建议拆多条").describe("完整知识条目(Markdown)。强烈推荐三段式:## 问题 / ## 原因 / ## 解决,必要时加 ## 适用场景"),
10081
- category: KhCategorySchema.describe("分类:convention / anti_pattern / architecture / reference / workflow / gotcha / tooling / decision"),
10082
- tags: z3.array(z3.string().min(1)).optional().describe("标签数组,建议格式 `tech:xxx` / `platform:xxx` / `type:xxx`,便于跨主题检索")
10083
- });
10084
- var _client2 = null;
10085
- function getClient2() {
10086
- if (!_client2)
10087
- _client2 = new KhClient;
10088
- return _client2;
10089
- }
10090
- var SECRET_PATTERNS = [
10091
- /\b[\w-]*(?:api|access|secret|private|sk)[\w-]*[_-]?(?:key|token)\b\s*[:=]\s*\S{4,}/i,
10092
- /\bsk-[A-Za-z0-9_-]{16,}/,
10093
- /\bBearer\s+[A-Za-z0-9._-]{20,}/,
10094
- /\b(?:password|passwd|pwd)\s*[:=]\s*\S{4,}/i,
10095
- /-----BEGIN [A-Z ]*PRIVATE KEY-----/
10096
- ];
10097
- function looksLikeSecret(s) {
10098
- return SECRET_PATTERNS.some((p) => p.test(s));
10099
- }
10100
- async function execute2(input) {
10101
- const parsed = ArgsSchema2.safeParse(input);
10102
- if (!parsed.success) {
10103
- return {
10104
- ok: false,
10105
- reason: "invalid_input",
10106
- message: parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ")
10107
- };
10108
- }
10109
- if (looksLikeSecret(parsed.data.insight)) {
10110
- return {
10111
- ok: false,
10112
- reason: "invalid_input",
10113
- message: "insight 内容疑似包含 secret(API key / Bearer token / 密码 / private key)。" + "请脱敏后再保存。"
10114
- };
10115
- }
10116
- return getClient2().save(parsed.data);
10117
- }
10118
- // tools/add-knowledge.ts
10119
- init_kh_client();
10120
- import { z as z4 } from "zod";
10121
- var description3 = [
10122
- "向团队知识库显式添加一条结构化知识。",
10123
- "**何时调用**:仅在用户明确要求「添加到知识库」且需要精确控制标题/内容/分类时。",
10124
- "**何时不需要**:日常对话沉淀请用 save_chat_insight(自动分类/去重/合并)。"
10125
- ].join(`
10126
- `);
10127
- var ArgsSchema3 = z4.object({
10128
- title: z4.string().min(2).max(60).describe("简短标题(2-60 字);好例子:「Prisma 7 需要 driver adapter」;坏例子:「今天修了一个 bug」"),
10129
- content: z4.string().min(10).describe("详细内容,支持 Markdown。必须独立可理解,不依赖上下文"),
10130
- category: z4.string().optional().describe("知识分类(可选,不传则服务端自动推断)"),
10131
- tags: z4.array(z4.string()).optional().describe("标签(可选,不传则服务端自动推断)"),
10132
- relatedFiles: z4.array(z4.string()).optional().describe("关联文件路径"),
10133
- relatedCommit: z4.string().optional().describe("关联 Git commit"),
10134
- relatedPR: z4.string().optional().describe("关联 PR 链接"),
10135
- scope: z4.string().optional().describe("目标可见范围(默认为用户所属组)"),
10136
- preConfirmed: z4.boolean().optional().describe("作者预确认(默认 false):勾选后置信度 base+0.15。仅在作者明确声明已验证时勾选")
10137
- });
10138
- var _client3 = null;
10139
- function getClient3() {
10140
- if (!_client3)
10141
- _client3 = new KhClient;
10142
- return _client3;
10143
- }
10144
- async function execute3(input) {
10145
- const parsed = ArgsSchema3.safeParse(input);
10146
- if (!parsed.success) {
10147
- return {
10148
- ok: false,
10149
- reason: "invalid_input",
10150
- message: parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ")
10151
- };
10152
- }
10153
- return getClient3().addKnowledge(parsed.data);
10154
- }
10155
- // tools/upload-document.ts
10156
- init_kh_client();
10157
- import { z as z5 } from "zod";
10158
- var description4 = [
10159
- "把一份完整的文本文件(md/txt/代码/json/csv 等)上传到知识库,自动切分/向量化/蒸馏。",
10160
- "**何时调用**:用户明确要求「存入知识库 / 上传到知识库」且文件已在本地。",
10161
- "**限制**:content 不超过 2MB;PDF/docx 等二进制请改用 upload_document_from_url 或 Web 上传。"
10162
- ].join(`
10163
- `);
10164
- var ChunkStrategySchema = z5.enum(["auto", "heading", "fixed", "paragraph", "separator"]);
10165
- var ArgsSchema4 = z5.object({
10166
- name: z5.string().min(1).describe("文件名,必须包含扩展名(用于推断格式),如「架构设计.md」"),
10167
- content: z5.string().min(1).describe("文件完整内容;二进制需用 base64 编码(强烈不推荐,优先 Web 上传)"),
10168
- encoding: z5.enum(["utf8", "base64"]).optional().describe("content 编码,默认 utf8"),
10169
- chunk_strategy: ChunkStrategySchema.optional().describe("切分策略,默认 auto"),
10170
- chunk_size: z5.number().int().min(200).max(8000).optional().describe("切分目标字符数,默认 1500"),
10171
- chunk_overlap: z5.number().int().min(0).max(1000).optional().describe("切分重叠字符数,默认 200"),
10172
- separator: z5.string().optional().describe("当 chunk_strategy=separator 时的自定义分隔符"),
10173
- distill: z5.boolean().optional().describe("是否触发 LLM 蒸馏(默认 true,蒸馏后原始分段归档)"),
10174
- category: z5.string().optional().describe("文档分类(可选,不传则按内容推断)"),
10175
- tags: z5.array(z5.string()).optional().describe("文档标签"),
10176
- scope: z5.string().optional().describe("目标可见范围"),
10177
- pre_confirmed: z5.boolean().optional().describe("作者预确认(默认 false):勾选后所有分段 base+0.15。仅在用户已核对内容时使用")
10178
- });
10179
- var _client4 = null;
10180
- function getClient4() {
10181
- if (!_client4)
10182
- _client4 = new KhClient;
10183
- return _client4;
10184
- }
10185
- async function execute4(input) {
10186
- const parsed = ArgsSchema4.safeParse(input);
10187
- if (!parsed.success) {
10188
- return {
10189
- ok: false,
10190
- reason: "invalid_input",
10191
- message: parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ")
10192
- };
10193
- }
10194
- return getClient4().uploadDocument(parsed.data);
10195
- }
10196
- // tools/upload-document-from-url.ts
10197
- init_kh_client();
10198
- import { z as z6 } from "zod";
10199
- var description5 = [
10200
- "从一个 HTTP(s) URL 导入文档到知识库。",
10201
- "**适用**:博客/Confluence/Notion 公开页/GitHub raw/内网 HTTP 等。",
10202
- "**何时不需要**:把当前对话总结沉淀回 KH 应优先用 save_chat_insight。"
10203
- ].join(`
10204
- `);
10205
- var ChunkStrategySchema2 = z6.enum(["auto", "heading", "fixed", "paragraph", "separator"]);
10206
- var ArgsSchema5 = z6.object({
10207
- url: z6.string().url().describe("文档的完整 HTTP(s) URL"),
10208
- chunk_strategy: ChunkStrategySchema2.optional().describe("切分策略,默认 auto"),
10209
- chunk_size: z6.number().int().min(200).max(8000).optional().describe("切分目标字符数,默认 1500"),
10210
- chunk_overlap: z6.number().int().min(0).max(1000).optional().describe("切分重叠字符数,默认 200"),
10211
- separator: z6.string().optional().describe("当 chunk_strategy=separator 时的自定义分隔符"),
10212
- distill: z6.boolean().optional().describe("是否触发 LLM 蒸馏(默认 true)"),
10213
- category: z6.string().optional().describe("文档分类(可选)"),
10214
- tags: z6.array(z6.string()).optional().describe("文档标签"),
10215
- scope: z6.string().optional().describe("目标可见范围"),
10216
- pre_confirmed: z6.boolean().optional().describe("作者预确认(默认 false):base+0.15。仅在已核对链接内容时使用")
10217
- });
10218
- var _client5 = null;
10219
- function getClient5() {
10220
- if (!_client5)
10221
- _client5 = new KhClient;
10222
- return _client5;
10223
- }
10224
- async function execute5(input) {
10225
- const parsed = ArgsSchema5.safeParse(input);
10226
- if (!parsed.success) {
10227
- return {
10228
- ok: false,
10229
- reason: "invalid_input",
10230
- message: parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ")
10231
- };
10232
- }
10233
- return getClient5().uploadDocumentFromUrl(parsed.data);
10234
- }
10235
- // tools/update-knowledge.ts
10236
- init_kh_client();
10237
- import { z as z7 } from "zod";
10238
- var description6 = [
10239
- "更新已有知识条目的内容或分类。",
10240
- "**何时调用**:发现知识条目需要补充新信息、修正错误、纠正分类时。",
10241
- "**前置**:必须有知识 ID(从 smart_search 结果获取)。"
10242
- ].join(`
10243
- `);
10244
- var ArgsSchema6 = z7.object({
10245
- knowledgeId: z7.string().min(1).describe("要更新的知识条目 ID(从 smart_search 结果获取)"),
10246
- appendContent: z7.string().optional().describe("追加到现有内容末尾的文本"),
10247
- replaceContent: z7.string().optional().describe("替换整个内容(谨慎使用,优先用 appendContent)"),
10248
- additionalTags: z7.array(z7.string()).optional().describe("需要追加的标签"),
10249
- category: z7.string().optional().describe("将该条知识改为指定分类(仅当分类明显错误时使用)")
10250
- });
10251
- var _client6 = null;
10252
- function getClient6() {
10253
- if (!_client6)
10254
- _client6 = new KhClient;
10255
- return _client6;
10256
- }
10257
- async function execute6(input) {
10258
- const parsed = ArgsSchema6.safeParse(input);
10259
- if (!parsed.success) {
10260
- return {
10261
- ok: false,
10262
- reason: "invalid_input",
10263
- message: parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ")
10264
- };
10265
- }
10266
- return getClient6().updateKnowledge(parsed.data);
10267
- }
10268
- // tools/flag-outdated.ts
10269
- init_kh_client();
10270
- import { z as z8 } from "zod";
10271
- var description7 = [
10272
- "标记知识库中的条目为过时或不正确(软删除)。",
10273
- "**何时调用**:搜到的知识与当前事实不符、被新版本取代、API 已废弃等。",
10274
- "**对比**:物理删除请用 delete_knowledge(仅作者本人或 admin)。"
10275
- ].join(`
10276
- `);
10277
- var ArgsSchema7 = z8.object({
10278
- knowledgeId: z8.string().min(1).describe("要标记的知识条目 ID(从 smart_search 结果获取)"),
10279
- reason: z8.string().min(5).max(500).describe("过时原因简述")
10280
- });
10281
- var _client7 = null;
10282
- function getClient7() {
10283
- if (!_client7)
10284
- _client7 = new KhClient;
10285
- return _client7;
10286
- }
10287
- async function execute7(input) {
10288
- const parsed = ArgsSchema7.safeParse(input);
10289
- if (!parsed.success) {
10290
- return {
10291
- ok: false,
10292
- reason: "invalid_input",
10293
- message: parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ")
10294
- };
10295
- }
10296
- return getClient7().flagOutdated(parsed.data);
10297
- }
10298
- // tools/delete-knowledge.ts
10299
- init_kh_client();
10300
- import { z as z9 } from "zod";
10301
- var description8 = [
10302
- "物理删除一条知识条目(含向量)。",
10303
- "**用途**:清理错误 scope、重复写入、彻底作废的条目。",
10304
- "**权限**:仅作者本人或 admin。一般「作废」请优先用 flag_outdated。"
10305
- ].join(`
10306
- `);
10307
- var ArgsSchema8 = z9.object({
10308
- knowledgeId: z9.string().min(1).describe("要删除的知识条目 ID(从 smart_search 结果获取)"),
10309
- reason: z9.string().min(3).max(300).describe("删除原因(写入审计日志)")
10310
- });
10311
- var _client8 = null;
10312
- function getClient8() {
10313
- if (!_client8)
10314
- _client8 = new KhClient;
10315
- return _client8;
10316
- }
10317
- async function execute8(input) {
10318
- const parsed = ArgsSchema8.safeParse(input);
10319
- if (!parsed.success) {
10320
- return {
10321
- ok: false,
10322
- reason: "invalid_input",
10323
- message: parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ")
10324
- };
10325
- }
10326
- return getClient8().deleteKnowledge(parsed.data);
10327
- }
10328
- // tools/list-recent.ts
10329
- init_kh_client();
10330
- import { z as z10 } from "zod";
10331
- var description9 = [
10332
- "查看最近新增或更新的知识条目。",
10333
- "**何时调用**:用户问「最近团队有什么新发现 / 知识库更新了什么」。"
10334
- ].join(`
10335
- `);
10336
- var ArgsSchema9 = z10.object({
10337
- days: z10.number().int().min(1).max(90).optional().describe("最近几天(默认 7)"),
10338
- limit: z10.number().int().min(1).max(50).optional().describe("返回条数(默认 10)"),
10339
- category: z10.string().optional().describe("限定分类")
10340
- });
10341
- var _client9 = null;
10342
- function getClient9() {
10343
- if (!_client9)
10344
- _client9 = new KhClient;
10345
- return _client9;
10346
- }
10347
- async function execute9(input) {
10348
- const parsed = ArgsSchema9.safeParse(input);
10349
- if (!parsed.success) {
10350
- return {
10351
- ok: false,
10352
- reason: "invalid_input",
10353
- message: parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ")
10354
- };
10355
- }
10356
- return getClient9().listRecent(parsed.data);
10357
- }
10358
- // tools/update-working-memory.ts
10359
- init_kh_client();
10360
- import { z as z11 } from "zod";
10361
- var description10 = [
10362
- "记录当前工作的实时笔记到 KH working memory。",
10363
- "**何时调用**:",
10364
- "- 用户给出操作约束(「不要重启」「先备份」)→ 立即写 constraints 分区",
10365
- "- 多步任务执行进度 → in_progress 分区",
10366
- "- 自定义主题(如 topic:debug-session)",
10367
- "**说明**:constraints 分区每次新对话自动加载,确保约束不因上下文压缩而丢失。"
10368
- ].join(`
10369
- `);
10370
- var ArgsSchema10 = z11.object({
10371
- section: z11.string().min(1).describe("记忆分区。系统分区: constraints(约束,最高优先级,不过期) / in_progress / recent_completed / system_assets / pending_decisions。也支持自定义主题如 topic:xxx"),
10372
- items: z11.array(z11.object({
10373
- content: z11.string().min(1).describe("记忆内容"),
10374
- priority: z11.number().optional().describe("优先级(0-10)")
10375
- })).describe("要写入的记忆条目"),
10376
- replace: z11.boolean().optional().describe("是否替换该分区全部内容(默认 false=追加)")
10377
- });
10378
- var _client10 = null;
10379
- function getClient10() {
10380
- if (!_client10)
10381
- _client10 = new KhClient;
10382
- return _client10;
10383
- }
10384
- async function execute10(input) {
10385
- const parsed = ArgsSchema10.safeParse(input);
10386
- if (!parsed.success) {
10387
- return {
10388
- ok: false,
10389
- reason: "invalid_input",
10390
- message: parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ")
10391
- };
10392
- }
10393
- return getClient10().updateWorkingMemory(parsed.data);
10394
- }
10395
- // tools/get-working-memory.ts
10396
- init_kh_client();
10397
- import { z as z12 } from "zod";
10398
- var description11 = [
10399
- "读取之前会话记录的工作状态、操作约束和临时笔记。",
10400
- "**何时调用**:开始新对话时恢复上下文,或排查「之前定的约束是什么」。"
10401
- ].join(`
10402
- `);
10403
- var ArgsSchema11 = z12.object({
10404
- section: z12.string().optional().describe("要读取的分区名;不传则返回全部分区")
10405
- });
10406
- var _client11 = null;
10407
- function getClient11() {
10408
- if (!_client11)
10409
- _client11 = new KhClient;
10410
- return _client11;
10411
- }
10412
- async function execute11(input) {
10413
- const parsed = ArgsSchema11.safeParse(input);
10414
- if (!parsed.success) {
10415
- return {
10416
- ok: false,
10417
- reason: "invalid_input",
10418
- message: parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ")
10419
- };
10420
- }
10421
- return getClient11().getWorkingMemory(parsed.data);
10422
- }
10423
- // tools/update-user-preference.ts
10424
- init_kh_client();
10425
- import { z as z13 } from "zod";
10426
- var description12 = [
10427
- "记录用户的个人偏好(如语言、编码风格、签名、常用工具等)。",
10428
- "**何时调用**:用户表达跨项目通用习惯(「用中文回复」「函数式写法」「作者署名 Andy」),或重复纠正同一行为时。",
10429
- "**生效**:偏好跨项目永久,每次对话自动加载。"
10430
- ].join(`
10431
- `);
10432
- var ArgsSchema12 = z13.object({
10433
- key: z13.string().min(1).max(64).describe("偏好键名,如 language / coding_style / signature / editor_preference"),
10434
- value: z13.string().min(1).max(500).describe("偏好值,简短描述"),
10435
- action: z13.enum(["set", "delete"]).optional().describe("操作:set=设置/更新(默认),delete=删除")
10436
- });
10437
- var _client12 = null;
10438
- function getClient12() {
10439
- if (!_client12)
10440
- _client12 = new KhClient;
10441
- return _client12;
10442
- }
10443
- async function execute12(input) {
10444
- const parsed = ArgsSchema12.safeParse(input);
10445
- if (!parsed.success) {
10446
- return {
10447
- ok: false,
10448
- reason: "invalid_input",
10449
- message: parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ")
10450
- };
10451
- }
10452
- return getClient12().updateUserPreference(parsed.data);
10453
- }
10454
- // tools/web-search.ts
10455
- init_kh_client();
10456
- import { z as z14 } from "zod";
10457
- var description13 = [
10458
- "通过公开网页搜索引擎查询外部信息(SearXNG 聚合 DuckDuckGo/Brave/Wikipedia/GitHub/StackOverflow 等)。",
10459
- "**适用**:KH 没有的时效信息、官方文档、外部讨论。",
10460
- "**对比**:团队历史经验仍优先用 smart_search。"
10461
- ].join(`
10462
- `);
10463
- var ArgsSchema13 = z14.object({
10464
- query: z14.string().min(1).describe("搜索词(直接用问题原文或关键词)"),
10465
- limit: z14.number().int().min(1).max(10).optional().describe("返回条数(1-10),默认 5"),
10466
- time_range: z14.enum(["", "day", "week", "month", "year"]).optional().describe("时间筛选:day/week/month/year;留空 = 不限")
10467
- });
10468
- var _client13 = null;
10469
- function getClient13() {
10470
- if (!_client13)
10471
- _client13 = new KhClient;
10472
- return _client13;
10473
- }
10474
- async function execute13(input) {
10475
- const parsed = ArgsSchema13.safeParse(input);
10476
- if (!parsed.success) {
10477
- return {
10478
- ok: false,
10479
- reason: "invalid_input",
10480
- message: parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ")
10481
- };
10482
- }
10483
- return getClient13().webSearch(parsed.data);
10484
- }
10485
- // tools/generate-image.ts
10486
- init_kh_client();
10487
- import { z as z15 } from "zod";
10488
- var description14 = [
10489
- "根据文字描述生成图片(插画 / 图标 / 示意图 / 海报)。",
10490
- "**适用**:用户明确要求「生成 / 画一张…图」、需要可视化示意图、或补充配图。",
10491
- "**反例**:搜「已有图片」不是这个工具——请用 web_search。",
10492
- "生成成功后返回的 Markdown 图片语法请原样保留在你的回答中。"
10493
- ].join(`
10494
- `);
10495
- var ArgsSchema14 = z15.object({
10496
- prompt: z15.string().min(4).max(4000).describe("详尽描述画面:主体、风格(写实/插画/3D/像素/卡通)、色调、构图、氛围。越具体越好"),
10497
- n: z15.number().int().min(1).max(3).optional().describe("生成张数(1-3,默认 1)"),
10498
- size: z15.string().regex(/^(?:auto|\d{3,4}x\d{3,4})$/).optional().describe("尺寸:1024x1024 正方 / 1024x1536 竖 / 1536x1024 横 / auto。不确定就传 auto"),
10499
- transparent: z15.boolean().optional().describe("是否需要透明背景(仅图标/贴纸用)"),
10500
- alt: z15.string().max(200).optional().describe("alt 文字;不传则用 prompt 前 120 字")
10501
- });
10502
- var _client14 = null;
10503
- function getClient14() {
10504
- if (!_client14)
10505
- _client14 = new KhClient;
10506
- return _client14;
10507
- }
10508
- async function execute14(input) {
10509
- const parsed = ArgsSchema14.safeParse(input);
10510
- if (!parsed.success) {
10511
- return {
10512
- ok: false,
10513
- reason: "invalid_input",
10514
- message: parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ")
10515
- };
10516
- }
10517
- return getClient14().generateImage(parsed.data);
10518
- }
9405
+ // plugins/codeforge-tools.ts
9406
+ import { tool } from "@opencode-ai/plugin";
9407
+
10519
9408
  // tools/ast-edit.ts
10520
9409
  import { promises as fs3 } from "node:fs";
10521
9410
  import * as path5 from "node:path";
10522
- import { z as z16 } from "zod";
9411
+ import { z } from "zod";
10523
9412
 
10524
9413
  // lib/ast-edit-engine.ts
10525
9414
  import * as crypto2 from "node:crypto";
@@ -10848,7 +9737,7 @@ function finish(before, after, beforeHash, affected) {
10848
9737
  }
10849
9738
 
10850
9739
  // tools/ast-edit.ts
10851
- var description15 = [
9740
+ var description = [
10852
9741
  "AST 风格的精确文件编辑:anchor + 哈希校验,避免 LLM 「整文件重写」误改无关代码。",
10853
9742
  "**anchor 仅支持单行**:含 `\\n` 的多行 anchor 会被直接拒绝(reason=invalid_input);多行改动请直接用 `write` 整文件。",
10854
9743
  "**何时调用**:",
@@ -10864,39 +9753,39 @@ var description15 = [
10864
9753
  "- 改动直接写入 worktree(session ↔ worktree 一一绑定,由 session-worktree-guard 隔离主仓)"
10865
9754
  ].join(`
10866
9755
  `);
10867
- var Common = z16.object({
10868
- target: z16.string().min(1).describe("目标文件路径(相对 cwd 或绝对)"),
10869
- before_hash: z16.string().optional().describe("操作前的 sha256 hex(不传则跳过校验,但强烈建议传;新文件传 null)"),
10870
- description: z16.string().optional().describe("可选:变更说明(写日志/审计用,不影响行为)")
9756
+ var Common = z.object({
9757
+ target: z.string().min(1).describe("目标文件路径(相对 cwd 或绝对)"),
9758
+ before_hash: z.string().optional().describe("操作前的 sha256 hex(不传则跳过校验,但强烈建议传;新文件传 null)"),
9759
+ description: z.string().optional().describe("可选:变更说明(写日志/审计用,不影响行为)")
10871
9760
  });
10872
9761
  var AnchorAction = Common.extend({
10873
- action: z16.enum([
9762
+ action: z.enum([
10874
9763
  "replace_anchor",
10875
9764
  "insert_after_anchor",
10876
9765
  "insert_before_anchor"
10877
9766
  ]),
10878
- anchor: z16.string().min(1).describe("anchor 文本子串或正则源(必须单行;含 \\n 会被拒绝,多行改动请用 write 整文件)"),
10879
- regex: z16.boolean().optional().describe("anchor 是否按 RegExp 解释,默认 false"),
10880
- occurrence: z16.number().int().min(1).optional().describe("第几次匹配(1-based),默认 1;多次命中时必须显式指定"),
10881
- payload: z16.string().describe("要写入的内容;引擎会按文件原 EOL 处理")
9767
+ anchor: z.string().min(1).describe("anchor 文本子串或正则源(必须单行;含 \\n 会被拒绝,多行改动请用 write 整文件)"),
9768
+ regex: z.boolean().optional().describe("anchor 是否按 RegExp 解释,默认 false"),
9769
+ occurrence: z.number().int().min(1).optional().describe("第几次匹配(1-based),默认 1;多次命中时必须显式指定"),
9770
+ payload: z.string().describe("要写入的内容;引擎会按文件原 EOL 处理")
10882
9771
  });
10883
9772
  var DeleteAction = Common.extend({
10884
- action: z16.literal("delete_range"),
10885
- start: z16.number().int().min(1).describe("起始行(1-based, inclusive)"),
10886
- end: z16.number().int().min(1).describe("结束行(1-based, inclusive)")
9773
+ action: z.literal("delete_range"),
9774
+ start: z.number().int().min(1).describe("起始行(1-based, inclusive)"),
9775
+ end: z.number().int().min(1).describe("结束行(1-based, inclusive)")
10887
9776
  });
10888
9777
  var RenameAction = Common.extend({
10889
- action: z16.literal("rename_symbol"),
10890
- old_name: z16.string().regex(/^[A-Za-z_$][\w$]*$/).describe("旧标识符"),
10891
- new_name: z16.string().regex(/^[A-Za-z_$][\w$]*$/).describe("新标识符")
9778
+ action: z.literal("rename_symbol"),
9779
+ old_name: z.string().regex(/^[A-Za-z_$][\w$]*$/).describe("旧标识符"),
9780
+ new_name: z.string().regex(/^[A-Za-z_$][\w$]*$/).describe("新标识符")
10892
9781
  });
10893
- var ArgsSchema15 = z16.discriminatedUnion("action", [
9782
+ var ArgsSchema = z.discriminatedUnion("action", [
10894
9783
  AnchorAction,
10895
9784
  DeleteAction,
10896
9785
  RenameAction
10897
9786
  ]);
10898
- async function execute15(input, opts = {}) {
10899
- const parsed = ArgsSchema15.safeParse(input);
9787
+ async function execute(input, opts = {}) {
9788
+ const parsed = ArgsSchema.safeParse(input);
10900
9789
  if (!parsed.success) {
10901
9790
  return {
10902
9791
  ok: false,
@@ -10981,7 +9870,7 @@ async function execute15(input, opts = {}) {
10981
9870
  };
10982
9871
  }
10983
9872
  // tools/repo-map.ts
10984
- import { z as z17 } from "zod";
9873
+ import { z as z2 } from "zod";
10985
9874
 
10986
9875
  // lib/repo-map.ts
10987
9876
  import { promises as fs4 } from "node:fs";
@@ -11342,7 +10231,7 @@ function escapeLabel(s) {
11342
10231
  }
11343
10232
 
11344
10233
  // tools/repo-map.ts
11345
- var description16 = [
10234
+ var description2 = [
11346
10235
  "扫描当前仓库,输出按 PageRank 排序的「项目地图」(top N 关键文件 + exports + deps)。",
11347
10236
  "**何时调用**:",
11348
10237
  "- planner agent 接到新需求 → 先 smart_search → 再 repo-map 找代码入口",
@@ -11354,16 +10243,16 @@ var description16 = [
11354
10243
  "- 项目地图本会话已生成且未发生大改"
11355
10244
  ].join(`
11356
10245
  `);
11357
- var ArgsSchema16 = z17.object({
11358
- root: z17.string().optional().describe("扫描根目录,默认当前 cwd;通常无需传"),
11359
- top: z17.number().int().min(1).max(100).optional().describe("展示 top N 文件,默认 20;想要全图可传 100"),
11360
- focus: z17.string().optional().describe("聚焦文件(仓内 POSIX 相对路径):把它和它的直接依赖 / 反向依赖排在前面"),
11361
- max_files: z17.number().int().min(10).max(5000).optional().describe("扫描文件数上限,默认 500;超过自动截断"),
11362
- format: z17.enum(["markdown", "mermaid", "both"]).optional().describe("输出格式:markdown(默认,文字 + 星级评分)/mermaid(flowchart 流程图,可贴 mermaid.live 渲染)/both(两者,方便对照)"),
11363
- _raw: z17.boolean().optional()
10246
+ var ArgsSchema2 = z2.object({
10247
+ root: z2.string().optional().describe("扫描根目录,默认当前 cwd;通常无需传"),
10248
+ top: z2.number().int().min(1).max(100).optional().describe("展示 top N 文件,默认 20;想要全图可传 100"),
10249
+ focus: z2.string().optional().describe("聚焦文件(仓内 POSIX 相对路径):把它和它的直接依赖 / 反向依赖排在前面"),
10250
+ max_files: z2.number().int().min(10).max(5000).optional().describe("扫描文件数上限,默认 500;超过自动截断"),
10251
+ format: z2.enum(["markdown", "mermaid", "both"]).optional().describe("输出格式:markdown(默认,文字 + 星级评分)/mermaid(flowchart 流程图,可贴 mermaid.live 渲染)/both(两者,方便对照)"),
10252
+ _raw: z2.boolean().optional()
11364
10253
  });
11365
- async function execute16(input) {
11366
- const parsed = ArgsSchema16.safeParse(input);
10254
+ async function execute2(input) {
10255
+ const parsed = ArgsSchema2.safeParse(input);
11367
10256
  if (!parsed.success) {
11368
10257
  return {
11369
10258
  ok: false,
@@ -11415,7 +10304,7 @@ async function execute16(input) {
11415
10304
  }
11416
10305
  }
11417
10306
  // tools/rules-debug.ts
11418
- import { z as z18 } from "zod";
10307
+ import { z as z3 } from "zod";
11419
10308
 
11420
10309
  // lib/rules-loader.ts
11421
10310
  import { promises as fs5 } from "node:fs";
@@ -11533,7 +10422,7 @@ async function debugRules(opts = {}) {
11533
10422
  }
11534
10423
 
11535
10424
  // tools/rules-debug.ts
11536
- var description17 = [
10425
+ var description3 = [
11537
10426
  "列出 CodeForge 三层规则系统当前生效的全部规则文件 + 加载顺序 + 优先级。",
11538
10427
  "**何时调用**:",
11539
10428
  "- 用户问「我改的规则为什么没生效 / 哪条规则覆盖了它」",
@@ -11541,18 +10430,18 @@ var description17 = [
11541
10430
  "- 排查规则文件读取失败原因"
11542
10431
  ].join(`
11543
10432
  `);
11544
- var ArgsSchema17 = z18.object({
11545
- current_agent: z18.string().optional().describe("当前 agent 名(用于加载 .codeforge/agents/<name>.md 那一层)"),
11546
- root: z18.string().optional().describe("项目根目录,默认 cwd"),
11547
- home_dir: z18.string().optional().describe("覆盖个人规则目录(默认 ~/.codeforge/rules)"),
11548
- project_dir: z18.string().optional().describe("覆盖项目规则目录(默认 <root>/.codeforge/rules)"),
11549
- skip_personal: z18.boolean().optional(),
11550
- skip_project: z18.boolean().optional(),
11551
- skip_agent: z18.boolean().optional(),
11552
- markdown: z18.boolean().optional().describe("默认 true:额外渲染 markdown 摘要给 LLM 看")
10433
+ var ArgsSchema3 = z3.object({
10434
+ current_agent: z3.string().optional().describe("当前 agent 名(用于加载 .codeforge/agents/<name>.md 那一层)"),
10435
+ root: z3.string().optional().describe("项目根目录,默认 cwd"),
10436
+ home_dir: z3.string().optional().describe("覆盖个人规则目录(默认 ~/.codeforge/rules)"),
10437
+ project_dir: z3.string().optional().describe("覆盖项目规则目录(默认 <root>/.codeforge/rules)"),
10438
+ skip_personal: z3.boolean().optional(),
10439
+ skip_project: z3.boolean().optional(),
10440
+ skip_agent: z3.boolean().optional(),
10441
+ markdown: z3.boolean().optional().describe("默认 true:额外渲染 markdown 摘要给 LLM 看")
11553
10442
  });
11554
- async function execute17(input) {
11555
- const parsed = ArgsSchema17.safeParse(input);
10443
+ async function execute3(input) {
10444
+ const parsed = ArgsSchema3.safeParse(input);
11556
10445
  if (!parsed.success) {
11557
10446
  return {
11558
10447
  ok: false,
@@ -11619,13 +10508,11 @@ function renderMarkdown2(r) {
11619
10508
  `);
11620
10509
  }
11621
10510
  // tools/review-approval.ts
11622
- import { z as z19 } from "zod";
10511
+ import { z as z4 } from "zod";
11623
10512
 
11624
10513
  // lib/approval-store.ts
11625
- init_runtime_paths();
11626
10514
  import { promises as fs6 } from "node:fs";
11627
10515
  import * as path8 from "node:path";
11628
-
11629
10516
  class ApprovalStore {
11630
10517
  base;
11631
10518
  constructor(base) {
@@ -11739,7 +10626,7 @@ class ApprovalStore {
11739
10626
  }
11740
10627
 
11741
10628
  // tools/review-approval.ts
11742
- var description18 = [
10629
+ var description4 = [
11743
10630
  "reviewer 专用:写入 APPROVE 审批记录。",
11744
10631
  "**何时调用**:reviewer 给出 `## Decision\\nAPPROVE` 之前必须调本工具,",
11745
10632
  "供 `/merge` 闭环或 codeforge orchestrator 后续放行依据。",
@@ -11748,15 +10635,15 @@ var description18 = [
11748
10635
  "**fallback**:codeforge 解析 reviewer boomerang 见 APPROVE 但无记录 → 自动以 source='codeforge-fallback' 补写。"
11749
10636
  ].join(`
11750
10637
  `);
11751
- var ArgsSchema18 = z19.object({
11752
- verdict: z19.enum(["APPROVE", "APPROVE_WITH_NOTES"]).describe("审批裁决;REQUEST_CHANGES / BLOCK 不应调本工具"),
11753
- pendingIds: z19.array(z19.string().min(1)).min(1, "pendingIds 至少 1 条").describe("本次 APPROVE 覆盖的 id 列表。推荐 session:<sid> / plan:<plan_id> / decision:<hash>;旧 pc-xxx 兼容"),
11754
- notes: z19.string().min(1, "notes 不能为空").max(2000, "notes 过长(> 2000 字),建议拆条").describe("审阅意见摘要(建议 ≤ 500 字)"),
11755
- decisionLine: z19.string().optional().describe("`## Decision` 节首行原文(默认 verdict 字面量,机审证据)"),
11756
- source: z19.enum(["reviewer", "codeforge-fallback"]).optional().describe("写入来源;默认 'reviewer',codeforge 补写时传 'codeforge-fallback'"),
11757
- reviewerAgent: z19.string().optional().describe("写入 agent name(默认 'reviewer';fallback 时为 'codeforge')"),
11758
- sessionId: z19.string().optional().describe("reviewer 子 session id(boomerang 溯源用,可选)"),
11759
- model: z19.string().optional().describe("审批模型 id(审计用,可选)")
10638
+ var ArgsSchema4 = z4.object({
10639
+ verdict: z4.enum(["APPROVE", "APPROVE_WITH_NOTES"]).describe("审批裁决;REQUEST_CHANGES / BLOCK 不应调本工具"),
10640
+ pendingIds: z4.array(z4.string().min(1)).min(1, "pendingIds 至少 1 条").describe("本次 APPROVE 覆盖的 id 列表。推荐 session:<sid> / plan:<plan_id> / decision:<hash>;旧 pc-xxx 兼容"),
10641
+ notes: z4.string().min(1, "notes 不能为空").max(2000, "notes 过长(> 2000 字),建议拆条").describe("审阅意见摘要(建议 ≤ 500 字)"),
10642
+ decisionLine: z4.string().optional().describe("`## Decision` 节首行原文(默认 verdict 字面量,机审证据)"),
10643
+ source: z4.enum(["reviewer", "codeforge-fallback"]).optional().describe("写入来源;默认 'reviewer',codeforge 补写时传 'codeforge-fallback'"),
10644
+ reviewerAgent: z4.string().optional().describe("写入 agent name(默认 'reviewer';fallback 时为 'codeforge')"),
10645
+ sessionId: z4.string().optional().describe("reviewer 子 session id(boomerang 溯源用,可选)"),
10646
+ model: z4.string().optional().describe("审批模型 id(审计用,可选)")
11760
10647
  });
11761
10648
  var _approvalStore = null;
11762
10649
  function getApprovalStore() {
@@ -11764,8 +10651,8 @@ function getApprovalStore() {
11764
10651
  _approvalStore = ApprovalStore.forProject(process.cwd());
11765
10652
  return _approvalStore;
11766
10653
  }
11767
- async function execute18(input) {
11768
- const parsed = ArgsSchema18.safeParse(input);
10654
+ async function execute4(input) {
10655
+ const parsed = ArgsSchema4.safeParse(input);
11769
10656
  if (!parsed.success) {
11770
10657
  return {
11771
10658
  ok: false,
@@ -11798,10 +10685,9 @@ async function execute18(input) {
11798
10685
  return { ok: true, written };
11799
10686
  }
11800
10687
  // tools/browser-navigate.ts
11801
- import { z as z20 } from "zod";
10688
+ import { z as z5 } from "zod";
11802
10689
 
11803
10690
  // lib/browser-control.ts
11804
- init_runtime_paths();
11805
10691
  import * as path9 from "node:path";
11806
10692
  var DEFAULT_CONFIG2 = {
11807
10693
  enabled: false,
@@ -12025,14 +10911,14 @@ async function createBrowserController(opts = {}) {
12025
10911
  }
12026
10912
 
12027
10913
  // tools/browser-navigate.ts
12028
- var description19 = [
10914
+ var description5 = [
12029
10915
  "打开指定 URL(受 tools.browser 配置控制;默认禁用)。",
12030
10916
  "**何时调用**:需要看 web 页面真实渲染、调试前端、复现 bug 报告链接。",
12031
10917
  "**注意**:仅允许 http/https;file:/data: 等协议被默认安全策略拒绝。"
12032
10918
  ].join(`
12033
10919
  `);
12034
- var ArgsSchema19 = z20.object({
12035
- url: z20.string().min(1).describe("要打开的 URL;必须是 http(s) 协议")
10920
+ var ArgsSchema5 = z5.object({
10921
+ url: z5.string().min(1).describe("要打开的 URL;必须是 http(s) 协议")
12036
10922
  });
12037
10923
  var _controller = null;
12038
10924
  var _cfg;
@@ -12042,8 +10928,8 @@ async function getController() {
12042
10928
  }
12043
10929
  return _controller;
12044
10930
  }
12045
- async function execute19(input) {
12046
- const parsed = ArgsSchema19.safeParse(input);
10931
+ async function execute5(input) {
10932
+ const parsed = ArgsSchema5.safeParse(input);
12047
10933
  if (!parsed.success) {
12048
10934
  return {
12049
10935
  ok: false,
@@ -12063,15 +10949,15 @@ async function execute19(input) {
12063
10949
  }
12064
10950
  }
12065
10951
  // tools/browser-click.ts
12066
- import { z as z21 } from "zod";
12067
- var description20 = [
10952
+ import { z as z6 } from "zod";
10953
+ var description6 = [
12068
10954
  "点击页面元素。属于「写操作」,semi 模式下应当让用户确认。",
12069
10955
  "**何时调用**:表单提交、按钮触发、SPA 导航。",
12070
10956
  "**避坑**:selector 优先用稳定属性(data-testid > role > 文本 > id),尽量不用 xpath。"
12071
10957
  ].join(`
12072
10958
  `);
12073
- var ArgsSchema20 = z21.object({
12074
- selector: z21.string().min(1).describe("CSS / Playwright locator 字符串,必须能唯一定位")
10959
+ var ArgsSchema6 = z6.object({
10960
+ selector: z6.string().min(1).describe("CSS / Playwright locator 字符串,必须能唯一定位")
12075
10961
  });
12076
10962
  var _controller2 = null;
12077
10963
  var _cfg2;
@@ -12081,8 +10967,8 @@ async function getController2() {
12081
10967
  }
12082
10968
  return _controller2;
12083
10969
  }
12084
- async function execute20(input) {
12085
- const parsed = ArgsSchema20.safeParse(input);
10970
+ async function execute6(input) {
10971
+ const parsed = ArgsSchema6.safeParse(input);
12086
10972
  if (!parsed.success) {
12087
10973
  return {
12088
10974
  ok: false,
@@ -12104,16 +10990,16 @@ async function execute20(input) {
12104
10990
  }
12105
10991
  }
12106
10992
  // tools/browser-fill.ts
12107
- import { z as z22 } from "zod";
12108
- var description21 = [
10993
+ import { z as z7 } from "zod";
10994
+ var description7 = [
12109
10995
  "向 input/textarea/contenteditable 元素写入文本。",
12110
10996
  "**何时调用**:登录表单、搜索框、富文本初始化。",
12111
10997
  "**安全**:value 不会被自动 mask;不要把真实凭据塞进来。"
12112
10998
  ].join(`
12113
10999
  `);
12114
- var ArgsSchema21 = z22.object({
12115
- selector: z22.string().min(1).describe("CSS / Playwright locator"),
12116
- value: z22.string().describe("要填入的文本;原样写入,不做转义")
11000
+ var ArgsSchema7 = z7.object({
11001
+ selector: z7.string().min(1).describe("CSS / Playwright locator"),
11002
+ value: z7.string().describe("要填入的文本;原样写入,不做转义")
12117
11003
  });
12118
11004
  var _controller3 = null;
12119
11005
  var _cfg3;
@@ -12123,8 +11009,8 @@ async function getController3() {
12123
11009
  }
12124
11010
  return _controller3;
12125
11011
  }
12126
- async function execute21(input) {
12127
- const parsed = ArgsSchema21.safeParse(input);
11012
+ async function execute7(input) {
11013
+ const parsed = ArgsSchema7.safeParse(input);
12128
11014
  if (!parsed.success) {
12129
11015
  return {
12130
11016
  ok: false,
@@ -12146,16 +11032,16 @@ async function execute21(input) {
12146
11032
  }
12147
11033
  }
12148
11034
  // tools/browser-screenshot.ts
12149
- import { z as z23 } from "zod";
12150
- var description22 = [
11035
+ import { z as z8 } from "zod";
11036
+ var description8 = [
12151
11037
  "截取当前页面(或指定元素)的屏幕快照。",
12152
11038
  "**何时调用**:前端 dev 调试、回归对比、用户报 bug 没截图时让 agent 自取。",
12153
11039
  "**注意**:必须先 navigate;输出路径写到 runtime 目录的 browser/screenshots/(XDG 全局位置,详见 lib/runtime-paths.ts)。"
12154
11040
  ].join(`
12155
11041
  `);
12156
- var ArgsSchema22 = z23.object({
12157
- fullPage: z23.boolean().optional().describe("是否截全长页面(默认仅可视区)"),
12158
- selector: z23.string().optional().describe("CSS 选择器;指定时只截该元素")
11042
+ var ArgsSchema8 = z8.object({
11043
+ fullPage: z8.boolean().optional().describe("是否截全长页面(默认仅可视区)"),
11044
+ selector: z8.string().optional().describe("CSS 选择器;指定时只截该元素")
12159
11045
  });
12160
11046
  var _controller4 = null;
12161
11047
  var _cfg4;
@@ -12165,8 +11051,8 @@ async function getController4() {
12165
11051
  }
12166
11052
  return _controller4;
12167
11053
  }
12168
- async function execute22(input) {
12169
- const parsed = ArgsSchema22.safeParse(input);
11054
+ async function execute8(input) {
11055
+ const parsed = ArgsSchema8.safeParse(input);
12170
11056
  if (!parsed.success) {
12171
11057
  return { ok: false, error: parsed.error.issues.map((i) => i.message).join("; ") };
12172
11058
  }
@@ -12178,16 +11064,16 @@ async function execute22(input) {
12178
11064
  }
12179
11065
  }
12180
11066
  // tools/browser-console.ts
12181
- import { z as z24 } from "zod";
12182
- var description23 = [
11067
+ import { z as z9 } from "zod";
11068
+ var description9 = [
12183
11069
  "读取浏览器控制台缓冲区(log/info/warn/error/debug)。",
12184
11070
  "**何时调用**:调前端 bug、看页面 runtime error、跟 React/Vue 警告。",
12185
11071
  "**注意**:缓冲区上限 500 条,超出会丢最早;只能读 navigate 之后的日志。"
12186
11072
  ].join(`
12187
11073
  `);
12188
- var ArgsSchema23 = z24.object({
12189
- level: z24.enum(["log", "info", "warn", "error", "debug"]).optional().describe("只过滤指定级别;缺省返回全部"),
12190
- sinceTs: z24.number().optional().describe("只返回 timestamp >= sinceTs 的条目")
11074
+ var ArgsSchema9 = z9.object({
11075
+ level: z9.enum(["log", "info", "warn", "error", "debug"]).optional().describe("只过滤指定级别;缺省返回全部"),
11076
+ sinceTs: z9.number().optional().describe("只返回 timestamp >= sinceTs 的条目")
12191
11077
  });
12192
11078
  var _controller5 = null;
12193
11079
  var _cfg5;
@@ -12197,8 +11083,8 @@ async function getController5() {
12197
11083
  }
12198
11084
  return _controller5;
12199
11085
  }
12200
- async function execute23(input) {
12201
- const parsed = ArgsSchema23.safeParse(input);
11086
+ async function execute9(input) {
11087
+ const parsed = ArgsSchema9.safeParse(input);
12202
11088
  if (!parsed.success) {
12203
11089
  return {
12204
11090
  ok: false,
@@ -12219,16 +11105,16 @@ async function execute23(input) {
12219
11105
  }
12220
11106
  }
12221
11107
  // tools/browser-network.ts
12222
- import { z as z25 } from "zod";
12223
- var description24 = [
11108
+ import { z as z10 } from "zod";
11109
+ var description10 = [
12224
11110
  "读取浏览器网络请求缓冲区(含 status / 失败原因 / 耗时)。",
12225
11111
  "**何时调用**:调 API 调用 4xx/5xx、CORS 报错、查 page 加载耗时分布。",
12226
11112
  "**注意**:缓冲区上限 500 条;只看 navigate 之后发起的请求。"
12227
11113
  ].join(`
12228
11114
  `);
12229
- var ArgsSchema24 = z25.object({
12230
- method: z25.string().optional().describe("HTTP 方法过滤(GET/POST/...),不区分大小写"),
12231
- sinceTs: z25.number().optional().describe("只返回 start_ts >= sinceTs 的请求")
11115
+ var ArgsSchema10 = z10.object({
11116
+ method: z10.string().optional().describe("HTTP 方法过滤(GET/POST/...),不区分大小写"),
11117
+ sinceTs: z10.number().optional().describe("只返回 start_ts >= sinceTs 的请求")
12232
11118
  });
12233
11119
  var _controller6 = null;
12234
11120
  var _cfg6;
@@ -12238,8 +11124,8 @@ async function getController6() {
12238
11124
  }
12239
11125
  return _controller6;
12240
11126
  }
12241
- async function execute24(input) {
12242
- const parsed = ArgsSchema24.safeParse(input);
11127
+ async function execute10(input) {
11128
+ const parsed = ArgsSchema10.safeParse(input);
12243
11129
  if (!parsed.success) {
12244
11130
  return {
12245
11131
  ok: false,
@@ -12260,7 +11146,7 @@ async function execute24(input) {
12260
11146
  }
12261
11147
  }
12262
11148
  // tools/model-chain.ts
12263
- import { z as z26 } from "zod";
11149
+ import { z as z11 } from "zod";
12264
11150
 
12265
11151
  // lib/model-config.ts
12266
11152
  import { promises as fs7 } from "node:fs";
@@ -12694,7 +11580,7 @@ function listAllAgentChains(config) {
12694
11580
  }
12695
11581
 
12696
11582
  // tools/model-chain.ts
12697
- var description25 = [
11583
+ var description11 = [
12698
11584
  "查询 CodeForge 配置文件 codeforge.json:每个 agent 的主模型 + fallback 链。",
12699
11585
  "**何时调用**:",
12700
11586
  "- 用户问「coder 用什么模型 / 备用是什么」",
@@ -12703,14 +11589,14 @@ var description25 = [
12703
11589
  "返回 JSON:{ ok, agents: [...], current_chain?, next_fallback? }"
12704
11590
  ].join(`
12705
11591
  `);
12706
- var ArgsSchema25 = z26.object({
12707
- agent: z26.string().optional().describe("查指定 agent;不传 → 列出全部"),
12708
- current: z26.string().optional().describe("当前已用过的模型(<provider>/<id>),用于算下一档"),
12709
- root: z26.string().optional().describe("项目根目录,默认 process.cwd()"),
12710
- config_file: z26.string().optional().describe("配置文件名;默认 codeforge.json")
11592
+ var ArgsSchema11 = z11.object({
11593
+ agent: z11.string().optional().describe("查指定 agent;不传 → 列出全部"),
11594
+ current: z11.string().optional().describe("当前已用过的模型(<provider>/<id>),用于算下一档"),
11595
+ root: z11.string().optional().describe("项目根目录,默认 process.cwd()"),
11596
+ config_file: z11.string().optional().describe("配置文件名;默认 codeforge.json")
12711
11597
  });
12712
- async function execute25(rawArgs) {
12713
- const parsed = ArgsSchema25.safeParse(rawArgs ?? {});
11598
+ async function execute11(rawArgs) {
11599
+ const parsed = ArgsSchema11.safeParse(rawArgs ?? {});
12714
11600
  if (!parsed.success) {
12715
11601
  return {
12716
11602
  ok: false,
@@ -12763,11 +11649,10 @@ function toEntry(r) {
12763
11649
  };
12764
11650
  }
12765
11651
  // tools/session-merge.ts
12766
- import { z as z27 } from "zod";
11652
+ import { z as z12 } from "zod";
12767
11653
 
12768
11654
  // lib/session-worktree.ts
12769
11655
  init_worktree_ops();
12770
- init_runtime_paths();
12771
11656
  import { execFile as execFile3 } from "node:child_process";
12772
11657
  import { promises as fs10 } from "node:fs";
12773
11658
  import * as path13 from "node:path";
@@ -12887,7 +11772,6 @@ function sleep(ms) {
12887
11772
  }
12888
11773
 
12889
11774
  // lib/session-worktree.ts
12890
- init_global_config();
12891
11775
  var REGISTRY_VERSION = 1;
12892
11776
  var DEFAULT_WORKTREE_SUBDIR = path13.join(".git", "codeforge-worktrees");
12893
11777
  function registryDir(mainRoot) {
@@ -13504,7 +12388,7 @@ async function runMergeLoop(opts) {
13504
12388
  });
13505
12389
  } catch (err) {
13506
12390
  const e = err;
13507
- if (isAbortError2(e)) {
12391
+ if (isAbortError(e)) {
13508
12392
  await handleAbortDirty(opts, config, entry);
13509
12393
  throw e;
13510
12394
  }
@@ -13588,7 +12472,7 @@ async function runMergeLoop(opts) {
13588
12472
  continue;
13589
12473
  } catch (err) {
13590
12474
  const e = err;
13591
- if (isAbortError2(e)) {
12475
+ if (isAbortError(e)) {
13592
12476
  await handleAbortDirty(opts, config, entry);
13593
12477
  throw e;
13594
12478
  }
@@ -13654,7 +12538,7 @@ async function handleAbortDirty(opts, config, entry) {
13654
12538
  console.warn(`[merge-loop] abort-dirty 处理失败 (session=${opts.sessionId}): ${err instanceof Error ? err.message : String(err)}`);
13655
12539
  }
13656
12540
  }
13657
- function isAbortError2(err) {
12541
+ function isAbortError(err) {
13658
12542
  return err instanceof Error && err.name === "AbortError";
13659
12543
  }
13660
12544
  function withTimeout2(p, ms, label, signal, hbOpts) {
@@ -13707,7 +12591,7 @@ function withTimeout2(p, ms, label, signal, hbOpts) {
13707
12591
  }
13708
12592
 
13709
12593
  // tools/session-merge.ts
13710
- var description26 = [
12594
+ var description12 = [
13711
12595
  "合并当前 session 绑定的 worktree 改动到主工作区(squash + review-fix-review 闭环)。",
13712
12596
  "**何时调用**:",
13713
12597
  "- action=status:查询当前 session worktree 状态(任意 agent 可调)",
@@ -13720,26 +12604,26 @@ var description26 = [
13720
12604
  "- discard 不可逆,worktree + branch 都会被强删"
13721
12605
  ].join(`
13722
12606
  `);
13723
- var PlanIdSchema = z27.string().regex(/^plan-\d{8}-\d{6}-\d{3}$/, "plan_id 格式:plan-YYYYMMDD-HHmmss-NNN").optional().describe("关联的 plan_id(reviewer 校验时用),格式 plan-YYYYMMDD-HHmmss-NNN");
13724
- var ArgsSchema26 = z27.discriminatedUnion("action", [
13725
- z27.object({
13726
- action: z27.literal("merge"),
13727
- session_id: z27.string().optional().describe("默认当前 session"),
12607
+ var PlanIdSchema = z12.string().regex(/^plan-\d{8}-\d{6}-\d{3}$/, "plan_id 格式:plan-YYYYMMDD-HHmmss-NNN").optional().describe("关联的 plan_id(reviewer 校验时用),格式 plan-YYYYMMDD-HHmmss-NNN");
12608
+ var ArgsSchema12 = z12.discriminatedUnion("action", [
12609
+ z12.object({
12610
+ action: z12.literal("merge"),
12611
+ session_id: z12.string().optional().describe("默认当前 session"),
13728
12612
  plan_id: PlanIdSchema,
13729
- force: z27.boolean().optional().describe("跳过 review 直接 squash merge(写审计)")
12613
+ force: z12.boolean().optional().describe("跳过 review 直接 squash merge(写审计)")
13730
12614
  }),
13731
- z27.object({
13732
- action: z27.literal("status"),
13733
- session_id: z27.string().optional().describe("默认当前 session")
12615
+ z12.object({
12616
+ action: z12.literal("status"),
12617
+ session_id: z12.string().optional().describe("默认当前 session")
13734
12618
  }),
13735
- z27.object({
13736
- action: z27.literal("discard"),
13737
- session_id: z27.string().optional().describe("默认当前 session")
12619
+ z12.object({
12620
+ action: z12.literal("discard"),
12621
+ session_id: z12.string().optional().describe("默认当前 session")
13738
12622
  }),
13739
- z27.object({
13740
- action: z27.literal("diff"),
13741
- session_id: z27.string().optional().describe("默认当前 session"),
13742
- stat: z27.boolean().optional().describe("true=只显示文件列表+统计,false=完整 diff(默认 false)")
12623
+ z12.object({
12624
+ action: z12.literal("diff"),
12625
+ session_id: z12.string().optional().describe("默认当前 session"),
12626
+ stat: z12.boolean().optional().describe("true=只显示文件列表+统计,false=完整 diff(默认 false)")
13743
12627
  })
13744
12628
  ]);
13745
12629
  var _ctx = {};
@@ -13774,8 +12658,8 @@ async function isSessionIdValid(sid, mainRoot) {
13774
12658
  return false;
13775
12659
  }
13776
12660
  }
13777
- async function execute26(input) {
13778
- const parsed = ArgsSchema26.safeParse(input);
12661
+ async function execute12(input) {
12662
+ const parsed = ArgsSchema12.safeParse(input);
13779
12663
  if (!parsed.success) {
13780
12664
  const action = input?.action ?? "status";
13781
12665
  return {
@@ -13896,10 +12780,9 @@ async function execute26(input) {
13896
12780
  }
13897
12781
  }
13898
12782
  // tools/plan-write.ts
13899
- import { z as z28 } from "zod";
12783
+ import { z as z13 } from "zod";
13900
12784
 
13901
12785
  // lib/plan-store.ts
13902
- init_runtime_paths();
13903
12786
  import { promises as fs11 } from "node:fs";
13904
12787
  import * as path14 from "node:path";
13905
12788
  var INDEX_VERSION = 1;
@@ -14147,7 +13030,7 @@ function formatTimestamp(d) {
14147
13030
  }
14148
13031
 
14149
13032
  // tools/plan-write.ts
14150
- var description27 = [
13033
+ var description13 = [
14151
13034
  "把完整方案文档写入 XDG runtime plans 目录,返回 plan_id 供 coder 后续 plan_read 使用。",
14152
13035
  "**何时调用**:",
14153
13036
  "- planner 完成方案设计后,在 boomerang 回报 codeforge 之前必须调用",
@@ -14158,10 +13041,10 @@ var description27 = [
14158
13041
  "**输出**:plan_id 形如 plan-YYYYMMDD-HHmmss-NNN,必须出现在 boomerang 回报中供 codeforge 解析"
14159
13042
  ].join(`
14160
13043
  `);
14161
- var ArgsSchema27 = z28.object({
14162
- title: z28.string().min(2, "title 至少 2 字符").max(80, "title 不超过 80 字符").describe('方案简短标题(2-80 字),如 "实现 worktree session 隔离 Phase 1"'),
14163
- content: z28.string().min(10, "content 不能为空").describe("方案 markdown 全文,建议 ≥ 50 行"),
14164
- tags: z28.array(z28.string().min(1)).optional().describe('标签(可选),如 ["phase:1", "arch:worktree"]')
13044
+ var ArgsSchema13 = z13.object({
13045
+ title: z13.string().min(2, "title 至少 2 字符").max(80, "title 不超过 80 字符").describe('方案简短标题(2-80 字),如 "实现 worktree session 隔离 Phase 1"'),
13046
+ content: z13.string().min(10, "content 不能为空").describe("方案 markdown 全文,建议 ≥ 50 行"),
13047
+ tags: z13.array(z13.string().min(1)).optional().describe('标签(可选),如 ["phase:1", "arch:worktree"]')
14165
13048
  });
14166
13049
  var _store = null;
14167
13050
  function getStore() {
@@ -14169,8 +13052,8 @@ function getStore() {
14169
13052
  _store = new PlanStore;
14170
13053
  return _store;
14171
13054
  }
14172
- async function execute27(input) {
14173
- const parsed = ArgsSchema27.safeParse(input);
13055
+ async function execute13(input) {
13056
+ const parsed = ArgsSchema13.safeParse(input);
14174
13057
  if (!parsed.success) {
14175
13058
  return {
14176
13059
  ok: false,
@@ -14203,8 +13086,8 @@ async function execute27(input) {
14203
13086
  // tools/plan-read.ts
14204
13087
  import { promises as fs12 } from "node:fs";
14205
13088
  import * as path15 from "node:path";
14206
- import { z as z29 } from "zod";
14207
- var description28 = [
13089
+ import { z as z14 } from "zod";
13090
+ var description14 = [
14208
13091
  "读取方案文档内容,支持按 plan_id 或绝对路径查询。",
14209
13092
  "**何时调用**:",
14210
13093
  "- coder/reviewer 接到任务后,第一件事就是 plan_read(plan_id=...) 拿完整方案",
@@ -14216,9 +13099,9 @@ var description28 = [
14216
13099
  "**Gate 闭合**:plan_id 成功 read 后自动调 markPlanReadOk 解除 session-worktree-guard hard gate(仅 session 存在且 requiredPlanId 匹配时 gate_closed=true)"
14217
13100
  ].join(`
14218
13101
  `);
14219
- var ArgsSchema28 = z29.object({
14220
- plan_id: z29.string().regex(/^plan-\d{8}-\d{6}-\d{3}$/, "plan_id 格式:plan-YYYYMMDD-HHmmss-NNN").optional().describe("方案 ID(推荐),格式 plan-YYYYMMDD-HHmmss-NNN"),
14221
- path: z29.string().min(1).optional().describe("方案绝对路径(兜底用,没有 plan_id 元数据时退而求其次)")
13102
+ var ArgsSchema14 = z14.object({
13103
+ plan_id: z14.string().regex(/^plan-\d{8}-\d{6}-\d{3}$/, "plan_id 格式:plan-YYYYMMDD-HHmmss-NNN").optional().describe("方案 ID(推荐),格式 plan-YYYYMMDD-HHmmss-NNN"),
13104
+ path: z14.string().min(1).optional().describe("方案绝对路径(兜底用,没有 plan_id 元数据时退而求其次)")
14222
13105
  }).refine((d) => d.plan_id || d.path, {
14223
13106
  message: "plan_id 与 path 至少传一个"
14224
13107
  });
@@ -14243,8 +13126,8 @@ async function resolveSessionId2() {
14243
13126
  function resolveMainRoot() {
14244
13127
  return _ctx2.resolveMainRoot ? _ctx2.resolveMainRoot() : process.cwd();
14245
13128
  }
14246
- async function execute28(input) {
14247
- const parsed = ArgsSchema28.safeParse(input);
13129
+ async function execute14(input) {
13130
+ const parsed = ArgsSchema14.safeParse(input);
14248
13131
  if (!parsed.success) {
14249
13132
  return {
14250
13133
  ok: false,
@@ -14330,7 +13213,7 @@ async function execute28(input) {
14330
13213
  }
14331
13214
  }
14332
13215
  // tools/adr-init.ts
14333
- import { z as z30 } from "zod";
13216
+ import { z as z15 } from "zod";
14334
13217
 
14335
13218
  // lib/adr-init.ts
14336
13219
  import { spawnSync } from "node:child_process";
@@ -14552,7 +13435,7 @@ async function runAdrInit(opts = {}) {
14552
13435
  }
14553
13436
 
14554
13437
  // tools/adr-init.ts
14555
- var description29 = [
13438
+ var description15 = [
14556
13439
  "把 CodeForge 的 ADR 体系(adr-check / adr-index-sync / pre-commit / pre-push / docs/adr 模板 / PR 模板)一键下发到当前 git 项目。",
14557
13440
  "**何时调用**:",
14558
13441
  "- 用户说「给这个项目加 ADR / 装一下 ADR 检查 / 同步一下 ADR 模板」",
@@ -14563,12 +13446,12 @@ var description29 = [
14563
13446
  "**\uD83D\uDEAB 失败时严禁绕过**:如果返回 ok=false reason=assets_not_found,必须停止并告知用户重装 codeforge(bash install.sh --global),**绝对不允许手动创建文件替代**——手动创建会产生格式错误的文件(如错误的数字编号前缀),破坏整个项目的 ADR 体系"
14564
13447
  ].join(`
14565
13448
  `);
14566
- var ArgsSchema29 = z30.object({
14567
- cwd: z30.string().optional().describe("目标项目根目录,默认 process.cwd();通常无需传"),
14568
- force: z30.boolean().optional().describe("已存在文件覆盖;覆盖前自动 .bak.<ts> 备份"),
14569
- dryRun: z30.boolean().optional().describe("只输出将要执行的写入计划,不实际写盘"),
14570
- writePrepare: z30.boolean().optional().describe("(npm 项目)自动合并 git config core.hooksPath 到 package.json scripts.prepare;写前自动 backup package.json;默认只建议不写"),
14571
- installPrePush: z30.boolean().optional().describe("是否同时生成 .githooks/pre-push hook,默认 true")
13449
+ var ArgsSchema15 = z15.object({
13450
+ cwd: z15.string().optional().describe("目标项目根目录,默认 process.cwd();通常无需传"),
13451
+ force: z15.boolean().optional().describe("已存在文件覆盖;覆盖前自动 .bak.<ts> 备份"),
13452
+ dryRun: z15.boolean().optional().describe("只输出将要执行的写入计划,不实际写盘"),
13453
+ writePrepare: z15.boolean().optional().describe("(npm 项目)自动合并 git config core.hooksPath 到 package.json scripts.prepare;写前自动 backup package.json;默认只建议不写"),
13454
+ installPrePush: z15.boolean().optional().describe("是否同时生成 .githooks/pre-push hook,默认 true")
14572
13455
  });
14573
13456
  function summarize(r) {
14574
13457
  const lines = [];
@@ -14606,8 +13489,8 @@ function summarize(r) {
14606
13489
  return lines.join(`
14607
13490
  `);
14608
13491
  }
14609
- async function execute29(input) {
14610
- const parsed = ArgsSchema29.safeParse(input);
13492
+ async function execute15(input) {
13493
+ const parsed = ArgsSchema15.safeParse(input);
14611
13494
  if (!parsed.success) {
14612
13495
  return {
14613
13496
  ok: false,
@@ -14894,7 +13777,6 @@ async function sendParentNotice(client, sessionID, text, opts = {}) {
14894
13777
  init_decision_parser();
14895
13778
 
14896
13779
  // lib/parent-map-store.ts
14897
- init_runtime_paths();
14898
13780
  import { promises as fs13 } from "node:fs";
14899
13781
  import * as path17 from "node:path";
14900
13782
  var PARENT_MAP_VERSION = 1;
@@ -15642,7 +14524,7 @@ var toolHeartbeatServer = async (ctx) => {
15642
14524
  var handler6 = toolHeartbeatServer;
15643
14525
 
15644
14526
  // plugins/codeforge-tools.ts
15645
- var z31 = tool.schema;
14527
+ var z16 = tool.schema;
15646
14528
  var PLUGIN_NAME7 = "codeforge-tools";
15647
14529
  logLifecycle(PLUGIN_NAME7, "import");
15648
14530
  function wrap(output, metadata) {
@@ -15684,331 +14566,98 @@ function projectValidate(toolName, schema, args) {
15684
14566
  output: JSON.stringify({ ok: false, tool: toolName, error: msg }, null, 2)
15685
14567
  };
15686
14568
  }
15687
- function buildKhWriteTools() {
15688
- return {
15689
- add_knowledge: tool({
15690
- description: description3,
15691
- args: {
15692
- title: z31.string().min(2).max(60).describe("简短标题(2-60 字)"),
15693
- content: z31.string().min(10).describe("详细内容(Markdown,独立可理解)"),
15694
- category: z31.string().optional().describe("分类(可选,自动推断)"),
15695
- tags: z31.array(z31.string()).optional(),
15696
- relatedFiles: z31.array(z31.string()).optional(),
15697
- relatedCommit: z31.string().optional(),
15698
- relatedPR: z31.string().optional(),
15699
- scope: z31.string().optional(),
15700
- preConfirmed: z31.boolean().optional()
15701
- },
15702
- async execute(args) {
15703
- return await runSafe("add_knowledge", async () => {
15704
- const v = projectValidate("add_knowledge", ArgsSchema3, args);
15705
- if (!v.ok)
15706
- return wrap(JSON.parse(v.output));
15707
- const result = await execute3(v.data);
15708
- return wrap(result, { title: `add_knowledge: ${args.title}` });
15709
- });
15710
- }
15711
- }),
15712
- upload_document: tool({
15713
- description: description4,
15714
- args: {
15715
- name: z31.string().min(1).describe("文件名(含扩展名)"),
15716
- content: z31.string().min(1).describe("文件完整内容"),
15717
- encoding: z31.enum(["utf8", "base64"]).optional(),
15718
- chunk_strategy: z31.enum(["auto", "heading", "fixed", "paragraph", "separator"]).optional(),
15719
- chunk_size: z31.number().int().optional(),
15720
- chunk_overlap: z31.number().int().optional(),
15721
- separator: z31.string().optional(),
15722
- distill: z31.boolean().optional(),
15723
- category: z31.string().optional(),
15724
- tags: z31.array(z31.string()).optional(),
15725
- scope: z31.string().optional(),
15726
- pre_confirmed: z31.boolean().optional()
15727
- },
15728
- async execute(args) {
15729
- return await runSafeTracked("upload_document", async () => {
15730
- const v = projectValidate("upload_document", ArgsSchema4, args);
15731
- if (!v.ok)
15732
- return wrap(JSON.parse(v.output));
15733
- const result = await execute4(v.data);
15734
- return wrap(result, { title: `upload_document: ${args.name}` });
15735
- });
15736
- }
15737
- }),
15738
- upload_document_from_url: tool({
15739
- description: description5,
15740
- args: {
15741
- url: z31.string().min(1).describe("HTTP(s) URL"),
15742
- chunk_strategy: z31.enum(["auto", "heading", "fixed", "paragraph", "separator"]).optional(),
15743
- chunk_size: z31.number().int().optional(),
15744
- chunk_overlap: z31.number().int().optional(),
15745
- separator: z31.string().optional(),
15746
- distill: z31.boolean().optional(),
15747
- category: z31.string().optional(),
15748
- tags: z31.array(z31.string()).optional(),
15749
- scope: z31.string().optional(),
15750
- pre_confirmed: z31.boolean().optional()
15751
- },
15752
- async execute(args) {
15753
- return await runSafeTracked("upload_document_from_url", async () => {
15754
- const v = projectValidate("upload_document_from_url", ArgsSchema5, args);
15755
- if (!v.ok)
15756
- return wrap(JSON.parse(v.output));
15757
- const result = await execute5(v.data);
15758
- return wrap(result, { title: `upload_document_from_url: ${args.url}` });
15759
- });
15760
- }
15761
- }),
15762
- update_knowledge: tool({
15763
- description: description6,
15764
- args: {
15765
- knowledgeId: z31.string().min(1).describe("知识 ID(从 smart_search 获取)"),
15766
- appendContent: z31.string().optional(),
15767
- replaceContent: z31.string().optional(),
15768
- additionalTags: z31.array(z31.string()).optional(),
15769
- category: z31.string().optional()
15770
- },
15771
- async execute(args) {
15772
- return await runSafe("update_knowledge", async () => {
15773
- const v = projectValidate("update_knowledge", ArgsSchema6, args);
15774
- if (!v.ok)
15775
- return wrap(JSON.parse(v.output));
15776
- const result = await execute6(v.data);
15777
- return wrap(result, { title: `update_knowledge: ${args.knowledgeId}` });
15778
- });
15779
- }
15780
- }),
15781
- flag_outdated: tool({
15782
- description: description7,
15783
- args: {
15784
- knowledgeId: z31.string().min(1).describe("知识 ID"),
15785
- reason: z31.string().min(5).max(500).describe("过时原因简述")
15786
- },
15787
- async execute(args) {
15788
- return await runSafe("flag_outdated", async () => {
15789
- const v = projectValidate("flag_outdated", ArgsSchema7, args);
15790
- if (!v.ok)
15791
- return wrap(JSON.parse(v.output));
15792
- const result = await execute7(v.data);
15793
- return wrap(result, { title: `flag_outdated: ${args.knowledgeId}` });
15794
- });
15795
- }
15796
- }),
15797
- delete_knowledge: tool({
15798
- description: description8,
15799
- args: {
15800
- knowledgeId: z31.string().min(1).describe("知识 ID"),
15801
- reason: z31.string().min(3).max(300).describe("删除原因(写入审计日志)")
15802
- },
15803
- async execute(args) {
15804
- return await runSafe("delete_knowledge", async () => {
15805
- const v = projectValidate("delete_knowledge", ArgsSchema8, args);
15806
- if (!v.ok)
15807
- return wrap(JSON.parse(v.output));
15808
- const result = await execute8(v.data);
15809
- return wrap(result, { title: `delete_knowledge: ${args.knowledgeId}` });
15810
- });
15811
- }
15812
- }),
15813
- list_recent: tool({
15814
- description: description9,
15815
- args: {
15816
- days: z31.number().int().min(1).max(90).optional().describe("最近几天(默认 7)"),
15817
- limit: z31.number().int().min(1).max(50).optional().describe("返回条数(默认 10)"),
15818
- category: z31.string().optional()
15819
- },
15820
- async execute(args) {
15821
- return await runSafe("list_recent", async () => {
15822
- const v = projectValidate("list_recent", ArgsSchema9, args);
15823
- if (!v.ok)
15824
- return wrap(JSON.parse(v.output));
15825
- const result = await execute9(v.data);
15826
- return wrap(result, { title: "list_recent" });
15827
- });
15828
- }
15829
- }),
15830
- update_working_memory: tool({
15831
- description: description10,
15832
- args: {
15833
- section: z31.string().min(1).describe("分区名(constraints / in_progress / topic:xxx 等)"),
15834
- items: z31.array(z31.object({
15835
- content: z31.string().min(1),
15836
- priority: z31.number().optional()
15837
- })),
15838
- replace: z31.boolean().optional().describe("是否替换分区全部内容(默认 false=追加)")
15839
- },
15840
- async execute(args) {
15841
- return await runSafe("update_working_memory", async () => {
15842
- const v = projectValidate("update_working_memory", ArgsSchema10, args);
15843
- if (!v.ok)
15844
- return wrap(JSON.parse(v.output));
15845
- const result = await execute10(v.data);
15846
- return wrap(result, { title: `update_working_memory: ${args.section}` });
15847
- });
15848
- }
15849
- }),
15850
- get_working_memory: tool({
15851
- description: description11,
15852
- args: {
15853
- section: z31.string().optional().describe("分区名;不传则返回全部")
15854
- },
15855
- async execute(args) {
15856
- return await runSafe("get_working_memory", async () => {
15857
- const v = projectValidate("get_working_memory", ArgsSchema11, args);
15858
- if (!v.ok)
15859
- return wrap(JSON.parse(v.output));
15860
- const result = await execute11(v.data);
15861
- return wrap(result, { title: "get_working_memory" });
15862
- });
15863
- }
15864
- }),
15865
- update_user_preference: tool({
15866
- description: description12,
15867
- args: {
15868
- key: z31.string().min(1).max(64).describe("偏好键名(language / coding_style / signature 等)"),
15869
- value: z31.string().min(1).max(500).describe("偏好值"),
15870
- action: z31.enum(["set", "delete"]).optional()
15871
- },
15872
- async execute(args) {
15873
- return await runSafe("update_user_preference", async () => {
15874
- const v = projectValidate("update_user_preference", ArgsSchema12, args);
15875
- if (!v.ok)
15876
- return wrap(JSON.parse(v.output));
15877
- const result = await execute12(v.data);
15878
- return wrap(result, { title: `update_user_preference: ${args.key}` });
15879
- });
15880
- }
15881
- }),
15882
- web_search: tool({
15883
- description: description13,
15884
- args: {
15885
- query: z31.string().min(1).describe("搜索词"),
15886
- limit: z31.number().int().min(1).max(10).optional(),
15887
- time_range: z31.enum(["", "day", "week", "month", "year"]).optional()
15888
- },
15889
- async execute(args) {
15890
- return await runSafeTracked("web_search", async () => {
15891
- const v = projectValidate("web_search", ArgsSchema13, args);
15892
- if (!v.ok)
15893
- return wrap(JSON.parse(v.output));
15894
- const result = await execute13(v.data);
15895
- return wrap(result, { title: `web_search: ${args.query}` });
15896
- });
15897
- }
15898
- }),
15899
- generate_image: tool({
15900
- description: description14,
15901
- args: {
15902
- prompt: z31.string().min(4).max(4000).describe("画面描述"),
15903
- n: z31.number().int().min(1).max(3).optional(),
15904
- size: z31.string().optional(),
15905
- transparent: z31.boolean().optional(),
15906
- alt: z31.string().optional()
15907
- },
15908
- async execute(args) {
15909
- return await runSafeTracked("generate_image", async () => {
15910
- const v = projectValidate("generate_image", ArgsSchema14, args);
15911
- if (!v.ok)
15912
- return wrap(JSON.parse(v.output));
15913
- const result = await execute14(v.data);
15914
- return wrap(result, { title: "generate_image" });
15915
- });
15916
- }
15917
- })
15918
- };
15919
- }
15920
14569
  function buildBrowserTools() {
15921
14570
  return {
15922
14571
  browser_navigate: tool({
15923
- description: description19,
14572
+ description: description5,
15924
14573
  args: {
15925
- url: z31.string().min(1).describe("要打开的 URL;必须是 http(s)")
14574
+ url: z16.string().min(1).describe("要打开的 URL;必须是 http(s)")
15926
14575
  },
15927
14576
  async execute(args) {
15928
14577
  return await runSafe("browser_navigate", async () => {
15929
- const v = projectValidate("browser_navigate", ArgsSchema19, args);
14578
+ const v = projectValidate("browser_navigate", ArgsSchema5, args);
15930
14579
  if (!v.ok)
15931
14580
  return wrap(JSON.parse(v.output));
15932
- const result = await execute19(v.data);
14581
+ const result = await execute5(v.data);
15933
14582
  return wrap(result, { title: `navigate: ${args.url}` });
15934
14583
  });
15935
14584
  }
15936
14585
  }),
15937
14586
  browser_click: tool({
15938
- description: description20,
14587
+ description: description6,
15939
14588
  args: {
15940
- selector: z31.string().min(1).describe("CSS / Playwright locator,必须能唯一定位")
14589
+ selector: z16.string().min(1).describe("CSS / Playwright locator,必须能唯一定位")
15941
14590
  },
15942
14591
  async execute(args) {
15943
14592
  return await runSafe("browser_click", async () => {
15944
- const v = projectValidate("browser_click", ArgsSchema20, args);
14593
+ const v = projectValidate("browser_click", ArgsSchema6, args);
15945
14594
  if (!v.ok)
15946
14595
  return wrap(JSON.parse(v.output));
15947
- const result = await execute20(v.data);
14596
+ const result = await execute6(v.data);
15948
14597
  return wrap(result, { title: `click: ${args.selector}` });
15949
14598
  });
15950
14599
  }
15951
14600
  }),
15952
14601
  browser_fill: tool({
15953
- description: description21,
14602
+ description: description7,
15954
14603
  args: {
15955
- selector: z31.string().min(1).describe("CSS / Playwright locator"),
15956
- value: z31.string().describe("要填入的文本;原样写入不转义")
14604
+ selector: z16.string().min(1).describe("CSS / Playwright locator"),
14605
+ value: z16.string().describe("要填入的文本;原样写入不转义")
15957
14606
  },
15958
14607
  async execute(args) {
15959
14608
  return await runSafe("browser_fill", async () => {
15960
- const v = projectValidate("browser_fill", ArgsSchema21, args);
14609
+ const v = projectValidate("browser_fill", ArgsSchema7, args);
15961
14610
  if (!v.ok)
15962
14611
  return wrap(JSON.parse(v.output));
15963
- const result = await execute21(v.data);
14612
+ const result = await execute7(v.data);
15964
14613
  return wrap(result, { title: `fill: ${args.selector}` });
15965
14614
  });
15966
14615
  }
15967
14616
  }),
15968
14617
  browser_screenshot: tool({
15969
- description: description22,
14618
+ description: description8,
15970
14619
  args: {
15971
- fullPage: z31.boolean().optional().describe("是否截全长页面(默认仅可视区)"),
15972
- selector: z31.string().optional().describe("CSS 选择器;指定时只截该元素")
14620
+ fullPage: z16.boolean().optional().describe("是否截全长页面(默认仅可视区)"),
14621
+ selector: z16.string().optional().describe("CSS 选择器;指定时只截该元素")
15973
14622
  },
15974
14623
  async execute(args) {
15975
14624
  return await runSafe("browser_screenshot", async () => {
15976
- const v = projectValidate("browser_screenshot", ArgsSchema22, args);
14625
+ const v = projectValidate("browser_screenshot", ArgsSchema8, args);
15977
14626
  if (!v.ok)
15978
14627
  return wrap(JSON.parse(v.output));
15979
- const result = await execute22(v.data);
14628
+ const result = await execute8(v.data);
15980
14629
  return wrap(result, { title: "screenshot" });
15981
14630
  });
15982
14631
  }
15983
14632
  }),
15984
14633
  browser_console: tool({
15985
- description: description23,
14634
+ description: description9,
15986
14635
  args: {
15987
- level: z31.enum(["log", "info", "warn", "error", "debug"]).optional().describe("过滤级别"),
15988
- sinceTs: z31.number().optional().describe("timestamp >= sinceTs 的条目")
14636
+ level: z16.enum(["log", "info", "warn", "error", "debug"]).optional().describe("过滤级别"),
14637
+ sinceTs: z16.number().optional().describe("timestamp >= sinceTs 的条目")
15989
14638
  },
15990
14639
  async execute(args) {
15991
14640
  return await runSafe("browser_console", async () => {
15992
- const v = projectValidate("browser_console", ArgsSchema23, args);
14641
+ const v = projectValidate("browser_console", ArgsSchema9, args);
15993
14642
  if (!v.ok)
15994
14643
  return wrap(JSON.parse(v.output));
15995
- const result = await execute23(v.data);
14644
+ const result = await execute9(v.data);
15996
14645
  return wrap(result, { title: "console.logs" });
15997
14646
  });
15998
14647
  }
15999
14648
  }),
16000
14649
  browser_network: tool({
16001
- description: description24,
14650
+ description: description10,
16002
14651
  args: {
16003
- method: z31.string().optional().describe("HTTP 方法过滤(GET/POST/...)"),
16004
- sinceTs: z31.number().optional().describe("start_ts >= sinceTs 的请求")
14652
+ method: z16.string().optional().describe("HTTP 方法过滤(GET/POST/...)"),
14653
+ sinceTs: z16.number().optional().describe("start_ts >= sinceTs 的请求")
16005
14654
  },
16006
14655
  async execute(args) {
16007
14656
  return await runSafe("browser_network", async () => {
16008
- const v = projectValidate("browser_network", ArgsSchema24, args);
14657
+ const v = projectValidate("browser_network", ArgsSchema10, args);
16009
14658
  if (!v.ok)
16010
14659
  return wrap(JSON.parse(v.output));
16011
- const result = await execute24(v.data);
14660
+ const result = await execute10(v.data);
16012
14661
  return wrap(result, { title: "network.requests" });
16013
14662
  });
16014
14663
  }
@@ -16016,20 +14665,6 @@ function buildBrowserTools() {
16016
14665
  };
16017
14666
  }
16018
14667
  var CORE_TOOL_NAMES = [
16019
- "smart_search",
16020
- "save_chat_insight",
16021
- "add_knowledge",
16022
- "upload_document",
16023
- "upload_document_from_url",
16024
- "update_knowledge",
16025
- "flag_outdated",
16026
- "delete_knowledge",
16027
- "list_recent",
16028
- "update_working_memory",
16029
- "get_working_memory",
16030
- "update_user_preference",
16031
- "web_search",
16032
- "generate_image",
16033
14668
  "ast_edit",
16034
14669
  "repo_map",
16035
14670
  "rules_debug",
@@ -16076,93 +14711,37 @@ var codeforgeToolsServer = async (ctx) => {
16076
14711
  resolveCurrentSessionId: () => process.env["CODEFORGE_SESSION_ID"] ?? ""
16077
14712
  });
16078
14713
  const browserTools = browserEnabled ? buildBrowserTools() : {};
16079
- const khWriteTools = buildKhWriteTools();
16080
14714
  return {
16081
14715
  tool: {
16082
- smart_search: tool({
16083
- description,
16084
- args: {
16085
- query: z31.string().min(1).describe("自然语言查询词,例如「登录限流方案」「dev 服务器部署流程」"),
16086
- limit: z31.number().int().min(1).max(15).optional().describe("返回条数,默认 5;超过 15 会被 KH 截断"),
16087
- prefer: z31.array(z31.string()).optional().describe("偏好分类(best-effort);常用 architecture / anti_pattern / convention")
16088
- },
16089
- async execute(args) {
16090
- return await runSafe("smart_search", async () => {
16091
- const v = projectValidate("smart_search", ArgsSchema, args);
16092
- if (!v.ok)
16093
- return wrap(JSON.parse(v.output));
16094
- const result = await execute(v.data);
16095
- const meta = { title: `smart_search: ${args.query}` };
16096
- const r = result;
16097
- if (r.ok && Array.isArray(r.insights))
16098
- meta["count"] = r.insights.length;
16099
- return wrap(result, meta);
16100
- });
16101
- }
16102
- }),
16103
- save_chat_insight: tool({
16104
- description: description2,
16105
- args: {
16106
- insight: z31.string().min(20).max(5000).describe("完整知识条目(Markdown)。推荐三段式:## 问题 / ## 原因 / ## 解决"),
16107
- category: z31.enum([
16108
- "convention",
16109
- "anti_pattern",
16110
- "architecture",
16111
- "reference",
16112
- "workflow",
16113
- "gotcha",
16114
- "tooling",
16115
- "decision"
16116
- ]).describe("分类"),
16117
- tags: z31.array(z31.string().min(1)).optional().describe("标签数组,建议 `tech:xxx` / `platform:xxx`")
16118
- },
16119
- async execute(args) {
16120
- return await runSafe("save_chat_insight", async () => {
16121
- const v = projectValidate("save_chat_insight", ArgsSchema2, args);
16122
- if (!v.ok)
16123
- return wrap(JSON.parse(v.output));
16124
- const result = await execute2(v.data);
16125
- const meta = {
16126
- title: `save_chat_insight: ${args.category}`,
16127
- category: args.category
16128
- };
16129
- const r = result;
16130
- if (r.ok && r.id)
16131
- meta["id"] = r.id;
16132
- return wrap(result, meta);
16133
- });
16134
- }
16135
- }),
16136
- ...khWriteTools,
16137
14716
  ast_edit: tool({
16138
- description: description15,
14717
+ description,
16139
14718
  args: {
16140
- action: z31.enum([
14719
+ action: z16.enum([
16141
14720
  "replace_anchor",
16142
14721
  "insert_after_anchor",
16143
14722
  "insert_before_anchor",
16144
14723
  "delete_range",
16145
14724
  "rename_symbol"
16146
14725
  ]).describe("编辑类型;不同 action 需要的字段不同"),
16147
- target: z31.string().min(1).describe("目标文件路径(相对 cwd 或绝对)"),
16148
- before_hash: z31.string().optional().describe("操作前的 sha256 hex(强烈建议传,新文件传 null/省略)"),
16149
- auto_stage: z31.boolean().optional().describe("已废弃(保留兼容字段):Phase 5 后 ast_edit 直接写入 session worktree,无需独立 stage"),
16150
- description: z31.string().optional().describe("可选变更说明(写入 audit log)"),
16151
- anchor: z31.string().optional().describe("anchor 类用:anchor 文本或 regex 源"),
16152
- regex: z31.boolean().optional().describe("anchor 是否按 RegExp 解释,默认 false"),
16153
- occurrence: z31.number().int().min(1).optional().describe("第几次匹配(1-based),默认 1"),
16154
- payload: z31.string().optional().describe("anchor 类用:要写入的内容"),
16155
- start: z31.number().int().min(1).optional().describe("delete_range 用:起始行(1-based)"),
16156
- end: z31.number().int().min(1).optional().describe("delete_range 用:结束行(1-based)"),
16157
- old_name: z31.string().optional().describe("rename_symbol 用:旧标识符"),
16158
- new_name: z31.string().optional().describe("rename_symbol 用:新标识符")
14726
+ target: z16.string().min(1).describe("目标文件路径(相对 cwd 或绝对)"),
14727
+ before_hash: z16.string().optional().describe("操作前的 sha256 hex(强烈建议传,新文件传 null/省略)"),
14728
+ auto_stage: z16.boolean().optional().describe("已废弃(保留兼容字段):Phase 5 后 ast_edit 直接写入 session worktree,无需独立 stage"),
14729
+ description: z16.string().optional().describe("可选变更说明(写入 audit log)"),
14730
+ anchor: z16.string().optional().describe("anchor 类用:anchor 文本或 regex 源"),
14731
+ regex: z16.boolean().optional().describe("anchor 是否按 RegExp 解释,默认 false"),
14732
+ occurrence: z16.number().int().min(1).optional().describe("第几次匹配(1-based),默认 1"),
14733
+ payload: z16.string().optional().describe("anchor 类用:要写入的内容"),
14734
+ start: z16.number().int().min(1).optional().describe("delete_range 用:起始行(1-based)"),
14735
+ end: z16.number().int().min(1).optional().describe("delete_range 用:结束行(1-based)"),
14736
+ old_name: z16.string().optional().describe("rename_symbol 用:旧标识符"),
14737
+ new_name: z16.string().optional().describe("rename_symbol 用:新标识符")
16159
14738
  },
16160
14739
  async execute(args) {
16161
14740
  return await runSafeTracked("ast_edit", async () => {
16162
- const v = projectValidate("ast_edit", ArgsSchema15, args);
14741
+ const v = projectValidate("ast_edit", ArgsSchema, args);
16163
14742
  if (!v.ok)
16164
14743
  return wrap(JSON.parse(v.output));
16165
- const result = await execute15(v.data);
14744
+ const result = await execute(v.data);
16166
14745
  const meta = {
16167
14746
  title: `ast_edit: ${args.action} ${args.target}`,
16168
14747
  action: args.action,
@@ -16176,19 +14755,19 @@ var codeforgeToolsServer = async (ctx) => {
16176
14755
  }
16177
14756
  }),
16178
14757
  repo_map: tool({
16179
- description: description16,
14758
+ description: description2,
16180
14759
  args: {
16181
- root: z31.string().optional().describe("扫描根目录,默认 cwd"),
16182
- top: z31.number().int().min(1).max(100).optional().describe("展示 top N 文件,默认 20"),
16183
- focus: z31.string().optional().describe("聚焦文件(POSIX 相对路径)"),
16184
- max_files: z31.number().int().min(10).max(5000).optional().describe("扫描上限,默认 500")
14760
+ root: z16.string().optional().describe("扫描根目录,默认 cwd"),
14761
+ top: z16.number().int().min(1).max(100).optional().describe("展示 top N 文件,默认 20"),
14762
+ focus: z16.string().optional().describe("聚焦文件(POSIX 相对路径)"),
14763
+ max_files: z16.number().int().min(10).max(5000).optional().describe("扫描上限,默认 500")
16185
14764
  },
16186
14765
  async execute(args) {
16187
14766
  return await runSafeTracked("repo_map", async () => {
16188
- const v = projectValidate("repo_map", ArgsSchema16, args);
14767
+ const v = projectValidate("repo_map", ArgsSchema2, args);
16189
14768
  if (!v.ok)
16190
14769
  return wrap(JSON.parse(v.output));
16191
- const result = await execute16(v.data);
14770
+ const result = await execute2(v.data);
16192
14771
  const meta = { title: "repo_map" };
16193
14772
  const r = result;
16194
14773
  if (r.ok && r.stats) {
@@ -16200,45 +14779,45 @@ var codeforgeToolsServer = async (ctx) => {
16200
14779
  }
16201
14780
  }),
16202
14781
  rules_debug: tool({
16203
- description: description17,
14782
+ description: description3,
16204
14783
  args: {
16205
- current_agent: z31.string().optional().describe("当前 agent 名"),
16206
- root: z31.string().optional().describe("项目根目录"),
16207
- home_dir: z31.string().optional().describe("覆盖个人规则目录"),
16208
- project_dir: z31.string().optional().describe("覆盖项目规则目录"),
16209
- skip_personal: z31.boolean().optional(),
16210
- skip_project: z31.boolean().optional(),
16211
- skip_agent: z31.boolean().optional(),
16212
- markdown: z31.boolean().optional().describe("默认 true:渲染 markdown 摘要")
14784
+ current_agent: z16.string().optional().describe("当前 agent 名"),
14785
+ root: z16.string().optional().describe("项目根目录"),
14786
+ home_dir: z16.string().optional().describe("覆盖个人规则目录"),
14787
+ project_dir: z16.string().optional().describe("覆盖项目规则目录"),
14788
+ skip_personal: z16.boolean().optional(),
14789
+ skip_project: z16.boolean().optional(),
14790
+ skip_agent: z16.boolean().optional(),
14791
+ markdown: z16.boolean().optional().describe("默认 true:渲染 markdown 摘要")
16213
14792
  },
16214
14793
  async execute(args) {
16215
14794
  return await runSafe("rules_debug", async () => {
16216
- const v = projectValidate("rules_debug", ArgsSchema17, args);
14795
+ const v = projectValidate("rules_debug", ArgsSchema3, args);
16217
14796
  if (!v.ok)
16218
14797
  return wrap(JSON.parse(v.output));
16219
- const result = await execute17(v.data);
14798
+ const result = await execute3(v.data);
16220
14799
  return wrap(result, { title: "rules_debug" });
16221
14800
  });
16222
14801
  }
16223
14802
  }),
16224
14803
  review_approval: tool({
16225
- description: description18,
14804
+ description: description4,
16226
14805
  args: {
16227
- verdict: z31.enum(["APPROVE", "APPROVE_WITH_NOTES"]).describe("审批裁决;REQUEST_CHANGES / BLOCK 不应调本工具"),
16228
- pendingIds: z31.array(z31.string().min(1)).min(1).describe("本次 APPROVE 覆盖的 pending change id 列表"),
16229
- notes: z31.string().min(1).max(2000).describe("审阅意见摘要(建议 ≤ 500 字)"),
16230
- decisionLine: z31.string().optional().describe("`## Decision` 节首行原文(默认 verdict 字面量,机审证据)"),
16231
- source: z31.enum(["reviewer", "codeforge-fallback"]).optional().describe("写入来源;默认 'reviewer',codeforge 补写时传 'codeforge-fallback'"),
16232
- reviewerAgent: z31.string().optional().describe("写入 agent name(默认 'reviewer';fallback 时为 'codeforge')"),
16233
- sessionId: z31.string().optional().describe("reviewer 子 session id(boomerang 溯源用,可选)"),
16234
- model: z31.string().optional().describe("审批模型 id(审计用,可选)")
14806
+ verdict: z16.enum(["APPROVE", "APPROVE_WITH_NOTES"]).describe("审批裁决;REQUEST_CHANGES / BLOCK 不应调本工具"),
14807
+ pendingIds: z16.array(z16.string().min(1)).min(1).describe("本次 APPROVE 覆盖的 pending change id 列表"),
14808
+ notes: z16.string().min(1).max(2000).describe("审阅意见摘要(建议 ≤ 500 字)"),
14809
+ decisionLine: z16.string().optional().describe("`## Decision` 节首行原文(默认 verdict 字面量,机审证据)"),
14810
+ source: z16.enum(["reviewer", "codeforge-fallback"]).optional().describe("写入来源;默认 'reviewer',codeforge 补写时传 'codeforge-fallback'"),
14811
+ reviewerAgent: z16.string().optional().describe("写入 agent name(默认 'reviewer';fallback 时为 'codeforge')"),
14812
+ sessionId: z16.string().optional().describe("reviewer 子 session id(boomerang 溯源用,可选)"),
14813
+ model: z16.string().optional().describe("审批模型 id(审计用,可选)")
16235
14814
  },
16236
14815
  async execute(args) {
16237
14816
  return await runSafe("review_approval", async () => {
16238
- const v = projectValidate("review_approval", ArgsSchema18, args);
14817
+ const v = projectValidate("review_approval", ArgsSchema4, args);
16239
14818
  if (!v.ok)
16240
14819
  return wrap(JSON.parse(v.output));
16241
- const result = await execute18(v.data);
14820
+ const result = await execute4(v.data);
16242
14821
  const meta = {
16243
14822
  title: `review_approval: ${args.verdict}`,
16244
14823
  verdict: args.verdict,
@@ -16250,19 +14829,19 @@ var codeforgeToolsServer = async (ctx) => {
16250
14829
  }),
16251
14830
  ...browserTools,
16252
14831
  model_chain: tool({
16253
- description: description25,
14832
+ description: description11,
16254
14833
  args: {
16255
- agent: z31.string().optional().describe("查指定 agent;不传 → 列出全部"),
16256
- current: z31.string().optional().describe("当前已用过的模型(<provider>/<id>),用于算下一档"),
16257
- root: z31.string().optional().describe("项目根目录,默认 cwd"),
16258
- config_file: z31.string().optional().describe("配置文件名;默认 codeforge.json")
14834
+ agent: z16.string().optional().describe("查指定 agent;不传 → 列出全部"),
14835
+ current: z16.string().optional().describe("当前已用过的模型(<provider>/<id>),用于算下一档"),
14836
+ root: z16.string().optional().describe("项目根目录,默认 cwd"),
14837
+ config_file: z16.string().optional().describe("配置文件名;默认 codeforge.json")
16259
14838
  },
16260
14839
  async execute(args) {
16261
14840
  return await runSafe("model_chain", async () => {
16262
- const v = projectValidate("model_chain", ArgsSchema25, args);
14841
+ const v = projectValidate("model_chain", ArgsSchema11, args);
16263
14842
  if (!v.ok)
16264
14843
  return wrap(JSON.parse(v.output));
16265
- const result = await execute25(v.data);
14844
+ const result = await execute11(v.data);
16266
14845
  const meta = { title: "model_chain" };
16267
14846
  const r = result;
16268
14847
  if (r.ok && Array.isArray(r.agents))
@@ -16274,17 +14853,17 @@ var codeforgeToolsServer = async (ctx) => {
16274
14853
  }
16275
14854
  }),
16276
14855
  session_merge: tool({
16277
- description: description26,
14856
+ description: description12,
16278
14857
  args: {
16279
- action: z31.enum(["merge", "status", "discard", "diff"]).describe("操作类型:merge=合并到主仓(orchestrator 专用)/ status=查询状态 / discard=放弃 / diff=查看 worktree 改动"),
16280
- session_id: z31.string().optional().describe("目标 session id;不传则用当前 session"),
16281
- plan_id: z31.string().optional().describe("关联的 plan_id(reviewer 校验时用),格式 plan-YYYYMMDD-HHmmss-NNN"),
16282
- force: z31.boolean().optional().describe("action=merge 时跳过 review 直接 squash merge(写审计)"),
16283
- stat: z31.boolean().optional().describe("action=diff 时:true=只显示文件列表+统计,false=完整 diff(默认 false)")
14858
+ action: z16.enum(["merge", "status", "discard", "diff"]).describe("操作类型:merge=合并到主仓(orchestrator 专用)/ status=查询状态 / discard=放弃 / diff=查看 worktree 改动"),
14859
+ session_id: z16.string().optional().describe("目标 session id;不传则用当前 session"),
14860
+ plan_id: z16.string().optional().describe("关联的 plan_id(reviewer 校验时用),格式 plan-YYYYMMDD-HHmmss-NNN"),
14861
+ force: z16.boolean().optional().describe("action=merge 时跳过 review 直接 squash merge(写审计)"),
14862
+ stat: z16.boolean().optional().describe("action=diff 时:true=只显示文件列表+统计,false=完整 diff(默认 false)")
16284
14863
  },
16285
14864
  async execute(args, input) {
16286
14865
  return await runSafe("session_merge", async () => {
16287
- const v = projectValidate("session_merge", ArgsSchema26, args);
14866
+ const v = projectValidate("session_merge", ArgsSchema12, args);
16288
14867
  if (!v.ok)
16289
14868
  return wrap(JSON.parse(v.output));
16290
14869
  const sid = input.sessionID;
@@ -16304,7 +14883,7 @@ var codeforgeToolsServer = async (ctx) => {
16304
14883
  }
16305
14884
  } : {}
16306
14885
  });
16307
- const result = await execute26(v.data);
14886
+ const result = await execute12(v.data);
16308
14887
  const meta = {
16309
14888
  title: `session_merge: ${args.action}`,
16310
14889
  action: args.action
@@ -16314,18 +14893,18 @@ var codeforgeToolsServer = async (ctx) => {
16314
14893
  }
16315
14894
  }),
16316
14895
  plan_write: tool({
16317
- description: description27,
14896
+ description: description13,
16318
14897
  args: {
16319
- title: z31.string().min(2).max(80).describe('方案简短标题(2-80 字),如 "实现 worktree session 隔离 Phase 1"'),
16320
- content: z31.string().min(10).describe("方案 markdown 全文,建议 ≥ 50 行"),
16321
- tags: z31.array(z31.string().min(1)).optional().describe('标签(可选),如 ["phase:1", "arch:worktree"]')
14898
+ title: z16.string().min(2).max(80).describe('方案简短标题(2-80 字),如 "实现 worktree session 隔离 Phase 1"'),
14899
+ content: z16.string().min(10).describe("方案 markdown 全文,建议 ≥ 50 行"),
14900
+ tags: z16.array(z16.string().min(1)).optional().describe('标签(可选),如 ["phase:1", "arch:worktree"]')
16322
14901
  },
16323
14902
  async execute(args) {
16324
14903
  return await runSafeTracked("plan_write", async () => {
16325
- const v = projectValidate("plan_write", ArgsSchema27, args);
14904
+ const v = projectValidate("plan_write", ArgsSchema13, args);
16326
14905
  if (!v.ok)
16327
14906
  return wrap(JSON.parse(v.output));
16328
- const result = await execute27(v.data);
14907
+ const result = await execute13(v.data);
16329
14908
  const meta = { title: `plan_write: ${args.title}` };
16330
14909
  const r = result;
16331
14910
  if (r.ok && r.plan_id)
@@ -16335,17 +14914,17 @@ var codeforgeToolsServer = async (ctx) => {
16335
14914
  }
16336
14915
  }),
16337
14916
  plan_read: tool({
16338
- description: description28,
14917
+ description: description14,
16339
14918
  args: {
16340
- plan_id: z31.string().optional().describe("方案 ID(推荐),格式 plan-YYYYMMDD-HHmmss-NNN"),
16341
- path: z31.string().optional().describe("方案绝对路径(兜底用,没有 plan_id 元数据时退而求其次)")
14919
+ plan_id: z16.string().optional().describe("方案 ID(推荐),格式 plan-YYYYMMDD-HHmmss-NNN"),
14920
+ path: z16.string().optional().describe("方案绝对路径(兜底用,没有 plan_id 元数据时退而求其次)")
16342
14921
  },
16343
14922
  async execute(args) {
16344
14923
  return await runSafe("plan_read", async () => {
16345
- const v = projectValidate("plan_read", ArgsSchema28, args);
14924
+ const v = projectValidate("plan_read", ArgsSchema14, args);
16346
14925
  if (!v.ok)
16347
14926
  return wrap(JSON.parse(v.output));
16348
- const result = await execute28(v.data);
14927
+ const result = await execute14(v.data);
16349
14928
  const meta = {
16350
14929
  title: `plan_read: ${args.plan_id ?? args.path ?? "(unknown)"}`
16351
14930
  };
@@ -16359,21 +14938,21 @@ var codeforgeToolsServer = async (ctx) => {
16359
14938
  }
16360
14939
  }),
16361
14940
  adr_init: tool({
16362
- description: description29,
14941
+ description: description15,
16363
14942
  args: {
16364
- cwd: z31.string().optional().describe("目标项目根目录,默认 process.cwd();通常无需传"),
16365
- force: z31.boolean().optional().describe("已存在文件覆盖;覆盖前自动 .bak.<ts> 备份"),
16366
- dryRun: z31.boolean().optional().describe("只输出将要执行的写入计划,不实际写盘"),
16367
- writePrepare: z31.boolean().optional().describe("(npm 项目)自动合并 git config core.hooksPath 到 package.json scripts.prepare;写前自动 backup"),
16368
- installPrePush: z31.boolean().optional().describe("是否同时生成 .githooks/pre-push hook,默认 true")
14943
+ cwd: z16.string().optional().describe("目标项目根目录,默认 process.cwd();通常无需传"),
14944
+ force: z16.boolean().optional().describe("已存在文件覆盖;覆盖前自动 .bak.<ts> 备份"),
14945
+ dryRun: z16.boolean().optional().describe("只输出将要执行的写入计划,不实际写盘"),
14946
+ writePrepare: z16.boolean().optional().describe("(npm 项目)自动合并 git config core.hooksPath 到 package.json scripts.prepare;写前自动 backup"),
14947
+ installPrePush: z16.boolean().optional().describe("是否同时生成 .githooks/pre-push hook,默认 true")
16369
14948
  },
16370
14949
  async execute(args) {
16371
14950
  return await runSafe("adr_init", async () => {
16372
- const v = projectValidate("adr_init", ArgsSchema29, args);
14951
+ const v = projectValidate("adr_init", ArgsSchema15, args);
16373
14952
  if (!v.ok)
16374
14953
  return wrap(JSON.parse(v.output));
16375
14954
  const dataWithCwd = { ...v.data, cwd: v.data.cwd ?? ctx.directory ?? process.cwd() };
16376
- const result = await execute29(dataWithCwd);
14955
+ const result = await execute15(dataWithCwd);
16377
14956
  return wrap(result, { title: "adr_init" });
16378
14957
  });
16379
14958
  }
@@ -16384,11 +14963,11 @@ var codeforgeToolsServer = async (ctx) => {
16384
14963
  var handler7 = codeforgeToolsServer;
16385
14964
 
16386
14965
  // plugins/discover-spec-suggest.ts
16387
- import { readFileSync as readFileSync3, readdirSync, statSync as statSync3 } from "node:fs";
14966
+ import { readFileSync as readFileSync3, readdirSync, statSync as statSync2 } from "node:fs";
16388
14967
  import { join as join15 } from "node:path";
16389
14968
 
16390
14969
  // lib/handoff-schema.ts
16391
- import { z as z32 } from "zod";
14970
+ import { z as z17 } from "zod";
16392
14971
 
16393
14972
  // node_modules/yaml/dist/index.js
16394
14973
  var composer = require_composer();
@@ -16441,92 +15020,92 @@ var MAX_HANDOFF_SIZE = 100 * 1024;
16441
15020
  var SLUG_REGEX = /^[a-z0-9][a-z0-9-]{0,49}$/;
16442
15021
  var SCORE_DIMENSIONS = ["functional", "ux", "technical", "constraints", "edge_cases"];
16443
15022
  var COMBO_VALUES = ["A", "B", "C", "D"];
16444
- var NeedSchema = z32.object({
16445
- id: z32.string().min(1),
16446
- type: z32.enum(["must", "should", "nice-to-have"]),
16447
- statement: z32.string().min(1),
16448
- rationale: z32.string().optional(),
16449
- acceptance: z32.array(z32.string()).optional()
15023
+ var NeedSchema = z17.object({
15024
+ id: z17.string().min(1),
15025
+ type: z17.enum(["must", "should", "nice-to-have"]),
15026
+ statement: z17.string().min(1),
15027
+ rationale: z17.string().optional(),
15028
+ acceptance: z17.array(z17.string()).optional()
16450
15029
  });
16451
- var BoundarySchema = z32.object({
16452
- excluded: z32.string().min(1),
16453
- reason: z32.string().min(1)
15030
+ var BoundarySchema = z17.object({
15031
+ excluded: z17.string().min(1),
15032
+ reason: z17.string().min(1)
16454
15033
  });
16455
- var AssumptionSchema = z32.object({
16456
- statement: z32.string().min(1),
16457
- confidence: z32.enum(["verified", "speculation", "high-risk-unknown"]),
16458
- needs_validation_by: z32.enum(["plan", "coder", "runtime"]).optional()
15034
+ var AssumptionSchema = z17.object({
15035
+ statement: z17.string().min(1),
15036
+ confidence: z17.enum(["verified", "speculation", "high-risk-unknown"]),
15037
+ needs_validation_by: z17.enum(["plan", "coder", "runtime"]).optional()
16459
15038
  });
16460
- var OpenIssueSchema = z32.union([
16461
- z32.string().min(1),
16462
- z32.object({
16463
- id: z32.string().optional(),
16464
- question: z32.string().min(1),
16465
- blocking: z32.boolean().optional()
15039
+ var OpenIssueSchema = z17.union([
15040
+ z17.string().min(1),
15041
+ z17.object({
15042
+ id: z17.string().optional(),
15043
+ question: z17.string().min(1),
15044
+ blocking: z17.boolean().optional()
16466
15045
  })
16467
15046
  ]);
16468
- var RedFlagsSchema = z32.object({
16469
- raised: z32.boolean(),
16470
- combos: z32.array(z32.enum(COMBO_VALUES)).default([]),
16471
- reasons: z32.array(z32.string()).default([]),
16472
- user_persisted_rounds: z32.number().int().nonnegative().default(0),
16473
- downstream_advisory: z32.string().nullable().optional()
15047
+ var RedFlagsSchema = z17.object({
15048
+ raised: z17.boolean(),
15049
+ combos: z17.array(z17.enum(COMBO_VALUES)).default([]),
15050
+ reasons: z17.array(z17.string()).default([]),
15051
+ user_persisted_rounds: z17.number().int().nonnegative().default(0),
15052
+ downstream_advisory: z17.string().nullable().optional()
16474
15053
  }).superRefine((rf, ctx) => {
16475
15054
  if (rf.raised) {
16476
15055
  if (rf.combos.length === 0) {
16477
15056
  ctx.addIssue({
16478
- code: z32.ZodIssueCode.custom,
15057
+ code: z17.ZodIssueCode.custom,
16479
15058
  message: "red_flags.raised=true 时 combos 必须 >=1",
16480
15059
  path: ["combos"]
16481
15060
  });
16482
15061
  }
16483
15062
  if (rf.reasons.length === 0) {
16484
15063
  ctx.addIssue({
16485
- code: z32.ZodIssueCode.custom,
15064
+ code: z17.ZodIssueCode.custom,
16486
15065
  message: "red_flags.raised=true 时 reasons 必须 >=1",
16487
15066
  path: ["reasons"]
16488
15067
  });
16489
15068
  }
16490
15069
  }
16491
15070
  });
16492
- var ScoresSchema = z32.object(Object.fromEntries(SCORE_DIMENSIONS.map((d) => [d, z32.number().min(0).max(1)])));
16493
- var PreCodingBlockerSchema = z32.object({
16494
- id: z32.string().regex(/^PRE-\d+$/, "id 必须形如 PRE-1 / PRE-2"),
16495
- blocker: z32.string().min(1),
16496
- source: z32.enum(["assumption", "red_flag", "open_issue"]),
16497
- must_resolve_by: z32.enum(["user", "codeforge"])
15071
+ var ScoresSchema = z17.object(Object.fromEntries(SCORE_DIMENSIONS.map((d) => [d, z17.number().min(0).max(1)])));
15072
+ var PreCodingBlockerSchema = z17.object({
15073
+ id: z17.string().regex(/^PRE-\d+$/, "id 必须形如 PRE-1 / PRE-2"),
15074
+ blocker: z17.string().min(1),
15075
+ source: z17.enum(["assumption", "red_flag", "open_issue"]),
15076
+ must_resolve_by: z17.enum(["user", "codeforge"])
16498
15077
  });
16499
- var KhRefSchema = z32.object({
16500
- kh_id: z32.string(),
16501
- title: z32.string(),
16502
- relevance: z32.enum(["positive", "negative", "neutral"]).optional()
15078
+ var KhRefSchema = z17.object({
15079
+ kh_id: z17.string(),
15080
+ title: z17.string(),
15081
+ relevance: z17.enum(["positive", "negative", "neutral"]).optional()
16503
15082
  });
16504
- var HandoffSchema = z32.object({
16505
- schema_version: z32.string().regex(/^\d+\.\d+\.\d+$/, "schema_version 必须形如 1.1.0 / 1.2.0"),
16506
- slug: z32.string().regex(SLUG_REGEX, "slug 仅允许 [a-z0-9-],长度 1-50,首字符为字母数字"),
16507
- title: z32.string().min(1).max(200),
16508
- created_at: z32.string().optional(),
16509
- discover_session_id: z32.string().optional(),
16510
- weighted_score: z32.number().min(0).max(1),
15083
+ var HandoffSchema = z17.object({
15084
+ schema_version: z17.string().regex(/^\d+\.\d+\.\d+$/, "schema_version 必须形如 1.1.0 / 1.2.0"),
15085
+ slug: z17.string().regex(SLUG_REGEX, "slug 仅允许 [a-z0-9-],长度 1-50,首字符为字母数字"),
15086
+ title: z17.string().min(1).max(200),
15087
+ created_at: z17.string().optional(),
15088
+ discover_session_id: z17.string().optional(),
15089
+ weighted_score: z17.number().min(0).max(1),
16511
15090
  scores: ScoresSchema,
16512
- needs: z32.array(NeedSchema).min(1, "needs 必须 >=1"),
16513
- boundaries: z32.array(BoundarySchema).min(1, "boundaries 必须 >=1"),
16514
- assumptions: z32.array(AssumptionSchema),
16515
- open_issues: z32.array(OpenIssueSchema).default([]),
16516
- rejected_alternatives: z32.array(z32.object({ option: z32.string(), reason: z32.string() })).default([]),
16517
- acceptance_criteria: z32.array(z32.object({
16518
- id: z32.string().regex(/^AC-/, "AC id 必须以 AC- 开头"),
16519
- description: z32.string().min(1),
16520
- measurable: z32.boolean().optional(),
16521
- metric: z32.string().optional()
15091
+ needs: z17.array(NeedSchema).min(1, "needs 必须 >=1"),
15092
+ boundaries: z17.array(BoundarySchema).min(1, "boundaries 必须 >=1"),
15093
+ assumptions: z17.array(AssumptionSchema),
15094
+ open_issues: z17.array(OpenIssueSchema).default([]),
15095
+ rejected_alternatives: z17.array(z17.object({ option: z17.string(), reason: z17.string() })).default([]),
15096
+ acceptance_criteria: z17.array(z17.object({
15097
+ id: z17.string().regex(/^AC-/, "AC id 必须以 AC- 开头"),
15098
+ description: z17.string().min(1),
15099
+ measurable: z17.boolean().optional(),
15100
+ metric: z17.string().optional()
16522
15101
  })).default([]),
16523
15102
  red_flags: RedFlagsSchema,
16524
- pre_coding_blockers: z32.array(PreCodingBlockerSchema).default([]),
16525
- kh_references: z32.array(KhRefSchema).default([]),
16526
- adr_refs: z32.array(z32.string()).default([]),
16527
- related_artifacts: z32.object({
16528
- prd: z32.string().optional(),
16529
- transcript: z32.string().optional()
15103
+ pre_coding_blockers: z17.array(PreCodingBlockerSchema).default([]),
15104
+ kh_references: z17.array(KhRefSchema).default([]),
15105
+ adr_refs: z17.array(z17.string()).default([]),
15106
+ related_artifacts: z17.object({
15107
+ prd: z17.string().optional(),
15108
+ transcript: z17.string().optional()
16530
15109
  }).optional()
16531
15110
  });
16532
15111
  function validateHandoff(rawYaml, fileSize) {
@@ -16571,7 +15150,7 @@ var SESSION_TTL_MS2 = 24 * 60 * 60 * 1000;
16571
15150
  var MATCH_THRESHOLD = 0.15;
16572
15151
  var MAX_CANDIDATES = 3;
16573
15152
  var NUDGE_MAX_LEN = 1500;
16574
- var SPECS_REL_DIR = join15(".codeforge", "specs");
15153
+ var SPECS_REL_DIR = join15("docs", "specs");
16575
15154
  var sessionMap = new Map;
16576
15155
  function pruneIfOversize2() {
16577
15156
  while (sessionMap.size > SESSION_CAP2) {
@@ -16593,13 +15172,13 @@ function defaultDirReader(p) {
16593
15172
  }
16594
15173
  function defaultDirExists(p) {
16595
15174
  try {
16596
- return statSync3(p).isDirectory();
15175
+ return statSync2(p).isDirectory();
16597
15176
  } catch {
16598
15177
  return false;
16599
15178
  }
16600
15179
  }
16601
15180
  function defaultStatReader(p) {
16602
- const st = statSync3(p);
15181
+ const st = statSync2(p);
16603
15182
  return { size: st.size, mtimeMs: st.mtimeMs, isDirectory: st.isDirectory() };
16604
15183
  }
16605
15184
  function tokenize(s) {
@@ -16794,7 +15373,7 @@ function renderCandidatesNudge(matched) {
16794
15373
  }
16795
15374
  lines.push("");
16796
15375
  lines.push("若用户确认走 spec:");
16797
- lines.push(" 1. 先 `read .codeforge/specs/<slug>/PRD.md` + `read .codeforge/specs/<slug>/handoff.yaml`");
15376
+ lines.push(" 1. 先 `read docs/specs/<slug>/PRD.md` + `read docs/specs/<slug>/handoff.yaml`");
16798
15377
  lines.push(" 2. 派 planner / coder 时 prompt 必须含 `spec=<slug>`");
16799
15378
  lines.push(" 3. 若 handoff 含 pre_coding_blockers[],派 coder 时需附 `pre_ack=<PRE-id>`");
16800
15379
  lines.push("────────────────────────────────────────");
@@ -16892,7 +15471,6 @@ function resolveConfig(c) {
16892
15471
  projectRoot: c.projectRoot,
16893
15472
  homeDir: c.homeDir ?? os5.homedir(),
16894
15473
  projectName: c.projectName ?? path19.basename(c.projectRoot),
16895
- kh: c.kh,
16896
15474
  now: c.now ?? Date.now,
16897
15475
  log: c.log ?? (() => {}),
16898
15476
  maxPerScope: c.maxPerScope ?? 1000
@@ -16947,17 +15525,6 @@ async function addMemory(params, cfgRaw) {
16947
15525
  updated_at: now,
16948
15526
  source: params.source ?? "manual"
16949
15527
  };
16950
- if (cfg.kh) {
16951
- try {
16952
- const r = await cfg.kh.add(base);
16953
- cfg.log("info", `[memories] add → KH ${r.id}`);
16954
- return { ok: true, id: r.id, written_to: "kh" };
16955
- } catch (err) {
16956
- cfg.log("warn", `[memories] KH 写入失败,降级本地`, {
16957
- error: err instanceof Error ? err.message : String(err)
16958
- });
16959
- }
16960
- }
16961
15528
  const file = fileFor(params.scope, cfg);
16962
15529
  const id = localId(now);
16963
15530
  const m = { id, ...base };
@@ -16987,20 +15554,6 @@ async function recallMemories(query, opts, cfgRaw) {
16987
15554
  const cfg = resolveConfig(cfgRaw);
16988
15555
  const limit = opts.limit ?? 5;
16989
15556
  const half = opts.halflifeMs ?? 7 * 24 * 3600 * 1000;
16990
- if (cfg.kh) {
16991
- try {
16992
- const items = await cfg.kh.search(query, {
16993
- scope: opts.scope,
16994
- project: opts.project ?? cfg.projectName,
16995
- limit
16996
- });
16997
- return items.map((m) => ({ ...m, score: 1 }));
16998
- } catch (err) {
16999
- cfg.log("warn", `[memories] KH search 失败,降级本地`, {
17000
- error: err instanceof Error ? err.message : String(err)
17001
- });
17002
- }
17003
- }
17004
15557
  const all = await listMemories({ scope: opts.scope, project: opts.project ?? cfg.projectName }, cfgRaw);
17005
15558
  const tokens = tokenize2(query);
17006
15559
  if (tokens.length === 0)
@@ -17237,8 +15790,7 @@ async function handleDirective(dir, ctx, memCfg, log7) {
17237
15790
  if (dir.kind === "remember") {
17238
15791
  const r = await addMemory({ scope: dir.scope, content: dir.content, source: "directive" }, memCfg);
17239
15792
  if (r.ok) {
17240
- const target = r.written_to === "kh" ? "KH" : "本地";
17241
- await safeReply(ctx, `\uD83E\uDDE0 已记住(${dir.scope} / ${target},id=${r.id})`);
15793
+ await safeReply(ctx, `\uD83E\uDDE0 已记住(${dir.scope} / 本地,id=${r.id})`);
17242
15794
  log7?.info(`[${PLUGIN_NAME9}] /remember ok`, {
17243
15795
  scope: dir.scope,
17244
15796
  id: r.id,
@@ -17375,9 +15927,9 @@ function buildFallbackMeta(cfg, agent, currentModel) {
17375
15927
  source: r.source
17376
15928
  };
17377
15929
  }
17378
- function buildSuggestion(meta, errorMessage4) {
15930
+ function buildSuggestion(meta, errorMessage2) {
17379
15931
  const head = `⚠️ \`${meta.agent}\` agent 调用 \`${meta.current_model}\` 失败`;
17380
- const reason = errorMessage4 ? `(${errorMessage4.slice(0, 80)})` : "";
15932
+ const reason = errorMessage2 ? `(${errorMessage2.slice(0, 80)})` : "";
17381
15933
  if (!meta.next_fallback) {
17382
15934
  return `${head}${reason}
17383
15935
  fallback 链已用尽:${meta.chain.join(" → ")}`;
@@ -17464,8 +16016,6 @@ var handler10 = modelFallbackServer;
17464
16016
  // plugins/subtask-heartbeat.ts
17465
16017
  import { promises as fsPromises } from "node:fs";
17466
16018
  import * as path20 from "node:path";
17467
- init_runtime_paths();
17468
- init_global_config();
17469
16019
  var recordSessionParent2 = recordSessionParent;
17470
16020
  var lookupParentSessionId2 = lookupParentSessionId;
17471
16021
  var deleteSessionParent2 = deleteSessionParent;
@@ -17589,11 +16139,11 @@ function extractTaskArgs(args) {
17589
16139
  const a = args;
17590
16140
  const rawDesc = typeof a["description"] === "string" ? a["description"] : null;
17591
16141
  const rawPrompt = typeof a["prompt"] === "string" ? a["prompt"] : null;
17592
- const description30 = rawDesc ?? (rawPrompt ? rawPrompt.slice(0, 60) : null);
16142
+ const description16 = rawDesc ?? (rawPrompt ? rawPrompt.slice(0, 60) : null);
17593
16143
  const subagentType = typeof a["subagent_type"] === "string" && a["subagent_type"] || typeof a["agent"] === "string" && a["agent"] || typeof a["agentType"] === "string" && a["agentType"] || typeof a["agent_type"] === "string" && a["agent_type"] || null;
17594
- if (!description30 && !subagentType)
16144
+ if (!description16 && !subagentType)
17595
16145
  return null;
17596
- return { description: description30, subagentType };
16146
+ return { description: description16, subagentType };
17597
16147
  }
17598
16148
  function enqueuePendingTask(parentID, entry, now = Date.now()) {
17599
16149
  const ts = entry.ts ?? now;
@@ -18179,18 +16729,15 @@ var parallelStatusServer = async (ctx) => {
18179
16729
  var handler12 = parallelStatusServer;
18180
16730
 
18181
16731
  // plugins/parallel-tool-nudge.ts
18182
- import { readFileSync as readFileSync4, readdirSync as readdirSync2, statSync as statSync4 } from "node:fs";
16732
+ import { readFileSync as readFileSync4, readdirSync as readdirSync2, statSync as statSync3 } from "node:fs";
18183
16733
  import { join as join17 } from "node:path";
18184
16734
  import { homedir as homedir6 } from "node:os";
18185
16735
  var PLUGIN_NAME13 = "parallel-tool-nudge";
18186
16736
  logLifecycle(PLUGIN_NAME13, "import", {});
18187
16737
  var PARALLEL_SAFE_TOOLS = [
18188
16738
  "read",
18189
- "smart_search",
18190
16739
  "repo_map",
18191
- "webfetch",
18192
- "list_recent",
18193
- "get_working_memory"
16740
+ "webfetch"
18194
16741
  ];
18195
16742
  var DEFAULT_AGENT_KEY = "__default__";
18196
16743
  var NUDGE_MAX_LEN2 = 800;
@@ -18203,7 +16750,7 @@ function defaultDirReader2(p) {
18203
16750
  }
18204
16751
  function defaultDirExists2(p) {
18205
16752
  try {
18206
- return statSync4(p).isDirectory();
16753
+ return statSync3(p).isDirectory();
18207
16754
  } catch {
18208
16755
  return false;
18209
16756
  }
@@ -18342,7 +16889,7 @@ ${toolsBulleted}
18342
16889
 
18343
16890
  Heuristics:
18344
16891
  - Need to read 3 files? → emit 3 \`read\` calls in ONE response, not 3 sequential turns
18345
- - Need search + map? → emit \`smart_search\` + \`repo_map\` in ONE response
16892
+ - Need to read + map? → emit \`read\` + \`repo_map\` in ONE response
18346
16893
  - DO NOT batch write tools (edit / write / ast_edit / bash)
18347
16894
  - DO NOT chain reads where each read's path depends on previous read's output
18348
16895
 
@@ -18467,7 +17014,6 @@ var handler14 = async (_ctx3) => {
18467
17014
 
18468
17015
  // lib/event-stream.ts
18469
17016
  import { promises as fs16 } from "node:fs";
18470
- init_runtime_paths();
18471
17017
  import * as path21 from "node:path";
18472
17018
  async function loadSession(id, opts = {}) {
18473
17019
  const file = resolveSessionFile(id, opts);
@@ -18782,7 +17328,6 @@ function isRecoveryWorthShowing(plan) {
18782
17328
  }
18783
17329
 
18784
17330
  // lib/block-pending.ts
18785
- init_runtime_paths();
18786
17331
  import { promises as fs17 } from "node:fs";
18787
17332
  import * as path22 from "node:path";
18788
17333
  function blockPendingFilePath(absRoot) {
@@ -19600,7 +18145,7 @@ init_worktree_ops();
19600
18145
 
19601
18146
  // lib/decompose.ts
19602
18147
  var DEFAULT_MAX_SUBTASKS = 8;
19603
- var DEFAULT_TIMEOUT_MS2 = 30000;
18148
+ var DEFAULT_TIMEOUT_MS = 30000;
19604
18149
  function buildSystemPrompt(maxSubtasks) {
19605
18150
  return [
19606
18151
  `你是任务拆分助手。把用户描述拆成 2 到 ${maxSubtasks} 个【可并行执行】的独立子任务。`,
@@ -19617,17 +18162,17 @@ function buildSystemPrompt(maxSubtasks) {
19617
18162
  ].join(`
19618
18163
  `);
19619
18164
  }
19620
- async function decomposeTask(description30, opts) {
18165
+ async function decomposeTask(description16, opts) {
19621
18166
  const log10 = opts.log ?? (() => {});
19622
18167
  if (opts.mockResponse) {
19623
18168
  return validateAndFinalize(opts.mockResponse, undefined, log10, opts.maxSubtasks ?? DEFAULT_MAX_SUBTASKS);
19624
18169
  }
19625
18170
  const maxSubtasks = opts.maxSubtasks ?? DEFAULT_MAX_SUBTASKS;
19626
- const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS2;
18171
+ const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
19627
18172
  let childSessionId;
19628
18173
  try {
19629
18174
  const created = await opts.client.session.create({
19630
- body: { title: `decompose:${clip5(description30, 60)}` },
18175
+ body: { title: `decompose:${clip5(description16, 60)}` },
19631
18176
  query: opts.directory ? { directory: opts.directory } : undefined
19632
18177
  });
19633
18178
  if (created.error || !created.data?.id) {
@@ -19644,7 +18189,7 @@ async function decomposeTask(description30, opts) {
19644
18189
  path: { id: childSessionId },
19645
18190
  body: {
19646
18191
  system: systemText,
19647
- parts: [{ type: "text", text: description30 }]
18192
+ parts: [{ type: "text", text: description16 }]
19648
18193
  },
19649
18194
  query: opts.directory ? { directory: opts.directory } : undefined
19650
18195
  }));
@@ -19824,7 +18369,6 @@ function sleep2(ms) {
19824
18369
  }
19825
18370
 
19826
18371
  // plugins/subtasks.ts
19827
- init_runtime_paths();
19828
18372
  var PLUGIN_NAME16 = "subtasks";
19829
18373
  function getLogFile(root = process.cwd()) {
19830
18374
  return path23.join(runtimeDir(root), "logs", "subtasks.log");
@@ -20149,8 +18693,8 @@ var subtasksServer = async (ctx) => {
20149
18693
  try {
20150
18694
  if (input?.command !== "parallel")
20151
18695
  return;
20152
- const description30 = (input.arguments ?? "").trim();
20153
- if (!description30) {
18696
+ const description16 = (input.arguments ?? "").trim();
18697
+ if (!description16) {
20154
18698
  return;
20155
18699
  }
20156
18700
  let autoMerge = false;
@@ -20189,7 +18733,7 @@ var subtasksServer = async (ctx) => {
20189
18733
  } : undefined;
20190
18734
  const replyLines = [];
20191
18735
  const messageCtx = {
20192
- content: `/parallel ${description30}`,
18736
+ content: `/parallel ${description16}`,
20193
18737
  reply: (s) => {
20194
18738
  replyLines.push(s);
20195
18739
  return Promise.resolve();
@@ -20859,21 +19403,7 @@ var TOOL_KIND_MAP = {
20859
19403
  glob: "search",
20860
19404
  grep: "search",
20861
19405
  search: "search",
20862
- "repo-map": "search",
20863
- smart_search: "search",
20864
- list_recent: "search",
20865
- get_working_memory: "search",
20866
- web_search: "search",
20867
- save_chat_insight: "other",
20868
- add_knowledge: "other",
20869
- upload_document: "other",
20870
- upload_document_from_url: "other",
20871
- update_knowledge: "other",
20872
- flag_outdated: "other",
20873
- delete_knowledge: "other",
20874
- update_working_memory: "other",
20875
- update_user_preference: "other",
20876
- generate_image: "other"
19406
+ "repo-map": "search"
20877
19407
  };
20878
19408
  function classifyTool(tool2) {
20879
19409
  const lower = tool2.toLowerCase();
@@ -21216,7 +19746,7 @@ import {
21216
19746
  readFileSync as readFileSync5,
21217
19747
  readdirSync as readdirSync3,
21218
19748
  renameSync,
21219
- statSync as statSync5,
19749
+ statSync as statSync4,
21220
19750
  unlinkSync,
21221
19751
  writeFileSync as writeFileSync2
21222
19752
  } from "node:fs";
@@ -21229,7 +19759,7 @@ import * as zlib from "node:zlib";
21229
19759
  // lib/version-injected.ts
21230
19760
  function getInjectedVersion() {
21231
19761
  try {
21232
- const v = "0.5.28";
19762
+ const v = "0.6.0";
21233
19763
  if (typeof v === "string" && /^\d+\.\d+\.\d+/.test(v)) {
21234
19764
  return v;
21235
19765
  }
@@ -21648,7 +20178,7 @@ function cleanupOldBackups(target, keep) {
21648
20178
  const full = join22(dir, f);
21649
20179
  let mtimeMs = 0;
21650
20180
  try {
21651
- mtimeMs = statSync5(full).mtimeMs;
20181
+ mtimeMs = statSync4(full).mtimeMs;
21652
20182
  } catch {}
21653
20183
  return { full, mtimeMs };
21654
20184
  }).sort((a, b) => b.mtimeMs - a.mtimeMs);
@@ -21960,55 +20490,55 @@ import * as path27 from "node:path";
21960
20490
  // lib/workflow-loader.ts
21961
20491
  import { promises as fs20 } from "node:fs";
21962
20492
  import * as path26 from "node:path";
21963
- import { z as z33 } from "zod";
21964
- var ActionSchema = z33.object({
21965
- tool: z33.string().min(1, "action.tool 不能为空"),
21966
- args: z33.record(z33.string(), z33.unknown()).optional().default({}),
21967
- on_error: z33.enum(["retry", "skip", "abort"]).optional()
20493
+ import { z as z18 } from "zod";
20494
+ var ActionSchema = z18.object({
20495
+ tool: z18.string().min(1, "action.tool 不能为空"),
20496
+ args: z18.record(z18.string(), z18.unknown()).optional().default({}),
20497
+ on_error: z18.enum(["retry", "skip", "abort"]).optional()
21968
20498
  });
21969
- var StepSchema = z33.object({
21970
- name: z33.string().min(1, "step.name 不能为空"),
21971
- agent: z33.string().min(1, "step.agent 不能为空").describe("agent 名(与 agents/<name>.md 对应)"),
21972
- description: z33.string().optional(),
21973
- inject_context: z33.record(z33.string(), z33.unknown()).optional(),
21974
- requires_human_approval: z33.boolean().optional().default(false),
21975
- actions: z33.array(ActionSchema).optional().default([]),
21976
- on_error: z33.enum(["retry", "skip", "abort"]).optional().default("abort"),
21977
- max_retries: z33.number().int().min(0).max(10).optional().default(2),
21978
- timeout: z33.string().regex(/^\d+(?:ms|s|m|h)$/, "timeout 必须是 数字+单位(ms/s/m/h),如 5m").optional(),
21979
- auto_feedback: z33.object({
21980
- test_cmd: z33.string().optional().describe("测试命令,如 npm test"),
21981
- lint_cmd: z33.string().optional().describe("lint 命令,如 npm run lint"),
21982
- max_retries: z33.number().int().min(1).max(10).optional().default(3),
21983
- error_excerpt_lines: z33.number().int().min(1).max(50).optional().default(5),
21984
- escalate_to: z33.string().optional().default("reviewer").describe("超上限后兜底 agent,默认 reviewer")
20499
+ var StepSchema = z18.object({
20500
+ name: z18.string().min(1, "step.name 不能为空"),
20501
+ agent: z18.string().min(1, "step.agent 不能为空").describe("agent 名(与 agents/<name>.md 对应)"),
20502
+ description: z18.string().optional(),
20503
+ inject_context: z18.record(z18.string(), z18.unknown()).optional(),
20504
+ requires_human_approval: z18.boolean().optional().default(false),
20505
+ actions: z18.array(ActionSchema).optional().default([]),
20506
+ on_error: z18.enum(["retry", "skip", "abort"]).optional().default("abort"),
20507
+ max_retries: z18.number().int().min(0).max(10).optional().default(2),
20508
+ timeout: z18.string().regex(/^\d+(?:ms|s|m|h)$/, "timeout 必须是 数字+单位(ms/s/m/h),如 5m").optional(),
20509
+ auto_feedback: z18.object({
20510
+ test_cmd: z18.string().optional().describe("测试命令,如 npm test"),
20511
+ lint_cmd: z18.string().optional().describe("lint 命令,如 npm run lint"),
20512
+ max_retries: z18.number().int().min(1).max(10).optional().default(3),
20513
+ error_excerpt_lines: z18.number().int().min(1).max(50).optional().default(5),
20514
+ escalate_to: z18.string().optional().default("reviewer").describe("超上限后兜底 agent,默认 reviewer")
21985
20515
  }).refine((d) => Boolean(d.test_cmd || d.lint_cmd), { message: "auto_feedback 必须至少配置 test_cmd 或 lint_cmd 之一" }).optional(),
21986
- on_decision: z33.object({
21987
- APPROVE: z33.union([
21988
- z33.literal("continue"),
21989
- z33.literal("abort"),
21990
- z33.object({ action: z33.literal("goto"), target: z33.string().min(1) })
20516
+ on_decision: z18.object({
20517
+ APPROVE: z18.union([
20518
+ z18.literal("continue"),
20519
+ z18.literal("abort"),
20520
+ z18.object({ action: z18.literal("goto"), target: z18.string().min(1) })
21991
20521
  ]).optional(),
21992
- REQUEST_CHANGES: z33.union([
21993
- z33.literal("continue"),
21994
- z33.literal("abort"),
21995
- z33.object({ action: z33.literal("goto"), target: z33.string().min(1) })
20522
+ REQUEST_CHANGES: z18.union([
20523
+ z18.literal("continue"),
20524
+ z18.literal("abort"),
20525
+ z18.object({ action: z18.literal("goto"), target: z18.string().min(1) })
21996
20526
  ]).optional(),
21997
- BLOCK: z33.union([
21998
- z33.literal("continue"),
21999
- z33.literal("abort"),
22000
- z33.object({ action: z33.literal("goto"), target: z33.string().min(1) })
20527
+ BLOCK: z18.union([
20528
+ z18.literal("continue"),
20529
+ z18.literal("abort"),
20530
+ z18.object({ action: z18.literal("goto"), target: z18.string().min(1) })
22001
20531
  ]).optional()
22002
20532
  }).refine((d) => Boolean(d.APPROVE || d.REQUEST_CHANGES || d.BLOCK), { message: "on_decision 必须至少配置 APPROVE / REQUEST_CHANGES / BLOCK 之一" }).optional()
22003
20533
  }).strict();
22004
- var WorkflowSchema = z33.object({
22005
- name: z33.string().min(1, "workflow.name 不能为空"),
22006
- description: z33.string().optional().default(""),
22007
- version: z33.string().optional().default("1.0.0"),
22008
- trigger: z33.string().min(1).refine((v) => /^\/[a-z][\w-]*$/.test(v) || /^event:[a-z][\w.-]+$/.test(v), "trigger 必须是 /command-name 或 event:xxx 形式"),
22009
- context_template: z33.string().optional(),
22010
- max_loops: z33.number().int().min(1).max(10).optional().default(3),
22011
- steps: z33.array(StepSchema).min(1, "workflow.steps 不能为空")
20534
+ var WorkflowSchema = z18.object({
20535
+ name: z18.string().min(1, "workflow.name 不能为空"),
20536
+ description: z18.string().optional().default(""),
20537
+ version: z18.string().optional().default("1.0.0"),
20538
+ trigger: z18.string().min(1).refine((v) => /^\/[a-z][\w-]*$/.test(v) || /^event:[a-z][\w.-]+$/.test(v), "trigger 必须是 /command-name 或 event:xxx 形式"),
20539
+ context_template: z18.string().optional(),
20540
+ max_loops: z18.number().int().min(1).max(10).optional().default(3),
20541
+ steps: z18.array(StepSchema).min(1, "workflow.steps 不能为空")
22012
20542
  }).strict();
22013
20543
  function parseWorkflowYaml(yaml, sourcePath = "<inline>") {
22014
20544
  let raw;
@@ -22611,6 +21141,13 @@ var CLASS_B_CALLER_WHITELIST = new Set([
22611
21141
  "reviewer-lite",
22612
21142
  "general"
22613
21143
  ]);
21144
+ var MERGE_CALLER_WHITELIST = new Set([
21145
+ "codeforge",
21146
+ "discover"
21147
+ ]);
21148
+ var FORCE_MERGE_CALLER_WHITELIST = new Set([
21149
+ "codeforge"
21150
+ ]);
22614
21151
  var CODEFORGE_WORKTREE_DIR_NAME = path28.join(".git", "codeforge-worktrees");
22615
21152
  function worktreesRoot(mainRoot) {
22616
21153
  return path28.join(mainRoot, CODEFORGE_WORKTREE_DIR_NAME);
@@ -22887,8 +21424,8 @@ var sessionWorktreeGuardPlugin = async (ctx) => {
22887
21424
  const action = argsObj["action"];
22888
21425
  if (action === "merge") {
22889
21426
  const caller = input.agent;
22890
- if (caller !== undefined && caller !== "codeforge") {
22891
- const reason = `[session-worktree-guard] DENIED: session_merge action=merge 仅 codeforge orchestrator 或用户可调;当前 caller=${caller}`;
21427
+ if (caller !== undefined && !MERGE_CALLER_WHITELIST.has(caller)) {
21428
+ const reason = `[session-worktree-guard] DENIED: session_merge action=merge 仅 codeforge orchestrator / discover / 用户可调;当前 caller=${caller}`;
22892
21429
  log13.warn(reason, {
22893
21430
  sessionId,
22894
21431
  tool: toolName,
@@ -22907,6 +21444,29 @@ var sessionWorktreeGuardPlugin = async (ctx) => {
22907
21444
  denied = new DeniedError(reason);
22908
21445
  return;
22909
21446
  }
21447
+ const wantsForce = argsObj["force"] === true;
21448
+ if (wantsForce && caller !== undefined && !FORCE_MERGE_CALLER_WHITELIST.has(caller)) {
21449
+ const reason = `[session-worktree-guard] DENIED: caller=${caller} 禁止 force=true;` + `跳过 review 闭环是破坏性操作,仅用户经 codeforge orchestrator 显式 /merge --force 才允许 ` + `(ADR:discover-self-merge-permission)`;
21450
+ log13.warn(reason, {
21451
+ sessionId,
21452
+ tool: toolName,
21453
+ action,
21454
+ caller,
21455
+ force: true
21456
+ });
21457
+ safeWriteLog(PLUGIN_NAME22, {
21458
+ hook: "tool.execute.before",
21459
+ tool: toolName,
21460
+ sessionID: input.sessionID,
21461
+ action: "deny",
21462
+ source: "merge-force-whitelist",
21463
+ caller,
21464
+ merge_action: "merge",
21465
+ force: true
21466
+ });
21467
+ denied = new DeniedError(reason);
21468
+ return;
21469
+ }
22910
21470
  }
22911
21471
  }
22912
21472
  if (perAgentEnabled && isWriteOperation(toolName, argsObj, mainRoot)) {