@andyqiu/codeforge 0.5.18 → 0.5.20

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
@@ -1069,17 +1069,17 @@ var require_visit = __commonJS((exports) => {
1069
1069
  visit.BREAK = BREAK;
1070
1070
  visit.SKIP = SKIP;
1071
1071
  visit.REMOVE = REMOVE;
1072
- function visit_(key, node, visitor, path18) {
1073
- const ctrl = callVisitor(key, node, visitor, path18);
1072
+ function visit_(key, node, visitor, path19) {
1073
+ const ctrl = callVisitor(key, node, visitor, path19);
1074
1074
  if (identity.isNode(ctrl) || identity.isPair(ctrl)) {
1075
- replaceNode(key, path18, ctrl);
1076
- return visit_(key, ctrl, visitor, path18);
1075
+ replaceNode(key, path19, ctrl);
1076
+ return visit_(key, ctrl, visitor, path19);
1077
1077
  }
1078
1078
  if (typeof ctrl !== "symbol") {
1079
1079
  if (identity.isCollection(node)) {
1080
- path18 = Object.freeze(path18.concat(node));
1080
+ path19 = Object.freeze(path19.concat(node));
1081
1081
  for (let i = 0;i < node.items.length; ++i) {
1082
- const ci = visit_(i, node.items[i], visitor, path18);
1082
+ const ci = visit_(i, node.items[i], visitor, path19);
1083
1083
  if (typeof ci === "number")
1084
1084
  i = ci - 1;
1085
1085
  else if (ci === BREAK)
@@ -1090,13 +1090,13 @@ var require_visit = __commonJS((exports) => {
1090
1090
  }
1091
1091
  }
1092
1092
  } else if (identity.isPair(node)) {
1093
- path18 = Object.freeze(path18.concat(node));
1094
- const ck = visit_("key", node.key, visitor, path18);
1093
+ path19 = Object.freeze(path19.concat(node));
1094
+ const ck = visit_("key", node.key, visitor, path19);
1095
1095
  if (ck === BREAK)
1096
1096
  return BREAK;
1097
1097
  else if (ck === REMOVE)
1098
1098
  node.key = null;
1099
- const cv = visit_("value", node.value, visitor, path18);
1099
+ const cv = visit_("value", node.value, visitor, path19);
1100
1100
  if (cv === BREAK)
1101
1101
  return BREAK;
1102
1102
  else if (cv === REMOVE)
@@ -1117,17 +1117,17 @@ var require_visit = __commonJS((exports) => {
1117
1117
  visitAsync.BREAK = BREAK;
1118
1118
  visitAsync.SKIP = SKIP;
1119
1119
  visitAsync.REMOVE = REMOVE;
1120
- async function visitAsync_(key, node, visitor, path18) {
1121
- const ctrl = await callVisitor(key, node, visitor, path18);
1120
+ async function visitAsync_(key, node, visitor, path19) {
1121
+ const ctrl = await callVisitor(key, node, visitor, path19);
1122
1122
  if (identity.isNode(ctrl) || identity.isPair(ctrl)) {
1123
- replaceNode(key, path18, ctrl);
1124
- return visitAsync_(key, ctrl, visitor, path18);
1123
+ replaceNode(key, path19, ctrl);
1124
+ return visitAsync_(key, ctrl, visitor, path19);
1125
1125
  }
1126
1126
  if (typeof ctrl !== "symbol") {
1127
1127
  if (identity.isCollection(node)) {
1128
- path18 = Object.freeze(path18.concat(node));
1128
+ path19 = Object.freeze(path19.concat(node));
1129
1129
  for (let i = 0;i < node.items.length; ++i) {
1130
- const ci = await visitAsync_(i, node.items[i], visitor, path18);
1130
+ const ci = await visitAsync_(i, node.items[i], visitor, path19);
1131
1131
  if (typeof ci === "number")
1132
1132
  i = ci - 1;
1133
1133
  else if (ci === BREAK)
@@ -1138,13 +1138,13 @@ var require_visit = __commonJS((exports) => {
1138
1138
  }
1139
1139
  }
1140
1140
  } else if (identity.isPair(node)) {
1141
- path18 = Object.freeze(path18.concat(node));
1142
- const ck = await visitAsync_("key", node.key, visitor, path18);
1141
+ path19 = Object.freeze(path19.concat(node));
1142
+ const ck = await visitAsync_("key", node.key, visitor, path19);
1143
1143
  if (ck === BREAK)
1144
1144
  return BREAK;
1145
1145
  else if (ck === REMOVE)
1146
1146
  node.key = null;
1147
- const cv = await visitAsync_("value", node.value, visitor, path18);
1147
+ const cv = await visitAsync_("value", node.value, visitor, path19);
1148
1148
  if (cv === BREAK)
1149
1149
  return BREAK;
1150
1150
  else if (cv === REMOVE)
@@ -1171,23 +1171,23 @@ var require_visit = __commonJS((exports) => {
1171
1171
  }
1172
1172
  return visitor;
1173
1173
  }
1174
- function callVisitor(key, node, visitor, path18) {
1174
+ function callVisitor(key, node, visitor, path19) {
1175
1175
  if (typeof visitor === "function")
1176
- return visitor(key, node, path18);
1176
+ return visitor(key, node, path19);
1177
1177
  if (identity.isMap(node))
1178
- return visitor.Map?.(key, node, path18);
1178
+ return visitor.Map?.(key, node, path19);
1179
1179
  if (identity.isSeq(node))
1180
- return visitor.Seq?.(key, node, path18);
1180
+ return visitor.Seq?.(key, node, path19);
1181
1181
  if (identity.isPair(node))
1182
- return visitor.Pair?.(key, node, path18);
1182
+ return visitor.Pair?.(key, node, path19);
1183
1183
  if (identity.isScalar(node))
1184
- return visitor.Scalar?.(key, node, path18);
1184
+ return visitor.Scalar?.(key, node, path19);
1185
1185
  if (identity.isAlias(node))
1186
- return visitor.Alias?.(key, node, path18);
1186
+ return visitor.Alias?.(key, node, path19);
1187
1187
  return;
1188
1188
  }
1189
- function replaceNode(key, path18, node) {
1190
- const parent = path18[path18.length - 1];
1189
+ function replaceNode(key, path19, node) {
1190
+ const parent = path19[path19.length - 1];
1191
1191
  if (identity.isCollection(parent)) {
1192
1192
  parent.items[key] = node;
1193
1193
  } else if (identity.isPair(parent)) {
@@ -1746,10 +1746,10 @@ var require_Collection = __commonJS((exports) => {
1746
1746
  var createNode = require_createNode();
1747
1747
  var identity = require_identity();
1748
1748
  var Node = require_Node();
1749
- function collectionFromPath(schema, path18, value) {
1749
+ function collectionFromPath(schema, path19, value) {
1750
1750
  let v = value;
1751
- for (let i = path18.length - 1;i >= 0; --i) {
1752
- const k = path18[i];
1751
+ for (let i = path19.length - 1;i >= 0; --i) {
1752
+ const k = path19[i];
1753
1753
  if (typeof k === "number" && Number.isInteger(k) && k >= 0) {
1754
1754
  const a = [];
1755
1755
  a[k] = v;
@@ -1768,7 +1768,7 @@ var require_Collection = __commonJS((exports) => {
1768
1768
  sourceObjects: new Map
1769
1769
  });
1770
1770
  }
1771
- var isEmptyPath = (path18) => path18 == null || typeof path18 === "object" && !!path18[Symbol.iterator]().next().done;
1771
+ var isEmptyPath = (path19) => path19 == null || typeof path19 === "object" && !!path19[Symbol.iterator]().next().done;
1772
1772
 
1773
1773
  class Collection extends Node.NodeBase {
1774
1774
  constructor(type, schema) {
@@ -1789,11 +1789,11 @@ var require_Collection = __commonJS((exports) => {
1789
1789
  copy.range = this.range.slice();
1790
1790
  return copy;
1791
1791
  }
1792
- addIn(path18, value) {
1793
- if (isEmptyPath(path18))
1792
+ addIn(path19, value) {
1793
+ if (isEmptyPath(path19))
1794
1794
  this.add(value);
1795
1795
  else {
1796
- const [key, ...rest] = path18;
1796
+ const [key, ...rest] = path19;
1797
1797
  const node = this.get(key, true);
1798
1798
  if (identity.isCollection(node))
1799
1799
  node.addIn(rest, value);
@@ -1803,8 +1803,8 @@ var require_Collection = __commonJS((exports) => {
1803
1803
  throw new Error(`Expected YAML collection at ${key}. Remaining path: ${rest}`);
1804
1804
  }
1805
1805
  }
1806
- deleteIn(path18) {
1807
- const [key, ...rest] = path18;
1806
+ deleteIn(path19) {
1807
+ const [key, ...rest] = path19;
1808
1808
  if (rest.length === 0)
1809
1809
  return this.delete(key);
1810
1810
  const node = this.get(key, true);
@@ -1813,8 +1813,8 @@ var require_Collection = __commonJS((exports) => {
1813
1813
  else
1814
1814
  throw new Error(`Expected YAML collection at ${key}. Remaining path: ${rest}`);
1815
1815
  }
1816
- getIn(path18, keepScalar) {
1817
- const [key, ...rest] = path18;
1816
+ getIn(path19, keepScalar) {
1817
+ const [key, ...rest] = path19;
1818
1818
  const node = this.get(key, true);
1819
1819
  if (rest.length === 0)
1820
1820
  return !keepScalar && identity.isScalar(node) ? node.value : node;
@@ -1829,15 +1829,15 @@ var require_Collection = __commonJS((exports) => {
1829
1829
  return n == null || allowScalar && identity.isScalar(n) && n.value == null && !n.commentBefore && !n.comment && !n.tag;
1830
1830
  });
1831
1831
  }
1832
- hasIn(path18) {
1833
- const [key, ...rest] = path18;
1832
+ hasIn(path19) {
1833
+ const [key, ...rest] = path19;
1834
1834
  if (rest.length === 0)
1835
1835
  return this.has(key);
1836
1836
  const node = this.get(key, true);
1837
1837
  return identity.isCollection(node) ? node.hasIn(rest) : false;
1838
1838
  }
1839
- setIn(path18, value) {
1840
- const [key, ...rest] = path18;
1839
+ setIn(path19, value) {
1840
+ const [key, ...rest] = path19;
1841
1841
  if (rest.length === 0) {
1842
1842
  this.set(key, value);
1843
1843
  } else {
@@ -4230,9 +4230,9 @@ var require_Document = __commonJS((exports) => {
4230
4230
  if (assertCollection(this.contents))
4231
4231
  this.contents.add(value);
4232
4232
  }
4233
- addIn(path18, value) {
4233
+ addIn(path19, value) {
4234
4234
  if (assertCollection(this.contents))
4235
- this.contents.addIn(path18, value);
4235
+ this.contents.addIn(path19, value);
4236
4236
  }
4237
4237
  createAlias(node, name) {
4238
4238
  if (!node.anchor) {
@@ -4281,30 +4281,30 @@ var require_Document = __commonJS((exports) => {
4281
4281
  delete(key) {
4282
4282
  return assertCollection(this.contents) ? this.contents.delete(key) : false;
4283
4283
  }
4284
- deleteIn(path18) {
4285
- if (Collection.isEmptyPath(path18)) {
4284
+ deleteIn(path19) {
4285
+ if (Collection.isEmptyPath(path19)) {
4286
4286
  if (this.contents == null)
4287
4287
  return false;
4288
4288
  this.contents = null;
4289
4289
  return true;
4290
4290
  }
4291
- return assertCollection(this.contents) ? this.contents.deleteIn(path18) : false;
4291
+ return assertCollection(this.contents) ? this.contents.deleteIn(path19) : false;
4292
4292
  }
4293
4293
  get(key, keepScalar) {
4294
4294
  return identity.isCollection(this.contents) ? this.contents.get(key, keepScalar) : undefined;
4295
4295
  }
4296
- getIn(path18, keepScalar) {
4297
- if (Collection.isEmptyPath(path18))
4296
+ getIn(path19, keepScalar) {
4297
+ if (Collection.isEmptyPath(path19))
4298
4298
  return !keepScalar && identity.isScalar(this.contents) ? this.contents.value : this.contents;
4299
- return identity.isCollection(this.contents) ? this.contents.getIn(path18, keepScalar) : undefined;
4299
+ return identity.isCollection(this.contents) ? this.contents.getIn(path19, keepScalar) : undefined;
4300
4300
  }
4301
4301
  has(key) {
4302
4302
  return identity.isCollection(this.contents) ? this.contents.has(key) : false;
4303
4303
  }
4304
- hasIn(path18) {
4305
- if (Collection.isEmptyPath(path18))
4304
+ hasIn(path19) {
4305
+ if (Collection.isEmptyPath(path19))
4306
4306
  return this.contents !== undefined;
4307
- return identity.isCollection(this.contents) ? this.contents.hasIn(path18) : false;
4307
+ return identity.isCollection(this.contents) ? this.contents.hasIn(path19) : false;
4308
4308
  }
4309
4309
  set(key, value) {
4310
4310
  if (this.contents == null) {
@@ -4313,13 +4313,13 @@ var require_Document = __commonJS((exports) => {
4313
4313
  this.contents.set(key, value);
4314
4314
  }
4315
4315
  }
4316
- setIn(path18, value) {
4317
- if (Collection.isEmptyPath(path18)) {
4316
+ setIn(path19, value) {
4317
+ if (Collection.isEmptyPath(path19)) {
4318
4318
  this.contents = value;
4319
4319
  } else if (this.contents == null) {
4320
- this.contents = Collection.collectionFromPath(this.schema, Array.from(path18), value);
4320
+ this.contents = Collection.collectionFromPath(this.schema, Array.from(path19), value);
4321
4321
  } else if (assertCollection(this.contents)) {
4322
- this.contents.setIn(path18, value);
4322
+ this.contents.setIn(path19, value);
4323
4323
  }
4324
4324
  }
4325
4325
  setSchema(version, options = {}) {
@@ -6214,9 +6214,9 @@ var require_cst_visit = __commonJS((exports) => {
6214
6214
  visit.BREAK = BREAK;
6215
6215
  visit.SKIP = SKIP;
6216
6216
  visit.REMOVE = REMOVE;
6217
- visit.itemAtPath = (cst, path18) => {
6217
+ visit.itemAtPath = (cst, path19) => {
6218
6218
  let item = cst;
6219
- for (const [field, index] of path18) {
6219
+ for (const [field, index] of path19) {
6220
6220
  const tok = item?.[field];
6221
6221
  if (tok && "items" in tok) {
6222
6222
  item = tok.items[index];
@@ -6225,23 +6225,23 @@ var require_cst_visit = __commonJS((exports) => {
6225
6225
  }
6226
6226
  return item;
6227
6227
  };
6228
- visit.parentCollection = (cst, path18) => {
6229
- const parent = visit.itemAtPath(cst, path18.slice(0, -1));
6230
- const field = path18[path18.length - 1][0];
6228
+ visit.parentCollection = (cst, path19) => {
6229
+ const parent = visit.itemAtPath(cst, path19.slice(0, -1));
6230
+ const field = path19[path19.length - 1][0];
6231
6231
  const coll = parent?.[field];
6232
6232
  if (coll && "items" in coll)
6233
6233
  return coll;
6234
6234
  throw new Error("Parent collection not found");
6235
6235
  };
6236
- function _visit(path18, item, visitor) {
6237
- let ctrl = visitor(item, path18);
6236
+ function _visit(path19, item, visitor) {
6237
+ let ctrl = visitor(item, path19);
6238
6238
  if (typeof ctrl === "symbol")
6239
6239
  return ctrl;
6240
6240
  for (const field of ["key", "value"]) {
6241
6241
  const token = item[field];
6242
6242
  if (token && "items" in token) {
6243
6243
  for (let i = 0;i < token.items.length; ++i) {
6244
- const ci = _visit(Object.freeze(path18.concat([[field, i]])), token.items[i], visitor);
6244
+ const ci = _visit(Object.freeze(path19.concat([[field, i]])), token.items[i], visitor);
6245
6245
  if (typeof ci === "number")
6246
6246
  i = ci - 1;
6247
6247
  else if (ci === BREAK)
@@ -6252,10 +6252,10 @@ var require_cst_visit = __commonJS((exports) => {
6252
6252
  }
6253
6253
  }
6254
6254
  if (typeof ctrl === "function" && field === "key")
6255
- ctrl = ctrl(item, path18);
6255
+ ctrl = ctrl(item, path19);
6256
6256
  }
6257
6257
  }
6258
- return typeof ctrl === "function" ? ctrl(item, path18) : ctrl;
6258
+ return typeof ctrl === "function" ? ctrl(item, path19) : ctrl;
6259
6259
  }
6260
6260
  exports.visit = visit;
6261
6261
  });
@@ -8011,11 +8011,11 @@ function shouldStopByStuck(history, cfg) {
8011
8011
  async function withTimeout4(p, timeoutMs) {
8012
8012
  if (timeoutMs <= 0)
8013
8013
  return await p;
8014
- return await new Promise((resolve16, reject) => {
8014
+ return await new Promise((resolve17, reject) => {
8015
8015
  const timer = setTimeout(() => reject(new Error(`timeout after ${timeoutMs}ms`)), timeoutMs);
8016
8016
  Promise.resolve(p).then((v) => {
8017
8017
  clearTimeout(timer);
8018
- resolve16(v);
8018
+ resolve17(v);
8019
8019
  }, (err) => {
8020
8020
  clearTimeout(timer);
8021
8021
  reject(err);
@@ -14575,6 +14575,292 @@ async function execute28(input) {
14575
14575
  };
14576
14576
  }
14577
14577
  }
14578
+ // tools/adr-init.ts
14579
+ import { z as z30 } from "zod";
14580
+
14581
+ // lib/adr-init.ts
14582
+ import { spawnSync } from "node:child_process";
14583
+ import { existsSync as existsSync4, promises as fsp } from "node:fs";
14584
+ import * as path16 from "node:path";
14585
+ import * as url from "node:url";
14586
+ function resolveAssetsRoot() {
14587
+ const here = path16.dirname(url.fileURLToPath(import.meta.url));
14588
+ let dir = here;
14589
+ for (let i = 0;i < 6; i++) {
14590
+ if (existsSync4(path16.join(dir, "package.json")) && existsSync4(path16.join(dir, "assets", "adr-init"))) {
14591
+ return path16.join(dir, "assets", "adr-init");
14592
+ }
14593
+ const parent = path16.dirname(dir);
14594
+ if (parent === dir)
14595
+ break;
14596
+ dir = parent;
14597
+ }
14598
+ throw new Error(`assets/adr-init/ 未找到(从 ${here} 上溯 6 层);` + `如果是 npm 安装请检查 package.json "files" 字段是否包含 "assets/"`);
14599
+ }
14600
+ function isGitRepo2(cwd) {
14601
+ try {
14602
+ const r = spawnSync("git", ["rev-parse", "--show-toplevel"], {
14603
+ cwd,
14604
+ stdio: "pipe",
14605
+ encoding: "utf8"
14606
+ });
14607
+ return r.status === 0;
14608
+ } catch {
14609
+ return false;
14610
+ }
14611
+ }
14612
+ function runGitConfigHooksPath(cwd) {
14613
+ try {
14614
+ const r = spawnSync("git", ["config", "core.hooksPath", ".githooks"], {
14615
+ cwd,
14616
+ stdio: "pipe",
14617
+ encoding: "utf8"
14618
+ });
14619
+ if (r.status === 0)
14620
+ return { ok: true };
14621
+ return { ok: false, error: (r.stderr ?? "").trim() || `exit=${r.status}` };
14622
+ } catch (e) {
14623
+ return { ok: false, error: e instanceof Error ? e.message : String(e) };
14624
+ }
14625
+ }
14626
+ async function runAdrInit(opts = {}) {
14627
+ const cwd = path16.resolve(opts.cwd ?? process.cwd());
14628
+ const force = !!opts.force;
14629
+ const dryRun = !!opts.dryRun;
14630
+ const writePrepare = !!opts.writePrepare;
14631
+ const installPrePush = opts.installPrePush !== false;
14632
+ const result = {
14633
+ ok: true,
14634
+ wrote: [],
14635
+ skipped: [],
14636
+ backedUp: [],
14637
+ suggestions: [],
14638
+ warnings: [],
14639
+ dryRun
14640
+ };
14641
+ if (!isGitRepo2(cwd)) {
14642
+ result.ok = false;
14643
+ result.reason = "not_git_repo";
14644
+ result.warnings.push(`${cwd} 不是 git 仓库;adr-init 需要 git 才能下发 hooks`);
14645
+ return result;
14646
+ }
14647
+ let assetsRoot;
14648
+ try {
14649
+ assetsRoot = resolveAssetsRoot();
14650
+ } catch (e) {
14651
+ result.ok = false;
14652
+ result.reason = "assets_not_found";
14653
+ result.warnings.push(e instanceof Error ? e.message : String(e));
14654
+ return result;
14655
+ }
14656
+ const ts = new Date().toISOString().replace(/[:.]/g, "-");
14657
+ const plan = [
14658
+ { src: "scripts/adr-check.mjs", dst: "scripts/adr-check.mjs" },
14659
+ { src: "scripts/adr-index-sync.mjs", dst: "scripts/adr-index-sync.mjs" },
14660
+ { src: "docs/adr/README.md", dst: "docs/adr/README.md" },
14661
+ { src: "docs/adr/template.md", dst: "docs/adr/template.md" },
14662
+ {
14663
+ src: ".github/pull_request_template.md",
14664
+ dst: ".github/pull_request_template.md"
14665
+ },
14666
+ { src: "githooks/pre-commit.sh", dst: ".githooks/pre-commit", chmod: 493 }
14667
+ ];
14668
+ if (installPrePush) {
14669
+ plan.push({
14670
+ src: "githooks/pre-push.sh",
14671
+ dst: ".githooks/pre-push",
14672
+ chmod: 493
14673
+ });
14674
+ }
14675
+ for (const item of plan) {
14676
+ const srcAbs = path16.join(assetsRoot, item.src);
14677
+ const dstAbs = path16.join(cwd, item.dst);
14678
+ if (!existsSync4(srcAbs)) {
14679
+ result.warnings.push(`资产缺失:${item.src}(跳过 ${item.dst})`);
14680
+ continue;
14681
+ }
14682
+ if (existsSync4(dstAbs)) {
14683
+ if (!force) {
14684
+ result.skipped.push(item.dst);
14685
+ continue;
14686
+ }
14687
+ const bakRel = `${item.dst}.bak.${ts}`;
14688
+ if (!dryRun) {
14689
+ try {
14690
+ await fsp.copyFile(dstAbs, path16.join(cwd, bakRel));
14691
+ } catch (e) {
14692
+ result.ok = false;
14693
+ result.reason = "io_error";
14694
+ result.warnings.push(`备份 ${item.dst} 失败:${e instanceof Error ? e.message : String(e)}`);
14695
+ return result;
14696
+ }
14697
+ }
14698
+ result.backedUp.push(bakRel);
14699
+ }
14700
+ if (!dryRun) {
14701
+ try {
14702
+ await fsp.mkdir(path16.dirname(dstAbs), { recursive: true });
14703
+ await fsp.copyFile(srcAbs, dstAbs);
14704
+ if (item.chmod !== undefined) {
14705
+ try {
14706
+ await fsp.chmod(dstAbs, item.chmod);
14707
+ } catch (e) {
14708
+ if (process.platform !== "win32") {
14709
+ result.warnings.push(`chmod ${item.dst} 失败:${e instanceof Error ? e.message : String(e)}`);
14710
+ }
14711
+ }
14712
+ }
14713
+ } catch (e) {
14714
+ result.ok = false;
14715
+ result.reason = "io_error";
14716
+ result.warnings.push(`写入 ${item.dst} 失败:${e instanceof Error ? e.message : String(e)}`);
14717
+ return result;
14718
+ }
14719
+ }
14720
+ result.wrote.push(item.dst);
14721
+ }
14722
+ if (!dryRun) {
14723
+ const r = runGitConfigHooksPath(cwd);
14724
+ if (r.ok) {
14725
+ result.suggestions.push("已运行:git config core.hooksPath .githooks(hooks 已启用)");
14726
+ } else {
14727
+ result.warnings.push(`自动启用 hooks 失败:${r.error ?? "未知错误"};请手动执行 git config core.hooksPath .githooks`);
14728
+ }
14729
+ } else {
14730
+ result.suggestions.push("[dry-run] 将运行:git config core.hooksPath .githooks");
14731
+ }
14732
+ const pkgPath = path16.join(cwd, "package.json");
14733
+ const isNpm = existsSync4(pkgPath);
14734
+ if (isNpm) {
14735
+ if (writePrepare) {
14736
+ try {
14737
+ const raw = await fsp.readFile(pkgPath, "utf8");
14738
+ const pkg = JSON.parse(raw);
14739
+ const existing = (pkg.scripts && typeof pkg.scripts.prepare === "string" ? pkg.scripts.prepare : "") ?? "";
14740
+ const target = "git config core.hooksPath .githooks";
14741
+ if (existing.includes(target)) {
14742
+ result.suggestions.push(`package.json scripts.prepare 已含目标命令,无需改动`);
14743
+ } else {
14744
+ const bakRel = `package.json.bak.${ts}`;
14745
+ if (!dryRun) {
14746
+ try {
14747
+ await fsp.copyFile(pkgPath, path16.join(cwd, bakRel));
14748
+ } catch (e) {
14749
+ result.warnings.push(`备份 package.json 失败:${e instanceof Error ? e.message : String(e)}`);
14750
+ }
14751
+ }
14752
+ result.backedUp.push(bakRel);
14753
+ if (existing.includes("husky")) {
14754
+ result.warnings.push(`package.json scripts.prepare 已含 husky,仍合并写入;如有问题可回滚 ${bakRel}`);
14755
+ }
14756
+ const merged = existing ? `${existing} && ${target}` : target;
14757
+ pkg.scripts = { ...pkg.scripts ?? {}, prepare: merged };
14758
+ if (!dryRun) {
14759
+ try {
14760
+ await fsp.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + `
14761
+ `, "utf8");
14762
+ } catch (e) {
14763
+ result.ok = false;
14764
+ result.reason = "io_error";
14765
+ result.warnings.push(`写入 package.json 失败:${e instanceof Error ? e.message : String(e)}`);
14766
+ return result;
14767
+ }
14768
+ }
14769
+ result.wrote.push("package.json");
14770
+ }
14771
+ } catch (e) {
14772
+ result.warnings.push(`读取 package.json 失败:${e instanceof Error ? e.message : String(e)}`);
14773
+ }
14774
+ } else {
14775
+ result.suggestions.push(`(可选) 在 package.json scripts.prepare 追加:"git config core.hooksPath .githooks",` + `或下次跑 codeforge adr-init --write-prepare 自动合并(写前自动 backup package.json)`);
14776
+ }
14777
+ } else {
14778
+ result.suggestions.push("非 npm 项目:请在 README / AGENTS.md 提醒首次 clone 后跑 git config core.hooksPath .githooks");
14779
+ }
14780
+ return result;
14781
+ }
14782
+
14783
+ // tools/adr-init.ts
14784
+ var description29 = [
14785
+ "把 CodeForge 的 ADR 体系(adr-check / adr-index-sync / pre-commit / pre-push / docs/adr 模板 / PR 模板)一键下发到当前 git 项目。",
14786
+ "**何时调用**:",
14787
+ "- 用户说「给这个项目加 ADR / 装一下 ADR 检查 / 同步一下 ADR 模板」",
14788
+ "- 新项目 init 流程中希望把决策记录体系一起带上",
14789
+ "**特点**:零 npm 依赖(用 git config core.hooksPath 而非 husky);跟仓库走(提交 .githooks/ 进 git);幂等(默认 skip 已存在文件,--force 自动 .bak.<ts> 备份)",
14790
+ "**何时不需要**:当前项目已有完整的 ADR + hooks(默认 skip 行为会保护,但调用本身浪费一次 IO)"
14791
+ ].join(`
14792
+ `);
14793
+ var ArgsSchema29 = z30.object({
14794
+ cwd: z30.string().optional().describe("目标项目根目录,默认 process.cwd();通常无需传"),
14795
+ force: z30.boolean().optional().describe("已存在文件覆盖;覆盖前自动 .bak.<ts> 备份"),
14796
+ dryRun: z30.boolean().optional().describe("只输出将要执行的写入计划,不实际写盘"),
14797
+ writePrepare: z30.boolean().optional().describe("(npm 项目)自动合并 git config core.hooksPath 到 package.json scripts.prepare;写前自动 backup package.json;默认只建议不写"),
14798
+ installPrePush: z30.boolean().optional().describe("是否同时生成 .githooks/pre-push hook,默认 true")
14799
+ });
14800
+ function summarize(r) {
14801
+ const lines = [];
14802
+ lines.push(`# adr-init 执行结果${r.dryRun ? "(dry-run)" : ""}`);
14803
+ if (r.wrote.length > 0) {
14804
+ lines.push(`
14805
+ ## 写入(${r.wrote.length})`);
14806
+ for (const f of r.wrote)
14807
+ lines.push(`- ${f}`);
14808
+ }
14809
+ if (r.skipped.length > 0) {
14810
+ lines.push(`
14811
+ ## 跳过 / 已存在(${r.skipped.length}) — 加 --force 覆盖`);
14812
+ for (const f of r.skipped)
14813
+ lines.push(`- ${f}`);
14814
+ }
14815
+ if (r.backedUp.length > 0) {
14816
+ lines.push(`
14817
+ ## 备份(${r.backedUp.length})`);
14818
+ for (const f of r.backedUp)
14819
+ lines.push(`- ${f}`);
14820
+ }
14821
+ if (r.suggestions.length > 0) {
14822
+ lines.push(`
14823
+ ## 建议`);
14824
+ for (const s of r.suggestions)
14825
+ lines.push(`- ${s}`);
14826
+ }
14827
+ if (r.warnings.length > 0) {
14828
+ lines.push(`
14829
+ ## 警告`);
14830
+ for (const w of r.warnings)
14831
+ lines.push(`- ${w}`);
14832
+ }
14833
+ return lines.join(`
14834
+ `);
14835
+ }
14836
+ async function execute29(input) {
14837
+ const parsed = ArgsSchema29.safeParse(input);
14838
+ if (!parsed.success) {
14839
+ return {
14840
+ ok: false,
14841
+ reason: "invalid_input",
14842
+ message: parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ")
14843
+ };
14844
+ }
14845
+ try {
14846
+ const result = await runAdrInit(parsed.data);
14847
+ if (!result.ok) {
14848
+ return {
14849
+ ok: false,
14850
+ reason: result.reason ?? "io_error",
14851
+ message: result.warnings.join("; ") || "adr-init 执行失败",
14852
+ result
14853
+ };
14854
+ }
14855
+ return { ok: true, result, summary: summarize(result) };
14856
+ } catch (e) {
14857
+ return {
14858
+ ok: false,
14859
+ reason: "io_error",
14860
+ message: e instanceof Error ? e.message : String(e)
14861
+ };
14862
+ }
14863
+ }
14578
14864
  // lib/opencode-runner.ts
14579
14865
  function makeOpencodeRunner(opts) {
14580
14866
  const log4 = opts.log ?? (() => {});
@@ -14732,20 +15018,20 @@ async function withTimeout3(p, ms, signal) {
14732
15018
  throw err;
14733
15019
  }
14734
15020
  }
14735
- return await new Promise((resolve13, reject) => {
15021
+ return await new Promise((resolve14, reject) => {
14736
15022
  let settled = false;
14737
15023
  const timer = setTimeout(() => {
14738
15024
  if (settled)
14739
15025
  return;
14740
15026
  settled = true;
14741
- resolve13({ kind: "timeout" });
15027
+ resolve14({ kind: "timeout" });
14742
15028
  }, ms);
14743
15029
  const onAbort = () => {
14744
15030
  if (settled)
14745
15031
  return;
14746
15032
  settled = true;
14747
15033
  clearTimeout(timer);
14748
- resolve13({ kind: "aborted" });
15034
+ resolve14({ kind: "aborted" });
14749
15035
  };
14750
15036
  signal?.addEventListener("abort", onAbort, { once: true });
14751
15037
  p.then((value) => {
@@ -14754,7 +15040,7 @@ async function withTimeout3(p, ms, signal) {
14754
15040
  settled = true;
14755
15041
  clearTimeout(timer);
14756
15042
  signal?.removeEventListener("abort", onAbort);
14757
- resolve13({ kind: "ok", value });
15043
+ resolve14({ kind: "ok", value });
14758
15044
  }, (err) => {
14759
15045
  if (settled)
14760
15046
  return;
@@ -14837,18 +15123,18 @@ init_decision_parser();
14837
15123
  // lib/parent-map-store.ts
14838
15124
  init_runtime_paths();
14839
15125
  import { promises as fs13 } from "node:fs";
14840
- import * as path16 from "node:path";
15126
+ import * as path17 from "node:path";
14841
15127
  var PARENT_MAP_VERSION = 1;
14842
15128
  var PARENT_MAP_LOCK_TIMEOUT_MS = 2000;
14843
15129
  var PARENT_MAP_MAX_ENTRIES = 256;
14844
15130
  function parentMapDir(mainRoot) {
14845
- return path16.join(runtimeDir(path16.resolve(mainRoot), { ensure: false }), "session-worktrees");
15131
+ return path17.join(runtimeDir(path17.resolve(mainRoot), { ensure: false }), "session-worktrees");
14846
15132
  }
14847
15133
  function parentMapPath(mainRoot) {
14848
- return path16.join(parentMapDir(mainRoot), "parent-map.json");
15134
+ return path17.join(parentMapDir(mainRoot), "parent-map.json");
14849
15135
  }
14850
15136
  function parentMapLockPath(mainRoot) {
14851
- return path16.join(parentMapDir(mainRoot), "parent-map.lock");
15137
+ return path17.join(parentMapDir(mainRoot), "parent-map.lock");
14852
15138
  }
14853
15139
  async function readParentMapFile(mainRoot) {
14854
15140
  const file = parentMapPath(mainRoot);
@@ -14868,7 +15154,7 @@ async function readParentMapFile(mainRoot) {
14868
15154
  }
14869
15155
  async function writeParentMapFile(mainRoot, payload) {
14870
15156
  const file = parentMapPath(mainRoot);
14871
- await fs13.mkdir(path16.dirname(file), { recursive: true });
15157
+ await fs13.mkdir(path17.dirname(file), { recursive: true });
14872
15158
  const tmp = `${file}.tmp-${process.pid}-${Date.now()}`;
14873
15159
  await fs13.writeFile(tmp, JSON.stringify(payload, null, 2), "utf8");
14874
15160
  await fs13.rename(tmp, file);
@@ -14903,7 +15189,7 @@ async function loadParentMap(mainRoot) {
14903
15189
  }
14904
15190
  async function mutateParentMap(mainRoot, mutator, opts = {}) {
14905
15191
  const lockPath = parentMapLockPath(mainRoot);
14906
- await fs13.mkdir(path16.dirname(lockPath), { recursive: true });
15192
+ await fs13.mkdir(path17.dirname(lockPath), { recursive: true });
14907
15193
  const lockOpts = {
14908
15194
  timeoutMs: opts.timeoutMs ?? PARENT_MAP_LOCK_TIMEOUT_MS,
14909
15195
  ...opts
@@ -14922,7 +15208,7 @@ async function appendParentEntry(mainRoot, childID, parentID, ts, opts = {}) {
14922
15208
  if (!childID || !parentID)
14923
15209
  return;
14924
15210
  const lockPath = parentMapLockPath(mainRoot);
14925
- await fs13.mkdir(path16.dirname(lockPath), { recursive: true });
15211
+ await fs13.mkdir(path17.dirname(lockPath), { recursive: true });
14926
15212
  const lockOpts = {
14927
15213
  timeoutMs: opts.timeoutMs ?? PARENT_MAP_LOCK_TIMEOUT_MS,
14928
15214
  ...opts
@@ -15170,7 +15456,7 @@ async function raceAbortTimeout(p, signal, timeoutMs, label) {
15170
15456
  e.name = "AbortError";
15171
15457
  throw e;
15172
15458
  }
15173
- return await new Promise((resolve14, reject) => {
15459
+ return await new Promise((resolve15, reject) => {
15174
15460
  let settled = false;
15175
15461
  const timer = setTimeout(() => {
15176
15462
  if (settled)
@@ -15195,7 +15481,7 @@ async function raceAbortTimeout(p, signal, timeoutMs, label) {
15195
15481
  settled = true;
15196
15482
  clearTimeout(timer);
15197
15483
  signal?.removeEventListener("abort", onAbort);
15198
- resolve14(v);
15484
+ resolve15(v);
15199
15485
  }, (e) => {
15200
15486
  if (settled)
15201
15487
  return;
@@ -15256,7 +15542,7 @@ function clip4(s, max) {
15256
15542
 
15257
15543
  // lib/codeforge-runtime.ts
15258
15544
  import { promises as fs14 } from "node:fs";
15259
- import * as path17 from "node:path";
15545
+ import * as path18 from "node:path";
15260
15546
  var DEFAULT_RUNTIME = {
15261
15547
  autonomy: {
15262
15548
  downgrade_on_risky: true
@@ -15299,7 +15585,7 @@ function loadRuntimeSync(opts = {}) {
15299
15585
  }
15300
15586
  async function loadRuntime(opts = {}) {
15301
15587
  const root = opts.root ?? process.cwd();
15302
- const abs = path17.resolve(root, opts.file ?? CONFIG_FILE);
15588
+ const abs = path18.resolve(root, opts.file ?? CONFIG_FILE);
15303
15589
  let raw;
15304
15590
  try {
15305
15591
  raw = await fs14.readFile(abs, "utf8");
@@ -15583,7 +15869,7 @@ var toolHeartbeatServer = async (ctx) => {
15583
15869
  var handler7 = toolHeartbeatServer;
15584
15870
 
15585
15871
  // plugins/codeforge-tools.ts
15586
- var z30 = tool.schema;
15872
+ var z31 = tool.schema;
15587
15873
  var PLUGIN_NAME8 = "codeforge-tools";
15588
15874
  logLifecycle(PLUGIN_NAME8, "import");
15589
15875
  function wrap(output, metadata) {
@@ -15630,15 +15916,15 @@ function buildKhWriteTools() {
15630
15916
  add_knowledge: tool({
15631
15917
  description: description3,
15632
15918
  args: {
15633
- title: z30.string().min(2).max(60).describe("简短标题(2-60 字)"),
15634
- content: z30.string().min(10).describe("详细内容(Markdown,独立可理解)"),
15635
- category: z30.string().optional().describe("分类(可选,自动推断)"),
15636
- tags: z30.array(z30.string()).optional(),
15637
- relatedFiles: z30.array(z30.string()).optional(),
15638
- relatedCommit: z30.string().optional(),
15639
- relatedPR: z30.string().optional(),
15640
- scope: z30.string().optional(),
15641
- preConfirmed: z30.boolean().optional()
15919
+ title: z31.string().min(2).max(60).describe("简短标题(2-60 字)"),
15920
+ content: z31.string().min(10).describe("详细内容(Markdown,独立可理解)"),
15921
+ category: z31.string().optional().describe("分类(可选,自动推断)"),
15922
+ tags: z31.array(z31.string()).optional(),
15923
+ relatedFiles: z31.array(z31.string()).optional(),
15924
+ relatedCommit: z31.string().optional(),
15925
+ relatedPR: z31.string().optional(),
15926
+ scope: z31.string().optional(),
15927
+ preConfirmed: z31.boolean().optional()
15642
15928
  },
15643
15929
  async execute(args) {
15644
15930
  return await runSafe("add_knowledge", async () => {
@@ -15653,18 +15939,18 @@ function buildKhWriteTools() {
15653
15939
  upload_document: tool({
15654
15940
  description: description4,
15655
15941
  args: {
15656
- name: z30.string().min(1).describe("文件名(含扩展名)"),
15657
- content: z30.string().min(1).describe("文件完整内容"),
15658
- encoding: z30.enum(["utf8", "base64"]).optional(),
15659
- chunk_strategy: z30.enum(["auto", "heading", "fixed", "paragraph", "separator"]).optional(),
15660
- chunk_size: z30.number().int().optional(),
15661
- chunk_overlap: z30.number().int().optional(),
15662
- separator: z30.string().optional(),
15663
- distill: z30.boolean().optional(),
15664
- category: z30.string().optional(),
15665
- tags: z30.array(z30.string()).optional(),
15666
- scope: z30.string().optional(),
15667
- pre_confirmed: z30.boolean().optional()
15942
+ name: z31.string().min(1).describe("文件名(含扩展名)"),
15943
+ content: z31.string().min(1).describe("文件完整内容"),
15944
+ encoding: z31.enum(["utf8", "base64"]).optional(),
15945
+ chunk_strategy: z31.enum(["auto", "heading", "fixed", "paragraph", "separator"]).optional(),
15946
+ chunk_size: z31.number().int().optional(),
15947
+ chunk_overlap: z31.number().int().optional(),
15948
+ separator: z31.string().optional(),
15949
+ distill: z31.boolean().optional(),
15950
+ category: z31.string().optional(),
15951
+ tags: z31.array(z31.string()).optional(),
15952
+ scope: z31.string().optional(),
15953
+ pre_confirmed: z31.boolean().optional()
15668
15954
  },
15669
15955
  async execute(args) {
15670
15956
  return await runSafeTracked("upload_document", async () => {
@@ -15679,16 +15965,16 @@ function buildKhWriteTools() {
15679
15965
  upload_document_from_url: tool({
15680
15966
  description: description5,
15681
15967
  args: {
15682
- url: z30.string().min(1).describe("HTTP(s) URL"),
15683
- chunk_strategy: z30.enum(["auto", "heading", "fixed", "paragraph", "separator"]).optional(),
15684
- chunk_size: z30.number().int().optional(),
15685
- chunk_overlap: z30.number().int().optional(),
15686
- separator: z30.string().optional(),
15687
- distill: z30.boolean().optional(),
15688
- category: z30.string().optional(),
15689
- tags: z30.array(z30.string()).optional(),
15690
- scope: z30.string().optional(),
15691
- pre_confirmed: z30.boolean().optional()
15968
+ url: z31.string().min(1).describe("HTTP(s) URL"),
15969
+ chunk_strategy: z31.enum(["auto", "heading", "fixed", "paragraph", "separator"]).optional(),
15970
+ chunk_size: z31.number().int().optional(),
15971
+ chunk_overlap: z31.number().int().optional(),
15972
+ separator: z31.string().optional(),
15973
+ distill: z31.boolean().optional(),
15974
+ category: z31.string().optional(),
15975
+ tags: z31.array(z31.string()).optional(),
15976
+ scope: z31.string().optional(),
15977
+ pre_confirmed: z31.boolean().optional()
15692
15978
  },
15693
15979
  async execute(args) {
15694
15980
  return await runSafeTracked("upload_document_from_url", async () => {
@@ -15703,11 +15989,11 @@ function buildKhWriteTools() {
15703
15989
  update_knowledge: tool({
15704
15990
  description: description6,
15705
15991
  args: {
15706
- knowledgeId: z30.string().min(1).describe("知识 ID(从 smart_search 获取)"),
15707
- appendContent: z30.string().optional(),
15708
- replaceContent: z30.string().optional(),
15709
- additionalTags: z30.array(z30.string()).optional(),
15710
- category: z30.string().optional()
15992
+ knowledgeId: z31.string().min(1).describe("知识 ID(从 smart_search 获取)"),
15993
+ appendContent: z31.string().optional(),
15994
+ replaceContent: z31.string().optional(),
15995
+ additionalTags: z31.array(z31.string()).optional(),
15996
+ category: z31.string().optional()
15711
15997
  },
15712
15998
  async execute(args) {
15713
15999
  return await runSafe("update_knowledge", async () => {
@@ -15722,8 +16008,8 @@ function buildKhWriteTools() {
15722
16008
  flag_outdated: tool({
15723
16009
  description: description7,
15724
16010
  args: {
15725
- knowledgeId: z30.string().min(1).describe("知识 ID"),
15726
- reason: z30.string().min(5).max(500).describe("过时原因简述")
16011
+ knowledgeId: z31.string().min(1).describe("知识 ID"),
16012
+ reason: z31.string().min(5).max(500).describe("过时原因简述")
15727
16013
  },
15728
16014
  async execute(args) {
15729
16015
  return await runSafe("flag_outdated", async () => {
@@ -15738,8 +16024,8 @@ function buildKhWriteTools() {
15738
16024
  delete_knowledge: tool({
15739
16025
  description: description8,
15740
16026
  args: {
15741
- knowledgeId: z30.string().min(1).describe("知识 ID"),
15742
- reason: z30.string().min(3).max(300).describe("删除原因(写入审计日志)")
16027
+ knowledgeId: z31.string().min(1).describe("知识 ID"),
16028
+ reason: z31.string().min(3).max(300).describe("删除原因(写入审计日志)")
15743
16029
  },
15744
16030
  async execute(args) {
15745
16031
  return await runSafe("delete_knowledge", async () => {
@@ -15754,9 +16040,9 @@ function buildKhWriteTools() {
15754
16040
  list_recent: tool({
15755
16041
  description: description9,
15756
16042
  args: {
15757
- days: z30.number().int().min(1).max(90).optional().describe("最近几天(默认 7)"),
15758
- limit: z30.number().int().min(1).max(50).optional().describe("返回条数(默认 10)"),
15759
- category: z30.string().optional()
16043
+ days: z31.number().int().min(1).max(90).optional().describe("最近几天(默认 7)"),
16044
+ limit: z31.number().int().min(1).max(50).optional().describe("返回条数(默认 10)"),
16045
+ category: z31.string().optional()
15760
16046
  },
15761
16047
  async execute(args) {
15762
16048
  return await runSafe("list_recent", async () => {
@@ -15771,12 +16057,12 @@ function buildKhWriteTools() {
15771
16057
  update_working_memory: tool({
15772
16058
  description: description10,
15773
16059
  args: {
15774
- section: z30.string().min(1).describe("分区名(constraints / in_progress / topic:xxx 等)"),
15775
- items: z30.array(z30.object({
15776
- content: z30.string().min(1),
15777
- priority: z30.number().optional()
16060
+ section: z31.string().min(1).describe("分区名(constraints / in_progress / topic:xxx 等)"),
16061
+ items: z31.array(z31.object({
16062
+ content: z31.string().min(1),
16063
+ priority: z31.number().optional()
15778
16064
  })),
15779
- replace: z30.boolean().optional().describe("是否替换分区全部内容(默认 false=追加)")
16065
+ replace: z31.boolean().optional().describe("是否替换分区全部内容(默认 false=追加)")
15780
16066
  },
15781
16067
  async execute(args) {
15782
16068
  return await runSafe("update_working_memory", async () => {
@@ -15791,7 +16077,7 @@ function buildKhWriteTools() {
15791
16077
  get_working_memory: tool({
15792
16078
  description: description11,
15793
16079
  args: {
15794
- section: z30.string().optional().describe("分区名;不传则返回全部")
16080
+ section: z31.string().optional().describe("分区名;不传则返回全部")
15795
16081
  },
15796
16082
  async execute(args) {
15797
16083
  return await runSafe("get_working_memory", async () => {
@@ -15806,9 +16092,9 @@ function buildKhWriteTools() {
15806
16092
  update_user_preference: tool({
15807
16093
  description: description12,
15808
16094
  args: {
15809
- key: z30.string().min(1).max(64).describe("偏好键名(language / coding_style / signature 等)"),
15810
- value: z30.string().min(1).max(500).describe("偏好值"),
15811
- action: z30.enum(["set", "delete"]).optional()
16095
+ key: z31.string().min(1).max(64).describe("偏好键名(language / coding_style / signature 等)"),
16096
+ value: z31.string().min(1).max(500).describe("偏好值"),
16097
+ action: z31.enum(["set", "delete"]).optional()
15812
16098
  },
15813
16099
  async execute(args) {
15814
16100
  return await runSafe("update_user_preference", async () => {
@@ -15823,9 +16109,9 @@ function buildKhWriteTools() {
15823
16109
  web_search: tool({
15824
16110
  description: description13,
15825
16111
  args: {
15826
- query: z30.string().min(1).describe("搜索词"),
15827
- limit: z30.number().int().min(1).max(10).optional(),
15828
- time_range: z30.enum(["", "day", "week", "month", "year"]).optional()
16112
+ query: z31.string().min(1).describe("搜索词"),
16113
+ limit: z31.number().int().min(1).max(10).optional(),
16114
+ time_range: z31.enum(["", "day", "week", "month", "year"]).optional()
15829
16115
  },
15830
16116
  async execute(args) {
15831
16117
  return await runSafeTracked("web_search", async () => {
@@ -15840,11 +16126,11 @@ function buildKhWriteTools() {
15840
16126
  generate_image: tool({
15841
16127
  description: description14,
15842
16128
  args: {
15843
- prompt: z30.string().min(4).max(4000).describe("画面描述"),
15844
- n: z30.number().int().min(1).max(3).optional(),
15845
- size: z30.string().optional(),
15846
- transparent: z30.boolean().optional(),
15847
- alt: z30.string().optional()
16129
+ prompt: z31.string().min(4).max(4000).describe("画面描述"),
16130
+ n: z31.number().int().min(1).max(3).optional(),
16131
+ size: z31.string().optional(),
16132
+ transparent: z31.boolean().optional(),
16133
+ alt: z31.string().optional()
15848
16134
  },
15849
16135
  async execute(args) {
15850
16136
  return await runSafeTracked("generate_image", async () => {
@@ -15863,7 +16149,7 @@ function buildBrowserTools() {
15863
16149
  browser_navigate: tool({
15864
16150
  description: description19,
15865
16151
  args: {
15866
- url: z30.string().min(1).describe("要打开的 URL;必须是 http(s)")
16152
+ url: z31.string().min(1).describe("要打开的 URL;必须是 http(s)")
15867
16153
  },
15868
16154
  async execute(args) {
15869
16155
  return await runSafe("browser_navigate", async () => {
@@ -15878,7 +16164,7 @@ function buildBrowserTools() {
15878
16164
  browser_click: tool({
15879
16165
  description: description20,
15880
16166
  args: {
15881
- selector: z30.string().min(1).describe("CSS / Playwright locator,必须能唯一定位")
16167
+ selector: z31.string().min(1).describe("CSS / Playwright locator,必须能唯一定位")
15882
16168
  },
15883
16169
  async execute(args) {
15884
16170
  return await runSafe("browser_click", async () => {
@@ -15893,8 +16179,8 @@ function buildBrowserTools() {
15893
16179
  browser_fill: tool({
15894
16180
  description: description21,
15895
16181
  args: {
15896
- selector: z30.string().min(1).describe("CSS / Playwright locator"),
15897
- value: z30.string().describe("要填入的文本;原样写入不转义")
16182
+ selector: z31.string().min(1).describe("CSS / Playwright locator"),
16183
+ value: z31.string().describe("要填入的文本;原样写入不转义")
15898
16184
  },
15899
16185
  async execute(args) {
15900
16186
  return await runSafe("browser_fill", async () => {
@@ -15909,8 +16195,8 @@ function buildBrowserTools() {
15909
16195
  browser_screenshot: tool({
15910
16196
  description: description22,
15911
16197
  args: {
15912
- fullPage: z30.boolean().optional().describe("是否截全长页面(默认仅可视区)"),
15913
- selector: z30.string().optional().describe("CSS 选择器;指定时只截该元素")
16198
+ fullPage: z31.boolean().optional().describe("是否截全长页面(默认仅可视区)"),
16199
+ selector: z31.string().optional().describe("CSS 选择器;指定时只截该元素")
15914
16200
  },
15915
16201
  async execute(args) {
15916
16202
  return await runSafe("browser_screenshot", async () => {
@@ -15925,8 +16211,8 @@ function buildBrowserTools() {
15925
16211
  browser_console: tool({
15926
16212
  description: description23,
15927
16213
  args: {
15928
- level: z30.enum(["log", "info", "warn", "error", "debug"]).optional().describe("过滤级别"),
15929
- sinceTs: z30.number().optional().describe("timestamp >= sinceTs 的条目")
16214
+ level: z31.enum(["log", "info", "warn", "error", "debug"]).optional().describe("过滤级别"),
16215
+ sinceTs: z31.number().optional().describe("timestamp >= sinceTs 的条目")
15930
16216
  },
15931
16217
  async execute(args) {
15932
16218
  return await runSafe("browser_console", async () => {
@@ -15941,8 +16227,8 @@ function buildBrowserTools() {
15941
16227
  browser_network: tool({
15942
16228
  description: description24,
15943
16229
  args: {
15944
- method: z30.string().optional().describe("HTTP 方法过滤(GET/POST/...)"),
15945
- sinceTs: z30.number().optional().describe("start_ts >= sinceTs 的请求")
16230
+ method: z31.string().optional().describe("HTTP 方法过滤(GET/POST/...)"),
16231
+ sinceTs: z31.number().optional().describe("start_ts >= sinceTs 的请求")
15946
16232
  },
15947
16233
  async execute(args) {
15948
16234
  return await runSafe("browser_network", async () => {
@@ -15978,7 +16264,8 @@ var CORE_TOOL_NAMES = [
15978
16264
  "model_chain",
15979
16265
  "session_merge",
15980
16266
  "plan_write",
15981
- "plan_read"
16267
+ "plan_read",
16268
+ "adr_init"
15982
16269
  ];
15983
16270
  var BROWSER_TOOL_NAMES = [
15984
16271
  "browser_navigate",
@@ -16022,9 +16309,9 @@ var codeforgeToolsServer = async (ctx) => {
16022
16309
  smart_search: tool({
16023
16310
  description,
16024
16311
  args: {
16025
- query: z30.string().min(1).describe("自然语言查询词,例如「登录限流方案」「dev 服务器部署流程」"),
16026
- limit: z30.number().int().min(1).max(15).optional().describe("返回条数,默认 5;超过 15 会被 KH 截断"),
16027
- prefer: z30.array(z30.string()).optional().describe("偏好分类(best-effort);常用 architecture / anti_pattern / convention")
16312
+ query: z31.string().min(1).describe("自然语言查询词,例如「登录限流方案」「dev 服务器部署流程」"),
16313
+ limit: z31.number().int().min(1).max(15).optional().describe("返回条数,默认 5;超过 15 会被 KH 截断"),
16314
+ prefer: z31.array(z31.string()).optional().describe("偏好分类(best-effort);常用 architecture / anti_pattern / convention")
16028
16315
  },
16029
16316
  async execute(args) {
16030
16317
  return await runSafe("smart_search", async () => {
@@ -16043,8 +16330,8 @@ var codeforgeToolsServer = async (ctx) => {
16043
16330
  save_chat_insight: tool({
16044
16331
  description: description2,
16045
16332
  args: {
16046
- insight: z30.string().min(20).max(5000).describe("完整知识条目(Markdown)。推荐三段式:## 问题 / ## 原因 / ## 解决"),
16047
- category: z30.enum([
16333
+ insight: z31.string().min(20).max(5000).describe("完整知识条目(Markdown)。推荐三段式:## 问题 / ## 原因 / ## 解决"),
16334
+ category: z31.enum([
16048
16335
  "convention",
16049
16336
  "anti_pattern",
16050
16337
  "architecture",
@@ -16054,7 +16341,7 @@ var codeforgeToolsServer = async (ctx) => {
16054
16341
  "tooling",
16055
16342
  "decision"
16056
16343
  ]).describe("分类"),
16057
- tags: z30.array(z30.string().min(1)).optional().describe("标签数组,建议 `tech:xxx` / `platform:xxx`")
16344
+ tags: z31.array(z31.string().min(1)).optional().describe("标签数组,建议 `tech:xxx` / `platform:xxx`")
16058
16345
  },
16059
16346
  async execute(args) {
16060
16347
  return await runSafe("save_chat_insight", async () => {
@@ -16077,25 +16364,25 @@ var codeforgeToolsServer = async (ctx) => {
16077
16364
  ast_edit: tool({
16078
16365
  description: description15,
16079
16366
  args: {
16080
- action: z30.enum([
16367
+ action: z31.enum([
16081
16368
  "replace_anchor",
16082
16369
  "insert_after_anchor",
16083
16370
  "insert_before_anchor",
16084
16371
  "delete_range",
16085
16372
  "rename_symbol"
16086
16373
  ]).describe("编辑类型;不同 action 需要的字段不同"),
16087
- target: z30.string().min(1).describe("目标文件路径(相对 cwd 或绝对)"),
16088
- before_hash: z30.string().optional().describe("操作前的 sha256 hex(强烈建议传,新文件传 null/省略)"),
16089
- auto_stage: z30.boolean().optional().describe("已废弃(保留兼容字段):Phase 5 后 ast_edit 直接写入 session worktree,无需独立 stage"),
16090
- description: z30.string().optional().describe("可选变更说明(写入 audit log)"),
16091
- anchor: z30.string().optional().describe("anchor 类用:anchor 文本或 regex 源"),
16092
- regex: z30.boolean().optional().describe("anchor 是否按 RegExp 解释,默认 false"),
16093
- occurrence: z30.number().int().min(1).optional().describe("第几次匹配(1-based),默认 1"),
16094
- payload: z30.string().optional().describe("anchor 类用:要写入的内容"),
16095
- start: z30.number().int().min(1).optional().describe("delete_range 用:起始行(1-based)"),
16096
- end: z30.number().int().min(1).optional().describe("delete_range 用:结束行(1-based)"),
16097
- old_name: z30.string().optional().describe("rename_symbol 用:旧标识符"),
16098
- new_name: z30.string().optional().describe("rename_symbol 用:新标识符")
16374
+ target: z31.string().min(1).describe("目标文件路径(相对 cwd 或绝对)"),
16375
+ before_hash: z31.string().optional().describe("操作前的 sha256 hex(强烈建议传,新文件传 null/省略)"),
16376
+ auto_stage: z31.boolean().optional().describe("已废弃(保留兼容字段):Phase 5 后 ast_edit 直接写入 session worktree,无需独立 stage"),
16377
+ description: z31.string().optional().describe("可选变更说明(写入 audit log)"),
16378
+ anchor: z31.string().optional().describe("anchor 类用:anchor 文本或 regex 源"),
16379
+ regex: z31.boolean().optional().describe("anchor 是否按 RegExp 解释,默认 false"),
16380
+ occurrence: z31.number().int().min(1).optional().describe("第几次匹配(1-based),默认 1"),
16381
+ payload: z31.string().optional().describe("anchor 类用:要写入的内容"),
16382
+ start: z31.number().int().min(1).optional().describe("delete_range 用:起始行(1-based)"),
16383
+ end: z31.number().int().min(1).optional().describe("delete_range 用:结束行(1-based)"),
16384
+ old_name: z31.string().optional().describe("rename_symbol 用:旧标识符"),
16385
+ new_name: z31.string().optional().describe("rename_symbol 用:新标识符")
16099
16386
  },
16100
16387
  async execute(args) {
16101
16388
  return await runSafeTracked("ast_edit", async () => {
@@ -16118,10 +16405,10 @@ var codeforgeToolsServer = async (ctx) => {
16118
16405
  repo_map: tool({
16119
16406
  description: description16,
16120
16407
  args: {
16121
- root: z30.string().optional().describe("扫描根目录,默认 cwd"),
16122
- top: z30.number().int().min(1).max(100).optional().describe("展示 top N 文件,默认 20"),
16123
- focus: z30.string().optional().describe("聚焦文件(POSIX 相对路径)"),
16124
- max_files: z30.number().int().min(10).max(5000).optional().describe("扫描上限,默认 500")
16408
+ root: z31.string().optional().describe("扫描根目录,默认 cwd"),
16409
+ top: z31.number().int().min(1).max(100).optional().describe("展示 top N 文件,默认 20"),
16410
+ focus: z31.string().optional().describe("聚焦文件(POSIX 相对路径)"),
16411
+ max_files: z31.number().int().min(10).max(5000).optional().describe("扫描上限,默认 500")
16125
16412
  },
16126
16413
  async execute(args) {
16127
16414
  return await runSafeTracked("repo_map", async () => {
@@ -16142,14 +16429,14 @@ var codeforgeToolsServer = async (ctx) => {
16142
16429
  rules_debug: tool({
16143
16430
  description: description17,
16144
16431
  args: {
16145
- current_agent: z30.string().optional().describe("当前 agent 名"),
16146
- root: z30.string().optional().describe("项目根目录"),
16147
- home_dir: z30.string().optional().describe("覆盖个人规则目录"),
16148
- project_dir: z30.string().optional().describe("覆盖项目规则目录"),
16149
- skip_personal: z30.boolean().optional(),
16150
- skip_project: z30.boolean().optional(),
16151
- skip_agent: z30.boolean().optional(),
16152
- markdown: z30.boolean().optional().describe("默认 true:渲染 markdown 摘要")
16432
+ current_agent: z31.string().optional().describe("当前 agent 名"),
16433
+ root: z31.string().optional().describe("项目根目录"),
16434
+ home_dir: z31.string().optional().describe("覆盖个人规则目录"),
16435
+ project_dir: z31.string().optional().describe("覆盖项目规则目录"),
16436
+ skip_personal: z31.boolean().optional(),
16437
+ skip_project: z31.boolean().optional(),
16438
+ skip_agent: z31.boolean().optional(),
16439
+ markdown: z31.boolean().optional().describe("默认 true:渲染 markdown 摘要")
16153
16440
  },
16154
16441
  async execute(args) {
16155
16442
  return await runSafe("rules_debug", async () => {
@@ -16164,14 +16451,14 @@ var codeforgeToolsServer = async (ctx) => {
16164
16451
  review_approval: tool({
16165
16452
  description: description18,
16166
16453
  args: {
16167
- verdict: z30.enum(["APPROVE", "APPROVE_WITH_NOTES"]).describe("审批裁决;REQUEST_CHANGES / BLOCK 不应调本工具"),
16168
- pendingIds: z30.array(z30.string().min(1)).min(1).describe("本次 APPROVE 覆盖的 pending change id 列表"),
16169
- notes: z30.string().min(1).max(2000).describe("审阅意见摘要(建议 ≤ 500 字)"),
16170
- decisionLine: z30.string().optional().describe("`## Decision` 节首行原文(默认 verdict 字面量,机审证据)"),
16171
- source: z30.enum(["reviewer", "codeforge-fallback"]).optional().describe("写入来源;默认 'reviewer',codeforge 补写时传 'codeforge-fallback'"),
16172
- reviewerAgent: z30.string().optional().describe("写入 agent name(默认 'reviewer';fallback 时为 'codeforge')"),
16173
- sessionId: z30.string().optional().describe("reviewer 子 session id(boomerang 溯源用,可选)"),
16174
- model: z30.string().optional().describe("审批模型 id(审计用,可选)")
16454
+ verdict: z31.enum(["APPROVE", "APPROVE_WITH_NOTES"]).describe("审批裁决;REQUEST_CHANGES / BLOCK 不应调本工具"),
16455
+ pendingIds: z31.array(z31.string().min(1)).min(1).describe("本次 APPROVE 覆盖的 pending change id 列表"),
16456
+ notes: z31.string().min(1).max(2000).describe("审阅意见摘要(建议 ≤ 500 字)"),
16457
+ decisionLine: z31.string().optional().describe("`## Decision` 节首行原文(默认 verdict 字面量,机审证据)"),
16458
+ source: z31.enum(["reviewer", "codeforge-fallback"]).optional().describe("写入来源;默认 'reviewer',codeforge 补写时传 'codeforge-fallback'"),
16459
+ reviewerAgent: z31.string().optional().describe("写入 agent name(默认 'reviewer';fallback 时为 'codeforge')"),
16460
+ sessionId: z31.string().optional().describe("reviewer 子 session id(boomerang 溯源用,可选)"),
16461
+ model: z31.string().optional().describe("审批模型 id(审计用,可选)")
16175
16462
  },
16176
16463
  async execute(args) {
16177
16464
  return await runSafe("review_approval", async () => {
@@ -16192,10 +16479,10 @@ var codeforgeToolsServer = async (ctx) => {
16192
16479
  model_chain: tool({
16193
16480
  description: description25,
16194
16481
  args: {
16195
- agent: z30.string().optional().describe("查指定 agent;不传 → 列出全部"),
16196
- current: z30.string().optional().describe("当前已用过的模型(<provider>/<id>),用于算下一档"),
16197
- root: z30.string().optional().describe("项目根目录,默认 cwd"),
16198
- config_file: z30.string().optional().describe("配置文件名;默认 codeforge.json")
16482
+ agent: z31.string().optional().describe("查指定 agent;不传 → 列出全部"),
16483
+ current: z31.string().optional().describe("当前已用过的模型(<provider>/<id>),用于算下一档"),
16484
+ root: z31.string().optional().describe("项目根目录,默认 cwd"),
16485
+ config_file: z31.string().optional().describe("配置文件名;默认 codeforge.json")
16199
16486
  },
16200
16487
  async execute(args) {
16201
16488
  return await runSafe("model_chain", async () => {
@@ -16216,11 +16503,11 @@ var codeforgeToolsServer = async (ctx) => {
16216
16503
  session_merge: tool({
16217
16504
  description: description26,
16218
16505
  args: {
16219
- action: z30.enum(["merge", "status", "discard", "diff"]).describe("操作类型:merge=合并到主仓(orchestrator 专用)/ status=查询状态 / discard=放弃 / diff=查看 worktree 改动"),
16220
- session_id: z30.string().optional().describe("目标 session id;不传则用当前 session"),
16221
- plan_id: z30.string().optional().describe("关联的 plan_id(reviewer 校验时用),格式 plan-YYYYMMDD-HHmmss-NNN"),
16222
- force: z30.boolean().optional().describe("action=merge 时跳过 review 直接 squash merge(写审计)"),
16223
- stat: z30.boolean().optional().describe("action=diff 时:true=只显示文件列表+统计,false=完整 diff(默认 false)")
16506
+ action: z31.enum(["merge", "status", "discard", "diff"]).describe("操作类型:merge=合并到主仓(orchestrator 专用)/ status=查询状态 / discard=放弃 / diff=查看 worktree 改动"),
16507
+ session_id: z31.string().optional().describe("目标 session id;不传则用当前 session"),
16508
+ plan_id: z31.string().optional().describe("关联的 plan_id(reviewer 校验时用),格式 plan-YYYYMMDD-HHmmss-NNN"),
16509
+ force: z31.boolean().optional().describe("action=merge 时跳过 review 直接 squash merge(写审计)"),
16510
+ stat: z31.boolean().optional().describe("action=diff 时:true=只显示文件列表+统计,false=完整 diff(默认 false)")
16224
16511
  },
16225
16512
  async execute(args, input) {
16226
16513
  return await runSafe("session_merge", async () => {
@@ -16256,9 +16543,9 @@ var codeforgeToolsServer = async (ctx) => {
16256
16543
  plan_write: tool({
16257
16544
  description: description27,
16258
16545
  args: {
16259
- title: z30.string().min(2).max(80).describe('方案简短标题(2-80 字),如 "实现 worktree session 隔离 Phase 1"'),
16260
- content: z30.string().min(10).describe("方案 markdown 全文,建议 ≥ 50 行"),
16261
- tags: z30.array(z30.string().min(1)).optional().describe('标签(可选),如 ["phase:1", "arch:worktree"]')
16546
+ title: z31.string().min(2).max(80).describe('方案简短标题(2-80 字),如 "实现 worktree session 隔离 Phase 1"'),
16547
+ content: z31.string().min(10).describe("方案 markdown 全文,建议 ≥ 50 行"),
16548
+ tags: z31.array(z31.string().min(1)).optional().describe('标签(可选),如 ["phase:1", "arch:worktree"]')
16262
16549
  },
16263
16550
  async execute(args) {
16264
16551
  return await runSafeTracked("plan_write", async () => {
@@ -16277,8 +16564,8 @@ var codeforgeToolsServer = async (ctx) => {
16277
16564
  plan_read: tool({
16278
16565
  description: description28,
16279
16566
  args: {
16280
- plan_id: z30.string().optional().describe("方案 ID(推荐),格式 plan-YYYYMMDD-HHmmss-NNN"),
16281
- path: z30.string().optional().describe("方案绝对路径(兜底用,没有 plan_id 元数据时退而求其次)")
16567
+ plan_id: z31.string().optional().describe("方案 ID(推荐),格式 plan-YYYYMMDD-HHmmss-NNN"),
16568
+ path: z31.string().optional().describe("方案绝对路径(兜底用,没有 plan_id 元数据时退而求其次)")
16282
16569
  },
16283
16570
  async execute(args) {
16284
16571
  return await runSafe("plan_read", async () => {
@@ -16297,6 +16584,25 @@ var codeforgeToolsServer = async (ctx) => {
16297
16584
  return wrap(result, meta);
16298
16585
  });
16299
16586
  }
16587
+ }),
16588
+ adr_init: tool({
16589
+ description: description29,
16590
+ args: {
16591
+ cwd: z31.string().optional().describe("目标项目根目录,默认 process.cwd();通常无需传"),
16592
+ force: z31.boolean().optional().describe("已存在文件覆盖;覆盖前自动 .bak.<ts> 备份"),
16593
+ dryRun: z31.boolean().optional().describe("只输出将要执行的写入计划,不实际写盘"),
16594
+ writePrepare: z31.boolean().optional().describe("(npm 项目)自动合并 git config core.hooksPath 到 package.json scripts.prepare;写前自动 backup"),
16595
+ installPrePush: z31.boolean().optional().describe("是否同时生成 .githooks/pre-push hook,默认 true")
16596
+ },
16597
+ async execute(args) {
16598
+ return await runSafe("adr_init", async () => {
16599
+ const v = projectValidate("adr_init", ArgsSchema29, args);
16600
+ if (!v.ok)
16601
+ return wrap(JSON.parse(v.output));
16602
+ const result = await execute29(v.data);
16603
+ return wrap(result, { title: "adr_init" });
16604
+ });
16605
+ }
16300
16606
  })
16301
16607
  }
16302
16608
  };
@@ -16305,10 +16611,10 @@ var handler8 = codeforgeToolsServer;
16305
16611
 
16306
16612
  // plugins/discover-spec-suggest.ts
16307
16613
  import { readFileSync as readFileSync3, readdirSync, statSync as statSync3 } from "node:fs";
16308
- import { join as join14 } from "node:path";
16614
+ import { join as join15 } from "node:path";
16309
16615
 
16310
16616
  // lib/handoff-schema.ts
16311
- import { z as z31 } from "zod";
16617
+ import { z as z32 } from "zod";
16312
16618
 
16313
16619
  // node_modules/yaml/dist/index.js
16314
16620
  var composer = require_composer();
@@ -16361,92 +16667,92 @@ var MAX_HANDOFF_SIZE = 100 * 1024;
16361
16667
  var SLUG_REGEX = /^[a-z0-9][a-z0-9-]{0,49}$/;
16362
16668
  var SCORE_DIMENSIONS = ["functional", "ux", "technical", "constraints", "edge_cases"];
16363
16669
  var COMBO_VALUES = ["A", "B", "C", "D"];
16364
- var NeedSchema = z31.object({
16365
- id: z31.string().min(1),
16366
- type: z31.enum(["must", "should", "nice-to-have"]),
16367
- statement: z31.string().min(1),
16368
- rationale: z31.string().optional(),
16369
- acceptance: z31.array(z31.string()).optional()
16670
+ var NeedSchema = z32.object({
16671
+ id: z32.string().min(1),
16672
+ type: z32.enum(["must", "should", "nice-to-have"]),
16673
+ statement: z32.string().min(1),
16674
+ rationale: z32.string().optional(),
16675
+ acceptance: z32.array(z32.string()).optional()
16370
16676
  });
16371
- var BoundarySchema = z31.object({
16372
- excluded: z31.string().min(1),
16373
- reason: z31.string().min(1)
16677
+ var BoundarySchema = z32.object({
16678
+ excluded: z32.string().min(1),
16679
+ reason: z32.string().min(1)
16374
16680
  });
16375
- var AssumptionSchema = z31.object({
16376
- statement: z31.string().min(1),
16377
- confidence: z31.enum(["verified", "speculation", "high-risk-unknown"]),
16378
- needs_validation_by: z31.enum(["plan", "coder", "runtime"]).optional()
16681
+ var AssumptionSchema = z32.object({
16682
+ statement: z32.string().min(1),
16683
+ confidence: z32.enum(["verified", "speculation", "high-risk-unknown"]),
16684
+ needs_validation_by: z32.enum(["plan", "coder", "runtime"]).optional()
16379
16685
  });
16380
- var OpenIssueSchema = z31.union([
16381
- z31.string().min(1),
16382
- z31.object({
16383
- id: z31.string().optional(),
16384
- question: z31.string().min(1),
16385
- blocking: z31.boolean().optional()
16686
+ var OpenIssueSchema = z32.union([
16687
+ z32.string().min(1),
16688
+ z32.object({
16689
+ id: z32.string().optional(),
16690
+ question: z32.string().min(1),
16691
+ blocking: z32.boolean().optional()
16386
16692
  })
16387
16693
  ]);
16388
- var RedFlagsSchema = z31.object({
16389
- raised: z31.boolean(),
16390
- combos: z31.array(z31.enum(COMBO_VALUES)).default([]),
16391
- reasons: z31.array(z31.string()).default([]),
16392
- user_persisted_rounds: z31.number().int().nonnegative().default(0),
16393
- downstream_advisory: z31.string().nullable().optional()
16694
+ var RedFlagsSchema = z32.object({
16695
+ raised: z32.boolean(),
16696
+ combos: z32.array(z32.enum(COMBO_VALUES)).default([]),
16697
+ reasons: z32.array(z32.string()).default([]),
16698
+ user_persisted_rounds: z32.number().int().nonnegative().default(0),
16699
+ downstream_advisory: z32.string().nullable().optional()
16394
16700
  }).superRefine((rf, ctx) => {
16395
16701
  if (rf.raised) {
16396
16702
  if (rf.combos.length === 0) {
16397
16703
  ctx.addIssue({
16398
- code: z31.ZodIssueCode.custom,
16704
+ code: z32.ZodIssueCode.custom,
16399
16705
  message: "red_flags.raised=true 时 combos 必须 >=1",
16400
16706
  path: ["combos"]
16401
16707
  });
16402
16708
  }
16403
16709
  if (rf.reasons.length === 0) {
16404
16710
  ctx.addIssue({
16405
- code: z31.ZodIssueCode.custom,
16711
+ code: z32.ZodIssueCode.custom,
16406
16712
  message: "red_flags.raised=true 时 reasons 必须 >=1",
16407
16713
  path: ["reasons"]
16408
16714
  });
16409
16715
  }
16410
16716
  }
16411
16717
  });
16412
- var ScoresSchema = z31.object(Object.fromEntries(SCORE_DIMENSIONS.map((d) => [d, z31.number().min(0).max(1)])));
16413
- var PreCodingBlockerSchema = z31.object({
16414
- id: z31.string().regex(/^PRE-\d+$/, "id 必须形如 PRE-1 / PRE-2"),
16415
- blocker: z31.string().min(1),
16416
- source: z31.enum(["assumption", "red_flag", "open_issue"]),
16417
- must_resolve_by: z31.enum(["user", "codeforge"])
16718
+ var ScoresSchema = z32.object(Object.fromEntries(SCORE_DIMENSIONS.map((d) => [d, z32.number().min(0).max(1)])));
16719
+ var PreCodingBlockerSchema = z32.object({
16720
+ id: z32.string().regex(/^PRE-\d+$/, "id 必须形如 PRE-1 / PRE-2"),
16721
+ blocker: z32.string().min(1),
16722
+ source: z32.enum(["assumption", "red_flag", "open_issue"]),
16723
+ must_resolve_by: z32.enum(["user", "codeforge"])
16418
16724
  });
16419
- var KhRefSchema = z31.object({
16420
- kh_id: z31.string(),
16421
- title: z31.string(),
16422
- relevance: z31.enum(["positive", "negative", "neutral"]).optional()
16725
+ var KhRefSchema = z32.object({
16726
+ kh_id: z32.string(),
16727
+ title: z32.string(),
16728
+ relevance: z32.enum(["positive", "negative", "neutral"]).optional()
16423
16729
  });
16424
- var HandoffSchema = z31.object({
16425
- schema_version: z31.string().regex(/^\d+\.\d+\.\d+$/, "schema_version 必须形如 1.1.0 / 1.2.0"),
16426
- slug: z31.string().regex(SLUG_REGEX, "slug 仅允许 [a-z0-9-],长度 1-50,首字符为字母数字"),
16427
- title: z31.string().min(1).max(200),
16428
- created_at: z31.string().optional(),
16429
- discover_session_id: z31.string().optional(),
16430
- weighted_score: z31.number().min(0).max(1),
16730
+ var HandoffSchema = z32.object({
16731
+ schema_version: z32.string().regex(/^\d+\.\d+\.\d+$/, "schema_version 必须形如 1.1.0 / 1.2.0"),
16732
+ slug: z32.string().regex(SLUG_REGEX, "slug 仅允许 [a-z0-9-],长度 1-50,首字符为字母数字"),
16733
+ title: z32.string().min(1).max(200),
16734
+ created_at: z32.string().optional(),
16735
+ discover_session_id: z32.string().optional(),
16736
+ weighted_score: z32.number().min(0).max(1),
16431
16737
  scores: ScoresSchema,
16432
- needs: z31.array(NeedSchema).min(1, "needs 必须 >=1"),
16433
- boundaries: z31.array(BoundarySchema).min(1, "boundaries 必须 >=1"),
16434
- assumptions: z31.array(AssumptionSchema),
16435
- open_issues: z31.array(OpenIssueSchema).default([]),
16436
- rejected_alternatives: z31.array(z31.object({ option: z31.string(), reason: z31.string() })).default([]),
16437
- acceptance_criteria: z31.array(z31.object({
16438
- id: z31.string().regex(/^AC-/, "AC id 必须以 AC- 开头"),
16439
- description: z31.string().min(1),
16440
- measurable: z31.boolean().optional(),
16441
- metric: z31.string().optional()
16738
+ needs: z32.array(NeedSchema).min(1, "needs 必须 >=1"),
16739
+ boundaries: z32.array(BoundarySchema).min(1, "boundaries 必须 >=1"),
16740
+ assumptions: z32.array(AssumptionSchema),
16741
+ open_issues: z32.array(OpenIssueSchema).default([]),
16742
+ rejected_alternatives: z32.array(z32.object({ option: z32.string(), reason: z32.string() })).default([]),
16743
+ acceptance_criteria: z32.array(z32.object({
16744
+ id: z32.string().regex(/^AC-/, "AC id 必须以 AC- 开头"),
16745
+ description: z32.string().min(1),
16746
+ measurable: z32.boolean().optional(),
16747
+ metric: z32.string().optional()
16442
16748
  })).default([]),
16443
16749
  red_flags: RedFlagsSchema,
16444
- pre_coding_blockers: z31.array(PreCodingBlockerSchema).default([]),
16445
- kh_references: z31.array(KhRefSchema).default([]),
16446
- adr_refs: z31.array(z31.string()).default([]),
16447
- related_artifacts: z31.object({
16448
- prd: z31.string().optional(),
16449
- transcript: z31.string().optional()
16750
+ pre_coding_blockers: z32.array(PreCodingBlockerSchema).default([]),
16751
+ kh_references: z32.array(KhRefSchema).default([]),
16752
+ adr_refs: z32.array(z32.string()).default([]),
16753
+ related_artifacts: z32.object({
16754
+ prd: z32.string().optional(),
16755
+ transcript: z32.string().optional()
16450
16756
  }).optional()
16451
16757
  });
16452
16758
  function validateHandoff(rawYaml, fileSize) {
@@ -16472,9 +16778,9 @@ function validateHandoff(rawYaml, fileSize) {
16472
16778
  const result = HandoffSchema.safeParse(parsed);
16473
16779
  if (!result.success) {
16474
16780
  const first = result.error.issues[0];
16475
- const path18 = first?.path?.join(".") ?? "(root)";
16781
+ const path19 = first?.path?.join(".") ?? "(root)";
16476
16782
  const msg = first?.message ?? "unknown";
16477
- return { ok: false, reason: `schema 校验失败:${path18}: ${msg}` };
16783
+ return { ok: false, reason: `schema 校验失败:${path19}: ${msg}` };
16478
16784
  }
16479
16785
  return { ok: true, data: result.data, schemaVersion: result.data.schema_version };
16480
16786
  }
@@ -16491,7 +16797,7 @@ var SESSION_TTL_MS2 = 24 * 60 * 60 * 1000;
16491
16797
  var MATCH_THRESHOLD = 0.15;
16492
16798
  var MAX_CANDIDATES = 3;
16493
16799
  var NUDGE_MAX_LEN = 1500;
16494
- var SPECS_REL_DIR = join14(".codeforge", "specs");
16800
+ var SPECS_REL_DIR = join15(".codeforge", "specs");
16495
16801
  var sessionMap = new Map;
16496
16802
  function pruneIfOversize2() {
16497
16803
  while (sessionMap.size > SESSION_CAP2) {
@@ -16598,7 +16904,7 @@ function loadSpecs(rootDir, opts = {}) {
16598
16904
  const dirExists = opts.dirExists ?? defaultDirExists;
16599
16905
  const statReader = opts.statReader ?? defaultStatReader;
16600
16906
  const log6 = makePluginLogger(PLUGIN_NAME9);
16601
- const specsRoot = join14(rootDir, SPECS_REL_DIR);
16907
+ const specsRoot = join15(rootDir, SPECS_REL_DIR);
16602
16908
  const records = [];
16603
16909
  if (!dirExists(specsRoot)) {
16604
16910
  log6.info(`specs 目录不存在,plugin 将 no-op`, { specsRoot });
@@ -16619,7 +16925,7 @@ function loadSpecs(rootDir, opts = {}) {
16619
16925
  log6.info(`跳过非合法 slug 命名的条目`, { entry });
16620
16926
  continue;
16621
16927
  }
16622
- const specDir = join14(specsRoot, entry);
16928
+ const specDir = join15(specsRoot, entry);
16623
16929
  let dirStat;
16624
16930
  try {
16625
16931
  dirStat = statReader(specDir);
@@ -16632,7 +16938,7 @@ function loadSpecs(rootDir, opts = {}) {
16632
16938
  }
16633
16939
  if (!dirStat.isDirectory)
16634
16940
  continue;
16635
- const handoffPath = join14(specDir, "handoff.yaml");
16941
+ const handoffPath = join15(specDir, "handoff.yaml");
16636
16942
  let fileStat;
16637
16943
  try {
16638
16944
  fileStat = statReader(handoffPath);
@@ -17639,13 +17945,13 @@ var handler11 = khReminderServer;
17639
17945
 
17640
17946
  // lib/memories.ts
17641
17947
  import { promises as fs15 } from "node:fs";
17642
- import * as path18 from "node:path";
17948
+ import * as path19 from "node:path";
17643
17949
  import * as os5 from "node:os";
17644
17950
  function resolveConfig(c) {
17645
17951
  return {
17646
17952
  projectRoot: c.projectRoot,
17647
17953
  homeDir: c.homeDir ?? os5.homedir(),
17648
- projectName: c.projectName ?? path18.basename(c.projectRoot),
17954
+ projectName: c.projectName ?? path19.basename(c.projectRoot),
17649
17955
  kh: c.kh,
17650
17956
  now: c.now ?? Date.now,
17651
17957
  log: c.log ?? (() => {}),
@@ -17654,9 +17960,9 @@ function resolveConfig(c) {
17654
17960
  }
17655
17961
  function fileFor(scope, cfg) {
17656
17962
  if (scope === "project") {
17657
- return path18.join(cfg.projectRoot, ".codeforge", "memories.json");
17963
+ return path19.join(cfg.projectRoot, ".codeforge", "memories.json");
17658
17964
  }
17659
- return path18.join(cfg.homeDir, ".codeforge", "memories.json");
17965
+ return path19.join(cfg.homeDir, ".codeforge", "memories.json");
17660
17966
  }
17661
17967
  async function readBank(p) {
17662
17968
  try {
@@ -17670,7 +17976,7 @@ async function readBank(p) {
17670
17976
  }
17671
17977
  }
17672
17978
  async function writeBank(p, items) {
17673
- await fs15.mkdir(path18.dirname(p), { recursive: true });
17979
+ await fs15.mkdir(path19.dirname(p), { recursive: true });
17674
17980
  const tmp = `${p}.tmp`;
17675
17981
  await fs15.writeFile(tmp, JSON.stringify(items, null, 2), "utf8");
17676
17982
  await fs15.rename(tmp, p);
@@ -18217,7 +18523,7 @@ var handler13 = modelFallbackServer;
18217
18523
 
18218
18524
  // plugins/subtask-heartbeat.ts
18219
18525
  import { promises as fsPromises } from "node:fs";
18220
- import * as path19 from "node:path";
18526
+ import * as path20 from "node:path";
18221
18527
  init_runtime_paths();
18222
18528
  init_global_config();
18223
18529
  var recordSessionParent2 = recordSessionParent;
@@ -18305,11 +18611,11 @@ function extractTaskArgs(args) {
18305
18611
  const a = args;
18306
18612
  const rawDesc = typeof a["description"] === "string" ? a["description"] : null;
18307
18613
  const rawPrompt = typeof a["prompt"] === "string" ? a["prompt"] : null;
18308
- const description29 = rawDesc ?? (rawPrompt ? rawPrompt.slice(0, 60) : null);
18614
+ const description30 = rawDesc ?? (rawPrompt ? rawPrompt.slice(0, 60) : null);
18309
18615
  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;
18310
- if (!description29 && !subagentType)
18616
+ if (!description30 && !subagentType)
18311
18617
  return null;
18312
- return { description: description29, subagentType };
18618
+ return { description: description30, subagentType };
18313
18619
  }
18314
18620
  function enqueuePendingTask(parentID, entry, now = Date.now()) {
18315
18621
  const ts = entry.ts ?? now;
@@ -18519,7 +18825,7 @@ function buildAfterLogLine(toolName, ok, durationMs, now = Date.now()) {
18519
18825
  }
18520
18826
  async function appendSubagentLog(filePath, line, log8) {
18521
18827
  try {
18522
- await fsPromises.mkdir(path19.dirname(filePath), { recursive: true });
18828
+ await fsPromises.mkdir(path20.dirname(filePath), { recursive: true });
18523
18829
  await fsPromises.appendFile(filePath, line + `
18524
18830
  `, "utf8");
18525
18831
  } catch (err) {
@@ -18853,7 +19159,7 @@ var handler15 = parallelStatusServer;
18853
19159
 
18854
19160
  // plugins/parallel-tool-nudge.ts
18855
19161
  import { readFileSync as readFileSync4, readdirSync as readdirSync2, statSync as statSync4 } from "node:fs";
18856
- import { join as join16 } from "node:path";
19162
+ import { join as join17 } from "node:path";
18857
19163
  import { homedir as homedir6 } from "node:os";
18858
19164
  var PLUGIN_NAME16 = "parallel-tool-nudge";
18859
19165
  logLifecycle(PLUGIN_NAME16, "import", {});
@@ -18909,10 +19215,10 @@ function loadAgentToolsMap(rootDir, opts = {}) {
18909
19215
  const reader = opts.reader ?? defaultReader2;
18910
19216
  const dirReader = opts.dirReader ?? defaultDirReader2;
18911
19217
  const dirExists = opts.dirExists ?? defaultDirExists2;
18912
- const homeAgentsDir = opts.homeAgentsDir ?? join16(homedir6(), ".config", "opencode", "agents");
19218
+ const homeAgentsDir = opts.homeAgentsDir ?? join17(homedir6(), ".config", "opencode", "agents");
18913
19219
  const candidateDirs = [
18914
- join16(rootDir, ".codeforge", "agents"),
18915
- join16(rootDir, "agents"),
19220
+ join17(rootDir, ".codeforge", "agents"),
19221
+ join17(rootDir, "agents"),
18916
19222
  homeAgentsDir
18917
19223
  ];
18918
19224
  const result = new Map;
@@ -18935,20 +19241,20 @@ function loadAgentToolsMap(rootDir, opts = {}) {
18935
19241
  for (const entry of entries) {
18936
19242
  if (!entry.endsWith(".md"))
18937
19243
  continue;
18938
- const path20 = join16(dir, entry);
19244
+ const path21 = join17(dir, entry);
18939
19245
  let content;
18940
19246
  try {
18941
- content = reader(path20);
19247
+ content = reader(path21);
18942
19248
  } catch (err) {
18943
19249
  log9.warn(`agent.md 读取失败(已跳过)`, {
18944
- path: path20,
19250
+ path: path21,
18945
19251
  error: err instanceof Error ? err.message : String(err)
18946
19252
  });
18947
19253
  continue;
18948
19254
  }
18949
19255
  const parsed = parseAgentFrontmatter(content);
18950
19256
  if (!parsed) {
18951
- log9.warn(`agent frontmatter 解析失败(已跳过)`, { path: path20 });
19257
+ log9.warn(`agent frontmatter 解析失败(已跳过)`, { path: path21 });
18952
19258
  continue;
18953
19259
  }
18954
19260
  if (result.has(parsed.name))
@@ -19141,7 +19447,7 @@ var handler17 = async (_ctx3) => {
19141
19447
  // lib/event-stream.ts
19142
19448
  import { promises as fs16 } from "node:fs";
19143
19449
  init_runtime_paths();
19144
- import * as path20 from "node:path";
19450
+ import * as path21 from "node:path";
19145
19451
  async function loadSession(id, opts = {}) {
19146
19452
  const file = resolveSessionFile(id, opts);
19147
19453
  const raw = await fs16.readFile(file, "utf8");
@@ -19161,7 +19467,7 @@ async function listSessions(opts = {}) {
19161
19467
  for (const e of entries) {
19162
19468
  if (!e.isFile() || !e.name.endsWith(".jsonl"))
19163
19469
  continue;
19164
- const file = path20.join(dir, e.name);
19470
+ const file = path21.join(dir, e.name);
19165
19471
  const id = e.name.replace(/\.jsonl$/, "");
19166
19472
  try {
19167
19473
  const stat = await fs16.stat(file);
@@ -19188,11 +19494,11 @@ async function listSessions(opts = {}) {
19188
19494
  return out;
19189
19495
  }
19190
19496
  function resolveDir(opts = {}) {
19191
- const root = path20.resolve(opts.root ?? process.cwd());
19192
- return opts.sessions_dir ? path20.resolve(root, opts.sessions_dir) : path20.join(runtimeDir(root), "sessions");
19497
+ const root = path21.resolve(opts.root ?? process.cwd());
19498
+ return opts.sessions_dir ? path21.resolve(root, opts.sessions_dir) : path21.join(runtimeDir(root), "sessions");
19193
19499
  }
19194
19500
  function resolveSessionFile(id, opts = {}) {
19195
- return path20.join(resolveDir(opts), `${id}.jsonl`);
19501
+ return path21.join(resolveDir(opts), `${id}.jsonl`);
19196
19502
  }
19197
19503
  function parseJsonl(id, raw) {
19198
19504
  const events = [];
@@ -19457,10 +19763,10 @@ function isRecoveryWorthShowing(plan) {
19457
19763
  // lib/block-pending.ts
19458
19764
  init_runtime_paths();
19459
19765
  import { promises as fs17 } from "node:fs";
19460
- import * as path21 from "node:path";
19766
+ import * as path22 from "node:path";
19461
19767
  function blockPendingFilePath(absRoot) {
19462
19768
  const rd = runtimeDir(absRoot, { ensure: false });
19463
- return path21.join(rd, "sessions", "autonomous-blocks.ndjson");
19769
+ return path22.join(rd, "sessions", "autonomous-blocks.ndjson");
19464
19770
  }
19465
19771
  function consumeLockPath(absRoot) {
19466
19772
  return blockPendingFilePath(absRoot) + ".consume.lock";
@@ -19525,7 +19831,7 @@ async function markBlocksConsumed(absRoot, entries) {
19525
19831
  if (entries.length === 0)
19526
19832
  return;
19527
19833
  const file = blockPendingFilePath(absRoot);
19528
- await fs17.mkdir(path21.dirname(file), { recursive: true });
19834
+ await fs17.mkdir(path22.dirname(file), { recursive: true });
19529
19835
  const now = new Date().toISOString();
19530
19836
  const lines = entries.map((e) => ({
19531
19837
  type: "consume",
@@ -19675,7 +19981,7 @@ var handler18 = sessionRecoveryServer;
19675
19981
 
19676
19982
  // plugins/subtasks.ts
19677
19983
  import { promises as fs18 } from "node:fs";
19678
- import * as path22 from "node:path";
19984
+ import * as path23 from "node:path";
19679
19985
 
19680
19986
  // lib/parallel-merge.ts
19681
19987
  init_worktree_ops();
@@ -20290,7 +20596,7 @@ function buildSystemPrompt(maxSubtasks) {
20290
20596
  ].join(`
20291
20597
  `);
20292
20598
  }
20293
- async function decomposeTask(description29, opts) {
20599
+ async function decomposeTask(description30, opts) {
20294
20600
  const log11 = opts.log ?? (() => {});
20295
20601
  if (opts.mockResponse) {
20296
20602
  return validateAndFinalize(opts.mockResponse, undefined, log11, opts.maxSubtasks ?? DEFAULT_MAX_SUBTASKS);
@@ -20300,7 +20606,7 @@ async function decomposeTask(description29, opts) {
20300
20606
  let childSessionId;
20301
20607
  try {
20302
20608
  const created = await opts.client.session.create({
20303
- body: { title: `decompose:${clip6(description29, 60)}` },
20609
+ body: { title: `decompose:${clip6(description30, 60)}` },
20304
20610
  query: opts.directory ? { directory: opts.directory } : undefined
20305
20611
  });
20306
20612
  if (created.error || !created.data?.id) {
@@ -20317,7 +20623,7 @@ async function decomposeTask(description29, opts) {
20317
20623
  path: { id: childSessionId },
20318
20624
  body: {
20319
20625
  system: systemText,
20320
- parts: [{ type: "text", text: description29 }]
20626
+ parts: [{ type: "text", text: description30 }]
20321
20627
  },
20322
20628
  query: opts.directory ? { directory: opts.directory } : undefined
20323
20629
  }));
@@ -20493,14 +20799,14 @@ function describe8(err) {
20493
20799
  }
20494
20800
  }
20495
20801
  function sleep2(ms) {
20496
- return new Promise((resolve16) => setTimeout(resolve16, ms));
20802
+ return new Promise((resolve17) => setTimeout(resolve17, ms));
20497
20803
  }
20498
20804
 
20499
20805
  // plugins/subtasks.ts
20500
20806
  init_runtime_paths();
20501
20807
  var PLUGIN_NAME19 = "subtasks";
20502
20808
  function getLogFile(root = process.cwd()) {
20503
- return path22.join(runtimeDir(root), "logs", "subtasks.log");
20809
+ return path23.join(runtimeDir(root), "logs", "subtasks.log");
20504
20810
  }
20505
20811
  var VERB_RE = /^([a-zA-Z]{3,12})/;
20506
20812
  var CN_VERBS = [
@@ -20805,7 +21111,7 @@ async function writeLog(level, msg, data) {
20805
21111
  `;
20806
21112
  try {
20807
21113
  const logFile = getLogFile();
20808
- await fs18.mkdir(path22.dirname(logFile), { recursive: true });
21114
+ await fs18.mkdir(path23.dirname(logFile), { recursive: true });
20809
21115
  await fs18.appendFile(logFile, line, "utf8");
20810
21116
  } catch {}
20811
21117
  }
@@ -20822,8 +21128,8 @@ var subtasksServer = async (ctx) => {
20822
21128
  try {
20823
21129
  if (input?.command !== "parallel")
20824
21130
  return;
20825
- const description29 = (input.arguments ?? "").trim();
20826
- if (!description29) {
21131
+ const description30 = (input.arguments ?? "").trim();
21132
+ if (!description30) {
20827
21133
  return;
20828
21134
  }
20829
21135
  let autoMerge = false;
@@ -20862,7 +21168,7 @@ var subtasksServer = async (ctx) => {
20862
21168
  } : undefined;
20863
21169
  const replyLines = [];
20864
21170
  const messageCtx = {
20865
- content: `/parallel ${description29}`,
21171
+ content: `/parallel ${description30}`,
20866
21172
  reply: (s) => {
20867
21173
  replyLines.push(s);
20868
21174
  return Promise.resolve();
@@ -21323,7 +21629,7 @@ var handler21 = tokenManagerServer;
21323
21629
 
21324
21630
  // plugins/tool-policy.ts
21325
21631
  import { promises as fs19 } from "node:fs";
21326
- import * as path24 from "node:path";
21632
+ import * as path25 from "node:path";
21327
21633
 
21328
21634
  // lib/tool-risk.ts
21329
21635
  var RISK_PATTERNS = [
@@ -21491,7 +21797,7 @@ function buildHaystackFor(args, matchOn) {
21491
21797
  }
21492
21798
 
21493
21799
  // lib/file-regex-acl.ts
21494
- import * as path23 from "node:path";
21800
+ import * as path24 from "node:path";
21495
21801
  function compileRule(r) {
21496
21802
  if (r instanceof RegExp)
21497
21803
  return r;
@@ -21557,7 +21863,7 @@ function normalizePath2(p) {
21557
21863
  let s = p.replace(/\\/g, "/");
21558
21864
  if (s.startsWith("./"))
21559
21865
  s = s.slice(2);
21560
- s = path23.posix.normalize(s);
21866
+ s = path24.posix.normalize(s);
21561
21867
  return s;
21562
21868
  }
21563
21869
  function checkFileAccess(acl, file, op) {
@@ -21660,9 +21966,9 @@ function decideToolCall(ctx, cfg = {}, currentAgent) {
21660
21966
  const action = risks.length > 0 || worstAcl === "deny" ? "deny" : "allow";
21661
21967
  return { action, reasons, risks, acl: aclResults };
21662
21968
  }
21663
- var POLICY_PATH = path24.join(".codeforge", "policy.json");
21969
+ var POLICY_PATH = path25.join(".codeforge", "policy.json");
21664
21970
  async function loadPolicy(root = process.cwd()) {
21665
- const file = path24.join(root, POLICY_PATH);
21971
+ const file = path25.join(root, POLICY_PATH);
21666
21972
  try {
21667
21973
  const raw = await fs19.readFile(file, "utf8");
21668
21974
  const data = JSON.parse(raw);
@@ -21760,15 +22066,15 @@ var toolPolicyServer = async (ctx) => {
21760
22066
  var handler22 = toolPolicyServer;
21761
22067
 
21762
22068
  // plugins/update-checker.ts
21763
- import { existsSync as existsSync5 } from "node:fs";
22069
+ import { existsSync as existsSync6 } from "node:fs";
21764
22070
  import { homedir as homedir8 } from "node:os";
21765
- import { join as join22 } from "node:path";
22071
+ import { join as join23 } from "node:path";
21766
22072
 
21767
22073
  // lib/update-checker-impl.ts
21768
22074
  import { createHash as createHash5 } from "node:crypto";
21769
22075
  import {
21770
22076
  copyFileSync,
21771
- existsSync as existsSync4,
22077
+ existsSync as existsSync5,
21772
22078
  mkdirSync as mkdirSync3,
21773
22079
  mkdtempSync,
21774
22080
  readFileSync as readFileSync5,
@@ -21779,15 +22085,15 @@ import {
21779
22085
  writeFileSync as writeFileSync2
21780
22086
  } from "node:fs";
21781
22087
  import { homedir as homedir7, tmpdir } from "node:os";
21782
- import { dirname as dirname13, join as join21 } from "node:path";
21783
- import { fileURLToPath } from "node:url";
22088
+ import { dirname as dirname14, join as join22 } from "node:path";
22089
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
21784
22090
  import * as https from "node:https";
21785
22091
  import * as zlib from "node:zlib";
21786
22092
 
21787
22093
  // lib/version-injected.ts
21788
22094
  function getInjectedVersion() {
21789
22095
  try {
21790
- const v = "0.5.18";
22096
+ const v = "0.5.20";
21791
22097
  if (typeof v === "string" && /^\d+\.\d+\.\d+/.test(v)) {
21792
22098
  return v;
21793
22099
  }
@@ -21875,23 +22181,23 @@ function readLocalVersion() {
21875
22181
  if (injected)
21876
22182
  return injected;
21877
22183
  try {
21878
- const here = fileURLToPath(import.meta.url);
21879
- const root = dirname13(dirname13(here));
21880
- const pkg = JSON.parse(readFileSync5(join21(root, "package.json"), "utf8"));
22184
+ const here = fileURLToPath2(import.meta.url);
22185
+ const root = dirname14(dirname14(here));
22186
+ const pkg = JSON.parse(readFileSync5(join22(root, "package.json"), "utf8"));
21881
22187
  return typeof pkg.version === "string" ? pkg.version : "0.0.0";
21882
22188
  } catch {
21883
22189
  return "0.0.0";
21884
22190
  }
21885
22191
  }
21886
22192
  function defaultCacheDir() {
21887
- return process.env["CODEFORGE_CACHE_DIR"] ?? join21(homedir7(), ".cache", "codeforge");
22193
+ return process.env["CODEFORGE_CACHE_DIR"] ?? join22(homedir7(), ".cache", "codeforge");
21888
22194
  }
21889
22195
  function defaultCacheFile() {
21890
- return join21(defaultCacheDir(), "update-check.json");
22196
+ return join22(defaultCacheDir(), "update-check.json");
21891
22197
  }
21892
22198
  function readCache(file) {
21893
22199
  try {
21894
- if (!existsSync4(file))
22200
+ if (!existsSync5(file))
21895
22201
  return null;
21896
22202
  const raw = readFileSync5(file, "utf8");
21897
22203
  const obj = JSON.parse(raw);
@@ -21905,7 +22211,7 @@ function readCache(file) {
21905
22211
  }
21906
22212
  function writeCache(file, entry) {
21907
22213
  try {
21908
- mkdirSync3(dirname13(file), { recursive: true });
22214
+ mkdirSync3(dirname14(file), { recursive: true });
21909
22215
  writeFileSync2(file, JSON.stringify(entry, null, 2), "utf8");
21910
22216
  } catch {}
21911
22217
  }
@@ -21923,9 +22229,9 @@ function fetchLatestTagFromGitHub(repo) {
21923
22229
  }
21924
22230
  });
21925
22231
  }
21926
- function getJsonWithRedirect(url, hopsLeft) {
21927
- return new Promise((resolve16, reject) => {
21928
- const u = new URL(url);
22232
+ function getJsonWithRedirect(url2, hopsLeft) {
22233
+ return new Promise((resolve17, reject) => {
22234
+ const u = new URL(url2);
21929
22235
  const headers = {
21930
22236
  "User-Agent": "codeforge-update-checker",
21931
22237
  Accept: "application/vnd.github+json"
@@ -21947,13 +22253,13 @@ function getJsonWithRedirect(url, hopsLeft) {
21947
22253
  reject(new Error("too_many_redirects"));
21948
22254
  return;
21949
22255
  }
21950
- const next = new URL(res.headers.location, url).toString();
21951
- getJsonWithRedirect(next, hopsLeft - 1).then(resolve16, reject);
22256
+ const next = new URL(res.headers.location, url2).toString();
22257
+ getJsonWithRedirect(next, hopsLeft - 1).then(resolve17, reject);
21952
22258
  return;
21953
22259
  }
21954
22260
  if (status === 404) {
21955
22261
  res.resume();
21956
- resolve16(null);
22262
+ resolve17(null);
21957
22263
  return;
21958
22264
  }
21959
22265
  if (status >= 400) {
@@ -21964,7 +22270,7 @@ function getJsonWithRedirect(url, hopsLeft) {
21964
22270
  let body = "";
21965
22271
  res.setEncoding("utf8");
21966
22272
  res.on("data", (chunk) => body += chunk);
21967
- res.on("end", () => resolve16(body));
22273
+ res.on("end", () => resolve17(body));
21968
22274
  });
21969
22275
  req.on("timeout", () => {
21970
22276
  req.destroy();
@@ -21979,9 +22285,9 @@ async function fetchLatestFromNpm(opts) {
21979
22285
  const channel = opts.channel ?? "latest";
21980
22286
  const timeoutMs = opts.timeoutMs ?? 5000;
21981
22287
  const encodedPkg = opts.pkg.startsWith("@") ? opts.pkg.replace("/", "%2F") : opts.pkg;
21982
- const url = `${registry}/${encodedPkg}/${encodeURIComponent(channel)}`;
22288
+ const url2 = `${registry}/${encodedPkg}/${encodeURIComponent(channel)}`;
21983
22289
  const fetcher = opts.httpFetcher ?? defaultHttpFetcher;
21984
- const body = await fetcher(url, timeoutMs);
22290
+ const body = await fetcher(url2, timeoutMs);
21985
22291
  if (body === null)
21986
22292
  return null;
21987
22293
  let parsed;
@@ -22003,9 +22309,9 @@ async function fetchLatestFromNpm(opts) {
22003
22309
  }
22004
22310
  return { version, tarballUrl, integrity };
22005
22311
  }
22006
- function defaultHttpFetcher(url, timeoutMs) {
22007
- return new Promise((resolve16, reject) => {
22008
- const u = new URL(url);
22312
+ function defaultHttpFetcher(url2, timeoutMs) {
22313
+ return new Promise((resolve17, reject) => {
22314
+ const u = new URL(url2);
22009
22315
  const headers = {
22010
22316
  "User-Agent": "codeforge-update-checker",
22011
22317
  Accept: "application/json"
@@ -22021,7 +22327,7 @@ function defaultHttpFetcher(url, timeoutMs) {
22021
22327
  const status = res.statusCode ?? 0;
22022
22328
  if (status === 404) {
22023
22329
  res.resume();
22024
- resolve16(null);
22330
+ resolve17(null);
22025
22331
  return;
22026
22332
  }
22027
22333
  if (status >= 400) {
@@ -22032,7 +22338,7 @@ function defaultHttpFetcher(url, timeoutMs) {
22032
22338
  let body = "";
22033
22339
  res.setEncoding("utf8");
22034
22340
  res.on("data", (chunk) => body += chunk);
22035
- res.on("end", () => resolve16(body));
22341
+ res.on("end", () => resolve17(body));
22036
22342
  });
22037
22343
  req.on("timeout", () => {
22038
22344
  req.destroy();
@@ -22043,15 +22349,15 @@ function defaultHttpFetcher(url, timeoutMs) {
22043
22349
  });
22044
22350
  }
22045
22351
  async function downloadAndExtractBundle(opts) {
22046
- const tmpRoot = opts.tmpDir ?? mkdtempSync(join21(tmpdir(), "codeforge-update-"));
22352
+ const tmpRoot = opts.tmpDir ?? mkdtempSync(join22(tmpdir(), "codeforge-update-"));
22047
22353
  mkdirSync3(tmpRoot, { recursive: true });
22048
22354
  const fetcher = opts.tarballFetcher ?? defaultBinaryFetcher;
22049
22355
  const tarballBuf = await fetcher(opts.tarballUrl);
22050
22356
  verifyIntegrity(tarballBuf, opts.expectedIntegrity);
22051
22357
  const tarBuf = zlib.gunzipSync(tarballBuf);
22052
22358
  extractTarToDir(tarBuf, tmpRoot);
22053
- const bundlePath = join21(tmpRoot, "package", "dist", "index.js");
22054
- if (!existsSync4(bundlePath)) {
22359
+ const bundlePath = join22(tmpRoot, "package", "dist", "index.js");
22360
+ if (!existsSync5(bundlePath)) {
22055
22361
  throw new Error(`bundle_not_found: ${bundlePath}`);
22056
22362
  }
22057
22363
  return { bundlePath, extractDir: tmpRoot };
@@ -22090,21 +22396,21 @@ function extractTarToDir(tarBuf, destRoot) {
22090
22396
  offset += 512;
22091
22397
  if (typeFlag === "0" || typeFlag === "" || typeFlag === "\x00") {
22092
22398
  const fileBuf = tarBuf.subarray(offset, offset + size);
22093
- const dest = join21(destRoot, fullName);
22094
- mkdirSync3(dirname13(dest), { recursive: true });
22399
+ const dest = join22(destRoot, fullName);
22400
+ mkdirSync3(dirname14(dest), { recursive: true });
22095
22401
  writeFileSync2(dest, fileBuf);
22096
22402
  } else if (typeFlag === "5") {
22097
- mkdirSync3(join21(destRoot, fullName), { recursive: true });
22403
+ mkdirSync3(join22(destRoot, fullName), { recursive: true });
22098
22404
  }
22099
22405
  offset += Math.ceil(size / 512) * 512;
22100
22406
  }
22101
22407
  }
22102
- function defaultBinaryFetcher(url) {
22103
- return downloadBinary(url, 3);
22408
+ function defaultBinaryFetcher(url2) {
22409
+ return downloadBinary(url2, 3);
22104
22410
  }
22105
- function downloadBinary(url, hopsLeft) {
22106
- return new Promise((resolve16, reject) => {
22107
- const u = new URL(url);
22411
+ function downloadBinary(url2, hopsLeft) {
22412
+ return new Promise((resolve17, reject) => {
22413
+ const u = new URL(url2);
22108
22414
  const req = https.request({
22109
22415
  host: u.hostname,
22110
22416
  port: u.port || undefined,
@@ -22120,8 +22426,8 @@ function downloadBinary(url, hopsLeft) {
22120
22426
  reject(new Error("too_many_redirects"));
22121
22427
  return;
22122
22428
  }
22123
- const next = new URL(res.headers.location, url).toString();
22124
- downloadBinary(next, hopsLeft - 1).then(resolve16, reject);
22429
+ const next = new URL(res.headers.location, url2).toString();
22430
+ downloadBinary(next, hopsLeft - 1).then(resolve17, reject);
22125
22431
  return;
22126
22432
  }
22127
22433
  if (status >= 400) {
@@ -22131,7 +22437,7 @@ function downloadBinary(url, hopsLeft) {
22131
22437
  }
22132
22438
  const chunks = [];
22133
22439
  res.on("data", (chunk) => chunks.push(chunk));
22134
- res.on("end", () => resolve16(Buffer.concat(chunks)));
22440
+ res.on("end", () => resolve17(Buffer.concat(chunks)));
22135
22441
  });
22136
22442
  req.on("timeout", () => {
22137
22443
  req.destroy();
@@ -22144,16 +22450,16 @@ function downloadBinary(url, hopsLeft) {
22144
22450
  function atomicReplaceBundle(opts) {
22145
22451
  const { source, target, oldVersion } = opts;
22146
22452
  const keep = opts.keepBackups ?? 3;
22147
- if (!existsSync4(source)) {
22453
+ if (!existsSync5(source)) {
22148
22454
  throw new Error(`atomic_source_missing: ${source}`);
22149
22455
  }
22150
- mkdirSync3(dirname13(target), { recursive: true });
22456
+ mkdirSync3(dirname14(target), { recursive: true });
22151
22457
  const newPath = `${target}.new`;
22152
22458
  const backupPath = `${target}.bak.${oldVersion}`;
22153
22459
  let strategy = "rename";
22154
22460
  try {
22155
22461
  copyFileSync(source, newPath);
22156
- if (existsSync4(target)) {
22462
+ if (existsSync5(target)) {
22157
22463
  try {
22158
22464
  renameSync(target, backupPath);
22159
22465
  } catch (e) {
@@ -22189,7 +22495,7 @@ function atomicReplaceBundle(opts) {
22189
22495
  return { backupPath, strategy };
22190
22496
  } catch (e) {
22191
22497
  try {
22192
- if (existsSync4(newPath))
22498
+ if (existsSync5(newPath))
22193
22499
  unlinkSync(newPath);
22194
22500
  } catch {}
22195
22501
  throw e;
@@ -22199,11 +22505,11 @@ function cleanupOldBackups(target, keep) {
22199
22505
  if (keep <= 0)
22200
22506
  return;
22201
22507
  try {
22202
- const dir = dirname13(target);
22508
+ const dir = dirname14(target);
22203
22509
  const base = target.substring(dir.length + 1);
22204
22510
  const prefix = `${base}.bak.`;
22205
22511
  const all = readdirSync3(dir).filter((f) => f.startsWith(prefix)).map((f) => {
22206
- const full = join21(dir, f);
22512
+ const full = join22(dir, f);
22207
22513
  let mtimeMs = 0;
22208
22514
  try {
22209
22515
  mtimeMs = statSync5(full).mtimeMs;
@@ -22225,9 +22531,9 @@ function loadCompatibility(opts) {
22225
22531
  const root = opts?.cwd ?? inferPluginRoot();
22226
22532
  if (!root)
22227
22533
  return null;
22228
- file = join21(root, "compatibility.json");
22534
+ file = join22(root, "compatibility.json");
22229
22535
  }
22230
- if (!existsSync4(file))
22536
+ if (!existsSync5(file))
22231
22537
  return null;
22232
22538
  const raw = readFileSync5(file, "utf8");
22233
22539
  const obj = JSON.parse(raw);
@@ -22249,8 +22555,8 @@ function loadCompatibility(opts) {
22249
22555
  }
22250
22556
  function inferPluginRoot() {
22251
22557
  try {
22252
- const here = fileURLToPath(import.meta.url);
22253
- return dirname13(dirname13(here));
22558
+ const here = fileURLToPath2(import.meta.url);
22559
+ return dirname14(dirname14(here));
22254
22560
  } catch {
22255
22561
  return null;
22256
22562
  }
@@ -22445,17 +22751,17 @@ function detectOpencodeVersion() {
22445
22751
  }
22446
22752
  function getOpencodeBundlePath() {
22447
22753
  const candidates = [];
22448
- candidates.push(join22(homedir8(), ".config", "opencode", "codeforge", "index.js"));
22754
+ candidates.push(join23(homedir8(), ".config", "opencode", "codeforge", "index.js"));
22449
22755
  if (process.platform === "win32") {
22450
22756
  const appData = process.env["APPDATA"];
22451
22757
  if (appData)
22452
- candidates.push(join22(appData, "opencode", "codeforge", "index.js"));
22758
+ candidates.push(join23(appData, "opencode", "codeforge", "index.js"));
22453
22759
  const localAppData = process.env["LOCALAPPDATA"];
22454
22760
  if (localAppData)
22455
- candidates.push(join22(localAppData, "opencode", "codeforge", "index.js"));
22761
+ candidates.push(join23(localAppData, "opencode", "codeforge", "index.js"));
22456
22762
  }
22457
22763
  for (const c of candidates) {
22458
- if (existsSync5(c))
22764
+ if (existsSync6(c))
22459
22765
  return c;
22460
22766
  }
22461
22767
  return candidates[0] ?? null;
@@ -22513,60 +22819,60 @@ async function postToast(ctx, message) {
22513
22819
  var handler23 = updateCheckerServer;
22514
22820
 
22515
22821
  // plugins/workflow-engine.ts
22516
- import * as path26 from "node:path";
22822
+ import * as path27 from "node:path";
22517
22823
 
22518
22824
  // lib/workflow-loader.ts
22519
22825
  import { promises as fs20 } from "node:fs";
22520
- import * as path25 from "node:path";
22521
- import { z as z32 } from "zod";
22522
- var ActionSchema = z32.object({
22523
- tool: z32.string().min(1, "action.tool 不能为空"),
22524
- args: z32.record(z32.string(), z32.unknown()).optional().default({}),
22525
- on_error: z32.enum(["retry", "skip", "abort"]).optional()
22826
+ import * as path26 from "node:path";
22827
+ import { z as z33 } from "zod";
22828
+ var ActionSchema = z33.object({
22829
+ tool: z33.string().min(1, "action.tool 不能为空"),
22830
+ args: z33.record(z33.string(), z33.unknown()).optional().default({}),
22831
+ on_error: z33.enum(["retry", "skip", "abort"]).optional()
22526
22832
  });
22527
- var StepSchema = z32.object({
22528
- name: z32.string().min(1, "step.name 不能为空"),
22529
- agent: z32.string().min(1, "step.agent 不能为空").describe("agent 名(与 agents/<name>.md 对应)"),
22530
- description: z32.string().optional(),
22531
- inject_context: z32.record(z32.string(), z32.unknown()).optional(),
22532
- requires_human_approval: z32.boolean().optional().default(false),
22533
- actions: z32.array(ActionSchema).optional().default([]),
22534
- on_error: z32.enum(["retry", "skip", "abort"]).optional().default("abort"),
22535
- max_retries: z32.number().int().min(0).max(10).optional().default(2),
22536
- timeout: z32.string().regex(/^\d+(?:ms|s|m|h)$/, "timeout 必须是 数字+单位(ms/s/m/h),如 5m").optional(),
22537
- auto_feedback: z32.object({
22538
- test_cmd: z32.string().optional().describe("测试命令,如 npm test"),
22539
- lint_cmd: z32.string().optional().describe("lint 命令,如 npm run lint"),
22540
- max_retries: z32.number().int().min(1).max(10).optional().default(3),
22541
- error_excerpt_lines: z32.number().int().min(1).max(50).optional().default(5),
22542
- escalate_to: z32.string().optional().default("reviewer").describe("超上限后兜底 agent,默认 reviewer")
22833
+ var StepSchema = z33.object({
22834
+ name: z33.string().min(1, "step.name 不能为空"),
22835
+ agent: z33.string().min(1, "step.agent 不能为空").describe("agent 名(与 agents/<name>.md 对应)"),
22836
+ description: z33.string().optional(),
22837
+ inject_context: z33.record(z33.string(), z33.unknown()).optional(),
22838
+ requires_human_approval: z33.boolean().optional().default(false),
22839
+ actions: z33.array(ActionSchema).optional().default([]),
22840
+ on_error: z33.enum(["retry", "skip", "abort"]).optional().default("abort"),
22841
+ max_retries: z33.number().int().min(0).max(10).optional().default(2),
22842
+ timeout: z33.string().regex(/^\d+(?:ms|s|m|h)$/, "timeout 必须是 数字+单位(ms/s/m/h),如 5m").optional(),
22843
+ auto_feedback: z33.object({
22844
+ test_cmd: z33.string().optional().describe("测试命令,如 npm test"),
22845
+ lint_cmd: z33.string().optional().describe("lint 命令,如 npm run lint"),
22846
+ max_retries: z33.number().int().min(1).max(10).optional().default(3),
22847
+ error_excerpt_lines: z33.number().int().min(1).max(50).optional().default(5),
22848
+ escalate_to: z33.string().optional().default("reviewer").describe("超上限后兜底 agent,默认 reviewer")
22543
22849
  }).refine((d) => Boolean(d.test_cmd || d.lint_cmd), { message: "auto_feedback 必须至少配置 test_cmd 或 lint_cmd 之一" }).optional(),
22544
- on_decision: z32.object({
22545
- APPROVE: z32.union([
22546
- z32.literal("continue"),
22547
- z32.literal("abort"),
22548
- z32.object({ action: z32.literal("goto"), target: z32.string().min(1) })
22850
+ on_decision: z33.object({
22851
+ APPROVE: z33.union([
22852
+ z33.literal("continue"),
22853
+ z33.literal("abort"),
22854
+ z33.object({ action: z33.literal("goto"), target: z33.string().min(1) })
22549
22855
  ]).optional(),
22550
- REQUEST_CHANGES: z32.union([
22551
- z32.literal("continue"),
22552
- z32.literal("abort"),
22553
- z32.object({ action: z32.literal("goto"), target: z32.string().min(1) })
22856
+ REQUEST_CHANGES: z33.union([
22857
+ z33.literal("continue"),
22858
+ z33.literal("abort"),
22859
+ z33.object({ action: z33.literal("goto"), target: z33.string().min(1) })
22554
22860
  ]).optional(),
22555
- BLOCK: z32.union([
22556
- z32.literal("continue"),
22557
- z32.literal("abort"),
22558
- z32.object({ action: z32.literal("goto"), target: z32.string().min(1) })
22861
+ BLOCK: z33.union([
22862
+ z33.literal("continue"),
22863
+ z33.literal("abort"),
22864
+ z33.object({ action: z33.literal("goto"), target: z33.string().min(1) })
22559
22865
  ]).optional()
22560
22866
  }).refine((d) => Boolean(d.APPROVE || d.REQUEST_CHANGES || d.BLOCK), { message: "on_decision 必须至少配置 APPROVE / REQUEST_CHANGES / BLOCK 之一" }).optional()
22561
22867
  }).strict();
22562
- var WorkflowSchema = z32.object({
22563
- name: z32.string().min(1, "workflow.name 不能为空"),
22564
- description: z32.string().optional().default(""),
22565
- version: z32.string().optional().default("1.0.0"),
22566
- trigger: z32.string().min(1).refine((v) => /^\/[a-z][\w-]*$/.test(v) || /^event:[a-z][\w.-]+$/.test(v), "trigger 必须是 /command-name 或 event:xxx 形式"),
22567
- context_template: z32.string().optional(),
22568
- max_loops: z32.number().int().min(1).max(10).optional().default(3),
22569
- steps: z32.array(StepSchema).min(1, "workflow.steps 不能为空")
22868
+ var WorkflowSchema = z33.object({
22869
+ name: z33.string().min(1, "workflow.name 不能为空"),
22870
+ description: z33.string().optional().default(""),
22871
+ version: z33.string().optional().default("1.0.0"),
22872
+ trigger: z33.string().min(1).refine((v) => /^\/[a-z][\w-]*$/.test(v) || /^event:[a-z][\w.-]+$/.test(v), "trigger 必须是 /command-name 或 event:xxx 形式"),
22873
+ context_template: z33.string().optional(),
22874
+ max_loops: z33.number().int().min(1).max(10).optional().default(3),
22875
+ steps: z33.array(StepSchema).min(1, "workflow.steps 不能为空")
22570
22876
  }).strict();
22571
22877
  function parseWorkflowYaml(yaml, sourcePath = "<inline>") {
22572
22878
  let raw;
@@ -22630,7 +22936,7 @@ async function loadWorkflowsFromDir(dir) {
22630
22936
  continue;
22631
22937
  if (!/\.ya?ml$/i.test(name))
22632
22938
  continue;
22633
- const full = path25.join(dir, name);
22939
+ const full = path26.join(dir, name);
22634
22940
  const r = await loadWorkflowFromFile(full);
22635
22941
  if (r.ok)
22636
22942
  loaded.push(r);
@@ -23020,7 +23326,7 @@ async function handleCommandInvoked(raw, workflowsDir = "workflows") {
23020
23326
  }
23021
23327
  var workflowEngineServer = async (ctx) => {
23022
23328
  const directory = ctx.directory ?? process.cwd();
23023
- const workflowsDir = path26.join(directory, "workflows");
23329
+ const workflowsDir = path27.join(directory, "workflows");
23024
23330
  ensureRegistry(workflowsDir).catch((err) => fallbackLog2.warn(`[${PLUGIN_NAME24}] preload workflows failed`, {
23025
23331
  error: err instanceof Error ? err.message : String(err)
23026
23332
  }));
@@ -23064,7 +23370,7 @@ var workflowEngineServer = async (ctx) => {
23064
23370
  var handler24 = workflowEngineServer;
23065
23371
 
23066
23372
  // plugins/session-worktree-guard.ts
23067
- import path27 from "node:path";
23373
+ import path28 from "node:path";
23068
23374
  var PLUGIN_NAME25 = "session-worktree-guard";
23069
23375
  logLifecycle(PLUGIN_NAME25, "import", {});
23070
23376
  var WRITE_INTENT_RE = />(?![=&])(?!\s*\/dev\/(?:null|stdout|stderr|fd\/\d+)\b)|\btee\b|\brm\b|\bmv\b|\bcp\b|\bmkdir\b|\btouch\b|\bchmod\b|\bchown\b|\bln\b/;
@@ -23169,23 +23475,23 @@ var CLASS_B_CALLER_WHITELIST = new Set([
23169
23475
  "reviewer-lite",
23170
23476
  "general"
23171
23477
  ]);
23172
- var CODEFORGE_WORKTREE_DIR_NAME = path27.join(".git", "codeforge-worktrees");
23478
+ var CODEFORGE_WORKTREE_DIR_NAME = path28.join(".git", "codeforge-worktrees");
23173
23479
  function worktreesRoot(mainRoot) {
23174
- return path27.join(mainRoot, CODEFORGE_WORKTREE_DIR_NAME);
23480
+ return path28.join(mainRoot, CODEFORGE_WORKTREE_DIR_NAME);
23175
23481
  }
23176
23482
  function isInsideAnyWorktreeDir(absPath, mainRoot) {
23177
- if (!path27.isAbsolute(absPath))
23483
+ if (!path28.isAbsolute(absPath))
23178
23484
  return false;
23179
23485
  const root = worktreesRoot(mainRoot);
23180
23486
  if (absPath === root)
23181
23487
  return false;
23182
- const prefix = root.endsWith(path27.sep) ? root : root + path27.sep;
23488
+ const prefix = root.endsWith(path28.sep) ? root : root + path28.sep;
23183
23489
  return absPath.startsWith(prefix);
23184
23490
  }
23185
23491
  function rewritePath(value, mainRoot, worktreeRoot) {
23186
23492
  if (!value)
23187
23493
  return null;
23188
- const resolved = path27.isAbsolute(value) ? value : path27.resolve(mainRoot, value);
23494
+ const resolved = path28.isAbsolute(value) ? value : path28.resolve(mainRoot, value);
23189
23495
  const wtPrefix2 = worktreeRoot.endsWith("/") ? worktreeRoot : worktreeRoot + "/";
23190
23496
  if (resolved === worktreeRoot || resolved.startsWith(wtPrefix2)) {
23191
23497
  return null;
@@ -23223,7 +23529,7 @@ function commandContainsMainRootExcludingWorktree(command, mainRoot, worktreePat
23223
23529
  }
23224
23530
  }
23225
23531
  const wtRoot = worktreesRoot(mainRoot);
23226
- const wtRootPrefix = wtRoot + path27.sep;
23532
+ const wtRootPrefix = wtRoot + path28.sep;
23227
23533
  const escapedWtRootPrefix = escapeRegex2(wtRootPrefix);
23228
23534
  const wtPathPattern = escapedWtRootPrefix + `[^\\s'"\\x60)]*`;
23229
23535
  const allWorktreePathsReForEscape = new RegExp(wtPathPattern, "g");
@@ -23278,8 +23584,8 @@ function collectWritePaths(toolName, argsObj, worktreeRoot) {
23278
23584
  const candidate = toolName === "write" || toolName === "edit" ? argsObj["filePath"] : toolName === "ast_edit" ? argsObj["target"] : undefined;
23279
23585
  if (typeof candidate !== "string" || candidate.length === 0)
23280
23586
  return out;
23281
- const abs = path27.isAbsolute(candidate) ? candidate : path27.resolve(worktreeRoot, candidate);
23282
- const rel = path27.relative(worktreeRoot, abs).split(path27.sep).join("/");
23587
+ const abs = path28.isAbsolute(candidate) ? candidate : path28.resolve(worktreeRoot, candidate);
23588
+ const rel = path28.relative(worktreeRoot, abs).split(path28.sep).join("/");
23283
23589
  out.push(rel);
23284
23590
  return out;
23285
23591
  }
@@ -23670,12 +23976,12 @@ var sessionWorktreeGuardPlugin = async (ctx) => {
23670
23976
  var handler25 = sessionWorktreeGuardPlugin;
23671
23977
 
23672
23978
  // lib/opencode-session-probe.ts
23673
- import * as path28 from "node:path";
23979
+ import * as path29 from "node:path";
23674
23980
  import * as os6 from "node:os";
23675
23981
  import { createRequire as createRequire2 } from "node:module";
23676
23982
  var requireFromHere = createRequire2(import.meta.url);
23677
23983
  var DEFAULT_LIVENESS_MS = 6 * 60 * 60000;
23678
- var DEFAULT_DB_PATH = path28.join(os6.homedir(), ".local/share/opencode/opencode.db");
23984
+ var DEFAULT_DB_PATH = path29.join(os6.homedir(), ".local/share/opencode/opencode.db");
23679
23985
  function createSessionProbe(opts = {}) {
23680
23986
  const dbPath = opts.dbPath ?? DEFAULT_DB_PATH;
23681
23987
  const httpBaseUrl = opts.httpBaseUrl ?? process.env["OPENCODE_SERVER_URL"];