@andyqiu/codeforge 0.3.13 → 0.3.14

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
@@ -1034,17 +1034,17 @@ var require_visit = __commonJS((exports) => {
1034
1034
  visit.BREAK = BREAK;
1035
1035
  visit.SKIP = SKIP;
1036
1036
  visit.REMOVE = REMOVE;
1037
- function visit_(key, node, visitor, path13) {
1038
- const ctrl = callVisitor(key, node, visitor, path13);
1037
+ function visit_(key, node, visitor, path12) {
1038
+ const ctrl = callVisitor(key, node, visitor, path12);
1039
1039
  if (identity.isNode(ctrl) || identity.isPair(ctrl)) {
1040
- replaceNode(key, path13, ctrl);
1041
- return visit_(key, ctrl, visitor, path13);
1040
+ replaceNode(key, path12, ctrl);
1041
+ return visit_(key, ctrl, visitor, path12);
1042
1042
  }
1043
1043
  if (typeof ctrl !== "symbol") {
1044
1044
  if (identity.isCollection(node)) {
1045
- path13 = Object.freeze(path13.concat(node));
1045
+ path12 = Object.freeze(path12.concat(node));
1046
1046
  for (let i = 0;i < node.items.length; ++i) {
1047
- const ci = visit_(i, node.items[i], visitor, path13);
1047
+ const ci = visit_(i, node.items[i], visitor, path12);
1048
1048
  if (typeof ci === "number")
1049
1049
  i = ci - 1;
1050
1050
  else if (ci === BREAK)
@@ -1055,13 +1055,13 @@ var require_visit = __commonJS((exports) => {
1055
1055
  }
1056
1056
  }
1057
1057
  } else if (identity.isPair(node)) {
1058
- path13 = Object.freeze(path13.concat(node));
1059
- const ck = visit_("key", node.key, visitor, path13);
1058
+ path12 = Object.freeze(path12.concat(node));
1059
+ const ck = visit_("key", node.key, visitor, path12);
1060
1060
  if (ck === BREAK)
1061
1061
  return BREAK;
1062
1062
  else if (ck === REMOVE)
1063
1063
  node.key = null;
1064
- const cv = visit_("value", node.value, visitor, path13);
1064
+ const cv = visit_("value", node.value, visitor, path12);
1065
1065
  if (cv === BREAK)
1066
1066
  return BREAK;
1067
1067
  else if (cv === REMOVE)
@@ -1082,17 +1082,17 @@ var require_visit = __commonJS((exports) => {
1082
1082
  visitAsync.BREAK = BREAK;
1083
1083
  visitAsync.SKIP = SKIP;
1084
1084
  visitAsync.REMOVE = REMOVE;
1085
- async function visitAsync_(key, node, visitor, path13) {
1086
- const ctrl = await callVisitor(key, node, visitor, path13);
1085
+ async function visitAsync_(key, node, visitor, path12) {
1086
+ const ctrl = await callVisitor(key, node, visitor, path12);
1087
1087
  if (identity.isNode(ctrl) || identity.isPair(ctrl)) {
1088
- replaceNode(key, path13, ctrl);
1089
- return visitAsync_(key, ctrl, visitor, path13);
1088
+ replaceNode(key, path12, ctrl);
1089
+ return visitAsync_(key, ctrl, visitor, path12);
1090
1090
  }
1091
1091
  if (typeof ctrl !== "symbol") {
1092
1092
  if (identity.isCollection(node)) {
1093
- path13 = Object.freeze(path13.concat(node));
1093
+ path12 = Object.freeze(path12.concat(node));
1094
1094
  for (let i = 0;i < node.items.length; ++i) {
1095
- const ci = await visitAsync_(i, node.items[i], visitor, path13);
1095
+ const ci = await visitAsync_(i, node.items[i], visitor, path12);
1096
1096
  if (typeof ci === "number")
1097
1097
  i = ci - 1;
1098
1098
  else if (ci === BREAK)
@@ -1103,13 +1103,13 @@ var require_visit = __commonJS((exports) => {
1103
1103
  }
1104
1104
  }
1105
1105
  } else if (identity.isPair(node)) {
1106
- path13 = Object.freeze(path13.concat(node));
1107
- const ck = await visitAsync_("key", node.key, visitor, path13);
1106
+ path12 = Object.freeze(path12.concat(node));
1107
+ const ck = await visitAsync_("key", node.key, visitor, path12);
1108
1108
  if (ck === BREAK)
1109
1109
  return BREAK;
1110
1110
  else if (ck === REMOVE)
1111
1111
  node.key = null;
1112
- const cv = await visitAsync_("value", node.value, visitor, path13);
1112
+ const cv = await visitAsync_("value", node.value, visitor, path12);
1113
1113
  if (cv === BREAK)
1114
1114
  return BREAK;
1115
1115
  else if (cv === REMOVE)
@@ -1136,23 +1136,23 @@ var require_visit = __commonJS((exports) => {
1136
1136
  }
1137
1137
  return visitor;
1138
1138
  }
1139
- function callVisitor(key, node, visitor, path13) {
1139
+ function callVisitor(key, node, visitor, path12) {
1140
1140
  if (typeof visitor === "function")
1141
- return visitor(key, node, path13);
1141
+ return visitor(key, node, path12);
1142
1142
  if (identity.isMap(node))
1143
- return visitor.Map?.(key, node, path13);
1143
+ return visitor.Map?.(key, node, path12);
1144
1144
  if (identity.isSeq(node))
1145
- return visitor.Seq?.(key, node, path13);
1145
+ return visitor.Seq?.(key, node, path12);
1146
1146
  if (identity.isPair(node))
1147
- return visitor.Pair?.(key, node, path13);
1147
+ return visitor.Pair?.(key, node, path12);
1148
1148
  if (identity.isScalar(node))
1149
- return visitor.Scalar?.(key, node, path13);
1149
+ return visitor.Scalar?.(key, node, path12);
1150
1150
  if (identity.isAlias(node))
1151
- return visitor.Alias?.(key, node, path13);
1151
+ return visitor.Alias?.(key, node, path12);
1152
1152
  return;
1153
1153
  }
1154
- function replaceNode(key, path13, node) {
1155
- const parent = path13[path13.length - 1];
1154
+ function replaceNode(key, path12, node) {
1155
+ const parent = path12[path12.length - 1];
1156
1156
  if (identity.isCollection(parent)) {
1157
1157
  parent.items[key] = node;
1158
1158
  } else if (identity.isPair(parent)) {
@@ -1711,10 +1711,10 @@ var require_Collection = __commonJS((exports) => {
1711
1711
  var createNode = require_createNode();
1712
1712
  var identity = require_identity();
1713
1713
  var Node = require_Node();
1714
- function collectionFromPath(schema, path13, value) {
1714
+ function collectionFromPath(schema, path12, value) {
1715
1715
  let v = value;
1716
- for (let i = path13.length - 1;i >= 0; --i) {
1717
- const k = path13[i];
1716
+ for (let i = path12.length - 1;i >= 0; --i) {
1717
+ const k = path12[i];
1718
1718
  if (typeof k === "number" && Number.isInteger(k) && k >= 0) {
1719
1719
  const a = [];
1720
1720
  a[k] = v;
@@ -1733,7 +1733,7 @@ var require_Collection = __commonJS((exports) => {
1733
1733
  sourceObjects: new Map
1734
1734
  });
1735
1735
  }
1736
- var isEmptyPath = (path13) => path13 == null || typeof path13 === "object" && !!path13[Symbol.iterator]().next().done;
1736
+ var isEmptyPath = (path12) => path12 == null || typeof path12 === "object" && !!path12[Symbol.iterator]().next().done;
1737
1737
 
1738
1738
  class Collection extends Node.NodeBase {
1739
1739
  constructor(type, schema) {
@@ -1754,11 +1754,11 @@ var require_Collection = __commonJS((exports) => {
1754
1754
  copy.range = this.range.slice();
1755
1755
  return copy;
1756
1756
  }
1757
- addIn(path13, value) {
1758
- if (isEmptyPath(path13))
1757
+ addIn(path12, value) {
1758
+ if (isEmptyPath(path12))
1759
1759
  this.add(value);
1760
1760
  else {
1761
- const [key, ...rest] = path13;
1761
+ const [key, ...rest] = path12;
1762
1762
  const node = this.get(key, true);
1763
1763
  if (identity.isCollection(node))
1764
1764
  node.addIn(rest, value);
@@ -1768,8 +1768,8 @@ var require_Collection = __commonJS((exports) => {
1768
1768
  throw new Error(`Expected YAML collection at ${key}. Remaining path: ${rest}`);
1769
1769
  }
1770
1770
  }
1771
- deleteIn(path13) {
1772
- const [key, ...rest] = path13;
1771
+ deleteIn(path12) {
1772
+ const [key, ...rest] = path12;
1773
1773
  if (rest.length === 0)
1774
1774
  return this.delete(key);
1775
1775
  const node = this.get(key, true);
@@ -1778,8 +1778,8 @@ var require_Collection = __commonJS((exports) => {
1778
1778
  else
1779
1779
  throw new Error(`Expected YAML collection at ${key}. Remaining path: ${rest}`);
1780
1780
  }
1781
- getIn(path13, keepScalar) {
1782
- const [key, ...rest] = path13;
1781
+ getIn(path12, keepScalar) {
1782
+ const [key, ...rest] = path12;
1783
1783
  const node = this.get(key, true);
1784
1784
  if (rest.length === 0)
1785
1785
  return !keepScalar && identity.isScalar(node) ? node.value : node;
@@ -1794,15 +1794,15 @@ var require_Collection = __commonJS((exports) => {
1794
1794
  return n == null || allowScalar && identity.isScalar(n) && n.value == null && !n.commentBefore && !n.comment && !n.tag;
1795
1795
  });
1796
1796
  }
1797
- hasIn(path13) {
1798
- const [key, ...rest] = path13;
1797
+ hasIn(path12) {
1798
+ const [key, ...rest] = path12;
1799
1799
  if (rest.length === 0)
1800
1800
  return this.has(key);
1801
1801
  const node = this.get(key, true);
1802
1802
  return identity.isCollection(node) ? node.hasIn(rest) : false;
1803
1803
  }
1804
- setIn(path13, value) {
1805
- const [key, ...rest] = path13;
1804
+ setIn(path12, value) {
1805
+ const [key, ...rest] = path12;
1806
1806
  if (rest.length === 0) {
1807
1807
  this.set(key, value);
1808
1808
  } else {
@@ -2608,7 +2608,7 @@ var require_merge = __commonJS((exports) => {
2608
2608
 
2609
2609
  // node_modules/yaml/dist/nodes/addPairToJSMap.js
2610
2610
  var require_addPairToJSMap = __commonJS((exports) => {
2611
- var log8 = require_log();
2611
+ var log6 = require_log();
2612
2612
  var merge = require_merge();
2613
2613
  var stringify = require_stringify();
2614
2614
  var identity = require_identity();
@@ -2657,7 +2657,7 @@ var require_addPairToJSMap = __commonJS((exports) => {
2657
2657
  let jsonStr = JSON.stringify(strKey);
2658
2658
  if (jsonStr.length > 40)
2659
2659
  jsonStr = jsonStr.substring(0, 36) + '..."';
2660
- log8.warn(ctx.doc.options.logLevel, `Keys with collection values will be stringified due to JS Object restrictions: ${jsonStr}. Set mapAsMap: true to use object keys.`);
2660
+ log6.warn(ctx.doc.options.logLevel, `Keys with collection values will be stringified due to JS Object restrictions: ${jsonStr}. Set mapAsMap: true to use object keys.`);
2661
2661
  ctx.mapKeyWarned = true;
2662
2662
  }
2663
2663
  return strKey;
@@ -4195,9 +4195,9 @@ var require_Document = __commonJS((exports) => {
4195
4195
  if (assertCollection(this.contents))
4196
4196
  this.contents.add(value);
4197
4197
  }
4198
- addIn(path13, value) {
4198
+ addIn(path12, value) {
4199
4199
  if (assertCollection(this.contents))
4200
- this.contents.addIn(path13, value);
4200
+ this.contents.addIn(path12, value);
4201
4201
  }
4202
4202
  createAlias(node, name) {
4203
4203
  if (!node.anchor) {
@@ -4246,30 +4246,30 @@ var require_Document = __commonJS((exports) => {
4246
4246
  delete(key) {
4247
4247
  return assertCollection(this.contents) ? this.contents.delete(key) : false;
4248
4248
  }
4249
- deleteIn(path13) {
4250
- if (Collection.isEmptyPath(path13)) {
4249
+ deleteIn(path12) {
4250
+ if (Collection.isEmptyPath(path12)) {
4251
4251
  if (this.contents == null)
4252
4252
  return false;
4253
4253
  this.contents = null;
4254
4254
  return true;
4255
4255
  }
4256
- return assertCollection(this.contents) ? this.contents.deleteIn(path13) : false;
4256
+ return assertCollection(this.contents) ? this.contents.deleteIn(path12) : false;
4257
4257
  }
4258
4258
  get(key, keepScalar) {
4259
4259
  return identity.isCollection(this.contents) ? this.contents.get(key, keepScalar) : undefined;
4260
4260
  }
4261
- getIn(path13, keepScalar) {
4262
- if (Collection.isEmptyPath(path13))
4261
+ getIn(path12, keepScalar) {
4262
+ if (Collection.isEmptyPath(path12))
4263
4263
  return !keepScalar && identity.isScalar(this.contents) ? this.contents.value : this.contents;
4264
- return identity.isCollection(this.contents) ? this.contents.getIn(path13, keepScalar) : undefined;
4264
+ return identity.isCollection(this.contents) ? this.contents.getIn(path12, keepScalar) : undefined;
4265
4265
  }
4266
4266
  has(key) {
4267
4267
  return identity.isCollection(this.contents) ? this.contents.has(key) : false;
4268
4268
  }
4269
- hasIn(path13) {
4270
- if (Collection.isEmptyPath(path13))
4269
+ hasIn(path12) {
4270
+ if (Collection.isEmptyPath(path12))
4271
4271
  return this.contents !== undefined;
4272
- return identity.isCollection(this.contents) ? this.contents.hasIn(path13) : false;
4272
+ return identity.isCollection(this.contents) ? this.contents.hasIn(path12) : false;
4273
4273
  }
4274
4274
  set(key, value) {
4275
4275
  if (this.contents == null) {
@@ -4278,13 +4278,13 @@ var require_Document = __commonJS((exports) => {
4278
4278
  this.contents.set(key, value);
4279
4279
  }
4280
4280
  }
4281
- setIn(path13, value) {
4282
- if (Collection.isEmptyPath(path13)) {
4281
+ setIn(path12, value) {
4282
+ if (Collection.isEmptyPath(path12)) {
4283
4283
  this.contents = value;
4284
4284
  } else if (this.contents == null) {
4285
- this.contents = Collection.collectionFromPath(this.schema, Array.from(path13), value);
4285
+ this.contents = Collection.collectionFromPath(this.schema, Array.from(path12), value);
4286
4286
  } else if (assertCollection(this.contents)) {
4287
- this.contents.setIn(path13, value);
4287
+ this.contents.setIn(path12, value);
4288
4288
  }
4289
4289
  }
4290
4290
  setSchema(version, options = {}) {
@@ -6179,9 +6179,9 @@ var require_cst_visit = __commonJS((exports) => {
6179
6179
  visit.BREAK = BREAK;
6180
6180
  visit.SKIP = SKIP;
6181
6181
  visit.REMOVE = REMOVE;
6182
- visit.itemAtPath = (cst, path13) => {
6182
+ visit.itemAtPath = (cst, path12) => {
6183
6183
  let item = cst;
6184
- for (const [field, index] of path13) {
6184
+ for (const [field, index] of path12) {
6185
6185
  const tok = item?.[field];
6186
6186
  if (tok && "items" in tok) {
6187
6187
  item = tok.items[index];
@@ -6190,23 +6190,23 @@ var require_cst_visit = __commonJS((exports) => {
6190
6190
  }
6191
6191
  return item;
6192
6192
  };
6193
- visit.parentCollection = (cst, path13) => {
6194
- const parent = visit.itemAtPath(cst, path13.slice(0, -1));
6195
- const field = path13[path13.length - 1][0];
6193
+ visit.parentCollection = (cst, path12) => {
6194
+ const parent = visit.itemAtPath(cst, path12.slice(0, -1));
6195
+ const field = path12[path12.length - 1][0];
6196
6196
  const coll = parent?.[field];
6197
6197
  if (coll && "items" in coll)
6198
6198
  return coll;
6199
6199
  throw new Error("Parent collection not found");
6200
6200
  };
6201
- function _visit(path13, item, visitor) {
6202
- let ctrl = visitor(item, path13);
6201
+ function _visit(path12, item, visitor) {
6202
+ let ctrl = visitor(item, path12);
6203
6203
  if (typeof ctrl === "symbol")
6204
6204
  return ctrl;
6205
6205
  for (const field of ["key", "value"]) {
6206
6206
  const token = item[field];
6207
6207
  if (token && "items" in token) {
6208
6208
  for (let i = 0;i < token.items.length; ++i) {
6209
- const ci = _visit(Object.freeze(path13.concat([[field, i]])), token.items[i], visitor);
6209
+ const ci = _visit(Object.freeze(path12.concat([[field, i]])), token.items[i], visitor);
6210
6210
  if (typeof ci === "number")
6211
6211
  i = ci - 1;
6212
6212
  else if (ci === BREAK)
@@ -6217,10 +6217,10 @@ var require_cst_visit = __commonJS((exports) => {
6217
6217
  }
6218
6218
  }
6219
6219
  if (typeof ctrl === "function" && field === "key")
6220
- ctrl = ctrl(item, path13);
6220
+ ctrl = ctrl(item, path12);
6221
6221
  }
6222
6222
  }
6223
- return typeof ctrl === "function" ? ctrl(item, path13) : ctrl;
6223
+ return typeof ctrl === "function" ? ctrl(item, path12) : ctrl;
6224
6224
  }
6225
6225
  exports.visit = visit;
6226
6226
  });
@@ -6429,11 +6429,11 @@ var require_lexer = __commonJS((exports) => {
6429
6429
  hasChars(n) {
6430
6430
  return this.pos + n <= this.buffer.length;
6431
6431
  }
6432
- setNext(state3) {
6432
+ setNext(state2) {
6433
6433
  this.buffer = this.buffer.substring(this.pos);
6434
6434
  this.pos = 0;
6435
6435
  this.lineEndPos = null;
6436
- this.next = state3;
6436
+ this.next = state2;
6437
6437
  return null;
6438
6438
  }
6439
6439
  peek(n) {
@@ -7489,14 +7489,14 @@ var require_parser = __commonJS((exports) => {
7489
7489
  case "scalar":
7490
7490
  case "single-quoted-scalar":
7491
7491
  case "double-quoted-scalar": {
7492
- const fs10 = this.flowScalar(this.type);
7492
+ const fs9 = this.flowScalar(this.type);
7493
7493
  if (atNextItem || it.value) {
7494
- map.items.push({ start, key: fs10, sep: [] });
7494
+ map.items.push({ start, key: fs9, sep: [] });
7495
7495
  this.onKeyLine = true;
7496
7496
  } else if (it.sep) {
7497
- this.stack.push(fs10);
7497
+ this.stack.push(fs9);
7498
7498
  } else {
7499
- Object.assign(it, { key: fs10, sep: [] });
7499
+ Object.assign(it, { key: fs9, sep: [] });
7500
7500
  this.onKeyLine = true;
7501
7501
  }
7502
7502
  return;
@@ -7624,13 +7624,13 @@ var require_parser = __commonJS((exports) => {
7624
7624
  case "scalar":
7625
7625
  case "single-quoted-scalar":
7626
7626
  case "double-quoted-scalar": {
7627
- const fs10 = this.flowScalar(this.type);
7627
+ const fs9 = this.flowScalar(this.type);
7628
7628
  if (!it || it.value)
7629
- fc.items.push({ start: [], key: fs10, sep: [] });
7629
+ fc.items.push({ start: [], key: fs9, sep: [] });
7630
7630
  else if (it.sep)
7631
- this.stack.push(fs10);
7631
+ this.stack.push(fs9);
7632
7632
  else
7633
- Object.assign(it, { key: fs10, sep: [] });
7633
+ Object.assign(it, { key: fs9, sep: [] });
7634
7634
  return;
7635
7635
  }
7636
7636
  case "flow-map-end":
@@ -7794,7 +7794,7 @@ var require_public_api = __commonJS((exports) => {
7794
7794
  var composer = require_composer();
7795
7795
  var Document = require_Document();
7796
7796
  var errors = require_errors();
7797
- var log8 = require_log();
7797
+ var log6 = require_log();
7798
7798
  var identity = require_identity();
7799
7799
  var lineCounter = require_line_counter();
7800
7800
  var parser = require_parser();
@@ -7846,7 +7846,7 @@ var require_public_api = __commonJS((exports) => {
7846
7846
  const doc = parseDocument(src, options);
7847
7847
  if (!doc)
7848
7848
  return null;
7849
- doc.warnings.forEach((warning) => log8.warn(doc.options.logLevel, warning));
7849
+ doc.warnings.forEach((warning) => log6.warn(doc.options.logLevel, warning));
7850
7850
  if (doc.errors.length > 0) {
7851
7851
  if (doc.options.logLevel !== "silent")
7852
7852
  throw doc.errors[0];
@@ -8066,7 +8066,7 @@ async function runAutoFeedback(cfg, ctx) {
8066
8066
  if (cmds.length === 0) {
8067
8067
  throw new Error("auto-feedback: 至少需要 test_cmd 或 lint_cmd 之一");
8068
8068
  }
8069
- const log13 = ctx.log ?? (() => {});
8069
+ const log14 = ctx.log ?? (() => {});
8070
8070
  const attempt = async () => {
8071
8071
  let last = {
8072
8072
  cmd: "",
@@ -8103,7 +8103,7 @@ ${excerpt}
8103
8103
 
8104
8104
  请基于错误信息修复 pending-changes(用 ast-edit / pending-changes.stage),完成后我会重新跑测试验证。`;
8105
8105
  const result2 = await ctx.dispatchToAgent("coder", prompt);
8106
- log13("info", `auto-feedback round ${round} dispatch summary: ${result2.summary.slice(0, 200)}`);
8106
+ log14("info", `auto-feedback round ${round} dispatch summary: ${result2.summary.slice(0, 200)}`);
8107
8107
  };
8108
8108
  const debugResult = await runWithAutoDebug({
8109
8109
  attempt,
@@ -8124,7 +8124,7 @@ ${excerpt}
8124
8124
  history: debugResult.history
8125
8125
  };
8126
8126
  if (!debugResult.ok && debugResult.stopReason === "max-rounds") {
8127
- log13("warn", `auto-feedback 达 max_retries=${cfg.max_retries},escalate to ${cfg.escalate_to}`);
8127
+ log14("warn", `auto-feedback 达 max_retries=${cfg.max_retries},escalate to ${cfg.escalate_to}`);
8128
8128
  const lastFail = debugResult.history[debugResult.history.length - 1];
8129
8129
  const excerpt = lastFail?.result ? extractErrorExcerpt(lastFail.result, cfg.error_excerpt_lines) : "(无失败结果)";
8130
8130
  const escalatePrompt = `coder 连续 ${cfg.max_retries} 轮自纠失败,错误片段:
@@ -14008,6 +14008,507 @@ var codeforgeToolsServer = async (ctx) => {
14008
14008
  };
14009
14009
  var handler8 = codeforgeToolsServer;
14010
14010
 
14011
+ // plugins/discover-spec-suggest.ts
14012
+ init_opencode_plugin_helpers();
14013
+ import { readFileSync as readFileSync4, readdirSync, statSync as statSync3 } from "node:fs";
14014
+ import { join as join11 } from "node:path";
14015
+
14016
+ // lib/handoff-schema.ts
14017
+ import { z as z28 } from "zod";
14018
+
14019
+ // node_modules/yaml/dist/index.js
14020
+ var composer = require_composer();
14021
+ var Document = require_Document();
14022
+ var Schema = require_Schema();
14023
+ var errors = require_errors();
14024
+ var Alias = require_Alias();
14025
+ var identity = require_identity();
14026
+ var Pair = require_Pair();
14027
+ var Scalar = require_Scalar();
14028
+ var YAMLMap = require_YAMLMap();
14029
+ var YAMLSeq = require_YAMLSeq();
14030
+ var cst = require_cst();
14031
+ var lexer = require_lexer();
14032
+ var lineCounter = require_line_counter();
14033
+ var parser = require_parser();
14034
+ var publicApi = require_public_api();
14035
+ var visit = require_visit();
14036
+ var $Composer = composer.Composer;
14037
+ var $Document = Document.Document;
14038
+ var $Schema = Schema.Schema;
14039
+ var $YAMLError = errors.YAMLError;
14040
+ var $YAMLParseError = errors.YAMLParseError;
14041
+ var $YAMLWarning = errors.YAMLWarning;
14042
+ var $Alias = Alias.Alias;
14043
+ var $isAlias = identity.isAlias;
14044
+ var $isCollection = identity.isCollection;
14045
+ var $isDocument = identity.isDocument;
14046
+ var $isMap = identity.isMap;
14047
+ var $isNode = identity.isNode;
14048
+ var $isPair = identity.isPair;
14049
+ var $isScalar = identity.isScalar;
14050
+ var $isSeq = identity.isSeq;
14051
+ var $Pair = Pair.Pair;
14052
+ var $Scalar = Scalar.Scalar;
14053
+ var $YAMLMap = YAMLMap.YAMLMap;
14054
+ var $YAMLSeq = YAMLSeq.YAMLSeq;
14055
+ var $Lexer = lexer.Lexer;
14056
+ var $LineCounter = lineCounter.LineCounter;
14057
+ var $Parser = parser.Parser;
14058
+ var $parse = publicApi.parse;
14059
+ var $parseAllDocuments = publicApi.parseAllDocuments;
14060
+ var $parseDocument = publicApi.parseDocument;
14061
+ var $stringify = publicApi.stringify;
14062
+ var $visit = visit.visit;
14063
+ var $visitAsync = visit.visitAsync;
14064
+
14065
+ // lib/handoff-schema.ts
14066
+ var MAX_HANDOFF_SIZE = 100 * 1024;
14067
+ var SLUG_REGEX = /^[a-z0-9][a-z0-9-]{0,49}$/;
14068
+ var SCORE_DIMENSIONS = ["functional", "ux", "technical", "constraints", "edge_cases"];
14069
+ var COMBO_VALUES = ["A", "B", "C", "D"];
14070
+ var NeedSchema = z28.object({
14071
+ id: z28.string().min(1),
14072
+ type: z28.enum(["must", "should", "nice-to-have"]),
14073
+ statement: z28.string().min(1),
14074
+ rationale: z28.string().optional(),
14075
+ acceptance: z28.array(z28.string()).optional()
14076
+ });
14077
+ var BoundarySchema = z28.object({
14078
+ excluded: z28.string().min(1),
14079
+ reason: z28.string().min(1)
14080
+ });
14081
+ var AssumptionSchema = z28.object({
14082
+ statement: z28.string().min(1),
14083
+ confidence: z28.enum(["verified", "speculation", "high-risk-unknown"]),
14084
+ needs_validation_by: z28.enum(["plan", "coder", "runtime"]).optional()
14085
+ });
14086
+ var OpenIssueSchema = z28.union([
14087
+ z28.string().min(1),
14088
+ z28.object({
14089
+ id: z28.string().optional(),
14090
+ question: z28.string().min(1),
14091
+ blocking: z28.boolean().optional()
14092
+ })
14093
+ ]);
14094
+ var RedFlagsSchema = z28.object({
14095
+ raised: z28.boolean(),
14096
+ combos: z28.array(z28.enum(COMBO_VALUES)).default([]),
14097
+ reasons: z28.array(z28.string()).default([]),
14098
+ user_persisted_rounds: z28.number().int().nonnegative().default(0),
14099
+ downstream_advisory: z28.string().nullable().optional()
14100
+ }).superRefine((rf, ctx) => {
14101
+ if (rf.raised) {
14102
+ if (rf.combos.length === 0) {
14103
+ ctx.addIssue({
14104
+ code: z28.ZodIssueCode.custom,
14105
+ message: "red_flags.raised=true 时 combos 必须 >=1",
14106
+ path: ["combos"]
14107
+ });
14108
+ }
14109
+ if (rf.reasons.length === 0) {
14110
+ ctx.addIssue({
14111
+ code: z28.ZodIssueCode.custom,
14112
+ message: "red_flags.raised=true 时 reasons 必须 >=1",
14113
+ path: ["reasons"]
14114
+ });
14115
+ }
14116
+ }
14117
+ });
14118
+ var ScoresSchema = z28.object(Object.fromEntries(SCORE_DIMENSIONS.map((d) => [d, z28.number().min(0).max(1)])));
14119
+ var PreCodingBlockerSchema = z28.object({
14120
+ id: z28.string().regex(/^PRE-\d+$/, "id 必须形如 PRE-1 / PRE-2"),
14121
+ blocker: z28.string().min(1),
14122
+ source: z28.enum(["assumption", "red_flag", "open_issue"]),
14123
+ must_resolve_by: z28.enum(["user", "codeforge"])
14124
+ });
14125
+ var KhRefSchema = z28.object({
14126
+ kh_id: z28.string(),
14127
+ title: z28.string(),
14128
+ relevance: z28.enum(["positive", "negative", "neutral"]).optional()
14129
+ });
14130
+ var HandoffSchema = z28.object({
14131
+ schema_version: z28.string().regex(/^\d+\.\d+\.\d+$/, "schema_version 必须形如 1.1.0 / 1.2.0"),
14132
+ slug: z28.string().regex(SLUG_REGEX, "slug 仅允许 [a-z0-9-],长度 1-50,首字符为字母数字"),
14133
+ title: z28.string().min(1).max(200),
14134
+ created_at: z28.string().optional(),
14135
+ discover_session_id: z28.string().optional(),
14136
+ weighted_score: z28.number().min(0).max(1),
14137
+ scores: ScoresSchema,
14138
+ needs: z28.array(NeedSchema).min(1, "needs 必须 >=1"),
14139
+ boundaries: z28.array(BoundarySchema).min(1, "boundaries 必须 >=1"),
14140
+ assumptions: z28.array(AssumptionSchema),
14141
+ open_issues: z28.array(OpenIssueSchema).default([]),
14142
+ rejected_alternatives: z28.array(z28.object({ option: z28.string(), reason: z28.string() })).default([]),
14143
+ acceptance_criteria: z28.array(z28.object({
14144
+ id: z28.string().regex(/^AC-/, "AC id 必须以 AC- 开头"),
14145
+ description: z28.string().min(1),
14146
+ measurable: z28.boolean().optional(),
14147
+ metric: z28.string().optional()
14148
+ })).default([]),
14149
+ red_flags: RedFlagsSchema,
14150
+ pre_coding_blockers: z28.array(PreCodingBlockerSchema).default([]),
14151
+ kh_references: z28.array(KhRefSchema).default([]),
14152
+ adr_refs: z28.array(z28.string()).default([]),
14153
+ related_artifacts: z28.object({
14154
+ prd: z28.string().optional(),
14155
+ transcript: z28.string().optional()
14156
+ }).optional()
14157
+ });
14158
+ function validateHandoff(rawYaml, fileSize) {
14159
+ if (fileSize > MAX_HANDOFF_SIZE) {
14160
+ return {
14161
+ ok: false,
14162
+ reason: `handoff.yaml 超过 ${MAX_HANDOFF_SIZE} 字节上限(实际 ${fileSize} 字节)`
14163
+ };
14164
+ }
14165
+ if (typeof rawYaml !== "string" || rawYaml.length === 0) {
14166
+ return { ok: false, reason: "handoff.yaml 内容为空" };
14167
+ }
14168
+ let parsed;
14169
+ try {
14170
+ parsed = $parse(rawYaml);
14171
+ } catch (err) {
14172
+ const msg = err instanceof Error ? err.message : String(err);
14173
+ return { ok: false, reason: `YAML 解析失败:${msg}` };
14174
+ }
14175
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
14176
+ return { ok: false, reason: "handoff.yaml 顶层必须是 object" };
14177
+ }
14178
+ const result = HandoffSchema.safeParse(parsed);
14179
+ if (!result.success) {
14180
+ const first = result.error.issues[0];
14181
+ const path12 = first?.path?.join(".") ?? "(root)";
14182
+ const msg = first?.message ?? "unknown";
14183
+ return { ok: false, reason: `schema 校验失败:${path12}: ${msg}` };
14184
+ }
14185
+ return { ok: true, data: result.data, schemaVersion: result.data.schema_version };
14186
+ }
14187
+ function isValidSlug(slug) {
14188
+ return typeof slug === "string" && SLUG_REGEX.test(slug);
14189
+ }
14190
+
14191
+ // plugins/discover-spec-suggest.ts
14192
+ var PLUGIN_NAME9 = "discover-spec-suggest";
14193
+ logLifecycle(PLUGIN_NAME9, "import", {});
14194
+ var TARGET_AGENT = "codeforge";
14195
+ var SESSION_CAP = 200;
14196
+ var SESSION_TTL_MS = 24 * 60 * 60 * 1000;
14197
+ var MATCH_THRESHOLD = 0.15;
14198
+ var MAX_CANDIDATES = 3;
14199
+ var NUDGE_MAX_LEN = 1500;
14200
+ var SPECS_REL_DIR = join11(".codeforge", "specs");
14201
+ var sessionMap = new Map;
14202
+ function pruneIfOversize() {
14203
+ while (sessionMap.size > SESSION_CAP) {
14204
+ const oldestKey = sessionMap.keys().next().value;
14205
+ if (oldestKey === undefined)
14206
+ break;
14207
+ sessionMap.delete(oldestKey);
14208
+ }
14209
+ }
14210
+ function isExpired(entry, now = Date.now()) {
14211
+ return now - entry.ts > SESSION_TTL_MS;
14212
+ }
14213
+ var specIndex = [];
14214
+ function defaultReader(p) {
14215
+ return readFileSync4(p, "utf8");
14216
+ }
14217
+ function defaultDirReader(p) {
14218
+ return readdirSync(p);
14219
+ }
14220
+ function defaultDirExists(p) {
14221
+ try {
14222
+ return statSync3(p).isDirectory();
14223
+ } catch {
14224
+ return false;
14225
+ }
14226
+ }
14227
+ function defaultStatReader(p) {
14228
+ const st = statSync3(p);
14229
+ return { size: st.size, mtimeMs: st.mtimeMs, isDirectory: st.isDirectory() };
14230
+ }
14231
+ function tokenize(s) {
14232
+ if (typeof s !== "string" || s.length === 0)
14233
+ return [];
14234
+ const lower = s.toLowerCase();
14235
+ const parts = lower.split(/[^a-z0-9\u4e00-\u9fff]+/u).filter(Boolean);
14236
+ return parts.filter((t) => /[\u4e00-\u9fff]/u.test(t) || t.length >= 2);
14237
+ }
14238
+ function jaccard(a, b) {
14239
+ if (a.size === 0 || b.size === 0)
14240
+ return 0;
14241
+ let inter = 0;
14242
+ for (const t of a)
14243
+ if (b.has(t))
14244
+ inter++;
14245
+ const union = a.size + b.size - inter;
14246
+ if (union === 0)
14247
+ return 0;
14248
+ return inter / union;
14249
+ }
14250
+ function escapeRegExp2(s) {
14251
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
14252
+ }
14253
+ function isEnglishToken(t) {
14254
+ return /^[a-zA-Z0-9_-]+$/.test(t);
14255
+ }
14256
+ function titleHit(queryTokens, titleLower) {
14257
+ if (typeof titleLower !== "string" || titleLower.length === 0)
14258
+ return 0;
14259
+ let hits = 0;
14260
+ for (const t of queryTokens) {
14261
+ if (typeof t !== "string" || t.length === 0)
14262
+ continue;
14263
+ const tLower = t.toLowerCase();
14264
+ if (isEnglishToken(tLower)) {
14265
+ const re = new RegExp(`\\b${escapeRegExp2(tLower)}\\b`);
14266
+ if (re.test(titleLower))
14267
+ hits++;
14268
+ } else {
14269
+ if (tLower.length >= 2 && titleLower.includes(tLower))
14270
+ hits++;
14271
+ }
14272
+ }
14273
+ return hits;
14274
+ }
14275
+ function matchSpecs(query, index) {
14276
+ const qTokens = new Set(tokenize(query));
14277
+ if (qTokens.size === 0)
14278
+ return [];
14279
+ const scored = [];
14280
+ for (const rec of index) {
14281
+ const j = jaccard(qTokens, rec.tokens);
14282
+ const t = titleHit(qTokens, rec.titleLower);
14283
+ const score = j * 0.6 + Math.min(t, 1) * 0.4;
14284
+ if (score >= MATCH_THRESHOLD) {
14285
+ scored.push({
14286
+ slug: rec.slug,
14287
+ title: rec.title,
14288
+ score,
14289
+ schemaVersion: rec.schemaVersion,
14290
+ mtime: rec.mtime
14291
+ });
14292
+ }
14293
+ }
14294
+ scored.sort((a, b) => {
14295
+ if (b.score !== a.score)
14296
+ return b.score - a.score;
14297
+ return b.mtime - a.mtime;
14298
+ });
14299
+ return scored.slice(0, MAX_CANDIDATES);
14300
+ }
14301
+ function loadSpecs(rootDir, opts = {}) {
14302
+ const reader = opts.reader ?? defaultReader;
14303
+ const dirReader = opts.dirReader ?? defaultDirReader;
14304
+ const dirExists = opts.dirExists ?? defaultDirExists;
14305
+ const statReader = opts.statReader ?? defaultStatReader;
14306
+ const log6 = makePluginLogger(PLUGIN_NAME9);
14307
+ const specsRoot = join11(rootDir, SPECS_REL_DIR);
14308
+ const records = [];
14309
+ if (!dirExists(specsRoot)) {
14310
+ log6.info(`specs 目录不存在,plugin 将 no-op`, { specsRoot });
14311
+ return records;
14312
+ }
14313
+ let entries;
14314
+ try {
14315
+ entries = dirReader(specsRoot);
14316
+ } catch (err) {
14317
+ log6.warn(`specs 目录读取失败`, {
14318
+ specsRoot,
14319
+ error: err instanceof Error ? err.message : String(err)
14320
+ });
14321
+ return records;
14322
+ }
14323
+ for (const entry of entries) {
14324
+ if (!isValidSlug(entry)) {
14325
+ log6.info(`跳过非合法 slug 命名的条目`, { entry });
14326
+ continue;
14327
+ }
14328
+ const specDir = join11(specsRoot, entry);
14329
+ let dirStat;
14330
+ try {
14331
+ dirStat = statReader(specDir);
14332
+ } catch (err) {
14333
+ log6.warn(`spec 目录 stat 失败`, {
14334
+ specDir,
14335
+ error: err instanceof Error ? err.message : String(err)
14336
+ });
14337
+ continue;
14338
+ }
14339
+ if (!dirStat.isDirectory)
14340
+ continue;
14341
+ const handoffPath = join11(specDir, "handoff.yaml");
14342
+ let fileStat;
14343
+ try {
14344
+ fileStat = statReader(handoffPath);
14345
+ } catch {
14346
+ continue;
14347
+ }
14348
+ if (fileStat.isDirectory)
14349
+ continue;
14350
+ if (fileStat.size > MAX_HANDOFF_SIZE) {
14351
+ log6.warn(`handoff.yaml 超过 ${MAX_HANDOFF_SIZE} 字节上限,已跳过`, {
14352
+ handoffPath,
14353
+ size: fileStat.size
14354
+ });
14355
+ continue;
14356
+ }
14357
+ let rawYaml;
14358
+ try {
14359
+ rawYaml = reader(handoffPath);
14360
+ } catch (err) {
14361
+ log6.warn(`handoff.yaml 读取失败`, {
14362
+ handoffPath,
14363
+ error: err instanceof Error ? err.message : String(err)
14364
+ });
14365
+ continue;
14366
+ }
14367
+ const result = validateHandoff(rawYaml, fileStat.size);
14368
+ if (!result.ok) {
14369
+ log6.warn(`handoff.yaml 校验失败,已跳过`, {
14370
+ handoffPath,
14371
+ reason: result.reason
14372
+ });
14373
+ continue;
14374
+ }
14375
+ const handoff = result.data;
14376
+ if (handoff.slug !== entry) {
14377
+ log6.warn(`handoff.slug 与目录名不一致,以目录名为准`, {
14378
+ dir: entry,
14379
+ handoffSlug: handoff.slug
14380
+ });
14381
+ }
14382
+ const corpus = [handoff.title];
14383
+ for (const need of handoff.needs ?? []) {
14384
+ if (need.statement)
14385
+ corpus.push(need.statement);
14386
+ if (need.rationale)
14387
+ corpus.push(need.rationale);
14388
+ }
14389
+ const tokens = new Set(tokenize(corpus.join(" ")));
14390
+ records.push({
14391
+ slug: entry,
14392
+ title: handoff.title,
14393
+ schemaVersion: result.schemaVersion,
14394
+ tokens,
14395
+ titleLower: handoff.title.toLowerCase(),
14396
+ mtime: fileStat.mtimeMs,
14397
+ handoff
14398
+ });
14399
+ }
14400
+ log6.info(`specs 加载完成`, {
14401
+ specsRoot,
14402
+ loaded: records.length,
14403
+ total_entries: entries.length
14404
+ });
14405
+ return records;
14406
+ }
14407
+ function renderCandidatesNudge(matched) {
14408
+ if (matched.length === 0)
14409
+ return "";
14410
+ const lines = [];
14411
+ lines.push("────────────────────────────────────────");
14412
+ lines.push("DISCOVER SPEC CANDIDATES (auto-injected by discover-spec-suggest plugin)");
14413
+ lines.push("────────────────────────────────────────");
14414
+ lines.push("以下 spec 与用户当前需求**可能**相关(jaccard + title 加权匹配)。");
14415
+ lines.push("**必须明文跟用户确认**是否走该 spec 路径;用户否认 → 忽略本提示继续原计划。");
14416
+ lines.push("");
14417
+ for (const m of matched) {
14418
+ const scorePct = (m.score * 100).toFixed(0);
14419
+ lines.push(` - \`${m.slug}\` (${m.schemaVersion}, score ${scorePct}%): ${m.title}`);
14420
+ }
14421
+ lines.push("");
14422
+ lines.push("若用户确认走 spec:");
14423
+ lines.push(" 1. 先 `read .codeforge/specs/<slug>/PRD.md` + `read .codeforge/specs/<slug>/handoff.yaml`");
14424
+ lines.push(" 2. 派 planner / coder 时 prompt 必须含 `spec=<slug>`");
14425
+ lines.push(" 3. 若 handoff 含 pre_coding_blockers[],派 coder 时需附 `pre_ack=<PRE-id>`");
14426
+ lines.push("────────────────────────────────────────");
14427
+ const body = lines.join(`
14428
+ `);
14429
+ if (body.length <= NUDGE_MAX_LEN)
14430
+ return body;
14431
+ return body.slice(0, NUDGE_MAX_LEN - 4) + `
14432
+ …`;
14433
+ }
14434
+ var log6 = makePluginLogger(PLUGIN_NAME9);
14435
+ var discoverSpecSuggestServer = async (ctx) => {
14436
+ try {
14437
+ const loaded = loadSpecs(ctx.directory ?? process.cwd());
14438
+ specIndex.length = 0;
14439
+ specIndex.push(...loaded);
14440
+ } catch (err) {
14441
+ log6.warn(`loadSpecs 失败(plugin 将 no-op)`, {
14442
+ error: err instanceof Error ? err.message : String(err)
14443
+ });
14444
+ }
14445
+ logLifecycle(PLUGIN_NAME9, "activate", {
14446
+ directory: ctx.directory,
14447
+ specs_loaded: specIndex.length,
14448
+ target_agent: TARGET_AGENT,
14449
+ match_threshold: MATCH_THRESHOLD,
14450
+ max_candidates: MAX_CANDIDATES,
14451
+ session_cap: SESSION_CAP,
14452
+ session_ttl_ms: SESSION_TTL_MS,
14453
+ no_op: specIndex.length === 0
14454
+ });
14455
+ return {
14456
+ "chat.message": async (input, output) => {
14457
+ await safeAsync(PLUGIN_NAME9, "chat.message", async () => {
14458
+ if (specIndex.length === 0)
14459
+ return;
14460
+ const text = extractUserText(output);
14461
+ if (!text)
14462
+ return;
14463
+ const agent = input?.agent ?? "unknown";
14464
+ const sid = input?.sessionID;
14465
+ if (!sid)
14466
+ return;
14467
+ sessionMap.set(sid, { text, agent, ts: Date.now() });
14468
+ pruneIfOversize();
14469
+ });
14470
+ },
14471
+ "experimental.chat.system.transform": async (input, output) => {
14472
+ await safeAsync(PLUGIN_NAME9, "experimental.chat.system.transform", async () => {
14473
+ if (specIndex.length === 0)
14474
+ return;
14475
+ if (!output || !Array.isArray(output.system))
14476
+ return;
14477
+ const sid = input?.sessionID;
14478
+ if (!sid)
14479
+ return;
14480
+ const entry = sessionMap.get(sid);
14481
+ if (!entry)
14482
+ return;
14483
+ if (isExpired(entry)) {
14484
+ sessionMap.delete(sid);
14485
+ return;
14486
+ }
14487
+ if (entry.agent !== TARGET_AGENT)
14488
+ return;
14489
+ if (!entry.text)
14490
+ return;
14491
+ const matched = matchSpecs(entry.text, specIndex);
14492
+ if (matched.length === 0)
14493
+ return;
14494
+ const nudge = renderCandidatesNudge(matched);
14495
+ if (!nudge)
14496
+ return;
14497
+ output.system.push(nudge);
14498
+ safeWriteLog(PLUGIN_NAME9, {
14499
+ hook: "experimental.chat.system.transform",
14500
+ sessionID: sid,
14501
+ agent: entry.agent,
14502
+ matched_slugs: matched.map((m) => m.slug),
14503
+ matched_count: matched.length,
14504
+ injected_chars: nudge.length
14505
+ });
14506
+ });
14507
+ }
14508
+ };
14509
+ };
14510
+ var handler9 = discoverSpecSuggestServer;
14511
+
14011
14512
  // plugins/kh-auto-context.ts
14012
14513
  init_kh_client();
14013
14514
 
@@ -14031,16 +14532,16 @@ class SessionScopedCache {
14031
14532
  const { client, sessionId, query, limit, timeoutMs } = args;
14032
14533
  const queryHash = this.hashQuery(sessionId, query);
14033
14534
  const inflightKey = `${sessionId}::${queryHash}`;
14034
- const sessionMap = this.cache.get(sessionId);
14035
- if (sessionMap) {
14036
- const entry = sessionMap.get(queryHash);
14535
+ const sessionMap2 = this.cache.get(sessionId);
14536
+ if (sessionMap2) {
14537
+ const entry = sessionMap2.get(queryHash);
14037
14538
  if (entry && this.now() - entry.cachedAt < this.ttlMs) {
14038
- sessionMap.delete(queryHash);
14039
- sessionMap.set(queryHash, entry);
14539
+ sessionMap2.delete(queryHash);
14540
+ sessionMap2.set(queryHash, entry);
14040
14541
  return entry.result;
14041
14542
  }
14042
14543
  if (entry)
14043
- sessionMap.delete(queryHash);
14544
+ sessionMap2.delete(queryHash);
14044
14545
  }
14045
14546
  const pending = this.inflight.get(inflightKey);
14046
14547
  if (pending)
@@ -14128,18 +14629,18 @@ class SessionScopedCache {
14128
14629
  }
14129
14630
  }
14130
14631
  writeCache(sessionId, queryHash, query, result) {
14131
- let sessionMap = this.cache.get(sessionId);
14132
- if (!sessionMap) {
14133
- sessionMap = new Map;
14134
- this.cache.set(sessionId, sessionMap);
14135
- }
14136
- sessionMap.delete(queryHash);
14137
- sessionMap.set(queryHash, { query, result, cachedAt: this.now() });
14138
- while (sessionMap.size > this.maxPerSession) {
14139
- const oldest = sessionMap.keys().next().value;
14632
+ let sessionMap2 = this.cache.get(sessionId);
14633
+ if (!sessionMap2) {
14634
+ sessionMap2 = new Map;
14635
+ this.cache.set(sessionId, sessionMap2);
14636
+ }
14637
+ sessionMap2.delete(queryHash);
14638
+ sessionMap2.set(queryHash, { query, result, cachedAt: this.now() });
14639
+ while (sessionMap2.size > this.maxPerSession) {
14640
+ const oldest = sessionMap2.keys().next().value;
14140
14641
  if (oldest === undefined)
14141
14642
  break;
14142
- sessionMap.delete(oldest);
14643
+ sessionMap2.delete(oldest);
14143
14644
  }
14144
14645
  }
14145
14646
  }
@@ -14147,7 +14648,7 @@ var sharedKhCache = new SessionScopedCache;
14147
14648
 
14148
14649
  // plugins/kh-auto-context.ts
14149
14650
  init_opencode_plugin_helpers();
14150
- var PLUGIN_NAME9 = "kh-auto-context";
14651
+ var PLUGIN_NAME10 = "kh-auto-context";
14151
14652
  var INJECTION_MODE = "observe-only";
14152
14653
  function resolveInjectionMode(client) {
14153
14654
  return client.hasTransport() ? "system-injected" : "observe-only";
@@ -14244,9 +14745,9 @@ var CODEFORGE_CONSTRAINTS = [
14244
14745
  priority: 8
14245
14746
  }
14246
14747
  ];
14247
- async function seedConstraints(client, log6) {
14748
+ async function seedConstraints(client, log7) {
14248
14749
  if (!client.hasTransport()) {
14249
- log6?.debug?.(`[${PLUGIN_NAME9}] seedConstraints: transport 不可用,跳过`, {});
14750
+ log7?.debug?.(`[${PLUGIN_NAME10}] seedConstraints: transport 不可用,跳过`, {});
14250
14751
  return { ok: false, reason: "transport_unavailable" };
14251
14752
  }
14252
14753
  try {
@@ -14260,7 +14761,7 @@ async function seedConstraints(client, log6) {
14260
14761
  });
14261
14762
  if (result && typeof result === "object" && "ok" in result && result.ok === false) {
14262
14763
  const r = result;
14263
- log6?.warn(`[${PLUGIN_NAME9}] seedConstraints 降级`, {
14764
+ log7?.warn(`[${PLUGIN_NAME10}] seedConstraints 降级`, {
14264
14765
  reason: r.reason,
14265
14766
  message: r.message
14266
14767
  });
@@ -14270,15 +14771,15 @@ async function seedConstraints(client, log6) {
14270
14771
  message: r.message
14271
14772
  };
14272
14773
  }
14273
- log6?.info(`[${PLUGIN_NAME9}] seedConstraints: 已写入 ${CODEFORGE_CONSTRAINTS.length} 条 constraints`, { count: CODEFORGE_CONSTRAINTS.length });
14774
+ log7?.info(`[${PLUGIN_NAME10}] seedConstraints: 已写入 ${CODEFORGE_CONSTRAINTS.length} 条 constraints`, { count: CODEFORGE_CONSTRAINTS.length });
14274
14775
  return { ok: true, itemsWritten: CODEFORGE_CONSTRAINTS.length };
14275
14776
  } catch (err) {
14276
14777
  const message = err instanceof Error ? err.message : String(err);
14277
- log6?.warn(`[${PLUGIN_NAME9}] seedConstraints 失败(已静默)`, { error: message });
14778
+ log7?.warn(`[${PLUGIN_NAME10}] seedConstraints 失败(已静默)`, { error: message });
14278
14779
  return { ok: false, reason: "exception", message };
14279
14780
  }
14280
14781
  }
14281
- function createSystemInjectedHook(client, sessionId, log6) {
14782
+ function createSystemInjectedHook(client, sessionId, log7) {
14282
14783
  const section = `topic:auto-context-${sessionId ?? "global"}`;
14283
14784
  return async (markdown) => {
14284
14785
  try {
@@ -14289,7 +14790,7 @@ function createSystemInjectedHook(client, sessionId, log6) {
14289
14790
  });
14290
14791
  if (result && typeof result === "object" && "ok" in result && result.ok === false) {
14291
14792
  const r = result;
14292
- log6?.warn(`[${PLUGIN_NAME9}] system-injected 降级到 observe-only:${r.reason ?? "unknown"}`, {
14793
+ log7?.warn(`[${PLUGIN_NAME10}] system-injected 降级到 observe-only:${r.reason ?? "unknown"}`, {
14293
14794
  sessionId,
14294
14795
  section,
14295
14796
  preview: markdown.slice(0, 200),
@@ -14298,12 +14799,12 @@ function createSystemInjectedHook(client, sessionId, log6) {
14298
14799
  });
14299
14800
  return;
14300
14801
  }
14301
- log6?.info(`[${PLUGIN_NAME9}] system-injected: 写入 KH working_memory 成功 (${markdown.length} chars)`, {
14802
+ log7?.info(`[${PLUGIN_NAME10}] system-injected: 写入 KH working_memory 成功 (${markdown.length} chars)`, {
14302
14803
  sessionId,
14303
14804
  section
14304
14805
  });
14305
14806
  } catch (err) {
14306
- log6?.warn(`[${PLUGIN_NAME9}] system-injected 抛异常,降级到 observe-only`, {
14807
+ log7?.warn(`[${PLUGIN_NAME10}] system-injected 抛异常,降级到 observe-only`, {
14307
14808
  sessionId,
14308
14809
  section,
14309
14810
  preview: markdown.slice(0, 200),
@@ -14320,7 +14821,7 @@ function inflightKey(sessionId, query) {
14320
14821
  async function runKhSearchAndInject(args) {
14321
14822
  const { query, ctx, opts, mode } = args;
14322
14823
  const cfg = opts.config ?? DEFAULT_CONFIG5;
14323
- const log6 = ctx.log;
14824
+ const log7 = ctx.log;
14324
14825
  const startedAt = Date.now();
14325
14826
  const racer = opts.scheduler?.raceTimeout ?? (async (p, ms) => {
14326
14827
  let timer = null;
@@ -14346,7 +14847,7 @@ async function runKhSearchAndInject(args) {
14346
14847
  });
14347
14848
  result = await racer(searchPromise, cfg.timeoutMs);
14348
14849
  } catch (err) {
14349
- log6?.warn(`[${PLUGIN_NAME9}] client.search threw (sync or async), return null`, {
14850
+ log7?.warn(`[${PLUGIN_NAME10}] client.search threw (sync or async), return null`, {
14350
14851
  query,
14351
14852
  elapsedMs: Date.now() - startedAt,
14352
14853
  sessionId: ctx.sessionId,
@@ -14356,7 +14857,7 @@ async function runKhSearchAndInject(args) {
14356
14857
  return null;
14357
14858
  }
14358
14859
  if (result === "__timeout__") {
14359
- log6?.warn(`[${PLUGIN_NAME9}] timeout`, {
14860
+ log7?.warn(`[${PLUGIN_NAME10}] timeout`, {
14360
14861
  query,
14361
14862
  ms: cfg.timeoutMs,
14362
14863
  elapsedMs: Date.now() - startedAt,
@@ -14366,7 +14867,7 @@ async function runKhSearchAndInject(args) {
14366
14867
  return null;
14367
14868
  }
14368
14869
  if (!result.ok) {
14369
- log6?.warn(`[${PLUGIN_NAME9}] kh degraded`, {
14870
+ log7?.warn(`[${PLUGIN_NAME10}] kh degraded`, {
14370
14871
  reason: result.reason,
14371
14872
  query,
14372
14873
  elapsedMs: Date.now() - startedAt,
@@ -14378,7 +14879,7 @@ async function runKhSearchAndInject(args) {
14378
14879
  const filtered = result.insights.filter((i) => (i.confidence ?? 0) >= cfg.minConfidence);
14379
14880
  if (filtered.length === 0) {
14380
14881
  opts.cache.record(query, []);
14381
- log6?.debug?.(`[${PLUGIN_NAME9}] no candidate above threshold`, {
14882
+ log7?.debug?.(`[${PLUGIN_NAME10}] no candidate above threshold`, {
14382
14883
  query,
14383
14884
  rawCount: result.insights.length,
14384
14885
  elapsedMs: Date.now() - startedAt,
@@ -14391,7 +14892,7 @@ async function runKhSearchAndInject(args) {
14391
14892
  try {
14392
14893
  await ctx.injectContext(payload.markdown);
14393
14894
  } catch (err) {
14394
- log6?.warn(`[${PLUGIN_NAME9}] injectContext threw`, {
14895
+ log7?.warn(`[${PLUGIN_NAME10}] injectContext threw`, {
14395
14896
  error: err instanceof Error ? err.message : String(err),
14396
14897
  query,
14397
14898
  sessionId: ctx.sessionId
@@ -14399,7 +14900,7 @@ async function runKhSearchAndInject(args) {
14399
14900
  }
14400
14901
  }
14401
14902
  opts.cache.record(query, filtered);
14402
- log6?.info(`[${PLUGIN_NAME9}] inject complete (${mode})`, {
14903
+ log7?.info(`[${PLUGIN_NAME10}] inject complete (${mode})`, {
14403
14904
  query,
14404
14905
  mode,
14405
14906
  candidateCount: filtered.length,
@@ -14412,21 +14913,21 @@ async function handleMessage2(raw, opts) {
14412
14913
  const cfg = opts.config ?? DEFAULT_CONFIG5;
14413
14914
  const mode = opts.mode ?? INJECTION_MODE;
14414
14915
  const ctx = raw ?? {};
14415
- const log6 = ctx.log;
14916
+ const log7 = ctx.log;
14416
14917
  const text = (ctx.content ?? "").trim();
14417
14918
  if (!shouldInject(text, cfg)) {
14418
- log6?.debug?.(`[${PLUGIN_NAME9}] skip (filter)`, { textLen: text.length });
14919
+ log7?.debug?.(`[${PLUGIN_NAME10}] skip (filter)`, { textLen: text.length });
14419
14920
  return;
14420
14921
  }
14421
14922
  const query = extractQuery(text);
14422
14923
  if (!query)
14423
14924
  return;
14424
14925
  if (opts.cache.shouldSkip(query)) {
14425
- log6?.debug?.(`[${PLUGIN_NAME9}] cache hit, skip`, { query });
14926
+ log7?.debug?.(`[${PLUGIN_NAME10}] cache hit, skip`, { query });
14426
14927
  return;
14427
14928
  }
14428
14929
  if (inflight2.size >= INFLIGHT_CAP) {
14429
- log6?.warn(`[${PLUGIN_NAME9}] inflight cap reached, skip`, {
14930
+ log7?.warn(`[${PLUGIN_NAME10}] inflight cap reached, skip`, {
14430
14931
  query,
14431
14932
  inflightSize: inflight2.size,
14432
14933
  cap: INFLIGHT_CAP,
@@ -14437,7 +14938,7 @@ async function handleMessage2(raw, opts) {
14437
14938
  const key = inflightKey(ctx.sessionId, query);
14438
14939
  inflight2.add(key);
14439
14940
  runKhSearchAndInject({ query, ctx, opts, mode }).catch((err) => {
14440
- log6?.warn(`[${PLUGIN_NAME9}] runKhSearchAndInject 顶层兜底捕获`, {
14941
+ log7?.warn(`[${PLUGIN_NAME10}] runKhSearchAndInject 顶层兜底捕获`, {
14441
14942
  error: err instanceof Error ? err.message : String(err),
14442
14943
  query,
14443
14944
  sessionId: ctx.sessionId
@@ -14446,32 +14947,32 @@ async function handleMessage2(raw, opts) {
14446
14947
  inflight2.delete(key);
14447
14948
  });
14448
14949
  }
14449
- logLifecycle(PLUGIN_NAME9, "import");
14950
+ logLifecycle(PLUGIN_NAME10, "import");
14450
14951
  var sharedClient2 = new KhClient;
14451
14952
  var sharedCache = new QueryCache(DEFAULT_CONFIG5.cacheTtlMs);
14452
14953
  var khAutoContextServer = async (ctx) => {
14453
- const log6 = makePluginLogger(PLUGIN_NAME9);
14954
+ const log7 = makePluginLogger(PLUGIN_NAME10);
14454
14955
  const runtimeMode = resolveInjectionMode(sharedClient2);
14455
- logLifecycle(PLUGIN_NAME9, "activate", {
14956
+ logLifecycle(PLUGIN_NAME10, "activate", {
14456
14957
  directory: ctx.directory,
14457
14958
  minConfidence: DEFAULT_CONFIG5.minConfidence,
14458
14959
  timeoutMs: DEFAULT_CONFIG5.timeoutMs,
14459
14960
  mode: runtimeMode,
14460
14961
  transport_available: sharedClient2.hasTransport()
14461
14962
  });
14462
- seedConstraints(sharedClient2, log6).catch((err) => {
14463
- log6.warn("seedConstraints 顶层兜底捕获", {
14963
+ seedConstraints(sharedClient2, log7).catch((err) => {
14964
+ log7.warn("seedConstraints 顶层兜底捕获", {
14464
14965
  error: err instanceof Error ? err.message : String(err)
14465
14966
  });
14466
14967
  });
14467
14968
  return {
14468
14969
  "chat.message": async (input, output) => {
14469
- await safeAsync(PLUGIN_NAME9, "chat.message", async () => {
14970
+ await safeAsync(PLUGIN_NAME10, "chat.message", async () => {
14470
14971
  const text = extractUserText(output);
14471
14972
  if (!text)
14472
14973
  return;
14473
- const injectContext = runtimeMode === "system-injected" ? createSystemInjectedHook(sharedClient2, input.sessionID, log6) : async (markdown) => {
14474
- log6.info(`KH context candidate (${markdown.length} chars)`, {
14974
+ const injectContext = runtimeMode === "system-injected" ? createSystemInjectedHook(sharedClient2, input.sessionID, log7) : async (markdown) => {
14975
+ log7.info(`KH context candidate (${markdown.length} chars)`, {
14475
14976
  sessionID: input.sessionID,
14476
14977
  mode: runtimeMode,
14477
14978
  preview: markdown.slice(0, 200)
@@ -14481,12 +14982,12 @@ var khAutoContextServer = async (ctx) => {
14481
14982
  content: text,
14482
14983
  sessionId: input.sessionID,
14483
14984
  injectContext,
14484
- log: log6
14985
+ log: log7
14485
14986
  }, { client: sharedClient2, cache: sharedCache, mode: runtimeMode });
14486
14987
  });
14487
14988
  },
14488
14989
  event: async ({ event }) => {
14489
- await safeAsync(PLUGIN_NAME9, "event", async () => {
14990
+ await safeAsync(PLUGIN_NAME10, "event", async () => {
14490
14991
  const e = event;
14491
14992
  if (e.type !== "session.idle")
14492
14993
  return;
@@ -14495,14 +14996,14 @@ var khAutoContextServer = async (ctx) => {
14495
14996
  if (typeof sid !== "string" || !sid)
14496
14997
  return;
14497
14998
  sharedKhCache.onSessionEnd(sid);
14498
- log6.debug?.(`[${PLUGIN_NAME9}] session.idle: cleared shared cache`, {
14999
+ log7.debug?.(`[${PLUGIN_NAME10}] session.idle: cleared shared cache`, {
14499
15000
  sessionID: sid
14500
15001
  });
14501
15002
  });
14502
15003
  }
14503
15004
  };
14504
15005
  };
14505
- var handler9 = khAutoContextServer;
15006
+ var handler10 = khAutoContextServer;
14506
15007
 
14507
15008
  // plugins/kh-reminder.ts
14508
15009
  init_opencode_plugin_helpers();
@@ -14623,8 +15124,8 @@ async function condense(input, opts = {}) {
14623
15124
  }
14624
15125
 
14625
15126
  // plugins/kh-reminder.ts
14626
- var PLUGIN_NAME10 = "kh-reminder";
14627
- logLifecycle(PLUGIN_NAME10, "import", {});
15127
+ var PLUGIN_NAME11 = "kh-reminder";
15128
+ logLifecycle(PLUGIN_NAME11, "import", {});
14628
15129
  var TRIGGER_WORDS_ZH = [
14629
15130
  "怎么",
14630
15131
  "怎样",
@@ -14770,7 +15271,7 @@ function evaluate(input) {
14770
15271
  lastTriggerAt.set(sessionId, now);
14771
15272
  return { triggered: true, reason, sessionId };
14772
15273
  }
14773
- function handleObserve(raw, log6) {
15274
+ function handleObserve(raw, log7) {
14774
15275
  const ctx = raw ?? {};
14775
15276
  if (!Array.isArray(ctx.messages))
14776
15277
  return null;
@@ -14778,7 +15279,7 @@ function handleObserve(raw, log6) {
14778
15279
  const result = evaluate(ctx);
14779
15280
  if (result.triggered && result.reason) {
14780
15281
  const reasonStr = formatReason(result.reason);
14781
- log6?.info(`[${PLUGIN_NAME10}] ⚡ KH 提醒(observe-only) · session=${result.sessionId} · ${reasonStr}`, {
15282
+ log7?.info(`[${PLUGIN_NAME11}] ⚡ KH 提醒(observe-only) · session=${result.sessionId} · ${reasonStr}`, {
14782
15283
  sessionId: result.sessionId,
14783
15284
  reason: result.reason,
14784
15285
  suggestion: "调用 smart_search 查项目历史;完成后用 save_chat_insight 沉淀"
@@ -14786,7 +15287,7 @@ function handleObserve(raw, log6) {
14786
15287
  }
14787
15288
  return result;
14788
15289
  } catch (err) {
14789
- log6?.warn(`[${PLUGIN_NAME10}] evaluate 异常(已隔离)`, {
15290
+ log7?.warn(`[${PLUGIN_NAME11}] evaluate 异常(已隔离)`, {
14790
15291
  error: err instanceof Error ? err.message : String(err)
14791
15292
  });
14792
15293
  return null;
@@ -14802,9 +15303,9 @@ function formatReason(r) {
14802
15303
  return `no-search-in-recent-rounds (last ${r.rounds})`;
14803
15304
  }
14804
15305
  }
14805
- var log6 = makePluginLogger(PLUGIN_NAME10);
15306
+ var log7 = makePluginLogger(PLUGIN_NAME11);
14806
15307
  var khReminderServer = async (ctx) => {
14807
- logLifecycle(PLUGIN_NAME10, "activate", {
15308
+ logLifecycle(PLUGIN_NAME11, "activate", {
14808
15309
  directory: ctx.directory,
14809
15310
  threshold: DEFAULT_THRESHOLD,
14810
15311
  cooldown_ms: COOLDOWN_MS,
@@ -14812,7 +15313,7 @@ var khReminderServer = async (ctx) => {
14812
15313
  });
14813
15314
  return {
14814
15315
  "experimental.chat.messages.transform": async (_input, output) => {
14815
- await safeAsync(PLUGIN_NAME10, "experimental.chat.messages.transform", async () => {
15316
+ await safeAsync(PLUGIN_NAME11, "experimental.chat.messages.transform", async () => {
14816
15317
  const list = output.messages;
14817
15318
  if (!Array.isArray(list) || list.length === 0)
14818
15319
  return;
@@ -14828,10 +15329,10 @@ var khReminderServer = async (ctx) => {
14828
15329
  `);
14829
15330
  return { role, content };
14830
15331
  });
14831
- const result = handleObserve({ messages: flat, sessionId }, log6);
15332
+ const result = handleObserve({ messages: flat, sessionId }, log7);
14832
15333
  if (!result)
14833
15334
  return;
14834
- safeWriteLog(PLUGIN_NAME10, {
15335
+ safeWriteLog(PLUGIN_NAME11, {
14835
15336
  hook: "experimental.chat.messages.transform",
14836
15337
  mode: "observe-only",
14837
15338
  sessionId: result.sessionId,
@@ -14844,7 +15345,7 @@ var khReminderServer = async (ctx) => {
14844
15345
  }
14845
15346
  };
14846
15347
  };
14847
- var handler10 = khReminderServer;
15348
+ var handler11 = khReminderServer;
14848
15349
 
14849
15350
  // lib/memories.ts
14850
15351
  import { promises as fs9 } from "node:fs";
@@ -14965,13 +15466,13 @@ async function recallMemories(query, opts, cfgRaw) {
14965
15466
  }
14966
15467
  }
14967
15468
  const all = await listMemories({ scope: opts.scope, project: opts.project ?? cfg.projectName }, cfgRaw);
14968
- const tokens = tokenize(query);
15469
+ const tokens = tokenize2(query);
14969
15470
  if (tokens.length === 0)
14970
15471
  return [];
14971
15472
  const now = cfg.now();
14972
15473
  const scored = [];
14973
15474
  for (const m of all) {
14974
- const sim = bagOfWordsScore(tokens, tokenize(m.content + " " + (m.tags ?? []).join(" ")));
15475
+ const sim = bagOfWordsScore(tokens, tokenize2(m.content + " " + (m.tags ?? []).join(" ")));
14975
15476
  if (sim <= 0)
14976
15477
  continue;
14977
15478
  const age = Math.max(0, now - m.updated_at);
@@ -15021,7 +15522,7 @@ ${lines.join(`
15021
15522
  recalled: recalled.length
15022
15523
  };
15023
15524
  }
15024
- function tokenize(s) {
15525
+ function tokenize2(s) {
15025
15526
  return s.toLowerCase().split(/[^a-z0-9_\u4e00-\u9fa5]+/u).filter((t) => t.length >= 2);
15026
15527
  }
15027
15528
  function bagOfWordsScore(query, doc) {
@@ -15046,7 +15547,7 @@ function bagOfWordsScore(query, doc) {
15046
15547
 
15047
15548
  // plugins/memories-context.ts
15048
15549
  init_opencode_plugin_helpers();
15049
- var PLUGIN_NAME11 = "memories-context";
15550
+ var PLUGIN_NAME12 = "memories-context";
15050
15551
  var INJECTION_MODE2 = "observe-only";
15051
15552
  var DEFAULT_CONFIG6 = {
15052
15553
  minTextLength: 8,
@@ -15135,23 +15636,23 @@ async function handleMessage3(raw, opts) {
15135
15636
  const cfg = opts.cfg ?? DEFAULT_CONFIG6;
15136
15637
  const cache2 = opts.cache ?? new QueryCache2(cfg.cacheTtlMs);
15137
15638
  const ctx = raw ?? {};
15138
- const log7 = ctx.log;
15639
+ const log8 = ctx.log;
15139
15640
  const text = (ctx.content ?? "").trim();
15140
15641
  if (!text)
15141
15642
  return { kind: "noop", reason: "empty", mode: INJECTION_MODE2 };
15142
15643
  const dir = parseDirective(text);
15143
15644
  if (dir.kind !== "none") {
15144
- return await handleDirective(dir, ctx, opts.memCfg, log7);
15645
+ return await handleDirective(dir, ctx, opts.memCfg, log8);
15145
15646
  }
15146
15647
  if (!shouldRecall(text, cfg)) {
15147
- log7?.debug?.(`[${PLUGIN_NAME11}] skip (filter)`, { textLen: text.length });
15648
+ log8?.debug?.(`[${PLUGIN_NAME12}] skip (filter)`, { textLen: text.length });
15148
15649
  return { kind: "noop", reason: "filtered", mode: INJECTION_MODE2 };
15149
15650
  }
15150
15651
  const query = extractQuery2(text);
15151
15652
  if (!query)
15152
15653
  return { kind: "noop", reason: "empty_query", mode: INJECTION_MODE2 };
15153
15654
  if (cache2.shouldSkip(query)) {
15154
- log7?.debug?.(`[${PLUGIN_NAME11}] cache hit`, { query });
15655
+ log8?.debug?.(`[${PLUGIN_NAME12}] cache hit`, { query });
15155
15656
  return { kind: "noop", reason: "cache", mode: INJECTION_MODE2 };
15156
15657
  }
15157
15658
  const racer = opts.scheduler?.raceTimeout ?? (async (p, ms) => {
@@ -15176,7 +15677,7 @@ async function handleMessage3(raw, opts) {
15176
15677
  }, opts.memCfg);
15177
15678
  const result = await racer(injectPromise, cfg.timeoutMs);
15178
15679
  if (result === "__timeout__") {
15179
- log7?.warn(`[${PLUGIN_NAME11}] timeout`, { query, ms: cfg.timeoutMs });
15680
+ log8?.warn(`[${PLUGIN_NAME12}] timeout`, { query, ms: cfg.timeoutMs });
15180
15681
  cache2.record(query, 0);
15181
15682
  return { kind: "noop", reason: "timeout", mode: INJECTION_MODE2 };
15182
15683
  }
@@ -15188,29 +15689,29 @@ async function handleMessage3(raw, opts) {
15188
15689
  try {
15189
15690
  await ctx.injectContext(result.text);
15190
15691
  } catch (err) {
15191
- log7?.warn(`[${PLUGIN_NAME11}] injectContext threw`, {
15692
+ log8?.warn(`[${PLUGIN_NAME12}] injectContext threw`, {
15192
15693
  error: err instanceof Error ? err.message : String(err)
15193
15694
  });
15194
15695
  }
15195
15696
  }
15196
15697
  cache2.record(query, result.recalled);
15197
- log7?.info(`[${PLUGIN_NAME11}] observe-only: ${result.used}/${result.recalled} memories ready`, { query, mode: INJECTION_MODE2 });
15698
+ log8?.info(`[${PLUGIN_NAME12}] observe-only: ${result.used}/${result.recalled} memories ready`, { query, mode: INJECTION_MODE2 });
15198
15699
  return { kind: "injected", payload: result, mode: INJECTION_MODE2 };
15199
15700
  }
15200
- async function handleDirective(dir, ctx, memCfg, log7) {
15701
+ async function handleDirective(dir, ctx, memCfg, log8) {
15201
15702
  if (dir.kind === "remember") {
15202
15703
  const r = await addMemory({ scope: dir.scope, content: dir.content, source: "directive" }, memCfg);
15203
15704
  if (r.ok) {
15204
15705
  const target = r.written_to === "kh" ? "KH" : "本地";
15205
15706
  await safeReply(ctx, `\uD83E\uDDE0 已记住(${dir.scope} / ${target},id=${r.id})`);
15206
- log7?.info(`[${PLUGIN_NAME11}] /remember ok`, {
15707
+ log8?.info(`[${PLUGIN_NAME12}] /remember ok`, {
15207
15708
  scope: dir.scope,
15208
15709
  id: r.id,
15209
15710
  mode: INJECTION_MODE2
15210
15711
  });
15211
15712
  } else {
15212
15713
  await safeReply(ctx, `❌ 记忆失败:${r.error ?? "unknown"}`);
15213
- log7?.warn(`[${PLUGIN_NAME11}] /remember failed`, { error: r.error });
15714
+ log8?.warn(`[${PLUGIN_NAME12}] /remember failed`, { error: r.error });
15214
15715
  }
15215
15716
  return { kind: "remembered", result: r, mode: INJECTION_MODE2 };
15216
15717
  }
@@ -15241,22 +15742,22 @@ async function safeReply(ctx, text) {
15241
15742
  await ctx.reply(text);
15242
15743
  } catch {}
15243
15744
  }
15244
- logLifecycle(PLUGIN_NAME11, "import");
15745
+ logLifecycle(PLUGIN_NAME12, "import");
15245
15746
  var sharedCache2 = new QueryCache2(DEFAULT_CONFIG6.cacheTtlMs);
15246
15747
  function buildMemCfg(directory) {
15247
15748
  return { projectRoot: directory };
15248
15749
  }
15249
15750
  var memoriesContextServer = async (ctx) => {
15250
- const log7 = makePluginLogger(PLUGIN_NAME11);
15751
+ const log8 = makePluginLogger(PLUGIN_NAME12);
15251
15752
  const memCfg = buildMemCfg(ctx.directory);
15252
- logLifecycle(PLUGIN_NAME11, "activate", {
15753
+ logLifecycle(PLUGIN_NAME12, "activate", {
15253
15754
  directory: ctx.directory,
15254
15755
  projectRoot: memCfg.projectRoot,
15255
15756
  mode: INJECTION_MODE2
15256
15757
  });
15257
15758
  return {
15258
15759
  "chat.message": async (input, output) => {
15259
- await safeAsync(PLUGIN_NAME11, "chat.message", async () => {
15760
+ await safeAsync(PLUGIN_NAME12, "chat.message", async () => {
15260
15761
  const text = extractUserText(output);
15261
15762
  if (!text)
15262
15763
  return;
@@ -15264,36 +15765,36 @@ var memoriesContextServer = async (ctx) => {
15264
15765
  content: text,
15265
15766
  sessionId: input.sessionID,
15266
15767
  injectContext: async (markdown) => {
15267
- log7.info(`memories context candidate (${markdown.length} chars)`, {
15768
+ log8.info(`memories context candidate (${markdown.length} chars)`, {
15268
15769
  sessionID: input.sessionID,
15269
15770
  mode: INJECTION_MODE2,
15270
15771
  preview: markdown.slice(0, 200)
15271
15772
  });
15272
15773
  },
15273
15774
  reply: async (msg) => {
15274
- log7.info(`directive reply (observe-only): ${msg}`, {
15775
+ log8.info(`directive reply (observe-only): ${msg}`, {
15275
15776
  sessionID: input.sessionID,
15276
15777
  mode: INJECTION_MODE2
15277
15778
  });
15278
15779
  },
15279
- log: log7
15780
+ log: log8
15280
15781
  }, { cache: sharedCache2, memCfg });
15281
15782
  });
15282
15783
  }
15283
15784
  };
15284
15785
  };
15285
- var handler11 = memoriesContextServer;
15786
+ var handler12 = memoriesContextServer;
15286
15787
 
15287
15788
  // plugins/model-fallback.ts
15288
15789
  init_opencode_plugin_helpers();
15289
- var PLUGIN_NAME12 = "model-fallback";
15790
+ var PLUGIN_NAME13 = "model-fallback";
15290
15791
  var state2 = {
15291
15792
  config: null,
15292
15793
  configPath: null,
15293
15794
  warnings: [],
15294
15795
  error: null
15295
15796
  };
15296
- logLifecycle(PLUGIN_NAME12, "import");
15797
+ logLifecycle(PLUGIN_NAME13, "import");
15297
15798
  function loadOnce(root) {
15298
15799
  if (state2.config !== null || state2.error !== null)
15299
15800
  return;
@@ -15303,11 +15804,11 @@ function loadOnce(root) {
15303
15804
  state2.configPath = r.path ?? null;
15304
15805
  state2.warnings = r.warnings;
15305
15806
  if (r.warnings.length > 0) {
15306
- safeWriteLog(PLUGIN_NAME12, { phase: "load.warnings", warnings: r.warnings });
15807
+ safeWriteLog(PLUGIN_NAME13, { phase: "load.warnings", warnings: r.warnings });
15307
15808
  }
15308
15809
  } else {
15309
15810
  state2.error = r.error ?? "unknown_load_error";
15310
- safeWriteLog(PLUGIN_NAME12, { phase: "load.failed", error: state2.error, path: r.path });
15811
+ safeWriteLog(PLUGIN_NAME13, { phase: "load.failed", error: state2.error, path: r.path });
15311
15812
  }
15312
15813
  }
15313
15814
  var MODEL_ERR_PATTERNS = [
@@ -15352,9 +15853,9 @@ fallback 链已用尽:${meta.chain.join(" → ")}`;
15352
15853
  完整链:${meta.chain.join(" → ")}`;
15353
15854
  }
15354
15855
  var modelFallbackServer = async (ctx) => {
15355
- const log7 = makePluginLogger(PLUGIN_NAME12);
15856
+ const log8 = makePluginLogger(PLUGIN_NAME13);
15356
15857
  loadOnce(ctx.directory ?? process.cwd());
15357
- logLifecycle(PLUGIN_NAME12, "activate", {
15858
+ logLifecycle(PLUGIN_NAME13, "activate", {
15358
15859
  directory: ctx.directory,
15359
15860
  config_path: state2.configPath,
15360
15861
  config_loaded: state2.config !== null,
@@ -15364,7 +15865,7 @@ var modelFallbackServer = async (ctx) => {
15364
15865
  });
15365
15866
  return {
15366
15867
  "chat.params": async (input, output) => {
15367
- await safeAsync(PLUGIN_NAME12, "chat.params", async () => {
15868
+ await safeAsync(PLUGIN_NAME13, "chat.params", async () => {
15368
15869
  if (!state2.config)
15369
15870
  return;
15370
15871
  const agent = input.agent;
@@ -15385,7 +15886,7 @@ var modelFallbackServer = async (ctx) => {
15385
15886
  next: meta.next_fallback,
15386
15887
  source: meta.source
15387
15888
  };
15388
- safeWriteLog(PLUGIN_NAME12, {
15889
+ safeWriteLog(PLUGIN_NAME13, {
15389
15890
  hook: "chat.params",
15390
15891
  agent,
15391
15892
  model: currentModel,
@@ -15395,7 +15896,7 @@ var modelFallbackServer = async (ctx) => {
15395
15896
  });
15396
15897
  },
15397
15898
  event: async ({ event }) => {
15398
- await safeAsync(PLUGIN_NAME12, "event", async () => {
15899
+ await safeAsync(PLUGIN_NAME13, "event", async () => {
15399
15900
  if (!state2.config)
15400
15901
  return;
15401
15902
  const e = event;
@@ -15411,8 +15912,8 @@ var modelFallbackServer = async (ctx) => {
15411
15912
  const model = props.model ?? "unknown/unknown";
15412
15913
  const meta = buildFallbackMeta(state2.config, agent, model);
15413
15914
  const suggestion = meta ? buildSuggestion(meta, String(message)) : `⚠️ ${agent}/${model} 失败:${message}`;
15414
- log7.warn(`[${PLUGIN_NAME12}] ${suggestion}`);
15415
- safeWriteLog(PLUGIN_NAME12, {
15915
+ log8.warn(`[${PLUGIN_NAME13}] ${suggestion}`);
15916
+ safeWriteLog(PLUGIN_NAME13, {
15416
15917
  hook: "event.error",
15417
15918
  eventType: e.type,
15418
15919
  agent,
@@ -15424,12 +15925,12 @@ var modelFallbackServer = async (ctx) => {
15424
15925
  }
15425
15926
  };
15426
15927
  };
15427
- var handler12 = modelFallbackServer;
15928
+ var handler13 = modelFallbackServer;
15428
15929
 
15429
15930
  // plugins/subtask-heartbeat.ts
15430
15931
  init_opencode_plugin_helpers();
15431
- var PLUGIN_NAME13 = "subtask-heartbeat";
15432
- logLifecycle(PLUGIN_NAME13, "import", {});
15932
+ var PLUGIN_NAME14 = "subtask-heartbeat";
15933
+ logLifecycle(PLUGIN_NAME14, "import", {});
15433
15934
  var HEARTBEAT_INTERVAL_MS2 = 30000;
15434
15935
  var HEARTBEAT_DEBOUNCE_MS = 25000;
15435
15936
  var TOAST_DURATION_MS3 = 5000;
@@ -15663,9 +16164,9 @@ function normalizeVariant3(raw) {
15663
16164
  return "default";
15664
16165
  return raw;
15665
16166
  }
15666
- async function showToast3(client, payload, log7) {
16167
+ async function showToast3(client, payload, log8) {
15667
16168
  if (typeof client?.tui?.showToast !== "function") {
15668
- log7?.debug?.("tui.showToast 不可用,noop");
16169
+ log8?.debug?.("tui.showToast 不可用,noop");
15669
16170
  return false;
15670
16171
  }
15671
16172
  try {
@@ -15679,32 +16180,32 @@ async function showToast3(client, payload, log7) {
15679
16180
  });
15680
16181
  return true;
15681
16182
  } catch (err) {
15682
- log7?.warn("tui.showToast 抛错(已隔离)", {
16183
+ log8?.warn("tui.showToast 抛错(已隔离)", {
15683
16184
  error: err instanceof Error ? err.message : String(err)
15684
16185
  });
15685
16186
  return false;
15686
16187
  }
15687
16188
  }
15688
- var log7 = makePluginLogger(PLUGIN_NAME13);
16189
+ var log8 = makePluginLogger(PLUGIN_NAME14);
15689
16190
  var subtaskHeartbeatServer = async (ctx) => {
15690
- logLifecycle(PLUGIN_NAME13, "activate", {
16191
+ logLifecycle(PLUGIN_NAME14, "activate", {
15691
16192
  directory: ctx.directory,
15692
16193
  intervalMs: HEARTBEAT_INTERVAL_MS2
15693
16194
  });
15694
16195
  const client = ctx.client;
15695
16196
  const interval = setInterval(() => {
15696
- safeAsync(PLUGIN_NAME13, "interval", async () => {
16197
+ safeAsync(PLUGIN_NAME14, "interval", async () => {
15697
16198
  const swept = sweepExpiredPendingTasks();
15698
16199
  if (swept > 0) {
15699
- safeWriteLog(PLUGIN_NAME13, { hook: "interval", pending_task_swept: swept });
16200
+ safeWriteLog(PLUGIN_NAME14, { hook: "interval", pending_task_swept: swept });
15700
16201
  }
15701
16202
  const beats = pickHeartbeats();
15702
16203
  if (beats.length === 0)
15703
16204
  return;
15704
16205
  for (const r of beats) {
15705
16206
  const t = buildHeartbeatToast(r);
15706
- const sent = await showToast3(client, t, log7);
15707
- safeWriteLog(PLUGIN_NAME13, {
16207
+ const sent = await showToast3(client, t, log8);
16208
+ safeWriteLog(PLUGIN_NAME14, {
15708
16209
  hook: "interval",
15709
16210
  child: r.childID,
15710
16211
  parent: r.parentID,
@@ -15721,7 +16222,7 @@ var subtaskHeartbeatServer = async (ctx) => {
15721
16222
  }
15722
16223
  return {
15723
16224
  event: async ({ event }) => {
15724
- await safeAsync(PLUGIN_NAME13, "event", async () => {
16225
+ await safeAsync(PLUGIN_NAME14, "event", async () => {
15725
16226
  const created = extractCreatedChild(event);
15726
16227
  if (created) {
15727
16228
  const pending = dequeuePendingTask(created.parentID);
@@ -15731,7 +16232,7 @@ var subtaskHeartbeatServer = async (ctx) => {
15731
16232
  agent: pending?.agent ?? created.agent,
15732
16233
  description: pending?.description ?? null
15733
16234
  });
15734
- safeWriteLog(PLUGIN_NAME13, {
16235
+ safeWriteLog(PLUGIN_NAME14, {
15735
16236
  hook: "event",
15736
16237
  type: "session.created",
15737
16238
  child: created.childID,
@@ -15741,8 +16242,8 @@ var subtaskHeartbeatServer = async (ctx) => {
15741
16242
  description_len: record.description?.length ?? 0
15742
16243
  });
15743
16244
  const startToast = buildStartToast(record);
15744
- const sent = await showToast3(client, { ...startToast, duration: START_TOAST_DURATION_MS }, log7);
15745
- safeWriteLog(PLUGIN_NAME13, {
16245
+ const sent = await showToast3(client, { ...startToast, duration: START_TOAST_DURATION_MS }, log8);
16246
+ safeWriteLog(PLUGIN_NAME14, {
15746
16247
  hook: "event",
15747
16248
  type: "session.created.toast",
15748
16249
  child: created.childID,
@@ -15756,8 +16257,8 @@ var subtaskHeartbeatServer = async (ctx) => {
15756
16257
  const r = clearInflight2(ended.sessionID);
15757
16258
  if (r) {
15758
16259
  const t = buildEndToast(r, ended.type);
15759
- const sent = await showToast3(client, t, log7);
15760
- safeWriteLog(PLUGIN_NAME13, {
16260
+ const sent = await showToast3(client, t, log8);
16261
+ safeWriteLog(PLUGIN_NAME14, {
15761
16262
  hook: "event",
15762
16263
  type: ended.type,
15763
16264
  child: r.childID,
@@ -15773,12 +16274,12 @@ var subtaskHeartbeatServer = async (ctx) => {
15773
16274
  const isTaskTool = input?.tool === "task";
15774
16275
  if (inflight3.size === 0 && !isTaskTool)
15775
16276
  return;
15776
- await safeAsync(PLUGIN_NAME13, "tool.execute.before", async () => {
16277
+ await safeAsync(PLUGIN_NAME14, "tool.execute.before", async () => {
15777
16278
  if (!input || typeof input.sessionID !== "string" || typeof input.tool !== "string")
15778
16279
  return;
15779
16280
  if (isTaskTool) {
15780
16281
  const args = output?.args ?? null;
15781
- safeWriteLog(PLUGIN_NAME13, {
16282
+ safeWriteLog(PLUGIN_NAME14, {
15782
16283
  hook: "tool.execute.before.task",
15783
16284
  sessionID: input.sessionID,
15784
16285
  args_keys: args && typeof args === "object" ? Object.keys(args) : null,
@@ -15790,7 +16291,7 @@ var subtaskHeartbeatServer = async (ctx) => {
15790
16291
  agent: extracted.subagentType,
15791
16292
  description: extracted.description
15792
16293
  });
15793
- safeWriteLog(PLUGIN_NAME13, {
16294
+ safeWriteLog(PLUGIN_NAME14, {
15794
16295
  hook: "tool.execute.before.task.enqueued",
15795
16296
  parent: input.sessionID,
15796
16297
  agent: extracted.subagentType,
@@ -15805,12 +16306,12 @@ var subtaskHeartbeatServer = async (ctx) => {
15805
16306
  }
15806
16307
  };
15807
16308
  };
15808
- var handler13 = subtaskHeartbeatServer;
16309
+ var handler14 = subtaskHeartbeatServer;
15809
16310
 
15810
16311
  // plugins/parallel-status.ts
15811
16312
  init_opencode_plugin_helpers();
15812
- var PLUGIN_NAME14 = "parallel-status";
15813
- logLifecycle(PLUGIN_NAME14, "import");
16313
+ var PLUGIN_NAME15 = "parallel-status";
16314
+ logLifecycle(PLUGIN_NAME15, "import");
15814
16315
  var ID_MAX_LEN = 16;
15815
16316
  var ID_KEEP_LEN = 13;
15816
16317
  function shortId(s) {
@@ -15842,8 +16343,8 @@ function formatInflightMarkdown(snapshot, now = Date.now()) {
15842
16343
  `);
15843
16344
  }
15844
16345
  var parallelStatusServer = async (ctx) => {
15845
- const log8 = makePluginLogger(PLUGIN_NAME14);
15846
- logLifecycle(PLUGIN_NAME14, "activate", { directory: ctx.directory });
16346
+ const log9 = makePluginLogger(PLUGIN_NAME15);
16347
+ logLifecycle(PLUGIN_NAME15, "activate", { directory: ctx.directory });
15847
16348
  return {
15848
16349
  "command.execute.before": async (input, output) => {
15849
16350
  try {
@@ -15862,72 +16363,24 @@ var parallelStatusServer = async (ctx) => {
15862
16363
  synthetic: false
15863
16364
  });
15864
16365
  }
15865
- log8.info(`[${PLUGIN_NAME14}] 已回写 ${snapshot.length} 条 inflight`);
16366
+ log9.info(`[${PLUGIN_NAME15}] 已回写 ${snapshot.length} 条 inflight`);
15866
16367
  } catch (err) {
15867
- log8.error(`[${PLUGIN_NAME14}] command.execute.before 异常(已隔离)`, {
16368
+ log9.error(`[${PLUGIN_NAME15}] command.execute.before 异常(已隔离)`, {
15868
16369
  error: err instanceof Error ? err.message : String(err)
15869
16370
  });
15870
16371
  }
15871
16372
  }
15872
16373
  };
15873
16374
  };
15874
- var handler14 = parallelStatusServer;
16375
+ var handler15 = parallelStatusServer;
15875
16376
 
15876
16377
  // plugins/parallel-tool-nudge.ts
15877
- import { readFileSync as readFileSync4, readdirSync, statSync as statSync3 } from "node:fs";
15878
- import { join as join12 } from "node:path";
16378
+ import { readFileSync as readFileSync5, readdirSync as readdirSync2, statSync as statSync4 } from "node:fs";
16379
+ import { join as join13 } from "node:path";
15879
16380
  import { homedir as homedir6 } from "node:os";
15880
-
15881
- // node_modules/yaml/dist/index.js
15882
- var composer = require_composer();
15883
- var Document = require_Document();
15884
- var Schema = require_Schema();
15885
- var errors = require_errors();
15886
- var Alias = require_Alias();
15887
- var identity = require_identity();
15888
- var Pair = require_Pair();
15889
- var Scalar = require_Scalar();
15890
- var YAMLMap = require_YAMLMap();
15891
- var YAMLSeq = require_YAMLSeq();
15892
- var cst = require_cst();
15893
- var lexer = require_lexer();
15894
- var lineCounter = require_line_counter();
15895
- var parser = require_parser();
15896
- var publicApi = require_public_api();
15897
- var visit = require_visit();
15898
- var $Composer = composer.Composer;
15899
- var $Document = Document.Document;
15900
- var $Schema = Schema.Schema;
15901
- var $YAMLError = errors.YAMLError;
15902
- var $YAMLParseError = errors.YAMLParseError;
15903
- var $YAMLWarning = errors.YAMLWarning;
15904
- var $Alias = Alias.Alias;
15905
- var $isAlias = identity.isAlias;
15906
- var $isCollection = identity.isCollection;
15907
- var $isDocument = identity.isDocument;
15908
- var $isMap = identity.isMap;
15909
- var $isNode = identity.isNode;
15910
- var $isPair = identity.isPair;
15911
- var $isScalar = identity.isScalar;
15912
- var $isSeq = identity.isSeq;
15913
- var $Pair = Pair.Pair;
15914
- var $Scalar = Scalar.Scalar;
15915
- var $YAMLMap = YAMLMap.YAMLMap;
15916
- var $YAMLSeq = YAMLSeq.YAMLSeq;
15917
- var $Lexer = lexer.Lexer;
15918
- var $LineCounter = lineCounter.LineCounter;
15919
- var $Parser = parser.Parser;
15920
- var $parse = publicApi.parse;
15921
- var $parseAllDocuments = publicApi.parseAllDocuments;
15922
- var $parseDocument = publicApi.parseDocument;
15923
- var $stringify = publicApi.stringify;
15924
- var $visit = visit.visit;
15925
- var $visitAsync = visit.visitAsync;
15926
-
15927
- // plugins/parallel-tool-nudge.ts
15928
16381
  init_opencode_plugin_helpers();
15929
- var PLUGIN_NAME15 = "parallel-tool-nudge";
15930
- logLifecycle(PLUGIN_NAME15, "import", {});
16382
+ var PLUGIN_NAME16 = "parallel-tool-nudge";
16383
+ logLifecycle(PLUGIN_NAME16, "import", {});
15931
16384
  var PARALLEL_SAFE_TOOLS = [
15932
16385
  "read",
15933
16386
  "smart_search",
@@ -15937,17 +16390,17 @@ var PARALLEL_SAFE_TOOLS = [
15937
16390
  "get_working_memory"
15938
16391
  ];
15939
16392
  var DEFAULT_AGENT_KEY = "__default__";
15940
- var NUDGE_MAX_LEN = 800;
16393
+ var NUDGE_MAX_LEN2 = 800;
15941
16394
  var agentToolsMap = new Map;
15942
- function defaultReader(p) {
15943
- return readFileSync4(p, "utf8");
16395
+ function defaultReader2(p) {
16396
+ return readFileSync5(p, "utf8");
15944
16397
  }
15945
- function defaultDirReader(p) {
15946
- return readdirSync(p);
16398
+ function defaultDirReader2(p) {
16399
+ return readdirSync2(p);
15947
16400
  }
15948
- function defaultDirExists(p) {
16401
+ function defaultDirExists2(p) {
15949
16402
  try {
15950
- return statSync3(p).isDirectory();
16403
+ return statSync4(p).isDirectory();
15951
16404
  } catch {
15952
16405
  return false;
15953
16406
  }
@@ -15977,19 +16430,19 @@ function parseAgentFrontmatter(content) {
15977
16430
  return { name, allowedTools };
15978
16431
  }
15979
16432
  function loadAgentToolsMap(rootDir, opts = {}) {
15980
- const reader = opts.reader ?? defaultReader;
15981
- const dirReader = opts.dirReader ?? defaultDirReader;
15982
- const dirExists = opts.dirExists ?? defaultDirExists;
15983
- const homeAgentsDir = opts.homeAgentsDir ?? join12(homedir6(), ".config", "opencode", "agents");
16433
+ const reader = opts.reader ?? defaultReader2;
16434
+ const dirReader = opts.dirReader ?? defaultDirReader2;
16435
+ const dirExists = opts.dirExists ?? defaultDirExists2;
16436
+ const homeAgentsDir = opts.homeAgentsDir ?? join13(homedir6(), ".config", "opencode", "agents");
15984
16437
  const candidateDirs = [
15985
- join12(rootDir, ".codeforge", "agents"),
15986
- join12(rootDir, "agents"),
16438
+ join13(rootDir, ".codeforge", "agents"),
16439
+ join13(rootDir, "agents"),
15987
16440
  homeAgentsDir
15988
16441
  ];
15989
16442
  const result = new Map;
15990
16443
  const safeSet = new Set(PARALLEL_SAFE_TOOLS);
15991
16444
  const unionTools = new Set;
15992
- const log8 = makePluginLogger(PLUGIN_NAME15);
16445
+ const log9 = makePluginLogger(PLUGIN_NAME16);
15993
16446
  for (const dir of candidateDirs) {
15994
16447
  if (!dirExists(dir))
15995
16448
  continue;
@@ -15997,7 +16450,7 @@ function loadAgentToolsMap(rootDir, opts = {}) {
15997
16450
  try {
15998
16451
  entries = dirReader(dir);
15999
16452
  } catch (err) {
16000
- log8.warn(`agents 目录读取失败(已跳过)`, {
16453
+ log9.warn(`agents 目录读取失败(已跳过)`, {
16001
16454
  dir,
16002
16455
  error: err instanceof Error ? err.message : String(err)
16003
16456
  });
@@ -16006,12 +16459,12 @@ function loadAgentToolsMap(rootDir, opts = {}) {
16006
16459
  for (const entry of entries) {
16007
16460
  if (!entry.endsWith(".md"))
16008
16461
  continue;
16009
- const path13 = join12(dir, entry);
16462
+ const path13 = join13(dir, entry);
16010
16463
  let content;
16011
16464
  try {
16012
16465
  content = reader(path13);
16013
16466
  } catch (err) {
16014
- log8.warn(`agent.md 读取失败(已跳过)`, {
16467
+ log9.warn(`agent.md 读取失败(已跳过)`, {
16015
16468
  path: path13,
16016
16469
  error: err instanceof Error ? err.message : String(err)
16017
16470
  });
@@ -16019,7 +16472,7 @@ function loadAgentToolsMap(rootDir, opts = {}) {
16019
16472
  }
16020
16473
  const parsed = parseAgentFrontmatter(content);
16021
16474
  if (!parsed) {
16022
- log8.warn(`agent frontmatter 解析失败(已跳过)`, { path: path13 });
16475
+ log9.warn(`agent frontmatter 解析失败(已跳过)`, { path: path13 });
16023
16476
  continue;
16024
16477
  }
16025
16478
  if (result.has(parsed.name))
@@ -16040,18 +16493,18 @@ function loadAgentToolsMap(rootDir, opts = {}) {
16040
16493
  return result;
16041
16494
  }
16042
16495
  var sessionAgentMap = new Map;
16043
- var SESSION_CAP = 200;
16044
- var SESSION_TTL_MS = 24 * 60 * 60 * 1000;
16045
- function pruneIfOversize() {
16046
- while (sessionAgentMap.size > SESSION_CAP) {
16496
+ var SESSION_CAP2 = 200;
16497
+ var SESSION_TTL_MS2 = 24 * 60 * 60 * 1000;
16498
+ function pruneIfOversize2() {
16499
+ while (sessionAgentMap.size > SESSION_CAP2) {
16047
16500
  const oldestKey = sessionAgentMap.keys().next().value;
16048
16501
  if (oldestKey === undefined)
16049
16502
  break;
16050
16503
  sessionAgentMap.delete(oldestKey);
16051
16504
  }
16052
16505
  }
16053
- function isExpired(entry, now) {
16054
- return now - entry.ts > SESSION_TTL_MS;
16506
+ function isExpired2(entry, now) {
16507
+ return now - entry.ts > SESSION_TTL_MS2;
16055
16508
  }
16056
16509
  function resolveAgent(sessionID, nowFn = Date.now) {
16057
16510
  if (!sessionID)
@@ -16060,7 +16513,7 @@ function resolveAgent(sessionID, nowFn = Date.now) {
16060
16513
  if (!entry)
16061
16514
  return "unknown";
16062
16515
  const now = nowFn();
16063
- if (isExpired(entry, now)) {
16516
+ if (isExpired2(entry, now)) {
16064
16517
  sessionAgentMap.delete(sessionID);
16065
16518
  return "unknown";
16066
16519
  }
@@ -16092,12 +16545,12 @@ Heuristics:
16092
16545
 
16093
16546
  This directive is re-injected every turn — it is not optional advice.
16094
16547
  ────────────────────────────────────────`;
16095
- if (body.length <= NUDGE_MAX_LEN)
16548
+ if (body.length <= NUDGE_MAX_LEN2)
16096
16549
  return body;
16097
- return body.slice(0, NUDGE_MAX_LEN - 4) + `
16550
+ return body.slice(0, NUDGE_MAX_LEN2 - 4) + `
16098
16551
  …`;
16099
16552
  }
16100
- var log8 = makePluginLogger(PLUGIN_NAME15);
16553
+ var log9 = makePluginLogger(PLUGIN_NAME16);
16101
16554
  var parallelToolNudgeServer = async (ctx) => {
16102
16555
  try {
16103
16556
  const loaded = loadAgentToolsMap(ctx.directory ?? process.cwd());
@@ -16105,30 +16558,30 @@ var parallelToolNudgeServer = async (ctx) => {
16105
16558
  for (const [k, v] of loaded.entries())
16106
16559
  agentToolsMap.set(k, v);
16107
16560
  } catch (err) {
16108
- log8.warn(`loadAgentToolsMap 失败(plugin 将 no-op)`, {
16561
+ log9.warn(`loadAgentToolsMap 失败(plugin 将 no-op)`, {
16109
16562
  error: err instanceof Error ? err.message : String(err)
16110
16563
  });
16111
16564
  }
16112
16565
  const realAgentCount = Math.max(0, agentToolsMap.size - (agentToolsMap.has(DEFAULT_AGENT_KEY) ? 1 : 0));
16113
16566
  if (realAgentCount === 0) {
16114
16567
  agentToolsMap.clear();
16115
- log8.warn(`0 real agents loaded; plugin will be no-op for this session`, {
16568
+ log9.warn(`0 real agents loaded; plugin will be no-op for this session`, {
16116
16569
  directory: ctx.directory
16117
16570
  });
16118
16571
  }
16119
16572
  const defaultTools = agentToolsMap.get(DEFAULT_AGENT_KEY) ?? [];
16120
- logLifecycle(PLUGIN_NAME15, "activate", {
16573
+ logLifecycle(PLUGIN_NAME16, "activate", {
16121
16574
  directory: ctx.directory,
16122
16575
  real_agents_loaded: realAgentCount,
16123
16576
  default_tools_union: defaultTools,
16124
16577
  parallel_safe_whitelist: PARALLEL_SAFE_TOOLS,
16125
- session_cap: SESSION_CAP,
16126
- session_ttl_ms: SESSION_TTL_MS,
16578
+ session_cap: SESSION_CAP2,
16579
+ session_ttl_ms: SESSION_TTL_MS2,
16127
16580
  no_op: agentToolsMap.size === 0
16128
16581
  });
16129
16582
  return {
16130
16583
  "chat.params": async (input, _output) => {
16131
- await safeAsync(PLUGIN_NAME15, "chat.params", async () => {
16584
+ await safeAsync(PLUGIN_NAME16, "chat.params", async () => {
16132
16585
  if (agentToolsMap.size === 0)
16133
16586
  return;
16134
16587
  const sid = input?.sessionID;
@@ -16136,11 +16589,11 @@ var parallelToolNudgeServer = async (ctx) => {
16136
16589
  if (!sid || !agent)
16137
16590
  return;
16138
16591
  sessionAgentMap.set(sid, { agent, ts: Date.now() });
16139
- pruneIfOversize();
16592
+ pruneIfOversize2();
16140
16593
  });
16141
16594
  },
16142
16595
  "experimental.chat.system.transform": async (input, output) => {
16143
- await safeAsync(PLUGIN_NAME15, "experimental.chat.system.transform", async () => {
16596
+ await safeAsync(PLUGIN_NAME16, "experimental.chat.system.transform", async () => {
16144
16597
  if (agentToolsMap.size === 0)
16145
16598
  return;
16146
16599
  if (!output || !Array.isArray(output.system))
@@ -16150,7 +16603,7 @@ var parallelToolNudgeServer = async (ctx) => {
16150
16603
  const tools = agent !== "unknown" ? agentToolsMap.get(agent) ?? agentToolsMap.get(DEFAULT_AGENT_KEY) ?? [] : agentToolsMap.get(DEFAULT_AGENT_KEY) ?? [];
16151
16604
  const nudge = renderNudge(agent, tools);
16152
16605
  output.system.push(nudge);
16153
- safeWriteLog(PLUGIN_NAME15, {
16606
+ safeWriteLog(PLUGIN_NAME16, {
16154
16607
  hook: "experimental.chat.system.transform",
16155
16608
  sessionID: sid,
16156
16609
  agent,
@@ -16162,12 +16615,12 @@ var parallelToolNudgeServer = async (ctx) => {
16162
16615
  }
16163
16616
  };
16164
16617
  };
16165
- var handler15 = parallelToolNudgeServer;
16618
+ var handler16 = parallelToolNudgeServer;
16166
16619
 
16167
16620
  // plugins/pwsh-utf8.ts
16168
16621
  init_opencode_plugin_helpers();
16169
- var PLUGIN_NAME16 = "pwsh-utf8";
16170
- logLifecycle(PLUGIN_NAME16, "import", {});
16622
+ var PLUGIN_NAME17 = "pwsh-utf8";
16623
+ logLifecycle(PLUGIN_NAME17, "import", {});
16171
16624
  var PRELUDE = "chcp 65001 *> $null; " + "[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new(); " + "$OutputEncoding = [System.Text.UTF8Encoding]::new(); ";
16172
16625
  function prependUtf8Prelude(command) {
16173
16626
  if (typeof command !== "string")
@@ -16180,15 +16633,15 @@ function prependUtf8Prelude(command) {
16180
16633
  return command;
16181
16634
  return PRELUDE + command;
16182
16635
  }
16183
- var handler16 = async (_ctx) => {
16636
+ var handler17 = async (_ctx) => {
16184
16637
  const enabled = process.platform === "win32" && process.env.CODEFORGE_DISABLE_PWSH_UTF8 !== "1";
16185
16638
  const reason = enabled ? "win32" : process.platform !== "win32" ? "non-win32" : "disabled-by-env";
16186
- logLifecycle(PLUGIN_NAME16, "activate", { enabled, platform: process.platform, reason });
16639
+ logLifecycle(PLUGIN_NAME17, "activate", { enabled, platform: process.platform, reason });
16187
16640
  if (!enabled)
16188
16641
  return {};
16189
16642
  return {
16190
16643
  "tool.execute.before": async (input, output) => {
16191
- await safeAsync(PLUGIN_NAME16, "tool.execute.before", async () => {
16644
+ await safeAsync(PLUGIN_NAME17, "tool.execute.before", async () => {
16192
16645
  if (input.tool !== "bash")
16193
16646
  return;
16194
16647
  const args = output.args ?? {};
@@ -16197,7 +16650,7 @@ var handler16 = async (_ctx) => {
16197
16650
  if (next !== undefined && next !== original) {
16198
16651
  args["command"] = next;
16199
16652
  output.args = args;
16200
- safeWriteLog(PLUGIN_NAME16, {
16653
+ safeWriteLog(PLUGIN_NAME17, {
16201
16654
  hook: "tool.execute.before",
16202
16655
  tool: input.tool,
16203
16656
  callID: input.callID,
@@ -16530,8 +16983,8 @@ function isRecoveryWorthShowing(plan) {
16530
16983
  }
16531
16984
 
16532
16985
  // plugins/session-recovery.ts
16533
- var PLUGIN_NAME17 = "session-recovery";
16534
- logLifecycle(PLUGIN_NAME17, "import", {});
16986
+ var PLUGIN_NAME18 = "session-recovery";
16987
+ logLifecycle(PLUGIN_NAME18, "import", {});
16535
16988
  async function processSessionStart(currentSessionId, opts = {}) {
16536
16989
  if (opts.disabled) {
16537
16990
  return { ok: true, injected: false, reason: "disabled" };
@@ -16541,7 +16994,7 @@ async function processSessionStart(currentSessionId, opts = {}) {
16541
16994
  excludeIds.add(currentSessionId);
16542
16995
  const r = await scanLastSession({ ...opts, excludeIds: [...excludeIds] });
16543
16996
  if (!r.ok) {
16544
- opts.log?.warn?.(`[${PLUGIN_NAME17}] 扫描失败:${r.error}`);
16997
+ opts.log?.warn?.(`[${PLUGIN_NAME18}] 扫描失败:${r.error}`);
16545
16998
  return { ok: false, injected: false, reason: "scan_error", error: r.error };
16546
16999
  }
16547
17000
  const plan = r.plan;
@@ -16555,7 +17008,7 @@ async function processSessionStart(currentSessionId, opts = {}) {
16555
17008
  await opts.injectRecovery(injection);
16556
17009
  } catch (err) {
16557
17010
  const msg = err instanceof Error ? err.message : String(err);
16558
- opts.log?.warn?.(`[${PLUGIN_NAME17}] injectRecovery 异常:${msg}`);
17011
+ opts.log?.warn?.(`[${PLUGIN_NAME18}] injectRecovery 异常:${msg}`);
16559
17012
  return { ok: false, injected: false, plan, reason: "inject_error", error: msg };
16560
17013
  }
16561
17014
  }
@@ -16584,13 +17037,13 @@ function renderPrompt(plan) {
16584
17037
  return lines.join(`
16585
17038
  `);
16586
17039
  }
16587
- var log9 = makePluginLogger(PLUGIN_NAME17);
17040
+ var log10 = makePluginLogger(PLUGIN_NAME18);
16588
17041
  var _lastInjection = null;
16589
17042
  var sessionRecoveryServer = async (ctx) => {
16590
- logLifecycle(PLUGIN_NAME17, "activate", { directory: ctx.directory });
17043
+ logLifecycle(PLUGIN_NAME18, "activate", { directory: ctx.directory });
16591
17044
  return {
16592
17045
  event: async ({ event }) => {
16593
- await safeAsync(PLUGIN_NAME17, "event", async () => {
17046
+ await safeAsync(PLUGIN_NAME18, "event", async () => {
16594
17047
  const e = event;
16595
17048
  if (!e || typeof e.type !== "string")
16596
17049
  return;
@@ -16600,12 +17053,12 @@ var sessionRecoveryServer = async (ctx) => {
16600
17053
  const root = typeof e.properties?.["root"] === "string" ? e.properties["root"] : ctx.directory ?? process.cwd();
16601
17054
  const r = await processSessionStart(sid, {
16602
17055
  root,
16603
- log: log9,
17056
+ log: log10,
16604
17057
  injectRecovery: (inj) => {
16605
17058
  _lastInjection = inj;
16606
17059
  }
16607
17060
  });
16608
- safeWriteLog(PLUGIN_NAME17, {
17061
+ safeWriteLog(PLUGIN_NAME18, {
16609
17062
  hook: "event",
16610
17063
  type: "session.start",
16611
17064
  ok: r.ok,
@@ -16614,13 +17067,13 @@ var sessionRecoveryServer = async (ctx) => {
16614
17067
  last_session_id: r.plan?.last_session_id
16615
17068
  });
16616
17069
  if (r.injected && r.plan) {
16617
- log9.info(`[${PLUGIN_NAME17}] 注入恢复提示(last=${r.plan.last_session_id?.slice(0, 8) ?? "?"}, reason=${r.plan.reason})`);
17070
+ log10.info(`[${PLUGIN_NAME18}] 注入恢复提示(last=${r.plan.last_session_id?.slice(0, 8) ?? "?"}, reason=${r.plan.reason})`);
16618
17071
  }
16619
17072
  });
16620
17073
  }
16621
17074
  };
16622
17075
  };
16623
- var handler17 = sessionRecoveryServer;
17076
+ var handler18 = sessionRecoveryServer;
16624
17077
 
16625
17078
  // plugins/subtasks.ts
16626
17079
  import { promises as fs12 } from "node:fs";
@@ -16906,7 +17359,7 @@ function resolveMergeFns(deps) {
16906
17359
  worktreeHasChangesFn: deps.worktreeHasChanges ?? worktreeHasChanges
16907
17360
  };
16908
17361
  }
16909
- async function mergeOneAttempt(r, opts, mergeFns, log10) {
17362
+ async function mergeOneAttempt(r, opts, mergeFns, log11) {
16910
17363
  const t0 = Date.now();
16911
17364
  const root = opts.mergeRoot;
16912
17365
  const {
@@ -16988,7 +17441,7 @@ async function mergeOneAttempt(r, opts, mergeFns, log10) {
16988
17441
  await mergeAbortFn({ root });
16989
17442
  } catch (abortErr) {
16990
17443
  abortFailed = true;
16991
- log10("error", `[parallel-merge] mergeAbort 失败(仓库可能锁死)`, {
17444
+ log11("error", `[parallel-merge] mergeAbort 失败(仓库可能锁死)`, {
16992
17445
  id: r.id,
16993
17446
  error: describe4(abortErr)
16994
17447
  });
@@ -17004,13 +17457,13 @@ async function mergeOneAttempt(r, opts, mergeFns, log10) {
17004
17457
  }
17005
17458
  attempt.ok = true;
17006
17459
  attempt.durationMs = Date.now() - t0;
17007
- await safeRemoveWorktree(removeWorktreeFn, root, wt, log10, r.id);
17460
+ await safeRemoveWorktree(removeWorktreeFn, root, wt, log11, r.id);
17008
17461
  return { attempt };
17009
17462
  } else {
17010
17463
  attempt.ok = true;
17011
17464
  attempt.skippedReason = "no_changes";
17012
17465
  attempt.durationMs = Date.now() - t0;
17013
- await safeRemoveWorktree(removeWorktreeFn, root, wt, log10, r.id);
17466
+ await safeRemoveWorktree(removeWorktreeFn, root, wt, log11, r.id);
17014
17467
  return { attempt };
17015
17468
  }
17016
17469
  } catch (err) {
@@ -17024,7 +17477,7 @@ async function mergeOneAttempt(r, opts, mergeFns, log10) {
17024
17477
  }
17025
17478
  }
17026
17479
  async function mergeWorktrees(results, opts, deps) {
17027
- const log10 = deps.log ?? (() => {});
17480
+ const log11 = deps.log ?? (() => {});
17028
17481
  const root = opts.mergeRoot;
17029
17482
  const mergeFns = resolveMergeFns(deps);
17030
17483
  const report = {
@@ -17066,10 +17519,10 @@ async function mergeWorktrees(results, opts, deps) {
17066
17519
  };
17067
17520
  report.attempts.push(attempt);
17068
17521
  report.skipped++;
17069
- await fireMergeAttempt(opts.onMergeAttempt, attempt, idx++, log10);
17522
+ await fireMergeAttempt(opts.onMergeAttempt, attempt, idx++, log11);
17070
17523
  }
17071
17524
  for (const r of mergeable) {
17072
- const { attempt, pendingItem } = await mergeOneAttempt(r, { mergeRoot: root }, mergeFns, log10);
17525
+ const { attempt, pendingItem } = await mergeOneAttempt(r, { mergeRoot: root }, mergeFns, log11);
17073
17526
  report.attempts.push(attempt);
17074
17527
  if (attempt.ok && !attempt.skippedReason)
17075
17528
  report.merged++;
@@ -17079,12 +17532,12 @@ async function mergeWorktrees(results, opts, deps) {
17079
17532
  report.conflicted++;
17080
17533
  if (pendingItem)
17081
17534
  report.pendingWorktrees.push(pendingItem);
17082
- await fireMergeAttempt(opts.onMergeAttempt, attempt, idx++, log10);
17535
+ await fireMergeAttempt(opts.onMergeAttempt, attempt, idx++, log11);
17083
17536
  }
17084
17537
  return report;
17085
17538
  }
17086
17539
  function createMergeQueue(opts, deps = {}) {
17087
- const log10 = deps.log ?? (() => {});
17540
+ const log11 = deps.log ?? (() => {});
17088
17541
  const mergeFns = {
17089
17542
  commitWorktreeIfDirtyFn: deps.commitWorktreeIfDirtyFn ?? commitWorktreeIfDirty,
17090
17543
  tryMergeFn: deps.tryMergeFn ?? tryMerge,
@@ -17125,10 +17578,10 @@ function createMergeQueue(opts, deps = {}) {
17125
17578
  conflicts: []
17126
17579
  });
17127
17580
  }
17128
- await fireMergeAttempt(opts.onMergeAttempt, attempt2, idx++, log10);
17581
+ await fireMergeAttempt(opts.onMergeAttempt, attempt2, idx++, log11);
17129
17582
  return;
17130
17583
  }
17131
- const { attempt, abortFailed, pendingItem } = await mergeOneAttempt(result, { mergeRoot: opts.mergeRoot, summaryCharLimit: opts.summaryCharLimit }, mergeFns, log10);
17584
+ const { attempt, abortFailed, pendingItem } = await mergeOneAttempt(result, { mergeRoot: opts.mergeRoot, summaryCharLimit: opts.summaryCharLimit }, mergeFns, log11);
17132
17585
  report.attempts.push(attempt);
17133
17586
  if (attempt.ok && !attempt.skippedReason)
17134
17587
  report.merged++;
@@ -17140,11 +17593,11 @@ function createMergeQueue(opts, deps = {}) {
17140
17593
  report.pendingWorktrees.push(pendingItem);
17141
17594
  if (abortFailed) {
17142
17595
  queueAborted = true;
17143
- log10("error", `[parallel-merge] queue aborted (mergeAbort failed)`, {
17596
+ log11("error", `[parallel-merge] queue aborted (mergeAbort failed)`, {
17144
17597
  id: result.id
17145
17598
  });
17146
17599
  }
17147
- await fireMergeAttempt(opts.onMergeAttempt, attempt, idx++, log10);
17600
+ await fireMergeAttempt(opts.onMergeAttempt, attempt, idx++, log11);
17148
17601
  });
17149
17602
  },
17150
17603
  async flushAndReport() {
@@ -17153,23 +17606,23 @@ function createMergeQueue(opts, deps = {}) {
17153
17606
  }
17154
17607
  };
17155
17608
  }
17156
- async function safeRemoveWorktree(fn, root, wt, log10, subtaskId) {
17609
+ async function safeRemoveWorktree(fn, root, wt, log11, subtaskId) {
17157
17610
  try {
17158
17611
  await fn({ root, worktree_path: wt, force: true });
17159
17612
  } catch (err) {
17160
- log10("warn", `[parallel] removeWorktree 失败 ${subtaskId}`, {
17613
+ log11("warn", `[parallel] removeWorktree 失败 ${subtaskId}`, {
17161
17614
  error: describe4(err),
17162
17615
  worktree: wt
17163
17616
  });
17164
17617
  }
17165
17618
  }
17166
- async function fireMergeAttempt(cb, attempt, idx, log10) {
17619
+ async function fireMergeAttempt(cb, attempt, idx, log11) {
17167
17620
  if (!cb)
17168
17621
  return;
17169
17622
  try {
17170
17623
  await cb(attempt, idx);
17171
17624
  } catch (err) {
17172
- log10("warn", `[parallel] onMergeAttempt 抛错(已隔离)`, {
17625
+ log11("warn", `[parallel] onMergeAttempt 抛错(已隔离)`, {
17173
17626
  id: attempt.subtaskId,
17174
17627
  error: describe4(err)
17175
17628
  });
@@ -17195,7 +17648,7 @@ async function schedule(opts) {
17195
17648
  }
17196
17649
  const now = opts.deps.now ?? Date.now;
17197
17650
  const start = now();
17198
- const log10 = opts.deps.log ?? (() => {});
17651
+ const log11 = opts.deps.log ?? (() => {});
17199
17652
  const concurrency = Math.max(1, opts.maxConcurrency ?? 4);
17200
17653
  const totalTimeout = opts.totalTimeout_ms ?? 30 * 60000;
17201
17654
  const limit = Math.max(1, opts.summaryCharLimit ?? 500);
@@ -17228,7 +17681,7 @@ async function schedule(opts) {
17228
17681
  mergeAbortFn: opts.deps.mergeAbort,
17229
17682
  removeWorktreeFn: opts.deps.removeWorktree,
17230
17683
  worktreeHasChangesFn: opts.deps.worktreeHasChanges,
17231
- log: log10
17684
+ log: log11
17232
17685
  });
17233
17686
  }
17234
17687
  const fireFinish = async (i, res) => {
@@ -17236,7 +17689,7 @@ async function schedule(opts) {
17236
17689
  try {
17237
17690
  queue.enqueue(res);
17238
17691
  } catch (err) {
17239
- log10("warn", `[parallel] queue.enqueue 抛错(已隔离)`, {
17692
+ log11("warn", `[parallel] queue.enqueue 抛错(已隔离)`, {
17240
17693
  id: res.id,
17241
17694
  error: describe5(err)
17242
17695
  });
@@ -17247,7 +17700,7 @@ async function schedule(opts) {
17247
17700
  try {
17248
17701
  await opts.onSubtaskFinish(res, i);
17249
17702
  } catch (err) {
17250
- log10("warn", `[parallel] onSubtaskFinish 抛错(已隔离)`, {
17703
+ log11("warn", `[parallel] onSubtaskFinish 抛错(已隔离)`, {
17251
17704
  id: res.id,
17252
17705
  error: describe5(err)
17253
17706
  });
@@ -17277,7 +17730,7 @@ async function schedule(opts) {
17277
17730
  try {
17278
17731
  await opts.onSubtaskStart(spec, i);
17279
17732
  } catch (err) {
17280
- log10("warn", `[parallel] onSubtaskStart 抛错(已隔离)`, {
17733
+ log11("warn", `[parallel] onSubtaskStart 抛错(已隔离)`, {
17281
17734
  id: spec.id,
17282
17735
  error: describe5(err)
17283
17736
  });
@@ -17328,7 +17781,7 @@ async function schedule(opts) {
17328
17781
  try {
17329
17782
  await alloc.cleanup();
17330
17783
  } catch (err) {
17331
- log10("warn", `[parallel] worktree 清理失败 ${spec.id}`, {
17784
+ log11("warn", `[parallel] worktree 清理失败 ${spec.id}`, {
17332
17785
  error: describe5(err)
17333
17786
  });
17334
17787
  }
@@ -17488,7 +17941,7 @@ function pickStatus(r, taskAborted, globalAborted) {
17488
17941
  // lib/opencode-runner.ts
17489
17942
  init_opencode_plugin_helpers();
17490
17943
  function makeOpencodeRunner(opts) {
17491
- const log10 = opts.log ?? (() => {});
17944
+ const log11 = opts.log ?? (() => {});
17492
17945
  return async (spec, runCtx) => {
17493
17946
  const startedAt = Date.now();
17494
17947
  let childSessionId;
@@ -17509,7 +17962,7 @@ function makeOpencodeRunner(opts) {
17509
17962
  };
17510
17963
  }
17511
17964
  childSessionId = created.data.id;
17512
- log10("info", `[opencode-runner] subtask=${spec.id} session=${childSessionId} created`);
17965
+ log11("info", `[opencode-runner] subtask=${spec.id} session=${childSessionId} created`);
17513
17966
  const promptText = composePromptText(spec);
17514
17967
  const promptPromise = opts.client.session.prompt({
17515
17968
  path: { id: childSessionId },
@@ -17562,7 +18015,7 @@ function makeOpencodeRunner(opts) {
17562
18015
  });
17563
18016
  changedFiles = pickDiffFiles(diff.data);
17564
18017
  } catch (err) {
17565
- log10("warn", `[opencode-runner] diff 取失败 ${spec.id}`, { error: describe6(err) });
18018
+ log11("warn", `[opencode-runner] diff 取失败 ${spec.id}`, { error: describe6(err) });
17566
18019
  }
17567
18020
  const elapsed = Date.now() - startedAt;
17568
18021
  const summary = lastText || `subtask ${spec.id} 完成(${elapsed}ms,无文本输出,finish=${finishReason || "unknown"})`;
@@ -17588,7 +18041,7 @@ function makeOpencodeRunner(opts) {
17588
18041
  query: opts.directory ? { directory: opts.directory } : undefined
17589
18042
  });
17590
18043
  } catch (err) {
17591
- log10("warn", `[opencode-runner] session.delete 失败 ${childSessionId}`, {
18044
+ log11("warn", `[opencode-runner] session.delete 失败 ${childSessionId}`, {
17592
18045
  error: describe6(err)
17593
18046
  });
17594
18047
  }
@@ -17702,14 +18155,14 @@ function clip4(s, max) {
17702
18155
  return s.length <= max ? s : s.slice(0, max - 1) + "…";
17703
18156
  }
17704
18157
  async function sendParentNotice(client, sessionID, text, opts = {}) {
17705
- const log10 = opts.log ?? (() => {});
18158
+ const log11 = opts.log ?? (() => {});
17706
18159
  if (!client?.session) {
17707
- log10("warn", "[sendParentNotice] client.session 不可用,noop");
18160
+ log11("warn", "[sendParentNotice] client.session 不可用,noop");
17708
18161
  return false;
17709
18162
  }
17710
18163
  const sessionAny = client.session;
17711
18164
  if (typeof sessionAny.promptAsync !== "function") {
17712
- log10("warn", "[sendParentNotice] promptAsync 不可用(SDK 太老?),noop");
18165
+ log11("warn", "[sendParentNotice] promptAsync 不可用(SDK 太老?),noop");
17713
18166
  return false;
17714
18167
  }
17715
18168
  try {
@@ -17730,12 +18183,12 @@ async function sendParentNotice(client, sessionID, text, opts = {}) {
17730
18183
  }
17731
18184
  });
17732
18185
  if (res && typeof res === "object" && "error" in res && res.error) {
17733
- log10("warn", "[sendParentNotice] promptAsync 返回 error", { error: res.error });
18186
+ log11("warn", "[sendParentNotice] promptAsync 返回 error", { error: res.error });
17734
18187
  return false;
17735
18188
  }
17736
18189
  return true;
17737
18190
  } catch (err) {
17738
- log10("warn", "[sendParentNotice] 抛错(已隔离)", {
18191
+ log11("warn", "[sendParentNotice] 抛错(已隔离)", {
17739
18192
  error: err instanceof Error ? err.message : String(err)
17740
18193
  });
17741
18194
  return false;
@@ -17765,9 +18218,9 @@ function buildSystemPrompt(maxSubtasks) {
17765
18218
  `);
17766
18219
  }
17767
18220
  async function decomposeTask(description26, opts) {
17768
- const log10 = opts.log ?? (() => {});
18221
+ const log11 = opts.log ?? (() => {});
17769
18222
  if (opts.mockResponse) {
17770
- return validateAndFinalize(opts.mockResponse, undefined, log10, opts.maxSubtasks ?? DEFAULT_MAX_SUBTASKS);
18223
+ return validateAndFinalize(opts.mockResponse, undefined, log11, opts.maxSubtasks ?? DEFAULT_MAX_SUBTASKS);
17771
18224
  }
17772
18225
  const maxSubtasks = opts.maxSubtasks ?? DEFAULT_MAX_SUBTASKS;
17773
18226
  const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS2;
@@ -17778,7 +18231,7 @@ async function decomposeTask(description26, opts) {
17778
18231
  query: opts.directory ? { directory: opts.directory } : undefined
17779
18232
  });
17780
18233
  if (created.error || !created.data?.id) {
17781
- log10("warn", "[decompose] session.create 失败", { error: describe7(created.error) });
18234
+ log11("warn", "[decompose] session.create 失败", { error: describe7(created.error) });
17782
18235
  return {
17783
18236
  ok: false,
17784
18237
  subtasks: [],
@@ -17800,22 +18253,22 @@ async function decomposeTask(description26, opts) {
17800
18253
  sleep(timeoutMs).then(() => ({ kind: "timeout" }))
17801
18254
  ]);
17802
18255
  if (raced.kind === "timeout") {
17803
- log10("warn", "[decompose] LLM 调用超时", { timeoutMs });
18256
+ log11("warn", "[decompose] LLM 调用超时", { timeoutMs });
17804
18257
  return { ok: false, subtasks: [], reason: "llm_unavailable" };
17805
18258
  }
17806
18259
  const r = raced.value;
17807
18260
  if (r.error || !r.data) {
17808
- log10("warn", "[decompose] session.prompt 返回错误", { error: describe7(r.error) });
18261
+ log11("warn", "[decompose] session.prompt 返回错误", { error: describe7(r.error) });
17809
18262
  return { ok: false, subtasks: [], reason: "llm_unavailable" };
17810
18263
  }
17811
18264
  const rawText = pickLastText2(r.data.parts ?? []);
17812
18265
  if (!rawText) {
17813
- log10("warn", "[decompose] LLM 输出为空");
18266
+ log11("warn", "[decompose] LLM 输出为空");
17814
18267
  return { ok: false, subtasks: [], reason: "parse_failed", raw: "" };
17815
18268
  }
17816
18269
  const parsed = extractJson(rawText);
17817
18270
  if (!parsed) {
17818
- log10("warn", "[decompose] JSON 解析失败", { raw: clip5(rawText, 200) });
18271
+ log11("warn", "[decompose] JSON 解析失败", { raw: clip5(rawText, 200) });
17819
18272
  return { ok: false, subtasks: [], reason: "parse_failed", raw: rawText };
17820
18273
  }
17821
18274
  if (parsed && typeof parsed === "object" && parsed.single_task === true) {
@@ -17823,7 +18276,7 @@ async function decomposeTask(description26, opts) {
17823
18276
  }
17824
18277
  const subtasksRaw = parsed.subtasks;
17825
18278
  if (!Array.isArray(subtasksRaw)) {
17826
- log10("warn", "[decompose] JSON 缺 subtasks 数组");
18279
+ log11("warn", "[decompose] JSON 缺 subtasks 数组");
17827
18280
  return { ok: false, subtasks: [], reason: "parse_failed", raw: rawText };
17828
18281
  }
17829
18282
  const normalized = [];
@@ -17839,12 +18292,12 @@ async function decomposeTask(description26, opts) {
17839
18292
  normalized.push({ description: desc, hintFiles });
17840
18293
  }
17841
18294
  if (normalized.length > maxSubtasks) {
17842
- log10("warn", "[decompose] LLM 返回子任务超过上限", { count: normalized.length, maxSubtasks });
18295
+ log11("warn", "[decompose] LLM 返回子任务超过上限", { count: normalized.length, maxSubtasks });
17843
18296
  return { ok: false, subtasks: [], reason: "parse_failed", raw: rawText };
17844
18297
  }
17845
- return validateAndFinalize(normalized, rawText, log10, maxSubtasks);
18298
+ return validateAndFinalize(normalized, rawText, log11, maxSubtasks);
17846
18299
  } catch (err) {
17847
- log10("warn", "[decompose] 抛错", { error: describe7(err) });
18300
+ log11("warn", "[decompose] 抛错", { error: describe7(err) });
17848
18301
  return { ok: false, subtasks: [], reason: "llm_unavailable" };
17849
18302
  } finally {
17850
18303
  if (childSessionId) {
@@ -17854,12 +18307,12 @@ async function decomposeTask(description26, opts) {
17854
18307
  query: opts.directory ? { directory: opts.directory } : undefined
17855
18308
  });
17856
18309
  } catch (err) {
17857
- log10("warn", "[decompose] session.delete 失败", { error: describe7(err) });
18310
+ log11("warn", "[decompose] session.delete 失败", { error: describe7(err) });
17858
18311
  }
17859
18312
  }
17860
18313
  }
17861
18314
  }
17862
- function validateAndFinalize(subtasks, raw, log10, maxSubtasks) {
18315
+ function validateAndFinalize(subtasks, raw, log11, maxSubtasks) {
17863
18316
  if (subtasks.length < 2) {
17864
18317
  return { ok: false, subtasks: [], reason: "single_task", raw };
17865
18318
  }
@@ -17869,7 +18322,7 @@ function validateAndFinalize(subtasks, raw, log10, maxSubtasks) {
17869
18322
  }
17870
18323
  }
17871
18324
  if (subtasks.length > maxSubtasks) {
17872
- log10("warn", "[decompose] LLM 返回子任务超过上限", { count: subtasks.length, maxSubtasks });
18325
+ log11("warn", "[decompose] LLM 返回子任务超过上限", { count: subtasks.length, maxSubtasks });
17873
18326
  return { ok: false, subtasks: [], reason: "parse_failed", raw };
17874
18327
  }
17875
18328
  for (let i = 0;i < subtasks.length; i++) {
@@ -17883,7 +18336,7 @@ function validateAndFinalize(subtasks, raw, log10, maxSubtasks) {
17883
18336
  continue;
17884
18337
  for (const f of b) {
17885
18338
  if (setA.has(f.trim())) {
17886
- log10("info", "[decompose] hintFiles 有交集,降级 single_task", {
18339
+ log11("info", "[decompose] hintFiles 有交集,降级 single_task", {
17887
18340
  a: subtasks[i].description,
17888
18341
  b: subtasks[j].description,
17889
18342
  file: f
@@ -17972,7 +18425,7 @@ function sleep(ms) {
17972
18425
 
17973
18426
  // plugins/subtasks.ts
17974
18427
  init_runtime_paths();
17975
- var PLUGIN_NAME18 = "subtasks";
18428
+ var PLUGIN_NAME19 = "subtasks";
17976
18429
  function getLogFile(root = process.cwd()) {
17977
18430
  return path15.join(runtimeDir(root), "logs", "subtasks.log");
17978
18431
  }
@@ -18038,7 +18491,7 @@ var mockRunner = async (spec) => {
18038
18491
  };
18039
18492
  async function handleParallelCommand(raw) {
18040
18493
  const ctx = raw ?? {};
18041
- const log10 = ctx.log;
18494
+ const log11 = ctx.log;
18042
18495
  const args = ctx.args ?? {};
18043
18496
  const parentId = (args.parentId ?? `task-${Date.now().toString(36)}`).replace(/[^a-zA-Z0-9._-]/g, "-");
18044
18497
  const rawDescription = typeof args.description === "string" ? args.description.trim() : "";
@@ -18052,7 +18505,7 @@ async function handleParallelCommand(raw) {
18052
18505
  specs = splitDescriptions(args.description, { parentId });
18053
18506
  }
18054
18507
  if (specs.length === 0) {
18055
- log10?.warn(`[${PLUGIN_NAME18}] /parallel 缺有效子任务`);
18508
+ log11?.warn(`[${PLUGIN_NAME19}] /parallel 缺有效子任务`);
18056
18509
  await safeReply2(ctx, "⚠ /parallel 需要至少 1 个子任务(用 ; 或换行分隔)");
18057
18510
  return { ok: false, reason: "no_subtasks" };
18058
18511
  }
@@ -18078,7 +18531,7 @@ async function handleParallelCommand(raw) {
18078
18531
  if (!gitOk) {
18079
18532
  autoMerge = false;
18080
18533
  downgradedAutoMerge = true;
18081
- log10?.warn("[subtasks] mergeRoot 非 git 仓库,降级 autoMerge=false");
18534
+ log11?.warn("[subtasks] mergeRoot 非 git 仓库,降级 autoMerge=false");
18082
18535
  const canNoticeEarly = Boolean(ctx.client && ctx.parentSessionID);
18083
18536
  if (canNoticeEarly) {
18084
18537
  await sendParentNotice(ctx.client, ctx.parentSessionID, "ℹ 当前目录不是 git 仓库,worktree 隔离已自动关闭,以单目录模式运行", { directory: ctx.directory });
@@ -18087,17 +18540,17 @@ async function handleParallelCommand(raw) {
18087
18540
  }
18088
18541
  const canNotice = Boolean(ctx.client && ctx.parentSessionID);
18089
18542
  if (specs.length === 1 && rawDescription.length >= 30 && ctx.client) {
18090
- log10?.info(`[${PLUGIN_NAME18}] 触发 AI 拆分(描述长度=${rawDescription.length})`);
18543
+ log11?.info(`[${PLUGIN_NAME19}] 触发 AI 拆分(描述长度=${rawDescription.length})`);
18091
18544
  const dec = await decomposeTask(rawDescription, {
18092
18545
  client: ctx.client,
18093
18546
  directory: ctx.directory,
18094
18547
  log: (lvl, msg, data) => {
18095
18548
  if (lvl === "error")
18096
- log10?.error(msg, data);
18549
+ log11?.error(msg, data);
18097
18550
  else if (lvl === "warn")
18098
- log10?.warn(msg, data);
18551
+ log11?.warn(msg, data);
18099
18552
  else
18100
- log10?.info(msg, data);
18553
+ log11?.info(msg, data);
18101
18554
  },
18102
18555
  mockResponse: ctx.decomposeMockResponse
18103
18556
  });
@@ -18108,7 +18561,7 @@ async function handleParallelCommand(raw) {
18108
18561
  timeout_ms: undefined,
18109
18562
  args: d.hintFiles && d.hintFiles.length > 0 ? { hintFiles: d.hintFiles } : undefined
18110
18563
  }));
18111
- log10?.info(`[${PLUGIN_NAME18}] AI 拆分成功 → ${specs.length} 个子任务`);
18564
+ log11?.info(`[${PLUGIN_NAME19}] AI 拆分成功 → ${specs.length} 个子任务`);
18112
18565
  if (canNotice) {
18113
18566
  const lines = [
18114
18567
  `\uD83E\uDD16 AI 已自动拆为 ${specs.length} 个并行子任务:`,
@@ -18120,7 +18573,7 @@ async function handleParallelCommand(raw) {
18120
18573
  });
18121
18574
  }
18122
18575
  } else if (dec.reason === "llm_unavailable" || dec.reason === "parse_failed") {
18123
- log10?.warn(`[${PLUGIN_NAME18}] AI 拆分失败(${dec.reason}),按单任务执行`);
18576
+ log11?.warn(`[${PLUGIN_NAME19}] AI 拆分失败(${dec.reason}),按单任务执行`);
18124
18577
  }
18125
18578
  }
18126
18579
  if (canNotice) {
@@ -18135,11 +18588,11 @@ async function handleParallelCommand(raw) {
18135
18588
  directory: ctx.directory,
18136
18589
  log: (lvl, msg, data) => {
18137
18590
  if (lvl === "error")
18138
- log10?.error(msg, data);
18591
+ log11?.error(msg, data);
18139
18592
  else if (lvl === "warn")
18140
- log10?.warn(msg, data);
18593
+ log11?.warn(msg, data);
18141
18594
  else
18142
- log10?.info(msg, data);
18595
+ log11?.info(msg, data);
18143
18596
  }
18144
18597
  });
18145
18598
  }
@@ -18183,11 +18636,11 @@ async function handleParallelCommand(raw) {
18183
18636
  allocateWorktree: downgradedAutoMerge ? undefined : ctx.allocateWorktree,
18184
18637
  log: (lvl, msg, data) => {
18185
18638
  if (lvl === "error")
18186
- log10?.error(msg, data);
18639
+ log11?.error(msg, data);
18187
18640
  else if (lvl === "warn")
18188
- log10?.warn(msg, data);
18641
+ log11?.warn(msg, data);
18189
18642
  else
18190
- log10?.info(msg, data);
18643
+ log11?.info(msg, data);
18191
18644
  },
18192
18645
  tryMerge: ctx.tryMerge,
18193
18646
  mergeCommit: ctx.mergeCommit,
@@ -18199,7 +18652,7 @@ async function handleParallelCommand(raw) {
18199
18652
  });
18200
18653
  } catch (err) {
18201
18654
  const msg = err instanceof Error ? err.message : String(err);
18202
- log10?.error(`[${PLUGIN_NAME18}] schedule 抛错`, { error: msg });
18655
+ log11?.error(`[${PLUGIN_NAME19}] schedule 抛错`, { error: msg });
18203
18656
  await safeReply2(ctx, `❌ 并发调度失败:${msg}`);
18204
18657
  return { ok: false, reason: msg };
18205
18658
  }
@@ -18217,7 +18670,7 @@ async function handleParallelCommand(raw) {
18217
18670
  }
18218
18671
  await safeReply2(ctx, summaryLines.join(`
18219
18672
  `));
18220
- log10?.info(`[${PLUGIN_NAME18}] schedule 完成`, {
18673
+ log11?.info(`[${PLUGIN_NAME19}] schedule 完成`, {
18221
18674
  parentId,
18222
18675
  success: result.digest.success,
18223
18676
  failed: result.digest.failed,
@@ -18230,7 +18683,7 @@ async function handleParallelCommand(raw) {
18230
18683
  try {
18231
18684
  await ctx.onCompleted(result);
18232
18685
  } catch (err) {
18233
- log10?.warn(`[${PLUGIN_NAME18}] onCompleted hook 抛错(已隔离)`, {
18686
+ log11?.warn(`[${PLUGIN_NAME19}] onCompleted hook 抛错(已隔离)`, {
18234
18687
  error: err instanceof Error ? err.message : String(err)
18235
18688
  });
18236
18689
  }
@@ -18272,7 +18725,7 @@ async function writeLog(level, msg, data) {
18272
18725
  const line = JSON.stringify({
18273
18726
  ts: new Date().toISOString(),
18274
18727
  level,
18275
- plugin: PLUGIN_NAME18,
18728
+ plugin: PLUGIN_NAME19,
18276
18729
  msg,
18277
18730
  data
18278
18731
  }) + `
@@ -18283,11 +18736,11 @@ async function writeLog(level, msg, data) {
18283
18736
  await fs12.appendFile(logFile, line, "utf8");
18284
18737
  } catch {}
18285
18738
  }
18286
- logLifecycle(PLUGIN_NAME18, "import");
18739
+ logLifecycle(PLUGIN_NAME19, "import");
18287
18740
  var subtasksServer = async (ctx) => {
18288
- const log10 = makePluginLogger(PLUGIN_NAME18);
18741
+ const log11 = makePluginLogger(PLUGIN_NAME19);
18289
18742
  const client = ctx?.client ?? undefined;
18290
- logLifecycle(PLUGIN_NAME18, "activate", {
18743
+ logLifecycle(PLUGIN_NAME19, "activate", {
18291
18744
  directory: ctx.directory,
18292
18745
  hasClient: Boolean(client)
18293
18746
  });
@@ -18312,7 +18765,7 @@ var subtasksServer = async (ctx) => {
18312
18765
  const gitOk = await isGitRepo({ root: repoRoot }).catch(() => false);
18313
18766
  if (!gitOk) {
18314
18767
  autoMerge = false;
18315
- log10?.warn("[subtasks] worktree_isolation=true 但非 git 仓库,自动降级 autoMerge=false");
18768
+ log11?.warn("[subtasks] worktree_isolation=true 但非 git 仓库,自动降级 autoMerge=false");
18316
18769
  const canNotice = Boolean(client);
18317
18770
  if (canNotice) {
18318
18771
  await sendParentNotice(client, input.sessionID, "ℹ 当前目录不是 git 仓库,worktree 隔离已自动关闭,以单目录模式运行", { directory: ctx.directory });
@@ -18341,7 +18794,7 @@ var subtasksServer = async (ctx) => {
18341
18794
  replyLines.push(s);
18342
18795
  return Promise.resolve();
18343
18796
  },
18344
- log: log10,
18797
+ log: log11,
18345
18798
  client,
18346
18799
  parentSessionID: input.sessionID,
18347
18800
  directory: ctx.directory,
@@ -18354,11 +18807,11 @@ var subtasksServer = async (ctx) => {
18354
18807
  perTaskTimeoutMs: 5 * 60000,
18355
18808
  log: (lvl, msg, data) => {
18356
18809
  if (lvl === "error")
18357
- log10.error(msg, data);
18810
+ log11.error(msg, data);
18358
18811
  else if (lvl === "warn")
18359
- log10.warn(msg, data);
18812
+ log11.warn(msg, data);
18360
18813
  else
18361
- log10.info(msg, data);
18814
+ log11.info(msg, data);
18362
18815
  }
18363
18816
  }) : undefined
18364
18817
  };
@@ -18384,20 +18837,20 @@ var subtasksServer = async (ctx) => {
18384
18837
  });
18385
18838
  }
18386
18839
  } catch (err) {
18387
- log10.error(`[${PLUGIN_NAME18}] command.execute.before 异常(已隔离)`, {
18840
+ log11.error(`[${PLUGIN_NAME19}] command.execute.before 异常(已隔离)`, {
18388
18841
  error: err instanceof Error ? err.message : String(err)
18389
18842
  });
18390
18843
  }
18391
18844
  }
18392
18845
  };
18393
18846
  };
18394
- var handler18 = subtasksServer;
18847
+ var handler19 = subtasksServer;
18395
18848
 
18396
18849
  // plugins/terminal-monitor.ts
18397
18850
  init_opencode_plugin_helpers();
18398
18851
  import * as crypto5 from "node:crypto";
18399
- var PLUGIN_NAME19 = "terminal-monitor";
18400
- logLifecycle(PLUGIN_NAME19, "import", {});
18852
+ var PLUGIN_NAME20 = "terminal-monitor";
18853
+ logLifecycle(PLUGIN_NAME20, "import", {});
18401
18854
  var DEFAULT_CONFIG7 = {
18402
18855
  minScore: 0.6,
18403
18856
  cooldownMs: 30000,
@@ -18679,17 +19132,17 @@ function describeError(err) {
18679
19132
  return String(err);
18680
19133
  }
18681
19134
  }
18682
- var log10 = makePluginLogger(PLUGIN_NAME19);
19135
+ var log11 = makePluginLogger(PLUGIN_NAME20);
18683
19136
  var lru = new FingerprintLRU2;
18684
19137
  var _lastNotification = null;
18685
19138
  var terminalMonitorServer = async (ctx) => {
18686
- logLifecycle(PLUGIN_NAME19, "activate", {
19139
+ logLifecycle(PLUGIN_NAME20, "activate", {
18687
19140
  directory: ctx.directory,
18688
19141
  triggerEventTypes: ["terminal.output", "terminal.exit"]
18689
19142
  });
18690
19143
  return {
18691
19144
  event: async ({ event }) => {
18692
- await safeAsync(PLUGIN_NAME19, "event", async () => {
19145
+ await safeAsync(PLUGIN_NAME20, "event", async () => {
18693
19146
  const e = event;
18694
19147
  if (!e || typeof e.type !== "string")
18695
19148
  return;
@@ -18698,12 +19151,12 @@ var terminalMonitorServer = async (ctx) => {
18698
19151
  const ev = { type: e.type, ...e.properties ?? {} };
18699
19152
  const r = await processTerminalEvent(ev, {
18700
19153
  lru,
18701
- log: log10,
19154
+ log: log11,
18702
19155
  notifyAgent: (msg) => {
18703
19156
  _lastNotification = msg;
18704
19157
  }
18705
19158
  });
18706
- safeWriteLog(PLUGIN_NAME19, {
19159
+ safeWriteLog(PLUGIN_NAME20, {
18707
19160
  hook: "event",
18708
19161
  type: e.type,
18709
19162
  notified: r.notified,
@@ -18711,19 +19164,19 @@ var terminalMonitorServer = async (ctx) => {
18711
19164
  findings: r.notification?.findings.length ?? 0
18712
19165
  });
18713
19166
  if (r.notified && r.notification) {
18714
- log10.info(`[${PLUGIN_NAME19}] 反馈 ${r.notification.findings.length} 条 → ${r.notification.summary}`);
19167
+ log11.info(`[${PLUGIN_NAME20}] 反馈 ${r.notification.findings.length} 条 → ${r.notification.summary}`);
18715
19168
  }
18716
19169
  });
18717
19170
  }
18718
19171
  };
18719
19172
  };
18720
- var handler19 = terminalMonitorServer;
19173
+ var handler20 = terminalMonitorServer;
18721
19174
 
18722
19175
  // plugins/token-manager.ts
18723
19176
  init_opencode_plugin_helpers();
18724
- var PLUGIN_NAME20 = "token-manager";
18725
- logLifecycle(PLUGIN_NAME20, "import", {});
18726
- async function handleMessageBefore(raw, log11, defaults) {
19177
+ var PLUGIN_NAME21 = "token-manager";
19178
+ logLifecycle(PLUGIN_NAME21, "import", {});
19179
+ async function handleMessageBefore(raw, log12, defaults) {
18727
19180
  const ctx = raw ?? {};
18728
19181
  if (!Array.isArray(ctx.messages) || ctx.messages.length === 0)
18729
19182
  return null;
@@ -18742,21 +19195,21 @@ async function handleMessageBefore(raw, log11, defaults) {
18742
19195
  };
18743
19196
  if (r.compressed) {
18744
19197
  ctx.messages = r.messages;
18745
- log11?.info(`[${PLUGIN_NAME20}] 压缩 ${r.before.count}→${r.after.count} 条 / ${r.before.tokens}→${r.after.tokens} tokens`);
19198
+ log12?.info(`[${PLUGIN_NAME21}] 压缩 ${r.before.count}→${r.after.count} 条 / ${r.before.tokens}→${r.after.tokens} tokens`);
18746
19199
  }
18747
19200
  return r;
18748
19201
  } catch (err) {
18749
- log11?.warn(`[${PLUGIN_NAME20}] 压缩异常(已隔离)`, {
19202
+ log12?.warn(`[${PLUGIN_NAME21}] 压缩异常(已隔离)`, {
18750
19203
  error: err instanceof Error ? err.message : String(err)
18751
19204
  });
18752
19205
  return null;
18753
19206
  }
18754
19207
  }
18755
- var log11 = makePluginLogger(PLUGIN_NAME20);
19208
+ var log12 = makePluginLogger(PLUGIN_NAME21);
18756
19209
  var tokenManagerServer = async (ctx) => {
18757
19210
  const rt = loadRuntimeSync();
18758
19211
  const threshold = rt.runtime.context.condenser_threshold_ratio;
18759
- logLifecycle(PLUGIN_NAME20, "activate", {
19212
+ logLifecycle(PLUGIN_NAME21, "activate", {
18760
19213
  directory: ctx.directory,
18761
19214
  threshold,
18762
19215
  target: DEFAULT_CONDENSE.target,
@@ -18765,7 +19218,7 @@ var tokenManagerServer = async (ctx) => {
18765
19218
  });
18766
19219
  return {
18767
19220
  "experimental.chat.messages.transform": async (_input, output) => {
18768
- await safeAsync(PLUGIN_NAME20, "experimental.chat.messages.transform", async () => {
19221
+ await safeAsync(PLUGIN_NAME21, "experimental.chat.messages.transform", async () => {
18769
19222
  const list = output.messages;
18770
19223
  if (!Array.isArray(list) || list.length === 0)
18771
19224
  return;
@@ -18775,10 +19228,10 @@ var tokenManagerServer = async (ctx) => {
18775
19228
  `);
18776
19229
  return { role, content };
18777
19230
  });
18778
- const r = await handleMessageBefore({ messages: flat }, log11, { threshold });
19231
+ const r = await handleMessageBefore({ messages: flat }, log12, { threshold });
18779
19232
  if (!r)
18780
19233
  return;
18781
- safeWriteLog(PLUGIN_NAME20, {
19234
+ safeWriteLog(PLUGIN_NAME21, {
18782
19235
  hook: "experimental.chat.messages.transform",
18783
19236
  mode: "observe-only",
18784
19237
  before_msgs: r.before.count,
@@ -18789,13 +19242,13 @@ var tokenManagerServer = async (ctx) => {
18789
19242
  reason: r.reason
18790
19243
  });
18791
19244
  if (r.compressed) {
18792
- log11.warn(`[${PLUGIN_NAME20}] advise condense: ${r.before.count}→${r.after.count} msgs / ${r.before.tokens}→${r.after.tokens} tokens (observe-only, no write-back to avoid opencode message schema mismatch)`);
19245
+ log12.warn(`[${PLUGIN_NAME21}] advise condense: ${r.before.count}→${r.after.count} msgs / ${r.before.tokens}→${r.after.tokens} tokens (observe-only, no write-back to avoid opencode message schema mismatch)`);
18793
19246
  }
18794
19247
  });
18795
19248
  }
18796
19249
  };
18797
19250
  };
18798
- var handler20 = tokenManagerServer;
19251
+ var handler21 = tokenManagerServer;
18799
19252
 
18800
19253
  // plugins/tool-policy.ts
18801
19254
  init_opencode_plugin_helpers();
@@ -18915,8 +19368,8 @@ function checkFileAccess(acl, file, op) {
18915
19368
  }
18916
19369
 
18917
19370
  // plugins/tool-policy.ts
18918
- var PLUGIN_NAME21 = "tool-policy";
18919
- logLifecycle(PLUGIN_NAME21, "import", {});
19371
+ var PLUGIN_NAME22 = "tool-policy";
19372
+ logLifecycle(PLUGIN_NAME22, "import", {});
18920
19373
  var EMPTY_ACL = { whitelistMode: false };
18921
19374
  var SUBAGENT_APPLY_DENY_LIST = new Set([
18922
19375
  "coder",
@@ -19000,18 +19453,18 @@ function classifyToolKind(toolName) {
19000
19453
  return "webfetch";
19001
19454
  return "other";
19002
19455
  }
19003
- async function resolveCurrentAgent(client, sessionID, log12) {
19456
+ async function resolveCurrentAgent(client, sessionID, log13) {
19004
19457
  try {
19005
19458
  const sessionApi = client?.session;
19006
19459
  if (!sessionApi || typeof sessionApi.get !== "function") {
19007
- log12.warn(`client.session.get unavailable`, { sessionID });
19460
+ log13.warn(`client.session.get unavailable`, { sessionID });
19008
19461
  return;
19009
19462
  }
19010
19463
  const res = await sessionApi.get({ path: { id: sessionID } });
19011
19464
  const data = res?.data ?? res;
19012
19465
  const rawAgent = data?.agent;
19013
19466
  if (typeof rawAgent !== "string" || rawAgent === "") {
19014
- log12.warn(`client.session.get returned no string agent (保守放行)`, {
19467
+ log13.warn(`client.session.get returned no string agent (保守放行)`, {
19015
19468
  sessionID,
19016
19469
  dataKeys: data && typeof data === "object" ? Object.keys(data) : null
19017
19470
  });
@@ -19019,20 +19472,20 @@ async function resolveCurrentAgent(client, sessionID, log12) {
19019
19472
  }
19020
19473
  return rawAgent;
19021
19474
  } catch (err) {
19022
- log12.warn(`client.session.get failed (保守放行)`, {
19475
+ log13.warn(`client.session.get failed (保守放行)`, {
19023
19476
  sessionID,
19024
19477
  error: err instanceof Error ? err.message : String(err)
19025
19478
  });
19026
19479
  return;
19027
19480
  }
19028
19481
  }
19029
- var log12 = makePluginLogger(PLUGIN_NAME21);
19482
+ var log13 = makePluginLogger(PLUGIN_NAME22);
19030
19483
  var toolPolicyServer = async (ctx) => {
19031
19484
  const directory = ctx.directory ?? process.cwd();
19032
19485
  const cfg = await loadPolicy(directory);
19033
19486
  const rt = loadRuntimeSync();
19034
19487
  cfg.defaultMode = rt.runtime.autonomy.default_mode;
19035
- logLifecycle(PLUGIN_NAME21, "activate", {
19488
+ logLifecycle(PLUGIN_NAME22, "activate", {
19036
19489
  directory,
19037
19490
  acl_loaded: !!cfg.acl,
19038
19491
  default_mode: cfg.defaultMode,
@@ -19041,13 +19494,13 @@ var toolPolicyServer = async (ctx) => {
19041
19494
  return {
19042
19495
  "tool.execute.before": async (input, output) => {
19043
19496
  let denied;
19044
- await safeAsync(PLUGIN_NAME21, "tool.execute.before", async () => {
19497
+ await safeAsync(PLUGIN_NAME22, "tool.execute.before", async () => {
19045
19498
  const toolName = input.tool;
19046
19499
  const argsObj = output.args ?? {};
19047
19500
  const needsAgentDetection = toolName === "pending_changes" && (argsObj.action === "apply" || argsObj.action === "apply_all");
19048
19501
  let currentAgent = undefined;
19049
19502
  if (needsAgentDetection) {
19050
- currentAgent = await resolveCurrentAgent(ctx.client, input.sessionID, log12);
19503
+ currentAgent = await resolveCurrentAgent(ctx.client, input.sessionID, log13);
19051
19504
  }
19052
19505
  const files = [];
19053
19506
  const filePath = argsObj["path"] ?? argsObj["filePath"] ?? argsObj["file"];
@@ -19062,7 +19515,7 @@ var toolPolicyServer = async (ctx) => {
19062
19515
  mode: cfg.defaultMode,
19063
19516
  files: files.length ? files : undefined
19064
19517
  }, cfg, currentAgent);
19065
- safeWriteLog(PLUGIN_NAME21, {
19518
+ safeWriteLog(PLUGIN_NAME22, {
19066
19519
  hook: "tool.execute.before",
19067
19520
  tool: toolName,
19068
19521
  callID: input.callID,
@@ -19072,14 +19525,14 @@ var toolPolicyServer = async (ctx) => {
19072
19525
  currentAgent
19073
19526
  });
19074
19527
  if (decision.action === "deny") {
19075
- log12.warn(`[${PLUGIN_NAME21}] DENY ${toolName}`, {
19528
+ log13.warn(`[${PLUGIN_NAME22}] DENY ${toolName}`, {
19076
19529
  action: argsObj.action,
19077
19530
  currentAgent,
19078
19531
  reasons: decision.reasons
19079
19532
  });
19080
19533
  denied = new Error(`[tool-policy] DENIED: ${decision.reasons.join(" / ")}`);
19081
19534
  } else if (decision.action === "confirm") {
19082
- log12.info(`[${PLUGIN_NAME21}] CONFIRM ${toolName}: ${decision.reasons.join("; ")}`);
19535
+ log13.info(`[${PLUGIN_NAME22}] CONFIRM ${toolName}: ${decision.reasons.join("; ")}`);
19083
19536
  }
19084
19537
  });
19085
19538
  if (denied)
@@ -19087,13 +19540,13 @@ var toolPolicyServer = async (ctx) => {
19087
19540
  }
19088
19541
  };
19089
19542
  };
19090
- var handler21 = toolPolicyServer;
19543
+ var handler22 = toolPolicyServer;
19091
19544
 
19092
19545
  // plugins/update-checker.ts
19093
19546
  init_opencode_plugin_helpers();
19094
19547
  import { existsSync as existsSync5 } from "node:fs";
19095
19548
  import { homedir as homedir8 } from "node:os";
19096
- import { join as join18 } from "node:path";
19549
+ import { join as join19 } from "node:path";
19097
19550
 
19098
19551
  // lib/update-checker-impl.ts
19099
19552
  import { createHash as createHash6 } from "node:crypto";
@@ -19102,15 +19555,15 @@ import {
19102
19555
  existsSync as existsSync4,
19103
19556
  mkdirSync as mkdirSync3,
19104
19557
  mkdtempSync,
19105
- readFileSync as readFileSync5,
19106
- readdirSync as readdirSync2,
19558
+ readFileSync as readFileSync6,
19559
+ readdirSync as readdirSync3,
19107
19560
  renameSync,
19108
- statSync as statSync4,
19561
+ statSync as statSync5,
19109
19562
  unlinkSync,
19110
19563
  writeFileSync as writeFileSync2
19111
19564
  } from "node:fs";
19112
19565
  import { homedir as homedir7, tmpdir } from "node:os";
19113
- import { dirname as dirname7, join as join17 } from "node:path";
19566
+ import { dirname as dirname7, join as join18 } from "node:path";
19114
19567
  import { fileURLToPath } from "node:url";
19115
19568
  import * as https from "node:https";
19116
19569
  import * as zlib from "node:zlib";
@@ -19118,7 +19571,7 @@ import * as zlib from "node:zlib";
19118
19571
  // lib/version-injected.ts
19119
19572
  function getInjectedVersion() {
19120
19573
  try {
19121
- const v = "0.3.13";
19574
+ const v = "0.3.14";
19122
19575
  if (typeof v === "string" && /^\d+\.\d+\.\d+/.test(v)) {
19123
19576
  return v;
19124
19577
  }
@@ -19208,23 +19661,23 @@ function readLocalVersion() {
19208
19661
  try {
19209
19662
  const here = fileURLToPath(import.meta.url);
19210
19663
  const root = dirname7(dirname7(here));
19211
- const pkg = JSON.parse(readFileSync5(join17(root, "package.json"), "utf8"));
19664
+ const pkg = JSON.parse(readFileSync6(join18(root, "package.json"), "utf8"));
19212
19665
  return typeof pkg.version === "string" ? pkg.version : "0.0.0";
19213
19666
  } catch {
19214
19667
  return "0.0.0";
19215
19668
  }
19216
19669
  }
19217
19670
  function defaultCacheDir() {
19218
- return process.env["CODEFORGE_CACHE_DIR"] ?? join17(homedir7(), ".cache", "codeforge");
19671
+ return process.env["CODEFORGE_CACHE_DIR"] ?? join18(homedir7(), ".cache", "codeforge");
19219
19672
  }
19220
19673
  function defaultCacheFile() {
19221
- return join17(defaultCacheDir(), "update-check.json");
19674
+ return join18(defaultCacheDir(), "update-check.json");
19222
19675
  }
19223
19676
  function readCache(file) {
19224
19677
  try {
19225
19678
  if (!existsSync4(file))
19226
19679
  return null;
19227
- const raw = readFileSync5(file, "utf8");
19680
+ const raw = readFileSync6(file, "utf8");
19228
19681
  const obj = JSON.parse(raw);
19229
19682
  if (obj && typeof obj === "object" && typeof obj.checkedAt === "number" && typeof obj.remote === "string" && typeof obj.repo === "string") {
19230
19683
  return obj;
@@ -19374,14 +19827,14 @@ function defaultHttpFetcher(url, timeoutMs) {
19374
19827
  });
19375
19828
  }
19376
19829
  async function downloadAndExtractBundle(opts) {
19377
- const tmpRoot = opts.tmpDir ?? mkdtempSync(join17(tmpdir(), "codeforge-update-"));
19830
+ const tmpRoot = opts.tmpDir ?? mkdtempSync(join18(tmpdir(), "codeforge-update-"));
19378
19831
  mkdirSync3(tmpRoot, { recursive: true });
19379
19832
  const fetcher = opts.tarballFetcher ?? defaultBinaryFetcher;
19380
19833
  const tarballBuf = await fetcher(opts.tarballUrl);
19381
19834
  verifyIntegrity(tarballBuf, opts.expectedIntegrity);
19382
19835
  const tarBuf = zlib.gunzipSync(tarballBuf);
19383
19836
  extractTarToDir(tarBuf, tmpRoot);
19384
- const bundlePath = join17(tmpRoot, "package", "dist", "index.js");
19837
+ const bundlePath = join18(tmpRoot, "package", "dist", "index.js");
19385
19838
  if (!existsSync4(bundlePath)) {
19386
19839
  throw new Error(`bundle_not_found: ${bundlePath}`);
19387
19840
  }
@@ -19421,11 +19874,11 @@ function extractTarToDir(tarBuf, destRoot) {
19421
19874
  offset += 512;
19422
19875
  if (typeFlag === "0" || typeFlag === "" || typeFlag === "\x00") {
19423
19876
  const fileBuf = tarBuf.subarray(offset, offset + size);
19424
- const dest = join17(destRoot, fullName);
19877
+ const dest = join18(destRoot, fullName);
19425
19878
  mkdirSync3(dirname7(dest), { recursive: true });
19426
19879
  writeFileSync2(dest, fileBuf);
19427
19880
  } else if (typeFlag === "5") {
19428
- mkdirSync3(join17(destRoot, fullName), { recursive: true });
19881
+ mkdirSync3(join18(destRoot, fullName), { recursive: true });
19429
19882
  }
19430
19883
  offset += Math.ceil(size / 512) * 512;
19431
19884
  }
@@ -19533,11 +19986,11 @@ function cleanupOldBackups(target, keep) {
19533
19986
  const dir = dirname7(target);
19534
19987
  const base = target.substring(dir.length + 1);
19535
19988
  const prefix = `${base}.bak.`;
19536
- const all = readdirSync2(dir).filter((f) => f.startsWith(prefix)).map((f) => {
19537
- const full = join17(dir, f);
19989
+ const all = readdirSync3(dir).filter((f) => f.startsWith(prefix)).map((f) => {
19990
+ const full = join18(dir, f);
19538
19991
  let mtimeMs = 0;
19539
19992
  try {
19540
- mtimeMs = statSync4(full).mtimeMs;
19993
+ mtimeMs = statSync5(full).mtimeMs;
19541
19994
  } catch {}
19542
19995
  return { full, mtimeMs };
19543
19996
  }).sort((a, b) => b.mtimeMs - a.mtimeMs);
@@ -19556,11 +20009,11 @@ function loadCompatibility(opts) {
19556
20009
  const root = opts?.cwd ?? inferPluginRoot();
19557
20010
  if (!root)
19558
20011
  return null;
19559
- file = join17(root, "compatibility.json");
20012
+ file = join18(root, "compatibility.json");
19560
20013
  }
19561
20014
  if (!existsSync4(file))
19562
20015
  return null;
19563
- const raw = readFileSync5(file, "utf8");
20016
+ const raw = readFileSync6(file, "utf8");
19564
20017
  const obj = JSON.parse(raw);
19565
20018
  if (!obj || typeof obj !== "object")
19566
20019
  return null;
@@ -19621,20 +20074,20 @@ function compareOpencodeVersion(opts) {
19621
20074
  }
19622
20075
 
19623
20076
  // plugins/update-checker.ts
19624
- var PLUGIN_NAME22 = "update-checker";
20077
+ var PLUGIN_NAME23 = "update-checker";
19625
20078
  var PLUGIN_VERSION = "2.0.0";
19626
- logLifecycle(PLUGIN_NAME22, "import", { version: PLUGIN_VERSION });
20079
+ logLifecycle(PLUGIN_NAME23, "import", { version: PLUGIN_VERSION });
19627
20080
  var updateCheckerServer = async (ctx) => {
19628
20081
  const yieldResult = shouldYieldToLocalPlugin({ directory: ctx.directory });
19629
20082
  if (yieldResult.yield) {
19630
- safeWriteLog(PLUGIN_NAME22, {
20083
+ safeWriteLog(PLUGIN_NAME23, {
19631
20084
  level: "info",
19632
20085
  msg: "dev_mode_yield_skip",
19633
20086
  reason: yieldResult.reason,
19634
20087
  markerPath: yieldResult.markerPath,
19635
20088
  detail: formatYieldLog(yieldResult)
19636
20089
  });
19637
- logLifecycle(PLUGIN_NAME22, "activate", {
20090
+ logLifecycle(PLUGIN_NAME23, "activate", {
19638
20091
  yield_to_local: true,
19639
20092
  yield_reason: yieldResult.reason,
19640
20093
  skipped: "auto_install + background_check"
@@ -19643,7 +20096,7 @@ var updateCheckerServer = async (ctx) => {
19643
20096
  }
19644
20097
  const rt = loadRuntimeSync();
19645
20098
  const u = rt.runtime.update;
19646
- logLifecycle(PLUGIN_NAME22, "activate", {
20099
+ logLifecycle(PLUGIN_NAME23, "activate", {
19647
20100
  version: PLUGIN_VERSION,
19648
20101
  auto_check_enabled: u.auto_check_enabled,
19649
20102
  interval_hours: u.interval_hours,
@@ -19655,14 +20108,14 @@ var updateCheckerServer = async (ctx) => {
19655
20108
  repo_fallback: u.repo,
19656
20109
  config_source: "codeforge.json"
19657
20110
  });
19658
- await safeAsync(PLUGIN_NAME22, "opencode_version_check", async () => {
20111
+ await safeAsync(PLUGIN_NAME23, "opencode_version_check", async () => {
19659
20112
  const compat = loadCompatibility();
19660
20113
  const opencodeVer = detectOpencodeVersion();
19661
20114
  const verdict = compareOpencodeVersion({
19662
20115
  currentOpencodeVer: opencodeVer,
19663
20116
  compat
19664
20117
  });
19665
- safeWriteLog(PLUGIN_NAME22, {
20118
+ safeWriteLog(PLUGIN_NAME23, {
19666
20119
  level: "info",
19667
20120
  msg: "opencode_version_check",
19668
20121
  opencodeVer,
@@ -19679,14 +20132,14 @@ var updateCheckerServer = async (ctx) => {
19679
20132
  }
19680
20133
  });
19681
20134
  if (!u.auto_check_enabled) {
19682
- safeWriteLog(PLUGIN_NAME22, {
20135
+ safeWriteLog(PLUGIN_NAME23, {
19683
20136
  level: "info",
19684
20137
  msg: "auto-check disabled (codeforge.json update.auto_check_enabled=false)"
19685
20138
  });
19686
20139
  return {};
19687
20140
  }
19688
20141
  setImmediate(() => {
19689
- safeAsync(PLUGIN_NAME22, "checkAndMaybeUpdate", async () => {
20142
+ safeAsync(PLUGIN_NAME23, "checkAndMaybeUpdate", async () => {
19690
20143
  const local = readLocalVersion();
19691
20144
  let npmResult = null;
19692
20145
  try {
@@ -19697,7 +20150,7 @@ var updateCheckerServer = async (ctx) => {
19697
20150
  timeoutMs: 5000
19698
20151
  });
19699
20152
  } catch (e) {
19700
- safeWriteLog(PLUGIN_NAME22, {
20153
+ safeWriteLog(PLUGIN_NAME23, {
19701
20154
  level: "warn",
19702
20155
  msg: "npm_fetch_failed",
19703
20156
  error: e.message
@@ -19706,7 +20159,7 @@ var updateCheckerServer = async (ctx) => {
19706
20159
  return;
19707
20160
  }
19708
20161
  if (!npmResult) {
19709
- safeWriteLog(PLUGIN_NAME22, {
20162
+ safeWriteLog(PLUGIN_NAME23, {
19710
20163
  level: "info",
19711
20164
  msg: "npm_no_release",
19712
20165
  package: u.package,
@@ -19715,7 +20168,7 @@ var updateCheckerServer = async (ctx) => {
19715
20168
  return;
19716
20169
  }
19717
20170
  const hasUpdate = cmpVersion(local, npmResult.version) < 0;
19718
- safeWriteLog(PLUGIN_NAME22, {
20171
+ safeWriteLog(PLUGIN_NAME23, {
19719
20172
  level: "info",
19720
20173
  msg: "npm_check_result",
19721
20174
  local,
@@ -19730,10 +20183,10 @@ var updateCheckerServer = async (ctx) => {
19730
20183
  更新命令:npx ${u.package} install --global`);
19731
20184
  return;
19732
20185
  }
19733
- await safeAsync(PLUGIN_NAME22, "auto_install_bundle", async () => {
20186
+ await safeAsync(PLUGIN_NAME23, "auto_install_bundle", async () => {
19734
20187
  const target = getOpencodeBundlePath();
19735
20188
  if (!target) {
19736
- safeWriteLog(PLUGIN_NAME22, {
20189
+ safeWriteLog(PLUGIN_NAME23, {
19737
20190
  level: "warn",
19738
20191
  msg: "auto_install_skip",
19739
20192
  reason: "无法定位 opencode bundle 路径"
@@ -19752,7 +20205,7 @@ var updateCheckerServer = async (ctx) => {
19752
20205
  oldVersion: local,
19753
20206
  keepBackups: u.backup_keep
19754
20207
  });
19755
- safeWriteLog(PLUGIN_NAME22, {
20208
+ safeWriteLog(PLUGIN_NAME23, {
19756
20209
  level: "info",
19757
20210
  msg: "auto_install_success",
19758
20211
  local,
@@ -19776,14 +20229,14 @@ function detectOpencodeVersion() {
19776
20229
  }
19777
20230
  function getOpencodeBundlePath() {
19778
20231
  const candidates = [];
19779
- candidates.push(join18(homedir8(), ".config", "opencode", "codeforge", "index.js"));
20232
+ candidates.push(join19(homedir8(), ".config", "opencode", "codeforge", "index.js"));
19780
20233
  if (process.platform === "win32") {
19781
20234
  const appData = process.env["APPDATA"];
19782
20235
  if (appData)
19783
- candidates.push(join18(appData, "opencode", "codeforge", "index.js"));
20236
+ candidates.push(join19(appData, "opencode", "codeforge", "index.js"));
19784
20237
  const localAppData = process.env["LOCALAPPDATA"];
19785
20238
  if (localAppData)
19786
- candidates.push(join18(localAppData, "opencode", "codeforge", "index.js"));
20239
+ candidates.push(join19(localAppData, "opencode", "codeforge", "index.js"));
19787
20240
  }
19788
20241
  for (const c of candidates) {
19789
20242
  if (existsSync5(c))
@@ -19792,7 +20245,7 @@ function getOpencodeBundlePath() {
19792
20245
  return candidates[0] ?? null;
19793
20246
  }
19794
20247
  async function fallbackToGitHubReleases(ctx, u) {
19795
- await safeAsync(PLUGIN_NAME22, "github_fallback", async () => {
20248
+ await safeAsync(PLUGIN_NAME23, "github_fallback", async () => {
19796
20249
  const result = await checkUpdateOnce({
19797
20250
  repo: u.repo,
19798
20251
  intervalMs: u.interval_hours * 3600 * 1000
@@ -19802,7 +20255,7 @@ async function fallbackToGitHubReleases(ctx, u) {
19802
20255
  }
19803
20256
  async function reportLegacyResult(ctx, result, repo) {
19804
20257
  if (result.error && !result.fromCache) {
19805
- safeWriteLog(PLUGIN_NAME22, {
20258
+ safeWriteLog(PLUGIN_NAME23, {
19806
20259
  level: "warn",
19807
20260
  msg: `update check failed: ${result.error}`,
19808
20261
  ...result
@@ -19810,7 +20263,7 @@ async function reportLegacyResult(ctx, result, repo) {
19810
20263
  return;
19811
20264
  }
19812
20265
  if (!result.hasUpdate) {
19813
- safeWriteLog(PLUGIN_NAME22, {
20266
+ safeWriteLog(PLUGIN_NAME23, {
19814
20267
  level: "info",
19815
20268
  msg: `up-to-date (local=${result.local}, remote=${result.remote}${result.fromCache ? ", from_cache" : ""})`,
19816
20269
  ...result
@@ -19820,7 +20273,7 @@ async function reportLegacyResult(ctx, result, repo) {
19820
20273
  const updateCmd = `bunx --bun github:${repo} install`;
19821
20274
  const toast = `[CodeForge] 有新版本:${result.local} → ${result.remote}
19822
20275
  更新命令:${updateCmd}`;
19823
- safeWriteLog(PLUGIN_NAME22, {
20276
+ safeWriteLog(PLUGIN_NAME23, {
19824
20277
  level: "info",
19825
20278
  msg: "new_version_available_github_fallback",
19826
20279
  local: result.local,
@@ -19831,17 +20284,17 @@ async function reportLegacyResult(ctx, result, repo) {
19831
20284
  await postToast(ctx, toast);
19832
20285
  }
19833
20286
  async function postToast(ctx, message) {
19834
- await safeAsync(PLUGIN_NAME22, "client.app.log", async () => {
20287
+ await safeAsync(PLUGIN_NAME23, "client.app.log", async () => {
19835
20288
  await ctx.client.app.log({
19836
20289
  body: {
19837
- service: PLUGIN_NAME22,
20290
+ service: PLUGIN_NAME23,
19838
20291
  level: "info",
19839
20292
  message
19840
20293
  }
19841
20294
  });
19842
20295
  });
19843
20296
  }
19844
- var handler22 = updateCheckerServer;
20297
+ var handler23 = updateCheckerServer;
19845
20298
 
19846
20299
  // plugins/workflow-engine.ts
19847
20300
  init_opencode_plugin_helpers();
@@ -19850,55 +20303,55 @@ import * as path19 from "node:path";
19850
20303
  // lib/workflow-loader.ts
19851
20304
  import { promises as fs14 } from "node:fs";
19852
20305
  import * as path18 from "node:path";
19853
- import { z as z28 } from "zod";
19854
- var ActionSchema = z28.object({
19855
- tool: z28.string().min(1, "action.tool 不能为空"),
19856
- args: z28.record(z28.string(), z28.unknown()).optional().default({}),
19857
- on_error: z28.enum(["retry", "skip", "abort"]).optional()
20306
+ import { z as z29 } from "zod";
20307
+ var ActionSchema = z29.object({
20308
+ tool: z29.string().min(1, "action.tool 不能为空"),
20309
+ args: z29.record(z29.string(), z29.unknown()).optional().default({}),
20310
+ on_error: z29.enum(["retry", "skip", "abort"]).optional()
19858
20311
  });
19859
- var StepSchema = z28.object({
19860
- name: z28.string().min(1, "step.name 不能为空"),
19861
- agent: z28.string().min(1, "step.agent 不能为空").describe("agent 名(与 agents/<name>.md 对应)"),
19862
- description: z28.string().optional(),
19863
- inject_context: z28.record(z28.string(), z28.unknown()).optional(),
19864
- requires_human_approval: z28.boolean().optional().default(false),
19865
- actions: z28.array(ActionSchema).optional().default([]),
19866
- on_error: z28.enum(["retry", "skip", "abort"]).optional().default("abort"),
19867
- max_retries: z28.number().int().min(0).max(10).optional().default(2),
19868
- timeout: z28.string().regex(/^\d+(?:ms|s|m|h)$/, "timeout 必须是 数字+单位(ms/s/m/h),如 5m").optional(),
19869
- auto_feedback: z28.object({
19870
- test_cmd: z28.string().optional().describe("测试命令,如 npm test"),
19871
- lint_cmd: z28.string().optional().describe("lint 命令,如 npm run lint"),
19872
- max_retries: z28.number().int().min(1).max(10).optional().default(3),
19873
- error_excerpt_lines: z28.number().int().min(1).max(50).optional().default(5),
19874
- escalate_to: z28.string().optional().default("reviewer").describe("超上限后兜底 agent,默认 reviewer")
20312
+ var StepSchema = z29.object({
20313
+ name: z29.string().min(1, "step.name 不能为空"),
20314
+ agent: z29.string().min(1, "step.agent 不能为空").describe("agent 名(与 agents/<name>.md 对应)"),
20315
+ description: z29.string().optional(),
20316
+ inject_context: z29.record(z29.string(), z29.unknown()).optional(),
20317
+ requires_human_approval: z29.boolean().optional().default(false),
20318
+ actions: z29.array(ActionSchema).optional().default([]),
20319
+ on_error: z29.enum(["retry", "skip", "abort"]).optional().default("abort"),
20320
+ max_retries: z29.number().int().min(0).max(10).optional().default(2),
20321
+ timeout: z29.string().regex(/^\d+(?:ms|s|m|h)$/, "timeout 必须是 数字+单位(ms/s/m/h),如 5m").optional(),
20322
+ auto_feedback: z29.object({
20323
+ test_cmd: z29.string().optional().describe("测试命令,如 npm test"),
20324
+ lint_cmd: z29.string().optional().describe("lint 命令,如 npm run lint"),
20325
+ max_retries: z29.number().int().min(1).max(10).optional().default(3),
20326
+ error_excerpt_lines: z29.number().int().min(1).max(50).optional().default(5),
20327
+ escalate_to: z29.string().optional().default("reviewer").describe("超上限后兜底 agent,默认 reviewer")
19875
20328
  }).refine((d) => Boolean(d.test_cmd || d.lint_cmd), { message: "auto_feedback 必须至少配置 test_cmd 或 lint_cmd 之一" }).optional(),
19876
- on_decision: z28.object({
19877
- APPROVE: z28.union([
19878
- z28.literal("continue"),
19879
- z28.literal("abort"),
19880
- z28.object({ action: z28.literal("goto"), target: z28.string().min(1) })
20329
+ on_decision: z29.object({
20330
+ APPROVE: z29.union([
20331
+ z29.literal("continue"),
20332
+ z29.literal("abort"),
20333
+ z29.object({ action: z29.literal("goto"), target: z29.string().min(1) })
19881
20334
  ]).optional(),
19882
- REQUEST_CHANGES: z28.union([
19883
- z28.literal("continue"),
19884
- z28.literal("abort"),
19885
- z28.object({ action: z28.literal("goto"), target: z28.string().min(1) })
20335
+ REQUEST_CHANGES: z29.union([
20336
+ z29.literal("continue"),
20337
+ z29.literal("abort"),
20338
+ z29.object({ action: z29.literal("goto"), target: z29.string().min(1) })
19886
20339
  ]).optional(),
19887
- BLOCK: z28.union([
19888
- z28.literal("continue"),
19889
- z28.literal("abort"),
19890
- z28.object({ action: z28.literal("goto"), target: z28.string().min(1) })
20340
+ BLOCK: z29.union([
20341
+ z29.literal("continue"),
20342
+ z29.literal("abort"),
20343
+ z29.object({ action: z29.literal("goto"), target: z29.string().min(1) })
19891
20344
  ]).optional()
19892
20345
  }).refine((d) => Boolean(d.APPROVE || d.REQUEST_CHANGES || d.BLOCK), { message: "on_decision 必须至少配置 APPROVE / REQUEST_CHANGES / BLOCK 之一" }).optional()
19893
20346
  }).strict();
19894
- var WorkflowSchema = z28.object({
19895
- name: z28.string().min(1, "workflow.name 不能为空"),
19896
- description: z28.string().optional().default(""),
19897
- version: z28.string().optional().default("1.0.0"),
19898
- trigger: z28.string().min(1).refine((v) => /^\/[a-z][\w-]*$/.test(v) || /^event:[a-z][\w.-]+$/.test(v), "trigger 必须是 /command-name 或 event:xxx 形式"),
19899
- context_template: z28.string().optional(),
19900
- max_loops: z28.number().int().min(1).max(10).optional().default(3),
19901
- steps: z28.array(StepSchema).min(1, "workflow.steps 不能为空")
20347
+ var WorkflowSchema = z29.object({
20348
+ name: z29.string().min(1, "workflow.name 不能为空"),
20349
+ description: z29.string().optional().default(""),
20350
+ version: z29.string().optional().default("1.0.0"),
20351
+ trigger: z29.string().min(1).refine((v) => /^\/[a-z][\w-]*$/.test(v) || /^event:[a-z][\w.-]+$/.test(v), "trigger 必须是 /command-name 或 event:xxx 形式"),
20352
+ context_template: z29.string().optional(),
20353
+ max_loops: z29.number().int().min(1).max(10).optional().default(3),
20354
+ steps: z29.array(StepSchema).min(1, "workflow.steps 不能为空")
19902
20355
  }).strict();
19903
20356
  function parseWorkflowYaml(yaml, sourcePath = "<inline>") {
19904
20357
  let raw;
@@ -20297,9 +20750,9 @@ async function runStepAutoFeedback(step, adapter) {
20297
20750
  }
20298
20751
 
20299
20752
  // plugins/workflow-engine.ts
20300
- var PLUGIN_NAME23 = "workflow-engine";
20301
- logLifecycle(PLUGIN_NAME23, "import", {});
20302
- var fallbackLog2 = makePluginLogger(PLUGIN_NAME23);
20753
+ var PLUGIN_NAME24 = "workflow-engine";
20754
+ logLifecycle(PLUGIN_NAME24, "import", {});
20755
+ var fallbackLog2 = makePluginLogger(PLUGIN_NAME24);
20303
20756
  var _registry = null;
20304
20757
  async function loadRegistry(workflowsDir) {
20305
20758
  const { loaded, failed } = await loadWorkflowsFromDir(workflowsDir);
@@ -20316,35 +20769,35 @@ async function ensureRegistry(workflowsDir = "workflows") {
20316
20769
  }
20317
20770
  async function handleCommandInvoked(raw, workflowsDir = "workflows") {
20318
20771
  const ctx = raw ?? {};
20319
- const log13 = ctx.log ?? fallbackLog2;
20772
+ const log14 = ctx.log ?? fallbackLog2;
20320
20773
  const command = typeof ctx.command === "string" ? ctx.command : null;
20321
20774
  if (!command) {
20322
- log13.warn(`[${PLUGIN_NAME23}] command.invoked 缺 command 字段`, ctx);
20775
+ log14.warn(`[${PLUGIN_NAME24}] command.invoked 缺 command 字段`, ctx);
20323
20776
  return null;
20324
20777
  }
20325
20778
  const reg = await ensureRegistry(workflowsDir);
20326
20779
  if (reg.errors.length) {
20327
- log13.warn(`[${PLUGIN_NAME23}] 有 ${reg.errors.length} 个 workflow 加载失败`, reg.errors);
20780
+ log14.warn(`[${PLUGIN_NAME24}] 有 ${reg.errors.length} 个 workflow 加载失败`, reg.errors);
20328
20781
  }
20329
20782
  const wf = reg.workflows.find((w) => matchesTrigger(w, command));
20330
20783
  if (!wf) {
20331
- log13.info(`[${PLUGIN_NAME23}] no workflow matches "${command}"`);
20784
+ log14.info(`[${PLUGIN_NAME24}] no workflow matches "${command}"`);
20332
20785
  return null;
20333
20786
  }
20334
- log13.info(`[${PLUGIN_NAME23}] dispatch "${command}" → workflow "${wf.name}"`);
20787
+ log14.info(`[${PLUGIN_NAME24}] dispatch "${command}" → workflow "${wf.name}"`);
20335
20788
  try {
20336
20789
  const result = await run(wf, {
20337
20790
  mode: ctx.adapter ? "real" : "dry_run",
20338
20791
  autonomy: ctx.autonomy ?? "semi",
20339
20792
  adapter: ctx.adapter
20340
20793
  });
20341
- log13.info(`[${PLUGIN_NAME23}] workflow "${wf.name}" 完成 (${result.plan.mode})`, {
20794
+ log14.info(`[${PLUGIN_NAME24}] workflow "${wf.name}" 完成 (${result.plan.mode})`, {
20342
20795
  steps: result.plan.steps.length,
20343
20796
  results: result.results.length
20344
20797
  });
20345
20798
  return result;
20346
20799
  } catch (err) {
20347
- log13.error(`[${PLUGIN_NAME23}] workflow "${wf.name}" 执行失败`, {
20800
+ log14.error(`[${PLUGIN_NAME24}] workflow "${wf.name}" 执行失败`, {
20348
20801
  error: err instanceof Error ? err.message : String(err)
20349
20802
  });
20350
20803
  throw err;
@@ -20353,15 +20806,15 @@ async function handleCommandInvoked(raw, workflowsDir = "workflows") {
20353
20806
  var workflowEngineServer = async (ctx) => {
20354
20807
  const directory = ctx.directory ?? process.cwd();
20355
20808
  const workflowsDir = path19.join(directory, "workflows");
20356
- ensureRegistry(workflowsDir).catch((err) => fallbackLog2.warn(`[${PLUGIN_NAME23}] preload workflows failed`, {
20809
+ ensureRegistry(workflowsDir).catch((err) => fallbackLog2.warn(`[${PLUGIN_NAME24}] preload workflows failed`, {
20357
20810
  error: err instanceof Error ? err.message : String(err)
20358
20811
  }));
20359
- logLifecycle(PLUGIN_NAME23, "activate", { directory, workflowsDir });
20812
+ logLifecycle(PLUGIN_NAME24, "activate", { directory, workflowsDir });
20360
20813
  return {
20361
20814
  "command.execute.before": async (input, output) => {
20362
- await safeAsync(PLUGIN_NAME23, "command.execute.before", async () => {
20815
+ await safeAsync(PLUGIN_NAME24, "command.execute.before", async () => {
20363
20816
  const cmd = input.command.startsWith("/") ? input.command : `/${input.command}`;
20364
- safeWriteLog(PLUGIN_NAME23, {
20817
+ safeWriteLog(PLUGIN_NAME24, {
20365
20818
  hook: "command.execute.before",
20366
20819
  command: cmd,
20367
20820
  sessionID: input.sessionID
@@ -20376,14 +20829,14 @@ var workflowEngineServer = async (ctx) => {
20376
20829
  });
20377
20830
  },
20378
20831
  "chat.message": async (input, output) => {
20379
- await safeAsync(PLUGIN_NAME23, "chat.message", async () => {
20832
+ await safeAsync(PLUGIN_NAME24, "chat.message", async () => {
20380
20833
  const text = extractUserText(output).trim();
20381
20834
  if (!text.startsWith("/"))
20382
20835
  return;
20383
20836
  const cmd = text.split(/\s+/)[0];
20384
20837
  if (!cmd)
20385
20838
  return;
20386
- safeWriteLog(PLUGIN_NAME23, {
20839
+ safeWriteLog(PLUGIN_NAME24, {
20387
20840
  hook: "chat.message",
20388
20841
  command: cmd,
20389
20842
  sessionID: input.sessionID
@@ -20393,11 +20846,11 @@ var workflowEngineServer = async (ctx) => {
20393
20846
  }
20394
20847
  };
20395
20848
  };
20396
- var handler23 = workflowEngineServer;
20849
+ var handler24 = workflowEngineServer;
20397
20850
 
20398
20851
  // src/index.ts
20399
20852
  var PLUGIN_ID = "codeforge";
20400
- var log13 = makePluginLogger(PLUGIN_ID);
20853
+ var log14 = makePluginLogger(PLUGIN_ID);
20401
20854
  logLifecycle(PLUGIN_ID, "import", { entry: "src/index.ts" });
20402
20855
  var HANDLERS = [
20403
20856
  { name: "agent-router", init: handler },
@@ -20407,22 +20860,23 @@ var HANDLERS = [
20407
20860
  { name: "auto-learning", init: handler5 },
20408
20861
  { name: "channels", init: handler6 },
20409
20862
  { name: "codeforge-tools", init: handler8 },
20410
- { name: "kh-auto-context", init: handler9 },
20411
- { name: "kh-reminder", init: handler10 },
20412
- { name: "memories-context", init: handler11 },
20413
- { name: "model-fallback", init: handler12 },
20414
- { name: "parallel-tool-nudge", init: handler15 },
20415
- { name: "pwsh-utf8", init: handler16 },
20416
- { name: "session-recovery", init: handler17 },
20417
- { name: "subtask-heartbeat", init: handler13 },
20418
- { name: "subtasks", init: handler18 },
20419
- { name: "parallel-status", init: handler14 },
20420
- { name: "terminal-monitor", init: handler19 },
20421
- { name: "token-manager", init: handler20 },
20863
+ { name: "discover-spec-suggest", init: handler9 },
20864
+ { name: "kh-auto-context", init: handler10 },
20865
+ { name: "kh-reminder", init: handler11 },
20866
+ { name: "memories-context", init: handler12 },
20867
+ { name: "model-fallback", init: handler13 },
20868
+ { name: "parallel-tool-nudge", init: handler16 },
20869
+ { name: "pwsh-utf8", init: handler17 },
20870
+ { name: "session-recovery", init: handler18 },
20871
+ { name: "subtask-heartbeat", init: handler14 },
20872
+ { name: "subtasks", init: handler19 },
20873
+ { name: "parallel-status", init: handler15 },
20874
+ { name: "terminal-monitor", init: handler20 },
20875
+ { name: "token-manager", init: handler21 },
20422
20876
  { name: "tool-heartbeat", init: handler7 },
20423
- { name: "tool-policy", init: handler21 },
20424
- { name: "update-checker", init: handler22 },
20425
- { name: "workflow-engine", init: handler23 }
20877
+ { name: "tool-policy", init: handler22 },
20878
+ { name: "update-checker", init: handler23 },
20879
+ { name: "workflow-engine", init: handler24 }
20426
20880
  ];
20427
20881
  function makeSerialHook(hookName, fns) {
20428
20882
  return async (input, output) => {
@@ -20430,7 +20884,7 @@ function makeSerialHook(hookName, fns) {
20430
20884
  try {
20431
20885
  await fn(input, output);
20432
20886
  } catch (err) {
20433
- log13.warn(`[${PLUGIN_ID}] ${hookName} handler 异常(已隔离)`, {
20887
+ log14.warn(`[${PLUGIN_ID}] ${hookName} handler 异常(已隔离)`, {
20434
20888
  error: err instanceof Error ? err.message : String(err)
20435
20889
  });
20436
20890
  }
@@ -20443,7 +20897,7 @@ function createCodeforgeServer(opts) {
20443
20897
  const yieldResult = shouldYieldToLocalPlugin({ directory: input.directory });
20444
20898
  if (yieldResult.yield) {
20445
20899
  const msg = formatYieldLog(yieldResult);
20446
- log13.info(msg, { reason: yieldResult.reason, markerPath: yieldResult.markerPath });
20900
+ log14.info(msg, { reason: yieldResult.reason, markerPath: yieldResult.markerPath });
20447
20901
  logLifecycle(PLUGIN_ID, "activate", {
20448
20902
  yield_to_local: true,
20449
20903
  yield_reason: yieldResult.reason,
@@ -20461,7 +20915,7 @@ function createCodeforgeServer(opts) {
20461
20915
  if (r.status === "fulfilled" && r.value && typeof r.value === "object") {
20462
20916
  hooksList.push(r.value);
20463
20917
  } else if (r.status === "rejected") {
20464
- log13.warn(`[${PLUGIN_ID}] handler ${HANDLERS[i].name} init failed (隔离,其他 handler 继续)`, { error: r.reason instanceof Error ? r.reason.message : String(r.reason) });
20918
+ log14.warn(`[${PLUGIN_ID}] handler ${HANDLERS[i].name} init failed (隔离,其他 handler 继续)`, { error: r.reason instanceof Error ? r.reason.message : String(r.reason) });
20465
20919
  }
20466
20920
  });
20467
20921
  logLifecycle(PLUGIN_ID, "activate", {
@@ -20510,7 +20964,7 @@ function createCodeforgeServer(opts) {
20510
20964
  try {
20511
20965
  await fn(envelope);
20512
20966
  } catch (err) {
20513
- log13.warn(`[${PLUGIN_ID}] event handler 异常(已隔离)`, {
20967
+ log14.warn(`[${PLUGIN_ID}] event handler 异常(已隔离)`, {
20514
20968
  error: err instanceof Error ? err.message : String(err)
20515
20969
  });
20516
20970
  }